From f428118fa031a2dce773b81c667abc81a2933504 Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Fri, 5 Jan 2024 10:57:26 +0100 Subject: [PATCH] Recently used tags --- IceCubesApp/App/AppRegistry.swift | 1 + .../Sources/Models/SwiftData/RecentTag.swift | 13 +++ .../StatusEditorAccessoryView.swift | 4 +- .../StatusEditorAutoCompleteView.swift | 95 ++++++++++++++++--- .../Status/Editor/StatusEditorViewModel.swift | 4 +- 5 files changed, 98 insertions(+), 19 deletions(-) create mode 100644 Packages/Models/Sources/Models/SwiftData/RecentTag.swift diff --git a/IceCubesApp/App/AppRegistry.swift b/IceCubesApp/App/AppRegistry.swift index ee73f33e..85bcb88b 100644 --- a/IceCubesApp/App/AppRegistry.swift +++ b/IceCubesApp/App/AppRegistry.swift @@ -136,6 +136,7 @@ extension View { Draft.self, LocalTimeline.self, TagGroup.self, + RecentTag.self, ]) } } diff --git a/Packages/Models/Sources/Models/SwiftData/RecentTag.swift b/Packages/Models/Sources/Models/SwiftData/RecentTag.swift new file mode 100644 index 00000000..0b33bcbb --- /dev/null +++ b/Packages/Models/Sources/Models/SwiftData/RecentTag.swift @@ -0,0 +1,13 @@ +import Foundation +import SwiftData +import SwiftUI + +@Model public class RecentTag: Equatable { + public var title: String = "" + public var lastUse: Date = Date() + + public init(title: String) { + self.title = title + self.lastUse = Date() + } +} diff --git a/Packages/Status/Sources/Status/Editor/Components/StatusEditorAccessoryView.swift b/Packages/Status/Sources/Status/Editor/Components/StatusEditorAccessoryView.swift index bb0007d5..54f35d44 100644 --- a/Packages/Status/Sources/Status/Editor/Components/StatusEditorAccessoryView.swift +++ b/Packages/Status/Sources/Status/Editor/Components/StatusEditorAccessoryView.swift @@ -38,7 +38,7 @@ struct StatusEditorAccessoryView: View { } .frame(height: 24) .padding(16) - .background(.ultraThinMaterial) + .background(.thinMaterial) .cornerRadius(8) #else Divider() @@ -47,7 +47,7 @@ struct StatusEditorAccessoryView: View { } .frame(height: 20) .padding(.vertical, 12) - .background(.ultraThinMaterial) + .background(.thinMaterial) #endif } .onAppear { diff --git a/Packages/Status/Sources/Status/Editor/Components/StatusEditorAutoCompleteView.swift b/Packages/Status/Sources/Status/Editor/Components/StatusEditorAutoCompleteView.swift index 98b8d937..dbb51108 100644 --- a/Packages/Status/Sources/Status/Editor/Components/StatusEditorAutoCompleteView.swift +++ b/Packages/Status/Sources/Status/Editor/Components/StatusEditorAutoCompleteView.swift @@ -2,26 +2,54 @@ import DesignSystem import EmojiText import Foundation import SwiftUI +import Models +import SwiftData @MainActor struct StatusEditorAutoCompleteView: View { + @Environment(\.modelContext) private var context + @Environment(Theme.self) private var theme + var viewModel: StatusEditorViewModel + + @State private var isTagSuggestionExpanded: Bool = false + + @Query(sort: \RecentTag.lastUse, order: .reverse) var recentTags: [RecentTag] var body: some View { if !viewModel.mentionsSuggestions.isEmpty || !viewModel.tagsSuggestions.isEmpty { - ScrollView(.horizontal, showsIndicators: false) { - LazyHStack { - if !viewModel.mentionsSuggestions.isEmpty { - suggestionsMentionsView - } else { - suggestionsTagView + VStack { + HStack { + ScrollView(.horizontal, showsIndicators: false) { + LazyHStack { + if !viewModel.mentionsSuggestions.isEmpty { + suggestionsMentionsView + } else { + suggestionsTagView + } + } + .padding(.horizontal, .layoutPadding) + } + .scrollContentBackground(.hidden) + if !viewModel.tagsSuggestions.isEmpty { + Spacer() + Button { + withAnimation { + isTagSuggestionExpanded.toggle() + } + } label: { + Image(systemName: isTagSuggestionExpanded ? "chevron.down.circle" : "chevron.up.circle") + } + .padding(.trailing, 8) } } - .padding(.horizontal, .layoutPadding) + .frame(height: 40) + if isTagSuggestionExpanded { + expandedTagsSuggestionView + } } - .frame(height: 40) - .background(.ultraThinMaterial) + .background(.thinMaterial) } } @@ -38,10 +66,11 @@ struct StatusEditorAutoCompleteView: View { .emojiSize(Font.scaledFootnoteFont.emojiSize) .emojiBaselineOffset(Font.scaledFootnoteFont.emojiBaselineOffset) .font(.scaledFootnote) + .fontWeight(.bold) .foregroundColor(theme.labelColor) Text("@\(account.acct)") - .font(.scaledCaption) - .foregroundColor(theme.tintColor) + .font(.scaledFootnote) + .foregroundStyle(theme.tintColor) } } } @@ -51,17 +80,53 @@ struct StatusEditorAutoCompleteView: View { private var suggestionsTagView: some View { ForEach(viewModel.tagsSuggestions) { tag in Button { - viewModel.selectHashtagSuggestion(tag: tag) + viewModel.selectHashtagSuggestion(tag: tag.name) + if let index = recentTags.firstIndex(where: { $0.title.lowercased() == tag.name.lowercased() }) { + recentTags[index].lastUse = Date() + } else { + context.insert(RecentTag(title: tag.name)) + } } label: { VStack(alignment: .leading) { Text("#\(tag.name)") .font(.scaledFootnote) - .foregroundColor(theme.tintColor) + .fontWeight(.bold) + .foregroundColor(theme.labelColor) Text("tag.suggested.mentions-\(tag.totalUses)") - .font(.scaledCaption) - .foregroundStyle(.secondary) + .font(.scaledFootnote) + .foregroundStyle(theme.tintColor) } } } } + + private var expandedTagsSuggestionView: some View { + ScrollView(.vertical) { + LazyVStack(alignment: .leading, spacing: 12) { + Text("status.editor.language-select.recently-used") + .font(.scaledSubheadline) + .foregroundStyle(theme.labelColor) + .fontWeight(.bold) + ForEach(recentTags) { tag in + HStack { + Button { + tag.lastUse = Date() + withAnimation { + isTagSuggestionExpanded = false + viewModel.selectHashtagSuggestion(tag: tag.title) + } + } label: { + Text("#\(tag.title)") + .font(.scaledFootnote) + .fontWeight(.bold) + .foregroundColor(theme.labelColor) + } + Spacer() + } + } + } + .padding(.horizontal, .layoutPadding) + } + .frame(height: 250) + } } diff --git a/Packages/Status/Sources/Status/Editor/StatusEditorViewModel.swift b/Packages/Status/Sources/Status/Editor/StatusEditorViewModel.swift index 294aa5dd..da140833 100644 --- a/Packages/Status/Sources/Status/Editor/StatusEditorViewModel.swift +++ b/Packages/Status/Sources/Status/Editor/StatusEditorViewModel.swift @@ -564,9 +564,9 @@ import SwiftUI } } - func selectHashtagSuggestion(tag: Tag) { + func selectHashtagSuggestion(tag: String) { if let range = currentSuggestionRange { - replaceTextWith(text: "#\(tag.name) ", inRange: range) + replaceTextWith(text: "#\(tag) ", inRange: range) } }