mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-12-22 21:08:15 +01:00
Swiftformat
This commit is contained in:
parent
94d50fafc4
commit
b259b6739e
@ -3,12 +3,12 @@ import AppAccount
|
||||
import Conversations
|
||||
import DesignSystem
|
||||
import Env
|
||||
import LinkPresentation
|
||||
import Lists
|
||||
import Models
|
||||
import Status
|
||||
import SwiftUI
|
||||
import Timeline
|
||||
import LinkPresentation
|
||||
import Models
|
||||
|
||||
@MainActor
|
||||
extension View {
|
||||
@ -46,7 +46,7 @@ extension View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func withSheetDestinations(sheetDestinations: Binding<SheetDestinations?>) -> some View {
|
||||
sheet(item: sheetDestinations) { destination in
|
||||
switch destination {
|
||||
@ -99,7 +99,7 @@ extension View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func withEnvironments() -> some View {
|
||||
environmentObject(CurrentAccount.shared)
|
||||
.environmentObject(UserPreferences.shared)
|
||||
@ -114,38 +114,39 @@ extension View {
|
||||
struct ActivityView: UIViewControllerRepresentable {
|
||||
let image: UIImage
|
||||
let status: Status
|
||||
|
||||
|
||||
class LinkDelegate: NSObject, UIActivityItemSource {
|
||||
let image: UIImage
|
||||
let status: Status
|
||||
|
||||
|
||||
init(image: UIImage, status: Status) {
|
||||
self.image = image
|
||||
self.status = status
|
||||
}
|
||||
|
||||
func activityViewControllerLinkMetadata(_ activityViewController: UIActivityViewController) -> LPLinkMetadata? {
|
||||
|
||||
func activityViewControllerLinkMetadata(_: UIActivityViewController) -> LPLinkMetadata? {
|
||||
let imageProvider = NSItemProvider(object: image)
|
||||
let metadata = LPLinkMetadata()
|
||||
metadata.imageProvider = imageProvider
|
||||
metadata.title = status.reblog?.content.asRawText ?? status.content.asRawText
|
||||
return metadata
|
||||
}
|
||||
|
||||
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
|
||||
|
||||
func activityViewControllerPlaceholderItem(_: UIActivityViewController) -> Any {
|
||||
image
|
||||
}
|
||||
|
||||
func activityViewController(_ activityViewController: UIActivityViewController,
|
||||
itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
|
||||
|
||||
func activityViewController(_: UIActivityViewController,
|
||||
itemForActivityType _: UIActivity.ActivityType?) -> Any?
|
||||
{
|
||||
nil
|
||||
}
|
||||
}
|
||||
|
||||
func makeUIViewController(context: UIViewControllerRepresentableContext<ActivityView>) -> UIActivityViewController {
|
||||
|
||||
func makeUIViewController(context _: UIViewControllerRepresentableContext<ActivityView>) -> UIActivityViewController {
|
||||
return UIActivityViewController(activityItems: [image, LinkDelegate(image: image, status: status)],
|
||||
applicationActivities: nil)
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: UIActivityViewController, context: UIViewControllerRepresentableContext<ActivityView>) {}
|
||||
|
||||
func updateUIViewController(_: UIActivityViewController, context _: UIViewControllerRepresentableContext<ActivityView>) {}
|
||||
}
|
||||
|
@ -100,7 +100,8 @@ struct IceCubesApp: App {
|
||||
|
||||
private func badgeFor(tab: Tab) -> Int {
|
||||
if tab == .notifications && selectedTab != tab,
|
||||
let token = appAccountsManager.currentAccount.oauthToken {
|
||||
let token = appAccountsManager.currentAccount.oauthToken
|
||||
{
|
||||
return watcher.unreadNotificationsCount + userPreferences.getNotificationsCount(for: token)
|
||||
}
|
||||
return 0
|
||||
@ -169,7 +170,8 @@ struct IceCubesApp: App {
|
||||
}
|
||||
selectedTab = newTab
|
||||
if selectedTab == .notifications,
|
||||
let token = appAccountsManager.currentAccount.oauthToken {
|
||||
let token = appAccountsManager.currentAccount.oauthToken
|
||||
{
|
||||
userPreferences.setNotification(count: 0, token: token)
|
||||
watcher.unreadNotificationsCount = 0
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ private struct SafariRouter: ViewModifier {
|
||||
@EnvironmentObject private var routerPath: RouterPath
|
||||
|
||||
@StateObject private var safariManager = InAppSafariManager()
|
||||
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.environment(\.openURL, OpenURLAction { url in
|
||||
@ -58,48 +58,48 @@ private struct SafariRouter: ViewModifier {
|
||||
|
||||
private class InAppSafariManager: NSObject, ObservableObject, SFSafariViewControllerDelegate {
|
||||
var windowScene: UIWindowScene?
|
||||
let viewController: UIViewController = UIViewController()
|
||||
let viewController: UIViewController = .init()
|
||||
var window: UIWindow?
|
||||
|
||||
@MainActor
|
||||
func open(_ url: URL) -> OpenURLAction.Result {
|
||||
guard let windowScene = windowScene else { return .systemAction }
|
||||
|
||||
self.window = setupWindow(windowScene: windowScene)
|
||||
|
||||
|
||||
window = setupWindow(windowScene: windowScene)
|
||||
|
||||
let configuration = SFSafariViewController.Configuration()
|
||||
configuration.entersReaderIfAvailable = UserPreferences.shared.inAppBrowserReaderView
|
||||
|
||||
|
||||
let safari = SFSafariViewController(url: url, configuration: configuration)
|
||||
safari.preferredBarTintColor = UIColor(Theme.shared.primaryBackgroundColor)
|
||||
safari.preferredControlTintColor = UIColor(Theme.shared.tintColor)
|
||||
safari.delegate = self
|
||||
|
||||
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.viewController.present(safari, animated: true)
|
||||
}
|
||||
|
||||
|
||||
return .handled
|
||||
}
|
||||
|
||||
func setupWindow(windowScene: UIWindowScene) -> UIWindow {
|
||||
let window = self.window ?? UIWindow(windowScene: windowScene)
|
||||
|
||||
|
||||
window.rootViewController = viewController
|
||||
window.makeKeyAndVisible()
|
||||
|
||||
|
||||
switch Theme.shared.selectedScheme {
|
||||
case .dark:
|
||||
window.overrideUserInterfaceStyle = .dark
|
||||
case .light:
|
||||
window.overrideUserInterfaceStyle = .light
|
||||
}
|
||||
|
||||
|
||||
self.window = window
|
||||
return window
|
||||
}
|
||||
|
||||
func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
|
||||
func safariViewControllerDidFinish(_: SFSafariViewController) {
|
||||
window?.resignKey()
|
||||
window?.isHidden = false
|
||||
window = nil
|
||||
@ -109,31 +109,30 @@ private class InAppSafariManager: NSObject, ObservableObject, SFSafariViewContro
|
||||
private struct WindowReader: UIViewRepresentable {
|
||||
var onUpdate: (UIWindow) -> Void
|
||||
|
||||
func makeUIView(context: Context) -> InjectView {
|
||||
func makeUIView(context _: Context) -> InjectView {
|
||||
InjectView(onUpdate: onUpdate)
|
||||
}
|
||||
|
||||
func updateUIView(_ uiView: InjectView, context: Context) {
|
||||
}
|
||||
func updateUIView(_: InjectView, context _: Context) {}
|
||||
|
||||
class InjectView: UIView {
|
||||
var onUpdate: (UIWindow) -> Void
|
||||
|
||||
|
||||
init(onUpdate: @escaping (UIWindow) -> Void) {
|
||||
self.onUpdate = onUpdate
|
||||
super.init(frame: .zero)
|
||||
isHidden = true
|
||||
isUserInteractionEnabled = false
|
||||
}
|
||||
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
|
||||
override func willMove(toWindow newWindow: UIWindow?) {
|
||||
super.willMove(toWindow: newWindow)
|
||||
|
||||
|
||||
if let window = newWindow {
|
||||
onUpdate(window)
|
||||
} else {
|
||||
|
@ -20,12 +20,13 @@ struct SideBarView<Content: View>: View {
|
||||
|
||||
private func badgeFor(tab: Tab) -> Int {
|
||||
if tab == .notifications && selectedTab != tab,
|
||||
let token = appAccounts.currentAccount.oauthToken {
|
||||
let token = appAccounts.currentAccount.oauthToken
|
||||
{
|
||||
return watcher.unreadNotificationsCount + userPreferences.getNotificationsCount(for: token)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
private func makeIconForTab(tab: Tab) -> some View {
|
||||
ZStack(alignment: .topTrailing) {
|
||||
SideBarIcon(systemIconName: tab.iconName,
|
||||
@ -37,7 +38,7 @@ struct SideBarView<Content: View>: View {
|
||||
.contentShape(Rectangle())
|
||||
.frame(width: .sidebarWidth, height: 50)
|
||||
}
|
||||
|
||||
|
||||
private func makeBadgeView(count: Int) -> some View {
|
||||
ZStack {
|
||||
Circle()
|
||||
@ -78,8 +79,9 @@ struct SideBarView<Content: View>: View {
|
||||
ZStack(alignment: .topTrailing) {
|
||||
AppAccountView(viewModel: .init(appAccount: account, isCompact: true))
|
||||
if showBadge,
|
||||
let token = account.oauthToken,
|
||||
userPreferences.getNotificationsCount(for: token) > 0 {
|
||||
let token = account.oauthToken,
|
||||
userPreferences.getNotificationsCount(for: token) > 0
|
||||
{
|
||||
makeBadgeView(count: userPreferences.getNotificationsCount(for: token))
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ struct DisplaySettingsView: View {
|
||||
theme.chosenFont = UIFont(name: "OpenDyslexic", size: 1)
|
||||
case .hyperLegible:
|
||||
theme.chosenFont = UIFont(name: "Atkinson Hyperlegible", size: 1)
|
||||
case.SFRounded:
|
||||
case .SFRounded:
|
||||
theme.chosenFont = UIFont.systemFont(ofSize: 1).rounded()
|
||||
case .custom:
|
||||
isFontSelectorPresented = true
|
||||
|
@ -64,7 +64,7 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
bestAttemptContent.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: "glass.caf"))
|
||||
|
||||
let preferences = UserPreferences.shared
|
||||
if let token = AppAccountsManager.shared.availableAccounts.first(where: { $0.oauthToken?.accessToken == notification.accessToken})?.oauthToken {
|
||||
if let token = AppAccountsManager.shared.availableAccounts.first(where: { $0.oauthToken?.accessToken == notification.accessToken })?.oauthToken {
|
||||
var currentCount = preferences.getNotificationsCount(for: token)
|
||||
currentCount += 1
|
||||
preferences.setNotification(count: currentCount, token: token)
|
||||
|
@ -150,9 +150,10 @@ struct AccountDetailHeaderView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if let note = viewModel.relationship?.note, !note.isEmpty,
|
||||
!viewModel.isCurrentUser {
|
||||
!viewModel.isCurrentUser
|
||||
{
|
||||
makeNoteView(note)
|
||||
}
|
||||
|
||||
@ -162,7 +163,7 @@ struct AccountDetailHeaderView: View {
|
||||
.environment(\.openURL, OpenURLAction { url in
|
||||
routerPath.handle(url: url)
|
||||
})
|
||||
|
||||
|
||||
fieldsView
|
||||
}
|
||||
.padding(.horizontal, .layoutPadding)
|
||||
@ -204,7 +205,7 @@ struct AccountDetailHeaderView: View {
|
||||
.padding(.top, 6)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ViewBuilder
|
||||
private func makeNoteView(_ note: String) -> some View {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
@ -221,7 +222,7 @@ struct AccountDetailHeaderView: View {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ViewBuilder
|
||||
private var fieldsView: some View {
|
||||
if !viewModel.fields.isEmpty {
|
||||
|
@ -485,8 +485,7 @@ public struct AccountDetailView: View {
|
||||
|
||||
private extension View {
|
||||
func applyAccountDetailsRowStyle(theme: Theme) -> some View {
|
||||
self
|
||||
.listRowInsets(.init())
|
||||
listRowInsets(.init())
|
||||
.listRowSeparator(.hidden)
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
}
|
||||
|
@ -6,12 +6,12 @@ public struct EditRelationshipNoteView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var client: Client
|
||||
|
||||
|
||||
// need this model to refresh after storing the new note on mastodon
|
||||
var accountDetailViewModel: AccountDetailViewModel
|
||||
|
||||
|
||||
@StateObject private var viewModel = EditRelationshipNoteViewModel()
|
||||
|
||||
|
||||
public var body: some View {
|
||||
NavigationStack {
|
||||
Form {
|
||||
@ -31,8 +31,8 @@ public struct EditRelationshipNoteView: View {
|
||||
.alert("account.relation.note.edit.error.save.title",
|
||||
isPresented: $viewModel.saveError,
|
||||
actions: {
|
||||
Button("alert.button.ok", action: {})
|
||||
}, message: { Text("account.relation.note.edit.error.save.message") })
|
||||
Button("alert.button.ok", action: {})
|
||||
}, message: { Text("account.relation.note.edit.error.save.message") })
|
||||
.task {
|
||||
viewModel.client = client
|
||||
viewModel.relatedAccountId = accountDetailViewModel.accountId
|
||||
@ -40,7 +40,7 @@ public struct EditRelationshipNoteView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ToolbarContentBuilder
|
||||
private var toolbarContent: some ToolbarContent {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
@ -48,7 +48,7 @@ public struct EditRelationshipNoteView: View {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button {
|
||||
Task {
|
||||
|
@ -6,15 +6,16 @@ class EditRelationshipNoteViewModel: ObservableObject {
|
||||
public var note: String = ""
|
||||
public var relatedAccountId: String?
|
||||
public var client: Client?
|
||||
|
||||
|
||||
@Published var isSaving: Bool = false
|
||||
@Published var saveError: Bool = false
|
||||
|
||||
|
||||
init() {}
|
||||
|
||||
|
||||
func save() async {
|
||||
if relatedAccountId != nil,
|
||||
client != nil {
|
||||
client != nil
|
||||
{
|
||||
isSaving = true
|
||||
do {
|
||||
let _ = try await client!.post(endpoint: Accounts.relationshipNote(id: relatedAccountId!, json: RelationshipNoteData(note: note)))
|
||||
|
@ -92,7 +92,8 @@ public struct AppAccountsSelectorView: View {
|
||||
Image(uiImage: image)
|
||||
}
|
||||
if let token = viewModel.appAccount.oauthToken,
|
||||
preferences.getNotificationsCount(for: token) > 0 {
|
||||
preferences.getNotificationsCount(for: token) > 0
|
||||
{
|
||||
Text("\(viewModel.account?.displayName ?? "") (\(preferences.getNotificationsCount(for: token)))")
|
||||
} else {
|
||||
Text("\(viewModel.account?.displayName ?? "")")
|
||||
|
@ -15,12 +15,10 @@ public extension Font {
|
||||
private static let onMac = ProcessInfo.processInfo.isiOSAppOnMac
|
||||
|
||||
private static func customFont(size: CGFloat, relativeTo textStyle: TextStyle) -> Font {
|
||||
|
||||
if let chosenFont = Theme.shared.chosenFont {
|
||||
if chosenFont.fontName == ".AppleSystemUIFontRounded-Regular" {
|
||||
return .system(size: size, design: .rounded)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return .custom(chosenFont.fontName, size: size, relativeTo: textStyle)
|
||||
}
|
||||
}
|
||||
@ -72,13 +70,11 @@ public extension Font {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
extension UIFont {
|
||||
public func rounded() -> UIFont {
|
||||
guard let descriptor = fontDescriptor.withDesign(.rounded) else {
|
||||
return self
|
||||
}
|
||||
return UIFont(descriptor: descriptor, size: pointSize)
|
||||
public extension UIFont {
|
||||
func rounded() -> UIFont {
|
||||
guard let descriptor = fontDescriptor.withDesign(.rounded) else {
|
||||
return self
|
||||
}
|
||||
return UIFont(descriptor: descriptor, size: pointSize)
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ public class Theme: ObservableObject {
|
||||
case .hyperLegible:
|
||||
return "Hyper Legible"
|
||||
case .SFRounded:
|
||||
return "SF Rounded"
|
||||
return "SF Rounded"
|
||||
case .custom:
|
||||
return "settings.display.font.custom"
|
||||
}
|
||||
@ -87,7 +87,7 @@ public class Theme: ObservableObject {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public var chosenFont: UIFont? {
|
||||
get {
|
||||
guard let chosenFontData,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import NukeUI
|
||||
import Nuke
|
||||
import NukeUI
|
||||
import Shimmer
|
||||
import SwiftUI
|
||||
|
||||
@ -97,17 +97,17 @@ public struct AvatarView: View {
|
||||
}
|
||||
|
||||
private struct AvatarPlaceholderView: View {
|
||||
let size: AvatarView.Size
|
||||
let size: AvatarView.Size
|
||||
|
||||
var body: some View {
|
||||
if size == .badge {
|
||||
Circle()
|
||||
.fill(.gray)
|
||||
.frame(width: size.size.width, height: size.size.height)
|
||||
} else {
|
||||
RoundedRectangle(cornerRadius: size.cornerRadius)
|
||||
.fill(.gray)
|
||||
.frame(width: size.size.width, height: size.size.height)
|
||||
}
|
||||
var body: some View {
|
||||
if size == .badge {
|
||||
Circle()
|
||||
.fill(.gray)
|
||||
.frame(width: size.size.width, height: size.size.height)
|
||||
} else {
|
||||
RoundedRectangle(cornerRadius: size.cornerRadius)
|
||||
.fill(.gray)
|
||||
.frame(width: size.size.width, height: size.size.height)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ public extension EnvironmentValues {
|
||||
get { self[IsCompact.self] }
|
||||
set { self[IsCompact.self] = newValue }
|
||||
}
|
||||
|
||||
|
||||
var isInCaptureMode: Bool {
|
||||
get { self[IsInCaptureMode.self] }
|
||||
set { self[IsInCaptureMode.self] = newValue }
|
||||
|
@ -96,13 +96,13 @@ public class UserPreferences: ObservableObject {
|
||||
public func setNotification(count: Int, token: OauthToken) {
|
||||
Self.sharedDefault?.set(count, forKey: "push_notifications_count_\(token.createdAt)")
|
||||
}
|
||||
|
||||
|
||||
public func getNotificationsCount(for token: OauthToken) -> Int {
|
||||
Self.sharedDefault?.integer(forKey: "push_notifications_count_\(token.createdAt)") ?? 0
|
||||
}
|
||||
|
||||
|
||||
public func getNotificationsTotalCount(for tokens: [OauthToken]) -> Int {
|
||||
var count: Int = 0
|
||||
var count = 0
|
||||
for token in tokens {
|
||||
count += getNotificationsCount(for: token)
|
||||
}
|
||||
@ -134,6 +134,3 @@ public class UserPreferences: ObservableObject {
|
||||
recentlyUsedLanguages = Array(copy.prefix(3))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -28,13 +28,11 @@ public struct Poll: Codable, Equatable, Hashable {
|
||||
public let voted: Bool?
|
||||
public let ownVotes: [Int]?
|
||||
public let options: [Option]
|
||||
|
||||
|
||||
// the votersCount can be null according to the docs when multiple is false.
|
||||
// Didn't find that to be true, but we make sure
|
||||
public var safeVotersCount: Int {
|
||||
get {
|
||||
return votersCount ?? votesCount
|
||||
}
|
||||
public var safeVotersCount: Int {
|
||||
return votersCount ?? votesCount
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,8 +56,8 @@ public protocol AnyStatus {
|
||||
}
|
||||
|
||||
public struct StatusViewId: Hashable {
|
||||
let id: String
|
||||
let editedAt: ServerDate?
|
||||
let id: String
|
||||
let editedAt: ServerDate?
|
||||
}
|
||||
|
||||
public extension AnyStatus {
|
||||
|
@ -164,7 +164,7 @@ public struct MuteData: Encodable, Sendable {
|
||||
|
||||
public struct RelationshipNoteData: Encodable, Sendable {
|
||||
public let comment: String
|
||||
|
||||
|
||||
public init(note comment: String) {
|
||||
self.comment = comment
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ public struct StatusPollView: View {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func percentForOption(option: Poll.Option) -> Int {
|
||||
let percent = ratioForOption(option: option) * 100
|
||||
return Int(round(percent))
|
||||
|
@ -43,10 +43,10 @@ public class StatusRowViewModel: ObservableObject {
|
||||
var filter: Filtered? {
|
||||
status.reblog?.filtered?.first ?? status.filtered?.first
|
||||
}
|
||||
|
||||
|
||||
var isThread: Bool {
|
||||
status.reblog?.inReplyToId != nil || status.reblog?.inReplyToAccountId != nil ||
|
||||
status.inReplyToId != nil || status.inReplyToAccountId != nil
|
||||
status.inReplyToId != nil || status.inReplyToAccountId != nil
|
||||
}
|
||||
|
||||
var highlightRowColor: Color {
|
||||
|
@ -8,11 +8,11 @@ struct StatusRowActionsView: View {
|
||||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var currentAccount: CurrentAccount
|
||||
@ObservedObject var viewModel: StatusRowViewModel
|
||||
|
||||
|
||||
func privateBoost() -> Bool {
|
||||
return self.viewModel.status.visibility == .priv && self.viewModel.status.account.id == self.currentAccount.account?.id
|
||||
return viewModel.status.visibility == .priv && viewModel.status.account.id == currentAccount.account?.id
|
||||
}
|
||||
|
||||
|
||||
@MainActor
|
||||
enum Actions: CaseIterable {
|
||||
case respond, boost, favorite, bookmark, share
|
||||
@ -22,10 +22,10 @@ struct StatusRowActionsView: View {
|
||||
case .respond:
|
||||
return "arrowshape.turn.up.left"
|
||||
case .boost:
|
||||
if (privateBoost) {
|
||||
if privateBoost {
|
||||
return viewModel.isReblogged ? "arrow.left.arrow.right.circle.fill" : "lock.rotation"
|
||||
}
|
||||
|
||||
|
||||
return viewModel.isReblogged ? "arrow.left.arrow.right.circle.fill" : "arrow.left.arrow.right.circle"
|
||||
case .favorite:
|
||||
return viewModel.isFavorited ? "star.fill" : "star"
|
||||
@ -96,7 +96,7 @@ struct StatusRowActionsView: View {
|
||||
}
|
||||
.buttonStyle(.borderless)
|
||||
.disabled(action == .boost &&
|
||||
(viewModel.status.visibility == .direct || viewModel.status.visibility == .priv && viewModel.status.account.id != currentAccount.account?.id))
|
||||
(viewModel.status.visibility == .direct || viewModel.status.visibility == .priv && viewModel.status.account.id != currentAccount.account?.id))
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import SwiftUI
|
||||
public struct StatusRowCardView: View {
|
||||
@Environment(\.openURL) private var openURL
|
||||
@Environment(\.isInCaptureMode) private var isInCaptureMode: Bool
|
||||
|
||||
|
||||
@EnvironmentObject private var theme: Theme
|
||||
let card: Card
|
||||
|
||||
|
@ -1,19 +1,19 @@
|
||||
import DesignSystem
|
||||
import Env
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
import DesignSystem
|
||||
import Network
|
||||
import SwiftUI
|
||||
|
||||
struct StatusRowContextMenu: View {
|
||||
@Environment(\.displayScale) var displayScale
|
||||
|
||||
|
||||
@EnvironmentObject private var sceneDelegate: SceneDelegate
|
||||
@EnvironmentObject private var preferences: UserPreferences
|
||||
@EnvironmentObject private var account: CurrentAccount
|
||||
@EnvironmentObject private var currentInstance: CurrentInstance
|
||||
|
||||
@ObservedObject var viewModel: StatusRowViewModel
|
||||
|
||||
|
||||
var boostLabel: some View {
|
||||
if self.viewModel.status.visibility == .priv && self.viewModel.status.account.id == self.account.account?.id {
|
||||
if self.viewModel.isReblogged {
|
||||
@ -21,13 +21,13 @@ struct StatusRowContextMenu: View {
|
||||
}
|
||||
return Label("status.action.boost-to-followers", systemImage: "lock.rotation")
|
||||
}
|
||||
|
||||
|
||||
if self.viewModel.isReblogged {
|
||||
return Label("status.action.unboost", systemImage: "arrow.left.arrow.right.circle")
|
||||
}
|
||||
return Label("status.action.boost", systemImage: "arrow.left.arrow.right.circle")
|
||||
}
|
||||
|
||||
|
||||
var body: some View {
|
||||
if !viewModel.isRemote {
|
||||
Button { Task {
|
||||
@ -85,11 +85,11 @@ struct StatusRowContextMenu: View {
|
||||
message: Text(viewModel.status.reblog?.content.asRawText ?? viewModel.status.content.asRawText)) {
|
||||
Label("status.action.share", systemImage: "square.and.arrow.up")
|
||||
}
|
||||
|
||||
|
||||
ShareLink(item: url) {
|
||||
Label("status.action.share-link", systemImage: "link")
|
||||
}
|
||||
|
||||
|
||||
Button {
|
||||
let view = HStack {
|
||||
StatusRowView(viewModel: viewModel)
|
||||
@ -200,10 +200,10 @@ struct StatusRowContextMenu: View {
|
||||
|
||||
struct ActivityView: UIViewControllerRepresentable {
|
||||
let image: Image
|
||||
|
||||
func makeUIViewController(context: UIViewControllerRepresentableContext<ActivityView>) -> UIActivityViewController {
|
||||
|
||||
func makeUIViewController(context _: UIViewControllerRepresentableContext<ActivityView>) -> UIActivityViewController {
|
||||
return UIActivityViewController(activityItems: [image], applicationActivities: nil)
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: UIActivityViewController, context: UIViewControllerRepresentableContext<ActivityView>) {}
|
||||
|
||||
func updateUIViewController(_: UIActivityViewController, context _: UIViewControllerRepresentableContext<ActivityView>) {}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import DesignSystem
|
||||
import Env
|
||||
import Models
|
||||
import SwiftUI
|
||||
import Env
|
||||
|
||||
struct StatusRowHeaderView: View {
|
||||
@Environment(\.isInCaptureMode) private var isInCaptureMode: Bool
|
||||
@ -57,19 +57,19 @@ struct StatusRowHeaderView: View {
|
||||
}
|
||||
if theme.avatarPosition == .top {
|
||||
dateView
|
||||
.font(.scaledFootnote)
|
||||
.foregroundColor(.gray)
|
||||
.lineLimit(1)
|
||||
.font(.scaledFootnote)
|
||||
.foregroundColor(.gray)
|
||||
.lineLimit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private var dateView: Text {
|
||||
Text(viewModel.status.account.bot ? "🤖 " : "") +
|
||||
Text(status.createdAt.relativeFormatted) +
|
||||
Text(" ⸱ ") +
|
||||
Text(Image(systemName: viewModel.status.visibility.iconName))
|
||||
Text(status.createdAt.relativeFormatted) +
|
||||
Text(" ⸱ ") +
|
||||
Text(Image(systemName: viewModel.status.visibility.iconName))
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
|
@ -156,7 +156,8 @@ public struct StatusRowMediaPreviewView: View {
|
||||
case .image:
|
||||
if isInCaptureMode,
|
||||
let image = Nuke.ImagePipeline.shared.cache.cachedImage(for: .init(url: attachment.url,
|
||||
processors: processors))?.image {
|
||||
processors: processors))?.image
|
||||
{
|
||||
Image(uiImage: image)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fill)
|
||||
@ -243,10 +244,11 @@ public struct StatusRowMediaPreviewView: View {
|
||||
cornerSensitiveButton
|
||||
}
|
||||
if !isInCaptureMode,
|
||||
let alt = attachment.description,
|
||||
let alt = attachment.description,
|
||||
!alt.isEmpty,
|
||||
!isNotifications,
|
||||
preferences.showAltTextForMedia {
|
||||
preferences.showAltTextForMedia
|
||||
{
|
||||
Button {
|
||||
altTextDisplayed = alt
|
||||
isAltAlertDisplayed = true
|
||||
|
@ -4,7 +4,7 @@ import SwiftUI
|
||||
|
||||
struct StatusRowTextView: View {
|
||||
@EnvironmentObject private var theme: Theme
|
||||
|
||||
|
||||
let status: AnyStatus
|
||||
let viewModel: StatusRowViewModel
|
||||
|
||||
|
@ -5,7 +5,7 @@ import SwiftUI
|
||||
|
||||
struct StatusRowTranslateView: View {
|
||||
@Environment(\.isInCaptureMode) private var isInCaptureMode: Bool
|
||||
|
||||
|
||||
@EnvironmentObject private var preferences: UserPreferences
|
||||
|
||||
let status: AnyStatus
|
||||
@ -27,7 +27,7 @@ struct StatusRowTranslateView: View {
|
||||
|
||||
var body: some View {
|
||||
if !isInCaptureMode,
|
||||
let userLang = preferences.serverPreferences?.postLanguage,
|
||||
let userLang = preferences.serverPreferences?.postLanguage,
|
||||
shouldShowTranslateButton
|
||||
{
|
||||
Button {
|
||||
|
@ -3,51 +3,51 @@ import Models
|
||||
|
||||
actor TimelineDatasource {
|
||||
private var statuses: [Status] = []
|
||||
|
||||
|
||||
var isEmpty: Bool {
|
||||
statuses.isEmpty
|
||||
}
|
||||
|
||||
|
||||
func get() -> [Status] {
|
||||
statuses
|
||||
}
|
||||
|
||||
|
||||
func reset() {
|
||||
statuses = []
|
||||
}
|
||||
|
||||
|
||||
func indexOf(statusId: String) -> Int? {
|
||||
statuses.firstIndex(where: { $0.id == statusId })
|
||||
}
|
||||
|
||||
|
||||
func contains(statusId: String) -> Bool {
|
||||
statuses.contains(where: { $0.id == statusId })
|
||||
}
|
||||
|
||||
|
||||
func set(_ statuses: [Status]) {
|
||||
self.statuses = statuses
|
||||
}
|
||||
|
||||
|
||||
func append(_ status: Status) {
|
||||
statuses.append(status)
|
||||
}
|
||||
|
||||
|
||||
func append(contentOf: [Status]) {
|
||||
statuses.append(contentsOf: contentOf)
|
||||
}
|
||||
|
||||
|
||||
func insert(_ status: Status, at: Int) {
|
||||
statuses.insert(status, at: at)
|
||||
}
|
||||
|
||||
|
||||
func insert(contentOf: [Status], at: Int) {
|
||||
statuses.insert(contentsOf: contentOf, at: at)
|
||||
}
|
||||
|
||||
|
||||
func replace(_ status: Status, at: Int) {
|
||||
statuses[at] = status
|
||||
}
|
||||
|
||||
|
||||
func remove(_ statusId: String) {
|
||||
statuses.removeAll(where: { $0.id == statusId })
|
||||
}
|
||||
|
@ -1,25 +1,25 @@
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
import Models
|
||||
import Nuke
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
final class TimelinePrefetcher: NSObject, ObservableObject, UICollectionViewDataSourcePrefetching {
|
||||
private let prefetcher = ImagePrefetcher()
|
||||
|
||||
weak var viewModel: TimelineViewModel?
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) {
|
||||
func collectionView(_: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) {
|
||||
let imageURLs = getImageURLs(for: indexPaths)
|
||||
prefetcher.startPrefetching(with: imageURLs)
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, cancelPrefetchingForItemsAt indexPaths: [IndexPath]) {
|
||||
func collectionView(_: UICollectionView, cancelPrefetchingForItemsAt indexPaths: [IndexPath]) {
|
||||
let imageURLs = getImageURLs(for: indexPaths)
|
||||
prefetcher.stopPrefetching(with: imageURLs)
|
||||
}
|
||||
|
||||
private func getImageURLs(for indexPaths: [IndexPath]) -> [URL] {
|
||||
guard let viewModel, case .display(let statuses, _) = viewModel.statusesState else {
|
||||
guard let viewModel, case let .display(statuses, _) = viewModel.statusesState else {
|
||||
return []
|
||||
}
|
||||
return indexPaths.compactMap {
|
||||
|
@ -92,7 +92,7 @@ class TimelineViewModel: ObservableObject {
|
||||
tag = try await client.get(endpoint: Tags.tag(id: id))
|
||||
} catch {}
|
||||
}
|
||||
|
||||
|
||||
func reset() async {
|
||||
await datasource.reset()
|
||||
}
|
||||
@ -214,7 +214,7 @@ extension TimelineViewModel: StatusesFetcher {
|
||||
|
||||
updateMentionsToBeHighlighted(&statuses)
|
||||
ReblogCache.shared.removeDuplicateReblogs(&statuses)
|
||||
|
||||
|
||||
await datasource.set(statuses)
|
||||
await cacheHome()
|
||||
|
||||
@ -231,7 +231,7 @@ extension TimelineViewModel: StatusesFetcher {
|
||||
var newStatuses: [Status] = await fetchNewPages(minId: latestStatus.id, maxPages: 10)
|
||||
|
||||
// Dedup statuses, a status with the same id could have been streamed in.
|
||||
let ids = await datasource.get().map{ $0.id }
|
||||
let ids = await datasource.get().map { $0.id }
|
||||
newStatuses = newStatuses.filter { status in
|
||||
!ids.contains(where: { $0 == status.id })
|
||||
}
|
||||
@ -322,10 +322,10 @@ extension TimelineViewModel: StatusesFetcher {
|
||||
var latestMinId = minId
|
||||
do {
|
||||
while var newStatuses: [Status] =
|
||||
try await client.get(endpoint: timeline.endpoint(sinceId: nil,
|
||||
maxId: nil,
|
||||
minId: latestMinId,
|
||||
offset: datasource.get().count)),
|
||||
try await client.get(endpoint: timeline.endpoint(sinceId: nil,
|
||||
maxId: nil,
|
||||
minId: latestMinId,
|
||||
offset: datasource.get().count)),
|
||||
!newStatuses.isEmpty,
|
||||
pagesLoaded < maxPages
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user