mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-11-25 09:41:02 +00:00
Code cleanup / format / lint
This commit is contained in:
parent
8cac9df8c6
commit
b89221a535
29 changed files with 239 additions and 262 deletions
|
@ -5,100 +5,100 @@
|
|||
// Created by Thomas Durand on 26/01/2023.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import MobileCoreServices
|
||||
import UIKit
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
import Models
|
||||
import Network
|
||||
|
||||
// Sample code was sending this from a thread to another, let asume @Sendable for this
|
||||
extension NSExtensionContext: @unchecked Sendable { }
|
||||
extension NSExtensionContext: @unchecked Sendable {}
|
||||
|
||||
class ActionRequestHandler: NSObject, NSExtensionRequestHandling {
|
||||
enum Error: Swift.Error {
|
||||
case inputProviderNotFound
|
||||
case loadedItemHasWrongType
|
||||
case urlNotFound
|
||||
case noHost
|
||||
case notMastodonInstance
|
||||
}
|
||||
enum Error: Swift.Error {
|
||||
case inputProviderNotFound
|
||||
case loadedItemHasWrongType
|
||||
case urlNotFound
|
||||
case noHost
|
||||
case notMastodonInstance
|
||||
}
|
||||
|
||||
func beginRequest(with context: NSExtensionContext) {
|
||||
// Do not call super in an Action extension with no user interface
|
||||
Task {
|
||||
do {
|
||||
let url = try await url(from: context)
|
||||
guard await url.isMastodonInstance else {
|
||||
throw Error.notMastodonInstance
|
||||
}
|
||||
await MainActor.run {
|
||||
let deeplink = url.iceCubesAppDeepLink
|
||||
let output = output(wrapping: deeplink)
|
||||
context.completeRequest(returningItems: output)
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
context.completeRequest(returningItems: [])
|
||||
}
|
||||
}
|
||||
func beginRequest(with context: NSExtensionContext) {
|
||||
// Do not call super in an Action extension with no user interface
|
||||
Task {
|
||||
do {
|
||||
let url = try await url(from: context)
|
||||
guard await url.isMastodonInstance else {
|
||||
throw Error.notMastodonInstance
|
||||
}
|
||||
await MainActor.run {
|
||||
let deeplink = url.iceCubesAppDeepLink
|
||||
let output = output(wrapping: deeplink)
|
||||
context.completeRequest(returningItems: output)
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
context.completeRequest(returningItems: [])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension URL {
|
||||
var isMastodonInstance: Bool {
|
||||
get async {
|
||||
do {
|
||||
guard let host = host() else {
|
||||
throw ActionRequestHandler.Error.noHost
|
||||
}
|
||||
let _: Instance = try await Client(server: host).get(endpoint: Instances.instance)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
var isMastodonInstance: Bool {
|
||||
get async {
|
||||
do {
|
||||
guard let host = host() else {
|
||||
throw ActionRequestHandler.Error.noHost
|
||||
}
|
||||
let _: Instance = try await Client(server: host).get(endpoint: Instances.instance)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var iceCubesAppDeepLink: URL {
|
||||
var components = URLComponents(url: self, resolvingAgainstBaseURL: false)!
|
||||
components.scheme = AppInfo.scheme.trimmingCharacters(in: [":", "/"])
|
||||
return components.url!
|
||||
}
|
||||
var iceCubesAppDeepLink: URL {
|
||||
var components = URLComponents(url: self, resolvingAgainstBaseURL: false)!
|
||||
components.scheme = AppInfo.scheme.trimmingCharacters(in: [":", "/"])
|
||||
return components.url!
|
||||
}
|
||||
}
|
||||
|
||||
extension ActionRequestHandler {
|
||||
/// Will look for an input item that might provide the property list that Javascript sent us
|
||||
private func url(from context: NSExtensionContext) async throws -> URL {
|
||||
for item in context.inputItems as! [NSExtensionItem] {
|
||||
guard let attachments = item.attachments else {
|
||||
continue
|
||||
}
|
||||
for itemProvider in attachments {
|
||||
guard itemProvider.hasItemConformingToTypeIdentifier(UTType.propertyList.identifier) else {
|
||||
continue
|
||||
}
|
||||
guard let dictionary = try await itemProvider.loadItem(forTypeIdentifier: UTType.propertyList.identifier) as? [String: Any] else {
|
||||
throw Error.loadedItemHasWrongType
|
||||
}
|
||||
let input = dictionary[NSExtensionJavaScriptPreprocessingResultsKey] as! [String: Any]? ?? [:]
|
||||
guard let absoluteStringUrl = input["url"] as? String, let url = URL(string: absoluteStringUrl) else {
|
||||
throw Error.urlNotFound
|
||||
}
|
||||
return url
|
||||
}
|
||||
/// Will look for an input item that might provide the property list that Javascript sent us
|
||||
private func url(from context: NSExtensionContext) async throws -> URL {
|
||||
for item in context.inputItems as! [NSExtensionItem] {
|
||||
guard let attachments = item.attachments else {
|
||||
continue
|
||||
}
|
||||
for itemProvider in attachments {
|
||||
guard itemProvider.hasItemConformingToTypeIdentifier(UTType.propertyList.identifier) else {
|
||||
continue
|
||||
}
|
||||
throw Error.inputProviderNotFound
|
||||
guard let dictionary = try await itemProvider.loadItem(forTypeIdentifier: UTType.propertyList.identifier) as? [String: Any] else {
|
||||
throw Error.loadedItemHasWrongType
|
||||
}
|
||||
let input = dictionary[NSExtensionJavaScriptPreprocessingResultsKey] as! [String: Any]? ?? [:]
|
||||
guard let absoluteStringUrl = input["url"] as? String, let url = URL(string: absoluteStringUrl) else {
|
||||
throw Error.urlNotFound
|
||||
}
|
||||
return url
|
||||
}
|
||||
}
|
||||
throw Error.inputProviderNotFound
|
||||
}
|
||||
|
||||
/// Wrap the output to the expected object so we send back results to JS
|
||||
private func output(wrapping deeplink: URL) -> [NSExtensionItem] {
|
||||
let results = ["deeplink": deeplink.absoluteString]
|
||||
let dictionary = [NSExtensionJavaScriptFinalizeArgumentKey: results]
|
||||
let provider = NSItemProvider(item: dictionary as NSDictionary, typeIdentifier: UTType.propertyList.identifier)
|
||||
let item = NSExtensionItem()
|
||||
item.attachments = [provider]
|
||||
return [item]
|
||||
}
|
||||
/// Wrap the output to the expected object so we send back results to JS
|
||||
private func output(wrapping deeplink: URL) -> [NSExtensionItem] {
|
||||
let results = ["deeplink": deeplink.absoluteString]
|
||||
let dictionary = [NSExtensionJavaScriptFinalizeArgumentKey: results]
|
||||
let provider = NSItemProvider(item: dictionary as NSDictionary, typeIdentifier: UTType.propertyList.identifier)
|
||||
let item = NSExtensionItem()
|
||||
item.attachments = [provider]
|
||||
return [item]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@ import Account
|
|||
import AppAccount
|
||||
import DesignSystem
|
||||
import Env
|
||||
import SwiftUI
|
||||
import Models
|
||||
import SwiftUI
|
||||
|
||||
struct SideBarView<Content: View>: View {
|
||||
@EnvironmentObject private var appAccounts: AppAccountsManager
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import SwiftUI
|
||||
import Account
|
||||
import AppAccount
|
||||
import DesignSystem
|
||||
import Env
|
||||
import Models
|
||||
import AppAccount
|
||||
import SwiftUI
|
||||
|
||||
struct AccountSettingsView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
@ -54,7 +54,6 @@ struct AccountSettingsView: View {
|
|||
} label: {
|
||||
Text("account.action.logout")
|
||||
}
|
||||
|
||||
}
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import SwiftUI
|
|||
import UserNotifications
|
||||
|
||||
struct ContentSettingsView: View {
|
||||
|
||||
@EnvironmentObject private var userPreferences: UserPreferences
|
||||
@EnvironmentObject private var theme: Theme
|
||||
|
||||
|
@ -19,7 +18,7 @@ struct ContentSettingsView: View {
|
|||
Text("settings.content.use-instance-settings")
|
||||
}
|
||||
} footer: {
|
||||
Text("settings.content.main-toggle.description")
|
||||
Text("settings.content.main-toggle.description")
|
||||
}
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
.onChange(of: userPreferences.useInstanceContentSettings) { newVal in
|
||||
|
@ -29,7 +28,6 @@ struct ContentSettingsView: View {
|
|||
userPreferences.appDefaultPostsSensitive = userPreferences.postIsSensitive
|
||||
userPreferences.appDefaultPostVisibility = userPreferences.postVisibility
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Section("settings.content.reading") {
|
||||
|
@ -54,22 +52,15 @@ struct ContentSettingsView: View {
|
|||
}
|
||||
.disabled(userPreferences.useInstanceContentSettings)
|
||||
|
||||
|
||||
Toggle(isOn: $userPreferences.appDefaultPostsSensitive) {
|
||||
Text("settings.content.default-sensitive")
|
||||
}
|
||||
.disabled(userPreferences.useInstanceContentSettings)
|
||||
|
||||
}
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
|
||||
}
|
||||
.navigationTitle("settings.content.navigation-title")
|
||||
.scrollContentBackground(.hidden)
|
||||
.background(theme.secondaryBackgroundColor)
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -101,7 +101,6 @@ struct PushNotificationsView: View {
|
|||
Text("settings.push.duplicate.footer")
|
||||
}
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
|
||||
}
|
||||
.navigationTitle("settings.push.navigation-title")
|
||||
.scrollContentBackground(.hidden)
|
||||
|
|
|
@ -75,7 +75,8 @@ struct SettingsTabs: View {
|
|||
if let index = indexSet.first {
|
||||
let account = appAccountsManager.availableAccounts[index]
|
||||
if let token = account.oauthToken,
|
||||
let sub = pushNotifications.subscriptions.first(where: { $0.account.token == token }) {
|
||||
let sub = pushNotifications.subscriptions.first(where: { $0.account.token == token })
|
||||
{
|
||||
Task {
|
||||
await sub.deleteSubscription()
|
||||
appAccountsManager.delete(account: account)
|
||||
|
|
|
@ -10,7 +10,7 @@ public struct EditAccountView: View {
|
|||
|
||||
@StateObject private var viewModel = EditAccountViewModel()
|
||||
|
||||
public init() { }
|
||||
public init() {}
|
||||
|
||||
public var body: some View {
|
||||
NavigationStack {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import SwiftUI
|
||||
import Models
|
||||
import Env
|
||||
import DesignSystem
|
||||
import Env
|
||||
import Models
|
||||
import Network
|
||||
import SwiftUI
|
||||
|
||||
struct EditFilterView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
@ -55,7 +55,7 @@ struct EditFilterView: View {
|
|||
.background(theme.secondaryBackgroundColor)
|
||||
.onAppear {
|
||||
if filter == nil {
|
||||
isTitleFocused = true
|
||||
isTitleFocused = true
|
||||
}
|
||||
}
|
||||
.toolbar {
|
||||
|
@ -76,7 +76,6 @@ struct EditFilterView: View {
|
|||
}
|
||||
}
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
|
||||
}
|
||||
|
||||
private var keywordsSection: some View {
|
||||
|
@ -182,7 +181,7 @@ struct EditFilterView: View {
|
|||
} else {
|
||||
let newFilter: ServerFilter = try await client.post(endpoint: ServerFilters.createFilter(json: data),
|
||||
forceVersion: .v2)
|
||||
self.filter = newFilter
|
||||
filter = newFilter
|
||||
}
|
||||
} catch {}
|
||||
isSavingFilter = false
|
||||
|
@ -193,12 +192,12 @@ struct EditFilterView: View {
|
|||
isSavingFilter = true
|
||||
do {
|
||||
let keyword: ServerFilter.Keyword = try await
|
||||
client.post(endpoint: ServerFilters.addKeyword(filter: filterId,
|
||||
keyword: name,
|
||||
wholeWord: true),
|
||||
forceVersion: .v2)
|
||||
self.keywords.append(keyword)
|
||||
} catch { }
|
||||
client.post(endpoint: ServerFilters.addKeyword(filter: filterId,
|
||||
keyword: name,
|
||||
wholeWord: true),
|
||||
forceVersion: .v2)
|
||||
keywords.append(keyword)
|
||||
} catch {}
|
||||
isSavingFilter = false
|
||||
}
|
||||
|
||||
|
@ -210,7 +209,7 @@ struct EditFilterView: View {
|
|||
if response?.statusCode == 200 {
|
||||
keywords.removeAll(where: { $0.id == keyword.id })
|
||||
}
|
||||
} catch { }
|
||||
} catch {}
|
||||
isSavingFilter = false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import SwiftUI
|
||||
import Env
|
||||
import Network
|
||||
import DesignSystem
|
||||
import Env
|
||||
import Models
|
||||
import Network
|
||||
import SwiftUI
|
||||
|
||||
public struct FiltersListView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
@ -14,7 +14,7 @@ public struct FiltersListView: View {
|
|||
@State private var isLoading: Bool = true
|
||||
@State private var filters: [ServerFilter] = []
|
||||
|
||||
public init() { }
|
||||
public init() {}
|
||||
|
||||
public var body: some View {
|
||||
NavigationStack {
|
||||
|
@ -31,7 +31,7 @@ public struct FiltersListView: View {
|
|||
VStack(alignment: .leading) {
|
||||
Text(filter.title)
|
||||
.font(.scaledSubheadline)
|
||||
Text("\(filter.context.map{ $0.name }.joined(separator: ", "))")
|
||||
Text("\(filter.context.map { $0.name }.joined(separator: ", "))")
|
||||
.font(.scaledBody)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import Models
|
|||
import Network
|
||||
import SwiftUI
|
||||
|
||||
extension AppAccount {
|
||||
public extension AppAccount {
|
||||
private static var keychain: KeychainSwift {
|
||||
let keychain = KeychainSwift()
|
||||
#if !DEBUG && !targetEnvironment(simulator)
|
||||
|
@ -13,17 +13,17 @@ extension AppAccount {
|
|||
return keychain
|
||||
}
|
||||
|
||||
public func save() throws {
|
||||
func save() throws {
|
||||
let encoder = JSONEncoder()
|
||||
let data = try encoder.encode(self)
|
||||
Self.keychain.set(data, forKey: key)
|
||||
}
|
||||
|
||||
public func delete() {
|
||||
func delete() {
|
||||
Self.keychain.delete(key)
|
||||
}
|
||||
|
||||
public static func retrieveAll() -> [AppAccount] {
|
||||
static func retrieveAll() -> [AppAccount] {
|
||||
migrateLegacyAccounts()
|
||||
let keychain = Self.keychain
|
||||
let decoder = JSONDecoder()
|
||||
|
@ -39,7 +39,7 @@ extension AppAccount {
|
|||
return accounts
|
||||
}
|
||||
|
||||
public static func migrateLegacyAccounts() {
|
||||
static func migrateLegacyAccounts() {
|
||||
let keychain = KeychainSwift()
|
||||
let decoder = JSONDecoder()
|
||||
let keys = keychain.allKeys
|
||||
|
@ -52,7 +52,7 @@ extension AppAccount {
|
|||
}
|
||||
}
|
||||
|
||||
public static func deleteAll() {
|
||||
static func deleteAll() {
|
||||
let keychain = Self.keychain
|
||||
let keys = keychain.allKeys
|
||||
for key in keys {
|
||||
|
|
|
@ -39,8 +39,8 @@ public struct ConversationDetailView: View {
|
|||
ForEach(viewModel.messages) { message in
|
||||
ConversationMessageView(message: message,
|
||||
conversation: viewModel.conversation)
|
||||
.padding(.vertical, 4)
|
||||
.id(message.id)
|
||||
.padding(.vertical, 4)
|
||||
.id(message.id)
|
||||
}
|
||||
bottomAnchorView
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ public struct ConversationDetailView: View {
|
|||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 14)
|
||||
.stroke(.gray, lineWidth: 1)
|
||||
)
|
||||
)
|
||||
.font(.scaledBody)
|
||||
if !viewModel.newMessageText.isEmpty {
|
||||
Button {
|
||||
|
|
|
@ -68,7 +68,7 @@ struct ConversationMessageView: View {
|
|||
}
|
||||
Group {
|
||||
Text(message.createdAt.shortDateFormatted) +
|
||||
Text(" ")
|
||||
Text(" ")
|
||||
Text(message.createdAt.asDate, style: .time)
|
||||
}
|
||||
.font(.scaledFootnote)
|
||||
|
|
|
@ -64,7 +64,7 @@ public class PushNotificationsService: ObservableObject {
|
|||
await withTaskGroup(of: Void.self, body: { group in
|
||||
group.addTask {
|
||||
await subscription.fetchSubscription()
|
||||
if await subscription.subscription != nil && !forceCreate {
|
||||
if await subscription.subscription != nil, !forceCreate {
|
||||
await subscription.deleteSubscription()
|
||||
await subscription.updateSubscription()
|
||||
} else if forceCreate {
|
||||
|
@ -176,15 +176,15 @@ public class PushNotificationSubscriptionSettings: ObservableObject {
|
|||
listenerURL += "?sandbox=true"
|
||||
#endif
|
||||
subscription =
|
||||
try await client.post(endpoint: Push.createSub(endpoint: listenerURL,
|
||||
p256dh: key,
|
||||
auth: authKey,
|
||||
mentions: isMentionNotificationEnabled,
|
||||
status: isNewPostsNotificationEnabled,
|
||||
reblog: isReblogNotificationEnabled,
|
||||
follow: isFollowNotificationEnabled,
|
||||
favorite: isFavoriteNotificationEnabled,
|
||||
poll: isPollNotificationEnabled))
|
||||
try await client.post(endpoint: Push.createSub(endpoint: listenerURL,
|
||||
p256dh: key,
|
||||
auth: authKey,
|
||||
mentions: isMentionNotificationEnabled,
|
||||
status: isNewPostsNotificationEnabled,
|
||||
reblog: isReblogNotificationEnabled,
|
||||
follow: isFollowNotificationEnabled,
|
||||
favorite: isFavoriteNotificationEnabled,
|
||||
poll: isPollNotificationEnabled))
|
||||
isEnabled = subscription != nil
|
||||
|
||||
} catch {
|
||||
|
|
|
@ -35,7 +35,7 @@ public enum SheetDestinations: Identifiable {
|
|||
public var id: String {
|
||||
switch self {
|
||||
case .editStatusEditor, .newStatusEditor, .replyToStatusEditor, .quoteStatusEditor,
|
||||
.mentionStatusEditor, .settings, .accountPushNotficationsSettings:
|
||||
.mentionStatusEditor, .settings, .accountPushNotficationsSettings:
|
||||
return "statusEditor"
|
||||
case .listEdit:
|
||||
return "listEdit"
|
||||
|
@ -67,8 +67,8 @@ public class RouterPath: ObservableObject {
|
|||
|
||||
public func handleStatus(status: AnyStatus, url: URL) -> OpenURLAction.Result {
|
||||
if url.pathComponents.count == 3 && url.pathComponents[1] == "tags" &&
|
||||
url.host() == status.account.url?.host(),
|
||||
let tag = url.pathComponents.last
|
||||
url.host() == status.account.url?.host(),
|
||||
let tag = url.pathComponents.last
|
||||
{
|
||||
// OK this test looks weird but it's
|
||||
// A 3 component path i.e. ["/", "tags", "tagname"]
|
||||
|
@ -110,7 +110,8 @@ public class RouterPath: ObservableObject {
|
|||
} else if let client = client,
|
||||
client.isAuth,
|
||||
client.hasConnection(with: url),
|
||||
let id = Int(url.lastPathComponent) {
|
||||
let id = Int(url.lastPathComponent)
|
||||
{
|
||||
if url.absoluteString.contains(client.server) {
|
||||
navigate(to: .statusDetail(id: String(id)))
|
||||
} else {
|
||||
|
|
|
@ -22,57 +22,42 @@ public class UserPreferences: ObservableObject {
|
|||
|
||||
@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
|
||||
@AppStorage("app_default_post_visibility") public var appDefaultPostVisibility:Models.Visibility = .pub
|
||||
@AppStorage("app_auto_expand_media") public var appAutoExpandMedia: ServerPreferences.AutoExpandMedia = .hideSensitive
|
||||
@AppStorage("app_default_post_visibility") public var appDefaultPostVisibility: Models.Visibility = .pub
|
||||
@AppStorage("app_default_posts_sensitive") public var appDefaultPostsSensitive = false
|
||||
|
||||
|
||||
public var postVisibility:Models.Visibility {
|
||||
get{
|
||||
if useInstanceContentSettings {
|
||||
return serverPreferences?.postVisibility ?? .pub
|
||||
}
|
||||
else {
|
||||
return appDefaultPostVisibility
|
||||
}
|
||||
public var postVisibility: Models.Visibility {
|
||||
if useInstanceContentSettings {
|
||||
return serverPreferences?.postVisibility ?? .pub
|
||||
} else {
|
||||
return appDefaultPostVisibility
|
||||
}
|
||||
}
|
||||
|
||||
public var postIsSensitive:Bool {
|
||||
get {
|
||||
if useInstanceContentSettings {
|
||||
return serverPreferences?.postIsSensitive ?? false
|
||||
}
|
||||
else {
|
||||
return appDefaultPostsSensitive
|
||||
}
|
||||
public var postIsSensitive: Bool {
|
||||
if useInstanceContentSettings {
|
||||
return serverPreferences?.postIsSensitive ?? false
|
||||
} else {
|
||||
return appDefaultPostsSensitive
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public var autoExpandSpoilers: Bool {
|
||||
get {
|
||||
if useInstanceContentSettings {
|
||||
return serverPreferences?.autoExpandSpoilers ?? true
|
||||
}
|
||||
else {
|
||||
return appAutoExpandSpoilers
|
||||
}
|
||||
if useInstanceContentSettings {
|
||||
return serverPreferences?.autoExpandSpoilers ?? true
|
||||
} else {
|
||||
return appAutoExpandSpoilers
|
||||
}
|
||||
}
|
||||
|
||||
public var autoExpandMedia: ServerPreferences.AutoExpandMedia {
|
||||
get {
|
||||
if useInstanceContentSettings {
|
||||
return serverPreferences?.autoExpandMedia ?? .hideSensitive
|
||||
}
|
||||
else {
|
||||
return appAutoExpandMedia
|
||||
}
|
||||
if useInstanceContentSettings {
|
||||
return serverPreferences?.autoExpandMedia ?? .hideSensitive
|
||||
} else {
|
||||
return appAutoExpandMedia
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public var pushNotificationsCount: Int {
|
||||
get {
|
||||
Self.sharedDefault?.integer(forKey: "push_notifications_count") ?? 0
|
||||
|
|
|
@ -20,7 +20,8 @@ public struct AppAccount: Codable, Identifiable, Hashable {
|
|||
|
||||
public init(server: String,
|
||||
accountName: String?,
|
||||
oauthToken: OauthToken? = nil) {
|
||||
oauthToken: OauthToken? = nil)
|
||||
{
|
||||
self.server = server
|
||||
self.accountName = accountName
|
||||
self.oauthToken = oauthToken
|
||||
|
|
|
@ -35,9 +35,9 @@ public struct NullableString: Codable, Equatable, Hashable {
|
|||
public init(from decoder: Decoder) throws {
|
||||
do {
|
||||
let container = try decoder.singleValueContainer()
|
||||
self.value = try container.decode(String.self)
|
||||
value = try container.decode(String.self)
|
||||
} catch {
|
||||
self.value = nil
|
||||
value = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,8 +23,8 @@ public struct ServerFilter: Codable, Identifiable, Hashable {
|
|||
public let expireIn: Int?
|
||||
}
|
||||
|
||||
extension ServerFilter.Context {
|
||||
public var iconName: String {
|
||||
public extension ServerFilter.Context {
|
||||
var iconName: String {
|
||||
switch self {
|
||||
case .home:
|
||||
return "rectangle.on.rectangle"
|
||||
|
@ -39,7 +39,7 @@ extension ServerFilter.Context {
|
|||
}
|
||||
}
|
||||
|
||||
public var name: String {
|
||||
var name: String {
|
||||
switch self {
|
||||
case .home:
|
||||
return "Home and lists"
|
||||
|
@ -55,8 +55,8 @@ extension ServerFilter.Context {
|
|||
}
|
||||
}
|
||||
|
||||
extension ServerFilter.Action {
|
||||
public var label: String {
|
||||
public extension ServerFilter.Action {
|
||||
var label: String {
|
||||
switch self {
|
||||
case .warn:
|
||||
return "Hide with a warning"
|
||||
|
|
|
@ -30,7 +30,7 @@ public enum ServerFilters: Endpoint {
|
|||
switch self {
|
||||
case let .addKeyword(_, keyword, wholeWord):
|
||||
return [.init(name: "keyword", value: keyword),
|
||||
.init(name: "whole_word", value: wholeWord ? "true": "false")]
|
||||
.init(name: "whole_word", value: wholeWord ? "true" : "false")]
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
@ -57,7 +57,8 @@ public struct ServerFilterData: Encodable {
|
|||
public init(title: String,
|
||||
context: [ServerFilter.Context],
|
||||
filterAction: ServerFilter.Action,
|
||||
expireIn: Int?) {
|
||||
expireIn: Int?)
|
||||
{
|
||||
self.title = title
|
||||
self.context = context
|
||||
self.filterAction = filterAction
|
||||
|
|
|
@ -76,7 +76,7 @@ class NotificationsViewModel: ObservableObject {
|
|||
newNotifications = newNotifications.filter { notification in
|
||||
!consolidatedNotifications.contains(where: { $0.id == notification.id })
|
||||
}
|
||||
self.notifications.append(contentsOf: newNotifications)
|
||||
notifications.append(contentsOf: newNotifications)
|
||||
consolidatedNotifications.insert(contentsOf: newNotifications.consolidated(), at: 0)
|
||||
}
|
||||
withAnimation {
|
||||
|
@ -98,7 +98,7 @@ class NotificationsViewModel: ObservableObject {
|
|||
maxId: lastId,
|
||||
types: queryTypes))
|
||||
consolidatedNotifications.append(contentsOf: newNotifications.consolidated())
|
||||
self.notifications.append(contentsOf: newNotifications)
|
||||
notifications.append(contentsOf: newNotifications)
|
||||
state = .display(notifications: consolidatedNotifications, nextPageState: newNotifications.count < 15 ? .none : .hasNextPage)
|
||||
} catch {
|
||||
state = .error(error: error)
|
||||
|
|
|
@ -159,11 +159,10 @@ struct StatusEditorMediaView: View {
|
|||
.foregroundColor(.red)
|
||||
}
|
||||
.alert("alert.error", isPresented: $isErrorDisplayed) {
|
||||
Button("Ok", action: { })
|
||||
Button("Ok", action: {})
|
||||
} message: {
|
||||
Text(error.error ?? "")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private var altMarker: some View {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import AVFoundation
|
||||
import Foundation
|
||||
import PhotosUI
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
import UniformTypeIdentifiers
|
||||
import AVFoundation
|
||||
|
||||
@MainActor
|
||||
enum StatusEditorUTTypeSupported: String, CaseIterable {
|
||||
|
@ -74,7 +74,7 @@ struct MovieFileTranseferable: Transferable {
|
|||
private let url: URL
|
||||
var compressedVideoURL: URL? {
|
||||
get async {
|
||||
return await withCheckedContinuation { continuation in
|
||||
await withCheckedContinuation { continuation in
|
||||
let urlAsset = AVURLAsset(url: url, options: nil)
|
||||
guard let exportSession = AVAssetExportSession(asset: urlAsset, presetName: AVAssetExportPresetMediumQuality) else {
|
||||
continuation.resume(returning: nil)
|
||||
|
@ -84,7 +84,7 @@ struct MovieFileTranseferable: Transferable {
|
|||
exportSession.outputURL = outputURL
|
||||
exportSession.outputFileType = .mp4
|
||||
exportSession.shouldOptimizeForNetworkUse = true
|
||||
exportSession.exportAsynchronously { () -> Void in
|
||||
exportSession.exportAsynchronously { () in
|
||||
continuation.resume(returning: outputURL)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ public class StatusEditorViewModel: ObservableObject {
|
|||
inflateSelectedMedias()
|
||||
}
|
||||
}
|
||||
|
||||
@Published var isMediasLoading: Bool = false
|
||||
|
||||
@Published var mediasImages: [StatusEditorMediaContainer] = []
|
||||
|
|
|
@ -225,13 +225,12 @@ public struct StatusRowView: View {
|
|||
private func makeStatusContentView(status: AnyStatus) -> some View {
|
||||
Group {
|
||||
if !status.spoilerText.asRawText.isEmpty {
|
||||
|
||||
HStack(alignment: .top) {
|
||||
Text("⚠︎")
|
||||
.font(.system(.subheadline , weight:.bold))
|
||||
.font(.system(.subheadline, weight: .bold))
|
||||
.foregroundColor(.secondary)
|
||||
EmojiTextApp(status.spoilerText, emojis: status.emojis, language: status.language)
|
||||
.font(.system(.subheadline , weight:.bold))
|
||||
.font(.system(.subheadline, weight: .bold))
|
||||
.foregroundColor(.secondary)
|
||||
.multilineTextAlignment(.leading)
|
||||
Spacer()
|
||||
|
@ -247,7 +246,7 @@ public struct StatusRowView: View {
|
|||
.accessibility(label: viewModel.displaySpoiler ? Text("status.show-more") : Text("status.show-less"))
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
.onTapGesture { // make whole row tapable to make up for smaller button size
|
||||
.onTapGesture { // make whole row tapable to make up for smaller button size
|
||||
withAnimation {
|
||||
viewModel.displaySpoiler.toggle()
|
||||
}
|
||||
|
@ -376,7 +375,8 @@ public struct StatusRowView: View {
|
|||
!viewModel.isCompact,
|
||||
theme.statusDisplayStyle == .large,
|
||||
status.content.statusesURLs.isEmpty,
|
||||
status.mediaAttachments.isEmpty {
|
||||
status.mediaAttachments.isEmpty
|
||||
{
|
||||
StatusCardView(card: card)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue