Migrate drafts to SwiftData

This commit is contained in:
Thomas Ricouard 2023-09-22 09:31:35 +02:00
parent 7eec1b8439
commit 0c4bde40af
8 changed files with 123 additions and 61 deletions

View file

@ -47,7 +47,7 @@
9F35DB4729506F6600B3281A /* NotificationTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F35DB4629506F6600B3281A /* NotificationTab.swift */; };
9F35DB4A29506FA100B3281A /* Notifications in Frameworks */ = {isa = PBXBuildFile; productRef = 9F35DB4929506FA100B3281A /* Notifications */; };
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 */; };
9F398AAB2935FFDB00A889F2 /* Models in Frameworks */ = {isa = PBXBuildFile; productRef = 9F398AAA2935FFDB00A889F2 /* Models */; };
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>"; };
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>"; };
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>"; };
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>"; };
@ -377,7 +377,7 @@
9F654BF0299AC46200D27FA5 /* Report */,
9FAE4AC9293783A200772766 /* Tabs */,
9FBFE63C292A715500C250E9 /* IceCubesApp.swift */,
9F398AA52935FE8A00A889F2 /* AppRouter.swift */,
9F398AA52935FE8A00A889F2 /* AppRegistry.swift */,
639CDF9B296AC82F00C35E58 /* SafariRouter.swift */,
9FAD85A7297582F100496AB1 /* QuickLookRepresentable.swift */,
9FAD85CE2975B68900496AB1 /* SideBarView.swift */,
@ -842,7 +842,7 @@
FA31A9AB2A66BF7C00D5F662 /* EditTagGroupView.swift in Sources */,
FAD203D02A66D8A80030A7FD /* Symbols.swift in Sources */,
9F398AB329360A4C00A889F2 /* TimelineTab.swift in Sources */,
9F398AA62935FE8A00A889F2 /* AppRouter.swift in Sources */,
9F398AA62935FE8A00A889F2 /* AppRegistry.swift in Sources */,
9FBFE63D292A715500C250E9 /* IceCubesApp.swift in Sources */,
9F4A48192976B21900A1A038 /* ProfileTab.swift in Sources */,
9F2B92FA295DA7D700DE16D0 /* AddAccountsView.swift in Sources */,

View file

@ -120,6 +120,12 @@ extension View {
.environment(PushNotificationsService.shared)
.environment(AppAccountsManager.shared.currentClient)
}
func withModelContainer() -> some View {
modelContainer(for: [
Draft.self,
])
}
}
struct ActivityView: UIViewControllerRepresentable {

View file

@ -8,6 +8,7 @@ import Network
import RevenueCat
import SwiftUI
import Timeline
import Status
@main
struct IceCubesApp: App {
@ -75,6 +76,7 @@ struct IceCubesApp: App {
}
}
}
.withModelContainer()
}
.commands {
appMenu

View file

@ -10,7 +10,6 @@ import SwiftUI
@AppStorage("remote_local_timeline") public var remoteLocalTimelines: [String] = []
@AppStorage("tag_groups") public var tagGroups: [TagGroup] = []
@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("is_open_ai_enabled") public var isOpenAIEnabled: Bool = true
@ -79,11 +78,7 @@ import SwiftUI
storage.preferredBrowser = preferredBrowser
}
}
public var draftsPosts: [String] {
didSet {
storage.draftsPosts = draftsPosts
}
}
public var showTranslateButton: Bool {
didSet {
storage.showTranslateButton = showTranslateButton
@ -379,7 +374,6 @@ import SwiftUI
remoteLocalTimelines = storage.remoteLocalTimelines
tagGroups = storage.tagGroups
preferredBrowser = storage.preferredBrowser
draftsPosts = storage.draftsPosts
showTranslateButton = storage.showTranslateButton
isOpenAIEnabled = storage.isOpenAIEnabled
recentlyUsedLanguages = storage.recentlyUsedLanguages

View file

@ -111,9 +111,10 @@ struct StatusEditorAccessoryView: View {
.accessibilityLabel("accessibility.editor.button.drafts")
.popover(isPresented: $isDraftsSheetDisplayed) {
if UIDevice.current.userInterfaceIdiom == .phone {
draftsSheetView
draftsListView
.presentationDetents([.medium])
} else {
draftsSheetView
draftsListView
.frame(width: 400, height: 500)
}
}
@ -176,6 +177,16 @@ struct StatusEditorAccessoryView: View {
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
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 {
NavigationStack {
ScrollView {

View 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()
}
}

View file

@ -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 = []
}
}

View file

@ -19,6 +19,7 @@ public struct StatusEditorView: View {
@Environment(Client.self) private var client
@Environment(CurrentAccount.self) private var currentAccount
@Environment(\.dismiss) private var dismiss
@Environment(\.modelContext) private var context
@State private var viewModel: StatusEditorViewModel
@FocusState private var isSpoilerTextFocused: Bool
@ -144,22 +145,23 @@ public struct StatusEditorView: View {
Text("action.cancel")
}
.keyboardShortcut(.cancelAction)
.confirmationDialog("",
isPresented: $isDismissAlertPresented,
actions: {
Button("status.draft.delete", role: .destructive) {
dismiss()
NotificationCenter.default.post(name: NotificationsName.shareSheetClose,
object: nil)
}
Button("status.draft.save") {
preferences.draftsPosts.insert(viewModel.statusText.string, at: 0)
dismiss()
NotificationCenter.default.post(name: NotificationsName.shareSheetClose,
object: nil)
}
Button("action.cancel", role: .cancel) {}
})
.confirmationDialog(
"",
isPresented: $isDismissAlertPresented,
actions: {
Button("status.draft.delete", role: .destructive) {
dismiss()
NotificationCenter.default.post(name: NotificationsName.shareSheetClose,
object: nil)
}
Button("status.draft.save") {
context.insert(Draft(content: viewModel.statusText.string))
dismiss()
NotificationCenter.default.post(name: NotificationsName.shareSheetClose,
object: nil)
}
Button("action.cancel", role: .cancel) {}
})
}
}
}