mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-05-30 09:06:27 +00:00
android dirty layout
This commit is contained in:
@@ -403,7 +403,7 @@ fun DesktopScreen(settingsState: SettingsViewState) {
|
||||
}
|
||||
VerticalDivider(Modifier.padding(start = DEFAULT_START_MODAL_WIDTH * fontSizeSqrtMultiplier))
|
||||
tryOrShowError("UserPicker", error = {}) {
|
||||
UserPicker(chatModel, userPickerState) {
|
||||
UserPicker(chatModel, userPickerState, scaffoldState.drawerState) {
|
||||
scope.launch { if (scaffoldState.drawerState.isOpen) scaffoldState.drawerState.close() else scaffoldState.drawerState.open() }
|
||||
userPickerState.value = AnimatedViewState.GONE
|
||||
}
|
||||
|
||||
+2
-1
@@ -252,7 +252,8 @@ fun ChatListView(chatModel: ChatModel, settingsState: SettingsViewState, setPerf
|
||||
UserPicker(
|
||||
chatModel = chatModel,
|
||||
userPickerState = userPickerState,
|
||||
contentAlignment = if (oneHandUI.value) Alignment.BottomStart else Alignment.TopStart
|
||||
contentAlignment = if (oneHandUI.value) Alignment.BottomStart else Alignment.TopStart,
|
||||
drawerState = scaffoldState.drawerState
|
||||
) {
|
||||
scope.launch { if (scaffoldState.drawerState.isOpen) scaffoldState.drawerState.close() else scaffoldState.drawerState.open() }
|
||||
userPickerState.value = AnimatedViewState.GONE
|
||||
|
||||
+1
@@ -100,6 +100,7 @@ fun ShareListView(chatModel: ChatModel, settingsState: SettingsViewState, stoppe
|
||||
showSettings = false,
|
||||
showCancel = true,
|
||||
contentAlignment = if (oneHandUI.value) Alignment.BottomStart else Alignment.TopStart,
|
||||
drawerState = scaffoldState.drawerState,
|
||||
cancelClicked = {
|
||||
chatModel.sharedContent.value = null
|
||||
userPickerState.value = AnimatedViewState.GONE
|
||||
|
||||
+201
-72
@@ -1,6 +1,8 @@
|
||||
package chat.simplex.common.views.chatlist
|
||||
|
||||
import SectionItemView
|
||||
import SectionView
|
||||
import TextIconSpaced
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
@@ -11,14 +13,16 @@ import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.*
|
||||
import androidx.compose.ui.draw.*
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import androidx.compose.ui.text.capitalize
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.intl.Locale
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.*
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatController.appPrefs
|
||||
@@ -29,17 +33,180 @@ import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.views.CreateProfile
|
||||
import chat.simplex.common.views.remote.*
|
||||
import chat.simplex.common.views.usersettings.doWithAuth
|
||||
import chat.simplex.common.views.usersettings.*
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@Composable
|
||||
private fun UserPickerOptionRow(icon: Painter, text: String, click: (() -> Unit)? = null, disabled: Boolean = false) {
|
||||
SectionItemView(click, disabled = disabled) {
|
||||
Icon(icon, text, tint = if (disabled) MaterialTheme.colors.secondary else MenuTextColor)
|
||||
TextIconSpaced()
|
||||
Text(text, color = if (disabled) MaterialTheme.colors.secondary else MenuTextColor)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UsersLayout(
|
||||
chatModel: ChatModel,
|
||||
onCurrentUserClick: () -> Unit,
|
||||
onUserClicked: (user: User) -> Unit,
|
||||
onShowAllProfilesClicked: () -> Unit
|
||||
) {
|
||||
val currentUser = remember { chatModel.currentUser }.value
|
||||
val stopped = chatModel.chatRunning.value == false
|
||||
|
||||
val users by remember {
|
||||
derivedStateOf {
|
||||
chatModel.users
|
||||
.filter { u -> !u.user.hidden && !u.user.activeUser }
|
||||
.take(3)
|
||||
}
|
||||
}
|
||||
|
||||
if (currentUser != null) {
|
||||
val mainColor = if (stopped) MaterialTheme.colors.secondary else MenuTextColor
|
||||
|
||||
SectionView(contentPadding = PaddingValues(DEFAULT_PADDING)) {
|
||||
Row {
|
||||
Column(modifier = Modifier.clickable(onClick = onCurrentUserClick, enabled = !stopped)) {
|
||||
ProfileImage(size = 54.dp * fontSizeSqrtMultiplier, image = currentUser.image)
|
||||
Text(
|
||||
currentUser.displayName,
|
||||
style = MaterialTheme.typography.caption,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = mainColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.weight(1f))
|
||||
users.forEach { u ->
|
||||
Column(
|
||||
Modifier.clickable(onClick = { onUserClicked(u.user) }, enabled = !stopped)
|
||||
) {
|
||||
ProfileImage(size = 44.dp * fontSizeSqrtMultiplier, image = u.user.image)
|
||||
}
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.clip(CircleShape)
|
||||
.border(
|
||||
BorderStroke(1.dp, mainColor),
|
||||
shape = CircleShape
|
||||
)
|
||||
.clickable(onClick = onShowAllProfilesClicked, enabled = !stopped)
|
||||
.size(44.dp * fontSizeSqrtMultiplier),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Icon(painterResource(MR.images.ic_more_horiz), stringResource(MR.strings.your_chat_profiles), tint = mainColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UserPickerUserSectionLayout (
|
||||
chatModel: ChatModel,
|
||||
userPickerState: MutableStateFlow<AnimatedViewState>,
|
||||
drawerState: DrawerState,
|
||||
remoteHosts: List<RemoteHostInfo>,
|
||||
showCustomModal: (@Composable ModalData.(ChatModel, () -> Unit) -> Unit) -> (() -> Unit),
|
||||
showModalWithSearch: (@Composable (ChatModel, MutableState<String>) -> Unit) -> Unit,
|
||||
withAuth: (title: String, desc: String, block: () -> Unit) -> Unit,
|
||||
) {
|
||||
val stopped = chatModel.chatRunning.value == false
|
||||
val profileHidden = rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
UsersLayout(
|
||||
chatModel = chatModel,
|
||||
onCurrentUserClick = showCustomModal { m, close -> UserProfileView(m, close) },
|
||||
onUserClicked = { user ->
|
||||
userPickerState.value = AnimatedViewState.HIDING
|
||||
if (!user.activeUser) {
|
||||
withBGApi {
|
||||
controller.showProgressIfNeeded {
|
||||
ModalManager.closeAllModalsEverywhere()
|
||||
chatModel.controller.changeActiveUser(user.remoteHostId, user.userId, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onShowAllProfilesClicked = {
|
||||
withAuth(
|
||||
generalGetString(MR.strings.auth_open_chat_profiles),
|
||||
generalGetString(MR.strings.auth_log_in_using_credential)
|
||||
) {
|
||||
showModalWithSearch { it, search -> UserProfilesView(it, search, profileHidden, drawerState) }
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
SectionView {
|
||||
UserPickerOptionRow(
|
||||
painterResource(MR.images.ic_qr_code),
|
||||
stringResource(MR.strings.your_simplex_contact_address),
|
||||
showCustomModal { it, close -> UserAddressView(it, shareViaProfile = it.currentUser.value!!.addressShared, close = close) }, disabled = stopped
|
||||
)
|
||||
UserPickerOptionRow(
|
||||
painterResource(MR.images.ic_toggle_on),
|
||||
stringResource(MR.strings.chat_preferences),
|
||||
click = if (stopped) null else ({
|
||||
showCustomModal { m, close ->
|
||||
PreferencesView(m, m.currentUser.value ?: return@showCustomModal, close)
|
||||
}()
|
||||
}),
|
||||
disabled = stopped
|
||||
)
|
||||
if (appPlatform.isAndroid) {
|
||||
UseFromDesktopPickerItem {
|
||||
ModalManager.start.showCustomModal { close ->
|
||||
ConnectDesktopView(close)
|
||||
}
|
||||
userPickerState.value = AnimatedViewState.GONE
|
||||
}
|
||||
} else {
|
||||
if (remoteHosts.isEmpty()) {
|
||||
UserPickerOptionRow(
|
||||
painterResource(MR.images.ic_smartphone_300),
|
||||
generalGetString(MR.strings.link_a_mobile),
|
||||
{
|
||||
ModalManager.start.showModal {
|
||||
ConnectMobileView()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
if (chatModel.desktopNoUserNoRemote) {
|
||||
UserPickerOptionRow(
|
||||
painterResource(MR.images.ic_manage_accounts),
|
||||
generalGetString(MR.strings.create_chat_profile),
|
||||
{
|
||||
doWithAuth(generalGetString(MR.strings.auth_open_chat_profiles), generalGetString(MR.strings.auth_log_in_using_credential)) {
|
||||
ModalManager.center.showModalCloseable { close ->
|
||||
LaunchedEffect(Unit) {
|
||||
userPickerState.value = AnimatedViewState.HIDING
|
||||
}
|
||||
CreateProfile(chat.simplex.common.platform.chatModel, close)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UserPicker(
|
||||
chatModel: ChatModel,
|
||||
userPickerState: MutableStateFlow<AnimatedViewState>,
|
||||
drawerState: DrawerState,
|
||||
showSettings: Boolean = true,
|
||||
contentAlignment: Alignment = Alignment.TopStart,
|
||||
showCancel: Boolean = false,
|
||||
@@ -155,7 +322,7 @@ fun UserPicker(
|
||||
) {
|
||||
Column(
|
||||
Modifier
|
||||
.height(404.dp)
|
||||
.height(IntrinsicSize.Min)
|
||||
.then(if (appPlatform.isAndroid) Modifier.fillMaxWidth() else Modifier.width(IntrinsicSize.Min))
|
||||
.background(MaterialTheme.colors.surface)
|
||||
) {
|
||||
@@ -182,7 +349,25 @@ fun UserPicker(
|
||||
}
|
||||
}
|
||||
|
||||
UsersView()
|
||||
UserPickerUserSectionLayout(
|
||||
chatModel = chatModel,
|
||||
userPickerState = userPickerState,
|
||||
drawerState = drawerState,
|
||||
remoteHosts = remoteHosts,
|
||||
showCustomModal = { modalView -> { ModalManager.start.showCustomModal { close -> modalView(chatModel, close) } } },
|
||||
withAuth = ::doWithAuth,
|
||||
showModalWithSearch = { modalView ->
|
||||
ModalManager.start.showCustomModal { close ->
|
||||
val search = rememberSaveable { mutableStateOf("") }
|
||||
ModalView(
|
||||
{ close() },
|
||||
endButtons = {
|
||||
SearchTextField(Modifier.fillMaxWidth(), placeholder = stringResource(MR.strings.search_verb), alwaysVisible = true) { search.value = it }
|
||||
},
|
||||
content = { modalView(chatModel, search) })
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
if (remoteHosts.isNotEmpty() && currentRemoteHost != null && chatModel.localUserCreated.value == true) {
|
||||
LocalDevicePickerItem(false) {
|
||||
@@ -204,40 +389,13 @@ fun UserPicker(
|
||||
Divider(Modifier.requiredHeight(1.dp))
|
||||
}
|
||||
}
|
||||
if (appPlatform.isAndroid) {
|
||||
UseFromDesktopPickerItem {
|
||||
ModalManager.start.showCustomModal { close ->
|
||||
ConnectDesktopView(close)
|
||||
}
|
||||
userPickerState.value = AnimatedViewState.GONE
|
||||
}
|
||||
Divider(Modifier.requiredHeight(1.dp))
|
||||
} else {
|
||||
if (remoteHosts.isEmpty()) {
|
||||
LinkAMobilePickerItem {
|
||||
ModalManager.start.showModal {
|
||||
ConnectMobileView()
|
||||
}
|
||||
userPickerState.value = AnimatedViewState.GONE
|
||||
}
|
||||
Divider(Modifier.requiredHeight(1.dp))
|
||||
}
|
||||
if (chatModel.desktopNoUserNoRemote) {
|
||||
CreateInitialProfile {
|
||||
doWithAuth(generalGetString(MR.strings.auth_open_chat_profiles), generalGetString(MR.strings.auth_log_in_using_credential)) {
|
||||
ModalManager.center.showModalCloseable { close ->
|
||||
LaunchedEffect(Unit) {
|
||||
userPickerState.value = AnimatedViewState.HIDING
|
||||
}
|
||||
CreateProfile(chat.simplex.common.platform.chatModel, close)
|
||||
}
|
||||
}
|
||||
}
|
||||
Divider(Modifier.requiredHeight(1.dp))
|
||||
}
|
||||
}
|
||||
if (showSettings) {
|
||||
SettingsPickerItem(settingsClicked)
|
||||
Divider(Modifier.padding(DEFAULT_PADDING))
|
||||
UserPickerOptionRow(
|
||||
painterResource(MR.images.ic_settings),
|
||||
generalGetString(MR.strings.settings_section_title_settings).lowercase().capitalize(Locale.current),
|
||||
settingsClicked
|
||||
)
|
||||
}
|
||||
if (showCancel) {
|
||||
CancelPickerItem(cancelClicked)
|
||||
@@ -406,42 +564,13 @@ fun LocalDeviceRow(active: Boolean) {
|
||||
|
||||
@Composable
|
||||
private fun UseFromDesktopPickerItem(onClick: () -> Unit) {
|
||||
SectionItemView(onClick, padding = PaddingValues(start = DEFAULT_PADDING + 7.dp, end = DEFAULT_PADDING), minHeight = 68.dp) {
|
||||
val text = generalGetString(MR.strings.settings_section_title_use_from_desktop).lowercase().capitalize(Locale.current)
|
||||
Icon(painterResource(MR.images.ic_desktop), text, Modifier.size(20.dp * fontSizeSqrtMultiplier), tint = MaterialTheme.colors.onBackground)
|
||||
Spacer(Modifier.width(DEFAULT_PADDING + 6.dp))
|
||||
Text(text, color = MenuTextColor)
|
||||
}
|
||||
}
|
||||
val text = generalGetString(MR.strings.settings_section_title_use_from_desktop).lowercase().capitalize(Locale.current)
|
||||
|
||||
@Composable
|
||||
private fun LinkAMobilePickerItem(onClick: () -> Unit) {
|
||||
SectionItemView(onClick, padding = PaddingValues(start = DEFAULT_PADDING + 7.dp, end = DEFAULT_PADDING), minHeight = 68.dp) {
|
||||
val text = generalGetString(MR.strings.link_a_mobile)
|
||||
Icon(painterResource(MR.images.ic_smartphone_300), text, Modifier.size(20.dp * fontSizeSqrtMultiplier), tint = MaterialTheme.colors.onBackground)
|
||||
Spacer(Modifier.width(DEFAULT_PADDING + 6.dp))
|
||||
Text(text, color = MenuTextColor)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CreateInitialProfile(onClick: () -> Unit) {
|
||||
SectionItemView(onClick, padding = PaddingValues(start = DEFAULT_PADDING + 7.dp, end = DEFAULT_PADDING), minHeight = 68.dp) {
|
||||
val text = generalGetString(MR.strings.create_chat_profile)
|
||||
Icon(painterResource(MR.images.ic_manage_accounts), text, Modifier.size(20.dp * fontSizeSqrtMultiplier), tint = MaterialTheme.colors.onBackground)
|
||||
Spacer(Modifier.width(DEFAULT_PADDING + 6.dp))
|
||||
Text(text, color = MenuTextColor)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SettingsPickerItem(onClick: () -> Unit) {
|
||||
SectionItemView(onClick, padding = PaddingValues(start = DEFAULT_PADDING + 7.dp, end = DEFAULT_PADDING), minHeight = 68.dp) {
|
||||
val text = generalGetString(MR.strings.settings_section_title_settings).lowercase().capitalize(Locale.current)
|
||||
Icon(painterResource(MR.images.ic_settings), text, Modifier.size(20.dp * fontSizeSqrtMultiplier), tint = MaterialTheme.colors.onBackground)
|
||||
Spacer(Modifier.width(DEFAULT_PADDING + 6.dp))
|
||||
Text(text, color = MenuTextColor)
|
||||
}
|
||||
UserPickerOptionRow(
|
||||
painterResource(MR.images.ic_desktop),
|
||||
text,
|
||||
onClick
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
||||
+2
@@ -34,6 +34,8 @@ fun UserProfileView(chatModel: ChatModel, close: () -> Unit) {
|
||||
KeyChangeEffect(u.value?.remoteHostId, u.value?.userId) {
|
||||
close()
|
||||
}
|
||||
|
||||
Log.d(TAG, "here: ${user?.userId}")
|
||||
if (user != null) {
|
||||
var profile by remember { mutableStateOf(user.profile.toProfile()) }
|
||||
UserProfileLayout(
|
||||
|
||||
Reference in New Issue
Block a user