Relay Filters

wip
This commit is contained in:
William Casarin
2023-02-08 11:07:58 -08:00
parent 989684cd37
commit 10596ddb09
13 changed files with 237 additions and 376 deletions

View File

@@ -31,7 +31,6 @@
4C0A3F8F280F640A000448DE /* ThreadModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F8E280F640A000448DE /* ThreadModel.swift */; }; 4C0A3F8F280F640A000448DE /* ThreadModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F8E280F640A000448DE /* ThreadModel.swift */; };
4C0A3F91280F6528000448DE /* ChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F90280F6528000448DE /* ChatView.swift */; }; 4C0A3F91280F6528000448DE /* ChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F90280F6528000448DE /* ChatView.swift */; };
4C0A3F93280F66F5000448DE /* ReplyMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F92280F66F5000448DE /* ReplyMap.swift */; }; 4C0A3F93280F66F5000448DE /* ReplyMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F92280F66F5000448DE /* ReplyMap.swift */; };
4C0A3F97280F8E02000448DE /* ThreadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0A3F96280F8E02000448DE /* ThreadView.swift */; };
4C216F32286E388800040376 /* DMChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C216F31286E388800040376 /* DMChatView.swift */; }; 4C216F32286E388800040376 /* DMChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C216F31286E388800040376 /* DMChatView.swift */; };
4C216F34286F5ACD00040376 /* DMView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C216F33286F5ACD00040376 /* DMView.swift */; }; 4C216F34286F5ACD00040376 /* DMView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C216F33286F5ACD00040376 /* DMView.swift */; };
4C216F362870A9A700040376 /* InputDismissKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C216F352870A9A700040376 /* InputDismissKeyboard.swift */; }; 4C216F362870A9A700040376 /* InputDismissKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C216F352870A9A700040376 /* InputDismissKeyboard.swift */; };
@@ -168,6 +167,7 @@
4CE6DF0427F7A08200C66700 /* damusUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DF0327F7A08200C66700 /* damusUITestsLaunchTests.swift */; }; 4CE6DF0427F7A08200C66700 /* damusUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DF0327F7A08200C66700 /* damusUITestsLaunchTests.swift */; };
4CE6DF1227F7A2B300C66700 /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = 4CE6DF1127F7A2B300C66700 /* Starscream */; }; 4CE6DF1227F7A2B300C66700 /* Starscream in Frameworks */ = {isa = PBXBuildFile; productRef = 4CE6DF1127F7A2B300C66700 /* Starscream */; };
4CE6DF1627F8DEBF00C66700 /* RelayConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DF1527F8DEBF00C66700 /* RelayConnection.swift */; }; 4CE6DF1627F8DEBF00C66700 /* RelayConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE6DF1527F8DEBF00C66700 /* RelayConnection.swift */; };
4CE8794829941DA700F758CC /* RelayFilters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CE8794729941DA700F758CC /* RelayFilters.swift */; };
4CEE2AED2805B22500AB5EEF /* NostrRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AEC2805B22500AB5EEF /* NostrRequest.swift */; }; 4CEE2AED2805B22500AB5EEF /* NostrRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AEC2805B22500AB5EEF /* NostrRequest.swift */; };
4CEE2AF1280B216B00AB5EEF /* EventDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AF0280B216B00AB5EEF /* EventDetailView.swift */; }; 4CEE2AF1280B216B00AB5EEF /* EventDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AF0280B216B00AB5EEF /* EventDetailView.swift */; };
4CEE2AF3280B25C500AB5EEF /* ProfilePicView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AF2280B25C500AB5EEF /* ProfilePicView.swift */; }; 4CEE2AF3280B25C500AB5EEF /* ProfilePicView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE2AF2280B25C500AB5EEF /* ProfilePicView.swift */; };
@@ -193,6 +193,7 @@
5C513FBA297F72980072348F /* CustomPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C513FB9297F72980072348F /* CustomPicker.swift */; }; 5C513FBA297F72980072348F /* CustomPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C513FB9297F72980072348F /* CustomPicker.swift */; };
5C513FCC2984ACA60072348F /* QRCodeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C513FCB2984ACA60072348F /* QRCodeView.swift */; }; 5C513FCC2984ACA60072348F /* QRCodeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C513FCB2984ACA60072348F /* QRCodeView.swift */; };
6439E014296790CF0020672B /* ProfileZoomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6439E013296790CF0020672B /* ProfileZoomView.swift */; }; 6439E014296790CF0020672B /* ProfileZoomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6439E013296790CF0020672B /* ProfileZoomView.swift */; };
643EA5C8296B764E005081BB /* RelayFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 643EA5C7296B764E005081BB /* RelayFilterView.swift */; };
647D9A8D2968520300A295DE /* SideMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 647D9A8C2968520300A295DE /* SideMenuView.swift */; }; 647D9A8D2968520300A295DE /* SideMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 647D9A8C2968520300A295DE /* SideMenuView.swift */; };
64FBD06F296255C400D9D3B2 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64FBD06E296255C400D9D3B2 /* Theme.swift */; }; 64FBD06F296255C400D9D3B2 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64FBD06E296255C400D9D3B2 /* Theme.swift */; };
6C7DE41F2955169800E66263 /* Vault in Frameworks */ = {isa = PBXBuildFile; productRef = 6C7DE41E2955169800E66263 /* Vault */; }; 6C7DE41F2955169800E66263 /* Vault in Frameworks */ = {isa = PBXBuildFile; productRef = 6C7DE41E2955169800E66263 /* Vault */; };
@@ -289,7 +290,6 @@
4C0A3F8E280F640A000448DE /* ThreadModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadModel.swift; sourceTree = "<group>"; }; 4C0A3F8E280F640A000448DE /* ThreadModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadModel.swift; sourceTree = "<group>"; };
4C0A3F90280F6528000448DE /* ChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatView.swift; sourceTree = "<group>"; }; 4C0A3F90280F6528000448DE /* ChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatView.swift; sourceTree = "<group>"; };
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>"; };
4C0A3F96280F8E02000448DE /* ThreadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadView.swift; sourceTree = "<group>"; };
4C216F31286E388800040376 /* DMChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DMChatView.swift; sourceTree = "<group>"; }; 4C216F31286E388800040376 /* DMChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DMChatView.swift; sourceTree = "<group>"; };
4C216F33286F5ACD00040376 /* DMView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DMView.swift; sourceTree = "<group>"; }; 4C216F33286F5ACD00040376 /* DMView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DMView.swift; sourceTree = "<group>"; };
4C216F352870A9A700040376 /* InputDismissKeyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputDismissKeyboard.swift; sourceTree = "<group>"; }; 4C216F352870A9A700040376 /* InputDismissKeyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputDismissKeyboard.swift; sourceTree = "<group>"; };
@@ -458,6 +458,7 @@
4CE6DF0127F7A08200C66700 /* damusUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = damusUITests.swift; sourceTree = "<group>"; }; 4CE6DF0127F7A08200C66700 /* damusUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = damusUITests.swift; sourceTree = "<group>"; };
4CE6DF0327F7A08200C66700 /* damusUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = damusUITestsLaunchTests.swift; sourceTree = "<group>"; }; 4CE6DF0327F7A08200C66700 /* damusUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = damusUITestsLaunchTests.swift; sourceTree = "<group>"; };
4CE6DF1527F8DEBF00C66700 /* RelayConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayConnection.swift; sourceTree = "<group>"; }; 4CE6DF1527F8DEBF00C66700 /* RelayConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayConnection.swift; sourceTree = "<group>"; };
4CE8794729941DA700F758CC /* RelayFilters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayFilters.swift; sourceTree = "<group>"; };
4CEE2AE72804F57C00AB5EEF /* libsecp256k1.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libsecp256k1.a; sourceTree = "<group>"; }; 4CEE2AE72804F57C00AB5EEF /* libsecp256k1.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libsecp256k1.a; sourceTree = "<group>"; };
4CEE2AEC2805B22500AB5EEF /* NostrRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NostrRequest.swift; sourceTree = "<group>"; }; 4CEE2AEC2805B22500AB5EEF /* NostrRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NostrRequest.swift; sourceTree = "<group>"; };
4CEE2AF0280B216B00AB5EEF /* EventDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventDetailView.swift; sourceTree = "<group>"; }; 4CEE2AF0280B216B00AB5EEF /* EventDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventDetailView.swift; sourceTree = "<group>"; };
@@ -484,6 +485,7 @@
5C513FB9297F72980072348F /* CustomPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomPicker.swift; sourceTree = "<group>"; }; 5C513FB9297F72980072348F /* CustomPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomPicker.swift; sourceTree = "<group>"; };
5C513FCB2984ACA60072348F /* QRCodeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeView.swift; sourceTree = "<group>"; }; 5C513FCB2984ACA60072348F /* QRCodeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeView.swift; sourceTree = "<group>"; };
6439E013296790CF0020672B /* ProfileZoomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileZoomView.swift; sourceTree = "<group>"; }; 6439E013296790CF0020672B /* ProfileZoomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileZoomView.swift; sourceTree = "<group>"; };
643EA5C7296B764E005081BB /* RelayFilterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayFilterView.swift; sourceTree = "<group>"; };
647D9A8C2968520300A295DE /* SideMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideMenuView.swift; sourceTree = "<group>"; }; 647D9A8C2968520300A295DE /* SideMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideMenuView.swift; sourceTree = "<group>"; };
64FBD06E296255C400D9D3B2 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = "<group>"; }; 64FBD06E296255C400D9D3B2 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = "<group>"; };
7C45AE70297353390031D7BC /* KFImageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KFImageModel.swift; sourceTree = "<group>"; }; 7C45AE70297353390031D7BC /* KFImageModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KFImageModel.swift; sourceTree = "<group>"; };
@@ -692,7 +694,6 @@
BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */, BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */,
4C3AC7A02835A81400E1F516 /* SetupView.swift */, 4C3AC7A02835A81400E1F516 /* SetupView.swift */,
E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */, E9E4ED0A295867B900DD7078 /* ThreadV2View.swift */,
4C0A3F96280F8E02000448DE /* ThreadView.swift */,
4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */, 4CA2EF9F280E37AC0044ACD8 /* TimelineView.swift */,
4CB55EF4295E679D007FD187 /* UserRelaysView.swift */, 4CB55EF4295E679D007FD187 /* UserRelaysView.swift */,
647D9A8C2968520300A295DE /* SideMenuView.swift */, 647D9A8C2968520300A295DE /* SideMenuView.swift */,
@@ -703,6 +704,7 @@
4CF0ABE42981EE0C00D66079 /* EULAView.swift */, 4CF0ABE42981EE0C00D66079 /* EULAView.swift */,
3AA247FE297E3D900090C62D /* RepostsView.swift */, 3AA247FE297E3D900090C62D /* RepostsView.swift */,
5C513FCB2984ACA60072348F /* QRCodeView.swift */, 5C513FCB2984ACA60072348F /* QRCodeView.swift */,
643EA5C7296B764E005081BB /* RelayFilterView.swift */,
); );
path = Views; path = Views;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -755,6 +757,7 @@
4CB883A72975FC1800DC99E7 /* Zaps.swift */, 4CB883A72975FC1800DC99E7 /* Zaps.swift */,
4CB883B5297730E400DC99E7 /* LNUrls.swift */, 4CB883B5297730E400DC99E7 /* LNUrls.swift */,
3AB72AB8298ECF30004BB58C /* Translator.swift */, 3AB72AB8298ECF30004BB58C /* Translator.swift */,
4CE8794729941DA700F758CC /* RelayFilters.swift */,
); );
path = Util; path = Util;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -1187,6 +1190,7 @@
F7908E97298B1FDF00AB113A /* NIPURLBuilder.swift in Sources */, F7908E97298B1FDF00AB113A /* NIPURLBuilder.swift in Sources */,
4C285C8228385570008A31F1 /* CarouselView.swift in Sources */, 4C285C8228385570008A31F1 /* CarouselView.swift in Sources */,
4C3EA67F28FFC01D00C48A62 /* InvoiceView.swift in Sources */, 4C3EA67F28FFC01D00C48A62 /* InvoiceView.swift in Sources */,
4CE8794829941DA700F758CC /* RelayFilters.swift in Sources */,
4CEE2B02280B39E800AB5EEF /* EventActionBar.swift in Sources */, 4CEE2B02280B39E800AB5EEF /* EventActionBar.swift in Sources */,
4C3BEFE0281DE1ED00B3DE84 /* DamusState.swift in Sources */, 4C3BEFE0281DE1ED00B3DE84 /* DamusState.swift in Sources */,
7C45AE71297353390031D7BC /* KFImageModel.swift in Sources */, 7C45AE71297353390031D7BC /* KFImageModel.swift in Sources */,
@@ -1269,6 +1273,7 @@
4CB55EF3295E5D59007FD187 /* RecommendedRelayView.swift in Sources */, 4CB55EF3295E5D59007FD187 /* RecommendedRelayView.swift in Sources */,
4CF0ABEC29844B4700D66079 /* AnyDecodable.swift in Sources */, 4CF0ABEC29844B4700D66079 /* AnyDecodable.swift in Sources */,
4C5F9118283D88E40052CD1C /* FollowingModel.swift in Sources */, 4C5F9118283D88E40052CD1C /* FollowingModel.swift in Sources */,
643EA5C8296B764E005081BB /* RelayFilterView.swift in Sources */,
4C3EA67D28FFBBA300C48A62 /* InvoicesView.swift in Sources */, 4C3EA67D28FFBBA300C48A62 /* InvoicesView.swift in Sources */,
4C363A8E28236FE4006E126D /* NoteContentView.swift in Sources */, 4C363A8E28236FE4006E126D /* NoteContentView.swift in Sources */,
4C90BD1A283AA67F008EE7EF /* Bech32.swift in Sources */, 4C90BD1A283AA67F008EE7EF /* Bech32.swift in Sources */,
@@ -1278,7 +1283,6 @@
F7908E92298B0F0700AB113A /* RelayDetailView.swift in Sources */, F7908E92298B0F0700AB113A /* RelayDetailView.swift in Sources */,
4C3A1D332960DB0500558C0F /* Markdown.swift in Sources */, 4C3A1D332960DB0500558C0F /* Markdown.swift in Sources */,
4CF0ABD42980996B00D66079 /* Report.swift in Sources */, 4CF0ABD42980996B00D66079 /* Report.swift in Sources */,
4C0A3F97280F8E02000448DE /* ThreadView.swift in Sources */,
4C06670B28FDE64700038D2A /* damus.c in Sources */, 4C06670B28FDE64700038D2A /* damus.c in Sources */,
3AAA95CC298E07E900F3D526 /* DeepLPlan.swift in Sources */, 3AAA95CC298E07E900F3D526 /* DeepLPlan.swift in Sources */,
4FE60CDD295E1C5E00105A1F /* Wallet.swift in Sources */, 4FE60CDD295E1C5E00105A1F /* Wallet.swift in Sources */,

