notifications P1: лента с действиями + «Скрыть все»

- NotificationsRepository: парс actions[] (label/link/type/primary),
  dismissAll (DELETE /api/v2/notifications), executeAction (GET/POST/PUT/DELETE по action.link)
- F7NotificationRow: FlowRow кнопок-действий (primary=заливка, прочие=контур)
- NotificationsSheet: заголовок «Уведомления» + «Скрыть все», проброс действий,
  оптимистичное скрытие после действия/скрыть-все

Контракты сверены с исходником notifications на forbion (deleteAllNotifications, actionToArray).
This commit is contained in:
b-dev-mobile
2026-07-13 10:11:32 +00:00
parent 5f81bd92f4
commit a30627a07d
3 changed files with 250 additions and 16 deletions
@@ -1,15 +1,20 @@
package ru.forbion.f7cloud.core.designsystem
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
@@ -27,11 +32,18 @@ import coil.compose.AsyncImage
import coil.request.ImageRequest
import okhttp3.Credentials
/** Кнопка-действие уведомления (label/link/type/primary) для рендера в строке. */
data class F7NotificationActionUi(
val label: String,
val primary: Boolean,
)
private fun TextStyle.doubled(): TextStyle = copy(
fontSize = fontSize * 2,
lineHeight = lineHeight * 2,
)
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun F7NotificationRow(
subject: String,
@@ -44,6 +56,8 @@ fun F7NotificationRow(
onDismiss: () -> Unit,
showDivider: Boolean,
modifier: Modifier = Modifier,
actions: List<F7NotificationActionUi> = emptyList(),
onActionClick: (Int) -> Unit = {},
) {
val context = LocalContext.current
Column(
@@ -134,8 +148,55 @@ fun F7NotificationRow(
.padding(start = 78.dp, top = 6.dp),
)
}
if (actions.isNotEmpty()) {
FlowRow(
modifier = Modifier
.fillMaxWidth()
.padding(start = 78.dp, top = 12.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
actions.forEachIndexed { index, action ->
NotificationActionButton(
label = action.label,
primary = action.primary,
onClick = { onActionClick(index) },
)
}
}
}
}
if (showDivider) {
HorizontalDivider(color = F7Colors.Border, thickness = 1.dp)
}
}
@Composable
private fun NotificationActionButton(
label: String,
primary: Boolean,
onClick: () -> Unit,
) {
val shape = RoundedCornerShape(22.dp)
val base = Modifier
.clip(shape)
.clickable(onClick = onClick)
val boxModifier = if (primary) {
base.background(F7Colors.Primary, shape)
} else {
base
.background(F7Colors.Surface, shape)
.border(BorderStroke(1.dp, F7Colors.Border), shape)
}
Box(
modifier = boxModifier.padding(horizontal = 20.dp, vertical = 10.dp),
contentAlignment = Alignment.Center,
) {
Text(
text = label,
style = MaterialTheme.typography.labelLarge.doubled(),
fontWeight = FontWeight.SemiBold,
color = if (primary) F7Colors.TextOnPrimary else F7Colors.TextPrimary,
)
}
}