@@ -11,10 +11,16 @@ import FirebaseCore
11
11
import FirebaseAnalytics
12
12
import FirebaseCrashlytics
13
13
14
+ class PreviewStateManager : ObservableObject {
15
+ @Published var deepLinkFile : DFFile ?
16
+ @Published var showingDeepLinkPreview = false
17
+ @Published var deepLinkTargetFileID : Int ? = nil
18
+ @Published var deepLinkFilePassword : String ? = nil
19
+ }
20
+
14
21
class AppDelegate : NSObject , UIApplicationDelegate {
15
22
func application( _ application: UIApplication ,
16
23
didFinishLaunchingWithOptions launchOptions: [ UIApplication . LaunchOptionsKey : Any ] ? = nil ) -> Bool {
17
- // Skip Firebase initialization if disabled via launch arguments
18
24
let shouldDisableFirebase = ProcessInfo . processInfo. arguments. contains ( " --DisableFirebase " )
19
25
if !shouldDisableFirebase {
20
26
FirebaseApp . configure ( )
@@ -36,6 +42,8 @@ class AppDelegate: NSObject, UIApplicationDelegate {
36
42
@main
37
43
struct Django_FilesApp : App {
38
44
@UIApplicationDelegateAdaptor ( AppDelegate . self) var delegate
45
+ @StateObject private var previewStateManager = PreviewStateManager ( )
46
+ @State private var showFileInfo = false
39
47
var sharedModelContainer : ModelContainer = {
40
48
let schema = Schema ( [
41
49
DjangoFilesSession . self,
@@ -112,26 +120,72 @@ struct Django_FilesApp: App {
112
120
TabViewWindow ( sessionManager: sessionManager, selectedTab: $selectedTab)
113
121
}
114
122
}
123
+ . environmentObject ( previewStateManager)
115
124
. onOpenURL { url in
116
- handleDeepLink ( url)
125
+ DeepLinks . shared. handleDeepLink (
126
+ url,
127
+ context: sharedModelContainer. mainContext,
128
+ sessionManager: sessionManager,
129
+ previewStateManager: previewStateManager,
130
+ selectedTab: $selectedTab,
131
+ hasExistingSessions: $hasExistingSessions,
132
+ showingServerConfirmation: $showingServerConfirmation,
133
+ pendingAuthURL: $pendingAuthURL,
134
+ pendingAuthSignature: $pendingAuthSignature
135
+ )
117
136
}
118
137
. sheet ( isPresented: $showingServerConfirmation) {
119
138
ServerConfirmationView (
120
139
serverURL: $pendingAuthURL,
121
140
signature: $pendingAuthSignature,
122
141
onConfirm: { setAsDefault in
123
142
Task {
124
- await handleServerConfirmation ( confirmed: true , setAsDefault: setAsDefault)
143
+ await DeepLinks . shared. handleServerConfirmation (
144
+ confirmed: true ,
145
+ setAsDefault: setAsDefault,
146
+ pendingAuthURL: $pendingAuthURL,
147
+ pendingAuthSignature: $pendingAuthSignature,
148
+ context: sharedModelContainer. mainContext,
149
+ sessionManager: sessionManager,
150
+ hasExistingSessions: $hasExistingSessions,
151
+ selectedTab: $selectedTab
152
+ )
125
153
}
126
154
} ,
127
155
onCancel: {
128
156
Task {
129
- await handleServerConfirmation ( confirmed: false , setAsDefault: false )
157
+ await DeepLinks . shared. handleServerConfirmation (
158
+ confirmed: false ,
159
+ setAsDefault: false ,
160
+ pendingAuthURL: $pendingAuthURL,
161
+ pendingAuthSignature: $pendingAuthSignature,
162
+ context: sharedModelContainer. mainContext,
163
+ sessionManager: sessionManager,
164
+ hasExistingSessions: $hasExistingSessions,
165
+ selectedTab: $selectedTab
166
+ )
130
167
}
131
168
} ,
132
169
context: sharedModelContainer. mainContext
133
170
)
134
171
}
172
+ . fullScreenCover ( isPresented: $previewStateManager. showingDeepLinkPreview) {
173
+ if let file = previewStateManager. deepLinkFile {
174
+ FilePreviewView (
175
+ file: . constant( file) ,
176
+ server: . constant( nil ) ,
177
+ showingPreview: $previewStateManager. showingDeepLinkPreview,
178
+ showFileInfo: $showFileInfo,
179
+ fileListDelegate: nil ,
180
+ allFiles: . constant( [ file] ) ,
181
+ currentIndex: 0 ,
182
+ onNavigate: { _ in }
183
+ )
184
+ . onDisappear {
185
+ previewStateManager. deepLinkFile = nil
186
+ }
187
+ }
188
+ }
135
189
}
136
190
. modelContainer ( sharedModelContainer)
137
191
#if os(macOS)
@@ -141,145 +195,6 @@ struct Django_FilesApp: App {
141
195
#endif
142
196
}
143
197
144
- private func handleDeepLink( _ url: URL ) {
145
- print ( " Deep link received: \( url) " )
146
- guard url. scheme == " djangofiles " else { return }
147
-
148
- // Extract the signature from the URL parameters
149
- guard let components = URLComponents ( url: url, resolvingAgainstBaseURL: true ) else {
150
- print ( " Invalid deep link URL " )
151
- return
152
- }
153
- print ( " Deep link host: \( components. host ?? " unknown " ) " )
154
- switch components. host {
155
- case " authorize " :
156
- deepLinkAuth ( components)
157
- case " serverlist " :
158
- selectedTab = . settings
159
- case " filelist " :
160
- handleFileListDeepLink ( components)
161
- default :
162
- ToastManager . shared. showToast ( message: " Unsupported deep link \( url) " )
163
- print ( " Unsupported deep link type: \( components. host ?? " unknown " ) " )
164
- }
165
- }
166
-
167
- private func handleFileListDeepLink( _ components: URLComponents ) {
168
- guard let urlString = components. queryItems? . first ( where: { $0. name == " url " } ) ? . value? . removingPercentEncoding,
169
- let serverURL = URL ( string: urlString) else {
170
- print ( " Invalid server URL in filelist deep link " )
171
- return
172
- }
173
-
174
- // Find the session with matching URL and select it
175
- let context = sharedModelContainer. mainContext
176
- let descriptor = FetchDescriptor < DjangoFilesSession > ( )
177
-
178
- Task {
179
- do {
180
- let existingSessions = try context. fetch ( descriptor)
181
- if let matchingSession = existingSessions. first ( where: { $0. url == serverURL. absoluteString } ) {
182
- await MainActor . run {
183
- sessionManager. selectedSession = matchingSession
184
- selectedTab = . files
185
- }
186
- } else {
187
- print ( " No session found for URL: \( serverURL. absoluteString) " )
188
- }
189
- } catch {
190
- print ( " Error fetching sessions: \( error) " )
191
- }
192
- }
193
- }
194
-
195
- private func deepLinkAuth( _ components: URLComponents ) {
196
- guard let signature = components. queryItems? . first ( where: { $0. name == " signature " } ) ? . value? . removingPercentEncoding,
197
- let serverURL = URL ( string: components. queryItems? . first ( where: { $0. name == " url " } ) ? . value? . removingPercentEncoding ?? " " ) else {
198
- print ( " Unable to parse auth deep link. " )
199
- return
200
- }
201
-
202
- // Check if a session with this URL already exists
203
- let context = sharedModelContainer. mainContext
204
- let descriptor = FetchDescriptor < DjangoFilesSession > ( )
205
-
206
- Task {
207
- do {
208
- let existingSessions = try context. fetch ( descriptor)
209
- if let existingSession = existingSessions. first ( where: { $0. url == serverURL. absoluteString } ) {
210
- // If session exists, just select it and update UI
211
- await MainActor . run {
212
- sessionManager. selectedSession = existingSession
213
- hasExistingSessions = true
214
- ToastManager . shared. showToast ( message: " Connected to existing server \( existingSession. url) " )
215
- }
216
- return
217
- }
218
-
219
- // No existing session, show confirmation dialog
220
- await MainActor . run {
221
- pendingAuthURL = serverURL
222
- pendingAuthSignature = signature
223
- showingServerConfirmation = true
224
- }
225
- } catch {
226
- print ( " Error checking for existing sessions: \( error) " )
227
- }
228
- }
229
- }
230
-
231
- private func handleServerConfirmation( confirmed: Bool , setAsDefault: Bool ) async {
232
- guard let serverURL = pendingAuthURL,
233
- let signature = pendingAuthSignature else {
234
- return
235
- }
236
-
237
- // If user cancelled, just clear the pending data and return
238
- if !confirmed {
239
- pendingAuthURL = nil
240
- pendingAuthSignature = nil
241
- return
242
- }
243
-
244
- await MainActor . run {
245
- // Create and authenticate the new session
246
- let context = sharedModelContainer. mainContext
247
-
248
- do {
249
- let descriptor = FetchDescriptor < DjangoFilesSession > ( )
250
- let existingSessions = try context. fetch ( descriptor)
251
-
252
- // Create and authenticate the new session
253
- Task {
254
- if let newSession = await sessionManager. createAndAuthenticateSession (
255
- url: serverURL,
256
- signature: signature,
257
- context: context
258
- ) {
259
- if setAsDefault {
260
- // Reset all other sessions to not be default
261
- for session in existingSessions {
262
- session. defaultSession = false
263
- }
264
- newSession. defaultSession = true
265
- }
266
- sessionManager. selectedSession = newSession
267
- hasExistingSessions = true
268
- selectedTab = . files
269
- ToastManager . shared. showToast ( message: " Successfully logged into \( newSession. url) " )
270
- }
271
- }
272
- } catch {
273
- ToastManager . shared. showToast ( message: " Problem signing into server \( error) " )
274
- print ( " Error creating new session: \( error) " )
275
- }
276
-
277
- // Clear pending auth data
278
- pendingAuthURL = nil
279
- pendingAuthSignature = nil
280
- }
281
- }
282
-
283
198
private func checkDefaultServer( ) {
284
199
let context = sharedModelContainer. mainContext
285
200
let descriptor = FetchDescriptor < DjangoFilesSession > ( )
@@ -313,10 +228,10 @@ struct Django_FilesApp: App {
313
228
if hasExistingSessions {
314
229
checkDefaultServer ( )
315
230
}
316
- isLoading = false // Set loading to false after check completes
231
+ isLoading = false
317
232
} catch {
318
233
print ( " Error checking for existing sessions: \( error) " )
319
- isLoading = false // Ensure we exit loading state even on error
234
+ isLoading = false
320
235
}
321
236
}
322
237
}
0 commit comments