Files
Calendarr-IOS/Calendarr iOS/Views/Calendar/CalendarDrawer.swift
Scarriffle e008d5d98a iOS drawer: compact show/hide-all, single menu button, corner-safe footer
Testing feedback:
- show-all / hide-all are now side by side (bordered), not two big stacked rows
- the busy quick-access strip becomes a single "Einstellungen" button that
  opens the full menu (which already holds accounts/profile/groups/server/sync/
  logout)
- extra horizontal + bottom padding so the footer button is not clipped by the
  iPhone's rounded corners / home indicator

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 06:09:38 +02:00

131 lines
4.8 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import SwiftUI
/// The left side drawer: calendar visibility + group/view switching, plus a
/// single entry into the full menu (Settings/Accounts/Profile//Sync/Logout).
struct CalendarDrawer: View {
let api: CalendarrAPI
let store: CalendarStore
let groups: [CalGroup]
let onSwitchGroup: (CalGroup?) -> Void
let onSelectView: (CalViewType) -> Void
let onOpenMenu: () -> Void
let onClose: () -> Void
@Environment(AppState.self) private var appState
@AppStorage("appLanguage") private var appLang = "system"
var body: some View {
VStack(spacing: 0) {
header
Divider()
viewSwitcher
if !groups.isEmpty {
Divider()
groupSwitcher
}
Divider()
CalendarFilterContent(api: api, store: store)
Divider()
navFooter
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
.background(Color(.systemBackground))
}
// MARK: Header
private var header: some View {
HStack(spacing: 12) {
Circle()
.fill(Color.accentColor)
.frame(width: 40, height: 40)
.overlay {
Text(appState.username.prefix(1).uppercased())
.font(.headline).foregroundStyle(.white)
}
VStack(alignment: .leading, spacing: 2) {
Text(appState.username).font(.headline).lineLimit(1)
Text(appState.serverURL
.replacingOccurrences(of: "https://", with: "")
.replacingOccurrences(of: "http://", with: ""))
.font(.caption).foregroundStyle(.secondary).lineLimit(1)
}
Spacer()
Button { onClose() } label: {
Image(systemName: "xmark").font(.system(size: 15, weight: .semibold))
}
.buttonStyle(.plain).foregroundStyle(.secondary)
}
.padding(.horizontal, 16).padding(.top, 12).padding(.bottom, 10)
}
// MARK: View switcher
private var viewSwitcher: some View {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 8) {
ForEach(CalViewType.allCases, id: \.self) { vt in
let selected = store.viewType == vt
Button { onSelectView(vt) } label: {
Label(vt.label(appLang), systemImage: vt.systemImage)
.font(.caption.weight(.medium))
.padding(.horizontal, 12).padding(.vertical, 7)
.background(selected ? Color.accentColor : Color(.secondarySystemBackground))
.foregroundStyle(selected ? .white : .primary)
.clipShape(Capsule())
}
.buttonStyle(.plain)
}
}
.padding(.horizontal, 16).padding(.vertical, 8)
}
}
// MARK: Group switcher
private var groupSwitcher: some View {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 8) {
chip(label: L10n.t("groups.personal", appLang),
systemImage: "person",
selected: store.activeGroup == nil) { onSwitchGroup(nil) }
ForEach(groups) { g in
chip(label: g.name,
systemImage: GroupIcons.symbol(g.icon),
selected: store.activeGroup?.id == g.id) { onSwitchGroup(g) }
}
}
.padding(.horizontal, 16).padding(.vertical, 8)
}
}
private func chip(label: String, systemImage: String, selected: Bool, action: @escaping () -> Void) -> some View {
Button(action: action) {
Label(label, systemImage: systemImage)
.font(.caption.weight(.medium))
.padding(.horizontal, 12).padding(.vertical, 7)
.background(selected ? Color.accentColor : Color(.secondarySystemBackground))
.foregroundStyle(selected ? .white : .primary)
.clipShape(Capsule())
}
.buttonStyle(.plain)
}
// MARK: Nav footer (single entry into the full menu)
private var navFooter: some View {
Button { onOpenMenu() } label: {
Label(L10n.t("menu.section.settings", appLang), systemImage: "gearshape")
.font(.body.weight(.medium))
.frame(maxWidth: .infinity)
.padding(.vertical, 12)
}
.buttonStyle(.plain)
.foregroundStyle(Color.accentColor)
.padding(.horizontal, 16)
// Extra bottom inset so the button clears the home indicator and the
// screen's rounded corners.
.padding(.bottom, 20)
}
}