mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2025-04-27 02:14:45 +00:00
Migrate drafts to SwiftData
This commit is contained in:
parent
7eec1b8439
commit
0c4bde40af
8 changed files with 123 additions and 61 deletions
|
@ -47,7 +47,7 @@
|
||||||
9F35DB4729506F6600B3281A /* NotificationTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F35DB4629506F6600B3281A /* NotificationTab.swift */; };
|
9F35DB4729506F6600B3281A /* NotificationTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F35DB4629506F6600B3281A /* NotificationTab.swift */; };
|
||||||
9F35DB4A29506FA100B3281A /* Notifications in Frameworks */ = {isa = PBXBuildFile; productRef = 9F35DB4929506FA100B3281A /* Notifications */; };
|
9F35DB4A29506FA100B3281A /* Notifications in Frameworks */ = {isa = PBXBuildFile; productRef = 9F35DB4929506FA100B3281A /* Notifications */; };
|
||||||
9F35DB4C2952005C00B3281A /* MessagesTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F35DB4B2952005C00B3281A /* MessagesTab.swift */; };
|
9F35DB4C2952005C00B3281A /* MessagesTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F35DB4B2952005C00B3281A /* MessagesTab.swift */; };
|
||||||
9F398AA62935FE8A00A889F2 /* AppRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F398AA52935FE8A00A889F2 /* AppRouter.swift */; };
|
9F398AA62935FE8A00A889F2 /* AppRegistry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F398AA52935FE8A00A889F2 /* AppRegistry.swift */; };
|
||||||
9F398AA92935FFDB00A889F2 /* Account in Frameworks */ = {isa = PBXBuildFile; productRef = 9F398AA82935FFDB00A889F2 /* Account */; };
|
9F398AA92935FFDB00A889F2 /* Account in Frameworks */ = {isa = PBXBuildFile; productRef = 9F398AA82935FFDB00A889F2 /* Account */; };
|
||||||
9F398AAB2935FFDB00A889F2 /* Models in Frameworks */ = {isa = PBXBuildFile; productRef = 9F398AAA2935FFDB00A889F2 /* Models */; };
|
9F398AAB2935FFDB00A889F2 /* Models in Frameworks */ = {isa = PBXBuildFile; productRef = 9F398AAA2935FFDB00A889F2 /* Models */; };
|
||||||
9F398AB329360A4C00A889F2 /* TimelineTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F398AB229360A4C00A889F2 /* TimelineTab.swift */; };
|
9F398AB329360A4C00A889F2 /* TimelineTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F398AB229360A4C00A889F2 /* TimelineTab.swift */; };
|
||||||
|
@ -207,7 +207,7 @@
|
||||||
9F35DB4B2952005C00B3281A /* MessagesTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesTab.swift; sourceTree = "<group>"; };
|
9F35DB4B2952005C00B3281A /* MessagesTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesTab.swift; sourceTree = "<group>"; };
|
||||||
9F38C233297D03120018F11E /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
|
9F38C233297D03120018F11E /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
9F398AA32935F90100A889F2 /* Models */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = Models; path = Packages/Models; sourceTree = "<group>"; };
|
9F398AA32935F90100A889F2 /* Models */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = Models; path = Packages/Models; sourceTree = "<group>"; };
|
||||||
9F398AA52935FE8A00A889F2 /* AppRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRouter.swift; sourceTree = "<group>"; };
|
9F398AA52935FE8A00A889F2 /* AppRegistry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRegistry.swift; sourceTree = "<group>"; };
|
||||||
9F398AAC2936005300A889F2 /* Account */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = Account; path = Packages/Account; sourceTree = "<group>"; };
|
9F398AAC2936005300A889F2 /* Account */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = Account; path = Packages/Account; sourceTree = "<group>"; };
|
||||||
9F398AB229360A4C00A889F2 /* TimelineTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineTab.swift; sourceTree = "<group>"; };
|
9F398AB229360A4C00A889F2 /* TimelineTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineTab.swift; sourceTree = "<group>"; };
|
||||||
9F4A48182976B21900A1A038 /* ProfileTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileTab.swift; sourceTree = "<group>"; };
|
9F4A48182976B21900A1A038 /* ProfileTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileTab.swift; sourceTree = "<group>"; };
|
||||||
|
@ -377,7 +377,7 @@
|
||||||
9F654BF0299AC46200D27FA5 /* Report */,
|
9F654BF0299AC46200D27FA5 /* Report */,
|
||||||
9FAE4AC9293783A200772766 /* Tabs */,
|
9FAE4AC9293783A200772766 /* Tabs */,
|
||||||
9FBFE63C292A715500C250E9 /* IceCubesApp.swift */,
|
9FBFE63C292A715500C250E9 /* IceCubesApp.swift */,
|
||||||
9F398AA52935FE8A00A889F2 /* AppRouter.swift */,
|
9F398AA52935FE8A00A889F2 /* AppRegistry.swift */,
|
||||||
639CDF9B296AC82F00C35E58 /* SafariRouter.swift */,
|
639CDF9B296AC82F00C35E58 /* SafariRouter.swift */,
|
||||||
9FAD85A7297582F100496AB1 /* QuickLookRepresentable.swift */,
|
9FAD85A7297582F100496AB1 /* QuickLookRepresentable.swift */,
|
||||||
9FAD85CE2975B68900496AB1 /* SideBarView.swift */,
|
9FAD85CE2975B68900496AB1 /* SideBarView.swift */,
|
||||||
|
@ -842,7 +842,7 @@
|
||||||
FA31A9AB2A66BF7C00D5F662 /* EditTagGroupView.swift in Sources */,
|
FA31A9AB2A66BF7C00D5F662 /* EditTagGroupView.swift in Sources */,
|
||||||
FAD203D02A66D8A80030A7FD /* Symbols.swift in Sources */,
|
FAD203D02A66D8A80030A7FD /* Symbols.swift in Sources */,
|
||||||
9F398AB329360A4C00A889F2 /* TimelineTab.swift in Sources */,
|
9F398AB329360A4C00A889F2 /* TimelineTab.swift in Sources */,
|
||||||
9F398AA62935FE8A00A889F2 /* AppRouter.swift in Sources */,
|
9F398AA62935FE8A00A889F2 /* AppRegistry.swift in Sources */,
|
||||||
9FBFE63D292A715500C250E9 /* IceCubesApp.swift in Sources */,
|
9FBFE63D292A715500C250E9 /* IceCubesApp.swift in Sources */,
|
||||||
9F4A48192976B21900A1A038 /* ProfileTab.swift in Sources */,
|
9F4A48192976B21900A1A038 /* ProfileTab.swift in Sources */,
|
||||||
9F2B92FA295DA7D700DE16D0 /* AddAccountsView.swift in Sources */,
|
9F2B92FA295DA7D700DE16D0 /* AddAccountsView.swift in Sources */,
|
||||||
|
|
|
@ -120,6 +120,12 @@ extension View {
|
||||||
.environment(PushNotificationsService.shared)
|
.environment(PushNotificationsService.shared)
|
||||||
.environment(AppAccountsManager.shared.currentClient)
|
.environment(AppAccountsManager.shared.currentClient)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func withModelContainer() -> some View {
|
||||||
|
modelContainer(for: [
|
||||||
|
Draft.self,
|
||||||
|
])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ActivityView: UIViewControllerRepresentable {
|
struct ActivityView: UIViewControllerRepresentable {
|
|
@ -8,6 +8,7 @@ import Network
|
||||||
import RevenueCat
|
import RevenueCat
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Timeline
|
import Timeline
|
||||||
|
import Status
|
||||||
|
|
||||||
@main
|
@main
|
||||||
struct IceCubesApp: App {
|
struct IceCubesApp: App {
|
||||||
|
@ -75,6 +76,7 @@ struct IceCubesApp: App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.withModelContainer()
|
||||||
}
|
}
|
||||||
.commands {
|
.commands {
|
||||||
appMenu
|
appMenu
|
||||||
|
|
|
@ -10,7 +10,6 @@ import SwiftUI
|
||||||
@AppStorage("remote_local_timeline") public var remoteLocalTimelines: [String] = []
|
@AppStorage("remote_local_timeline") public var remoteLocalTimelines: [String] = []
|
||||||
@AppStorage("tag_groups") public var tagGroups: [TagGroup] = []
|
@AppStorage("tag_groups") public var tagGroups: [TagGroup] = []
|
||||||
@AppStorage("preferred_browser") public var preferredBrowser: PreferredBrowser = .inAppSafari
|
@AppStorage("preferred_browser") public var preferredBrowser: PreferredBrowser = .inAppSafari
|
||||||
@AppStorage("draft_posts") public var draftsPosts: [String] = []
|
|
||||||
@AppStorage("show_translate_button_inline") public var showTranslateButton: Bool = true
|
@AppStorage("show_translate_button_inline") public var showTranslateButton: Bool = true
|
||||||
@AppStorage("is_open_ai_enabled") public var isOpenAIEnabled: Bool = true
|
@AppStorage("is_open_ai_enabled") public var isOpenAIEnabled: Bool = true
|
||||||
|
|
||||||
|
@ -79,11 +78,7 @@ import SwiftUI
|
||||||
storage.preferredBrowser = preferredBrowser
|
storage.preferredBrowser = preferredBrowser
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public var draftsPosts: [String] {
|
|
||||||
didSet {
|
|
||||||
storage.draftsPosts = draftsPosts
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public var showTranslateButton: Bool {
|
public var showTranslateButton: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
storage.showTranslateButton = showTranslateButton
|
storage.showTranslateButton = showTranslateButton
|
||||||
|
@ -379,7 +374,6 @@ import SwiftUI
|
||||||
remoteLocalTimelines = storage.remoteLocalTimelines
|
remoteLocalTimelines = storage.remoteLocalTimelines
|
||||||
tagGroups = storage.tagGroups
|
tagGroups = storage.tagGroups
|
||||||
preferredBrowser = storage.preferredBrowser
|
preferredBrowser = storage.preferredBrowser
|
||||||
draftsPosts = storage.draftsPosts
|
|
||||||
showTranslateButton = storage.showTranslateButton
|
showTranslateButton = storage.showTranslateButton
|
||||||
isOpenAIEnabled = storage.isOpenAIEnabled
|
isOpenAIEnabled = storage.isOpenAIEnabled
|
||||||
recentlyUsedLanguages = storage.recentlyUsedLanguages
|
recentlyUsedLanguages = storage.recentlyUsedLanguages
|
||||||
|
|
|
@ -111,9 +111,10 @@ struct StatusEditorAccessoryView: View {
|
||||||
.accessibilityLabel("accessibility.editor.button.drafts")
|
.accessibilityLabel("accessibility.editor.button.drafts")
|
||||||
.popover(isPresented: $isDraftsSheetDisplayed) {
|
.popover(isPresented: $isDraftsSheetDisplayed) {
|
||||||
if UIDevice.current.userInterfaceIdiom == .phone {
|
if UIDevice.current.userInterfaceIdiom == .phone {
|
||||||
draftsSheetView
|
draftsListView
|
||||||
|
.presentationDetents([.medium])
|
||||||
} else {
|
} else {
|
||||||
draftsSheetView
|
draftsListView
|
||||||
.frame(width: 400, height: 500)
|
.frame(width: 400, height: 500)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,6 +177,16 @@ struct StatusEditorAccessoryView: View {
|
||||||
viewModel.setInitialLanguageSelection(preference: preferences.recentlyUsedLanguages.first ?? preferences.serverPreferences?.postLanguage)
|
viewModel.setInitialLanguageSelection(preference: preferences.recentlyUsedLanguages.first ?? preferences.serverPreferences?.postLanguage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var draftsListView: some View {
|
||||||
|
DraftsListView(selectedDraft: .init(get: {
|
||||||
|
nil
|
||||||
|
}, set: { draft in
|
||||||
|
if let draft {
|
||||||
|
viewModel.insertStatusText(text: draft.content)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private func languageTextView(isoCode: String, nativeName: String?, name: String?) -> some View {
|
private func languageTextView(isoCode: String, nativeName: String?, name: String?) -> some View {
|
||||||
|
@ -269,38 +280,6 @@ struct StatusEditorAccessoryView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var draftsSheetView: some View {
|
|
||||||
NavigationStack {
|
|
||||||
List {
|
|
||||||
ForEach(preferences.draftsPosts, id: \.self) { draft in
|
|
||||||
Button {
|
|
||||||
viewModel.insertStatusText(text: draft)
|
|
||||||
isDraftsSheetDisplayed = false
|
|
||||||
} label: {
|
|
||||||
Text(draft)
|
|
||||||
.lineLimit(3)
|
|
||||||
.foregroundStyle(theme.labelColor)
|
|
||||||
}.listRowBackground(theme.primaryBackgroundColor)
|
|
||||||
}
|
|
||||||
.onDelete { indexes in
|
|
||||||
if let index = indexes.first {
|
|
||||||
preferences.draftsPosts.remove(at: index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.toolbar {
|
|
||||||
ToolbarItem(placement: .navigationBarLeading) {
|
|
||||||
Button("action.cancel", action: { isDraftsSheetDisplayed = false })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.scrollContentBackground(.hidden)
|
|
||||||
.background(theme.secondaryBackgroundColor)
|
|
||||||
.navigationTitle("status.editor.drafts.navigation-title")
|
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
|
||||||
}
|
|
||||||
.presentationDetents([.medium])
|
|
||||||
}
|
|
||||||
|
|
||||||
private var customEmojisSheet: some View {
|
private var customEmojisSheet: some View {
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
|
|
15
Packages/Status/Sources/Status/Editor/Drafts/Draft.swift
Normal file
15
Packages/Status/Sources/Status/Editor/Drafts/Draft.swift
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import SwiftData
|
||||||
|
import SwiftUI
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
@Model public class Draft: Identifiable {
|
||||||
|
@Attribute(.unique) public var id: UUID
|
||||||
|
public var content: String
|
||||||
|
public var creationDate: Date
|
||||||
|
|
||||||
|
public init(content: String) {
|
||||||
|
self.id = UUID()
|
||||||
|
self.content = content
|
||||||
|
self.creationDate = Date()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
import SwiftUI
|
||||||
|
import SwiftData
|
||||||
|
import DesignSystem
|
||||||
|
|
||||||
|
struct DraftsListView: View {
|
||||||
|
@AppStorage("draft_posts") public var legacyDraftPosts: [String] = []
|
||||||
|
|
||||||
|
@Environment(\.dismiss) private var dismiss
|
||||||
|
@Environment(\.modelContext) private var context
|
||||||
|
|
||||||
|
@Environment(Theme.self) private var theme
|
||||||
|
|
||||||
|
@Query(sort: \Draft.creationDate, order: .reverse) var drafts: [Draft]
|
||||||
|
|
||||||
|
@Binding var selectedDraft: Draft?
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
NavigationStack {
|
||||||
|
List {
|
||||||
|
ForEach(drafts) { draft in
|
||||||
|
Button {
|
||||||
|
selectedDraft = draft
|
||||||
|
dismiss()
|
||||||
|
} label: {
|
||||||
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
|
Text(draft.content)
|
||||||
|
.font(.body)
|
||||||
|
.lineLimit(3)
|
||||||
|
.foregroundStyle(theme.labelColor)
|
||||||
|
Text(draft.creationDate, style: .relative)
|
||||||
|
.font(.footnote)
|
||||||
|
.foregroundStyle(.gray)
|
||||||
|
}
|
||||||
|
}.listRowBackground(theme.primaryBackgroundColor)
|
||||||
|
}
|
||||||
|
.onDelete { indexes in
|
||||||
|
if let index = indexes.first {
|
||||||
|
context.delete(drafts[index])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItem(placement: .navigationBarLeading) {
|
||||||
|
Button("action.cancel", action: { dismiss() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.scrollContentBackground(.hidden)
|
||||||
|
.background(theme.secondaryBackgroundColor)
|
||||||
|
.navigationTitle("status.editor.drafts.navigation-title")
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
.onAppear {
|
||||||
|
migrateUserPreferencesDraft()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func migrateUserPreferencesDraft() {
|
||||||
|
for draft in legacyDraftPosts {
|
||||||
|
let newDraft = Draft(content: draft)
|
||||||
|
context.insert(newDraft)
|
||||||
|
}
|
||||||
|
legacyDraftPosts = []
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ public struct StatusEditorView: View {
|
||||||
@Environment(Client.self) private var client
|
@Environment(Client.self) private var client
|
||||||
@Environment(CurrentAccount.self) private var currentAccount
|
@Environment(CurrentAccount.self) private var currentAccount
|
||||||
@Environment(\.dismiss) private var dismiss
|
@Environment(\.dismiss) private var dismiss
|
||||||
|
@Environment(\.modelContext) private var context
|
||||||
|
|
||||||
@State private var viewModel: StatusEditorViewModel
|
@State private var viewModel: StatusEditorViewModel
|
||||||
@FocusState private var isSpoilerTextFocused: Bool
|
@FocusState private var isSpoilerTextFocused: Bool
|
||||||
|
@ -144,22 +145,23 @@ public struct StatusEditorView: View {
|
||||||
Text("action.cancel")
|
Text("action.cancel")
|
||||||
}
|
}
|
||||||
.keyboardShortcut(.cancelAction)
|
.keyboardShortcut(.cancelAction)
|
||||||
.confirmationDialog("",
|
.confirmationDialog(
|
||||||
isPresented: $isDismissAlertPresented,
|
"",
|
||||||
actions: {
|
isPresented: $isDismissAlertPresented,
|
||||||
Button("status.draft.delete", role: .destructive) {
|
actions: {
|
||||||
dismiss()
|
Button("status.draft.delete", role: .destructive) {
|
||||||
NotificationCenter.default.post(name: NotificationsName.shareSheetClose,
|
dismiss()
|
||||||
object: nil)
|
NotificationCenter.default.post(name: NotificationsName.shareSheetClose,
|
||||||
}
|
object: nil)
|
||||||
Button("status.draft.save") {
|
}
|
||||||
preferences.draftsPosts.insert(viewModel.statusText.string, at: 0)
|
Button("status.draft.save") {
|
||||||
dismiss()
|
context.insert(Draft(content: viewModel.statusText.string))
|
||||||
NotificationCenter.default.post(name: NotificationsName.shareSheetClose,
|
dismiss()
|
||||||
object: nil)
|
NotificationCenter.default.post(name: NotificationsName.shareSheetClose,
|
||||||
}
|
object: nil)
|
||||||
Button("action.cancel", role: .cancel) {}
|
}
|
||||||
})
|
Button("action.cancel", role: .cancel) {}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue