Temporarily process deep link from HomeView

This commit is contained in:
PangMo5 2021-08-25 20:15:57 +09:00
parent b544bd66cc
commit 5d96de329f
9 changed files with 222 additions and 81 deletions

View File

@ -189,6 +189,7 @@
6220D0C726D62D8700B8E046 /* VideoPlayerCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6220D0C526D62D8700B8E046 /* VideoPlayerCoordinator.swift */; };
6220D0C926D63F3700B8E046 /* Stinsen in Frameworks */ = {isa = PBXBuildFile; productRef = 6220D0C826D63F3700B8E046 /* Stinsen */; };
6220D0CA26D63F4D00B8E046 /* MainCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C29E9E26D1016600C1D2E7 /* MainCoordinator.swift */; };
6220D0CC26D640C400B8E046 /* AppURLHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6220D0CB26D640C400B8E046 /* AppURLHandler.swift */; };
6225FCCB2663841E00E067F6 /* ParallaxHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6225FCCA2663841E00E067F6 /* ParallaxHeader.swift */; };
6228B1C22670EB010067FD35 /* PersistenceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5377CBFD263B596B003A4E83 /* PersistenceController.swift */; };
624C21752685CF60007F1390 /* SearchablePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 624C21742685CF60007F1390 /* SearchablePickerView.swift */; };
@ -430,6 +431,7 @@
6220D0BC26D60D6600B8E046 /* ItemViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemViewModel.swift; sourceTree = "<group>"; };
6220D0BF26D61C5000B8E046 /* ItemCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemCoordinator.swift; sourceTree = "<group>"; };
6220D0C526D62D8700B8E046 /* VideoPlayerCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayerCoordinator.swift; sourceTree = "<group>"; };
6220D0CB26D640C400B8E046 /* AppURLHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppURLHandler.swift; sourceTree = "<group>"; };
6225FCCA2663841E00E067F6 /* ParallaxHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParallaxHeader.swift; sourceTree = "<group>"; };
624C21742685CF60007F1390 /* SearchablePickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchablePickerView.swift; sourceTree = "<group>"; };
625CB5672678B6FB00530A6E /* SplashView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashView.swift; sourceTree = "<group>"; };
@ -997,6 +999,7 @@
62EC352E267666A5000E9F2D /* SessionManager.swift */,
536D3D73267BA8170004248C /* BackgroundManager.swift */,
53649AB0269CFB1900A2D8B7 /* LogManager.swift */,
6220D0CB26D640C400B8E046 /* AppURLHandler.swift */,
);
path = Singleton;
sourceTree = "<group>";
@ -1513,6 +1516,7 @@
53649AB1269CFB1900A2D8B7 /* LogManager.swift in Sources */,
62E632E9267D3FF50063E547 /* SeasonItemViewModel.swift in Sources */,
625CB56A2678B71200530A6E /* SplashViewModel.swift in Sources */,
6220D0CC26D640C400B8E046 /* AppURLHandler.swift in Sources */,
62E632F3267D54030063E547 /* DetailItemViewModel.swift in Sources */,
53DE4BD2267098F300739748 /* SearchBarView.swift in Sources */,
53E4E649263F725B00F67C6B /* MultiSelectorView.swift in Sources */,

View File

@ -177,5 +177,8 @@ struct ConnectToServerView: View {
dismissButton: .cancel())
}
.navigationTitle(NSLocalizedString("Connect to Server", comment: ""))
.onAppear {
AppURLHandler.shared.appURLState = .allowedInLogin
}
}
}

View File

