mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-08-28 16:15:03 +00:00
changes
This commit is contained in:
+3
@@ -81,6 +81,9 @@ actual class GlobalExceptionsHandler: Thread.UncaughtExceptionHandler {
|
||||
chatModel.chatId.value = null
|
||||
chatItems.clearAndNotify()
|
||||
}
|
||||
withChats {
|
||||
chatItems.clearAndNotify()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// ChatList, nothing to do. Maybe to show other view except ChatList
|
||||
|
||||
+14
-19
@@ -66,13 +66,12 @@ object ChatModel {
|
||||
val chatId = mutableStateOf<String?>(null)
|
||||
val chatsContext = ChatsContext(null)
|
||||
val reportsChatsContext = ChatsContext(MsgContentTag.Report)
|
||||
// declaration of chatsContext should be before any other variable that is taken from ChatsContext class and used in the model, otherwise, strange crash with NullPointerException for "this" parameter in random functions
|
||||
val chats: State<List<Chat>> = chatsContext.chats
|
||||
/** if you modify the items by adding/removing them, use helpers methods like [addAndNotify], [removeLastAndNotify], [removeAllAndNotify], [clearAndNotify] and so on.
|
||||
* If some helper is missing, create it. Notify is needed to track state of items that we added manually (not via api call). See [apiLoadMessages].
|
||||
* If you use api call to get the items, use just [add] instead of [addAndNotify].
|
||||
* Never modify underlying list directly because it produces unexpected results in ChatView's LazyColumn (setting by index is ok) */
|
||||
// declaration of chatsContext should be after any other variable that is directly attached to ChatsContext class, otherwise, strange crash with NullPointerException for "this" parameter in random functions
|
||||
val chatItems: State<SnapshotStateList<ChatItem>> = chatsContext.chatItems
|
||||
// rhId, chatId
|
||||
val deletedChats = mutableStateOf<List<Pair<Long?, String>>>(emptyList())
|
||||
val chatItemStatuses = mutableMapOf<Long, CIStatus>()
|
||||
@@ -173,7 +172,7 @@ object ChatModel {
|
||||
// return true if you handled the click
|
||||
var centerPanelBackgroundClickHandler: (() -> Boolean)? = null
|
||||
|
||||
fun chatItemsForContent(contentTag: MsgContentTag?): State<List<ChatItem>> = when(contentTag) {
|
||||
fun chatItemsForContent(contentTag: MsgContentTag?): State<SnapshotStateList<ChatItem>> = when(contentTag) {
|
||||
null -> chatsContext.chatItems
|
||||
MsgContentTag.Report -> reportsChatsContext.chatItems
|
||||
else -> TODO()
|
||||
@@ -619,9 +618,9 @@ object ChatModel {
|
||||
}
|
||||
}
|
||||
|
||||
val popChatCollector = PopChatCollector()
|
||||
val popChatCollector = PopChatCollector(contentTag)
|
||||
|
||||
class PopChatCollector {
|
||||
class PopChatCollector(contentTag: MsgContentTag?) {
|
||||
private val subject = MutableSharedFlow<Unit>()
|
||||
private var remoteHostId: Long? = null
|
||||
private val chatsToPop = mutableMapOf<ChatId, Instant>()
|
||||
@@ -631,7 +630,7 @@ object ChatModel {
|
||||
subject
|
||||
.throttleLatest(2000)
|
||||
.collect {
|
||||
withChats {
|
||||
withChats(contentTag) {
|
||||
chats.replaceAll(popCollectedChats())
|
||||
}
|
||||
}
|
||||
@@ -671,7 +670,7 @@ object ChatModel {
|
||||
|
||||
fun markChatItemsRead(remoteHostId: Long?, chatInfo: ChatInfo, itemIds: List<Long>? = null) {
|
||||
val cInfo = chatInfo
|
||||
val markedRead = markItemsReadInCurrentChat(chatInfo, itemIds)
|
||||
val markedRead = markItemsReadInCurrentChat(chatInfo, contentTag, itemIds)
|
||||
// update preview
|
||||
val chatIdx = getChatIndex(remoteHostId, cInfo.id)
|
||||
if (chatIdx >= 0) {
|
||||
@@ -806,7 +805,7 @@ object ChatModel {
|
||||
}
|
||||
|
||||
fun removeLiveDummy() {
|
||||
if (chatItems.value.lastOrNull()?.id == ChatItem.TEMP_LIVE_CHAT_ITEM_ID) {
|
||||
if (chatItemsForContent(null).value.lastOrNull()?.id == ChatItem.TEMP_LIVE_CHAT_ITEM_ID) {
|
||||
withApi {
|
||||
withChats {
|
||||
chatItems.removeLastAndNotify()
|
||||
@@ -815,11 +814,11 @@ object ChatModel {
|
||||
}
|
||||
}
|
||||
|
||||
private fun markItemsReadInCurrentChat(chatInfo: ChatInfo, itemIds: List<Long>? = null): Int {
|
||||
private fun markItemsReadInCurrentChat(chatInfo: ChatInfo, contentTag: MsgContentTag?, itemIds: List<Long>? = null): Int {
|
||||
val cInfo = chatInfo
|
||||
var markedRead = 0
|
||||
if (chatId.value == cInfo.id) {
|
||||
val items = chatItems.value
|
||||
val items = chatItemsForContent(contentTag).value
|
||||
var i = items.lastIndex
|
||||
val itemIdsFromRange = itemIds?.toMutableSet() ?: mutableSetOf()
|
||||
val markedReadIds = mutableSetOf<Long>()
|
||||
@@ -864,19 +863,17 @@ object ChatModel {
|
||||
}
|
||||
}
|
||||
|
||||
fun getChatItemIndexOrNull(cItem: ChatItem): Int? {
|
||||
val reversedChatItems = chatItems.asReversed()
|
||||
fun getChatItemIndexOrNull(cItem: ChatItem, reversedChatItems: List<ChatItem>): Int? {
|
||||
val index = reversedChatItems.indexOfFirst { it.id == cItem.id }
|
||||
return if (index != -1) index else null
|
||||
}
|
||||
|
||||
// this function analyses "connected" events and assumes that each member will be there only once
|
||||
fun getConnectedMemberNames(cItem: ChatItem): Pair<Int, List<String>> {
|
||||
fun getConnectedMemberNames(cItem: ChatItem, reversedChatItems: List<ChatItem>): Pair<Int, List<String>> {
|
||||
var count = 0
|
||||
val ns = mutableListOf<String>()
|
||||
var idx = getChatItemIndexOrNull(cItem)
|
||||
var idx = getChatItemIndexOrNull(cItem, reversedChatItems)
|
||||
if (cItem.mergeCategory != null && idx != null) {
|
||||
val reversedChatItems = chatItems.asReversed()
|
||||
while (idx < reversedChatItems.size) {
|
||||
val ci = reversedChatItems[idx]
|
||||
if (ci.mergeCategory != cItem.mergeCategory) break
|
||||
@@ -893,9 +890,8 @@ object ChatModel {
|
||||
|
||||
// returns the index of the first item in the same merged group (the first hidden item)
|
||||
// and the previous visible item with another merge category
|
||||
fun getPrevShownChatItem(ciIndex: Int?, ciCategory: CIMergeCategory?): Pair<Int?, ChatItem?> {
|
||||
fun getPrevShownChatItem(ciIndex: Int?, ciCategory: CIMergeCategory?, reversedChatItems: List<ChatItem>): Pair<Int?, ChatItem?> {
|
||||
var i = ciIndex ?: return null to null
|
||||
val reversedChatItems = chatItems.asReversed()
|
||||
val fst = reversedChatItems.lastIndex
|
||||
while (i < fst) {
|
||||
i++
|
||||
@@ -908,8 +904,7 @@ object ChatModel {
|
||||
}
|
||||
|
||||
// returns the previous member in the same merge group and the count of members in this group
|
||||
fun getPrevHiddenMember(member: GroupMember, range: IntRange): Pair<GroupMember?, Int> {
|
||||
val reversedChatItems = chatItems.asReversed()
|
||||
fun getPrevHiddenMember(member: GroupMember, range: IntRange, reversedChatItems: List<ChatItem>): Pair<GroupMember?, Int> {
|
||||
var prevMember: GroupMember? = null
|
||||
val names: MutableSet<Long> = mutableSetOf()
|
||||
for (i in range) {
|
||||
|
||||
+3
@@ -3052,6 +3052,9 @@ object ChatController {
|
||||
withChats {
|
||||
updateGroup(rh, groupInfo)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
updateGroup(rh, groupInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ private fun sendCommand(chatModel: ChatModel, composeState: MutableState<Compose
|
||||
// TODO show active remote host in chat console?
|
||||
chatModel.controller.sendCmd(chatModel.remoteHostId(), CC.Console(s))
|
||||
// LALAL
|
||||
// for (i in 201..210) {
|
||||
// for (i in 221..240) {
|
||||
// chatModel.controller.sendCmd(chatModel.remoteHostId(), CC.Console("/_report #5 5965 reason=community $i"))
|
||||
// }
|
||||
composeState.value = ComposeState(useLinkPreviews = false)
|
||||
|
||||
+6
@@ -36,6 +36,7 @@ import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatController.appPrefs
|
||||
import chat.simplex.common.model.ChatModel.controller
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.model.ChatModel.withReportsChatsIfOpen
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.views.usersettings.*
|
||||
@@ -1250,6 +1251,11 @@ suspend fun save(applyToMode: DefaultThemeMode?, newTheme: ThemeModeOverride?, c
|
||||
updateChatInfo(chat.remoteHostId, chat.chatInfo.copy(groupInfo = chat.chatInfo.groupInfo.copy(uiThemes = changedThemes)))
|
||||
}
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
if (chat.chatInfo is ChatInfo.Group) {
|
||||
updateChatInfo(chat.remoteHostId, chat.chatInfo.copy(groupInfo = chat.chatInfo.groupInfo.copy(uiThemes = changedThemes)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-4
@@ -4,7 +4,6 @@ import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.runtime.*
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.platform.chatModel
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
@@ -240,14 +239,13 @@ data class ActiveChatState (
|
||||
}
|
||||
}
|
||||
|
||||
fun visibleItemIndexesNonReversed(mergedItems: State<MergedItems>, listState: LazyListState): IntRange {
|
||||
fun visibleItemIndexesNonReversed(mergedItems: State<MergedItems>, reversedItemsSize: Int, listState: LazyListState): IntRange {
|
||||
val zero = 0 .. 0
|
||||
if (listState.layoutInfo.totalItemsCount == 0) return zero
|
||||
val newest = mergedItems.value.items.getOrNull(listState.firstVisibleItemIndex)?.startIndexInReversedItems
|
||||
val oldest = mergedItems.value.items.getOrNull(listState.layoutInfo.visibleItemsInfo.last().index)?.lastIndexInReversed()
|
||||
if (newest == null || oldest == null) return zero
|
||||
val size = chatModel.chatItems.value.size
|
||||
val range = size - oldest .. size - newest
|
||||
val range = reversedItemsSize - oldest .. reversedItemsSize - newest
|
||||
if (range.first < 0 || range.last < 0) return zero
|
||||
|
||||
// visible items mapped to their underlying data structure which is chatModel.chatItems
|
||||
|
||||
+44
-13
@@ -77,7 +77,7 @@ fun ChatView(
|
||||
ModalManager.end.closeModals()
|
||||
}
|
||||
} else {
|
||||
val showArchivedReports = remember { mutableStateOf(false) }
|
||||
val showArchivedReports = remember { mutableStateOf(if (reportsView) reportsShowArchived else false) }
|
||||
val groupReports = remember { derivedStateOf {
|
||||
val reportsCount = if (activeChatInfo.value is ChatInfo.Group) activeChatStats.value?.reportsCount ?: 0 else 0
|
||||
GroupReports(reportsCount, reportsView, showArchivedReports.value) }
|
||||
@@ -125,7 +125,10 @@ fun ChatView(
|
||||
}
|
||||
}
|
||||
val clipboard = LocalClipboardManager.current
|
||||
CompositionLocalProvider(LocalAppBarHandler provides rememberAppBarHandler(chatInfo.id, keyboardCoversBar = false)) {
|
||||
CompositionLocalProvider(
|
||||
LocalAppBarHandler provides rememberAppBarHandler(chatInfo.id, keyboardCoversBar = false),
|
||||
LocalContentTag provides groupReports.value.contentTag
|
||||
) {
|
||||
when (chatInfo) {
|
||||
is ChatInfo.Direct, is ChatInfo.Group, is ChatInfo.Local -> {
|
||||
var groupMembersJob: Job = remember { Job() }
|
||||
@@ -511,6 +514,11 @@ fun ChatView(
|
||||
withChats {
|
||||
updateChatItem(cInfo, updatedCI)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
if (cItem.content.msgContent is MsgContent.MCReport) {
|
||||
updateChatItem(cInfo, updatedCI)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -528,7 +536,9 @@ fun ChatView(
|
||||
groupMembersJob.cancel()
|
||||
groupMembersJob = scope.launch(Dispatchers.Default) {
|
||||
var initialCiInfo = loadChatItemInfo() ?: return@launch
|
||||
ModalManager.end.closeModals()
|
||||
if (!ModalManager.end.hasModalOpen(ModalViewId.GROUP_REPORTS)) {
|
||||
ModalManager.end.closeModals()
|
||||
}
|
||||
ModalManager.end.showModalCloseable(endButtons = {
|
||||
ShareButton {
|
||||
clipboard.shareText(itemInfoShareText(chatModel, cItem, initialCiInfo, chatModel.controller.appPrefs.developerTools.get()))
|
||||
@@ -1078,6 +1088,10 @@ private fun ContactVerifiedShield() {
|
||||
Icon(painterResource(MR.images.ic_verified_user), null, Modifier.size(18.dp * fontSizeSqrtMultiplier).padding(end = 3.dp, top = 1.dp), tint = MaterialTheme.colors.secondary)
|
||||
}
|
||||
|
||||
/** Saves current scroll position when [GroupReports] are open and user opens [ChatItemInfoView], for example, and goes back */
|
||||
private var reportsListState: LazyListState? = null
|
||||
private var reportsShowArchived: Boolean = false
|
||||
|
||||
@Composable
|
||||
fun BoxScope.ChatItemsList(
|
||||
remoteHostId: Long?,
|
||||
@@ -1130,12 +1144,18 @@ fun BoxScope.ChatItemsList(
|
||||
)
|
||||
val listState = rememberUpdatedState(rememberSaveable(chatInfo.id, searchValueIsEmpty.value, saver = LazyListState.Saver) {
|
||||
val index = mergedItems.value.items.indexOfLast { it.hasUnread() }
|
||||
if (index <= 0) {
|
||||
val reportsState = reportsListState
|
||||
if (reportsState != null) {
|
||||
reportsListState = null
|
||||
reportsShowArchived = false
|
||||
reportsState
|
||||
} else if (index <= 0) {
|
||||
LazyListState(0, 0)
|
||||
} else {
|
||||
LazyListState(index + 1, -maxHeightForList.value)
|
||||
}
|
||||
})
|
||||
SaveReportsStateOnDispose(groupReports, listState)
|
||||
val maxHeight = remember { derivedStateOf { listState.value.layoutInfo.viewportEndOffset - topPaddingToContentPx.value } }
|
||||
val loadingMoreItems = remember { mutableStateOf(false) }
|
||||
val animatedScrollingInProgress = remember { mutableStateOf(false) }
|
||||
@@ -1146,7 +1166,7 @@ fun BoxScope.ChatItemsList(
|
||||
try {
|
||||
loadingMoreItems.value = true
|
||||
loadMessages(chatId, pagination) {
|
||||
visibleItemIndexesNonReversed(mergedItems, listState.value)
|
||||
visibleItemIndexesNonReversed(mergedItems, reversedChatItems.value.size, listState.value)
|
||||
}
|
||||
} finally {
|
||||
loadingMoreItems.value = false
|
||||
@@ -1304,7 +1324,7 @@ fun BoxScope.ChatItemsList(
|
||||
val rangeValue = range.value
|
||||
val (prevMember, memCount) =
|
||||
if (rangeValue != null) {
|
||||
chatModel.getPrevHiddenMember(member, rangeValue)
|
||||
chatModel.getPrevHiddenMember(member, rangeValue, reversedChatItems.value)
|
||||
} else {
|
||||
null to 1
|
||||
}
|
||||
@@ -1702,7 +1722,7 @@ private fun PreloadItemsBefore(
|
||||
var lastIndexToLoadFrom: Int? = findLastIndexToLoadFromInSplits(firstVisibleIndex, lastVisibleIndex, remaining, splits)
|
||||
val items = reversedChatItems.value
|
||||
if (splits.isEmpty() && items.isNotEmpty() && lastVisibleIndex > mergedItems.value.items.size - remaining && items.size >= ChatPagination.INITIAL_COUNT) {
|
||||
lastIndexToLoadFrom = items.lastIndex
|
||||
lastIndexToLoadFrom = 0
|
||||
}
|
||||
if (allowLoad.value && lastIndexToLoadFrom != null) {
|
||||
items.getOrNull(items.lastIndex - lastIndexToLoadFrom)?.id
|
||||
@@ -1910,6 +1930,16 @@ private fun FloatingDate(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SaveReportsStateOnDispose(groupReports: State<GroupReports>, listState: State<LazyListState>) {
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
reportsListState = if (groupReports.value.reportsView && ModalManager.end.hasModalOpen(ModalViewId.GROUP_REPORTS)) listState.value else null
|
||||
reportsShowArchived = if (groupReports.value.reportsView && ModalManager.end.hasModalOpen(ModalViewId.GROUP_REPORTS)) groupReports.value.showArchived else false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DownloadFilesButton(
|
||||
forwardConfirmation: ForwardConfirmation.FilesNotAccepted,
|
||||
@@ -2051,7 +2081,7 @@ private fun scrollToItem(
|
||||
val oldSize = reversedChatItems.value.size
|
||||
withContext(Dispatchers.Default) {
|
||||
loadMessages(chatInfo.value.id, pagination) {
|
||||
visibleItemIndexesNonReversed(mergedItems, listState.value)
|
||||
visibleItemIndexesNonReversed(mergedItems, reversedChatItems.value.size, listState.value)
|
||||
}
|
||||
}
|
||||
var repeatsLeft = 50
|
||||
@@ -2199,10 +2229,10 @@ private fun selectUnselectChatItem(
|
||||
) {
|
||||
val itemIds = mutableSetOf<Long>()
|
||||
if (!revealed.value) {
|
||||
val currIndex = chatModel.getChatItemIndexOrNull(ci)
|
||||
val currIndex = chatModel.getChatItemIndexOrNull(ci, reversedChatItems.value)
|
||||
val ciCategory = ci.mergeCategory
|
||||
if (currIndex != null && ciCategory != null) {
|
||||
val (prevHidden, _) = chatModel.getPrevShownChatItem(currIndex, ciCategory)
|
||||
val (prevHidden, _) = chatModel.getPrevShownChatItem(currIndex, ciCategory, reversedChatItems.value)
|
||||
val range = chatViewItemsRange(currIndex, prevHidden)
|
||||
if (range != null) {
|
||||
val reversed = reversedChatItems.value
|
||||
@@ -2251,7 +2281,9 @@ private fun deleteMessages(chatRh: Long?, chatInfo: ChatInfo, itemIds: List<Long
|
||||
for (di in deleted) {
|
||||
val toChatItem = di.toChatItem?.chatItem
|
||||
if (toChatItem != null) {
|
||||
upsertChatItem(chatRh, chatInfo, toChatItem)
|
||||
if (toChatItem.content.msgContent is MsgContent.MCReport) {
|
||||
upsertChatItem(chatRh, chatInfo, toChatItem)
|
||||
}
|
||||
} else {
|
||||
removeChatItem(chatRh, chatInfo, di.deletedChatItem.chatItem)
|
||||
}
|
||||
@@ -2291,7 +2323,6 @@ private fun markUnreadChatAsRead(chatId: String) {
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
replaceChat(chatRh, chat.id, chat.copy(chatStats = chat.chatStats.copy(unreadChat = false)))
|
||||
markChatTagRead(chat)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2470,7 +2501,7 @@ private fun ViewConfiguration.bigTouchSlop(slop: Float = 50f) = object: ViewConf
|
||||
private fun forwardContent(chatItemsIds: List<Long>, chatInfo: ChatInfo) {
|
||||
chatModel.chatId.value = null
|
||||
chatModel.sharedContent.value = SharedContent.Forward(
|
||||
chatModel.chatItems.value.filter { chatItemsIds.contains(it.id) },
|
||||
chatModel.chatItemsForContent(null).value.filter { chatItemsIds.contains(it.id) },
|
||||
chatInfo
|
||||
)
|
||||
}
|
||||
|
||||
+1
-1
@@ -798,7 +798,7 @@ fun ComposeView(
|
||||
|
||||
fun editPrevMessage() {
|
||||
if (composeState.value.contextItem != ComposeContextItem.NoContextItem || composeState.value.preview != ComposePreview.NoPreview) return
|
||||
val lastEditable = chatModel.chatItems.value.findLast { it.meta.editable }
|
||||
val lastEditable = chatModel.chatItemsForContent(null).value.findLast { it.meta.editable }
|
||||
if (lastEditable != null) {
|
||||
composeState.value = ComposeState(editingItem = lastEditable, useLinkPreviews = useLinkPreviews)
|
||||
}
|
||||
|
||||
+4
@@ -26,6 +26,7 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.model.ChatModel.withReportsChatsIfOpen
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.chat.ChatInfoToolbarTitle
|
||||
import chat.simplex.common.views.helpers.*
|
||||
@@ -64,6 +65,9 @@ fun AddGroupMembersView(rhId: Long?, groupInfo: GroupInfo, creatingGroup: Boolea
|
||||
withChats {
|
||||
upsertGroupMember(rhId, groupInfo, member)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
upsertGroupMember(rhId, groupInfo, member)
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
|
||||
+4
@@ -30,6 +30,7 @@ import androidx.compose.ui.unit.*
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatController.appPrefs
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.model.ChatModel.withReportsChatsIfOpen
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.views.usersettings.*
|
||||
@@ -198,6 +199,9 @@ private fun removeMemberAlert(rhId: Long?, groupInfo: GroupInfo, mem: GroupMembe
|
||||
withChats {
|
||||
upsertGroupMember(rhId, groupInfo, updatedMember)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
upsertGroupMember(rhId, groupInfo, updatedMember)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
+34
-9
@@ -28,6 +28,7 @@ import androidx.compose.ui.unit.*
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatModel.controller
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.model.ChatModel.withReportsChatsIfOpen
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.chat.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
@@ -65,6 +66,9 @@ fun GroupMemberInfoView(
|
||||
withChats {
|
||||
updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second)
|
||||
}
|
||||
close.invoke()
|
||||
}
|
||||
}
|
||||
@@ -142,6 +146,9 @@ fun GroupMemberInfoView(
|
||||
withChats {
|
||||
upsertGroupMember(rhId, groupInfo, mem)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
upsertGroupMember(rhId, groupInfo, mem)
|
||||
}
|
||||
}.onFailure {
|
||||
newRole.value = prevValue
|
||||
}
|
||||
@@ -157,6 +164,9 @@ fun GroupMemberInfoView(
|
||||
withChats {
|
||||
updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second)
|
||||
}
|
||||
close.invoke()
|
||||
}
|
||||
}
|
||||
@@ -171,6 +181,9 @@ fun GroupMemberInfoView(
|
||||
withChats {
|
||||
updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second)
|
||||
}
|
||||
close.invoke()
|
||||
}
|
||||
}
|
||||
@@ -188,6 +201,9 @@ fun GroupMemberInfoView(
|
||||
withChats {
|
||||
updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second)
|
||||
}
|
||||
close.invoke()
|
||||
}
|
||||
}
|
||||
@@ -203,16 +219,16 @@ fun GroupMemberInfoView(
|
||||
verify = { code ->
|
||||
chatModel.controller.apiVerifyGroupMember(rhId, mem.groupId, mem.groupMemberId, code)?.let { r ->
|
||||
val (verified, existingCode) = r
|
||||
withChats {
|
||||
upsertGroupMember(
|
||||
rhId,
|
||||
groupInfo,
|
||||
mem.copy(
|
||||
activeConn = mem.activeConn?.copy(
|
||||
connectionCode = if (verified) SecurityCode(existingCode, Clock.System.now()) else null
|
||||
)
|
||||
)
|
||||
val copy = mem.copy(
|
||||
activeConn = mem.activeConn?.copy(
|
||||
connectionCode = if (verified) SecurityCode(existingCode, Clock.System.now()) else null
|
||||
)
|
||||
)
|
||||
withChats {
|
||||
upsertGroupMember(rhId, groupInfo, copy)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
upsertGroupMember(rhId, groupInfo, copy)
|
||||
}
|
||||
r
|
||||
}
|
||||
@@ -246,6 +262,9 @@ fun removeMemberDialog(rhId: Long?, groupInfo: GroupInfo, member: GroupMember, c
|
||||
withChats {
|
||||
upsertGroupMember(rhId, groupInfo, removedMember)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
upsertGroupMember(rhId, groupInfo, removedMember)
|
||||
}
|
||||
}
|
||||
close?.invoke()
|
||||
}
|
||||
@@ -753,6 +772,9 @@ fun updateMemberSettings(rhId: Long?, gInfo: GroupInfo, member: GroupMember, mem
|
||||
withChats {
|
||||
upsertGroupMember(rhId, gInfo, member.copy(memberSettings = memberSettings))
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
upsertGroupMember(rhId, gInfo, member.copy(memberSettings = memberSettings))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -786,6 +808,9 @@ fun blockMemberForAll(rhId: Long?, gInfo: GroupInfo, member: GroupMember, blocke
|
||||
withChats {
|
||||
upsertGroupMember(rhId, gInfo, updatedMember)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
upsertGroupMember(rhId, gInfo, updatedMember)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -45,6 +45,9 @@ fun GroupPreferencesView(m: ChatModel, rhId: Long?, chatId: String, close: () ->
|
||||
updateGroup(rhId, g)
|
||||
currentPreferences = preferences
|
||||
}
|
||||
withChats {
|
||||
updateGroup(rhId, g)
|
||||
}
|
||||
}
|
||||
afterSave()
|
||||
}
|
||||
|
||||
+4
@@ -18,6 +18,7 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.model.ChatModel.withReportsChatsIfOpen
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.*
|
||||
@@ -42,6 +43,9 @@ fun GroupProfileView(rhId: Long?, groupInfo: GroupInfo, chatModel: ChatModel, cl
|
||||
withChats {
|
||||
updateGroup(rhId, gInfo)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
updateGroup(rhId, gInfo)
|
||||
}
|
||||
close.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -17,6 +17,8 @@ import dev.icerock.moko.resources.compose.stringResource
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
val LocalContentTag: ProvidableCompositionLocal<MsgContentTag?> = staticCompositionLocalOf { null }
|
||||
|
||||
data class GroupReports(
|
||||
val reportsCount: Int,
|
||||
val reportsView: Boolean,
|
||||
|
||||
+4
@@ -27,6 +27,7 @@ import chat.simplex.common.views.chat.item.MarkdownText
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.model.ChatModel
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.model.ChatModel.withReportsChatsIfOpen
|
||||
import chat.simplex.common.model.GroupInfo
|
||||
import chat.simplex.common.platform.ColumnWithScrollBar
|
||||
import chat.simplex.common.platform.chatJsonLength
|
||||
@@ -53,6 +54,9 @@ fun GroupWelcomeView(m: ChatModel, rhId: Long?, groupInfo: GroupInfo, close: ()
|
||||
withChats {
|
||||
updateGroup(rhId, res)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
updateGroup(rhId, res)
|
||||
}
|
||||
welcomeText.value = welcome ?: ""
|
||||
}
|
||||
afterSave()
|
||||
|
||||
+3
-2
@@ -13,6 +13,7 @@ import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatModel.getChatItemIndexOrNull
|
||||
import chat.simplex.common.platform.onRightClick
|
||||
import chat.simplex.common.views.chat.group.LocalContentTag
|
||||
|
||||
@Composable
|
||||
fun CIChatFeatureView(
|
||||
@@ -75,9 +76,9 @@ private fun mergedFeatures(chatItem: ChatItem, chatInfo: ChatInfo): List<Feature
|
||||
val m = ChatModel
|
||||
val fs: ArrayList<FeatureInfo> = arrayListOf()
|
||||
val icons: MutableSet<PainterBox> = mutableSetOf()
|
||||
var i = getChatItemIndexOrNull(chatItem)
|
||||
val reversedChatItems = m.chatItemsForContent(LocalContentTag.current).value.asReversed()
|
||||
var i = getChatItemIndexOrNull(chatItem, reversedChatItems)
|
||||
if (i != null) {
|
||||
val reversedChatItems = m.chatItems.asReversed()
|
||||
while (i < reversedChatItems.size) {
|
||||
val f = featureInfo(reversedChatItems[i], chatInfo) ?: break
|
||||
if (!icons.contains(f.icon)) {
|
||||
|
||||
+11
-8
@@ -28,6 +28,7 @@ import chat.simplex.common.model.ChatModel.currentUser
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.chat.*
|
||||
import chat.simplex.common.views.chat.group.LocalContentTag
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.res.MR
|
||||
import kotlinx.datetime.Clock
|
||||
@@ -500,8 +501,8 @@ fun ChatItemView(
|
||||
DeleteItemMenu()
|
||||
}
|
||||
|
||||
fun mergedGroupEventText(chatItem: ChatItem): String? {
|
||||
val (count, ns) = chatModel.getConnectedMemberNames(chatItem)
|
||||
fun mergedGroupEventText(chatItem: ChatItem, reversedChatItems: List<ChatItem>): String? {
|
||||
val (count, ns) = chatModel.getConnectedMemberNames(chatItem, reversedChatItems)
|
||||
val members = when {
|
||||
ns.size == 1 -> String.format(generalGetString(MR.strings.rcv_group_event_1_member_connected), ns[0])
|
||||
ns.size == 2 -> String.format(generalGetString(MR.strings.rcv_group_event_2_members_connected), ns[0], ns[1])
|
||||
@@ -520,9 +521,9 @@ fun ChatItemView(
|
||||
}
|
||||
}
|
||||
|
||||
fun eventItemViewText(): AnnotatedString {
|
||||
fun eventItemViewText(reversedChatItems: List<ChatItem>): AnnotatedString {
|
||||
val memberDisplayName = cItem.memberDisplayName
|
||||
val t = mergedGroupEventText(cItem)
|
||||
val t = mergedGroupEventText(cItem, reversedChatItems)
|
||||
return if (!revealed.value && t != null) {
|
||||
chatEventText(t, cItem.timestampText)
|
||||
} else if (memberDisplayName != null) {
|
||||
@@ -536,7 +537,8 @@ fun ChatItemView(
|
||||
}
|
||||
|
||||
@Composable fun EventItemView() {
|
||||
CIEventView(eventItemViewText())
|
||||
val reversedChatItems = chatModel.chatItemsForContent(LocalContentTag.current).value.asReversed()
|
||||
CIEventView(eventItemViewText(reversedChatItems))
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -729,20 +731,21 @@ fun DeleteItemAction(
|
||||
deleteMessage: (Long, CIDeleteMode) -> Unit,
|
||||
deleteMessages: (List<Long>) -> Unit,
|
||||
) {
|
||||
val contentTag = LocalContentTag.current
|
||||
ItemAction(
|
||||
stringResource(MR.strings.delete_verb),
|
||||
painterResource(MR.images.ic_delete),
|
||||
onClick = {
|
||||
showMenu.value = false
|
||||
if (!revealed.value) {
|
||||
val currIndex = chatModel.getChatItemIndexOrNull(cItem)
|
||||
val reversedChatItems = chatModel.chatItemsForContent(contentTag).value.asReversed()
|
||||
val currIndex = chatModel.getChatItemIndexOrNull(cItem, reversedChatItems)
|
||||
val ciCategory = cItem.mergeCategory
|
||||
if (currIndex != null && ciCategory != null) {
|
||||
val (prevHidden, _) = chatModel.getPrevShownChatItem(currIndex, ciCategory)
|
||||
val (prevHidden, _) = chatModel.getPrevShownChatItem(currIndex, ciCategory, reversedChatItems)
|
||||
val range = chatViewItemsRange(currIndex, prevHidden)
|
||||
if (range != null) {
|
||||
val itemIds: ArrayList<Long> = arrayListOf()
|
||||
val reversedChatItems = chatModel.chatItems.asReversed()
|
||||
for (i in range) {
|
||||
itemIds.add(reversedChatItems[i].id)
|
||||
}
|
||||
|
||||
+4
-2
@@ -12,8 +12,10 @@ import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatController.chatModel
|
||||
import chat.simplex.common.model.ChatModel.getChatItemIndexOrNull
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.chat.group.LocalContentTag
|
||||
import chat.simplex.common.views.helpers.generalGetString
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
@@ -42,10 +44,10 @@ fun MarkedDeletedItemView(ci: ChatItem, timedMessagesTTL: Int?, revealed: State<
|
||||
|
||||
@Composable
|
||||
private fun MergedMarkedDeletedText(chatItem: ChatItem, revealed: State<Boolean>) {
|
||||
var i = getChatItemIndexOrNull(chatItem)
|
||||
val reversedChatItems = chatModel.chatItemsForContent(LocalContentTag.current).value.asReversed()
|
||||
var i = getChatItemIndexOrNull(chatItem, reversedChatItems)
|
||||
val ciCategory = chatItem.mergeCategory
|
||||
val text = if (!revealed.value && ciCategory != null && i != null) {
|
||||
val reversedChatItems = ChatModel.chatItems.asReversed()
|
||||
var moderated = 0
|
||||
var blocked = 0
|
||||
var blockedByAdmin = 0
|
||||
|
||||
+19
-6
@@ -22,6 +22,7 @@ import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatModel.markChatTagRead
|
||||
import chat.simplex.common.model.ChatModel.updateChatTagRead
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.model.ChatModel.withReportsChatsIfOpen
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.chat.*
|
||||
@@ -252,7 +253,7 @@ suspend fun setGroupMembers(rhId: Long?, groupInfo: GroupInfo, chatModel: ChatMo
|
||||
fun ContactMenuItems(chat: Chat, contact: Contact, chatModel: ChatModel, showMenu: MutableState<Boolean>, showMarkRead: Boolean) {
|
||||
if (contact.activeConn != null) {
|
||||
if (showMarkRead) {
|
||||
MarkReadChatAction(chat, chatModel, showMenu)
|
||||
MarkReadChatAction(chat, showMenu)
|
||||
} else {
|
||||
MarkUnreadChatAction(chat, chatModel, showMenu)
|
||||
}
|
||||
@@ -292,7 +293,7 @@ fun GroupMenuItems(
|
||||
}
|
||||
else -> {
|
||||
if (showMarkRead) {
|
||||
MarkReadChatAction(chat, chatModel, showMenu)
|
||||
MarkReadChatAction(chat, showMenu)
|
||||
} else {
|
||||
MarkUnreadChatAction(chat, chatModel, showMenu)
|
||||
}
|
||||
@@ -313,7 +314,7 @@ fun GroupMenuItems(
|
||||
@Composable
|
||||
fun NoteFolderMenuItems(chat: Chat, showMenu: MutableState<Boolean>, showMarkRead: Boolean) {
|
||||
if (showMarkRead) {
|
||||
MarkReadChatAction(chat, chatModel, showMenu)
|
||||
MarkReadChatAction(chat, showMenu)
|
||||
} else {
|
||||
MarkUnreadChatAction(chat, chatModel, showMenu)
|
||||
}
|
||||
@@ -321,12 +322,12 @@ fun NoteFolderMenuItems(chat: Chat, showMenu: MutableState<Boolean>, showMarkRea
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MarkReadChatAction(chat: Chat, chatModel: ChatModel, showMenu: MutableState<Boolean>) {
|
||||
fun MarkReadChatAction(chat: Chat, showMenu: MutableState<Boolean>) {
|
||||
ItemAction(
|
||||
stringResource(MR.strings.mark_read),
|
||||
painterResource(MR.images.ic_check),
|
||||
onClick = {
|
||||
markChatRead(chat, chatModel)
|
||||
markChatRead(chat)
|
||||
ntfManager.cancelNotificationsForChat(chat.id)
|
||||
showMenu.value = false
|
||||
}
|
||||
@@ -563,13 +564,16 @@ private fun InvalidDataView() {
|
||||
}
|
||||
}
|
||||
|
||||
fun markChatRead(c: Chat, chatModel: ChatModel) {
|
||||
fun markChatRead(c: Chat) {
|
||||
var chat = c
|
||||
withApi {
|
||||
if (chat.chatStats.unreadCount > 0) {
|
||||
withChats {
|
||||
markChatItemsRead(chat.remoteHostId, chat.chatInfo)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
markChatItemsRead(chat.remoteHostId, chat.chatInfo)
|
||||
}
|
||||
chatModel.controller.apiChatRead(
|
||||
chat.remoteHostId,
|
||||
chat.chatInfo.chatType,
|
||||
@@ -589,6 +593,9 @@ fun markChatRead(c: Chat, chatModel: ChatModel) {
|
||||
replaceChat(chat.remoteHostId, chat.id, chat.copy(chatStats = chat.chatStats.copy(unreadChat = false)))
|
||||
markChatTagRead(chat)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
replaceChat(chat.remoteHostId, chat.id, chat.copy(chatStats = chat.chatStats.copy(unreadChat = false)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -611,6 +618,9 @@ fun markChatUnread(chat: Chat, chatModel: ChatModel) {
|
||||
replaceChat(chat.remoteHostId, chat.id, chat.copy(chatStats = chat.chatStats.copy(unreadChat = true)))
|
||||
updateChatTagRead(chat, wasUnread)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
replaceChat(chat.remoteHostId, chat.id, chat.copy(chatStats = chat.chatStats.copy(unreadChat = true)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -866,6 +876,9 @@ fun updateChatSettings(remoteHostId: Long?, chatInfo: ChatInfo, chatSettings: Ch
|
||||
withChats {
|
||||
updateChatInfo(remoteHostId, newChatInfo)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
updateChatInfo(remoteHostId, newChatInfo)
|
||||
}
|
||||
if (chatSettings.enableNtfs != MsgFilter.All) {
|
||||
ntfManager.cancelNotificationsForChat(chatInfo.id)
|
||||
}
|
||||
|
||||
+7
@@ -32,6 +32,7 @@ import chat.simplex.common.model.ChatController.apiDeleteChatTag
|
||||
import chat.simplex.common.model.ChatController.apiSetChatTags
|
||||
import chat.simplex.common.model.ChatController.appPrefs
|
||||
import chat.simplex.common.model.ChatModel.withChats
|
||||
import chat.simplex.common.model.ChatModel.withReportsChatsIfOpen
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.chat.item.ItemAction
|
||||
@@ -425,6 +426,9 @@ private fun setTag(rhId: Long?, tagId: Long?, chat: Chat, close: () -> Unit) {
|
||||
withChats {
|
||||
updateGroup(rhId, group)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
updateGroup(rhId, group)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {}
|
||||
@@ -462,6 +466,9 @@ private fun deleteTag(rhId: Long?, tag: ChatTag, saving: MutableState<Boolean>)
|
||||
withChats {
|
||||
updateGroup(rhId, group)
|
||||
}
|
||||
withReportsChatsIfOpen {
|
||||
updateGroup(rhId, group)
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user