mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-11-05 16:59:30 +00:00
Run SwiftFormat
This commit is contained in:
parent
5991641d32
commit
a79c5691e0
49 changed files with 234 additions and 237 deletions
|
@ -14,7 +14,7 @@ struct IceCubesApp: App {
|
|||
@UIApplicationDelegateAdaptor private var appDelegate: AppDelegate
|
||||
|
||||
@Environment(\.scenePhase) private var scenePhase
|
||||
|
||||
|
||||
@StateObject private var appAccountsManager = AppAccountsManager.shared
|
||||
@StateObject private var currentInstance = CurrentInstance.shared
|
||||
@StateObject private var currentAccount = CurrentAccount.shared
|
||||
|
@ -28,7 +28,7 @@ struct IceCubesApp: App {
|
|||
@State private var selectSidebarItem: Tab? = .timeline
|
||||
@State private var popToRootTab: Tab = .other
|
||||
@State private var sideBarLoadedTabs: Set<Tab> = Set()
|
||||
|
||||
|
||||
private var availableTabs: [Tab] {
|
||||
appAccountsManager.currentClient.isAuth ? Tab.loggedInTabs() : Tab.loggedOutTab()
|
||||
}
|
||||
|
@ -172,7 +172,7 @@ struct IceCubesApp: App {
|
|||
private func refreshPushSubs() {
|
||||
PushNotificationsService.shared.requestPushNotifications()
|
||||
}
|
||||
|
||||
|
||||
@CommandsBuilder
|
||||
private var appMenu: some Commands {
|
||||
CommandGroup(replacing: .newItem) {
|
||||
|
@ -199,7 +199,7 @@ struct IceCubesApp: App {
|
|||
|
||||
class AppDelegate: NSObject, UIApplicationDelegate {
|
||||
let themeObserver = ThemeObserverViewController(nibName: nil, bundle: nil)
|
||||
|
||||
|
||||
func application(_: UIApplication,
|
||||
didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool
|
||||
{
|
||||
|
@ -220,13 +220,12 @@ class AppDelegate: NSObject, UIApplicationDelegate {
|
|||
}
|
||||
|
||||
func application(_: UIApplication, didFailToRegisterForRemoteNotificationsWithError _: Error) {}
|
||||
|
||||
}
|
||||
|
||||
class ThemeObserverViewController: UIViewController {
|
||||
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||
super.traitCollectionDidChange(previousTraitCollection)
|
||||
|
||||
|
||||
print(traitCollection.userInterfaceStyle.rawValue)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ struct SideBarView<Content: View>: View {
|
|||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var watcher: StreamWatcher
|
||||
@EnvironmentObject private var userPreferences: UserPreferences
|
||||
|
||||
|
||||
@Binding var selectedTab: Tab
|
||||
@Binding var popToRootTab: Tab
|
||||
var tabs: [Tab]
|
||||
|
@ -23,7 +23,7 @@ struct SideBarView<Content: View>: View {
|
|||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
private var profileView: some View {
|
||||
Button {
|
||||
selectedTab = .profile
|
||||
|
@ -35,7 +35,7 @@ struct SideBarView<Content: View>: View {
|
|||
.frame(width: .sidebarWidth, height: 60)
|
||||
.background(selectedTab == .profile ? theme.secondaryBackgroundColor : .clear)
|
||||
}
|
||||
|
||||
|
||||
private func makeIconForTab(tab: Tab) -> some View {
|
||||
ZStack(alignment: .topTrailing) {
|
||||
SideBarIcon(systemIconName: tab.iconName,
|
||||
|
@ -55,7 +55,7 @@ struct SideBarView<Content: View>: View {
|
|||
.contentShape(Rectangle())
|
||||
.frame(width: .sidebarWidth, height: 50)
|
||||
}
|
||||
|
||||
|
||||
private var postButton: some View {
|
||||
Button {
|
||||
routerPath.presentedSheet = .newStatusEditor(visibility: userPreferences.serverPreferences?.postVisibility ?? .pub)
|
||||
|
@ -68,7 +68,7 @@ struct SideBarView<Content: View>: View {
|
|||
.buttonStyle(.borderedProminent)
|
||||
.keyboardShortcut("n", modifiers: .command)
|
||||
}
|
||||
|
||||
|
||||
private func makeAccountButton(account: AppAccount) -> some View {
|
||||
Button {
|
||||
if account.id == appAccounts.currentAccount.id {
|
||||
|
@ -84,9 +84,9 @@ struct SideBarView<Content: View>: View {
|
|||
.frame(width: .sidebarWidth, height: 50)
|
||||
.padding(.vertical, 8)
|
||||
.background(selectedTab == .profile && account.id == appAccounts.currentAccount.id ?
|
||||
theme.secondaryBackgroundColor : .clear)
|
||||
theme.secondaryBackgroundColor : .clear)
|
||||
}
|
||||
|
||||
|
||||
private var tabsView: some View {
|
||||
ForEach(tabs) { tab in
|
||||
Button {
|
||||
|
@ -107,7 +107,7 @@ struct SideBarView<Content: View>: View {
|
|||
.background(tab == selectedTab ? theme.secondaryBackgroundColor : .clear)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: 0) {
|
||||
ScrollView {
|
||||
|
@ -141,12 +141,12 @@ struct SideBarView<Content: View>: View {
|
|||
|
||||
private struct SideBarIcon: View {
|
||||
@EnvironmentObject private var theme: Theme
|
||||
|
||||
|
||||
let systemIconName: String
|
||||
let isSelected: Bool
|
||||
|
||||
|
||||
@State private var isHovered: Bool = false
|
||||
|
||||
|
||||
var body: some View {
|
||||
Image(systemName: systemIconName)
|
||||
.resizable()
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import AppAccount
|
||||
import Env
|
||||
import Models
|
||||
import Network
|
||||
import Notifications
|
||||
import SwiftUI
|
||||
import Timeline
|
||||
import Models
|
||||
|
||||
struct NotificationsTab: View {
|
||||
@EnvironmentObject private var client: Client
|
||||
|
@ -13,7 +13,7 @@ struct NotificationsTab: View {
|
|||
@EnvironmentObject private var userPreferences: UserPreferences
|
||||
@StateObject private var routerPath = RouterPath()
|
||||
@Binding var popToRootTab: Tab
|
||||
|
||||
|
||||
let lockedType: Models.Notification.NotificationType?
|
||||
|
||||
var body: some View {
|
||||
|
|
|
@ -5,9 +5,9 @@ import Env
|
|||
import Models
|
||||
import Network
|
||||
import NukeUI
|
||||
import SafariServices
|
||||
import Shimmer
|
||||
import SwiftUI
|
||||
import SafariServices
|
||||
|
||||
struct AddAccountView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
@ -81,7 +81,7 @@ struct AddAccountView: View {
|
|||
instanceNamePublisher.send(newValue)
|
||||
}
|
||||
.onReceive(instanceNamePublisher.debounce(for: .milliseconds(500), scheduler: DispatchQueue.main)) { newValue in
|
||||
let newValue = newValue
|
||||
let newValue = newValue
|
||||
.replacingOccurrences(of: "http://", with: "")
|
||||
.replacingOccurrences(of: "https://", with: "")
|
||||
let client = Client(server: newValue)
|
||||
|
@ -171,8 +171,8 @@ struct AddAccountView: View {
|
|||
(Text("instance.list.users-\(instance.users)")
|
||||
+ Text(" ⸱ ")
|
||||
+ Text("instance.list.posts-\(instance.statuses)"))
|
||||
.font(.scaledFootnote)
|
||||
.foregroundColor(.gray)
|
||||
.font(.scaledFootnote)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
|
@ -235,11 +235,9 @@ struct AddAccountView: View {
|
|||
struct SafariView: UIViewControllerRepresentable {
|
||||
let url: URL
|
||||
|
||||
func makeUIViewController(context: UIViewControllerRepresentableContext<SafariView>) -> SFSafariViewController {
|
||||
func makeUIViewController(context _: UIViewControllerRepresentableContext<SafariView>) -> SFSafariViewController {
|
||||
SFSafariViewController(url: url)
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: SFSafariViewController, context: UIViewControllerRepresentableContext<SafariView>) {
|
||||
|
||||
}
|
||||
func updateUIViewController(_: SFSafariViewController, context _: UIViewControllerRepresentableContext<SafariView>) {}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import DesignSystem
|
||||
import Env
|
||||
import Models
|
||||
import Status
|
||||
import SwiftUI
|
||||
import Env
|
||||
|
||||
struct DisplaySettingsView: View {
|
||||
@EnvironmentObject private var theme: Theme
|
||||
|
@ -16,15 +16,15 @@ struct DisplaySettingsView: View {
|
|||
Toggle("settings.display.theme.systemColor", isOn: $theme.followSystemColorScheme)
|
||||
themeSelectorButton
|
||||
ColorPicker("settings.display.theme.tint", selection: $theme.tintColor)
|
||||
.onChange(of: theme.tintColor) { newValue in
|
||||
.onChange(of: theme.tintColor) { _ in
|
||||
theme.followSystemColorScheme = false
|
||||
}
|
||||
ColorPicker("settings.display.theme.background", selection: $theme.primaryBackgroundColor)
|
||||
.onChange(of: theme.primaryBackgroundColor) { newValue in
|
||||
.onChange(of: theme.primaryBackgroundColor) { _ in
|
||||
theme.followSystemColorScheme = false
|
||||
}
|
||||
ColorPicker("settings.display.theme.secondary-background", selection: $theme.secondaryBackgroundColor)
|
||||
.onChange(of: theme.primaryBackgroundColor) { newValue in
|
||||
.onChange(of: theme.primaryBackgroundColor) { _ in
|
||||
theme.followSystemColorScheme = false
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ struct DisplaySettingsView: View {
|
|||
}
|
||||
if ProcessInfo.processInfo.isiOSAppOnMac {
|
||||
VStack {
|
||||
Slider(value: $userPreferences.fontSizeScale, in: 0.5...1.5, step: 0.1)
|
||||
Slider(value: $userPreferences.fontSizeScale, in: 0.5 ... 1.5, step: 0.1)
|
||||
Text("Font scaling: \(String(format: "%.1f", userPreferences.fontSizeScale))")
|
||||
.font(.scaledBody)
|
||||
}
|
||||
|
|
|
@ -134,7 +134,7 @@ struct SettingsTabs: View {
|
|||
NavigationLink(destination: SupportAppView()) {
|
||||
Label("settings.app.support", systemImage: "wand.and.stars")
|
||||
}
|
||||
|
||||
|
||||
if let reviewURL = URL(string: "https://apps.apple.com/app/id\(AppInfo.appStoreAppId)?action=write-review") {
|
||||
Link(destination: reviewURL) {
|
||||
Label("Rate Ice Cubes", systemImage: "link")
|
||||
|
@ -142,11 +142,11 @@ struct SettingsTabs: View {
|
|||
.tint(theme.labelColor)
|
||||
}
|
||||
} header: {
|
||||
Text("settings.section.app")
|
||||
Text("settings.section.app")
|
||||
} footer: {
|
||||
if let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
|
||||
Text("App Version: \(appVersion)").frame(maxWidth: .infinity, alignment: .center)
|
||||
}
|
||||
if let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
|
||||
Text("App Version: \(appVersion)").frame(maxWidth: .infinity, alignment: .center)
|
||||
}
|
||||
}
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ struct SupportAppView: View {
|
|||
var productId: String {
|
||||
"icecubes.tipjar.\(rawValue)"
|
||||
}
|
||||
|
||||
|
||||
var title: LocalizedStringKey {
|
||||
switch self {
|
||||
case .one:
|
||||
|
@ -26,7 +26,7 @@ struct SupportAppView: View {
|
|||
return "settings.support.three.title"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var subtitle: LocalizedStringKey {
|
||||
switch self {
|
||||
case .one:
|
||||
|
|
|
@ -94,12 +94,12 @@ struct AddRemoteTimelineView: View {
|
|||
Text(instance.info?.shortDescription ?? "")
|
||||
.font(.scaledBody)
|
||||
.foregroundColor(.gray)
|
||||
|
||||
|
||||
(Text("instance.list.users-\(instance.users)")
|
||||
+ Text(" ⸱ ")
|
||||
+ Text("instance.list.posts-\(instance.statuses)"))
|
||||
.font(.scaledFootnote)
|
||||
.foregroundColor(.gray)
|
||||
.font(.scaledFootnote)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
|
|
|
@ -7,7 +7,7 @@ import Network
|
|||
import SwiftUI
|
||||
import Timeline
|
||||
|
||||
struct TimelineTab: View {
|
||||
struct TimelineTab: View {
|
||||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var currentAccount: CurrentAccount
|
||||
@EnvironmentObject private var preferences: UserPreferences
|
||||
|
@ -104,7 +104,7 @@ struct TimelineTab: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Menu("timeline.filter.local") {
|
||||
ForEach(preferences.remoteLocalTimelines, id: \.self) { server in
|
||||
Button {
|
||||
|
|
|
@ -119,8 +119,8 @@ struct AccountDetailHeaderView: View {
|
|||
relationship: relationship,
|
||||
shouldDisplayNotify: true,
|
||||
relationshipUpdated: { relationship in
|
||||
viewModel.relationship = relationship
|
||||
}))
|
||||
viewModel.relationship = relationship
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import SwiftUI
|
|||
public struct AccountDetailView: View {
|
||||
@Environment(\.openURL) private var openURL
|
||||
@Environment(\.redactionReasons) private var reasons
|
||||
|
||||
|
||||
@EnvironmentObject private var watcher: StreamWatcher
|
||||
@EnvironmentObject private var currentAccount: CurrentAccount
|
||||
@EnvironmentObject private var preferences: UserPreferences
|
||||
|
@ -149,7 +149,7 @@ public struct AccountDetailView: View {
|
|||
Text("Error: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ViewBuilder
|
||||
private var joinedAtView: some View {
|
||||
if let joinedAt = viewModel.account?.createdAt.asDate {
|
||||
|
@ -377,7 +377,7 @@ public struct AccountDetailView: View {
|
|||
if !viewModel.isCurrentUser {
|
||||
Button {
|
||||
routerPath.presentedSheet = .mentionStatusEditor(account: account,
|
||||
visibility: preferences.serverPreferences?.postVisibility ?? .pub)
|
||||
visibility: preferences.serverPreferences?.postVisibility ?? .pub)
|
||||
} label: {
|
||||
Label("account.action.mention", systemImage: "at")
|
||||
}
|
||||
|
@ -386,9 +386,9 @@ public struct AccountDetailView: View {
|
|||
} label: {
|
||||
Label("account.action.message", systemImage: "tray.full")
|
||||
}
|
||||
|
||||
|
||||
Divider()
|
||||
|
||||
|
||||
if viewModel.relationship?.blocking == true {
|
||||
Button {
|
||||
Task {
|
||||
|
|
|
@ -140,7 +140,7 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
|||
featuredTags: featuredTags,
|
||||
relationships: [])
|
||||
}
|
||||
|
||||
|
||||
func fetchFamilliarFollowers() async {
|
||||
let familiarFollowers: [FamiliarAccounts]? = try? await client?.get(endpoint: Accounts.familiarFollowers(withAccount: accountId))
|
||||
self.familiarFollowers = familiarFollowers?.first?.accounts ?? []
|
||||
|
|
|
@ -5,7 +5,7 @@ import SwiftUI
|
|||
public enum AccountsListMode {
|
||||
case following(accountId: String), followers(accountId: String)
|
||||
case favouritedBy(statusId: String), rebloggedBy(statusId: String)
|
||||
|
||||
|
||||
var title: LocalizedStringKey {
|
||||
switch self {
|
||||
case .following:
|
||||
|
|
|
@ -9,14 +9,15 @@ public class FollowButtonViewModel: ObservableObject {
|
|||
|
||||
public let accountId: String
|
||||
public let shouldDisplayNotify: Bool
|
||||
public let relationshipUpdated: ((Relationship) -> Void)
|
||||
public let relationshipUpdated: (Relationship) -> Void
|
||||
@Published public private(set) var relationship: Relationship
|
||||
@Published public private(set) var isUpdating: Bool = false
|
||||
|
||||
public init(accountId: String,
|
||||
relationship: Relationship,
|
||||
shouldDisplayNotify: Bool,
|
||||
relationshipUpdated: @escaping ((Relationship) -> Void)) {
|
||||
relationshipUpdated: @escaping ((Relationship) -> Void))
|
||||
{
|
||||
self.accountId = accountId
|
||||
self.relationship = relationship
|
||||
self.shouldDisplayNotify = shouldDisplayNotify
|
||||
|
|
|
@ -26,7 +26,7 @@ public struct AppAccountView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ViewBuilder
|
||||
private var compactView: some View {
|
||||
HStack {
|
||||
|
@ -35,7 +35,7 @@ public struct AppAccountView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private var fullView: some View {
|
||||
HStack {
|
||||
if let account = viewModel.account {
|
||||
|
|
|
@ -20,7 +20,7 @@ struct ConversationsListRow: View {
|
|||
VStack(alignment: .leading, spacing: 4) {
|
||||
HStack {
|
||||
EmojiTextApp(.init(stringValue: conversation.accounts.map { $0.safeDisplayName }.joined(separator: ", ")),
|
||||
emojis: conversation.accounts.flatMap{ $0.emojis })
|
||||
emojis: conversation.accounts.flatMap { $0.emojis })
|
||||
.font(.scaledSubheadline)
|
||||
.fontWeight(.semibold)
|
||||
.foregroundColor(theme.labelColor)
|
||||
|
|
|
@ -51,7 +51,7 @@ public struct ConversationsListView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if viewModel.nextPage != nil {
|
||||
HStack {
|
||||
Spacer()
|
||||
|
|
|
@ -10,7 +10,7 @@ class ConversationsListViewModel: ObservableObject {
|
|||
@Published var isLoadingNextPage: Bool = false
|
||||
@Published var conversations: [Conversation] = []
|
||||
@Published var isError: Bool = false
|
||||
|
||||
|
||||
var nextPage: LinkHandler?
|
||||
|
||||
public init() {}
|
||||
|
@ -31,7 +31,7 @@ class ConversationsListViewModel: ObservableObject {
|
|||
isLoadingFirstPage = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func fetchNextPage() async {
|
||||
if let maxId = nextPage?.maxId, let client {
|
||||
do {
|
||||
|
@ -43,8 +43,7 @@ class ConversationsListViewModel: ObservableObject {
|
|||
nextPage = nil
|
||||
}
|
||||
isLoadingNextPage = false
|
||||
} catch {
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import SwiftUI
|
||||
|
||||
public let availableColorsSets: [ColorSetCouple] =
|
||||
[.init(light: IceCubeLight(), dark: IceCubeDark()),
|
||||
.init(light: IceCubeNeonLight(), dark: IceCubeNeonDark()),
|
||||
.init(light: DesertLight(), dark: DesertDark()),
|
||||
.init(light: NemesisLight(), dark: NemesisDark()),
|
||||
.init(light: MediumLight(), dark: MediumDark())]
|
||||
[.init(light: IceCubeLight(), dark: IceCubeDark()),
|
||||
.init(light: IceCubeNeonLight(), dark: IceCubeNeonDark()),
|
||||
.init(light: DesertLight(), dark: DesertDark()),
|
||||
.init(light: NemesisLight(), dark: NemesisDark()),
|
||||
.init(light: MediumLight(), dark: MediumDark())]
|
||||
|
||||
public protocol ColorSet {
|
||||
var name: ColorSetName { get }
|
||||
|
@ -37,7 +37,7 @@ public struct ColorSetCouple: Identifiable {
|
|||
public var id: String {
|
||||
dark.name.rawValue + light.name.rawValue
|
||||
}
|
||||
|
||||
|
||||
public let light: ColorSet
|
||||
public let dark: ColorSet
|
||||
}
|
||||
|
|
|
@ -1,71 +1,69 @@
|
|||
import SwiftUI
|
||||
import Env
|
||||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
extension Font {
|
||||
|
||||
public static func userScaledFontSize(baseSize: CGFloat) -> CGFloat {
|
||||
public extension Font {
|
||||
static func userScaledFontSize(baseSize: CGFloat) -> CGFloat {
|
||||
UIFontMetrics.default.scaledValue(for: baseSize * UserPreferences.shared.fontSizeScale)
|
||||
}
|
||||
|
||||
public static var scaledTitle: Font {
|
||||
|
||||
static var scaledTitle: Font {
|
||||
if ProcessInfo.processInfo.isiOSAppOnMac {
|
||||
return .system(size: userScaledFontSize(baseSize: 28))
|
||||
} else {
|
||||
return .title
|
||||
}
|
||||
}
|
||||
|
||||
public static var scaledHeadline: Font {
|
||||
|
||||
static var scaledHeadline: Font {
|
||||
if ProcessInfo.processInfo.isiOSAppOnMac {
|
||||
return .system(size: userScaledFontSize(baseSize: 20), weight: .semibold)
|
||||
} else {
|
||||
return .headline
|
||||
}
|
||||
}
|
||||
|
||||
public static var scaledBody: Font {
|
||||
|
||||
static var scaledBody: Font {
|
||||
if ProcessInfo.processInfo.isiOSAppOnMac {
|
||||
return .system(size: userScaledFontSize(baseSize: 19))
|
||||
} else {
|
||||
return .body
|
||||
}
|
||||
}
|
||||
|
||||
public static var scaledBodyUIFont: UIFont {
|
||||
|
||||
static var scaledBodyUIFont: UIFont {
|
||||
if ProcessInfo.processInfo.isiOSAppOnMac {
|
||||
return UIFont.systemFont(ofSize: userScaledFontSize(baseSize: 19))
|
||||
} else {
|
||||
return UIFont.systemFont(ofSize: 17)
|
||||
}
|
||||
}
|
||||
|
||||
public static var scaledCallout: Font {
|
||||
|
||||
static var scaledCallout: Font {
|
||||
if ProcessInfo.processInfo.isiOSAppOnMac {
|
||||
return .system(size: userScaledFontSize(baseSize: 17))
|
||||
} else {
|
||||
return .callout
|
||||
}
|
||||
}
|
||||
|
||||
public static var scaledSubheadline: Font {
|
||||
|
||||
static var scaledSubheadline: Font {
|
||||
if ProcessInfo.processInfo.isiOSAppOnMac {
|
||||
return .system(size: userScaledFontSize(baseSize: 16))
|
||||
} else {
|
||||
return .subheadline
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static var scaledFootnote: Font {
|
||||
|
||||
static var scaledFootnote: Font {
|
||||
if ProcessInfo.processInfo.isiOSAppOnMac {
|
||||
return .system(size: userScaledFontSize(baseSize: 15))
|
||||
} else {
|
||||
return .footnote
|
||||
}
|
||||
}
|
||||
|
||||
public static var scaledCaption: Font {
|
||||
|
||||
static var scaledCaption: Font {
|
||||
if ProcessInfo.processInfo.isiOSAppOnMac {
|
||||
return .system(size: userScaledFontSize(baseSize: 14))
|
||||
} else {
|
||||
|
|
|
@ -11,9 +11,9 @@ public extension View {
|
|||
|
||||
struct ThemeApplier: ViewModifier {
|
||||
@Environment(\EnvironmentValues.colorScheme) var colorScheme
|
||||
|
||||
|
||||
@ObservedObject var theme: Theme
|
||||
|
||||
|
||||
var actualColorScheme: SwiftUI.ColorScheme? {
|
||||
if theme.followSystemColorScheme {
|
||||
return nil
|
||||
|
@ -29,12 +29,13 @@ struct ThemeApplier: ViewModifier {
|
|||
.onAppear {
|
||||
// If theme is never set before set the default store. This should only execute once after install.
|
||||
if !theme.isThemePreviouslySet {
|
||||
theme.selectedSet = colorScheme == .dark ? .iceCubeDark : .iceCubeLight
|
||||
theme.selectedSet = colorScheme == .dark ? .iceCubeDark : .iceCubeLight
|
||||
theme.isThemePreviouslySet = true
|
||||
} else if theme.followSystemColorScheme && theme.isThemePreviouslySet,
|
||||
} else if theme.followSystemColorScheme, theme.isThemePreviouslySet,
|
||||
let sets = availableColorsSets
|
||||
.first(where: { $0.light.name == theme.selectedSet || $0.dark.name == theme.selectedSet }) {
|
||||
theme.selectedSet = colorScheme == .dark ? sets.dark.name : sets.light.name
|
||||
.first(where: { $0.light.name == theme.selectedSet || $0.dark.name == theme.selectedSet })
|
||||
{
|
||||
theme.selectedSet = colorScheme == .dark ? sets.dark.name : sets.light.name
|
||||
}
|
||||
setWindowTint(theme.tintColor)
|
||||
setBarsColor(theme.primaryBackgroundColor)
|
||||
|
@ -48,7 +49,8 @@ struct ThemeApplier: ViewModifier {
|
|||
.onChange(of: colorScheme) { newColorScheme in
|
||||
if theme.followSystemColorScheme,
|
||||
let sets = availableColorsSets
|
||||
.first(where: { $0.light.name == theme.selectedSet || $0.dark.name == theme.selectedSet }) {
|
||||
.first(where: { $0.light.name == theme.selectedSet || $0.dark.name == theme.selectedSet })
|
||||
{
|
||||
theme.selectedSet = newColorScheme == .dark ? sets.dark.name : sets.light.name
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ public struct EmptyView: View {
|
|||
public let iconName: String
|
||||
public let title: LocalizedStringKey
|
||||
public let message: LocalizedStringKey
|
||||
|
||||
|
||||
public init(iconName: String, title: LocalizedStringKey, message: LocalizedStringKey) {
|
||||
self.iconName = iconName
|
||||
self.title = title
|
||||
|
|
|
@ -44,7 +44,7 @@ struct ThemeBoxView: View {
|
|||
.foregroundColor(color.tintColor)
|
||||
.font(.system(size: 20))
|
||||
.fontWeight(.bold)
|
||||
|
||||
|
||||
Text("design.theme.toots-preview")
|
||||
.foregroundColor(color.labelColor)
|
||||
.frame(maxWidth: .infinity)
|
||||
|
|
|
@ -34,7 +34,7 @@ public enum PollVotingFrequency: String, CaseIterable {
|
|||
case .oneVote: return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public var displayString: LocalizedStringKey {
|
||||
switch self {
|
||||
case .oneVote: return "env.poll-vote-frequency.one"
|
||||
|
|
|
@ -22,9 +22,9 @@ public struct ExploreView: View {
|
|||
loadingView
|
||||
} else if !viewModel.searchQuery.isEmpty {
|
||||
if viewModel.isSearching {
|
||||
HStack { }
|
||||
.listRowBackground(theme.secondaryBackgroundColor)
|
||||
.listRowSeparator(.hidden)
|
||||
HStack {}
|
||||
.listRowBackground(theme.secondaryBackgroundColor)
|
||||
.listRowSeparator(.hidden)
|
||||
} else if let results = viewModel.results[viewModel.searchQuery], !results.isEmpty {
|
||||
makeSearchResultsView(results: results)
|
||||
} else {
|
||||
|
|
|
@ -27,6 +27,7 @@ class ExploreViewModel: ObservableObject {
|
|||
isSearching = true
|
||||
}
|
||||
}
|
||||
|
||||
@Published var results: [String: SearchResults] = [:]
|
||||
@Published var isLoaded = false
|
||||
@Published var isSearching = false
|
||||
|
|
|
@ -9,7 +9,7 @@ public struct HTMLString: Decodable, Equatable {
|
|||
public let asRawText: String
|
||||
public let statusesURLs: [URL]
|
||||
public let asSafeMarkdownAttributedString: AttributedString
|
||||
|
||||
|
||||
public init(from decoder: Decoder) {
|
||||
do {
|
||||
let container = try decoder.singleValueContainer()
|
||||
|
@ -17,7 +17,7 @@ public struct HTMLString: Decodable, Equatable {
|
|||
} catch {
|
||||
htmlValue = ""
|
||||
}
|
||||
|
||||
|
||||
do {
|
||||
asMarkdown = try HTMLParser().parse(html: htmlValue)
|
||||
.toMarkdown()
|
||||
|
@ -25,7 +25,7 @@ public struct HTMLString: Decodable, Equatable {
|
|||
} catch {
|
||||
asMarkdown = htmlValue
|
||||
}
|
||||
|
||||
|
||||
var statusesURLs: [URL] = []
|
||||
do {
|
||||
let document: Document = try SwiftSoup.parse(htmlValue)
|
||||
|
@ -42,9 +42,9 @@ public struct HTMLString: Decodable, Equatable {
|
|||
} catch {
|
||||
asRawText = htmlValue
|
||||
}
|
||||
|
||||
|
||||
self.statusesURLs = statusesURLs
|
||||
|
||||
|
||||
do {
|
||||
let options = AttributedString.MarkdownParsingOptions(allowsExtendedAttributes: true,
|
||||
interpretedSyntax: .inlineOnlyPreservingWhitespace)
|
||||
|
@ -53,7 +53,7 @@ public struct HTMLString: Decodable, Equatable {
|
|||
asSafeMarkdownAttributedString = AttributedString(stringLiteral: htmlValue)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public init(stringValue: String) {
|
||||
htmlValue = stringValue
|
||||
asMarkdown = stringValue
|
||||
|
|
|
@ -22,7 +22,7 @@ extension ServerDate {
|
|||
dateFormatter.dateStyle = .medium
|
||||
return dateFormatter
|
||||
}()
|
||||
|
||||
|
||||
private static let calendar = Calendar(identifier: .gregorian)
|
||||
|
||||
public var asDate: Date {
|
||||
|
|
|
@ -9,7 +9,7 @@ public struct SearchResults: Decodable {
|
|||
public var relationships: [Relationship] = []
|
||||
public let statuses: [Status]
|
||||
public let hashtags: [Tag]
|
||||
|
||||
|
||||
public var isEmpty: Bool {
|
||||
accounts.isEmpty && statuses.isEmpty && hashtags.isEmpty
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ public struct StatusHistory: Decodable, Identifiable {
|
|||
public var id: String {
|
||||
createdAt.description
|
||||
}
|
||||
|
||||
|
||||
public let content: HTMLString
|
||||
public let createdAt: ServerDate
|
||||
public let emojis: [Emoji]
|
||||
|
|
|
@ -2,7 +2,7 @@ import Foundation
|
|||
|
||||
public struct DeepLClient {
|
||||
private let endpoint = "https://api-free.deepl.com/v2/translate"
|
||||
|
||||
|
||||
private var APIKey: String {
|
||||
if let path = Bundle.main.path(forResource: "Secret", ofType: "plist") {
|
||||
let secret = NSDictionary(contentsOfFile: path)
|
||||
|
@ -10,28 +10,29 @@ public struct DeepLClient {
|
|||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
|
||||
private var authorizationHeaderValue: String {
|
||||
"DeepL-Auth-Key \(APIKey)"
|
||||
}
|
||||
|
||||
|
||||
public struct Response: Decodable {
|
||||
public struct Translation: Decodable {
|
||||
public let detectedSourceLanguage: String
|
||||
public let text: String
|
||||
}
|
||||
|
||||
public let translations: [Translation]
|
||||
}
|
||||
|
||||
|
||||
private var decoder: JSONDecoder {
|
||||
let decoder = JSONDecoder()
|
||||
decoder.keyDecodingStrategy = .convertFromSnakeCase
|
||||
return decoder
|
||||
}
|
||||
|
||||
|
||||
public init() {}
|
||||
|
||||
public func request(target: String, source: String?, text: String) async throws -> String {
|
||||
|
||||
public func request(target: String, source _: String?, text: String) async throws -> String {
|
||||
do {
|
||||
var components = URLComponents(string: endpoint)!
|
||||
var queryItems: [URLQueryItem] = []
|
||||
|
|
|
@ -75,7 +75,7 @@ public struct OpenAIClient {
|
|||
public let object: String
|
||||
public let model: String
|
||||
public let choices: [Choice]
|
||||
|
||||
|
||||
public var trimmedText: String {
|
||||
guard var text = choices.first?.text else {
|
||||
return ""
|
||||
|
|
|
@ -41,7 +41,7 @@ extension Models.Notification.NotificationType {
|
|||
return "pencil.line"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func menuTitle() -> LocalizedStringKey {
|
||||
switch self {
|
||||
case .status:
|
||||
|
|
|
@ -11,7 +11,7 @@ public struct NotificationsListView: View {
|
|||
@EnvironmentObject private var watcher: StreamWatcher
|
||||
@EnvironmentObject private var client: Client
|
||||
@StateObject private var viewModel = NotificationsViewModel()
|
||||
|
||||
|
||||
let lockedType: Models.Notification.NotificationType?
|
||||
|
||||
public init(lockedType: Models.Notification.NotificationType?) {
|
||||
|
@ -22,7 +22,7 @@ public struct NotificationsListView: View {
|
|||
ScrollView {
|
||||
LazyVStack {
|
||||
notificationsView
|
||||
.frame(maxWidth: .maxColumnWidth)
|
||||
.frame(maxWidth: .maxColumnWidth)
|
||||
}
|
||||
.padding(.top, .layoutPadding + 16)
|
||||
.background(theme.primaryBackgroundColor)
|
||||
|
|
|
@ -43,8 +43,8 @@ class NotificationsViewModel: ObservableObject {
|
|||
private var queryTypes: [String]? {
|
||||
if let selectedType {
|
||||
var excludedTypes = Models.Notification.NotificationType.allCases
|
||||
excludedTypes.removeAll(where: { $0 == selectedType})
|
||||
return excludedTypes.map{ $0.rawValue }
|
||||
excludedTypes.removeAll(where: { $0 == selectedType })
|
||||
return excludedTypes.map { $0.rawValue }
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ class NotificationsViewModel: ObservableObject {
|
|||
}
|
||||
|
||||
func handleEvent(event: any StreamEvent) {
|
||||
if let event = event as? StreamEventNotification,
|
||||
if let event = event as? StreamEventNotification,
|
||||
!notifications.contains(where: { $0.id == event.notification.id })
|
||||
{
|
||||
if let selectedType, event.notification.type == selectedType.rawValue {
|
||||
|
|
|
@ -16,7 +16,7 @@ class StatusDetailViewModel: ObservableObject {
|
|||
|
||||
@Published var state: State = .loading
|
||||
@Published var title: LocalizedStringKey = ""
|
||||
|
||||
|
||||
init(statusId: String) {
|
||||
state = .loading
|
||||
self.statusId = statusId
|
||||
|
@ -59,7 +59,7 @@ class StatusDetailViewModel: ObservableObject {
|
|||
let status: Status
|
||||
let context: StatusContext
|
||||
}
|
||||
|
||||
|
||||
private func fetchStatusDetail() async {
|
||||
guard let client, let statusId else { return }
|
||||
do {
|
||||
|
@ -70,7 +70,7 @@ class StatusDetailViewModel: ObservableObject {
|
|||
state = .error(error: error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func fetchContextData(client: Client, statusId: String) async throws -> ContextData {
|
||||
async let status: Status = client.get(endpoint: Statuses.status(id: statusId))
|
||||
async let context: StatusContext = client.get(endpoint: Statuses.context(id: statusId))
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import DesignSystem
|
||||
import Env
|
||||
import Models
|
||||
import NukeUI
|
||||
import PhotosUI
|
||||
import SwiftUI
|
||||
import NukeUI
|
||||
|
||||
struct StatusEditorAccessoryView: View {
|
||||
@EnvironmentObject private var preferences: UserPreferences
|
||||
|
@ -53,7 +53,7 @@ struct StatusEditorAccessoryView: View {
|
|||
Image(systemName: "archivebox")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if !viewModel.customEmojis.isEmpty {
|
||||
Button {
|
||||
isCustomEmojisSheetDisplay = true
|
||||
|
@ -167,7 +167,7 @@ struct StatusEditorAccessoryView: View {
|
|||
}
|
||||
.presentationDetents([.medium])
|
||||
}
|
||||
|
||||
|
||||
private var customEmojisSheet: some View {
|
||||
NavigationStack {
|
||||
ScrollView {
|
||||
|
|
|
@ -10,8 +10,8 @@ enum StatusEditorUTTypeSupported: String, CaseIterable {
|
|||
case image = "public.image"
|
||||
case jpeg = "public.jpeg"
|
||||
case png = "public.png"
|
||||
|
||||
static func types() -> [UTType] {
|
||||
|
||||
static func types() -> [UTType] {
|
||||
[.url, .text, .plainText, .image, .jpeg, .png]
|
||||
}
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ public struct StatusEditorView: View {
|
|||
NotificationCenter.default.post(name: NotificationsName.shareSheetClose,
|
||||
object: nil)
|
||||
}
|
||||
|
||||
|
||||
Task {
|
||||
await viewModel.fetchCustomEmojis()
|
||||
}
|
||||
|
|
|
@ -53,9 +53,9 @@ public class StatusEditorViewModel: ObservableObject {
|
|||
@Published var mediasImages: [ImageContainer] = []
|
||||
@Published var replyToStatus: Status?
|
||||
@Published var embeddedStatus: Status?
|
||||
|
||||
|
||||
@Published var customEmojis: [Emoji] = []
|
||||
|
||||
|
||||
var canPost: Bool {
|
||||
statusText.length > 0 || !mediasImages.isEmpty
|
||||
}
|
||||
|
@ -126,9 +126,8 @@ public class StatusEditorViewModel: ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Status Text manipulations
|
||||
|
||||
|
||||
func insertStatusText(text: String) {
|
||||
let string = statusText
|
||||
string.mutableString.insert(text, at: selectedRange.location)
|
||||
|
@ -195,7 +194,7 @@ public class StatusEditorViewModel: ObservableObject {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func processText() {
|
||||
statusText.addAttributes([.foregroundColor: UIColor(Color.label),
|
||||
.underlineColor: .clear],
|
||||
|
@ -245,7 +244,7 @@ public class StatusEditorViewModel: ObservableObject {
|
|||
range: NSRange(location: range.location, length: range.length))
|
||||
}
|
||||
|
||||
var mediaAdded: Bool = false
|
||||
var mediaAdded = false
|
||||
statusText.enumerateAttribute(.attachment, in: range) { attachment, range, _ in
|
||||
if let attachment = attachment as? NSTextAttachment, let image = attachment.image {
|
||||
mediasImages.append(.init(image: image, mediaAttachment: nil, error: nil))
|
||||
|
@ -254,7 +253,7 @@ public class StatusEditorViewModel: ObservableObject {
|
|||
mediaAdded = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if mediaAdded {
|
||||
processMediasToUpload()
|
||||
}
|
||||
|
@ -262,6 +261,7 @@ public class StatusEditorViewModel: ObservableObject {
|
|||
}
|
||||
|
||||
// MARK: - Shar sheet / Item provider
|
||||
|
||||
private func processItemsProvider(items: [NSItemProvider]) {
|
||||
Task {
|
||||
var initialText: String = ""
|
||||
|
@ -290,21 +290,20 @@ public class StatusEditorViewModel: ObservableObject {
|
|||
}
|
||||
|
||||
// MARK: - Polls
|
||||
|
||||
|
||||
func resetPollDefaults() {
|
||||
pollOptions = ["", ""]
|
||||
pollDuration = .oneDay
|
||||
pollVotingFrequency = .oneVote
|
||||
}
|
||||
|
||||
|
||||
private func getPollOptionsForAPI() -> [String]? {
|
||||
let options = pollOptions.filter { !$0.trimmingCharacters(in: .whitespaces).isEmpty }
|
||||
return options.isEmpty ? nil : options
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Embeds
|
||||
|
||||
|
||||
private func checkEmbed() {
|
||||
if let url = embeddedStatusURL,
|
||||
!statusText.string.contains(url.absoluteString)
|
||||
|
@ -447,7 +446,7 @@ public class StatusEditorViewModel: ObservableObject {
|
|||
if let index = indexOf(container: container) {
|
||||
do {
|
||||
let media: MediaAttachment = try await client.put(endpoint: Media.media(id: attachment.id,
|
||||
description: description))
|
||||
description: description))
|
||||
mediasImages[index] = .init(image: nil, mediaAttachment: media, error: nil)
|
||||
} catch {}
|
||||
}
|
||||
|
@ -462,13 +461,14 @@ public class StatusEditorViewModel: ObservableObject {
|
|||
filename: "file",
|
||||
data: data)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Custom emojis
|
||||
|
||||
func fetchCustomEmojis() async {
|
||||
guard let client else { return }
|
||||
do {
|
||||
customEmojis = try await client.get(endpoint: CustomEmojis.customEmojis) ?? []
|
||||
} catch { }
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ public extension StatusEditorViewModel {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var title: LocalizedStringKey {
|
||||
switch self {
|
||||
case .new, .mention, .shareExtension:
|
||||
|
|
|
@ -1,35 +1,35 @@
|
|||
import SwiftUI
|
||||
import DesignSystem
|
||||
import Models
|
||||
import Network
|
||||
import DesignSystem
|
||||
import SwiftUI
|
||||
|
||||
public struct StatusEditHistoryView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
|
||||
@EnvironmentObject private var client: Client
|
||||
@EnvironmentObject private var theme: Theme
|
||||
|
||||
|
||||
private let statusId: String
|
||||
|
||||
|
||||
@State private var history: [StatusHistory]?
|
||||
|
||||
|
||||
public init(statusId: String) {
|
||||
self.statusId = statusId
|
||||
}
|
||||
|
||||
|
||||
public var body: some View {
|
||||
NavigationStack {
|
||||
List {
|
||||
Section {
|
||||
if let history {
|
||||
ForEach(history) { edit in
|
||||
VStack(alignment: .leading, spacing: 8){
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
EmojiTextApp(edit.content, emojis: edit.emojis)
|
||||
.font(.scaledBody)
|
||||
Group {
|
||||
Text(edit.createdAt.asDate, style: .date) +
|
||||
Text("status.summary.at-time") +
|
||||
Text(edit.createdAt.asDate, style: .time)
|
||||
Text("status.summary.at-time") +
|
||||
Text(edit.createdAt.asDate, style: .time)
|
||||
}
|
||||
.font(.footnote)
|
||||
.foregroundColor(.gray)
|
||||
|
|
|
@ -20,11 +20,11 @@ class VideoPlayerViewModel: ObservableObject {
|
|||
self?.player?.play()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func pause() {
|
||||
player?.pause()
|
||||
}
|
||||
|
||||
|
||||
func play() {
|
||||
player?.play()
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ class VideoPlayerViewModel: ObservableObject {
|
|||
struct VideoPlayerView: View {
|
||||
@Environment(\.scenePhase) private var scenePhase
|
||||
@StateObject var viewModel: VideoPlayerViewModel
|
||||
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
VideoPlayer(player: viewModel.player)
|
||||
|
|
|
@ -10,7 +10,7 @@ public struct StatusPollView: View {
|
|||
@EnvironmentObject private var currentInstance: CurrentInstance
|
||||
@EnvironmentObject private var currentAccount: CurrentAccount
|
||||
@StateObject private var viewModel: StatusPollViewModel
|
||||
|
||||
|
||||
private var status: AnyStatus
|
||||
|
||||
public init(poll: Poll, status: AnyStatus) {
|
||||
|
|
|
@ -101,9 +101,9 @@ struct StatusActionsView: View {
|
|||
Divider()
|
||||
HStack {
|
||||
Text(viewModel.status.createdAt.asDate, style: .date) +
|
||||
Text("status.summary.at-time") +
|
||||
Text(viewModel.status.createdAt.asDate, style: .time) +
|
||||
Text(" ·")
|
||||
Text("status.summary.at-time") +
|
||||
Text(viewModel.status.createdAt.asDate, style: .time) +
|
||||
Text(" ·")
|
||||
Image(systemName: viewModel.status.visibility.iconName)
|
||||
Spacer()
|
||||
Text(viewModel.status.application?.name ?? "")
|
||||
|
@ -116,14 +116,14 @@ struct StatusActionsView: View {
|
|||
}
|
||||
.font(.scaledCaption)
|
||||
.foregroundColor(.gray)
|
||||
|
||||
|
||||
if let editedAt = viewModel.status.editedAt {
|
||||
Divider()
|
||||
HStack {
|
||||
Text("status.summary.edited-time") +
|
||||
Text(editedAt.asDate, style: .date) +
|
||||
Text("status.summary.at-time") +
|
||||
Text(editedAt.asDate, style: .time)
|
||||
Text(editedAt.asDate, style: .date) +
|
||||
Text("status.summary.at-time") +
|
||||
Text(editedAt.asDate, style: .time)
|
||||
Spacer()
|
||||
}
|
||||
.onTapGesture {
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import DesignSystem
|
||||
import Env
|
||||
import Models
|
||||
import Nuke
|
||||
import NukeUI
|
||||
import Shimmer
|
||||
import SwiftUI
|
||||
import Nuke
|
||||
|
||||
public struct StatusMediaPreviewView: View {
|
||||
@Environment(\.openURL) private var openURL
|
||||
|
||||
|
||||
@EnvironmentObject private var preferences: UserPreferences
|
||||
@EnvironmentObject private var quickLook: QuickLook
|
||||
@EnvironmentObject private var theme: Theme
|
||||
|
@ -146,7 +146,7 @@ public struct StatusMediaPreviewView: View {
|
|||
let availableWidth = UIScreen.main.bounds.width - (.layoutPadding * 2) - avatarColumnWidth
|
||||
let newSize = imageSize(from: size,
|
||||
newWidth: availableWidth)
|
||||
|
||||
|
||||
LazyImage(url: attachment.url) { state in
|
||||
if let image = state.image {
|
||||
image
|
||||
|
@ -175,7 +175,6 @@ public struct StatusMediaPreviewView: View {
|
|||
.fill(Color.gray)
|
||||
.frame(maxHeight: isNotifications || theme.statusDisplayStyle == .compact ? imageMaxHeight : nil)
|
||||
.shimmering()
|
||||
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -197,7 +196,6 @@ public struct StatusMediaPreviewView: View {
|
|||
isAltAlertDisplayed = true
|
||||
} label: {
|
||||
Text("ALT")
|
||||
|
||||
}
|
||||
.padding(8)
|
||||
.background(.thinMaterial)
|
||||
|
@ -298,7 +296,6 @@ public struct StatusMediaPreviewView: View {
|
|||
Label("status.media.sensitive.show", systemImage: "eye")
|
||||
} else {
|
||||
Label("status.media.content.show", systemImage: "eye")
|
||||
|
||||
}
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
|
@ -307,21 +304,21 @@ public struct StatusMediaPreviewView: View {
|
|||
}
|
||||
|
||||
private var cornerSensitiveButton: some View {
|
||||
HStack{
|
||||
HStack {
|
||||
Button {
|
||||
withAnimation {
|
||||
isHidingMedia = true
|
||||
}
|
||||
} label: {
|
||||
Image(systemName: "eye.slash")
|
||||
.frame(minHeight:21) // Match the alt button in case it is also present
|
||||
.frame(minHeight: 21) // Match the alt button in case it is also present
|
||||
}
|
||||
.padding(10)
|
||||
.buttonStyle(.borderedProminent)
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ViewBuilder
|
||||
private func contextMenuForMedia(mediaAttachement: MediaAttachment) -> some View {
|
||||
if let url = mediaAttachement.url {
|
||||
|
@ -337,7 +334,7 @@ public struct StatusMediaPreviewView: View {
|
|||
do {
|
||||
let image = try await ImagePipeline.shared.image(for: url).image
|
||||
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
|
||||
} catch { }
|
||||
} catch {}
|
||||
}
|
||||
} label: {
|
||||
Label("status.media.contextmenu.save", systemImage: "square.and.arrow.down")
|
||||
|
@ -347,7 +344,7 @@ public struct StatusMediaPreviewView: View {
|
|||
do {
|
||||
let image = try await ImagePipeline.shared.image(for: url).image
|
||||
UIPasteboard.general.image = image
|
||||
} catch { }
|
||||
} catch {}
|
||||
}
|
||||
} label: {
|
||||
Label("status.media.contextmenu.copy", systemImage: "doc.on.doc")
|
||||
|
|
|
@ -67,31 +67,31 @@ public struct StatusRowView: View {
|
|||
viewModel.displaySpoiler = false
|
||||
}
|
||||
}
|
||||
.contextMenu {
|
||||
StatusRowContextMenu(viewModel: viewModel)
|
||||
}
|
||||
.accessibilityElement(children: viewModel.isFocused ? .contain : .combine)
|
||||
.accessibilityActions {
|
||||
// Add the individual mentions as accessibility actions
|
||||
ForEach(viewModel.status.mentions, id: \.id) { mention in
|
||||
Button("@\(mention.username)") {
|
||||
routerPath.navigate(to: .accountDetail(id: mention.id))
|
||||
}
|
||||
}
|
||||
|
||||
Button(viewModel.displaySpoiler ? "status.show-more" : "status.show-less") {
|
||||
withAnimation {
|
||||
viewModel.displaySpoiler.toggle()
|
||||
}
|
||||
}
|
||||
|
||||
Button("@\(viewModel.status.account.username)") {
|
||||
routerPath.navigate(to: .accountDetail(id: viewModel.status.account.id))
|
||||
}
|
||||
|
||||
StatusRowContextMenu(viewModel: viewModel)
|
||||
}
|
||||
.background {
|
||||
.contextMenu {
|
||||
StatusRowContextMenu(viewModel: viewModel)
|
||||
}
|
||||
.accessibilityElement(children: viewModel.isFocused ? .contain : .combine)
|
||||
.accessibilityActions {
|
||||
// Add the individual mentions as accessibility actions
|
||||
ForEach(viewModel.status.mentions, id: \.id) { mention in
|
||||
Button("@\(mention.username)") {
|
||||
routerPath.navigate(to: .accountDetail(id: mention.id))
|
||||
}
|
||||
}
|
||||
|
||||
Button(viewModel.displaySpoiler ? "status.show-more" : "status.show-less") {
|
||||
withAnimation {
|
||||
viewModel.displaySpoiler.toggle()
|
||||
}
|
||||
}
|
||||
|
||||
Button("@\(viewModel.status.account.username)") {
|
||||
routerPath.navigate(to: .accountDetail(id: viewModel.status.account.id))
|
||||
}
|
||||
|
||||
StatusRowContextMenu(viewModel: viewModel)
|
||||
}
|
||||
.background {
|
||||
Color.clear
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
|
@ -125,15 +125,15 @@ public struct StatusRowView: View {
|
|||
Text("status.row.was-boosted")
|
||||
} else {
|
||||
Text("status.row.you-boosted")
|
||||
}
|
||||
}
|
||||
.accessibilityElement()
|
||||
.accessibilityLabel(
|
||||
Text("\(viewModel.status.account.safeDisplayName)")
|
||||
+ Text(" ")
|
||||
+ Text(viewModel.status.account.username != account.account?.username ? "status.row.was-boosted" : "status.row.you-boosted")
|
||||
)
|
||||
.font(.scaledFootnote)
|
||||
}
|
||||
}
|
||||
.accessibilityElement()
|
||||
.accessibilityLabel(
|
||||
Text("\(viewModel.status.account.safeDisplayName)")
|
||||
+ Text(" ")
|
||||
+ Text(viewModel.status.account.username != account.account?.username ? "status.row.was-boosted" : "status.row.you-boosted")
|
||||
)
|
||||
.font(.scaledFootnote)
|
||||
.foregroundColor(.gray)
|
||||
.fontWeight(.semibold)
|
||||
.onTapGesture {
|
||||
|
@ -192,22 +192,22 @@ public struct StatusRowView: View {
|
|||
.buttonStyle(.plain)
|
||||
Spacer()
|
||||
menuButton
|
||||
.accessibilityHidden(true)
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
.accessibilityElement()
|
||||
.accessibilityLabel(Text("\(status.account.displayName), \(status.createdAt.formatted)"))
|
||||
.accessibilityLabel(Text("\(status.account.displayName), \(status.createdAt.formatted)"))
|
||||
}
|
||||
makeStatusContentView(status: status)
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
viewModel.navigateToDetail(routerPath: routerPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
.accessibilityElement(children: viewModel.isFocused ? .contain : .combine)
|
||||
.accessibilityAction {
|
||||
viewModel.navigateToDetail(routerPath: routerPath)
|
||||
}
|
||||
viewModel.navigateToDetail(routerPath: routerPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
.accessibilityElement(children: viewModel.isFocused ? .contain : .combine)
|
||||
.accessibilityAction {
|
||||
viewModel.navigateToDetail(routerPath: routerPath)
|
||||
}
|
||||
}
|
||||
|
||||
private func makeStatusContentView(status: AnyStatus) -> some View {
|
||||
|
@ -225,7 +225,7 @@ public struct StatusRowView: View {
|
|||
.buttonStyle(.bordered)
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
|
||||
|
||||
if !viewModel.displaySpoiler {
|
||||
HStack {
|
||||
EmojiTextApp(status.content, emojis: status.emojis)
|
||||
|
@ -235,7 +235,7 @@ public struct StatusRowView: View {
|
|||
})
|
||||
Spacer()
|
||||
}
|
||||
|
||||
|
||||
makeTranslateView(status: status)
|
||||
|
||||
if let poll = status.poll {
|
||||
|
@ -243,7 +243,7 @@ public struct StatusRowView: View {
|
|||
}
|
||||
|
||||
makeMediasView(status: status)
|
||||
.accessibilityHidden(!viewModel.isFocused)
|
||||
.accessibilityHidden(!viewModel.isFocused)
|
||||
makeCardView(status: status)
|
||||
}
|
||||
}
|
||||
|
@ -282,14 +282,15 @@ public struct StatusRowView: View {
|
|||
.foregroundColor(.gray)
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
|
||||
|
||||
@ViewBuilder
|
||||
private func makeTranslateView(status: AnyStatus) -> some View {
|
||||
if let userLang = preferences.serverPreferences?.postLanguage,
|
||||
status.language != nil,
|
||||
userLang != status.language,
|
||||
!status.content.asRawText.isEmpty,
|
||||
viewModel.translation == nil {
|
||||
viewModel.translation == nil
|
||||
{
|
||||
Button {
|
||||
Task {
|
||||
await viewModel.translate(userLang: userLang)
|
||||
|
@ -314,7 +315,7 @@ public struct StatusRowView: View {
|
|||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ViewBuilder
|
||||
private func makeMediasView(status: AnyStatus) -> some View {
|
||||
if !status.mediaAttachments.isEmpty {
|
||||
|
@ -334,7 +335,7 @@ public struct StatusRowView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ViewBuilder
|
||||
private func makeCardView(status: AnyStatus) -> some View {
|
||||
if let card = status.card,
|
||||
|
|
|
@ -22,7 +22,7 @@ public class StatusRowViewModel: ObservableObject {
|
|||
@Published var displaySpoiler: Bool = false
|
||||
@Published var isEmbedLoading: Bool = true
|
||||
@Published var isFiltered: Bool = false
|
||||
|
||||
|
||||
@Published var translation: String?
|
||||
@Published var isLoadingTranslation: Bool = false
|
||||
|
||||
|
@ -223,7 +223,7 @@ public class StatusRowViewModel: ObservableObject {
|
|||
reblogsCount = status.reblog?.reblogsCount ?? status.reblogsCount
|
||||
repliesCount = status.reblog?.repliesCount ?? status.repliesCount
|
||||
}
|
||||
|
||||
|
||||
func translate(userLang: String) async {
|
||||
let client = DeepLClient()
|
||||
do {
|
||||
|
|
|
@ -38,7 +38,7 @@ public enum TimelineFilter: Hashable, Equatable {
|
|||
return server
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func localizedTitle() -> LocalizedStringKey {
|
||||
switch self {
|
||||
case .federated:
|
||||
|
@ -57,7 +57,7 @@ public enum TimelineFilter: Hashable, Equatable {
|
|||
return LocalizedStringKey(server)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func iconName() -> String? {
|
||||
switch self {
|
||||
case .federated:
|
||||
|
|
Loading…
Reference in a new issue