mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2025-01-11 16:45:27 +00:00
Recently used tags
This commit is contained in:
parent
3e968525ac
commit
f428118fa0
5 changed files with 98 additions and 19 deletions
|
@ -136,6 +136,7 @@ extension View {
|
||||||
Draft.self,
|
Draft.self,
|
||||||
LocalTimeline.self,
|
LocalTimeline.self,
|
||||||
TagGroup.self,
|
TagGroup.self,
|
||||||
|
RecentTag.self,
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
13
Packages/Models/Sources/Models/SwiftData/RecentTag.swift
Normal file
13
Packages/Models/Sources/Models/SwiftData/RecentTag.swift
Normal file
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,7 +38,7 @@ struct StatusEditorAccessoryView: View {
|
||||||
}
|
}
|
||||||
.frame(height: 24)
|
.frame(height: 24)
|
||||||
.padding(16)
|
.padding(16)
|
||||||
.background(.ultraThinMaterial)
|
.background(.thinMaterial)
|
||||||
.cornerRadius(8)
|
.cornerRadius(8)
|
||||||
#else
|
#else
|
||||||
Divider()
|
Divider()
|
||||||
|
@ -47,7 +47,7 @@ struct StatusEditorAccessoryView: View {
|
||||||
}
|
}
|
||||||
.frame(height: 20)
|
.frame(height: 20)
|
||||||
.padding(.vertical, 12)
|
.padding(.vertical, 12)
|
||||||
.background(.ultraThinMaterial)
|
.background(.thinMaterial)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
|
|
|
@ -2,14 +2,25 @@ import DesignSystem
|
||||||
import EmojiText
|
import EmojiText
|
||||||
import Foundation
|
import Foundation
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import Models
|
||||||
|
import SwiftData
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
struct StatusEditorAutoCompleteView: View {
|
struct StatusEditorAutoCompleteView: View {
|
||||||
|
@Environment(\.modelContext) private var context
|
||||||
|
|
||||||
@Environment(Theme.self) private var theme
|
@Environment(Theme.self) private var theme
|
||||||
|
|
||||||
var viewModel: StatusEditorViewModel
|
var viewModel: StatusEditorViewModel
|
||||||
|
|
||||||
|
@State private var isTagSuggestionExpanded: Bool = false
|
||||||
|
|
||||||
|
@Query(sort: \RecentTag.lastUse, order: .reverse) var recentTags: [RecentTag]
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
if !viewModel.mentionsSuggestions.isEmpty || !viewModel.tagsSuggestions.isEmpty {
|
if !viewModel.mentionsSuggestions.isEmpty || !viewModel.tagsSuggestions.isEmpty {
|
||||||
|
VStack {
|
||||||
|
HStack {
|
||||||
ScrollView(.horizontal, showsIndicators: false) {
|
ScrollView(.horizontal, showsIndicators: false) {
|
||||||
LazyHStack {
|
LazyHStack {
|
||||||
if !viewModel.mentionsSuggestions.isEmpty {
|
if !viewModel.mentionsSuggestions.isEmpty {
|
||||||
|
@ -20,8 +31,25 @@ struct StatusEditorAutoCompleteView: View {
|
||||||
}
|
}
|
||||||
.padding(.horizontal, .layoutPadding)
|
.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)
|
||||||
|
}
|
||||||
|
}
|
||||||
.frame(height: 40)
|
.frame(height: 40)
|
||||||
.background(.ultraThinMaterial)
|
if isTagSuggestionExpanded {
|
||||||
|
expandedTagsSuggestionView
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.background(.thinMaterial)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,10 +66,11 @@ struct StatusEditorAutoCompleteView: View {
|
||||||
.emojiSize(Font.scaledFootnoteFont.emojiSize)
|
.emojiSize(Font.scaledFootnoteFont.emojiSize)
|
||||||
.emojiBaselineOffset(Font.scaledFootnoteFont.emojiBaselineOffset)
|
.emojiBaselineOffset(Font.scaledFootnoteFont.emojiBaselineOffset)
|
||||||
.font(.scaledFootnote)
|
.font(.scaledFootnote)
|
||||||
|
.fontWeight(.bold)
|
||||||
.foregroundColor(theme.labelColor)
|
.foregroundColor(theme.labelColor)
|
||||||
Text("@\(account.acct)")
|
Text("@\(account.acct)")
|
||||||
.font(.scaledCaption)
|
.font(.scaledFootnote)
|
||||||
.foregroundColor(theme.tintColor)
|
.foregroundStyle(theme.tintColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,17 +80,53 @@ struct StatusEditorAutoCompleteView: View {
|
||||||
private var suggestionsTagView: some View {
|
private var suggestionsTagView: some View {
|
||||||
ForEach(viewModel.tagsSuggestions) { tag in
|
ForEach(viewModel.tagsSuggestions) { tag in
|
||||||
Button {
|
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: {
|
} label: {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
Text("#\(tag.name)")
|
Text("#\(tag.name)")
|
||||||
.font(.scaledFootnote)
|
.font(.scaledFootnote)
|
||||||
.foregroundColor(theme.tintColor)
|
.fontWeight(.bold)
|
||||||
|
.foregroundColor(theme.labelColor)
|
||||||
Text("tag.suggested.mentions-\(tag.totalUses)")
|
Text("tag.suggested.mentions-\(tag.totalUses)")
|
||||||
.font(.scaledCaption)
|
.font(.scaledFootnote)
|
||||||
.foregroundStyle(.secondary)
|
.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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -564,9 +564,9 @@ import SwiftUI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func selectHashtagSuggestion(tag: Tag) {
|
func selectHashtagSuggestion(tag: String) {
|
||||||
if let range = currentSuggestionRange {
|
if let range = currentSuggestionRange {
|
||||||
replaceTextWith(text: "#\(tag.name) ", inRange: range)
|
replaceTextWith(text: "#\(tag) ", inRange: range)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue