Relay Filters
wip
This commit is contained in:
@@ -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 */,
|
||||||
|
|||||||
@@ -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!
|
||||||
|
|
||||||
|
|||||||
@@ -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: ""))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
80
damus/Util/RelayFilters.swift
Normal file
80
damus/Util/RelayFilters.swift
Normal 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) }
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
59
damus/Views/RelayFilterView.swift
Normal file
59
damus/Views/RelayFilterView.swift
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
|
|
||||||
@main
|
@main
|
||||||
struct damusApp: App {
|
struct damusApp: App {
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
|
|||||||
@@ -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: [])
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user