Skip to content

Commit

Permalink
feat: server editing (#42)
Browse files Browse the repository at this point in the history
  • Loading branch information
bouassaba authored Nov 28, 2024
1 parent 9192373 commit 9708421
Show file tree
Hide file tree
Showing 7 changed files with 296 additions and 29 deletions.
21 changes: 21 additions & 0 deletions Sources/Helpers/String+URL.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) 2024 Anass Bouassaba.
//
// Use of this software is governed by the Business Source License
// included in the file LICENSE in the root of this repository.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the GNU Affero General Public License v3.0 only, included in the file
// AGPL-3.0-only in the root of this repository.

import Foundation

extension String {
func isValidURL() -> Bool {
guard !isEmpty else { return false }
if let url = URL(string: self), let scheme = url.scheme, let host = url.host {
return ["http", "https"].contains(scheme) && !host.isEmpty
}
return false
}
}
18 changes: 9 additions & 9 deletions Sources/Screens/Server/ServerCreate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

import SwiftUI

struct ServerCreate: View {
struct ServerCreate: View, FormValidatable {
@Environment(\.modelContext) private var context
@Environment(\.dismiss) var dismiss
@State private var name = ""
Expand All @@ -20,18 +20,16 @@ struct ServerCreate: View {

var body: some View {
Form {
Section(header: VOSectionHeader("Name")) {
Section(header: VOSectionHeader("Details")) {
TextField("Name", text: $name)
.disabled(isProcessing)
}
Section(header: VOSectionHeader("API URL")) {
TextField("API URL", text: $apiURL)
Section(header: VOSectionHeader("URLs")) {
TextField("API", text: $apiURL)
.textInputAutocapitalization(.never)
.autocorrectionDisabled()
.disabled(isProcessing)
}
Section(header: VOSectionHeader("Identity Provider URL")) {
TextField("Identity Provider URL", text: $idpURL)
TextField("Identity Provider", text: $idpURL)
.textInputAutocapitalization(.never)
.autocorrectionDisabled()
.disabled(isProcessing)
Expand Down Expand Up @@ -77,7 +75,9 @@ struct ServerCreate: View {
}
}

private func isValid() -> Bool {
!normalizedName.isEmpty && !apiURL.isEmpty && !idpURL.isEmpty
// MARK: - FormValidatable

func isValid() -> Bool {
!normalizedName.isEmpty && apiURL.isValidURL() && idpURL.isValidURL()
}
}
69 changes: 69 additions & 0 deletions Sources/Screens/Server/ServerEditAPIURL.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright (c) 2024 Anass Bouassaba.
//
// Use of this software is governed by the Business Source License
// included in the file LICENSE in the root of this repository.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the GNU Affero General Public License v3.0 only, included in the file
// AGPL-3.0-only in the root of this repository.

import SwiftUI

struct ServerEditAPIURL: View, FormValidatable {
@EnvironmentObject private var tokenStore: TokenStore
@Environment(\.dismiss) private var dismiss
@Environment(\.modelContext) private var context
@State private var value = ""
@State private var isProcessing = false
private let server: Server

init(_ server: Server) {
self.server = server
}

var body: some View {
Form {
TextField("API URL", text: $value)
}
.navigationBarTitleDisplayMode(.inline)
.navigationTitle("Change API URL")
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
if isProcessing {
ProgressView()
} else {
Button("Save") {
performSave()
}
.disabled(!isValid())
}
}
}
.onAppear {
value = server.apiURL
}
}

private func performSave() {
isProcessing = true
Task {
server.apiURL = value
try? context.save()

UserDefaults.standard.server = server
tokenStore.recreateClient()

DispatchQueue.main.async {
dismiss()
isProcessing = false
}
}
}

// MARK: - FormValidatable

func isValid() -> Bool {
value.isValidURL()
}
}
69 changes: 69 additions & 0 deletions Sources/Screens/Server/ServerEditIdentityProviderURL.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright (c) 2024 Anass Bouassaba.
//
// Use of this software is governed by the Business Source License
// included in the file LICENSE in the root of this repository.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the GNU Affero General Public License v3.0 only, included in the file
// AGPL-3.0-only in the root of this repository.

import SwiftUI

struct ServerEditIdentityProviderURL: View, FormValidatable {
@EnvironmentObject private var tokenStore: TokenStore
@Environment(\.dismiss) private var dismiss
@Environment(\.modelContext) private var context
@State private var value = ""
@State private var isProcessing = false
private let server: Server

init(_ server: Server) {
self.server = server
}

var body: some View {
Form {
TextField("Identity Provider URL", text: $value)
}
.navigationBarTitleDisplayMode(.inline)
.navigationTitle("Change Identity Provider URL")
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
if isProcessing {
ProgressView()
} else {
Button("Save") {
performSave()
}
.disabled(!isValid())
}
}
}
.onAppear {
value = server.idpURL
}
}

private func performSave() {
isProcessing = true
Task {
server.idpURL = value
try? context.save()

UserDefaults.standard.server = server
tokenStore.recreateClient()

DispatchQueue.main.async {
dismiss()
isProcessing = false
}
}
}

// MARK: - FormValidatable

func isValid() -> Bool {
value.isValidURL()
}
}
72 changes: 72 additions & 0 deletions Sources/Screens/Server/ServerEditName.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright (c) 2024 Anass Bouassaba.
//
// Use of this software is governed by the Business Source License
// included in the file LICENSE in the root of this repository.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the GNU Affero General Public License v3.0 only, included in the file
// AGPL-3.0-only in the root of this repository.

import SwiftUI

struct ServerEditName: View, FormValidatable {
@EnvironmentObject private var tokenStore: TokenStore
@Environment(\.dismiss) private var dismiss
@Environment(\.modelContext) private var context
@State private var value = ""
@State private var isProcessing = false
private let server: Server

init(_ server: Server) {
self.server = server
}

var body: some View {
Form {
TextField("Name", text: $value)
}
.navigationBarTitleDisplayMode(.inline)
.navigationTitle("Change Name")
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
if isProcessing {
ProgressView()
} else {
Button("Save") {
performSave()
}
.disabled(!isValid())
}
}
}
.onAppear {
value = server.name
}
}

private var normalizedValue: String {
value.trimmingCharacters(in: .whitespaces)
}

private func performSave() {
isProcessing = true
Task {
server.name = normalizedValue
try? context.save()

UserDefaults.standard.server = server

DispatchQueue.main.async {
dismiss()
isProcessing = false
}
}
}

// MARK: - FormValidatable

func isValid() -> Bool {
!normalizedValue.isEmpty
}
}
73 changes: 53 additions & 20 deletions Sources/Screens/Server/ServerOverview.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,31 +28,64 @@ struct ServerOverview: View {

var body: some View {
Form {
Section(header: VOSectionHeader("API URL")) {
Text(server.apiURL)
}
Section(header: VOSectionHeader("Identity Provider URL")) {
Text(server.idpURL)
Section(header: VOSectionHeader("Name")) {
NavigationLink(destination: ServerEditName(server)) {
HStack {
Text("Name")
Spacer()
Text(server.name)
.lineLimit(1)
.truncationMode(.tail)
.foregroundStyle(.secondary)
}
}
.disabled(server.isCloud)
}
Section {
Button {
activateConfirmationIsPresented = true
} label: {
Section(header: VOSectionHeader("URLs")) {
NavigationLink(destination: ServerEditAPIURL(server)) {
HStack {
Text("Activate Server")
if isActivating {
Spacer()
ProgressView()
}
Text("API")
Spacer()
Text(server.apiURL)
.lineLimit(1)
.truncationMode(.middle)
.foregroundStyle(.secondary)
}
}
.disabled(server.isActive || isProcessing)
.confirmationDialog("Activate Server", isPresented: $activateConfirmationIsPresented) {
Button("Activate") {
performActivate()
.disabled(server.isCloud)
NavigationLink(destination: ServerEditIdentityProviderURL(server)) {
HStack {
Text("Identity Provider")
Spacer()
Text(server.idpURL)
.lineLimit(1)
.truncationMode(.middle)
.foregroundStyle(.secondary)
}
}
.disabled(server.isCloud)
}
Section(header: VOSectionHeader("Advanced")) {
if !server.isActive {
Button {
activateConfirmationIsPresented = true
} label: {
HStack {
Text("Activate Server")
if isActivating {
Spacer()
ProgressView()
}
}
}
.disabled(isProcessing)
.confirmationDialog("Activate Server", isPresented: $activateConfirmationIsPresented) {
Button("Activate") {
performActivate()
}
} message: {
Text("Are you sure you want to activate this server?")
}
} message: {
Text("Are you sure you want to activate this server?")
}
if !server.isCloud {
Button(role: .destructive) {
Expand Down
3 changes: 3 additions & 0 deletions Voltaserve.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@
Organization/OrganizationSettings.swift,
Organization/OrganizationStore.swift,
Server/ServerCreate.swift,
Server/ServerEditAPIURL.swift,
Server/ServerEditIdentityProviderURL.swift,
Server/ServerEditName.swift,
Server/ServerList.swift,
Server/ServerModel.swift,
Server/ServerOverview.swift,
Expand Down

0 comments on commit 9708421

Please sign in to comment.