Skip to content

Commit 9acce0e

Browse files
committed
fix deeplink auth cookies, tab selection, old deeplinks
1 parent d47bf2b commit 9acce0e

File tree

5 files changed

+92
-63
lines changed

5 files changed

+92
-63
lines changed

Django Files/API/DFAPI.swift

Lines changed: 47 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,22 @@ struct DFAPI {
223223
selectedServer.token = token
224224
}
225225

226+
@MainActor
227+
private func handleAuthResponse(response: HTTPURLResponse, url: URL, selectedServer: DjangoFilesSession, token: String) {
228+
// Extract cookies from response
229+
if let headerFields = response.allHeaderFields as? [String: String] {
230+
let cookies = HTTPCookie.cookies(withResponseHeaderFields: headerFields, for: url)
231+
// Store cookies in the shared cookie storage
232+
cookies.forEach { cookie in
233+
HTTPCookieStorage.shared.setCookie(cookie)
234+
}
235+
selectedServer.cookies = cookies
236+
}
237+
238+
// Update the token in the server object
239+
selectedServer.token = token
240+
}
241+
226242
public func localLogin(username: String, password: String, selectedServer: DjangoFilesSession) async -> Bool {
227243
let request = DFLocalLoginRequest(username: username, password: password)
228244
do {
@@ -244,20 +260,11 @@ struct DFAPI {
244260
throw URLError(.badServerResponse)
245261
}
246262

247-
// Extract cookies from response
248-
if let headerFields = httpResponse.allHeaderFields as? [String: String] {
249-
let cookies = HTTPCookie.cookies(withResponseHeaderFields: headerFields, for: urlRequest.url!)
250-
// Store cookies in the shared cookie storage
251-
cookies.forEach { cookie in
252-
HTTPCookieStorage.shared.setCookie(cookie)
253-
}
254-
await updateSessionCookies(selectedServer, cookies)
255-
}
263+
let userToken = try decoder.decode(UserToken.self, from: data)
256264

257-
let userToken = try JSONDecoder().decode(UserToken.self, from: data)
265+
// Use shared function to handle cookies and token
266+
await handleAuthResponse(response: httpResponse, url: urlRequest.url!, selectedServer: selectedServer, token: userToken.token)
258267

259-
// Update the token in the server object
260-
await updateSessionToken(selectedServer, userToken.token)
261268
return true
262269
} catch {
263270
print("Local login request failed \(error)")
@@ -284,7 +291,6 @@ struct DFAPI {
284291
if let url = urlRequest.url {
285292
// Set the cookie directly in the request header
286293
urlRequest.setValue("sessionid=\(sessionKey)", forHTTPHeaderField: "Cookie")
287-
//print("Using session key cookie: \(sessionKey) on \(url)")
288294

289295
// Also set it in the cookie storage
290296
let cookieProperties: [HTTPCookiePropertyKey: Any] = [
@@ -298,7 +304,6 @@ struct DFAPI {
298304

299305
if let cookie = HTTPCookie(properties: cookieProperties) {
300306
HTTPCookieStorage.shared.setCookie(cookie)
301-
// print("Set cookie: \(cookie)")
302307
}
303308
}
304309

@@ -307,48 +312,17 @@ struct DFAPI {
307312
configuration.httpCookieStorage = .shared
308313
configuration.httpCookieAcceptPolicy = .always
309314

310-
// Print all cookies before making the request
311-
// print("Cookies before request:")
312-
// HTTPCookieStorage.shared.cookies?.forEach { cookie in
313-
// print(" - \(cookie.name): \(cookie.value)")
314-
// }
315-
316315
let session = URLSession(configuration: configuration)
317316
let (_, response) = try await session.data(for: urlRequest)
318317

319-
// print("response: \(response)")
320-
321318
guard let httpResponse = response as? HTTPURLResponse,
322319
httpResponse.statusCode == 200 else {
323320
print("Request failed with status: \((response as? HTTPURLResponse)?.statusCode ?? -1)")
324321
throw URLError(.badServerResponse)
325322
}
326323

327-
// Print request headers for debugging
328-
// print("Request headers:")
329-
// urlRequest.allHTTPHeaderFields?.forEach { key, value in
330-
// print(" - \(key): \(value)")
331-
// }
332-
333-
// Print response headers for debugging
334-
// print("Response headers:")
335-
// (response as? HTTPURLResponse)?.allHeaderFields.forEach { key, value in
336-
// print(" - \(key): \(value)")
337-
// }
338-
339-
// Extract cookies from response
340-
if let headerFields = httpResponse.allHeaderFields as? [String: String] {
341-
let cookies = HTTPCookie.cookies(withResponseHeaderFields: headerFields, for: urlRequest.url!)
342-
// Store cookies in the shared cookie storage
343-
cookies.forEach { cookie in
344-
HTTPCookieStorage.shared.setCookie(cookie)
345-
// print("Received cookie from response: \(cookie)")
346-
}
347-
await updateSessionCookies(selectedServer, cookies)
348-
}
349-
350-
// Update the token in the server object using the MainActor method
351-
await updateSessionToken(selectedServer, token)
324+
// Use shared function to handle cookies and token
325+
await handleAuthResponse(response: httpResponse, url: urlRequest.url!, selectedServer: selectedServer, token: token)
352326

353327
return true
354328
} catch {
@@ -396,21 +370,35 @@ struct DFAPI {
396370
let token: String
397371
}
398372

399-
public func applicationAuth(signature: String) async -> String? {
373+
public func applicationAuth(signature: String, selectedServer: DjangoFilesSession? = nil) async -> String? {
400374
let request = DFApplicationAuthRequest(signature: signature)
401375
do {
402376
let json = try JSONEncoder().encode(request)
403-
print(json)
404-
print("JSON Data: \(String(data: json, encoding: .utf8) ?? "invalid json")")
405-
let responseBody = try await makeAPIRequest(
406-
body: json,
407-
path: getAPIPath(.auth_application),
408-
parameters: [:],
409-
method: .post,
410-
headerFields: [.contentType: "application/json"]
411-
)
412-
let response = try decoder.decode(DFApplicationAuthResponse.self, from: responseBody)
413-
return response.token
377+
378+
// Create URL request manually to access response headers
379+
var urlRequest = URLRequest(url: encodeParametersIntoURL(path: getAPIPath(.auth_application), parameters: [:]))
380+
urlRequest.httpMethod = "POST"
381+
urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
382+
urlRequest.httpBody = json
383+
384+
// Use default session configuration which persists cookies
385+
let configuration = URLSessionConfiguration.default
386+
let session = URLSession(configuration: configuration)
387+
let (data, response) = try await session.data(for: urlRequest)
388+
389+
guard let httpResponse = response as? HTTPURLResponse,
390+
httpResponse.statusCode == 200 else {
391+
throw URLError(.badServerResponse)
392+
}
393+
394+
let auth_response = try decoder.decode(DFApplicationAuthResponse.self, from: data)
395+
396+
// Use shared function to handle cookies and token if server is provided
397+
if let selectedServer = selectedServer {
398+
await handleAuthResponse(response: httpResponse, url: urlRequest.url!, selectedServer: selectedServer, token: auth_response.token)
399+
}
400+
401+
return auth_response.token
414402
} catch {
415403
print("Application auth request failed \(error)")
416404
return nil

Django Files/Django_FilesApp.swift

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ struct Django_FilesApp: App {
2525
@StateObject private var sessionManager = SessionManager()
2626
@State private var hasExistingSessions = false
2727
@State private var isLoading = true
28+
@State private var selectedTab: TabViewWindow.Tab = .files
2829

2930
init() {
3031
// print("📱 Setting up WebSocketToastObserver")
@@ -40,7 +41,7 @@ struct Django_FilesApp: App {
4041
checkForExistingSessions()
4142
}
4243
} else if hasExistingSessions {
43-
TabViewWindow(sessionManager: sessionManager)
44+
TabViewWindow(sessionManager: sessionManager, selectedTab: $selectedTab)
4445
} else {
4546
SessionEditor(onBoarding: true, session: nil, onSessionCreated: { newSession in
4647
sessionManager.selectedSession = newSession
@@ -61,22 +62,56 @@ struct Django_FilesApp: App {
6162
}
6263

6364
private func handleDeepLink(_ url: URL) {
64-
// print("Deep link received: \(url)")
65+
print("Deep link received: \(url)")
6566
guard url.scheme == "djangofiles" else { return }
6667

6768
// Extract the signature from the URL parameters
6869
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: true) else {
6970
print("Invalid deep link URL")
7071
return
7172
}
73+
print("Deep link host: \(components.host ?? "unknown")")
7274
switch components.host {
7375
case "authorize":
7476
deepLinkAuth(components)
77+
case "serverlist":
78+
selectedTab = .serverList
79+
case "filelist":
80+
handleFileListDeepLink(components)
7581
default:
82+
ToastManager.shared.showToast(message: "Unsupported deep link \(url)")
7683
print("Unsupported deep link type: \(components.host ?? "unknown")")
7784
}
7885
}
7986

87+
private func handleFileListDeepLink(_ components: URLComponents) {
88+
guard let urlString = components.queryItems?.first(where: { $0.name == "url" })?.value?.removingPercentEncoding,
89+
let serverURL = URL(string: urlString) else {
90+
print("Invalid server URL in filelist deep link")
91+
return
92+
}
93+
94+
// Find the session with matching URL and select it
95+
let context = sharedModelContainer.mainContext
96+
let descriptor = FetchDescriptor<DjangoFilesSession>()
97+
98+
Task {
99+
do {
100+
let existingSessions = try context.fetch(descriptor)
101+
if let matchingSession = existingSessions.first(where: { $0.url == serverURL.absoluteString }) {
102+
await MainActor.run {
103+
sessionManager.selectedSession = matchingSession
104+
selectedTab = .files
105+
}
106+
} else {
107+
print("No session found for URL: \(serverURL.absoluteString)")
108+
}
109+
} catch {
110+
print("Error fetching sessions: \(error)")
111+
}
112+
}
113+
}
114+
80115
private func deepLinkAuth(_ components: URLComponents) {
81116
guard let signature = components.queryItems?.first(where: { $0.name == "signature" })?.value?.removingPercentEncoding,
82117
let serverURL = URL(string: components.queryItems?.first(where: { $0.name == "url" })?.value?.removingPercentEncoding ?? "") else {

Django Files/Util/SessionManager.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class SessionManager: ObservableObject {
2525
let api = DFAPI(url: URL(string: serverURL)!, token: "")
2626

2727
// Get token using the signature
28-
if let token = await api.applicationAuth(signature: signature) {
28+
if let token = await api.applicationAuth(signature: signature, selectedServer: newSession) {
2929
newSession.token = token
3030
newSession.auth = true
3131

Django Files/Views/Preview.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -801,6 +801,7 @@ struct FilePreviewView: View {
801801
.padding(8)
802802
}
803803
.buttonStyle(.borderless)
804+
.padding(.leading, 1)
804805
Spacer()
805806
}
806807
.background(.ultraThinMaterial)

Django Files/Views/TabView.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,23 @@ import SwiftData
1111
struct TabViewWindow: View {
1212
@Environment(\.modelContext) private var modelContext
1313
@ObservedObject var sessionManager: SessionManager
14+
@Binding var selectedTab: Tab
1415

1516
@State private var showingServerSelector = false
1617
@Query private var sessions: [DjangoFilesSession]
1718
@State private var needsRefresh = false
1819
@State private var serverChangeRefreshTrigger = UUID()
1920
@State private var serverNeedsAuth: DjangoFilesSession?
2021

21-
@State private var selectedTab: Tab = .files
2222
@State private var showLoginSheet = false
2323
@State private var filesNavigationPath = NavigationPath()
2424
@State private var albumsNavigationPath = NavigationPath()
2525

26+
init(sessionManager: SessionManager, selectedTab: Binding<Tab>) {
27+
self.sessionManager = sessionManager
28+
_selectedTab = selectedTab
29+
}
30+
2631
enum Tab {
2732
case files, albums, shorts, serverList, userSettings, serverSettings, mobileWeb
2833
}

0 commit comments

Comments
 (0)