desktop: better logic when switching chats (#2898)
* desktop: better logic when switching chats * auto scroll to top when selected chat changes * multiplatform: members page performance * preloading group members --------- Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
This commit is contained in:
parent
8dcb70c019
commit
782355ccb5
@ -38,6 +38,7 @@ import chat.simplex.common.views.chatlist.updateChatSettings
|
||||
import chat.simplex.res.MR
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.datetime.Clock
|
||||
|
||||
@Composable
|
||||
@ -51,15 +52,16 @@ fun ChatInfoView(
|
||||
close: () -> Unit,
|
||||
) {
|
||||
BackHandler(onBack = close)
|
||||
val chat = chatModel.chats.firstOrNull { it.id == chatModel.chatId.value }
|
||||
val currentUser = chatModel.currentUser.value
|
||||
val connStats = remember { mutableStateOf(connectionStats) }
|
||||
val contact = rememberUpdatedState(contact).value
|
||||
val chat = remember(contact.id) { chatModel.chats.firstOrNull { it.id == contact.id } }
|
||||
val currentUser = remember { chatModel.currentUser }.value
|
||||
val connStats = remember(contact.id, connectionStats) { mutableStateOf(connectionStats) }
|
||||
val developerTools = chatModel.controller.appPrefs.developerTools.get()
|
||||
if (chat != null && currentUser != null) {
|
||||
val contactNetworkStatus = remember(chatModel.networkStatuses.toMap()) {
|
||||
val contactNetworkStatus = remember(chatModel.networkStatuses.toMap(), contact) {
|
||||
mutableStateOf(chatModel.contactNetworkStatus(contact))
|
||||
}
|
||||
val sendReceipts = remember { mutableStateOf(SendReceipts.fromBool(contact.chatSettings.sendRcpts, currentUser.sendRcptsContacts)) }
|
||||
val sendReceipts = remember(contact.id) { mutableStateOf(SendReceipts.fromBool(contact.chatSettings.sendRcpts, currentUser.sendRcptsContacts)) }
|
||||
ChatInfoLayout(
|
||||
chat,
|
||||
contact,
|
||||
@ -239,7 +241,7 @@ fun ChatInfoLayout(
|
||||
currentUser: User,
|
||||
sendReceipts: State<SendReceipts>,
|
||||
setSendReceipts: (SendReceipts) -> Unit,
|
||||
connStats: MutableState<ConnectionStats?>,
|
||||
connStats: State<ConnectionStats?>,
|
||||
contactNetworkStatus: NetworkStatus,
|
||||
customUserProfile: Profile?,
|
||||
localAlias: String,
|
||||
@ -256,10 +258,15 @@ fun ChatInfoLayout(
|
||||
verifyClicked: () -> Unit,
|
||||
) {
|
||||
val cStats = connStats.value
|
||||
val scrollState = rememberScrollState()
|
||||
val scope = rememberCoroutineScope()
|
||||
KeyChangeEffect(chat.id) {
|
||||
scope.launch { scrollState.scrollTo(0) }
|
||||
}
|
||||
Column(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.verticalScroll(scrollState)
|
||||
) {
|
||||
Row(
|
||||
Modifier.fillMaxWidth(),
|
||||
|
@ -133,27 +133,45 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: () -> Unit) {
|
||||
chatModel.chatId.value = null
|
||||
},
|
||||
info = {
|
||||
if (ModalManager.end.hasModalsOpen()) {
|
||||
ModalManager.end.closeModals()
|
||||
return@ChatLayout
|
||||
}
|
||||
hideKeyboard(view)
|
||||
withApi {
|
||||
// The idea is to preload information before showing a modal because large groups can take time to load all members
|
||||
var preloadedContactInfo: Pair<ConnectionStats, Profile?>? = null
|
||||
var preloadedCode: String? = null
|
||||
var preloadedLink: Pair<String, GroupMemberRole>? = null
|
||||
if (chat.chatInfo is ChatInfo.Direct) {
|
||||
val contactInfo = chatModel.controller.apiContactInfo(chat.chatInfo.apiId)
|
||||
val (_, code) = chatModel.controller.apiGetContactCode(chat.chatInfo.apiId)
|
||||
ModalManager.end.closeModals()
|
||||
ModalManager.end.showModalCloseable(true) { close ->
|
||||
remember { derivedStateOf { (chatModel.getContactChat(chat.chatInfo.apiId)?.chatInfo as? ChatInfo.Direct)?.contact } }.value?.let { ct ->
|
||||
ChatInfoView(chatModel, ct, contactInfo?.first, contactInfo?.second, chat.chatInfo.localAlias, code, close)
|
||||
}
|
||||
}
|
||||
preloadedContactInfo = chatModel.controller.apiContactInfo(chat.chatInfo.apiId)
|
||||
preloadedCode = chatModel.controller.apiGetContactCode(chat.chatInfo.apiId).second
|
||||
} else if (chat.chatInfo is ChatInfo.Group) {
|
||||
setGroupMembers(chat.chatInfo.groupInfo, chatModel)
|
||||
val link = chatModel.controller.apiGetGroupLink(chat.chatInfo.groupInfo.groupId)
|
||||
var groupLink = link?.first
|
||||
var groupLinkMemberRole = link?.second
|
||||
ModalManager.end.closeModals()
|
||||
ModalManager.end.showModalCloseable(true) { close ->
|
||||
GroupChatInfoView(chatModel, groupLink, groupLinkMemberRole, {
|
||||
groupLink = it.first;
|
||||
groupLinkMemberRole = it.second
|
||||
preloadedLink = chatModel.controller.apiGetGroupLink(chat.chatInfo.groupInfo.groupId)
|
||||
}
|
||||
ModalManager.end.showModalCloseable(true) { close ->
|
||||
val chat = remember { activeChat }.value
|
||||
if (chat?.chatInfo is ChatInfo.Direct) {
|
||||
var contactInfo: Pair<ConnectionStats, Profile?>? by remember { mutableStateOf(preloadedContactInfo) }
|
||||
var code: String? by remember { mutableStateOf(preloadedCode) }
|
||||
KeyChangeEffect(chat.id, ChatModel.networkStatuses.toMap()) {
|
||||
contactInfo = chatModel.controller.apiContactInfo(chat.chatInfo.apiId)
|
||||
preloadedContactInfo = contactInfo
|
||||
code = chatModel.controller.apiGetContactCode(chat.chatInfo.apiId).second
|
||||
preloadedCode = code
|
||||
}
|
||||
ChatInfoView(chatModel, (chat.chatInfo as ChatInfo.Direct).contact, contactInfo?.first, contactInfo?.second, chat.chatInfo.localAlias, code, close)
|
||||
} else if (chat?.chatInfo is ChatInfo.Group) {
|
||||
var link: Pair<String, GroupMemberRole>? by remember(chat.id) { mutableStateOf(preloadedLink) }
|
||||
KeyChangeEffect(chat.id) {
|
||||
setGroupMembers((chat.chatInfo as ChatInfo.Group).groupInfo, chatModel)
|
||||
link = chatModel.controller.apiGetGroupLink(chat.chatInfo.groupInfo.groupId)
|
||||
preloadedLink = link
|
||||
}
|
||||
GroupChatInfoView(chatModel, link?.first, link?.second, {
|
||||
link = it
|
||||
preloadedLink = it
|
||||
}, close)
|
||||
}
|
||||
}
|
||||
|
@ -71,6 +71,9 @@ fun AddGroupMembersView(groupInfo: GroupInfo, creatingGroup: Boolean = false, ch
|
||||
removeContact = { contactId -> selectedContacts.removeIf { it == contactId } },
|
||||
close = close,
|
||||
)
|
||||
KeyChangeEffect(chatModel.chatId.value) {
|
||||
close()
|
||||
}
|
||||
}
|
||||
|
||||
fun getContactsToAdd(chatModel: ChatModel, search: String): List<Contact> {
|
||||
|
@ -8,8 +8,8 @@ import SectionSpacer
|
||||
import SectionTextFooter
|
||||
import SectionView
|
||||
import androidx.compose.desktop.ui.tooling.preview.Preview
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
@ -33,11 +33,12 @@ import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.views.chat.*
|
||||
import chat.simplex.common.views.chatlist.*
|
||||
import chat.simplex.res.MR
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
const val SMALL_GROUPS_RCPS_MEM_LIMIT: Int = 20
|
||||
|
||||
@Composable
|
||||
fun GroupChatInfoView(chatModel: ChatModel, groupLink: String?, groupLinkMemberRole: GroupMemberRole?, onGroupLinkUpdated: (Pair<String?, GroupMemberRole?>) -> Unit, close: () -> Unit) {
|
||||
fun GroupChatInfoView(chatModel: ChatModel, groupLink: String?, groupLinkMemberRole: GroupMemberRole?, onGroupLinkUpdated: (Pair<String, GroupMemberRole>?) -> Unit, close: () -> Unit) {
|
||||
BackHandler(onBack = close)
|
||||
val chat = chatModel.chats.firstOrNull { it.id == chatModel.chatId.value }
|
||||
val currentUser = chatModel.currentUser.value
|
||||
@ -177,79 +178,92 @@ fun GroupChatInfoLayout(
|
||||
leaveGroup: () -> Unit,
|
||||
manageGroupLink: () -> Unit,
|
||||
) {
|
||||
Column(
|
||||
val listState = rememberLazyListState()
|
||||
val scope = rememberCoroutineScope()
|
||||
KeyChangeEffect(chat.id) {
|
||||
scope.launch { listState.scrollToItem(0) }
|
||||
}
|
||||
val searchText = rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue()) }
|
||||
val filteredMembers = remember(members) { derivedStateOf { members.filter { it.chatViewName.lowercase().contains(searchText.value.text.trim()) } } }
|
||||
LazyColumn(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.fillMaxWidth(),
|
||||
state = listState
|
||||
) {
|
||||
Row(
|
||||
Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
GroupChatInfoHeader(chat.chatInfo)
|
||||
}
|
||||
SectionSpacer()
|
||||
item {
|
||||
Row(
|
||||
Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
GroupChatInfoHeader(chat.chatInfo)
|
||||
}
|
||||
SectionSpacer()
|
||||
|
||||
SectionView {
|
||||
if (groupInfo.canEdit) {
|
||||
EditGroupProfileButton(editGroupProfile)
|
||||
}
|
||||
if (groupInfo.groupProfile.description != null || groupInfo.canEdit) {
|
||||
AddOrEditWelcomeMessage(groupInfo.groupProfile.description, addOrEditWelcomeMessage)
|
||||
}
|
||||
GroupPreferencesButton(openPreferences)
|
||||
if (members.filter { it.memberCurrent }.size <= SMALL_GROUPS_RCPS_MEM_LIMIT) {
|
||||
SendReceiptsOption(currentUser, sendReceipts, setSendReceipts)
|
||||
} else {
|
||||
SendReceiptsOptionDisabled()
|
||||
}
|
||||
}
|
||||
SectionTextFooter(stringResource(MR.strings.only_group_owners_can_change_prefs))
|
||||
SectionDividerSpaced(maxTopPadding = true)
|
||||
|
||||
SectionView(title = String.format(generalGetString(MR.strings.group_info_section_title_num_members), members.count() + 1)) {
|
||||
if (groupInfo.canAddMembers) {
|
||||
if (groupLink == null) {
|
||||
CreateGroupLinkButton(manageGroupLink)
|
||||
SectionView {
|
||||
if (groupInfo.canEdit) {
|
||||
EditGroupProfileButton(editGroupProfile)
|
||||
}
|
||||
if (groupInfo.groupProfile.description != null || groupInfo.canEdit) {
|
||||
AddOrEditWelcomeMessage(groupInfo.groupProfile.description, addOrEditWelcomeMessage)
|
||||
}
|
||||
GroupPreferencesButton(openPreferences)
|
||||
if (members.filter { it.memberCurrent }.size <= SMALL_GROUPS_RCPS_MEM_LIMIT) {
|
||||
SendReceiptsOption(currentUser, sendReceipts, setSendReceipts)
|
||||
} else {
|
||||
GroupLinkButton(manageGroupLink)
|
||||
}
|
||||
|
||||
val onAddMembersClick = if (chat.chatInfo.incognito) ::cantInviteIncognitoAlert else addMembers
|
||||
val tint = if (chat.chatInfo.incognito) MaterialTheme.colors.secondary else MaterialTheme.colors.primary
|
||||
AddMembersButton(tint, onAddMembersClick)
|
||||
}
|
||||
val searchText = rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue()) }
|
||||
val filteredMembers = remember { derivedStateOf { members.filter { it.chatViewName.lowercase().contains(searchText.value.text.trim()) } } }
|
||||
if (members.size > 8) {
|
||||
SectionItemView(padding = PaddingValues(start = 14.dp, end = DEFAULT_PADDING_HALF)) {
|
||||
SearchRowView(searchText)
|
||||
SendReceiptsOptionDisabled()
|
||||
}
|
||||
}
|
||||
SectionItemView(minHeight = 54.dp) {
|
||||
MemberRow(groupInfo.membership, user = true)
|
||||
}
|
||||
MembersList(filteredMembers.value, showMemberInfo)
|
||||
}
|
||||
SectionDividerSpaced(maxTopPadding = true, maxBottomPadding = false)
|
||||
SectionView {
|
||||
ClearChatButton(clearChat)
|
||||
if (groupInfo.canDelete) {
|
||||
DeleteGroupButton(deleteGroup)
|
||||
}
|
||||
if (groupInfo.membership.memberCurrent) {
|
||||
LeaveGroupButton(leaveGroup)
|
||||
}
|
||||
}
|
||||
SectionTextFooter(stringResource(MR.strings.only_group_owners_can_change_prefs))
|
||||
SectionDividerSpaced(maxTopPadding = true)
|
||||
|
||||
if (developerTools) {
|
||||
SectionDividerSpaced()
|
||||
SectionView(title = stringResource(MR.strings.section_title_for_console)) {
|
||||
InfoRow(stringResource(MR.strings.info_row_local_name), groupInfo.localDisplayName)
|
||||
InfoRow(stringResource(MR.strings.info_row_database_id), groupInfo.apiId.toString())
|
||||
SectionView(title = String.format(generalGetString(MR.strings.group_info_section_title_num_members), members.count() + 1)) {
|
||||
if (groupInfo.canAddMembers) {
|
||||
if (groupLink == null) {
|
||||
CreateGroupLinkButton(manageGroupLink)
|
||||
} else {
|
||||
GroupLinkButton(manageGroupLink)
|
||||
}
|
||||
val onAddMembersClick = if (chat.chatInfo.incognito) ::cantInviteIncognitoAlert else addMembers
|
||||
val tint = if (chat.chatInfo.incognito) MaterialTheme.colors.secondary else MaterialTheme.colors.primary
|
||||
AddMembersButton(tint, onAddMembersClick)
|
||||
}
|
||||
if (members.size > 8) {
|
||||
SectionItemView(padding = PaddingValues(start = 14.dp, end = DEFAULT_PADDING_HALF)) {
|
||||
SearchRowView(searchText)
|
||||
}
|
||||
}
|
||||
SectionItemView(minHeight = 54.dp) {
|
||||
MemberRow(groupInfo.membership, user = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
SectionBottomSpacer()
|
||||
items(filteredMembers.value) { member ->
|
||||
Divider()
|
||||
SectionItemView({ showMemberInfo(member) }, minHeight = 54.dp) {
|
||||
MemberRow(member)
|
||||
}
|
||||
}
|
||||
item {
|
||||
SectionDividerSpaced(maxTopPadding = true, maxBottomPadding = false)
|
||||
SectionView {
|
||||
ClearChatButton(clearChat)
|
||||
if (groupInfo.canDelete) {
|
||||
DeleteGroupButton(deleteGroup)
|
||||
}
|
||||
if (groupInfo.membership.memberCurrent) {
|
||||
LeaveGroupButton(leaveGroup)
|
||||
}
|
||||
}
|
||||
|
||||
if (developerTools) {
|
||||
SectionDividerSpaced()
|
||||
SectionView(title = stringResource(MR.strings.section_title_for_console)) {
|
||||
InfoRow(stringResource(MR.strings.info_row_local_name), groupInfo.localDisplayName)
|
||||
InfoRow(stringResource(MR.strings.info_row_database_id), groupInfo.apiId.toString())
|
||||
}
|
||||
}
|
||||
SectionBottomSpacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -330,18 +344,6 @@ private fun AddMembersButton(tint: Color = MaterialTheme.colors.primary, onClick
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MembersList(members: List<GroupMember>, showMemberInfo: (GroupMember) -> Unit) {
|
||||
Column {
|
||||
members.forEachIndexed { index, member ->
|
||||
Divider()
|
||||
SectionItemView({ showMemberInfo(member) }, minHeight = 54.dp) {
|
||||
MemberRow(member)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MemberRow(member: GroupMember, user: Boolean = false) {
|
||||
Row(
|
||||
|
@ -23,7 +23,7 @@ import chat.simplex.common.views.newchat.QRCode
|
||||
import chat.simplex.res.MR
|
||||
|
||||
@Composable
|
||||
fun GroupLinkView(chatModel: ChatModel, groupInfo: GroupInfo, connReqContact: String?, memberRole: GroupMemberRole?, onGroupLinkUpdated: (Pair<String?, GroupMemberRole?>) -> Unit) {
|
||||
fun GroupLinkView(chatModel: ChatModel, groupInfo: GroupInfo, connReqContact: String?, memberRole: GroupMemberRole?, onGroupLinkUpdated: (Pair<String, GroupMemberRole>?) -> Unit) {
|
||||
var groupLink by rememberSaveable { mutableStateOf(connReqContact) }
|
||||
val groupLinkMemberRole = rememberSaveable { mutableStateOf(memberRole) }
|
||||
var creatingLink by rememberSaveable { mutableStateOf(false) }
|
||||
@ -34,7 +34,7 @@ fun GroupLinkView(chatModel: ChatModel, groupInfo: GroupInfo, connReqContact: St
|
||||
if (link != null) {
|
||||
groupLink = link.first
|
||||
groupLinkMemberRole.value = link.second
|
||||
onGroupLinkUpdated(groupLink to groupLinkMemberRole.value)
|
||||
onGroupLinkUpdated(link)
|
||||
}
|
||||
creatingLink = false
|
||||
}
|
||||
@ -60,7 +60,7 @@ fun GroupLinkView(chatModel: ChatModel, groupInfo: GroupInfo, connReqContact: St
|
||||
if (link != null) {
|
||||
groupLink = link.first
|
||||
groupLinkMemberRole.value = link.second
|
||||
onGroupLinkUpdated(groupLink to groupLinkMemberRole.value)
|
||||
onGroupLinkUpdated(link)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -75,7 +75,7 @@ fun GroupLinkView(chatModel: ChatModel, groupInfo: GroupInfo, connReqContact: St
|
||||
val r = chatModel.controller.apiDeleteGroupLink(groupInfo.groupId)
|
||||
if (r) {
|
||||
groupLink = null
|
||||
onGroupLinkUpdated(null to null)
|
||||
onGroupLinkUpdated(null)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -368,7 +368,12 @@ fun ContactConnectionMenuItems(chatInfo: ChatInfo.ContactConnection, chatModel:
|
||||
stringResource(MR.strings.delete_verb),
|
||||
painterResource(MR.images.ic_delete),
|
||||
onClick = {
|
||||
deleteContactConnectionAlert(chatInfo.contactConnection, chatModel) {}
|
||||
deleteContactConnectionAlert(chatInfo.contactConnection, chatModel) {
|
||||
if (chatModel.chatId.value == null) {
|
||||
ModalManager.center.closeModals()
|
||||
ModalManager.end.closeModals()
|
||||
}
|
||||
}
|
||||
showMenu.value = false
|
||||
},
|
||||
color = Color.Red
|
||||
|
@ -61,6 +61,13 @@ fun ChatListView(chatModel: ChatModel, settingsState: SettingsViewState, setPerf
|
||||
connectIfOpenedViaUri(url, chatModel)
|
||||
}
|
||||
}
|
||||
if (appPlatform.isDesktop) {
|
||||
KeyChangeEffect(chatModel.chatId.value) {
|
||||
if (chatModel.chatId.value != null) {
|
||||
ModalManager.end.closeModalsExceptFirst()
|
||||
}
|
||||
}
|
||||
}
|
||||
val endPadding = if (appPlatform.isDesktop) 56.dp else 0.dp
|
||||
var searchInList by rememberSaveable { mutableStateOf("") }
|
||||
val scope = rememberCoroutineScope()
|
||||
|
@ -97,6 +97,12 @@ class ModalManager(private val placement: ModalPlacement? = null) {
|
||||
modalCount.value = 0
|
||||
}
|
||||
|
||||
fun closeModalsExceptFirst() {
|
||||
while (modalCount.value > 1) {
|
||||
closeModal()
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
@Composable
|
||||
fun showInView() {
|
||||
|
@ -329,3 +329,43 @@ fun DisposableEffectOnRotate(always: () -> Unit = {}, whenDispose: () -> Unit =
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the [block] only after initial value of the [key1] changes, not after initial launch
|
||||
* */
|
||||
@Composable
|
||||
@NonRestartableComposable
|
||||
fun KeyChangeEffect(
|
||||
key1: Any?,
|
||||
block: suspend CoroutineScope.() -> Unit
|
||||
) {
|
||||
val initialKey = remember { key1 }
|
||||
var anyChange by remember { mutableStateOf(false) }
|
||||
LaunchedEffect(key1) {
|
||||
if (anyChange || key1 != initialKey) {
|
||||
block()
|
||||
anyChange = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the [block] only after initial value of the [key1] or [key2] changes, not after initial launch
|
||||
* */
|
||||
@Composable
|
||||
@NonRestartableComposable
|
||||
fun KeyChangeEffect(
|
||||
key1: Any?,
|
||||
key2: Any?,
|
||||
block: suspend CoroutineScope.() -> Unit
|
||||
) {
|
||||
val initialKey = remember { key1 }
|
||||
val initialKey2 = remember { key2 }
|
||||
var anyChange by remember { mutableStateOf(false) }
|
||||
LaunchedEffect(key1) {
|
||||
if (anyChange || key1 != initialKey || key2 != initialKey2) {
|
||||
block()
|
||||
anyChange = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ actual fun screenOrientation(): ScreenOrientation = ScreenOrientation.UNDEFINED
|
||||
|
||||
@Composable // LALAL
|
||||
actual fun screenWidth(): Dp {
|
||||
return java.awt.Toolkit.getDefaultToolkit().screenSize.width.dp.also { println("LALAL $it") }
|
||||
return java.awt.Toolkit.getDefaultToolkit().screenSize.width.dp
|
||||
/*var width by remember { mutableStateOf(java.awt.Toolkit.getDefaultToolkit().screenSize.width.also { println("LALAL $it") }) }
|
||||
SideEffect {
|
||||
if (width != java.awt.Toolkit.getDefaultToolkit().screenSize.width)
|
||||
|
Loading…
Reference in New Issue
Block a user