The Mac App

This commit is contained in:
Thomas Ricouard 2023-01-16 14:40:23 +01:00
parent 6a7db8714d
commit c05768c793
7 changed files with 199 additions and 23 deletions

View file

@ -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;

View file

@ -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

View 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)
}
}

View file

@ -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 ""
}
}
}

View file

@ -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) {

View file

@ -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()
})
}

View file

@ -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: