damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

commit b5a81e2586cb8c20549f40137d7de97c04b49af3
parent 9e0b9debb457424433066d06798b2ad4d93ba0be
Author: Daniel D’Aquino <daniel@daquino.me>
Date:   Wed, 18 Sep 2024 19:14:07 -0700

Merge branch 'release_1.10'

Diffstat:
Mdamus/Components/SupporterBadge.swift | 6++++--
Mdamus/ContentView.swift | 22+++++++++++++++++-----
Mdamus/Models/FriendFilter.swift | 13+++++++++++--
Mdamus/Models/NotificationsManager.swift | 2+-
Mdamus/Models/Post.swift | 12+++++++-----
Mdamus/Models/PushNotificationClient.swift | 2+-
Mdamus/Models/UserSettingsStore.swift | 9+++++----
Mdamus/Views/Buttons/FriendsButton.swift | 8++++----
Mdamus/Views/DirectMessagesView.swift | 2+-
Mdamus/Views/NoteContentView.swift | 3+--
Mdamus/Views/Notifications/NotificationsView.swift | 16++++++++++++++--
Mdamus/Views/Purple/DamusPurpleAccountView.swift | 4++--
Mdamus/Views/Settings/DeveloperSettingsView.swift | 4----
Mdamus/Views/Settings/NotificationSettingsView.swift | 12++++++------
MdamusTests/ReplyTests.swift | 3++-
15 files changed, 76 insertions(+), 42 deletions(-)

diff --git a/damus/Components/SupporterBadge.swift b/damus/Components/SupporterBadge.swift @@ -11,11 +11,13 @@ struct SupporterBadge: View { let percent: Int? let purple_account: DamusPurple.Account? let style: Style + let text_color: Color - init(percent: Int?, purple_account: DamusPurple.Account? = nil, style: Style) { + init(percent: Int?, purple_account: DamusPurple.Account? = nil, style: Style, text_color: Color = .secondary) { self.percent = percent self.purple_account = purple_account self.style = style + self.text_color = text_color } let size: CGFloat = 17 @@ -31,7 +33,7 @@ struct SupporterBadge: View { if self.style == .full { let date = format_date(date: purple_account.created_at, time_style: .none) Text(date) - .foregroundStyle(.secondary) + .foregroundStyle(text_color) .font(.caption) } } diff --git a/damus/ContentView.swift b/damus/ContentView.swift @@ -77,7 +77,12 @@ struct ContentView: View { @State var active_sheet: Sheets? = nil @State var damus_state: DamusState! - @SceneStorage("ContentView.selected_timeline") var selected_timeline: Timeline = .home + @State var menu_subtitle: String? = nil + @SceneStorage("ContentView.selected_timeline") var selected_timeline: Timeline = .home { + willSet { + self.menu_subtitle = nil + } + } @State var muting: MuteItem? = nil @State var confirm_mute: Bool = false @State var hide_bar: Bool = false @@ -101,9 +106,16 @@ struct ContentView: View { isSideBarOpened = false } - var timelineNavItem: Text { - return Text(timeline_name(selected_timeline)) - .bold() + var timelineNavItem: some View { + VStack { + Text(timeline_name(selected_timeline)) + .bold() + if let menu_subtitle { + Text(menu_subtitle) + .font(.caption) + .foregroundStyle(.secondary) + } + } } func MainContent(damus: DamusState) -> some View { @@ -122,7 +134,7 @@ struct ContentView: View { PostingTimelineView(damus_state: damus_state!, home: home, active_sheet: $active_sheet) case .notifications: - NotificationsView(state: damus, notifications: home.notifications) + NotificationsView(state: damus, notifications: home.notifications, subtitle: $menu_subtitle) case .dms: DirectMessagesView(damus_state: damus_state!, model: damus_state!.dms, settings: damus_state!.settings) diff --git a/damus/Models/FriendFilter.swift b/damus/Models/FriendFilter.swift @@ -9,7 +9,7 @@ import Foundation enum FriendFilter: String, StringCodable { case all - case friends + case friends_of_friends init?(from string: String) { guard let ff = FriendFilter(rawValue: string) else { @@ -27,8 +27,17 @@ enum FriendFilter: String, StringCodable { switch self { case .all: return true - case .friends: + case .friends_of_friends: return contacts.is_in_friendosphere(pubkey) } } + + func description() -> String { + switch self { + case .all: + return NSLocalizedString("All", comment: "Human-readable short description of the 'friends filter' when it is set to 'all'") + case .friends_of_friends: + return NSLocalizedString("Friends of friends", comment: "Human-readable short description of the 'friends filter' when it is set to 'friends-of-friends'") + } + } } diff --git a/damus/Models/NotificationsManager.swift b/damus/Models/NotificationsManager.swift @@ -27,7 +27,7 @@ func process_local_notification(state: HeadlessDamusState, event ev: NostrEvent) func should_display_notification(state: HeadlessDamusState, event ev: NostrEvent, mode: UserSettingsStore.NotificationsMode) -> Bool { // Do not show notification if it's coming from a mode different from the one selected by our user - guard state.settings.notifications_mode == mode else { + guard state.settings.notification_mode == mode else { return false } diff --git a/damus/Models/Post.swift b/damus/Models/Post.swift @@ -51,7 +51,7 @@ struct NostrPost { } /// Parse the post's contents to find more tags to apply to the final nostr event - private func make_post_tags(post_blocks: [Block], tags: [[String]]) -> PostTags { + func make_post_tags(post_blocks: [Block], tags: [[String]]) -> PostTags { var new_tags = tags for post_block in post_blocks { @@ -89,10 +89,12 @@ struct NostrPost { // MARK: - Helper structures and functions -/// A struct used for temporarily holding tag information that was parsed from a post contents to aid in building a nostr event -fileprivate struct PostTags { - let blocks: [Block] - let tags: [[String]] +extension NostrPost { + /// A struct used for temporarily holding tag information that was parsed from a post contents to aid in building a nostr event + struct PostTags { + let blocks: [Block] + let tags: [[String]] + } } func parse_post_blocks(content: String) -> [Block] { diff --git a/damus/Models/PushNotificationClient.swift b/damus/Models/PushNotificationClient.swift @@ -18,7 +18,7 @@ struct PushNotificationClient { mutating func set_device_token(new_device_token: Data) async throws { self.device_token = new_device_token - if settings.enable_experimental_push_notifications && settings.notifications_mode == .push { + if settings.enable_push_notifications && settings.notification_mode == .push { try await self.send_token() } } diff --git a/damus/Models/UserSettingsStore.swift b/damus/Models/UserSettingsStore.swift @@ -155,8 +155,8 @@ class UserSettingsStore: ObservableObject { @Setting(key: "like_notification", default_value: true) var like_notification: Bool - @StringSetting(key: "notifications_mode", default_value: .local) - var notifications_mode: NotificationsMode + @StringSetting(key: "notification_mode", default_value: .push) + var notification_mode: NotificationsMode @Setting(key: "notification_only_from_following", default_value: false) var notification_only_from_following: Bool @@ -207,8 +207,9 @@ class UserSettingsStore: ObservableObject { @Setting(key: "always_show_onboarding_suggestions", default_value: false) var always_show_onboarding_suggestions: Bool - @Setting(key: "enable_experimental_push_notifications", default_value: false) - var enable_experimental_push_notifications: Bool + // @Setting(key: "enable_experimental_push_notifications", default_value: false) + // This was a feature flag setting during early development, but now this is enabled for everyone. + var enable_push_notifications: Bool = true @StringSetting(key: "push_notification_environment", default_value: .production) var push_notification_environment: PushNotificationClient.Environment diff --git a/damus/Views/Buttons/FriendsButton.swift b/damus/Views/Buttons/FriendsButton.swift @@ -14,12 +14,12 @@ struct FriendsButton: View { Button(action: { switch self.filter { case .all: - self.filter = .friends - case .friends: + self.filter = .friends_of_friends + case .friends_of_friends: self.filter = .all } }) { - if filter == .friends { + if filter == .friends_of_friends { LINEAR_GRADIENT .mask(Image("user-added") .resizable() @@ -28,7 +28,7 @@ struct FriendsButton: View { Image("user-added") .resizable() .frame(width: 28, height: 28) - .foregroundColor(DamusColors.adaptableGrey) + .foregroundColor(.gray) } } .buttonStyle(.plain) diff --git a/damus/Views/DirectMessagesView.swift b/damus/Views/DirectMessagesView.swift @@ -103,7 +103,7 @@ struct DirectMessagesView: View { func would_filter_non_friends_from_dms(contacts: Contacts, dms: [DirectMessageModel]) -> Bool { for dm in dms { - if !FriendFilter.friends.filter(contacts: contacts, pubkey: dm.pubkey) { + if !FriendFilter.friends_of_friends.filter(contacts: contacts, pubkey: dm.pubkey) { return true } } diff --git a/damus/Views/NoteContentView.swift b/damus/Views/NoteContentView.swift @@ -120,8 +120,7 @@ struct NoteContentView: View { EventView(damus: damus_state, event: self.event, options: .embedded_text_only) .padding(.top) } - .background(.thinMaterial) - .preferredColorScheme(.dark) + .background(.thickMaterial) .onTapGesture(perform: { damus_state.nav.push(route: Route.Thread(thread: .init(event: self.event, damus_state: damus_state))) dismiss() diff --git a/damus/Views/Notifications/NotificationsView.swift b/damus/Views/Notifications/NotificationsView.swift @@ -56,6 +56,7 @@ struct NotificationsView: View { @ObservedObject var notifications: NotificationsModel @StateObject var filter = NotificationFilter() @SceneStorage("NotificationsView.filter_state") var filter_state: NotificationFilterState = .all + @Binding var subtitle: String? @Environment(\.colorScheme) var colorScheme @@ -100,6 +101,15 @@ struct NotificationsView: View { } .toolbar { ToolbarItem(placement: .navigationBarTrailing) { + Button( + action: { state.nav.push(route: Route.NotificationSettings(settings: state.settings)) }, + label: { + Image("settings") + .foregroundColor(.gray) + } + ) + } + ToolbarItem(placement: .navigationBarTrailing) { if would_filter_non_friends_from_notifications(contacts: state.contacts, state: filter_state, items: self.notifications.notifications) { FriendsButton(filter: $filter.fine_filter) } @@ -107,12 +117,14 @@ struct NotificationsView: View { } .onChange(of: filter.fine_filter) { val in state.settings.friend_filter = val + self.subtitle = filter.fine_filter.description() } .onChange(of: filter_state) { val in filter.state = val } .onAppear { self.filter.fine_filter = state.settings.friend_filter + self.subtitle = filter.fine_filter.description() filter.state = filter_state } .safeAreaInset(edge: .top, spacing: 0) { @@ -163,7 +175,7 @@ struct NotificationsView: View { struct NotificationsView_Previews: PreviewProvider { static var previews: some View { - NotificationsView(state: test_damus_state, notifications: NotificationsModel(), filter: NotificationFilter()) + NotificationsView(state: test_damus_state, notifications: NotificationsModel(), filter: NotificationFilter(), subtitle: .constant(nil)) } } @@ -174,7 +186,7 @@ func would_filter_non_friends_from_notifications(contacts: Contacts, state: Noti continue } - if item.would_filter({ ev in FriendFilter.friends.filter(contacts: contacts, pubkey: ev.pubkey) }) { + if item.would_filter({ ev in FriendFilter.friends_of_friends.filter(contacts: contacts, pubkey: ev.pubkey) }) { return true } } diff --git a/damus/Views/Purple/DamusPurpleAccountView.swift b/damus/Views/Purple/DamusPurpleAccountView.swift @@ -64,7 +64,6 @@ struct DamusPurpleAccountView: View { .padding(.bottom, 20) } .foregroundColor(.white.opacity(0.8)) - .preferredColorScheme(.dark) .background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 12, style: .continuous)) .padding() } @@ -81,7 +80,8 @@ struct DamusPurpleAccountView: View { SupporterBadge( percent: nil, purple_account: account, - style: .full + style: .full, + text_color: .white ) } } diff --git a/damus/Views/Settings/DeveloperSettingsView.swift b/damus/Views/Settings/DeveloperSettingsView.swift @@ -18,10 +18,6 @@ struct DeveloperSettingsView: View { .toggleStyle(.switch) if settings.developer_mode { Toggle(NSLocalizedString("Always show onboarding", comment: "Developer mode setting to always show onboarding suggestions."), isOn: $settings.always_show_onboarding_suggestions) - - Toggle(NSLocalizedString("Enable experimental push notifications", comment: "Developer mode setting to enable experimental push notifications."), isOn: $settings.enable_experimental_push_notifications) - .toggleStyle(.switch) - Picker(NSLocalizedString("Push notification environment", comment: "Prompt selection of the Push notification environment (Developer feature to switch between real/production mode to test modes)."), selection: Binding( get: { () -> PushNotificationClient.Environment in diff --git a/damus/Views/Settings/NotificationSettingsView.swift b/damus/Views/Settings/NotificationSettingsView.swift @@ -36,7 +36,7 @@ struct NotificationSettingsView: View { do { try await damus_state.push_notification_client.send_token() await self.sync_up_remote_notification_settings() - settings.notifications_mode = new_value + settings.notification_mode = new_value } catch { notification_mode_setting_error = String(format: NSLocalizedString("Error configuring push notifications with the server: %@", comment: "Error label shown when user tries to enable push notifications but something fails"), error.localizedDescription) @@ -47,7 +47,7 @@ struct NotificationSettingsView: View { Task { do { try await damus_state.push_notification_client.revoke_token() - settings.notifications_mode = new_value + settings.notification_mode = new_value notification_preferences_sync_state = .not_applicable } catch { @@ -67,7 +67,7 @@ struct NotificationSettingsView: View { set: { new_value in let old_value = raw_binding.wrappedValue raw_binding.wrappedValue = new_value - if self.settings.notifications_mode == .push { + if self.settings.notification_mode == .push { Task { await self.send_push_notification_preferences(on_failure: { raw_binding.wrappedValue = old_value @@ -114,7 +114,7 @@ struct NotificationSettingsView: View { var body: some View { Form { - if settings.enable_experimental_push_notifications { + if settings.enable_push_notifications { Section( header: Text("General", comment: "Section header for general damus notifications user configuration"), footer: VStack { @@ -126,7 +126,7 @@ struct NotificationSettingsView: View { ) { Picker(NSLocalizedString("Notifications mode", comment: "Prompt selection of the notification mode (Feature to switch between local notifications (generated from user's own phone) or push notifications (generated by Damus server)."), selection: Binding( - get: { settings.notifications_mode }, + get: { settings.notification_mode }, set: { newValue in self.try_to_set_notifications_mode(new_value: newValue) } @@ -194,7 +194,7 @@ struct NotificationSettingsView: View { } .onAppear(perform: { Task { - if self.settings.notifications_mode == .push { + if self.settings.notification_mode == .push { await self.sync_up_remote_notification_settings() } } diff --git a/damusTests/ReplyTests.swift b/damusTests/ReplyTests.swift @@ -57,7 +57,8 @@ class ReplyTests: XCTestCase { let ev = NostrEvent(content: content, keypair: test_keypair, tags: [])! let blocks = parse_note_content(content: .init(note: ev, keypair: test_keypair)).blocks let post_blocks = parse_post_blocks(content: content) - let post_tags = make_post_tags(post_blocks: post_blocks, tags: []) + let post = NostrPost(content: content, kind: NostrKind.text, tags: []) + let post_tags = post.make_post_tags(post_blocks: post_blocks, tags: []) let tr = interpret_event_refs(tags: ev.tags) XCTAssertNil(tr)