Add localization (DE/EN), vertical-scroll month view, context menus, custom colors
- Vertical-scroll month view with multi-day event spans, zig-zag month divider, CW number per week, on-demand event loading while scrolling - Top bar redesign: icon-only view picker on right, month title centered - Long-press context menus on day cells (month) and hour slots (week/day) for "New event", "Open in week view", "Open in day view", "Open in month view" - Localization system with system/de/en switch covering top bar, view picker, settings, menu, profile, server, accounts, event editor, agenda - Three new color pickers (text/background/line) + today-marker color applied in calendar views; current-time line now uses today color - App icon: removed alpha channel, accent color set to icon green (#20A050) - TestFlight: ITSAppUsesNonExemptEncryption=NO baked into Info.plist keys Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@ struct EventEditorSheet: View {
|
||||
let onSaved: () async -> Void
|
||||
|
||||
@Environment(\.dismiss) var dismiss
|
||||
@AppStorage("appLanguage") private var appLang = "system"
|
||||
@State private var title = ""
|
||||
@State private var isAllDay = false
|
||||
@State private var startDate = Date()
|
||||
@@ -29,36 +30,36 @@ struct EventEditorSheet: View {
|
||||
NavigationStack {
|
||||
Form {
|
||||
Section {
|
||||
TextField("Titel", text: $title)
|
||||
TextField(L10n.t("event.title_placeholder", appLang), text: $title)
|
||||
.font(.body.weight(.medium))
|
||||
}
|
||||
|
||||
Section {
|
||||
Toggle("Ganztägig", isOn: $isAllDay.animation())
|
||||
Toggle(L10n.t("event.allday", appLang), isOn: $isAllDay.animation())
|
||||
.tint(Color.accentColor)
|
||||
|
||||
if isAllDay {
|
||||
DatePicker("Start", selection: $startDate, displayedComponents: .date)
|
||||
DatePicker("Ende", selection: $endDate, displayedComponents: .date)
|
||||
DatePicker(L10n.t("event.start", appLang), selection: $startDate, displayedComponents: .date)
|
||||
DatePicker(L10n.t("event.end", appLang), selection: $endDate, displayedComponents: .date)
|
||||
} else {
|
||||
DatePicker("Start", selection: $startDate)
|
||||
DatePicker("Ende", selection: $endDate)
|
||||
DatePicker(L10n.t("event.start", appLang), selection: $startDate)
|
||||
DatePicker(L10n.t("event.end", appLang), selection: $endDate)
|
||||
}
|
||||
}
|
||||
|
||||
Section {
|
||||
TextField("Ort", text: $location)
|
||||
TextField("Beschreibung", text: $notes, axis: .vertical)
|
||||
TextField(L10n.t("event.location", appLang), text: $location)
|
||||
TextField(L10n.t("event.description", appLang), text: $notes, axis: .vertical)
|
||||
.lineLimit(3...6)
|
||||
}
|
||||
|
||||
Section("Kalender") {
|
||||
Section(L10n.t("event.calendar_section", appLang)) {
|
||||
if store.writableCalendars.isEmpty {
|
||||
Text("Keine beschreibbaren Kalender vorhanden")
|
||||
Text(L10n.t("event.no_writable", appLang))
|
||||
.foregroundStyle(.secondary)
|
||||
.font(.callout)
|
||||
} else {
|
||||
Picker("Kalender", selection: $selectedCalendarId) {
|
||||
Picker(L10n.t("event.calendar_picker", appLang), selection: $selectedCalendarId) {
|
||||
ForEach(store.writableCalendars) { cal in
|
||||
HStack {
|
||||
Circle()
|
||||
@@ -72,9 +73,9 @@ struct EventEditorSheet: View {
|
||||
}
|
||||
}
|
||||
|
||||
Section("Farbe") {
|
||||
Section(L10n.t("event.color_section", appLang)) {
|
||||
HStack {
|
||||
Text("Terminfarbe")
|
||||
Text(L10n.t("event.color", appLang))
|
||||
Spacer()
|
||||
ColorPicker("", selection: Binding(
|
||||
get: { Color(hex: color.isEmpty ? (selectedCal?.color ?? "#4285f4") : color) },
|
||||
@@ -82,7 +83,7 @@ struct EventEditorSheet: View {
|
||||
), supportsOpacity: false)
|
||||
.labelsHidden()
|
||||
if !color.isEmpty {
|
||||
Button("Zurücksetzen") { color = "" }
|
||||
Button(L10n.t("event.reset_color", appLang)) { color = "" }
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
@@ -95,14 +96,18 @@ struct EventEditorSheet: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle(isEditing ? "Termin bearbeiten" : "Neuer Termin")
|
||||
.navigationTitle(isEditing
|
||||
? L10n.t("event.edit_title", appLang)
|
||||
: L10n.t("event.new_title", appLang))
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button("Abbrechen") { dismiss() }
|
||||
Button(L10n.t("common.cancel", appLang)) { dismiss() }
|
||||
}
|
||||
ToolbarItem(placement: .primaryAction) {
|
||||
Button(isEditing ? "Sichern" : "Hinzufügen") {
|
||||
Button(isEditing
|
||||
? L10n.t("event.save", appLang)
|
||||
: L10n.t("event.add", appLang)) {
|
||||
Task { await save() }
|
||||
}
|
||||
.bold()
|
||||
|
||||
Reference in New Issue
Block a user