@ -8,8 +8,8 @@
*/
import Foundation
import SwiftUI
import Stinsen
import SwiftUI
struct HomeView: View {
@EnvironmentObject var homeRouter: NavigationRouter<HomeCoordinator.Route>
@ -37,7 +37,9 @@ struct HomeView: View {
.fontWeight(.bold)
Spacer()
Button {
homeRouter.route(to: .library(viewModel: .init(parentID: libraryID, filters: viewModel.recentFilterSet), title: library?.name ?? ""))
homeRouter
.route(to: .library(viewModel: .init(parentID: libraryID, filters: viewModel.recentFilterSet),
title: library?.name ?? ""))
} label: {
HStack {
Text("See All").font(.subheadline).fontWeight(.bold)
@ -45,7 +47,7 @@ struct HomeView: View {
}
}
}.padding(.leading, 16)
.padding(.trailing, 16)
.padding(.trailing, 16)
LatestMediaView(viewModel: .init(libraryID: libraryID))
}
}
@ -67,5 +69,11 @@ struct HomeView: View {
}
}
}
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
AppURLHandler.shared.appURLState = .allowed
AppURLHandler.shared.processLaunchedURLIfNeeded()
}
}
}
}

View File

@ -18,6 +18,17 @@
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>jellyfin</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>ITSAppUsesNonExemptEncryption</key>

View File

@ -234,6 +234,9 @@ struct JellyfinPlayerApp: App {
.onShake {
EmailHelper.shared.sendLogs(logURL: LogManager.shared.logFileURL())
}
.onOpenURL { url in
AppURLHandler.shared.processDeepLink(url: url)
}
}
}

View File

