bottombar: навигационная панель по дизайну «Новое меню»
Панель стала единой навигационной: слева назад (в Конференциях — чаты), далее ярлыки-разделы Почта · Карточки · Конференции (активный подсвечен), справа выдвижное меню. Профиль/Уведомления/Настройки/Создать убраны из панели (Профиль/Уведомления/Настройки — в выдвижном меню; бейдж уведомлений перенесён на кнопку меню). Создание раньше вызывалось ТОЛЬКО из панели → чтобы не потерять: - новый общий компонент F7CreateButton (зелёный круг «+»); - добавлен в шапку Файлов (→ меню создания) и Контактов (→ добавить контакт). Задачи/Поддержка уже имели свои кнопки; Почта — «+» в шапке. F7BottomBarConfig упрощён (единый layout), F7MobileBottomBar получил слоты SectionMail/SectionCards/SectionConferences + activeTabKey.
This commit is contained in:
@@ -483,6 +483,7 @@ fun AppScaffold(
|
|||||||
serverUrl = currentSession.serverUrl,
|
serverUrl = currentSession.serverUrl,
|
||||||
userId = userId,
|
userId = userId,
|
||||||
config = bottomBarConfig,
|
config = bottomBarConfig,
|
||||||
|
activeTabKey = activeTab.name,
|
||||||
menuOpen = menuOpen,
|
menuOpen = menuOpen,
|
||||||
chatsHighlighted = activeTab == AppTab.Talk && !talkInRoom,
|
chatsHighlighted = activeTab == AppTab.Talk && !talkInRoom,
|
||||||
navBackHighlighted = (activeTab == AppTab.Mail && mailSidebarOpen) ||
|
navBackHighlighted = (activeTab == AppTab.Mail && mailSidebarOpen) ||
|
||||||
@@ -499,36 +500,22 @@ fun AppScaffold(
|
|||||||
else -> Unit
|
else -> Unit
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onCreateClick = {
|
onMailClick = {
|
||||||
when (activeTab) {
|
if (activeTab != AppTab.Mail) {
|
||||||
AppTab.Files -> {
|
pushTabHistory(activeTab)
|
||||||
if (F7AppPermissions.missing(context).isNotEmpty()) {
|
activeTab = AppTab.Mail
|
||||||
onRequestRuntimePermissions { filesUploadRequest++ }
|
|
||||||
} else {
|
|
||||||
filesUploadRequest++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AppTab.Contacts -> contactsCreateRequest++
|
|
||||||
AppTab.Tasks -> tasksCreateRequest++
|
|
||||||
AppTab.Support -> supportCreateRequest++
|
|
||||||
else -> Unit
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onProfileClick = {
|
onCardsClick = {
|
||||||
menuOpen = false
|
if (activeTab != AppTab.Deck) {
|
||||||
profileOpen = true
|
pushTabHistory(activeTab)
|
||||||
|
activeTab = AppTab.Deck
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onNotificationsClick = {
|
onConferencesClick = {
|
||||||
menuOpen = false
|
if (activeTab != AppTab.Talk) {
|
||||||
hasNotificationBadge = false
|
pushTabHistory(activeTab)
|
||||||
notificationsOpen = true
|
activeTab = AppTab.Talk
|
||||||
},
|
|
||||||
onSettingsClick = {
|
|
||||||
when (activeTab) {
|
|
||||||
AppTab.Mail -> mailSettingsOpen = true
|
|
||||||
AppTab.Calendar -> calendarSettingsRequest++
|
|
||||||
AppTab.Files -> filesSettingsOpen = true
|
|
||||||
else -> Unit
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onMenuClick = { menuOpen = !menuOpen },
|
onMenuClick = { menuOpen = !menuOpen },
|
||||||
@@ -647,6 +634,7 @@ fun AppScaffold(
|
|||||||
// Нативные пункты по дизайну меню
|
// Нативные пункты по дизайну меню
|
||||||
label.equals("Уведомления", ignoreCase = true) -> {
|
label.equals("Уведомления", ignoreCase = true) -> {
|
||||||
menuOpen = false
|
menuOpen = false
|
||||||
|
hasNotificationBadge = false
|
||||||
notificationsOpen = true
|
notificationsOpen = true
|
||||||
}
|
}
|
||||||
label.equals("Настройки", ignoreCase = true) -> {
|
label.equals("Настройки", ignoreCase = true) -> {
|
||||||
|
|||||||
+14
-63
@@ -3,10 +3,9 @@ package ru.forbion.f7cloud.core.designsystem
|
|||||||
enum class F7BottomBarSlot {
|
enum class F7BottomBarSlot {
|
||||||
Chats,
|
Chats,
|
||||||
NavBack,
|
NavBack,
|
||||||
Create,
|
SectionMail,
|
||||||
Profile,
|
SectionCards,
|
||||||
Notifications,
|
SectionConferences,
|
||||||
Settings,
|
|
||||||
Menu,
|
Menu,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -16,69 +15,21 @@ data class F7BottomBarConfig(
|
|||||||
val buttonCount: Int get() = slots.size
|
val buttonCount: Int get() = slots.size
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
// Единая навигационная панель по дизайну «Новое меню»:
|
||||||
|
// слева «назад» (в Конференциях — «чаты»), затем ярлыки-разделы
|
||||||
|
// Почта · Карточки · Конференции, справа — выдвижное меню.
|
||||||
|
// Профиль/Уведомления/Настройки/Создать вынесены (меню и шапки экранов).
|
||||||
fun forContext(
|
fun forContext(
|
||||||
tabKey: String,
|
tabKey: String,
|
||||||
talkInRoom: Boolean,
|
talkInRoom: Boolean,
|
||||||
): F7BottomBarConfig = when (tabKey) {
|
): F7BottomBarConfig {
|
||||||
"Talk" -> if (talkInRoom) {
|
val left = if (tabKey == "Talk") F7BottomBarSlot.Chats else F7BottomBarSlot.NavBack
|
||||||
F7BottomBarConfig(listOf(F7BottomBarSlot.Profile, F7BottomBarSlot.Notifications, F7BottomBarSlot.Menu))
|
return F7BottomBarConfig(
|
||||||
} else {
|
|
||||||
F7BottomBarConfig(
|
|
||||||
listOf(
|
|
||||||
F7BottomBarSlot.Chats,
|
|
||||||
F7BottomBarSlot.Profile,
|
|
||||||
F7BottomBarSlot.Notifications,
|
|
||||||
F7BottomBarSlot.Menu,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
"Files" -> F7BottomBarConfig(
|
|
||||||
listOf(
|
listOf(
|
||||||
F7BottomBarSlot.NavBack,
|
left,
|
||||||
F7BottomBarSlot.Profile,
|
F7BottomBarSlot.SectionMail,
|
||||||
F7BottomBarSlot.Notifications,
|
F7BottomBarSlot.SectionCards,
|
||||||
F7BottomBarSlot.Create,
|
F7BottomBarSlot.SectionConferences,
|
||||||
F7BottomBarSlot.Settings,
|
|
||||||
F7BottomBarSlot.Menu,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
"Contacts" -> F7BottomBarConfig(
|
|
||||||
listOf(
|
|
||||||
F7BottomBarSlot.Create,
|
|
||||||
F7BottomBarSlot.Profile,
|
|
||||||
F7BottomBarSlot.Notifications,
|
|
||||||
F7BottomBarSlot.Menu,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
"Tasks" -> F7BottomBarConfig(
|
|
||||||
listOf(
|
|
||||||
F7BottomBarSlot.Create,
|
|
||||||
F7BottomBarSlot.Profile,
|
|
||||||
F7BottomBarSlot.Notifications,
|
|
||||||
F7BottomBarSlot.Menu,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
"Support" -> F7BottomBarConfig(
|
|
||||||
listOf(
|
|
||||||
F7BottomBarSlot.Create,
|
|
||||||
F7BottomBarSlot.Profile,
|
|
||||||
F7BottomBarSlot.Notifications,
|
|
||||||
F7BottomBarSlot.Menu,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
"Mail", "Calendar" -> F7BottomBarConfig(
|
|
||||||
listOf(
|
|
||||||
F7BottomBarSlot.NavBack,
|
|
||||||
F7BottomBarSlot.Profile,
|
|
||||||
F7BottomBarSlot.Notifications,
|
|
||||||
F7BottomBarSlot.Settings,
|
|
||||||
F7BottomBarSlot.Menu,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
else -> F7BottomBarConfig(
|
|
||||||
listOf(
|
|
||||||
F7BottomBarSlot.Profile,
|
|
||||||
F7BottomBarSlot.Notifications,
|
|
||||||
F7BottomBarSlot.Menu,
|
F7BottomBarSlot.Menu,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
package ru.forbion.f7cloud.core.designsystem
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.border
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.draw.shadow
|
||||||
|
import androidx.compose.ui.graphics.Brush
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Зелёная круглая кнопка «+» — единый элемент создания в шапках экранов
|
||||||
|
* (Почта/Файлы/Контакты/…). Градиент Primary, как у других зелёных кнопок.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun F7CreateButton(
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
size: Dp = 44.dp,
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = modifier
|
||||||
|
.size(size)
|
||||||
|
.shadow(3.dp, CircleShape, spotColor = F7Colors.Primary.copy(alpha = 0.25f))
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(
|
||||||
|
Brush.linearGradient(
|
||||||
|
listOf(F7Colors.PrimaryGradientStart, F7Colors.PrimaryGradientEnd),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.border(1.dp, F7Colors.Primary.copy(alpha = 0.22f), CircleShape)
|
||||||
|
.clickable(onClick = onClick),
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "+",
|
||||||
|
color = Color.White,
|
||||||
|
fontSize = 26.sp,
|
||||||
|
fontWeight = FontWeight.Light,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+20
-22
@@ -27,10 +27,9 @@ import coil.compose.AsyncImage
|
|||||||
data class F7BottomBarActions(
|
data class F7BottomBarActions(
|
||||||
val onChatsClick: () -> Unit = {},
|
val onChatsClick: () -> Unit = {},
|
||||||
val onNavBackClick: () -> Unit = {},
|
val onNavBackClick: () -> Unit = {},
|
||||||
val onCreateClick: () -> Unit = {},
|
val onMailClick: () -> Unit = {},
|
||||||
val onProfileClick: () -> Unit = {},
|
val onCardsClick: () -> Unit = {},
|
||||||
val onNotificationsClick: () -> Unit = {},
|
val onConferencesClick: () -> Unit = {},
|
||||||
val onSettingsClick: () -> Unit = {},
|
|
||||||
val onMenuClick: () -> Unit = {},
|
val onMenuClick: () -> Unit = {},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -53,6 +52,7 @@ fun F7MobileBottomBar(
|
|||||||
userId: String,
|
userId: String,
|
||||||
config: F7BottomBarConfig,
|
config: F7BottomBarConfig,
|
||||||
actions: F7BottomBarActions,
|
actions: F7BottomBarActions,
|
||||||
|
activeTabKey: String = "",
|
||||||
menuOpen: Boolean = false,
|
menuOpen: Boolean = false,
|
||||||
chatsHighlighted: Boolean = false,
|
chatsHighlighted: Boolean = false,
|
||||||
navBackHighlighted: Boolean = false,
|
navBackHighlighted: Boolean = false,
|
||||||
@@ -97,26 +97,23 @@ fun F7MobileBottomBar(
|
|||||||
iconRotation = if (navBackHighlighted) 180f else 0f,
|
iconRotation = if (navBackHighlighted) 180f else 0f,
|
||||||
onClick = actions.onNavBackClick,
|
onClick = actions.onNavBackClick,
|
||||||
)
|
)
|
||||||
F7BottomBarSlot.Create -> F7BottomBarIconSlot(
|
F7BottomBarSlot.SectionMail -> F7BottomBarIconSlot(
|
||||||
iconUrl = "$base/themes/forbion/images/header/green-plus.svg",
|
iconUrl = "$base/themes/forbion/images/header/mail-header-icon.svg",
|
||||||
contentDescription = "Создать",
|
contentDescription = "Почта",
|
||||||
onClick = actions.onCreateClick,
|
highlighted = activeTabKey == "Mail",
|
||||||
|
onClick = actions.onMailClick,
|
||||||
)
|
)
|
||||||
F7BottomBarSlot.Profile -> F7BottomBarIconSlot(
|
F7BottomBarSlot.SectionCards -> F7BottomBarIconSlot(
|
||||||
iconUrl = "$base/themes/forbion/images/header/profile-menu-icon-big.svg",
|
iconUrl = "$base/themes/forbion/images/header/deck-header-icon.svg",
|
||||||
contentDescription = "Профиль",
|
contentDescription = "Карточки",
|
||||||
onClick = actions.onProfileClick,
|
highlighted = activeTabKey == "Deck",
|
||||||
|
onClick = actions.onCardsClick,
|
||||||
)
|
)
|
||||||
F7BottomBarSlot.Notifications -> F7BottomBarIconSlot(
|
F7BottomBarSlot.SectionConferences -> F7BottomBarIconSlot(
|
||||||
iconUrl = "$base/themes/forbion/images/header/not-menu-icon-big.svg",
|
iconUrl = "$base/themes/forbion/images/header/spreed-header-icon.svg",
|
||||||
contentDescription = "Уведомления",
|
contentDescription = "Конференции",
|
||||||
showBadge = showNotificationBadge,
|
highlighted = activeTabKey == "Talk",
|
||||||
onClick = actions.onNotificationsClick,
|
onClick = actions.onConferencesClick,
|
||||||
)
|
|
||||||
F7BottomBarSlot.Settings -> F7BottomBarIconSlot(
|
|
||||||
iconUrl = "$base/themes/forbion/images/header/setting-menu-icon.svg",
|
|
||||||
contentDescription = "Настройки",
|
|
||||||
onClick = actions.onSettingsClick,
|
|
||||||
)
|
)
|
||||||
F7BottomBarSlot.Menu -> F7BottomBarIconSlot(
|
F7BottomBarSlot.Menu -> F7BottomBarIconSlot(
|
||||||
iconUrl = if (menuOpen) {
|
iconUrl = if (menuOpen) {
|
||||||
@@ -126,6 +123,7 @@ fun F7MobileBottomBar(
|
|||||||
},
|
},
|
||||||
contentDescription = "Меню",
|
contentDescription = "Меню",
|
||||||
highlighted = menuOpen,
|
highlighted = menuOpen,
|
||||||
|
showBadge = showNotificationBadge,
|
||||||
onClick = actions.onMenuClick,
|
onClick = actions.onMenuClick,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-8
@@ -48,6 +48,7 @@ import coil.compose.AsyncImage
|
|||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import ru.forbion.f7cloud.core.auth.AuthSession
|
import ru.forbion.f7cloud.core.auth.AuthSession
|
||||||
import ru.forbion.f7cloud.core.designsystem.F7Colors
|
import ru.forbion.f7cloud.core.designsystem.F7Colors
|
||||||
|
import ru.forbion.f7cloud.core.designsystem.F7CreateButton
|
||||||
import ru.forbion.f7cloud.core.designsystem.F7ModuleScreen
|
import ru.forbion.f7cloud.core.designsystem.F7ModuleScreen
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -76,11 +77,23 @@ fun ContactsScreen(
|
|||||||
loading = state.loading && state.contacts.isEmpty(),
|
loading = state.loading && state.contacts.isEmpty(),
|
||||||
error = state.error,
|
error = state.error,
|
||||||
) {
|
) {
|
||||||
ContactsSearchBar(
|
// Поиск + кнопка создания контакта (создание раньше вызывалось из нижней
|
||||||
serverUrl = session.serverUrl,
|
// панели, теперь — из шапки, панель стала навигационной)
|
||||||
query = state.searchQuery,
|
Row(
|
||||||
onQueryChange = vm::setSearchQuery,
|
modifier = Modifier
|
||||||
)
|
.fillMaxWidth()
|
||||||
|
.padding(bottom = 8.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
ContactsSearchBar(
|
||||||
|
serverUrl = session.serverUrl,
|
||||||
|
query = state.searchQuery,
|
||||||
|
onQueryChange = vm::setSearchQuery,
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
)
|
||||||
|
F7CreateButton(onClick = vm::openAddSheet, size = 40.dp)
|
||||||
|
}
|
||||||
if (state.syncing && state.contacts.isNotEmpty()) {
|
if (state.syncing && state.contacts.isNotEmpty()) {
|
||||||
Text(
|
Text(
|
||||||
"Обновление…",
|
"Обновление…",
|
||||||
@@ -297,12 +310,11 @@ private fun ContactsSearchBar(
|
|||||||
serverUrl: String,
|
serverUrl: String,
|
||||||
query: String,
|
query: String,
|
||||||
onQueryChange: (String) -> Unit,
|
onQueryChange: (String) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val base = serverUrl.trimEnd('/')
|
val base = serverUrl.trimEnd('/')
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(bottom = 8.dp)
|
|
||||||
.height(40.dp)
|
.height(40.dp)
|
||||||
.shadow(2.dp, RoundedCornerShape(100.dp), spotColor = Color(0xFFCBCBCB))
|
.shadow(2.dp, RoundedCornerShape(100.dp), spotColor = Color(0xFFCBCBCB))
|
||||||
.clip(RoundedCornerShape(100.dp))
|
.clip(RoundedCornerShape(100.dp))
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
|||||||
import ru.forbion.f7cloud.core.auth.AuthSession
|
import ru.forbion.f7cloud.core.auth.AuthSession
|
||||||
import ru.forbion.f7cloud.core.designsystem.F7AlertDialog
|
import ru.forbion.f7cloud.core.designsystem.F7AlertDialog
|
||||||
import ru.forbion.f7cloud.core.designsystem.F7Colors
|
import ru.forbion.f7cloud.core.designsystem.F7Colors
|
||||||
|
import ru.forbion.f7cloud.core.designsystem.F7CreateButton
|
||||||
import ru.forbion.f7cloud.core.designsystem.F7ModuleScreen
|
import ru.forbion.f7cloud.core.designsystem.F7ModuleScreen
|
||||||
import ru.forbion.f7cloud.core.designsystem.F7OverlayDismissHandler
|
import ru.forbion.f7cloud.core.designsystem.F7OverlayDismissHandler
|
||||||
import ru.forbion.f7cloud.core.designsystem.F7OutlinedField
|
import ru.forbion.f7cloud.core.designsystem.F7OutlinedField
|
||||||
@@ -189,6 +190,9 @@ fun FilesScreen(
|
|||||||
FilesViewToggleButton(viewMode) {
|
FilesViewToggleButton(viewMode) {
|
||||||
viewMode = if (viewMode == FilesViewMode.List) FilesViewMode.Grid else FilesViewMode.List
|
viewMode = if (viewMode == FilesViewMode.List) FilesViewMode.Grid else FilesViewMode.List
|
||||||
}
|
}
|
||||||
|
// Создание (загрузка/новая папка) — раньше вызывалось из нижней панели,
|
||||||
|
// теперь кнопка в шапке (нижняя панель стала навигационной)
|
||||||
|
F7CreateButton(onClick = { showCreateMenu = true }, size = 40.dp)
|
||||||
}
|
}
|
||||||
// Хлебные крошки
|
// Хлебные крошки
|
||||||
val crumbSegments = state.currentPath.split("/").filter { it.isNotBlank() }
|
val crumbSegments = state.currentPath.split("/").filter { it.isNotBlank() }
|
||||||
|
|||||||
Reference in New Issue
Block a user