- 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>
92 lines
3.6 KiB
Swift
92 lines
3.6 KiB
Swift
import SwiftUI
|
|
|
|
struct MenuSheet: View {
|
|
let api: CalendarrAPI
|
|
@Environment(AppState.self) var appState
|
|
@Environment(\.dismiss) var dismiss
|
|
@AppStorage("appLanguage") private var appLang = "system"
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
List {
|
|
// User info header
|
|
Section {
|
|
HStack(spacing: 12) {
|
|
Circle()
|
|
.fill(Color.accentColor)
|
|
.frame(width: 44, height: 44)
|
|
.overlay {
|
|
Text(appState.username.prefix(1).uppercased())
|
|
.font(.title3.bold())
|
|
.foregroundStyle(.white)
|
|
}
|
|
VStack(alignment: .leading, spacing: 2) {
|
|
Text(appState.username).font(.headline)
|
|
Text(appState.serverURL
|
|
.replacingOccurrences(of: "https://", with: "")
|
|
.replacingOccurrences(of: "http://", with: ""))
|
|
.font(.caption)
|
|
.foregroundStyle(.secondary)
|
|
.lineLimit(1)
|
|
}
|
|
if appState.isAdmin {
|
|
Spacer()
|
|
Text(L10n.t("menu.admin", appLang))
|
|
.font(.caption2.weight(.semibold))
|
|
.padding(.horizontal, 8).padding(.vertical, 3)
|
|
.background(Color.accentColor.opacity(0.15))
|
|
.foregroundStyle(Color.accentColor)
|
|
.clipShape(Capsule())
|
|
}
|
|
}
|
|
.padding(.vertical, 4)
|
|
}
|
|
|
|
Section(L10n.t("menu.section.settings", appLang)) {
|
|
NavigationLink {
|
|
ProfileView(api: api)
|
|
} label: {
|
|
Label(L10n.t("menu.profile", appLang), systemImage: "person.circle")
|
|
}
|
|
|
|
NavigationLink {
|
|
SettingsView(api: api)
|
|
} label: {
|
|
Label(L10n.t("menu.appearance", appLang), systemImage: "paintpalette")
|
|
}
|
|
|
|
NavigationLink {
|
|
AccountsView(api: api)
|
|
} label: {
|
|
Label(L10n.t("menu.accounts", appLang), systemImage: "tray.2")
|
|
}
|
|
|
|
NavigationLink {
|
|
ServerView()
|
|
} label: {
|
|
Label(L10n.t("menu.server", appLang), systemImage: "server.rack")
|
|
}
|
|
}
|
|
|
|
Section {
|
|
Button(role: .destructive) {
|
|
dismiss()
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
|
appState.logout()
|
|
}
|
|
} label: {
|
|
Label(L10n.t("menu.logout", appLang), systemImage: "rectangle.portrait.and.arrow.right")
|
|
}
|
|
}
|
|
}
|
|
.navigationTitle(L10n.t("nav.menu", appLang))
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.toolbar {
|
|
ToolbarItem(placement: .primaryAction) {
|
|
Button(L10n.t("nav.done", appLang)) { dismiss() }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|