Support DeepLink jellyfin://Users/{UserID}/Items/{ItemID}

This commit is contained in:
PangMo5 2021-09-22 05:28:14 +09:00
parent b07e345efd
commit b92d66e26e
5 changed files with 92 additions and 8 deletions

View File

@ -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 */,

View File

@ -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()
}

View File

@ -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()
}
}
}
}

View File

@ -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)
}

View File

@ -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)
}
} 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)
}
}