From 6254cea6009ad392fe93fdf255202f7753632f44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20D=E2=80=99Aquino?= Date: Mon, 16 Sep 2024 12:21:10 -0700 Subject: [PATCH] Improve notification view filtering UX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add subtitle below the toolbar title to indicate the state of the filter - Add settings icon to take user to the notification settings page, and thus make that more discoverable Testing ------- PASS Device: iPhone 13 mini iOS: 17.6.1 Coverage: 1. Switching back and forth between the notifications tab and other tabs causes subtitle to show/hide as expected in both filter options (all, friends) 2. Subtitle follows the friends filter 3. Subtitle shows after restarting the app 4. Settings icon appears and takes user to the notification setting view 5. Notification settings can be updated from that view. Changelog-Changed: Improve notification view filtering UX Closes: https://github.com/damus-io/damus/issues/2480 Signed-off-by: Daniel D’Aquino --- damus/ContentView.swift | 22 ++++++++++++++----- damus/Models/FriendFilter.swift | 13 +++++++++-- damus/Views/Buttons/FriendsButton.swift | 6 ++--- damus/Views/DirectMessagesView.swift | 2 +- .../Notifications/NotificationsView.swift | 16 ++++++++++++-- 5 files changed, 46 insertions(+), 13 deletions(-) diff --git a/damus/ContentView.swift b/damus/ContentView.swift index 3f93f41b..2f52e46a 100644 --- 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 @@ -159,9 +164,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 { @@ -180,7 +192,7 @@ struct ContentView: View { PostingTimelineView 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 index b2a15544..fd0399e9 100644 --- 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/Views/Buttons/FriendsButton.swift b/damus/Views/Buttons/FriendsButton.swift index 4d44695a..97862747 100644 --- 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() diff --git a/damus/Views/DirectMessagesView.swift b/damus/Views/DirectMessagesView.swift index 0adf2cf9..dff8d08c 100644 --- 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/Notifications/NotificationsView.swift b/damus/Views/Notifications/NotificationsView.swift index 2f824d18..1168ae33 100644 --- 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 @@ -99,6 +100,15 @@ struct NotificationsView: View { .tag(NotificationFilterState.replies) } .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 } }