From 4122b4cb1d820c380d3a24e7914659e5ddc6ae07 Mon Sep 17 00:00:00 2001 From: Diogo Date: Thu, 12 Dec 2024 22:39:23 +0000 Subject: [PATCH] simplify tag casts and collapse --- apps/ios/Shared/Model/ChatModel.swift | 8 +- .../Shared/Views/ChatList/ChatListView.swift | 185 ++++++++++++------ 2 files changed, 134 insertions(+), 59 deletions(-) diff --git a/apps/ios/Shared/Model/ChatModel.swift b/apps/ios/Shared/Model/ChatModel.swift index 7ee80a6176..f1562bd28f 100644 --- a/apps/ios/Shared/Model/ChatModel.swift +++ b/apps/ios/Shared/Model/ChatModel.swift @@ -104,10 +104,10 @@ class ChatTagsModel: ObservableObject { static let shared = ChatTagsModel() let presetTags: [ChatTagFilter] = [ - .presetTag(icon: "star", activeIcon: "star.fill", filter: { c in c.chatSettings?.favorite ?? false }), - .presetTag(icon: "person", activeIcon: "person.fill", filter: { if case .direct = $0 { true } else { false }}), - .presetTag(icon: "person.2", activeIcon: "person.2.fill", filter: { filterGroupChat($0) }), - .presetTag(icon: "briefcase", activeIcon: "briefcase.fill", filter: { filterBusinessChat($0) }) + .presetTag(icon: "star", activeIcon: "star.fill", text: NSLocalizedString("Favorite", comment: "preset chat list"), enabled: false, filter: { c in c.chatSettings?.favorite ?? false }), + .presetTag(icon: "person", activeIcon: "person.fill", text: NSLocalizedString("Contacts", comment: "preset chat list"), enabled: false, filter: { if case .direct = $0 { true } else { false }}), + .presetTag(icon: "person.2", activeIcon: "person.2.fill", text: NSLocalizedString("Groups", comment: "preset chat list"), enabled: false, filter: { filterGroupChat($0) }), + .presetTag(icon: "briefcase", activeIcon: "briefcase.fill", text: NSLocalizedString("Business", comment: "preset chat list"), enabled: false, filter: { filterBusinessChat($0) }) ] @Published var tags: [ChatTagFilter] = [] diff --git a/apps/ios/Shared/Views/ChatList/ChatListView.swift b/apps/ios/Shared/Views/ChatList/ChatListView.swift index 806c85bf84..f439e18770 100644 --- a/apps/ios/Shared/Views/ChatList/ChatListView.swift +++ b/apps/ios/Shared/Views/ChatList/ChatListView.swift @@ -449,20 +449,7 @@ struct ChatListView: View { func filterByTag(_ chat: Chat) -> Bool { if let tag = chatTagsModel.selectedTag { - switch tag { - case let .presetTag(_, _, filter): - return filter(chat.chatInfo) - case let .chatTag(_, _, tagId): - let cInfo = chat.chatInfo - switch cInfo { - case let .direct(contact): - return contact.chatTags.contains(tagId) - case let .group(gInfo): - return gInfo.chatTags.contains(tagId) - default: - return false - } - } + return tag.filter(chat.chatInfo) } else { return true } @@ -645,8 +632,8 @@ struct ChatListSearchBar: View { enum ChatTagFilter: Identifiable, Equatable { static func == (lhs: ChatTagFilter, rhs: ChatTagFilter) -> Bool { switch (lhs, rhs) { - case let (.presetTag(icon1, activeIcon1, _), .presetTag(icon2, activeIcon2, _)): - return icon1 == icon2 && activeIcon1 == activeIcon2 + case let (.presetTag(icon1, activeIcon1, text1, enabled1, _), .presetTag(icon2, activeIcon2, text2, enabled2, _)): + return icon1 == icon2 && activeIcon1 == activeIcon2 && text1 == text2 && enabled1 == enabled2 case let (.chatTag(emoji1, text1, tagId1), .chatTag(emoji2, text2, tagId2)): return emoji1 == emoji2 && text1 == text2 && tagId1 == tagId2 default: @@ -654,56 +641,75 @@ enum ChatTagFilter: Identifiable, Equatable { } } - case presetTag(icon: String, activeIcon: String, filter: (_ cInfo: ChatInfo) -> Bool) + case presetTag(icon: String, activeIcon: String, text: String, enabled: Bool, filter: (_ cInfo: ChatInfo) -> Bool) case chatTag(emoji: String, text: String, tagId: Int64) public var id: String { switch self { - case let .presetTag(icon, _, _): "preset \(icon)" + case let .presetTag(icon, _, _, _, _): "preset \(icon)" case let .chatTag(emoji, _, _): "chatTag \(emoji)" } } + + public var text: String { + switch self { + case let .presetTag(_, _, text, _, _): return text + case let .chatTag(_, text, _): return text + } + } + + public var enabled: Bool { + switch self { + case let .presetTag(_, _, _, enabled, _): return enabled + case .chatTag: return true + } + } + + public func getIcon(active: Bool) -> String { + switch self { + case let .presetTag(icon, activeIcon, _, _, _): return active ? activeIcon : icon + case let .chatTag(emoji, _, _): return emoji + } + } + + public func filter(_ cInfo: ChatInfo) -> Bool { + switch self { + case let .presetTag(_, _, _, _, filter): return filter(cInfo) + case let .chatTag(_, _, tagId): + switch cInfo { + case let .direct(contact): + return contact.chatTags.contains(tagId) + case let .group(gInfo): + return gInfo.chatTags.contains(tagId) + default: + return false + } + } + } } struct ChatTagsView: View { @EnvironmentObject var chatTagsModel: ChatTagsModel @EnvironmentObject var chatModel: ChatModel - var presetTagFilters: [ChatTagFilter] { - get { - var tags: [ChatTagFilter] = [] - var filtersLeft = chatTagsModel.presetTags - for chat in chatModel.chats { - for filter in filtersLeft { - if case let .presetTag(_, _, filterFunction) = filter { - if filterFunction(chat.chatInfo) { - tags.append(filter) - break - } - } - } - - filtersLeft.removeAll { tags.contains($0) } - if filtersLeft.isEmpty { - break - } - } - - return tags - } - } - var body: some View { - let tags = presetTagFilters + chatTagsModel.tags HStack { - ForEach(tags, id: \.id) { tag in + let presetFilters = presetTagFilters() + + let enabledPresetFilters = presetFilters.filter { $0.enabled } + if enabledPresetFilters.count + chatTagsModel.tags.count <= 5 { + expandedPresetTagsFiltersView(presetFilters) + } else { + collapsedTagsFilterView(enabledPresetFilters) + } + + ForEach(chatTagsModel.tags, id: \.id) { tag in let current = chatTagsModel.selectedTag == tag - let color: Color = current ? .accentColor : .secondary + let color: Color? = current ? .accentColor : nil ZStack { switch tag { - case let .presetTag(icon, activeIcon, _): - Image(systemName: current ? activeIcon : icon) - .foregroundColor(color) + case .presetTag: + EmptyView() case let .chatTag(emoji, text, _): HStack(spacing: 4) { Text(emoji) @@ -712,13 +718,9 @@ struct ChatTagsView: View { Text(text).fontWeight(current ? .medium : .regular).foregroundColor(color) } } - } - } - .onTapGesture { - if (chatTagsModel.selectedTag == tag) { - chatTagsModel.selectedTag = nil - } else { - chatTagsModel.selectedTag = tag + .onTapGesture { + setSelectedTag(tag) + } } } } @@ -730,6 +732,79 @@ struct ChatTagsView: View { } } + + @ViewBuilder private func expandedPresetTagsFiltersView(_ filters: [ChatTagFilter]) -> some View { + ForEach(filters, id: \.id) { tag in + let current = chatTagsModel.selectedTag == tag + let color: Color? = current ? .accentColor : (tag.enabled ? nil : .secondary) + if chatTagsModel.tags.isEmpty || tag.enabled { + ZStack { + Image(systemName: tag.getIcon(active: chatTagsModel.selectedTag == tag)) + .foregroundColor(color) + } + .onTapGesture { + if tag.enabled { + setSelectedTag(tag) + } + } + } else { + EmptyView() + } + } + } + + @ViewBuilder private func collapsedTagsFilterView(_ filters: [ChatTagFilter]) -> some View { + let presetFilterIsSelected = filters.contains(where: { $0 == chatTagsModel.selectedTag }) + let color: Color = presetFilterIsSelected ? .accentColor : .secondary + Menu { + ForEach(filters, id: \.id) { tag in + Button { + setSelectedTag(tag) + } label: { + HStack { + Image(systemName: tag.getIcon(active: chatTagsModel.selectedTag == tag)) + Text(tag.text) + } + } + } + + } label: { + Image(systemName: "list.bullet") + .foregroundColor(color) + } + } + + private func presetTagFilters() -> [ChatTagFilter] { + var tags: [ChatTagFilter] = chatTagsModel.presetTags + var filtersLeft = chatTagsModel.presetTags + for chat in chatModel.chats { + for filter in filtersLeft { + if case let .presetTag(icon, activeIcon, text, _, filterFunction) = filter { + if let i = tags.firstIndex(where: { $0.id == filter.id }), filterFunction(chat.chatInfo) { + tags[i] = .presetTag(icon: icon, activeIcon: activeIcon, text: text, enabled: true, filter: filterFunction) + } + } + } + + filtersLeft.removeAll { fl in + tags.contains(where: { $0.id == fl.id && $0.enabled } ) + } + if filtersLeft.isEmpty { + break + } + } + + return tags + } + + private func setSelectedTag(_ tag: ChatTagFilter) { + if (chatTagsModel.selectedTag == tag) { + chatTagsModel.selectedTag = nil + } else { + chatTagsModel.selectedTag = tag + } + } + private func getChatTags() { Task { do {