Support DeepLink jellyfin://Users/{UserID}/Items/{ItemID}
This commit is contained in:
parent
b07e345efd
commit
b92d66e26e
|
@ -249,6 +249,7 @@
|
|||
62EC353126766848000E9F2D /* ServerEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EC352B26766675000E9F2D /* ServerEnvironment.swift */; };
|
||||
62EC353226766849000E9F2D /* SessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EC352E267666A5000E9F2D /* SessionManager.swift */; };
|
||||
62EC353426766B03000E9F2D /* DeviceRotationViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EC353326766B03000E9F2D /* DeviceRotationViewModifier.swift */; };
|
||||
62ECA01826FA685A00E8EBB7 /* DeepLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62ECA01726FA685A00E8EBB7 /* DeepLink.swift */; };
|
||||
AE8C3159265D6F90008AA076 /* bitrates.json in Resources */ = {isa = PBXBuildFile; fileRef = AE8C3158265D6F90008AA076 /* bitrates.json */; };
|
||||
E100720726BDABC100CE3E31 /* MediaPlayButtonRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E100720626BDABC100CE3E31 /* MediaPlayButtonRowView.swift */; };
|
||||
E131691726C583BC0074BFEE /* LogConstructor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E131691626C583BC0074BFEE /* LogConstructor.swift */; };
|
||||
|
@ -483,6 +484,7 @@
|
|||
62EC352B26766675000E9F2D /* ServerEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerEnvironment.swift; sourceTree = "<group>"; };
|
||||
62EC352E267666A5000E9F2D /* SessionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionManager.swift; sourceTree = "<group>"; };
|
||||
62EC353326766B03000E9F2D /* DeviceRotationViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceRotationViewModifier.swift; sourceTree = "<group>"; };
|
||||
62ECA01726FA685A00E8EBB7 /* DeepLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLink.swift; sourceTree = "<group>"; };
|
||||
AE8C3158265D6F90008AA076 /* bitrates.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = bitrates.json; sourceTree = "<group>"; };
|
||||
BEEC50E7EFD4848C0E320941 /* Pods-JellyfinPlayer iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JellyfinPlayer iOS.release.xcconfig"; path = "Target Support Files/Pods-JellyfinPlayer iOS/Pods-JellyfinPlayer iOS.release.xcconfig"; sourceTree = "<group>"; };
|
||||
D79953919FED0C4DF72BA578 /* Pods-JellyfinPlayer tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JellyfinPlayer tvOS.release.xcconfig"; path = "Target Support Files/Pods-JellyfinPlayer tvOS/Pods-JellyfinPlayer tvOS.release.xcconfig"; sourceTree = "<group>"; };
|
||||
|
@ -771,6 +773,7 @@
|
|||
children = (
|
||||
62C29E9D26D0FE5900C1D2E7 /* Coordinators */,
|
||||
53F866422687A45400DCD1D7 /* Components */,
|
||||
62ECA01926FA6D6900E8EBB7 /* Singleton */,
|
||||
53AD124C2670278D0094A276 /* JellyfinPlayer.entitlements */,
|
||||
5377CBF8263B596B003A4E83 /* Assets.xcassets */,
|
||||
5338F74D263B61370014BF09 /* ConnectToServerView.swift */,
|
||||
|
@ -796,6 +799,7 @@
|
|||
53F8377C265FF67C00F456B3 /* VideoPlayerSettingsView.swift */,
|
||||
625CB5672678B6FB00530A6E /* SplashView.swift */,
|
||||
625CB56E2678C23300530A6E /* HomeView.swift */,
|
||||
62ECA01726FA685A00E8EBB7 /* DeepLink.swift */,
|
||||
);
|
||||
path = JellyfinPlayer;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1019,6 +1023,13 @@
|
|||
53649AB0269CFB1900A2D8B7 /* LogManager.swift */,
|
||||
62EC352B26766675000E9F2D /* ServerEnvironment.swift */,
|
||||
62EC352E267666A5000E9F2D /* SessionManager.swift */,
|
||||
);
|
||||
path = Singleton;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
62ECA01926FA6D6900E8EBB7 /* Singleton */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6220D0CB26D640C400B8E046 /* AppURLHandler.swift */,
|
||||
);
|
||||
path = Singleton;
|
||||
|
@ -1587,6 +1598,7 @@
|
|||
E173DA5226D04AAF00CC4EB7 /* ColorExtension.swift in Sources */,
|
||||
53892770263C25230035E14B /* NextUpView.swift in Sources */,
|
||||
625CB5682678B6FB00530A6E /* SplashView.swift in Sources */,
|
||||
62ECA01826FA685A00E8EBB7 /* DeepLink.swift in Sources */,
|
||||
535BAEA5264A151C005FA86D /* VideoPlayer.swift in Sources */,
|
||||
62E632E6267D3F5B0063E547 /* EpisodeItemViewModel.swift in Sources */,
|
||||
E131691726C583BC0074BFEE /* LogConstructor.swift in Sources */,
|
||||
|
|
|
@ -39,6 +39,7 @@ import SwiftUI
|
|||
let nc = NotificationCenter.default
|
||||
nc.addObserver(self, selector: #selector(didLogIn), name: Notification.Name("didSignIn"), object: nil)
|
||||
nc.addObserver(self, selector: #selector(didLogOut), name: Notification.Name("didSignOut"), object: nil)
|
||||
nc.addObserver(self, selector: #selector(processDeepLink), name: Notification.Name("processDeepLink"), object: nil)
|
||||
}
|
||||
|
||||
@objc func didLogIn() {
|
||||
|
@ -51,6 +52,19 @@ import SwiftUI
|
|||
root(\.connectToServer)
|
||||
}
|
||||
|
||||
@objc func processDeepLink(_ notification: Notification) {
|
||||
guard let deepLink = notification.object as? DeepLink else { return }
|
||||
if let coordinator = hasRoot(\.mainTab) {
|
||||
switch deepLink {
|
||||
case let .item(item):
|
||||
coordinator.focusFirst(\.home)
|
||||
.child
|
||||
.popToRoot()
|
||||
.route(to: \.item, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func makeMainTab() -> MainTabCoordinator {
|
||||
MainTabCoordinator()
|
||||
}
|
||||
|
|
|
@ -38,4 +38,13 @@ final class MainTabCoordinator: TabCoordinatable {
|
|||
Image(systemName: "folder")
|
||||
Text("All Media")
|
||||
}
|
||||
|
||||
@ViewBuilder func customize(_ view: AnyView) -> some View {
|
||||
view.onAppear {
|
||||
AppURLHandler.shared.appURLState = .allowed
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
|
||||
AppURLHandler.shared.processLaunchedURLIfNeeded()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
//
|
||||
/*
|
||||
* SwiftFin is subject to the terms of the Mozilla Public
|
||||
* License, v2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import JellyfinAPI
|
||||
|
||||
enum DeepLinkError: LocalizedError {
|
||||
case general
|
||||
|
||||
var errorDescription: String? {
|
||||
switch self {
|
||||
case .general:
|
||||
return "Couldn't create deep link"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum DeepLink {
|
||||
case item(BaseItemDto)
|
||||
}
|
|
@ -7,15 +7,14 @@
|
|||
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
||||
*/
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
import JellyfinAPI
|
||||
import Stinsen
|
||||
|
||||
final class AppURLHandler {
|
||||
static let deepLinkScheme = "jellyfin"
|
||||
|
||||
@RouterObject
|
||||
var router: HomeCoordinator.Router?
|
||||
|
||||
enum AppURLState {
|
||||
case launched
|
||||
case allowedInLogin
|
||||
|
@ -35,6 +34,8 @@ final class AppURLHandler {
|
|||
|
||||
static let shared = AppURLHandler()
|
||||
|
||||
var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var appURLState: AppURLState = .launched
|
||||
var launchURL: URL?
|
||||
}
|
||||
|
@ -46,9 +47,7 @@ extension AppURLHandler {
|
|||
return false
|
||||
}
|
||||
if AppURLHandler.shared.appURLState.allowedScheme(with: url) {
|
||||
if launchURL == nil {
|
||||
return processURL(url)
|
||||
}
|
||||
return processURL(url)
|
||||
} else {
|
||||
launchURL = url
|
||||
}
|
||||
|
@ -56,7 +55,8 @@ extension AppURLHandler {
|
|||
}
|
||||
|
||||
func processLaunchedURLIfNeeded() {
|
||||
guard let launchURL = launchURL else { return }
|
||||
guard let launchURL = launchURL,
|
||||
!launchURL.absoluteString.isEmpty else { return }
|
||||
if processDeepLink(url: launchURL) {
|
||||
self.launchURL = nil
|
||||
}
|
||||
|
@ -76,12 +76,35 @@ extension AppURLHandler {
|
|||
|
||||
// /Users/{UserID}/Items/{ItemID}
|
||||
if url.pathComponents[safe: 2]?.lowercased() == "items",
|
||||
let userID = url.pathComponents[safe: 1],
|
||||
let itemID = url.pathComponents[safe: 3]
|
||||
{
|
||||
// router?.route(to: \.item(item: item))
|
||||
// It would be nice if the ItemViewModel could be initialized to id later.
|
||||
getItem(userID: userID, itemID: itemID) { item in
|
||||
guard let item = item else { return }
|
||||
NotificationCenter.default.post(name: Notification.Name("processDeepLink"), object: DeepLink.item(item))
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
extension AppURLHandler {
|
||||
func getItem(userID: String, itemID: String, completion: @escaping (BaseItemDto?) -> Void) {
|
||||
UserLibraryAPI.getItem(userId: userID, itemId: itemID)
|
||||
.sink(receiveCompletion: { innerCompletion in
|
||||
switch innerCompletion {
|
||||
case .failure:
|
||||
completion(nil)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}, receiveValue: { item in
|
||||
completion(item)
|
||||
})
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue