This commit is contained in:
Thomas Ricouard 2024-02-14 12:48:14 +01:00
parent 2d988d48c1
commit 1f858414d8
146 changed files with 1610 additions and 1637 deletions

View file

@ -49,7 +49,7 @@ struct AppView: View {
} else if UIDevice.current.userInterfaceIdiom == .vision {
return Tab.visionOSTab()
}
return sidebarTabs.tabs.map{ $0.tab }
return sidebarTabs.tabs.map { $0.tab }
}
var tabBarView: some View {

View file

@ -72,8 +72,8 @@ private struct SafariRouter: ViewModifier {
}
#if !os(visionOS)
@MainActor
@Observable private class InAppSafariManager: NSObject, SFSafariViewControllerDelegate {
@MainActor
@Observable private class InAppSafariManager: NSObject, SFSafariViewControllerDelegate {
var windowScene: UIWindowScene?
let viewController: UIViewController = .init()
var window: UIWindow?
@ -123,7 +123,7 @@ private struct SafariRouter: ViewModifier {
window = nil
}
}
}
}
#endif
private struct WindowReader: UIViewRepresentable {

View file

@ -1,7 +1,7 @@
import SwiftUI
import Env
import AppAccount
import DesignSystem
import Env
import SwiftUI
@MainActor
struct NavigationSheet<Content: View>: View {

View file

@ -1,8 +1,8 @@
import SwiftUI
import Env
import AppAccount
import DesignSystem
import Env
import Network
import SwiftUI
@MainActor
struct NavigationTab<Content: View>: View {

View file

@ -60,9 +60,9 @@ struct NotificationsTab: View {
}
}
}
.onChange(of: selectedTab, { _, newValue in
.onChange(of: selectedTab) { _, _ in
clearNotifications()
})
}
.onChange(of: pushNotificationsService.handledNotification) { _, newValue in
if let newValue, let type = newValue.notification.supportedType {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {

View file

@ -5,8 +5,8 @@ import Models
import Network
import NukeUI
import SwiftUI
import UserNotifications
import Timeline
import UserNotifications
@MainActor
struct ContentSettingsView: View {

View file

@ -1,8 +1,8 @@
import SwiftUI
import SwiftData
import Models
import Env
import DesignSystem
import Env
import Models
import SwiftData
import SwiftUI
struct RecenTagsSettingView: View {
@Environment(\.modelContext) private var context

View file

@ -1,8 +1,8 @@
import SwiftUI
import SwiftData
import Models
import Env
import DesignSystem
import Env
import Models
import SwiftData
import SwiftUI
struct RemoteTimelinesSettingView: View {
@Environment(\.modelContext) private var context

View file

@ -1,8 +1,8 @@
import SwiftUI
import SwiftData
import Models
import Env
import DesignSystem
import Env
import Models
import SwiftData
import SwiftUI
struct TagsGroupSettingView: View {
@Environment(\.modelContext) private var context

View file

@ -71,7 +71,7 @@ enum Tab: Int, Identifiable, Hashable, CaseIterable, Codable {
case .links:
NavigationTab { TrendingLinksListView(cards: []) }
case .post:
VStack { }
VStack {}
case .other:
EmptyView()
}
@ -114,7 +114,6 @@ enum Tab: Int, Identifiable, Hashable, CaseIterable, Codable {
Label("explore.section.trending.links", systemImage: iconName)
case .other:
EmptyView()
}
}

View file

@ -95,7 +95,7 @@ struct TimelineTab: View {
}
switch newValue {
case let .tagGroup(title, _, _):
if let group = tagGroups.first(where: { $0.title == title}) {
if let group = tagGroups.first(where: { $0.title == title }) {
selectedTagGroup = group
}
default:
@ -212,7 +212,7 @@ struct TimelineTab: View {
@ViewBuilder
private var pinButton: some View {
let index = pinnedFilters.firstIndex(where: { $0.id == timeline.id})
let index = pinnedFilters.firstIndex(where: { $0.id == timeline.id })
Button {
withAnimation {
if let index {

View file

@ -1,7 +1,7 @@
import SwiftUI
import Env
import AppAccount
import DesignSystem
import Env
import SwiftUI
@MainActor
struct ToolbarTab: ToolbarContent {
@ -17,7 +17,8 @@ struct ToolbarTab: ToolbarContent {
statusEditorToolbarItem(routerPath: routerPath,
visibility: userPreferences.postVisibility)
if UIDevice.current.userInterfaceIdiom != .pad ||
(UIDevice.current.userInterfaceIdiom == .pad && horizontalSizeClass == .compact) {
(UIDevice.current.userInterfaceIdiom == .pad && horizontalSizeClass == .compact)
{
ToolbarItem(placement: .navigationBarLeading) {
AppAccountsSelectorView(routerPath: routerPath)
}

View file

@ -24,7 +24,7 @@ extension NotificationService {
var _plaintext: Data?
do {
_plaintext = try AES.GCM.open(sealedBox, using: key)
} catch { }
} catch {}
guard let plaintext = _plaintext else {
return nil
}

View file

@ -2,11 +2,11 @@ import Account
import AppAccount
import DesignSystem
import Env
import Models
import Network
import StatusKit
import SwiftUI
import UIKit
import Models
class ShareViewController: UIViewController {
override func viewDidLoad() {

View file

@ -31,7 +31,7 @@ let package = Package(
.product(name: "Models", package: "Models"),
.product(name: "StatusKit", package: "StatusKit"),
.product(name: "Env", package: "Env"),
.product(name: "ButtonKit", package: "ButtonKit")
.product(name: "ButtonKit", package: "ButtonKit"),
],
swiftSettings: [
.enableExperimentalFeature("StrictConcurrency"),

View file

@ -37,7 +37,7 @@ public struct AccountDetailContextMenu: View {
Task {
do {
viewModel.relationship = try await client.post(endpoint: Accounts.unblock(id: account.id))
} catch { }
} catch {}
}
} label: {
Label("account.action.unblock", systemImage: "person.crop.circle.badge.exclamationmark")
@ -55,7 +55,7 @@ public struct AccountDetailContextMenu: View {
Task {
do {
viewModel.relationship = try await client.post(endpoint: Accounts.unmute(id: account.id))
} catch { }
} catch {}
}
} label: {
Label("account.action.unmute", systemImage: "speaker")
@ -67,7 +67,7 @@ public struct AccountDetailContextMenu: View {
Task {
do {
viewModel.relationship = try await client.post(endpoint: Accounts.mute(id: account.id, json: MuteData(duration: duration.rawValue)))
} catch { }
} catch {}
}
}
}
@ -86,7 +86,7 @@ public struct AccountDetailContextMenu: View {
viewModel.relationship = try await client.post(endpoint: Accounts.follow(id: account.id,
notify: false,
reblogs: relationship.showingReblogs))
} catch { }
} catch {}
}
} label: {
Label("account.action.notify-disable", systemImage: "bell.fill")
@ -98,7 +98,7 @@ public struct AccountDetailContextMenu: View {
viewModel.relationship = try await client.post(endpoint: Accounts.follow(id: account.id,
notify: true,
reblogs: relationship.showingReblogs))
} catch { }
} catch {}
}
} label: {
Label("account.action.notify-enable", systemImage: "bell")
@ -111,7 +111,7 @@ public struct AccountDetailContextMenu: View {
viewModel.relationship = try await client.post(endpoint: Accounts.follow(id: account.id,
notify: relationship.notifying,
reblogs: false))
} catch { }
} catch {}
}
} label: {
Label("account.action.reboosts-hide", image: "Rocket.Fill")
@ -123,7 +123,7 @@ public struct AccountDetailContextMenu: View {
viewModel.relationship = try await client.post(endpoint: Accounts.follow(id: account.id,
notify: relationship.notifying,
reblogs: true))
} catch { }
} catch {}
}
} label: {
Label("account.action.reboosts-show", image: "Rocket")

View file

@ -220,7 +220,6 @@ public struct AccountDetailView: View {
AvatarView(account.avatar, config: .badge)
.padding(.leading, -4)
.accessibilityLabel(account.safeDisplayName)
}
.accessibilityAddTraits(.isImage)
.buttonStyle(.plain)
@ -288,7 +287,6 @@ public struct AccountDetailView: View {
routerPath.presentedSheet = .mentionStatusEditor(account: account,
visibility: preferences.postVisibility)
#endif
}
} label: {
Image(systemName: "arrowshape.turn.up.left")
@ -370,7 +368,7 @@ public struct AccountDetailView: View {
Task {
do {
viewModel.relationship = try await client.post(endpoint: Accounts.block(id: account.id))
} catch { }
} catch {}
}
}
}

View file

@ -151,7 +151,7 @@ import SwiftUI
self.familiarFollowers = familiarFollowers?.first?.accounts ?? []
}
func fetchNewestStatuses(pullToRefresh: Bool) async {
func fetchNewestStatuses(pullToRefresh _: Bool) async {
guard let client else { return }
do {
statusesState = .loading
@ -166,7 +166,7 @@ import SwiftUI
pinned: nil))
StatusDataControllerProvider.shared.updateDataControllers(for: statuses, client: client)
if selectedTab == .boosts {
boosts = statuses.filter{ $0.reblog != nil }
boosts = statuses.filter { $0.reblog != nil }
}
if selectedTab == .statuses {
pinned =
@ -206,8 +206,8 @@ import SwiftUI
pinned: nil))
statuses.append(contentsOf: newStatuses)
if selectedTab == .boosts {
let newBoosts = statuses.filter{ $0.reblog != nil }
self.boosts.append(contentsOf: newBoosts)
let newBoosts = statuses.filter { $0.reblog != nil }
boosts.append(contentsOf: newBoosts)
}
StatusDataControllerProvider.shared.updateDataControllers(for: newStatuses, client: client)
if selectedTab == .boosts {
@ -253,7 +253,8 @@ import SwiftUI
if let event = event as? StreamEventUpdate {
if event.status.account.id == currentAccount.account?.id {
if (event.status.inReplyToId == nil && selectedTab == .statuses) ||
(event.status.inReplyToId != nil && selectedTab == .replies) {
(event.status.inReplyToId != nil && selectedTab == .replies)
{
statuses.insert(event.status, at: 0)
statusesState = .display(statuses: statuses, nextPageState: .hasNextPage)
}

View file

@ -1,8 +1,8 @@
import Models
import Network
import Observation
import SwiftUI
import OSLog
import SwiftUI
public enum AccountsListMode {
case following(accountId: String), followers(accountId: String)
@ -144,8 +144,6 @@ public enum AccountsListMode {
relationships: relationships,
nextPageState: .none)
}
} catch {
}
} catch {}
}
}

View file

@ -2,8 +2,8 @@ import DesignSystem
import Env
import Models
import Network
import SwiftUI
import NukeUI
import SwiftUI
@MainActor
public struct EditAccountView: View {
@ -14,7 +14,7 @@ public struct EditAccountView: View {
@State private var viewModel = EditAccountViewModel()
public init() { }
public init() {}
public var body: some View {
NavigationStack {

View file

@ -1,9 +1,9 @@
import Models
import Network
import Observation
import SwiftUI
import PhotosUI
import StatusKit
import SwiftUI
@MainActor
@Observable class EditAccountViewModel {
@ -33,12 +33,13 @@ import StatusKit
var isPhotoPickerPresented: Bool = false {
didSet {
if !isPhotoPickerPresented && mediaPickers.isEmpty {
if !isPhotoPickerPresented, mediaPickers.isEmpty {
isChangingAvatar = false
isChangingHeader = false
}
}
}
var isChangingAvatar: Bool = false
var isChangingHeader: Bool = false

View file

@ -4,8 +4,8 @@ import Foundation
import Models
import Network
import Observation
import SwiftUI
import OSLog
import SwiftUI
@MainActor
@Observable public class FollowButtonViewModel {

View file

@ -1,7 +1,7 @@
import DesignSystem
import Env
import Models
import SwiftUI
import Env
public struct ListsListView: View {
@Environment(CurrentAccount.self) private var currentAccount
@ -43,4 +43,3 @@ public struct ListsListView: View {
.navigationBarTitleDisplayMode(.inline)
}
}

View file

@ -1,9 +1,9 @@
import StatusKit
import Network
import SwiftUI
import DesignSystem
import Env
import Models
import DesignSystem
import Network
import StatusKit
import SwiftUI
@MainActor
public struct AccountStatusesListView: View {

View file

@ -1,8 +1,8 @@
import SwiftUI
import Models
import StatusKit
import Network
import Env
import Models
import Network
import StatusKit
import SwiftUI
@MainActor
@Observable
@ -40,7 +40,7 @@ public class AccountStatusesListViewModel: StatusesFetcher {
self.mode = mode
}
public func fetchNewestStatuses(pullToRefresh: Bool) async {
public func fetchNewestStatuses(pullToRefresh _: Bool) async {
guard let client else { return }
statusesState = .loading
do {
@ -63,11 +63,7 @@ public class AccountStatusesListViewModel: StatusesFetcher {
nextPageState: nextPage?.maxId != nil ? .hasNextPage : .none)
}
public func statusDidAppear(status: Status) {
public func statusDidAppear(status _: Status) {}
}
public func statusDidDisappear(status: Status) {
}
public func statusDidDisappear(status _: Status) {}
}

View file

@ -1,7 +1,7 @@
import DesignSystem
import Env
import Models
import SwiftUI
import Env
public struct FollowedTagsListView: View {
@Environment(CurrentAccount.self) private var currentAccount
@ -32,4 +32,3 @@ public struct FollowedTagsListView: View {
.navigationBarTitleDisplayMode(.inline)
}
}

View file

@ -205,12 +205,12 @@ struct ConversationMessageView: View {
.frame(height: 200)
.contentShape(Rectangle())
.onTapGesture {
#if targetEnvironment(macCatalyst) || os(visionOS)
#if targetEnvironment(macCatalyst) || os(visionOS)
openWindow(value: WindowDestinationMedia.mediaViewer(attachments: [attachement],
selectedAttachment: attachement))
#else
#else
quickLook.prepareFor(selectedMediaAttachment: attachement, mediaAttachments: [attachement])
#endif
#endif
}
}

View file

@ -201,4 +201,3 @@ public struct ThreadsLight: ColorSet {
public init() {}
}

View file

@ -70,7 +70,6 @@ public class SceneDelegate: NSObject, UIWindowSceneDelegate, Sendable {
delegate.windowHeight = newHeight
}
#endif
}
}
}

View file

@ -197,7 +197,7 @@ import SwiftUI
// better against the tintColor
private func computeContrastingTintColor() {
func luminance(_ color: Color.Resolved) -> Float {
return 0.299 * color.red + 0.587 * color.green + 0.114 * color.blue;
return 0.299 * color.red + 0.587 * color.green + 0.114 * color.blue
}
let resolvedTintColor = tintColor.resolve(in: .init())
@ -340,7 +340,7 @@ import SwiftUI
ConstellationLight(),
ConstellationDark(),
ThreadsLight(),
ThreadsDark()
ThreadsDark(),
]
}

View file

@ -77,16 +77,14 @@ struct ThemeApplier: ViewModifier {
}
private func setWindowUserInterfaceStyle(_ userInterfaceStyle: UIUserInterfaceStyle) {
allWindows()
.forEach {
$0.overrideUserInterfaceStyle = userInterfaceStyle
for window in allWindows() {
window.overrideUserInterfaceStyle = userInterfaceStyle
}
}
private func setWindowTint(_ color: Color) {
allWindows()
.forEach {
$0.tintColor = UIColor(color)
for window in allWindows() {
window.tintColor = UIColor(color)
}
}

View file

@ -3,7 +3,7 @@ import SwiftUI
public struct CancelToolbarItem: ToolbarContent {
@Environment(\.dismiss) private var dismiss
public init() { }
public init() {}
public var body: some ToolbarContent {
ToolbarItem(placement: .navigationBarLeading) {

View file

@ -4,7 +4,7 @@ public struct NextPageView: View {
@State private var isLoadingNextPage: Bool = false
@State private var showRetry: Bool = false
let loadNextPage: (() async throws -> Void)
let loadNextPage: () async throws -> Void
public init(loadNextPage: @escaping (() async throws -> Void)) {
self.loadNextPage = loadNextPage

View file

@ -148,7 +148,8 @@ public enum SheetDestination: Identifiable {
return .handled
} else if url.lastPathComponent.first == "@",
let host = url.host,
!host.hasPrefix("www") {
!host.hasPrefix("www")
{
let acct = "\(url.lastPathComponent)@\(host)"
Task {
await navigateToAccountFrom(acct: acct, url: url)

View file

@ -65,7 +65,7 @@ import OSLog
connect()
}
watchedStreams = streams
streams.forEach { stream in
for stream in streams {
sendMessage(message: StreamMessage(type: "subscribe", stream: stream.rawValue))
}
}
@ -159,19 +159,19 @@ import OSLog
public func emmitDeleteEvent(for status: String) {
let event = StreamEventDelete(status: status)
self.events.append(event)
self.latestEvent = event
events.append(event)
latestEvent = event
}
public func emmitEditEvent(for status: Status) {
let event = StreamEventStatusUpdate(status: status)
self.events.append(event)
self.latestEvent = event
events.append(event)
latestEvent = event
}
public func emmitPostEvent(for status: Status) {
let event = StreamEventUpdate(status: status)
self.events.append(event)
self.latestEvent = event
events.append(event)
latestEvent = event
}
}

View file

@ -183,7 +183,6 @@ import SwiftUI
}
}
public var alwaysUseDeepl: Bool {
didSet {
storage.alwaysUseDeepl = alwaysUseDeepl
@ -415,7 +414,7 @@ import SwiftUI
}
public var totalNotificationsCount: Int {
notificationsCount.compactMap{ $0.value }.reduce(0, +)
notificationsCount.compactMap { $0.value }.reduce(0, +)
}
public func reloadNotificationsCount(tokens: [OauthToken]) {

View file

@ -1,7 +1,7 @@
@testable import Env
import XCTest
import SwiftUI
import Network
import SwiftUI
import XCTest
@MainActor
final class RouterTests: XCTestCase {

View file

@ -1,8 +1,8 @@
import DesignSystem
import Models
import Network
import StatusKit
import SwiftUI
import Network
public struct TrendingLinksListView: View {
@Environment(Theme.self) private var theme

View file

@ -1,5 +1,5 @@
import SwiftUI
import Models
import SwiftUI
enum DisplayType {
case image

View file

@ -1,9 +1,9 @@
import AVKit
import DesignSystem
import Env
import Models
import Observation
import SwiftUI
import Models
@MainActor
@Observable public class MediaUIAttachmentVideoViewModel {
@ -23,7 +23,7 @@ import Models
#if !os(visionOS)
player?.preventsDisplaySleepDuringVideoPlayback = false
#endif
if (autoPlay || forceAutoPlay) && !isCompact {
if autoPlay || forceAutoPlay, !isCompact {
player?.play()
isPlaying = true
} else {
@ -179,7 +179,8 @@ public struct MediaUIAttachmentVideoView: View {
!viewModel.forceAutoPlay,
!isFullScreen,
!viewModel.isPlaying,
!isCompact {
!isCompact
{
Button(action: {
viewModel.play()
}, label: {

View file

@ -1,8 +1,8 @@
import AVFoundation
import Models
import Nuke
import QuickLook
import SwiftUI
import AVFoundation
public struct MediaUIView: View, @unchecked Sendable {
private let data: [DisplayData]

View file

@ -1,6 +1,6 @@
import SwiftUI
import NukeUI
import Nuke
import NukeUI
import SwiftUI
struct QuickLookToolbarItem: ToolbarContent, @unchecked Sendable {
let itemUrl: URL

View file

@ -92,9 +92,9 @@ public final class Account: Codable, Identifiable, Hashable, Sendable, Equatable
self.discoverable = discoverable
if let displayName, !displayName.isEmpty {
self.cachedDisplayName = .init(stringValue: displayName)
cachedDisplayName = .init(stringValue: displayName)
} else {
self.cachedDisplayName = .init(stringValue: "@\(username)")
cachedDisplayName = .init(stringValue: "@\(username)")
}
}
@ -122,29 +122,29 @@ public final class Account: Codable, Identifiable, Hashable, Sendable, Equatable
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(String.self, forKey: .id)
self.username = try container.decode(String.self, forKey: .username)
self.displayName = try container.decodeIfPresent(String.self, forKey: .displayName)
self.avatar = try container.decode(URL.self, forKey: .avatar)
self.header = try container.decode(URL.self, forKey: .header)
self.acct = try container.decode(String.self, forKey: .acct)
self.note = try container.decode(HTMLString.self, forKey: .note)
self.createdAt = try container.decode(ServerDate.self, forKey: .createdAt)
self.followersCount = try container.decodeIfPresent(Int.self, forKey: .followersCount)
self.followingCount = try container.decodeIfPresent(Int.self, forKey: .followingCount)
self.statusesCount = try container.decodeIfPresent(Int.self, forKey: .statusesCount)
self.lastStatusAt = try container.decodeIfPresent(String.self, forKey: .lastStatusAt)
self.fields = try container.decode([Account.Field].self, forKey: .fields)
self.locked = try container.decode(Bool.self, forKey: .locked)
self.emojis = try container.decode([Emoji].self, forKey: .emojis)
self.url = try container.decodeIfPresent(URL.self, forKey: .url)
self.source = try container.decodeIfPresent(Account.Source.self, forKey: .source)
self.bot = try container.decode(Bool.self, forKey: .bot)
self.discoverable = try container.decodeIfPresent(Bool.self, forKey: .discoverable)
id = try container.decode(String.self, forKey: .id)
username = try container.decode(String.self, forKey: .username)
displayName = try container.decodeIfPresent(String.self, forKey: .displayName)
avatar = try container.decode(URL.self, forKey: .avatar)
header = try container.decode(URL.self, forKey: .header)
acct = try container.decode(String.self, forKey: .acct)
note = try container.decode(HTMLString.self, forKey: .note)
createdAt = try container.decode(ServerDate.self, forKey: .createdAt)
followersCount = try container.decodeIfPresent(Int.self, forKey: .followersCount)
followingCount = try container.decodeIfPresent(Int.self, forKey: .followingCount)
statusesCount = try container.decodeIfPresent(Int.self, forKey: .statusesCount)
lastStatusAt = try container.decodeIfPresent(String.self, forKey: .lastStatusAt)
fields = try container.decode([Account.Field].self, forKey: .fields)
locked = try container.decode(Bool.self, forKey: .locked)
emojis = try container.decode([Emoji].self, forKey: .emojis)
url = try container.decodeIfPresent(URL.self, forKey: .url)
source = try container.decodeIfPresent(Account.Source.self, forKey: .source)
bot = try container.decode(Bool.self, forKey: .bot)
discoverable = try container.decodeIfPresent(Bool.self, forKey: .discoverable)
if let displayName, !displayName.isEmpty {
self.cachedDisplayName = .init(stringValue: displayName)
cachedDisplayName = .init(stringValue: displayName)
} else {
self.cachedDisplayName = .init(stringValue: "@\(username)")
cachedDisplayName = .init(stringValue: "@\(username)")
}
}

View file

@ -22,7 +22,7 @@ public struct StreamEventUpdate: StreamEvent {
public struct StreamEventStatusUpdate: StreamEvent {
public let date = Date()
public var id: String { status.id + (status.editedAt?.asDate.description ?? "")}
public var id: String { status.id + (status.editedAt?.asDate.description ?? "") }
public let status: Status
public init(status: Status) {
self.status = status

View file

@ -3,8 +3,8 @@ import Foundation
import Models
import Observation
import os
import SwiftUI
import OSLog
import SwiftUI
@Observable public final class Client: Equatable, Identifiable, Hashable, @unchecked Sendable {
public static func == (lhs: Client, rhs: Client) -> Bool {
@ -286,7 +286,8 @@ import OSLog
method: String,
mimeType: String,
filename: String,
data: Data) throws -> URLRequest {
data: Data) throws -> URLRequest
{
let url = try makeURL(endpoint: endpoint, forceVersion: version)
var request = makeURLRequest(url: url, endpoint: endpoint, httpMethod: method)
let boundary = UUID().uuidString

View file

@ -34,8 +34,8 @@ public struct InstanceSocialClient: Sendable {
}
}
extension Array where Self.Element == InstanceSocial {
fileprivate func sorted(by keyword: String) -> Self {
private extension Array where Self.Element == InstanceSocial {
func sorted(by keyword: String) -> Self {
let keyword = keyword.trimmingCharacters(in: .whitespacesAndNewlines)
var newArray = self

View file

@ -164,7 +164,7 @@ import SwiftUI
Task {
do {
let _: Marker = try await client.post(endpoint: Markers.markNotifications(lastReadId: id))
} catch { }
} catch {}
}
}

View file

@ -37,5 +37,4 @@ extension StatusEditor {
}
}
}
}

View file

@ -1,7 +1,7 @@
import DesignSystem
import Env
#if !os(visionOS) && !DEBUG
import GiphyUISDK
import GiphyUISDK
#endif
import Models
import NukeUI
@ -159,7 +159,6 @@ extension StatusEditor {
.accessibilityLabel("accessibility.editor.button.attach-photo")
.disabled(viewModel.showPoll)
Button {
// all SEVM have the same visibility value
followUpSEVMs.append(ViewModel(mode: .new(visibility: focusedSEVM.visibility)))
@ -188,7 +187,6 @@ extension StatusEditor {
}
}
if preferences.isOpenAIEnabled {
AIMenu.disabled(!viewModel.canPost)
}
@ -268,7 +266,5 @@ extension StatusEditor {
}
}
}
}
}

View file

@ -1,12 +1,11 @@
import DesignSystem
import EmojiText
import Foundation
import SwiftUI
import Models
import SwiftData
import SwiftUI
extension StatusEditor {
@MainActor
struct AutoCompleteView: View {
@Environment(\.modelContext) var context
@ -22,7 +21,8 @@ extension StatusEditor {
var body: some View {
if !viewModel.mentionsSuggestions.isEmpty ||
!viewModel.tagsSuggestions.isEmpty ||
(viewModel.showRecentsTagsInline && !recentTags.isEmpty) {
(viewModel.showRecentsTagsInline && !recentTags.isEmpty)
{
VStack {
HStack {
ScrollView(.horizontal, showsIndicators: false) {

View file

@ -1,10 +1,10 @@
import DesignSystem
import EmojiText
import Env
import Foundation
import SwiftUI
import Models
import SwiftData
import Env
import SwiftUI
extension StatusEditor.AutoCompleteView {
@MainActor

View file

@ -1,10 +1,9 @@
import DesignSystem
import EmojiText
import Foundation
import SwiftUI
import Models
import SwiftData
import SwiftUI
extension StatusEditor.AutoCompleteView {
struct MentionsView: View {

View file

@ -1,10 +1,9 @@
import DesignSystem
import EmojiText
import Foundation
import SwiftUI
import Models
import SwiftData
import SwiftUI
extension StatusEditor.AutoCompleteView {
struct RecentTagsView: View {

View file

@ -1,10 +1,9 @@
import DesignSystem
import EmojiText
import Foundation
import SwiftUI
import Models
import SwiftData
import SwiftUI
extension StatusEditor.AutoCompleteView {
struct RemoteTagsView: View {

View file

@ -35,5 +35,4 @@ extension StatusEditor {
Coordinator(picker: self)
}
}
}

View file

@ -7,5 +7,4 @@ extension StatusEditor {
let categoryName: String
var emojis: [Emoji]
}
}

View file

@ -2,9 +2,9 @@ import AVFoundation
import Foundation
import UIKit
extension StatusEditor {
public actor Compressor {
public init() { }
public extension StatusEditor {
actor Compressor {
public init() {}
enum CompressorError: Error {
case noData
@ -106,5 +106,4 @@ extension StatusEditor {
}
}
}
}

View file

@ -1,11 +1,10 @@
import DesignSystem
import Env
import SwiftUI
import Models
import NukeUI
import SwiftUI
extension StatusEditor {
@MainActor
struct CustomEmojisView: View {
@Environment(\.dismiss) private var dismiss

View file

@ -1,10 +1,10 @@
#if !os(visionOS) && !DEBUG
import DesignSystem
import GiphyUISDK
import SwiftUI
import UIKit
import DesignSystem
import GiphyUISDK
import SwiftUI
import UIKit
struct GifPickerView: UIViewControllerRepresentable {
struct GifPickerView: UIViewControllerRepresentable {
@Environment(Theme.self) private var theme
var completion: (String) -> Void
@ -49,5 +49,5 @@ struct GifPickerView: UIViewControllerRepresentable {
parent.completion(url ?? "")
}
}
}
}
#endif

View file

@ -1,10 +1,9 @@
import DesignSystem
import Env
import SwiftUI
import Models
import SwiftUI
extension StatusEditor {
@MainActor
struct LangButton: View {
@Environment(Theme.self) private var theme

View file

@ -13,5 +13,4 @@ extension StatusEditor {
let mediaAttachment: MediaAttachment?
let error: Error?
}
}

View file

@ -171,5 +171,4 @@ extension StatusEditor {
return translation?.content.asRawText
}
}
}

View file

@ -249,5 +249,4 @@ extension StatusEditor {
.cornerRadius(8)
}
}
}

View file

@ -121,5 +121,4 @@ extension StatusEditor {
return index == count - 1 && count < maxEntries
}
}
}

View file

@ -86,10 +86,10 @@ extension StatusEditor {
static var transferRepresentation: some TransferRepresentation {
FileRepresentation(importedContentType: .movie) { receivedTransferrable in
return MovieFileTranseferable(url: receivedTransferrable.localURL)
MovieFileTranseferable(url: receivedTransferrable.localURL)
}
FileRepresentation(importedContentType: .video) { receivedTransferrable in
return MovieFileTranseferable(url: receivedTransferrable.localURL)
MovieFileTranseferable(url: receivedTransferrable.localURL)
}
}
}
@ -112,7 +112,7 @@ extension StatusEditor {
static var transferRepresentation: some TransferRepresentation {
FileRepresentation(importedContentType: .gif) { receivedTransferrable in
return GifFileTranseferable(url: receivedTransferrable.localURL)
GifFileTranseferable(url: receivedTransferrable.localURL)
}
}
}
@ -133,7 +133,7 @@ public extension StatusEditor {
public static var transferRepresentation: some TransferRepresentation {
FileRepresentation(importedContentType: .image) { receivedTransferrable in
return ImageFileTranseferable(url: receivedTransferrable.localURL)
ImageFileTranseferable(url: receivedTransferrable.localURL)
}
}
}
@ -141,15 +141,15 @@ public extension StatusEditor {
public extension ReceivedTransferredFile {
var localURL: URL {
if self.isOriginalFile {
if isOriginalFile {
return file
}
let copy = URL.temporaryDirectory.appending(path: "\(UUID().uuidString).\(self.file.pathExtension)")
try? FileManager.default.copyItem(at: self.file, to: copy)
let copy = URL.temporaryDirectory.appending(path: "\(UUID().uuidString).\(file.pathExtension)")
try? FileManager.default.copyItem(at: file, to: copy)
return copy
}
}
public extension URL {
func mimeType() -> String {
if let mimeType = UTType(filenameExtension: pathExtension)?.preferredMIMEType {

View file

@ -49,5 +49,4 @@ extension StatusEditor {
}
}
}
}

View file

@ -4,5 +4,4 @@ extension StatusEditor {
enum EditorFocusState: Hashable {
case main, followUp(index: UUID)
}
}

View file

@ -159,7 +159,6 @@ extension StatusEditor {
}
}
@ViewBuilder
private var characterCountAndLangView: some View {
let value = (currentInstance.instance?.configuration?.statuses.maxCharacters ?? 500) + viewModel.statusTextCharacterLength
@ -223,5 +222,4 @@ extension StatusEditor {
Task { await viewModel.fetchCustomEmojis() }
}
}
}

View file

@ -10,9 +10,9 @@ import StoreKit
import SwiftUI
import UIKit
extension StatusEditor {
public extension StatusEditor {
@MainActor
public struct MainView: View {
struct MainView: View {
@Environment(AppAccountsManager.self) private var appAccounts
@Environment(CurrentAccount.self) private var currentAccount
@Environment(Theme.self) private var theme
@ -151,5 +151,4 @@ extension StatusEditor {
.presentationBackgroundInteraction(.enabled)
}
}
}

View file

@ -1 +1 @@
public enum StatusEditor { }
public enum StatusEditor {}

View file

@ -30,5 +30,4 @@ extension StatusEditor {
}
}
}
}

View file

@ -1,8 +1,8 @@
import DesignSystem
import Env
import Models
import StoreKit
import SwiftUI
import DesignSystem
extension StatusEditor {
@MainActor

View file

@ -7,10 +7,9 @@ import Network
import PhotosUI
import SwiftUI
extension StatusEditor {
public extension StatusEditor {
@MainActor
@Observable public class ViewModel: NSObject, Identifiable {
@Observable class ViewModel: NSObject, Identifiable {
public let id = UUID()
var mode: Mode
@ -132,15 +131,16 @@ extension StatusEditor {
}
var allMediaHasDescription: Bool {
var everyMediaHasAltText: Bool = true;
mediaContainers.forEach { mediaContainer in
if (((mediaContainer.mediaAttachment?.description) == nil) ||
mediaContainer.mediaAttachment?.description?.count == 0) {
var everyMediaHasAltText = true
for mediaContainer in mediaContainers {
if ((mediaContainer.mediaAttachment?.description) == nil) ||
mediaContainer.mediaAttachment?.description?.count == 0
{
everyMediaHasAltText = false
}
}
return everyMediaHasAltText;
return everyMediaHasAltText
}
var shouldDisplayDismissWarning: Bool {
@ -200,12 +200,12 @@ extension StatusEditor {
func postStatus() async -> Status? {
guard let client else { return nil }
do {
if (!allMediaHasDescription && UserPreferences.shared.appRequireAltText) {
if !allMediaHasDescription && UserPreferences.shared.appRequireAltText {
throw PostError.missingAltText
}
if postingTimer == nil {
Timer.scheduledTimer(withTimeInterval: 0.05, repeats: true) { timer in
Timer.scheduledTimer(withTimeInterval: 0.05, repeats: true) { _ in
Task { @MainActor in
if self.postingProgress < 100 {
self.postingProgress += 0.5
@ -616,7 +616,8 @@ extension StatusEditor {
if !tagsSuggestions.isEmpty ||
!mentionsSuggestions.isEmpty ||
currentSuggestionRange != nil ||
showRecentsTagsInline {
showRecentsTagsInline
{
withAnimation {
tagsSuggestions = []
mentionsSuggestions = []
@ -838,7 +839,7 @@ extension StatusEditor {
error: nil
)
}
} catch { }
} catch {}
}
try? await Task.sleep(for: .seconds(5))
} while !Task.isCancelled
@ -859,7 +860,7 @@ extension StatusEditor {
mediaAttachment: media,
error: nil
)
} catch { }
} catch {}
}
}

View file

@ -56,7 +56,7 @@ public struct StatusEditHistoryView: View {
.task {
do {
history = try await client.get(endpoint: Statuses.history(id: statusId))
} catch { }
} catch {}
}
.listStyle(.plain)
.scrollContentBackground(.hidden)

View file

@ -8,7 +8,7 @@ private func stripToPureLanguage(inText: String) -> String {
var resultStr = inText
[hashtagRegex, emojiRegex, atRegex].forEach { regex in
for regex in [hashtagRegex, emojiRegex, atRegex] {
let splitArray = resultStr.split(separator: regex, omittingEmptySubsequences: true)
resultStr = splitArray.joined() as String
}

View file

@ -78,7 +78,7 @@ public struct StatusPollView: View {
// Make sure they're all the same width using a ZStack with 100% hiding behind the
// real percentage.
Text("100%").hidden().overlay(alignment: .trailing) {
Text("\(absolutePercent(for:option.votesCount ?? 0))%")
Text("\(absolutePercent(for: option.votesCount ?? 0))%")
.font(.scaledSubheadline)
}
}
@ -121,7 +121,7 @@ public struct StatusPollView: View {
return Text("accessibility.status.poll.option-prefix-\(index + 1)-of-\(viewModel.poll.options.count)") +
Text(", ") +
Text(option.title) +
Text(showPercentage ? ", \(absolutePercent(for:option.votesCount ?? 0))%" : "")
Text(showPercentage ? ", \(absolutePercent(for: option.votesCount ?? 0))%" : "")
}
private var footerView: some View {
@ -188,19 +188,20 @@ public struct StatusPollView: View {
private struct _PercentWidthLayout: Layout {
let percent: CGFloat
func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache _: inout ()) -> CGSize {
guard let view = subviews.first else { return CGSize.zero }
return view.sizeThatFits(proposal)
}
func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache _: inout ()) {
guard let view = subviews.first,
let width = proposal.width
else { return }
view.place(
at: bounds.origin,
proposal: ProposedViewSize(width: percent / 100 * width, height: proposal.height))
proposal: ProposedViewSize(width: percent / 100 * width, height: proposal.height)
)
}
}
}

View file

@ -35,7 +35,7 @@ import SwiftUI
votes = poll.ownVotes ?? []
showResults = true
}
} catch { }
} catch {}
}
public func handleSelection(_ pollIndex: Int) {

View file

@ -1,5 +1,5 @@
import SwiftUI
import Models
import SwiftUI
/// A utility that creates a suitable combined accessibility label for a `StatusRowView` that is not focused.
@MainActor

View file

@ -28,7 +28,7 @@ public struct StatusRowView: View {
private let context: Context
public init(viewModel: StatusRowViewModel, context: Context = .timeline) {
self._viewModel = .init(initialValue: viewModel)
_viewModel = .init(initialValue: viewModel)
self.context = context
}
@ -100,7 +100,8 @@ public struct StatusRowView: View {
}
if !reasons.contains(.placeholder),
viewModel.showActions, isFocused || theme.statusActionsDisplay != .none,
!isInCaptureMode {
!isInCaptureMode
{
StatusRowActionsView(isBlockConfirmationPresented: $isBlockConfirmationPresented,
viewModel: viewModel)
.tint(isFocused ? theme.tintColor : .gray)
@ -196,13 +197,14 @@ public struct StatusRowView: View {
)
})
.confirmationDialog("",
isPresented: $isBlockConfirmationPresented) {
isPresented: $isBlockConfirmationPresented)
{
Button("account.action.block", role: .destructive) {
Task {
do {
let operationAccount = viewModel.status.reblog?.account ?? viewModel.status.account
viewModel.authorRelationship = try await client.post(endpoint: Accounts.block(id: operationAccount.id))
} catch { }
} catch {}
}
}
}
@ -358,4 +360,3 @@ public struct StatusRowView: View {
.environment(PushNotificationsService.shared)
.environment(QuickLook.shared)
}

View file

@ -1,9 +1,9 @@
import DesignSystem
import Env
import Models
import Nuke
import NukeUI
import SwiftUI
import Env
@MainActor
public struct StatusRowCardView: View {
@ -53,10 +53,11 @@ public struct StatusRowCardView: View {
let sitesWithIcons = ["apps.apple.com", "music.apple.com", "podcasts.apple.com", "open.spotify.com"]
if isCompact {
compactLinkPreview(title, url)
} else if (UIDevice.current.userInterfaceIdiom == .pad ||
} else if UIDevice.current.userInterfaceIdiom == .pad ||
UIDevice.current.userInterfaceIdiom == .mac ||
UIDevice.current.userInterfaceIdiom == .vision),
let host = url.host(), sitesWithIcons.contains(host) {
UIDevice.current.userInterfaceIdiom == .vision,
let host = url.host(), sitesWithIcons.contains(host)
{
iconLinkPreview(title, url)
} else {
defaultLinkPreview(title, url)
@ -156,7 +157,7 @@ public struct StatusRowCardView: View {
.foregroundColor(theme.tintColor)
.lineLimit(1)
if let history = card.history {
let uses = history.compactMap{ Int($0.accounts )}.reduce(0, +)
let uses = history.compactMap { Int($0.accounts) }.reduce(0, +)
HStack(spacing: 4) {
Image(systemName: "bubble.left.and.text.bubble.right")
Text("trending-tag-people-talking \(uses)")
@ -250,12 +251,12 @@ struct DefaultPreviewImage: View {
let originalWidth: CGFloat
let originalHeight: CGFloat
func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache _: inout ()) -> CGSize {
guard !subviews.isEmpty else { return CGSize.zero }
return calculateSize(proposal)
}
func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache _: inout ()) {
guard let view = subviews.first else { return }
let size = calculateSize(proposal)

View file

@ -204,7 +204,7 @@ struct StatusRowContextMenu: View {
do {
let operationAccount = viewModel.status.reblog?.account ?? viewModel.status.account
viewModel.authorRelationship = try await client.post(endpoint: Accounts.unmute(id: operationAccount.id))
} catch { }
} catch {}
}
} label: {
Label("account.action.unmute", systemImage: "speaker")
@ -217,7 +217,7 @@ struct StatusRowContextMenu: View {
do {
let operationAccount = viewModel.status.reblog?.account ?? viewModel.status.account
viewModel.authorRelationship = try await client.post(endpoint: Accounts.mute(id: operationAccount.id, json: MuteData(duration: duration.rawValue)))
} catch { }
} catch {}
}
}
}
@ -271,7 +271,7 @@ struct StatusRowContextMenu: View {
do {
let operationAccount = viewModel.status.reblog?.account ?? viewModel.status.account
viewModel.authorRelationship = try await client.post(endpoint: Accounts.unblock(id: operationAccount.id))
} catch { }
} catch {}
}
} label: {
Label("account.action.unblock", systemImage: "person.crop.circle.badge.exclamationmark")

View file

@ -1,8 +1,8 @@
import DesignSystem
import Env
import Models
import SwiftUI
import Network
import SwiftUI
@MainActor
struct StatusRowHeaderView: View {
@ -66,7 +66,8 @@ struct StatusRowHeaderView: View {
}
if !redactionReasons.contains(.placeholder) {
if (theme.displayFullUsername && theme.avatarPosition == .leading) ||
theme.avatarPosition == .top {
theme.avatarPosition == .top
{
Text("@\(theme.displayFullUsername ? viewModel.finalStatus.account.acct : viewModel.finalStatus.account.username)")
.font(.scaledFootnote)
.foregroundStyle(.secondary)

View file

@ -23,13 +23,13 @@ public struct StatusRowMediaPreviewView: View {
self.sensitive = sensitive
}
#if targetEnvironment(macCatalyst)
#if targetEnvironment(macCatalyst)
private var showsScrollIndicators: Bool { attachments.count > 1 }
private var scrollBottomPadding: CGFloat?
#else
#else
private var showsScrollIndicators: Bool = false
private var scrollBottomPadding: CGFloat? = 0
#endif
#endif
private var imageMaxHeight: CGFloat {
if isCompact {
@ -468,7 +468,7 @@ private struct FeaturedImagePreView: View {
self.maxSize = maxSize
}
func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache _: inout ()) -> CGSize {
guard !subviews.isEmpty else { return CGSize.zero }
if let maxSize { return maxSize }
@ -476,7 +476,7 @@ private struct FeaturedImagePreView: View {
return calculateSize(proposal)
}
func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache _: inout ()) {
guard let view = subviews.first else { return }
let size = if let maxSize { maxSize } else { calculateSize(proposal) }

View file

@ -14,7 +14,8 @@ struct StatusRowTagView: View {
if isHomeTimeline,
let tag = viewModel.finalStatus.content.links.first(where: { link in
link.type == .hashtag && currentAccount.tags.contains(where: { $0.name.lowercased() == link.title.lowercased() })
}) {
})
{
Text("#\(tag.title)")
.font(.scaledFootnote)
.foregroundStyle(.secondary)

View file

@ -30,7 +30,6 @@ public enum RemoteTimelineFilter: String, CaseIterable, Hashable, Equatable {
}
public enum TimelineFilter: Hashable, Equatable, Identifiable {
case home, local, federated, trending
case hashtag(tag: String, accountId: String?)
case tagGroup(title: String, tags: [String], symbolName: String?)
@ -50,7 +49,6 @@ public enum TimelineFilter: Hashable, Equatable, Identifiable {
default:
return title
}
}
public func hash(into hasher: inout Hasher) {

View file

@ -1,9 +1,9 @@
import DesignSystem
import Env
import Foundation
import Models
import Observation
import SwiftUI
import DesignSystem
@MainActor
@Observable class TimelineUnreadStatusesObserver {

View file

@ -1,5 +1,5 @@
import SwiftUI
import DesignSystem
import SwiftUI
public struct TimelineContentFilterView: View {
@Environment(Theme.self) private var theme

View file

@ -1,5 +1,5 @@
import SwiftUI
import DesignSystem
import SwiftUI
struct TimelineHeaderView<Content: View>: View {
@Environment(Theme.self) private var theme

View file

@ -1,8 +1,8 @@
import SwiftUI
import DesignSystem
import Env
import Models
import DesignSystem
import Network
import SwiftUI
@MainActor
struct TimelineQuickAccessPills: View {
@ -25,14 +25,15 @@ struct TimelineQuickAccessPills: View {
}
.scrollClipDisabled()
.scrollIndicators(.never)
.onChange(of: currentAccount.lists, { _, lists in
.onChange(of: currentAccount.lists) { _, lists in
guard client.isAuth else { return }
var filters = pinnedFilters
for (index, filter) in filters.enumerated() {
switch filter {
case .list(let list):
case let .list(list):
if let accountList = lists.first(where: { $0.id == list.id }),
accountList.title != list.title {
accountList.title != list.title
{
filters[index] = .list(list: accountList)
}
default:
@ -40,7 +41,7 @@ struct TimelineQuickAccessPills: View {
}
}
pinnedFilters = filters
})
}
}
@ViewBuilder
@ -87,7 +88,7 @@ struct TimelineQuickAccessPills: View {
private func isFilterSupported(_ filter: TimelineFilter) -> Bool {
switch filter {
case .list(let list):
case let .list(list):
return currentAccount.lists.contains(where: { $0.id == list.id })
default:
return true
@ -100,23 +101,23 @@ struct PillDropDelegate: DropDelegate {
@Binding var items: [TimelineFilter]
@Binding var draggedItem: TimelineFilter?
func dropUpdated(info: DropInfo) -> DropProposal? {
func dropUpdated(info _: DropInfo) -> DropProposal? {
return DropProposal(operation: .move)
}
func performDrop(info: DropInfo) -> Bool {
func performDrop(info _: DropInfo) -> Bool {
draggedItem = nil
return true
}
func dropEntered(info: DropInfo) {
func dropEntered(info _: DropInfo) {
if let draggedItem {
let fromIndex = items.firstIndex(of: draggedItem)
if let fromIndex {
let toIndex = items.firstIndex(of: destinationItem)
if let toIndex, fromIndex != toIndex {
withAnimation {
self.items.move(fromOffsets: IndexSet(integer: fromIndex), toOffset: (toIndex > fromIndex ? (toIndex + 1) : toIndex))
self.items.move(fromOffsets: IndexSet(integer: fromIndex), toOffset: toIndex > fromIndex ? (toIndex + 1) : toIndex)
}
}
}

View file

@ -1,6 +1,6 @@
import SwiftUI
import Models
import Env
import Models
import SwiftUI
struct TimelineTagGroupheaderView: View {
@Environment(RouterPath.self) private var routerPath

View file

@ -1,7 +1,7 @@
import SwiftUI
import Models
import DesignSystem
import Env
import Models
import SwiftUI
struct TimelineTagHeaderView: View {
@Environment(CurrentAccount.self) private var account

View file

@ -147,19 +147,20 @@ public struct TimelineView: View {
}
}
}
.onChange(of: account.lists, { _, lists in
.onChange(of: account.lists) { _, lists in
guard client.isAuth else { return }
switch timeline {
case let .list(list):
if let accountList = lists.first(where: { $0.id == list.id }),
list.id == accountList.id,
accountList.title != list.title {
accountList.title != list.title
{
timeline = .list(list: accountList)
}
default:
break
}
})
}
.onChange(of: timeline) { oldValue, newValue in
switch newValue {
case let .remoteLocal(server, _):

View file

@ -11,7 +11,7 @@ import SwiftUI
var statusesState: StatusesState = .loading
var timeline: TimelineFilter = .federated {
willSet {
if timeline == .home && newValue != .resume {
if timeline == .home, newValue != .resume {
saveMarker()
}
}
@ -355,7 +355,7 @@ extension TimelineViewModel: StatusesFetcher {
// High chance the user is scrolled to the top.
// We need to update the statuses state, and then scroll to the previous top most status.
if let topStatus, visibileStatuses.contains(where: { $0.id == topStatus.id}), scrollToTopVisible {
if let topStatus, visibileStatuses.contains(where: { $0.id == topStatus.id }), scrollToTopVisible {
pendingStatusesObserver.disableUpdate = true
let statuses = await datasource.getFiltered()
statusesState = .display(statuses: statuses,
@ -425,7 +425,6 @@ extension TimelineViewModel: StatusesFetcher {
minId: nil,
offset: statuses.count))
await datasource.append(contentOf: newStatuses)
StatusDataControllerProvider.shared.updateDataControllers(for: newStatuses, client: client)
@ -450,6 +449,7 @@ extension TimelineViewModel: StatusesFetcher {
}
// MARK: - MARKER
extension TimelineViewModel {
func fetchMarker() async -> Marker.Content? {
guard let client else {
@ -469,7 +469,7 @@ extension TimelineViewModel {
guard let id = await cache.getLatestSeenStatus(for: client, filter: timeline.id)?.first else { return }
do {
let _: Marker = try await client.post(endpoint: Markers.markHome(lastReadId: id))
} catch { }
} catch {}
}
}
}

View file

@ -73,9 +73,9 @@ public actor TimelineCache {
func setLatestSeenStatuses(_ statuses: [Status], for client: Client, filter: String) {
let statuses = statuses.sorted(by: { $0.createdAt.asDate > $1.createdAt.asDate })
if filter == "Home" {
UserDefaults.standard.set(statuses.map{ $0.id }, forKey: "timeline-last-seen-\(client.id)")
UserDefaults.standard.set(statuses.map { $0.id }, forKey: "timeline-last-seen-\(client.id)")
} else {
UserDefaults.standard.set(statuses.map{ $0.id }, forKey: "timeline-last-seen-\(client.id)-\(filter)")
UserDefaults.standard.set(statuses.map { $0.id }, forKey: "timeline-last-seen-\(client.id)-\(filter)")
}
}

View file

@ -1,6 +1,6 @@
import Env
import Foundation
import Models
import Env
actor TimelineDatasource {
private var statuses: [Status] = []
@ -24,7 +24,8 @@ actor TimelineDatasource {
!showReplies && status.inReplyToId != nil && status.inReplyToAccountId != status.account.id ||
!showBoosts && status.reblog != nil ||
!showThreads && status.inReplyToAccountId == status.account.id ||
!showQuotePosts && !status.content.statusesURLs.isEmpty {
!showQuotePosts && !status.content.statusesURLs.isEmpty
{
return false
}
return true

View file

@ -1,8 +1,7 @@
import Models
import Network
@testable import Timeline
import XCTest
import Network
import Models
final class TimelineFilterTests: XCTestCase {
func testCodableHome() throws {

View file

@ -1,7 +1,7 @@
import Models
import Network
@testable import Timeline
import XCTest
import Network
import Models
@MainActor
final class TimelineViewModelTests: XCTestCase {