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