feat: clearer shared calendars + move group switch into the menu
Three Android UX fixes reported from on-device testing: 1. A calendar shared with the user now shows read-only affordances: parse the server's new read_only flag on events, show a lock icon in the filter sheet, and exclude read-only shared calendars from the event editor's calendar picker (getWritableCalendars) so saves no longer 403. The calendar's display name (owner's name) comes from the server. 2. (server-side) group event name prefix removed — no client change needed. 3. Move the personal/group view switcher out of the crowded top bar into the burger MenuSheet as a "group view" section (personal + each group with a check mark), mirroring iOS. The GroupSwitcher pill and its top-bar wiring are gone; the active-group banner stays. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -270,7 +270,12 @@ class CalendarRepository @Inject constructor(
|
|||||||
/** Resolve all calendars the user can create events in. */
|
/** Resolve all calendars the user can create events in. */
|
||||||
suspend fun getWritableCalendars(): List<WritableCalendar> = withContext(Dispatchers.IO) {
|
suspend fun getWritableCalendars(): List<WritableCalendar> = withContext(Dispatchers.IO) {
|
||||||
val result = mutableListOf<WritableCalendar>()
|
val result = mutableListOf<WritableCalendar>()
|
||||||
runCatching { api.getLocalCalendars() }.getOrDefault(emptyList()).forEach { cal ->
|
runCatching { api.getLocalCalendars() }.getOrDefault(emptyList())
|
||||||
|
// Exclude read-only shared calendars — offering them in the event
|
||||||
|
// editor only leads to a 403 on save. Own + read_write (incl. group)
|
||||||
|
// calendars stay.
|
||||||
|
.filter { it.owned || it.permission == "read_write" }
|
||||||
|
.forEach { cal ->
|
||||||
result += WritableCalendar("local-${cal.id}", cal.name, cal.color, "local", cal.id)
|
result += WritableCalendar("local-${cal.id}", cal.name, cal.color, "local", cal.id)
|
||||||
}
|
}
|
||||||
runCatching { api.getCalDAVAccounts() }.getOrDefault(emptyList())
|
runCatching { api.getCalDAVAccounts() }.getOrDefault(emptyList())
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ data class CalEvent(
|
|||||||
val displayTitle: String? = null,
|
val displayTitle: String? = null,
|
||||||
// Reminder offsets in minutes-before-start (0 = at start). Local events only.
|
// Reminder offsets in minutes-before-start (0 = at start). Local events only.
|
||||||
val reminders: List<Int> = emptyList(),
|
val reminders: List<Int> = emptyList(),
|
||||||
|
// True for events from a calendar shared with the user read-only.
|
||||||
|
val readOnly: Boolean = false,
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
* Group view supplies a server-resolved colour (display_color); otherwise
|
* Group view supplies a server-resolved colour (display_color); otherwise
|
||||||
@@ -126,6 +128,7 @@ data class CalEvent(
|
|||||||
reminders = json.optJSONArray("reminders")?.let { arr ->
|
reminders = json.optJSONArray("reminders")?.let { arr ->
|
||||||
(0 until arr.length()).mapNotNull { (arr.opt(it) as? Number)?.toInt() }
|
(0 until arr.length()).mapNotNull { (arr.opt(it) as? Number)?.toInt() }
|
||||||
} ?: emptyList(),
|
} ?: emptyList(),
|
||||||
|
readOnly = json.optBoolean("read_only", false),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ object L10n {
|
|||||||
"filter.button" to "Kalender ein-/ausblenden",
|
"filter.button" to "Kalender ein-/ausblenden",
|
||||||
"filter.sync_error" to "Synchronisierung fehlgeschlagen",
|
"filter.sync_error" to "Synchronisierung fehlgeschlagen",
|
||||||
"filter.banish" to "Dauerhaft ausblenden",
|
"filter.banish" to "Dauerhaft ausblenden",
|
||||||
|
"filter.read_only" to "Nur lesen",
|
||||||
"filter.banished_footer" to "Dauerhaft ausgeblendete Kalender erscheinen unter »Konten & Kalender« und können dort wieder eingeblendet werden.",
|
"filter.banished_footer" to "Dauerhaft ausgeblendete Kalender erscheinen unter »Konten & Kalender« und können dort wieder eingeblendet werden.",
|
||||||
"accounts.banished_header" to "Ausgeblendete Kalender",
|
"accounts.banished_header" to "Ausgeblendete Kalender",
|
||||||
"accounts.banished_unhide" to "Wieder einblenden",
|
"accounts.banished_unhide" to "Wieder einblenden",
|
||||||
@@ -268,6 +269,7 @@ object L10n {
|
|||||||
"filter.button" to "Show/hide calendars",
|
"filter.button" to "Show/hide calendars",
|
||||||
"filter.sync_error" to "Sync failed",
|
"filter.sync_error" to "Sync failed",
|
||||||
"filter.banish" to "Hide permanently",
|
"filter.banish" to "Hide permanently",
|
||||||
|
"filter.read_only" to "Read-only",
|
||||||
"filter.banished_footer" to "Permanently hidden calendars appear under “Accounts & Calendars”, where you can show them again.",
|
"filter.banished_footer" to "Permanently hidden calendars appear under “Accounts & Calendars”, where you can show them again.",
|
||||||
"accounts.banished_header" to "Hidden calendars",
|
"accounts.banished_header" to "Hidden calendars",
|
||||||
"accounts.banished_unhide" to "Show again",
|
"accounts.banished_unhide" to "Show again",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.size
|
|||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Archive
|
import androidx.compose.material.icons.filled.Archive
|
||||||
|
import androidx.compose.material.icons.filled.Lock
|
||||||
import androidx.compose.material.icons.filled.Notifications
|
import androidx.compose.material.icons.filled.Notifications
|
||||||
import androidx.compose.material.icons.filled.NotificationsOff
|
import androidx.compose.material.icons.filled.NotificationsOff
|
||||||
import androidx.compose.material.icons.filled.WarningAmber
|
import androidx.compose.material.icons.filled.WarningAmber
|
||||||
@@ -33,7 +34,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
import com.scarriffle.calendarr.ui.tr
|
import com.scarriffle.calendarr.ui.tr
|
||||||
import com.scarriffle.calendarr.util.colorFromHex
|
import com.scarriffle.calendarr.util.colorFromHex
|
||||||
|
|
||||||
data class CalendarFilterEntry(val key: String, val name: String, val color: String, val source: String = "")
|
data class CalendarFilterEntry(val key: String, val name: String, val color: String, val source: String = "", val readOnly: Boolean = false)
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -109,6 +110,14 @@ fun CalendarFilterSheet(
|
|||||||
modifier = Modifier.weight(1f).padding(start = 12.dp),
|
modifier = Modifier.weight(1f).padding(start = 12.dp),
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
)
|
)
|
||||||
|
if (entry.readOnly) {
|
||||||
|
Icon(
|
||||||
|
Icons.Filled.Lock,
|
||||||
|
contentDescription = tr("filter.read_only"),
|
||||||
|
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
modifier = Modifier.padding(end = 4.dp).size(15.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
if (!groupMode) {
|
if (!groupMode) {
|
||||||
val remDisabled = entry.key in state.reminderDisabledKeys
|
val remDisabled = entry.key in state.reminderDisabledKeys
|
||||||
IconButton(onClick = { vm.setCalendarRemindersDisabled(entry.key, disabled = !remDisabled) }) {
|
IconButton(onClick = { vm.setCalendarRemindersDisabled(entry.key, disabled = !remDisabled) }) {
|
||||||
|
|||||||
@@ -18,12 +18,10 @@ 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
|
||||||
import androidx.compose.material.icons.filled.Menu
|
import androidx.compose.material.icons.filled.Menu
|
||||||
import androidx.compose.material.icons.filled.People
|
|
||||||
import androidx.compose.material.icons.filled.Today
|
import androidx.compose.material.icons.filled.Today
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.DropdownMenu
|
import androidx.compose.material3.DropdownMenu
|
||||||
@@ -156,9 +154,6 @@ fun CalendarScreen(
|
|||||||
viewType = state.viewType,
|
viewType = state.viewType,
|
||||||
loading = state.isLoading || state.isBackgroundCaching,
|
loading = state.isLoading || state.isBackgroundCaching,
|
||||||
viewMenuOpen = viewMenuOpen,
|
viewMenuOpen = viewMenuOpen,
|
||||||
groups = state.groups,
|
|
||||||
activeGroup = state.activeGroup,
|
|
||||||
onSwitchGroup = { vm.switchGroup(it) },
|
|
||||||
onMenu = { showMenu = true },
|
onMenu = { showMenu = true },
|
||||||
onPrev = { goPrev() },
|
onPrev = { goPrev() },
|
||||||
onToday = { goToday() },
|
onToday = { goToday() },
|
||||||
@@ -213,6 +208,9 @@ fun CalendarScreen(
|
|||||||
if (showMenu) {
|
if (showMenu) {
|
||||||
MenuSheet(
|
MenuSheet(
|
||||||
isAdmin = false,
|
isAdmin = false,
|
||||||
|
groups = state.groups,
|
||||||
|
activeGroup = state.activeGroup,
|
||||||
|
onSwitchGroup = { showMenu = false; vm.switchGroup(it) },
|
||||||
onDismiss = { showMenu = false },
|
onDismiss = { showMenu = false },
|
||||||
onProfile = { showMenu = false; overlay = Overlay.PROFILE },
|
onProfile = { showMenu = false; overlay = Overlay.PROFILE },
|
||||||
onAppearance = { showMenu = false; overlay = Overlay.SETTINGS },
|
onAppearance = { showMenu = false; overlay = Overlay.SETTINGS },
|
||||||
@@ -398,9 +396,6 @@ private fun CompactTopBar(
|
|||||||
viewType: CalViewType,
|
viewType: CalViewType,
|
||||||
loading: Boolean,
|
loading: Boolean,
|
||||||
viewMenuOpen: Boolean,
|
viewMenuOpen: Boolean,
|
||||||
groups: List<Group>,
|
|
||||||
activeGroup: Group?,
|
|
||||||
onSwitchGroup: (Group?) -> Unit,
|
|
||||||
onMenu: () -> Unit,
|
onMenu: () -> Unit,
|
||||||
onPrev: () -> Unit,
|
onPrev: () -> Unit,
|
||||||
onToday: () -> Unit,
|
onToday: () -> Unit,
|
||||||
@@ -437,9 +432,6 @@ private fun CompactTopBar(
|
|||||||
strokeWidth = 2.dp,
|
strokeWidth = 2.dp,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (groups.isNotEmpty()) {
|
|
||||||
GroupSwitcher(groups = groups, activeGroup = activeGroup, onSwitchGroup = onSwitchGroup)
|
|
||||||
}
|
|
||||||
CompactIcon(Icons.Filled.FilterList, onFilter, tr("filter.button"))
|
CompactIcon(Icons.Filled.FilterList, onFilter, tr("filter.button"))
|
||||||
Box {
|
Box {
|
||||||
CompactIcon(viewType.icon, { onViewMenuToggle(true) }, tr("view.change"))
|
CompactIcon(viewType.icon, { onViewMenuToggle(true) }, tr("view.change"))
|
||||||
@@ -474,50 +466,6 @@ private fun CompactIcon(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
private fun GroupSwitcher(groups: List<Group>, activeGroup: Group?, onSwitchGroup: (Group?) -> Unit) {
|
|
||||||
var open by remember { mutableStateOf(false) }
|
|
||||||
val active = activeGroup != null
|
|
||||||
Box {
|
|
||||||
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(
|
|
||||||
Icons.Filled.People,
|
|
||||||
contentDescription = tr("groups.title"),
|
|
||||||
modifier = Modifier.size(21.dp),
|
|
||||||
tint = if (active) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.onSurfaceVariant,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
DropdownMenu(expanded = open, onDismissRequest = { open = false }) {
|
|
||||||
DropdownMenuItem(
|
|
||||||
text = { Text(tr("group.switch.personal")) },
|
|
||||||
trailingIcon = { if (!active) Icon(Icons.Filled.Check, contentDescription = null) },
|
|
||||||
onClick = { open = false; onSwitchGroup(null) },
|
|
||||||
)
|
|
||||||
groups.forEach { g ->
|
|
||||||
DropdownMenuItem(
|
|
||||||
text = { Text(g.name) },
|
|
||||||
leadingIcon = { GroupIcon(g.icon) },
|
|
||||||
trailingIcon = { if (activeGroup?.id == g.id) Icon(Icons.Filled.Check, contentDescription = null) },
|
|
||||||
onClick = { open = false; onSwitchGroup(g) },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun GroupBanner(group: Group, onExit: () -> Unit) {
|
private fun GroupBanner(group: Group, onExit: () -> Unit) {
|
||||||
Surface(color = MaterialTheme.colorScheme.primary.copy(alpha = 0.15f), modifier = Modifier.fillMaxWidth()) {
|
Surface(color = MaterialTheme.colorScheme.primary.copy(alpha = 0.15f), modifier = Modifier.fillMaxWidth()) {
|
||||||
@@ -543,7 +491,7 @@ private fun GroupBanner(group: Group, onExit: () -> Unit) {
|
|||||||
* can be toggled back on. */
|
* can be toggled back on. */
|
||||||
private fun allKnownCalendars(vm: CalendarViewModel): List<CalendarFilterEntry> {
|
private fun allKnownCalendars(vm: CalendarViewModel): List<CalendarFilterEntry> {
|
||||||
return vm.knownCalendars()
|
return vm.knownCalendars()
|
||||||
.map { CalendarFilterEntry(calendarKey(it.source, it.calendarId), it.calendarName.ifBlank { it.source }, it.effectiveColor, it.source) }
|
.map { CalendarFilterEntry(calendarKey(it.source, it.calendarId), it.calendarName.ifBlank { it.source }, it.effectiveColor, it.source, it.readOnly) }
|
||||||
.distinctBy { it.key }
|
.distinctBy { it.key }
|
||||||
.sortedBy { it.name.lowercase() }
|
.sortedBy { it.name.lowercase() }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,12 +13,14 @@ import androidx.compose.foundation.rememberScrollState
|
|||||||
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.AccountCircle
|
import androidx.compose.material.icons.filled.AccountCircle
|
||||||
|
import androidx.compose.material.icons.filled.CalendarMonth
|
||||||
|
import androidx.compose.material.icons.filled.Check
|
||||||
import androidx.compose.material.icons.filled.Dns
|
import androidx.compose.material.icons.filled.Dns
|
||||||
import androidx.compose.material.icons.filled.Logout
|
import androidx.compose.material.icons.filled.Logout
|
||||||
import androidx.compose.material.icons.filled.Palette
|
import androidx.compose.material.icons.filled.Palette
|
||||||
import androidx.compose.material.icons.filled.People
|
import androidx.compose.material.icons.filled.People
|
||||||
|
import androidx.compose.material.icons.filled.Person
|
||||||
import androidx.compose.material.icons.filled.Sync
|
import androidx.compose.material.icons.filled.Sync
|
||||||
import androidx.compose.material.icons.filled.CalendarMonth
|
|
||||||
import androidx.compose.material3.Divider
|
import androidx.compose.material3.Divider
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
@@ -31,12 +33,17 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.scarriffle.calendarr.domain.model.Group
|
||||||
|
import com.scarriffle.calendarr.ui.groups.GroupIcon
|
||||||
import com.scarriffle.calendarr.ui.tr
|
import com.scarriffle.calendarr.ui.tr
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun MenuSheet(
|
fun MenuSheet(
|
||||||
isAdmin: Boolean,
|
isAdmin: Boolean,
|
||||||
|
groups: List<Group>,
|
||||||
|
activeGroup: Group?,
|
||||||
|
onSwitchGroup: (Group?) -> Unit,
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
onProfile: () -> Unit,
|
onProfile: () -> Unit,
|
||||||
onAppearance: () -> Unit,
|
onAppearance: () -> Unit,
|
||||||
@@ -64,6 +71,27 @@ fun MenuSheet(
|
|||||||
MenuRow(Icons.Filled.AccountCircle, tr("menu.profile"), onProfile)
|
MenuRow(Icons.Filled.AccountCircle, tr("menu.profile"), onProfile)
|
||||||
MenuRow(Icons.Filled.Palette, tr("menu.appearance"), onAppearance)
|
MenuRow(Icons.Filled.Palette, tr("menu.appearance"), onAppearance)
|
||||||
MenuRow(Icons.Filled.CalendarMonth, tr("menu.accounts"), onAccounts)
|
MenuRow(Icons.Filled.CalendarMonth, tr("menu.accounts"), onAccounts)
|
||||||
|
// Group view switch (personal + each group) — moved here from the
|
||||||
|
// top bar to free up space. Group *management* stays below.
|
||||||
|
if (groups.isNotEmpty()) {
|
||||||
|
Divider(Modifier.padding(vertical = 4.dp))
|
||||||
|
MenuSectionLabel(tr("groups.view"))
|
||||||
|
GroupSwitchRow(
|
||||||
|
label = tr("group.switch.personal"),
|
||||||
|
checked = activeGroup == null,
|
||||||
|
leading = { Icon(Icons.Filled.Person, contentDescription = null, tint = MaterialTheme.colorScheme.onSurfaceVariant) },
|
||||||
|
onClick = { onSwitchGroup(null) },
|
||||||
|
)
|
||||||
|
groups.forEach { g ->
|
||||||
|
GroupSwitchRow(
|
||||||
|
label = g.name,
|
||||||
|
checked = activeGroup?.id == g.id,
|
||||||
|
leading = { GroupIcon(g.icon) },
|
||||||
|
onClick = { onSwitchGroup(g) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Divider(Modifier.padding(vertical = 4.dp))
|
||||||
MenuRow(Icons.Filled.People, tr("menu.groups"), onGroups)
|
MenuRow(Icons.Filled.People, tr("menu.groups"), onGroups)
|
||||||
Divider(Modifier.padding(vertical = 4.dp))
|
Divider(Modifier.padding(vertical = 4.dp))
|
||||||
MenuRow(Icons.Filled.Sync, tr("menu.sync"), onSync)
|
MenuRow(Icons.Filled.Sync, tr("menu.sync"), onSync)
|
||||||
@@ -85,3 +113,34 @@ private fun MenuRow(icon: ImageVector, label: String, onClick: () -> Unit) {
|
|||||||
Text(label, style = MaterialTheme.typography.bodyLarge)
|
Text(label, style = MaterialTheme.typography.bodyLarge)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun MenuSectionLabel(text: String) {
|
||||||
|
Text(
|
||||||
|
text,
|
||||||
|
style = MaterialTheme.typography.labelMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
modifier = Modifier.padding(horizontal = 20.dp, vertical = 4.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A group-view choice (personal or a specific group) with a trailing check. */
|
||||||
|
@Composable
|
||||||
|
private fun GroupSwitchRow(
|
||||||
|
label: String,
|
||||||
|
checked: Boolean,
|
||||||
|
leading: @Composable () -> Unit,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
Modifier.fillMaxWidth().clickable(onClick = onClick).padding(horizontal = 20.dp, vertical = 12.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
leading()
|
||||||
|
Spacer(Modifier.width(18.dp))
|
||||||
|
Text(label, style = MaterialTheme.typography.bodyLarge, modifier = Modifier.weight(1f))
|
||||||
|
if (checked) {
|
||||||
|
Icon(Icons.Filled.Check, contentDescription = null, tint = MaterialTheme.colorScheme.primary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user