version 1.0.0
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct VelodyiPhoneApp: App {
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
iPhoneLibraryView()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import SwiftUI
|
||||
|
||||
struct iPhoneLibraryView: View {
|
||||
@State private var viewModel = iPhoneLibraryViewModel()
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
List(viewModel.tracks) { track in
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(track.title)
|
||||
.font(.headline)
|
||||
Text(track.artist)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
.overlay {
|
||||
if viewModel.tracks.isEmpty {
|
||||
ContentUnavailableView(
|
||||
"No Local Tracks Yet",
|
||||
systemImage: "music.note.list",
|
||||
description: Text("This iPhone target currently exposes the offline catalog shell only.")
|
||||
)
|
||||
}
|
||||
}
|
||||
.navigationTitle("Velody")
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Button("Sync") {
|
||||
Task {
|
||||
await viewModel.refreshSync()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.safeAreaInset(edge: .bottom) {
|
||||
Text(viewModel.syncStatus)
|
||||
.font(.footnote)
|
||||
.foregroundStyle(.secondary)
|
||||
.padding()
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.background(.ultraThinMaterial)
|
||||
}
|
||||
}
|
||||
.task {
|
||||
await viewModel.loadIfNeeded()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import Foundation
|
||||
import Observation
|
||||
import VelodyDomain
|
||||
import VelodyNetworking
|
||||
import VelodyPersistence
|
||||
import VelodySync
|
||||
import VelodyUtilities
|
||||
|
||||
@MainActor
|
||||
@Observable
|
||||
final class iPhoneLibraryViewModel {
|
||||
var tracks: [LibraryTrack] = []
|
||||
var syncStatus = "Offline library not synced yet"
|
||||
|
||||
private let store = InMemoryLocalLibraryStore()
|
||||
private let keychainService = MemoryKeychainService()
|
||||
private let syncCoordinator: PlaceholderSyncCoordinator
|
||||
private var hasLoaded = false
|
||||
|
||||
init() {
|
||||
let environment = ServerEnvironment(
|
||||
baseURL: URL(string: "http://localhost:3000")!,
|
||||
appVersion: "0.1.0"
|
||||
)
|
||||
let apiClient = StubVelodyAPIClient(environment: environment)
|
||||
self.syncCoordinator = PlaceholderSyncCoordinator(
|
||||
apiClient: apiClient,
|
||||
store: store
|
||||
)
|
||||
}
|
||||
|
||||
func loadIfNeeded() async {
|
||||
guard !hasLoaded else { return }
|
||||
hasLoaded = true
|
||||
await keychainService.save("placeholder-bootstrap-token", forKey: "bootstrap-token")
|
||||
await refreshSync()
|
||||
}
|
||||
|
||||
func refreshSync() async {
|
||||
do {
|
||||
let result = try await syncCoordinator.performInitialSync()
|
||||
tracks = result.tracks
|
||||
syncStatus = result.statusMessage
|
||||
} catch {
|
||||
syncStatus = "Sync placeholder failed: \(error.localizedDescription)"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user