menu: состав пунктов точно по дизайну (+Уведомления/Настройки)

По дизайну выдвижного меню: Файлы/Календарь/Контакты/Карточки/Заметки +
Bitrix/1C/Личный кабинет + Уведомления/Настройки. Убраны Почта/Конференции/
Задачи/Поддержка (остаются в нижней панели).
- F7AppMenuItem: опц. localIcon (ImageVector); нативные пункты рисуются в
  круглом бейдже с зелёной обводкой (стиль glass) вместо серверной SVG;
- Уведомления → NotificationsSheet, Настройки → настройки активного таба,
  обработка по label в AppScaffold.onItemClick.
This commit is contained in:
b-dev-mobile
2026-07-08 13:18:45 +00:00
parent fda34a5b69
commit a30af98a8d
3 changed files with 85 additions and 25 deletions
@@ -1,5 +1,8 @@
package ru.forbion.f7cloud.mobile.ui package ru.forbion.f7cloud.mobile.ui
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Notifications
import androidx.compose.material.icons.filled.Settings
import ru.forbion.f7cloud.core.designsystem.F7AppMenuItem import ru.forbion.f7cloud.core.designsystem.F7AppMenuItem
enum class AppTab( enum class AppTab(
@@ -29,16 +32,14 @@ private data class AppMenuEntry(
val webPath: String? = null, val webPath: String? = null,
) )
// Состав меню — по дизайну выдвижного меню (Почта/Конференции/Задачи/Поддержка — только
// в нижней панели; Уведомления/Настройки добавлены как нативные пункты, см. appMenuItems).
private val coreAppMenuEntries = listOf( private val coreAppMenuEntries = listOf(
AppMenuEntry(AppTab.Mail, "Почта", "mail-glass.svg"),
AppMenuEntry(AppTab.Files, "Файлы", "files-glass.svg"), AppMenuEntry(AppTab.Files, "Файлы", "files-glass.svg"),
AppMenuEntry(AppTab.Calendar, "Календарь", "calendar-glass.svg"), AppMenuEntry(AppTab.Calendar, "Календарь", "calendar-glass.svg"),
AppMenuEntry(AppTab.Contacts, "Контакты", "contact-glass.svg"), AppMenuEntry(AppTab.Contacts, "Контакты", "contact-glass.svg"),
AppMenuEntry(AppTab.Talk, "Конференции", "spreed-glass.svg"),
AppMenuEntry(AppTab.Deck, "Карточки", "deck-glass.svg"), AppMenuEntry(AppTab.Deck, "Карточки", "deck-glass.svg"),
AppMenuEntry(AppTab.Tasks, "Задачи", "task-glass.svg"),
AppMenuEntry(null, "Заметки", "notes-glass.svg", webPath = "/apps/notes/"), AppMenuEntry(null, "Заметки", "notes-glass.svg", webPath = "/apps/notes/"),
AppMenuEntry(AppTab.Support, "Поддержка", "icon-header-f7support.svg"),
) )
data class AppMenuExternalSite( data class AppMenuExternalSite(
@@ -71,7 +72,22 @@ fun appMenuItems(
externalUrl = site.openUrl, externalUrl = site.openUrl,
) )
} }
return core + external // Нативные пункты по дизайну — действия обрабатываются в AppScaffold по label
val native = listOf(
F7AppMenuItem(
label = "Уведомления",
iconUrl = "",
selected = false,
localIcon = Icons.Filled.Notifications,
),
F7AppMenuItem(
label = "Настройки",
iconUrl = "",
selected = false,
localIcon = Icons.Filled.Settings,
),
)
return core + external + native
} }
fun appTabFromMenuIndex(index: Int): AppTab? { fun appTabFromMenuIndex(index: Int): AppTab? {
@@ -637,24 +637,42 @@ fun AppScaffold(
onItemClick = { index -> onItemClick = { index ->
val item = appMenuItemsList.getOrNull(index) ?: return@F7AppMenuSheet val item = appMenuItemsList.getOrNull(index) ?: return@F7AppMenuSheet
val external = item.externalUrl val external = item.externalUrl
val isPersonalCabinet = item.label.trim().equals("Личный кабинет", ignoreCase = true) val label = item.label.trim()
if (isPersonalCabinet) { when {
// Профиль — нативная панель вместо веб-ЛК forbion // Профиль — нативная панель вместо веб-ЛК forbion
menuOpen = false label.equals("Личный кабинет", ignoreCase = true) -> {
profileOpen = true menuOpen = false
} else if (!external.isNullOrBlank()) { profileOpen = true
runCatching {
context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(external)))
} }
menuOpen = false // Нативные пункты по дизайну меню
} else { label.equals("Уведомления", ignoreCase = true) -> {
appTabFromMenuIndex(index)?.let { tab -> menuOpen = false
if (tab != activeTab) { notificationsOpen = true
pushTabHistory(activeTab) }
activeTab = tab label.equals("Настройки", ignoreCase = true) -> {
menuOpen = false
when (activeTab) {
AppTab.Mail -> mailSettingsOpen = true
AppTab.Calendar -> calendarSettingsRequest++
AppTab.Files -> filesSettingsOpen = true
else -> Unit
} }
} }
menuOpen = false !external.isNullOrBlank() -> {
runCatching {
context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(external)))
}
menuOpen = false
}
else -> {
appTabFromMenuIndex(index)?.let { tab ->
if (tab != activeTab) {
pushTabHistory(activeTab)
activeTab = tab
}
}
menuOpen = false
}
} }
}, },
) )
@@ -25,8 +25,10 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.itemsIndexed import androidx.compose.foundation.lazy.grid.itemsIndexed
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -40,6 +42,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
@@ -63,6 +66,9 @@ data class F7AppMenuItem(
val iconUrl: String, val iconUrl: String,
val selected: Boolean, val selected: Boolean,
val externalUrl: String? = null, val externalUrl: String? = null,
// Локальная иконка вместо серверной (для нативных пунктов — Уведомления/Настройки),
// рисуется в круглом бейдже с зелёной обводкой под стиль glass-иконок.
val localIcon: ImageVector? = null,
) )
@Composable @Composable
@@ -245,12 +251,32 @@ private fun F7AppMenuGridItem(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp),
) { ) {
AsyncImage( val localIcon = item.localIcon
model = item.iconUrl, if (localIcon != null) {
contentDescription = item.label, // Нативный пункт: иконка в круглом бейдже с зелёной обводкой (стиль glass)
modifier = Modifier.size(MenuIconSize), Box(
contentScale = ContentScale.Fit, modifier = Modifier
) .size(MenuIconSize)
.clip(CircleShape)
.background(Color.White)
.border(1.5.dp, F7Colors.Green30, CircleShape),
contentAlignment = Alignment.Center,
) {
Icon(
localIcon,
contentDescription = item.label,
tint = F7Colors.Primary,
modifier = Modifier.size(MenuIconSize * 0.46f),
)
}
} else {
AsyncImage(
model = item.iconUrl,
contentDescription = item.label,
modifier = Modifier.size(MenuIconSize),
contentScale = ContentScale.Fit,
)
}
Text( Text(
text = item.label, text = item.label,
style = MaterialTheme.typography.labelLarge.copy( style = MaterialTheme.typography.labelLarge.copy(