Improve notification view filtering UX

- 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 <daniel@daquino.me>
This commit is contained in:
Daniel D’Aquino
2024-09-16 12:21:10 -07:00
parent ce63f6a96b
commit 6254cea600
5 changed files with 46 additions and 13 deletions

View File

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

View File

@@ -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'")
}
}
}

View File

@@ -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()

View File

@@ -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
}
}

View File

@@ -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
}
}