mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-12-22 07:06:40 +00:00
The Mac App
This commit is contained in:
parent
6a7db8714d
commit
c05768c793
7 changed files with 199 additions and 23 deletions
|
@ -16,7 +16,7 @@
|
|||
9F2A540E2969A0B0009B2D7C /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9F2A540D2969A0B0009B2D7C /* StoreKit.framework */; };
|
||||
9F2A5411296A1429009B2D7C /* PushNotificationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F2A5410296A1429009B2D7C /* PushNotificationsView.swift */; };
|
||||
9F2A5419296AB631009B2D7C /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F2A5418296AB631009B2D7C /* NotificationService.swift */; };
|
||||
9F2A541D296AB631009B2D7C /* IceCubesNotifications.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 9F2A5416296AB631009B2D7C /* IceCubesNotifications.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
9F2A541D296AB631009B2D7C /* IceCubesNotifications.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 9F2A5416296AB631009B2D7C /* IceCubesNotifications.appex */; platformFilter = ios; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
9F2A5424296AB67A009B2D7C /* Env in Frameworks */ = {isa = PBXBuildFile; productRef = 9F2A5423296AB67A009B2D7C /* Env */; };
|
||||
9F2A5426296AB67E009B2D7C /* KeychainSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 9F2A5425296AB67E009B2D7C /* KeychainSwift */; };
|
||||
9F2A5428296AB683009B2D7C /* Models in Frameworks */ = {isa = PBXBuildFile; productRef = 9F2A5427296AB683009B2D7C /* Models */; };
|
||||
|
@ -45,7 +45,7 @@
|
|||
9FAD85832971BF7200496AB1 /* Secret.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9FAD85822971BF7200496AB1 /* Secret.plist */; };
|
||||
9FAD858B29743F7400496AB1 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FAD858A29743F7400496AB1 /* ShareViewController.swift */; };
|
||||
9FAD858E29743F7400496AB1 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9FAD858C29743F7400496AB1 /* MainInterface.storyboard */; };
|
||||
9FAD859229743F7400496AB1 /* IceCubesShareExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 9FAD858829743F7400496AB1 /* IceCubesShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
9FAD859229743F7400496AB1 /* IceCubesShareExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 9FAD858829743F7400496AB1 /* IceCubesShareExtension.appex */; platformFilter = ios; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
9FAD85982974405D00496AB1 /* Status in Frameworks */ = {isa = PBXBuildFile; productRef = 9FAD85972974405D00496AB1 /* Status */; };
|
||||
9FAD859A297440CB00496AB1 /* KeychainSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 9FAD8599297440CB00496AB1 /* KeychainSwift */; };
|
||||
9FAD859C2974422700496AB1 /* AppAccount in Frameworks */ = {isa = PBXBuildFile; productRef = 9FAD859B2974422700496AB1 /* AppAccount */; };
|
||||
|
@ -53,6 +53,7 @@
|
|||
9FAD85A0297456A100496AB1 /* Models in Frameworks */ = {isa = PBXBuildFile; productRef = 9FAD859F297456A100496AB1 /* Models */; };
|
||||
9FAD85A2297456A400496AB1 /* Env in Frameworks */ = {isa = PBXBuildFile; productRef = 9FAD85A1297456A400496AB1 /* Env */; };
|
||||
9FAD85A4297456A800496AB1 /* DesignSystem in Frameworks */ = {isa = PBXBuildFile; productRef = 9FAD85A3297456A800496AB1 /* DesignSystem */; };
|
||||
9FAD85A8297582F100496AB1 /* QuickLookRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FAD85A7297582F100496AB1 /* QuickLookRepresentable.swift */; };
|
||||
9FAE4ACB293783B000772766 /* SettingsTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FAE4ACA293783B000772766 /* SettingsTab.swift */; };
|
||||
9FAE4ACE29379A5A00772766 /* KeychainSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 9FAE4ACD29379A5A00772766 /* KeychainSwift */; };
|
||||
9FBFE63D292A715500C250E9 /* IceCubesApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FBFE63C292A715500C250E9 /* IceCubesApp.swift */; };
|
||||
|
@ -137,6 +138,7 @@
|
|||
9FAD858D29743F7400496AB1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
|
||||
9FAD858F29743F7400496AB1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
9FAD859629743F7E00496AB1 /* IceCubesShareExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = IceCubesShareExtension.entitlements; sourceTree = "<group>"; };
|
||||
9FAD85A7297582F100496AB1 /* QuickLookRepresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickLookRepresentable.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>"; };
|
||||
9FBFE639292A715500C250E9 /* IceCubesApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = IceCubesApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
|
@ -218,6 +220,7 @@
|
|||
9FBFE63C292A715500C250E9 /* IceCubesApp.swift */,
|
||||
9F398AA52935FE8A00A889F2 /* AppRouteur.swift */,
|
||||
639CDF9B296AC82F00C35E58 /* SafariRouteur.swift */,
|
||||
9FAD85A7297582F100496AB1 /* QuickLookRepresentable.swift */,
|
||||
);
|
||||
path = App;
|
||||
sourceTree = "<group>";
|
||||
|
@ -536,6 +539,7 @@
|
|||
9F7335F22967608F00AFF0BA /* AddRemoteTimelineVIew.swift in Sources */,
|
||||
9F55C68D2955968700F94077 /* ExploreTab.swift in Sources */,
|
||||
9F2A5411296A1429009B2D7C /* PushNotificationsView.swift in Sources */,
|
||||
9FAD85A8297582F100496AB1 /* QuickLookRepresentable.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -544,11 +548,13 @@
|
|||
/* Begin PBXTargetDependency section */
|
||||
9F2A541C296AB631009B2D7C /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
platformFilter = ios;
|
||||
target = 9F2A5415296AB631009B2D7C /* IceCubesNotifications */;
|
||||
targetProxy = 9F2A541B296AB631009B2D7C /* PBXContainerItemProxy */;
|
||||
};
|
||||
9FAD859129743F7400496AB1 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
platformFilter = ios;
|
||||
target = 9FAD858729743F7400496AB1 /* IceCubesShareExtension */;
|
||||
targetProxy = 9FAD859029743F7400496AB1 /* PBXContainerItemProxy */;
|
||||
};
|
||||
|
@ -810,6 +816,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = IceCubesApp/IceCubesApp.entitlements;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 730;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
|
@ -861,6 +868,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = IceCubesApp/IceCubesApp.entitlements;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 730;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
|
|
|
@ -5,9 +5,9 @@ import Network
|
|||
import KeychainSwift
|
||||
import Env
|
||||
import DesignSystem
|
||||
import QuickLook
|
||||
import RevenueCat
|
||||
import AppAccount
|
||||
import Account
|
||||
|
||||
@main
|
||||
struct IceCubesApp: App {
|
||||
|
@ -48,7 +48,10 @@ struct IceCubesApp: App {
|
|||
.environmentObject(theme)
|
||||
.environmentObject(watcher)
|
||||
.environmentObject(PushNotificationsService.shared)
|
||||
.quickLookPreview($quickLook.url, in: quickLook.urls)
|
||||
.sheet(item: $quickLook.url, content: { url in
|
||||
QuickLookPreview(selectedURL: url, urls: quickLook.urls)
|
||||
.edgesIgnoringSafeArea(.bottom)
|
||||
})
|
||||
}
|
||||
.onChange(of: scenePhase) { scenePhase in
|
||||
handleScenePhase(scenePhase: scenePhase)
|
||||
|
@ -63,14 +66,11 @@ struct IceCubesApp: App {
|
|||
|
||||
@ViewBuilder
|
||||
private var appView: some View {
|
||||
/*
|
||||
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||
splitView
|
||||
sidebarView
|
||||
} else {
|
||||
tabBarView
|
||||
}
|
||||
*/
|
||||
tabBarView
|
||||
}
|
||||
|
||||
private func badgeFor(tab: Tab) -> Int {
|
||||
|
@ -80,6 +80,51 @@ struct IceCubesApp: App {
|
|||
return 0
|
||||
}
|
||||
|
||||
private var sidebarView: some View {
|
||||
HStack(spacing: 0) {
|
||||
VStack(alignment: .center) {
|
||||
if let account = currentAccount.account {
|
||||
AvatarView(url: account.avatar)
|
||||
.frame(width: 70, height: 50)
|
||||
.background(selectedTab == .profile ? theme.secondaryBackgroundColor : .clear)
|
||||
.onTapGesture {
|
||||
selectedTab = .profile
|
||||
}
|
||||
}
|
||||
ForEach(availableTabs) { tab in
|
||||
Button {
|
||||
if tab == selectedTab {
|
||||
popToRootTab = .other
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
|
||||
popToRootTab = tab
|
||||
}
|
||||
}
|
||||
selectedTab = tab
|
||||
} label: {
|
||||
Image(systemName: tab.iconName)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 24, height: 24)
|
||||
.foregroundColor(tab == selectedTab ? theme.tintColor : .gray)
|
||||
}
|
||||
.frame(width: 70, height: 50)
|
||||
.background(tab == selectedTab ? theme.secondaryBackgroundColor : .clear)
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
.frame(width: 70)
|
||||
.background(.clear)
|
||||
Divider()
|
||||
.edgesIgnoringSafeArea(.top)
|
||||
if selectedTab == .profile, let account = currentAccount.account {
|
||||
AccountDetailView(account: account)
|
||||
} else {
|
||||
selectedTab.makeContentView(popToRootTab: $popToRootTab)
|
||||
}
|
||||
}
|
||||
.background(.thinMaterial)
|
||||
}
|
||||
|
||||
private var tabBarView: some View {
|
||||
TabView(selection: .init(get: {
|
||||
selectedTab
|
||||
|
|
74
IceCubesApp/App/QuickLookRepresentable.swift
Normal file
74
IceCubesApp/App/QuickLookRepresentable.swift
Normal file
|
@ -0,0 +1,74 @@
|
|||
import UIKit
|
||||
import SwiftUI
|
||||
import QuickLook
|
||||
|
||||
extension URL: Identifiable {
|
||||
public var id: String {
|
||||
absoluteString
|
||||
}
|
||||
}
|
||||
|
||||
struct QuickLookPreview: UIViewControllerRepresentable {
|
||||
let selectedURL: URL
|
||||
let urls: [URL]
|
||||
|
||||
func makeUIViewController(context: Context) -> UINavigationController {
|
||||
let controller = AppQLPreviewCpntroller()
|
||||
controller.dataSource = context.coordinator
|
||||
controller.delegate = context.coordinator
|
||||
let nav = UINavigationController(rootViewController: controller)
|
||||
return nav
|
||||
}
|
||||
|
||||
func updateUIViewController(
|
||||
_ uiViewController: UINavigationController, context: Context) {}
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
return Coordinator(parent: self)
|
||||
}
|
||||
|
||||
class Coordinator: NSObject, QLPreviewControllerDataSource, QLPreviewControllerDelegate {
|
||||
let parent: QuickLookPreview
|
||||
|
||||
init(parent: QuickLookPreview) {
|
||||
self.parent = parent
|
||||
}
|
||||
|
||||
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
|
||||
return parent.urls.count
|
||||
}
|
||||
|
||||
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
|
||||
return parent.urls[index] as QLPreviewItem
|
||||
}
|
||||
|
||||
func previewController(_ controller: QLPreviewController, editingModeFor previewItem: QLPreviewItem) -> QLPreviewItemEditingMode {
|
||||
.createCopy
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AppQLPreviewCpntroller: QLPreviewController {
|
||||
private var closeButton: UIBarButtonItem {
|
||||
.init(title: "Done", style: .plain, target: self, action: #selector(onCloseButton))
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
navigationItem.rightBarButtonItem = closeButton
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
navigationItem.rightBarButtonItem = closeButton
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
navigationItem.rightBarButtonItem = closeButton
|
||||
}
|
||||
|
||||
@objc private func onCloseButton() {
|
||||
dismiss(animated: true)
|
||||
}
|
||||
}
|
|
@ -6,6 +6,8 @@ import SwiftUI
|
|||
|
||||
enum Tab: Int, Identifiable, Hashable {
|
||||
case timeline, notifications, explore, messages, settings, other
|
||||
case trending, federated, local
|
||||
case profile
|
||||
|
||||
var id: Int {
|
||||
rawValue
|
||||
|
@ -16,7 +18,11 @@ enum Tab: Int, Identifiable, Hashable {
|
|||
}
|
||||
|
||||
static func loggedInTabs() -> [Tab] {
|
||||
[.timeline, .notifications, .explore, .messages, .settings]
|
||||
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||
return [.timeline, .trending, .federated, .local, .notifications, .explore, .messages, .settings]
|
||||
} else {
|
||||
return [.timeline, .notifications, .explore, .messages, .settings]
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
|
@ -24,6 +30,12 @@ enum Tab: Int, Identifiable, Hashable {
|
|||
switch self {
|
||||
case .timeline:
|
||||
TimelineTab(popToRootTab: popToRootTab)
|
||||
case .trending:
|
||||
TimelineTab(popToRootTab: popToRootTab, timeline: .trending)
|
||||
case .local:
|
||||
TimelineTab(popToRootTab: popToRootTab, timeline: .local)
|
||||
case .federated:
|
||||
TimelineTab(popToRootTab: popToRootTab, timeline: .federated)
|
||||
case .notifications:
|
||||
NotificationsTab(popToRootTab: popToRootTab)
|
||||
case .explore:
|
||||
|
@ -32,7 +44,7 @@ enum Tab: Int, Identifiable, Hashable {
|
|||
MessagesTab(popToRootTab: popToRootTab)
|
||||
case .settings:
|
||||
SettingsTabs(popToRootTab: popToRootTab)
|
||||
case .other:
|
||||
case .other, .profile:
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
|
@ -41,18 +53,47 @@ enum Tab: Int, Identifiable, Hashable {
|
|||
var label: some View {
|
||||
switch self {
|
||||
case .timeline:
|
||||
Label("Timeline", systemImage: "rectangle.on.rectangle")
|
||||
Label("Timeline", systemImage: iconName)
|
||||
case .trending:
|
||||
Label("Trending", systemImage: iconName)
|
||||
case .local:
|
||||
Label("Local", systemImage: iconName)
|
||||
case .federated:
|
||||
Label("Federated", systemImage: iconName)
|
||||
case .notifications:
|
||||
Label("Notifications", systemImage: "bell")
|
||||
Label("Notifications", systemImage: iconName)
|
||||
case .explore:
|
||||
Label("Explore", systemImage: "magnifyingglass")
|
||||
Label("Explore", systemImage: iconName)
|
||||
case .messages:
|
||||
Label("Messages", systemImage: "tray")
|
||||
Label("Messages", systemImage: iconName)
|
||||
case .settings:
|
||||
Label("Settings", systemImage: "gear")
|
||||
case .other:
|
||||
Label("Settings", systemImage: iconName)
|
||||
case .other, .profile:
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
|
||||
var iconName: String {
|
||||
switch self {
|
||||
case .timeline:
|
||||
return "rectangle.on.rectangle"
|
||||
case .trending:
|
||||
return "chart.line.uptrend.xyaxis"
|
||||
case .local:
|
||||
return "person.2"
|
||||
case .federated:
|
||||
return "globe.americas"
|
||||
case .notifications:
|
||||
return "bell"
|
||||
case .explore:
|
||||
return "magnifyingglass"
|
||||
case .messages:
|
||||
return "tray"
|
||||
case .settings:
|
||||
return "gear"
|
||||
case .other, .profile:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,8 +16,16 @@ struct TimelineTab: View {
|
|||
@Binding var popToRootTab: Tab
|
||||
|
||||
@State private var didAppear: Bool = false
|
||||
@State private var timeline: TimelineFilter = .home
|
||||
@State private var timeline: TimelineFilter
|
||||
@State private var scrollToTopSignal: Int = 0
|
||||
|
||||
private let canFilterTimeline: Bool
|
||||
|
||||
init(popToRootTab: Binding<Tab>, timeline: TimelineFilter? = nil) {
|
||||
canFilterTimeline = timeline == nil
|
||||
self.timeline = timeline ?? .home
|
||||
_popToRootTab = popToRootTab
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
NavigationStack(path: $routeurPath.path) {
|
||||
|
@ -31,7 +39,7 @@ struct TimelineTab: View {
|
|||
}
|
||||
.onAppear {
|
||||
routeurPath.client = client
|
||||
if !didAppear {
|
||||
if !didAppear && canFilterTimeline {
|
||||
didAppear = true
|
||||
timeline = client.isAuth ? .home : .federated
|
||||
}
|
||||
|
@ -124,8 +132,10 @@ struct TimelineTab: View {
|
|||
|
||||
@ToolbarContentBuilder
|
||||
private var toolbarView: some ToolbarContent {
|
||||
ToolbarTitleMenu {
|
||||
timelineFilterButton
|
||||
if canFilterTimeline {
|
||||
ToolbarTitleMenu {
|
||||
timelineFilterButton
|
||||
}
|
||||
}
|
||||
if client.isAuth {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
|
|
|
@ -172,14 +172,12 @@ public struct StatusMediaPreviewView: View {
|
|||
.resizable()
|
||||
.aspectRatio(contentMode: .fill)
|
||||
.frame(maxHeight: imageMaxHeight)
|
||||
.frame(maxWidth: imageMaxHeight)
|
||||
.cornerRadius(4)
|
||||
},
|
||||
placeholder: {
|
||||
RoundedRectangle(cornerRadius: 4)
|
||||
.fill(Color.gray)
|
||||
.frame(maxHeight: imageMaxHeight)
|
||||
.frame(maxWidth: imageMaxHeight)
|
||||
.shimmering()
|
||||
})
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ public enum TimelineFilter: Hashable, Equatable {
|
|||
case .federated:
|
||||
return "globe.americas"
|
||||
case .local:
|
||||
return "person.3"
|
||||
return "person.2"
|
||||
case .trending:
|
||||
return "chart.line.uptrend.xyaxis"
|
||||
case .home:
|
||||
|
|
Loading…
Reference in a new issue