mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2025-02-03 16:07:31 +01:00
Refactor + add more shortcuts on macOS
This commit is contained in:
parent
494b0df0e3
commit
cf0f0fd891
@ -80,6 +80,10 @@
|
||||
9FAE4ACE29379A5A00772766 /* KeychainSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 9FAE4ACD29379A5A00772766 /* KeychainSwift */; };
|
||||
9FB143D12983104700A27BB1 /* glass.caf in Resources */ = {isa = PBXBuildFile; fileRef = 9F2A542B296B1177009B2D7C /* glass.caf */; };
|
||||
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 */; };
|
||||
9FBFE64E292A72BD00C250E9 /* Network in Frameworks */ = {isa = PBXBuildFile; productRef = 9FBFE64D292A72BD00C250E9 /* Network */; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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; };
|
||||
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>"; };
|
||||
@ -337,9 +345,9 @@
|
||||
9F398AB429360A5800A889F2 /* App */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9FB183232AE926BB00BBB692 /* Main */,
|
||||
9F654BF0299AC46200D27FA5 /* Report */,
|
||||
9FAE4AC9293783A200772766 /* Tabs */,
|
||||
9FBFE63C292A715500C250E9 /* IceCubesApp.swift */,
|
||||
9F398AA52935FE8A00A889F2 /* AppRegistry.swift */,
|
||||
639CDF9B296AC82F00C35E58 /* SafariRouter.swift */,
|
||||
9FAD85CE2975B68900496AB1 /* SideBarView.swift */,
|
||||
@ -418,6 +426,18 @@
|
||||
path = Tabs;
|
||||
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 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -791,16 +811,20 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
9FE151A6293C90F900E9683D /* IconSelectorView.swift in Sources */,
|
||||
9FB183222AE9268800BBB692 /* IceCubesApp+Menu.swift in Sources */,
|
||||
9FB183252AE926E900BBB692 /* IceCubesApp+Sidebar.swift in Sources */,
|
||||
9F7D939A29805DBD00EE6B7A /* AccountSettingView.swift in Sources */,
|
||||
069709AA298C9AD7006E4CB5 /* AboutView.swift in Sources */,
|
||||
9F2B92FC295DA94500DE16D0 /* InstanceInfoView.swift in Sources */,
|
||||
C9B22677297F6C2E001F9EFE /* ContentSettingsView.swift in Sources */,
|
||||
9FA6FD6229C04A8800E2312C /* TranslationSettingsView.swift in Sources */,
|
||||
9FB183272AE9279F00BBB692 /* IceCubesApp+Tabbar.swift in Sources */,
|
||||
9F35DB4C2952005C00B3281A /* MessagesTab.swift in Sources */,
|
||||
9FAD85CF2975B68900496AB1 /* SideBarView.swift in Sources */,
|
||||
9FAE4ACB293783B000772766 /* SettingsTab.swift in Sources */,
|
||||
9F7335F92968576500AFF0BA /* DisplaySettingsView.swift in Sources */,
|
||||
9F2A540729699698009B2D7C /* SupportAppView.swift in Sources */,
|
||||
9FB183292AE9449100BBB692 /* IceCubesApp+Scene.swift in Sources */,
|
||||
9F2B92F6295AE04800DE16D0 /* Tabs.swift in Sources */,
|
||||
FA31A9AB2A66BF7C00D5F662 /* EditTagGroupView.swift in Sources */,
|
||||
FAD203D02A66D8A80030A7FD /* Symbols.swift in Sources */,
|
||||
@ -891,7 +915,7 @@
|
||||
"@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_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = iphoneos;
|
||||
@ -925,7 +949,7 @@
|
||||
"@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_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = iphoneos;
|
||||
@ -960,7 +984,7 @@
|
||||
"@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_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = iphoneos;
|
||||
@ -994,7 +1018,7 @@
|
||||
"@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_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = iphoneos;
|
||||
@ -1174,7 +1198,7 @@
|
||||
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
||||
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
MARKETING_VERSION = 1.9.3;
|
||||
MARKETING_VERSION = 1.9.4;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_ID_PREFIX).IceCubesApp";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = auto;
|
||||
@ -1228,7 +1252,7 @@
|
||||
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
||||
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
MARKETING_VERSION = 1.9.3;
|
||||
MARKETING_VERSION = 1.9.4;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_ID_PREFIX).IceCubesApp";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = auto;
|
||||
@ -1263,7 +1287,7 @@
|
||||
"@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_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = iphoneos;
|
||||
@ -1298,7 +1322,7 @@
|
||||
"@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_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = iphoneos;
|
||||
|
@ -10,6 +10,7 @@ import Models
|
||||
import Status
|
||||
import SwiftUI
|
||||
import Timeline
|
||||
import MediaUI
|
||||
|
||||
@MainActor
|
||||
extension View {
|
||||
@ -130,6 +131,7 @@ extension View {
|
||||
.environment(AppAccountsManager.shared)
|
||||
.environment(PushNotificationsService.shared)
|
||||
.environment(AppAccountsManager.shared.currentClient)
|
||||
.environment(QuickLook.shared)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
.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()
|
||||
.environment(routerPath)
|
||||
}
|
||||
@ -110,7 +125,6 @@ struct TimelineTab: View {
|
||||
} label: {
|
||||
Label(TimelineFilter.latest.localizedTitle(), systemImage: TimelineFilter.latest.iconName() ?? "")
|
||||
}
|
||||
.keyboardShortcut("r", modifiers: .command)
|
||||
Divider()
|
||||
}
|
||||
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" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
|
@ -52,7 +52,7 @@ class ShareViewController: UIViewController {
|
||||
}
|
||||
}
|
||||
|
||||
NotificationCenter.default.addObserver(forName: NotificationsName.shareSheetClose,
|
||||
NotificationCenter.default.addObserver(forName: .shareSheetClose,
|
||||
object: nil,
|
||||
queue: nil)
|
||||
{ [weak self] _ in
|
||||
|
@ -26,6 +26,7 @@ public struct AccountsListRow: View {
|
||||
@Environment(CurrentAccount.self) private var currentAccount
|
||||
@Environment(RouterPath.self) private var routerPath
|
||||
@Environment(Client.self) private var client
|
||||
@Environment(QuickLook.self) private var quickLook
|
||||
|
||||
@State var viewModel: AccountsListRowViewModel
|
||||
|
||||
@ -122,7 +123,7 @@ public struct AccountsListRow: View {
|
||||
.environment(theme)
|
||||
.environment(currentAccount)
|
||||
.environment(client)
|
||||
.environment(QuickLook())
|
||||
.environment(quickLook)
|
||||
.environment(routerPath)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,10 @@
|
||||
import UIKit
|
||||
|
||||
public enum NotificationsName {
|
||||
public static let shareSheetClose = NSNotification.Name("shareSheetClose")
|
||||
public extension Notification.Name {
|
||||
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 mediaAttachments: [MediaAttachment] = []
|
||||
|
||||
public init() {}
|
||||
public static let shared = QuickLook()
|
||||
|
||||
private init() {}
|
||||
|
||||
public func prepareFor(selectedMediaAttachment: MediaAttachment, mediaAttachments: [MediaAttachment]) {
|
||||
self.selectedMediaAttachment = selectedMediaAttachment
|
||||
|
@ -28,15 +28,9 @@ public enum RouterDestination: Hashable {
|
||||
public enum WindowDestination: Hashable, Codable {
|
||||
case newStatusEditor(visibility: Models.Visibility)
|
||||
case mediaViewer(attachments: [MediaAttachment], selectedAttachment: MediaAttachment)
|
||||
|
||||
var initialSize: CGSize {
|
||||
switch self {
|
||||
case .newStatusEditor:
|
||||
return .init(width: 500, height: 700)
|
||||
case .mediaViewer:
|
||||
return .init(width: 800, height: 600)
|
||||
}
|
||||
}
|
||||
case editStatusEditor(status: Status)
|
||||
case replyToStatusEditor(status: Status)
|
||||
case quoteStatusEditor(status: Status)
|
||||
}
|
||||
|
||||
public enum SheetDestination: Identifiable {
|
||||
|
@ -89,7 +89,7 @@ public struct StatusEditorView: View {
|
||||
viewModel.prepareStatusText()
|
||||
if !client.isAuth {
|
||||
dismiss()
|
||||
NotificationCenter.default.post(name: NotificationsName.shareSheetClose,
|
||||
NotificationCenter.default.post(name: .shareSheetClose,
|
||||
object: nil)
|
||||
}
|
||||
|
||||
@ -141,7 +141,7 @@ public struct StatusEditorView: View {
|
||||
isDismissAlertPresented = true
|
||||
} else {
|
||||
dismiss()
|
||||
NotificationCenter.default.post(name: NotificationsName.shareSheetClose,
|
||||
NotificationCenter.default.post(name: .shareSheetClose,
|
||||
object: nil)
|
||||
}
|
||||
} label: {
|
||||
@ -154,13 +154,13 @@ public struct StatusEditorView: View {
|
||||
actions: {
|
||||
Button("status.draft.delete", role: .destructive) {
|
||||
dismiss()
|
||||
NotificationCenter.default.post(name: NotificationsName.shareSheetClose,
|
||||
NotificationCenter.default.post(name: .shareSheetClose,
|
||||
object: nil)
|
||||
}
|
||||
Button("status.draft.save") {
|
||||
context.insert(Draft(content: viewModel.statusText.string))
|
||||
dismiss()
|
||||
NotificationCenter.default.post(name: NotificationsName.shareSheetClose,
|
||||
NotificationCenter.default.post(name: .shareSheetClose,
|
||||
object: nil)
|
||||
}
|
||||
Button("action.cancel", role: .cancel) {}
|
||||
@ -213,7 +213,7 @@ public struct StatusEditorView: View {
|
||||
if status != nil {
|
||||
dismiss()
|
||||
SoundEffectManager.shared.playSound(of: .tootSent)
|
||||
NotificationCenter.default.post(name: NotificationsName.shareSheetClose,
|
||||
NotificationCenter.default.post(name: .shareSheetClose,
|
||||
object: nil)
|
||||
if !viewModel.mode.isInShareExtension, !preferences.requestedReview, !ProcessInfo.processInfo.isMacCatalystApp {
|
||||
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(UserPreferences.self) private var userPreferences
|
||||
|
||||
@Environment(\.openWindow) private var openWindow
|
||||
@Environment(\.isStatusFocused) private var isFocused
|
||||
|
||||
var viewModel: StatusRowViewModel
|
||||
@ -204,7 +205,11 @@ struct StatusRowActionsView: View {
|
||||
switch action {
|
||||
case .respond:
|
||||
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:
|
||||
SoundEffectManager.shared.playSound(of: .favorite)
|
||||
await statusDataController.toggleFavorite(remoteStatus: viewModel.localStatusId)
|
||||
|
@ -7,6 +7,7 @@ import SwiftUI
|
||||
@MainActor
|
||||
struct StatusRowContextMenu: View {
|
||||
@Environment(\.displayScale) var displayScale
|
||||
@Environment(\.openWindow) var openWindow
|
||||
|
||||
@Environment(Client.self) private var client
|
||||
@Environment(SceneDelegate.self) private var sceneDelegate
|
||||
@ -14,6 +15,7 @@ struct StatusRowContextMenu: View {
|
||||
@Environment(CurrentAccount.self) private var account
|
||||
@Environment(CurrentInstance.self) private var currentInstance
|
||||
@Environment(StatusDataController.self) private var statusDataController
|
||||
@Environment(QuickLook.self) private var quickLook
|
||||
|
||||
var viewModel: StatusRowViewModel
|
||||
|
||||
@ -51,12 +53,20 @@ struct StatusRowContextMenu: View {
|
||||
systemImage: "bookmark")
|
||||
}
|
||||
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("status.action.reply", systemImage: "arrowshape.turn.up.left")
|
||||
}
|
||||
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("status.action.quote", systemImage: "quote.bubble")
|
||||
}
|
||||
@ -91,7 +101,7 @@ struct StatusRowContextMenu: View {
|
||||
.environment(account)
|
||||
.environment(currentInstance)
|
||||
.environment(SceneDelegate())
|
||||
.environment(QuickLook())
|
||||
.environment(quickLook)
|
||||
.environment(viewModel.client)
|
||||
.preferredColorScheme(Theme.shared.selectedScheme == .dark ? .dark : .light)
|
||||
.foregroundColor(Theme.shared.labelColor)
|
||||
|
Loading…
x
Reference in New Issue
Block a user