ReadKeep/readeck/UI/AddBookmark/AddBookmarkView.swift
Ilyas Hallak da7bb1613c feat: Add bookmark creation functionality and WebView dark mode support
- Add CREATE API endpoint for new bookmarks with POST request
- Implement CreateBookmarkRequestDto and CreateBookmarkResponseDto
- Create AddBookmarkView with form validation and clipboard integration
- Add AddBookmarkViewModel with URL validation and label parsing
- Implement CreateBookmarkUseCase with convenience methods
- Extend BookmarksRepository with createBookmark method returning server message
- Add comprehensive error handling for bookmark creation scenarios
- Integrate WebView dark mode support with CSS variables and system color scheme
- Add dynamic theme switching based on iOS appearance settings
- Enhance WebView styling with iOS-native colors and typography
- Fix BookmarksView refresh after bookmark creation
- Add floating action button and sheet presentation for adding bookmarks
- Implement form validation with real-time feedback
- Add clipboard URL detection and paste functionality
2025-06-11 23:02:58 +02:00

134 lines
5.2 KiB
Swift

import SwiftUI
struct AddBookmarkView: View {
@State private var viewModel = AddBookmarkViewModel()
@Environment(\.dismiss) private var dismiss
var body: some View {
NavigationView {
Form {
Section(header: Text("Bookmark Details")) {
VStack(alignment: .leading, spacing: 8) {
Text("URL *")
.font(.caption)
.foregroundColor(.secondary)
TextField("https://example.com", text: $viewModel.url)
.textFieldStyle(RoundedBorderTextFieldStyle())
.keyboardType(.URL)
.autocapitalization(.none)
.autocorrectionDisabled()
}
VStack(alignment: .leading, spacing: 8) {
Text("Titel (optional)")
.font(.caption)
.foregroundColor(.secondary)
TextField("Bookmark Titel", text: $viewModel.title)
.textFieldStyle(RoundedBorderTextFieldStyle())
}
}
Section(header: Text("Labels")) {
VStack(alignment: .leading, spacing: 8) {
Text("Labels (durch Komma getrennt)")
.font(.caption)
.foregroundColor(.secondary)
TextField("work, important, later", text: $viewModel.labelsText)
.textFieldStyle(RoundedBorderTextFieldStyle())
}
if !viewModel.parsedLabels.isEmpty {
LazyVGrid(columns: [
GridItem(.adaptive(minimum: 80))
], spacing: 8) {
ForEach(viewModel.parsedLabels, id: \.self) { label in
Text(label)
.font(.caption)
.padding(.horizontal, 8)
.padding(.vertical, 4)
.background(Color.blue.opacity(0.1))
.foregroundColor(.blue)
.clipShape(Capsule())
}
}
}
}
Section {
Button("Aus Zwischenablage einfügen") {
viewModel.pasteFromClipboard()
}
.disabled(viewModel.clipboardURL == nil)
if let clipboardURL = viewModel.clipboardURL {
Text("Zwischenablage: \(clipboardURL)")
.font(.caption)
.foregroundColor(.secondary)
.lineLimit(1)
.truncationMode(.middle)
}
}
}
.navigationTitle("Bookmark hinzufügen")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button("Abbrechen") {
dismiss()
}
}
ToolbarItem(placement: .navigationBarTrailing) {
Button("Speichern") {
Task {
await viewModel.createBookmark()
}
}
.disabled(!viewModel.isValid || viewModel.isLoading)
}
}
.overlay {
if viewModel.isLoading {
ZStack {
Color.black.opacity(0.3)
VStack(spacing: 16) {
ProgressView()
.scaleEffect(1.2)
Text("Bookmark wird erstellt...")
.font(.subheadline)
}
.padding(24)
.background(Color(.systemBackground))
.clipShape(RoundedRectangle(cornerRadius: 12))
.shadow(radius: 10)
}
.ignoresSafeArea()
}
}
.alert("Erfolgreich", isPresented: $viewModel.showSuccessAlert) {
Button("OK") {
dismiss()
}
} message: {
Text("Bookmark wurde erfolgreich hinzugefügt!")
}
.alert("Fehler", isPresented: $viewModel.showErrorAlert) {
Button("OK", role: .cancel) { }
} message: {
Text(viewModel.errorMessage ?? "Unbekannter Fehler")
}
}
.onAppear {
viewModel.checkClipboard()
}
}
}
#Preview {
AddBookmarkView()
}