Skip to content

Commit 2b337a9

Browse files
committed
core changes (wip)
1 parent 6dba6a4 commit 2b337a9

22 files changed

+2738
-736
lines changed

apple/InlineMac/App/AppDelegate.swift

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
2525

2626
private var cancellables = Set<AnyCancellable>()
2727

28-
func applicationWillFinishLaunching(_ notification: Notification) {
28+
func applicationWillFinishLaunching(_: Notification) {
2929
// Disable native tabbing
3030
NSWindow.allowsAutomaticWindowTabbing = false
3131

@@ -39,7 +39,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
3939
dependencies.logOut = logOut
4040
}
4141

42-
func applicationDidFinishLaunching(_ aNotification: Notification) {
42+
func applicationDidFinishLaunching(_: Notification) {
4343
initializeServices()
4444
setupMainWindow()
4545
setupMainMenu()
@@ -69,11 +69,11 @@ class AppDelegate: NSObject, NSApplicationDelegate {
6969
}
7070
}
7171

72-
func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
72+
func applicationSupportsSecureRestorableState(_: NSApplication) -> Bool {
7373
true
7474
}
7575

76-
func applicationDidResignActive(_ notification: Notification) {
76+
func applicationDidResignActive(_: Notification) {
7777
// Task {
7878
// if Auth.shared.isLoggedIn {
7979
// // Mark offline
@@ -82,7 +82,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
8282
// }
8383
}
8484

85-
func applicationDidBecomeActive(_ notification: Notification) {
85+
func applicationDidBecomeActive(_: Notification) {
8686
// Task {
8787
// if Auth.shared.isLoggedIn {
8888
// // Mark online
@@ -105,14 +105,14 @@ class AppDelegate: NSObject, NSApplicationDelegate {
105105
mainWindowController = controller
106106
}
107107

108-
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
108+
func applicationShouldHandleReopen(_: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
109109
if !flag {
110110
setupMainWindow()
111111
}
112112
return true
113113
}
114114

115-
func application(_ application: NSApplication, open urls: [URL]) {
115+
func application(_: NSApplication, open urls: [URL]) {
116116
// Handle URLs when app is already running
117117
for url in urls {
118118
log.debug("Received URL via application:open: \(url)")
@@ -137,10 +137,10 @@ class AppDelegate: NSObject, NSApplicationDelegate {
137137

138138
// Handle different URL patterns
139139
switch url.host {
140-
case "user":
141-
handleUserURL(pathComponents: pathComponents)
142-
default:
143-
log.warning("Unhandled URL host: \(url.host ?? "nil")")
140+
case "user":
141+
handleUserURL(pathComponents: pathComponents)
142+
default:
143+
log.warning("Unhandled URL host: \(url.host ?? "nil")")
144144
}
145145
}
146146
}
@@ -200,7 +200,7 @@ extension AppDelegate {
200200
}
201201

202202
func application(
203-
_ application: NSApplication,
203+
_: NSApplication,
204204
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
205205
) {
206206
log.debug("Registered for remote notifications: \(deviceToken)")
@@ -209,7 +209,7 @@ extension AppDelegate {
209209
}
210210

211211
func application(
212-
_ application: NSApplication,
212+
_: NSApplication,
213213
didFailToRegisterForRemoteNotificationsWithError error: Error
214214
) {
215215
log.error("Failed to register for remote notifications \(error)")
@@ -278,7 +278,7 @@ extension AppDelegate {
278278
// MARK: - URL Scheme Handling
279279

280280
extension AppDelegate {
281-
@objc func handleURLEvent(_ event: NSAppleEventDescriptor, withReplyEvent replyEvent: NSAppleEventDescriptor) {
281+
@objc func handleURLEvent(_ event: NSAppleEventDescriptor, withReplyEvent _: NSAppleEventDescriptor) {
282282
guard let urlString = event.paramDescriptor(forKeyword: keyDirectObject)?.stringValue,
283283
let url = URL(string: urlString)
284284
else {

apple/InlineMac/App/AppDependencies.swift

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public struct AppDependencies {
1818

1919
// Per window
2020
let nav: Nav = .main
21+
var nav2: Nav2? = nil
2122
var keyMonitor: KeyMonitor?
2223

2324
// Optional
@@ -26,7 +27,8 @@ public struct AppDependencies {
2627
}
2728

2829
extension View {
29-
func environment(dependencies deps: AppDependencies) -> AnyView {
30+
@ViewBuilder
31+
func environment(dependencies deps: AppDependencies) -> some View {
3032
var result = environment(\.auth, deps.auth)
3133
.environmentObject(deps.viewModel)
3234
.environmentObject(deps.overlay)
@@ -42,12 +44,12 @@ extension View {
4244
.environment(\.logOut, deps.logOut)
4345
.environment(\.keyMonitor, deps.keyMonitor)
4446
.environment(\.dependencies, deps)
45-
.eraseToAnyView()
47+
.environment(deps.nav2)
4648

4749
if let rootData = deps.rootData {
48-
result = result.environmentObject(rootData).eraseToAnyView()
50+
result.environmentObject(rootData)
51+
} else {
52+
result
4953
}
50-
51-
return result
5254
}
5355
}

apple/InlineMac/App/Nav2.swift

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
import Combine
2+
import Foundation
3+
import InlineKit
4+
import Logger
5+
import Observation
6+
7+
enum Nav2Route: Equatable, Hashable, Codable {
8+
case empty
9+
case chat(peer: Peer)
10+
case chatInfo(peer: Peer)
11+
case profile(userId: Int64)
12+
case createSpace
13+
case newChat
14+
case inviteToSpace
15+
}
16+
17+
enum TabId: Hashable, Codable {
18+
case home
19+
case space(id: Int64, name: String)
20+
// case chat(Int64, spaceId: Int64)
21+
22+
var spaceId: Int64? {
23+
switch self {
24+
case let .space(id, _):
25+
id
26+
default:
27+
nil
28+
}
29+
}
30+
31+
var tabTitle: String? {
32+
switch self {
33+
case let .space(_, name):
34+
name
35+
case .home:
36+
"Home"
37+
}
38+
}
39+
}
40+
41+
struct Nav2Entry: Codable {
42+
var route: Nav2Route
43+
var tab: TabId
44+
}
45+
46+
/// Manages navigation per window
47+
@Observable class Nav2 {
48+
@ObservationIgnored private let log = Log.scoped("Nav2", enableTracing: false)
49+
@ObservationIgnored private var saveStateTask: Task<Void, Never>?
50+
51+
// MARK: - State
52+
53+
var tabs: [TabId] = [.home, .space(id: 1, name: "Inline")]
54+
55+
var activeTabIndex: Int = 0
56+
57+
/// History of navigation entries, current entry is last item in the history array
58+
var history: [Nav2Entry] = []
59+
60+
var forwardHistory: [Nav2Entry] = []
61+
62+
var activeSpaceId: Int64? {
63+
history.last?.tab.spaceId
64+
}
65+
66+
var activeTab: TabId {
67+
tabs[activeTabIndex]
68+
}
69+
70+
// MARK: - Methods
71+
72+
func navigate(to route: Nav2Route) {
73+
log.trace("Navigating to \(route)")
74+
history.append(Nav2Entry(route: route, tab: activeTab))
75+
}
76+
77+
func removeTab(at index: Int) {
78+
guard index < tabs.count else { return }
79+
guard tabs.count > 1 else { return }
80+
81+
tabs.remove(at: index)
82+
83+
if activeTabIndex >= tabs.count {
84+
activeTabIndex = tabs.count - 1
85+
} else if activeTabIndex > index {
86+
activeTabIndex -= 1
87+
}
88+
89+
saveStateLowPriority()
90+
}
91+
92+
func setActiveTab(index: Int) {
93+
guard index < tabs.count else { return }
94+
activeTabIndex = index
95+
saveStateLowPriority()
96+
}
97+
98+
// MARK: - Initialization & Persistence
99+
100+
init() {
101+
// UNCOMMENT THIS WHEN WE HAVE A PERSISTENT STATE
102+
// loadState()
103+
}
104+
105+
// File URL for persistence
106+
private var stateFileURL: URL {
107+
FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
108+
.appendingPathComponent("nav_state_v2.json")
109+
}
110+
111+
struct Persisted: Codable {
112+
var tabs: [TabId]
113+
var activeTabIndex: Int
114+
}
115+
116+
private func saveState() {
117+
let state = Persisted(
118+
tabs: tabs,
119+
activeTabIndex: activeTabIndex
120+
)
121+
122+
do {
123+
let encoder = JSONEncoder()
124+
let data = try encoder.encode(state)
125+
try data.write(to: stateFileURL)
126+
} catch {
127+
Log.shared.error("Failed to save navigation state: \(error.localizedDescription)")
128+
}
129+
}
130+
131+
private func loadState() {
132+
guard FileManager.default.fileExists(atPath: stateFileURL.path) else { return }
133+
134+
do {
135+
let data = try Data(contentsOf: stateFileURL)
136+
let decoder = JSONDecoder()
137+
let state = try decoder.decode(Persisted.self, from: data)
138+
139+
print("loaded nav state \(state)")
140+
141+
// Update state
142+
tabs = state.tabs
143+
activeTabIndex = state.activeTabIndex
144+
} catch {
145+
Log.shared.error("Failed to load navigation state: \(error.localizedDescription)")
146+
// If loading fails, reset to default state
147+
reset()
148+
}
149+
}
150+
151+
// Called on logout
152+
func reset() {
153+
tabs = [.home]
154+
activeTabIndex = 0
155+
history = []
156+
forwardHistory = []
157+
158+
// Delete persisted state file
159+
try? FileManager.default.removeItem(at: stateFileURL)
160+
}
161+
162+
// Utility
163+
private func saveStateLowPriority() {
164+
saveStateTask?.cancel()
165+
saveStateTask = Task(priority: .background) {
166+
saveState()
167+
}
168+
}
169+
}

0 commit comments

Comments
 (0)