Impressia/Vernissage/VernissageApp.swift

198 lines
8.0 KiB
Swift
Raw Normal View History

2022-12-28 09:37:16 +01:00
//
// https://mczachurski.dev
// Copyright © 2022 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import SwiftUI
2023-01-24 20:38:21 +01:00
import Nuke
import NukeUI
2022-12-28 09:37:16 +01:00
@main
2023-01-03 14:09:22 +01:00
struct VernissageApp: App {
2022-12-31 16:31:05 +01:00
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
let coreDataHandler = CoreDataHandler.shared
2023-02-03 15:16:30 +01:00
2023-01-23 18:01:27 +01:00
@StateObject var applicationState = ApplicationState.shared
2023-02-03 15:16:30 +01:00
@StateObject var client = Client.shared
@StateObject var routerPath = RouterPath()
2023-02-14 07:32:00 +01:00
@StateObject var tipsStore = TipsStore()
2022-12-31 16:31:05 +01:00
@State var applicationViewMode: ApplicationViewMode = .loading
2023-01-12 18:34:48 +01:00
@State var tintColor = ApplicationState.shared.tintColor.color()
2023-01-13 13:37:01 +01:00
@State var theme = ApplicationState.shared.theme.colorScheme()
let timer = Timer.publish(every: 120, on: .main, in: .common).autoconnect()
2023-02-03 15:16:30 +01:00
2022-12-28 09:37:16 +01:00
var body: some Scene {
WindowGroup {
2023-01-23 18:01:27 +01:00
NavigationStack(path: $routerPath.path) {
2022-12-31 16:31:05 +01:00
switch applicationViewMode {
case .loading:
2023-01-06 13:44:02 +01:00
LoadingView()
2023-01-23 18:01:27 +01:00
.withAppRouteur()
.withSheetDestinations(sheetDestinations: $routerPath.presentedSheet)
2022-12-31 16:31:05 +01:00
case .signIn:
2023-02-20 16:41:18 +01:00
SignInView { accountData in
self.setApplicationState(accountData: accountData)
2022-12-31 16:31:05 +01:00
}
2023-01-23 18:01:27 +01:00
.withAppRouteur()
.withSheetDestinations(sheetDestinations: $routerPath.presentedSheet)
2022-12-31 16:31:05 +01:00
case .mainView:
2023-01-23 18:01:27 +01:00
MainView()
.withAppRouteur()
.withSheetDestinations(sheetDestinations: $routerPath.presentedSheet)
2023-02-13 21:10:07 +01:00
.withOverlayDestinations(overlayDestinations: $routerPath.presentedOverlay)
2022-12-31 16:31:05 +01:00
}
}
2023-01-23 18:01:27 +01:00
.environment(\.managedObjectContext, coreDataHandler.container.viewContext)
.environmentObject(applicationState)
2023-02-03 15:16:30 +01:00
.environmentObject(client)
2023-01-23 18:01:27 +01:00
.environmentObject(routerPath)
2023-02-14 07:32:00 +01:00
.environmentObject(tipsStore)
2023-01-12 18:34:48 +01:00
.tint(self.tintColor)
2023-01-13 13:37:01 +01:00
.preferredColorScheme(self.theme)
2023-01-12 18:34:48 +01:00
.task {
2023-01-14 20:45:49 +01:00
UIPageControl.appearance().currentPageIndicatorTintColor = UIColor.label
UIPageControl.appearance().pageIndicatorTintColor = UIColor.secondaryLabel
2023-01-24 20:38:21 +01:00
// Set custom configurations for Nuke image/data loaders.
self.setImagePipelines()
2023-01-24 12:22:53 +01:00
2023-01-24 20:38:21 +01:00
// Load user preferences from database.
self.loadUserPreferences()
2023-01-13 13:37:01 +01:00
2023-02-06 14:50:28 +01:00
// Refresh other access tokens.
await self.refreshAccessTokens()
2023-01-24 20:38:21 +01:00
// Verify access token correctness.
2023-01-29 19:11:44 +01:00
let authorizationSession = AuthorizationSession()
2023-02-12 09:13:04 +01:00
let currentAccount = AccountDataHandler.shared.getCurrentAccountData()
await AuthorizationService.shared.verifyAccount(session: authorizationSession, currentAccount: currentAccount) { accountData in
2023-01-03 14:09:22 +01:00
guard let accountData = accountData else {
self.applicationViewMode = .signIn
return
}
2022-12-31 16:31:05 +01:00
2023-02-20 16:41:18 +01:00
self.setApplicationState(accountData: accountData, checkNewPhotos: true)
2023-01-29 19:11:44 +01:00
}
2022-12-29 17:27:15 +01:00
}
.navigationViewStyle(.stack)
2023-01-06 13:05:21 +01:00
.onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)) { _ in
Task {
// Refresh indicator of new photos when application is become active.
2023-02-22 14:33:03 +01:00
await self.calculateNewPhotosInBackground()
}
2023-01-06 13:05:21 +01:00
}
.onReceive(timer) { time in
Task {
// Refresh indicator of new photos each two minutes (when application is in the foreground)..
2023-02-22 14:33:03 +01:00
await self.calculateNewPhotosInBackground()
}
}
2023-01-23 18:01:27 +01:00
.onChange(of: applicationState.theme) { newValue in
self.theme = newValue.colorScheme()
}
.onChange(of: applicationState.tintColor) { newValue in
self.tintColor = newValue.color()
}
2023-02-23 08:09:02 +01:00
.onChange(of: applicationState.account) { newValue in
if newValue == nil {
self.applicationViewMode = .signIn
}
}
2022-12-28 09:37:16 +01:00
}
}
2023-02-20 16:41:18 +01:00
private func setApplicationState(accountData: AccountData, checkNewPhotos: Bool = false) {
Task { @MainActor in
let accountModel = AccountModel(accountData: accountData)
let instance = try? await self.client.instances.instance(url: accountModel.serverUrl)
// Refresh client state.
2023-02-20 16:41:18 +01:00
self.client.setAccount(account: accountModel)
// Refresh application state.
self.applicationState.changeApplicationState(accountModel: accountModel,
instance: instance,
lastSeenStatusId: accountData.lastSeenStatusId)
// Change view displayed by application.
2023-02-20 16:41:18 +01:00
self.applicationViewMode = .mainView
// Check amount of newly added photos.
if checkNewPhotos {
2023-02-22 14:33:03 +01:00
await self.calculateNewPhotosInBackground()
2023-02-20 16:41:18 +01:00
}
}
}
2023-01-24 20:38:21 +01:00
private func loadUserPreferences() {
let defaultSettings = ApplicationSettingsHandler.shared.getDefaultSettings()
if let tintColor = TintColor(rawValue: Int(defaultSettings.tintColor)) {
self.applicationState.tintColor = tintColor
self.tintColor = tintColor.color()
}
if let theme = Theme(rawValue: Int(defaultSettings.theme)) {
self.applicationState.theme = theme
self.theme = theme.colorScheme()
}
if let avatarShape = AvatarShape(rawValue: Int(defaultSettings.avatarShape)) {
self.applicationState.avatarShape = avatarShape
}
}
private func setImagePipelines() {
let pipeline = ImagePipeline {
$0.dataLoader = DataLoader(configuration: {
// Disable disk caching built into URLSession
let conf = DataLoader.defaultConfiguration
conf.urlCache = nil
return conf
}())
$0.imageCache = ImageCache.shared
2023-01-29 19:11:44 +01:00
$0.dataCache = try! DataCache(name: AppConstants.imagePipelineCacheName)
2023-01-24 20:38:21 +01:00
}
ImagePipeline.shared = pipeline
}
2023-02-06 14:50:28 +01:00
private func refreshAccessTokens() async {
let defaultSettings = ApplicationSettingsHandler.shared.getDefaultSettings()
// Run refreshing access tokens once per day.
guard let refreshTokenDate = Calendar.current.date(byAdding: .day, value: 1, to: defaultSettings.lastRefreshTokens), refreshTokenDate < Date.now else {
return
}
// Refresh access tokens.
await AuthorizationService.shared.refreshAccessTokens()
// Update time when refresh tokens has been updated.
defaultSettings.lastRefreshTokens = Date.now
CoreDataHandler.shared.save()
}
2023-02-22 14:33:03 +01:00
private func calculateNewPhotosInBackground() async {
if let account = self.applicationState.account {
self.applicationState.amountOfNewStatuses = await HomeTimelineService.shared.amountOfNewStatuses(for: account)
}
}
2022-12-31 16:31:05 +01:00
}
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication,
configurationForConnecting connectingSceneSession: UISceneSession,
options: UIScene.ConnectionOptions
) -> UISceneConfiguration {
let sceneConfig: UISceneConfiguration = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role)
sceneConfig.delegateClass = SceneDelegate.self
return sceneConfig
}
2022-12-28 09:37:16 +01:00
}