148 lines
4.7 KiB
Swift
148 lines
4.7 KiB
Swift
//
|
|
// RecordsScreen.swift
|
|
// AutoCat
|
|
//
|
|
// Created by Selim Mustafaev on 11.03.2025.
|
|
// Copyright © 2025 Selim Mustafaev. All rights reserved.
|
|
//
|
|
|
|
import SwiftUI
|
|
import AutoCatCore
|
|
|
|
struct RecordsScreen: View {
|
|
|
|
@State var viewModel: RecordsViewModel
|
|
|
|
@State var showEditAlert = false
|
|
@State var numberText = ""
|
|
@State var selectedRecordId: String = ""
|
|
|
|
init() {
|
|
|
|
let resolver = ServiceContainer.shared
|
|
self.viewModel = RecordsViewModel(
|
|
recordService: resolver.resolve(VehicleRecordServiceProtocol.self),
|
|
storageService: resolver.resolve(StorageServiceProtocol.self),
|
|
recordPlayer: resolver.resolve(RecordPlayerServiceProtocol.self)
|
|
)
|
|
}
|
|
|
|
init(viewModel: RecordsViewModel) {
|
|
|
|
self.viewModel = viewModel
|
|
}
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
List {
|
|
ForEach(viewModel.recordSections) { section in
|
|
Section(header: Text(section.header)) {
|
|
ForEach(section.elements) { model in
|
|
AudioRecordView(
|
|
record: .init(
|
|
dto: model,
|
|
isPlaying: model.id == viewModel.playingRecord?.id
|
|
),
|
|
progress: viewModel.progress
|
|
) {
|
|
viewModel.onPlayTapped(record: model)
|
|
}
|
|
.listRowInsets(EdgeInsets())
|
|
.swipeActions(allowsFullSwipe: false) {
|
|
makeActions(for: model)
|
|
}
|
|
.contextMenu {
|
|
makeActions(for: model, useLabels: true)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.listStyle(.inset)
|
|
.hud($viewModel.hud)
|
|
.navigationTitle("Voice records")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.onAppear {
|
|
Task { await viewModel.onAppear() }
|
|
}
|
|
.toolbar {
|
|
ToolbarItem(placement: .primaryAction) {
|
|
Button {
|
|
Task { await viewModel.startRecording() }
|
|
} label: {
|
|
Image(systemName: "plus")
|
|
}
|
|
.alert("Recording...", isPresented: $viewModel.showRecordingAlert) {
|
|
Button("Cancel", role: .cancel) {
|
|
Task { await viewModel.cancelRecording() }
|
|
}
|
|
Button("Done") {
|
|
Task { await viewModel.stopRecording() }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.noteAlert(
|
|
title: String(localized: "Edit plate number"),
|
|
body: $numberText,
|
|
isPresented: $showEditAlert
|
|
) { text in
|
|
Task { await viewModel.editRecord(id: selectedRecordId, number: numberText) }
|
|
}
|
|
.navigationDestination(for: VehicleEventDto.self) { event in
|
|
MapScreen(mapInput: .event(event))
|
|
}
|
|
}
|
|
}
|
|
|
|
@ViewBuilder
|
|
func makeActions(for record: AudioRecordDto, useLabels: Bool = false) -> some View {
|
|
|
|
if useLabels {
|
|
makeMenuActions(for: record)
|
|
}
|
|
|
|
if let url = record.url {
|
|
ShareLink(item: url)
|
|
}
|
|
|
|
Button {
|
|
selectedRecordId = record.id
|
|
numberText = record.number ?? ""
|
|
showEditAlert = true
|
|
} label: {
|
|
Label(useLabels ? "Edit plate number" : "", systemImage: "pencil")
|
|
}
|
|
|
|
Button(role: .destructive) {
|
|
Task { await viewModel.deleteRecord(id: record.id) }
|
|
} label: {
|
|
Label(useLabels ? "Delete" : "", systemImage: "trash")
|
|
}
|
|
}
|
|
|
|
@ViewBuilder
|
|
func makeMenuActions(for record: AudioRecordDto) -> some View {
|
|
|
|
Button {
|
|
viewModel.showRawRecognizedText(record)
|
|
} label: {
|
|
Label("Show recognized text", systemImage: "textformat")
|
|
}
|
|
|
|
if let event = record.event {
|
|
NavigationLink(value: event) {
|
|
Label("Show on map", systemImage: "map")
|
|
}
|
|
}
|
|
|
|
if record.number != nil {
|
|
Button {
|
|
viewModel.check(record)
|
|
} label: {
|
|
Label("Check", systemImage: "eye")
|
|
}
|
|
}
|
|
}
|
|
}
|