Pure-Native SwiftUI-App für Moodlit. Pendant zur SvelteKit-Web-App auf moodlit.mana.how; konsumiert ManaCore + ManaTokens + ManaAuthUI aus den Schwester-Repos. Stack: - SwiftUI Universal (iOS 18 / macOS 15), Swift 6 strict concurrency - mana-swift-core + mana-swift-ui (lokale SPM-Pakete via XcodeGen) - Bundle ev.mana.moodlit, Team QP3GLU8PH3, App-Group group.ev.mana.moodlit Features: - 24 Mood-Presets als Swift-Konstanten (Port von default-moods.ts) - Custom-Moods + Sequenzen via MoodlitAPI (Actor mit JWT-Bearer-Calls über AuthenticatedTransport, automatischer 401-Retry) - MoodPlayerView mit Idle-Timer-Off, Status-Bar-Hidden, Timer-Auto- Close, Favorite-Toggle, Play/Pause, Auto-Hide-Controls - SequencePlayerView mit Crossfade-Rotation durch alle Sequence-Moods (Net new ggü. Web — dort ist Sequence-Playback nicht verkabelt) - AnimatedMoodView rendert alle 21 AnimationTypes als 30-fps Timeline- View mit sin/cos-modulierten Filter-Effekten - Cards-Pattern Auth-Gate: Presets ohne Login sichtbar, Custom- Creation triggert ManaAuthGate.require → Login-Sheet - Theme: ManaTheme.twilight Forward (Violett #7c3aed) Build verified: - xcodebuild iOS Simulator (iPhone 17) → BUILD SUCCEEDED - xcodebuild macOS → BUILD SUCCEEDED Offen (μ-7.1+): Apple-Dev-Portal-Setup (Bundle, Capabilities), TestFlight, Widget, Settings-UI (Brightness/Speed), Hex-Color-Picker mit Text-Input, Visual-Polish der per-Animation Effekte. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
70 lines
2 KiB
Swift
70 lines
2 KiB
Swift
import SwiftUI
|
|
|
|
/// Sequence-Erstellungs-Form mit Mood-Multipicker.
|
|
public struct CreateSequenceSheet: View {
|
|
let onCreate: (_ name: String, _ moodIds: [String], _ durationSec: Int) -> Void
|
|
|
|
@Environment(\.dismiss) private var dismiss
|
|
@Environment(MoodStore.self) private var store
|
|
@State private var name = ""
|
|
@State private var durationSec = 30
|
|
@State private var selectedMoodIds: Set<String> = []
|
|
|
|
public init(onCreate: @escaping (_ name: String, _ moodIds: [String], _ durationSec: Int) -> Void) {
|
|
self.onCreate = onCreate
|
|
}
|
|
|
|
public var body: some View {
|
|
NavigationStack {
|
|
Form {
|
|
Section("Name") {
|
|
TextField("Sequenz-Name", text: $name)
|
|
}
|
|
|
|
Section("Dauer pro Mood") {
|
|
Stepper("\(durationSec) Sekunden", value: $durationSec, in: 5...600, step: 5)
|
|
}
|
|
|
|
Section("Moods (\(selectedMoodIds.count) ausgewählt)") {
|
|
ForEach(store.allMoods) { mood in
|
|
HStack {
|
|
Image(systemName: selectedMoodIds.contains(mood.id) ? "checkmark.circle.fill" : "circle")
|
|
.foregroundStyle(selectedMoodIds.contains(mood.id) ? MoodlitTheme.primary : MoodlitTheme.mutedForeground)
|
|
Text(mood.name)
|
|
Spacer()
|
|
Text(mood.animation.displayName)
|
|
.font(.caption2)
|
|
.foregroundStyle(MoodlitTheme.mutedForeground)
|
|
}
|
|
.contentShape(Rectangle())
|
|
.onTapGesture { toggle(mood.id) }
|
|
}
|
|
}
|
|
}
|
|
.navigationTitle("Neue Sequenz")
|
|
#if os(iOS)
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
#endif
|
|
.toolbar {
|
|
ToolbarItem(placement: .cancellationAction) {
|
|
Button("Abbrechen") { dismiss() }
|
|
}
|
|
ToolbarItem(placement: .confirmationAction) {
|
|
Button("Erstellen") {
|
|
let trimmed = name.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
onCreate(trimmed, Array(selectedMoodIds), durationSec)
|
|
}
|
|
.disabled(name.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty || selectedMoodIds.isEmpty)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private func toggle(_ id: String) {
|
|
if selectedMoodIds.contains(id) {
|
|
selectedMoodIds.remove(id)
|
|
} else {
|
|
selectedMoodIds.insert(id)
|
|
}
|
|
}
|
|
}
|