Allow setting server url in settings

This commit is contained in:
Christoph Hagen 2024-03-01 17:37:10 +01:00
parent 1d992b0bd2
commit 3dc5674a3a
5 changed files with 82 additions and 28 deletions

View File

@ -5,7 +5,7 @@ struct CapsApp: App {
static let thumbnailImageSize: CGFloat = 60
let database = Database(server: URL(string: "https://christophhagen.de/caps")!)
let database = Database()
var body: some Scene {
WindowGroup {

View File

@ -33,7 +33,16 @@ final class Database: ObservableObject {
private let encoder = JSONEncoder()
private let decoder = JSONDecoder()
let serverUrl: URL
@AppStorage("serverUrl")
var serverPath: String = "" {
didSet {
images.server = serverUrl
}
}
var serverUrl: URL? {
.init(string: serverPath)
}
let folderUrl: URL
@ -159,20 +168,20 @@ final class Database: ObservableObject {
return _classifierClassesCache!
}
init(server: URL, folder: URL = FileManager.default.documentDirectory) {
self.serverUrl = server
init(folder: URL = FileManager.default.documentDirectory) {
self.folderUrl = folder
self.caps = [:]
let imageFolder = folder.appendingPathComponent("images")
self.images = try! ImageCache(
folder: imageFolder,
server: server,
server: nil,
thumbnailSize: CapsApp.thumbnailImageSize)
self.localClassifierVersion = storedLocalClassifierVersion
self.serverClassifierVersion = storedServerClassifierVersion
images.server = serverUrl
ensureFolderExistence(gridStorageFolder)
loadCaps()
updatePendingImageUploadCount(imageUploads: imageUploads)
@ -200,20 +209,20 @@ final class Database: ObservableObject {
folderUrl.appendingPathComponent("uploads")
}
private var serverDbUrl: URL {
serverUrl.appendingPathComponent("caps.json")
private var serverDbUrl: URL? {
serverUrl?.appendingPathComponent("caps.json")
}
private var serverClassifierUrl: URL {
serverUrl.appendingPathComponent("classifier.mlmodel")
private var serverClassifierUrl: URL? {
serverUrl?.appendingPathComponent("classifier.mlmodel")
}
private var serverClassifierClassesUrl: URL {
serverUrl.appendingPathComponent("classifier.classes")
private var serverClassifierClassesUrl: URL? {
serverUrl?.appendingPathComponent("classifier.classes")
}
private var serverClassifierVersionUrl: URL {
serverUrl.appendingPathComponent("version")
private var serverClassifierVersionUrl: URL? {
serverUrl?.appendingPathComponent("version")
}
private var gridStorageFolder: URL {
@ -224,7 +233,7 @@ final class Database: ObservableObject {
guard let path = caps[cap]?.mainImagePath else {
return nil
}
return serverUrl.appendingPathComponent(path)
return serverUrl?.appendingPathComponent(path)
}
// MARK: Disk storage
@ -305,6 +314,10 @@ final class Database: ObservableObject {
@discardableResult
func downloadCaps() async -> Bool {
guard let serverDbUrl else {
log("No server url set to download cap data")
return false
}
log("Downloading cap data from \(serverDbUrl)")
let data: Data
let response: URLResponse
@ -357,6 +370,10 @@ final class Database: ObservableObject {
@discardableResult
func updateServerClassifierVersion() async -> Bool {
guard let serverClassifierVersionUrl else {
log("No server url to download classifier version")
return false
}
let data: Data
let response: URLResponse
do {
@ -385,6 +402,10 @@ final class Database: ObservableObject {
@discardableResult
func downloadClassifier() async -> Bool {
guard let serverClassifierUrl else {
log("No server url to download classifier")
return false
}
log("Downloading classifier")
let progress = ClassifierProgress()
@ -428,6 +449,10 @@ final class Database: ObservableObject {
@discardableResult
func downloadClassifierClasses() async -> Bool {
guard let serverClassifierClassesUrl else {
log("No server url to download classifier classes")
return false
}
log("Downloading classifier classes")
let data: Data
let response: URLResponse
@ -612,6 +637,10 @@ final class Database: ObservableObject {
@discardableResult
private func upload(imageAt url: URL, for cap: Int) async -> Bool {
guard let serverUrl else {
log("No server url to upload image")
return false
}
guard hasServerAuthentication else {
return false
}
@ -619,10 +648,10 @@ final class Database: ObservableObject {
log("No image data found for image \(url.lastPathComponent) (Cap \(cap))")
return false
}
let serverUrl = serverUrl
let serverImageUrl = serverUrl
.appendingPathComponent("image")
.appendingPathComponent("\(cap)")
var request = URLRequest(url: serverUrl)
var request = URLRequest(url: serverImageUrl)
request.addValue(serverAuthenticationKey, forHTTPHeaderField: "key")
request.httpMethod = "POST"
do {
@ -677,6 +706,10 @@ final class Database: ObservableObject {
@discardableResult
private func upload(cap: Cap) async -> Bool {
guard let serverUrl else {
log("No server url to upload cap")
return false
}
guard hasServerAuthentication else {
return false
}
@ -736,6 +769,10 @@ final class Database: ObservableObject {
}
func delete(image: CapImage) async -> Bool {
guard let serverUrl else {
log("No server url to delete image")
return false
}
guard hasServerAuthentication else {
log("No authorization to delete cap image")
return false
@ -784,6 +821,10 @@ final class Database: ObservableObject {
}
func delete(cap: Int) async -> Bool {
guard let serverUrl else {
log("No server url to delete cap")
return false
}
guard hasServerAuthentication else {
log("No authorization to delete cap")
return false
@ -1027,7 +1068,8 @@ extension Database.ClassifierProgress: URLSessionDownloadDelegate {
extension Database {
static var mock: Database {
let db = Database(server: URL(string: "https://christophhagen.de/caps")!)
let db = Database()
db.serverPath = "https://caps.christophhagen.de"
db.caps = [
Cap(id: 123, name: "My new cap"),
Cap(id: 234, name: "My favorite cap"),
@ -1041,7 +1083,8 @@ extension Database {
}
static var largeMock: Database {
let db = Database(server: URL(string: "https://christophhagen.de/caps")!)
let db = Database()
db.serverPath = "https://caps.christophhagen.de"
db.caps = (1..<500)
.map { Cap(id: $0, name: "Cap \($0)") }
.reduce(into: [:]) { $0[$1.id] = $1 }

View File

@ -5,7 +5,7 @@ final class ImageCache: ObservableObject {
let folder: URL
let server: URL
var server: URL?
let thumbnailSize: CGFloat
@ -22,7 +22,7 @@ final class ImageCache: ObservableObject {
@Published
private(set) var imageCount = 0
init(folder: URL, server: URL, thumbnailSize: CGFloat) throws {
init(folder: URL, server: URL?, thumbnailSize: CGFloat) throws {
self.folder = folder
self.server = server
self.thumbnailSize = thumbnailSize * UIScreen.main.scale
@ -37,8 +37,8 @@ final class ImageCache: ObservableObject {
folder.appendingPathComponent(String(format: "%04d-%02d.jpg", image.cap, image.version))
}
private func remoteImageUrl(_ image: CapImage) -> URL {
server.appendingPathComponent(String(format: "images/%04d/%04d-%02d.jpg", image.cap, image.cap, image.version))
private func remoteImageUrl(_ image: CapImage) -> URL? {
server?.appendingPathComponent(String(format: "images/%04d/%04d-%02d.jpg", image.cap, image.cap, image.version))
}
@discardableResult
@ -134,7 +134,9 @@ final class ImageCache: ObservableObject {
}
private func loadRemoteImage(_ image: CapImage) async -> URL? {
let remoteURL = remoteImageUrl(image)
guard let remoteURL = remoteImageUrl(image) else {
return nil
}
return await loadRemoteImage(at: remoteURL)
}

View File

@ -14,10 +14,6 @@ struct CapRowView: View {
@EnvironmentObject
var database: Database
var imageUrl: URL {
database.serverUrl.appendingPathComponent(cap.mainImagePath)
}
var body: some View {
HStack(alignment: .center) {
VStack(alignment: .leading, spacing: 0) {

View File

@ -18,6 +18,18 @@ struct SettingsView: View {
var body: some View {
NavigationView {
VStack(alignment: .leading, spacing: 3) {
Text("Server")
.font(.footnote)
.textCase(.uppercase)
.foregroundColor(.secondary)
.padding(.top)
Group {
FancyTextField(
text: $database.serverPath,
icon: .globe,
placeholder: "Server url")
.keyboardType(.URL)
}.padding(.horizontal)
Text("Authentication")
.font(.footnote)
.textCase(.uppercase)
@ -25,6 +37,7 @@ struct SettingsView: View {
.padding(.top)
Group {
FancyTextField(text: $serverAuthenticationKey, icon: .key, placeholder: "Server key")
.keyboardType(.asciiCapable)
}.padding(.horizontal)
Text("Statistics")
.font(.footnote)