View File

@@ -27,12 +27,16 @@ enum Sheets: Identifiable {
case post case post
case report(ReportTarget) case report(ReportTarget)
case reply(NostrEvent) case reply(NostrEvent)
case event(NostrEvent)
case filter
var id: String { var id: String {
switch self { switch self {
case .report: return "report" case .report: return "report"
case .post: return "post" case .post: return "post"
case .reply(let ev): return "reply-" + ev.id case .reply(let ev): return "reply-" + ev.id
case .event(let ev): return "event-" + ev.id
case .filter: return "filter"
} }
} }
} }
@@ -282,7 +286,16 @@ struct ContentView: View {
.foregroundColor(.gray) .foregroundColor(.gray)
} }
} }
Button(action: {
//isFilterVisible.toggle()
self.active_sheet = .filter
}) {
// checklist, checklist.checked, lisdt.bullet, list.bullet.circle, line.3.horizontal.decrease..., line.3.horizontail.decrease
Label("Filter", systemImage: "line.3.horizontal.decrease")
.foregroundColor(.gray)
//.contentShape(Rectangle())
}
} }
} }
} }
@@ -311,6 +324,17 @@ struct ContentView: View {
PostView(replying_to: nil, references: [], damus_state: damus_state!) PostView(replying_to: nil, references: [], damus_state: damus_state!)
case .reply(let event): case .reply(let event):
ReplyView(replying_to: event, damus: damus_state!) ReplyView(replying_to: event, damus: damus_state!)
case .event(let event):
EventDetailView()
case .filter:
let timeline = selected_timeline ?? .home
if #available(iOS 16.0, *) {
RelayFilterView(state: damus_state!, timeline: timeline)
.presentationDetents([.height(550)])
.presentationDragIndicator(.visible)
} else {
RelayFilterView(state: damus_state!, timeline: timeline)
}
} }
} }
.onOpenURL { url in .onOpenURL { url in
@@ -429,6 +453,8 @@ struct ContentView: View {
let post_res = obj.object as! NostrPostResult let post_res = obj.object as! NostrPostResult
switch post_res { switch post_res {
case .post(let post): case .post(let post):
//let post = tup.0
//let to_relays = tup.1
print("post \(post.content)") print("post \(post.content)")
let new_ev = post_to_event(post: post, privkey: privkey, pubkey: pubkey) let new_ev = post_to_event(post: post, privkey: privkey, pubkey: pubkey)
self.damus_state?.pool.send(.event(new_ev)) self.damus_state?.pool.send(.event(new_ev))
@@ -576,7 +602,8 @@ struct ContentView: View {
previews: PreviewCache(), previews: PreviewCache(),
zaps: Zaps(our_pubkey: pubkey), zaps: Zaps(our_pubkey: pubkey),
lnurls: LNUrls(), lnurls: LNUrls(),
settings: UserSettingsStore() settings: UserSettingsStore(),
relay_filters: RelayFilters(our_pubkey: pubkey)
) )
home.damus_state = self.damus_state! home.damus_state = self.damus_state!

View File

@@ -21,6 +21,7 @@ struct DamusState {
let zaps: Zaps let zaps: Zaps
let lnurls: LNUrls let lnurls: LNUrls
let settings: UserSettingsStore let settings: UserSettingsStore
let relay_filters: RelayFilters
var pubkey: String { var pubkey: String {
return keypair.pubkey return keypair.pubkey
@@ -32,6 +33,6 @@ struct DamusState {
static var empty: DamusState { static var empty: DamusState {
return DamusState.init(pool: RelayPool(), keypair: Keypair(pubkey: "", privkey: ""), likes: EventCounter(our_pubkey: ""), boosts: EventCounter(our_pubkey: ""), contacts: Contacts(our_pubkey: ""), tips: TipCounter(our_pubkey: ""), profiles: Profiles(), dms: DirectMessagesModel(our_pubkey: ""), previews: PreviewCache(), zaps: Zaps(our_pubkey: ""), lnurls: LNUrls(), settings: UserSettingsStore()) return DamusState.init(pool: RelayPool(), keypair: Keypair(pubkey: "", privkey: ""), likes: EventCounter(our_pubkey: ""), boosts: EventCounter(our_pubkey: ""), contacts: Contacts(our_pubkey: ""), tips: TipCounter(our_pubkey: ""), profiles: Profiles(), dms: DirectMessagesModel(our_pubkey: ""), previews: PreviewCache(), zaps: Zaps(our_pubkey: ""), lnurls: LNUrls(), settings: UserSettingsStore(), relay_filters: RelayFilters(our_pubkey: ""))
} }
} }

View File

@@ -36,7 +36,8 @@ class SearchHomeModel: ObservableObject {
func subscribe() { func subscribe() {
loading = true loading = true
damus_state.pool.subscribe(sub_id: base_subid, filters: [get_base_filter()], handler: handle_event) let to_relays = determine_to_relays(pool: damus_state.pool, filters: damus_state.relay_filters)
damus_state.pool.subscribe(sub_id: base_subid, filters: [get_base_filter()], handler: handle_event, to: to_relays)
} }
func unsubscribe(to: String? = nil) { func unsubscribe(to: String? = nil) {

View File

@@ -98,6 +98,21 @@ func event_matches_hashtag(_ ev: NostrEvent, hashtags: [String]) -> Bool {
return false return false
} }
func tag_is_hashtag(_ tag: [String]) -> Bool {
// "hashtag" is deprecated, will remove in the future
return tag.count >= 2 && (tag[0] == "hashtag" || tag[0] == "t")
}
func has_hashtag(_ tags: [[String]], hashtag: String) -> Bool {
for tag in tags {
if tag_is_hashtag(tag) && tag[1] == hashtag {
return true
}
}
return false
}
func event_matches_filter(_ ev: NostrEvent, filter: NostrFilter) -> Bool { func event_matches_filter(_ ev: NostrEvent, filter: NostrFilter) -> Bool {
if let hashtags = filter.hashtag { if let hashtags = filter.hashtag {
return event_matches_hashtag(ev, hashtags: hashtags) return event_matches_hashtag(ev, hashtags: hashtags)

View File

@@ -42,6 +42,8 @@ class RelayPool {
var relays: [Relay] = [] var relays: [Relay] = []
var handlers: [RelayHandler] = [] var handlers: [RelayHandler] = []
var request_queue: [QueuedRequest] = [] var request_queue: [QueuedRequest] = []
var seen: Set<String> = Set()
var counts: [String: UInt64] = [:]
var descriptors: [RelayDescriptor] { var descriptors: [RelayDescriptor] {
relays.map { $0.descriptor } relays.map { $0.descriptor }
@@ -149,9 +151,9 @@ class RelayPool {
self.send(.unsubscribe(sub_id), to: to) self.send(.unsubscribe(sub_id), to: to)
} }
func subscribe(sub_id: String, filters: [NostrFilter], handler: @escaping (String, NostrConnectionEvent) -> ()) { func subscribe(sub_id: String, filters: [NostrFilter], handler: @escaping (String, NostrConnectionEvent) -> (), to: [String]? = nil) {
register_handler(sub_id: sub_id, handler: handler) register_handler(sub_id: sub_id, handler: handler)
send(.subscribe(.init(filters: filters, sub_id: sub_id))) send(.subscribe(.init(filters: filters, sub_id: sub_id)), to: to)
} }
func subscribe_to(sub_id: String, filters: [NostrFilter], to: [String]?, handler: @escaping (String, NostrConnectionEvent) -> ()) { func subscribe_to(sub_id: String, filters: [NostrFilter], to: [String]?, handler: @escaping (String, NostrConnectionEvent) -> ()) {
@@ -241,8 +243,25 @@ class RelayPool {
} }
} }
func record_seen(relay_id: String, event: NostrConnectionEvent) {
if case .nostr_event(let ev) = event {
if case .event(_, let nev) = ev {
let k = relay_id + nev.id
if !seen.contains(k) {
seen.insert(k)
if counts[relay_id] == nil {
counts[relay_id] = 1
} else {
counts[relay_id] = (counts[relay_id] ?? 0) + 1
}
}
}
}
}
func handle_event(relay_id: String, event: NostrConnectionEvent) { func handle_event(relay_id: String, event: NostrConnectionEvent) {
record_last_pong(relay_id: relay_id, event: event) record_last_pong(relay_id: relay_id, event: event)
record_seen(relay_id: relay_id, event: event)
// run req queue when we reconnect // run req queue when we reconnect
if case .ws_event(let ws) = event { if case .ws_event(let ws) = event {

View File

@@ -0,0 +1,80 @@
//
// RelayFilters.swift
// damus
//
// Created by William Casarin on 2023-02-08.
//
import Foundation
struct RelayFilter: Hashable {
let timeline: Timeline
let relay_id: String
}
class RelayFilters {
private let our_pubkey: String
private var disabled: Set<RelayFilter>
func is_filtered(timeline: Timeline, relay_id: String) -> Bool {
let filter = RelayFilter(timeline: timeline, relay_id: relay_id)
let contains = disabled.contains(filter)
return contains
}
func remove(timeline: Timeline, relay_id: String) {
let filter = RelayFilter(timeline: timeline, relay_id: relay_id)
if !disabled.contains(filter) {
return
}
disabled.remove(filter)
save_relay_filters(our_pubkey, filters: disabled)
}
func insert(timeline: Timeline, relay_id: String) {
let filter = RelayFilter(timeline: timeline, relay_id: relay_id)
if disabled.contains(filter) {
return
}
disabled.insert(filter)
save_relay_filters(our_pubkey, filters: disabled)
}
init(our_pubkey: String) {
self.our_pubkey = our_pubkey
disabled = load_relay_filters(our_pubkey)
}
}
func save_relay_filters(_ pubkey: String, filters: Set<RelayFilter>) {
let key = pk_setting_key(pubkey, key: "relay_filters")
let arr = Array(filters.map { filter in "\(filter.timeline)\t\(filter.relay_id)" })
UserDefaults.standard.set(arr, forKey: key)
}
func load_relay_filters(_ pubkey: String) -> Set<RelayFilter> {
let key = pk_setting_key(pubkey, key: "relay_filters")
guard let filters = UserDefaults.standard.stringArray(forKey: key) else {
return Set()
}
return filters.reduce(into: Set()) { s, str in
let parts = str.components(separatedBy: "\t")
guard parts.count == 2 else {
return
}
guard let timeline = Timeline.init(rawValue: parts[0]) else {
return
}
let filter = RelayFilter(timeline: timeline, relay_id: parts[1])
s.insert(filter)
}
}
func determine_to_relays(pool: RelayPool, filters: RelayFilters) -> [String] {
return pool.descriptors
.map { $0.url.absoluteString }
.filter { !filters.is_filtered(timeline: .search, relay_id: $0) }
}

View File

@@ -7,119 +7,10 @@
import SwiftUI import SwiftUI
struct CollapsedEvents: Identifiable {
let count: Int
let start: Int
let end: Int
var id: String = UUID().description
}
enum CollapsedEvent: Identifiable {
case event(NostrEvent, Highlight)
case collapsed(CollapsedEvents)
var id: String {
switch self {
case .event(let ev, _):
return ev.id
case .collapsed(let c):
return c.id
}
}
}
struct EventDetailView: View { struct EventDetailView: View {
let sub_id = UUID().description
let damus: DamusState
@StateObject var thread: ThreadModel
@State var collapsed: Bool = true
func toggle_collapse_thread(scroller: ScrollViewProxy, id mid: String?, animate: Bool = true) {
self.collapsed = !self.collapsed
if let id = mid {
if !self.collapsed {
scroll_to_event(scroller: scroller, id: id, delay: 0.1, animate: animate)
}
}
}
func uncollapse_section(scroller: ScrollViewProxy, c: CollapsedEvents)
{
let ev = thread.events[c.start]
print("uncollapsing section at \(c.start) '\(ev.content.prefix(12))...'")
let start_id = ev.id
toggle_collapse_thread(scroller: scroller, id: start_id, animate: false)
}
func CollapsedEventView(_ cev: CollapsedEvent, scroller: ScrollViewProxy) -> some View {
Group {
switch cev {
case .collapsed(let c):
Text(String(format: NSLocalizedString("collapsed_event_view_other_notes", comment: "Text to indicate that the thread was collapsed and that there are other notes to view if tapped."), c.count))
.padding([.top,.bottom], 8)
.font(.footnote)
.foregroundColor(.gray)
.onTapGesture {
//self.uncollapse_section(scroller: proxy, c: c)
//self.toggle_collapse_thread(scroller: proxy, id: nil)
if let ev = thread.events[safe: c.start] {
thread.set_active_event(ev, privkey: damus.keypair.privkey)
}
toggle_thread_view()
}
case .event(let ev, _):
EventView(damus: damus, event: ev, has_action_bar: true)
.onTapGesture {
if thread.initial_event.id == ev.id {
toggle_thread_view()
} else {
thread.set_active_event(ev, privkey: damus.keypair.privkey)
}
}
}
}
}
var body: some View { var body: some View {
ScrollViewReader { proxy in Text("EventDetailView")
if thread.loading {
ProgressView().progressViewStyle(.circular)
}
ScrollView(.vertical) {
LazyVStack {
let collapsed_events = calculated_collapsed_events(
privkey: damus.keypair.privkey,
collapsed: self.collapsed,
active: thread.event,
events: thread.events
)
ForEach(collapsed_events, id: \.id) { cev in
CollapsedEventView(cev, scroller: proxy)
}
}
.padding(.horizontal)
.padding(.top)
EndBlock()
}
.onChange(of: thread.loading) { val in
scroll_after_load(thread: thread, proxy: proxy)
}
.onAppear() {
scroll_after_load(thread: thread, proxy: proxy)
}
}
.navigationBarTitle(NSLocalizedString("Thread", comment: "Navigation bar title for note thread."))
}
func toggle_thread_view() {
NotificationCenter.default.post(name: .toggle_thread_view, object: nil)
} }
} }
@@ -130,167 +21,13 @@ func scroll_after_load(thread: ThreadModel, proxy: ScrollViewProxy) {
} }
} }
struct EventDetailView_Previews: PreviewProvider { struct EventDetailView_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
let state = test_damus_state() let state = test_damus_state()
let tm = ThreadModel(evid: "4da698ceac09a16cdb439276fa3d13ef8f6620ffb45d11b76b3f103483c2d0b0", damus_state: state) EventDetailView()
EventDetailView(damus: state, thread: tm)
} }
} }
/// Find the entire reply path for the active event
func make_reply_map(active: NostrEvent, events: [NostrEvent], privkey: String?) -> [String: ()]
{
let event_map: [String: Int] = zip(events,0...events.count).reduce(into: [:]) { (acc, arg1) in
let (ev, i) = arg1
acc[ev.id] = i
}
var is_reply: [String: ()] = [:]
var i: Int = 0
var start: Int = 0
var iterations: Int = 0
if events.count == 0 {
return is_reply
}
for ev in events {
/// does this event reply to the active event?
let ev_refs = ev.event_refs(privkey)
for ev_ref in ev_refs {
if let reply = ev_ref.is_reply {
if reply.ref_id == active.id {
is_reply[ev.id] = ()
start = i
}
}
}
/// does the active event reply to this event?
let active_refs = active.event_refs(privkey)
for active_ref in active_refs {
if let reply = active_ref.is_reply {
if reply.ref_id == ev.id {
is_reply[ev.id] = ()
start = i
}
}
}
i += 1
}
i = start
while true {
if iterations > 1024 {
// infinite loop? or super large thread
print("breaking from large reply_map... big thread??")
break
}
let ev = events[i]
let ref_ids = ev.referenced_ids
if ref_ids.count == 0 {
break
}
let ref_id = ref_ids[ref_ids.count-1]
let pubkey = ref_id.ref_id
is_reply[pubkey] = ()
if let mi = event_map[pubkey] {
i = mi
} else {
break
}
iterations += 1
}
return is_reply
}
func determine_highlight(reply_map: [String: ()], current: NostrEvent, active: NostrEvent) -> Highlight
{
if current.id == active.id {
return .main
} else if reply_map[current.id] != nil {
return .reply
} else {
return .none
}
}
func calculated_collapsed_events(privkey: String?, collapsed: Bool, active: NostrEvent?, events: [NostrEvent]) -> [CollapsedEvent] {
var count: Int = 0
guard let active = active else {
return []
}
let reply_map = make_reply_map(active: active, events: events, privkey: privkey)
if !collapsed {
return events.reduce(into: []) { acc, ev in
let highlight = determine_highlight(reply_map: reply_map, current: ev, active: active)
return acc.append(.event(ev, highlight))
}
}
let nevents = events.count
var start: Int = 0
var i: Int = 0
return events.reduce(into: []) { (acc, ev) in
let highlight = determine_highlight(reply_map: reply_map, current: ev, active: active)
switch highlight {
case .none:
if i == 0 {
start = 1
}
count += 1
case .main: fallthrough
case .custom: fallthrough
case .reply:
if count != 0 {
let c = CollapsedEvents(count: count, start: start, end: i)
acc.append(.collapsed(c))
start = i
count = 0
}
acc.append(.event(ev, highlight))
}
if i == nevents-1 {
if count != 0 {
let c = CollapsedEvents(count: count, start: i-count, end: i)
acc.append(.collapsed(c))
count = 0
}
}
i += 1
}
}
func any_collapsed(_ evs: [CollapsedEvent]) -> Bool {
for ev in evs {
switch ev {
case .collapsed:
return true
case .event:
continue
}
}
return false
}
func print_event(_ ev: NostrEvent) { func print_event(_ ev: NostrEvent) {
print(ev.description) print(ev.description)

View File

@@ -7,7 +7,7 @@
import SwiftUI import SwiftUI
enum Timeline: String, CustomStringConvertible { enum Timeline: String, CustomStringConvertible, Hashable {
case home case home
case notifications case notifications
case search case search

View File

@@ -0,0 +1,59 @@
//
// RelayFilterView.swift
// damus
//
// Created by Ben Weeks on 1/8/23.
//
import SwiftUI
struct RelayFilterView: View {
let state: DamusState
let timeline: Timeline
//@State var relays: [RelayDescriptor]
//@EnvironmentObject var user_settings: UserSettingsStore
//@State var relays: [RelayDescriptor]
init(state: DamusState, timeline: Timeline) {
self.state = state
self.timeline = timeline
//_relays = State(initialValue: state.pool.descriptors)
}
var relays: [RelayDescriptor] {
return state.pool.descriptors
}
func toggle_binding(relay_id: String) -> Binding<Bool> {
return Binding(get: {
!state.relay_filters.is_filtered(timeline: timeline, relay_id: relay_id)
}, set: { on in
if !on {
state.relay_filters.insert(timeline: timeline, relay_id: relay_id)
} else {
state.relay_filters.remove(timeline: timeline, relay_id: relay_id)
}
})
}
var body: some View {
Text("To filter your \(timeline.rawValue) feed, please choose applicable relays from the list below:")
.padding()
.padding(.top, 20)
.padding(.bottom, 0)
List(Array(relays), id: \.url) { relay in
//RelayView(state: state, relay: relay.url.absoluteString)
let relay_id = relay.url.absoluteString
Toggle(relay_id, isOn: toggle_binding(relay_id: relay_id))
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
}
}
}
struct RelayFilterView_Previews: PreviewProvider {
static var previews: some View {
RelayFilterView(state: test_damus_state(), timeline: .search)
}
}

View File

@@ -1,99 +0,0 @@
//
// ThreadView.swift
// damus
//
// Created by William Casarin on 2022-04-19.
//
import SwiftUI
struct ThreadView: View {
@StateObject var thread: ThreadModel
let damus: DamusState
@State var is_chatroom: Bool
@State var metadata: ChatroomMetadata? = nil
@State var seen_first: Bool = false
@Environment(\.dismiss) var dismiss
var body: some View {
Group {
if is_chatroom {
ChatroomView(damus: damus)
.navigationBarTitle(metadata?.name ?? NSLocalizedString("Chat", comment: "Navigation bar title for Chatroom view."))
.environmentObject(thread)
} else {
EventDetailView(damus: damus, thread: thread)
.navigationBarTitle(metadata?.name ?? NSLocalizedString("Thread", comment: "Navigation bar title for threaded event detail view."))
.environmentObject(thread)
}
/*
NavigationLink(destination: edv, isActive: $is_chatroom) {
EmptyView()
}
*/
}
.onReceive(handle_notify(.switched_timeline)) { n in
dismiss()
}
.onReceive(handle_notify(.toggle_thread_view)) { _ in
is_chatroom = !is_chatroom
//print("is_chatroom: \(is_chatroom)")
}
.onReceive(handle_notify(.chatroom_meta)) { n in
let meta = n.object as! ChatroomMetadata
self.metadata = meta
}
.onChange(of: thread.events) { val in
if seen_first {
return
}
if let ev = thread.events.first {
guard ev.is_root_event() else {
return
}
seen_first = true
is_chatroom = should_show_chatroom(ev)
}
}
.onAppear() {
thread.subscribe()
}
.onDisappear() {
thread.unsubscribe()
}
}
}
/*
struct ThreadView_Previews: PreviewProvider {
static var previews: some View {
ThreadView()
}
}
*/
func should_show_chatroom(_ ev: NostrEvent) -> Bool {
if ev.known_kind == .chat || ev.known_kind == .channel_create {
return true
}
return has_hashtag(ev.tags, hashtag: "chat")
}
func tag_is_hashtag(_ tag: [String]) -> Bool {
// "hashtag" is deprecated, will remove in the future
return tag.count >= 2 && (tag[0] == "hashtag" || tag[0] == "t")
}
func has_hashtag(_ tags: [[String]], hashtag: String) -> Bool {
for tag in tags {
if tag_is_hashtag(tag) && tag[1] == hashtag {
return true
}
}
return false
}

View File

@@ -7,7 +7,6 @@
import SwiftUI import SwiftUI
@main @main
struct damusApp: App { struct damusApp: App {
var body: some Scene { var body: some Scene {

View File

@@ -88,6 +88,24 @@ class damusTests: XCTestCase {
XCTAssertEqual(parsed, expected) XCTAssertEqual(parsed, expected)
} }
func testSaveRelayFilters() {
var filters = Set<RelayFilter>()
let filter1 = RelayFilter(timeline: .search, relay_id: "wss://abc.com")
let filter2 = RelayFilter(timeline: .home, relay_id: "wss://abc.com")
filters.insert(filter1)
filters.insert(filter2)
let pubkey = "test_pubkey"
save_relay_filters(pubkey, filters: filters)
let loaded_filters = load_relay_filters(pubkey)
XCTAssertEqual(loaded_filters.count, 2)
XCTAssertTrue(loaded_filters.contains(filter1))
XCTAssertTrue(loaded_filters.contains(filter2))
XCTAssertEqual(filters, loaded_filters)
}
func testParseUrl() { func testParseUrl() {
let parsed = parse_mentions(content: "a https://jb55.com b", tags: []) let parsed = parse_mentions(content: "a https://jb55.com b", tags: [])