3 changed files with 149 additions and 0 deletions
@ -0,0 +1,123 @@
|
||||
package com.utun.chat.ui.components |
||||
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult |
||||
import androidx.activity.result.contract.ActivityResultContracts |
||||
import androidx.compose.foundation.layout.* |
||||
import androidx.compose.material3.* |
||||
import androidx.compose.runtime.* |
||||
import androidx.compose.runtime.saveable.rememberSaveable |
||||
import androidx.compose.ui.Alignment |
||||
import androidx.compose.ui.Modifier |
||||
import androidx.compose.ui.res.stringResource |
||||
import androidx.compose.ui.unit.dp |
||||
import com.utun.chat.ChatApplication |
||||
import com.utun.chat.R |
||||
import com.utun.chat.data.* |
||||
import kotlinx.coroutines.launch |
||||
|
||||
/** Один диалог для меню списка и кнопки в шапке группового чата. */ |
||||
@Composable |
||||
fun GroupPropertiesDialog(channel: Channel, onDismiss: () -> Unit) { |
||||
val repository = ChatApplication.instance.avatars |
||||
val groups by repository.groups.collectAsState() |
||||
val edits by repository.edits.collectAsState() |
||||
val state = groups[channel.id] |
||||
val editState = edits["group:${channel.id}"] ?: AvatarEditState.IDLE |
||||
val working = editState == AvatarEditState.WORKING |
||||
var soundOn by rememberSaveable(channel.id) { mutableStateOf(!NotificationSound.isMuted(channel.id)) } |
||||
var pickedAccount by rememberSaveable { mutableStateOf(0L) } |
||||
var pickedGroup by rememberSaveable { mutableStateOf("") } |
||||
val scope = rememberCoroutineScope() |
||||
val picker = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri -> |
||||
if (uri != null) repository.editGroup(pickedGroup, uri, pickedAccount) |
||||
} |
||||
LaunchedEffect(channel.id) { repository.ensureGroup(channel.id, true) } |
||||
AlertDialog(onDismissRequest = onDismiss, |
||||
title = { Text(stringResource(R.string.group_properties)) }, |
||||
text = { |
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { |
||||
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(12.dp)) { |
||||
GroupAvatar(channel.id, channel.name, size = 64.dp) |
||||
Column { Text(channel.name, style = MaterialTheme.typography.titleMedium); Text(channel.id) } |
||||
} |
||||
if (state?.loading == true || working) LinearProgressIndicator(Modifier.fillMaxWidth()) |
||||
if (state?.error == true) { |
||||
Text(stringResource(R.string.avatar_load_failed), color = MaterialTheme.colorScheme.error) |
||||
TextButton(onClick = { repository.ensureGroup(channel.id, true) }) { Text(stringResource(R.string.avatar_retry)) } |
||||
} |
||||
if (state?.canEdit == true) { |
||||
Text(stringResource(R.string.group_avatar_limits), style = MaterialTheme.typography.bodySmall) |
||||
Row { |
||||
TextButton(enabled = !working, onClick = { |
||||
pickedAccount = repository.accountId; pickedGroup = channel.id; picker.launch("image/*") |
||||
}) { Text(stringResource(R.string.avatar_upload)) } |
||||
TextButton(enabled = !working && state.hasImage, onClick = { |
||||
repository.editGroup(channel.id, null, repository.accountId) |
||||
}) { Text(stringResource(R.string.avatar_remove)) } |
||||
} |
||||
} else Text(stringResource(R.string.group_avatar_key_required), style = MaterialTheme.typography.bodySmall) |
||||
if (editState == AvatarEditState.ERROR) |
||||
Text(stringResource(R.string.avatar_edit_failed), color = MaterialTheme.colorScheme.error) |
||||
Row(verticalAlignment = Alignment.CenterVertically) { |
||||
Text(stringResource(R.string.sound), modifier = Modifier.weight(1f)) |
||||
Switch(checked = soundOn, onCheckedChange = { |
||||
soundOn = it; NotificationSound.setMuted(channel.id, !it) |
||||
scope.launch { ChatApplication.instance.configProvider.setChannelMuted(channel.id, !it) } |
||||
}) |
||||
} |
||||
} |
||||
}, |
||||
confirmButton = { TextButton(onClick = onDismiss) { Text(stringResource(R.string.close)) } }) |
||||
} |
||||
|
||||
@Composable |
||||
fun ProfileAvatarControls() { |
||||
val repository = ChatApplication.instance.avatars |
||||
val members by repository.members.collectAsState() |
||||
val edits by repository.edits.collectAsState() |
||||
val servers = remember(members) { repository.storageServers() } |
||||
var selectedServer by rememberSaveable { mutableStateOf(0L) } |
||||
var menu by remember { mutableStateOf(false) } |
||||
var pickedAccount by rememberSaveable { mutableStateOf(0L) } |
||||
var pickedServer by rememberSaveable { mutableStateOf(0L) } |
||||
val state = edits["profile"] ?: AvatarEditState.IDLE |
||||
LaunchedEffect(servers) { |
||||
if (servers.none { it.nodeId == selectedServer }) selectedServer = servers.firstOrNull()?.nodeId ?: 0L |
||||
} |
||||
val picker = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri -> |
||||
if (uri != null) repository.editProfile(pickedServer, uri, pickedAccount) |
||||
} |
||||
Column(verticalArrangement = Arrangement.spacedBy(6.dp)) { |
||||
Text(stringResource(R.string.profile_avatar), style = MaterialTheme.typography.titleMedium) |
||||
UserAvatar(null, repository.accountId, "", size = 64.dp, profile = true) |
||||
Text(stringResource(R.string.profile_avatar_hint), style = MaterialTheme.typography.bodySmall) |
||||
if (servers.isEmpty()) Text(stringResource(R.string.avatar_no_storage), style = MaterialTheme.typography.bodySmall) |
||||
else Box { |
||||
TextButton(onClick = { menu = true }, enabled = state != AvatarEditState.WORKING) { |
||||
Text(stringResource(R.string.avatar_storage_server, servers.firstOrNull { it.nodeId == selectedServer }?.let { |
||||
"${it.name.ifBlank { AvatarProtocol.hex(it.nodeId) }} (${AvatarProtocol.hex(it.nodeId)})" |
||||
} ?: "…")) |
||||
} |
||||
DropdownMenu(expanded = menu, onDismissRequest = { menu = false }) { |
||||
servers.forEach { server -> |
||||
DropdownMenuItem(text = { Text("${server.name} (${AvatarProtocol.hex(server.nodeId)})") }, |
||||
onClick = { selectedServer = server.nodeId; menu = false }) |
||||
} |
||||
} |
||||
} |
||||
Row { |
||||
TextButton(enabled = selectedServer != 0L && state != AvatarEditState.WORKING, onClick = { |
||||
pickedAccount = repository.accountId; pickedServer = selectedServer; picker.launch("image/*") |
||||
}) { Text(stringResource(R.string.avatar_upload)) } |
||||
TextButton(enabled = repository.accountId != 0L && state != AvatarEditState.WORKING, onClick = { |
||||
repository.editProfile(0, null, repository.accountId) |
||||
}) { Text(stringResource(R.string.avatar_remove)) } |
||||
} |
||||
when (state) { |
||||
AvatarEditState.WORKING -> LinearProgressIndicator(Modifier.fillMaxWidth()) |
||||
AvatarEditState.ERROR -> Text(stringResource(R.string.avatar_edit_failed), color = MaterialTheme.colorScheme.error) |
||||
AvatarEditState.SAVED -> Text(stringResource(R.string.profile_avatar_saved), style = MaterialTheme.typography.bodySmall) |
||||
else -> Unit |
||||
} |
||||
} |
||||
} |
||||
Loading…
Reference in new issue