mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-10-31 22:28:58 +00:00
Swiftformat
This commit is contained in:
parent
96344e2815
commit
7f6419ebae
161 changed files with 1777 additions and 1746 deletions
1
.swiftformat
Normal file
1
.swiftformat
Normal file
|
@ -0,0 +1 @@
|
|||
--indent 2
|
|
@ -644,7 +644,7 @@
|
|||
CODE_SIGN_IDENTITY = "-";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
CURRENT_PROJECT_VERSION = 730;
|
||||
DEVELOPMENT_TEAM = Z6P74P6T99;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = IceCubesShareExtension/Info.plist;
|
||||
|
@ -674,7 +674,7 @@
|
|||
CODE_SIGN_IDENTITY = "-";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
CURRENT_PROJECT_VERSION = 730;
|
||||
DEVELOPMENT_TEAM = Z6P74P6T99;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = IceCubesShareExtension/Info.plist;
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import Account
|
||||
import AppAccount
|
||||
import DesignSystem
|
||||
import Env
|
||||
import Lists
|
||||
import Status
|
||||
import SwiftUI
|
||||
import Timeline
|
||||
import Account
|
||||
import Env
|
||||
import Status
|
||||
import DesignSystem
|
||||
import Lists
|
||||
import AppAccount
|
||||
|
||||
@MainActor
|
||||
extension View {
|
||||
func withAppRouteur() -> some View {
|
||||
self.navigationDestination(for: RouteurDestinations.self) { destination in
|
||||
navigationDestination(for: RouteurDestinations.self) { destination in
|
||||
switch destination {
|
||||
case let .accountDetail(id):
|
||||
AccountDetailView(accountId: id)
|
||||
|
@ -37,7 +37,7 @@ extension View {
|
|||
}
|
||||
|
||||
func withSheetDestinations(sheetDestinations: Binding<SheetDestinations?>) -> some View {
|
||||
self.sheet(item: sheetDestinations) { destination in
|
||||
sheet(item: sheetDestinations) { destination in
|
||||
switch destination {
|
||||
case let .replyToStatusEditor(status):
|
||||
StatusEditorView(mode: .replyTo(status: status))
|
||||
|
@ -71,8 +71,7 @@ extension View {
|
|||
}
|
||||
|
||||
func withEnvironments() -> some View {
|
||||
self
|
||||
.environmentObject(CurrentAccount.shared)
|
||||
environmentObject(CurrentAccount.shared)
|
||||
.environmentObject(UserPreferences.shared)
|
||||
.environmentObject(CurrentInstance.shared)
|
||||
.environmentObject(Theme.shared)
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import SwiftUI
|
||||
import AVFoundation
|
||||
import Timeline
|
||||
import Network
|
||||
import KeychainSwift
|
||||
import Env
|
||||
import DesignSystem
|
||||
import RevenueCat
|
||||
import AppAccount
|
||||
import Account
|
||||
import AppAccount
|
||||
import AVFoundation
|
||||
import DesignSystem
|
||||
import Env
|
||||
import KeychainSwift
|
||||
import Network
|
||||
import RevenueCat
|
||||
import SwiftUI
|
||||
import Timeline
|
||||
|
||||
@main
|
||||
struct IceCubesApp: App {
|
||||
|
@ -34,25 +34,25 @@ struct IceCubesApp: App {
|
|||
var body: some Scene {
|
||||
WindowGroup {
|
||||
appView
|
||||
.applyTheme(theme)
|
||||
.onAppear {
|
||||
setNewClientsInEnv(client: appAccountsManager.currentClient)
|
||||
setupRevenueCat()
|
||||
refreshPushSubs()
|
||||
}
|
||||
.environmentObject(appAccountsManager)
|
||||
.environmentObject(appAccountsManager.currentClient)
|
||||
.environmentObject(quickLook)
|
||||
.environmentObject(currentAccount)
|
||||
.environmentObject(currentInstance)
|
||||
.environmentObject(userPreferences)
|
||||
.environmentObject(theme)
|
||||
.environmentObject(watcher)
|
||||
.environmentObject(PushNotificationsService.shared)
|
||||
.sheet(item: $quickLook.url, content: { url in
|
||||
QuickLookPreview(selectedURL: url, urls: quickLook.urls)
|
||||
.edgesIgnoringSafeArea(.bottom)
|
||||
})
|
||||
.applyTheme(theme)
|
||||
.onAppear {
|
||||
setNewClientsInEnv(client: appAccountsManager.currentClient)
|
||||
setupRevenueCat()
|
||||
refreshPushSubs()
|
||||
}
|
||||
.environmentObject(appAccountsManager)
|
||||
.environmentObject(appAccountsManager.currentClient)
|
||||
.environmentObject(quickLook)
|
||||
.environmentObject(currentAccount)
|
||||
.environmentObject(currentInstance)
|
||||
.environmentObject(userPreferences)
|
||||
.environmentObject(theme)
|
||||
.environmentObject(watcher)
|
||||
.environmentObject(PushNotificationsService.shared)
|
||||
.sheet(item: $quickLook.url, content: { url in
|
||||
QuickLookPreview(selectedURL: url, urls: quickLook.urls)
|
||||
.edgesIgnoringSafeArea(.bottom)
|
||||
})
|
||||
}
|
||||
.onChange(of: scenePhase) { scenePhase in
|
||||
handleScenePhase(scenePhase: scenePhase)
|
||||
|
@ -168,14 +168,16 @@ struct IceCubesApp: App {
|
|||
}
|
||||
|
||||
class AppDelegate: NSObject, UIApplicationDelegate {
|
||||
func application(_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
|
||||
func application(_: UIApplication,
|
||||
didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool
|
||||
{
|
||||
try? AVAudioSession.sharedInstance().setCategory(.ambient, options: .mixWithOthers)
|
||||
return true
|
||||
}
|
||||
|
||||
func application(_ application: UIApplication,
|
||||
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
|
||||
func application(_: UIApplication,
|
||||
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)
|
||||
{
|
||||
PushNotificationsService.shared.pushToken = deviceToken
|
||||
Task {
|
||||
await PushNotificationsService.shared.fetchSubscriptions(accounts: AppAccountsManager.shared.pushAccounts)
|
||||
|
@ -183,6 +185,5 @@ class AppDelegate: NSObject, UIApplicationDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
|
||||
}
|
||||
func application(_: UIApplication, didFailToRegisterForRemoteNotificationsWithError _: Error) {}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import UIKit
|
||||
import SwiftUI
|
||||
import QuickLook
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
extension URL: Identifiable {
|
||||
public var id: String {
|
||||
|
@ -21,7 +21,8 @@ struct QuickLookPreview: UIViewControllerRepresentable {
|
|||
}
|
||||
|
||||
func updateUIViewController(
|
||||
_ uiViewController: UINavigationController, context: Context) {}
|
||||
_: UINavigationController, context _: Context
|
||||
) {}
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
return Coordinator(parent: self)
|
||||
|
@ -34,15 +35,15 @@ struct QuickLookPreview: UIViewControllerRepresentable {
|
|||
self.parent = parent
|
||||
}
|
||||
|
||||
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
|
||||
func numberOfPreviewItems(in _: QLPreviewController) -> Int {
|
||||
return parent.urls.count
|
||||
}
|
||||
|
||||
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
|
||||
func previewController(_: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
|
||||
return parent.urls[index] as QLPreviewItem
|
||||
}
|
||||
|
||||
func previewController(_ controller: QLPreviewController, editingModeFor previewItem: QLPreviewItem) -> QLPreviewItemEditingMode {
|
||||
func previewController(_: QLPreviewController, editingModeFor _: QLPreviewItem) -> QLPreviewItemEditingMode {
|
||||
.createCopy
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,85 +1,85 @@
|
|||
import SwiftUI
|
||||
import SafariServices
|
||||
import Env
|
||||
import DesignSystem
|
||||
import Env
|
||||
import SafariServices
|
||||
import SwiftUI
|
||||
|
||||
extension View {
|
||||
func withSafariRouteur() -> some View {
|
||||
modifier(SafariRouteur())
|
||||
}
|
||||
func withSafariRouteur() -> some View {
|
||||
modifier(SafariRouteur())
|
||||
}
|
||||
}
|
||||
|
||||
private struct SafariRouteur: ViewModifier {
|
||||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var preferences: UserPreferences
|
||||
@EnvironmentObject private var routeurPath: RouterPath
|
||||
@EnvironmentObject private var theme: Theme
|
||||
@EnvironmentObject private var preferences: UserPreferences
|
||||
@EnvironmentObject private var routeurPath: RouterPath
|
||||
|
||||
@State private var safari: SFSafariViewController?
|
||||
@State private var safari: SFSafariViewController?
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.environment(\.openURL, OpenURLAction { url in
|
||||
routeurPath.handle(url: url)
|
||||
})
|
||||
.onAppear {
|
||||
routeurPath.urlHandler = { url in
|
||||
guard preferences.preferredBrowser == .inAppSafari else { return .systemAction }
|
||||
// SFSafariViewController only supports initial URLs with http:// or https:// schemes.
|
||||
guard let scheme = url.scheme, ["https", "http"].contains(scheme.lowercased()) else {
|
||||
return .systemAction
|
||||
}
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.environment(\.openURL, OpenURLAction { url in
|
||||
routeurPath.handle(url: url)
|
||||
})
|
||||
.onAppear {
|
||||
routeurPath.urlHandler = { url in
|
||||
guard preferences.preferredBrowser == .inAppSafari else { return .systemAction }
|
||||
// SFSafariViewController only supports initial URLs with http:// or https:// schemes.
|
||||
guard let scheme = url.scheme, ["https", "http"].contains(scheme.lowercased()) else {
|
||||
return .systemAction
|
||||
}
|
||||
|
||||
let safari = SFSafariViewController(url: url)
|
||||
safari.preferredBarTintColor = UIColor(theme.primaryBackgroundColor)
|
||||
safari.preferredControlTintColor = UIColor(theme.tintColor)
|
||||
let safari = SFSafariViewController(url: url)
|
||||
safari.preferredBarTintColor = UIColor(theme.primaryBackgroundColor)
|
||||
safari.preferredControlTintColor = UIColor(theme.tintColor)
|
||||
|
||||
self.safari = safari
|
||||
return .handled
|
||||
}
|
||||
}
|
||||
.background {
|
||||
SafariPresenter(safari: safari)
|
||||
}
|
||||
self.safari = safari
|
||||
return .handled
|
||||
}
|
||||
}
|
||||
.background {
|
||||
SafariPresenter(safari: safari)
|
||||
}
|
||||
}
|
||||
|
||||
struct SafariPresenter: UIViewRepresentable {
|
||||
var safari: SFSafariViewController?
|
||||
|
||||
func makeUIView(context _: Context) -> UIView {
|
||||
let view = UIView(frame: .zero)
|
||||
view.isHidden = true
|
||||
view.isUserInteractionEnabled = false
|
||||
return view
|
||||
}
|
||||
|
||||
struct SafariPresenter: UIViewRepresentable {
|
||||
var safari: SFSafariViewController?
|
||||
|
||||
func makeUIView(context: Context) -> UIView {
|
||||
let view = UIView(frame: .zero)
|
||||
view.isHidden = true
|
||||
view.isUserInteractionEnabled = false
|
||||
return view
|
||||
}
|
||||
|
||||
func updateUIView(_ uiView: UIView, context: Context) {
|
||||
guard let safari = safari, let viewController = uiView.findTopViewController() else { return }
|
||||
viewController.present(safari, animated: true)
|
||||
}
|
||||
func updateUIView(_ uiView: UIView, context _: Context) {
|
||||
guard let safari = safari, let viewController = uiView.findTopViewController() else { return }
|
||||
viewController.present(safari, animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension UIView {
|
||||
func findTopViewController() -> UIViewController? {
|
||||
if let nextResponder = self.next as? UIViewController {
|
||||
return nextResponder.topViewController()
|
||||
} else if let nextResponder = self.next as? UIView {
|
||||
return nextResponder.findTopViewController()
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
func findTopViewController() -> UIViewController? {
|
||||
if let nextResponder = next as? UIViewController {
|
||||
return nextResponder.topViewController()
|
||||
} else if let nextResponder = next as? UIView {
|
||||
return nextResponder.findTopViewController()
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension UIViewController {
|
||||
func topViewController() -> UIViewController? {
|
||||
if let nvc = self as? UINavigationController {
|
||||
return nvc.visibleViewController?.topViewController()
|
||||
} else if let tbc = self as? UITabBarController, let selected = tbc.selectedViewController {
|
||||
return selected.topViewController()
|
||||
} else if let presented = self.presentedViewController {
|
||||
return presented.topViewController()
|
||||
}
|
||||
return self
|
||||
func topViewController() -> UIViewController? {
|
||||
if let nvc = self as? UINavigationController {
|
||||
return nvc.visibleViewController?.topViewController()
|
||||
} else if let tbc = self as? UITabBarController, let selected = tbc.selectedViewController {
|
||||
return selected.topViewController()
|
||||
} else if let presented = presentedViewController {
|
||||
return presented.topViewController()
|
||||
}
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import SwiftUI
|
||||
import Env
|
||||
import Account
|
||||
import DesignSystem
|
||||
import AppAccount
|
||||
import DesignSystem
|
||||
import Env
|
||||
import SwiftUI
|
||||
|
||||
struct SideBarView<Content: View>: View {
|
||||
@EnvironmentObject private var currentAccount: CurrentAccount
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import SwiftUI
|
||||
import Env
|
||||
import Models
|
||||
import Shimmer
|
||||
import Explore
|
||||
import Env
|
||||
import Network
|
||||
import AppAccount
|
||||
import Env
|
||||
import Explore
|
||||
import Models
|
||||
import Network
|
||||
import Shimmer
|
||||
import SwiftUI
|
||||
|
||||
struct ExploreTab: View {
|
||||
@EnvironmentObject private var preferences: UserPreferences
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import SwiftUI
|
||||
import Env
|
||||
import Network
|
||||
import Account
|
||||
import Models
|
||||
import Shimmer
|
||||
import AppAccount
|
||||
import Conversations
|
||||
import Env
|
||||
import AppAccount
|
||||
import Models
|
||||
import Network
|
||||
import Shimmer
|
||||
import SwiftUI
|
||||
|
||||
struct MessagesTab: View {
|
||||
@EnvironmentObject private var watcher: StreamWatcher
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import SwiftUI
|
||||
import Timeline
|
||||
import AppAccount
|
||||
import Env
|
||||
import Network
|
||||
import Notifications
|
||||
import AppAccount
|
||||
import SwiftUI
|
||||
import Timeline
|
||||
|
||||
struct NotificationsTab: View {
|
||||
@EnvironmentObject private var client: Client
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import SwiftUI
|
||||
import Network
|
||||
import Models
|
||||
import Env
|
||||
import DesignSystem
|
||||
import NukeUI
|
||||
import Shimmer
|
||||
import AppAccount
|
||||
import Combine
|
||||
import DesignSystem
|
||||
import Env
|
||||
import Models
|
||||
import Network
|
||||
import NukeUI
|
||||
import Shimmer
|
||||
import SwiftUI
|
||||
|
||||
struct AddAccountView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
@ -133,7 +133,7 @@ struct AddAccountView: View {
|
|||
if instances.isEmpty {
|
||||
placeholderRow
|
||||
} else {
|
||||
ForEach(instanceName.isEmpty ? instances : instances.filter{ $0.name.contains(instanceName.lowercased()) }) { instance in
|
||||
ForEach(instanceName.isEmpty ? instances : instances.filter { $0.name.contains(instanceName.lowercased()) }) { instance in
|
||||
Button {
|
||||
self.instanceName = instance.name
|
||||
} label: {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import SwiftUI
|
||||
import Models
|
||||
import DesignSystem
|
||||
import Models
|
||||
import Status
|
||||
import SwiftUI
|
||||
|
||||
struct DisplaySettingsView: View {
|
||||
@EnvironmentObject private var theme: Theme
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import SwiftUI
|
||||
import DesignSystem
|
||||
import SwiftUI
|
||||
|
||||
struct IconSelectorView: View {
|
||||
enum Icon: Int, CaseIterable, Identifiable {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import SwiftUI
|
||||
import Models
|
||||
import DesignSystem
|
||||
import Models
|
||||
import NukeUI
|
||||
import SwiftUI
|
||||
|
||||
struct InstanceInfoView: View {
|
||||
@EnvironmentObject private var theme: Theme
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import SwiftUI
|
||||
import Models
|
||||
import DesignSystem
|
||||
import NukeUI
|
||||
import Network
|
||||
import UserNotifications
|
||||
import Env
|
||||
import AppAccount
|
||||
import DesignSystem
|
||||
import Env
|
||||
import Models
|
||||
import Network
|
||||
import NukeUI
|
||||
import SwiftUI
|
||||
import UserNotifications
|
||||
|
||||
struct PushNotificationsView: View {
|
||||
@EnvironmentObject private var theme: Theme
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import Account
|
||||
import AppAccount
|
||||
import DesignSystem
|
||||
import Env
|
||||
import Models
|
||||
import Network
|
||||
import SwiftUI
|
||||
import Timeline
|
||||
import Env
|
||||
import Network
|
||||
import Account
|
||||
import Models
|
||||
import DesignSystem
|
||||
import AppAccount
|
||||
|
||||
struct SettingsTabs: View {
|
||||
@EnvironmentObject private var pushNotifications: PushNotificationsService
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import SwiftUI
|
||||
import Env
|
||||
import DesignSystem
|
||||
import Env
|
||||
import RevenueCat
|
||||
import Shimmer
|
||||
import SwiftUI
|
||||
|
||||
struct SupportAppView: View {
|
||||
enum Tips: String, CaseIterable {
|
||||
|
@ -133,7 +133,7 @@ struct SupportAppView: View {
|
|||
})
|
||||
.onAppear {
|
||||
loadingProducts = true
|
||||
Purchases.shared.getProducts(Tips.allCases.map{ $0.productId }) { products in
|
||||
Purchases.shared.getProducts(Tips.allCases.map { $0.productId }) { products in
|
||||
self.products = products.sorted(by: { $0.price < $1.price })
|
||||
withAnimation {
|
||||
loadingProducts = false
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import Foundation
|
||||
import Status
|
||||
import Account
|
||||
import Explore
|
||||
import Foundation
|
||||
import Status
|
||||
import SwiftUI
|
||||
|
||||
enum Tab: Int, Identifiable, Hashable {
|
||||
|
@ -96,4 +96,3 @@ enum Tab: Int, Identifiable, Hashable {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import SwiftUI
|
||||
import Network
|
||||
import Models
|
||||
import Env
|
||||
import Combine
|
||||
import DesignSystem
|
||||
import Env
|
||||
import Models
|
||||
import Network
|
||||
import NukeUI
|
||||
import Shimmer
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
struct AddRemoteTimelineView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
@ -83,7 +83,7 @@ struct AddRemoteTimelineView: View {
|
|||
ProgressView()
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
} else {
|
||||
ForEach(instanceName.isEmpty ? instances : instances.filter{ $0.name.contains(instanceName.lowercased()) }) { instance in
|
||||
ForEach(instanceName.isEmpty ? instances : instances.filter { $0.name.contains(instanceName.lowercased()) }) { instance in
|
||||
Button {
|
||||
self.instanceName = instance.name
|
||||
} label: {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import SwiftUI
|
||||
import Timeline
|
||||
import Env
|
||||
import Network
|
||||
import AppAccount
|
||||
import Combine
|
||||
import DesignSystem
|
||||
import Env
|
||||
import Models
|
||||
import AppAccount
|
||||
import Network
|
||||
import SwiftUI
|
||||
import Timeline
|
||||
|
||||
struct TimelineTab: View {
|
||||
@EnvironmentObject private var theme: Theme
|
||||
|
@ -72,7 +72,6 @@ struct TimelineTab: View {
|
|||
.environmentObject(routeurPath)
|
||||
}
|
||||
|
||||
|
||||
@ViewBuilder
|
||||
private var timelineFilterButton: some View {
|
||||
ForEach(TimelineFilter.availableTimeline(client: client), id: \.self) { timeline in
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import UserNotifications
|
||||
import KeychainSwift
|
||||
import Env
|
||||
import CryptoKit
|
||||
import Env
|
||||
import KeychainSwift
|
||||
import Models
|
||||
import UIKit
|
||||
import UserNotifications
|
||||
|
||||
@MainActor
|
||||
class NotificationService: UNNotificationServiceExtension {
|
||||
|
||||
var contentHandler: ((UNNotificationContent) -> Void)?
|
||||
var bestAttemptContent: UNMutableNotificationContent?
|
||||
|
||||
|
@ -20,20 +19,23 @@ class NotificationService: UNNotificationServiceExtension {
|
|||
let auth = PushNotificationsService.shared.notificationsAuthKeyAsKey
|
||||
|
||||
guard let encodedPayload = bestAttemptContent.userInfo["m"] as? String,
|
||||
let payload = Data(base64Encoded: encodedPayload.URLSafeBase64ToBase64()) else {
|
||||
let payload = Data(base64Encoded: encodedPayload.URLSafeBase64ToBase64())
|
||||
else {
|
||||
contentHandler(bestAttemptContent)
|
||||
return
|
||||
}
|
||||
|
||||
guard let encodedPublicKey = bestAttemptContent.userInfo["k"] as? String,
|
||||
let publicKeyData = Data(base64Encoded: encodedPublicKey.URLSafeBase64ToBase64()),
|
||||
let publicKey = try? P256.KeyAgreement.PublicKey(x963Representation: publicKeyData) else {
|
||||
let publicKey = try? P256.KeyAgreement.PublicKey(x963Representation: publicKeyData)
|
||||
else {
|
||||
contentHandler(bestAttemptContent)
|
||||
return
|
||||
}
|
||||
|
||||
guard let encodedSalt = bestAttemptContent.userInfo["s"] as? String,
|
||||
let salt = Data(base64Encoded: encodedSalt.URLSafeBase64ToBase64()) else {
|
||||
let salt = Data(base64Encoded: encodedSalt.URLSafeBase64ToBase64())
|
||||
else {
|
||||
contentHandler(bestAttemptContent)
|
||||
return
|
||||
}
|
||||
|
@ -43,7 +45,8 @@ class NotificationService: UNNotificationServiceExtension {
|
|||
auth: auth,
|
||||
privateKey: privateKey,
|
||||
publicKey: publicKey),
|
||||
let notification = try? JSONDecoder().decode(MastodonPushNotification.self, from: plaintextData) else {
|
||||
let notification = try? JSONDecoder().decode(MastodonPushNotification.self, from: plaintextData)
|
||||
else {
|
||||
contentHandler(bestAttemptContent)
|
||||
return
|
||||
}
|
||||
|
@ -52,7 +55,7 @@ class NotificationService: UNNotificationServiceExtension {
|
|||
bestAttemptContent.subtitle = ""
|
||||
bestAttemptContent.body = notification.body.escape()
|
||||
bestAttemptContent.userInfo["plaintext"] = plaintextData
|
||||
bestAttemptContent.sound = UNNotificationSound.init(named: UNNotificationSoundName(rawValue: "glass.wav"))
|
||||
bestAttemptContent.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: "glass.wav"))
|
||||
|
||||
let preferences = UserPreferences.shared
|
||||
preferences.pushNotificationsCount += 1
|
||||
|
@ -60,7 +63,8 @@ class NotificationService: UNNotificationServiceExtension {
|
|||
bestAttemptContent.badge = .init(integerLiteral: preferences.pushNotificationsCount)
|
||||
|
||||
if let urlString = notification.icon,
|
||||
let url = URL(string: urlString) {
|
||||
let url = URL(string: urlString)
|
||||
{
|
||||
let temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("notification-attachments")
|
||||
try? FileManager.default.createDirectory(at: temporaryDirectoryURL, withIntermediateDirectories: true, attributes: nil)
|
||||
let filename = url.lastPathComponent
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Foundation
|
||||
import CryptoKit
|
||||
import Foundation
|
||||
|
||||
extension NotificationService {
|
||||
static func decrypt(payload: Data, salt: Data, auth: Data, privateKey: P256.KeyAgreement.PrivateKey, publicKey: P256.KeyAgreement.PublicKey) -> Data? {
|
||||
|
@ -41,7 +41,7 @@ extension NotificationService {
|
|||
return Data(unpadded)
|
||||
}
|
||||
|
||||
static private func info(type: String, clientPublicKey: Data, serverPublicKey: Data) -> Data {
|
||||
private static func info(type: String, clientPublicKey: Data, serverPublicKey: Data) -> Data {
|
||||
var info = Data()
|
||||
|
||||
info.append("Content-Encoding: ".data(using: .utf8)!)
|
||||
|
@ -62,14 +62,12 @@ extension NotificationService {
|
|||
|
||||
extension String {
|
||||
func escape() -> String {
|
||||
return self
|
||||
.replacingOccurrences(of: "&", with: "&")
|
||||
return replacingOccurrences(of: "&", with: "&")
|
||||
.replacingOccurrences(of: "<", with: "<")
|
||||
.replacingOccurrences(of: ">", with: ">")
|
||||
.replacingOccurrences(of: """, with: "\"")
|
||||
.replacingOccurrences(of: "'", with: "'")
|
||||
.replacingOccurrences(of: "'", with: "’")
|
||||
|
||||
}
|
||||
|
||||
func URLSafeBase64ToBase64() -> String {
|
||||
|
@ -83,4 +81,3 @@ extension String {
|
|||
return base64
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,14 +8,14 @@
|
|||
<dict>
|
||||
<key>NSExtensionActivationRule</key>
|
||||
<dict>
|
||||
<key>NSExtensionActivationSupportsText</key>
|
||||
<true/>
|
||||
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
|
||||
<integer>1</integer>
|
||||
<key>NSExtensionActivationSupportsWebPageWithMaxCount</key>
|
||||
<integer>1</integer>
|
||||
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
|
||||
<integer>4</integer>
|
||||
<key>NSExtensionActivationSupportsText</key>
|
||||
<true/>
|
||||
<key>NSExtensionActivationSupportsWebPageWithMaxCount</key>
|
||||
<integer>1</integer>
|
||||
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>NSExtensionMainStoryboard</key>
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import Account
|
||||
import AppAccount
|
||||
import DesignSystem
|
||||
import Env
|
||||
import Network
|
||||
import Status
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
import Status
|
||||
import DesignSystem
|
||||
import Account
|
||||
import Network
|
||||
import Env
|
||||
import AppAccount
|
||||
|
||||
class ShareViewController: UIViewController {
|
||||
@IBOutlet var container: UIView!
|
||||
|
@ -35,9 +35,9 @@ class ShareViewController: UIViewController {
|
|||
.tint(theme.tintColor)
|
||||
.preferredColorScheme(colorScheme == .light ? .light : .dark)
|
||||
let childView = UIHostingController(rootView: view)
|
||||
self.addChild(childView)
|
||||
childView.view.frame = self.container.bounds
|
||||
self.container.addSubview(childView.view)
|
||||
addChild(childView)
|
||||
childView.view.frame = container.bounds
|
||||
container.addSubview(childView.view)
|
||||
childView.didMove(toParent: self)
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ class ShareViewController: UIViewController {
|
|||
NotificationCenter.default.addObserver(forName: NotificationsName.shareSheetClose,
|
||||
object: nil,
|
||||
queue: nil) { _ in
|
||||
self.close()
|
||||
self.close()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,8 @@ let package = Package(
|
|||
products: [
|
||||
.library(
|
||||
name: "Account",
|
||||
targets: ["Account"]),
|
||||
targets: ["Account"]
|
||||
),
|
||||
],
|
||||
dependencies: [
|
||||
.package(name: "Network", path: "../Network"),
|
||||
|
@ -25,9 +26,11 @@ let package = Package(
|
|||
.product(name: "Network", package: "Network"),
|
||||
.product(name: "Models", package: "Models"),
|
||||
.product(name: "Status", package: "Status"),
|
||||
]),
|
||||
]
|
||||
),
|
||||
.testTarget(
|
||||
name: "AccountTests",
|
||||
dependencies: ["Account"]),
|
||||
dependencies: ["Account"]
|
||||
),
|
||||
]
|
||||
)
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import SwiftUI
|
||||
import Models
|
||||
import DesignSystem
|
||||
import Env
|
||||
import Shimmer
|
||||
import NukeUI
|
||||
import EmojiText
|
||||
import Env
|
||||
import Models
|
||||
import NukeUI
|
||||
import Shimmer
|
||||
import SwiftUI
|
||||
|
||||
struct AccountDetailHeaderView: View {
|
||||
@EnvironmentObject private var theme: Theme
|
||||
|
@ -30,7 +30,7 @@ struct AccountDetailHeaderView: View {
|
|||
}
|
||||
|
||||
private var headerImageView: some View {
|
||||
GeometryReader { proxy in
|
||||
GeometryReader { _ in
|
||||
ZStack(alignment: .bottomTrailing) {
|
||||
if reasons.contains(.placeholder) {
|
||||
Rectangle()
|
||||
|
@ -79,11 +79,11 @@ struct AccountDetailHeaderView: View {
|
|||
private var accountAvatarView: some View {
|
||||
HStack {
|
||||
AvatarView(url: account.avatar, size: .account)
|
||||
.onTapGesture {
|
||||
Task {
|
||||
await quickLook.prepareFor(urls: [account.avatar], selectedURL: account.avatar)
|
||||
.onTapGesture {
|
||||
Task {
|
||||
await quickLook.prepareFor(urls: [account.avatar], selectedURL: account.avatar)
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
Group {
|
||||
Button {
|
||||
|
@ -109,10 +109,10 @@ struct AccountDetailHeaderView: View {
|
|||
HStack {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
EmojiTextApp(account.safeDisplayName.asMarkdown, emojis: account.emojis)
|
||||
.font(.headline)
|
||||
.font(.headline)
|
||||
Text("@\(account.acct)")
|
||||
.font(.callout)
|
||||
.foregroundColor(.gray)
|
||||
.font(.callout)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
Spacer()
|
||||
if let relationship = viewModel.relationship, !viewModel.isCurrentUser {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import SwiftUI
|
||||
import DesignSystem
|
||||
import EmojiText
|
||||
import Env
|
||||
import Models
|
||||
import Network
|
||||
import Status
|
||||
import Shimmer
|
||||
import DesignSystem
|
||||
import Env
|
||||
import EmojiText
|
||||
import Status
|
||||
import SwiftUI
|
||||
|
||||
public struct AccountDetailView: View {
|
||||
@Environment(\.redactionReasons) private var reasons
|
||||
|
@ -94,9 +94,10 @@ public struct AccountDetailView: View {
|
|||
await viewModel.fetchStatuses()
|
||||
}
|
||||
}
|
||||
.onChange(of: watcher.latestEvent?.id) { id in
|
||||
.onChange(of: watcher.latestEvent?.id) { _ in
|
||||
if let latestEvent = watcher.latestEvent,
|
||||
viewModel.accountId == currentAccount.account?.id {
|
||||
viewModel.accountId == currentAccount.account?.id
|
||||
{
|
||||
viewModel.handleEvent(event: latestEvent, currentAccount: currentAccount)
|
||||
}
|
||||
}
|
||||
|
@ -396,4 +397,3 @@ struct AccountDetailView_Previews: PreviewProvider {
|
|||
AccountDetailView(account: .placeholder())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import SwiftUI
|
||||
import Network
|
||||
import Models
|
||||
import Status
|
||||
import Env
|
||||
import Models
|
||||
import Network
|
||||
import Status
|
||||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
||||
|
@ -57,6 +57,7 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Published var statusesState: StatusesState = .loading
|
||||
|
||||
@Published var relationship: Relationshionship?
|
||||
|
@ -90,14 +91,14 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
|||
/// When coming from a URL like a mention tap in a status.
|
||||
init(accountId: String) {
|
||||
self.accountId = accountId
|
||||
self.isCurrentUser = false
|
||||
isCurrentUser = false
|
||||
}
|
||||
|
||||
/// When the account is already fetched by the parent caller.
|
||||
init(account: Account) {
|
||||
self.accountId = account.id
|
||||
accountId = account.id
|
||||
self.account = account
|
||||
self.accountState = .data(account: account)
|
||||
accountState = .data(account: account)
|
||||
}
|
||||
|
||||
struct AccountData {
|
||||
|
@ -151,20 +152,20 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
|||
do {
|
||||
tabState = .statuses(statusesState: .loading)
|
||||
statuses =
|
||||
try await client.get(endpoint: Accounts.statuses(id: accountId,
|
||||
sinceId: nil,
|
||||
tag: nil,
|
||||
onlyMedia: selectedTab == .media ? true : nil,
|
||||
excludeReplies: selectedTab == .statuses && !isCurrentUser ? true : nil,
|
||||
pinned: nil))
|
||||
if selectedTab == .statuses {
|
||||
pinned =
|
||||
try await client.get(endpoint: Accounts.statuses(id: accountId,
|
||||
sinceId: nil,
|
||||
tag: nil,
|
||||
onlyMedia: nil,
|
||||
excludeReplies: nil,
|
||||
pinned: true))
|
||||
onlyMedia: selectedTab == .media ? true : nil,
|
||||
excludeReplies: selectedTab == .statuses && !isCurrentUser ? true : nil,
|
||||
pinned: nil))
|
||||
if selectedTab == .statuses {
|
||||
pinned =
|
||||
try await client.get(endpoint: Accounts.statuses(id: accountId,
|
||||
sinceId: nil,
|
||||
tag: nil,
|
||||
onlyMedia: nil,
|
||||
excludeReplies: nil,
|
||||
pinned: true))
|
||||
}
|
||||
if isCurrentUser {
|
||||
(favourites, favouritesNextPage) = try await client.getWithLink(endpoint: Accounts.favourites(sinceId: nil))
|
||||
|
@ -184,12 +185,12 @@ class AccountDetailViewModel: ObservableObject, StatusesFetcher {
|
|||
guard let lastId = statuses.last?.id else { return }
|
||||
tabState = .statuses(statusesState: .display(statuses: statuses, nextPageState: .loadingNextPage))
|
||||
let newStatuses: [Status] =
|
||||
try await client.get(endpoint: Accounts.statuses(id: accountId,
|
||||
sinceId: lastId,
|
||||
tag: nil,
|
||||
onlyMedia: selectedTab == .media ? true : nil,
|
||||
excludeReplies: selectedTab == .statuses && !isCurrentUser ? true : nil,
|
||||
pinned: nil))
|
||||
try await client.get(endpoint: Accounts.statuses(id: accountId,
|
||||
sinceId: lastId,
|
||||
tag: nil,
|
||||
onlyMedia: selectedTab == .media ? true : nil,
|
||||
excludeReplies: selectedTab == .statuses && !isCurrentUser ? true : nil,
|
||||
pinned: nil))
|
||||
statuses.append(contentsOf: newStatuses)
|
||||
tabState = .statuses(statusesState: .display(statuses: statuses,
|
||||
nextPageState: newStatuses.count < 20 ? .none : .hasNextPage))
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import SwiftUI
|
||||
import DesignSystem
|
||||
import EmojiText
|
||||
import Env
|
||||
import Models
|
||||
import Network
|
||||
import DesignSystem
|
||||
import Env
|
||||
import EmojiText
|
||||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
public class AccountsListRowViewModel: ObservableObject {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import SwiftUI
|
||||
import Network
|
||||
import Models
|
||||
import Env
|
||||
import Shimmer
|
||||
import DesignSystem
|
||||
import Env
|
||||
import Models
|
||||
import Network
|
||||
import Shimmer
|
||||
import SwiftUI
|
||||
|
||||
public struct AccountsListView: View {
|
||||
@EnvironmentObject private var theme: Theme
|
||||
|
@ -19,7 +19,7 @@ public struct AccountsListView: View {
|
|||
List {
|
||||
switch viewModel.state {
|
||||
case .loading:
|
||||
ForEach(Account.placeholders()) { account in
|
||||
ForEach(Account.placeholders()) { _ in
|
||||
AccountsListRow(viewModel: .init(account: .placeholder(), relationShip: .placeholder()))
|
||||
.redacted(reason: .placeholder)
|
||||
.shimmering()
|
||||
|
@ -30,7 +30,7 @@ public struct AccountsListView: View {
|
|||
if let relationship = relationships.first(where: { $0.id == account.id }) {
|
||||
AccountsListRow(viewModel: .init(account: account,
|
||||
relationShip: relationship))
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,7 @@ public struct AccountsListView: View {
|
|||
.navigationBarTitleDisplayMode(.inline)
|
||||
.task {
|
||||
viewModel.client = client
|
||||
guard !didAppear else { return}
|
||||
guard !didAppear else { return }
|
||||
didAppear = true
|
||||
await viewModel.fetch()
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import SwiftUI
|
||||
import Models
|
||||
import Network
|
||||
import SwiftUI
|
||||
|
||||
public enum AccountsListMode {
|
||||
case following(accountId: String), followers(accountId: String)
|
||||
|
@ -30,6 +30,7 @@ class AccountsListViewModel: ObservableObject {
|
|||
public enum PagingState {
|
||||
case hasNextPage, loadingNextPage, none
|
||||
}
|
||||
|
||||
case loading
|
||||
case display(accounts: [Account],
|
||||
relationships: [Relationshionship],
|
||||
|
@ -69,11 +70,11 @@ class AccountsListViewModel: ObservableObject {
|
|||
}
|
||||
nextPageId = link?.maxId
|
||||
relationships = try await client.get(endpoint:
|
||||
Accounts.relationships(ids: accounts.map{ $0.id }))
|
||||
Accounts.relationships(ids: accounts.map { $0.id }))
|
||||
state = .display(accounts: accounts,
|
||||
relationships: relationships,
|
||||
nextPageState: link?.maxId != nil ? .hasNextPage : .none)
|
||||
} catch { }
|
||||
} catch {}
|
||||
}
|
||||
|
||||
func fetchNextPage() async {
|
||||
|
@ -93,12 +94,12 @@ class AccountsListViewModel: ObservableObject {
|
|||
(newAccounts, link) = try await client.getWithLink(endpoint: Statuses.rebloggedBy(id: statusId,
|
||||
maxId: nextPageId))
|
||||
case let .favouritedBy(statusId):
|
||||
(newAccounts, link) = try await client.getWithLink(endpoint: Statuses.favouritedBy(id: statusId,
|
||||
maxId: nextPageId))
|
||||
(newAccounts, link) = try await client.getWithLink(endpoint: Statuses.favouritedBy(id: statusId,
|
||||
maxId: nextPageId))
|
||||
}
|
||||
accounts.append(contentsOf: newAccounts)
|
||||
let newRelationships: [Relationshionship] =
|
||||
try await client.get(endpoint: Accounts.relationships(ids: newAccounts.map{ $0.id }))
|
||||
try await client.get(endpoint: Accounts.relationships(ids: newAccounts.map { $0.id }))
|
||||
|
||||
relationships.append(contentsOf: newRelationships)
|
||||
self.nextPageId = link?.maxId
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import SwiftUI
|
||||
import DesignSystem
|
||||
import Models
|
||||
import Network
|
||||
import DesignSystem
|
||||
import SwiftUI
|
||||
|
||||
struct EditAccountView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
@ -31,8 +31,8 @@ struct EditAccountView: View {
|
|||
.alert("Error while saving your profile",
|
||||
isPresented: $viewModel.saveError,
|
||||
actions: {
|
||||
Button("Ok", action: { })
|
||||
}, message: { Text("Error while saving your profile, please try again.") })
|
||||
Button("Ok", action: {})
|
||||
}, message: { Text("Error while saving your profile, please try again.") })
|
||||
.task {
|
||||
viewModel.client = client
|
||||
await viewModel.fetchAccount()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import SwiftUI
|
||||
import Models
|
||||
import Network
|
||||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
class EditAccountViewModel: ObservableObject {
|
||||
|
@ -18,7 +18,7 @@ class EditAccountViewModel: ObservableObject {
|
|||
@Published var isSaving: Bool = false
|
||||
@Published var saveError: Bool = false
|
||||
|
||||
init() { }
|
||||
init() {}
|
||||
|
||||
func fetchAccount() async {
|
||||
guard let client else { return }
|
||||
|
@ -34,20 +34,20 @@ class EditAccountViewModel: ObservableObject {
|
|||
withAnimation {
|
||||
isLoading = false
|
||||
}
|
||||
} catch { }
|
||||
} catch {}
|
||||
}
|
||||
|
||||
func save() async {
|
||||
isSaving = true
|
||||
do {
|
||||
let response =
|
||||
try await client?.patch(endpoint: Accounts.updateCredentials(displayName: displayName,
|
||||
note: note,
|
||||
privacy: postPrivacy,
|
||||
isSensitive: isSensitive,
|
||||
isBot: isBot,
|
||||
isLocked: isLocked,
|
||||
isDiscoverable: isDiscoverable))
|
||||
try await client?.patch(endpoint: Accounts.updateCredentials(displayName: displayName,
|
||||
note: note,
|
||||
privacy: postPrivacy,
|
||||
isSensitive: isSensitive,
|
||||
isBot: isBot,
|
||||
isLocked: isLocked,
|
||||
isDiscoverable: isDiscoverable))
|
||||
if response?.statusCode != 200 {
|
||||
saveError = true
|
||||
}
|
||||
|
@ -57,5 +57,4 @@ class EditAccountViewModel: ObservableObject {
|
|||
saveError = true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import Foundation
|
||||
import SwiftUI
|
||||
import Models
|
||||
import Network
|
||||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
public class FollowButtonViewModel: ObservableObject {
|
||||
|
@ -9,8 +9,8 @@ public class FollowButtonViewModel: ObservableObject {
|
|||
|
||||
public let accountId: String
|
||||
public let shouldDisplayNotify: Bool
|
||||
@Published private(set) public var relationship: Relationshionship
|
||||
@Published private(set) public var isUpdating: Bool = false
|
||||
@Published public private(set) var relationship: Relationshionship
|
||||
@Published public private(set) var isUpdating: Bool = false
|
||||
|
||||
public init(accountId: String, relationship: Relationshionship, shouldDisplayNotify: Bool) {
|
||||
self.accountId = accountId
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import XCTest
|
||||
@testable import Account
|
||||
import XCTest
|
||||
|
||||
final class AccountTests: XCTestCase {
|
||||
func testExample() throws {
|
||||
// This is an example of a functional test case.
|
||||
// Use XCTAssert and related functions to verify your tests produce the correct
|
||||
// results.
|
||||
XCTAssertEqual(Account().text, "Hello, World!")
|
||||
}
|
||||
func testExample() throws {
|
||||
// This is an example of a functional test case.
|
||||
// Use XCTAssert and related functions to verify your tests produce the correct
|
||||
// results.
|
||||
XCTAssertEqual(Account().text, "Hello, World!")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,8 @@ let package = Package(
|
|||
products: [
|
||||
.library(
|
||||
name: "AppAccount",
|
||||
targets: ["AppAccount"]),
|
||||
targets: ["AppAccount"]
|
||||
),
|
||||
],
|
||||
dependencies: [
|
||||
.package(name: "Network", path: "../Network"),
|
||||
|
@ -27,6 +28,7 @@ let package = Package(
|
|||
.product(name: "Models", package: "Models"),
|
||||
.product(name: "Env", package: "Env"),
|
||||
.product(name: "DesignSystem", package: "DesignSystem"),
|
||||
])
|
||||
]
|
||||
),
|
||||
]
|
||||
)
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import SwiftUI
|
||||
import Network
|
||||
import CryptoKit
|
||||
import KeychainSwift
|
||||
import Models
|
||||
import CryptoKit
|
||||
import Network
|
||||
import SwiftUI
|
||||
|
||||
public struct AppAccount: Codable, Identifiable {
|
||||
public let server: String
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import SwiftUI
|
||||
import DesignSystem
|
||||
import Env
|
||||
import EmojiText
|
||||
import Env
|
||||
import SwiftUI
|
||||
|
||||
public struct AppAccountView: View {
|
||||
@EnvironmentObject private var routeurPath: RouterPath
|
||||
|
@ -43,7 +43,8 @@ public struct AppAccountView: View {
|
|||
}
|
||||
.onTapGesture {
|
||||
if appAccounts.currentAccount.id == viewModel.appAccount.id,
|
||||
let account = viewModel.account {
|
||||
let account = viewModel.account
|
||||
{
|
||||
routeurPath.navigate(to: .accountDetailWithAccount(account: account))
|
||||
} else {
|
||||
appAccounts.currentAccount = viewModel.appAccount
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import SwiftUI
|
||||
import Models
|
||||
import Network
|
||||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
public class AppAccountViewModel: ObservableObject {
|
||||
|
@ -15,7 +15,7 @@ public class AppAccountViewModel: ObservableObject {
|
|||
|
||||
public init(appAccount: AppAccount) {
|
||||
self.appAccount = appAccount
|
||||
self.client = .init(server: appAccount.server, oauthToken: appAccount.oauthToken)
|
||||
client = .init(server: appAccount.server, oauthToken: appAccount.oauthToken)
|
||||
}
|
||||
|
||||
func fetchAccount() async {
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import SwiftUI
|
||||
import Network
|
||||
import Env
|
||||
import Models
|
||||
import Network
|
||||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
public class AppAccountsManager: ObservableObject {
|
||||
@AppStorage("latestCurrentAccountKey", store: UserPreferences.sharedDefault)
|
||||
static public var latestCurrentAccountKey: String = ""
|
||||
public static var latestCurrentAccountKey: String = ""
|
||||
|
||||
@Published public var currentAccount: AppAccount {
|
||||
didSet {
|
||||
|
@ -15,12 +15,13 @@ public class AppAccountsManager: ObservableObject {
|
|||
oauthToken: currentAccount.oauthToken)
|
||||
}
|
||||
}
|
||||
|
||||
@Published public var availableAccounts: [AppAccount]
|
||||
@Published public var currentClient: Client
|
||||
|
||||
public var pushAccounts: [PushNotificationsService.PushAccounts] {
|
||||
availableAccounts.filter{ $0.oauthToken != nil}
|
||||
.map{ .init(server: $0.server, token: $0.oauthToken!) }
|
||||
availableAccounts.filter { $0.oauthToken != nil }
|
||||
.map { .init(server: $0.server, token: $0.oauthToken!) }
|
||||
}
|
||||
|
||||
public static var shared = AppAccountsManager()
|
||||
|
@ -43,7 +44,7 @@ public class AppAccountsManager: ObservableObject {
|
|||
try account.save()
|
||||
availableAccounts.append(account)
|
||||
currentAccount = account
|
||||
} catch { }
|
||||
} catch {}
|
||||
}
|
||||
|
||||
public func delete(account: AppAccount) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import SwiftUI
|
||||
import Env
|
||||
import DesignSystem
|
||||
import Env
|
||||
import SwiftUI
|
||||
|
||||
public struct AppAccountsSelectorView: View {
|
||||
@EnvironmentObject private var currentAccount: CurrentAccount
|
||||
|
@ -15,7 +15,8 @@ public struct AppAccountsSelectorView: View {
|
|||
|
||||
public init(routeurPath: RouterPath,
|
||||
accountCreationEnabled: Bool = true,
|
||||
avatarSize: AvatarView.Size = .badge) {
|
||||
avatarSize: AvatarView.Size = .badge)
|
||||
{
|
||||
self.routeurPath = routeurPath
|
||||
self.accountCreationEnabled = accountCreationEnabled
|
||||
self.avatarSize = avatarSize
|
||||
|
@ -59,7 +60,8 @@ public struct AppAccountsSelectorView: View {
|
|||
Section(viewModel.acct) {
|
||||
Button {
|
||||
if let account = currentAccount.account,
|
||||
viewModel.account?.id == account.id {
|
||||
viewModel.account?.id == account.id
|
||||
{
|
||||
routeurPath.navigate(to: .accountDetailWithAccount(account: account))
|
||||
} else {
|
||||
appAccounts.currentAccount = viewModel.appAccount
|
||||
|
@ -98,5 +100,4 @@ public struct AppAccountsSelectorView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,7 +11,8 @@ let package = Package(
|
|||
products: [
|
||||
.library(
|
||||
name: "Conversations",
|
||||
targets: ["Conversations"]),
|
||||
targets: ["Conversations"]
|
||||
),
|
||||
],
|
||||
dependencies: [
|
||||
.package(name: "Models", path: "../Models"),
|
||||
|
@ -27,7 +28,7 @@ let package = Package(
|
|||
.product(name: "Network", package: "Network"),
|
||||
.product(name: "Env", package: "Env"),
|
||||
.product(name: "DesignSystem", package: "DesignSystem"),
|
||||
]),
|
||||
]
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import SwiftUI
|
||||
import Models
|
||||
import Accounts
|
||||
import DesignSystem
|
||||
import Env
|
||||
import Models
|
||||
import Network
|
||||
import SwiftUI
|
||||
|
||||
struct ConversationsListRow: View {
|
||||
@EnvironmentObject private var client: Client
|
||||
|
@ -19,7 +19,7 @@ struct ConversationsListRow: View {
|
|||
AvatarView(url: conversation.accounts.first!.avatar)
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
HStack {
|
||||
Text(conversation.accounts.map{ $0.safeDisplayName }.joined(separator: ", "))
|
||||
Text(conversation.accounts.map { $0.safeDisplayName }.joined(separator: ", "))
|
||||
.font(.headline)
|
||||
.foregroundColor(theme.labelColor)
|
||||
.multilineTextAlignment(.leading)
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import SwiftUI
|
||||
import Network
|
||||
import Models
|
||||
import DesignSystem
|
||||
import Shimmer
|
||||
import Env
|
||||
import Models
|
||||
import Network
|
||||
import Shimmer
|
||||
import SwiftUI
|
||||
|
||||
public struct ConversationsListView: View {
|
||||
@EnvironmentObject private var routeurPath: RouterPath
|
||||
|
@ -13,7 +13,7 @@ public struct ConversationsListView: View {
|
|||
|
||||
@StateObject private var viewModel = ConversationsListViewModel()
|
||||
|
||||
public init() { }
|
||||
public init() {}
|
||||
|
||||
private var conversations: [Conversation] {
|
||||
if viewModel.isLoadingFirstPage {
|
||||
|
@ -61,7 +61,7 @@ public struct ConversationsListView: View {
|
|||
.toolbar {
|
||||
StatusEditorToolbarItem(visibility: .direct)
|
||||
}
|
||||
.onChange(of: watcher.latestEvent?.id) { id in
|
||||
.onChange(of: watcher.latestEvent?.id) { _ in
|
||||
if let latestEvent = watcher.latestEvent {
|
||||
viewModel.handleEvent(event: latestEvent)
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ class ConversationsListViewModel: ObservableObject {
|
|||
@Published var conversations: [Conversation] = []
|
||||
@Published var isError: Bool = false
|
||||
|
||||
public init() { }
|
||||
public init() {}
|
||||
|
||||
func fetchConversations() async {
|
||||
guard let client else { return }
|
||||
|
|
|
@ -11,14 +11,15 @@ let package = Package(
|
|||
products: [
|
||||
.library(
|
||||
name: "DesignSystem",
|
||||
targets: ["DesignSystem"]),
|
||||
targets: ["DesignSystem"]
|
||||
),
|
||||
],
|
||||
dependencies: [
|
||||
.package(name: "Models", path: "../Models"),
|
||||
.package(name: "Env", path: "../Env"),
|
||||
.package(url: "https://github.com/markiv/SwiftUI-Shimmer", exact: "1.1.0"),
|
||||
.package(url: "https://github.com/kean/Nuke", from: "11.5.0"),
|
||||
.package(url: "https://github.com/divadretlaw/EmojiText", from: "1.1.0")
|
||||
.package(url: "https://github.com/divadretlaw/EmojiText", from: "1.1.0"),
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
|
@ -29,8 +30,8 @@ let package = Package(
|
|||
.product(name: "Shimmer", package: "SwiftUI-Shimmer"),
|
||||
.product(name: "NukeUI", package: "Nuke"),
|
||||
.product(name: "Nuke", package: "Nuke"),
|
||||
.product(name: "EmojiText", package: "EmojiText")
|
||||
]),
|
||||
.product(name: "EmojiText", package: "EmojiText"),
|
||||
]
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
import Foundation
|
||||
import SwiftUI
|
||||
import NukeUI
|
||||
import Models
|
||||
import NukeUI
|
||||
import SwiftUI
|
||||
|
||||
extension Account {
|
||||
public extension Account {
|
||||
private struct Part: Identifiable {
|
||||
let id = UUID().uuidString
|
||||
let value: Substring
|
||||
}
|
||||
|
||||
public var safeDisplayName: String {
|
||||
var safeDisplayName: String {
|
||||
if displayName.isEmpty {
|
||||
return username
|
||||
}
|
||||
return displayName
|
||||
}
|
||||
|
||||
public var displayNameWithoutEmojis: String {
|
||||
var displayNameWithoutEmojis: String {
|
||||
var name = safeDisplayName
|
||||
for emoji in emojis {
|
||||
name = name.replacingOccurrences(of: ":\(emoji.shortcode):", with: "")
|
||||
|
|
|
@ -24,9 +24,9 @@ public enum ColorSetName: String {
|
|||
public struct IceCubeDark: ColorSet {
|
||||
public var name: ColorSetName = .iceCubeDark
|
||||
public var scheme: ColorScheme = .dark
|
||||
public var tintColor: Color = Color(red: 187/255, green: 59/255, blue: 226/255)
|
||||
public var primaryBackgroundColor: Color = Color(red: 16/255, green: 21/255, blue: 35/255)
|
||||
public var secondaryBackgroundColor: Color = Color(red: 30/255, green: 35/255, blue: 62/255)
|
||||
public var tintColor: Color = .init(red: 187 / 255, green: 59 / 255, blue: 226 / 255)
|
||||
public var primaryBackgroundColor: Color = .init(red: 16 / 255, green: 21 / 255, blue: 35 / 255)
|
||||
public var secondaryBackgroundColor: Color = .init(red: 30 / 255, green: 35 / 255, blue: 62 / 255)
|
||||
public var labelColor: Color = .white
|
||||
|
||||
public init() {}
|
||||
|
@ -35,9 +35,9 @@ public struct IceCubeDark: ColorSet {
|
|||
public struct IceCubeLight: ColorSet {
|
||||
public var name: ColorSetName = .iceCubeLight
|
||||
public var scheme: ColorScheme = .light
|
||||
public var tintColor: Color = Color(red: 187/255, green: 59/255, blue: 226/255)
|
||||
public var tintColor: Color = .init(red: 187 / 255, green: 59 / 255, blue: 226 / 255)
|
||||
public var primaryBackgroundColor: Color = .white
|
||||
public var secondaryBackgroundColor: Color = Color(hex:0xF0F1F2)
|
||||
public var secondaryBackgroundColor: Color = .init(hex: 0xF0F1F2)
|
||||
public var labelColor: Color = .black
|
||||
|
||||
public init() {}
|
||||
|
@ -46,9 +46,9 @@ public struct IceCubeLight: ColorSet {
|
|||
public struct DesertDark: ColorSet {
|
||||
public var name: ColorSetName = .desertDark
|
||||
public var scheme: ColorScheme = .dark
|
||||
public var tintColor: Color = Color(hex: 0xdf915e)
|
||||
public var primaryBackgroundColor: Color = Color(hex: 0x433744)
|
||||
public var secondaryBackgroundColor: Color = Color(hex:0x654868)
|
||||
public var tintColor: Color = .init(hex: 0xDF915E)
|
||||
public var primaryBackgroundColor: Color = .init(hex: 0x433744)
|
||||
public var secondaryBackgroundColor: Color = .init(hex: 0x654868)
|
||||
public var labelColor: Color = .white
|
||||
|
||||
public init() {}
|
||||
|
@ -57,9 +57,9 @@ public struct DesertDark: ColorSet {
|
|||
public struct DesertLight: ColorSet {
|
||||
public var name: ColorSetName = .desertLight
|
||||
public var scheme: ColorScheme = .light
|
||||
public var tintColor: Color = Color(hex: 0xdf915e)
|
||||
public var primaryBackgroundColor: Color = Color(hex: 0xfcf2eb)
|
||||
public var secondaryBackgroundColor: Color = Color(hex:0xeeede7)
|
||||
public var tintColor: Color = .init(hex: 0xDF915E)
|
||||
public var primaryBackgroundColor: Color = .init(hex: 0xFCF2EB)
|
||||
public var secondaryBackgroundColor: Color = .init(hex: 0xEEEDE7)
|
||||
public var labelColor: Color = .black
|
||||
|
||||
public init() {}
|
||||
|
@ -68,9 +68,9 @@ public struct DesertLight: ColorSet {
|
|||
public struct NemesisDark: ColorSet {
|
||||
public var name: ColorSetName = .nemesisDark
|
||||
public var scheme: ColorScheme = .dark
|
||||
public var tintColor: Color = Color(hex: 0x17a2f2)
|
||||
public var primaryBackgroundColor: Color = Color(hex: 0x000000)
|
||||
public var secondaryBackgroundColor: Color = Color(hex:0x151e2b)
|
||||
public var tintColor: Color = .init(hex: 0x17A2F2)
|
||||
public var primaryBackgroundColor: Color = .init(hex: 0x000000)
|
||||
public var secondaryBackgroundColor: Color = .init(hex: 0x151E2B)
|
||||
public var labelColor: Color = .white
|
||||
|
||||
public init() {}
|
||||
|
@ -79,13 +79,10 @@ public struct NemesisDark: ColorSet {
|
|||
public struct NemesisLight: ColorSet {
|
||||
public var name: ColorSetName = .nemesisLight
|
||||
public var scheme: ColorScheme = .light
|
||||
public var tintColor: Color = Color(hex: 0x17a2f2)
|
||||
public var primaryBackgroundColor: Color = Color(hex: 0xffffff)
|
||||
public var secondaryBackgroundColor: Color = Color(hex:0xe8ecef)
|
||||
public var tintColor: Color = .init(hex: 0x17A2F2)
|
||||
public var primaryBackgroundColor: Color = .init(hex: 0xFFFFFF)
|
||||
public var secondaryBackgroundColor: Color = .init(hex: 0xE8ECEF)
|
||||
public var labelColor: Color = .black
|
||||
|
||||
public init() {}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import Foundation
|
||||
|
||||
extension CGFloat {
|
||||
public static let layoutPadding: CGFloat = 20
|
||||
public static let dividerPadding: CGFloat = 2
|
||||
public static let statusColumnsSpacing: CGFloat = 8
|
||||
public static let maxColumnWidth: CGFloat = 650
|
||||
public extension CGFloat {
|
||||
static let layoutPadding: CGFloat = 20
|
||||
static let dividerPadding: CGFloat = 2
|
||||
static let statusColumnsSpacing: CGFloat = 8
|
||||
static let maxColumnWidth: CGFloat = 650
|
||||
}
|
||||
|
|
|
@ -1,28 +1,28 @@
|
|||
import SwiftUI
|
||||
|
||||
extension Color {
|
||||
public static var brand: Color {
|
||||
Color(red: 187/255, green: 59/255, blue: 226/255)
|
||||
public extension Color {
|
||||
static var brand: Color {
|
||||
Color(red: 187 / 255, green: 59 / 255, blue: 226 / 255)
|
||||
}
|
||||
|
||||
public static var primaryBackground: Color {
|
||||
Color(red: 16/255, green: 21/255, blue: 35/255)
|
||||
static var primaryBackground: Color {
|
||||
Color(red: 16 / 255, green: 21 / 255, blue: 35 / 255)
|
||||
}
|
||||
|
||||
public static var secondaryBackground: Color {
|
||||
Color(red: 30/255, green: 35/255, blue: 62/255)
|
||||
static var secondaryBackground: Color {
|
||||
Color(red: 30 / 255, green: 35 / 255, blue: 62 / 255)
|
||||
}
|
||||
|
||||
public static var label: Color {
|
||||
static var label: Color {
|
||||
Color("label", bundle: .module)
|
||||
}
|
||||
}
|
||||
|
||||
extension Color: RawRepresentable {
|
||||
public init?(rawValue: Int) {
|
||||
let red = Double((rawValue & 0xFF0000) >> 16) / 0xFF
|
||||
let red = Double((rawValue & 0xFF0000) >> 16) / 0xFF
|
||||
let green = Double((rawValue & 0x00FF00) >> 8) / 0xFF
|
||||
let blue = Double(rawValue & 0x0000FF) / 0xFF
|
||||
let blue = Double(rawValue & 0x0000FF) / 0xFF
|
||||
self = Color(red: red, green: green, blue: blue)
|
||||
}
|
||||
|
||||
|
@ -42,11 +42,10 @@ extension Color: RawRepresentable {
|
|||
}
|
||||
|
||||
extension Color {
|
||||
init(hex: Int, opacity: Double = 1.0) {
|
||||
let red = Double((hex & 0xff0000) >> 16) / 255.0
|
||||
let green = Double((hex & 0xff00) >> 8) / 255.0
|
||||
let blue = Double((hex & 0xff) >> 0) / 255.0
|
||||
self.init(.sRGB, red: red, green: green, blue: blue, opacity: opacity)
|
||||
}
|
||||
init(hex: Int, opacity: Double = 1.0) {
|
||||
let red = Double((hex & 0xFF0000) >> 16) / 255.0
|
||||
let green = Double((hex & 0xFF00) >> 8) / 255.0
|
||||
let blue = Double((hex & 0xFF) >> 0) / 255.0
|
||||
self.init(.sRGB, red: red, green: green, blue: blue, opacity: opacity)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -127,17 +127,17 @@ public class Theme: ObservableObject {
|
|||
DesertDark(),
|
||||
DesertLight(),
|
||||
NemesisDark(),
|
||||
NemesisLight()
|
||||
NemesisLight(),
|
||||
]
|
||||
}
|
||||
|
||||
public func setColor(withName name: ColorSetName) {
|
||||
let colorSet = Theme.allColorSet.filter { $0.name == name }.first ?? IceCubeDark()
|
||||
self.selectedScheme = colorSet.scheme
|
||||
self.tintColor = colorSet.tintColor
|
||||
self.primaryBackgroundColor = colorSet.primaryBackgroundColor
|
||||
self.secondaryBackgroundColor = colorSet.secondaryBackgroundColor
|
||||
self.labelColor = colorSet.labelColor
|
||||
self.storedSet = name
|
||||
selectedScheme = colorSet.scheme
|
||||
tintColor = colorSet.tintColor
|
||||
primaryBackgroundColor = colorSet.primaryBackgroundColor
|
||||
secondaryBackgroundColor = colorSet.secondaryBackgroundColor
|
||||
labelColor = colorSet.labelColor
|
||||
storedSet = name
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,68 +1,68 @@
|
|||
import SwiftUI
|
||||
#if canImport(UIKit)
|
||||
import UIKit
|
||||
import UIKit
|
||||
#endif
|
||||
|
||||
public extension View {
|
||||
func applyTheme(_ theme: Theme) -> some View {
|
||||
modifier(ThemeApplier(theme: theme))
|
||||
}
|
||||
func applyTheme(_ theme: Theme) -> some View {
|
||||
modifier(ThemeApplier(theme: theme))
|
||||
}
|
||||
}
|
||||
|
||||
struct ThemeApplier: ViewModifier {
|
||||
@ObservedObject var theme: Theme
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.tint(theme.tintColor)
|
||||
.preferredColorScheme(theme.selectedScheme == ColorScheme.dark ? .dark : .light)
|
||||
#if canImport(UIKit)
|
||||
.onAppear {
|
||||
setWindowTint(theme.tintColor)
|
||||
setWindowUserInterfaceStyle(theme.selectedScheme)
|
||||
setBarsColor(theme.primaryBackgroundColor)
|
||||
}
|
||||
.onChange(of: theme.tintColor) { newValue in
|
||||
setWindowTint(newValue)
|
||||
}
|
||||
.onChange(of: theme.selectedScheme) { newValue in
|
||||
setWindowUserInterfaceStyle(newValue)
|
||||
}
|
||||
.onChange(of: theme.primaryBackgroundColor) { newValue in
|
||||
setBarsColor(newValue)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ObservedObject var theme: Theme
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.tint(theme.tintColor)
|
||||
.preferredColorScheme(theme.selectedScheme == ColorScheme.dark ? .dark : .light)
|
||||
#if canImport(UIKit)
|
||||
.onAppear {
|
||||
setWindowTint(theme.tintColor)
|
||||
setWindowUserInterfaceStyle(theme.selectedScheme)
|
||||
setBarsColor(theme.primaryBackgroundColor)
|
||||
}
|
||||
.onChange(of: theme.tintColor) { newValue in
|
||||
setWindowTint(newValue)
|
||||
}
|
||||
.onChange(of: theme.selectedScheme) { newValue in
|
||||
setWindowUserInterfaceStyle(newValue)
|
||||
}
|
||||
.onChange(of: theme.primaryBackgroundColor) { newValue in
|
||||
setBarsColor(newValue)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
private func setWindowUserInterfaceStyle(_ colorScheme: ColorScheme) {
|
||||
allWindows()
|
||||
.forEach {
|
||||
switch colorScheme {
|
||||
case .dark:
|
||||
$0.overrideUserInterfaceStyle = .dark
|
||||
case .light:
|
||||
$0.overrideUserInterfaceStyle = .light
|
||||
}
|
||||
}
|
||||
allWindows()
|
||||
.forEach {
|
||||
switch colorScheme {
|
||||
case .dark:
|
||||
$0.overrideUserInterfaceStyle = .dark
|
||||
case .light:
|
||||
$0.overrideUserInterfaceStyle = .light
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func setWindowTint(_ color: Color) {
|
||||
allWindows()
|
||||
.forEach {
|
||||
$0.tintColor = UIColor(color)
|
||||
}
|
||||
allWindows()
|
||||
.forEach {
|
||||
$0.tintColor = UIColor(color)
|
||||
}
|
||||
}
|
||||
|
||||
private func setBarsColor(_ color: Color) {
|
||||
UINavigationBar.appearance().isTranslucent = true
|
||||
UINavigationBar.appearance().barTintColor = UIColor(color)
|
||||
UINavigationBar.appearance().isTranslucent = true
|
||||
UINavigationBar.appearance().barTintColor = UIColor(color)
|
||||
}
|
||||
|
||||
private func allWindows() -> [UIWindow] {
|
||||
UIApplication.shared.connectedScenes
|
||||
.compactMap { $0 as? UIWindowScene }
|
||||
.flatMap { $0.windows }
|
||||
UIApplication.shared.connectedScenes
|
||||
.compactMap { $0 as? UIWindowScene }
|
||||
.flatMap { $0.windows }
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import SwiftUI
|
||||
import Shimmer
|
||||
import NukeUI
|
||||
import Shimmer
|
||||
import SwiftUI
|
||||
|
||||
public struct AvatarView: View {
|
||||
@Environment(\.redactionReasons) private var reasons
|
||||
|
@ -48,21 +48,21 @@ public struct AvatarView: View {
|
|||
RoundedRectangle(cornerRadius: size.cornerRadius)
|
||||
.fill(.gray)
|
||||
.frame(width: size.size.width, height: size.size.height)
|
||||
} else {
|
||||
LazyImage(url: url) { state in
|
||||
if let image = state.image {
|
||||
image
|
||||
.resizingMode(.aspectFit)
|
||||
} else if state.isLoading {
|
||||
placeholderView
|
||||
.shimmering()
|
||||
} else {
|
||||
placeholderView
|
||||
}
|
||||
} else {
|
||||
LazyImage(url: url) { state in
|
||||
if let image = state.image {
|
||||
image
|
||||
.resizingMode(.aspectFit)
|
||||
} else if state.isLoading {
|
||||
placeholderView
|
||||
.shimmering()
|
||||
} else {
|
||||
placeholderView
|
||||
}
|
||||
.processors([.resize(size: size.size), .roundedCorners(radius: size.cornerRadius)])
|
||||
.frame(width: size.size.width, height: size.size.height)
|
||||
}
|
||||
.processors([.resize(size: size.size), .roundedCorners(radius: size.cornerRadius)])
|
||||
.frame(width: size.size.width, height: size.size.height)
|
||||
}
|
||||
}
|
||||
.clipShape(clipShape)
|
||||
.overlay(
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import Foundation
|
||||
import EmojiText
|
||||
import Models
|
||||
import Foundation
|
||||
import HTML2Markdown
|
||||
import Models
|
||||
import SwiftUI
|
||||
|
||||
public struct EmojiTextApp: View {
|
||||
|
|
|
@ -4,7 +4,7 @@ public struct ErrorView: View {
|
|||
public let title: String
|
||||
public let message: String
|
||||
public let buttonTitle: String
|
||||
public let onButtonPress: (() -> Void)
|
||||
public let onButtonPress: () -> Void
|
||||
|
||||
public init(title: String, message: String, buttonTitle: String, onButtonPress: @escaping (() -> Void)) {
|
||||
self.title = title
|
||||
|
|
|
@ -40,5 +40,5 @@ public struct ScrollViewOffsetReader<Content: View>: View {
|
|||
|
||||
private struct OffsetPreferenceKey: PreferenceKey {
|
||||
static var defaultValue: CGFloat = .zero
|
||||
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {}
|
||||
static func reduce(value _: inout CGFloat, nextValue _: () -> CGFloat) {}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import SwiftUI
|
||||
import Env
|
||||
import Models
|
||||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
extension View {
|
||||
public func statusEditorToolbarItem(routeurPath: RouterPath, visibility: Models.Visibility) -> some ToolbarContent {
|
||||
public extension View {
|
||||
func statusEditorToolbarItem(routeurPath: RouterPath, visibility: Models.Visibility) -> some ToolbarContent {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button {
|
||||
routeurPath.presentedSheet = .newStatusEditor(visibility: visibility)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Env
|
||||
import Models
|
||||
import SwiftUI
|
||||
import Env
|
||||
|
||||
public struct TagRowView: View {
|
||||
@EnvironmentObject private var routeurPath: RouterPath
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import SwiftUI
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
public struct ThemePreviewView: View {
|
||||
private let gutterSpace: Double = 8
|
||||
|
@ -10,15 +10,15 @@ public struct ThemePreviewView: View {
|
|||
|
||||
public var body: some View {
|
||||
ScrollView {
|
||||
HStack (spacing: gutterSpace) {
|
||||
HStack(spacing: gutterSpace) {
|
||||
ThemeBoxView(color: IceCubeDark())
|
||||
ThemeBoxView(color: IceCubeLight())
|
||||
}
|
||||
HStack (spacing: gutterSpace) {
|
||||
HStack(spacing: gutterSpace) {
|
||||
ThemeBoxView(color: DesertDark())
|
||||
ThemeBoxView(color: DesertLight())
|
||||
}
|
||||
HStack (spacing: gutterSpace) {
|
||||
HStack(spacing: gutterSpace) {
|
||||
ThemeBoxView(color: NemesisDark())
|
||||
ThemeBoxView(color: NemesisLight())
|
||||
}
|
||||
|
@ -31,7 +31,6 @@ public struct ThemePreviewView: View {
|
|||
}
|
||||
|
||||
struct ThemeBoxView: View {
|
||||
|
||||
@EnvironmentObject var theme: Theme
|
||||
private let gutterSpace = 8.0
|
||||
@State private var isSelected = false
|
||||
|
@ -46,7 +45,7 @@ struct ThemeBoxView: View {
|
|||
.cornerRadius(4)
|
||||
.shadow(radius: 2, x: 2, y: 4)
|
||||
|
||||
VStack (spacing: gutterSpace) {
|
||||
VStack(spacing: gutterSpace) {
|
||||
Text(color.name.rawValue)
|
||||
.foregroundColor(color.tintColor)
|
||||
.font(.system(size: 20))
|
||||
|
@ -95,4 +94,3 @@ struct ThemeBoxView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,11 +11,12 @@ let package = Package(
|
|||
products: [
|
||||
.library(
|
||||
name: "Env",
|
||||
targets: ["Env"]),
|
||||
targets: ["Env"]
|
||||
),
|
||||
],
|
||||
dependencies: [
|
||||
.package(name: "Models", path: "../Models"),
|
||||
.package(name: "Network", path: "../Network")
|
||||
.package(name: "Network", path: "../Network"),
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
|
@ -23,6 +24,7 @@ let package = Package(
|
|||
dependencies: [
|
||||
.product(name: "Models", package: "Models"),
|
||||
.product(name: "Network", package: "Network"),
|
||||
]),
|
||||
]
|
||||
),
|
||||
]
|
||||
)
|
||||
|
|
|
@ -10,9 +10,9 @@ public class CurrentAccount: ObservableObject {
|
|||
|
||||
private var client: Client?
|
||||
|
||||
static public let shared = CurrentAccount()
|
||||
public static let shared = CurrentAccount()
|
||||
|
||||
private init() { }
|
||||
private init() {}
|
||||
|
||||
public func setClient(client: Client) {
|
||||
self.client = client
|
||||
|
@ -36,7 +36,7 @@ public class CurrentAccount: ObservableObject {
|
|||
do {
|
||||
let connections: [String] = try await client.get(endpoint: Instances.peers)
|
||||
client.addConnections(connections)
|
||||
} catch { }
|
||||
} catch {}
|
||||
}
|
||||
|
||||
public func fetchCurrentAccount() async {
|
||||
|
@ -70,7 +70,7 @@ public class CurrentAccount: ObservableObject {
|
|||
do {
|
||||
let list: Models.List = try await client.post(endpoint: Lists.createList(title: title))
|
||||
lists.append(list)
|
||||
} catch { }
|
||||
} catch {}
|
||||
}
|
||||
|
||||
public func deleteList(list: Models.List) async {
|
||||
|
@ -97,7 +97,7 @@ public class CurrentAccount: ObservableObject {
|
|||
guard let client else { return nil }
|
||||
do {
|
||||
let tag: Tag = try await client.post(endpoint: Tags.unfollow(id: id))
|
||||
tags.removeAll{ $0.id == tag.id }
|
||||
tags.removeAll { $0.id == tag.id }
|
||||
return tag
|
||||
} catch {
|
||||
return nil
|
||||
|
|
|
@ -8,9 +8,9 @@ public class CurrentInstance: ObservableObject {
|
|||
|
||||
private var client: Client?
|
||||
|
||||
static public let shared = CurrentInstance()
|
||||
public static let shared = CurrentInstance()
|
||||
|
||||
private init() { }
|
||||
private init() {}
|
||||
|
||||
public func setClient(client: Client) {
|
||||
self.client = client
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Foundation
|
||||
|
||||
public enum PreferredBrowser: Int, CaseIterable {
|
||||
case inAppSafari
|
||||
case safari
|
||||
case inAppSafari
|
||||
case safari
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import Foundation
|
||||
import UserNotifications
|
||||
import SwiftUI
|
||||
import KeychainSwift
|
||||
import CryptoKit
|
||||
import Foundation
|
||||
import KeychainSwift
|
||||
import Models
|
||||
import Network
|
||||
import SwiftUI
|
||||
import UserNotifications
|
||||
|
||||
@MainActor
|
||||
public class PushNotificationsService: ObservableObject {
|
||||
|
@ -36,6 +36,7 @@ public class PushNotificationsService: ObservableObject {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Published public var isFollowNotificationEnabled: Bool = true
|
||||
@Published public var isFavoriteNotificationEnabled: Bool = true
|
||||
@Published public var isReblogNotificationEnabled: Bool = true
|
||||
|
@ -54,7 +55,7 @@ public class PushNotificationsService: ObservableObject {
|
|||
}
|
||||
|
||||
public func requestPushNotifications() {
|
||||
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { (_, _) in
|
||||
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { _, _ in
|
||||
DispatchQueue.main.async {
|
||||
UIApplication.shared.registerForRemoteNotifications()
|
||||
}
|
||||
|
@ -68,7 +69,7 @@ public class PushNotificationsService: ObservableObject {
|
|||
do {
|
||||
let sub: PushSubscription = try await client.get(endpoint: Push.subscription)
|
||||
subscriptions.append(sub)
|
||||
} catch { }
|
||||
} catch {}
|
||||
}
|
||||
refreshSubscriptionsUI()
|
||||
}
|
||||
|
@ -80,15 +81,15 @@ public class PushNotificationsService: ObservableObject {
|
|||
guard let pushToken = pushToken, isUserPushEnabled else { return }
|
||||
for account in accounts {
|
||||
let client = Client(server: account.server, oauthToken: account.token)
|
||||
do {
|
||||
var listenerURL = Constants.endpoint
|
||||
listenerURL += "/push/"
|
||||
listenerURL += pushToken.hexString
|
||||
listenerURL += "/\(account.server)"
|
||||
#if DEBUG
|
||||
listenerURL += "?sandbox=true"
|
||||
#endif
|
||||
let sub: PushSubscription =
|
||||
do {
|
||||
var listenerURL = Constants.endpoint
|
||||
listenerURL += "/push/"
|
||||
listenerURL += pushToken.hexString
|
||||
listenerURL += "/\(account.server)"
|
||||
#if DEBUG
|
||||
listenerURL += "?sandbox=true"
|
||||
#endif
|
||||
let sub: PushSubscription =
|
||||
try await client.post(endpoint: Push.createSub(endpoint: listenerURL,
|
||||
p256dh: key,
|
||||
auth: authKey,
|
||||
|
@ -98,9 +99,9 @@ public class PushNotificationsService: ObservableObject {
|
|||
follow: isFollowNotificationEnabled,
|
||||
favourite: isFavoriteNotificationEnabled,
|
||||
poll: isPollNotificationEnabled))
|
||||
subscriptions.append(sub)
|
||||
} catch { }
|
||||
}
|
||||
subscriptions.append(sub)
|
||||
} catch {}
|
||||
}
|
||||
refreshSubscriptionsUI()
|
||||
}
|
||||
|
||||
|
@ -109,7 +110,7 @@ public class PushNotificationsService: ObservableObject {
|
|||
let client = Client(server: account.server, oauthToken: account.token)
|
||||
do {
|
||||
_ = try await client.delete(endpoint: Push.subscription)
|
||||
} catch { }
|
||||
} catch {}
|
||||
}
|
||||
await fetchSubscriptions(accounts: accounts)
|
||||
refreshSubscriptionsUI()
|
||||
|
@ -133,7 +134,8 @@ public class PushNotificationsService: ObservableObject {
|
|||
|
||||
public var notificationsPrivateKeyAsKey: P256.KeyAgreement.PrivateKey {
|
||||
if let key = keychain.get(Constants.keychainPrivateKey),
|
||||
let data = Data(base64Encoded: key) {
|
||||
let data = Data(base64Encoded: key)
|
||||
{
|
||||
do {
|
||||
return try P256.KeyAgreement.PrivateKey(rawRepresentation: data)
|
||||
} catch {
|
||||
|
@ -154,7 +156,8 @@ public class PushNotificationsService: ObservableObject {
|
|||
|
||||
public var notificationsAuthKeyAsKey: Data {
|
||||
if let key = keychain.get(Constants.keychainAuthKey),
|
||||
let data = Data(base64Encoded: key) {
|
||||
let data = Data(base64Encoded: key)
|
||||
{
|
||||
return data
|
||||
} else {
|
||||
let key = Self.makeRandomeNotificationsAuthKey()
|
||||
|
@ -165,7 +168,7 @@ public class PushNotificationsService: ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
static private func makeRandomeNotificationsAuthKey() -> Data {
|
||||
private static func makeRandomeNotificationsAuthKey() -> Data {
|
||||
let byteCount = 16
|
||||
var bytes = Data(count: byteCount)
|
||||
_ = bytes.withUnsafeMutableBytes { SecRandomCopyBytes(kSecRandomDefault, byteCount, $0.baseAddress!) }
|
||||
|
@ -178,4 +181,3 @@ extension Data {
|
|||
return map { String(format: "%02.2hhx", arguments: [$0]) }.joined()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,9 +8,7 @@ public class QuickLook: ObservableObject {
|
|||
@Published public private(set) var isPreparing: Bool = false
|
||||
@Published public private(set) var latestError: Error?
|
||||
|
||||
public init() {
|
||||
|
||||
}
|
||||
public init() {}
|
||||
|
||||
public func prepareFor(urls: [URL], selectedURL: URL) async {
|
||||
withAnimation {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import Foundation
|
||||
import SwiftUI
|
||||
import Models
|
||||
import Network
|
||||
import SwiftUI
|
||||
|
||||
public enum RouteurDestinations: Hashable {
|
||||
case accountDetail(id: String)
|
||||
|
@ -59,7 +59,8 @@ public class RouterPath: ObservableObject {
|
|||
|
||||
public func handleStatus(status: AnyStatus, url: URL) -> OpenURLAction.Result {
|
||||
if url.pathComponents.contains(where: { $0 == "tags" }),
|
||||
let tag = url.pathComponents.last {
|
||||
let tag = url.pathComponents.last
|
||||
{
|
||||
navigate(to: .hashTag(tag: tag, account: nil))
|
||||
return .handled
|
||||
} else if let mention = status.mentions.first(where: { $0.url == url }) {
|
||||
|
@ -68,7 +69,8 @@ public class RouterPath: ObservableObject {
|
|||
} else if let client = client,
|
||||
client.isAuth,
|
||||
client.hasConnection(with: url),
|
||||
let id = Int(url.lastPathComponent) {
|
||||
let id = Int(url.lastPathComponent)
|
||||
{
|
||||
if url.absoluteString.contains(client.server) {
|
||||
navigate(to: .statusDetail(id: String(id)))
|
||||
} else {
|
||||
|
@ -81,7 +83,8 @@ public class RouterPath: ObservableObject {
|
|||
|
||||
public func handle(url: URL) -> OpenURLAction.Result {
|
||||
if url.pathComponents.contains(where: { $0 == "tags" }),
|
||||
let tag = url.pathComponents.last {
|
||||
let tag = url.pathComponents.last
|
||||
{
|
||||
navigate(to: .hashTag(tag: tag, account: nil))
|
||||
return .handled
|
||||
} else if url.lastPathComponent.first == "@", let host = url.host {
|
||||
|
|
|
@ -91,7 +91,7 @@ public class StreamWatcher: ObservableObject {
|
|||
break
|
||||
}
|
||||
|
||||
self.receiveMessage()
|
||||
self.receiveMessage()
|
||||
|
||||
case .failure:
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(10)) { [weak self] in
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import SwiftUI
|
||||
import Foundation
|
||||
import Models
|
||||
import Network
|
||||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
public class UserPreferences: ObservableObject {
|
||||
public static let sharedDefault = UserDefaults.init(suiteName: "group.icecubesapps")
|
||||
public static let sharedDefault = UserDefaults(suiteName: "group.icecubesapps")
|
||||
public static let shared = UserPreferences()
|
||||
|
||||
private var client: Client?
|
||||
|
@ -25,7 +25,7 @@ public class UserPreferences: ObservableObject {
|
|||
|
||||
@Published public var serverPreferences: ServerPreferences?
|
||||
|
||||
private init() { }
|
||||
private init() {}
|
||||
|
||||
public func setClient(client: Client) {
|
||||
self.client = client
|
||||
|
|
|
@ -11,7 +11,8 @@ let package = Package(
|
|||
products: [
|
||||
.library(
|
||||
name: "Explore",
|
||||
targets: ["Explore"]),
|
||||
targets: ["Explore"]
|
||||
),
|
||||
],
|
||||
dependencies: [
|
||||
.package(name: "Account", path: "../Account"),
|
||||
|
@ -30,8 +31,8 @@ let package = Package(
|
|||
.product(name: "Models", package: "Models"),
|
||||
.product(name: "Env", package: "Env"),
|
||||
.product(name: "Status", package: "Status"),
|
||||
.product(name: "DesignSystem", package: "DesignSystem")
|
||||
])
|
||||
.product(name: "DesignSystem", package: "DesignSystem"),
|
||||
]
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import SwiftUI
|
||||
import Env
|
||||
import Network
|
||||
import DesignSystem
|
||||
import Models
|
||||
import Status
|
||||
import Shimmer
|
||||
import Account
|
||||
import DesignSystem
|
||||
import Env
|
||||
import Models
|
||||
import Network
|
||||
import Shimmer
|
||||
import Status
|
||||
import SwiftUI
|
||||
|
||||
public struct ExploreView: View {
|
||||
@EnvironmentObject private var theme: Theme
|
||||
|
@ -14,7 +14,7 @@ public struct ExploreView: View {
|
|||
|
||||
@StateObject private var viewModel = ExploreViewModel()
|
||||
|
||||
public init() { }
|
||||
public init() {}
|
||||
|
||||
public var body: some View {
|
||||
List {
|
||||
|
@ -30,7 +30,7 @@ public struct ExploreView: View {
|
|||
EmptyView(iconName: "magnifyingglass",
|
||||
title: "Search your instance",
|
||||
message: "From this screen you can search anything on \(client.server)")
|
||||
.listRowBackground(theme.secondaryBackgroundColor)
|
||||
.listRowBackground(theme.secondaryBackgroundColor)
|
||||
} else {
|
||||
if !viewModel.trendingTags.isEmpty {
|
||||
trendingTagsSection
|
||||
|
@ -64,8 +64,8 @@ public struct ExploreView: View {
|
|||
suggestedTokens: $viewModel.suggestedToken,
|
||||
prompt: Text("Search users, posts and tags"),
|
||||
token: { token in
|
||||
Text(token.rawValue)
|
||||
})
|
||||
Text(token.rawValue)
|
||||
})
|
||||
}
|
||||
|
||||
private var loadingView: some View {
|
||||
|
@ -114,11 +114,11 @@ public struct ExploreView: View {
|
|||
Section("Suggested Users") {
|
||||
ForEach(viewModel.suggestedAccounts
|
||||
.prefix(upTo: viewModel.suggestedAccounts.count > 3 ? 3 : viewModel.suggestedAccounts.count)) { account in
|
||||
if let relationship = viewModel.suggestedAccountsRelationShips.first(where: { $0.id == account.id }) {
|
||||
AccountsListRow(viewModel: .init(account: account, relationShip: relationship))
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
if let relationship = viewModel.suggestedAccountsRelationShips.first(where: { $0.id == account.id }) {
|
||||
AccountsListRow(viewModel: .init(account: account, relationShip: relationship))
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
NavigationLink {
|
||||
List {
|
||||
ForEach(viewModel.suggestedAccounts) { account in
|
||||
|
@ -145,10 +145,10 @@ public struct ExploreView: View {
|
|||
Section("Trending Tags") {
|
||||
ForEach(viewModel.trendingTags
|
||||
.prefix(upTo: viewModel.trendingTags.count > 5 ? 5 : viewModel.trendingTags.count)) { tag in
|
||||
TagRowView(tag: tag)
|
||||
TagRowView(tag: tag)
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
.padding(.vertical, 4)
|
||||
}
|
||||
.padding(.vertical, 4)
|
||||
}
|
||||
NavigationLink {
|
||||
List {
|
||||
ForEach(viewModel.trendingTags) { tag in
|
||||
|
@ -174,10 +174,10 @@ public struct ExploreView: View {
|
|||
Section("Trending Posts") {
|
||||
ForEach(viewModel.trendingStatuses
|
||||
.prefix(upTo: viewModel.trendingStatuses.count > 3 ? 3 : viewModel.trendingStatuses.count)) { status in
|
||||
StatusRowView(viewModel: .init(status: status, isCompact: false))
|
||||
StatusRowView(viewModel: .init(status: status, isCompact: false))
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
|
||||
NavigationLink {
|
||||
List {
|
||||
|
@ -204,10 +204,10 @@ public struct ExploreView: View {
|
|||
Section("Trending Links") {
|
||||
ForEach(viewModel.trendingLinks
|
||||
.prefix(upTo: viewModel.trendingLinks.count > 3 ? 3 : viewModel.trendingLinks.count)) { card in
|
||||
StatusCardView(card: card)
|
||||
StatusCardView(card: card)
|
||||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
NavigationLink {
|
||||
List {
|
||||
ForEach(viewModel.trendingLinks) { card in
|
||||
|
@ -228,5 +228,4 @@ public struct ExploreView: View {
|
|||
.listRowBackground(theme.primaryBackgroundColor)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import SwiftUI
|
||||
import Combine
|
||||
import Models
|
||||
import Network
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
class ExploreViewModel: ObservableObject {
|
||||
|
@ -61,7 +61,7 @@ class ExploreViewModel: ObservableObject {
|
|||
$searchQuery
|
||||
.removeDuplicates()
|
||||
.debounce(for: .milliseconds(500), scheduler: DispatchQueue.main)
|
||||
.sink(receiveValue: { [weak self] newValue in
|
||||
.sink(receiveValue: { [weak self] _ in
|
||||
guard let self else { return }
|
||||
|
||||
if self.searchQuery.starts(with: "@") {
|
||||
|
@ -81,12 +81,12 @@ class ExploreViewModel: ObservableObject {
|
|||
guard let client else { return }
|
||||
do {
|
||||
let data = try await fetchTrendingsData(client: client)
|
||||
self.suggestedAccounts = data.suggestedAccounts
|
||||
self.trendingTags = data.trendingTags
|
||||
self.trendingStatuses = data.trendingStatuses
|
||||
self.trendingLinks = data.trendingLinks
|
||||
suggestedAccounts = data.suggestedAccounts
|
||||
trendingTags = data.trendingTags
|
||||
trendingStatuses = data.trendingStatuses
|
||||
trendingLinks = data.trendingLinks
|
||||
|
||||
self.suggestedAccountsRelationShips = try await client.get(endpoint: Accounts.relationships(ids: self.suggestedAccounts.map{ $0.id }))
|
||||
suggestedAccountsRelationShips = try await client.get(endpoint: Accounts.relationships(ids: suggestedAccounts.map { $0.id }))
|
||||
withAnimation {
|
||||
isLoaded = true
|
||||
}
|
||||
|
@ -127,10 +127,10 @@ class ExploreViewModel: ObservableObject {
|
|||
following: nil),
|
||||
forceVersion: .v2)
|
||||
let relationships: [Relationshionship] =
|
||||
try await client.get(endpoint: Accounts.relationships(ids: results.accounts.map{ $0.id }))
|
||||
try await client.get(endpoint: Accounts.relationships(ids: results.accounts.map { $0.id }))
|
||||
results.relationships = relationships
|
||||
self.results[searchQuery] = results
|
||||
} catch { }
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,8 @@ let package = Package(
|
|||
products: [
|
||||
.library(
|
||||
name: "Lists",
|
||||
targets: ["Lists"]),
|
||||
targets: ["Lists"]
|
||||
),
|
||||
],
|
||||
dependencies: [
|
||||
.package(name: "Network", path: "../Network"),
|
||||
|
@ -26,8 +27,8 @@ let package = Package(
|
|||
.product(name: "Network", package: "Network"),
|
||||
.product(name: "Models", package: "Models"),
|
||||
.product(name: "Env", package: "Env"),
|
||||
.product(name: "DesignSystem", package: "DesignSystem")
|
||||
]),
|
||||
.product(name: "DesignSystem", package: "DesignSystem"),
|
||||
]
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import SwiftUI
|
||||
import Network
|
||||
import DesignSystem
|
||||
import Env
|
||||
import Models
|
||||
import Network
|
||||
import SwiftUI
|
||||
|
||||
public struct ListAddAccountView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
@ -14,7 +14,6 @@ public struct ListAddAccountView: View {
|
|||
@State private var isCreateListAlertPresented: Bool = false
|
||||
@State private var createListTitle: String = ""
|
||||
|
||||
|
||||
public init(account: Account) {
|
||||
_viewModel = StateObject(wrappedValue: .init(account: account))
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import SwiftUI
|
||||
import Models
|
||||
import Network
|
||||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
class ListAddAccountViewModel: ObservableObject {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import SwiftUI
|
||||
import Models
|
||||
import DesignSystem
|
||||
import Network
|
||||
import EmojiText
|
||||
import Models
|
||||
import Network
|
||||
import SwiftUI
|
||||
|
||||
public struct ListEditView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import SwiftUI
|
||||
import Models
|
||||
import Network
|
||||
import SwiftUI
|
||||
|
||||
@MainActor
|
||||
public class ListEditViewModel: ObservableObject {
|
||||
|
@ -29,10 +29,10 @@ public class ListEditViewModel: ObservableObject {
|
|||
func delete(account: Account) async {
|
||||
guard let client else { return }
|
||||
do {
|
||||
let response = try await client.delete(endpoint: Lists.updateAccounts(listId: list.id, accounts: [account.id]))
|
||||
let response = try await client.delete(endpoint: Lists.updateAccounts(listId: list.id, accounts: [account.id]))
|
||||
if response?.statusCode == 200 {
|
||||
accounts.removeAll(where: { $0.id == account.id })
|
||||
}
|
||||
} catch { }
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,8 @@ let package = Package(
|
|||
products: [
|
||||
.library(
|
||||
name: "Models",
|
||||
targets: ["Models"]),
|
||||
targets: ["Models"]
|
||||
),
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "https://gitlab.com/mflint/HTML2Markdown", exact: "1.0.0"),
|
||||
|
@ -21,9 +22,11 @@ let package = Package(
|
|||
.target(
|
||||
name: "Models",
|
||||
dependencies: ["HTML2Markdown",
|
||||
"SwiftSoup"]),
|
||||
"SwiftSoup"]
|
||||
),
|
||||
.testTarget(
|
||||
name: "ModelsTests",
|
||||
dependencies: ["Models"]),
|
||||
dependencies: ["Models"]
|
||||
),
|
||||
]
|
||||
)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import Foundation
|
||||
|
||||
public struct Account: Codable, Identifiable, Equatable, Hashable {
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(id)
|
||||
}
|
||||
|
|
|
@ -5,8 +5,8 @@ import SwiftUI
|
|||
|
||||
public typealias HTMLString = String
|
||||
|
||||
extension HTMLString {
|
||||
public var asMarkdown: String {
|
||||
public extension HTMLString {
|
||||
var asMarkdown: String {
|
||||
do {
|
||||
let dom = try HTMLParser().parse(html: self)
|
||||
return dom.toMarkdown()
|
||||
|
@ -17,7 +17,7 @@ extension HTMLString {
|
|||
}
|
||||
}
|
||||
|
||||
public var asRawText: String {
|
||||
var asRawText: String {
|
||||
do {
|
||||
let document: Document = try SwiftSoup.parse(self)
|
||||
return try document.text()
|
||||
|
@ -26,7 +26,7 @@ extension HTMLString {
|
|||
}
|
||||
}
|
||||
|
||||
public func findStatusesURLs() -> [URL]? {
|
||||
func findStatusesURLs() -> [URL]? {
|
||||
do {
|
||||
let document: Document = try SwiftSoup.parse(self)
|
||||
let links: Elements = try document.select("a")
|
||||
|
@ -34,7 +34,8 @@ extension HTMLString {
|
|||
for link in links {
|
||||
let href = try link.attr("href")
|
||||
if let url = URL(string: href),
|
||||
let _ = Int(url.lastPathComponent) {
|
||||
let _ = Int(url.lastPathComponent)
|
||||
{
|
||||
URLs.append(url)
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +45,7 @@ extension HTMLString {
|
|||
}
|
||||
}
|
||||
|
||||
public var asSafeAttributedString: AttributedString {
|
||||
var asSafeAttributedString: AttributedString {
|
||||
do {
|
||||
let options = AttributedString.MarkdownParsingOptions(allowsExtendedAttributes: true,
|
||||
interpretedSyntax: .inlineOnlyPreservingWhitespace)
|
||||
|
@ -54,4 +55,3 @@ extension HTMLString {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,6 @@ extension ServerDate {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
extension Calendar {
|
||||
func numberOfDaysBetween(_ from: Date, and to: Date) -> Int {
|
||||
let fromDate = startOfDay(for: from)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Foundation
|
||||
|
||||
public struct AppInfo {
|
||||
public enum AppInfo {
|
||||
public static let clientName = "IceCubesApp"
|
||||
public static let scheme = "icecubesapp://"
|
||||
public static let scopes = "read write follow push"
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import Foundation
|
||||
|
||||
public struct Emoji: Codable, Hashable, Identifiable {
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(shortcode)
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ public struct InstanceSocial: Decodable, Identifiable {
|
|||
public struct Info: Decodable {
|
||||
public let shortDescription: String
|
||||
}
|
||||
|
||||
public let id: String
|
||||
public let name: String
|
||||
public let dead: Bool
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import Foundation
|
||||
|
||||
public struct MastodonPushNotification: Codable {
|
||||
|
||||
public let accessToken: String
|
||||
|
||||
public let notificationID: Int
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import Foundation
|
||||
|
||||
public struct MediaAttachement: Codable, Identifiable, Hashable {
|
||||
|
||||
public struct MetaContainer: Codable, Equatable {
|
||||
public struct Meta: Codable, Equatable {
|
||||
public let width: Int?
|
||||
public let height: Int?
|
||||
}
|
||||
|
||||
public let original: Meta?
|
||||
}
|
||||
|
||||
|
@ -23,9 +23,9 @@ public struct MediaAttachement: Codable, Identifiable, Hashable {
|
|||
public var supportedType: SupportedType? {
|
||||
SupportedType(rawValue: type)
|
||||
}
|
||||
|
||||
public let url: URL?
|
||||
public let previewUrl: URL?
|
||||
public let description: String?
|
||||
public let meta: MetaContainer?
|
||||
}
|
||||
|
||||
|
|
|
@ -27,4 +27,3 @@ public struct Notification: Codable, Identifiable {
|
|||
[.placeholder(), .placeholder(), .placeholder(), .placeholder(), .placeholder(), .placeholder(), .placeholder(), .placeholder()]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ public struct Relationshionship: Codable {
|
|||
public let note: String
|
||||
public let notifying: Bool
|
||||
|
||||
static public func placeholder() -> Relationshionship {
|
||||
public static func placeholder() -> Relationshionship {
|
||||
.init(id: UUID().uuidString,
|
||||
following: false,
|
||||
showingReblogs: false,
|
||||
|
|
|
@ -4,12 +4,13 @@ public struct Application: Codable, Identifiable {
|
|||
public var id: String {
|
||||
name
|
||||
}
|
||||
|
||||
public let name: String
|
||||
public let website: URL?
|
||||
}
|
||||
|
||||
extension Application {
|
||||
public init(from decoder: Decoder) throws {
|
||||
public extension Application {
|
||||
init(from decoder: Decoder) throws {
|
||||
let values = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
name = try values.decodeIfPresent(String.self, forKey: .name) ?? ""
|
||||
|
@ -53,7 +54,6 @@ public protocol AnyStatus {
|
|||
var language: String? { get }
|
||||
}
|
||||
|
||||
|
||||
public struct Status: AnyStatus, Codable, Identifiable {
|
||||
public var viewId: String {
|
||||
id + createdAt + (editedAt ?? "")
|
||||
|
|
|
@ -6,7 +6,7 @@ public struct RawStreamEvent: Decodable {
|
|||
public let payload: String
|
||||
}
|
||||
|
||||
public protocol StreamEvent: Identifiable{
|
||||
public protocol StreamEvent: Identifiable {
|
||||
var date: Date { get }
|
||||
var id: String { get }
|
||||
}
|
||||
|
|
|
@ -8,5 +8,4 @@ public struct StreamMessage: Encodable {
|
|||
self.type = type
|
||||
self.stream = stream
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,11 +25,11 @@ public struct Tag: Codable, Identifiable, Equatable, Hashable {
|
|||
public let history: [History]
|
||||
|
||||
public var totalUses: Int {
|
||||
history.compactMap{ Int($0.uses) }.reduce(0, +)
|
||||
history.compactMap { Int($0.uses) }.reduce(0, +)
|
||||
}
|
||||
|
||||
public var totalAccounts: Int {
|
||||
history.compactMap{ Int($0.accounts) }.reduce(0, +)
|
||||
history.compactMap { Int($0.accounts) }.reduce(0, +)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import XCTest
|
||||
@testable import Models
|
||||
import XCTest
|
||||
|
||||
final class ModelsTests: XCTestCase {
|
||||
func testExample() throws {
|
||||
// This is an example of a functional test case.
|
||||
// Use XCTAssert and related functions to verify your tests produce the correct
|
||||
// results.
|
||||
XCTAssertEqual(Models().text, "Hello, World!")
|
||||
}
|
||||
func testExample() throws {
|
||||
// This is an example of a functional test case.
|
||||
// Use XCTAssert and related functions to verify your tests produce the correct
|
||||
// results.
|
||||
XCTAssertEqual(Models().text, "Hello, World!")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,8 @@ let package = Package(
|
|||
products: [
|
||||
.library(
|
||||
name: "Network",
|
||||
targets: ["Network"]),
|
||||
targets: ["Network"]
|
||||
),
|
||||
],
|
||||
dependencies: [
|
||||
.package(name: "Models", path: "../Models"),
|
||||
|
@ -20,10 +21,12 @@ let package = Package(
|
|||
.target(
|
||||
name: "Network",
|
||||
dependencies: [
|
||||
.product(name: "Models", package: "Models")
|
||||
]),
|
||||
.product(name: "Models", package: "Models"),
|
||||
]
|
||||
),
|
||||
.testTarget(
|
||||
name: "NetworkTests",
|
||||
dependencies: ["Network"]),
|
||||
dependencies: ["Network"]
|
||||
),
|
||||
]
|
||||
)
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import Foundation
|
||||
import SwiftUI
|
||||
import Models
|
||||
import SwiftUI
|
||||
|
||||
public class Client: ObservableObject, Equatable {
|
||||
public static func == (lhs: Client, rhs: Client) -> Bool {
|
||||
lhs.isAuth == rhs.isAuth &&
|
||||
lhs.server == rhs.server &&
|
||||
lhs.oauthToken?.accessToken == rhs.oauthToken?.accessToken
|
||||
lhs.server == rhs.server &&
|
||||
lhs.oauthToken?.accessToken == rhs.oauthToken?.accessToken
|
||||
}
|
||||
|
||||
public enum Version: String {
|
||||
|
@ -36,10 +36,10 @@ public class Client: ObservableObject, Equatable {
|
|||
public init(server: String, version: Version = .v1, oauthToken: OauthToken? = nil) {
|
||||
self.server = server
|
||||
self.version = version
|
||||
self.urlSession = URLSession.shared
|
||||
self.decoder.keyDecodingStrategy = .convertFromSnakeCase
|
||||
urlSession = URLSession.shared
|
||||
decoder.keyDecodingStrategy = .convertFromSnakeCase
|
||||
self.oauthToken = oauthToken
|
||||
self.connections = Set([server])
|
||||
connections = Set([server])
|
||||
}
|
||||
|
||||
public func addConnections(_ connections: [String]) {
|
||||
|
@ -99,7 +99,8 @@ public class Client: ObservableObject, Equatable {
|
|||
let (data, httpResponse) = try await urlSession.data(for: makeGet(endpoint: endpoint))
|
||||
var linkHandler: LinkHandler?
|
||||
if let response = httpResponse as? HTTPURLResponse,
|
||||
let link = response.allHeaderFields["Link"] as? String{
|
||||
let link = response.allHeaderFields["Link"] as? String
|
||||
{
|
||||
linkHandler = .init(rawLink: link)
|
||||
}
|
||||
logResponseOnError(httpResponse: httpResponse, data: data)
|
||||
|
@ -137,7 +138,8 @@ public class Client: ObservableObject, Equatable {
|
|||
|
||||
private func makeEntityRequest<Entity: Decodable>(endpoint: Endpoint,
|
||||
method: String,
|
||||
forceVersion: Version? = nil) async throws -> Entity {
|
||||
forceVersion: Version? = nil) async throws -> Entity
|
||||
{
|
||||
let url = makeURL(endpoint: endpoint, forceVersion: forceVersion)
|
||||
let request = makeURLRequest(url: url, endpoint: endpoint, httpMethod: method)
|
||||
let (data, httpResponse) = try await urlSession.data(for: request)
|
||||
|
@ -147,7 +149,7 @@ public class Client: ObservableObject, Equatable {
|
|||
|
||||
public func oauthURL() async throws -> URL {
|
||||
let app: InstanceApp = try await post(endpoint: Apps.registerApp)
|
||||
self.oauthApp = app
|
||||
oauthApp = app
|
||||
return makeURL(endpoint: Oauth.authorize(clientId: app.clientId))
|
||||
}
|
||||
|
||||
|
@ -156,13 +158,14 @@ public class Client: ObservableObject, Equatable {
|
|||
throw OauthError.missingApp
|
||||
}
|
||||
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
|
||||
let code = components.queryItems?.first(where: { $0.name == "code"})?.value else {
|
||||
let code = components.queryItems?.first(where: { $0.name == "code" })?.value
|
||||
else {
|
||||
throw OauthError.invalidRedirectURL
|
||||
}
|
||||
let token: OauthToken = try await post(endpoint: Oauth.token(code: code,
|
||||
clientId: app.clientId,
|
||||
clientSecret: app.clientSecret))
|
||||
self.oauthToken = token
|
||||
oauthToken = token
|
||||
return token
|
||||
}
|
||||
|
||||
|
@ -177,7 +180,8 @@ public class Client: ObservableObject, Equatable {
|
|||
method: String,
|
||||
mimeType: String,
|
||||
filename: String,
|
||||
data: Data) async throws -> Entity {
|
||||
data: Data) async throws -> Entity
|
||||
{
|
||||
let url = makeURL(endpoint: endpoint, forceVersion: version)
|
||||
var request = makeURLRequest(url: url, endpoint: endpoint, httpMethod: method)
|
||||
let boundary = UUID().uuidString
|
||||
|
|
|
@ -33,7 +33,7 @@ public enum Accounts: Endpoint {
|
|||
|
||||
public func path() -> String {
|
||||
switch self {
|
||||
case .accounts(let id):
|
||||
case let .accounts(id):
|
||||
return "accounts/\(id)"
|
||||
case .favourites:
|
||||
return "favourites"
|
||||
|
@ -41,29 +41,29 @@ public enum Accounts: Endpoint {
|
|||
return "bookmarks"
|
||||
case .followedTags:
|
||||
return "followed_tags"
|
||||
case .featuredTags(let id):
|
||||
case let .featuredTags(id):
|
||||
return "accounts/\(id)/featured_tags"
|
||||
case .verifyCredentials:
|
||||
return "accounts/verify_credentials"
|
||||
case .updateCredentials:
|
||||
return "accounts/update_credentials"
|
||||
case .statuses(let id, _, _, _, _, _):
|
||||
case let .statuses(id, _, _, _, _, _):
|
||||
return "accounts/\(id)/statuses"
|
||||
case .relationships:
|
||||
return "accounts/relationships"
|
||||
case .follow(let id, _):
|
||||
case let .follow(id, _):
|
||||
return "accounts/\(id)/follow"
|
||||
case .unfollow(let id):
|
||||
case let .unfollow(id):
|
||||
return "accounts/\(id)/unfollow"
|
||||
case .familiarFollowers:
|
||||
return "accounts/familiar_followers"
|
||||
case .suggestions:
|
||||
return "suggestions"
|
||||
case .following(let id, _):
|
||||
case let .following(id, _):
|
||||
return "accounts/\(id)/following"
|
||||
case .followers(let id, _):
|
||||
case let .followers(id, _):
|
||||
return "accounts/\(id)/followers"
|
||||
case .lists(let id):
|
||||
case let .lists(id):
|
||||
return "accounts/\(id)/lists"
|
||||
case .preferences:
|
||||
return "preferences"
|
||||
|
@ -72,7 +72,7 @@ public enum Accounts: Endpoint {
|
|||
|
||||
public func queryItems() -> [URLQueryItem]? {
|
||||
switch self {
|
||||
case .statuses(_, let sinceId, let tag, let onlyMedia, let excludeReplies, let pinned):
|
||||
case let .statuses(_, sinceId, tag, onlyMedia, excludeReplies, pinned):
|
||||
var params: [URLQueryItem] = []
|
||||
if let tag {
|
||||
params.append(.init(name: "tagged", value: tag))
|
||||
|
|
|
@ -18,7 +18,7 @@ public enum Apps: Endpoint {
|
|||
.init(name: "client_name", value: AppInfo.clientName),
|
||||
.init(name: "redirect_uris", value: AppInfo.scheme),
|
||||
.init(name: "scopes", value: AppInfo.scopes),
|
||||
.init(name: "website", value: AppInfo.weblink)
|
||||
.init(name: "website", value: AppInfo.weblink),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ public protocol Endpoint {
|
|||
var jsonValue: Encodable? { get }
|
||||
}
|
||||
|
||||
extension Endpoint {
|
||||
public var jsonValue: Encodable? {
|
||||
public extension Endpoint {
|
||||
var jsonValue: Encodable? {
|
||||
nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ public enum Lists: Endpoint {
|
|||
|
||||
public func queryItems() -> [URLQueryItem]? {
|
||||
switch self {
|
||||
case .accounts(_):
|
||||
case .accounts:
|
||||
return [.init(name: "limit", value: String(0))]
|
||||
case let .createList(title):
|
||||
return [.init(name: "title", value: title)]
|
||||
|
|
|
@ -17,7 +17,7 @@ public enum Notifications: Endpoint {
|
|||
|
||||
public func queryItems() -> [URLQueryItem]? {
|
||||
switch self {
|
||||
case .notifications(let sinceId, let maxId, let types):
|
||||
case let .notifications(sinceId, maxId, types):
|
||||
var params = makePaginationParam(sinceId: sinceId, maxId: maxId, mindId: nil) ?? []
|
||||
if let types {
|
||||
for type in types {
|
||||
|
|
|
@ -21,7 +21,7 @@ public enum Oauth: Endpoint {
|
|||
.init(name: "response_type", value: "code"),
|
||||
.init(name: "client_id", value: clientId),
|
||||
.init(name: "redirect_uri", value: AppInfo.scheme),
|
||||
.init(name: "scope", value: AppInfo.scopes)
|
||||
.init(name: "scope", value: AppInfo.scopes),
|
||||
]
|
||||
case let .token(code, clientId, clientSecret):
|
||||
return [
|
||||
|
@ -30,7 +30,7 @@ public enum Oauth: Endpoint {
|
|||
.init(name: "client_secret", value: clientSecret),
|
||||
.init(name: "redirect_uri", value: AppInfo.scheme),
|
||||
.init(name: "code", value: code),
|
||||
.init(name: "scope", value: AppInfo.scopes)
|
||||
.init(name: "scope", value: AppInfo.scopes),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue