fix: Android Gruppen-Umschalter sichtbar + Menü überlappt Navigationsleiste nicht
Top-Bar-Gruppenumschalter ist jetzt eine tonale Pille (Akzentfarbe im Gruppenmodus) statt eines flachen Icons – klar als Button erkennbar; das Dropdown markiert die aktive Auswahl. Gruppen-Verwaltung: Tipp auf eine Gruppe öffnet direkt deren Ansicht, Verwalten liegt aufs Zahnrad. Menü-Bottom-Sheet respektiert jetzt das Navigationsleisten-Inset (+ Scroll-Fallback), sodass Abmelden/Server wechseln nicht mehr hinter den Android-Buttons liegen. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,8 @@ import androidx.compose.animation.fadeIn
|
|||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.animation.slideInVertically
|
import androidx.compose.animation.slideInVertically
|
||||||
import androidx.compose.animation.slideOutVertically
|
import androidx.compose.animation.slideOutVertically
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
@@ -16,6 +18,7 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Add
|
import androidx.compose.material.icons.filled.Add
|
||||||
|
import androidx.compose.material.icons.filled.Check
|
||||||
import androidx.compose.material.icons.filled.ChevronLeft
|
import androidx.compose.material.icons.filled.ChevronLeft
|
||||||
import androidx.compose.material.icons.filled.ChevronRight
|
import androidx.compose.material.icons.filled.ChevronRight
|
||||||
import androidx.compose.material.icons.filled.FilterList
|
import androidx.compose.material.icons.filled.FilterList
|
||||||
@@ -36,6 +39,7 @@ import androidx.compose.material3.Surface
|
|||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -45,6 +49,7 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
@@ -243,6 +248,7 @@ fun CalendarScreen(
|
|||||||
Overlay.GROUPS -> GroupsScreen(
|
Overlay.GROUPS -> GroupsScreen(
|
||||||
onClose = { overlay = Overlay.NONE },
|
onClose = { overlay = Overlay.NONE },
|
||||||
onChanged = { vm.loadGroups(); vm.loadWritableCalendars() },
|
onChanged = { vm.loadGroups(); vm.loadWritableCalendars() },
|
||||||
|
onOpenGroupView = { g -> overlay = Overlay.NONE; vm.switchGroup(g) },
|
||||||
)
|
)
|
||||||
Overlay.NONE -> Unit
|
Overlay.NONE -> Unit
|
||||||
}
|
}
|
||||||
@@ -380,27 +386,42 @@ private fun CompactIcon(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Top-bar switcher: "My calendar" + each group; flips the calendar into the group overlay. */
|
/**
|
||||||
|
* Top-bar switcher: "My calendar" + each group; flips the calendar into the
|
||||||
|
* group overlay. Rendered as a tonal pill so it stands out from the flat icons
|
||||||
|
* (filled in the accent colour while a group overlay is active).
|
||||||
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
private fun GroupSwitcher(groups: List<Group>, activeGroup: Group?, onSwitchGroup: (Group?) -> Unit) {
|
private fun GroupSwitcher(groups: List<Group>, activeGroup: Group?, onSwitchGroup: (Group?) -> Unit) {
|
||||||
var open by remember { mutableStateOf(false) }
|
var open by remember { mutableStateOf(false) }
|
||||||
|
val active = activeGroup != null
|
||||||
Box {
|
Box {
|
||||||
IconButton(onClick = { open = true }, modifier = Modifier.size(40.dp)) {
|
Box(
|
||||||
|
Modifier
|
||||||
|
.padding(horizontal = 2.dp)
|
||||||
|
.size(38.dp)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(if (active) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surfaceVariant)
|
||||||
|
.clickable { open = true },
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Filled.People,
|
Icons.Filled.People,
|
||||||
contentDescription = tr("groups.title"),
|
contentDescription = tr("groups.title"),
|
||||||
modifier = Modifier.size(22.dp),
|
modifier = Modifier.size(21.dp),
|
||||||
tint = if (activeGroup != null) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface,
|
tint = if (active) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
DropdownMenu(expanded = open, onDismissRequest = { open = false }) {
|
DropdownMenu(expanded = open, onDismissRequest = { open = false }) {
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text(tr("group.switch.personal")) },
|
text = { Text(tr("group.switch.personal")) },
|
||||||
|
trailingIcon = { if (!active) Icon(Icons.Filled.Check, contentDescription = null) },
|
||||||
onClick = { open = false; onSwitchGroup(null) },
|
onClick = { open = false; onSwitchGroup(null) },
|
||||||
)
|
)
|
||||||
groups.forEach { g ->
|
groups.forEach { g ->
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text("${g.icon ?: "👥"} ${g.name}") },
|
text = { Text("${g.icon ?: "👥"} ${g.name}") },
|
||||||
|
trailingIcon = { if (activeGroup?.id == g.id) Icon(Icons.Filled.Check, contentDescription = null) },
|
||||||
onClick = { open = false; onSwitchGroup(g) },
|
onClick = { open = false; onSwitchGroup(g) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,8 +21,10 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
|||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Add
|
import androidx.compose.material.icons.filled.Add
|
||||||
|
import androidx.compose.material.icons.filled.ChevronRight
|
||||||
import androidx.compose.material.icons.filled.Close
|
import androidx.compose.material.icons.filled.Close
|
||||||
import androidx.compose.material.icons.filled.Delete
|
import androidx.compose.material.icons.filled.Delete
|
||||||
|
import androidx.compose.material.icons.filled.Tune
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.Checkbox
|
import androidx.compose.material3.Checkbox
|
||||||
@@ -66,6 +68,7 @@ private val GROUP_ICONS = listOf(
|
|||||||
fun GroupsScreen(
|
fun GroupsScreen(
|
||||||
onClose: () -> Unit,
|
onClose: () -> Unit,
|
||||||
onChanged: () -> Unit,
|
onChanged: () -> Unit,
|
||||||
|
onOpenGroupView: (Group) -> Unit = {},
|
||||||
vm: GroupsViewModel = hiltViewModel(),
|
vm: GroupsViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
var createOpen by remember { mutableStateOf(false) }
|
var createOpen by remember { mutableStateOf(false) }
|
||||||
@@ -97,16 +100,22 @@ fun GroupsScreen(
|
|||||||
}
|
}
|
||||||
items(vm.groups, key = { it.id }) { g ->
|
items(vm.groups, key = { it.id }) { g ->
|
||||||
Row(
|
Row(
|
||||||
Modifier.fillMaxWidth().clickable { manageId = g.id }.padding(vertical = 12.dp),
|
Modifier.fillMaxWidth().clickable { onOpenGroupView(g) }.padding(vertical = 10.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Text(g.icon ?: "👥", style = MaterialTheme.typography.titleMedium)
|
Text(g.icon ?: "👥", style = MaterialTheme.typography.titleMedium)
|
||||||
Text(g.name, modifier = Modifier.weight(1f).padding(start = 12.dp), style = MaterialTheme.typography.bodyLarge)
|
Column(Modifier.weight(1f).padding(start = 12.dp)) {
|
||||||
Text(
|
Text(g.name, style = MaterialTheme.typography.bodyLarge)
|
||||||
tr("groups.member_count", g.memberCount),
|
Text(
|
||||||
style = MaterialTheme.typography.bodySmall,
|
tr("groups.member_count", g.memberCount),
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
)
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
IconButton(onClick = { manageId = g.id }) {
|
||||||
|
Icon(Icons.Filled.Tune, contentDescription = tr("groups.manage"), tint = MaterialTheme.colorScheme.onSurfaceVariant)
|
||||||
|
}
|
||||||
|
Icon(Icons.Filled.ChevronRight, contentDescription = tr("groups.view"), tint = MaterialTheme.colorScheme.onSurfaceVariant)
|
||||||
}
|
}
|
||||||
Divider()
|
Divider()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,11 @@ import androidx.compose.foundation.layout.Row
|
|||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.AccountCircle
|
import androidx.compose.material.icons.filled.AccountCircle
|
||||||
import androidx.compose.material.icons.filled.Dns
|
import androidx.compose.material.icons.filled.Dns
|
||||||
@@ -44,7 +47,14 @@ fun MenuSheet(
|
|||||||
onSwitchServer: () -> Unit,
|
onSwitchServer: () -> Unit,
|
||||||
) {
|
) {
|
||||||
ModalBottomSheet(onDismissRequest = onDismiss) {
|
ModalBottomSheet(onDismissRequest = onDismiss) {
|
||||||
Column(Modifier.fillMaxWidth().padding(bottom = 24.dp)) {
|
// Inset for the system navigation bar so the last rows aren't hidden
|
||||||
|
// behind the Android nav buttons; scroll as a fallback on short screens.
|
||||||
|
Column(
|
||||||
|
Modifier.fillMaxWidth()
|
||||||
|
.verticalScroll(rememberScrollState())
|
||||||
|
.navigationBarsPadding()
|
||||||
|
.padding(bottom = 12.dp),
|
||||||
|
) {
|
||||||
Text(
|
Text(
|
||||||
"Calendarr",
|
"Calendarr",
|
||||||
style = MaterialTheme.typography.titleLarge,
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
|||||||
Reference in New Issue
Block a user