Skip to content
2 changes: 1 addition & 1 deletion kDrive/AppRouter+BasicLink.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public extension AppRouter {
)
}

@MainActor func handleSimpleLink(deeplink: Any, fileId: Int, isOfficeLink: Bool) async {
@MainActor func handleSimpleLink(deeplink: LinkDriveProvider, fileId: Int, isOfficeLink: Bool) async {
guard let driveFileManager = await accountManager
.getMatchingDriveFileManagerOrSwitchAccount(deeplink: deeplink) else {
Log.sceneDelegate(
Expand Down
4 changes: 4 additions & 0 deletions kDrive/AppRouter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ public struct AppRouter: AppNavigable {
matomo.track(eventWithCategory: .deeplink, name: "internal")
await handleSimpleLink(deeplink: filePreviewLink, fileId: filePreviewLink.fileId, isOfficeLink: false)

case .favoritePreview(let favoritePreviewLink):
matomo.track(eventWithCategory: .deeplink, name: "internal")
await handleSimpleLink(deeplink: favoritePreviewLink, fileId: favoritePreviewLink.fileId, isOfficeLink: false)

case .search(let searchLink):
handleSearchLink(searchLink: searchLink)

Expand Down
25 changes: 3 additions & 22 deletions kDriveCore/Data/Cache/AccountManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public protocol AccountManageable: AnyObject {
func forceReload()
func reloadTokensAndAccounts()
func getDriveFileManager(for driveId: Int, userId: Int) -> DriveFileManager?
@MainActor func getMatchingDriveFileManagerOrSwitchAccount(deeplink: Any) async -> DriveFileManager?
@MainActor func getMatchingDriveFileManagerOrSwitchAccount(deeplink: LinkDriveProvider) async -> DriveFileManager?
func getFirstAvailableDriveFileManager(for userId: Int) throws -> DriveFileManager
func getFirstMatchingDriveFileManager(for userId: Int, driveId: Int) throws -> DriveFileManager?

Expand Down Expand Up @@ -243,30 +243,11 @@ public class AccountManager: RefreshTokenDelegate, AccountManageable {
return (driveFileManager, matchingAccount)
}

@MainActor public func getMatchingDriveFileManagerOrSwitchAccount(deeplink: Any) async
@MainActor public func getMatchingDriveFileManagerOrSwitchAccount(deeplink: LinkDriveProvider) async
-> DriveFileManager? {
let driveId = deeplink.driveId
var driveFileManager: DriveFileManager?
var matchingAccount: Account?
let driveId: Int

switch deeplink {
case let deeplink as PublicShareLink:
driveId = deeplink.driveId
case let deeplink as SharedWithMeLink:
driveId = deeplink.driveId
case let deeplink as OfficeLink:
driveId = deeplink.driveId
case let deeplink as PrivateShareLink:
driveId = deeplink.driveId
case let deeplink as DirectoryLink:
driveId = deeplink.driveId
case let deeplink as FilePreviewLink:
driveId = deeplink.driveId
case let deeplink as BasicLink:
driveId = deeplink.driveId
default:
return nil
}

let orderedAccounts = accounts.sorted { account1, account2 in
let isAccount1Connected = account1 == currentAccount
Expand Down
4 changes: 4 additions & 0 deletions kDriveCore/Utils/DeeplinkParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ public struct DeeplinkParser: DeeplinkParsable {
await router.navigate(to: .filePreview(filePreviewLink: filePreviewLink))
return true
}
if let favoritePreviewLink = FavoritePreviewLink(filePreviewURL: url) {
await router.navigate(to: .favoritePreview(favoritePreviewLink: favoritePreviewLink))
return true
}
if let basicLink = BasicLink(basicURL: url) {
await router.navigate(to: .basic(basicLink: basicLink))
return true
Expand Down
2 changes: 1 addition & 1 deletion kDriveCore/Utils/Deeplinks/BasicLink.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public enum BasicLinkTab: String, Sendable {
case favorites
}

public struct BasicLink: Sendable, Equatable {
public struct BasicLink: Sendable, Equatable, LinkDriveProvider {
public static let parsingRegex = Regex(pattern: #"^.*/app/drive/([0-9]+)/([a-z-]+)(?:.*/([0-9]+))?$"#)

public let basicURL: URL
Expand Down
23 changes: 23 additions & 0 deletions kDriveCore/Utils/Deeplinks/DeeplinkProtocol.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
Infomaniak kDrive - iOS App
Copyright (C) 2025 Infomaniak Network SA

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import Foundation

public protocol LinkDriveProvider: Sendable {
var driveId: Int { get }
}
2 changes: 1 addition & 1 deletion kDriveCore/Utils/Deeplinks/DirectoryLink.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import Foundation
import SwiftRegex

public struct DirectoryLink: Sendable, Equatable {
public struct DirectoryLink: Sendable, Equatable, LinkDriveProvider {
public static let parsingRegex = Regex(pattern: #"^.*/app/drive/([0-9]+)/files/([0-9]+)$"#)

public let directoryURL: URL
Expand Down
52 changes: 52 additions & 0 deletions kDriveCore/Utils/Deeplinks/FavoritePreviewLink.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
Infomaniak kDrive - iOS App
Copyright (C) 2025 Infomaniak Network SA

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import Foundation
import SwiftRegex

public struct FavoritePreviewLink: Sendable, Equatable, LinkDriveProvider {
public static let parsingRegex = Regex(pattern: #"^.*/app/drive/([0-9]+)/favorites/preview/[a-z]+/([0-9]+)$"#)

public let filePreviewURL: URL
public let driveId: Int
public let fileId: Int

public init?(filePreviewURL: URL) {
guard let components = URLComponents(url: filePreviewURL, resolvingAgainstBaseURL: true) else {
return nil
}

let path = components.path
guard let matches = Self.parsingRegex?.matches(in: path) else {
return nil
}

guard let firstMatch = matches.first,
let driveId = firstMatch[safe: 1],
let driveIdInt = Int(driveId),
let fileId = firstMatch[safe: 2],
let fileIdInt = Int(fileId)
else {
return nil
}

self.filePreviewURL = filePreviewURL
self.driveId = driveIdInt
self.fileId = fileIdInt
}
}
2 changes: 1 addition & 1 deletion kDriveCore/Utils/Deeplinks/FilePreviewLink.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import Foundation
import SwiftRegex

public struct FilePreviewLink: Sendable, Equatable {
public struct FilePreviewLink: Sendable, Equatable, LinkDriveProvider {
public static let parsingRegex = Regex(pattern: #"^.*/app/drive/([0-9]+)/files/([0-9]+)/preview/[a-z]+/([0-9]+)$"#)

public let filePreviewURL: URL
Expand Down
2 changes: 1 addition & 1 deletion kDriveCore/Utils/Deeplinks/OfficeLink.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import Foundation
import SwiftRegex

public struct OfficeLink: Sendable, Equatable {
public struct OfficeLink: Sendable, Equatable, LinkDriveProvider {
public static let parsingRegex = Regex(pattern: #"^.*/app/office/([0-9]+)/([0-9]+)$"#)

public let officeURL: URL
Expand Down
2 changes: 1 addition & 1 deletion kDriveCore/Utils/Deeplinks/PrivateShareLink.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import Foundation
import SwiftRegex

public struct PrivateShareLink: Sendable, Equatable {
public struct PrivateShareLink: Sendable, Equatable, LinkDriveProvider {
public static let parsingRegex = Regex(pattern: #"^.*/app/drive/([0-9]+)/redirect/([0-9]+)$"#)

public let privateShareUrl: URL
Expand Down
2 changes: 1 addition & 1 deletion kDriveCore/Utils/Deeplinks/PublicShareLink.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import Foundation
import SwiftRegex

public struct PublicShareLink: Sendable, Equatable {
public struct PublicShareLink: Sendable, Equatable, LinkDriveProvider {
public static let parsingRegex = Regex(pattern: #"^.*/app/share/([0-9]+)/([a-z0-9-]+)$"#)

public let publicShareURL: URL
Expand Down
2 changes: 1 addition & 1 deletion kDriveCore/Utils/Deeplinks/SearchLink.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import Foundation
import SwiftRegex

public struct SearchLink: Sendable, Equatable {
public struct SearchLink: Sendable, Equatable, LinkDriveProvider {
public static let parsingRegex = Regex(pattern: #"^.*/app/drive/([0-9]+)/search.*$"#)

public let searchURL: URL
Expand Down
2 changes: 1 addition & 1 deletion kDriveCore/Utils/Deeplinks/SharedWithMeLink.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import Foundation
import SwiftRegex

public struct SharedWithMeLink: Sendable, Equatable {
public struct SharedWithMeLink: Sendable, Equatable, LinkDriveProvider {
public static let validationRegex =
Regex(pattern: #"^.*/app/drive/([0-9]+)/shared-with-me(.*)?$"#)

Expand Down
1 change: 1 addition & 0 deletions kDriveCore/Utils/Routable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public enum NavigationRoutes: Equatable {
case publicShare(publicShareLink: PublicShareLink)
case directory(directoryLink: DirectoryLink)
case filePreview(filePreviewLink: FilePreviewLink)
case favoritePreview(favoritePreviewLink: FavoritePreviewLink)
case search(searchLink: SearchLink)
case basic(basicLink: BasicLink)

Expand Down
2 changes: 1 addition & 1 deletion kDriveTests/kDrive/Launch/MockAccountManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class MockAccountManager: AccountManageable, RefreshTokenDelegate {

func getFirstAvailableDriveFileManager(for userId: Int) throws -> DriveFileManager { fatalError("Not implemented") }

@MainActor func getMatchingDriveFileManagerOrSwitchAccount(deeplink: Any)
@MainActor func getMatchingDriveFileManagerOrSwitchAccount(deeplink: LinkDriveProvider)
-> DriveFileManager? {
fatalError("Not implemented")
}
Expand Down
63 changes: 63 additions & 0 deletions kDriveTests/kDriveCore/DeepLinks/UTFavoritePreviewLink.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
Infomaniak kDrive - iOS App
Copyright (C) 2025 Infomaniak Network SA

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import Foundation
import kDriveCore
import Testing

@Suite("UTFavoritePreviewLink")
struct UTFavoritePreviewLink {
@Test("Parse a fav link with a preview", arguments: [
(1234, 5678, "https://ksuite.infomaniak.com/all/kdrive/app/drive/1234/favorites/preview/pdf/5678"),
(1234, 5678, "https://ksuite.infomaniak.com/all/kdrive/app/drive/1234/favorites/preview/image/5678")
])
func parseFavoritePreview(driveId: Int, fileId: Int, deeplink: String) async throws {
// WHEN
guard let url = URL(string: deeplink),
let parsedResult = FavoritePreviewLink(filePreviewURL: url) else {
Issue.record("Failed to parse the URL")
return
}

// THEN
#expect(parsedResult.filePreviewURL == url)
#expect(parsedResult.driveId == driveId)
#expect(parsedResult.fileId == fileId)
}

@Test("Fail to parse a non favorite preview link", arguments: [
"https://ksuite.infomaniak.com/all/kdrive/app/drive/1234/favorites/preview/noop",
"https://ksuite.infomaniak.com/all/kdrive/app/drive/123/files/456/preview/pdf/789",
"https://ksuite.infomaniak.com/all/kdrive/app/drive/123/files/456/preview/image/789",
"https://ksuite.infomaniak.com/all/kdrive/app/drive/123/favorites",
"https://ksuite.infomaniak.com/all/kdrive/app/drive/123/files/456",
"https://kdrive.infomaniak.com/app/office/123/456",
"https://kdrive.infomaniak.com/app/drive/123/redirect/456"
])
func parseFilePreviewFail(deeplink: String) async throws {
// WHEN
guard let url = URL(string: deeplink),
FavoritePreviewLink(filePreviewURL: url) != nil else {
// THEN
// success
return
}

Issue.record("This should fail to parse the URL")
}
}
Loading