From f0b0eade37483338c39f3f532e0686e09684f877 Mon Sep 17 00:00:00 2001 From: Scott Penrose Date: Sat, 29 Apr 2023 14:38:28 -0400 Subject: [PATCH 1/9] Convert to NavigationStack - Fixes linking issues on SideMenu and tab switching issues - I currently bumped to iOS 16+ to get iterate and get this working. --- damus.xcodeproj/project.pbxproj | 8 ++ damus/ContentView.swift | 9 +- damus/Util/Router.swift | 115 ++++++++++++++++++++++++++ damus/Views/Profile/ProfileView.swift | 18 ++-- damus/Views/SideMenuView.swift | 20 ++--- 5 files changed, 149 insertions(+), 21 deletions(-) create mode 100644 damus/Util/Router.swift diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj index dec49e2c..763cd9b0 100644 --- a/damus.xcodeproj/project.pbxproj +++ b/damus.xcodeproj/project.pbxproj @@ -304,6 +304,7 @@ 9CA876E229A00CEA0003B9A3 /* AttachMediaUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CA876E129A00CE90003B9A3 /* AttachMediaUtility.swift */; }; BA693074295D649800ADDB87 /* UserSettingsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA693073295D649800ADDB87 /* UserSettingsStore.swift */; }; BAB68BED29543FA3007BA466 /* SelectWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */; }; + D2277EEA2A089BD5006C3807 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2277EE92A089BD5006C3807 /* Router.swift */; }; DD597CBD2963D85A00C64D32 /* MarkdownTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD597CBC2963D85A00C64D32 /* MarkdownTests.swift */; }; E4FA1C032A24BB7F00482697 /* SearchSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4FA1C022A24BB7F00482697 /* SearchSettingsView.swift */; }; E990020F2955F837003BBC5A /* EditMetadataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E990020E2955F837003BBC5A /* EditMetadataView.swift */; }; @@ -753,6 +754,7 @@ 9CA876E129A00CE90003B9A3 /* AttachMediaUtility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachMediaUtility.swift; sourceTree = ""; }; BA693073295D649800ADDB87 /* UserSettingsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSettingsStore.swift; sourceTree = ""; }; BAB68BEC29543FA3007BA466 /* SelectWalletView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectWalletView.swift; sourceTree = ""; }; + D2277EE92A089BD5006C3807 /* Router.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = ""; }; DD597CBC2963D85A00C64D32 /* MarkdownTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarkdownTests.swift; sourceTree = ""; }; E4FA1C022A24BB7F00482697 /* SearchSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchSettingsView.swift; sourceTree = ""; }; E990020E2955F837003BBC5A /* EditMetadataView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditMetadataView.swift; sourceTree = ""; }; @@ -1155,6 +1157,7 @@ 50B5685229F97CB400A23243 /* CredentialHandler.swift */, 4C7D09582A05BEAD00943473 /* KeyboardVisible.swift */, 3A8CC6CB2A2CFEF900940F5F /* StringUtil.swift */, + D2277EE92A089BD5006C3807 /* Router.swift */, ); path = Util; sourceTree = ""; @@ -1307,7 +1310,9 @@ 4CE6DEE427F7A08100C66700 /* Products */, 4CEE2AE62804F57B00AB5EEF /* Frameworks */, ); + indentWidth = 4; sourceTree = ""; + tabWidth = 4; }; 4CE6DEE427F7A08100C66700 /* Products */ = { isa = PBXGroup; @@ -1861,6 +1866,7 @@ 5C6E1DAD2A193EC2008FC15A /* GradientButtonStyle.swift in Sources */, 4C8EC52529D1FA6C0085D9A8 /* DamusColors.swift in Sources */, 3A4647CF2A413ADC00386AD8 /* CondensedProfilePicturesView.swift in Sources */, + D2277EEA2A089BD5006C3807 /* Router.swift in Sources */, 4C5C7E6A284EDE2E00A22DF5 /* SearchResultsView.swift in Sources */, 4CE1399429F0669900AC6A0B /* BigButton.swift in Sources */, 7C60CAEF298471A1009C80D6 /* CoreSVG.swift in Sources */, @@ -2254,6 +2260,7 @@ INFOPLIST_KEY_UILaunchStoryboardName = Launch.storyboard; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2302,6 +2309,7 @@ INFOPLIST_KEY_UILaunchStoryboardName = Launch.storyboard; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/damus/ContentView.swift b/damus/ContentView.swift index fae00f24..c4fccbf3 100644 --- a/damus/ContentView.swift +++ b/damus/ContentView.swift @@ -97,6 +97,7 @@ struct ContentView: View { @SceneStorage("ContentView.filter_state") var filter_state : FilterState = .posts_and_replies @State private var isSideBarOpened = false var home: HomeModel = HomeModel() + @StateObject var navigationCoordinator: NavigationCoordinator = NavigationCoordinator() let sub_id = UUID().description @@ -287,7 +288,7 @@ struct ContentView: View { var body: some View { VStack(alignment: .leading, spacing: 0) { if let damus = self.damus_state { - NavigationView { + NavigationStack(path: $navigationCoordinator.path) { TabView { // Prevents navbar appearance change on scroll MainContent(damus: damus) .toolbar() { @@ -327,6 +328,12 @@ struct ContentView: View { .overlay( SideMenuView(damus_state: damus, isSidebarVisible: $isSideBarOpened.animation()) ) + .navigationDestination(for: Route.self) { route in + route.view(navigationCordinator: navigationCoordinator) + } + .onReceive(handle_notify(.switched_timeline)) { _ in + navigationCoordinator.popToRoot() + } } .navigationViewStyle(.stack) diff --git a/damus/Util/Router.swift b/damus/Util/Router.swift new file mode 100644 index 00000000..c6b599a7 --- /dev/null +++ b/damus/Util/Router.swift @@ -0,0 +1,115 @@ +// +// Router.swift +// damus +// +// Created by Scott Penrose on 5/7/23. +// + +import SwiftUI + +enum Route: Hashable { + case Profile(damusSate: DamusState, profile: ProfileModel, followers: FollowersModel) + case Followers(damusState: DamusState, environmentObject: FollowersModel) + case Relay(damusState: DamusState, relay: String, showActionButtons: Binding) + case Following(damusState: DamusState, following: FollowingModel) + case MuteList(damusState: DamusState, users: [String]) + case RelayConfig(damusState: DamusState) + case Bookmarks(damusState: DamusState) + case Config(damusState: DamusState) + case EditMetadata(damusState: DamusState) + case DMChat(damusState: DamusState, dms: DirectMessageModel) + case UserRelays(damusState: DamusState, relays: [String]) + + @ViewBuilder + func view(navigationCordinator: NavigationCoordinator) -> some View { + switch self { + case .Profile (let damusState, let profile, let followers): + ProfileView(damus_state: damusState, profile: profile, followers: followers) + case .Followers (let damusState, let environmentObject): + FollowersView(damus_state: damusState) + .environmentObject(environmentObject) + case .Relay (let damusState, let relay, let showActionButtons): + RelayView(state: damusState, relay: relay, showActionButtons: showActionButtons) + case .Following(let damusState, let following): + FollowingView(damus_state: damusState, following: following) + case .MuteList(let damusState, let users): + MutelistView(damus_state: damusState, users: users) + case .RelayConfig(let damusState): + RelayConfigView(state: damusState) + case .Bookmarks(let damusState): + BookmarksView(state: damusState) + case .Config(let damusState): + ConfigView(state: damusState) + case .EditMetadata(let damusState): + EditMetadataView(damus_state: damusState) + case .DMChat(let damusState, let dms): + DMChatView(damus_state: damusState, dms: dms) + case .UserRelays(let damusState, let relays): + UserRelaysView(state: damusState, relays: relays) + } + } + + static func == (lhs: Route, rhs: Route) -> Bool { + switch (lhs, rhs) { + case (.Profile (_, let lhs_profile, _), .Profile(_, let rhs_profile, _)): + return lhs_profile == rhs_profile + case (.Followers (_, _), .Followers (_, _)): + return true + case (.Relay (_, let lhs_relay, _), .Relay (_, let rhs_relay, _)): + return lhs_relay == rhs_relay + case (.Following(_, _), .Following(_, _)): + return true + case (.MuteList(_, let lhs_users), .MuteList(_, let rhs_users)): + return lhs_users == rhs_users + case (.RelayConfig(_), .RelayConfig(_)): + return true + case (.Bookmarks(_), .Bookmarks(_)): + return true + case (.Config(_), .Config(_)): + return true + case (.EditMetadata(_), .EditMetadata(_)): + return true + case (.DMChat(_, let lhs_dms), .DMChat(_, let rhs_dms)): + return lhs_dms.our_pubkey == rhs_dms.our_pubkey + case (.UserRelays(_, let lhs_relays), .UserRelays(_, let rhs_relays)): + return lhs_relays == rhs_relays + default: + return false + } + } + + func hash(into hasher: inout Hasher) { + switch self { + case .Profile(_, let profile, _): + hasher.combine(profile.pubkey) + case .Followers(_, _): + hasher.combine("followers") + case .Relay(_, let relay, _): + hasher.combine(relay) + case .Following(_, _): + hasher.combine("following") + case .MuteList(_, let users): + hasher.combine(users) + case .RelayConfig(_): + hasher.combine("relayConfig") + case .Bookmarks(_): + hasher.combine("bookmarks") + case .Config(_): + hasher.combine("config") + case .EditMetadata(_): + hasher.combine("editMetadata") + case .DMChat(_, let dms): + hasher.combine(dms.our_pubkey) + case .UserRelays(_, let relays): + hasher.combine(relays) + } + } +} + +class NavigationCoordinator: ObservableObject { + @Published var path = [Route]() + + func popToRoot() { + path = [] + } +} diff --git a/damus/Views/Profile/ProfileView.swift b/damus/Views/Profile/ProfileView.swift index 714c1512..ff2f010a 100644 --- a/damus/Views/Profile/ProfileView.swift +++ b/damus/Views/Profile/ProfileView.swift @@ -77,7 +77,7 @@ struct EditButton: View { @Environment(\.colorScheme) var colorScheme var body: some View { - NavigationLink(destination: EditMetadataView(damus_state: damus_state)) { + NavigationLink(value: Route.EditMetadata(damusState: damus_state)) { Text("Edit", comment: "Button to edit user's profile.") .frame(height: 30) .padding(.horizontal,25) @@ -300,8 +300,7 @@ struct ProfileView: View { var dmButton: some View { let dm_model = damus_state.dms.lookup_or_create(profile.pubkey) - let dmview = DMChatView(damus_state: damus_state, dms: dm_model) - return NavigationLink(destination: dmview) { + return NavigationLink(value: Route.DMChat(damusState: damus_state, dms: dm_model)) { Image("messages") .profile_button_style(scheme: colorScheme) } @@ -325,7 +324,7 @@ struct ProfileView: View { follow_state: damus_state.contacts.follow_state(profile.pubkey) ) } else if damus_state.keypair.privkey != nil { - NavigationLink(destination: EditMetadataView(damus_state: damus_state)) { + NavigationLink(value: Route.EditMetadata(damusState: damus_state)) { EditButton(damus_state: damus_state) } } @@ -404,7 +403,7 @@ struct ProfileView: View { if let contact = profile.contacts { let contacts = contact.referenced_pubkeys.map { $0.ref_id } let following_model = FollowingModel(damus_state: damus_state, contacts: contacts) - NavigationLink(destination: FollowingView(damus_state: damus_state, following: following_model)) { + NavigationLink(value: Route.Following(damusState: damus_state, following: following_model)) { HStack { let noun_text = Text(verbatim: "\(followingCountString(profile.following))").font(.subheadline).foregroundColor(.gray) Text("\(Text(verbatim: profile.following.formatted()).font(.subheadline.weight(.medium))) \(noun_text)", comment: "Sentence composed of 2 variables to describe how many profiles a user is following. In source English, the first variable is the number of profiles being followed, and the second variable is 'Following'.") @@ -412,10 +411,9 @@ struct ProfileView: View { } .buttonStyle(PlainButtonStyle()) } - let fview = FollowersView(damus_state: damus_state) - .environmentObject(followers) + if followers.contacts != nil { - NavigationLink(destination: fview) { + NavigationLink(value: Route.Followers(damusState: damus_state, environmentObject: followers)) { followersCount } .buttonStyle(PlainButtonStyle()) @@ -433,12 +431,12 @@ struct ProfileView: View { let noun_text = Text(verbatim: relaysCountString(relays.keys.count)).font(.subheadline).foregroundColor(.gray) let relay_text = Text("\(Text(verbatim: relays.keys.count.formatted()).font(.subheadline.weight(.medium))) \(noun_text)", comment: "Sentence composed of 2 variables to describe how many relay servers a user is connected. In source English, the first variable is the number of relay servers, and the second variable is 'Relay' or 'Relays'.") if profile.pubkey == damus_state.pubkey && damus_state.is_privkey_user { - NavigationLink(destination: RelayConfigView(state: damus_state)) { + NavigationLink(value: Route.RelayConfig(damusState: damus_state)) { relay_text } .buttonStyle(PlainButtonStyle()) } else { - NavigationLink(destination: UserRelaysView(state: damus_state, relays: Array(relays.keys).sorted())) { + NavigationLink(value: Route.UserRelays(damusState: damus_state, relays: Array(relays.keys).sorted())) { relay_text } .buttonStyle(PlainButtonStyle()) diff --git a/damus/Views/SideMenuView.swift b/damus/Views/SideMenuView.swift index d89096f8..cb83f94a 100644 --- a/damus/Views/SideMenuView.swift +++ b/damus/Views/SideMenuView.swift @@ -45,7 +45,7 @@ struct SideMenuView: View { func SidemenuItems(profile_model: ProfileModel, followers: FollowersModel) -> some View { return VStack(spacing: verticalSpacing) { - NavigationLink(destination: ProfileView(damus_state: damus_state, profile: profile_model, followers: followers)) { + NavigationLink(value: Route.Profile(damusSate: damus_state, profile: profile_model, followers: followers)) { navLabel(title: NSLocalizedString("Profile", comment: "Sidebar menu label for Profile view."), img: "user") } @@ -64,19 +64,19 @@ struct SideMenuView: View { }*/ } - NavigationLink(destination: MutelistView(damus_state: damus_state, users: get_mutelist_users(damus_state.contacts.mutelist) )) { + NavigationLink(value: Route.MuteList(damusState: damus_state, users: get_mutelist_users(damus_state.contacts.mutelist))) { navLabel(title: NSLocalizedString("Muted", comment: "Sidebar menu label for muted users view."), img: "mute") } - NavigationLink(destination: RelayConfigView(state: damus_state)) { + NavigationLink(value: Route.RelayConfig(damusState: damus_state)) { navLabel(title: NSLocalizedString("Relays", comment: "Sidebar menu label for Relays view."), img: "world-relays") } - NavigationLink(destination: BookmarksView(state: damus_state)) { + NavigationLink(value: Route.Bookmarks(damusState: damus_state)) { navLabel(title: NSLocalizedString("Bookmarks", comment: "Sidebar menu label for Bookmarks view."), img: "bookmark") } - NavigationLink(destination: ConfigView(state: damus_state)) { + NavigationLink(value: Route.Config(damusState: damus_state)) { navLabel(title: NSLocalizedString("Settings", comment: "Sidebar menu label for accessing the app settings"), img: "settings") } } @@ -126,15 +126,15 @@ struct SideMenuView: View { ZStack(alignment: .top) { fillColor() .ignoresSafeArea() - + VStack(alignment: .leading, spacing: 0) { MainSidemenu .simultaneousGesture(TapGesture().onEnded { isSidebarVisible = false }) - + Divider() - + HStack() { Button(action: { //ConfigView(state: damus_state) @@ -150,9 +150,9 @@ struct SideMenuView: View { .frame(maxWidth: .infinity, alignment: .leading) .dynamicTypeSize(.xSmall) }) - + Spacer() - + Button(action: { showQRCode.toggle() }, label: { From 242455410e30b5810f7564fcb76729373ecc6d56 Mon Sep 17 00:00:00 2001 From: Scott Penrose Date: Fri, 26 May 2023 16:58:24 -0400 Subject: [PATCH 2/9] Convert more NavigationLinks to router --- damus/Components/UserView.swift | 13 +- damus/ContentView.swift | 2 +- damus/Util/Router.swift | 189 +++++++++++++----- damus/Views/ActionBar/EventDetailBar.swift | 7 +- damus/Views/ConfigView.swift | 33 ++- damus/Views/DMChatView.swift | 3 +- damus/Views/Events/BuilderEventView.swift | 3 +- damus/Views/Events/EventProfile.swift | 2 +- .../Views/Notifications/EventGroupView.swift | 3 +- .../Notifications/NotificationItemView.swift | 2 +- damus/Views/Profile/MaybeAnonPfpView.swift | 2 +- damus/Views/Profile/ProfileView.swift | 124 ++++++------ damus/Views/Relays/RelayDetailView.swift | 4 +- damus/Views/Relays/RelayView.swift | 5 +- damus/Views/Relays/SignalView.swift | 2 +- damus/Views/Reposts/RepostedEvent.swift | 3 +- damus/Views/Search/SearchingEventView.swift | 6 +- damus/Views/SearchResultsView.swift | 3 +- damus/Views/SideMenuView.swift | 61 +++--- 19 files changed, 274 insertions(+), 193 deletions(-) diff --git a/damus/Components/UserView.swift b/damus/Components/UserView.swift index 1df3b9e3..524a8dad 100644 --- a/damus/Components/UserView.swift +++ b/damus/Components/UserView.swift @@ -11,21 +11,10 @@ struct UserViewRow: View { let damus_state: DamusState let pubkey: String - @State var navigating: Bool = false - var body: some View { - let dest = ProfileView(damus_state: damus_state, pubkey: pubkey) - UserView(damus_state: damus_state, pubkey: pubkey) .contentShape(Rectangle()) - .background( - NavigationLink(destination: dest, isActive: $navigating) { - EmptyView() - } - ) - .onTapGesture { - navigating = true - } + .background(.clear) } } diff --git a/damus/ContentView.swift b/damus/ContentView.swift index c4fccbf3..b4618bbe 100644 --- a/damus/ContentView.swift +++ b/damus/ContentView.swift @@ -329,7 +329,7 @@ struct ContentView: View { SideMenuView(damus_state: damus, isSidebarVisible: $isSideBarOpened.animation()) ) .navigationDestination(for: Route.self) { route in - route.view(navigationCordinator: navigationCoordinator) + route.view(navigationCordinator: navigationCoordinator, damusState: damus_state!) } .onReceive(handle_notify(.switched_timeline)) { _ in navigationCoordinator.popToRoot() diff --git a/damus/Util/Router.swift b/damus/Util/Router.swift index c6b599a7..85433b86 100644 --- a/damus/Util/Router.swift +++ b/damus/Util/Router.swift @@ -8,71 +8,131 @@ import SwiftUI enum Route: Hashable { - case Profile(damusSate: DamusState, profile: ProfileModel, followers: FollowersModel) - case Followers(damusState: DamusState, environmentObject: FollowersModel) - case Relay(damusState: DamusState, relay: String, showActionButtons: Binding) - case Following(damusState: DamusState, following: FollowingModel) - case MuteList(damusState: DamusState, users: [String]) - case RelayConfig(damusState: DamusState) - case Bookmarks(damusState: DamusState) - case Config(damusState: DamusState) - case EditMetadata(damusState: DamusState) - case DMChat(damusState: DamusState, dms: DirectMessageModel) - case UserRelays(damusState: DamusState, relays: [String]) + case ProfileByKey(pubkey: String) + case Profile(profile: ProfileModel, followers: FollowersModel) + case Followers(environmentObject: FollowersModel) + case Relay(relay: String, showActionButtons: Binding) + case RelayDetail(relay: String, metadata: RelayMetadata) + case Following(following: FollowingModel) + case MuteList(users: [String]) + case RelayConfig + case Bookmarks + case Config + case EditMetadata + case DMChat(dms: DirectMessageModel) + case UserRelays(relays: [String]) + case KeySettings(keypair: Keypair) + case AppearanceSettings(settings: UserSettingsStore) // Observed object.. is this an issue? + case NotificationSettings(settings: UserSettingsStore) // Observed object.. is this an issue? + case ZapSettings(settings: UserSettingsStore) // Observed object.. is this an issue? + case TranslationSettings(settings: UserSettingsStore) // Observed object.. is this an issue? + case Thread(thread: ThreadModel) + case Reposts(reposts: RepostsModel) + case Reactions(reactions: ReactionsModel) + case Zaps(target: ZapTarget) + case Search(search: SearchModel) @ViewBuilder - func view(navigationCordinator: NavigationCoordinator) -> some View { + func view(navigationCordinator: NavigationCoordinator, damusState: DamusState) -> some View { switch self { - case .Profile (let damusState, let profile, let followers): + case .ProfileByKey(let pubkey): + ProfileView(damus_state: damusState, pubkey: pubkey) + case .Profile(let profile, let followers): ProfileView(damus_state: damusState, profile: profile, followers: followers) - case .Followers (let damusState, let environmentObject): + case .Followers(let environmentObject): FollowersView(damus_state: damusState) .environmentObject(environmentObject) - case .Relay (let damusState, let relay, let showActionButtons): + case .Relay(let relay, let showActionButtons): RelayView(state: damusState, relay: relay, showActionButtons: showActionButtons) - case .Following(let damusState, let following): + case .RelayDetail(let relay, let metadata): + RelayDetailView(state: damusState, relay: relay, nip11: metadata) + case .Following(let following): FollowingView(damus_state: damusState, following: following) - case .MuteList(let damusState, let users): + case .MuteList(let users): MutelistView(damus_state: damusState, users: users) - case .RelayConfig(let damusState): + case .RelayConfig: RelayConfigView(state: damusState) - case .Bookmarks(let damusState): + case .Bookmarks: BookmarksView(state: damusState) - case .Config(let damusState): + case .Config: ConfigView(state: damusState) - case .EditMetadata(let damusState): + case .EditMetadata: EditMetadataView(damus_state: damusState) - case .DMChat(let damusState, let dms): + case .DMChat(let dms): DMChatView(damus_state: damusState, dms: dms) - case .UserRelays(let damusState, let relays): + case .UserRelays(let relays): UserRelaysView(state: damusState, relays: relays) + case .KeySettings(let keypair): + KeySettingsView(keypair: keypair) + case .AppearanceSettings(let settings): + AppearanceSettingsView(settings: settings) + case .NotificationSettings(let settings): + NotificationSettingsView(settings: settings) + case .ZapSettings(let settings): + ZapSettingsView(settings: settings) + case .TranslationSettings(let settings): + NotificationSettingsView(settings: settings) + case .Thread(let thread): + ThreadView(state: damusState, thread: thread) + case .Reposts(let reposts): + RepostsView(damus_state: damusState, model: reposts) + case .Reactions(let reactions): + ReactionsView(damus_state: damusState, model: reactions) + case .Zaps(let target): + ZapsView(state: damusState, target: target) + case .Search(let search): + SearchView(appstate: damusState, search: search) } } static func == (lhs: Route, rhs: Route) -> Bool { switch (lhs, rhs) { - case (.Profile (_, let lhs_profile, _), .Profile(_, let rhs_profile, _)): + case (.ProfileByKey (let lhs_pubkey), .ProfileByKey(let rhs_pubkey)): + return lhs_pubkey == rhs_pubkey + case (.Profile (let lhs_profile, _), .Profile(let rhs_profile, _)): return lhs_profile == rhs_profile - case (.Followers (_, _), .Followers (_, _)): + case (.Followers (_), .Followers (_)): return true - case (.Relay (_, let lhs_relay, _), .Relay (_, let rhs_relay, _)): + case (.Relay (let lhs_relay, _), .Relay (let rhs_relay, _)): return lhs_relay == rhs_relay - case (.Following(_, _), .Following(_, _)): + case (.RelayDetail(let lhs_relay, _), .RelayDetail(let rhs_relay, _)): + return lhs_relay == rhs_relay + case (.Following(_), .Following(_)): return true - case (.MuteList(_, let lhs_users), .MuteList(_, let rhs_users)): + case (.MuteList(let lhs_users), .MuteList(let rhs_users)): return lhs_users == rhs_users - case (.RelayConfig(_), .RelayConfig(_)): + case (.RelayConfig, .RelayConfig): return true - case (.Bookmarks(_), .Bookmarks(_)): + case (.Bookmarks, .Bookmarks): return true - case (.Config(_), .Config(_)): + case (.Config, .Config): return true - case (.EditMetadata(_), .EditMetadata(_)): + case (.EditMetadata, .EditMetadata): return true - case (.DMChat(_, let lhs_dms), .DMChat(_, let rhs_dms)): + case (.DMChat(let lhs_dms), .DMChat(let rhs_dms)): return lhs_dms.our_pubkey == rhs_dms.our_pubkey - case (.UserRelays(_, let lhs_relays), .UserRelays(_, let rhs_relays)): + case (.UserRelays(let lhs_relays), .UserRelays(let rhs_relays)): return lhs_relays == rhs_relays + case (.KeySettings(let lhs_keypair), .KeySettings(let rhs_keypair)): + return lhs_keypair.pubkey == rhs_keypair.pubkey + case (.AppearanceSettings(_), .AppearanceSettings(_)): + return true + case (.NotificationSettings(_), .NotificationSettings(_)): + return true + case (.ZapSettings(_), .ZapSettings(_)): + return true + case (.TranslationSettings(_), .TranslationSettings(_)): + return true + case (.Thread(let lhs_threadModel), .Thread(thread: let rhs_threadModel)): + return lhs_threadModel.event.id == rhs_threadModel.event.id + case (.Reposts(let lhs_reposts), .Reposts(let rhs_reposts)): + return lhs_reposts.target == rhs_reposts.target + case (.Reactions(let lhs_reactions), .Reactions(let rhs_reactions)): + return lhs_reactions.target == rhs_reactions.target + case (.Zaps(let lhs_target), .Zaps(let rhs_target)): + return lhs_target == rhs_target + case (.Search(let lhs_search), .Search(let rhs_search)): + return lhs_search.sub_id == rhs_search.sub_id && lhs_search.profiles_subid == rhs_search.profiles_subid default: return false } @@ -80,28 +140,67 @@ enum Route: Hashable { func hash(into hasher: inout Hasher) { switch self { - case .Profile(_, let profile, _): + case .ProfileByKey(let pubkey): + hasher.combine("profilebykey") + hasher.combine(pubkey) + case .Profile(let profile, _): + hasher.combine("profile") hasher.combine(profile.pubkey) - case .Followers(_, _): + case .Followers(_): hasher.combine("followers") - case .Relay(_, let relay, _): + case .Relay(let relay, _): + hasher.combine("relay") hasher.combine(relay) - case .Following(_, _): + case .RelayDetail(let relay, _): + hasher.combine("relayDetail") + hasher.combine(relay) + case .Following(_): hasher.combine("following") - case .MuteList(_, let users): + case .MuteList(let users): + hasher.combine("muteList") hasher.combine(users) - case .RelayConfig(_): + case .RelayConfig: hasher.combine("relayConfig") - case .Bookmarks(_): + case .Bookmarks: hasher.combine("bookmarks") - case .Config(_): + case .Config: hasher.combine("config") - case .EditMetadata(_): + case .EditMetadata: hasher.combine("editMetadata") - case .DMChat(_, let dms): + case .DMChat(let dms): + hasher.combine("dms") hasher.combine(dms.our_pubkey) - case .UserRelays(_, let relays): + case .UserRelays(let relays): + hasher.combine("userRelays") hasher.combine(relays) + case .KeySettings(let keypair): + hasher.combine("keySettings") + hasher.combine(keypair.pubkey) + case .AppearanceSettings(_): + hasher.combine("appearanceSettings") + case .NotificationSettings(_): + hasher.combine("notificationSettings") + case .ZapSettings(_): + hasher.combine("zapSettings") + case .TranslationSettings(_): + hasher.combine("translationSettings") + case .Thread(let threadModel): + hasher.combine("thread") + hasher.combine(threadModel.event.id) + case .Reposts(let reposts): + hasher.combine("reposts") + hasher.combine(reposts.target) + case .Zaps(let target): + hasher.combine("zaps") + hasher.combine(target.id) + hasher.combine(target.pubkey) + case .Reactions(let reactions): + hasher.combine("reactions") + hasher.combine(reactions.target) + case .Search(let search): + hasher.combine("search") + hasher.combine(search.sub_id) + hasher.combine(search.profiles_subid) } } } diff --git a/damus/Views/ActionBar/EventDetailBar.swift b/damus/Views/ActionBar/EventDetailBar.swift index 00e9edcc..9775f052 100644 --- a/damus/Views/ActionBar/EventDetailBar.swift +++ b/damus/Views/ActionBar/EventDetailBar.swift @@ -25,7 +25,7 @@ struct EventDetailBar: View { var body: some View { HStack { if bar.boosts > 0 { - NavigationLink(destination: RepostsView(damus_state: state, model: RepostsModel(state: state, target: target))) { + NavigationLink(value: Route.Reposts(reposts: RepostsModel(state: state, target: target))) { let noun = Text(verbatim: repostsCountString(bar.boosts)).foregroundColor(.gray) Text("\(Text(verbatim: bar.boosts.formatted()).font(.body.bold())) \(noun)", comment: "Sentence composed of 2 variables to describe how many reposts. In source English, the first variable is the number of reposts, and the second variable is 'Repost' or 'Reposts'.") } @@ -33,7 +33,7 @@ struct EventDetailBar: View { } if bar.likes > 0 && !state.settings.onlyzaps_mode { - NavigationLink(destination: ReactionsView(damus_state: state, model: ReactionsModel(state: state, target: target))) { + NavigationLink(value: Route.Reactions(reactions: ReactionsModel(state: state, target: target))) { let noun = Text(verbatim: reactionsCountString(bar.likes)).foregroundColor(.gray) Text("\(Text(verbatim: bar.likes.formatted()).font(.body.bold())) \(noun)", comment: "Sentence composed of 2 variables to describe how many reactions there are on a post. In source English, the first variable is the number of reactions, and the second variable is 'Reaction' or 'Reactions'.") } @@ -41,8 +41,7 @@ struct EventDetailBar: View { } if bar.zaps > 0 { - let dst = ZapsView(state: state, target: .note(id: target, author: target_pk)) - NavigationLink(destination: dst) { + NavigationLink(value: Route.Zaps(target: .note(id: target, author: target_pk))) { let noun = Text(verbatim: zapsCountString(bar.zaps)).foregroundColor(.gray) Text("\(Text(verbatim: bar.zaps.formatted()).font(.body.bold())) \(noun)", comment: "Sentence composed of 2 variables to describe how many zap payments there are on a post. In source English, the first variable is the number of zap payments, and the second variable is 'Zap' or 'Zaps'.") } diff --git a/damus/Views/ConfigView.swift b/damus/Views/ConfigView.swift index a65dc7b9..f2e3b348 100644 --- a/damus/Views/ConfigView.swift +++ b/damus/Views/ConfigView.swift @@ -18,16 +18,16 @@ struct ConfigView: View { @State var delete_account_warning: Bool = false @State var confirm_delete_account: Bool = false @State var delete_text: String = "" - + @ObservedObject var settings: UserSettingsStore private let DELETE_KEYWORD = "DELETE" - + init(state: DamusState) { self.state = state _settings = ObservedObject(initialValue: state.settings) } - + func textColor() -> Color { colorScheme == .light ? DamusColors.black : DamusColors.white } @@ -36,31 +36,30 @@ struct ConfigView: View { ZStack(alignment: .leading) { Form { Section { - NavigationLink(destination: KeySettingsView(keypair: state.keypair)) { + NavigationLink(value: Route.KeySettings(keypair: state.keypair)) { IconLabel(NSLocalizedString("Keys", comment: "Settings section for managing keys"), img_name: "key", color: .purple) } - - NavigationLink(destination: AppearanceSettingsView(settings: settings)) { + + NavigationLink(value: Route.AppearanceSettings(settings: settings)) { IconLabel(NSLocalizedString("Appearance", comment: "Section header for text and appearance settings"), img_name: "eye", color: .red) } - + NavigationLink(destination: SearchSettingsView(settings: settings)) { IconLabel(NSLocalizedString("Search/Universe", comment: "Section header for search/universe settings"), img_name: "magnifyingglass", color: .red) } - - NavigationLink(destination: NotificationSettingsView(settings: settings)) { + + NavigationLink(value: Route.NotificationSettings(settings: settings)) { IconLabel(NSLocalizedString("Notifications", comment: "Section header for Damus notifications"), img_name: "notification-bell-on", color: .blue) } - - NavigationLink(destination: ZapSettingsView(settings: settings)) { + + NavigationLink(value: Route.ZapSettings(settings: settings)) { IconLabel(NSLocalizedString("Zaps", comment: "Section header for zap settings"), img_name: "zap.fill", color: .orange) } - - NavigationLink(destination: TranslationSettingsView(settings: settings)) { + + NavigationLink(value: Route.TranslationSettings(settings: settings)) { IconLabel(NSLocalizedString("Translation", comment: "Section header for text and appearance settings"), img_name: "globe", color: .green) } } - Section(NSLocalizedString("Sign Out", comment: "Section title for signing out")) { Button(action: { @@ -116,11 +115,11 @@ struct ConfigView: View { guard let full_kp = state.keypair.to_full() else { return } - + guard delete_text == DELETE_KEYWORD else { return } - + let ev = created_deleted_account_profile(keypair: full_kp) state.postbox.send(ev) notify(.logout, ()) @@ -164,7 +163,7 @@ func handle_string_amount(new_value: String) -> Int? { guard let amt = NumberFormatter().number(from: filtered) as? Int else { return nil } - + return amt } diff --git a/damus/Views/DMChatView.swift b/damus/Views/DMChatView.swift index c5e6bab9..248241ee 100644 --- a/damus/Views/DMChatView.swift +++ b/damus/Views/DMChatView.swift @@ -61,8 +61,7 @@ struct DMChatView: View, KeyboardReadable { var Header: some View { let profile = damus_state.profiles.lookup(id: pubkey) - let profile_page = ProfileView(damus_state: damus_state, pubkey: pubkey) - return NavigationLink(destination: profile_page) { + return NavigationLink(value: Route.ProfileByKey(pubkey: pubkey)) { HStack { ProfilePicView(pubkey: pubkey, size: 24, highlight: .none, profiles: damus_state.profiles, disable_animation: damus_state.settings.disable_animation) diff --git a/damus/Views/Events/BuilderEventView.swift b/damus/Views/Events/BuilderEventView.swift index 11c12c40..ebcc56dd 100644 --- a/damus/Views/Events/BuilderEventView.swift +++ b/damus/Views/Events/BuilderEventView.swift @@ -72,8 +72,7 @@ struct BuilderEventView: View { if let event { let ev = event.get_inner_event(cache: damus.events) ?? event let thread = ThreadModel(event: ev, damus_state: damus) - let dest = ThreadView(state: damus, thread: thread) - NavigationLink(destination: dest) { + NavigationLink(value: Route.Thread(thread: thread)) { EventView(damus: damus, event: event, options: .embedded) .padding([.top, .bottom], 8) }.buttonStyle(.plain) diff --git a/damus/Views/Events/EventProfile.swift b/damus/Views/Events/EventProfile.swift index c06a6476..aab88d0d 100644 --- a/damus/Views/Events/EventProfile.swift +++ b/damus/Views/Events/EventProfile.swift @@ -37,7 +37,7 @@ struct EventProfile: View { var body: some View { HStack(alignment: .center) { VStack { - NavigationLink(destination: ProfileView(damus_state: damus_state, pubkey: pubkey)) { + NavigationLink(value: Route.ProfileByKey(pubkey: pubkey)) { ProfilePicView(pubkey: pubkey, size: pfp_size, highlight: .none, profiles: damus_state.profiles, disable_animation: disable_animation) } } diff --git a/damus/Views/Notifications/EventGroupView.swift b/damus/Views/Notifications/EventGroupView.swift index 008c8b5e..a473381b 100644 --- a/damus/Views/Notifications/EventGroupView.swift +++ b/damus/Views/Notifications/EventGroupView.swift @@ -240,8 +240,7 @@ struct EventGroupView: View { if let event { let thread = ThreadModel(event: event, damus_state: state) - let dest = ThreadView(state: state, thread: thread) - NavigationLink(destination: dest) { + NavigationLink(value: Route.Thread(thread: thread)) { VStack(alignment: .leading) { GroupDescription(unique_pubkeys) EventBody(damus_state: state, event: event, size: .normal, options: [.truncate_content]) diff --git a/damus/Views/Notifications/NotificationItemView.swift b/damus/Views/Notifications/NotificationItemView.swift index 745837be..7eaa8586 100644 --- a/damus/Views/Notifications/NotificationItemView.swift +++ b/damus/Views/Notifications/NotificationItemView.swift @@ -59,7 +59,7 @@ struct NotificationItemView: View { EventGroupView(state: state, event: ev, group: .reaction(evgrp)) case .reply(let ev): - NavigationLink(destination: ThreadView(state: state, thread: ThreadModel(event: ev, damus_state: state))) { + NavigationLink(value: Route.Thread(thread: ThreadModel(event: ev, damus_state: state))) { EventView(damus: state, event: ev, options: options) } .buttonStyle(.plain) diff --git a/damus/Views/Profile/MaybeAnonPfpView.swift b/damus/Views/Profile/MaybeAnonPfpView.swift index 845ea3f6..8bc45e06 100644 --- a/damus/Views/Profile/MaybeAnonPfpView.swift +++ b/damus/Views/Profile/MaybeAnonPfpView.swift @@ -28,7 +28,7 @@ struct MaybeAnonPfpView: View { .font(.largeTitle) .frame(width: size, height: size) } else { - NavigationLink(destination: ProfileView(damus_state: state, pubkey: pubkey)) { + NavigationLink(value: Route.ProfileByKey(pubkey: pubkey)) { ProfilePicView(pubkey: pubkey, size: size, highlight: .none, profiles: state.profiles, disable_animation: state.settings.disable_animation) } } diff --git a/damus/Views/Profile/ProfileView.swift b/damus/Views/Profile/ProfileView.swift index ff2f010a..a4a0cfb3 100644 --- a/damus/Views/Profile/ProfileView.swift +++ b/damus/Views/Profile/ProfileView.swift @@ -73,11 +73,11 @@ func followedByString(_ friend_intersection: [String], profiles: Profiles, local struct EditButton: View { let damus_state: DamusState - + @Environment(\.colorScheme) var colorScheme - + var body: some View { - NavigationLink(value: Route.EditMetadata(damusState: damus_state)) { + NavigationLink(value: Route.EditMetadata) { Text("Edit", comment: "Button to edit user's profile.") .frame(height: 30) .padding(.horizontal,25) @@ -92,11 +92,11 @@ struct EditButton: View { .lineLimit(1) } } - + func fillColor() -> Color { colorScheme == .light ? DamusColors.black : DamusColors.white } - + func borderColor() -> Color { colorScheme == .light ? DamusColors.black : DamusColors.white } @@ -104,11 +104,11 @@ struct EditButton: View { struct VisualEffectView: UIViewRepresentable { var effect: UIVisualEffect? - + func makeUIView(context: UIViewRepresentableContext) -> UIVisualEffectView { UIVisualEffectView() } - + func updateUIView(_ uiView: UIVisualEffectView, context: UIViewRepresentableContext) { uiView.effect = effect } @@ -120,52 +120,52 @@ struct ProfileView: View { let bannerHeight: CGFloat = 150.0 static let markdown = Markdown() - + @State var is_zoomed: Bool = false @State var show_share_sheet: Bool = false @State var show_qr_code: Bool = false @State var action_sheet_presented: Bool = false @State var filter_state : FilterState = .posts @State var yOffset: CGFloat = 0 - + @StateObject var profile: ProfileModel @StateObject var followers: FollowersModel @StateObject var zap_button_model: ZapButtonModel = ZapButtonModel() - + init(damus_state: DamusState, profile: ProfileModel, followers: FollowersModel) { self.damus_state = damus_state self._profile = StateObject(wrappedValue: profile) self._followers = StateObject(wrappedValue: followers) } - + init(damus_state: DamusState, pubkey: String) { self.damus_state = damus_state self._profile = StateObject(wrappedValue: ProfileModel(pubkey: pubkey, damus: damus_state)) self._followers = StateObject(wrappedValue: FollowersModel(damus_state: damus_state, target: pubkey)) } - + @Environment(\.dismiss) var dismiss @Environment(\.colorScheme) var colorScheme @Environment(\.presentationMode) var presentationMode - + func imageBorderColor() -> Color { colorScheme == .light ? DamusColors.white : DamusColors.black } - + func bannerBlurViewOpacity() -> Double { let progress = -(yOffset + navbarHeight) / 100 return Double(-yOffset > navbarHeight ? progress : 0) } - + var bannerSection: some View { GeometryReader { proxy -> AnyView in - + let minY = proxy.frame(in: .global).minY - + DispatchQueue.main.async { self.yOffset = minY } - + return AnyView( VStack(spacing: 0) { ZStack { @@ -173,10 +173,10 @@ struct ProfileView: View { .aspectRatio(contentMode: .fill) .frame(width: proxy.size.width, height: minY > 0 ? bannerHeight + minY : bannerHeight) .clipped() - + VisualEffectView(effect: UIBlurEffect(style: .systemUltraThinMaterial)).opacity(bannerBlurViewOpacity()) } - + Divider().opacity(bannerBlurViewOpacity()) } .frame(height: minY > 0 ? bannerHeight + minY : nil) @@ -187,11 +187,11 @@ struct ProfileView: View { .frame(height: bannerHeight) .allowsHitTesting(false) } - + var navbarHeight: CGFloat { return 100.0 - (Theme.safeAreaInsets?.top ?? 0) } - + @ViewBuilder func navImage(img: String) -> some View { Image(img) @@ -199,7 +199,7 @@ struct ProfileView: View { .background(Color.black.opacity(0.6)) .clipShape(Circle()) } - + var navBackButton: some View { Button { presentationMode.wrappedValue.dismiss() @@ -207,7 +207,7 @@ struct ProfileView: View { navImage(img: "chevron-left") } } - + var navActionSheetButton: some View { Button(action: { action_sheet_presented = true @@ -218,7 +218,7 @@ struct ProfileView: View { Button(NSLocalizedString("Share", comment: "Button to share the link to a profile.")) { show_share_sheet = true } - + Button(NSLocalizedString("QR Code", comment: "Button to view profile's qr code.")) { show_qr_code = true } @@ -238,7 +238,7 @@ struct ProfileView: View { else { return } - + guard let new_ev = remove_from_mutelist(keypair: keypair, prev: mutelist, to_remove: profile.pubkey) else { return } @@ -254,7 +254,7 @@ struct ProfileView: View { } } } - + var customNavbar: some View { HStack { navBackButton @@ -265,7 +265,7 @@ struct ProfileView: View { .padding(.horizontal) .accentColor(DamusColors.white) } - + func lnButton(lnurl: String, profile: Profile) -> some View { let button_img = profile.reactions == false ? "zap.fill" : "zap" return Button(action: { @@ -278,7 +278,7 @@ struct ProfileView: View { if profile.reactions == false { Text("OnlyZaps Enabled", comment: "Non-tappable text in context menu that shows up when the zap button on profile is long pressed to indicate that the user has enabled OnlyZaps, meaning that they would like to be only zapped and not accept reactions to their notes.") } - + if let addr = profile.lud16 { Button { UIPasteboard.general.string = addr @@ -293,30 +293,30 @@ struct ProfileView: View { } } } - + } .cornerRadius(24) } var dmButton: some View { let dm_model = damus_state.dms.lookup_or_create(profile.pubkey) - return NavigationLink(value: Route.DMChat(damusState: damus_state, dms: dm_model)) { + return NavigationLink(value: Route.DMChat(dms: dm_model)) { Image("messages") .profile_button_style(scheme: colorScheme) } } - + func actionSection(profile_data: Profile?) -> some View { return Group { - + if let profile = profile_data { if let lnurl = profile.lnurl, lnurl != "" { lnButton(lnurl: lnurl, profile: profile) } } - + dmButton - + if profile.pubkey != damus_state.pubkey { FollowButtonView( target: profile.get_follow_target(), @@ -324,26 +324,26 @@ struct ProfileView: View { follow_state: damus_state.contacts.follow_state(profile.pubkey) ) } else if damus_state.keypair.privkey != nil { - NavigationLink(value: Route.EditMetadata(damusState: damus_state)) { + NavigationLink(value: Route.EditMetadata) { EditButton(damus_state: damus_state) } } - + } } - + func pfpOffset() -> CGFloat { let progress = -yOffset / navbarHeight let offset = (pfp_size / 4.0) * (progress < 1.0 ? progress : 1) return offset > 0 ? offset : 0 } - + func pfpScale() -> CGFloat { let progress = -yOffset / navbarHeight let scale = 1.0 - (0.5 * (progress < 1.0 ? progress : 1)) return scale < 1 ? scale : 1 } - + func nameSection(profile_data: Profile?) -> some View { return Group { HStack(alignment: .center) { @@ -357,17 +357,17 @@ struct ProfileView: View { .fullScreenCover(isPresented: $is_zoomed) { ProfilePicImageView(pubkey: profile.pubkey, profiles: damus_state.profiles, disable_animation: damus_state.settings.disable_animation) } - + Spacer() - + actionSection(profile_data: profile_data) } - + let follows_you = profile.pubkey != damus_state.pubkey && profile.follows(pubkey: damus_state.pubkey) ProfileNameView(pubkey: profile.pubkey, profile: profile_data, follows_you: follows_you, damus: damus_state) } } - + var followersCount: some View { HStack { if followers.count == nil { @@ -384,26 +384,26 @@ struct ProfileView: View { } } } - + var aboutSection: some View { VStack(alignment: .leading, spacing: 8.0) { let profile_data = damus_state.profiles.lookup(id: profile.pubkey) - + nameSection(profile_data: profile_data) if let about = profile_data?.about { AboutView(state: damus_state, about: about) } - + if let url = profile_data?.website_url { WebsiteLink(url: url) } - + HStack { if let contact = profile.contacts { let contacts = contact.referenced_pubkeys.map { $0.ref_id } let following_model = FollowingModel(damus_state: damus_state, contacts: contacts) - NavigationLink(value: Route.Following(damusState: damus_state, following: following_model)) { + NavigationLink(value: Route.Following(following: following_model)) { HStack { let noun_text = Text(verbatim: "\(followingCountString(profile.following))").font(.subheadline).foregroundColor(.gray) Text("\(Text(verbatim: profile.following.formatted()).font(.subheadline.weight(.medium))) \(noun_text)", comment: "Sentence composed of 2 variables to describe how many profiles a user is following. In source English, the first variable is the number of profiles being followed, and the second variable is 'Following'.") @@ -413,7 +413,7 @@ struct ProfileView: View { } if followers.contacts != nil { - NavigationLink(value: Route.Followers(damusState: damus_state, environmentObject: followers)) { + NavigationLink(value: Route.Followers(environmentObject: followers)) { followersCount } .buttonStyle(PlainButtonStyle()) @@ -425,18 +425,18 @@ struct ProfileView: View { followers.subscribe() } } - + if let relays = profile.relays { // Only open relay config view if the user is logged in with private key and they are looking at their own profile. let noun_text = Text(verbatim: relaysCountString(relays.keys.count)).font(.subheadline).foregroundColor(.gray) let relay_text = Text("\(Text(verbatim: relays.keys.count.formatted()).font(.subheadline.weight(.medium))) \(noun_text)", comment: "Sentence composed of 2 variables to describe how many relay servers a user is connected. In source English, the first variable is the number of relay servers, and the second variable is 'Relay' or 'Relays'.") if profile.pubkey == damus_state.pubkey && damus_state.is_privkey_user { - NavigationLink(value: Route.RelayConfig(damusState: damus_state)) { + NavigationLink(value: Route.RelayConfig) { relay_text } .buttonStyle(PlainButtonStyle()) } else { - NavigationLink(value: Route.UserRelays(damusState: damus_state, relays: Array(relays.keys).sorted())) { + NavigationLink(value: Route.UserRelays(relays: Array(relays.keys).sorted())) { relay_text } .buttonStyle(PlainButtonStyle()) @@ -462,7 +462,7 @@ struct ProfileView: View { } .padding(.horizontal) } - + var body: some View { ZStack { ScrollView(.vertical) { @@ -540,7 +540,7 @@ struct ProfileView_Previews: PreviewProvider { func test_damus_state() -> DamusState { let pubkey = "3efdaebb1d8923ebd99c9e7ace3b4194ab45512e2be79c1b7d68d9243e0d2681" let damus = DamusState.empty - + let prof = Profile(name: "damus", display_name: "damus", about: "iOS app!", picture: "https://damus.io/img/logo.png", banner: "", website: "https://damus.io", lud06: nil, lud16: "jb55@sendsats.lol", nip05: "damus.io", damus_donation: nil) let tsprof = TimestampedProfile(profile: prof, timestamp: 0, event: test_event) damus.profiles.add(id: pubkey, profile: tsprof) @@ -549,15 +549,15 @@ func test_damus_state() -> DamusState { struct KeyView: View { let pubkey: String - + @Environment(\.colorScheme) var colorScheme - + @State private var isCopied = false - + func keyColor() -> Color { colorScheme == .light ? DamusColors.black : DamusColors.white } - + private func copyPubkey(_ pubkey: String) { UIPasteboard.general.string = pubkey UIImpactFeedbackGenerator(style: .medium).impactOccurred() @@ -570,10 +570,10 @@ struct KeyView: View { } } } - + var body: some View { let bech32 = bech32_pubkey(pubkey) ?? pubkey - + HStack { Text(verbatim: "\(abbrev_pubkey(bech32, amount: 16))") .font(.footnote) @@ -581,7 +581,7 @@ struct KeyView: View { .padding(5) .padding([.leading, .trailing], 5) .background(RoundedRectangle(cornerRadius: 11).foregroundColor(DamusColors.adaptableGrey)) - + if isCopied != true { Button { copyPubkey(bech32) diff --git a/damus/Views/Relays/RelayDetailView.swift b/damus/Views/Relays/RelayDetailView.swift index ef5a65c4..ec71da7a 100644 --- a/damus/Views/Relays/RelayDetailView.swift +++ b/damus/Views/Relays/RelayDetailView.swift @@ -72,7 +72,9 @@ struct RelayDetailView: View { if let pubkey = nip11.pubkey { Section(NSLocalizedString("Admin", comment: "Label to display relay contact user.")) { - UserViewRow(damus_state: state, pubkey: pubkey) + NavigationLink(value: Route.ProfileByKey(pubkey: pubkey), label: { + UserViewRow(damus_state: state, pubkey: pubkey) + }) } } if let relay_connection { diff --git a/damus/Views/Relays/RelayView.swift b/damus/Views/Relays/RelayView.swift index c199cdf4..85c855c2 100644 --- a/damus/Views/Relays/RelayView.swift +++ b/damus/Views/Relays/RelayView.swift @@ -30,8 +30,9 @@ struct RelayView: View { if let meta = state.relay_metadata.lookup(relay_id: relay) { Text(relay) .background( - NavigationLink("", destination: RelayDetailView(state: state, relay: relay, nip11: meta)).opacity(0.0) - .disabled(showActionButtons) + NavigationLink(value: Route.RelayDetail(relay: relay, metadata: meta), label: { + EmptyView() + }).opacity(0.0).disabled(showActionButtons) ) Spacer() diff --git a/damus/Views/Relays/SignalView.swift b/damus/Views/Relays/SignalView.swift index 64890f54..b0f1c67b 100644 --- a/damus/Views/Relays/SignalView.swift +++ b/damus/Views/Relays/SignalView.swift @@ -14,7 +14,7 @@ struct SignalView: View { var body: some View { Group { if signal.signal != signal.max_signal { - NavigationLink(destination: RelayConfigView(state: state)) { + NavigationLink(value: Route.RelayConfig) { Text("\(signal.signal)/\(signal.max_signal)", comment: "Fraction of how many of the user's relay servers that are operational.") .font(.callout) .foregroundColor(.gray) diff --git a/damus/Views/Reposts/RepostedEvent.swift b/damus/Views/Reposts/RepostedEvent.swift index 0e32dd93..465a0622 100644 --- a/damus/Views/Reposts/RepostedEvent.swift +++ b/damus/Views/Reposts/RepostedEvent.swift @@ -16,9 +16,8 @@ struct RepostedEvent: View { var body: some View { VStack(alignment: .leading) { let prof = damus.profiles.lookup(id: event.pubkey) - let booster_profile = ProfileView(damus_state: damus, pubkey: event.pubkey) - NavigationLink(destination: booster_profile) { + NavigationLink(value: Route.ProfileByKey(pubkey: event.pubkey)) { Reposted(damus: damus, pubkey: event.pubkey, profile: prof) .padding(.horizontal) } diff --git a/damus/Views/Search/SearchingEventView.swift b/damus/Views/Search/SearchingEventView.swift index 9741cbf7..6093f73d 100644 --- a/damus/Views/Search/SearchingEventView.swift +++ b/damus/Views/Search/SearchingEventView.swift @@ -100,14 +100,12 @@ struct SearchingEventView: View { .progressViewStyle(.circular) } case .found(let ev): - NavigationLink(destination: ThreadView(state: state, thread: ThreadModel(event: ev, damus_state: state))) { - + NavigationLink(value: Route.Thread(thread: ThreadModel(event: ev, damus_state: state))) { EventView(damus: state, event: ev) } .buttonStyle(PlainButtonStyle()) case .found_profile(let pk): - NavigationLink(destination: ProfileView(damus_state: state, pubkey: pk)) { - + NavigationLink(value: Route.ProfileByKey(pubkey: pk)) { FollowUserView(target: .pubkey(pk), damus_state: state) } .buttonStyle(PlainButtonStyle()) diff --git a/damus/Views/SearchResultsView.swift b/damus/Views/SearchResultsView.swift index bc18ce39..545cbec2 100644 --- a/damus/Views/SearchResultsView.swift +++ b/damus/Views/SearchResultsView.swift @@ -44,8 +44,7 @@ struct InnerSearchResults: View { func HashtagSearch(_ ht: String) -> some View { let search_model = SearchModel(state: damus_state, search: .filter_hashtag([ht])) - let dst = SearchView(appstate: damus_state, search: search_model) - return NavigationLink(destination: dst) { + return NavigationLink(value: Route.Search(search: search_model)) { Text("Search hashtag: #\(ht)", comment: "Navigation link to search hashtag.") } } diff --git a/damus/Views/SideMenuView.swift b/damus/Views/SideMenuView.swift index cb83f94a..17486e14 100644 --- a/damus/Views/SideMenuView.swift +++ b/damus/Views/SideMenuView.swift @@ -11,23 +11,23 @@ struct SideMenuView: View { let damus_state: DamusState @Binding var isSidebarVisible: Bool @State var confirm_logout: Bool = false - + @State private var showQRCode = false - + @Environment(\.colorScheme) var colorScheme - + var sideBarWidth = min(UIScreen.main.bounds.size.width * 0.65, 400.0) let verticalSpacing: CGFloat = 20 let padding: CGFloat = 30 - + func fillColor() -> Color { colorScheme == .light ? DamusColors.white : DamusColors.black } - + func textColor() -> Color { colorScheme == .light ? DamusColors.black : DamusColors.white } - + var body: some View { ZStack { GeometryReader { _ in @@ -42,20 +42,20 @@ struct SideMenuView: View { content } } - + func SidemenuItems(profile_model: ProfileModel, followers: FollowersModel) -> some View { return VStack(spacing: verticalSpacing) { - NavigationLink(value: Route.Profile(damusSate: damus_state, profile: profile_model, followers: followers)) { + NavigationLink(value: Route.Profile(profile: profile_model, followers: followers)) { navLabel(title: NSLocalizedString("Profile", comment: "Sidebar menu label for Profile view."), img: "user") } - + NavigationLink(destination: WalletView(damus_state: damus_state, model: damus_state.wallet)) { navLabel(title: NSLocalizedString("Wallet", comment: "Sidebar menu label for Wallet view."), img: "wallet") /* HStack { Image("wallet") .tint(DamusColors.adaptableBlack) - + Text(NSLocalizedString("wallet", comment: "Sidebar menu label for Wallet view.")) .font(.title2) .foregroundColor(textColor()) @@ -63,36 +63,35 @@ struct SideMenuView: View { .dynamicTypeSize(.xSmall) }*/ } - - NavigationLink(value: Route.MuteList(damusState: damus_state, users: get_mutelist_users(damus_state.contacts.mutelist))) { + + NavigationLink(value: Route.MuteList(users: get_mutelist_users(damus_state.contacts.mutelist))) { navLabel(title: NSLocalizedString("Muted", comment: "Sidebar menu label for muted users view."), img: "mute") } - - NavigationLink(value: Route.RelayConfig(damusState: damus_state)) { + + NavigationLink(value: Route.RelayConfig) { navLabel(title: NSLocalizedString("Relays", comment: "Sidebar menu label for Relays view."), img: "world-relays") } - - NavigationLink(value: Route.Bookmarks(damusState: damus_state)) { + + NavigationLink(value: Route.Bookmarks) { navLabel(title: NSLocalizedString("Bookmarks", comment: "Sidebar menu label for Bookmarks view."), img: "bookmark") } - - NavigationLink(value: Route.Config(damusState: damus_state)) { + + NavigationLink(value: Route.Config) { navLabel(title: NSLocalizedString("Settings", comment: "Sidebar menu label for accessing the app settings"), img: "settings") } } } - + var MainSidemenu: some View { VStack(alignment: .leading, spacing: 0) { let profile = damus_state.profiles.lookup(id: damus_state.pubkey) let followers = FollowersModel(damus_state: damus_state, target: damus_state.pubkey) let profile_model = ProfileModel(pubkey: damus_state.pubkey, damus: damus_state) - - NavigationLink(destination: ProfileView(damus_state: damus_state, profile: profile_model, followers: followers)) { - + + NavigationLink(value: Route.Profile(profile: profile_model, followers: followers), label: { HStack { ProfilePicView(pubkey: damus_state.pubkey, size: 60, highlight: .none, profiles: damus_state.profiles, disable_animation: damus_state.settings.disable_animation) - + VStack(alignment: .leading) { if let display_name = profile?.display_name { Text(display_name) @@ -109,10 +108,10 @@ struct SideMenuView: View { } } .padding(.bottom, verticalSpacing) - } - + }) + Divider() - + ScrollView { SidemenuItems(profile_model: profile_model, followers: followers) .labelStyle(SideMenuLabelStyle()) @@ -120,7 +119,7 @@ struct SideMenuView: View { } } } - + var content: some View { HStack(alignment: .top) { ZStack(alignment: .top) { @@ -186,20 +185,20 @@ struct SideMenuView: View { Spacer() } } - - + + @ViewBuilder func navLabel(title: String, img: String) -> some View { Image(img) .tint(DamusColors.adaptableBlack) - + Text(title) .font(.title2) .foregroundColor(textColor()) .frame(maxWidth: .infinity, alignment: .leading) .dynamicTypeSize(.xSmall) } - + struct SideMenuLabelStyle: LabelStyle { func makeBody(configuration: Configuration) -> some View { HStack(alignment: .center, spacing: 8) { From c50ccef56df8af8c6a46868f2d0cc8f266763e7f Mon Sep 17 00:00:00 2001 From: Scott Penrose Date: Sat, 3 Jun 2023 13:22:43 -0400 Subject: [PATCH 3/9] Convert onboarding flow navigation links --- damus/Util/Router.swift | 37 +++++++++++++++++++++++++++++ damus/Views/CreateAccountView.swift | 9 ++----- damus/Views/EULAView.swift | 10 ++------ damus/Views/LoginView.swift | 21 ++++++---------- damus/Views/SetupView.swift | 13 +++++----- 5 files changed, 54 insertions(+), 36 deletions(-) diff --git a/damus/Util/Router.swift b/damus/Util/Router.swift index 85433b86..c6067408 100644 --- a/damus/Util/Router.swift +++ b/damus/Util/Router.swift @@ -31,6 +31,10 @@ enum Route: Hashable { case Reactions(reactions: ReactionsModel) case Zaps(target: ZapTarget) case Search(search: SearchModel) + case EULA + case Login + case CreateAccount + case SaveKeys(account: CreateAccountModel) @ViewBuilder func view(navigationCordinator: NavigationCoordinator, damusState: DamusState) -> some View { @@ -82,6 +86,18 @@ enum Route: Hashable { ZapsView(state: damusState, target: target) case .Search(let search): SearchView(appstate: damusState, search: search) + case .EULA: + EULAView() + .environmentObject(navigationCordinator) + case .Login: + LoginView() + .environmentObject(navigationCordinator) + case .CreateAccount: + CreateAccountView() + .environmentObject(navigationCordinator) + case .SaveKeys(let account): + SaveKeysView(account: account) + .environmentObject(navigationCordinator) } } @@ -133,6 +149,14 @@ enum Route: Hashable { return lhs_target == rhs_target case (.Search(let lhs_search), .Search(let rhs_search)): return lhs_search.sub_id == rhs_search.sub_id && lhs_search.profiles_subid == rhs_search.profiles_subid + case (.EULA, .EULA): + return true + case (.Login, .Login): + return true + case (.CreateAccount, .CreateAccount): + return true + case (.SaveKeys(let lhs_account), .SaveKeys(let rhs_account)): + return lhs_account.pubkey == rhs_account.pubkey default: return false } @@ -201,6 +225,15 @@ enum Route: Hashable { hasher.combine("search") hasher.combine(search.sub_id) hasher.combine(search.profiles_subid) + case .EULA: + hasher.combine("eula") + case .Login: + hasher.combine("login") + case .CreateAccount: + hasher.combine("createAccount") + case .SaveKeys(let account): + hasher.combine("saveKeys") + hasher.combine(account.pubkey) } } } @@ -208,6 +241,10 @@ enum Route: Hashable { class NavigationCoordinator: ObservableObject { @Published var path = [Route]() + func push(route: Route) { + path.append(route) + } + func popToRoot() { path = [] } diff --git a/damus/Views/CreateAccountView.swift b/damus/Views/CreateAccountView.swift index 3bfbaf2e..04b64dfa 100644 --- a/damus/Views/CreateAccountView.swift +++ b/damus/Views/CreateAccountView.swift @@ -10,8 +10,7 @@ import SwiftUI struct CreateAccountView: View { @StateObject var account: CreateAccountModel = CreateAccountModel() @StateObject var profileUploadViewModel = ProfileUploadingViewModel() - - @State var is_done: Bool = false + @EnvironmentObject var navigationCoordinator: NavigationCoordinator func SignupForm(@ViewBuilder content: () -> FormContent) -> some View { return VStack(alignment: .leading, spacing: 10.0, content: content) @@ -25,10 +24,6 @@ struct CreateAccountView: View { var body: some View { ZStack(alignment: .top) { - NavigationLink(destination: SaveKeysView(account: account), isActive: $is_done) { - EmptyView() - } - VStack { VStack(alignment: .center) { ProfilePictureSelector(pubkey: account.pubkey, viewModel: profileUploadViewModel, callback: uploadedProfilePicture(image_url:)) @@ -63,7 +58,7 @@ struct CreateAccountView: View { .padding(.top, 10) Button(action: { - self.is_done = true + navigationCoordinator.push(route: Route.SaveKeys(account: account)) }) { HStack { Text("Create account now", comment: "Button to create account.") diff --git a/damus/Views/EULAView.swift b/damus/Views/EULAView.swift index f4d4b26d..b61c4998 100644 --- a/damus/Views/EULAView.swift +++ b/damus/Views/EULAView.swift @@ -56,18 +56,13 @@ By using our Application, you signify your acceptance of this EULA. If you do no """ struct EULAView: View { - @State private var login = false - @State var accepted = false @Environment(\.colorScheme) var colorScheme @Environment(\.dismiss) var dismiss + @EnvironmentObject var navigationCoordinator: NavigationCoordinator var body: some View { ZStack { ScrollView { - NavigationLink(destination: LoginView(accepted: $accepted), isActive: $login) { - EmptyView() - } - Text(Markdown.parse(content: eula)) .padding() } @@ -96,8 +91,7 @@ struct EULAView: View { } Button(action: { - accepted = true - login.toggle() + navigationCoordinator.push(route: Route.Login) }) { HStack { Text("Accept", comment: "Button to accept the end user license agreement before being allowed into the app.") diff --git a/damus/Views/LoginView.swift b/damus/Views/LoginView.swift index 9ebdb6da..8b8032cb 100644 --- a/damus/Views/LoginView.swift +++ b/damus/Views/LoginView.swift @@ -33,13 +33,11 @@ enum ParsedKey { } struct LoginView: View { - @State private var create_account = false @State var key: String = "" @State var is_pubkey: Bool = false @State var error: String? = nil @State private var credential_handler = CredentialHandler() - - @Binding var accepted: Bool + @EnvironmentObject var navigationCoordinator: NavigationCoordinator func get_error(parsed_key: ParsedKey?) -> String? { if self.error != nil { @@ -55,12 +53,6 @@ struct LoginView: View { var body: some View { ZStack(alignment: .top) { - if accepted { - NavigationLink(destination: CreateAccountView(), isActive: $create_account) { - EmptyView() - } - } - VStack { SignInHeader() .padding(.top, 100) @@ -107,7 +99,8 @@ struct LoginView: View { .padding(.top, 10) } - CreateAccountPrompt(create_account: $create_account) + CreateAccountPrompt() + .environmentObject(navigationCoordinator) .padding(.top, 10) Spacer() @@ -337,14 +330,14 @@ struct SignInEntry: View { } struct CreateAccountPrompt: View { - @Binding var create_account: Bool + @EnvironmentObject var navigationCoordinator: NavigationCoordinator var body: some View { HStack { Text("New to Nostr?", comment: "Ask the user if they are new to Nostr") .foregroundColor(Color("DamusMediumGrey")) Button(NSLocalizedString("Create account", comment: "Button to navigate to create account view.")) { - create_account.toggle() + navigationCoordinator.push(route: Route.CreateAccount) } Spacer() @@ -358,8 +351,8 @@ struct LoginView_Previews: PreviewProvider { let pubkey = "npub18m76awca3y37hkvuneavuw6pjj4525fw90necxmadrvjg0sdy6qsngq955" let bech32_pubkey = "KeyInput" Group { - LoginView(key: pubkey, accepted: .constant(true)) - LoginView(key: bech32_pubkey, accepted: .constant(true)) + LoginView(key: pubkey) + LoginView(key: bech32_pubkey) } } } diff --git a/damus/Views/SetupView.swift b/damus/Views/SetupView.swift index c5f12b01..75eed8fd 100644 --- a/damus/Views/SetupView.swift +++ b/damus/Views/SetupView.swift @@ -17,16 +17,12 @@ func hex_col(r: UInt8, g: UInt8, b: UInt8) -> Color { struct SetupView: View { - @State private var eula = false + @StateObject var navigationCoordinator: NavigationCoordinator = NavigationCoordinator() var body: some View { - NavigationView { + NavigationStack(path: $navigationCoordinator.path) { ZStack { VStack(alignment: .center) { - NavigationLink(destination: EULAView(), isActive: $eula) { - EmptyView() - } - Spacer() Image("logo-nobg") @@ -53,7 +49,7 @@ struct SetupView: View { Spacer() Button(action: { - eula.toggle() + navigationCoordinator.push(route: Route.EULA) }) { HStack { Text("Let's get started!", comment: "Button to continue to login page.") @@ -72,6 +68,9 @@ struct SetupView: View { .ignoresSafeArea(), alignment: .top ) + .navigationDestination(for: Route.self) { route in + route.view(navigationCordinator: navigationCoordinator, damusState: DamusState.empty) + } } .navigationBarTitleDisplayMode(.inline) .navigationViewStyle(StackNavigationViewStyle()) From f361f55bd57a63b1835c136a776932ae1c5935bd Mon Sep 17 00:00:00 2001 From: Scott Penrose Date: Sat, 3 Jun 2023 13:47:50 -0400 Subject: [PATCH 4/9] Convert wallet NavigationLinks --- damus/Util/Router.swift | 15 +++++++++++++++ damus/Views/SideMenuView.swift | 2 +- damus/Views/Wallet/ConnectWalletView.swift | 9 +++------ damus/Views/Wallet/WalletView.swift | 3 +++ 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/damus/Util/Router.swift b/damus/Util/Router.swift index c6067408..4303ff9b 100644 --- a/damus/Util/Router.swift +++ b/damus/Util/Router.swift @@ -35,6 +35,8 @@ enum Route: Hashable { case Login case CreateAccount case SaveKeys(account: CreateAccountModel) + case Wallet(wallet: WalletModel) + case WalletScanner(result: Binding) @ViewBuilder func view(navigationCordinator: NavigationCoordinator, damusState: DamusState) -> some View { @@ -98,6 +100,11 @@ enum Route: Hashable { case .SaveKeys(let account): SaveKeysView(account: account) .environmentObject(navigationCordinator) + case .Wallet(let walletModel): + WalletView(damus_state: damusState, model: walletModel) + .environmentObject(navigationCordinator) + case .WalletScanner(let walletScanResult): + WalletScannerView(result: walletScanResult) } } @@ -157,6 +164,10 @@ enum Route: Hashable { return true case (.SaveKeys(let lhs_account), .SaveKeys(let rhs_account)): return lhs_account.pubkey == rhs_account.pubkey + case (.Wallet(_), .Wallet(_)): + return true + case (.WalletScanner(_), .WalletScanner(_)): + return true default: return false } @@ -234,6 +245,10 @@ enum Route: Hashable { case .SaveKeys(let account): hasher.combine("saveKeys") hasher.combine(account.pubkey) + case .Wallet(_): + hasher.combine("wallet") + case .WalletScanner(_): + hasher.combine("walletScanner") } } } diff --git a/damus/Views/SideMenuView.swift b/damus/Views/SideMenuView.swift index 17486e14..1a8c8331 100644 --- a/damus/Views/SideMenuView.swift +++ b/damus/Views/SideMenuView.swift @@ -49,7 +49,7 @@ struct SideMenuView: View { navLabel(title: NSLocalizedString("Profile", comment: "Sidebar menu label for Profile view."), img: "user") } - NavigationLink(destination: WalletView(damus_state: damus_state, model: damus_state.wallet)) { + NavigationLink(value: Route.Wallet(wallet: damus_state.wallet)) { navLabel(title: NSLocalizedString("Wallet", comment: "Sidebar menu label for Wallet view."), img: "wallet") /* HStack { diff --git a/damus/Views/Wallet/ConnectWalletView.swift b/damus/Views/Wallet/ConnectWalletView.swift index a926935b..b91f08bb 100644 --- a/damus/Views/Wallet/ConnectWalletView.swift +++ b/damus/Views/Wallet/ConnectWalletView.swift @@ -9,6 +9,7 @@ import SwiftUI struct ConnectWalletView: View { @Environment(\.openURL) private var openURL + @EnvironmentObject var navigationCoordinator: NavigationCoordinator @ObservedObject var model: WalletModel @State var scanning: Bool = false @@ -63,17 +64,13 @@ struct ConnectWalletView: View { } var ConnectWallet: some View { - VStack { - NavigationLink(destination: WalletScannerView(result: $wallet_scan_result), isActive: $scanning) { - EmptyView() - } - + VStack { AlbyButton() { openURL(URL(string:"https://nwc.getalby.com/apps/new?c=Damus")!) } BigButton(NSLocalizedString("Attach Wallet", comment: "Text for button to attach Nostr Wallet Connect lightning wallet.")) { - scanning = true + navigationCoordinator.push(route: Route.WalletScanner(result: $wallet_scan_result)) } if let err = self.error { diff --git a/damus/Views/Wallet/WalletView.swift b/damus/Views/Wallet/WalletView.swift index 8208e2e5..f373d959 100644 --- a/damus/Views/Wallet/WalletView.swift +++ b/damus/Views/Wallet/WalletView.swift @@ -11,6 +11,7 @@ struct WalletView: View { let damus_state: DamusState @ObservedObject var model: WalletModel @ObservedObject var settings: UserSettingsStore + @EnvironmentObject var navigationCoordinator: NavigationCoordinator init(damus_state: DamusState, model: WalletModel? = nil) { self.damus_state = damus_state @@ -156,8 +157,10 @@ struct WalletView: View { switch model.connect_state { case .new: ConnectWalletView(model: model) + .environmentObject(navigationCoordinator) case .none: ConnectWalletView(model: model) + .environmentObject(navigationCoordinator) case .existing(let nwc): MainWalletView(nwc: nwc) .onAppear() { From 8258c5beb082d9ae23d527aeeb9a47d19dbaf8fe Mon Sep 17 00:00:00 2001 From: Scott Penrose Date: Sat, 3 Jun 2023 15:37:46 -0400 Subject: [PATCH 5/9] Convert ContentView navigation links --- damus/ContentView.swift | 68 ++++++++--------------------------------- 1 file changed, 12 insertions(+), 56 deletions(-) diff --git a/damus/ContentView.swift b/damus/ContentView.swift index b4618bbe..6771a9bf 100644 --- a/damus/ContentView.swift +++ b/damus/ContentView.swift @@ -82,14 +82,6 @@ struct ContentView: View { @State var damus_state: DamusState? = nil @SceneStorage("ContentView.selected_timeline") var selected_timeline: Timeline = .home @State var is_deleted_account: Bool = false - @State var active_profile: String? = nil - @State var active_search: NostrFilter? = nil - @State var active_event: NostrEvent? = nil - @State var profile_open: Bool = false - @State var thread_open: Bool = false - @State var search_open: Bool = false - @State var wallet_open: Bool = false - @State var active_nwc: WalletConnectURL? = nil @State var muting: String? = nil @State var confirm_mute: Bool = false @State var user_muted_confirm: Bool = false @@ -156,10 +148,7 @@ struct ContentView: View { } func popToRoot() { - profile_open = false - thread_open = false - search_open = false - wallet_open = false + navigationCoordinator.popToRoot() isSideBarOpened = false } @@ -170,21 +159,6 @@ struct ContentView: View { func MainContent(damus: DamusState) -> some View { VStack { - NavigationLink(destination: WalletView(damus_state: damus, model: damus_state!.wallet), isActive: $wallet_open) { - EmptyView() - } - NavigationLink(destination: MaybeProfileView, isActive: $profile_open) { - EmptyView() - } - if let active_event { - let thread = ThreadModel(event: active_event, damus_state: damus_state!) - NavigationLink(destination: ThreadView(state: damus_state!, thread: thread), isActive: $thread_open) { - EmptyView() - } - } - NavigationLink(destination: MaybeSearchView, isActive: $search_open) { - EmptyView() - } switch selected_timeline { case .search: if #available(iOS 16.0, *) { @@ -226,28 +200,6 @@ struct ContentView: View { } } - var MaybeSearchView: some View { - Group { - if let search = self.active_search { - SearchView(appstate: damus_state!, search: SearchModel(state: damus_state!, search: search)) - } else { - EmptyView() - } - } - } - - var MaybeProfileView: some View { - Group { - if let pk = self.active_profile { - let profile_model = ProfileModel(pubkey: pk, damus: damus_state!) - let followers = FollowersModel(damus_state: damus_state!, target: pk) - ProfileView(damus_state: damus_state!, profile: profile_model, followers: followers) - } else { - EmptyView() - } - } - } - func MaybeReportView(target: ReportTarget) -> some View { Group { if let damus_state { @@ -264,25 +216,29 @@ struct ContentView: View { func open_event(ev: NostrEvent) { popToRoot() - self.active_event = ev - self.thread_open = true + + let thread = ThreadModel(event: ev, damus_state: damus_state!) + navigationCoordinator.push(route: Route.Thread(thread: thread)) } func open_wallet(nwc: WalletConnectURL) { self.damus_state!.wallet.new(nwc) - self.wallet_open = true + navigationCoordinator.push(route: Route.Wallet(wallet: damus_state!.wallet)) } func open_profile(id: String) { popToRoot() - self.active_profile = id - self.profile_open = true + + let profile_model = ProfileModel(pubkey: id, damus: damus_state!) + let followers = FollowersModel(damus_state: damus_state!, target: id) + navigationCoordinator.push(route: Route.Profile(profile: profile_model, followers: followers)) } func open_search(filt: NostrFilter) { popToRoot() - self.active_search = filt - self.search_open = true + + let search = SearchModel(state: damus_state!, search: filt) + navigationCoordinator.push(route: Route.Search(search: search)) } var body: some View { From 0018e7ad57077e096ec507cf29a1ecdbba2e6c92 Mon Sep 17 00:00:00 2001 From: Scott Penrose Date: Sat, 3 Jun 2023 20:45:58 -0400 Subject: [PATCH 6/9] Convert remaining navigation links --- damus/ContentView.swift | 9 +++++++-- damus/Models/DirectMessagesModel.swift | 10 ---------- damus/Util/Router.swift | 19 +++++++++++++++---- damus/Views/BookmarksView.swift | 2 ++ damus/Views/ConfigView.swift | 2 +- damus/Views/DirectMessagesView.swift | 9 ++++----- damus/Views/FollowingView.swift | 6 ------ .../Views/Notifications/EventGroupView.swift | 3 +++ .../Notifications/NotificationItemView.swift | 6 ++++++ .../Notifications/NotificationsView.swift | 2 ++ .../Notifications/ProfilePicturesView.swift | 9 ++------- damus/Views/Profile/ProfileView.swift | 2 ++ damus/Views/SearchHomeView.swift | 2 ++ damus/Views/SearchView.swift | 2 ++ damus/Views/Timeline/InnerTimelineView.swift | 16 +++++----------- damus/Views/TimelineView.swift | 2 ++ 16 files changed, 55 insertions(+), 46 deletions(-) diff --git a/damus/ContentView.swift b/damus/ContentView.swift index 6771a9bf..4ea84c57 100644 --- a/damus/ContentView.swift +++ b/damus/ContentView.swift @@ -143,6 +143,7 @@ struct ContentView: View { ZStack { if let damus = self.damus_state { TimelineView(events: home.events, loading: .constant(false), damus: damus, show_friend_icon: false, filter: filter) + .environmentObject(navigationCoordinator) } } } @@ -163,10 +164,12 @@ struct ContentView: View { case .search: if #available(iOS 16.0, *) { SearchHomeView(damus_state: damus_state!, model: SearchHomeModel(damus_state: damus_state!)) + .environmentObject(navigationCoordinator) .scrollDismissesKeyboard(.immediately) } else { // Fallback on earlier versions SearchHomeView(damus_state: damus_state!, model: SearchHomeModel(damus_state: damus_state!)) + .environmentObject(navigationCoordinator) } case .home: @@ -174,9 +177,11 @@ struct ContentView: View { case .notifications: NotificationsView(state: damus, notifications: home.notifications) + .environmentObject(navigationCoordinator) case .dms: DirectMessagesView(damus_state: damus_state!, model: damus_state!.dms, settings: damus_state!.settings) + .environmentObject(navigationCoordinator) } } .navigationBarTitle(timeline_name(selected_timeline), displayMode: .inline) @@ -483,8 +488,8 @@ struct ContentView: View { switch local.type { case .dm: selected_timeline = .dms - damus_state.dms.open_dm_by_pk(target.pubkey) - + damus_state.dms.set_active_dm(target.pubkey) + navigationCoordinator.push(route: Route.DMChat(dms: damus_state.dms.active_model)) case .like: fallthrough case .zap: fallthrough case .mention: fallthrough diff --git a/damus/Models/DirectMessagesModel.swift b/damus/Models/DirectMessagesModel.swift index 89f0d457..7402de67 100644 --- a/damus/Models/DirectMessagesModel.swift +++ b/damus/Models/DirectMessagesModel.swift @@ -30,16 +30,6 @@ class DirectMessagesModel: ObservableObject { self.active_model = model } - func open_dm_by_pk(_ pubkey: String) { - self.set_active_dm(pubkey) - self.open_dm = true - } - - func open_dm_by_model(_ model: DirectMessageModel) { - self.set_active_dm_model(model) - self.open_dm = true - } - func set_active_dm(_ pubkey: String) { for model in self.dms where model.pubkey == pubkey { self.set_active_dm_model(model) diff --git a/damus/Util/Router.swift b/damus/Util/Router.swift index 4303ff9b..857e5787 100644 --- a/damus/Util/Router.swift +++ b/damus/Util/Router.swift @@ -22,10 +22,11 @@ enum Route: Hashable { case DMChat(dms: DirectMessageModel) case UserRelays(relays: [String]) case KeySettings(keypair: Keypair) - case AppearanceSettings(settings: UserSettingsStore) // Observed object.. is this an issue? - case NotificationSettings(settings: UserSettingsStore) // Observed object.. is this an issue? - case ZapSettings(settings: UserSettingsStore) // Observed object.. is this an issue? - case TranslationSettings(settings: UserSettingsStore) // Observed object.. is this an issue? + case AppearanceSettings(settings: UserSettingsStore) + case NotificationSettings(settings: UserSettingsStore) + case ZapSettings(settings: UserSettingsStore) + case TranslationSettings(settings: UserSettingsStore) + case SearchSettings(settings: UserSettingsStore) case Thread(thread: ThreadModel) case Reposts(reposts: RepostsModel) case Reactions(reactions: ReactionsModel) @@ -43,8 +44,10 @@ enum Route: Hashable { switch self { case .ProfileByKey(let pubkey): ProfileView(damus_state: damusState, pubkey: pubkey) + .environmentObject(navigationCordinator) case .Profile(let profile, let followers): ProfileView(damus_state: damusState, profile: profile, followers: followers) + .environmentObject(navigationCordinator) case .Followers(let environmentObject): FollowersView(damus_state: damusState) .environmentObject(environmentObject) @@ -60,6 +63,7 @@ enum Route: Hashable { RelayConfigView(state: damusState) case .Bookmarks: BookmarksView(state: damusState) + .environmentObject(navigationCordinator) case .Config: ConfigView(state: damusState) case .EditMetadata: @@ -78,6 +82,8 @@ enum Route: Hashable { ZapSettingsView(settings: settings) case .TranslationSettings(let settings): NotificationSettingsView(settings: settings) + case .SearchSettings(let settings): + SearchSettingsView(settings: settings) case .Thread(let thread): ThreadView(state: damusState, thread: thread) case .Reposts(let reposts): @@ -88,6 +94,7 @@ enum Route: Hashable { ZapsView(state: damusState, target: target) case .Search(let search): SearchView(appstate: damusState, search: search) + .environmentObject(navigationCordinator) case .EULA: EULAView() .environmentObject(navigationCordinator) @@ -146,6 +153,8 @@ enum Route: Hashable { return true case (.TranslationSettings(_), .TranslationSettings(_)): return true + case (.SearchSettings(_), .SearchSettings(_)): + return true case (.Thread(let lhs_threadModel), .Thread(thread: let rhs_threadModel)): return lhs_threadModel.event.id == rhs_threadModel.event.id case (.Reposts(let lhs_reposts), .Reposts(let rhs_reposts)): @@ -219,6 +228,8 @@ enum Route: Hashable { hasher.combine("zapSettings") case .TranslationSettings(_): hasher.combine("translationSettings") + case .SearchSettings(_): + hasher.combine("searchSettings") case .Thread(let threadModel): hasher.combine("thread") hasher.combine(threadModel.event.id) diff --git a/damus/Views/BookmarksView.swift b/damus/Views/BookmarksView.swift index 5af09b0d..27c4f938 100644 --- a/damus/Views/BookmarksView.swift +++ b/damus/Views/BookmarksView.swift @@ -14,6 +14,7 @@ struct BookmarksView: View { @State private var clearAllAlert: Bool = false @Environment(\.dismiss) var dismiss + @EnvironmentObject var navigationCoordinator: NavigationCoordinator @ObservedObject var manager: BookmarksManager init(state: DamusState) { @@ -38,6 +39,7 @@ struct BookmarksView: View { } else { ScrollView { InnerTimelineView(events: EventHolder(events: bookmarks, incoming: []), damus: state, filter: noneFilter) + .environmentObject(navigationCoordinator) } } diff --git a/damus/Views/ConfigView.swift b/damus/Views/ConfigView.swift index f2e3b348..90fcdf74 100644 --- a/damus/Views/ConfigView.swift +++ b/damus/Views/ConfigView.swift @@ -44,7 +44,7 @@ struct ConfigView: View { IconLabel(NSLocalizedString("Appearance", comment: "Section header for text and appearance settings"), img_name: "eye", color: .red) } - NavigationLink(destination: SearchSettingsView(settings: settings)) { + NavigationLink(value: Route.SearchSettings(settings: settings)) { IconLabel(NSLocalizedString("Search/Universe", comment: "Section header for search/universe settings"), img_name: "magnifyingglass", color: .red) } diff --git a/damus/Views/DirectMessagesView.swift b/damus/Views/DirectMessagesView.swift index ea5fa146..8e12e565 100644 --- a/damus/Views/DirectMessagesView.swift +++ b/damus/Views/DirectMessagesView.swift @@ -18,13 +18,11 @@ struct DirectMessagesView: View { @State var dm_type: DMType = .friend @ObservedObject var model: DirectMessagesModel @ObservedObject var settings: UserSettingsStore + + @EnvironmentObject var navigationCoordinator: NavigationCoordinator func MainContent(requests: Bool) -> some View { ScrollView { - let chat = DMChatView(damus_state: damus_state, dms: model.active_model) - NavigationLink(destination: chat, isActive: $model.open_dm) { - EmptyView() - } LazyVStack(spacing: 0) { if model.dms.isEmpty, !model.loading { EmptyTimelineView() @@ -54,7 +52,8 @@ struct DirectMessagesView: View { if ok, let ev = model.events.last { EventView(damus: damus_state, event: ev, pubkey: model.pubkey, options: options) .onTapGesture { - self.model.open_dm_by_model(model) + self.model.set_active_dm_model(model) + navigationCoordinator.push(route: Route.DMChat(dms: self.model.active_model)) } Divider() diff --git a/damus/Views/FollowingView.swift b/damus/Views/FollowingView.swift index 578974c7..0060d429 100644 --- a/damus/Views/FollowingView.swift +++ b/damus/Views/FollowingView.swift @@ -12,14 +12,8 @@ struct FollowUserView: View { let damus_state: DamusState static let markdown = Markdown() - @State var navigating: Bool = false var body: some View { - let dest = ProfileView(damus_state: damus_state, pubkey: target.pubkey) - NavigationLink(destination: dest, isActive: $navigating) { - EmptyView() - } - HStack { UserViewRow(damus_state: damus_state, pubkey: target.pubkey) diff --git a/damus/Views/Notifications/EventGroupView.swift b/damus/Views/Notifications/EventGroupView.swift index a473381b..2bba92b0 100644 --- a/damus/Views/Notifications/EventGroupView.swift +++ b/damus/Views/Notifications/EventGroupView.swift @@ -192,6 +192,8 @@ struct EventGroupView: View { let state: DamusState let event: NostrEvent? let group: EventGroupType + + @EnvironmentObject var navigationCoordinator: NavigationCoordinator func GroupDescription(_ pubkeys: [String]) -> some View { Text(verbatim: "\(reacting_to_text(profiles: state.profiles, our_pubkey: state.pubkey, group: group, ev: event, pubkeys: pubkeys))") @@ -237,6 +239,7 @@ struct EventGroupView: View { let unique_pubkeys = event_group_unique_pubkeys(profiles: state.profiles, group: group) ProfilePicturesView(state: state, pubkeys: unique_pubkeys) + .environmentObject(navigationCoordinator) if let event { let thread = ThreadModel(event: event, damus_state: state) diff --git a/damus/Views/Notifications/NotificationItemView.swift b/damus/Views/Notifications/NotificationItemView.swift index 7eaa8586..e4e87bd8 100644 --- a/damus/Views/Notifications/NotificationItemView.swift +++ b/damus/Views/Notifications/NotificationItemView.swift @@ -30,6 +30,8 @@ func notification_item_event(events: EventCache, notif: NotificationItem) -> Sho struct NotificationItemView: View { let state: DamusState let item: NotificationItem + + @EnvironmentObject var navigationCoordinator: NavigationCoordinator var show_item: ShowItem { notification_item_event(events: state.events, notif: item) @@ -48,15 +50,19 @@ struct NotificationItemView: View { switch item { case .repost(_, let evgrp): EventGroupView(state: state, event: ev, group: .repost(evgrp)) + .environmentObject(navigationCoordinator) case .event_zap(_, let zapgrp): EventGroupView(state: state, event: ev, group: .zap(zapgrp)) + .environmentObject(navigationCoordinator) case .profile_zap(let grp): EventGroupView(state: state, event: nil, group: .profile_zap(grp)) + .environmentObject(navigationCoordinator) case .reaction(_, let evgrp): EventGroupView(state: state, event: ev, group: .reaction(evgrp)) + .environmentObject(navigationCoordinator) case .reply(let ev): NavigationLink(value: Route.Thread(thread: ThreadModel(event: ev, damus_state: state))) { diff --git a/damus/Views/Notifications/NotificationsView.swift b/damus/Views/Notifications/NotificationsView.swift index 417acfc7..c3f14980 100644 --- a/damus/Views/Notifications/NotificationsView.swift +++ b/damus/Views/Notifications/NotificationsView.swift @@ -89,6 +89,7 @@ struct NotificationsView: View { @SceneStorage("NotificationsView.filter_state") var filter_state: NotificationFilterState = .all @Environment(\.colorScheme) var colorScheme + @EnvironmentObject var navigationCoordinator: NavigationCoordinator var mystery: some View { VStack(spacing: 20) { @@ -173,6 +174,7 @@ struct NotificationsView: View { .frame(height: 5) ForEach(filter.filter(contacts: state.contacts, items: notifications.notifications), id: \.id) { item in NotificationItemView(state: state, item: item) + .environmentObject(navigationCoordinator) } } .background(GeometryReader { proxy -> Color in diff --git a/damus/Views/Notifications/ProfilePicturesView.swift b/damus/Views/Notifications/ProfilePicturesView.swift index 5250d60a..50f3f46e 100644 --- a/damus/Views/Notifications/ProfilePicturesView.swift +++ b/damus/Views/Notifications/ProfilePicturesView.swift @@ -11,19 +11,14 @@ struct ProfilePicturesView: View { let state: DamusState let pubkeys: [String] - @State var nav_target: String? = nil - @State var navigating: Bool = false + @EnvironmentObject var navigationCoordinator: NavigationCoordinator var body: some View { - NavigationLink(destination: ProfileView(damus_state: state, pubkey: nav_target ?? ""), isActive: $navigating) { - EmptyView() - } HStack { ForEach(pubkeys.prefix(8), id: \.self) { pubkey in ProfilePicView(pubkey: pubkey, size: 32.0, highlight: .none, profiles: state.profiles, disable_animation: state.settings.disable_animation) .onTapGesture { - nav_target = pubkey - navigating = true + navigationCoordinator.push(route: Route.ProfileByKey(pubkey: pubkey)) } } } diff --git a/damus/Views/Profile/ProfileView.swift b/damus/Views/Profile/ProfileView.swift index a4a0cfb3..3c64221d 100644 --- a/damus/Views/Profile/ProfileView.swift +++ b/damus/Views/Profile/ProfileView.swift @@ -132,6 +132,8 @@ struct ProfileView: View { @StateObject var followers: FollowersModel @StateObject var zap_button_model: ZapButtonModel = ZapButtonModel() + @EnvironmentObject var navigationCoordinator: NavigationCoordinator + init(damus_state: DamusState, profile: ProfileModel, followers: FollowersModel) { self.damus_state = damus_state self._profile = StateObject(wrappedValue: profile) diff --git a/damus/Views/SearchHomeView.swift b/damus/Views/SearchHomeView.swift index 7b31ed86..3e439ee4 100644 --- a/damus/Views/SearchHomeView.swift +++ b/damus/Views/SearchHomeView.swift @@ -14,6 +14,7 @@ struct SearchHomeView: View { @StateObject var model: SearchHomeModel @State var search: String = "" @FocusState private var isFocused: Bool + @EnvironmentObject var navigationCoordinator: NavigationCoordinator let preferredLanguages = Set(Locale.preferredLanguages.map { localeToLanguage($0) }) @@ -67,6 +68,7 @@ struct SearchHomeView: View { return preferredLanguages.contains(note_lang) } ) + .environmentObject(navigationCoordinator) .refreshable { // Fetch new information by unsubscribing and resubscribing to the relay model.unsubscribe() diff --git a/damus/Views/SearchView.swift b/damus/Views/SearchView.swift index b828cba8..668b2e06 100644 --- a/damus/Views/SearchView.swift +++ b/damus/Views/SearchView.swift @@ -11,9 +11,11 @@ struct SearchView: View { let appstate: DamusState @StateObject var search: SearchModel @Environment(\.dismiss) var dismiss + @EnvironmentObject var navigationCoordinator: NavigationCoordinator var body: some View { TimelineView(events: search.events, loading: $search.loading, damus: appstate, show_friend_icon: true, filter: { _ in true }) + .environmentObject(navigationCoordinator) .navigationBarTitle(describe_search(search.search)) .onReceive(handle_notify(.switched_timeline)) { obj in dismiss() diff --git a/damus/Views/Timeline/InnerTimelineView.swift b/damus/Views/Timeline/InnerTimelineView.swift index 0d33c1a6..b8e90619 100644 --- a/damus/Views/Timeline/InnerTimelineView.swift +++ b/damus/Views/Timeline/InnerTimelineView.swift @@ -12,8 +12,8 @@ struct InnerTimelineView: View { @ObservedObject var events: EventHolder let state: DamusState let filter: (NostrEvent) -> Bool - @State var nav_target: NostrEvent - @State var navigating: Bool = false + + @EnvironmentObject var navigationCoordinator: NavigationCoordinator static var count: Int = 0 @@ -23,8 +23,6 @@ struct InnerTimelineView: View { self.filter = filter print("rendering InnerTimelineView \(InnerTimelineView.count)") InnerTimelineView.count += 1 - // dummy event to avoid MaybeThreadView - self._nav_target = State(initialValue: test_event) } var event_options: EventViewOptions { @@ -36,11 +34,6 @@ struct InnerTimelineView: View { } var body: some View { - let thread = ThreadModel(event: nav_target, damus_state: state) - let dest = ThreadView(state: state, thread: thread) - NavigationLink(destination: dest, isActive: $navigating) { - EmptyView() - } LazyVStack(spacing: 0) { let events = self.events.events if events.isEmpty { @@ -53,8 +46,9 @@ struct InnerTimelineView: View { let ind = tup.1 EventView(damus: state, event: ev, options: event_options) .onTapGesture { - nav_target = ev.get_inner_event(cache: state.events) ?? ev - navigating = true + let event = ev.get_inner_event(cache: state.events) ?? ev + let thread = ThreadModel(event: event, damus_state: state) + navigationCoordinator.push(route: Route.Thread(thread: thread)) } .padding(.top, 7) .onAppear { diff --git a/damus/Views/TimelineView.swift b/damus/Views/TimelineView.swift index 326ae8bc..0ed1f555 100644 --- a/damus/Views/TimelineView.swift +++ b/damus/Views/TimelineView.swift @@ -8,6 +8,7 @@ import SwiftUI struct TimelineView: View { + @EnvironmentObject var navigationCoordinator: NavigationCoordinator @ObservedObject var events: EventHolder @Binding var loading: Bool @@ -27,6 +28,7 @@ struct TimelineView: View { .frame(height: 1) InnerTimelineView(events: events, damus: damus, filter: loading ? { _ in true } : filter) + .environmentObject(navigationCoordinator) .redacted(reason: loading ? .placeholder : []) .shimmer(loading) .disabled(loading) From a76ddea7da5e2efc624d42300514807417af4d9b Mon Sep 17 00:00:00 2001 From: Scott Penrose Date: Wed, 14 Jun 2023 10:28:27 -0400 Subject: [PATCH 7/9] Remove popToRoot when tapping damus:// internal links --- damus/ContentView.swift | 6 ------ 1 file changed, 6 deletions(-) diff --git a/damus/ContentView.swift b/damus/ContentView.swift index 4ea84c57..3d0d43e5 100644 --- a/damus/ContentView.swift +++ b/damus/ContentView.swift @@ -220,8 +220,6 @@ struct ContentView: View { } func open_event(ev: NostrEvent) { - popToRoot() - let thread = ThreadModel(event: ev, damus_state: damus_state!) navigationCoordinator.push(route: Route.Thread(thread: thread)) } @@ -232,16 +230,12 @@ struct ContentView: View { } func open_profile(id: String) { - popToRoot() - let profile_model = ProfileModel(pubkey: id, damus: damus_state!) let followers = FollowersModel(damus_state: damus_state!, target: id) navigationCoordinator.push(route: Route.Profile(profile: profile_model, followers: followers)) } func open_search(filt: NostrFilter) { - popToRoot() - let search = SearchModel(state: damus_state!, search: filt) navigationCoordinator.push(route: Route.Search(search: search)) } From 58a707685c65cea0759fbc325fa94f7db0f8916b Mon Sep 17 00:00:00 2001 From: Scott Penrose Date: Wed, 14 Jun 2023 11:35:03 -0400 Subject: [PATCH 8/9] Fix FollowUserView not allowing profile tapping --- damus/Util/Router.swift | 3 +++ damus/Views/FollowingView.swift | 9 +++++++++ damus/Views/Reactions/ReactionView.swift | 2 ++ damus/Views/ReactionsView.swift | 2 ++ damus/Views/Reposts/RepostView.swift | 2 ++ damus/Views/RepostsView.swift | 2 ++ damus/Views/Search/SearchingEventView.swift | 2 ++ damus/Views/SearchHomeView.swift | 1 + damus/Views/SearchResultsView.swift | 5 +++++ 9 files changed, 28 insertions(+) diff --git a/damus/Util/Router.swift b/damus/Util/Router.swift index 857e5787..a522507e 100644 --- a/damus/Util/Router.swift +++ b/damus/Util/Router.swift @@ -57,6 +57,7 @@ enum Route: Hashable { RelayDetailView(state: damusState, relay: relay, nip11: metadata) case .Following(let following): FollowingView(damus_state: damusState, following: following) + .environmentObject(navigationCordinator) case .MuteList(let users): MutelistView(damus_state: damusState, users: users) case .RelayConfig: @@ -88,8 +89,10 @@ enum Route: Hashable { ThreadView(state: damusState, thread: thread) case .Reposts(let reposts): RepostsView(damus_state: damusState, model: reposts) + .environmentObject(navigationCordinator) case .Reactions(let reactions): ReactionsView(damus_state: damusState, model: reactions) + .environmentObject(navigationCordinator) case .Zaps(let target): ZapsView(state: damusState, target: target) case .Search(let search): diff --git a/damus/Views/FollowingView.swift b/damus/Views/FollowingView.swift index 0060d429..af995597 100644 --- a/damus/Views/FollowingView.swift +++ b/damus/Views/FollowingView.swift @@ -10,12 +10,16 @@ import SwiftUI struct FollowUserView: View { let target: FollowTarget let damus_state: DamusState + @EnvironmentObject var navigationCoordinator: NavigationCoordinator static let markdown = Markdown() var body: some View { HStack { UserViewRow(damus_state: damus_state, pubkey: target.pubkey) + .onTapGesture { + navigationCoordinator.push(route: Route.ProfileByKey(pubkey: target.pubkey)) + } FollowButtonView(target: target, follows_you: false, follow_state: damus_state.contacts.follow_state(target.pubkey)) } @@ -46,12 +50,14 @@ struct FollowersView: View { let damus_state: DamusState @EnvironmentObject var followers: FollowersModel + @EnvironmentObject var navigationCoordinator: NavigationCoordinator var body: some View { ScrollView { LazyVStack(alignment: .leading) { ForEach(followers.contacts ?? [], id: \.self) { pk in FollowUserView(target: .pubkey(pk), damus_state: damus_state) + .environmentObject(navigationCoordinator) } } .padding(.horizontal) @@ -70,12 +76,15 @@ struct FollowingView: View { let damus_state: DamusState let following: FollowingModel + + @EnvironmentObject var navigationCoordinator: NavigationCoordinator var body: some View { ScrollView { LazyVStack(alignment: .leading) { ForEach(following.contacts.reversed(), id: \.self) { pk in FollowUserView(target: .pubkey(pk), damus_state: damus_state) + .environmentObject(navigationCoordinator) } } .padding() diff --git a/damus/Views/Reactions/ReactionView.swift b/damus/Views/Reactions/ReactionView.swift index 6b99fc79..1a7ca30e 100644 --- a/damus/Views/Reactions/ReactionView.swift +++ b/damus/Views/Reactions/ReactionView.swift @@ -10,6 +10,7 @@ import SwiftUI struct ReactionView: View { let damus_state: DamusState let reaction: NostrEvent + @EnvironmentObject var navigationCoordinator: NavigationCoordinator var content: String { return to_reaction_emoji(ev: reaction) ?? "" @@ -22,6 +23,7 @@ struct ReactionView: View { .frame(width: 50, height: 50) FollowUserView(target: .pubkey(reaction.pubkey), damus_state: damus_state) + .environmentObject(navigationCoordinator) } } } diff --git a/damus/Views/ReactionsView.swift b/damus/Views/ReactionsView.swift index 7d74e3ed..1ca21ddd 100644 --- a/damus/Views/ReactionsView.swift +++ b/damus/Views/ReactionsView.swift @@ -10,6 +10,7 @@ import SwiftUI struct ReactionsView: View { let damus_state: DamusState @StateObject var model: ReactionsModel + @EnvironmentObject var navigationCoordinator: NavigationCoordinator @Environment(\.dismiss) var dismiss @@ -18,6 +19,7 @@ struct ReactionsView: View { LazyVStack { ForEach(model.events, id: \.id) { ev in ReactionView(damus_state: damus_state, reaction: ev) + .environmentObject(navigationCoordinator) } } .padding() diff --git a/damus/Views/Reposts/RepostView.swift b/damus/Views/Reposts/RepostView.swift index f619f4fb..7a7e0054 100644 --- a/damus/Views/Reposts/RepostView.swift +++ b/damus/Views/Reposts/RepostView.swift @@ -10,9 +10,11 @@ import SwiftUI struct RepostView: View { let damus_state: DamusState let repost: NostrEvent + @EnvironmentObject var navigationCoordinator: NavigationCoordinator var body: some View { FollowUserView(target: .pubkey(repost.pubkey), damus_state: damus_state) + .environmentObject(navigationCoordinator) } } diff --git a/damus/Views/RepostsView.swift b/damus/Views/RepostsView.swift index a2bf1dc6..c059382f 100644 --- a/damus/Views/RepostsView.swift +++ b/damus/Views/RepostsView.swift @@ -10,12 +10,14 @@ import SwiftUI struct RepostsView: View { let damus_state: DamusState @StateObject var model: RepostsModel + @EnvironmentObject var navigationCoordinator: NavigationCoordinator var body: some View { ScrollView { LazyVStack { ForEach(model.events, id: \.id) { ev in RepostView(damus_state: damus_state, repost: ev) + .environmentObject(navigationCoordinator) } } .padding() diff --git a/damus/Views/Search/SearchingEventView.swift b/damus/Views/Search/SearchingEventView.swift index 6093f73d..044875fc 100644 --- a/damus/Views/Search/SearchingEventView.swift +++ b/damus/Views/Search/SearchingEventView.swift @@ -24,6 +24,7 @@ struct SearchingEventView: View { let state: DamusState let evid: String let search_type: SearchType + @EnvironmentObject var navigationCoordinator: NavigationCoordinator @State var search_state: SearchState = .searching @@ -107,6 +108,7 @@ struct SearchingEventView: View { case .found_profile(let pk): NavigationLink(value: Route.ProfileByKey(pubkey: pk)) { FollowUserView(target: .pubkey(pk), damus_state: state) + .environmentObject(navigationCoordinator) } .buttonStyle(PlainButtonStyle()) case .not_found: diff --git a/damus/Views/SearchHomeView.swift b/damus/Views/SearchHomeView.swift index 3e439ee4..5772bcdb 100644 --- a/damus/Views/SearchHomeView.swift +++ b/damus/Views/SearchHomeView.swift @@ -78,6 +78,7 @@ struct SearchHomeView: View { var SearchContent: some View { SearchResultsView(damus_state: damus_state, search: $search) + .environmentObject(navigationCoordinator) .refreshable { // Fetch new information by unsubscribing and resubscribing to the relay model.unsubscribe() diff --git a/damus/Views/SearchResultsView.swift b/damus/Views/SearchResultsView.swift index 545cbec2..5a74d801 100644 --- a/damus/Views/SearchResultsView.swift +++ b/damus/Views/SearchResultsView.swift @@ -37,9 +37,11 @@ enum Search: Identifiable { struct InnerSearchResults: View { let damus_state: DamusState let search: Search? + @EnvironmentObject var navigationCoordinator: NavigationCoordinator func ProfileSearchResult(pk: String) -> some View { FollowUserView(target: .pubkey(pk), damus_state: damus_state) + .environmentObject(navigationCoordinator) } func HashtagSearch(_ ht: String) -> some View { @@ -68,6 +70,7 @@ struct InnerSearchResults: View { case .nip05(let addr): SearchingEventView(state: damus_state, evid: addr, search_type: .nip05) + .environmentObject(navigationCoordinator) case .profile(let prof): let decoded = try? bech32_decode(prof) @@ -106,10 +109,12 @@ struct SearchResultsView: View { let damus_state: DamusState @Binding var search: String @State var result: Search? = nil + @EnvironmentObject var navigationCoordinator: NavigationCoordinator var body: some View { ScrollView { InnerSearchResults(damus_state: damus_state, search: result) + .environmentObject(navigationCoordinator) .padding() } .frame(maxHeight: .infinity) From 69663b8207e5c434ae30cfe72c91079152509d22 Mon Sep 17 00:00:00 2001 From: Scott Penrose Date: Wed, 14 Jun 2023 15:45:12 -0400 Subject: [PATCH 9/9] A few more navigation links from rebase --- damus/ContentView.swift | 1 + damus/Util/Router.swift | 8 ++++++++ damus/Views/Profile/ProfileView.swift | 3 ++- damus/Views/QRCodeView.swift | 16 +++++----------- damus/Views/Relays/RecommendedRelayView.swift | 4 +--- damus/Views/SideMenuView.swift | 2 ++ 6 files changed, 19 insertions(+), 15 deletions(-) diff --git a/damus/ContentView.swift b/damus/ContentView.swift index 3d0d43e5..619fc0c7 100644 --- a/damus/ContentView.swift +++ b/damus/ContentView.swift @@ -282,6 +282,7 @@ struct ContentView: View { .tabViewStyle(.page(indexDisplayMode: .never)) .overlay( SideMenuView(damus_state: damus, isSidebarVisible: $isSideBarOpened.animation()) + .environmentObject(navigationCoordinator) ) .navigationDestination(for: Route.self) { route in route.view(navigationCordinator: navigationCoordinator, damusState: damus_state!) diff --git a/damus/Util/Router.swift b/damus/Util/Router.swift index a522507e..647d01c6 100644 --- a/damus/Util/Router.swift +++ b/damus/Util/Router.swift @@ -38,6 +38,7 @@ enum Route: Hashable { case SaveKeys(account: CreateAccountModel) case Wallet(wallet: WalletModel) case WalletScanner(result: Binding) + case FollowersYouKnow(friendedFollowers: [String]) @ViewBuilder func view(navigationCordinator: NavigationCoordinator, damusState: DamusState) -> some View { @@ -115,6 +116,8 @@ enum Route: Hashable { .environmentObject(navigationCordinator) case .WalletScanner(let walletScanResult): WalletScannerView(result: walletScanResult) + case .FollowersYouKnow(let friendedFollowers): + FollowersYouKnowView(damus_state: damusState, friended_followers: friendedFollowers) } } @@ -180,6 +183,8 @@ enum Route: Hashable { return true case (.WalletScanner(_), .WalletScanner(_)): return true + case (.FollowersYouKnow(let lhs_friendedFollowers), .FollowersYouKnow(let rhs_friendedFollowers)): + return lhs_friendedFollowers == rhs_friendedFollowers default: return false } @@ -263,6 +268,9 @@ enum Route: Hashable { hasher.combine("wallet") case .WalletScanner(_): hasher.combine("walletScanner") + case .FollowersYouKnow(let friendedFollowers): + hasher.combine("followersYouKnow") + hasher.combine(friendedFollowers) } } } diff --git a/damus/Views/Profile/ProfileView.swift b/damus/Views/Profile/ProfileView.swift index 3c64221d..80305ea2 100644 --- a/damus/Views/Profile/ProfileView.swift +++ b/damus/Views/Profile/ProfileView.swift @@ -451,7 +451,7 @@ struct ProfileView: View { if !friended_followers.isEmpty { Spacer() - NavigationLink(destination: FollowersYouKnowView(damus_state: damus_state, friended_followers: friended_followers)) { + NavigationLink(value: Route.FollowersYouKnow(friendedFollowers: friended_followers)) { HStack { CondensedProfilePicturesView(state: damus_state, pubkeys: friended_followers, maxPictures: 3) Text(followedByString(friended_followers, profiles: damus_state.profiles)) @@ -521,6 +521,7 @@ struct ProfileView: View { } .fullScreenCover(isPresented: $show_qr_code) { QRCodeView(damus_state: damus_state, pubkey: profile.pubkey) + .environmentObject(navigationCoordinator) } if damus_state.is_privkey_user { diff --git a/damus/Views/QRCodeView.swift b/damus/Views/QRCodeView.swift index 86dd1669..90604873 100644 --- a/damus/Views/QRCodeView.swift +++ b/damus/Views/QRCodeView.swift @@ -45,12 +45,12 @@ struct QRCodeView: View { @State var pubkey: String @Environment(\.presentationMode) var presentationMode + @EnvironmentObject var navigationCoordinator: NavigationCoordinator @State private var selectedTab = 0 @State var scanResult: ProfileScanResult? = nil - - @State var showProfileView: Bool = false + @State var profile: Profile? = nil @State var error: String? = nil @@ -209,13 +209,6 @@ struct QRCodeView: View { Spacer() - if let scanResult { - let dst = ProfileView(damus_state: damus_state, pubkey: scanResult.pubkey) - NavigationLink(destination: dst, isActive: $showProfileView) { - EmptyView() - } - } - Spacer() Button(action: { @@ -271,9 +264,10 @@ struct QRCodeView: View { func show_profile_after_delay() { DispatchQueue.main.asyncAfter(deadline: .now() + animationDuration) { - showProfileView = true + if let scanResult { + navigationCoordinator.push(route: Route.ProfileByKey(pubkey: scanResult.pubkey)) + } } - } func cameraAnimate(completion: @escaping () -> Void) { diff --git a/damus/Views/Relays/RecommendedRelayView.swift b/damus/Views/Relays/RecommendedRelayView.swift index 47026170..92cf726e 100644 --- a/damus/Views/Relays/RecommendedRelayView.swift +++ b/damus/Views/Relays/RecommendedRelayView.swift @@ -42,9 +42,7 @@ struct RecommendedRelayView: View { Text(relay).layoutPriority(1) if let meta = damus.relay_metadata.lookup(relay_id: relay) { - NavigationLink ( destination: - RelayDetailView(state: damus, relay: relay, nip11: meta) - ){ + NavigationLink(value: Route.RelayDetail(relay: relay, metadata: meta)){ EmptyView() } .opacity(0.0) diff --git a/damus/Views/SideMenuView.swift b/damus/Views/SideMenuView.swift index 1a8c8331..6cd04b5c 100644 --- a/damus/Views/SideMenuView.swift +++ b/damus/Views/SideMenuView.swift @@ -15,6 +15,7 @@ struct SideMenuView: View { @State private var showQRCode = false @Environment(\.colorScheme) var colorScheme + @EnvironmentObject var navigationCoordinator: NavigationCoordinator var sideBarWidth = min(UIScreen.main.bounds.size.width * 0.65, 400.0) let verticalSpacing: CGFloat = 20 @@ -161,6 +162,7 @@ struct SideMenuView: View { .dynamicTypeSize(.xSmall) }).fullScreenCover(isPresented: $showQRCode) { QRCodeView(damus_state: damus_state, pubkey: damus_state.pubkey) + .environmentObject(navigationCoordinator) } } .padding(.top, verticalSpacing)