feat: top bar declutter — view/filter/groups/sync into one burger popup (iOS)
The top bar now shows only nav + title + a single burger. Tapping it opens a compact menu: View (with a fixed icon, no longer per-view), Filter, Groups (if any), Sync, and an "Einstellungen" entry that opens the existing full menu. Removed the separate group/view/filter icons from both the flat bar and the Liquid Glass toolbar. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -126,12 +126,7 @@ struct CalendarHostView: View {
|
||||
}
|
||||
}
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
HStack(spacing: 8) {
|
||||
if !groups.isEmpty { groupMenu }
|
||||
viewPickerMenu
|
||||
filterButton
|
||||
Button { showMenu = true } label: { Image(systemName: "line.3.horizontal") }
|
||||
}
|
||||
menuButton
|
||||
}
|
||||
}
|
||||
.safeAreaInset(edge: .top, spacing: 0) {
|
||||
@@ -200,15 +195,8 @@ struct CalendarHostView: View {
|
||||
.minimumScaleFactor(0.7)
|
||||
.layoutPriority(1)
|
||||
Spacer(minLength: 6)
|
||||
if !groups.isEmpty { groupMenu }
|
||||
viewPickerMenu
|
||||
filterButton
|
||||
Button { showMenu = true } label: {
|
||||
Image(systemName: "line.3.horizontal")
|
||||
.font(.system(size: 18, weight: .medium))
|
||||
.frame(width: 36, height: 36)
|
||||
}
|
||||
.padding(.trailing, 2)
|
||||
menuButton
|
||||
.padding(.trailing, 2)
|
||||
}
|
||||
.frame(height: 48)
|
||||
}
|
||||
@@ -217,39 +205,6 @@ struct CalendarHostView: View {
|
||||
barContents.background(.bar)
|
||||
}
|
||||
|
||||
private var filterButton: some View {
|
||||
Button { showFilter = true } label: {
|
||||
Image(systemName: "line.3.horizontal.decrease.circle")
|
||||
.font(.system(size: 17, weight: .medium))
|
||||
.foregroundStyle(store.hiddenCalendarKeys.isEmpty ? .primary : Color.accentColor)
|
||||
.frame(width: 36, height: 36)
|
||||
}
|
||||
.accessibilityLabel(L10n.t("filter.button", appLang))
|
||||
}
|
||||
|
||||
// Group switcher: "Persönlich" + each group. Selecting a group flips the
|
||||
// calendar into the combined overlay (like the web's group view).
|
||||
private var groupMenu: some View {
|
||||
Menu {
|
||||
Button { switchGroup(nil) } label: {
|
||||
Label(L10n.t("groups.personal", appLang),
|
||||
systemImage: store.activeGroup == nil ? "checkmark" : "person")
|
||||
}
|
||||
ForEach(groups) { g in
|
||||
Button { switchGroup(g) } label: {
|
||||
Label(g.name,
|
||||
systemImage: store.activeGroup?.id == g.id ? "checkmark" : GroupIcons.symbol(g.icon))
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Image(systemName: store.activeGroup == nil ? "person.2" : "person.2.fill")
|
||||
.font(.system(size: 17, weight: .medium))
|
||||
.foregroundStyle(store.activeGroup == nil ? .primary : Color.accentColor)
|
||||
.frame(width: 36, height: 36)
|
||||
}
|
||||
.accessibilityLabel(L10n.t("groups.title", appLang))
|
||||
}
|
||||
|
||||
@ViewBuilder private var groupBanner: some View {
|
||||
if let g = store.activeGroup {
|
||||
HStack(spacing: 6) {
|
||||
@@ -273,20 +228,58 @@ struct CalendarHostView: View {
|
||||
Task { await forceReload() }
|
||||
}
|
||||
|
||||
private var viewPickerMenu: some View {
|
||||
/// The single top-bar action: a compact popup holding view / filter /
|
||||
/// groups / sync, plus an "Einstellungen" entry that opens the full menu.
|
||||
/// (Replaces the separate view / filter / group icons in the bar.)
|
||||
private var menuButton: some View {
|
||||
Menu {
|
||||
ForEach(CalViewType.allCases, id: \.self) { vt in
|
||||
Button { store.viewType = vt } label: {
|
||||
Label(vt.label(appLang), systemImage: vt.systemImage)
|
||||
// View (fixed icon, not per-view)
|
||||
Menu {
|
||||
ForEach(CalViewType.allCases, id: \.self) { vt in
|
||||
Button { store.viewType = vt } label: {
|
||||
Label(vt.label(appLang), systemImage: store.viewType == vt ? "checkmark" : vt.systemImage)
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Label(L10n.t("view.change", appLang), systemImage: "rectangle.3.group")
|
||||
}
|
||||
// Filter
|
||||
Button { showFilter = true } label: {
|
||||
Label(L10n.t("filter.button", appLang), systemImage: "line.3.horizontal.decrease.circle")
|
||||
}
|
||||
// Groups
|
||||
if !groups.isEmpty {
|
||||
Menu {
|
||||
Button { switchGroup(nil) } label: {
|
||||
Label(L10n.t("groups.personal", appLang),
|
||||
systemImage: store.activeGroup == nil ? "checkmark" : "person")
|
||||
}
|
||||
ForEach(groups) { g in
|
||||
Button { switchGroup(g) } label: {
|
||||
Label(g.name,
|
||||
systemImage: store.activeGroup?.id == g.id ? "checkmark" : GroupIcons.symbol(g.icon))
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Label(L10n.t("groups.title", appLang), systemImage: "person.2")
|
||||
}
|
||||
}
|
||||
// Sync
|
||||
Button { Task { await SettingsSync.pull(api: api); await forceReload() } } label: {
|
||||
Label(L10n.t("menu.sync", appLang), systemImage: "arrow.triangle.2.circlepath")
|
||||
}
|
||||
Divider()
|
||||
// Full settings menu
|
||||
Button { showMenu = true } label: {
|
||||
Label(L10n.t("menu.section.settings", appLang), systemImage: "gearshape")
|
||||
}
|
||||
} label: {
|
||||
Image(systemName: store.viewType.systemImage)
|
||||
.font(.system(size: 17, weight: .medium))
|
||||
.foregroundStyle(.primary)
|
||||
Image(systemName: "line.3.horizontal")
|
||||
.font(.system(size: 18, weight: .medium))
|
||||
.foregroundStyle(store.activeGroup == nil && store.hiddenCalendarKeys.isEmpty ? .primary : Color.accentColor)
|
||||
.frame(width: 36, height: 36)
|
||||
}
|
||||
.accessibilityLabel(L10n.t("view.change", appLang))
|
||||
.accessibilityLabel(L10n.t("nav.menu", appLang))
|
||||
}
|
||||
|
||||
// MARK: – Error banner
|
||||
|
||||
Reference in New Issue
Block a user