From 46cbd4f7d1b4ee801ba65acf32d838fa93224fd6 Mon Sep 17 00:00:00 2001 From: David Ullmer Date: Tue, 12 Jul 2022 19:51:47 +0200 Subject: [PATCH] Implement suggestions --- Shared/Coordinators/SettingsCoordinator.swift | 8 +++ Shared/Errors/NetworkError.swift | 2 +- Shared/Generated/Strings.swift | 8 ++- .../QuickConnectSettingsViewModel.swift | 46 ++++++++++++++++++ Shared/ViewModels/SettingsViewModel.swift | 31 ------------ Shared/ViewModels/UserSignInViewModel.swift | 8 +-- Swiftfin.xcodeproj/project.pbxproj | 8 +++ .../QuickConnectSettingsView.swift | 44 +++++++++++++++++ .../Views/SettingsView/SettingsView.swift | 20 +++----- Swiftfin/Views/UserSignInView.swift | 2 +- Translations/en.lproj/Localizable.strings | Bin 13248 -> 13562 bytes 11 files changed, 123 insertions(+), 54 deletions(-) create mode 100644 Shared/ViewModels/QuickConnectSettingsViewModel.swift create mode 100644 Swiftfin/Views/SettingsView/QuickConnectSettingsView.swift diff --git a/Shared/Coordinators/SettingsCoordinator.swift b/Shared/Coordinators/SettingsCoordinator.swift index 5382cbc0..2448c785 100644 --- a/Shared/Coordinators/SettingsCoordinator.swift +++ b/Shared/Coordinators/SettingsCoordinator.swift @@ -28,6 +28,8 @@ final class SettingsCoordinator: NavigationCoordinatable { var missingSettings = makeMissingSettings @Route(.push) var about = makeAbout + @Route(.push) + var quickConnect = makeQuickConnectSettings @ViewBuilder func makeServerDetail() -> some View { @@ -60,6 +62,12 @@ final class SettingsCoordinator: NavigationCoordinatable { AboutView() } + @ViewBuilder + func makeQuickConnectSettings() -> some View { + let viewModel = QuickConnectSettingsViewModel() + QuickConnectSettingsView(viewModel: viewModel) + } + @ViewBuilder func makeStart() -> some View { let viewModel = SettingsViewModel(server: SessionManager.main.currentLogin.server, user: SessionManager.main.currentLogin.user) diff --git a/Shared/Errors/NetworkError.swift b/Shared/Errors/NetworkError.swift index 7778f61a..f2e91809 100644 --- a/Shared/Errors/NetworkError.swift +++ b/Shared/Errors/NetworkError.swift @@ -91,7 +91,7 @@ enum NetworkError: Error { default: errorMessage = ErrorMessage(code: code, title: L10n.error, - message: L10n.unknownError) + message: displayMessage ?? L10n.unknownError) } } diff --git a/Shared/Generated/Strings.swift b/Shared/Generated/Strings.swift index fe7b6245..392492ec 100644 --- a/Shared/Generated/Strings.swift +++ b/Shared/Generated/Strings.swift @@ -34,6 +34,8 @@ internal enum L10n { internal static var audioAndCaptions: String { return L10n.tr("Localizable", "audioAndCaptions") } /// Audio Track internal static var audioTrack: String { return L10n.tr("Localizable", "audioTrack") } + /// Authorize + internal static var authorize: String { return L10n.tr("Localizable", "authorize") } /// Auto Play internal static var autoPlay: String { return L10n.tr("Localizable", "autoPlay") } /// Back @@ -264,10 +266,12 @@ internal enum L10n { internal static var publicUsers: String { return L10n.tr("Localizable", "publicUsers") } /// Quick Connect internal static var quickConnect: String { return L10n.tr("Localizable", "quickConnect") } - /// Authorize - internal static var quickConnectAuthorize: String { return L10n.tr("Localizable", "quickConnectAuthorize") } /// Quick Connect code internal static var quickConnectCode: String { return L10n.tr("Localizable", "quickConnectCode") } + /// Invalid Quick Connect code + internal static var quickConnectInvalidError: String { return L10n.tr("Localizable", "quickConnectInvalidError") } + /// Authorizing Quick Connect successful. Please continue on your other device. + internal static var quickConnectSuccessMessage: String { return L10n.tr("Localizable", "quickConnectSuccessMessage") } /// Rated internal static var rated: String { return L10n.tr("Localizable", "rated") } /// Recently Added diff --git a/Shared/ViewModels/QuickConnectSettingsViewModel.swift b/Shared/ViewModels/QuickConnectSettingsViewModel.swift new file mode 100644 index 00000000..eea1f7d2 --- /dev/null +++ b/Shared/ViewModels/QuickConnectSettingsViewModel.swift @@ -0,0 +1,46 @@ +// +// 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 (c) 2022 Jellyfin & Jellyfin Contributors +// + +import Foundation +import JellyfinAPI + +final class QuickConnectSettingsViewModel: ViewModel { + + @Published + var quickConnectCode = "" + @Published + var showSuccessMessage = false + + var alertTitle: String { + var message: String = "" + if errorMessage?.code != ErrorMessage.noShowErrorCode { + message.append(contentsOf: "\(errorMessage?.code ?? ErrorMessage.noShowErrorCode)\n") + } + message.append(contentsOf: "\(errorMessage?.title ?? L10n.unknownError)") + return message + } + + func sendQuickConnect() { + QuickConnectAPI.authorize(code: self.quickConnectCode) + .trackActivity(loading) + .sink(receiveCompletion: { completion in + self.handleAPIRequestError(displayMessage: L10n.quickConnectInvalidError, completion: completion) + switch completion { + case .failure: + LogManager.log.debug("Invalid Quick Connect code entered") + default: + break + } + }, receiveValue: { _ in + // receiving a successful HTTP response indicates a valid code + LogManager.log.debug("Valid Quick connect code entered") + self.showSuccessMessage = true + }) + .store(in: &cancellables) + } +} diff --git a/Shared/ViewModels/SettingsViewModel.swift b/Shared/ViewModels/SettingsViewModel.swift index 64d7099b..6a9b535d 100644 --- a/Shared/ViewModels/SettingsViewModel.swift +++ b/Shared/ViewModels/SettingsViewModel.swift @@ -14,21 +14,12 @@ import SwiftUI final class SettingsViewModel: ViewModel { - @RouterObject - var router: SettingsCoordinator.Router? - var bitrates: [Bitrates] = [] var langs: [TrackLanguage] = [] let server: SwiftfinStore.State.Server let user: SwiftfinStore.State.User - @Published - var quickConnectCode = "" - - @Published - var validQuickConnect = true - init(server: SwiftfinStore.State.Server, user: SwiftfinStore.State.User) { self.server = server @@ -55,26 +46,4 @@ final class SettingsViewModel: ViewModel { }.sorted(by: { $0.name < $1.name }) self.langs.insert(.auto, at: 0) } - - func sendQuickConnect() { - QuickConnectAPI.authorize(code: self.quickConnectCode) - .sink(receiveCompletion: { completion in - switch completion { - case .failure: - LogManager.log.debug("Invalid Quick Connect code entered") - self.validQuickConnect = false - default: - self.handleAPIRequestError(displayMessage: "Error", completion: completion) - } - }, receiveValue: { value in - if !value { - LogManager.log.debug("Invalid Quick Connect code entered") - self.validQuickConnect = false - } else { - LogManager.log.debug("Valid Quick connect code entered") - self.router?.dismissCoordinator() - } - }) - .store(in: &cancellables) - } } diff --git a/Shared/ViewModels/UserSignInViewModel.swift b/Shared/ViewModels/UserSignInViewModel.swift index 412ff59c..ba96db73 100644 --- a/Shared/ViewModels/UserSignInViewModel.swift +++ b/Shared/ViewModels/UserSignInViewModel.swift @@ -17,9 +17,6 @@ final class UserSignInViewModel: ViewModel { var router: UserSignInCoordinator.Router? let server: SwiftfinStore.State.Server - @Published - var isLoadingUsers = false - @Published var publicUsers: [UserDto] = [] @@ -32,7 +29,7 @@ final class UserSignInViewModel: ViewModel { if errorMessage?.code != ErrorMessage.noShowErrorCode { message.append(contentsOf: "\(errorMessage?.code ?? ErrorMessage.noShowErrorCode)\n") } - message.append(contentsOf: "\(errorMessage?.title ?? "Unkown Error")") + message.append(contentsOf: "\(errorMessage?.title ?? L10n.unknownError)") return message } @@ -57,14 +54,13 @@ final class UserSignInViewModel: ViewModel { } func loadUsers() { - self.isLoadingUsers = true JellyfinAPIAPI.basePath = server.currentURI UserAPI.getPublicUsers() + .trackActivity(loading) .sink(receiveCompletion: { completion in self.handleAPIRequestError(displayMessage: L10n.unableToConnectServer, completion: completion) }, receiveValue: { response in self.publicUsers = response - self.isLoadingUsers = false }) .store(in: &cancellables) } diff --git a/Swiftfin.xcodeproj/project.pbxproj b/Swiftfin.xcodeproj/project.pbxproj index 1162e948..e52d0c28 100644 --- a/Swiftfin.xcodeproj/project.pbxproj +++ b/Swiftfin.xcodeproj/project.pbxproj @@ -248,6 +248,8 @@ 62EC353426766B03000E9F2D /* DeviceRotationViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EC353326766B03000E9F2D /* DeviceRotationViewModifier.swift */; }; 62ECA01826FA685A00E8EBB7 /* DeepLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62ECA01726FA685A00E8EBB7 /* DeepLink.swift */; }; 631759CF2879DB6A00A621AD /* PublicUserSignInCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 631759CE2879DB6A00A621AD /* PublicUserSignInCellView.swift */; }; + 6334175B287DDFB9000603CE /* QuickConnectSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6334175A287DDFB9000603CE /* QuickConnectSettingsView.swift */; }; + 6334175D287DE0D0000603CE /* QuickConnectSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6334175C287DE0D0000603CE /* QuickConnectSettingsViewModel.swift */; }; 637FCAF4287B5B2600C0A353 /* UDPBroadcast.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 637FCAF3287B5B2600C0A353 /* UDPBroadcast.xcframework */; }; 637FCAF5287B5B2600C0A353 /* UDPBroadcast.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 637FCAF3287B5B2600C0A353 /* UDPBroadcast.xcframework */; }; AE8C3159265D6F90008AA076 /* bitrates.json in Resources */ = {isa = PBXBuildFile; fileRef = AE8C3158265D6F90008AA076 /* bitrates.json */; }; @@ -744,6 +746,8 @@ 62EC353326766B03000E9F2D /* DeviceRotationViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceRotationViewModifier.swift; sourceTree = ""; }; 62ECA01726FA685A00E8EBB7 /* DeepLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLink.swift; sourceTree = ""; }; 631759CE2879DB6A00A621AD /* PublicUserSignInCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublicUserSignInCellView.swift; sourceTree = ""; }; + 6334175A287DDFB9000603CE /* QuickConnectSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickConnectSettingsView.swift; sourceTree = ""; }; + 6334175C287DE0D0000603CE /* QuickConnectSettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickConnectSettingsViewModel.swift; sourceTree = ""; }; 637FCAF3287B5B2600C0A353 /* UDPBroadcast.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = UDPBroadcast.xcframework; path = Carthage/Build/UDPBroadcast.xcframework; sourceTree = ""; }; AE8C3158265D6F90008AA076 /* bitrates.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = bitrates.json; sourceTree = ""; }; C400DB6927FE894F007B65FE /* LiveTVChannelsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveTVChannelsView.swift; sourceTree = ""; }; @@ -1058,6 +1062,7 @@ 09389CC626819B4500AE350E /* VideoPlayerModel.swift */, E126F73F278A655300A522BF /* VideoPlayerViewModel */, 625CB57B2678CE1000530A6E /* ViewModel.swift */, + 6334175C287DE0D0000603CE /* QuickConnectSettingsViewModel.swift */, ); path = ViewModels; sourceTree = ""; @@ -1882,6 +1887,7 @@ E176DE6F278E369F001EFD8D /* MissingItemsSettingsView.swift */, E1E5D5472783CCF900692DFE /* OverlaySettingsView.swift */, 539B2DA4263BA5B8007FF1A4 /* SettingsView.swift */, + 6334175A287DDFB9000603CE /* QuickConnectSettingsView.swift */, ); path = SettingsView; sourceTree = ""; @@ -2474,12 +2480,14 @@ E1C812CE277AE43100918266 /* VideoPlayerViewModel.swift in Sources */, E10D87DA2784E4F100BD264C /* ItemViewDetailsView.swift in Sources */, E1C812C3277A8E5D00918266 /* VLCPlayerOverlayView.swift in Sources */, + 6334175B287DDFB9000603CE /* QuickConnectSettingsView.swift in Sources */, E1002B642793CEE800E47059 /* ChapterInfoExtensions.swift in Sources */, E188460026DECB9E00B0C5B7 /* ItemLandscapeTopBarView.swift in Sources */, 6267B3D626710B8900A7371D /* CollectionExtensions.swift in Sources */, E13DD3F5271793BB009D4DAF /* UserSignInView.swift in Sources */, 5D1603FC278A3D5800D22B99 /* SubtitleSize.swift in Sources */, E1F0204E26CCCA74001C1C3B /* VideoPlayerJumpLength.swift in Sources */, + 6334175D287DE0D0000603CE /* QuickConnectSettingsViewModel.swift in Sources */, 53649AB1269CFB1900A2D8B7 /* LogManager.swift in Sources */, E13DD3E127176BD3009D4DAF /* ServerListViewModel.swift in Sources */, 62E632E9267D3FF50063E547 /* SeasonItemViewModel.swift in Sources */, diff --git a/Swiftfin/Views/SettingsView/QuickConnectSettingsView.swift b/Swiftfin/Views/SettingsView/QuickConnectSettingsView.swift new file mode 100644 index 00000000..a6e47215 --- /dev/null +++ b/Swiftfin/Views/SettingsView/QuickConnectSettingsView.swift @@ -0,0 +1,44 @@ +// +// 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 (c) 2022 Jellyfin & Jellyfin Contributors +// + +import Foundation +import SwiftUI + +struct QuickConnectSettingsView: View { + + @ObservedObject + var viewModel: QuickConnectSettingsViewModel + + var body: some View { + Form { + Section(header: L10n.quickConnect.text) { + TextField(L10n.quickConnectCode, text: $viewModel.quickConnectCode) + .keyboardType(.numberPad) + .disabled(viewModel.isLoading) + + Button { + viewModel.sendQuickConnect() + } label: { + L10n.authorize.text + .font(.callout) + .disabled((viewModel.quickConnectCode.count != 6) || viewModel.isLoading) + } + } + .alert(isPresented: $viewModel.showSuccessMessage) { + Alert(title: L10n.quickConnect.text, + message: L10n.quickConnectSuccessMessage.text, + dismissButton: .default(L10n.ok.text)) + } + } + .alert(item: $viewModel.errorMessage) { _ in + Alert(title: Text(viewModel.alertTitle), + message: Text(viewModel.errorMessage?.message ?? L10n.unknownError), + dismissButton: .cancel()) + } + } +} diff --git a/Swiftfin/Views/SettingsView/SettingsView.swift b/Swiftfin/Views/SettingsView/SettingsView.swift index 14f034a1..9d03aeba 100644 --- a/Swiftfin/Views/SettingsView/SettingsView.swift +++ b/Swiftfin/Views/SettingsView/SettingsView.swift @@ -81,22 +81,16 @@ struct SettingsView: View { L10n.switchUser.text .font(.callout) } - } - - Section(header: L10n.quickConnect.text) { - TextField(L10n.quickConnectCode, text: $viewModel.quickConnectCode) - .keyboardType(.numberPad) - .onChange(of: viewModel.quickConnectCode, perform: { _ in - viewModel.validQuickConnect = true - }) - .foregroundColor(viewModel.validQuickConnect ? .none : Color.red) Button { - viewModel.sendQuickConnect() + settingsRouter.route(to: \.quickConnect) } label: { - L10n.quickConnectAuthorize.text - .font(.callout) - .disabled(viewModel.quickConnectCode.isEmpty) + HStack { + L10n.quickConnect.text + .foregroundColor(.primary) + Spacer() + Image(systemName: "chevron.right") + } } } diff --git a/Swiftfin/Views/UserSignInView.swift b/Swiftfin/Views/UserSignInView.swift index 6c7dc759..9f86944a 100644 --- a/Swiftfin/Views/UserSignInView.swift +++ b/Swiftfin/Views/UserSignInView.swift @@ -71,7 +71,7 @@ struct UserSignInView: View { } label: { Image(systemName: "arrow.clockwise.circle.fill") } - .disabled(viewModel.isLoadingUsers || viewModel.isLoading) + .disabled(viewModel.isLoading) } } .headerProminence(.increased) diff --git a/Translations/en.lproj/Localizable.strings b/Translations/en.lproj/Localizable.strings index d9ce8d0ca6f51bb815c1ef4a2c0214670d7931d0..39a9f2cb085d02e95c8cf674ef8b1fdf8765493e 100644 GIT binary patch delta 226 zcmX?*{ws4siSgt`szQ_J87ob`VI*bf$&klT#*oO6!;s04!r;nK1cdoOti+(eU<<@b z45%s;CjT~)o}6T?B^k_6%8(3%sSL$H=nJJ28PX@uHkM`21e%-909Ku)0F*~on+D|N zFz5kI4*=4sK--Ifqyo^ae4wrppiA