@ -0,0 +1,102 @@
//
/*
* 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 Stinsen
import URLNavigator
final class AppURLHandler {
static let deepLinkScheme = "jellyfin"
@RouterObject
var router: NavigationRouter<HomeCoordinator.Route>?
enum AppURLState {
case launched
case allowedInLogin
case allowed
func allowedScheme(with url: URL) -> Bool {
switch self {
case .launched:
return false
case .allowed:
return true
case .allowedInLogin:
return false
}
}
}
static let shared = AppURLHandler()
var appURLState: AppURLState = .launched
var launchURL: URL?
}
extension AppURLHandler {
@discardableResult
func processDeepLink(url: URL) -> Bool {
guard url.scheme == Self.deepLinkScheme || url.scheme == "widget-extension" else {
return false
}
print(AppURLHandler.shared.appURLState.allowedScheme(with: url))
if AppURLHandler.shared.appURLState.allowedScheme(with: url) {
if launchURL == nil {
return processURL(url)
}
} else {
launchURL = url
}
return true
}
func processLaunchedURLIfNeeded() {
print("!@#!@#!@#!@#!@#!@")
print(launchURL)
guard let launchURL = launchURL else { return }
if processDeepLink(url: launchURL) {
self.launchURL = nil
}
}
private func processURL(_ url: URL) -> Bool {
print("processURL(_ url: URL) -> Bool")
if processURLForUser(url: url) {
return true
}
return false
}
private func processURLForUser(url: URL) -> Bool {
print("processURLForUser(_ url: URL) -> Bool")
print(url)
print(url.host)
print(url.path)
print(url.pathComponents)
print(url.pathComponents[safe: 0])
print(url.pathComponents[safe: 1])
print(url.pathComponents[safe: 2])
print(url.pathComponents[safe: 3])
guard url.host?.lowercased() == "users",
url.pathComponents[safe: 1]?.isEmpty == false else { return false }
// /Users/{UserID}/Items/{ItemID}
if url.pathComponents[safe: 2]?.lowercased() == "items",
let itemID = url.pathComponents[safe: 3]
{
print("Passed!@#")
router?.route(to: .item(viewModel: .init(id: itemID)))
return true
}
return false
}
}

View File

@ -29,6 +29,10 @@ final class ConnectToServerViewModel: ViewModel {
private let discovery = ServerDiscovery()
@Published var servers: [ServerDiscovery.ServerLookupResponse] = []
@Published var searching = false
override init() {
super.init()
}
func getPublicUsers() {
if ServerEnvironment.current.server != nil {

View File

@ -24,7 +24,6 @@ final class HomeViewModel: ViewModel {
override init() {
super.init()
refresh()
}

View File

@ -142,14 +142,14 @@ struct NextUpEntryView: View {
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
} else {
switch family {
case .systemSmall:
small(item: entry.items.first)
case .systemMedium:
medium(items: entry.items)
case .systemLarge:
large(items: entry.items)
default:
EmptyView()
case .systemSmall:
small(item: entry.items.first)
case .systemMedium:
medium(items: entry.items)
case .systemLarge:
large(items: entry.items)
default:
EmptyView()
}
}
}
@ -198,51 +198,55 @@ extension NextUpEntryView {
}
func smallVideoView(item: (BaseItemDto, UIImage?)) -> some View {
VStack(alignment: .leading) {
if let image = item.1 {
Image(uiImage: image)
.resizable()
.aspectRatio(.init(width: 1, height: 0.5625), contentMode: .fill)
.clipped()
.cornerRadius(8)
.shadow(radius: 8)
}
Text(item.0.seriesName ?? "")
.font(.caption)
.fontWeight(.semibold)
.foregroundColor(.primary)
.lineLimit(1)
Text("\(item.0.name ?? "") · S\(item.0.parentIndexNumber ?? 0):E\(item.0.indexNumber ?? 0)")
.font(.caption)
.fontWeight(.semibold)
.foregroundColor(.secondary)
.lineLimit(1)
}
}
func largeVideoView(item: (BaseItemDto, UIImage?)) -> some View {
HStack(spacing: 20) {
if let image = item.1 {
Image(uiImage: image)
.resizable()
.aspectRatio(.init(width: 1, height: 0.5625), contentMode: .fill)
.clipped()
.cornerRadius(8)
.shadow(radius: 8)
}
VStack(alignment: .leading, spacing: 8) {
Link(destination: URL(string: "widget-extension://Users/\(SessionManager.current.user.user_id!)/Items/\(item.0.id!)")!, label: {
VStack(alignment: .leading) {
if let image = item.1 {
Image(uiImage: image)
.resizable()
.aspectRatio(.init(width: 1, height: 0.5625), contentMode: .fill)
.clipped()
.cornerRadius(8)
.shadow(radius: 8)
}
Text(item.0.seriesName ?? "")
.font(.caption)
.fontWeight(.semibold)
.foregroundColor(.primary)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
.lineLimit(1)
Text("\(item.0.name ?? "") · S\(item.0.parentIndexNumber ?? 0):E\(item.0.indexNumber ?? 0)")
.font(.caption)
.fontWeight(.semibold)
.foregroundColor(.secondary)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
.lineLimit(1)
}
}
})
}
func largeVideoView(item: (BaseItemDto, UIImage?)) -> some View {
Link(destination: URL(string: "widget-extension://Users/\(SessionManager.current.user.user_id!)/Items/\(item.0.id!)")!, label: {
HStack(spacing: 20) {
if let image = item.1 {
Image(uiImage: image)
.resizable()
.aspectRatio(.init(width: 1, height: 0.5625), contentMode: .fill)
.clipped()
.cornerRadius(8)
.shadow(radius: 8)
}
VStack(alignment: .leading, spacing: 8) {
Text(item.0.seriesName ?? "")
.font(.caption)
.fontWeight(.semibold)
.foregroundColor(.primary)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
Text("\(item.0.name ?? "") · S\(item.0.parentIndexNumber ?? 0):E\(item.0.indexNumber ?? 0)")
.font(.caption)
.fontWeight(.semibold)
.foregroundColor(.secondary)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
}
}
})
}
}
@ -281,33 +285,36 @@ extension NextUpEntryView {
func large(items: [(BaseItemDto, UIImage?)]) -> some View {
VStack(spacing: 0) {
if let firstItem = items[safe: 0] {
ZStack(alignment: .topTrailing) {
ZStack(alignment: .bottomLeading) {
if let image = firstItem.1 {
Image(uiImage: image)
.centerCropped()
.innerShadow(color: Color.black.opacity(0.5), radius: 0.5)
}
VStack(alignment: .leading, spacing: 8) {
Text(firstItem.0.seriesName ?? "")
.font(.caption)
.fontWeight(.semibold)
.foregroundColor(.white)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
Text("\(firstItem.0.name ?? "") · S\(firstItem.0.parentIndexNumber ?? 0):E\(firstItem.0.indexNumber ?? 0)")
.font(.caption)
.fontWeight(.semibold)
.foregroundColor(.gray)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
}
.shadow(radius: 8)
.padding(12)
}
headerSymbol
.padding(12)
}
.clipped()
.shadow(radius: 8)
Link(destination: URL(string: "widget-extension://Users/\(SessionManager.current.user.user_id!)/Items/\(firstItem.0.id!)")!,
label: {
ZStack(alignment: .topTrailing) {
ZStack(alignment: .bottomLeading) {
if let image = firstItem.1 {
Image(uiImage: image)
.centerCropped()
.innerShadow(color: Color.black.opacity(0.5), radius: 0.5)
}
VStack(alignment: .leading, spacing: 8) {
Text(firstItem.0.seriesName ?? "")
.font(.caption)
.fontWeight(.semibold)
.foregroundColor(.white)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
Text("\(firstItem.0.name ?? "") · S\(firstItem.0.parentIndexNumber ?? 0):E\(firstItem.0.indexNumber ?? 0)")
.font(.caption)
.fontWeight(.semibold)
.foregroundColor(.gray)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
}
.shadow(radius: 8)
.padding(12)
}
headerSymbol
.padding(12)
}
.clipped()
.shadow(radius: 8)
})
}
VStack(spacing: 8) {
if let secondItem = items[safe: 1] {
@ -354,7 +361,7 @@ struct NextUpWidget_Previews: PreviewProvider {
(.init(name: "Name0", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series0"),
UIImage(named: "WidgetHeaderSymbol")),
(.init(name: "Name1", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series1"),
UIImage(named: "WidgetHeaderSymbol"))
UIImage(named: "WidgetHeaderSymbol")),
],
error: nil))
.previewContext(WidgetPreviewContext(family: .systemMedium))
@ -365,7 +372,7 @@ struct NextUpWidget_Previews: PreviewProvider {
(.init(name: "Name1", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series1"),
UIImage(named: "WidgetHeaderSymbol")),
(.init(name: "Name2", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series2"),
UIImage(named: "WidgetHeaderSymbol"))
UIImage(named: "WidgetHeaderSymbol")),
],
error: nil))
.previewContext(WidgetPreviewContext(family: .systemLarge))
@ -380,7 +387,7 @@ struct NextUpWidget_Previews: PreviewProvider {
(.init(name: "Name0", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series0"),
UIImage(named: "WidgetHeaderSymbol")),
(.init(name: "Name1", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series1"),
UIImage(named: "WidgetHeaderSymbol"))
UIImage(named: "WidgetHeaderSymbol")),
],
error: nil))
.previewContext(WidgetPreviewContext(family: .systemMedium))
@ -392,7 +399,7 @@ struct NextUpWidget_Previews: PreviewProvider {
(.init(name: "Name1", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series1"),
UIImage(named: "WidgetHeaderSymbol")),
(.init(name: "Name2", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series2"),
UIImage(named: "WidgetHeaderSymbol"))
UIImage(named: "WidgetHeaderSymbol")),
],
error: nil))
.previewContext(WidgetPreviewContext(family: .systemLarge))
@ -405,7 +412,7 @@ struct NextUpWidget_Previews: PreviewProvider {
NextUpEntryView(entry: .init(date: Date(),
items: [
(.init(name: "Name0", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series0"),
UIImage(named: "WidgetHeaderSymbol"))
UIImage(named: "WidgetHeaderSymbol")),
],
error: nil))
.previewContext(WidgetPreviewContext(family: .systemMedium))
@ -415,7 +422,7 @@ struct NextUpWidget_Previews: PreviewProvider {
(.init(name: "Name0", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series0"),
UIImage(named: "WidgetHeaderSymbol")),
(.init(name: "Name1", indexNumber: 10, parentIndexNumber: 0, seriesName: "Series1"),
UIImage(named: "WidgetHeaderSymbol"))
UIImage(named: "WidgetHeaderSymbol")),
],
error: nil))
.previewContext(WidgetPreviewContext(family: .systemLarge))