mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2025-04-27 02:14:45 +00:00
Automatically remove spaces in server names (#1600)
* Automatically remove spaces in server names If a server name includes a space (which can happen if the string is pasted / autocompleted), this space is removed, which results in the app not crashing. Fixes #1599 Signed-off-by: Paul Schuetz <pa.schuetz@web.de> * Format --------- Signed-off-by: Paul Schuetz <pa.schuetz@web.de> Co-authored-by: Thomas Ricouard <ricouard77@gmail.com>
This commit is contained in:
parent
d32c5c004c
commit
0b5e764556
21 changed files with 135 additions and 99 deletions
|
@ -31,12 +31,12 @@ extension View {
|
||||||
case let .conversationDetail(conversation):
|
case let .conversationDetail(conversation):
|
||||||
ConversationDetailView(conversation: conversation)
|
ConversationDetailView(conversation: conversation)
|
||||||
case let .hashTag(tag, accountId):
|
case let .hashTag(tag, accountId):
|
||||||
TimelineView(timeline: .constant(.hashtag(tag: tag, accountId: accountId)),
|
TimelineView(timeline: .constant(.hashtag(tag: tag, accountId: accountId)),
|
||||||
selectedTagGroup: .constant(nil),
|
selectedTagGroup: .constant(nil),
|
||||||
scrollToTopSignal: .constant(0),
|
scrollToTopSignal: .constant(0),
|
||||||
canFilterTimeline: false)
|
canFilterTimeline: false)
|
||||||
case let .list(list):
|
case let .list(list):
|
||||||
TimelineView(timeline: .constant(.list(list: list)),
|
TimelineView(timeline: .constant(.list(list: list)),
|
||||||
selectedTagGroup: .constant(nil),
|
selectedTagGroup: .constant(nil),
|
||||||
scrollToTopSignal: .constant(0),
|
scrollToTopSignal: .constant(0),
|
||||||
canFilterTimeline: false)
|
canFilterTimeline: false)
|
||||||
|
@ -131,12 +131,12 @@ extension View {
|
||||||
.environment(PushNotificationsService.shared)
|
.environment(PushNotificationsService.shared)
|
||||||
.environment(AppAccountsManager.shared.currentClient)
|
.environment(AppAccountsManager.shared.currentClient)
|
||||||
}
|
}
|
||||||
|
|
||||||
func withModelContainer() -> some View {
|
func withModelContainer() -> some View {
|
||||||
modelContainer(for: [
|
modelContainer(for: [
|
||||||
Draft.self,
|
Draft.self,
|
||||||
LocalTimeline.self,
|
LocalTimeline.self,
|
||||||
TagGroup.self
|
TagGroup.self,
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,9 @@ import Env
|
||||||
import KeychainSwift
|
import KeychainSwift
|
||||||
import Network
|
import Network
|
||||||
import RevenueCat
|
import RevenueCat
|
||||||
|
import Status
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Timeline
|
import Timeline
|
||||||
import Status
|
|
||||||
|
|
||||||
@main
|
@main
|
||||||
struct IceCubesApp: App {
|
struct IceCubesApp: App {
|
||||||
|
@ -217,7 +217,7 @@ struct IceCubesApp: App {
|
||||||
case .active:
|
case .active:
|
||||||
watcher.watch(streams: [.user, .direct])
|
watcher.watch(streams: [.user, .direct])
|
||||||
UNUserNotificationCenter.current().setBadgeCount(0)
|
UNUserNotificationCenter.current().setBadgeCount(0)
|
||||||
userPreferences.reloadNotificationsCount(tokens: appAccountsManager.availableAccounts.compactMap{ $0.oauthToken })
|
userPreferences.reloadNotificationsCount(tokens: appAccountsManager.availableAccounts.compactMap(\.oauthToken))
|
||||||
Task {
|
Task {
|
||||||
await userPreferences.refreshServerPreferences()
|
await userPreferences.refreshServerPreferences()
|
||||||
}
|
}
|
||||||
|
@ -286,10 +286,9 @@ class AppDelegate: NSObject, UIApplicationDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
func application(_: UIApplication, didFailToRegisterForRemoteNotificationsWithError _: Error) {}
|
func application(_: UIApplication, didFailToRegisterForRemoteNotificationsWithError _: Error) {}
|
||||||
|
|
||||||
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) async -> UIBackgroundFetchResult {
|
func application(_: UIApplication, didReceiveRemoteNotification _: [AnyHashable: Any]) async -> UIBackgroundFetchResult {
|
||||||
|
UserPreferences.shared.reloadNotificationsCount(tokens: AppAccountsManager.shared.availableAccounts.compactMap(\.oauthToken))
|
||||||
UserPreferences.shared.reloadNotificationsCount(tokens: AppAccountsManager.shared.availableAccounts.compactMap{ $0.oauthToken })
|
|
||||||
return .noData
|
return .noData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,10 @@ struct AddAccountView: View {
|
||||||
|
|
||||||
@FocusState private var isInstanceURLFieldFocused: Bool
|
@FocusState private var isInstanceURLFieldFocused: Bool
|
||||||
|
|
||||||
|
private func cleanServerStr(_ server: String) -> String {
|
||||||
|
server.replacingOccurrences(of: " ", with: "")
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
Form {
|
Form {
|
||||||
|
@ -54,6 +58,9 @@ struct AddAccountView: View {
|
||||||
.textInputAutocapitalization(.never)
|
.textInputAutocapitalization(.never)
|
||||||
.autocorrectionDisabled()
|
.autocorrectionDisabled()
|
||||||
.focused($isInstanceURLFieldFocused)
|
.focused($isInstanceURLFieldFocused)
|
||||||
|
.onChange(of: instanceName) { _, _ in
|
||||||
|
instanceName = cleanServerStr(instanceName)
|
||||||
|
}
|
||||||
if let instanceFetchError {
|
if let instanceFetchError {
|
||||||
Text(instanceFetchError)
|
Text(instanceFetchError)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,9 @@ import Foundation
|
||||||
import Models
|
import Models
|
||||||
import Network
|
import Network
|
||||||
import Nuke
|
import Nuke
|
||||||
|
import SwiftData
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Timeline
|
import Timeline
|
||||||
import SwiftData
|
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
struct SettingsTabs: View {
|
struct SettingsTabs: View {
|
||||||
|
@ -29,7 +29,7 @@ struct SettingsTabs: View {
|
||||||
@State private var timelineCache = TimelineCache()
|
@State private var timelineCache = TimelineCache()
|
||||||
|
|
||||||
@Binding var popToRootTab: Tab
|
@Binding var popToRootTab: Tab
|
||||||
|
|
||||||
@Query(sort: \LocalTimeline.creationDate, order: .reverse) var localTimelines: [LocalTimeline]
|
@Query(sort: \LocalTimeline.creationDate, order: .reverse) var localTimelines: [LocalTimeline]
|
||||||
@Query(sort: \TagGroup.creationDate, order: .reverse) var tagGroups: [TagGroup]
|
@Query(sort: \TagGroup.creationDate, order: .reverse) var tagGroups: [TagGroup]
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ struct TranslationSettingsView: View {
|
||||||
}
|
}
|
||||||
.onAppear(perform: updatePrefs)
|
.onAppear(perform: updatePrefs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private var deepLToggle: some View {
|
private var deepLToggle: some View {
|
||||||
@Bindable var preferences = preferences
|
@Bindable var preferences = preferences
|
||||||
|
@ -52,7 +52,7 @@ struct TranslationSettingsView: View {
|
||||||
}
|
}
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private var deepLPicker: some View {
|
private var deepLPicker: some View {
|
||||||
@Bindable var preferences = preferences
|
@Bindable var preferences = preferences
|
||||||
|
@ -61,7 +61,7 @@ struct TranslationSettingsView: View {
|
||||||
Text("DeepL API Pro").tag(false)
|
Text("DeepL API Pro").tag(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private var autoDetectSection: some View {
|
private var autoDetectSection: some View {
|
||||||
@Bindable var preferences = preferences
|
@Bindable var preferences = preferences
|
||||||
|
|
|
@ -22,7 +22,7 @@ struct EditTagGroupView: View {
|
||||||
|
|
||||||
private var editingTagGroup: TagGroup?
|
private var editingTagGroup: TagGroup?
|
||||||
private var onSaved: ((TagGroup) -> Void)?
|
private var onSaved: ((TagGroup) -> Void)?
|
||||||
|
|
||||||
private var canSave: Bool {
|
private var canSave: Bool {
|
||||||
!title.isEmpty &&
|
!title.isEmpty &&
|
||||||
// At least have 2 tags, one main and one additional.
|
// At least have 2 tags, one main and one additional.
|
||||||
|
|
|
@ -4,14 +4,14 @@ import DesignSystem
|
||||||
import Env
|
import Env
|
||||||
import Models
|
import Models
|
||||||
import Network
|
import Network
|
||||||
|
import SwiftData
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Timeline
|
import Timeline
|
||||||
import SwiftData
|
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
struct TimelineTab: View {
|
struct TimelineTab: View {
|
||||||
@Environment(\.modelContext) private var context
|
@Environment(\.modelContext) private var context
|
||||||
|
|
||||||
@Environment(AppAccountsManager.self) private var appAccount
|
@Environment(AppAccountsManager.self) private var appAccount
|
||||||
@Environment(Theme.self) private var theme
|
@Environment(Theme.self) private var theme
|
||||||
@Environment(CurrentAccount.self) private var currentAccount
|
@Environment(CurrentAccount.self) private var currentAccount
|
||||||
|
@ -24,13 +24,13 @@ struct TimelineTab: View {
|
||||||
@State private var timeline: TimelineFilter = .home
|
@State private var timeline: TimelineFilter = .home
|
||||||
@State private var selectedTagGroup: TagGroup?
|
@State private var selectedTagGroup: TagGroup?
|
||||||
@State private var scrollToTopSignal: Int = 0
|
@State private var scrollToTopSignal: Int = 0
|
||||||
|
|
||||||
@Query(sort: \LocalTimeline.creationDate, order: .reverse) var localTimelines: [LocalTimeline]
|
@Query(sort: \LocalTimeline.creationDate, order: .reverse) var localTimelines: [LocalTimeline]
|
||||||
@Query(sort: \TagGroup.creationDate, order: .reverse) var tagGroups: [TagGroup]
|
@Query(sort: \TagGroup.creationDate, order: .reverse) var tagGroups: [TagGroup]
|
||||||
|
|
||||||
@AppStorage("remote_local_timeline") var legacyLocalTimelines: [String] = []
|
@AppStorage("remote_local_timeline") var legacyLocalTimelines: [String] = []
|
||||||
@AppStorage("tag_groups") var legacyTagGroups: [LegacyTagGroup] = []
|
@AppStorage("tag_groups") var legacyTagGroups: [LegacyTagGroup] = []
|
||||||
|
|
||||||
@AppStorage("last_timeline_filter") var lastTimelineFilter: TimelineFilter = .home
|
@AppStorage("last_timeline_filter") var lastTimelineFilter: TimelineFilter = .home
|
||||||
|
|
||||||
private let canFilterTimeline: Bool
|
private let canFilterTimeline: Bool
|
||||||
|
@ -249,7 +249,7 @@ struct TimelineTab: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func resetTimelineFilter() {
|
private func resetTimelineFilter() {
|
||||||
if client.isAuth, canFilterTimeline {
|
if client.isAuth, canFilterTimeline {
|
||||||
timeline = lastTimelineFilter
|
timeline = lastTimelineFilter
|
||||||
|
@ -257,14 +257,14 @@ struct TimelineTab: View {
|
||||||
timeline = .federated
|
timeline = .federated
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func migrateUserPreferencesTimeline() {
|
func migrateUserPreferencesTimeline() {
|
||||||
for instance in legacyLocalTimelines {
|
for instance in legacyLocalTimelines {
|
||||||
context.insert(LocalTimeline(instance: instance))
|
context.insert(LocalTimeline(instance: instance))
|
||||||
}
|
}
|
||||||
legacyLocalTimelines = []
|
legacyLocalTimelines = []
|
||||||
}
|
}
|
||||||
|
|
||||||
func migrateUserPreferencesTagGroups() {
|
func migrateUserPreferencesTagGroups() {
|
||||||
for group in legacyTagGroups {
|
for group in legacyTagGroups {
|
||||||
context.insert(TagGroup(title: group.title, symbolName: group.sfSymbolName, tags: group.tags))
|
context.insert(TagGroup(title: group.title, symbolName: group.sfSymbolName, tags: group.tags))
|
||||||
|
|
|
@ -66,7 +66,7 @@ class NotificationService: UNNotificationServiceExtension {
|
||||||
let preferences = UserPreferences.shared
|
let preferences = UserPreferences.shared
|
||||||
let tokens = AppAccountsManager.shared.pushAccounts.map(\.token)
|
let tokens = AppAccountsManager.shared.pushAccounts.map(\.token)
|
||||||
preferences.reloadNotificationsCount(tokens: tokens)
|
preferences.reloadNotificationsCount(tokens: tokens)
|
||||||
|
|
||||||
if let token = AppAccountsManager.shared.availableAccounts.first(where: { $0.oauthToken?.accessToken == notification.accessToken })?.oauthToken {
|
if let token = AppAccountsManager.shared.availableAccounts.first(where: { $0.oauthToken?.accessToken == notification.accessToken })?.oauthToken {
|
||||||
var currentCount = preferences.notificationsCount[token] ?? 0
|
var currentCount = preferences.notificationsCount[token] ?? 0
|
||||||
currentCount += 1
|
currentCount += 1
|
||||||
|
|
|
@ -105,11 +105,10 @@ struct ConversationMessageView: View {
|
||||||
Button {
|
Button {
|
||||||
Task {
|
Task {
|
||||||
do {
|
do {
|
||||||
let status: Status
|
let status: Status = if isLiked {
|
||||||
if isLiked {
|
try await client.post(endpoint: Statuses.unfavorite(id: message.id))
|
||||||
status = try await client.post(endpoint: Statuses.unfavorite(id: message.id))
|
|
||||||
} else {
|
} else {
|
||||||
status = try await client.post(endpoint: Statuses.favorite(id: message.id))
|
try await client.post(endpoint: Statuses.favorite(id: message.id))
|
||||||
}
|
}
|
||||||
withAnimation {
|
withAnimation {
|
||||||
isLiked = status.favourited == true
|
isLiked = status.favourited == true
|
||||||
|
@ -122,11 +121,10 @@ struct ConversationMessageView: View {
|
||||||
}
|
}
|
||||||
Button { Task {
|
Button { Task {
|
||||||
do {
|
do {
|
||||||
let status: Status
|
let status: Status = if isBookmarked {
|
||||||
if isBookmarked {
|
try await client.post(endpoint: Statuses.unbookmark(id: message.id))
|
||||||
status = try await client.post(endpoint: Statuses.unbookmark(id: message.id))
|
|
||||||
} else {
|
} else {
|
||||||
status = try await client.post(endpoint: Statuses.bookmark(id: message.id))
|
try await client.post(endpoint: Statuses.bookmark(id: message.id))
|
||||||
}
|
}
|
||||||
withAnimation {
|
withAnimation {
|
||||||
isBookmarked = status.bookmarked == true
|
isBookmarked = status.bookmarked == true
|
||||||
|
|
|
@ -18,8 +18,8 @@ import SwiftUI
|
||||||
@AppStorage(ThemeKey.primaryBackground.rawValue) public var primaryBackgroundColor: Color = .white
|
@AppStorage(ThemeKey.primaryBackground.rawValue) public var primaryBackgroundColor: Color = .white
|
||||||
@AppStorage(ThemeKey.secondaryBackground.rawValue) public var secondaryBackgroundColor: Color = .gray
|
@AppStorage(ThemeKey.secondaryBackground.rawValue) public var secondaryBackgroundColor: Color = .gray
|
||||||
@AppStorage(ThemeKey.label.rawValue) public var labelColor: Color = .black
|
@AppStorage(ThemeKey.label.rawValue) public var labelColor: Color = .black
|
||||||
@AppStorage(ThemeKey.avatarPosition2.rawValue) var avatarPosition: AvatarPosition = AvatarPosition.top
|
@AppStorage(ThemeKey.avatarPosition2.rawValue) var avatarPosition: AvatarPosition = .top
|
||||||
@AppStorage(ThemeKey.avatarShape2.rawValue) var avatarShape: AvatarShape = AvatarShape.rounded
|
@AppStorage(ThemeKey.avatarShape2.rawValue) var avatarShape: AvatarShape = .rounded
|
||||||
@AppStorage(ThemeKey.selectedSet.rawValue) var storedSet: ColorSetName = .iceCubeDark
|
@AppStorage(ThemeKey.selectedSet.rawValue) var storedSet: ColorSetName = .iceCubeDark
|
||||||
@AppStorage(ThemeKey.statusActionsDisplay.rawValue) public var statusActionsDisplay: StatusActionsDisplay = .full
|
@AppStorage(ThemeKey.statusActionsDisplay.rawValue) public var statusActionsDisplay: StatusActionsDisplay = .full
|
||||||
@AppStorage(ThemeKey.statusDisplayStyle.rawValue) public var statusDisplayStyle: StatusDisplayStyle = .large
|
@AppStorage(ThemeKey.statusDisplayStyle.rawValue) public var statusDisplayStyle: StatusDisplayStyle = .large
|
||||||
|
@ -273,7 +273,7 @@ import SwiftUI
|
||||||
ConstellationDark(),
|
ConstellationDark(),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
public func applySet(set: ColorSetName) {
|
public func applySet(set: ColorSetName) {
|
||||||
selectedSet = set
|
selectedSet = set
|
||||||
setColor(withName: set)
|
setColor(withName: set)
|
||||||
|
|
|
@ -11,10 +11,10 @@ import SwiftUI
|
||||||
@AppStorage("show_translate_button_inline") public var showTranslateButton: Bool = true
|
@AppStorage("show_translate_button_inline") public var showTranslateButton: Bool = true
|
||||||
@AppStorage("show_pending_at_bottom") public var pendingShownAtBottom: Bool = false
|
@AppStorage("show_pending_at_bottom") public var pendingShownAtBottom: Bool = false
|
||||||
@AppStorage("is_open_ai_enabled") public var isOpenAIEnabled: Bool = true
|
@AppStorage("is_open_ai_enabled") public var isOpenAIEnabled: Bool = true
|
||||||
|
|
||||||
@AppStorage("recently_used_languages") public var recentlyUsedLanguages: [String] = []
|
@AppStorage("recently_used_languages") public var recentlyUsedLanguages: [String] = []
|
||||||
@AppStorage("social_keyboard_composer") public var isSocialKeyboardEnabled: Bool = true
|
@AppStorage("social_keyboard_composer") public var isSocialKeyboardEnabled: Bool = true
|
||||||
|
|
||||||
@AppStorage("use_instance_content_settings") public var useInstanceContentSettings: Bool = true
|
@AppStorage("use_instance_content_settings") public var useInstanceContentSettings: Bool = true
|
||||||
@AppStorage("app_auto_expand_spoilers") public var appAutoExpandSpoilers = false
|
@AppStorage("app_auto_expand_spoilers") public var appAutoExpandSpoilers = false
|
||||||
@AppStorage("app_auto_expand_media") public var appAutoExpandMedia: ServerPreferences.AutoExpandMedia = .hideSensitive
|
@AppStorage("app_auto_expand_media") public var appAutoExpandMedia: ServerPreferences.AutoExpandMedia = .hideSensitive
|
||||||
|
@ -25,41 +25,41 @@ import SwiftUI
|
||||||
@AppStorage("always_use_deepl") public var alwaysUseDeepl = false
|
@AppStorage("always_use_deepl") public var alwaysUseDeepl = false
|
||||||
@AppStorage("user_deepl_api_free") public var userDeeplAPIFree = true
|
@AppStorage("user_deepl_api_free") public var userDeeplAPIFree = true
|
||||||
@AppStorage("auto_detect_post_language") public var autoDetectPostLanguage = true
|
@AppStorage("auto_detect_post_language") public var autoDetectPostLanguage = true
|
||||||
|
|
||||||
@AppStorage("suppress_dupe_reblogs") public var suppressDupeReblogs: Bool = false
|
@AppStorage("suppress_dupe_reblogs") public var suppressDupeReblogs: Bool = false
|
||||||
|
|
||||||
@AppStorage("inAppBrowserReaderView") public var inAppBrowserReaderView = false
|
@AppStorage("inAppBrowserReaderView") public var inAppBrowserReaderView = false
|
||||||
|
|
||||||
@AppStorage("haptic_tab") public var hapticTabSelectionEnabled = true
|
@AppStorage("haptic_tab") public var hapticTabSelectionEnabled = true
|
||||||
@AppStorage("haptic_timeline") public var hapticTimelineEnabled = true
|
@AppStorage("haptic_timeline") public var hapticTimelineEnabled = true
|
||||||
@AppStorage("haptic_button_press") public var hapticButtonPressEnabled = true
|
@AppStorage("haptic_button_press") public var hapticButtonPressEnabled = true
|
||||||
@AppStorage("sound_effect_enabled") public var soundEffectEnabled = true
|
@AppStorage("sound_effect_enabled") public var soundEffectEnabled = true
|
||||||
|
|
||||||
@AppStorage("show_tab_label_iphone") public var showiPhoneTabLabel = true
|
@AppStorage("show_tab_label_iphone") public var showiPhoneTabLabel = true
|
||||||
@AppStorage("show_alt_text_for_media") public var showAltTextForMedia = true
|
@AppStorage("show_alt_text_for_media") public var showAltTextForMedia = true
|
||||||
|
|
||||||
@AppStorage("show_second_column_ipad") public var showiPadSecondaryColumn = true
|
@AppStorage("show_second_column_ipad") public var showiPadSecondaryColumn = true
|
||||||
|
|
||||||
@AppStorage("swipeactions-status-trailing-right") public var swipeActionsStatusTrailingRight = StatusAction.favorite
|
@AppStorage("swipeactions-status-trailing-right") public var swipeActionsStatusTrailingRight = StatusAction.favorite
|
||||||
@AppStorage("swipeactions-status-trailing-left") public var swipeActionsStatusTrailingLeft = StatusAction.boost
|
@AppStorage("swipeactions-status-trailing-left") public var swipeActionsStatusTrailingLeft = StatusAction.boost
|
||||||
@AppStorage("swipeactions-status-leading-left") public var swipeActionsStatusLeadingLeft = StatusAction.reply
|
@AppStorage("swipeactions-status-leading-left") public var swipeActionsStatusLeadingLeft = StatusAction.reply
|
||||||
@AppStorage("swipeactions-status-leading-right") public var swipeActionsStatusLeadingRight = StatusAction.none
|
@AppStorage("swipeactions-status-leading-right") public var swipeActionsStatusLeadingRight = StatusAction.none
|
||||||
@AppStorage("swipeactions-use-theme-color") public var swipeActionsUseThemeColor = false
|
@AppStorage("swipeactions-use-theme-color") public var swipeActionsUseThemeColor = false
|
||||||
@AppStorage("swipeactions-icon-style") public var swipeActionsIconStyle: SwipeActionsIconStyle = .iconWithText
|
@AppStorage("swipeactions-icon-style") public var swipeActionsIconStyle: SwipeActionsIconStyle = .iconWithText
|
||||||
|
|
||||||
@AppStorage("requested_review") public var requestedReview = false
|
@AppStorage("requested_review") public var requestedReview = false
|
||||||
|
|
||||||
@AppStorage("collapse-long-posts") public var collapseLongPosts = true
|
@AppStorage("collapse-long-posts") public var collapseLongPosts = true
|
||||||
|
|
||||||
@AppStorage("share-button-behavior") public var shareButtonBehavior: PreferredShareButtonBehavior = .linkAndText
|
@AppStorage("share-button-behavior") public var shareButtonBehavior: PreferredShareButtonBehavior = .linkAndText
|
||||||
|
|
||||||
init() { }
|
init() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static let sharedDefault = UserDefaults(suiteName: "group.com.thomasricouard.IceCubesApp")
|
public static let sharedDefault = UserDefaults(suiteName: "group.com.thomasricouard.IceCubesApp")
|
||||||
public static let shared = UserPreferences()
|
public static let shared = UserPreferences()
|
||||||
private let storage = Storage()
|
private let storage = Storage()
|
||||||
|
|
||||||
private var client: Client?
|
private var client: Client?
|
||||||
|
|
||||||
public var preferredBrowser: PreferredBrowser {
|
public var preferredBrowser: PreferredBrowser {
|
||||||
|
@ -73,171 +73,202 @@ import SwiftUI
|
||||||
storage.showTranslateButton = showTranslateButton
|
storage.showTranslateButton = showTranslateButton
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var pendingShownAtBottom : Bool {
|
public var pendingShownAtBottom : Bool {
|
||||||
didSet {
|
didSet {
|
||||||
storage.pendingShownAtBottom = pendingShownAtBottom
|
storage.pendingShownAtBottom = pendingShownAtBottom
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var isOpenAIEnabled: Bool {
|
public var isOpenAIEnabled: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
storage.isOpenAIEnabled = isOpenAIEnabled
|
storage.isOpenAIEnabled = isOpenAIEnabled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var recentlyUsedLanguages: [String] {
|
public var recentlyUsedLanguages: [String] {
|
||||||
didSet {
|
didSet {
|
||||||
storage.recentlyUsedLanguages = recentlyUsedLanguages
|
storage.recentlyUsedLanguages = recentlyUsedLanguages
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var isSocialKeyboardEnabled: Bool {
|
public var isSocialKeyboardEnabled: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
storage.isSocialKeyboardEnabled = isSocialKeyboardEnabled
|
storage.isSocialKeyboardEnabled = isSocialKeyboardEnabled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var useInstanceContentSettings: Bool {
|
public var useInstanceContentSettings: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
storage.useInstanceContentSettings = useInstanceContentSettings
|
storage.useInstanceContentSettings = useInstanceContentSettings
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var appAutoExpandSpoilers: Bool {
|
public var appAutoExpandSpoilers: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
storage.appAutoExpandSpoilers = appAutoExpandSpoilers
|
storage.appAutoExpandSpoilers = appAutoExpandSpoilers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var appAutoExpandMedia: ServerPreferences.AutoExpandMedia {
|
public var appAutoExpandMedia: ServerPreferences.AutoExpandMedia {
|
||||||
didSet {
|
didSet {
|
||||||
storage.appAutoExpandMedia = appAutoExpandMedia
|
storage.appAutoExpandMedia = appAutoExpandMedia
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var appDefaultPostVisibility: Models.Visibility {
|
public var appDefaultPostVisibility: Models.Visibility {
|
||||||
didSet {
|
didSet {
|
||||||
storage.appDefaultPostVisibility = appDefaultPostVisibility
|
storage.appDefaultPostVisibility = appDefaultPostVisibility
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var appDefaultReplyVisibility: Models.Visibility {
|
public var appDefaultReplyVisibility: Models.Visibility {
|
||||||
didSet {
|
didSet {
|
||||||
storage.appDefaultReplyVisibility = appDefaultReplyVisibility
|
storage.appDefaultReplyVisibility = appDefaultReplyVisibility
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var appDefaultPostsSensitive: Bool {
|
public var appDefaultPostsSensitive: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
storage.appDefaultPostsSensitive = appDefaultPostsSensitive
|
storage.appDefaultPostsSensitive = appDefaultPostsSensitive
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var autoPlayVideo: Bool {
|
public var autoPlayVideo: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
storage.autoPlayVideo = autoPlayVideo
|
storage.autoPlayVideo = autoPlayVideo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var alwaysUseDeepl: Bool {
|
public var alwaysUseDeepl: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
storage.alwaysUseDeepl = alwaysUseDeepl
|
storage.alwaysUseDeepl = alwaysUseDeepl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var userDeeplAPIFree: Bool {
|
public var userDeeplAPIFree: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
storage.userDeeplAPIFree = userDeeplAPIFree
|
storage.userDeeplAPIFree = userDeeplAPIFree
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var autoDetectPostLanguage: Bool {
|
public var autoDetectPostLanguage: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
storage.autoDetectPostLanguage = autoDetectPostLanguage
|
storage.autoDetectPostLanguage = autoDetectPostLanguage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var suppressDupeReblogs: Bool {
|
public var suppressDupeReblogs: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
storage.suppressDupeReblogs = suppressDupeReblogs
|
storage.suppressDupeReblogs = suppressDupeReblogs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var inAppBrowserReaderView: Bool {
|
public var inAppBrowserReaderView: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
storage.inAppBrowserReaderView = inAppBrowserReaderView
|
storage.inAppBrowserReaderView = inAppBrowserReaderView
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var hapticTabSelectionEnabled: Bool {
|
public var hapticTabSelectionEnabled: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
storage.hapticTabSelectionEnabled = hapticTabSelectionEnabled
|
storage.hapticTabSelectionEnabled = hapticTabSelectionEnabled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var hapticTimelineEnabled: Bool {
|
public var hapticTimelineEnabled: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
storage.hapticTimelineEnabled = hapticTimelineEnabled
|
storage.hapticTimelineEnabled = hapticTimelineEnabled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var hapticButtonPressEnabled: Bool {
|
public var hapticButtonPressEnabled: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
storage.hapticButtonPressEnabled = hapticButtonPressEnabled
|
storage.hapticButtonPressEnabled = hapticButtonPressEnabled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var soundEffectEnabled: Bool {
|
public var soundEffectEnabled: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
storage.soundEffectEnabled = soundEffectEnabled
|
storage.soundEffectEnabled = soundEffectEnabled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var showiPhoneTabLabel: Bool {
|
public var showiPhoneTabLabel: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
storage.showiPhoneTabLabel = showiPhoneTabLabel
|
storage.showiPhoneTabLabel = showiPhoneTabLabel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var showAltTextForMedia: Bool {
|
public var showAltTextForMedia: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
storage.showAltTextForMedia = showAltTextForMedia
|
storage.showAltTextForMedia = showAltTextForMedia
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var showiPadSecondaryColumn: Bool {
|
public var showiPadSecondaryColumn: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
storage.showiPadSecondaryColumn = showiPadSecondaryColumn
|
storage.showiPadSecondaryColumn = showiPadSecondaryColumn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var swipeActionsStatusTrailingRight: StatusAction {
|
public var swipeActionsStatusTrailingRight: StatusAction {
|
||||||
didSet {
|
didSet {
|
||||||
storage.swipeActionsStatusTrailingRight = swipeActionsStatusTrailingRight
|
storage.swipeActionsStatusTrailingRight = swipeActionsStatusTrailingRight
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var swipeActionsStatusTrailingLeft: StatusAction {
|
public var swipeActionsStatusTrailingLeft: StatusAction {
|
||||||
didSet {
|
didSet {
|
||||||
storage.swipeActionsStatusTrailingLeft = swipeActionsStatusTrailingLeft
|
storage.swipeActionsStatusTrailingLeft = swipeActionsStatusTrailingLeft
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var swipeActionsStatusLeadingLeft: StatusAction {
|
public var swipeActionsStatusLeadingLeft: StatusAction {
|
||||||
didSet {
|
didSet {
|
||||||
storage.swipeActionsStatusLeadingLeft = swipeActionsStatusLeadingLeft
|
storage.swipeActionsStatusLeadingLeft = swipeActionsStatusLeadingLeft
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var swipeActionsStatusLeadingRight: StatusAction {
|
public var swipeActionsStatusLeadingRight: StatusAction {
|
||||||
didSet {
|
didSet {
|
||||||
storage.swipeActionsStatusLeadingRight = swipeActionsStatusLeadingRight
|
storage.swipeActionsStatusLeadingRight = swipeActionsStatusLeadingRight
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var swipeActionsUseThemeColor: Bool {
|
public var swipeActionsUseThemeColor: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
storage.swipeActionsUseThemeColor = swipeActionsUseThemeColor
|
storage.swipeActionsUseThemeColor = swipeActionsUseThemeColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var swipeActionsIconStyle: SwipeActionsIconStyle {
|
public var swipeActionsIconStyle: SwipeActionsIconStyle {
|
||||||
didSet {
|
didSet {
|
||||||
storage.swipeActionsIconStyle = swipeActionsIconStyle
|
storage.swipeActionsIconStyle = swipeActionsIconStyle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var requestedReview: Bool {
|
public var requestedReview: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
storage.requestedReview = requestedReview
|
storage.requestedReview = requestedReview
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var collapseLongPosts: Bool {
|
public var collapseLongPosts: Bool {
|
||||||
didSet {
|
didSet {
|
||||||
storage.collapseLongPosts = collapseLongPosts
|
storage.collapseLongPosts = collapseLongPosts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var shareButtonBehavior: PreferredShareButtonBehavior {
|
public var shareButtonBehavior: PreferredShareButtonBehavior {
|
||||||
didSet {
|
didSet {
|
||||||
storage.shareButtonBehavior = shareButtonBehavior
|
storage.shareButtonBehavior = shareButtonBehavior
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public enum SwipeActionsIconStyle: String, CaseIterable {
|
public enum SwipeActionsIconStyle: String, CaseIterable {
|
||||||
case iconWithText, iconOnly
|
case iconWithText, iconOnly
|
||||||
|
|
||||||
public var description: LocalizedStringKey {
|
public var description: LocalizedStringKey {
|
||||||
switch self {
|
switch self {
|
||||||
case .iconWithText:
|
case .iconWithText:
|
||||||
|
@ -246,7 +277,7 @@ import SwiftUI
|
||||||
"enum.swipeactions.icon-only"
|
"enum.swipeactions.icon-only"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Have to implement this manually here due to compiler not implicitly
|
// Have to implement this manually here due to compiler not implicitly
|
||||||
// inserting `nonisolated`, which leads to a warning:
|
// inserting `nonisolated`, which leads to a warning:
|
||||||
//
|
//
|
||||||
|
@ -257,7 +288,7 @@ import SwiftUI
|
||||||
[.iconWithText, .iconOnly]
|
[.iconWithText, .iconOnly]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var postVisibility: Models.Visibility {
|
public var postVisibility: Models.Visibility {
|
||||||
if useInstanceContentSettings {
|
if useInstanceContentSettings {
|
||||||
serverPreferences?.postVisibility ?? .pub
|
serverPreferences?.postVisibility ?? .pub
|
||||||
|
@ -265,26 +296,26 @@ import SwiftUI
|
||||||
appDefaultPostVisibility
|
appDefaultPostVisibility
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func conformReplyVisibilityConstraints() {
|
public func conformReplyVisibilityConstraints() {
|
||||||
appDefaultReplyVisibility = getReplyVisibility()
|
appDefaultReplyVisibility = getReplyVisibility()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getReplyVisibility() -> Models.Visibility {
|
private func getReplyVisibility() -> Models.Visibility {
|
||||||
getMinVisibility(postVisibility, appDefaultReplyVisibility)
|
getMinVisibility(postVisibility, appDefaultReplyVisibility)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func getReplyVisibility(of status: Status) -> Models.Visibility {
|
public func getReplyVisibility(of status: Status) -> Models.Visibility {
|
||||||
getMinVisibility(getReplyVisibility(), status.visibility)
|
getMinVisibility(getReplyVisibility(), status.visibility)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getMinVisibility(_ vis1: Models.Visibility, _ vis2: Models.Visibility) -> Models.Visibility {
|
private func getMinVisibility(_ vis1: Models.Visibility, _ vis2: Models.Visibility) -> Models.Visibility {
|
||||||
let no1 = Self.getIntOfVisibility(vis1)
|
let no1 = Self.getIntOfVisibility(vis1)
|
||||||
let no2 = Self.getIntOfVisibility(vis2)
|
let no2 = Self.getIntOfVisibility(vis2)
|
||||||
|
|
||||||
return no1 < no2 ? vis1 : vis2
|
return no1 < no2 ? vis1 : vis2
|
||||||
}
|
}
|
||||||
|
|
||||||
public var postIsSensitive: Bool {
|
public var postIsSensitive: Bool {
|
||||||
if useInstanceContentSettings {
|
if useInstanceContentSettings {
|
||||||
serverPreferences?.postIsSensitive ?? false
|
serverPreferences?.postIsSensitive ?? false
|
||||||
|
@ -292,7 +323,7 @@ import SwiftUI
|
||||||
appDefaultPostsSensitive
|
appDefaultPostsSensitive
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var autoExpandSpoilers: Bool {
|
public var autoExpandSpoilers: Bool {
|
||||||
if useInstanceContentSettings {
|
if useInstanceContentSettings {
|
||||||
serverPreferences?.autoExpandSpoilers ?? true
|
serverPreferences?.autoExpandSpoilers ?? true
|
||||||
|
@ -300,7 +331,7 @@ import SwiftUI
|
||||||
appAutoExpandSpoilers
|
appAutoExpandSpoilers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var autoExpandMedia: ServerPreferences.AutoExpandMedia {
|
public var autoExpandMedia: ServerPreferences.AutoExpandMedia {
|
||||||
if useInstanceContentSettings {
|
if useInstanceContentSettings {
|
||||||
serverPreferences?.autoExpandMedia ?? .hideSensitive
|
serverPreferences?.autoExpandMedia ?? .hideSensitive
|
||||||
|
@ -308,7 +339,7 @@ import SwiftUI
|
||||||
appAutoExpandMedia
|
appAutoExpandMedia
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var notificationsCount: [OauthToken: Int] = [:] {
|
public var notificationsCount: [OauthToken: Int] = [:] {
|
||||||
didSet {
|
didSet {
|
||||||
for (key, value) in notificationsCount {
|
for (key, value) in notificationsCount {
|
||||||
|
@ -316,32 +347,32 @@ import SwiftUI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var totalNotificationsCount: Int {
|
public var totalNotificationsCount: Int {
|
||||||
notificationsCount.compactMap{ $0.value }.reduce(0, +)
|
notificationsCount.compactMap(\.value).reduce(0, +)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func reloadNotificationsCount(tokens: [OauthToken]) {
|
public func reloadNotificationsCount(tokens: [OauthToken]) {
|
||||||
notificationsCount = [:]
|
notificationsCount = [:]
|
||||||
for token in tokens {
|
for token in tokens {
|
||||||
notificationsCount[token] = Self.sharedDefault?.integer(forKey: "push_notifications_count_\(token.createdAt)") ?? 0
|
notificationsCount[token] = Self.sharedDefault?.integer(forKey: "push_notifications_count_\(token.createdAt)") ?? 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var serverPreferences: ServerPreferences?
|
public var serverPreferences: ServerPreferences?
|
||||||
|
|
||||||
public func setClient(client: Client) {
|
public func setClient(client: Client) {
|
||||||
self.client = client
|
self.client = client
|
||||||
Task {
|
Task {
|
||||||
await refreshServerPreferences()
|
await refreshServerPreferences()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func refreshServerPreferences() async {
|
public func refreshServerPreferences() async {
|
||||||
guard let client, client.isAuth else { return }
|
guard let client, client.isAuth else { return }
|
||||||
serverPreferences = try? await client.get(endpoint: Accounts.preferences)
|
serverPreferences = try? await client.get(endpoint: Accounts.preferences)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func markLanguageAsSelected(isoCode: String) {
|
public func markLanguageAsSelected(isoCode: String) {
|
||||||
var copy = recentlyUsedLanguages
|
var copy = recentlyUsedLanguages
|
||||||
if let index = copy.firstIndex(of: isoCode) {
|
if let index = copy.firstIndex(of: isoCode) {
|
||||||
|
@ -350,7 +381,7 @@ import SwiftUI
|
||||||
copy.insert(isoCode, at: 0)
|
copy.insert(isoCode, at: 0)
|
||||||
recentlyUsedLanguages = Array(copy.prefix(3))
|
recentlyUsedLanguages = Array(copy.prefix(3))
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func getIntOfVisibility(_ vis: Models.Visibility) -> Int {
|
public static func getIntOfVisibility(_ vis: Models.Visibility) -> Int {
|
||||||
switch vis {
|
switch vis {
|
||||||
case .direct:
|
case .direct:
|
||||||
|
@ -363,7 +394,7 @@ import SwiftUI
|
||||||
3
|
3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private init() {
|
private init() {
|
||||||
preferredBrowser = storage.preferredBrowser
|
preferredBrowser = storage.preferredBrowser
|
||||||
showTranslateButton = storage.showTranslateButton
|
showTranslateButton = storage.showTranslateButton
|
||||||
|
|
|
@ -7,11 +7,11 @@ public struct CardsListView: View {
|
||||||
@Environment(Theme.self) private var theme
|
@Environment(Theme.self) private var theme
|
||||||
|
|
||||||
let cards: [Card]
|
let cards: [Card]
|
||||||
|
|
||||||
public init(cards: [Card]) {
|
public init(cards: [Card]) {
|
||||||
self.cards = cards
|
self.cards = cards
|
||||||
}
|
}
|
||||||
|
|
||||||
public var body: some View {
|
public var body: some View {
|
||||||
List {
|
List {
|
||||||
ForEach(cards) { card in
|
ForEach(cards) { card in
|
||||||
|
|
|
@ -226,7 +226,7 @@ public struct ExploreView: View {
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
.padding(.vertical, 8)
|
.padding(.vertical, 8)
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigationLink(value: RouterDestination.trendingLinks(cards: viewModel.trendingLinks)) {
|
NavigationLink(value: RouterDestination.trendingLinks(cards: viewModel.trendingLinks)) {
|
||||||
Text("see-more")
|
Text("see-more")
|
||||||
.foregroundColor(theme.tintColor)
|
.foregroundColor(theme.tintColor)
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
|
import Foundation
|
||||||
import SwiftData
|
import SwiftData
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Foundation
|
|
||||||
|
|
||||||
@Model public class Draft {
|
@Model public class Draft {
|
||||||
@Attribute(.unique) public var id: UUID
|
@Attribute(.unique) public var id: UUID
|
||||||
public var content: String
|
public var content: String
|
||||||
public var creationDate: Date
|
public var creationDate: Date
|
||||||
|
|
||||||
public init(content: String) {
|
public init(content: String) {
|
||||||
self.id = UUID()
|
id = UUID()
|
||||||
self.content = content
|
self.content = content
|
||||||
self.creationDate = Date()
|
creationDate = Date()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
|
import Foundation
|
||||||
import SwiftData
|
import SwiftData
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Foundation
|
|
||||||
|
|
||||||
@Model public class LocalTimeline {
|
@Model public class LocalTimeline {
|
||||||
public var instance: String
|
public var instance: String
|
||||||
public var creationDate: Date
|
public var creationDate: Date
|
||||||
|
|
||||||
public init(instance: String) {
|
public init(instance: String) {
|
||||||
self.instance = instance
|
self.instance = instance
|
||||||
self.creationDate = Date()
|
creationDate = Date()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
|
import Foundation
|
||||||
import SwiftData
|
import SwiftData
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Foundation
|
|
||||||
|
|
||||||
@Model public class TagGroup: Equatable {
|
@Model public class TagGroup: Equatable {
|
||||||
public var title: String
|
public var title: String
|
||||||
public var symbolName: String
|
public var symbolName: String
|
||||||
public var tags: [String]
|
public var tags: [String]
|
||||||
public var creationDate: Date
|
public var creationDate: Date
|
||||||
|
|
||||||
public init(title: String, symbolName: String, tags: [String]) {
|
public init(title: String, symbolName: String, tags: [String]) {
|
||||||
self.title = title
|
self.title = title
|
||||||
self.symbolName = symbolName
|
self.symbolName = symbolName
|
||||||
self.tags = tags
|
self.tags = tags
|
||||||
self.creationDate = Date()
|
creationDate = Date()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,4 +62,3 @@ public struct FeaturedTag: Codable, Identifiable {
|
||||||
extension Tag: Sendable {}
|
extension Tag: Sendable {}
|
||||||
extension Tag.History: Sendable {}
|
extension Tag.History: Sendable {}
|
||||||
extension FeaturedTag: Sendable {}
|
extension FeaturedTag: Sendable {}
|
||||||
|
|
||||||
|
|
|
@ -177,7 +177,7 @@ 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 {
|
private var draftsListView: some View {
|
||||||
DraftsListView(selectedDraft: .init(get: {
|
DraftsListView(selectedDraft: .init(get: {
|
||||||
nil
|
nil
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
import SwiftUI
|
|
||||||
import SwiftData
|
|
||||||
import DesignSystem
|
import DesignSystem
|
||||||
import Models
|
import Models
|
||||||
|
import SwiftData
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
struct DraftsListView: View {
|
struct DraftsListView: View {
|
||||||
@AppStorage("draft_posts") public var legacyDraftPosts: [String] = []
|
@AppStorage("draft_posts") public var legacyDraftPosts: [String] = []
|
||||||
|
|
||||||
@Environment(\.dismiss) private var dismiss
|
@Environment(\.dismiss) private var dismiss
|
||||||
@Environment(\.modelContext) private var context
|
@Environment(\.modelContext) private var context
|
||||||
|
|
||||||
@Environment(Theme.self) private var theme
|
@Environment(Theme.self) private var theme
|
||||||
|
|
||||||
@Query(sort: \Draft.creationDate, order: .reverse) var drafts: [Draft]
|
@Query(sort: \Draft.creationDate, order: .reverse) var drafts: [Draft]
|
||||||
|
|
||||||
@Binding var selectedDraft: Draft?
|
@Binding var selectedDraft: Draft?
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
List {
|
List {
|
||||||
|
@ -54,7 +54,7 @@ struct DraftsListView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func migrateUserPreferencesDraft() {
|
func migrateUserPreferencesDraft() {
|
||||||
for draft in legacyDraftPosts {
|
for draft in legacyDraftPosts {
|
||||||
let newDraft = Draft(content: draft)
|
let newDraft = Draft(content: draft)
|
||||||
|
|
|
@ -161,7 +161,8 @@ public struct StatusEditorView: View {
|
||||||
object: nil)
|
object: nil)
|
||||||
}
|
}
|
||||||
Button("action.cancel", role: .cancel) {}
|
Button("action.cancel", role: .cancel) {}
|
||||||
})
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,8 @@ public struct TimelineView: View {
|
||||||
|
|
||||||
public init(timeline: Binding<TimelineFilter>,
|
public init(timeline: Binding<TimelineFilter>,
|
||||||
selectedTagGroup: Binding<TagGroup?>,
|
selectedTagGroup: Binding<TagGroup?>,
|
||||||
scrollToTopSignal: Binding<Int>, canFilterTimeline: Bool) {
|
scrollToTopSignal: Binding<Int>, canFilterTimeline: Bool)
|
||||||
|
{
|
||||||
_timeline = timeline
|
_timeline = timeline
|
||||||
_selectedTagGroup = selectedTagGroup
|
_selectedTagGroup = selectedTagGroup
|
||||||
_scrollToTopSignal = scrollToTopSignal
|
_scrollToTopSignal = scrollToTopSignal
|
||||||
|
|
Loading…
Reference in a new issue