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) {