mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2025-04-27 02:14:45 +00:00
Split/Refactor editor autocomplete
This commit is contained in:
parent
e4f7a6954b
commit
bb55154b75
8 changed files with 374 additions and 174 deletions
|
@ -87,6 +87,9 @@
|
||||||
9FB183292AE9449100BBB692 /* IceCubesApp+Scene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FB183282AE9449100BBB692 /* IceCubesApp+Scene.swift */; };
|
9FB183292AE9449100BBB692 /* IceCubesApp+Scene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FB183282AE9449100BBB692 /* IceCubesApp+Scene.swift */; };
|
||||||
9FBFE63D292A715500C250E9 /* IceCubesApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FBFE63C292A715500C250E9 /* IceCubesApp.swift */; };
|
9FBFE63D292A715500C250E9 /* IceCubesApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FBFE63C292A715500C250E9 /* IceCubesApp.swift */; };
|
||||||
9FBFE64E292A72BD00C250E9 /* Network in Frameworks */ = {isa = PBXBuildFile; productRef = 9FBFE64D292A72BD00C250E9 /* Network */; };
|
9FBFE64E292A72BD00C250E9 /* Network in Frameworks */ = {isa = PBXBuildFile; productRef = 9FBFE64D292A72BD00C250E9 /* Network */; };
|
||||||
|
9FC14EF22B494D180006CEE1 /* TagsGroupSettingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FC14EF12B494D180006CEE1 /* TagsGroupSettingView.swift */; };
|
||||||
|
9FC14EF42B494D940006CEE1 /* RemoteTimelinesSettingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FC14EF32B494D940006CEE1 /* RemoteTimelinesSettingView.swift */; };
|
||||||
|
9FC14EF62B494DFF0006CEE1 /* RecenTagsSettingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FC14EF52B494DFF0006CEE1 /* RecenTagsSettingView.swift */; };
|
||||||
9FD34823293D06E800DB0EE9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9FD34822293D06E800DB0EE9 /* Assets.xcassets */; };
|
9FD34823293D06E800DB0EE9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9FD34822293D06E800DB0EE9 /* Assets.xcassets */; };
|
||||||
9FD542E72962D2FF0045321A /* Lists in Frameworks */ = {isa = PBXBuildFile; productRef = 9FD542E62962D2FF0045321A /* Lists */; };
|
9FD542E72962D2FF0045321A /* Lists in Frameworks */ = {isa = PBXBuildFile; productRef = 9FD542E62962D2FF0045321A /* Lists */; };
|
||||||
9FE151A6293C90F900E9683D /* IconSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FE151A5293C90F900E9683D /* IconSelectorView.swift */; };
|
9FE151A6293C90F900E9683D /* IconSelectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FE151A5293C90F900E9683D /* IconSelectorView.swift */; };
|
||||||
|
@ -225,6 +228,9 @@
|
||||||
9FB183282AE9449100BBB692 /* IceCubesApp+Scene.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IceCubesApp+Scene.swift"; sourceTree = "<group>"; };
|
9FB183282AE9449100BBB692 /* IceCubesApp+Scene.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IceCubesApp+Scene.swift"; sourceTree = "<group>"; };
|
||||||
9FBFE639292A715500C250E9 /* Ice Cubes.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Ice Cubes.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
9FBFE639292A715500C250E9 /* Ice Cubes.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Ice Cubes.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
9FBFE63C292A715500C250E9 /* IceCubesApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IceCubesApp.swift; sourceTree = "<group>"; };
|
9FBFE63C292A715500C250E9 /* IceCubesApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IceCubesApp.swift; sourceTree = "<group>"; };
|
||||||
|
9FC14EF12B494D180006CEE1 /* TagsGroupSettingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagsGroupSettingView.swift; sourceTree = "<group>"; };
|
||||||
|
9FC14EF32B494D940006CEE1 /* RemoteTimelinesSettingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteTimelinesSettingView.swift; sourceTree = "<group>"; };
|
||||||
|
9FC14EF52B494DFF0006CEE1 /* RecenTagsSettingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecenTagsSettingView.swift; sourceTree = "<group>"; };
|
||||||
9FC60CB82AE6C2F600C6EAD2 /* IceCubesActionExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = IceCubesActionExtension.entitlements; sourceTree = "<group>"; };
|
9FC60CB82AE6C2F600C6EAD2 /* IceCubesActionExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = IceCubesActionExtension.entitlements; sourceTree = "<group>"; };
|
||||||
9FCBB3D22984EFD5009B77EE /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
9FCBB3D22984EFD5009B77EE /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
9FCBB3D429859615009B77EE /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
9FCBB3D429859615009B77EE /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
|
@ -518,6 +524,9 @@
|
||||||
D08A9C3429956CFA00204A4A /* SwipeActionsSettingsView.swift */,
|
D08A9C3429956CFA00204A4A /* SwipeActionsSettingsView.swift */,
|
||||||
9FA6FD6129C04A8800E2312C /* TranslationSettingsView.swift */,
|
9FA6FD6129C04A8800E2312C /* TranslationSettingsView.swift */,
|
||||||
9F15D6012B3D6E280008C220 /* TabbarEntriesSettingsView.swift */,
|
9F15D6012B3D6E280008C220 /* TabbarEntriesSettingsView.swift */,
|
||||||
|
9FC14EF12B494D180006CEE1 /* TagsGroupSettingView.swift */,
|
||||||
|
9FC14EF32B494D940006CEE1 /* RemoteTimelinesSettingView.swift */,
|
||||||
|
9FC14EF52B494DFF0006CEE1 /* RecenTagsSettingView.swift */,
|
||||||
);
|
);
|
||||||
path = Settings;
|
path = Settings;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -706,7 +715,6 @@
|
||||||
be,
|
be,
|
||||||
uk,
|
uk,
|
||||||
"zh-Hant",
|
"zh-Hant",
|
||||||
Base,
|
|
||||||
);
|
);
|
||||||
mainGroup = 9FBFE630292A715500C250E9;
|
mainGroup = 9FBFE630292A715500C250E9;
|
||||||
packageReferences = (
|
packageReferences = (
|
||||||
|
@ -814,6 +822,8 @@
|
||||||
9F35DB4C2952005C00B3281A /* MessagesTab.swift in Sources */,
|
9F35DB4C2952005C00B3281A /* MessagesTab.swift in Sources */,
|
||||||
9FAD85CF2975B68900496AB1 /* SideBarView.swift in Sources */,
|
9FAD85CF2975B68900496AB1 /* SideBarView.swift in Sources */,
|
||||||
9FAE4ACB293783B000772766 /* SettingsTab.swift in Sources */,
|
9FAE4ACB293783B000772766 /* SettingsTab.swift in Sources */,
|
||||||
|
9FC14EF42B494D940006CEE1 /* RemoteTimelinesSettingView.swift in Sources */,
|
||||||
|
9FC14EF22B494D180006CEE1 /* TagsGroupSettingView.swift in Sources */,
|
||||||
9F15D6022B3D6E280008C220 /* TabbarEntriesSettingsView.swift in Sources */,
|
9F15D6022B3D6E280008C220 /* TabbarEntriesSettingsView.swift in Sources */,
|
||||||
9F7335F92968576500AFF0BA /* DisplaySettingsView.swift in Sources */,
|
9F7335F92968576500AFF0BA /* DisplaySettingsView.swift in Sources */,
|
||||||
9F2A540729699698009B2D7C /* SupportAppView.swift in Sources */,
|
9F2A540729699698009B2D7C /* SupportAppView.swift in Sources */,
|
||||||
|
@ -831,6 +841,7 @@
|
||||||
9F654BEF299AC45B00D27FA5 /* ReportView.swift in Sources */,
|
9F654BEF299AC45B00D27FA5 /* ReportView.swift in Sources */,
|
||||||
D08A9C3529956CFA00204A4A /* SwipeActionsSettingsView.swift in Sources */,
|
D08A9C3529956CFA00204A4A /* SwipeActionsSettingsView.swift in Sources */,
|
||||||
9F7335F22967608F00AFF0BA /* AddRemoteTimelineView.swift in Sources */,
|
9F7335F22967608F00AFF0BA /* AddRemoteTimelineView.swift in Sources */,
|
||||||
|
9FC14EF62B494DFF0006CEE1 /* RecenTagsSettingView.swift in Sources */,
|
||||||
9F6028562B3F36AE00476078 /* AppView.swift in Sources */,
|
9F6028562B3F36AE00476078 /* AppView.swift in Sources */,
|
||||||
9F55C68D2955968700F94077 /* ExploreTab.swift in Sources */,
|
9F55C68D2955968700F94077 /* ExploreTab.swift in Sources */,
|
||||||
9F1E8B47298EBCBB00609F80 /* HapticSettingsView.swift in Sources */,
|
9F1E8B47298EBCBB00609F80 /* HapticSettingsView.swift in Sources */,
|
||||||
|
|
|
@ -46648,6 +46648,124 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"settings.general.recent-tags" : {
|
||||||
|
"localizations" : {
|
||||||
|
"be" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "needs_review",
|
||||||
|
"value" : "Recently Used Tags"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ca" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "needs_review",
|
||||||
|
"value" : "Recently Used Tags"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"de" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "needs_review",
|
||||||
|
"value" : "Recently Used Tags"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Recently Used Tags"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"en-GB" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Recently Used Tags"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"es" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "needs_review",
|
||||||
|
"value" : "Recently Used Tags"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"eu" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "needs_review",
|
||||||
|
"value" : "Recently Used Tags"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fr" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "needs_review",
|
||||||
|
"value" : "Recently Used Tags"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"it" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "needs_review",
|
||||||
|
"value" : "Recently Used Tags"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ja" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "needs_review",
|
||||||
|
"value" : "Recently Used Tags"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ko" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "needs_review",
|
||||||
|
"value" : "Recently Used Tags"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nb" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "needs_review",
|
||||||
|
"value" : "Recently Used Tags"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nl" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "needs_review",
|
||||||
|
"value" : "Recently Used Tags"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pl" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "needs_review",
|
||||||
|
"value" : "Recently Used Tags"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pt-BR" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "needs_review",
|
||||||
|
"value" : "Recently Used Tags"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tr" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "needs_review",
|
||||||
|
"value" : "Recently Used Tags"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uk" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "needs_review",
|
||||||
|
"value" : "Recently Used Tags"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hans" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "needs_review",
|
||||||
|
"value" : "Recently Used Tags"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zh-Hant" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "needs_review",
|
||||||
|
"value" : "Recently Used Tags"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"settings.general.remote-timelines" : {
|
"settings.general.remote-timelines" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"be" : {
|
"be" : {
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
import DesignSystem
|
||||||
|
import EmojiText
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
import Models
|
||||||
|
import SwiftData
|
||||||
|
|
||||||
|
|
||||||
|
extension StatusEditorAutoCompleteView {
|
||||||
|
struct ExpandedView: View {
|
||||||
|
@Environment(\.modelContext) private var context
|
||||||
|
@Environment(Theme.self) private var theme
|
||||||
|
|
||||||
|
var viewModel: StatusEditorViewModel
|
||||||
|
@Binding var isTagSuggestionExpanded: Bool
|
||||||
|
|
||||||
|
@Query(sort: \RecentTag.lastUse, order: .reverse) var recentTags: [RecentTag]
|
||||||
|
|
||||||
|
var body: 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: 200)
|
||||||
|
.gesture(DragGesture(minimumDistance: 0, coordinateSpace: .local)
|
||||||
|
.onEnded({ value in
|
||||||
|
withAnimation {
|
||||||
|
if value.translation.height > 0 {
|
||||||
|
isTagSuggestionExpanded = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
import DesignSystem
|
||||||
|
import EmojiText
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
import Models
|
||||||
|
import SwiftData
|
||||||
|
|
||||||
|
|
||||||
|
extension StatusEditorAutoCompleteView {
|
||||||
|
struct MentionsView: View {
|
||||||
|
@Environment(Theme.self) private var theme
|
||||||
|
|
||||||
|
var viewModel: StatusEditorViewModel
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ForEach(viewModel.mentionsSuggestions) { account in
|
||||||
|
Button {
|
||||||
|
viewModel.selectMentionSuggestion(account: account)
|
||||||
|
} label: {
|
||||||
|
HStack {
|
||||||
|
AvatarView(account.avatar, config: AvatarView.FrameConfig.badge)
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
EmojiTextApp(.init(stringValue: account.safeDisplayName),
|
||||||
|
emojis: account.emojis)
|
||||||
|
.emojiSize(Font.scaledFootnoteFont.emojiSize)
|
||||||
|
.emojiBaselineOffset(Font.scaledFootnoteFont.emojiBaselineOffset)
|
||||||
|
.font(.scaledFootnote)
|
||||||
|
.fontWeight(.bold)
|
||||||
|
.foregroundColor(theme.labelColor)
|
||||||
|
Text("@\(account.acct)")
|
||||||
|
.font(.scaledFootnote)
|
||||||
|
.foregroundStyle(theme.tintColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
import DesignSystem
|
||||||
|
import EmojiText
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
import Models
|
||||||
|
import SwiftData
|
||||||
|
|
||||||
|
|
||||||
|
extension StatusEditorAutoCompleteView {
|
||||||
|
struct RecentTagsView: View {
|
||||||
|
@Environment(Theme.self) private var theme
|
||||||
|
|
||||||
|
var viewModel: StatusEditorViewModel
|
||||||
|
@Binding var isTagSuggestionExpanded: Bool
|
||||||
|
|
||||||
|
@Query(sort: \RecentTag.lastUse, order: .reverse) var recentTags: [RecentTag]
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ForEach(recentTags) { tag in
|
||||||
|
Button {
|
||||||
|
withAnimation {
|
||||||
|
isTagSuggestionExpanded = false
|
||||||
|
viewModel.selectHashtagSuggestion(tag: tag.title)
|
||||||
|
}
|
||||||
|
tag.lastUse = Date()
|
||||||
|
} label: {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text("#\(tag.title)")
|
||||||
|
.font(.scaledFootnote)
|
||||||
|
.fontWeight(.bold)
|
||||||
|
.foregroundColor(theme.labelColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
import DesignSystem
|
||||||
|
import EmojiText
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
import Models
|
||||||
|
import SwiftData
|
||||||
|
|
||||||
|
|
||||||
|
extension StatusEditorAutoCompleteView {
|
||||||
|
struct RemoteTagsView: View {
|
||||||
|
@Environment(\.modelContext) private var context
|
||||||
|
@Environment(Theme.self) private var theme
|
||||||
|
|
||||||
|
var viewModel: StatusEditorViewModel
|
||||||
|
@Binding var isTagSuggestionExpanded: Bool
|
||||||
|
|
||||||
|
@Query(sort: \RecentTag.lastUse, order: .reverse) var recentTags: [RecentTag]
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ForEach(viewModel.tagsSuggestions) { tag in
|
||||||
|
Button {
|
||||||
|
withAnimation {
|
||||||
|
isTagSuggestionExpanded = false
|
||||||
|
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)
|
||||||
|
.fontWeight(.bold)
|
||||||
|
.foregroundColor(theme.labelColor)
|
||||||
|
Text("tag.suggested.mentions-\(tag.totalUses)")
|
||||||
|
.font(.scaledFootnote)
|
||||||
|
.foregroundStyle(theme.tintColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
import DesignSystem
|
||||||
|
import EmojiText
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
import Models
|
||||||
|
import SwiftData
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
struct StatusEditorAutoCompleteView: View {
|
||||||
|
@Environment(\.modelContext) var context
|
||||||
|
|
||||||
|
@Environment(Theme.self) 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 ||
|
||||||
|
(viewModel.showRecentsTagsInline && !recentTags.isEmpty) {
|
||||||
|
VStack {
|
||||||
|
HStack {
|
||||||
|
ScrollView(.horizontal, showsIndicators: false) {
|
||||||
|
LazyHStack {
|
||||||
|
if !viewModel.mentionsSuggestions.isEmpty {
|
||||||
|
Self.MentionsView(viewModel: viewModel)
|
||||||
|
} 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(height: 40)
|
||||||
|
if isTagSuggestionExpanded {
|
||||||
|
Self.ExpandedView(viewModel: viewModel, isTagSuggestionExpanded: $isTagSuggestionExpanded)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.background(.thinMaterial)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
private var suggestionsTagView: some View {
|
||||||
|
if viewModel.showRecentsTagsInline {
|
||||||
|
Self.RecentTagsView(viewModel: viewModel, isTagSuggestionExpanded: $isTagSuggestionExpanded)
|
||||||
|
} else {
|
||||||
|
Self.RemoteTagsView(viewModel: viewModel, isTagSuggestionExpanded: $isTagSuggestionExpanded)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,173 +0,0 @@
|
||||||
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 ||
|
|
||||||
(viewModel.showRecentsTagsInline && !recentTags.isEmpty) {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.frame(height: 40)
|
|
||||||
if isTagSuggestionExpanded {
|
|
||||||
expandedTagsSuggestionView
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.background(.thinMaterial)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var suggestionsMentionsView: some View {
|
|
||||||
ForEach(viewModel.mentionsSuggestions) { account in
|
|
||||||
Button {
|
|
||||||
viewModel.selectMentionSuggestion(account: account)
|
|
||||||
} label: {
|
|
||||||
HStack {
|
|
||||||
AvatarView(account.avatar, config: AvatarView.FrameConfig.badge)
|
|
||||||
VStack(alignment: .leading) {
|
|
||||||
EmojiTextApp(.init(stringValue: account.safeDisplayName),
|
|
||||||
emojis: account.emojis)
|
|
||||||
.emojiSize(Font.scaledFootnoteFont.emojiSize)
|
|
||||||
.emojiBaselineOffset(Font.scaledFootnoteFont.emojiBaselineOffset)
|
|
||||||
.font(.scaledFootnote)
|
|
||||||
.fontWeight(.bold)
|
|
||||||
.foregroundColor(theme.labelColor)
|
|
||||||
Text("@\(account.acct)")
|
|
||||||
.font(.scaledFootnote)
|
|
||||||
.foregroundStyle(theme.tintColor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ViewBuilder
|
|
||||||
private var suggestionsTagView: some View {
|
|
||||||
if viewModel.showRecentsTagsInline {
|
|
||||||
recentTagsSuggestionView
|
|
||||||
} else {
|
|
||||||
remoteTagsSuggestionView
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var recentTagsSuggestionView: some View {
|
|
||||||
ForEach(recentTags) { tag in
|
|
||||||
Button {
|
|
||||||
withAnimation {
|
|
||||||
isTagSuggestionExpanded = false
|
|
||||||
viewModel.selectHashtagSuggestion(tag: tag.title)
|
|
||||||
}
|
|
||||||
tag.lastUse = Date()
|
|
||||||
} label: {
|
|
||||||
VStack(alignment: .leading) {
|
|
||||||
Text("#\(tag.title)")
|
|
||||||
.font(.scaledFootnote)
|
|
||||||
.fontWeight(.bold)
|
|
||||||
.foregroundColor(theme.labelColor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var remoteTagsSuggestionView: some View {
|
|
||||||
ForEach(viewModel.tagsSuggestions) { tag in
|
|
||||||
Button {
|
|
||||||
withAnimation {
|
|
||||||
isTagSuggestionExpanded = false
|
|
||||||
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)
|
|
||||||
.fontWeight(.bold)
|
|
||||||
.foregroundColor(theme.labelColor)
|
|
||||||
Text("tag.suggested.mentions-\(tag.totalUses)")
|
|
||||||
.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: 200)
|
|
||||||
.gesture(DragGesture(minimumDistance: 0, coordinateSpace: .local)
|
|
||||||
.onEnded({ value in
|
|
||||||
withAnimation {
|
|
||||||
if value.translation.height > 0 {
|
|
||||||
isTagSuggestionExpanded = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue