mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-11-26 10:11:00 +00:00
SwiftFormat
This commit is contained in:
parent
f1267620be
commit
6c307aba63
58 changed files with 313 additions and 299 deletions
|
@ -3,13 +3,13 @@ import AppAccount
|
|||
import Conversations
|
||||
import DesignSystem
|
||||
import Env
|
||||
import Explore
|
||||
import LinkPresentation
|
||||
import Lists
|
||||
import Models
|
||||
import Status
|
||||
import SwiftUI
|
||||
import Timeline
|
||||
import Explore
|
||||
|
||||
@MainActor
|
||||
extension View {
|
||||
|
|
|
@ -112,7 +112,8 @@ struct IceCubesApp: App {
|
|||
SideBarView(selectedTab: $selectedTab,
|
||||
popToRootTab: $popToRootTab,
|
||||
tabs: availableTabs,
|
||||
routerPath: sidebarRouterPath) {
|
||||
routerPath: sidebarRouterPath)
|
||||
{
|
||||
GeometryReader { _ in
|
||||
HStack(spacing: 0) {
|
||||
ZStack {
|
||||
|
|
|
@ -30,18 +30,16 @@ struct AddAccountView: View {
|
|||
private let instanceNamePublisher = PassthroughSubject<String, Never>()
|
||||
|
||||
private var sanitizedName: String {
|
||||
get {
|
||||
var name = instanceName
|
||||
.replacingOccurrences(of: "http://", with: "")
|
||||
.replacingOccurrences(of: "https://", with: "")
|
||||
|
||||
if name.contains("@") {
|
||||
let parts = name.components(separatedBy: "@")
|
||||
name = parts[parts.count-1] // [@]username@server.address.com
|
||||
name = parts[parts.count - 1] // [@]username@server.address.com
|
||||
}
|
||||
return name
|
||||
}
|
||||
}
|
||||
|
||||
@FocusState private var isInstanceURLFieldFocused: Bool
|
||||
|
||||
|
@ -94,8 +92,8 @@ struct AddAccountView: View {
|
|||
.onChange(of: instanceName) { newValue in
|
||||
instanceNamePublisher.send(newValue)
|
||||
}
|
||||
.onReceive(instanceNamePublisher.debounce(for: .milliseconds(500), scheduler: DispatchQueue.main)) { newValue in
|
||||
//let newValue = newValue
|
||||
.onReceive(instanceNamePublisher.debounce(for: .milliseconds(500), scheduler: DispatchQueue.main)) { _ in
|
||||
// let newValue = newValue
|
||||
// .replacingOccurrences(of: "http://", with: "")
|
||||
// .replacingOccurrences(of: "https://", with: "")
|
||||
let client = Client(server: sanitizedName)
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import Combine
|
||||
import DesignSystem
|
||||
import Env
|
||||
import Models
|
||||
import Network
|
||||
import Status
|
||||
import SwiftUI
|
||||
import Combine
|
||||
|
||||
class DisplaySettingsLocalColors: ObservableObject {
|
||||
@Published var tintColor = Theme.shared.tintColor
|
||||
|
@ -17,19 +17,19 @@ class DisplaySettingsLocalColors: ObservableObject {
|
|||
init() {
|
||||
$tintColor
|
||||
.debounce(for: .seconds(0.5), scheduler: DispatchQueue.main)
|
||||
.sink(receiveValue: { newColor in Theme.shared.tintColor = newColor } )
|
||||
.sink(receiveValue: { newColor in Theme.shared.tintColor = newColor })
|
||||
.store(in: &subscriptions)
|
||||
$primaryBackgroundColor
|
||||
.debounce(for: .seconds(0.5), scheduler: DispatchQueue.main)
|
||||
.sink(receiveValue: { newColor in Theme.shared.primaryBackgroundColor = newColor } )
|
||||
.sink(receiveValue: { newColor in Theme.shared.primaryBackgroundColor = newColor })
|
||||
.store(in: &subscriptions)
|
||||
$secondaryBackgroundColor
|
||||
.debounce(for: .seconds(0.5), scheduler: DispatchQueue.main)
|
||||
.sink(receiveValue: { newColor in Theme.shared.secondaryBackgroundColor = newColor } )
|
||||
.sink(receiveValue: { newColor in Theme.shared.secondaryBackgroundColor = newColor })
|
||||
.store(in: &subscriptions)
|
||||
$labelColor
|
||||
.debounce(for: .seconds(0.5), scheduler: DispatchQueue.main)
|
||||
.sink(receiveValue: { newColor in Theme.shared.labelColor = newColor } )
|
||||
.sink(receiveValue: { newColor in Theme.shared.labelColor = newColor })
|
||||
.store(in: &subscriptions)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,7 +105,7 @@ struct SupportAppView: View {
|
|||
private func fetchStoreProducts() {
|
||||
Purchases.shared.getProducts(Tip.allCases.map { $0.productId }) { products in
|
||||
self.subscription = products.first(where: { $0.productIdentifier == Tip.supporter.productId })
|
||||
self.products = products.filter{ $0.productIdentifier != Tip.supporter.productId}.sorted(by: { $0.price < $1.price })
|
||||
self.products = products.filter { $0.productIdentifier != Tip.supporter.productId }.sorted(by: { $0.price < $1.price })
|
||||
withAnimation {
|
||||
loadingProducts = false
|
||||
}
|
||||
|
@ -179,7 +179,6 @@ struct SupportAppView: View {
|
|||
Spacer()
|
||||
makePurchaseButton(product: subscription)
|
||||
}
|
||||
|
||||
}
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import Account
|
||||
import DesignSystem
|
||||
import Explore
|
||||
import Foundation
|
||||
import Status
|
||||
import SwiftUI
|
||||
import DesignSystem
|
||||
|
||||
enum Tab: Int, Identifiable, Hashable {
|
||||
case timeline, notifications, mentions, explore, messages, settings, other
|
||||
|
|
|
@ -54,7 +54,8 @@ class ShareViewController: UIViewController {
|
|||
|
||||
NotificationCenter.default.addObserver(forName: NotificationsName.shareSheetClose,
|
||||
object: nil,
|
||||
queue: nil) { _ in
|
||||
queue: nil)
|
||||
{ _ in
|
||||
self.close()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import SwiftUI
|
||||
import Env
|
||||
import Network
|
||||
import SwiftUI
|
||||
|
||||
public struct AccountDetailContextMenu: View {
|
||||
@EnvironmentObject private var client: Client
|
||||
|
|
|
@ -51,7 +51,8 @@ public struct AccountDetailView: View {
|
|||
|
||||
Picker("", selection: $viewModel.selectedTab) {
|
||||
ForEach(isCurrentUser ? AccountDetailViewModel.Tab.currentAccountTabs : AccountDetailViewModel.Tab.accountTabs,
|
||||
id: \.self) { tab in
|
||||
id: \.self)
|
||||
{ tab in
|
||||
Image(systemName: tab.iconName)
|
||||
.tag(tab)
|
||||
}
|
||||
|
|
|
@ -98,6 +98,5 @@ public struct AccountsListRow: View {
|
|||
.environmentObject(currentAccount)
|
||||
.environmentObject(client)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,9 +23,9 @@ struct EditFilterView: View {
|
|||
@FocusState private var isTitleFocused: Bool
|
||||
|
||||
private var data: ServerFilterData {
|
||||
var expiresIn: String? = nil;
|
||||
var expiresIn: String?
|
||||
// we add 50 seconds, otherwise we immediately show 6d for a 7d filter (6d, 23h, 59s)
|
||||
switch(expirySelection){
|
||||
switch expirySelection {
|
||||
case .infinite:
|
||||
expiresIn = "" // need to send an empty value in order for the server to clear this field in the filter
|
||||
case .custom:
|
||||
|
@ -95,9 +95,8 @@ struct EditFilterView: View {
|
|||
}
|
||||
if expirySelection != .infinite {
|
||||
DatePicker("filter.edit.expiry.date-time",
|
||||
selection: Binding<Date>(get: {self.expiresAt ?? Date()}, set: {self.expiresAt = $0}),
|
||||
displayedComponents: [.date, .hourAndMinute]
|
||||
)
|
||||
selection: Binding<Date>(get: { self.expiresAt ?? Date() }, set: { self.expiresAt = $0 }),
|
||||
displayedComponents: [.date, .hourAndMinute])
|
||||
.disabled(expirySelection != .custom)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,8 @@ public struct AppAccountView: View {
|
|||
.offset(x: 5, y: -5)
|
||||
} else if viewModel.showBadge,
|
||||
let token = viewModel.appAccount.oauthToken,
|
||||
preferences.getNotificationsCount(for: token) > 0 {
|
||||
preferences.getNotificationsCount(for: token) > 0
|
||||
{
|
||||
let notificationsCount = preferences.getNotificationsCount(for: token)
|
||||
ZStack {
|
||||
Circle()
|
||||
|
|
|
@ -177,7 +177,8 @@ struct ConversationMessageView: View {
|
|||
let width = mediaWidth(proxy: proxy)
|
||||
if let url = attachement.url {
|
||||
LazyImage(request: makeImageRequest(for: url,
|
||||
size: .init(width: width, height: 200))) { state in
|
||||
size: .init(width: width, height: 200)))
|
||||
{ state in
|
||||
if let image = state.image {
|
||||
image
|
||||
.resizable()
|
||||
|
|
|
@ -47,7 +47,8 @@ public struct ConversationsListView: View {
|
|||
} else if viewModel.isError {
|
||||
ErrorView(title: "conversations.error.title",
|
||||
message: "conversations.error.message",
|
||||
buttonTitle: "conversations.error.button") {
|
||||
buttonTitle: "conversations.error.button")
|
||||
{
|
||||
Task {
|
||||
await viewModel.fetchConversations()
|
||||
}
|
||||
|
|
|
@ -109,11 +109,13 @@ public extension UIFont {
|
|||
}
|
||||
return UIFont(descriptor: descriptor, size: pointSize)
|
||||
}
|
||||
|
||||
var emojiSize: CGFloat {
|
||||
self.pointSize
|
||||
pointSize
|
||||
}
|
||||
|
||||
var emojiBaselineOffset: CGFloat {
|
||||
// Center emoji with capital letter size of font
|
||||
-(self.emojiSize - self.capHeight) / 2
|
||||
-(emojiSize - capHeight) / 2
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,15 +7,12 @@ import SwiftUI
|
|||
// images named in lower case are Apple's symbols
|
||||
// images inamed in CamelCase are custom
|
||||
|
||||
extension Label where Title == Text, Icon == Image {
|
||||
|
||||
public init (_ title: LocalizedStringKey, imageNamed: String) {
|
||||
public extension Label where Title == Text, Icon == Image {
|
||||
init(_ title: LocalizedStringKey, imageNamed: String) {
|
||||
if imageNamed.lowercased() == imageNamed {
|
||||
self.init(title, systemImage: imageNamed)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
self.init(title, image: imageNamed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -70,7 +70,6 @@ public class RouterPath: ObservableObject {
|
|||
@Published public var path: [RouterDestination] = []
|
||||
@Published public var presentedSheet: SheetDestination?
|
||||
|
||||
|
||||
public init() {}
|
||||
|
||||
public func navigate(to: RouterDestination) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import AVKit
|
||||
import CoreHaptics
|
||||
import UIKit
|
||||
import AVKit
|
||||
|
||||
public class SoundEffectManager {
|
||||
public static let shared: SoundEffectManager = .init()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import Foundation
|
||||
import SwiftUI
|
||||
import Models
|
||||
import Network
|
||||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
public protocol StatusDataControlling: ObservableObject {
|
||||
|
@ -65,23 +65,23 @@ public final class StatusDataController: StatusDataControlling {
|
|||
self.status = status
|
||||
self.client = client
|
||||
|
||||
self.isReblogged = status.reblogged == true
|
||||
self.isBookmarked = status.bookmarked == true
|
||||
self.isFavorited = status.favourited == true
|
||||
isReblogged = status.reblogged == true
|
||||
isBookmarked = status.bookmarked == true
|
||||
isFavorited = status.favourited == true
|
||||
|
||||
self.reblogsCount = status.reblogsCount
|
||||
self.repliesCount = status.repliesCount
|
||||
self.favoritesCount = status.favouritesCount
|
||||
reblogsCount = status.reblogsCount
|
||||
repliesCount = status.repliesCount
|
||||
favoritesCount = status.favouritesCount
|
||||
}
|
||||
|
||||
public func updateFrom(status: AnyStatus, publishUpdate: Bool) {
|
||||
self.isReblogged = status.reblogged == true
|
||||
self.isBookmarked = status.bookmarked == true
|
||||
self.isFavorited = status.favourited == true
|
||||
isReblogged = status.reblogged == true
|
||||
isBookmarked = status.bookmarked == true
|
||||
isFavorited = status.favourited == true
|
||||
|
||||
self.reblogsCount = status.reblogsCount
|
||||
self.repliesCount = status.repliesCount
|
||||
self.favoritesCount = status.favouritesCount
|
||||
reblogsCount = status.reblogsCount
|
||||
repliesCount = status.repliesCount
|
||||
favoritesCount = status.favouritesCount
|
||||
|
||||
if publishUpdate {
|
||||
objectWillChange.send()
|
||||
|
@ -105,7 +105,6 @@ public final class StatusDataController: StatusDataControlling {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public func toggleReblog(remoteStatus: String?) async {
|
||||
guard client.isAuth else { return }
|
||||
isReblogged.toggle()
|
||||
|
|
|
@ -70,7 +70,7 @@ public class UserPreferences: ObservableObject {
|
|||
// Main actor-isolated static property 'allCases' cannot be used to
|
||||
// satisfy nonisolated protocol requirement
|
||||
//
|
||||
nonisolated public static var allCases: [Self] {
|
||||
public nonisolated static var allCases: [Self] {
|
||||
[.iconWithText, .iconOnly]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -127,7 +127,8 @@ public struct ExploreView: View {
|
|||
private var suggestedAccountsSection: some View {
|
||||
Section("explore.section.suggested-users") {
|
||||
ForEach(viewModel.suggestedAccounts
|
||||
.prefix(upTo: viewModel.suggestedAccounts.count > 3 ? 3 : viewModel.suggestedAccounts.count)) { account in
|
||||
.prefix(upTo: viewModel.suggestedAccounts.count > 3 ? 3 : viewModel.suggestedAccounts.count))
|
||||
{ account in
|
||||
if let relationship = viewModel.suggestedAccountsRelationShips.first(where: { $0.id == account.id }) {
|
||||
AccountsListRow(viewModel: .init(account: account, relationShip: relationship))
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
|
@ -144,7 +145,8 @@ public struct ExploreView: View {
|
|||
private var trendingTagsSection: some View {
|
||||
Section("explore.section.trending.tags") {
|
||||
ForEach(viewModel.trendingTags
|
||||
.prefix(upTo: viewModel.trendingTags.count > 5 ? 5 : viewModel.trendingTags.count)) { tag in
|
||||
.prefix(upTo: viewModel.trendingTags.count > 5 ? 5 : viewModel.trendingTags.count))
|
||||
{ tag in
|
||||
TagRowView(tag: tag)
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
.padding(.vertical, 4)
|
||||
|
@ -160,7 +162,8 @@ public struct ExploreView: View {
|
|||
private var trendingPostsSection: some View {
|
||||
Section("explore.section.trending.posts") {
|
||||
ForEach(viewModel.trendingStatuses
|
||||
.prefix(upTo: viewModel.trendingStatuses.count > 3 ? 3 : viewModel.trendingStatuses.count)) { status in
|
||||
.prefix(upTo: viewModel.trendingStatuses.count > 3 ? 3 : viewModel.trendingStatuses.count))
|
||||
{ status in
|
||||
StatusRowView(viewModel: { .init(status: status, client: client, routerPath: routerPath) })
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
.padding(.vertical, 8)
|
||||
|
@ -177,7 +180,8 @@ public struct ExploreView: View {
|
|||
private var trendingLinksSection: some View {
|
||||
Section("explore.section.trending.links") {
|
||||
ForEach(viewModel.trendingLinks
|
||||
.prefix(upTo: viewModel.trendingLinks.count > 3 ? 3 : viewModel.trendingLinks.count)) { card in
|
||||
.prefix(upTo: viewModel.trendingLinks.count > 3 ? 3 : viewModel.trendingLinks.count))
|
||||
{ card in
|
||||
StatusRowCardView(card: card)
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
.padding(.vertical, 8)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import SwiftUI
|
||||
import Models
|
||||
import DesignSystem
|
||||
import Models
|
||||
import SwiftUI
|
||||
|
||||
public struct TagsListView: View {
|
||||
@EnvironmentObject private var theme: Theme
|
||||
|
|
|
@ -2,6 +2,7 @@ import Foundation
|
|||
|
||||
public struct ServerError: Decodable, Error {
|
||||
public let error: String?
|
||||
public var httpCode: Int
|
||||
}
|
||||
|
||||
extension ServerError: Sendable {}
|
||||
|
|
|
@ -103,7 +103,6 @@ public final class Status: AnyStatus, Codable, Identifiable, Equatable, Hashable
|
|||
public let sensitive: Bool
|
||||
public let language: String?
|
||||
|
||||
|
||||
public init(id: String, content: HTMLString, account: Account, createdAt: ServerDate, editedAt: ServerDate?, reblog: ReblogStatus?, mediaAttachments: [MediaAttachment], mentions: [Mention], repliesCount: Int, reblogsCount: Int, favouritesCount: Int, card: Card?, favourited: Bool?, reblogged: Bool?, pinned: Bool?, bookmarked: Bool?, emojis: [Emoji], url: String?, application: Application?, inReplyToId: String?, inReplyToAccountId: String?, visibility: Visibility, poll: Poll?, spoilerText: HTMLString, filtered: [Filtered]?, sensitive: Bool, language: String?) {
|
||||
self.id = id
|
||||
self.content = content
|
||||
|
@ -277,5 +276,3 @@ extension Status: Sendable {}
|
|||
|
||||
// Every property in ReblogStatus is immutable.
|
||||
extension ReblogStatus: Sendable {}
|
||||
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import Combine
|
||||
import Foundation
|
||||
import Models
|
||||
import SwiftUI
|
||||
import os
|
||||
import SwiftUI
|
||||
|
||||
public final class Client: ObservableObject, Equatable, Identifiable, Hashable {
|
||||
public static func == (lhs: Client, rhs: Client) -> Bool {
|
||||
|
@ -61,7 +61,7 @@ public final class Client: ObservableObject, Equatable, Identifiable, Hashable {
|
|||
public init(server: String, version: Version = .v1, oauthToken: OauthToken? = nil) {
|
||||
self.server = server
|
||||
self.version = version
|
||||
self.critical = .init(initialState: Critical(oauthToken: oauthToken, connections: [server]))
|
||||
critical = .init(initialState: Critical(oauthToken: oauthToken, connections: [server]))
|
||||
urlSession = URLSession.shared
|
||||
decoder.keyDecodingStrategy = .convertFromSnakeCase
|
||||
}
|
||||
|
@ -141,7 +141,7 @@ public final class Client: ObservableObject, Equatable, Identifiable, Hashable {
|
|||
linkHandler = .init(rawLink: link)
|
||||
}
|
||||
logResponseOnError(httpResponse: httpResponse, data: data)
|
||||
return (try decoder.decode(Entity.self, from: data), linkHandler)
|
||||
return try (decoder.decode(Entity.self, from: data), linkHandler)
|
||||
}
|
||||
|
||||
public func post<Entity: Decodable>(endpoint: Endpoint, forceVersion: Version? = nil) async throws -> Entity {
|
||||
|
@ -184,7 +184,10 @@ public final class Client: ObservableObject, Equatable, Identifiable, Hashable {
|
|||
do {
|
||||
return try decoder.decode(Entity.self, from: data)
|
||||
} catch {
|
||||
if let serverError = try? decoder.decode(ServerError.self, from: data) {
|
||||
if var serverError = try? decoder.decode(ServerError.self, from: data) {
|
||||
if let httpResponse = httpResponse as? HTTPURLResponse {
|
||||
serverError.httpCode = httpResponse.statusCode
|
||||
}
|
||||
throw serverError
|
||||
}
|
||||
throw error
|
||||
|
|
|
@ -47,7 +47,7 @@ public struct OpenAIClient {
|
|||
}
|
||||
|
||||
public init(content: String, temperature: CGFloat) {
|
||||
self.messages = [.init(content: content)]
|
||||
messages = [.init(content: content)]
|
||||
self.temperature = temperature
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,23 +47,23 @@ extension Models.Notification.NotificationType {
|
|||
|
||||
func icon(isPrivate: Bool) -> Image {
|
||||
if isPrivate {
|
||||
return Image(systemName:"tray.fill")
|
||||
return Image(systemName: "tray.fill")
|
||||
}
|
||||
switch self {
|
||||
case .status:
|
||||
return Image(systemName:"pencil")
|
||||
return Image(systemName: "pencil")
|
||||
case .mention:
|
||||
return Image(systemName:"at")
|
||||
return Image(systemName: "at")
|
||||
case .reblog:
|
||||
return Image("Rocket.Fill")
|
||||
case .follow, .follow_request:
|
||||
return Image(systemName:"person.fill.badge.plus")
|
||||
return Image(systemName: "person.fill.badge.plus")
|
||||
case .favourite:
|
||||
return Image(systemName:"star.fill")
|
||||
return Image(systemName: "star.fill")
|
||||
case .poll:
|
||||
return Image(systemName:"chart.bar.fill")
|
||||
return Image(systemName: "chart.bar.fill")
|
||||
case .update:
|
||||
return Image(systemName:"pencil.line")
|
||||
return Image(systemName: "pencil.line")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -145,7 +145,8 @@ public struct NotificationsListView: View {
|
|||
case .error:
|
||||
ErrorView(title: "notifications.error.title",
|
||||
message: "notifications.error.message",
|
||||
buttonTitle: "action.retry") {
|
||||
buttonTitle: "action.retry")
|
||||
{
|
||||
Task {
|
||||
await viewModel.fetchNotifications()
|
||||
}
|
||||
|
|
|
@ -155,7 +155,8 @@ public struct StatusDetailView: View {
|
|||
private var errorView: some View {
|
||||
ErrorView(title: "status.error.title",
|
||||
message: "status.error.message",
|
||||
buttonTitle: "action.retry") {
|
||||
buttonTitle: "action.retry")
|
||||
{
|
||||
Task {
|
||||
await viewModel.fetch()
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import Env
|
||||
import Foundation
|
||||
import Models
|
||||
import Network
|
||||
import SwiftUI
|
||||
import Env
|
||||
|
||||
@MainActor
|
||||
class StatusDetailViewModel: ObservableObject {
|
||||
|
|
|
@ -51,7 +51,8 @@ struct StatusEditorAccessoryView: View {
|
|||
matching: .any(of: [.images, .videos]))
|
||||
.fileImporter(isPresented: $isFileImporterPresented,
|
||||
allowedContentTypes: [.image, .video],
|
||||
allowsMultipleSelection: true) { result in
|
||||
allowsMultipleSelection: true)
|
||||
{ result in
|
||||
if let urls = try? result.get() {
|
||||
viewModel.processURLs(urls: urls)
|
||||
}
|
||||
|
@ -59,7 +60,6 @@ struct StatusEditorAccessoryView: View {
|
|||
.accessibilityLabel("accessibility.editor.button.attach-photo")
|
||||
.disabled(viewModel.showPoll)
|
||||
|
||||
|
||||
Button {
|
||||
withAnimation {
|
||||
viewModel.showPoll.toggle()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import AVFoundation
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AVFoundation
|
||||
|
||||
actor StatusEditorCompressor {
|
||||
enum CompressorError: Error {
|
||||
|
@ -8,7 +8,7 @@ actor StatusEditorCompressor {
|
|||
}
|
||||
|
||||
func compressImageFrom(url: URL) async -> Data? {
|
||||
return await withCheckedContinuation{ continuation in
|
||||
return await withCheckedContinuation { continuation in
|
||||
let sourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
|
||||
guard let source = CGImageSourceCreateWithURL(url as CFURL, sourceOptions) else {
|
||||
continuation.resume(returning: nil)
|
||||
|
@ -26,7 +26,7 @@ actor StatusEditorCompressor {
|
|||
kCGImageSourceCreateThumbnailFromImageAlways: true,
|
||||
kCGImageSourceCreateThumbnailWithTransform: true,
|
||||
kCGImageSourceThumbnailMaxPixelSize: maxPixelSize,
|
||||
] as [CFString : Any] as CFDictionary
|
||||
] as [CFString: Any] as CFDictionary
|
||||
|
||||
guard let cgImage = CGImageSourceCreateThumbnailAtIndex(source, 0, downsampleOptions) else {
|
||||
continuation.resume(returning: nil)
|
||||
|
@ -45,7 +45,7 @@ actor StatusEditorCompressor {
|
|||
}()
|
||||
|
||||
let destinationProperties = [
|
||||
kCGImageDestinationLossyCompressionQuality: isPNG ? 1.0 : 0.75
|
||||
kCGImageDestinationLossyCompressionQuality: isPNG ? 1.0 : 0.75,
|
||||
] as CFDictionary
|
||||
|
||||
CGImageDestinationAddImage(imageDestination, cgImage, destinationProperties)
|
||||
|
@ -66,12 +66,13 @@ actor StatusEditorCompressor {
|
|||
throw CompressorError.noData
|
||||
}
|
||||
|
||||
let maxSize: Int = 10 * 1024 * 1024
|
||||
let maxSize = 10 * 1024 * 1024
|
||||
|
||||
if imageData.count > maxSize {
|
||||
while imageData.count > maxSize {
|
||||
guard let compressedImage = UIImage(data: imageData),
|
||||
let compressedData = compressedImage.jpegData(compressionQuality: 0.8) else {
|
||||
let compressedData = compressedImage.jpegData(compressionQuality: 0.8)
|
||||
else {
|
||||
throw CompressorError.noData
|
||||
}
|
||||
imageData = compressedData
|
||||
|
@ -97,5 +98,4 @@ actor StatusEditorCompressor {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ enum StatusEditorUTTypeSupported: String, CaseIterable {
|
|||
// Main actor-isolated static property 'allCases' cannot be used to
|
||||
// satisfy nonisolated protocol requirement
|
||||
//
|
||||
nonisolated public static var allCases: [StatusEditorUTTypeSupported] {
|
||||
public nonisolated static var allCases: [StatusEditorUTTypeSupported] {
|
||||
[.url, .text, .plaintext, .image, .jpeg, .png, .tiff, .video,
|
||||
.movie, .mp4, .gif, .gif2, .quickTimeMovie, .uiimage, .adobeRawImage]
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ public class StatusEditorViewModel: NSObject, ObservableObject {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
var theme: Theme?
|
||||
var preferences: UserPreferences?
|
||||
var languageConfirmationDialogLanguages: (detected: String, selected: String)?
|
||||
|
@ -391,7 +392,8 @@ public class StatusEditorViewModel: NSObject, ObservableObject {
|
|||
error: nil))
|
||||
} else if let content = content as? ImageFileTranseferable,
|
||||
let compressedData = await compressor.compressImageFrom(url: content.url),
|
||||
let image = UIImage(data: compressedData) {
|
||||
let image = UIImage(data: compressedData)
|
||||
{
|
||||
mediasImages.append(.init(image: image,
|
||||
movieTransferable: nil,
|
||||
gifTransferable: nil,
|
||||
|
@ -616,7 +618,8 @@ public class StatusEditorViewModel: NSObject, ObservableObject {
|
|||
}
|
||||
} else if let videoURL = originalContainer.movieTransferable?.url,
|
||||
let compressedVideoURL = await compressor.compressVideo(videoURL),
|
||||
let data = try? Data(contentsOf: compressedVideoURL) {
|
||||
let data = try? Data(contentsOf: compressedVideoURL)
|
||||
{
|
||||
let uploadedMedia = try await uploadMedia(data: data, mimeType: compressedVideoURL.mimeType())
|
||||
mediasImages[index] = .init(image: mode.isInShareExtension ? originalContainer.image : nil,
|
||||
movieTransferable: originalContainer.movieTransferable,
|
||||
|
|
|
@ -16,7 +16,8 @@ public struct StatusesListView<Fetcher>: View where Fetcher: StatusesFetcher {
|
|||
public init(fetcher: Fetcher,
|
||||
client: Client,
|
||||
routerPath: RouterPath,
|
||||
isRemote: Bool = false) {
|
||||
isRemote: Bool = false)
|
||||
{
|
||||
self.fetcher = fetcher
|
||||
self.isRemote = isRemote
|
||||
self.client = client
|
||||
|
@ -33,7 +34,8 @@ public struct StatusesListView<Fetcher>: View where Fetcher: StatusesFetcher {
|
|||
case .error:
|
||||
ErrorView(title: "status.error.title",
|
||||
message: "status.error.loading.message",
|
||||
buttonTitle: "action.retry") {
|
||||
buttonTitle: "action.retry")
|
||||
{
|
||||
Task {
|
||||
await fetcher.fetchNewestStatuses()
|
||||
}
|
||||
|
|
|
@ -22,7 +22,8 @@ class VideoPlayerViewModel: ObservableObject {
|
|||
}
|
||||
guard let player else { return }
|
||||
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime,
|
||||
object: player.currentItem, queue: .main) { [weak self] _ in
|
||||
object: player.currentItem, queue: .main)
|
||||
{ [weak self] _ in
|
||||
if autoPlay {
|
||||
self?.player?.seek(to: CMTime.zero)
|
||||
self?.player?.play()
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import Combine
|
||||
import DesignSystem
|
||||
import Env
|
||||
import Models
|
||||
import NaturalLanguage
|
||||
import Network
|
||||
import SwiftUI
|
||||
import DesignSystem
|
||||
|
||||
@MainActor
|
||||
public class StatusRowViewModel: ObservableObject {
|
||||
|
@ -39,10 +39,11 @@ public class StatusRowViewModel: ObservableObject {
|
|||
recalcCollapse()
|
||||
}
|
||||
}
|
||||
|
||||
// number of lines to show, nil means show the whole post
|
||||
@Published var lineLimit: Int? = nil
|
||||
// post length determining if the post should be collapsed
|
||||
let collapseThresholdLength : Int = 750
|
||||
let collapseThresholdLength: Int = 750
|
||||
// number of text lines to show on a collpased post
|
||||
let collapsedLines: Int = 8
|
||||
// user preference, set in init
|
||||
|
@ -94,7 +95,7 @@ public class StatusRowViewModel: ObservableObject {
|
|||
textDisabled: Bool = false)
|
||||
{
|
||||
self.status = status
|
||||
self.finalStatus = status.reblog ?? status
|
||||
finalStatus = status.reblog ?? status
|
||||
self.client = client
|
||||
self.routerPath = routerPath
|
||||
self.isFocused = isFocused
|
||||
|
@ -112,7 +113,6 @@ public class StatusRowViewModel: ObservableObject {
|
|||
displaySpoiler = !finalStatus.spoilerText.asRawText.isEmpty
|
||||
}
|
||||
|
||||
|
||||
if status.mentions.first(where: { $0.id == CurrentAccount.shared.account?.id }) != nil {
|
||||
userMentionned = true
|
||||
} else {
|
||||
|
@ -187,7 +187,8 @@ public class StatusRowViewModel: ObservableObject {
|
|||
if !content.statusesURLs.isEmpty,
|
||||
let url = content.statusesURLs.first,
|
||||
!StatusEmbedCache.shared.badStatusesURLs.contains(url),
|
||||
client.hasConnection(with: url) {
|
||||
client.hasConnection(with: url)
|
||||
{
|
||||
return url
|
||||
}
|
||||
return nil
|
||||
|
@ -224,8 +225,7 @@ public class StatusRowViewModel: ObservableObject {
|
|||
}
|
||||
if let embed {
|
||||
StatusEmbedCache.shared.set(url: url, status: embed)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
StatusEmbedCache.shared.badStatusesURLs.insert(url)
|
||||
}
|
||||
withAnimation {
|
||||
|
|
|
@ -24,7 +24,7 @@ struct StatusRowActionsView: View {
|
|||
// Main actor-isolated static property 'allCases' cannot be used to
|
||||
// satisfy nonisolated protocol requirement
|
||||
//
|
||||
nonisolated public static var allCases: [StatusRowActionsView.Action] {
|
||||
public nonisolated static var allCases: [StatusRowActionsView.Action] {
|
||||
[.respond, .boost, .favorite, .bookmark, .share]
|
||||
}
|
||||
|
||||
|
@ -99,7 +99,8 @@ struct StatusRowActionsView: View {
|
|||
{
|
||||
ShareLink(item: url,
|
||||
subject: Text(viewModel.finalStatus.account.safeDisplayName),
|
||||
message: Text(viewModel.finalStatus.content.asRawText)) {
|
||||
message: Text(viewModel.finalStatus.content.asRawText))
|
||||
{
|
||||
action.image(dataController: statusDataController)
|
||||
}
|
||||
.buttonStyle(.statusAction())
|
||||
|
@ -142,7 +143,8 @@ struct StatusRowActionsView: View {
|
|||
(viewModel.status.visibility == .direct || viewModel.status.visibility == .priv && viewModel.status.account.id != currentAccount.account?.id))
|
||||
if let count = action.count(dataController: statusDataController,
|
||||
viewModel: viewModel,
|
||||
theme: theme), !viewModel.isRemote {
|
||||
theme: theme), !viewModel.isRemote
|
||||
{
|
||||
Text("\(count)")
|
||||
.foregroundColor(Color(UIColor.secondaryLabel))
|
||||
.font(.scaledFootnote)
|
||||
|
@ -151,7 +153,6 @@ struct StatusRowActionsView: View {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private func handleAction(action: Action) {
|
||||
Task {
|
||||
if viewModel.isRemote, viewModel.localStatusId == nil || viewModel.localStatus == nil {
|
||||
|
|
|
@ -71,7 +71,8 @@ struct StatusRowContextMenu: View {
|
|||
{
|
||||
ShareLink(item: url,
|
||||
subject: Text(viewModel.status.reblog?.account.safeDisplayName ?? viewModel.status.account.safeDisplayName),
|
||||
message: Text(viewModel.status.reblog?.content.asRawText ?? viewModel.status.content.asRawText)) {
|
||||
message: Text(viewModel.status.reblog?.content.asRawText ?? viewModel.status.content.asRawText))
|
||||
{
|
||||
Label("status.action.share", systemImage: "square.and.arrow.up")
|
||||
}
|
||||
|
||||
|
|
|
@ -124,7 +124,8 @@ public struct StatusRowMediaPreviewView: View {
|
|||
}
|
||||
}
|
||||
.alert("status.editor.media.image-description",
|
||||
isPresented: $isAltAlertDisplayed) {
|
||||
isPresented: $isAltAlertDisplayed)
|
||||
{
|
||||
Button("alert.button.ok", action: {})
|
||||
} message: {
|
||||
Text(altTextDisplayed ?? "")
|
||||
|
|
|
@ -34,7 +34,7 @@ public actor TimelineCache {
|
|||
try await engine.removeAllData()
|
||||
let itemKeys = statuses.map { CacheKey($0[keyPath: \.id]) }
|
||||
let dataAndKeys = try zip(itemKeys, statuses)
|
||||
.map { (key: $0, data: try encoder.encode($1)) }
|
||||
.map { try (key: $0, data: encoder.encode($1)) }
|
||||
try await engine.write(dataAndKeys)
|
||||
} catch {}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ actor TimelineDatasource {
|
|||
}
|
||||
|
||||
func get() -> [Status] {
|
||||
statuses.filter{ $0.filtered?.first?.filter.filterAction != .hide }
|
||||
statuses.filter { $0.filtered?.first?.filter.filterAction != .hide }
|
||||
}
|
||||
|
||||
func reset() {
|
||||
|
|
Loading…
Reference in a new issue