likes, mention parsing, lots of stuff
Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
@@ -13,9 +13,14 @@
|
|||||||
4C0A3F93280F66F5000448DE /* ReplyMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F92280F66F5000448DE /* ReplyMap.swift */; };
|
4C0A3F93280F66F5000448DE /* ReplyMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F92280F66F5000448DE /* ReplyMap.swift */; };
|
||||||
4C0A3F95280F6C78000448DE /* ReplyQuoteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F94280F6C78000448DE /* ReplyQuoteView.swift */; };
|
4C0A3F95280F6C78000448DE /* ReplyQuoteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F94280F6C78000448DE /* ReplyQuoteView.swift */; };
|
||||||
4C0A3F97280F8E02000448DE /* ThreadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F96280F8E02000448DE /* ThreadView.swift */; };
|
4C0A3F97280F8E02000448DE /* ThreadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F96280F8E02000448DE /* ThreadView.swift */; };
|
||||||
|
4C363A8428233689006E126D /* Parser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363A8328233689006E126D /* Parser.swift */; };
|
||||||
4C3BEFD22819DB9B00B3DE84 /* ProfileModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFD12819DB9B00B3DE84 /* ProfileModel.swift */; };
|
4C3BEFD22819DB9B00B3DE84 /* ProfileModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFD12819DB9B00B3DE84 /* ProfileModel.swift */; };
|
||||||
4C3BEFD42819DE8F00B3DE84 /* NostrKind.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFD32819DE8F00B3DE84 /* NostrKind.swift */; };
|
4C3BEFD42819DE8F00B3DE84 /* NostrKind.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFD32819DE8F00B3DE84 /* NostrKind.swift */; };
|
||||||
4C3BEFD6281D995700B3DE84 /* ActionBarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFD5281D995700B3DE84 /* ActionBarModel.swift */; };
|
4C3BEFD6281D995700B3DE84 /* ActionBarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFD5281D995700B3DE84 /* ActionBarModel.swift */; };
|
||||||
|
4C3BEFDA281DCA1400B3DE84 /* LikeCounter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFD9281DCA1400B3DE84 /* LikeCounter.swift */; };
|
||||||
|
4C3BEFDC281DCE6100B3DE84 /* Liked.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFDB281DCE6100B3DE84 /* Liked.swift */; };
|
||||||
|
4C3BEFDE281DD59C00B3DE84 /* ParsedRefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFDD281DD59C00B3DE84 /* ParsedRefs.swift */; };
|
||||||
|
4C3BEFE0281DE1ED00B3DE84 /* DamusState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFDF281DE1ED00B3DE84 /* DamusState.swift */; };
|
||||||
4C75EFA427FA577B0006080F /* PostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C75EFA327FA577B0006080F /* PostView.swift */; };
|
4C75EFA427FA577B0006080F /* PostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C75EFA327FA577B0006080F /* PostView.swift */; };
|
||||||
4C75EFA627FF87A20006080F /* Nostr.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C75EFA527FF87A20006080F /* Nostr.swift */; };
|
4C75EFA627FF87A20006080F /* Nostr.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C75EFA527FF87A20006080F /* Nostr.swift */; };
|
||||||
4C75EFAD28049CFB0006080F /* PostButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C75EFAC28049CFB0006080F /* PostButton.swift */; };
|
4C75EFAD28049CFB0006080F /* PostButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C75EFAC28049CFB0006080F /* PostButton.swift */; };
|
||||||
@@ -26,6 +31,7 @@
|
|||||||
4C75EFB728049D990006080F /* RelayPool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C75EFB628049D990006080F /* RelayPool.swift */; };
|
4C75EFB728049D990006080F /* RelayPool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C75EFB628049D990006080F /* RelayPool.swift */; };
|
||||||
4C75EFB92804A2740006080F /* EventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C75EFB82804A2740006080F /* EventView.swift */; };
|
4C75EFB92804A2740006080F /* EventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C75EFB82804A2740006080F /* EventView.swift */; };
|
||||||
4C75EFBB2804A34C0006080F /* ProofOfWork.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C75EFBA2804A34C0006080F /* ProofOfWork.swift */; };
|
4C75EFBB2804A34C0006080F /* ProofOfWork.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C75EFBA2804A34C0006080F /* ProofOfWork.swift */; };
|
||||||
|
4C7FF7D52823313F009601DB /* Mentions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C7FF7D42823313F009601DB /* Mentions.swift */; };
|
||||||
4C8682872814DE470026224F /* ProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C8682862814DE470026224F /* ProfileView.swift */; };
|
4C8682872814DE470026224F /* ProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C8682862814DE470026224F /* ProfileView.swift */; };
|
||||||
4CA2EFA0280E37AC0044ACD8 /* TimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */; };
|
4CA2EFA0280E37AC0044ACD8 /* TimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */; };
|
||||||
4CACA9D5280C31E100D9BBE8 /* ReplyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */; };
|
4CACA9D5280C31E100D9BBE8 /* ReplyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */; };
|
||||||
@@ -74,9 +80,14 @@
|
|||||||
4C0A3F92280F66F5000448DE /* ReplyMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyMap.swift; sourceTree = "<group>"; };
|
4C0A3F92280F66F5000448DE /* ReplyMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyMap.swift; sourceTree = "<group>"; };
|
||||||
4C0A3F94280F6C78000448DE /* ReplyQuoteView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyQuoteView.swift; sourceTree = "<group>"; };
|
4C0A3F94280F6C78000448DE /* ReplyQuoteView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyQuoteView.swift; sourceTree = "<group>"; };
|
||||||
4C0A3F96280F8E02000448DE /* ThreadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadView.swift; sourceTree = "<group>"; };
|
4C0A3F96280F8E02000448DE /* ThreadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadView.swift; sourceTree = "<group>"; };
|
||||||
|
4C363A8328233689006E126D /* Parser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Parser.swift; sourceTree = "<group>"; };
|
||||||
4C3BEFD12819DB9B00B3DE84 /* ProfileModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileModel.swift; sourceTree = "<group>"; };
|
4C3BEFD12819DB9B00B3DE84 /* ProfileModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileModel.swift; sourceTree = "<group>"; };
|
||||||
4C3BEFD32819DE8F00B3DE84 /* NostrKind.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NostrKind.swift; sourceTree = "<group>"; };
|
4C3BEFD32819DE8F00B3DE84 /* NostrKind.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NostrKind.swift; sourceTree = "<group>"; };
|
||||||
4C3BEFD5281D995700B3DE84 /* ActionBarModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionBarModel.swift; sourceTree = "<group>"; };
|
4C3BEFD5281D995700B3DE84 /* ActionBarModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionBarModel.swift; sourceTree = "<group>"; };
|
||||||
|
4C3BEFD9281DCA1400B3DE84 /* LikeCounter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LikeCounter.swift; sourceTree = "<group>"; };
|
||||||
|
4C3BEFDB281DCE6100B3DE84 /* Liked.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Liked.swift; sourceTree = "<group>"; };
|
||||||
|
4C3BEFDD281DD59C00B3DE84 /* ParsedRefs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParsedRefs.swift; sourceTree = "<group>"; };
|
||||||
|
4C3BEFDF281DE1ED00B3DE84 /* DamusState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusState.swift; sourceTree = "<group>"; };
|
||||||
4C75EFA327FA577B0006080F /* PostView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostView.swift; sourceTree = "<group>"; };
|
4C75EFA327FA577B0006080F /* PostView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostView.swift; sourceTree = "<group>"; };
|
||||||
4C75EFA527FF87A20006080F /* Nostr.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Nostr.swift; sourceTree = "<group>"; };
|
4C75EFA527FF87A20006080F /* Nostr.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Nostr.swift; sourceTree = "<group>"; };
|
||||||
4C75EFA72804823E0006080F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
4C75EFA72804823E0006080F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||||
@@ -88,6 +99,7 @@
|
|||||||
4C75EFB628049D990006080F /* RelayPool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayPool.swift; sourceTree = "<group>"; };
|
4C75EFB628049D990006080F /* RelayPool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayPool.swift; sourceTree = "<group>"; };
|
||||||
4C75EFB82804A2740006080F /* EventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventView.swift; sourceTree = "<group>"; };
|
4C75EFB82804A2740006080F /* EventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventView.swift; sourceTree = "<group>"; };
|
||||||
4C75EFBA2804A34C0006080F /* ProofOfWork.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProofOfWork.swift; sourceTree = "<group>"; };
|
4C75EFBA2804A34C0006080F /* ProofOfWork.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProofOfWork.swift; sourceTree = "<group>"; };
|
||||||
|
4C7FF7D42823313F009601DB /* Mentions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Mentions.swift; sourceTree = "<group>"; };
|
||||||
4C8682862814DE470026224F /* ProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = "<group>"; };
|
4C8682862814DE470026224F /* ProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = "<group>"; };
|
||||||
4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineView.swift; sourceTree = "<group>"; };
|
4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineView.swift; sourceTree = "<group>"; };
|
||||||
4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyView.swift; sourceTree = "<group>"; };
|
4CACA9D4280C31E100D9BBE8 /* ReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplyView.swift; sourceTree = "<group>"; };
|
||||||
@@ -148,6 +160,11 @@
|
|||||||
4C0A3F92280F66F5000448DE /* ReplyMap.swift */,
|
4C0A3F92280F66F5000448DE /* ReplyMap.swift */,
|
||||||
4C3BEFD12819DB9B00B3DE84 /* ProfileModel.swift */,
|
4C3BEFD12819DB9B00B3DE84 /* ProfileModel.swift */,
|
||||||
4C3BEFD5281D995700B3DE84 /* ActionBarModel.swift */,
|
4C3BEFD5281D995700B3DE84 /* ActionBarModel.swift */,
|
||||||
|
4C3BEFD9281DCA1400B3DE84 /* LikeCounter.swift */,
|
||||||
|
4C3BEFDB281DCE6100B3DE84 /* Liked.swift */,
|
||||||
|
4C3BEFDD281DD59C00B3DE84 /* ParsedRefs.swift */,
|
||||||
|
4C3BEFDF281DE1ED00B3DE84 /* DamusState.swift */,
|
||||||
|
4C7FF7D42823313F009601DB /* Mentions.swift */,
|
||||||
);
|
);
|
||||||
path = Models;
|
path = Models;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -192,6 +209,14 @@
|
|||||||
path = Nostr;
|
path = Nostr;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
4C7FF7D628233637009601DB /* Util */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
4C363A8328233689006E126D /* Parser.swift */,
|
||||||
|
);
|
||||||
|
path = Util;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
4CE6DEDA27F7A08100C66700 = {
|
4CE6DEDA27F7A08100C66700 = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -216,6 +241,7 @@
|
|||||||
4CE6DEE527F7A08100C66700 /* damus */ = {
|
4CE6DEE527F7A08100C66700 /* damus */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
4C7FF7D628233637009601DB /* Util */,
|
||||||
4C0A3F8D280F63FF000448DE /* Models */,
|
4C0A3F8D280F63FF000448DE /* Models */,
|
||||||
4C75EFAB28049CC80006080F /* Nostr */,
|
4C75EFAB28049CC80006080F /* Nostr */,
|
||||||
4C75EFA72804823E0006080F /* Info.plist */,
|
4C75EFA72804823E0006080F /* Info.plist */,
|
||||||
@@ -407,14 +433,19 @@
|
|||||||
4CEE2AF5280B29E600AB5EEF /* TimeAgo.swift in Sources */,
|
4CEE2AF5280B29E600AB5EEF /* TimeAgo.swift in Sources */,
|
||||||
4C75EFAD28049CFB0006080F /* PostButton.swift in Sources */,
|
4C75EFAD28049CFB0006080F /* PostButton.swift in Sources */,
|
||||||
4C75EFB92804A2740006080F /* EventView.swift in Sources */,
|
4C75EFB92804A2740006080F /* EventView.swift in Sources */,
|
||||||
|
4C7FF7D52823313F009601DB /* Mentions.swift in Sources */,
|
||||||
4C0A3F8C280F5FCA000448DE /* ChatroomView.swift in Sources */,
|
4C0A3F8C280F5FCA000448DE /* ChatroomView.swift in Sources */,
|
||||||
4C0A3F91280F6528000448DE /* ChatView.swift in Sources */,
|
4C0A3F91280F6528000448DE /* ChatView.swift in Sources */,
|
||||||
4C75EFA627FF87A20006080F /* Nostr.swift in Sources */,
|
4C75EFA627FF87A20006080F /* Nostr.swift in Sources */,
|
||||||
4C75EFB328049D640006080F /* NostrEvent.swift in Sources */,
|
4C75EFB328049D640006080F /* NostrEvent.swift in Sources */,
|
||||||
4CA2EFA0280E37AC0044ACD8 /* TimelineView.swift in Sources */,
|
4CA2EFA0280E37AC0044ACD8 /* TimelineView.swift in Sources */,
|
||||||
|
4C363A8428233689006E126D /* Parser.swift in Sources */,
|
||||||
|
4C3BEFDC281DCE6100B3DE84 /* Liked.swift in Sources */,
|
||||||
4C75EFB128049D510006080F /* NostrResponse.swift in Sources */,
|
4C75EFB128049D510006080F /* NostrResponse.swift in Sources */,
|
||||||
4CEE2AF7280B2DEA00AB5EEF /* ProfileName.swift in Sources */,
|
4CEE2AF7280B2DEA00AB5EEF /* ProfileName.swift in Sources */,
|
||||||
|
4C3BEFDE281DD59C00B3DE84 /* ParsedRefs.swift in Sources */,
|
||||||
4CEE2B02280B39E800AB5EEF /* EventActionBar.swift in Sources */,
|
4CEE2B02280B39E800AB5EEF /* EventActionBar.swift in Sources */,
|
||||||
|
4C3BEFE0281DE1ED00B3DE84 /* DamusState.swift in Sources */,
|
||||||
4C0A3F8F280F640A000448DE /* ThreadModel.swift in Sources */,
|
4C0A3F8F280F640A000448DE /* ThreadModel.swift in Sources */,
|
||||||
4C3BEFD22819DB9B00B3DE84 /* ProfileModel.swift in Sources */,
|
4C3BEFD22819DB9B00B3DE84 /* ProfileModel.swift in Sources */,
|
||||||
4C0A3F93280F66F5000448DE /* ReplyMap.swift in Sources */,
|
4C0A3F93280F66F5000448DE /* ReplyMap.swift in Sources */,
|
||||||
@@ -429,6 +460,7 @@
|
|||||||
4CE6DEE727F7A08100C66700 /* damusApp.swift in Sources */,
|
4CE6DEE727F7A08100C66700 /* damusApp.swift in Sources */,
|
||||||
4CEE2AED2805B22500AB5EEF /* NostrRequest.swift in Sources */,
|
4CEE2AED2805B22500AB5EEF /* NostrRequest.swift in Sources */,
|
||||||
4C0A3F95280F6C78000448DE /* ReplyQuoteView.swift in Sources */,
|
4C0A3F95280F6C78000448DE /* ReplyQuoteView.swift in Sources */,
|
||||||
|
4C3BEFDA281DCA1400B3DE84 /* LikeCounter.swift in Sources */,
|
||||||
4CACA9D5280C31E100D9BBE8 /* ReplyView.swift in Sources */,
|
4CACA9D5280C31E100D9BBE8 /* ReplyView.swift in Sources */,
|
||||||
4C0A3F97280F8E02000448DE /* ThreadView.swift in Sources */,
|
4C0A3F97280F8E02000448DE /* ThreadView.swift in Sources */,
|
||||||
4C75EFA427FA577B0006080F /* PostView.swift in Sources */,
|
4C75EFA427FA577B0006080F /* PostView.swift in Sources */,
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ struct ContentView: View {
|
|||||||
@State var profiles: Profiles = Profiles()
|
@State var profiles: Profiles = Profiles()
|
||||||
@State var friends: [String: ()] = [:]
|
@State var friends: [String: ()] = [:]
|
||||||
@State var loading: Bool = true
|
@State var loading: Bool = true
|
||||||
@State var pool: RelayPool? = nil
|
@State var damus: DamusState? = nil
|
||||||
@State var selected_timeline: Timeline? = .home
|
@State var selected_timeline: Timeline? = .home
|
||||||
@State var is_thread_open: Bool = false
|
@State var is_thread_open: Bool = false
|
||||||
@State var is_profile_open: Bool = false
|
@State var is_profile_open: Bool = false
|
||||||
@@ -135,15 +135,15 @@ struct ContentView: View {
|
|||||||
|
|
||||||
var PostingTimelineView: some View {
|
var PostingTimelineView: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
if let pool = self.pool {
|
if let damus = self.damus {
|
||||||
TimelineView(events: $friend_events, pool: pool)
|
TimelineView(events: $friend_events, damus: damus)
|
||||||
.environmentObject(profiles)
|
.environmentObject(profiles)
|
||||||
}
|
}
|
||||||
PostButtonContainer
|
PostButtonContainer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func MainContent(pool: RelayPool) -> some View {
|
func MainContent(damus: DamusState) -> some View {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
VStack {
|
VStack {
|
||||||
switch selected_timeline {
|
switch selected_timeline {
|
||||||
@@ -154,13 +154,13 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case .notifications:
|
case .notifications:
|
||||||
TimelineView(events: $notifications, pool: pool)
|
TimelineView(events: $notifications, damus: damus)
|
||||||
.environmentObject(profiles)
|
.environmentObject(profiles)
|
||||||
.navigationTitle("Notifications")
|
.navigationTitle("Notifications")
|
||||||
|
|
||||||
case .global:
|
case .global:
|
||||||
|
|
||||||
TimelineView(events: $events, pool: pool)
|
TimelineView(events: $events, damus: damus)
|
||||||
.environmentObject(profiles)
|
.environmentObject(profiles)
|
||||||
.navigationTitle("Global")
|
.navigationTitle("Global")
|
||||||
case .none:
|
case .none:
|
||||||
@@ -174,9 +174,9 @@ struct ContentView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack {
|
VStack {
|
||||||
if let pool = self.pool {
|
if let damus = self.damus {
|
||||||
ZStack {
|
ZStack {
|
||||||
MainContent(pool: pool)
|
MainContent(damus: damus)
|
||||||
.padding([.bottom], -8.0)
|
.padding([.bottom], -8.0)
|
||||||
|
|
||||||
LoadingContainer
|
LoadingContainer
|
||||||
@@ -193,14 +193,14 @@ struct ContentView: View {
|
|||||||
case .post:
|
case .post:
|
||||||
PostView(references: [])
|
PostView(references: [])
|
||||||
case .reply(let event):
|
case .reply(let event):
|
||||||
ReplyView(replying_to: event, pool: pool!)
|
ReplyView(replying_to: event, damus: damus!)
|
||||||
.environmentObject(profiles)
|
.environmentObject(profiles)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onReceive(handle_notify(.boost)) { notif in
|
.onReceive(handle_notify(.boost)) { notif in
|
||||||
let ev = notif.object as! NostrEvent
|
let ev = notif.object as! NostrEvent
|
||||||
let boost = make_boost_event(ev, privkey: privkey, pubkey: pubkey)
|
let boost = make_boost_event(ev, privkey: privkey, pubkey: pubkey)
|
||||||
self.pool?.send(.event(boost))
|
self.damus?.pool.send(.event(boost))
|
||||||
}
|
}
|
||||||
.onReceive(handle_notify(.open_thread)) { obj in
|
.onReceive(handle_notify(.open_thread)) { obj in
|
||||||
//let ev = obj.object as! NostrEvent
|
//let ev = obj.object as! NostrEvent
|
||||||
@@ -211,9 +211,18 @@ struct ContentView: View {
|
|||||||
let ev = notif.object as! NostrEvent
|
let ev = notif.object as! NostrEvent
|
||||||
self.active_sheet = .reply(ev)
|
self.active_sheet = .reply(ev)
|
||||||
}
|
}
|
||||||
|
.onReceive(handle_notify(.like)) { like in
|
||||||
|
let ev = like.object as! NostrEvent
|
||||||
|
guard let like_ev = make_like_event(pubkey: pubkey, liked: ev) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
like_ev.calculate_id()
|
||||||
|
like_ev.sign(privkey: privkey)
|
||||||
|
self.damus?.pool.send(.event(like_ev))
|
||||||
|
}
|
||||||
.onReceive(handle_notify(.broadcast_event)) { obj in
|
.onReceive(handle_notify(.broadcast_event)) { obj in
|
||||||
let ev = obj.object as! NostrEvent
|
let ev = obj.object as! NostrEvent
|
||||||
self.pool?.send(.event(ev))
|
self.damus?.pool.send(.event(ev))
|
||||||
}
|
}
|
||||||
.onReceive(handle_notify(.post)) { obj in
|
.onReceive(handle_notify(.post)) { obj in
|
||||||
let post_res = obj.object as! NostrPostResult
|
let post_res = obj.object as! NostrPostResult
|
||||||
@@ -221,15 +230,15 @@ struct ContentView: View {
|
|||||||
case .post(let post):
|
case .post(let post):
|
||||||
print("post \(post.content)")
|
print("post \(post.content)")
|
||||||
let new_ev = post.to_event(privkey: privkey, pubkey: pubkey)
|
let new_ev = post.to_event(privkey: privkey, pubkey: pubkey)
|
||||||
self.pool?.send(.event(new_ev))
|
self.damus?.pool.send(.event(new_ev))
|
||||||
case .cancel:
|
case .cancel:
|
||||||
active_sheet = nil
|
active_sheet = nil
|
||||||
print("post cancelled")
|
print("post cancelled")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onReceive(timer) { n in
|
.onReceive(timer) { n in
|
||||||
self.pool?.connect_to_disconnected()
|
self.damus?.pool.connect_to_disconnected()
|
||||||
self.loading = (self.pool?.num_connecting ?? 0) != 0
|
self.loading = (self.damus?.pool.num_connecting ?? 0) != 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,7 +301,9 @@ struct ContentView: View {
|
|||||||
|
|
||||||
pool.register_handler(sub_id: sub_id, handler: handle_event)
|
pool.register_handler(sub_id: sub_id, handler: handle_event)
|
||||||
|
|
||||||
self.pool = pool
|
self.damus = DamusState(pool: pool, pubkey: pubkey,
|
||||||
|
likes: EventCounter(our_pubkey: pubkey),
|
||||||
|
boosts: EventCounter(our_pubkey: pubkey))
|
||||||
pool.connect()
|
pool.connect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,7 +317,28 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handle_boost_event(_ ev: NostrEvent) {
|
||||||
|
damus!.boosts.add_event(ev)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handle_like_event(_ ev: NostrEvent) {
|
||||||
|
guard let e = ev.last_refid() else {
|
||||||
|
// no id ref? invalid like event
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK SIGS ON THESE
|
||||||
|
|
||||||
|
switch damus!.likes.add_event(ev) {
|
||||||
|
case .user_already_liked:
|
||||||
|
break
|
||||||
|
case .success(let n):
|
||||||
|
let liked = Liked(like: ev, id: e.ref_id, total: n)
|
||||||
|
notify(.liked, liked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func handle_metadata_event(_ ev: NostrEvent) {
|
func handle_metadata_event(_ ev: NostrEvent) {
|
||||||
guard let profile: Profile = decode_data(Data(ev.content.utf8)) else {
|
guard let profile: Profile = decode_data(Data(ev.content.utf8)) else {
|
||||||
return
|
return
|
||||||
@@ -335,11 +367,15 @@ struct ContentView: View {
|
|||||||
func send_filters(relay_id: String) {
|
func send_filters(relay_id: String) {
|
||||||
// TODO: since times should be based on events from a specific relay
|
// TODO: since times should be based on events from a specific relay
|
||||||
// perhaps we could mark this in the relay pool somehow
|
// perhaps we could mark this in the relay pool somehow
|
||||||
|
|
||||||
let last_text_event = get_last_event_of_kind(relay_id: relay_id, kind: NostrKind.text.rawValue)
|
let last_text_event = get_last_event_of_kind(relay_id: relay_id, kind: NostrKind.text.rawValue)
|
||||||
let since = get_since_time(last_event: last_text_event)
|
let since = get_since_time(last_event: last_text_event)
|
||||||
var since_filter = NostrFilter.filter_text
|
var since_filter = NostrFilter.filter_kinds([1,5,6])
|
||||||
since_filter.since = since
|
since_filter.since = since
|
||||||
|
|
||||||
|
let last_like_event = get_last_event_of_kind(relay_id: relay_id, kind: 7)
|
||||||
|
var like_filter = NostrFilter.filter_kinds([7])
|
||||||
|
like_filter.since = get_since_time(last_event: last_like_event)
|
||||||
|
//like_filter.ids = get_like_pow()
|
||||||
|
|
||||||
let last_metadata_event = get_last_event_of_kind(relay_id: relay_id, kind: NostrKind.metadata.rawValue)
|
let last_metadata_event = get_last_event_of_kind(relay_id: relay_id, kind: NostrKind.metadata.rawValue)
|
||||||
var profile_filter = NostrFilter.filter_profiles
|
var profile_filter = NostrFilter.filter_profiles
|
||||||
@@ -355,9 +391,9 @@ struct ContentView: View {
|
|||||||
var contacts_filter = NostrFilter.filter_contacts
|
var contacts_filter = NostrFilter.filter_contacts
|
||||||
contacts_filter.authors = [self.pubkey]
|
contacts_filter.authors = [self.pubkey]
|
||||||
|
|
||||||
let filters = [since_filter, profile_filter, contacts_filter]
|
let filters = [since_filter, profile_filter, contacts_filter, like_filter]
|
||||||
print("connected to \(relay_id), refreshing from \(since)")
|
print("connected to \(relay_id), refreshing from \(since)")
|
||||||
self.pool?.send(.subscribe(.init(filters: filters, sub_id: sub_id)), to: [relay_id])
|
self.damus?.pool.send(.subscribe(.init(filters: filters, sub_id: sub_id)), to: [relay_id])
|
||||||
//self.pool?.send(.subscribe(.init(filters: [notification_filter], sub_id: "notifications")))
|
//self.pool?.send(.subscribe(.init(filters: [notification_filter], sub_id: "notifications")))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,32 +418,42 @@ struct ContentView: View {
|
|||||||
self.friend_events = self.friend_events.sorted { $0.created_at > $1.created_at }
|
self.friend_events = self.friend_events.sorted { $0.created_at > $1.created_at }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handle_text_event(_ ev: NostrEvent) {
|
||||||
|
if should_hide_event(ev) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.events.append(ev)
|
||||||
|
self.events = self.events.sorted { $0.created_at > $1.created_at }
|
||||||
|
|
||||||
|
handle_friend_event(ev)
|
||||||
|
|
||||||
|
if is_notification(ev: ev, pubkey: pubkey) {
|
||||||
|
handle_notification(ev: ev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func process_event(relay_id: String, ev: NostrEvent) {
|
func process_event(relay_id: String, ev: NostrEvent) {
|
||||||
if has_events[ev.id] == nil {
|
if has_events[ev.id] != nil {
|
||||||
has_events[ev.id] = ()
|
return
|
||||||
let last_k = get_last_event_of_kind(relay_id: relay_id, kind: ev.kind)
|
}
|
||||||
if last_k == nil || ev.created_at > last_k!.created_at {
|
|
||||||
last_event_of_kind[relay_id]?[ev.kind] = ev
|
has_events[ev.id] = ()
|
||||||
}
|
let last_k = get_last_event_of_kind(relay_id: relay_id, kind: ev.kind)
|
||||||
if ev.kind == 1 {
|
if last_k == nil || ev.created_at > last_k!.created_at {
|
||||||
if !should_hide_event(ev) {
|
last_event_of_kind[relay_id]?[ev.kind] = ev
|
||||||
self.events.append(ev)
|
}
|
||||||
self.events = self.events.sorted { $0.created_at > $1.created_at }
|
if ev.kind == 1 {
|
||||||
|
handle_text_event(ev)
|
||||||
handle_friend_event(ev)
|
} else if ev.kind == 0 {
|
||||||
|
handle_metadata_event(ev)
|
||||||
if is_notification(ev: ev, pubkey: pubkey) {
|
} else if ev.kind == 7 {
|
||||||
handle_notification(ev: ev)
|
handle_like_event(ev)
|
||||||
}
|
} else if ev.kind == 3 {
|
||||||
}
|
handle_contact_event(ev)
|
||||||
} else if ev.kind == 0 {
|
|
||||||
handle_metadata_event(ev)
|
if ev.pubkey == pubkey {
|
||||||
} else if ev.kind == 3 {
|
process_friend_events()
|
||||||
handle_contact_event(ev)
|
|
||||||
|
|
||||||
if ev.pubkey == pubkey {
|
|
||||||
process_friend_events()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -436,27 +482,29 @@ struct ContentView: View {
|
|||||||
case .error(let merr):
|
case .error(let merr):
|
||||||
let desc = merr.debugDescription
|
let desc = merr.debugDescription
|
||||||
if desc.contains("Software caused connection abort") {
|
if desc.contains("Software caused connection abort") {
|
||||||
self.pool?.reconnect(to: [relay_id])
|
self.damus?.pool.reconnect(to: [relay_id])
|
||||||
}
|
}
|
||||||
case .disconnected: fallthrough
|
case .disconnected: fallthrough
|
||||||
case .cancelled:
|
case .cancelled:
|
||||||
self.pool?.reconnect(to: [relay_id])
|
self.damus?.pool.reconnect(to: [relay_id])
|
||||||
case .reconnectSuggested(let t):
|
case .reconnectSuggested(let t):
|
||||||
if t {
|
if t {
|
||||||
self.pool?.reconnect(to: [relay_id])
|
self.damus?.pool.reconnect(to: [relay_id])
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
self.loading = (self.pool?.num_connecting ?? 0) != 0
|
self.loading = (self.damus?.pool.num_connecting ?? 0) != 0
|
||||||
|
|
||||||
print("ws_event \(ev)")
|
print("ws_event \(ev)")
|
||||||
|
|
||||||
case .nostr_event(let ev):
|
case .nostr_event(let ev):
|
||||||
switch ev {
|
switch ev {
|
||||||
case .event(let sub_id, let ev):
|
case .event(let sub_id, let ev):
|
||||||
if sub_id != self.sub_id {
|
// globally handle likes
|
||||||
|
let always_process = ev.known_kind == .like || ev.known_kind == .contacts || ev.known_kind == .metadata
|
||||||
|
if !always_process && sub_id != self.sub_id {
|
||||||
// TODO: other views like threads might have their own sub ids, so ignore those events... or should we?
|
// TODO: other views like threads might have their own sub ids, so ignore those events... or should we?
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -593,3 +641,8 @@ func make_boost_event(_ ev: NostrEvent, privkey: String, pubkey: String) -> Nost
|
|||||||
boost.sign(privkey: privkey)
|
boost.sign(privkey: privkey)
|
||||||
return boost
|
return boost
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func get_like_pow() -> [String] {
|
||||||
|
return ["00000"] // 20 bits
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,14 +9,21 @@ import Foundation
|
|||||||
|
|
||||||
|
|
||||||
class ActionBarModel: ObservableObject {
|
class ActionBarModel: ObservableObject {
|
||||||
@Published var our_like_event: NostrEvent? = nil
|
@Published var our_like: NostrEvent?
|
||||||
@Published var our_boost_event: NostrEvent? = nil
|
@Published var our_boost: NostrEvent?
|
||||||
|
@Published var likes: Int
|
||||||
|
|
||||||
|
init(likes: Int, our_like: NostrEvent?, our_boost: NostrEvent?) {
|
||||||
|
self.likes = likes
|
||||||
|
self.our_like = our_like
|
||||||
|
self.our_boost = our_boost
|
||||||
|
}
|
||||||
|
|
||||||
var liked: Bool {
|
var liked: Bool {
|
||||||
return our_like_event != nil
|
return our_like != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var boosted: Bool {
|
var boosted: Bool {
|
||||||
return our_boost_event != nil
|
return our_boost != nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
16
damus/Models/DamusState.swift
Normal file
16
damus/Models/DamusState.swift
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
//
|
||||||
|
// DamusState.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by William Casarin on 2022-04-30.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct DamusState {
|
||||||
|
let pool: RelayPool
|
||||||
|
let pubkey: String
|
||||||
|
let likes: EventCounter
|
||||||
|
let boosts: EventCounter
|
||||||
|
|
||||||
|
}
|
||||||
53
damus/Models/LikeCounter.swift
Normal file
53
damus/Models/LikeCounter.swift
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
//
|
||||||
|
// LikeCounter.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by William Casarin on 2022-04-30.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
|
class EventCounter {
|
||||||
|
var counts: [String: Int] = [:]
|
||||||
|
var user_events: [String: Set<String>] = [:]
|
||||||
|
var our_events: [String: NostrEvent] = [:]
|
||||||
|
var our_pubkey: String
|
||||||
|
|
||||||
|
enum LikeResult {
|
||||||
|
case user_already_liked
|
||||||
|
case success(Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
init (our_pubkey: String) {
|
||||||
|
self.our_pubkey = our_pubkey
|
||||||
|
}
|
||||||
|
|
||||||
|
func add_event(_ ev: NostrEvent) -> LikeResult {
|
||||||
|
let pubkey = ev.pubkey
|
||||||
|
|
||||||
|
if self.user_events[pubkey] == nil {
|
||||||
|
self.user_events[pubkey] = Set()
|
||||||
|
}
|
||||||
|
|
||||||
|
if user_events[pubkey]!.contains(ev.id) {
|
||||||
|
// don't double count
|
||||||
|
return .user_already_liked
|
||||||
|
}
|
||||||
|
|
||||||
|
user_events[pubkey]!.insert(ev.id)
|
||||||
|
|
||||||
|
if ev.pubkey == self.our_pubkey {
|
||||||
|
our_events[ev.id] = ev
|
||||||
|
}
|
||||||
|
|
||||||
|
if counts[ev.id] == nil {
|
||||||
|
counts[ev.id] = 1
|
||||||
|
return .success(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
counts[ev.id]! += 1
|
||||||
|
|
||||||
|
return .success(counts[ev.id]!)
|
||||||
|
}
|
||||||
|
}
|
||||||
19
damus/Models/Liked.swift
Normal file
19
damus/Models/Liked.swift
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
//
|
||||||
|
// Liked.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by William Casarin on 2022-04-30.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct Liked {
|
||||||
|
let like: NostrEvent
|
||||||
|
let id: String
|
||||||
|
let total: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LikeRefs {
|
||||||
|
let thread_id: String?
|
||||||
|
let like_id: String
|
||||||
|
}
|
||||||
132
damus/Models/Mentions.swift
Normal file
132
damus/Models/Mentions.swift
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
//
|
||||||
|
// Mentions.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by William Casarin on 2022-05-04.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum MentionType {
|
||||||
|
case pubkey
|
||||||
|
case event
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Mention {
|
||||||
|
let index: Int
|
||||||
|
let kind: MentionType
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Block {
|
||||||
|
case text(String)
|
||||||
|
case mention(Mention)
|
||||||
|
|
||||||
|
var is_text: Bool {
|
||||||
|
if case .text = self {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var is_mention: Bool {
|
||||||
|
if case .mention = self {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ParsedMentions {
|
||||||
|
let blocks: [Block]
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parser {
|
||||||
|
var pos: Int
|
||||||
|
var str: String
|
||||||
|
|
||||||
|
init(pos: Int, str: String) {
|
||||||
|
self.pos = pos
|
||||||
|
self.str = str
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func consume_until(_ p: Parser, match: Character) -> Bool {
|
||||||
|
var i: Int = 0
|
||||||
|
let sub = substring(p.str, start: p.pos, end: p.str.count)
|
||||||
|
for c in sub {
|
||||||
|
if c == match {
|
||||||
|
p.pos += i
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func substring(_ s: String, start: Int, end: Int) -> Substring {
|
||||||
|
let ind = s.index(s.startIndex, offsetBy: start)
|
||||||
|
let end = s.index(s.startIndex, offsetBy: end)
|
||||||
|
return s[ind..<end]
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_textblock(str: String, from: Int, to: Int) -> Block {
|
||||||
|
return .text(String(substring(str, start: from, end: to)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_mentions(content: String, tags: [[String]]) -> [Block] {
|
||||||
|
let p = Parser(pos: 0, str: content)
|
||||||
|
var blocks: [Block] = []
|
||||||
|
var starting_from: Int = 0
|
||||||
|
|
||||||
|
while p.pos < content.count {
|
||||||
|
if (!consume_until(p, match: "#")) {
|
||||||
|
blocks.append(parse_textblock(str: p.str, from: starting_from, to: p.str.count))
|
||||||
|
return blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
let pre_mention = p.pos
|
||||||
|
if let mention = parse_mention(p, tags: tags) {
|
||||||
|
blocks.append(parse_textblock(str: p.str, from: starting_from, to: pre_mention))
|
||||||
|
blocks.append(.mention(mention))
|
||||||
|
starting_from = p.pos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_mention(_ p: Parser, tags: [[String]]) -> Mention? {
|
||||||
|
let start = p.pos
|
||||||
|
|
||||||
|
if !parse_str(p, "#[") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let digit = parse_digit(p) else {
|
||||||
|
p.pos = start
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !parse_char(p, "]") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var kind: MentionType = .pubkey
|
||||||
|
if digit > tags.count - 1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if tags[digit].count == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch tags[digit][0] {
|
||||||
|
case "e": kind = .event
|
||||||
|
case "p": kind = .pubkey
|
||||||
|
default: return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return Mention(index: digit, kind: kind)
|
||||||
|
}
|
||||||
|
|
||||||
15
damus/Models/ParsedRefs.swift
Normal file
15
damus/Models/ParsedRefs.swift
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
//
|
||||||
|
// ParsedRefs.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by William Casarin on 2022-04-30.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
|
struct ReplyRefs {
|
||||||
|
let thread_id: String
|
||||||
|
let direct_reply: String
|
||||||
|
}
|
||||||
|
|
||||||
@@ -10,34 +10,35 @@ import Foundation
|
|||||||
class ProfileModel: ObservableObject {
|
class ProfileModel: ObservableObject {
|
||||||
@Published var events: [NostrEvent] = []
|
@Published var events: [NostrEvent] = []
|
||||||
let pubkey: String
|
let pubkey: String
|
||||||
let pool: RelayPool
|
let damus: DamusState
|
||||||
|
|
||||||
var seen_event: Set<String> = Set()
|
var seen_event: Set<String> = Set()
|
||||||
var sub_id = UUID().description
|
var sub_id = UUID().description
|
||||||
|
|
||||||
|
init(pubkey: String, damus: DamusState) {
|
||||||
init(pubkey: String, pool: RelayPool) {
|
|
||||||
self.pubkey = pubkey
|
self.pubkey = pubkey
|
||||||
self.pool = pool
|
self.damus = damus
|
||||||
}
|
}
|
||||||
|
|
||||||
func unsubscribe() {
|
func unsubscribe() {
|
||||||
print("unsubscribing from profile \(pubkey) with sub_id \(sub_id)")
|
print("unsubscribing from profile \(pubkey) with sub_id \(sub_id)")
|
||||||
pool.unsubscribe(sub_id: sub_id)
|
damus.pool.unsubscribe(sub_id: sub_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func subscribe() {
|
func subscribe() {
|
||||||
let kinds: [Int] = [
|
let kinds: [Int] = [
|
||||||
NostrKind.text.rawValue,
|
NostrKind.text.rawValue,
|
||||||
NostrKind.delete.rawValue,
|
NostrKind.delete.rawValue,
|
||||||
|
NostrKind.contacts.rawValue,
|
||||||
|
NostrKind.metadata.rawValue,
|
||||||
NostrKind.boost.rawValue
|
NostrKind.boost.rawValue
|
||||||
]
|
]
|
||||||
|
|
||||||
var filter = NostrFilter.filter_kinds(kinds)
|
var filter = NostrFilter.filter_authors([pubkey])
|
||||||
filter.authors = [pubkey]
|
filter.kinds = kinds
|
||||||
|
|
||||||
print("subscribing to profile \(pubkey) with sub_id \(sub_id)")
|
print("subscribing to profile \(pubkey) with sub_id \(sub_id)")
|
||||||
pool.subscribe(sub_id: sub_id, filters: [filter], handler: handle_event)
|
damus.pool.subscribe(sub_id: sub_id, filters: [filter], handler: handle_event)
|
||||||
}
|
}
|
||||||
|
|
||||||
func add_event(_ ev: NostrEvent) {
|
func add_event(_ ev: NostrEvent) {
|
||||||
|
|||||||
@@ -62,14 +62,16 @@ class ThreadModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func subscribe() {
|
func subscribe() {
|
||||||
let kinds: [Int] = [1, 5, 6]
|
let kinds: [Int] = [1, 5, 6, 7]
|
||||||
var ref_events = NostrFilter.filter_kinds(kinds)
|
var ref_events = NostrFilter.filter_kinds(kinds)
|
||||||
var events_filter = NostrFilter.filter_kinds(kinds)
|
var events_filter = NostrFilter.filter_kinds(kinds)
|
||||||
|
//var likes_filter = NostrFilter.filter_kinds(7])
|
||||||
|
|
||||||
// TODO: add referenced relays
|
// TODO: add referenced relays
|
||||||
ref_events.referenced_ids = event.referenced_ids.map { $0.ref_id }
|
ref_events.referenced_ids = event.referenced_ids.map { $0.ref_id }
|
||||||
ref_events.referenced_ids!.append(event.id)
|
ref_events.referenced_ids!.append(event.id)
|
||||||
|
|
||||||
|
//likes_filter.ids = ref_events.referenced_ids!
|
||||||
events_filter.ids = ref_events.referenced_ids!
|
events_filter.ids = ref_events.referenced_ids!
|
||||||
|
|
||||||
print("subscribing to thread \(event.id) with sub_id \(sub_id)")
|
print("subscribing to thread \(event.id) with sub_id \(sub_id)")
|
||||||
@@ -110,7 +112,9 @@ class ThreadModel: ObservableObject {
|
|||||||
switch res {
|
switch res {
|
||||||
case .event(let sub_id, let ev):
|
case .event(let sub_id, let ev):
|
||||||
if sub_id == self.sub_id {
|
if sub_id == self.sub_id {
|
||||||
add_event(ev)
|
if ev.known_kind == .text {
|
||||||
|
add_event(ev)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case .notice(let note):
|
case .notice(let note):
|
||||||
|
|||||||
@@ -63,15 +63,7 @@ class NostrEvent: Codable, Identifiable, CustomStringConvertible {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func get_referenced_ids(key: String) -> [ReferencedId] {
|
private func get_referenced_ids(key: String) -> [ReferencedId] {
|
||||||
return tags.reduce(into: []) { (acc, tag) in
|
return damus.get_referenced_ids(tags: self.tags, key: key)
|
||||||
if tag.count >= 2 && tag[0] == key {
|
|
||||||
var relay_id: String? = nil
|
|
||||||
if tag.count >= 3 {
|
|
||||||
relay_id = tag[2]
|
|
||||||
}
|
|
||||||
acc.append(ReferencedId(ref_id: tag[1], relay_id: relay_id, key: key))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func is_root_event() -> Bool {
|
public func is_root_event() -> Bool {
|
||||||
@@ -107,6 +99,23 @@ class NostrEvent: Codable, Identifiable, CustomStringConvertible {
|
|||||||
return first
|
return first
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func last_refid() -> ReferencedId? {
|
||||||
|
var mlast: Int? = nil
|
||||||
|
var i: Int = 0
|
||||||
|
for tag in tags {
|
||||||
|
if tag.count >= 2 && tag[0] == "e" {
|
||||||
|
mlast = i
|
||||||
|
}
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let last = mlast else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return tag_to_refid(tags[last])
|
||||||
|
}
|
||||||
|
|
||||||
public func directly_references(_ id: String) -> Bool {
|
public func directly_references(_ id: String) -> Bool {
|
||||||
// conditions: if it only has 1 e ref
|
// conditions: if it only has 1 e ref
|
||||||
// OR it has more than 1 e ref, ignoring the first
|
// OR it has more than 1 e ref, ignoring the first
|
||||||
@@ -152,14 +161,6 @@ class NostrEvent: Codable, Identifiable, CustomStringConvertible {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
public func reply_ids() -> [ReferencedId] {
|
|
||||||
var ids = self.referenced_ids.first.map { [$0] } ?? []
|
|
||||||
ids.append(ReferencedId(ref_id: self.id, relay_id: nil, key: "e"))
|
|
||||||
ids.append(contentsOf: self.referenced_pubkeys)
|
|
||||||
ids.append(ReferencedId(ref_id: self.pubkey, relay_id: nil, key: "p"))
|
|
||||||
return ids
|
|
||||||
}
|
|
||||||
|
|
||||||
public var referenced_ids: [ReferencedId] {
|
public var referenced_ids: [ReferencedId] {
|
||||||
return get_referenced_ids(key: "e")
|
return get_referenced_ids(key: "e")
|
||||||
}
|
}
|
||||||
@@ -353,3 +354,75 @@ func random_bytes(count: Int) -> Data {
|
|||||||
}
|
}
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func tag_to_refid(_ tag: [String]) -> ReferencedId? {
|
||||||
|
if tag.count == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if tag.count == 1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var relay_id: String? = nil
|
||||||
|
if tag.count > 2 {
|
||||||
|
relay_id = tag[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
return ReferencedId(ref_id: tag[1], relay_id: relay_id, key: tag[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_reply_refs(tags: [[String]]) -> ReplyRefs? {
|
||||||
|
let ids = get_referenced_ids(tags: tags, key: "e")
|
||||||
|
|
||||||
|
if ids.count == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let first = ids.first!
|
||||||
|
let last = ids.last!
|
||||||
|
|
||||||
|
return ReplyRefs(thread_id: first.ref_id, direct_reply: last.ref_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func get_referenced_ids(tags: [[String]], key: String) -> [ReferencedId] {
|
||||||
|
return tags.reduce(into: []) { (acc, tag) in
|
||||||
|
if tag.count >= 2 && tag[0] == key {
|
||||||
|
var relay_id: String? = nil
|
||||||
|
if tag.count >= 3 {
|
||||||
|
relay_id = tag[2]
|
||||||
|
}
|
||||||
|
acc.append(ReferencedId(ref_id: tag[1], relay_id: relay_id, key: key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func make_like_event(pubkey: String, liked: NostrEvent) -> NostrEvent? {
|
||||||
|
var tags: [[String]]
|
||||||
|
|
||||||
|
if let refs = parse_reply_refs(tags: liked.tags) {
|
||||||
|
if refs.thread_id == refs.direct_reply {
|
||||||
|
tags = [["e", refs.thread_id], ["e", liked.id]]
|
||||||
|
} else {
|
||||||
|
tags = [["e", refs.thread_id], ["e", refs.direct_reply], ["e", liked.id]]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// root event
|
||||||
|
tags = [["e", liked.id]]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return NostrEvent(content: "", pubkey: pubkey, kind: 7, tags: tags)
|
||||||
|
}
|
||||||
|
|
||||||
|
func gather_reply_ids(our_pubkey: String, from: NostrEvent) -> [ReferencedId] {
|
||||||
|
var ids = get_referenced_ids(tags: from.tags, key: "e").first.map { [$0] } ?? []
|
||||||
|
|
||||||
|
ids.append(ReferencedId(ref_id: from.id, relay_id: nil, key: "e"))
|
||||||
|
ids.append(contentsOf: from.referenced_pubkeys.filter { $0.ref_id != our_pubkey })
|
||||||
|
if from.pubkey != our_pubkey {
|
||||||
|
ids.append(ReferencedId(ref_id: from.pubkey, relay_id: nil, key: "p"))
|
||||||
|
}
|
||||||
|
return ids
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ struct NostrFilter: Codable {
|
|||||||
public static var filter_contacts: NostrFilter {
|
public static var filter_contacts: NostrFilter {
|
||||||
return filter_kinds([3])
|
return filter_kinds([3])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static func filter_authors(_ authors: [String]) -> NostrFilter {
|
||||||
|
return NostrFilter(ids: nil, kinds: nil, referenced_ids: nil, pubkeys: nil, since: nil, until: nil, authors: authors)
|
||||||
|
}
|
||||||
|
|
||||||
public static func filter_kinds(_ kinds: [Int]) -> NostrFilter {
|
public static func filter_kinds(_ kinds: [Int]) -> NostrFilter {
|
||||||
return NostrFilter(ids: nil, kinds: kinds, referenced_ids: nil, pubkeys: nil, since: nil, until: nil, authors: nil)
|
return NostrFilter(ids: nil, kinds: kinds, referenced_ids: nil, pubkeys: nil, since: nil, until: nil, authors: nil)
|
||||||
|
|||||||
@@ -37,6 +37,12 @@ extension Notification.Name {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Notification.Name {
|
||||||
|
static var liked: Notification.Name {
|
||||||
|
return Notification.Name("liked")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension Notification.Name {
|
extension Notification.Name {
|
||||||
static var click_profile_pic: Notification.Name {
|
static var click_profile_pic: Notification.Name {
|
||||||
return Notification.Name("click_profile_pic")
|
return Notification.Name("click_profile_pic")
|
||||||
|
|||||||
42
damus/Util/Parser.swift
Normal file
42
damus/Util/Parser.swift
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
//
|
||||||
|
// Parser.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by William Casarin on 2022-05-04.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
func parse_str(_ p: Parser, _ s: String) -> Bool {
|
||||||
|
let sub = substring(p.str, start: p.pos, end: p.pos + s.count)
|
||||||
|
if sub == s {
|
||||||
|
p.pos += s.count
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_char(_ p: Parser, _ c: Character) -> Bool{
|
||||||
|
let ind = p.str.index(p.str.startIndex, offsetBy: p.pos)
|
||||||
|
|
||||||
|
if p.str[ind] == c {
|
||||||
|
p.pos += 1
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_digit(_ p: Parser) -> Int? {
|
||||||
|
let ind = p.str.index(p.str.startIndex, offsetBy: p.pos)
|
||||||
|
|
||||||
|
if let c = p.str[ind].unicodeScalars.first {
|
||||||
|
let d = Int(c.value) - 48
|
||||||
|
if d >= 0 && d < 10 {
|
||||||
|
p.pos += 1
|
||||||
|
return Int(d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
@@ -11,7 +11,10 @@ struct ChatView: View {
|
|||||||
let event: NostrEvent
|
let event: NostrEvent
|
||||||
let prev_ev: NostrEvent?
|
let prev_ev: NostrEvent?
|
||||||
let next_ev: NostrEvent?
|
let next_ev: NostrEvent?
|
||||||
|
|
||||||
|
let likes: EventCounter
|
||||||
|
let our_pubkey: String
|
||||||
|
|
||||||
@EnvironmentObject var profiles: Profiles
|
@EnvironmentObject var profiles: Profiles
|
||||||
@EnvironmentObject var thread: ThreadModel
|
@EnvironmentObject var thread: ThreadModel
|
||||||
|
|
||||||
@@ -130,7 +133,7 @@ struct ChatView: View {
|
|||||||
.textSelection(.enabled)
|
.textSelection(.enabled)
|
||||||
|
|
||||||
if is_active || next_ev == nil || next_ev!.pubkey != event.pubkey {
|
if is_active || next_ev == nil || next_ev!.pubkey != event.pubkey {
|
||||||
EventActionBar(event: event)
|
EventActionBar(event: event, our_pubkey: our_pubkey, bar: make_actionbar_model(ev: event, counter: likes))
|
||||||
.environmentObject(profiles)
|
.environmentObject(profiles)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import SwiftUI
|
|||||||
struct ChatroomView: View {
|
struct ChatroomView: View {
|
||||||
@EnvironmentObject var thread: ThreadModel
|
@EnvironmentObject var thread: ThreadModel
|
||||||
@Environment(\.dismiss) var dismiss
|
@Environment(\.dismiss) var dismiss
|
||||||
|
let likes: EventCounter
|
||||||
|
let our_pubkey: String
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ScrollViewReader { scroller in
|
ScrollViewReader { scroller in
|
||||||
@@ -19,7 +21,9 @@ struct ChatroomView: View {
|
|||||||
ForEach(Array(zip(thread.events, thread.events.indices)), id: \.0.id) { (ev, ind) in
|
ForEach(Array(zip(thread.events, thread.events.indices)), id: \.0.id) { (ev, ind) in
|
||||||
ChatView(event: thread.events[ind],
|
ChatView(event: thread.events[ind],
|
||||||
prev_ev: ind > 0 ? thread.events[ind-1] : nil,
|
prev_ev: ind > 0 ? thread.events[ind-1] : nil,
|
||||||
next_ev: ind == count-1 ? nil : thread.events[ind+1]
|
next_ev: ind == count-1 ? nil : thread.events[ind+1],
|
||||||
|
likes: likes,
|
||||||
|
our_pubkey: our_pubkey
|
||||||
)
|
)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
if thread.event.id == ev.id {
|
if thread.event.id == ev.id {
|
||||||
|
|||||||
@@ -19,9 +19,10 @@ enum ActionBarSheet: Identifiable {
|
|||||||
|
|
||||||
struct EventActionBar: View {
|
struct EventActionBar: View {
|
||||||
let event: NostrEvent
|
let event: NostrEvent
|
||||||
|
let our_pubkey: String
|
||||||
@State var sheet: ActionBarSheet? = nil
|
@State var sheet: ActionBarSheet? = nil
|
||||||
@EnvironmentObject var profiles: Profiles
|
@EnvironmentObject var profiles: Profiles
|
||||||
@StateObject var bar: ActionBarModel = ActionBarModel()
|
@StateObject var bar: ActionBarModel
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack {
|
HStack {
|
||||||
@@ -38,25 +39,40 @@ struct EventActionBar: View {
|
|||||||
}
|
}
|
||||||
.padding([.trailing], 40)
|
.padding([.trailing], 40)
|
||||||
|
|
||||||
EventActionButton(img: bar.liked ? "heart.fill" : "heart", col: bar.liked ? Color.red : nil) {
|
HStack(alignment: .bottom) {
|
||||||
if bar.liked {
|
Text("\(bar.likes > 0 ? "\(bar.likes)" : "")")
|
||||||
notify(.delete, bar.our_like_event)
|
.font(.footnote)
|
||||||
} else {
|
.foregroundColor(Color.gray)
|
||||||
notify(.like, event)
|
|
||||||
|
EventActionButton(img: bar.liked ? "heart.fill" : "heart", col: bar.liked ? Color.red : nil) {
|
||||||
|
if bar.liked {
|
||||||
|
notify(.delete, bar.our_like)
|
||||||
|
} else {
|
||||||
|
notify(.like, event)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding([.trailing], 40)
|
.padding([.trailing], 40)
|
||||||
|
|
||||||
EventActionButton(img: "arrow.2.squarepath", col: bar.boosted ? Color.green : nil) {
|
EventActionButton(img: "arrow.2.squarepath", col: bar.boosted ? Color.green : nil) {
|
||||||
if bar.boosted {
|
if bar.boosted {
|
||||||
notify(.delete, bar.our_boost_event)
|
notify(.delete, bar.our_boost)
|
||||||
} else {
|
} else {
|
||||||
notify(.boost, event)
|
notify(.boost, event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
.contentShape(Rectangle())
|
.onReceive(handle_notify(.liked)) { n in
|
||||||
|
let liked = n.object as! Liked
|
||||||
|
if liked.id != event.id {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.bar.likes = liked.total
|
||||||
|
if liked.like.pubkey == our_pubkey {
|
||||||
|
self.bar.our_like = liked.like
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,6 +83,5 @@ func EventActionButton(img: String, col: Color?, action: @escaping () -> ()) ->
|
|||||||
.font(.footnote)
|
.font(.footnote)
|
||||||
.foregroundColor(col == nil ? Color.gray : col!)
|
.foregroundColor(col == nil ? Color.gray : col!)
|
||||||
}
|
}
|
||||||
.contentShape(Rectangle())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ enum CollapsedEvent: Identifiable {
|
|||||||
|
|
||||||
struct EventDetailView: View {
|
struct EventDetailView: View {
|
||||||
let sub_id = UUID().description
|
let sub_id = UUID().description
|
||||||
let pool: RelayPool
|
let damus: DamusState
|
||||||
|
|
||||||
@StateObject var thread: ThreadModel
|
@StateObject var thread: ThreadModel
|
||||||
@State var collapsed: Bool = true
|
@State var collapsed: Bool = true
|
||||||
@@ -70,7 +70,7 @@ struct EventDetailView: View {
|
|||||||
toggle_thread_view()
|
toggle_thread_view()
|
||||||
}
|
}
|
||||||
case .event(let ev, let highlight):
|
case .event(let ev, let highlight):
|
||||||
EventView(event: ev, highlight: highlight, has_action_bar: true, pool: pool)
|
EventView(event: ev, highlight: highlight, has_action_bar: true, damus: damus)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
if thread.event.id == ev.id {
|
if thread.event.id == ev.id {
|
||||||
toggle_thread_view()
|
toggle_thread_view()
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ struct EventView: View {
|
|||||||
let event: NostrEvent
|
let event: NostrEvent
|
||||||
let highlight: Highlight
|
let highlight: Highlight
|
||||||
let has_action_bar: Bool
|
let has_action_bar: Bool
|
||||||
let pool: RelayPool
|
let damus: DamusState
|
||||||
|
|
||||||
@EnvironmentObject var profiles: Profiles
|
@EnvironmentObject var profiles: Profiles
|
||||||
@EnvironmentObject var action_bar: ActionBarModel
|
@EnvironmentObject var action_bar: ActionBarModel
|
||||||
@@ -49,7 +49,7 @@ struct EventView: View {
|
|||||||
let profile = profiles.lookup(id: event.pubkey)
|
let profile = profiles.lookup(id: event.pubkey)
|
||||||
HStack {
|
HStack {
|
||||||
VStack {
|
VStack {
|
||||||
let pv = ProfileView(pool: pool, profile: ProfileModel(pubkey: event.pubkey, pool: pool))
|
let pv = ProfileView(damus: damus, profile: ProfileModel(pubkey: event.pubkey, damus: damus))
|
||||||
.environmentObject(profiles)
|
.environmentObject(profiles)
|
||||||
|
|
||||||
NavigationLink(destination: pv) {
|
NavigationLink(destination: pv) {
|
||||||
@@ -81,7 +81,7 @@ struct EventView: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
if has_action_bar {
|
if has_action_bar {
|
||||||
EventActionBar(event: event)
|
EventActionBar(event: event, our_pubkey: damus.pubkey, bar: make_actionbar_model(ev: event, counter: damus.likes))
|
||||||
.environmentObject(profiles)
|
.environmentObject(profiles)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,3 +152,11 @@ func reply_others_desc(n: Int, n_pubkeys: Int) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
func make_actionbar_model(ev: NostrEvent, counter: EventCounter) -> ActionBarModel {
|
||||||
|
let likes = counter.counts[ev.id]
|
||||||
|
let our_like = counter.our_events[ev.id]
|
||||||
|
let our_boost: NostrEvent? = nil
|
||||||
|
|
||||||
|
return ActionBarModel(likes: likes ?? 0, our_like: our_like, our_boost: our_boost)
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,14 +24,6 @@ struct NostrPost {
|
|||||||
tag.append(relay_id)
|
tag.append(relay_id)
|
||||||
}
|
}
|
||||||
new_ev.tags.append(tag)
|
new_ev.tags.append(tag)
|
||||||
// filter our pubkeys
|
|
||||||
new_ev.tags = new_ev.tags.filter {
|
|
||||||
if $0[0] == "p" {
|
|
||||||
return $0[1] != pubkey
|
|
||||||
} else {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
new_ev.calculate_id()
|
new_ev.calculate_id()
|
||||||
new_ev.sign(privkey: privkey)
|
new_ev.sign(privkey: privkey)
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ enum ProfileTab: Hashable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct ProfileView: View {
|
struct ProfileView: View {
|
||||||
let pool: RelayPool
|
let damus: DamusState
|
||||||
|
|
||||||
@State private var selected_tab: ProfileTab = .posts
|
@State private var selected_tab: ProfileTab = .posts
|
||||||
@StateObject var profile: ProfileModel
|
@StateObject var profile: ProfileModel
|
||||||
|
|
||||||
@@ -54,7 +55,7 @@ struct ProfileView: View {
|
|||||||
Group {
|
Group {
|
||||||
switch(selected_tab) {
|
switch(selected_tab) {
|
||||||
case .posts:
|
case .posts:
|
||||||
TimelineView(events: $profile.events, pool: pool)
|
TimelineView(events: $profile.events, damus: damus)
|
||||||
.environmentObject(profiles)
|
.environmentObject(profiles)
|
||||||
case .following:
|
case .following:
|
||||||
Text("Following")
|
Text("Following")
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ func all_referenced_pubkeys(_ ev: NostrEvent) -> [ReferencedId] {
|
|||||||
|
|
||||||
struct ReplyView: View {
|
struct ReplyView: View {
|
||||||
let replying_to: NostrEvent
|
let replying_to: NostrEvent
|
||||||
let pool: RelayPool
|
let damus: DamusState
|
||||||
|
|
||||||
@EnvironmentObject var profiles: Profiles
|
@EnvironmentObject var profiles: Profiles
|
||||||
|
|
||||||
@@ -35,8 +35,8 @@ struct ReplyView: View {
|
|||||||
.foregroundColor(.gray)
|
.foregroundColor(.gray)
|
||||||
.font(.footnote)
|
.font(.footnote)
|
||||||
}
|
}
|
||||||
EventView(event: replying_to, highlight: .none, has_action_bar: false, pool: pool)
|
EventView(event: replying_to, highlight: .none, has_action_bar: false, damus: damus)
|
||||||
PostView(references: replying_to.reply_ids())
|
PostView(references: gather_reply_ids(our_pubkey: damus.pubkey, from: replying_to))
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import SwiftUI
|
|||||||
struct ThreadView: View {
|
struct ThreadView: View {
|
||||||
@State var is_chatroom: Bool = false
|
@State var is_chatroom: Bool = false
|
||||||
@StateObject var thread: ThreadModel
|
@StateObject var thread: ThreadModel
|
||||||
let pool: RelayPool
|
let damus: DamusState
|
||||||
|
|
||||||
@EnvironmentObject var profiles: Profiles
|
@EnvironmentObject var profiles: Profiles
|
||||||
@Environment(\.dismiss) var dismiss
|
@Environment(\.dismiss) var dismiss
|
||||||
@@ -19,12 +19,12 @@ struct ThreadView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
Group {
|
Group {
|
||||||
if is_chatroom {
|
if is_chatroom {
|
||||||
ChatroomView()
|
ChatroomView(likes: damus.likes, our_pubkey: damus.pubkey)
|
||||||
.navigationBarTitle("Chat")
|
.navigationBarTitle("Chat")
|
||||||
.environmentObject(profiles)
|
.environmentObject(profiles)
|
||||||
.environmentObject(thread)
|
.environmentObject(thread)
|
||||||
} else {
|
} else {
|
||||||
EventDetailView(pool: pool, thread: thread)
|
EventDetailView(damus: damus, thread: thread)
|
||||||
.navigationBarTitle("Thread")
|
.navigationBarTitle("Thread")
|
||||||
.environmentObject(profiles)
|
.environmentObject(profiles)
|
||||||
.environmentObject(thread)
|
.environmentObject(thread)
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ struct TimelineView: View {
|
|||||||
|
|
||||||
@EnvironmentObject var profiles: Profiles
|
@EnvironmentObject var profiles: Profiles
|
||||||
|
|
||||||
let pool: RelayPool
|
let damus: DamusState
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
MainContent
|
MainContent
|
||||||
@@ -37,11 +37,11 @@ struct TimelineView: View {
|
|||||||
.environmentObject(profiles)
|
.environmentObject(profiles)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let tv = ThreadView(thread: ThreadModel(ev: ev, pool: pool), pool: pool)
|
let tv = ThreadView(thread: ThreadModel(ev: ev, pool: damus.pool), damus: damus)
|
||||||
.environmentObject(profiles)
|
.environmentObject(profiles)
|
||||||
|
|
||||||
NavigationLink(destination: tv) {
|
NavigationLink(destination: tv) {
|
||||||
EventView(event: ev, highlight: .none, has_action_bar: true, pool: pool)
|
EventView(event: ev, highlight: .none, has_action_bar: true, damus: damus)
|
||||||
}
|
}
|
||||||
.isDetailLink(true)
|
.isDetailLink(true)
|
||||||
.buttonStyle(PlainButtonStyle())
|
.buttonStyle(PlainButtonStyle())
|
||||||
|
|||||||
@@ -32,5 +32,15 @@ class damusTests: XCTestCase {
|
|||||||
// Put the code you want to measure the time of here.
|
// Put the code you want to measure the time of here.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testParseMention() throws {
|
||||||
|
let parsed = parse_mentions(content: "this is #[0] a mention", tags: [["e", "event_id"]])
|
||||||
|
|
||||||
|
XCTAssertNotNil(parsed)
|
||||||
|
XCTAssertEqual(parsed.count, 3)
|
||||||
|
XCTAssertTrue(parsed[0].is_text)
|
||||||
|
XCTAssertTrue(parsed[1].is_mention)
|
||||||
|
XCTAssertTrue(parsed[2].is_text)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user