Allow setting self-host backends from the iOS app

This commit is contained in:
Jackson Harper
2023-01-19 18:26:28 +08:00
parent 1006515eec
commit 2bb5c24b21
4 changed files with 135 additions and 8 deletions

View File

@ -8,7 +8,7 @@ struct DebugMenuView: View {
@EnvironmentObject var dataService: DataService
@Binding var selectedEnvironment: AppEnvironment
let appEnvironments: [AppEnvironment] = [.local, .demo, .dev, .prod]
let appEnvironments: [AppEnvironment] = [.local, .demo, .prod]
var body: some View {
VStack {

View File

@ -0,0 +1,86 @@
import Models
import Services
import SwiftUI
import Utils
import Views
class SelfHostSettingsViewModel: ObservableObject {
@State var showCreateError = false
}
struct SelfHostSettingsView: View {
@State var apiServerAddress = ""
@State var webServerAddress = ""
@State var ttsServerAddress = ""
@State var showConfirmAlert = false
@Environment(\.dismiss) private var dismiss
@EnvironmentObject var dataService: DataService
@StateObject var viewModel = SelfHostSettingsViewModel()
var allFieldsSet: Bool {
apiServerAddress.count > 0 && webServerAddress.count > 0 && ttsServerAddress.count > 0
}
var saveButton: some View {
Button(action: {
showConfirmAlert = true
}, label: {
Text("Save")
})
.disabled(!allFieldsSet)
}
var body: some View {
Form {
Section("API Server Base URL") {
TextField("URL", text: $apiServerAddress, prompt: Text("https://api-prod.omnivore.app"))
.keyboardType(.URL)
}
Section("Web Server URL") {
TextField("URL", text: $webServerAddress, prompt: Text("https://omnivore.app"))
.keyboardType(.URL)
}
Section("Text-to-speech Server URL") {
TextField("URL", text: $ttsServerAddress, prompt: Text("https://tts.omnivore.app"))
.keyboardType(.URL)
}
Section {
Section {
Text("""
Omnivore is a free and open-source project and allows self-hosting.
If you have chosen to deploy your own server instance, fill in the \
above fields to connect to your private self-hosted instance.
[Learn more about self-hosting Omnivore](https://docs.omnivore.app/self-hosting/self-hosting.html)
""")
.accentColor(.blue)
}
}
}
.accentColor(.appGrayText)
.alert(isPresented: $showConfirmAlert) {
Alert(
title: Text("Changing your environment settings will close the app."),
dismissButton: .cancel(Text("Ok")) {
AppEnvironment.setCustom(serverBaseURL: apiServerAddress, webAppBaseURL: webServerAddress, ttsBaseURL: ttsServerAddress)
dataService.switchAppEnvironment(appEnvironment: AppEnvironment.custom)
}
)
}
.navigationViewStyle(.stack)
.navigationTitle("Self-hosting Options")
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(leading:
Button(action: {
dismiss()
}, label: { Text("Cancel") }),
trailing: saveButton)
}
}

View File

@ -18,6 +18,7 @@ struct WelcomeView: View {
@State private var showTermsModal = false
@State private var showPrivacyModal = false
@State private var showEmailLoginModal = false
@State private var showAdvancedLogin = false
@State private var showAboutPage = false
@State private var selectedEnvironment = AppEnvironment.initialAppEnvironment
@State private var containerSize: CGSize = .zero
@ -83,6 +84,8 @@ struct WelcomeView: View {
Button("View Privacy Policy") {
showPrivacyModal = true
}
Spacer()
}
.sheet(isPresented: $showPrivacyModal) {
VStack {
@ -235,6 +238,18 @@ struct WelcomeView: View {
}
footerView
Spacer()
Button(
action: { showAdvancedLogin = true },
label: {
Text("Self-hosting options")
.font(Font.appCaption)
.foregroundColor(.appGrayTextContrast)
.underline()
.frame(maxWidth: .infinity, alignment: .center)
}
)
.padding(.vertical)
}
.padding()
.sheet(isPresented: $showEmailLoginModal) {
@ -243,6 +258,11 @@ struct WelcomeView: View {
.sheet(isPresented: $showDebugModal) {
DebugMenuView(selectedEnvironment: $selectedEnvironment)
}
.sheet(isPresented: $showAdvancedLogin) {
NavigationView {
SelfHostSettingsView()
}
}
.alert(deletedAccountConfirmationMessage, isPresented: $authenticator.showAppleRevokeTokenAlert) {
Button("View Details") {
openURL(URL(string: "https://support.apple.com/en-us/HT210426")!)

View File

@ -3,10 +3,10 @@ import Utils
public enum AppEnvironment: String {
case local
case dev
case prod
case demo
case test
case custom
public static let initialAppEnvironment: AppEnvironment = {
#if DEBUG
@ -32,47 +32,68 @@ private let devWebURL = "https://web-dev.omnivore.app"
private let demoWebURL = "https://demo.omnivore.app"
private let prodWebURL = "https://omnivore.app"
private enum AppEnvironmentUserDefaultKey: String {
case serverBaseURL = "AppEnvironment_serverBaseURL"
case webAppBaseURL = "AppEnvironment_webAppBaseURL"
case ttsBaseURL = "AppEnvironment_ttsBaseURL"
}
public extension AppEnvironment {
static func setCustom(serverBaseURL: String, webAppBaseURL: String, ttsBaseURL: String) {
UserDefaults.standard.set(serverBaseURL, forKey: AppEnvironmentUserDefaultKey.serverBaseURL.rawValue)
UserDefaults.standard.set(webAppBaseURL, forKey: AppEnvironmentUserDefaultKey.webAppBaseURL.rawValue)
UserDefaults.standard.set(ttsBaseURL, forKey: AppEnvironmentUserDefaultKey.ttsBaseURL.rawValue)
}
var graphqlPath: String {
"\(serverBaseURL.absoluteString)/api/graphql"
}
var serverBaseURL: URL {
switch self {
case .dev:
return URL(string: devBaseURL)!
case .demo:
return URL(string: demoBaseURL)!
case .prod:
return URL(string: prodBaseURL)!
case .test, .local:
return URL(string: "http://localhost:4000")!
case .custom:
guard let str = UserDefaults.standard.string(forKey: AppEnvironmentUserDefaultKey.serverBaseURL.rawValue), let url = URL(string: str) else {
fatalError("custom serverBaseURL not set")
}
return url
}
}
var webAppBaseURL: URL {
switch self {
case .dev:
return URL(string: devWebURL)!
case .demo:
return URL(string: demoWebURL)!
case .prod:
return URL(string: prodWebURL)!
case .test, .local:
return URL(string: "http://localhost:3000")!
case .custom:
guard let str = UserDefaults.standard.string(forKey: AppEnvironmentUserDefaultKey.webAppBaseURL.rawValue), let url = URL(string: str) else {
fatalError("custom webAppBaseURL not set")
}
return url
}
}
var ttsBaseURL: URL {
switch self {
case .dev:
return URL(string: "notimplemented")!
case .demo:
return URL(string: demoTtsURL)!
case .prod:
return URL(string: prodTtsURL)!
case .test, .local:
return URL(string: "http://localhost:4000")!
case .custom:
guard let str = UserDefaults.standard.string(forKey: AppEnvironmentUserDefaultKey.ttsBaseURL.rawValue), let url = URL(string: str) else {
fatalError("custom ttsBaseURL not set")
}
return url
}
}
}