mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2025-04-27 02:14:45 +00:00
Refactor + add more shortcuts on macOS
This commit is contained in:
parent
494b0df0e3
commit
cf0f0fd891
18 changed files with 500 additions and 372 deletions
|
@ -80,6 +80,10 @@
|
||||||
9FAE4ACE29379A5A00772766 /* KeychainSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 9FAE4ACD29379A5A00772766 /* KeychainSwift */; };
|
9FAE4ACE29379A5A00772766 /* KeychainSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 9FAE4ACD29379A5A00772766 /* KeychainSwift */; };
|
||||||
9FB143D12983104700A27BB1 /* glass.caf in Resources */ = {isa = PBXBuildFile; fileRef = 9F2A542B296B1177009B2D7C /* glass.caf */; };
|
9FB143D12983104700A27BB1 /* glass.caf in Resources */ = {isa = PBXBuildFile; fileRef = 9F2A542B296B1177009B2D7C /* glass.caf */; };
|
||||||
9FB143D22983104A00A27BB1 /* glass.wav in Resources */ = {isa = PBXBuildFile; fileRef = 9F2A542D296B1CC0009B2D7C /* glass.wav */; };
|
9FB143D22983104A00A27BB1 /* glass.wav in Resources */ = {isa = PBXBuildFile; fileRef = 9F2A542D296B1CC0009B2D7C /* glass.wav */; };
|
||||||
|
9FB183222AE9268800BBB692 /* IceCubesApp+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FB183212AE9268800BBB692 /* IceCubesApp+Menu.swift */; };
|
||||||
|
9FB183252AE926E900BBB692 /* IceCubesApp+Sidebar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FB183242AE926E900BBB692 /* IceCubesApp+Sidebar.swift */; };
|
||||||
|
9FB183272AE9279F00BBB692 /* IceCubesApp+Tabbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FB183262AE9279F00BBB692 /* IceCubesApp+Tabbar.swift */; };
|
||||||
|
9FB183292AE9449100BBB692 /* IceCubesApp+Scene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FB183282AE9449100BBB692 /* IceCubesApp+Scene.swift */; };
|
||||||
9FBFE63D292A715500C250E9 /* IceCubesApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FBFE63C292A715500C250E9 /* IceCubesApp.swift */; };
|
9FBFE63D292A715500C250E9 /* IceCubesApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FBFE63C292A715500C250E9 /* IceCubesApp.swift */; };
|
||||||
9FBFE64E292A72BD00C250E9 /* Network in Frameworks */ = {isa = PBXBuildFile; productRef = 9FBFE64D292A72BD00C250E9 /* Network */; };
|
9FBFE64E292A72BD00C250E9 /* Network in Frameworks */ = {isa = PBXBuildFile; productRef = 9FBFE64D292A72BD00C250E9 /* Network */; };
|
||||||
9FD34823293D06E800DB0EE9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9FD34822293D06E800DB0EE9 /* Assets.xcassets */; };
|
9FD34823293D06E800DB0EE9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9FD34822293D06E800DB0EE9 /* Assets.xcassets */; };
|
||||||
|
@ -214,6 +218,10 @@
|
||||||
9FAD85CE2975B68900496AB1 /* SideBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideBarView.swift; sourceTree = "<group>"; };
|
9FAD85CE2975B68900496AB1 /* SideBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideBarView.swift; sourceTree = "<group>"; };
|
||||||
9FAE4AC8293774FF00772766 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
9FAE4AC8293774FF00772766 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||||
9FAE4ACA293783B000772766 /* SettingsTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTab.swift; sourceTree = "<group>"; };
|
9FAE4ACA293783B000772766 /* SettingsTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTab.swift; sourceTree = "<group>"; };
|
||||||
|
9FB183212AE9268800BBB692 /* IceCubesApp+Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IceCubesApp+Menu.swift"; sourceTree = "<group>"; };
|
||||||
|
9FB183242AE926E900BBB692 /* IceCubesApp+Sidebar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IceCubesApp+Sidebar.swift"; sourceTree = "<group>"; };
|
||||||
|
9FB183262AE9279F00BBB692 /* IceCubesApp+Tabbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IceCubesApp+Tabbar.swift"; sourceTree = "<group>"; };
|
||||||
|
9FB183282AE9449100BBB692 /* IceCubesApp+Scene.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IceCubesApp+Scene.swift"; sourceTree = "<group>"; };
|
||||||
9FBFE639292A715500C250E9 /* IceCubesApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = IceCubesApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
9FBFE639292A715500C250E9 /* IceCubesApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = IceCubesApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
9FBFE63C292A715500C250E9 /* IceCubesApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IceCubesApp.swift; sourceTree = "<group>"; };
|
9FBFE63C292A715500C250E9 /* IceCubesApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IceCubesApp.swift; sourceTree = "<group>"; };
|
||||||
9FBFE642292A715600C250E9 /* IceCubesApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = IceCubesApp.entitlements; sourceTree = "<group>"; };
|
9FBFE642292A715600C250E9 /* IceCubesApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = IceCubesApp.entitlements; sourceTree = "<group>"; };
|
||||||
|
@ -337,9 +345,9 @@
|
||||||
9F398AB429360A5800A889F2 /* App */ = {
|
9F398AB429360A5800A889F2 /* App */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
9FB183232AE926BB00BBB692 /* Main */,
|
||||||
9F654BF0299AC46200D27FA5 /* Report */,
|
9F654BF0299AC46200D27FA5 /* Report */,
|
||||||
9FAE4AC9293783A200772766 /* Tabs */,
|
9FAE4AC9293783A200772766 /* Tabs */,
|
||||||
9FBFE63C292A715500C250E9 /* IceCubesApp.swift */,
|
|
||||||
9F398AA52935FE8A00A889F2 /* AppRegistry.swift */,
|
9F398AA52935FE8A00A889F2 /* AppRegistry.swift */,
|
||||||
639CDF9B296AC82F00C35E58 /* SafariRouter.swift */,
|
639CDF9B296AC82F00C35E58 /* SafariRouter.swift */,
|
||||||
9FAD85CE2975B68900496AB1 /* SideBarView.swift */,
|
9FAD85CE2975B68900496AB1 /* SideBarView.swift */,
|
||||||
|
@ -418,6 +426,18 @@
|
||||||
path = Tabs;
|
path = Tabs;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
9FB183232AE926BB00BBB692 /* Main */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
9FBFE63C292A715500C250E9 /* IceCubesApp.swift */,
|
||||||
|
9FB183212AE9268800BBB692 /* IceCubesApp+Menu.swift */,
|
||||||
|
9FB183242AE926E900BBB692 /* IceCubesApp+Sidebar.swift */,
|
||||||
|
9FB183262AE9279F00BBB692 /* IceCubesApp+Tabbar.swift */,
|
||||||
|
9FB183282AE9449100BBB692 /* IceCubesApp+Scene.swift */,
|
||||||
|
);
|
||||||
|
path = Main;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
9FBFE630292A715500C250E9 = {
|
9FBFE630292A715500C250E9 = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -791,16 +811,20 @@
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
9FE151A6293C90F900E9683D /* IconSelectorView.swift in Sources */,
|
9FE151A6293C90F900E9683D /* IconSelectorView.swift in Sources */,
|
||||||
|
9FB183222AE9268800BBB692 /* IceCubesApp+Menu.swift in Sources */,
|
||||||
|
9FB183252AE926E900BBB692 /* IceCubesApp+Sidebar.swift in Sources */,
|
||||||
9F7D939A29805DBD00EE6B7A /* AccountSettingView.swift in Sources */,
|
9F7D939A29805DBD00EE6B7A /* AccountSettingView.swift in Sources */,
|
||||||
069709AA298C9AD7006E4CB5 /* AboutView.swift in Sources */,
|
069709AA298C9AD7006E4CB5 /* AboutView.swift in Sources */,
|
||||||
9F2B92FC295DA94500DE16D0 /* InstanceInfoView.swift in Sources */,
|
9F2B92FC295DA94500DE16D0 /* InstanceInfoView.swift in Sources */,
|
||||||
C9B22677297F6C2E001F9EFE /* ContentSettingsView.swift in Sources */,
|
C9B22677297F6C2E001F9EFE /* ContentSettingsView.swift in Sources */,
|
||||||
9FA6FD6229C04A8800E2312C /* TranslationSettingsView.swift in Sources */,
|
9FA6FD6229C04A8800E2312C /* TranslationSettingsView.swift in Sources */,
|
||||||
|
9FB183272AE9279F00BBB692 /* IceCubesApp+Tabbar.swift in Sources */,
|
||||||
9F35DB4C2952005C00B3281A /* MessagesTab.swift in Sources */,
|
9F35DB4C2952005C00B3281A /* MessagesTab.swift in Sources */,
|
||||||
9FAD85CF2975B68900496AB1 /* SideBarView.swift in Sources */,
|
9FAD85CF2975B68900496AB1 /* SideBarView.swift in Sources */,
|
||||||
9FAE4ACB293783B000772766 /* SettingsTab.swift in Sources */,
|
9FAE4ACB293783B000772766 /* SettingsTab.swift in Sources */,
|
||||||
9F7335F92968576500AFF0BA /* DisplaySettingsView.swift in Sources */,
|
9F7335F92968576500AFF0BA /* DisplaySettingsView.swift in Sources */,
|
||||||
9F2A540729699698009B2D7C /* SupportAppView.swift in Sources */,
|
9F2A540729699698009B2D7C /* SupportAppView.swift in Sources */,
|
||||||
|
9FB183292AE9449100BBB692 /* IceCubesApp+Scene.swift in Sources */,
|
||||||
9F2B92F6295AE04800DE16D0 /* Tabs.swift in Sources */,
|
9F2B92F6295AE04800DE16D0 /* Tabs.swift in Sources */,
|
||||||
FA31A9AB2A66BF7C00D5F662 /* EditTagGroupView.swift in Sources */,
|
FA31A9AB2A66BF7C00D5F662 /* EditTagGroupView.swift in Sources */,
|
||||||
FAD203D02A66D8A80030A7FD /* Symbols.swift in Sources */,
|
FAD203D02A66D8A80030A7FD /* Symbols.swift in Sources */,
|
||||||
|
@ -891,7 +915,7 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.9.3;
|
MARKETING_VERSION = 1.9.4;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_ID_PREFIX).IceCubesApp.IceCubesNotifications";
|
PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_ID_PREFIX).IceCubesApp.IceCubesNotifications";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
|
@ -925,7 +949,7 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.9.3;
|
MARKETING_VERSION = 1.9.4;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_ID_PREFIX).IceCubesApp.IceCubesNotifications";
|
PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_ID_PREFIX).IceCubesApp.IceCubesNotifications";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
|
@ -960,7 +984,7 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.9.3;
|
MARKETING_VERSION = 1.9.4;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_ID_PREFIX).IceCubesApp.IceCubesShareExtension";
|
PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_ID_PREFIX).IceCubesApp.IceCubesShareExtension";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
|
@ -994,7 +1018,7 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.9.3;
|
MARKETING_VERSION = 1.9.4;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_ID_PREFIX).IceCubesApp.IceCubesShareExtension";
|
PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_ID_PREFIX).IceCubesApp.IceCubesShareExtension";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
|
@ -1174,7 +1198,7 @@
|
||||||
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
||||||
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
MARKETING_VERSION = 1.9.3;
|
MARKETING_VERSION = 1.9.4;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_ID_PREFIX).IceCubesApp";
|
PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_ID_PREFIX).IceCubesApp";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = auto;
|
SDKROOT = auto;
|
||||||
|
@ -1228,7 +1252,7 @@
|
||||||
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
||||||
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
MARKETING_VERSION = 1.9.3;
|
MARKETING_VERSION = 1.9.4;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_ID_PREFIX).IceCubesApp";
|
PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_ID_PREFIX).IceCubesApp";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = auto;
|
SDKROOT = auto;
|
||||||
|
@ -1263,7 +1287,7 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.9.3;
|
MARKETING_VERSION = 1.9.4;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_ID_PREFIX).IceCubesApp.IceCubesActionExtension";
|
PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_ID_PREFIX).IceCubesApp.IceCubesActionExtension";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
|
@ -1298,7 +1322,7 @@
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.9.3;
|
MARKETING_VERSION = 1.9.4;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_ID_PREFIX).IceCubesApp.IceCubesActionExtension";
|
PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_ID_PREFIX).IceCubesApp.IceCubesActionExtension";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
|
|
|
@ -10,6 +10,7 @@ import Models
|
||||||
import Status
|
import Status
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Timeline
|
import Timeline
|
||||||
|
import MediaUI
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
extension View {
|
extension View {
|
||||||
|
@ -130,6 +131,7 @@ extension View {
|
||||||
.environment(AppAccountsManager.shared)
|
.environment(AppAccountsManager.shared)
|
||||||
.environment(PushNotificationsService.shared)
|
.environment(PushNotificationsService.shared)
|
||||||
.environment(AppAccountsManager.shared.currentClient)
|
.environment(AppAccountsManager.shared.currentClient)
|
||||||
|
.environment(QuickLook.shared)
|
||||||
}
|
}
|
||||||
|
|
||||||
func withModelContainer() -> some View {
|
func withModelContainer() -> some View {
|
||||||
|
|
|
@ -1,339 +0,0 @@
|
||||||
import Account
|
|
||||||
import AppAccount
|
|
||||||
import AVFoundation
|
|
||||||
import DesignSystem
|
|
||||||
import Env
|
|
||||||
import KeychainSwift
|
|
||||||
import Network
|
|
||||||
import RevenueCat
|
|
||||||
import Status
|
|
||||||
import SwiftUI
|
|
||||||
import Timeline
|
|
||||||
import MediaUI
|
|
||||||
|
|
||||||
@main
|
|
||||||
struct IceCubesApp: App {
|
|
||||||
@UIApplicationDelegateAdaptor private var appDelegate: AppDelegate
|
|
||||||
|
|
||||||
@Environment(\.scenePhase) private var scenePhase
|
|
||||||
@Environment(\.openWindow) private var openWindow
|
|
||||||
|
|
||||||
@State private var appAccountsManager = AppAccountsManager.shared
|
|
||||||
@State private var currentInstance = CurrentInstance.shared
|
|
||||||
@State private var currentAccount = CurrentAccount.shared
|
|
||||||
@State private var userPreferences = UserPreferences.shared
|
|
||||||
@State private var pushNotificationsService = PushNotificationsService.shared
|
|
||||||
@State private var watcher = StreamWatcher()
|
|
||||||
@State private var quickLook = QuickLook()
|
|
||||||
@State private var theme = Theme.shared
|
|
||||||
@State private var sidebarRouterPath = RouterPath()
|
|
||||||
|
|
||||||
@State private var selectedTab: Tab = .timeline
|
|
||||||
@State private var popToRootTab: Tab = .other
|
|
||||||
@State private var sideBarLoadedTabs: Set<Tab> = Set()
|
|
||||||
@State private var isSupporter: Bool = false
|
|
||||||
|
|
||||||
private var availableTabs: [Tab] {
|
|
||||||
appAccountsManager.currentClient.isAuth ? Tab.loggedInTabs() : Tab.loggedOutTab()
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some Scene {
|
|
||||||
appScene
|
|
||||||
otherScenes
|
|
||||||
}
|
|
||||||
|
|
||||||
private var appScene: some Scene {
|
|
||||||
WindowGroup(id: "MainWindow") {
|
|
||||||
appView
|
|
||||||
.applyTheme(theme)
|
|
||||||
.onAppear {
|
|
||||||
setNewClientsInEnv(client: appAccountsManager.currentClient)
|
|
||||||
setupRevenueCat()
|
|
||||||
refreshPushSubs()
|
|
||||||
}
|
|
||||||
.environment(appAccountsManager)
|
|
||||||
.environment(appAccountsManager.currentClient)
|
|
||||||
.environment(quickLook)
|
|
||||||
.environment(currentAccount)
|
|
||||||
.environment(currentInstance)
|
|
||||||
.environment(userPreferences)
|
|
||||||
.environment(theme)
|
|
||||||
.environment(watcher)
|
|
||||||
.environment(pushNotificationsService)
|
|
||||||
.environment(\.isSupporter, isSupporter)
|
|
||||||
.sheet(item: $quickLook.selectedMediaAttachment) { selectedMediaAttachment in
|
|
||||||
MediaUIView(selectedAttachment: selectedMediaAttachment,
|
|
||||||
attachments: quickLook.mediaAttachments)
|
|
||||||
.presentationBackground(.ultraThinMaterial)
|
|
||||||
.presentationCornerRadius(16)
|
|
||||||
.withEnvironments()
|
|
||||||
}
|
|
||||||
.onChange(of: pushNotificationsService.handledNotification) { _, newValue in
|
|
||||||
if newValue != nil {
|
|
||||||
pushNotificationsService.handledNotification = nil
|
|
||||||
if appAccountsManager.currentAccount.oauthToken?.accessToken != newValue?.account.token.accessToken,
|
|
||||||
let account = appAccountsManager.availableAccounts.first(where:
|
|
||||||
{ $0.oauthToken?.accessToken == newValue?.account.token.accessToken })
|
|
||||||
{
|
|
||||||
appAccountsManager.currentAccount = account
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
|
|
||||||
selectedTab = .notifications
|
|
||||||
pushNotificationsService.handledNotification = newValue
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
selectedTab = .notifications
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.withModelContainer()
|
|
||||||
}
|
|
||||||
.commands {
|
|
||||||
appMenu
|
|
||||||
}
|
|
||||||
.onChange(of: scenePhase) { _, newValue in
|
|
||||||
handleScenePhase(scenePhase: newValue)
|
|
||||||
}
|
|
||||||
.onChange(of: appAccountsManager.currentClient) { _, newValue in
|
|
||||||
setNewClientsInEnv(client: newValue)
|
|
||||||
if newValue.isAuth {
|
|
||||||
watcher.watch(streams: [.user, .direct])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ViewBuilder
|
|
||||||
private var appView: some View {
|
|
||||||
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
|
||||||
sidebarView
|
|
||||||
} else {
|
|
||||||
tabBarView
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func badgeFor(tab: Tab) -> Int {
|
|
||||||
if tab == .notifications, selectedTab != tab,
|
|
||||||
let token = appAccountsManager.currentAccount.oauthToken
|
|
||||||
{
|
|
||||||
return watcher.unreadNotificationsCount + (userPreferences.notificationsCount[token] ?? 0)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
private var sidebarView: some View {
|
|
||||||
SideBarView(selectedTab: $selectedTab,
|
|
||||||
popToRootTab: $popToRootTab,
|
|
||||||
tabs: availableTabs)
|
|
||||||
{
|
|
||||||
HStack(spacing: 0) {
|
|
||||||
ZStack {
|
|
||||||
if selectedTab == .profile {
|
|
||||||
ProfileTab(popToRootTab: $popToRootTab)
|
|
||||||
}
|
|
||||||
ForEach(availableTabs) { tab in
|
|
||||||
if tab == selectedTab || sideBarLoadedTabs.contains(tab) {
|
|
||||||
tab
|
|
||||||
.makeContentView(popToRootTab: $popToRootTab)
|
|
||||||
.opacity(tab == selectedTab ? 1 : 0)
|
|
||||||
.transition(.opacity)
|
|
||||||
.id("\(tab)\(appAccountsManager.currentAccount.id)")
|
|
||||||
.onAppear {
|
|
||||||
sideBarLoadedTabs.insert(tab)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
EmptyView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if appAccountsManager.currentClient.isAuth,
|
|
||||||
userPreferences.showiPadSecondaryColumn
|
|
||||||
{
|
|
||||||
Divider().edgesIgnoringSafeArea(.all)
|
|
||||||
notificationsSecondaryColumn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.onChange(of: $appAccountsManager.currentAccount.id) {
|
|
||||||
sideBarLoadedTabs.removeAll()
|
|
||||||
}
|
|
||||||
.environment(sidebarRouterPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var notificationsSecondaryColumn: some View {
|
|
||||||
NotificationsTab(popToRootTab: $popToRootTab, lockedType: nil)
|
|
||||||
.environment(\.isSecondaryColumn, true)
|
|
||||||
.frame(maxWidth: .secondaryColumnWidth)
|
|
||||||
.id(appAccountsManager.currentAccount.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var tabBarView: some View {
|
|
||||||
TabView(selection: .init(get: {
|
|
||||||
selectedTab
|
|
||||||
}, set: { newTab in
|
|
||||||
if newTab == selectedTab {
|
|
||||||
/// Stupid hack to trigger onChange binding in tab views.
|
|
||||||
popToRootTab = .other
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
|
|
||||||
popToRootTab = selectedTab
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HapticManager.shared.fireHaptic(of: .tabSelection)
|
|
||||||
SoundEffectManager.shared.playSound(of: .tabSelection)
|
|
||||||
|
|
||||||
selectedTab = newTab
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
if selectedTab == .notifications,
|
|
||||||
let token = appAccountsManager.currentAccount.oauthToken
|
|
||||||
{
|
|
||||||
userPreferences.notificationsCount[token] = 0
|
|
||||||
watcher.unreadNotificationsCount = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
})) {
|
|
||||||
ForEach(availableTabs) { tab in
|
|
||||||
tab.makeContentView(popToRootTab: $popToRootTab)
|
|
||||||
.tabItem {
|
|
||||||
if userPreferences.showiPhoneTabLabel {
|
|
||||||
tab.label
|
|
||||||
} else {
|
|
||||||
Image(systemName: tab.iconName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.tag(tab)
|
|
||||||
.badge(badgeFor(tab: tab))
|
|
||||||
.toolbarBackground(theme.primaryBackgroundColor.opacity(0.50), for: .tabBar)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.id(appAccountsManager.currentClient.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var otherScenes: some Scene {
|
|
||||||
WindowGroup(for: WindowDestination.self) { destination in
|
|
||||||
Group {
|
|
||||||
switch destination.wrappedValue {
|
|
||||||
case let .newStatusEditor(visibility):
|
|
||||||
StatusEditorView(mode: .new(visibility: visibility))
|
|
||||||
case let .mediaViewer(attachments, selectedAttachment):
|
|
||||||
MediaUIView(selectedAttachment: selectedAttachment,
|
|
||||||
attachments: attachments)
|
|
||||||
case .none:
|
|
||||||
EmptyView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.withEnvironments()
|
|
||||||
.withModelContainer()
|
|
||||||
.applyTheme(theme)
|
|
||||||
}
|
|
||||||
.defaultSize(width: 600, height: 800)
|
|
||||||
.windowResizability(.automatic)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func setNewClientsInEnv(client: Client) {
|
|
||||||
currentAccount.setClient(client: client)
|
|
||||||
currentInstance.setClient(client: client)
|
|
||||||
userPreferences.setClient(client: client)
|
|
||||||
Task {
|
|
||||||
await currentInstance.fetchCurrentInstance()
|
|
||||||
watcher.setClient(client: client, instanceStreamingURL: currentInstance.instance?.urls?.streamingApi)
|
|
||||||
watcher.watch(streams: [.user, .direct])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func handleScenePhase(scenePhase: ScenePhase) {
|
|
||||||
switch scenePhase {
|
|
||||||
case .background:
|
|
||||||
watcher.stopWatching()
|
|
||||||
case .active:
|
|
||||||
watcher.watch(streams: [.user, .direct])
|
|
||||||
UNUserNotificationCenter.current().setBadgeCount(0)
|
|
||||||
userPreferences.reloadNotificationsCount(tokens: appAccountsManager.availableAccounts.compactMap(\.oauthToken))
|
|
||||||
Task {
|
|
||||||
await userPreferences.refreshServerPreferences()
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func setupRevenueCat() {
|
|
||||||
Purchases.logLevel = .error
|
|
||||||
Purchases.configure(withAPIKey: "appl_JXmiRckOzXXTsHKitQiicXCvMQi")
|
|
||||||
Purchases.shared.getCustomerInfo { info, _ in
|
|
||||||
if info?.entitlements["Supporter"]?.isActive == true {
|
|
||||||
isSupporter = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func refreshPushSubs() {
|
|
||||||
PushNotificationsService.shared.requestPushNotifications()
|
|
||||||
}
|
|
||||||
|
|
||||||
@CommandsBuilder
|
|
||||||
private var appMenu: some Commands {
|
|
||||||
CommandGroup(replacing: .newItem) {
|
|
||||||
Button("menu.new-window") {
|
|
||||||
openWindow(id: "MainWindow")
|
|
||||||
}
|
|
||||||
.keyboardShortcut("n", modifiers: .shift)
|
|
||||||
Button("menu.new-post") {
|
|
||||||
if ProcessInfo.processInfo.isMacCatalystApp {
|
|
||||||
openWindow(value: WindowDestination.newStatusEditor(visibility: userPreferences.postVisibility))
|
|
||||||
} else {
|
|
||||||
sidebarRouterPath.presentedSheet = .newStatusEditor(visibility: userPreferences.postVisibility)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.keyboardShortcut("n", modifiers: .command)
|
|
||||||
}
|
|
||||||
CommandGroup(replacing: .textFormatting) {
|
|
||||||
Menu("menu.font") {
|
|
||||||
Button("menu.font.bigger") {
|
|
||||||
if theme.fontSizeScale < 1.5 {
|
|
||||||
theme.fontSizeScale += 0.1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button("menu.font.smaller") {
|
|
||||||
if theme.fontSizeScale > 0.5 {
|
|
||||||
theme.fontSizeScale -= 0.1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AppDelegate: NSObject, UIApplicationDelegate {
|
|
||||||
func application(_: UIApplication,
|
|
||||||
didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool
|
|
||||||
{
|
|
||||||
try? AVAudioSession.sharedInstance().setCategory(.ambient)
|
|
||||||
PushNotificationsService.shared.setAccounts(accounts: AppAccountsManager.shared.pushAccounts)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func application(_: UIApplication,
|
|
||||||
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)
|
|
||||||
{
|
|
||||||
PushNotificationsService.shared.pushToken = deviceToken
|
|
||||||
Task {
|
|
||||||
PushNotificationsService.shared.setAccounts(accounts: AppAccountsManager.shared.pushAccounts)
|
|
||||||
await PushNotificationsService.shared.updateSubscriptions(forceCreate: false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func application(_: UIApplication, didFailToRegisterForRemoteNotificationsWithError _: Error) {}
|
|
||||||
|
|
||||||
func application(_: UIApplication, didReceiveRemoteNotification _: [AnyHashable: Any]) async -> UIBackgroundFetchResult {
|
|
||||||
UserPreferences.shared.reloadNotificationsCount(tokens: AppAccountsManager.shared.availableAccounts.compactMap(\.oauthToken))
|
|
||||||
return .noData
|
|
||||||
}
|
|
||||||
|
|
||||||
func application(_: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options _: UIScene.ConnectionOptions) -> UISceneConfiguration {
|
|
||||||
let configuration = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role)
|
|
||||||
if connectingSceneSession.role == .windowApplication {
|
|
||||||
configuration.delegateClass = SceneDelegate.self
|
|
||||||
}
|
|
||||||
return configuration
|
|
||||||
}
|
|
||||||
}
|
|
58
IceCubesApp/App/Main/IceCubesApp+Menu.swift
Normal file
58
IceCubesApp/App/Main/IceCubesApp+Menu.swift
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import SwiftUI
|
||||||
|
import Env
|
||||||
|
|
||||||
|
extension IceCubesApp {
|
||||||
|
@CommandsBuilder
|
||||||
|
var appMenu: some Commands {
|
||||||
|
CommandGroup(replacing: .newItem) {
|
||||||
|
Button("menu.new-window") {
|
||||||
|
openWindow(id: "MainWindow")
|
||||||
|
}
|
||||||
|
.keyboardShortcut("n", modifiers: .shift)
|
||||||
|
Button("menu.new-post") {
|
||||||
|
if ProcessInfo.processInfo.isMacCatalystApp {
|
||||||
|
openWindow(value: WindowDestination.newStatusEditor(visibility: userPreferences.postVisibility))
|
||||||
|
} else {
|
||||||
|
sidebarRouterPath.presentedSheet = .newStatusEditor(visibility: userPreferences.postVisibility)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.keyboardShortcut("n", modifiers: .command)
|
||||||
|
}
|
||||||
|
CommandGroup(replacing: .textFormatting) {
|
||||||
|
Menu("menu.font") {
|
||||||
|
Button("menu.font.bigger") {
|
||||||
|
if theme.fontSizeScale < 1.5 {
|
||||||
|
theme.fontSizeScale += 0.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button("menu.font.smaller") {
|
||||||
|
if theme.fontSizeScale > 0.5 {
|
||||||
|
theme.fontSizeScale -= 0.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CommandMenu("tab.timeline") {
|
||||||
|
Button("timeline.latest") {
|
||||||
|
NotificationCenter.default.post(name: .refreshTimeline, object: nil)
|
||||||
|
}
|
||||||
|
.keyboardShortcut("r", modifiers: .command)
|
||||||
|
Button("timeline.home") {
|
||||||
|
NotificationCenter.default.post(name: .homeTimeline, object: nil)
|
||||||
|
}
|
||||||
|
.keyboardShortcut("h", modifiers: .shift)
|
||||||
|
Button("timeline.trending") {
|
||||||
|
NotificationCenter.default.post(name: .trendingTimeline, object: nil)
|
||||||
|
}
|
||||||
|
.keyboardShortcut("t", modifiers: .shift)
|
||||||
|
Button("timeline.federated") {
|
||||||
|
NotificationCenter.default.post(name: .federatedTimeline, object: nil)
|
||||||
|
}
|
||||||
|
.keyboardShortcut("f", modifiers: .shift)
|
||||||
|
Button("timeline.local") {
|
||||||
|
NotificationCenter.default.post(name: .localTimeline, object: nil)
|
||||||
|
}
|
||||||
|
.keyboardShortcut("l", modifiers: .shift)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
102
IceCubesApp/App/Main/IceCubesApp+Scene.swift
Normal file
102
IceCubesApp/App/Main/IceCubesApp+Scene.swift
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
import SwiftUI
|
||||||
|
import Env
|
||||||
|
import Status
|
||||||
|
import MediaUI
|
||||||
|
|
||||||
|
extension IceCubesApp {
|
||||||
|
var appScene: some Scene {
|
||||||
|
WindowGroup(id: "MainWindow") {
|
||||||
|
appView
|
||||||
|
.applyTheme(theme)
|
||||||
|
.onAppear {
|
||||||
|
setNewClientsInEnv(client: appAccountsManager.currentClient)
|
||||||
|
setupRevenueCat()
|
||||||
|
refreshPushSubs()
|
||||||
|
}
|
||||||
|
.environment(appAccountsManager)
|
||||||
|
.environment(appAccountsManager.currentClient)
|
||||||
|
.environment(quickLook)
|
||||||
|
.environment(currentAccount)
|
||||||
|
.environment(currentInstance)
|
||||||
|
.environment(userPreferences)
|
||||||
|
.environment(theme)
|
||||||
|
.environment(watcher)
|
||||||
|
.environment(pushNotificationsService)
|
||||||
|
.environment(\.isSupporter, isSupporter)
|
||||||
|
.sheet(item: $quickLook.selectedMediaAttachment) { selectedMediaAttachment in
|
||||||
|
MediaUIView(selectedAttachment: selectedMediaAttachment,
|
||||||
|
attachments: quickLook.mediaAttachments)
|
||||||
|
.presentationBackground(.ultraThinMaterial)
|
||||||
|
.presentationCornerRadius(16)
|
||||||
|
.withEnvironments()
|
||||||
|
}
|
||||||
|
.onChange(of: pushNotificationsService.handledNotification) { _, newValue in
|
||||||
|
if newValue != nil {
|
||||||
|
pushNotificationsService.handledNotification = nil
|
||||||
|
if appAccountsManager.currentAccount.oauthToken?.accessToken != newValue?.account.token.accessToken,
|
||||||
|
let account = appAccountsManager.availableAccounts.first(where:
|
||||||
|
{ $0.oauthToken?.accessToken == newValue?.account.token.accessToken })
|
||||||
|
{
|
||||||
|
appAccountsManager.currentAccount = account
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
|
||||||
|
selectedTab = .notifications
|
||||||
|
pushNotificationsService.handledNotification = newValue
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
selectedTab = .notifications
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.withModelContainer()
|
||||||
|
}
|
||||||
|
.commands {
|
||||||
|
appMenu
|
||||||
|
}
|
||||||
|
.onChange(of: scenePhase) { _, newValue in
|
||||||
|
handleScenePhase(scenePhase: newValue)
|
||||||
|
}
|
||||||
|
.onChange(of: appAccountsManager.currentClient) { _, newValue in
|
||||||
|
setNewClientsInEnv(client: newValue)
|
||||||
|
if newValue.isAuth {
|
||||||
|
watcher.watch(streams: [.user, .direct])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
private var appView: some View {
|
||||||
|
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||||
|
sidebarView
|
||||||
|
} else {
|
||||||
|
tabBarView
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var otherScenes: some Scene {
|
||||||
|
WindowGroup(for: WindowDestination.self) { destination in
|
||||||
|
Group {
|
||||||
|
switch destination.wrappedValue {
|
||||||
|
case let .newStatusEditor(visibility):
|
||||||
|
StatusEditorView(mode: .new(visibility: visibility))
|
||||||
|
case let .editStatusEditor(status):
|
||||||
|
StatusEditorView(mode: .edit(status: status))
|
||||||
|
case let .quoteStatusEditor(status):
|
||||||
|
StatusEditorView(mode: .quote(status: status))
|
||||||
|
case let .replyToStatusEditor(status):
|
||||||
|
StatusEditorView(mode: .replyTo(status: status))
|
||||||
|
case let .mediaViewer(attachments, selectedAttachment):
|
||||||
|
MediaUIView(selectedAttachment: selectedAttachment,
|
||||||
|
attachments: attachments)
|
||||||
|
case .none:
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.withEnvironments()
|
||||||
|
.withModelContainer()
|
||||||
|
.applyTheme(theme)
|
||||||
|
}
|
||||||
|
.defaultSize(width: 600, height: 800)
|
||||||
|
.windowResizability(.automatic)
|
||||||
|
}
|
||||||
|
}
|
50
IceCubesApp/App/Main/IceCubesApp+Sidebar.swift
Normal file
50
IceCubesApp/App/Main/IceCubesApp+Sidebar.swift
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
import SwiftUI
|
||||||
|
import Env
|
||||||
|
|
||||||
|
extension IceCubesApp {
|
||||||
|
var sidebarView: some View {
|
||||||
|
SideBarView(selectedTab: $selectedTab,
|
||||||
|
popToRootTab: $popToRootTab,
|
||||||
|
tabs: availableTabs)
|
||||||
|
{
|
||||||
|
HStack(spacing: 0) {
|
||||||
|
ZStack {
|
||||||
|
if selectedTab == .profile {
|
||||||
|
ProfileTab(popToRootTab: $popToRootTab)
|
||||||
|
}
|
||||||
|
ForEach(availableTabs) { tab in
|
||||||
|
if tab == selectedTab || sideBarLoadedTabs.contains(tab) {
|
||||||
|
tab
|
||||||
|
.makeContentView(popToRootTab: $popToRootTab)
|
||||||
|
.opacity(tab == selectedTab ? 1 : 0)
|
||||||
|
.transition(.opacity)
|
||||||
|
.id("\(tab)\(appAccountsManager.currentAccount.id)")
|
||||||
|
.onAppear {
|
||||||
|
sideBarLoadedTabs.insert(tab)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if appAccountsManager.currentClient.isAuth,
|
||||||
|
userPreferences.showiPadSecondaryColumn
|
||||||
|
{
|
||||||
|
Divider().edgesIgnoringSafeArea(.all)
|
||||||
|
notificationsSecondaryColumn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.onChange(of: $appAccountsManager.currentAccount.id) {
|
||||||
|
sideBarLoadedTabs.removeAll()
|
||||||
|
}
|
||||||
|
.environment(sidebarRouterPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
var notificationsSecondaryColumn: some View {
|
||||||
|
NotificationsTab(popToRootTab: $popToRootTab, lockedType: nil)
|
||||||
|
.environment(\.isSecondaryColumn, true)
|
||||||
|
.frame(maxWidth: .secondaryColumnWidth)
|
||||||
|
.id(appAccountsManager.currentAccount.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
58
IceCubesApp/App/Main/IceCubesApp+Tabbar.swift
Normal file
58
IceCubesApp/App/Main/IceCubesApp+Tabbar.swift
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import SwiftUI
|
||||||
|
import Env
|
||||||
|
|
||||||
|
extension IceCubesApp {
|
||||||
|
var tabBarView: some View {
|
||||||
|
TabView(selection: .init(get: {
|
||||||
|
selectedTab
|
||||||
|
}, set: { newTab in
|
||||||
|
if newTab == selectedTab {
|
||||||
|
/// Stupid hack to trigger onChange binding in tab views.
|
||||||
|
popToRootTab = .other
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
|
||||||
|
popToRootTab = selectedTab
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HapticManager.shared.fireHaptic(of: .tabSelection)
|
||||||
|
SoundEffectManager.shared.playSound(of: .tabSelection)
|
||||||
|
|
||||||
|
selectedTab = newTab
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
if selectedTab == .notifications,
|
||||||
|
let token = appAccountsManager.currentAccount.oauthToken
|
||||||
|
{
|
||||||
|
userPreferences.notificationsCount[token] = 0
|
||||||
|
watcher.unreadNotificationsCount = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})) {
|
||||||
|
ForEach(availableTabs) { tab in
|
||||||
|
tab.makeContentView(popToRootTab: $popToRootTab)
|
||||||
|
.tabItem {
|
||||||
|
if userPreferences.showiPhoneTabLabel {
|
||||||
|
tab.label
|
||||||
|
} else {
|
||||||
|
Image(systemName: tab.iconName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.tag(tab)
|
||||||
|
.badge(badgeFor(tab: tab))
|
||||||
|
.toolbarBackground(theme.primaryBackgroundColor.opacity(0.50), for: .tabBar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.id(appAccountsManager.currentClient.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private func badgeFor(tab: Tab) -> Int {
|
||||||
|
if tab == .notifications, selectedTab != tab,
|
||||||
|
let token = appAccountsManager.currentAccount.oauthToken
|
||||||
|
{
|
||||||
|
return watcher.unreadNotificationsCount + (userPreferences.notificationsCount[token] ?? 0)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
120
IceCubesApp/App/Main/IceCubesApp.swift
Normal file
120
IceCubesApp/App/Main/IceCubesApp.swift
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
import Account
|
||||||
|
import AppAccount
|
||||||
|
import AVFoundation
|
||||||
|
import DesignSystem
|
||||||
|
import Env
|
||||||
|
import KeychainSwift
|
||||||
|
import Network
|
||||||
|
import RevenueCat
|
||||||
|
import Status
|
||||||
|
import SwiftUI
|
||||||
|
import Timeline
|
||||||
|
import MediaUI
|
||||||
|
|
||||||
|
@main
|
||||||
|
struct IceCubesApp: App {
|
||||||
|
@UIApplicationDelegateAdaptor private var appDelegate: AppDelegate
|
||||||
|
|
||||||
|
@Environment(\.scenePhase) var scenePhase
|
||||||
|
@Environment(\.openWindow) var openWindow
|
||||||
|
|
||||||
|
@State var appAccountsManager = AppAccountsManager.shared
|
||||||
|
@State var currentInstance = CurrentInstance.shared
|
||||||
|
@State var currentAccount = CurrentAccount.shared
|
||||||
|
@State var userPreferences = UserPreferences.shared
|
||||||
|
@State var pushNotificationsService = PushNotificationsService.shared
|
||||||
|
@State var watcher = StreamWatcher()
|
||||||
|
@State var quickLook = QuickLook.shared
|
||||||
|
@State var theme = Theme.shared
|
||||||
|
@State var sidebarRouterPath = RouterPath()
|
||||||
|
|
||||||
|
@State var selectedTab: Tab = .timeline
|
||||||
|
@State var popToRootTab: Tab = .other
|
||||||
|
@State var sideBarLoadedTabs: Set<Tab> = Set()
|
||||||
|
@State var isSupporter: Bool = false
|
||||||
|
|
||||||
|
var availableTabs: [Tab] {
|
||||||
|
appAccountsManager.currentClient.isAuth ? Tab.loggedInTabs() : Tab.loggedOutTab()
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some Scene {
|
||||||
|
appScene
|
||||||
|
otherScenes
|
||||||
|
}
|
||||||
|
|
||||||
|
func setNewClientsInEnv(client: Client) {
|
||||||
|
currentAccount.setClient(client: client)
|
||||||
|
currentInstance.setClient(client: client)
|
||||||
|
userPreferences.setClient(client: client)
|
||||||
|
Task {
|
||||||
|
await currentInstance.fetchCurrentInstance()
|
||||||
|
watcher.setClient(client: client, instanceStreamingURL: currentInstance.instance?.urls?.streamingApi)
|
||||||
|
watcher.watch(streams: [.user, .direct])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleScenePhase(scenePhase: ScenePhase) {
|
||||||
|
switch scenePhase {
|
||||||
|
case .background:
|
||||||
|
watcher.stopWatching()
|
||||||
|
case .active:
|
||||||
|
watcher.watch(streams: [.user, .direct])
|
||||||
|
UNUserNotificationCenter.current().setBadgeCount(0)
|
||||||
|
userPreferences.reloadNotificationsCount(tokens: appAccountsManager.availableAccounts.compactMap(\.oauthToken))
|
||||||
|
Task {
|
||||||
|
await userPreferences.refreshServerPreferences()
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupRevenueCat() {
|
||||||
|
Purchases.logLevel = .error
|
||||||
|
Purchases.configure(withAPIKey: "appl_JXmiRckOzXXTsHKitQiicXCvMQi")
|
||||||
|
Purchases.shared.getCustomerInfo { info, _ in
|
||||||
|
if info?.entitlements["Supporter"]?.isActive == true {
|
||||||
|
isSupporter = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func refreshPushSubs() {
|
||||||
|
PushNotificationsService.shared.requestPushNotifications()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AppDelegate: NSObject, UIApplicationDelegate {
|
||||||
|
func application(_: UIApplication,
|
||||||
|
didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool
|
||||||
|
{
|
||||||
|
try? AVAudioSession.sharedInstance().setCategory(.ambient)
|
||||||
|
PushNotificationsService.shared.setAccounts(accounts: AppAccountsManager.shared.pushAccounts)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func application(_: UIApplication,
|
||||||
|
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)
|
||||||
|
{
|
||||||
|
PushNotificationsService.shared.pushToken = deviceToken
|
||||||
|
Task {
|
||||||
|
PushNotificationsService.shared.setAccounts(accounts: AppAccountsManager.shared.pushAccounts)
|
||||||
|
await PushNotificationsService.shared.updateSubscriptions(forceCreate: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func application(_: UIApplication, didFailToRegisterForRemoteNotificationsWithError _: Error) {}
|
||||||
|
|
||||||
|
func application(_: UIApplication, didReceiveRemoteNotification _: [AnyHashable: Any]) async -> UIBackgroundFetchResult {
|
||||||
|
UserPreferences.shared.reloadNotificationsCount(tokens: AppAccountsManager.shared.availableAccounts.compactMap(\.oauthToken))
|
||||||
|
return .noData
|
||||||
|
}
|
||||||
|
|
||||||
|
func application(_: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options _: UIScene.ConnectionOptions) -> UISceneConfiguration {
|
||||||
|
let configuration = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role)
|
||||||
|
if connectingSceneSession.role == .windowApplication {
|
||||||
|
configuration.delegateClass = SceneDelegate.self
|
||||||
|
}
|
||||||
|
return configuration
|
||||||
|
}
|
||||||
|
}
|
|
@ -98,6 +98,21 @@ struct TimelineTab: View {
|
||||||
selectedTagGroup = nil
|
selectedTagGroup = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.onReceive(NotificationCenter.default.publisher(for: .refreshTimeline)) { _ in
|
||||||
|
timeline = .latest
|
||||||
|
}
|
||||||
|
.onReceive(NotificationCenter.default.publisher(for: .trendingTimeline)) { _ in
|
||||||
|
timeline = .trending
|
||||||
|
}
|
||||||
|
.onReceive(NotificationCenter.default.publisher(for: .localTimeline)) { _ in
|
||||||
|
timeline = .local
|
||||||
|
}
|
||||||
|
.onReceive(NotificationCenter.default.publisher(for: .federatedTimeline)) { _ in
|
||||||
|
timeline = .federated
|
||||||
|
}
|
||||||
|
.onReceive(NotificationCenter.default.publisher(for: .homeTimeline)) { _ in
|
||||||
|
timeline = .home
|
||||||
|
}
|
||||||
.withSafariRouter()
|
.withSafariRouter()
|
||||||
.environment(routerPath)
|
.environment(routerPath)
|
||||||
}
|
}
|
||||||
|
@ -110,7 +125,6 @@ struct TimelineTab: View {
|
||||||
} label: {
|
} label: {
|
||||||
Label(TimelineFilter.latest.localizedTitle(), systemImage: TimelineFilter.latest.iconName() ?? "")
|
Label(TimelineFilter.latest.localizedTitle(), systemImage: TimelineFilter.latest.iconName() ?? "")
|
||||||
}
|
}
|
||||||
.keyboardShortcut("r", modifiers: .command)
|
|
||||||
Divider()
|
Divider()
|
||||||
}
|
}
|
||||||
ForEach(TimelineFilter.availableTimeline(client: client), id: \.self) { timeline in
|
ForEach(TimelineFilter.availableTimeline(client: client), id: \.self) { timeline in
|
||||||
|
|
|
@ -30068,6 +30068,28 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"menu.timeline" : {
|
||||||
|
"extractionState" : "stale",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Timeline"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"menu.timeline.refresh" : {
|
||||||
|
"extractionState" : "stale",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Jump to Latest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"notifications-others-count %lld" : {
|
"notifications-others-count %lld" : {
|
||||||
"extractionState" : "manual",
|
"extractionState" : "manual",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
|
|
|
@ -52,7 +52,7 @@ class ShareViewController: UIViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(forName: NotificationsName.shareSheetClose,
|
NotificationCenter.default.addObserver(forName: .shareSheetClose,
|
||||||
object: nil,
|
object: nil,
|
||||||
queue: nil)
|
queue: nil)
|
||||||
{ [weak self] _ in
|
{ [weak self] _ in
|
||||||
|
|
|
@ -26,6 +26,7 @@ public struct AccountsListRow: View {
|
||||||
@Environment(CurrentAccount.self) private var currentAccount
|
@Environment(CurrentAccount.self) private var currentAccount
|
||||||
@Environment(RouterPath.self) private var routerPath
|
@Environment(RouterPath.self) private var routerPath
|
||||||
@Environment(Client.self) private var client
|
@Environment(Client.self) private var client
|
||||||
|
@Environment(QuickLook.self) private var quickLook
|
||||||
|
|
||||||
@State var viewModel: AccountsListRowViewModel
|
@State var viewModel: AccountsListRowViewModel
|
||||||
|
|
||||||
|
@ -122,7 +123,7 @@ public struct AccountsListRow: View {
|
||||||
.environment(theme)
|
.environment(theme)
|
||||||
.environment(currentAccount)
|
.environment(currentAccount)
|
||||||
.environment(client)
|
.environment(client)
|
||||||
.environment(QuickLook())
|
.environment(quickLook)
|
||||||
.environment(routerPath)
|
.environment(routerPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
public enum NotificationsName {
|
public extension Notification.Name {
|
||||||
public static let shareSheetClose = NSNotification.Name("shareSheetClose")
|
static let shareSheetClose = NSNotification.Name("shareSheetClose")
|
||||||
|
static let refreshTimeline = Notification.Name("refreshTimeline")
|
||||||
|
static let homeTimeline = Notification.Name("homeTimeline")
|
||||||
|
static let trendingTimeline = Notification.Name("trendingTimeline")
|
||||||
|
static let federatedTimeline = Notification.Name("federatedTimeline")
|
||||||
|
static let localTimeline = Notification.Name("localTimeline")
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,9 @@ import QuickLook
|
||||||
public var selectedMediaAttachment: MediaAttachment?
|
public var selectedMediaAttachment: MediaAttachment?
|
||||||
public var mediaAttachments: [MediaAttachment] = []
|
public var mediaAttachments: [MediaAttachment] = []
|
||||||
|
|
||||||
public init() {}
|
public static let shared = QuickLook()
|
||||||
|
|
||||||
|
private init() {}
|
||||||
|
|
||||||
public func prepareFor(selectedMediaAttachment: MediaAttachment, mediaAttachments: [MediaAttachment]) {
|
public func prepareFor(selectedMediaAttachment: MediaAttachment, mediaAttachments: [MediaAttachment]) {
|
||||||
self.selectedMediaAttachment = selectedMediaAttachment
|
self.selectedMediaAttachment = selectedMediaAttachment
|
||||||
|
|
|
@ -28,15 +28,9 @@ public enum RouterDestination: Hashable {
|
||||||
public enum WindowDestination: Hashable, Codable {
|
public enum WindowDestination: Hashable, Codable {
|
||||||
case newStatusEditor(visibility: Models.Visibility)
|
case newStatusEditor(visibility: Models.Visibility)
|
||||||
case mediaViewer(attachments: [MediaAttachment], selectedAttachment: MediaAttachment)
|
case mediaViewer(attachments: [MediaAttachment], selectedAttachment: MediaAttachment)
|
||||||
|
case editStatusEditor(status: Status)
|
||||||
var initialSize: CGSize {
|
case replyToStatusEditor(status: Status)
|
||||||
switch self {
|
case quoteStatusEditor(status: Status)
|
||||||
case .newStatusEditor:
|
|
||||||
return .init(width: 500, height: 700)
|
|
||||||
case .mediaViewer:
|
|
||||||
return .init(width: 800, height: 600)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum SheetDestination: Identifiable {
|
public enum SheetDestination: Identifiable {
|
||||||
|
|
|
@ -89,7 +89,7 @@ public struct StatusEditorView: View {
|
||||||
viewModel.prepareStatusText()
|
viewModel.prepareStatusText()
|
||||||
if !client.isAuth {
|
if !client.isAuth {
|
||||||
dismiss()
|
dismiss()
|
||||||
NotificationCenter.default.post(name: NotificationsName.shareSheetClose,
|
NotificationCenter.default.post(name: .shareSheetClose,
|
||||||
object: nil)
|
object: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +141,7 @@ public struct StatusEditorView: View {
|
||||||
isDismissAlertPresented = true
|
isDismissAlertPresented = true
|
||||||
} else {
|
} else {
|
||||||
dismiss()
|
dismiss()
|
||||||
NotificationCenter.default.post(name: NotificationsName.shareSheetClose,
|
NotificationCenter.default.post(name: .shareSheetClose,
|
||||||
object: nil)
|
object: nil)
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
|
@ -154,13 +154,13 @@ public struct StatusEditorView: View {
|
||||||
actions: {
|
actions: {
|
||||||
Button("status.draft.delete", role: .destructive) {
|
Button("status.draft.delete", role: .destructive) {
|
||||||
dismiss()
|
dismiss()
|
||||||
NotificationCenter.default.post(name: NotificationsName.shareSheetClose,
|
NotificationCenter.default.post(name: .shareSheetClose,
|
||||||
object: nil)
|
object: nil)
|
||||||
}
|
}
|
||||||
Button("status.draft.save") {
|
Button("status.draft.save") {
|
||||||
context.insert(Draft(content: viewModel.statusText.string))
|
context.insert(Draft(content: viewModel.statusText.string))
|
||||||
dismiss()
|
dismiss()
|
||||||
NotificationCenter.default.post(name: NotificationsName.shareSheetClose,
|
NotificationCenter.default.post(name: .shareSheetClose,
|
||||||
object: nil)
|
object: nil)
|
||||||
}
|
}
|
||||||
Button("action.cancel", role: .cancel) {}
|
Button("action.cancel", role: .cancel) {}
|
||||||
|
@ -213,7 +213,7 @@ public struct StatusEditorView: View {
|
||||||
if status != nil {
|
if status != nil {
|
||||||
dismiss()
|
dismiss()
|
||||||
SoundEffectManager.shared.playSound(of: .tootSent)
|
SoundEffectManager.shared.playSound(of: .tootSent)
|
||||||
NotificationCenter.default.post(name: NotificationsName.shareSheetClose,
|
NotificationCenter.default.post(name: .shareSheetClose,
|
||||||
object: nil)
|
object: nil)
|
||||||
if !viewModel.mode.isInShareExtension, !preferences.requestedReview, !ProcessInfo.processInfo.isMacCatalystApp {
|
if !viewModel.mode.isInShareExtension, !preferences.requestedReview, !ProcessInfo.processInfo.isMacCatalystApp {
|
||||||
if let scene = UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene {
|
if let scene = UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene {
|
||||||
|
|
|
@ -11,6 +11,7 @@ struct StatusRowActionsView: View {
|
||||||
@Environment(StatusDataController.self) private var statusDataController
|
@Environment(StatusDataController.self) private var statusDataController
|
||||||
@Environment(UserPreferences.self) private var userPreferences
|
@Environment(UserPreferences.self) private var userPreferences
|
||||||
|
|
||||||
|
@Environment(\.openWindow) private var openWindow
|
||||||
@Environment(\.isStatusFocused) private var isFocused
|
@Environment(\.isStatusFocused) private var isFocused
|
||||||
|
|
||||||
var viewModel: StatusRowViewModel
|
var viewModel: StatusRowViewModel
|
||||||
|
@ -204,7 +205,11 @@ struct StatusRowActionsView: View {
|
||||||
switch action {
|
switch action {
|
||||||
case .respond:
|
case .respond:
|
||||||
SoundEffectManager.shared.playSound(of: .share)
|
SoundEffectManager.shared.playSound(of: .share)
|
||||||
viewModel.routerPath.presentedSheet = .replyToStatusEditor(status: viewModel.localStatus ?? viewModel.status)
|
if ProcessInfo.processInfo.isMacCatalystApp {
|
||||||
|
openWindow(value: WindowDestination.replyToStatusEditor(status: viewModel.localStatus ?? viewModel.status))
|
||||||
|
} else {
|
||||||
|
viewModel.routerPath.presentedSheet = .replyToStatusEditor(status: viewModel.localStatus ?? viewModel.status)
|
||||||
|
}
|
||||||
case .favorite:
|
case .favorite:
|
||||||
SoundEffectManager.shared.playSound(of: .favorite)
|
SoundEffectManager.shared.playSound(of: .favorite)
|
||||||
await statusDataController.toggleFavorite(remoteStatus: viewModel.localStatusId)
|
await statusDataController.toggleFavorite(remoteStatus: viewModel.localStatusId)
|
||||||
|
|
|
@ -7,6 +7,7 @@ import SwiftUI
|
||||||
@MainActor
|
@MainActor
|
||||||
struct StatusRowContextMenu: View {
|
struct StatusRowContextMenu: View {
|
||||||
@Environment(\.displayScale) var displayScale
|
@Environment(\.displayScale) var displayScale
|
||||||
|
@Environment(\.openWindow) var openWindow
|
||||||
|
|
||||||
@Environment(Client.self) private var client
|
@Environment(Client.self) private var client
|
||||||
@Environment(SceneDelegate.self) private var sceneDelegate
|
@Environment(SceneDelegate.self) private var sceneDelegate
|
||||||
|
@ -14,6 +15,7 @@ struct StatusRowContextMenu: View {
|
||||||
@Environment(CurrentAccount.self) private var account
|
@Environment(CurrentAccount.self) private var account
|
||||||
@Environment(CurrentInstance.self) private var currentInstance
|
@Environment(CurrentInstance.self) private var currentInstance
|
||||||
@Environment(StatusDataController.self) private var statusDataController
|
@Environment(StatusDataController.self) private var statusDataController
|
||||||
|
@Environment(QuickLook.self) private var quickLook
|
||||||
|
|
||||||
var viewModel: StatusRowViewModel
|
var viewModel: StatusRowViewModel
|
||||||
|
|
||||||
|
@ -51,12 +53,20 @@ struct StatusRowContextMenu: View {
|
||||||
systemImage: "bookmark")
|
systemImage: "bookmark")
|
||||||
}
|
}
|
||||||
Button {
|
Button {
|
||||||
viewModel.routerPath.presentedSheet = .replyToStatusEditor(status: viewModel.status)
|
if ProcessInfo.processInfo.isMacCatalystApp {
|
||||||
|
openWindow(value: WindowDestination.replyToStatusEditor(status: viewModel.status))
|
||||||
|
} else {
|
||||||
|
viewModel.routerPath.presentedSheet = .replyToStatusEditor(status: viewModel.status)
|
||||||
|
}
|
||||||
} label: {
|
} label: {
|
||||||
Label("status.action.reply", systemImage: "arrowshape.turn.up.left")
|
Label("status.action.reply", systemImage: "arrowshape.turn.up.left")
|
||||||
}
|
}
|
||||||
Button {
|
Button {
|
||||||
viewModel.routerPath.presentedSheet = .quoteStatusEditor(status: viewModel.status)
|
if ProcessInfo.processInfo.isMacCatalystApp {
|
||||||
|
openWindow(value: WindowDestination.quoteStatusEditor(status: viewModel.status))
|
||||||
|
} else {
|
||||||
|
viewModel.routerPath.presentedSheet = .quoteStatusEditor(status: viewModel.status)
|
||||||
|
}
|
||||||
} label: {
|
} label: {
|
||||||
Label("status.action.quote", systemImage: "quote.bubble")
|
Label("status.action.quote", systemImage: "quote.bubble")
|
||||||
}
|
}
|
||||||
|
@ -91,7 +101,7 @@ struct StatusRowContextMenu: View {
|
||||||
.environment(account)
|
.environment(account)
|
||||||
.environment(currentInstance)
|
.environment(currentInstance)
|
||||||
.environment(SceneDelegate())
|
.environment(SceneDelegate())
|
||||||
.environment(QuickLook())
|
.environment(quickLook)
|
||||||
.environment(viewModel.client)
|
.environment(viewModel.client)
|
||||||
.preferredColorScheme(Theme.shared.selectedScheme == .dark ? .dark : .light)
|
.preferredColorScheme(Theme.shared.selectedScheme == .dark ? .dark : .light)
|
||||||
.foregroundColor(Theme.shared.labelColor)
|
.foregroundColor(Theme.shared.labelColor)
|
||||||
|
|
Loading…
Reference in a new issue