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>
This commit is contained in:
Scarriffle
2026-07-15 06:09:38 +02:00
parent d90a1a552c
commit e008d5d98a
3 changed files with 25 additions and 52 deletions

View File

@@ -1,21 +1,14 @@
import SwiftUI
/// Destinations the drawer can open (presented as sheets by the host).
enum DrawerDestination: Int, Identifiable {
case profile, settings, accounts, groups, server
var id: Int { rawValue }
}
/// The left side drawer: central navigation + calendar visibility + group
/// switching. Replaces the old menu popup and filter sheet.
/// 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 onOpenDestination: (DrawerDestination) -> Void
let onSync: () -> Void
let onOpenMenu: () -> Void
let onClose: () -> Void
@Environment(AppState.self) private var appState
@@ -118,34 +111,20 @@ struct CalendarDrawer: View {
.buttonStyle(.plain)
}
// MARK: Nav footer (quick access)
// MARK: Nav footer (single entry into the full menu)
private var navFooter: some View {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 4) {
navButton(L10n.t("menu.appearance", appLang), "paintpalette") { onOpenDestination(.settings) }
navButton(L10n.t("menu.accounts", appLang), "tray.2") { onOpenDestination(.accounts) }
navButton(L10n.t("menu.profile", appLang), "person.circle") { onOpenDestination(.profile) }
navButton(L10n.t("groups.title", appLang), "person.2") { onOpenDestination(.groups) }
navButton(L10n.t("menu.server", appLang), "server.rack") { onOpenDestination(.server) }
navButton(L10n.t("menu.sync", appLang), "arrow.triangle.2.circlepath") { onSync() }
navButton(L10n.t("menu.logout", appLang), "rectangle.portrait.and.arrow.right", role: .destructive) {
appState.logout()
}
}
.padding(.horizontal, 12).padding(.vertical, 10)
}
}
private func navButton(_ label: String, _ systemImage: String, role: ButtonRole? = nil, action: @escaping () -> Void) -> some View {
Button(role: role, action: action) {
VStack(spacing: 4) {
Image(systemName: systemImage).font(.system(size: 18))
Text(label).font(.caption2).lineLimit(1)
}
.frame(width: 64)
.foregroundStyle(role == .destructive ? Color.red : Color.accentColor)
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)
}
}

View File

@@ -34,7 +34,6 @@ struct CalendarHostView: View {
@State private var groups: [CalGroup] = []
@State private var showNewBirthday = false
@State private var showDrawer = false
@State private var drawerDestination: DrawerDestination? = nil
private var titleString: String {
if store.viewType == .month {
@@ -63,8 +62,7 @@ struct CalendarHostView: View {
api: api, store: store, groups: groups,
onSwitchGroup: { g in closeDrawer(); switchGroup(g) },
onSelectView: { vt in store.viewType = vt; closeDrawer() },
onOpenDestination: { dest in closeDrawer(); drawerDestination = dest },
onSync: { closeDrawer(); Task { await syncFromServer(force: true) } },
onOpenMenu: { closeDrawer(); showMenu = true },
onClose: { closeDrawer() }
)
.frame(width: drawerWidth)
@@ -92,15 +90,6 @@ struct CalendarHostView: View {
)
}
}
.sheet(item: $drawerDestination) { dest in
switch dest {
case .profile: ProfileView(api: api)
case .settings: SettingsView(api: api)
case .accounts: AccountsView(api: api)
case .groups: GroupsView(api: api)
case .server: ServerView()
}
}
}
private var drawerWidth: CGFloat { min(UIScreen.main.bounds.width - 40, 360) }

View File

@@ -33,12 +33,17 @@ struct CalendarFilterContent: View {
} else {
List {
Section {
Button(L10n.t("filter.show_all", appLang)) {
hidden = []; store.setHiddenCalendars(hidden)
}
Button(L10n.t("filter.hide_all", appLang)) {
hidden = allKeys; store.setHiddenCalendars(hidden)
HStack(spacing: 10) {
Button(L10n.t("filter.show_all", appLang)) {
hidden = []; store.setHiddenCalendars(hidden)
}
.buttonStyle(.bordered).frame(maxWidth: .infinity)
Button(L10n.t("filter.hide_all", appLang)) {
hidden = allKeys; store.setHiddenCalendars(hidden)
}
.buttonStyle(.bordered).frame(maxWidth: .infinity)
}
.listRowInsets(EdgeInsets(top: 6, leading: 12, bottom: 6, trailing: 12))
}
let visibleLocals = localCalendars.filter {
!banished.contains(CalendarStore.calendarKey(source: "local", calendarId: "\($0.id)"))