Clarify avatar editor ownership and UI publication state

This commit is contained in:
evgeny committed 2026-10-28 11:36:13 +01:00
1 parent 977992e834
commit 81f36d15c8
3 files changed
+23 -3

No files matched your search

@@ -24,7 +24,10 @@ import com.utun.chat.ChatApplication
import com.utun.chat.R
import com.utun.chat.ui.screens.AvatarViewerScreen
/** Короткое нажатие открывает просмотр; удержание можно передать меню содержащей строки. */
/** Круглое превью участника из app-scoped AvatarRepository с инициалами до загрузки.
* group=null ищет ссылку owner среди доступных групп; revision запускает повторную проверку кеша.
* Короткое нажатие открывает галерею оригиналов, удержание передаётся onLongClick; enabled выключает оба жеста.
* profile=true показывает локальный профиль текущего аккаунта и отключает жесты независимо от enabled. */
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun UserAvatar(group: String?, owner: Long, name: String, modifier: Modifier = Modifier,
@@ -41,6 +44,7 @@ fun UserAvatar(group: String?, owner: Long, name: String, modifier: Modifier = M
if (viewing) AvatarViewerScreen(group,owner,name,onClose={ viewing=false })
}
/** Превью отдельного аватара группы, подгружаемое через ensureGroup; клики задаёт содержащий компонент. */
@Composable
fun GroupAvatar(group: String, name: String, modifier: Modifier = Modifier, size: Dp = 44.dp) {
val repository = ChatApplication.instance.avatars
@@ -49,6 +53,7 @@ fun GroupAvatar(group: String, name: String, modifier: Modifier = Modifier, size
AvatarImage(repository.groupBitmap(group), name, modifier, size)
}
/** Круглый crop Bitmap либо инициалы первых двух слов name; при пустом имени — "#". Bitmap не изменяется. */
@Composable
private fun AvatarImage(bitmap: Bitmap?, name: String, modifier: Modifier, size: Dp) {
Box(modifier.size(size).clip(CircleShape).background(MaterialTheme.colorScheme.secondaryContainer),
@@ -39,7 +39,14 @@ import kotlinx.coroutines.*
import java.io.File
import kotlin.math.roundToInt
/** Фото перемещается под круглой рамкой аватара. Подтверждение передаёт владение исходным файлом репозиторию. */
/**
* Редактор пользовательского аватара: копирует URI в cacheDir на IO worker, учитывает EXIF,
* принимает JPEG/PNG/WebP до 32 МиБ и 16×1024×1024 пикселей. Для экрана декодирует уменьшенную копию.
* Сдвиг и zoom 1..16 выбирают квадрат полного ориентированного оригинала; круг — только маска просмотра.
* onConfirm получает временный File и нормализованный AvatarCrop и принимает обязанность удалить файл
* (ProfileAvatarControls передаёт его в AvatarRepository.editProfile). После подтверждения диалог его не удаляет.
* onCancel должен убрать диалог из композиции: при dispose неподтверждённая копия удаляется на IO worker.
*/
@Composable
fun AvatarCropDialog(uri: Uri, onCancel: () -> Unit, onConfirm: (File, AvatarCrop) -> Unit) {
val context=LocalContext.current
@@ -15,7 +15,11 @@ import com.utun.chat.R
import com.utun.chat.data.*
import kotlinx.coroutines.launch
/** Один диалог для меню списка и кнопки в шапке группового чата. */
/** Свойства CHAT-группы: отдельный аватар группы и локальный звук уведомлений.
* При открытии обновляет снимок; canEdit показывает кнопки публикации, окончательный допуск проверяет core.
* Выбранный URI передаётся в editGroup без crop-диалога: репозиторий берёт центральный квадрат.
* Account/group фиксируются до picker, изменения применяются сразу и продолжаются после закрытия диалога.
*/
@Composable
fun GroupPropertiesDialog(channel: Channel, onDismiss: () -> Unit) {
val repository = ChatApplication.instance.avatars
@@ -71,6 +75,10 @@ fun GroupPropertiesDialog(channel: Channel, onDismiss: () -> Unit) {
confirmButton = { TextButton(onClick = onDismiss) { Text(stringResource(R.string.close)) } })
}
/** Настройки локального профиля: выбор подписанного хранителя, crop-редактор и снятие аватара.
* Список хранителей не требует online; account/server фиксируются до запуска picker.
* Временный файл редактора передаётся во владение editProfile. SAVED подтверждает локальное намерение;
* файлы и ссылки публикуются репозиторием отдельно в подходящих группах, без ожидания в этом UI. */
@Composable
fun ProfileAvatarControls() {
val repository = ChatApplication.instance.avatars