mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2024-12-17 17:20:21 +01:00
Merge branch 'stable' into stable-android
This commit is contained in:
+5
-4
@@ -1,10 +1,11 @@
|
||||
package chat.simplex.common.platform
|
||||
|
||||
import android.util.Log
|
||||
import chat.simplex.common.model.ChatController.appPrefs
|
||||
|
||||
actual object Log {
|
||||
actual fun d(tag: String, text: String) = Log.d(tag, text).run{}
|
||||
actual fun e(tag: String, text: String) = Log.e(tag, text).run{}
|
||||
actual fun i(tag: String, text: String) = Log.i(tag, text).run{}
|
||||
actual fun w(tag: String, text: String) = Log.w(tag, text).run{}
|
||||
actual fun d(tag: String, text: String) { if (appPrefs.logLevel.get() <= LogLevel.DEBUG && appPrefs.developerTools.get()) Log.d(tag, text) }
|
||||
actual fun e(tag: String, text: String) { if (appPrefs.logLevel.get() <= LogLevel.ERROR || !appPrefs.developerTools.get()) Log.e(tag, text) }
|
||||
actual fun i(tag: String, text: String) { if (appPrefs.logLevel.get() <= LogLevel.INFO && appPrefs.developerTools.get()) Log.i(tag, text) }
|
||||
actual fun w(tag: String, text: String) { if (appPrefs.logLevel.get() <= LogLevel.WARNING || !appPrefs.developerTools.get()) Log.w(tag, text) }
|
||||
}
|
||||
|
||||
+2
@@ -132,6 +132,7 @@ class AppPreferences {
|
||||
val chatLastStart = mkDatePreference(SHARED_PREFS_CHAT_LAST_START, null)
|
||||
val chatStopped = mkBoolPreference(SHARED_PREFS_CHAT_STOPPED, false)
|
||||
val developerTools = mkBoolPreference(SHARED_PREFS_DEVELOPER_TOOLS, false)
|
||||
val logLevel = mkEnumPreference(SHARED_PREFS_LOG_LEVEL, LogLevel.WARNING) { LogLevel.entries.firstOrNull { it.name == this } }
|
||||
val showInternalErrors = mkBoolPreference(SHARED_PREFS_SHOW_INTERNAL_ERRORS, false)
|
||||
val showSlowApiCalls = mkBoolPreference(SHARED_PREFS_SHOW_SLOW_API_CALLS, false)
|
||||
val terminalAlwaysVisible = mkBoolPreference(SHARED_PREFS_TERMINAL_ALWAYS_VISIBLE, false)
|
||||
@@ -393,6 +394,7 @@ class AppPreferences {
|
||||
private const val SHARED_PREFS_CHAT_LAST_START = "ChatLastStart"
|
||||
private const val SHARED_PREFS_CHAT_STOPPED = "ChatStopped"
|
||||
private const val SHARED_PREFS_DEVELOPER_TOOLS = "DeveloperTools"
|
||||
private const val SHARED_PREFS_LOG_LEVEL = "LogLevel"
|
||||
private const val SHARED_PREFS_SHOW_INTERNAL_ERRORS = "ShowInternalErrors"
|
||||
private const val SHARED_PREFS_SHOW_SLOW_API_CALLS = "ShowSlowApiCalls"
|
||||
private const val SHARED_PREFS_TERMINAL_ALWAYS_VISIBLE = "TerminalAlwaysVisible"
|
||||
|
||||
@@ -2,6 +2,10 @@ package chat.simplex.common.platform
|
||||
|
||||
const val TAG = "SIMPLEX"
|
||||
|
||||
enum class LogLevel {
|
||||
DEBUG, INFO, WARNING, ERROR
|
||||
}
|
||||
|
||||
expect object Log {
|
||||
fun d(tag: String, text: String)
|
||||
fun e(tag: String, text: String)
|
||||
|
||||
+48
-20
@@ -131,26 +131,14 @@ fun ChatInfoView(
|
||||
},
|
||||
syncContactConnection = {
|
||||
withBGApi {
|
||||
val cStats = chatModel.controller.apiSyncContactRatchet(chatRh, contact.contactId, force = false)
|
||||
connStats.value = cStats
|
||||
if (cStats != null) {
|
||||
withChats {
|
||||
updateContactConnectionStats(chatRh, contact, cStats)
|
||||
}
|
||||
}
|
||||
syncContactConnection(chatRh, contact, connStats, force = false)
|
||||
close.invoke()
|
||||
}
|
||||
},
|
||||
syncContactConnectionForce = {
|
||||
showSyncConnectionForceAlert(syncConnectionForce = {
|
||||
withBGApi {
|
||||
val cStats = chatModel.controller.apiSyncContactRatchet(chatRh, contact.contactId, force = true)
|
||||
connStats.value = cStats
|
||||
if (cStats != null) {
|
||||
withChats {
|
||||
updateContactConnectionStats(chatRh, contact, cStats)
|
||||
}
|
||||
}
|
||||
syncContactConnection(chatRh, contact, connStats, force = true)
|
||||
close.invoke()
|
||||
}
|
||||
})
|
||||
@@ -189,6 +177,16 @@ fun ChatInfoView(
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun syncContactConnection(rhId: Long?, contact: Contact, connectionStats: MutableState<ConnectionStats?>, force: Boolean) {
|
||||
val cStats = chatModel.controller.apiSyncContactRatchet(rhId, contact.contactId, force = force)
|
||||
connectionStats.value = cStats
|
||||
if (cStats != null) {
|
||||
withChats {
|
||||
updateContactConnectionStats(rhId, contact, cStats)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class SendReceipts {
|
||||
object Yes: SendReceipts()
|
||||
object No: SendReceipts()
|
||||
@@ -505,7 +503,7 @@ fun ChatInfoLayout(
|
||||
currentUser: User,
|
||||
sendReceipts: State<SendReceipts>,
|
||||
setSendReceipts: (SendReceipts) -> Unit,
|
||||
connStats: State<ConnectionStats?>,
|
||||
connStats: MutableState<ConnectionStats?>,
|
||||
contactNetworkStatus: NetworkStatus,
|
||||
customUserProfile: Profile?,
|
||||
localAlias: String,
|
||||
@@ -553,8 +551,8 @@ fun ChatInfoLayout(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
SearchButton(modifier = Modifier.fillMaxWidth(0.25f), chat, contact, close, onSearchClicked)
|
||||
AudioCallButton(modifier = Modifier.fillMaxWidth(0.33f), chat, contact)
|
||||
VideoButton(modifier = Modifier.fillMaxWidth(0.5f), chat, contact)
|
||||
AudioCallButton(modifier = Modifier.fillMaxWidth(0.33f), chat, contact, connStats)
|
||||
VideoButton(modifier = Modifier.fillMaxWidth(0.5f), chat, contact, connStats)
|
||||
MuteButton(modifier = Modifier.fillMaxWidth(1f), chat, contact)
|
||||
}
|
||||
}
|
||||
@@ -825,12 +823,14 @@ fun MuteButton(
|
||||
fun AudioCallButton(
|
||||
modifier: Modifier,
|
||||
chat: Chat,
|
||||
contact: Contact
|
||||
contact: Contact,
|
||||
connectionStats: MutableState<ConnectionStats?>
|
||||
) {
|
||||
CallButton(
|
||||
modifier = modifier,
|
||||
chat,
|
||||
contact,
|
||||
connectionStats,
|
||||
icon = painterResource(MR.images.ic_call),
|
||||
title = generalGetString(MR.strings.info_view_call_button),
|
||||
mediaType = CallMediaType.Audio
|
||||
@@ -841,12 +841,14 @@ fun AudioCallButton(
|
||||
fun VideoButton(
|
||||
modifier: Modifier,
|
||||
chat: Chat,
|
||||
contact: Contact
|
||||
contact: Contact,
|
||||
connectionStats: MutableState<ConnectionStats?>
|
||||
) {
|
||||
CallButton(
|
||||
modifier = modifier,
|
||||
chat,
|
||||
contact,
|
||||
connectionStats,
|
||||
icon = painterResource(MR.images.ic_videocam),
|
||||
title = generalGetString(MR.strings.info_view_video_button),
|
||||
mediaType = CallMediaType.Video
|
||||
@@ -858,6 +860,7 @@ fun CallButton(
|
||||
modifier: Modifier,
|
||||
chat: Chat,
|
||||
contact: Contact,
|
||||
connectionStats: MutableState<ConnectionStats?>,
|
||||
icon: Painter,
|
||||
title: String,
|
||||
mediaType: CallMediaType
|
||||
@@ -879,7 +882,23 @@ fun CallButton(
|
||||
disabledLook = !canCall,
|
||||
onClick =
|
||||
when {
|
||||
canCall -> { { startChatCall(chat.remoteHostId, chat.chatInfo, mediaType) } }
|
||||
canCall -> { {
|
||||
val connStats = connectionStats.value
|
||||
if (connStats != null) {
|
||||
if (connStats.ratchetSyncState == RatchetSyncState.Ok) {
|
||||
startChatCall(chat.remoteHostId, chat.chatInfo, mediaType)
|
||||
} else if (connStats.ratchetSyncAllowed) {
|
||||
showFixConnectionAlert(syncConnection = {
|
||||
withBGApi { syncContactConnection(chat.remoteHostId, contact, connectionStats, force = false) }
|
||||
})
|
||||
} else {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(MR.strings.cant_call_contact_alert_title),
|
||||
generalGetString(MR.strings.encryption_renegotiation_in_progress)
|
||||
)
|
||||
}
|
||||
}
|
||||
} }
|
||||
contact.nextSendGrpInv -> { { showCantCallContactSendMessageAlert() } }
|
||||
!contact.active -> { { showCantCallContactDeletedAlert() } }
|
||||
!contact.ready -> { { showCantCallContactConnectingAlert() } }
|
||||
@@ -1265,6 +1284,15 @@ fun showSyncConnectionForceAlert(syncConnectionForce: () -> Unit) {
|
||||
)
|
||||
}
|
||||
|
||||
fun showFixConnectionAlert(syncConnection: () -> Unit) {
|
||||
AlertManager.shared.showAlertDialog(
|
||||
title = generalGetString(MR.strings.sync_connection_question),
|
||||
text = generalGetString(MR.strings.sync_connection_desc),
|
||||
confirmText = generalGetString(MR.strings.sync_connection_confirm),
|
||||
onConfirm = syncConnection,
|
||||
)
|
||||
}
|
||||
|
||||
fun queueInfoText(info: Pair<RcvMsgInfo?, ServerQueueInfo>): String {
|
||||
val (rcvMsgInfo, qInfo) = info
|
||||
val msgInfo: String = if (rcvMsgInfo != null) json.encodeToString(rcvMsgInfo) else generalGetString(MR.strings.message_queue_info_none)
|
||||
|
||||
+8
-5
@@ -296,6 +296,7 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
|
||||
}
|
||||
}
|
||||
SectionBottomSpacer()
|
||||
SectionBottomSpacer()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,6 +310,7 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
|
||||
QuotedMsgView(qi)
|
||||
}
|
||||
SectionBottomSpacer()
|
||||
SectionBottomSpacer()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,6 +326,7 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
|
||||
ForwardedFromView(forwardedFromItem)
|
||||
}
|
||||
SectionBottomSpacer()
|
||||
SectionBottomSpacer()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -395,6 +398,7 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
|
||||
}
|
||||
}
|
||||
SectionBottomSpacer()
|
||||
SectionBottomSpacer()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,12 +437,11 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
|
||||
|
||||
Column {
|
||||
if (numTabs() > 1) {
|
||||
Column(
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxHeight(),
|
||||
verticalArrangement = Arrangement.SpaceBetween
|
||||
.fillMaxHeight()
|
||||
) {
|
||||
Column(Modifier.weight(1f)) {
|
||||
Column {
|
||||
when (val sel = selection.value) {
|
||||
is CIInfoTab.Delivery -> {
|
||||
DeliveryTab(sel.memberDeliveryStatuses)
|
||||
@@ -479,7 +482,7 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
|
||||
}
|
||||
}
|
||||
val oneHandUI = remember { appPrefs.oneHandUI.state }
|
||||
Box(Modifier.offset(x = 0.dp, y = if (oneHandUI.value) -AppBarHeight * fontSizeSqrtMultiplier else 0.dp)) {
|
||||
Box(Modifier.align(Alignment.BottomCenter).navigationBarsPadding().offset(x = 0.dp, y = if (oneHandUI.value) -AppBarHeight * fontSizeSqrtMultiplier else 0.dp)) {
|
||||
TabRow(
|
||||
selectedTabIndex = availableTabs.indexOfFirst { it::class == selection.value::class },
|
||||
Modifier.height(AppBarHeight * fontSizeSqrtMultiplier),
|
||||
|
||||
+46
-15
@@ -665,13 +665,18 @@ fun ChatLayout(
|
||||
AdaptingBottomPaddingLayout(Modifier, CHAT_COMPOSE_LAYOUT_ID, composeViewHeight) {
|
||||
if (chatInfo != null) {
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
ChatItemsList(
|
||||
remoteHostId, chatInfo, unreadCount, composeState, composeViewHeight, searchValue,
|
||||
useLinkPreviews, linkMode, selectedChatItems, showMemberInfo, loadMessages, deleteMessage, deleteMessages,
|
||||
receiveFile, cancelFile, joinGroup, acceptCall, acceptFeature, openDirectChat, forwardItem,
|
||||
updateContactStats, updateMemberStats, syncContactConnection, syncMemberConnection, findModelChat, findModelMember,
|
||||
setReaction, showItemDetails, markItemsRead, markChatRead, remember { { onComposed(it) } }, developerTools, showViaProxy,
|
||||
)
|
||||
// disables scrolling to top of chat item on click inside the bubble
|
||||
CompositionLocalProvider(LocalBringIntoViewSpec provides object : BringIntoViewSpec {
|
||||
override fun calculateScrollDistance(offset: Float, size: Float, containerSize: Float): Float = 0f
|
||||
}) {
|
||||
ChatItemsList(
|
||||
remoteHostId, chatInfo, unreadCount, composeState, composeViewHeight, searchValue,
|
||||
useLinkPreviews, linkMode, selectedChatItems, showMemberInfo, showChatInfo = info, loadMessages, deleteMessage, deleteMessages,
|
||||
receiveFile, cancelFile, joinGroup, acceptCall, acceptFeature, openDirectChat, forwardItem,
|
||||
updateContactStats, updateMemberStats, syncContactConnection, syncMemberConnection, findModelChat, findModelMember,
|
||||
setReaction, showItemDetails, markItemsRead, markChatRead, remember { { onComposed(it) } }, developerTools, showViaProxy,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Box(
|
||||
@@ -939,6 +944,7 @@ fun BoxScope.ChatItemsList(
|
||||
linkMode: SimplexLinkMode,
|
||||
selectedChatItems: MutableState<Set<Long>?>,
|
||||
showMemberInfo: (GroupInfo, GroupMember) -> Unit,
|
||||
showChatInfo: () -> Unit,
|
||||
loadMessages: suspend (ChatId, ChatPagination, ActiveChatState, visibleItemIndexesNonReversed: () -> IntRange) -> Unit,
|
||||
deleteMessage: (Long, CIDeleteMode) -> Unit,
|
||||
deleteMessages: (List<Long>) -> Unit,
|
||||
@@ -984,6 +990,7 @@ fun BoxScope.ChatItemsList(
|
||||
})
|
||||
val maxHeight = remember { derivedStateOf { listState.value.layoutInfo.viewportEndOffset - topPaddingToContentPx.value } }
|
||||
val loadingMoreItems = remember { mutableStateOf(false) }
|
||||
val animatedScrollingInProgress = remember { mutableStateOf(false) }
|
||||
val ignoreLoadingRequests = remember(remoteHostId) { mutableSetOf<Long>() }
|
||||
if (!loadingMoreItems.value) {
|
||||
PreloadItems(chatInfo.id, if (searchValueIsEmpty.value) ignoreLoadingRequests else mutableSetOf(), mergedItems, listState, ChatPagination.UNTIL_PRELOAD_COUNT) { chatId, pagination ->
|
||||
@@ -1004,7 +1011,7 @@ fun BoxScope.ChatItemsList(
|
||||
val chatInfoUpdated = rememberUpdatedState(chatInfo)
|
||||
val highlightedItems = remember { mutableStateOf(setOf<Long>()) }
|
||||
val scope = rememberCoroutineScope()
|
||||
val scrollToItem: (Long) -> Unit = remember { scrollToItem(searchValue, loadingMoreItems, highlightedItems, chatInfoUpdated, maxHeight, scope, reversedChatItems, mergedItems, listState, loadMessages) }
|
||||
val scrollToItem: (Long) -> Unit = remember { scrollToItem(searchValue, loadingMoreItems, animatedScrollingInProgress, highlightedItems, chatInfoUpdated, maxHeight, scope, reversedChatItems, mergedItems, listState, loadMessages) }
|
||||
val scrollToQuotedItemFromItem: (Long) -> Unit = remember { findQuotedItemFromItem(remoteHostIdUpdated, chatInfoUpdated, scope, scrollToItem) }
|
||||
|
||||
LoadLastItems(loadingMoreItems, remoteHostId, chatInfo)
|
||||
@@ -1065,7 +1072,7 @@ fun BoxScope.ChatItemsList(
|
||||
highlightedItems.value = setOf()
|
||||
}
|
||||
}
|
||||
ChatItemView(remoteHostId, chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, linkMode = linkMode, revealed = revealed, highlighted = highlighted, range = range, fillMaxWidth = fillMaxWidth, selectedChatItems = selectedChatItems, selectChatItem = { selectUnselectChatItem(true, cItem, revealed, selectedChatItems) }, deleteMessage = deleteMessage, deleteMessages = deleteMessages, receiveFile = receiveFile, cancelFile = cancelFile, joinGroup = joinGroup, acceptCall = acceptCall, acceptFeature = acceptFeature, openDirectChat = openDirectChat, forwardItem = forwardItem, updateContactStats = updateContactStats, updateMemberStats = updateMemberStats, syncContactConnection = syncContactConnection, syncMemberConnection = syncMemberConnection, findModelChat = findModelChat, findModelMember = findModelMember, scrollToItem = scrollToItem, scrollToQuotedItemFromItem = scrollToQuotedItemFromItem, setReaction = setReaction, showItemDetails = showItemDetails, reveal = reveal, showMemberInfo = showMemberInfo, developerTools = developerTools, showViaProxy = showViaProxy, itemSeparation = itemSeparation, showTimestamp = itemSeparation.timestamp)
|
||||
ChatItemView(remoteHostId, chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, linkMode = linkMode, revealed = revealed, highlighted = highlighted, range = range, fillMaxWidth = fillMaxWidth, selectedChatItems = selectedChatItems, selectChatItem = { selectUnselectChatItem(true, cItem, revealed, selectedChatItems) }, deleteMessage = deleteMessage, deleteMessages = deleteMessages, receiveFile = receiveFile, cancelFile = cancelFile, joinGroup = joinGroup, acceptCall = acceptCall, acceptFeature = acceptFeature, openDirectChat = openDirectChat, forwardItem = forwardItem, updateContactStats = updateContactStats, updateMemberStats = updateMemberStats, syncContactConnection = syncContactConnection, syncMemberConnection = syncMemberConnection, findModelChat = findModelChat, findModelMember = findModelMember, scrollToItem = scrollToItem, scrollToQuotedItemFromItem = scrollToQuotedItemFromItem, setReaction = setReaction, showItemDetails = showItemDetails, reveal = reveal, showMemberInfo = showMemberInfo, showChatInfo = showChatInfo, developerTools = developerTools, showViaProxy = showViaProxy, itemSeparation = itemSeparation, showTimestamp = itemSeparation.timestamp)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1314,7 +1321,7 @@ fun BoxScope.ChatItemsList(
|
||||
}
|
||||
}
|
||||
}
|
||||
FloatingButtons(loadingMoreItems, mergedItems, unreadCount, maxHeight, composeViewHeight, searchValue, markChatRead, listState)
|
||||
FloatingButtons(loadingMoreItems, animatedScrollingInProgress, mergedItems, unreadCount, maxHeight, composeViewHeight, searchValue, markChatRead, listState)
|
||||
FloatingDate(Modifier.padding(top = 10.dp + topPaddingToContent(true)).align(Alignment.TopCenter), mergedItems, listState)
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
@@ -1323,6 +1330,15 @@ fun BoxScope.ChatItemsList(
|
||||
chatViewScrollState.value = it
|
||||
}
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
snapshotFlow { listState.value.isScrollInProgress }
|
||||
.filter { !it }
|
||||
.collect {
|
||||
if (animatedScrollingInProgress.value) {
|
||||
animatedScrollingInProgress.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -1400,6 +1416,7 @@ private fun NotifyChatListOnFinishingComposition(
|
||||
@Composable
|
||||
fun BoxScope.FloatingButtons(
|
||||
loadingMoreItems: MutableState<Boolean>,
|
||||
animatedScrollingInProgress: MutableState<Boolean>,
|
||||
mergedItems: State<MergedItems>,
|
||||
unreadCount: State<Int>,
|
||||
maxHeight: State<Int>,
|
||||
@@ -1439,8 +1456,14 @@ fun BoxScope.FloatingButtons(
|
||||
bottomUnreadCount,
|
||||
showBottomButtonWithCounter,
|
||||
showBottomButtonWithArrow,
|
||||
animatedScrollingInProgress,
|
||||
composeViewHeight,
|
||||
onClick = { scope.launch { tryBlockAndSetLoadingMore(loadingMoreItems) { listState.value.animateScrollToItem(0) } } }
|
||||
onClick = {
|
||||
scope.launch {
|
||||
animatedScrollingInProgress.value = true
|
||||
tryBlockAndSetLoadingMore(loadingMoreItems) { listState.value.animateScrollToItem(0) }
|
||||
}
|
||||
}
|
||||
)
|
||||
// Don't show top FAB if is in search
|
||||
if (searchValue.value.isNotEmpty()) return
|
||||
@@ -1451,11 +1474,15 @@ fun BoxScope.FloatingButtons(
|
||||
TopEndFloatingButton(
|
||||
Modifier.padding(end = DEFAULT_PADDING, top = 24.dp + topPaddingToContent(true)).align(Alignment.TopEnd),
|
||||
topUnreadCount,
|
||||
animatedScrollingInProgress,
|
||||
onClick = {
|
||||
val index = mergedItems.value.items.indexOfLast { it.hasUnread() }
|
||||
if (index != -1) {
|
||||
// scroll to the top unread item
|
||||
scope.launch { tryBlockAndSetLoadingMore(loadingMoreItems) { listState.value.animateScrollToItem(index + 1, -maxHeight.value) } }
|
||||
scope.launch {
|
||||
animatedScrollingInProgress.value = true
|
||||
tryBlockAndSetLoadingMore(loadingMoreItems) { listState.value.animateScrollToItem(index + 1, -maxHeight.value) }
|
||||
}
|
||||
}
|
||||
},
|
||||
onLongClick = { showDropDown.value = true }
|
||||
@@ -1595,10 +1622,11 @@ fun MemberImage(member: GroupMember) {
|
||||
private fun TopEndFloatingButton(
|
||||
modifier: Modifier = Modifier,
|
||||
unreadCount: State<Int>,
|
||||
animatedScrollingInProgress: State<Boolean>,
|
||||
onClick: () -> Unit,
|
||||
onLongClick: () -> Unit
|
||||
) {
|
||||
if (unreadCount.value > 0) {
|
||||
if (remember { derivedStateOf { unreadCount.value > 0 && !animatedScrollingInProgress.value } }.value) {
|
||||
val interactionSource = interactionSourceWithDetection(onClick, onLongClick)
|
||||
FloatingActionButton(
|
||||
{}, // no action here
|
||||
@@ -1839,6 +1867,7 @@ private fun lastFullyVisibleIemInListState(topPaddingToContentPx: State<Int>, de
|
||||
private fun scrollToItem(
|
||||
searchValue: State<String>,
|
||||
loadingMoreItems: MutableState<Boolean>,
|
||||
animatedScrollingInProgress: MutableState<Boolean>,
|
||||
highlightedItems: MutableState<Set<Long>>,
|
||||
chatInfo: State<ChatInfo>,
|
||||
maxHeight: State<Int>,
|
||||
@@ -1876,6 +1905,7 @@ private fun scrollToItem(
|
||||
highlightedItems.value = setOf(itemId)
|
||||
} else {
|
||||
withContext(scope.coroutineContext) {
|
||||
animatedScrollingInProgress.value = true
|
||||
listState.value.animateScrollToItem(min(reversedChatItems.value.lastIndex, index + 1), -maxHeight.value)
|
||||
highlightedItems.value = setOf(itemId)
|
||||
}
|
||||
@@ -1937,10 +1967,11 @@ private fun BoxScope.BottomEndFloatingButton(
|
||||
unreadCount: State<Int>,
|
||||
showButtonWithCounter: State<Boolean>,
|
||||
showButtonWithArrow: State<Boolean>,
|
||||
animatedScrollingInProgress: State<Boolean>,
|
||||
composeViewHeight: State<Dp>,
|
||||
onClick: () -> Unit
|
||||
) = when {
|
||||
showButtonWithCounter.value -> {
|
||||
showButtonWithCounter.value && !animatedScrollingInProgress.value -> {
|
||||
FloatingActionButton(
|
||||
onClick = onClick,
|
||||
elevation = FloatingActionButtonDefaults.elevation(0.dp, 0.dp, 0.dp, 0.dp),
|
||||
@@ -1954,7 +1985,7 @@ private fun BoxScope.BottomEndFloatingButton(
|
||||
)
|
||||
}
|
||||
}
|
||||
showButtonWithArrow.value -> {
|
||||
showButtonWithArrow.value && !animatedScrollingInProgress.value -> {
|
||||
FloatingActionButton(
|
||||
onClick = onClick,
|
||||
elevation = FloatingActionButtonDefaults.elevation(0.dp, 0.dp, 0.dp, 0.dp),
|
||||
|
||||
+49
-25
@@ -8,8 +8,6 @@ import SectionSpacer
|
||||
import SectionTextFooter
|
||||
import SectionView
|
||||
import androidx.compose.desktop.ui.tooling.preview.Preview
|
||||
import java.net.URI
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.text.InlineTextContent
|
||||
import androidx.compose.foundation.text.appendInlineContent
|
||||
@@ -58,6 +56,19 @@ fun GroupMemberInfoView(
|
||||
val developerTools = chatModel.controller.appPrefs.developerTools.get()
|
||||
var progressIndicator by remember { mutableStateOf(false) }
|
||||
|
||||
fun syncMemberConnection() {
|
||||
withBGApi {
|
||||
val r = chatModel.controller.apiSyncGroupMemberRatchet(rhId, groupInfo.apiId, member.groupMemberId, force = false)
|
||||
if (r != null) {
|
||||
connStats.value = r.second
|
||||
withChats {
|
||||
updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second)
|
||||
}
|
||||
close.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (chat != null) {
|
||||
val newRole = remember { mutableStateOf(member.memberRole) }
|
||||
GroupMemberInfoLayout(
|
||||
@@ -78,19 +89,30 @@ fun GroupMemberInfoView(
|
||||
}
|
||||
},
|
||||
createMemberContact = {
|
||||
withBGApi {
|
||||
progressIndicator = true
|
||||
val memberContact = chatModel.controller.apiCreateMemberContact(rhId, groupInfo.apiId, member.groupMemberId)
|
||||
if (memberContact != null) {
|
||||
val memberChat = Chat(remoteHostId = rhId, ChatInfo.Direct(memberContact), chatItems = arrayListOf())
|
||||
withChats {
|
||||
addChat(memberChat)
|
||||
openLoadedChat(memberChat)
|
||||
if (connectionStats != null) {
|
||||
if (connectionStats.ratchetSyncState == RatchetSyncState.Ok) {
|
||||
withBGApi {
|
||||
progressIndicator = true
|
||||
val memberContact = chatModel.controller.apiCreateMemberContact(rhId, groupInfo.apiId, member.groupMemberId)
|
||||
if (memberContact != null) {
|
||||
val memberChat = Chat(remoteHostId = rhId, ChatInfo.Direct(memberContact), chatItems = arrayListOf())
|
||||
withChats {
|
||||
addChat(memberChat)
|
||||
openLoadedChat(memberChat)
|
||||
}
|
||||
closeAll()
|
||||
chatModel.setContactNetworkStatus(memberContact, NetworkStatus.Connected())
|
||||
}
|
||||
progressIndicator = false
|
||||
}
|
||||
closeAll()
|
||||
chatModel.setContactNetworkStatus(memberContact, NetworkStatus.Connected())
|
||||
} else if (connectionStats.ratchetSyncAllowed) {
|
||||
showFixConnectionAlert(syncConnection = { syncMemberConnection() })
|
||||
} else {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(MR.strings.cant_send_message_to_member_alert_title),
|
||||
generalGetString(MR.strings.encryption_renegotiation_in_progress)
|
||||
)
|
||||
}
|
||||
progressIndicator = false
|
||||
}
|
||||
},
|
||||
connectViaAddress = { connReqUri ->
|
||||
@@ -149,16 +171,7 @@ fun GroupMemberInfoView(
|
||||
})
|
||||
},
|
||||
syncMemberConnection = {
|
||||
withBGApi {
|
||||
val r = chatModel.controller.apiSyncGroupMemberRatchet(rhId, groupInfo.apiId, member.groupMemberId, force = false)
|
||||
if (r != null) {
|
||||
connStats.value = r.second
|
||||
withChats {
|
||||
updateGroupMemberConnectionStats(rhId, groupInfo, r.first, r.second)
|
||||
}
|
||||
close.invoke()
|
||||
}
|
||||
}
|
||||
syncMemberConnection()
|
||||
},
|
||||
syncMemberConnectionForce = {
|
||||
showSyncConnectionForceAlert(syncConnectionForce = {
|
||||
@@ -335,9 +348,20 @@ fun GroupMemberInfoLayout(
|
||||
val knownChat = if (contactId != null) knownDirectChat(contactId) else null
|
||||
if (knownChat != null) {
|
||||
val (chat, contact) = knownChat
|
||||
val knownContactConnectionStats: MutableState<ConnectionStats?> = remember { mutableStateOf(null) }
|
||||
|
||||
LaunchedEffect(contact.contactId) {
|
||||
withBGApi {
|
||||
val contactInfo = chatModel.controller.apiContactInfo(chat.remoteHostId, chat.chatInfo.apiId)
|
||||
if (contactInfo != null) {
|
||||
knownContactConnectionStats.value = contactInfo.first
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OpenChatButton(modifier = Modifier.fillMaxWidth(0.33f), onClick = { openDirectChat(contact.contactId) })
|
||||
AudioCallButton(modifier = Modifier.fillMaxWidth(0.5f), chat, contact)
|
||||
VideoButton(modifier = Modifier.fillMaxWidth(1f), chat, contact)
|
||||
AudioCallButton(modifier = Modifier.fillMaxWidth(0.5f), chat, contact, knownContactConnectionStats)
|
||||
VideoButton(modifier = Modifier.fillMaxWidth(1f), chat, contact, knownContactConnectionStats)
|
||||
} else if (groupInfo.fullGroupPreferences.directMessages.on(groupInfo.membership)) {
|
||||
if (contactId != null) {
|
||||
OpenChatButton(modifier = Modifier.fillMaxWidth(0.33f), onClick = { openDirectChat(contactId) }) // legacy - only relevant for direct contacts created when joining group
|
||||
|
||||
+49
-19
@@ -24,12 +24,12 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.*
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatModel.controller
|
||||
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.helpers.*
|
||||
import chat.simplex.res.MR
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlin.math.*
|
||||
|
||||
@@ -51,6 +51,12 @@ fun chatEventText(eventText: String, ts: String): AnnotatedString =
|
||||
withStyle(chatEventStyle) { append("$eventText $ts") }
|
||||
}
|
||||
|
||||
data class ChatItemReactionMenuItem (
|
||||
val name: String,
|
||||
val image: String?,
|
||||
val onClick: (() -> Unit)?
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun ChatItemView(
|
||||
rhId: Long?,
|
||||
@@ -87,6 +93,7 @@ fun ChatItemView(
|
||||
showItemDetails: (ChatInfo, ChatItem) -> Unit,
|
||||
reveal: (Boolean) -> Unit,
|
||||
showMemberInfo: (GroupInfo, GroupMember) -> Unit,
|
||||
showChatInfo: () -> Unit,
|
||||
developerTools: Boolean,
|
||||
showViaProxy: Boolean,
|
||||
showTimestamp: Boolean,
|
||||
@@ -120,7 +127,7 @@ fun ChatItemView(
|
||||
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.chatItemOffset(cItem, itemSeparation.largeGap, inverted = true, revealed = true)) {
|
||||
cItem.reactions.forEach { r ->
|
||||
val showReactionMenu = remember { mutableStateOf(false) }
|
||||
val reactionMembers = remember { mutableStateOf(emptyList<MemberReaction>()) }
|
||||
val reactionMenuItems = remember { mutableStateOf(emptyList<ChatItemReactionMenuItem>()) }
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val enterInteraction = remember { HoverInteraction.Enter() }
|
||||
KeyChangeEffect(highlighted.value) {
|
||||
@@ -134,18 +141,39 @@ fun ChatItemView(
|
||||
var modifier = Modifier.padding(horizontal = 5.dp, vertical = 2.dp).clip(RoundedCornerShape(8.dp))
|
||||
if (cInfo.featureEnabled(ChatFeature.Reactions)) {
|
||||
fun showReactionsMenu() {
|
||||
if (cInfo is ChatInfo.Group) {
|
||||
withBGApi {
|
||||
try {
|
||||
val members = controller.apiGetReactionMembers(rhId, cInfo.groupInfo.groupId, cItem.id, r.reaction)
|
||||
if (members != null) {
|
||||
showReactionMenu.value = true
|
||||
reactionMembers.value = members
|
||||
when (cInfo) {
|
||||
is ChatInfo.Group -> {
|
||||
withBGApi {
|
||||
try {
|
||||
val members = controller.apiGetReactionMembers(rhId, cInfo.groupInfo.groupId, cItem.id, r.reaction)
|
||||
if (members != null) {
|
||||
showReactionMenu.value = true
|
||||
reactionMenuItems.value = members.map {
|
||||
val enabled = cInfo.groupInfo.membership.groupMemberId != it.groupMember.groupMemberId
|
||||
val click = if (enabled) ({ showMemberInfo(cInfo.groupInfo, it.groupMember) }) else null
|
||||
ChatItemReactionMenuItem(it.groupMember.displayName, it.groupMember.image, click)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.d(TAG, "chatItemView ChatItemReactions onLongClick: unexpected exception: ${e.stackTraceToString()}")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.d(TAG, "hatItemView ChatItemReactions onLongClick: unexpected exception: ${e.stackTraceToString()}")
|
||||
}
|
||||
}
|
||||
is ChatInfo.Direct -> {
|
||||
showReactionMenu.value = true
|
||||
val reactions = mutableListOf<ChatItemReactionMenuItem>()
|
||||
|
||||
if (!r.userReacted || r.totalReacted > 1) {
|
||||
val contact = cInfo.contact
|
||||
reactions.add(ChatItemReactionMenuItem(contact.displayName, contact.image, showChatInfo))
|
||||
}
|
||||
|
||||
if (r.userReacted) {
|
||||
reactions.add(ChatItemReactionMenuItem(generalGetString(MR.strings.sender_you_pronoun), currentUser.value?.image, null))
|
||||
}
|
||||
reactionMenuItems.value = reactions
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
modifier = modifier
|
||||
@@ -166,19 +194,19 @@ fun ChatItemView(
|
||||
Row(modifier.padding(2.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
ReactionIcon(r.reaction.text, fontSize = 12.sp)
|
||||
DefaultDropdownMenu(showMenu = showReactionMenu) {
|
||||
reactionMembers.value.forEach { m ->
|
||||
reactionMenuItems.value.forEach { m ->
|
||||
ItemAction(
|
||||
text = m.groupMember.displayName,
|
||||
composable = { ProfileImage(44.dp, m.groupMember.image) },
|
||||
text = m.name,
|
||||
composable = { ProfileImage(44.dp, m.image) },
|
||||
onClick = {
|
||||
if (cInfo is ChatInfo.Group && cInfo.groupInfo.membership.groupMemberId != m.groupMember.groupMemberId) {
|
||||
showMemberInfo(cInfo.groupInfo, m.groupMember)
|
||||
showReactionMenu.value = false
|
||||
} else {
|
||||
val click = m.onClick
|
||||
if (click != null) {
|
||||
click()
|
||||
showReactionMenu.value = false
|
||||
}
|
||||
},
|
||||
lineLimit = 1
|
||||
lineLimit = 1,
|
||||
color = if (m.onClick == null) MaterialTheme.colors.secondary else MenuTextColor
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1188,6 +1216,7 @@ fun PreviewChatItemView(
|
||||
showItemDetails = { _, _ -> },
|
||||
reveal = {},
|
||||
showMemberInfo = { _, _ ->},
|
||||
showChatInfo = {},
|
||||
developerTools = false,
|
||||
showViaProxy = false,
|
||||
showTimestamp = true,
|
||||
@@ -1233,6 +1262,7 @@ fun PreviewChatItemViewDeletedContent() {
|
||||
showItemDetails = { _, _ -> },
|
||||
reveal = {},
|
||||
showMemberInfo = { _, _ ->},
|
||||
showChatInfo = {},
|
||||
developerTools = false,
|
||||
showViaProxy = false,
|
||||
preview = true,
|
||||
|
||||
+14
-7
@@ -30,6 +30,7 @@ import kotlinx.datetime.*
|
||||
import java.io.*
|
||||
import java.net.URI
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.StandardCopyOption
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
@@ -44,11 +45,14 @@ fun DatabaseView() {
|
||||
val chatArchiveFile = remember { mutableStateOf<String?>(null) }
|
||||
val stopped = remember { m.chatRunning }.value == false
|
||||
val saveArchiveLauncher = rememberFileChooserLauncher(false) { to: URI? ->
|
||||
val file = chatArchiveFile.value
|
||||
if (file != null && to != null) {
|
||||
copyFileToFile(File(file), to) {
|
||||
chatArchiveFile.value = null
|
||||
}
|
||||
val archive = chatArchiveFile.value
|
||||
if (archive != null && to != null) {
|
||||
copyFileToFile(File(archive), to) {}
|
||||
}
|
||||
// delete no matter the database was exported or canceled the export process
|
||||
if (archive != null) {
|
||||
File(archive).delete()
|
||||
chatArchiveFile.value = null
|
||||
}
|
||||
}
|
||||
val appFilesCountAndSize = remember { mutableStateOf(directoryFileCountAndSize(appFilesDir.absolutePath)) }
|
||||
@@ -680,6 +684,8 @@ suspend fun importArchive(
|
||||
} finally {
|
||||
File(archivePath).delete()
|
||||
}
|
||||
} else {
|
||||
progressIndicator.value = false
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -691,14 +697,15 @@ private fun saveArchiveFromURI(importedArchiveURI: URI): String? {
|
||||
if (inputStream != null && archiveName != null) {
|
||||
val archivePath = "$databaseExportDir${File.separator}$archiveName"
|
||||
val destFile = File(archivePath)
|
||||
Files.copy(inputStream, destFile.toPath())
|
||||
Files.copy(inputStream, destFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
|
||||
archivePath
|
||||
} else {
|
||||
Log.e(TAG, "saveArchiveFromURI null inputStream")
|
||||
null
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "saveArchiveFromURI error: ${e.message}")
|
||||
AlertManager.shared.showAlertMsg(generalGetString(MR.strings.error_saving_database), e.stackTraceToString())
|
||||
Log.e(TAG, "saveArchiveFromURI error: ${e.stackTraceToString()}")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
+7
@@ -14,6 +14,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatController.appPrefs
|
||||
import chat.simplex.common.platform.*
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
@@ -44,6 +45,12 @@ fun DeveloperView(withAuth: (title: String, desc: String, block: () -> Unit) ->
|
||||
if (devTools.value) {
|
||||
SectionDividerSpaced(maxTopPadding = true)
|
||||
SectionView(stringResource(MR.strings.developer_options_section).uppercase()) {
|
||||
SettingsActionItemWithContent(painterResource(MR.images.ic_breaking_news), stringResource(MR.strings.debug_logs)) {
|
||||
DefaultSwitch(
|
||||
checked = remember { appPrefs.logLevel.state }.value <= LogLevel.DEBUG,
|
||||
onCheckedChange = { appPrefs.logLevel.set(if (it) LogLevel.DEBUG else LogLevel.WARNING) }
|
||||
)
|
||||
}
|
||||
SettingsPreferenceItem(painterResource(MR.images.ic_drive_folder_upload), stringResource(MR.strings.confirm_database_upgrades), m.controller.appPrefs.confirmDBUpgrades)
|
||||
if (appPlatform.isDesktop) {
|
||||
TerminalAlwaysVisibleItem(m.controller.appPrefs.terminalAlwaysVisible) { checked ->
|
||||
|
||||
+4
-1
@@ -735,7 +735,10 @@ private fun ConditionsAppliedToOtherOperatorsText(userServers: List<UserOperator
|
||||
}
|
||||
|
||||
if (otherOperatorsToApply.value.isNotEmpty()) {
|
||||
ReadableText(MR.strings.operator_conditions_will_be_applied)
|
||||
ReadableText(
|
||||
MR.strings.operator_conditions_will_be_applied,
|
||||
args = otherOperatorsToApply.value.joinToString(", ") { it.legalName_ }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -524,6 +524,10 @@
|
||||
<string name="sync_connection_force_question">Renegotiate encryption?</string>
|
||||
<string name="sync_connection_force_desc">The encryption is working and the new encryption agreement is not required. It may result in connection errors!</string>
|
||||
<string name="sync_connection_force_confirm">Renegotiate</string>
|
||||
<string name="sync_connection_question">Fix connection?</string>
|
||||
<string name="sync_connection_desc">Connection requires encryption renegotiation.</string>
|
||||
<string name="sync_connection_confirm">Fix</string>
|
||||
<string name="encryption_renegotiation_in_progress">Encryption renegotiation in progress.</string>
|
||||
<string name="view_security_code">View security code</string>
|
||||
<string name="verify_security_code">Verify security code</string>
|
||||
|
||||
@@ -903,6 +907,7 @@
|
||||
<string name="show_dev_options">Show:</string>
|
||||
<string name="hide_dev_options">Hide:</string>
|
||||
<string name="show_developer_options">Show developer options</string>
|
||||
<string name="debug_logs">Enable logs</string>
|
||||
<string name="developer_options">Database IDs and Transport isolation option.</string>
|
||||
<string name="developer_options_section">Developer options</string>
|
||||
<string name="show_internal_errors">Show internal errors</string>
|
||||
@@ -1334,6 +1339,7 @@
|
||||
<string name="chat_database_exported_migrate">You may migrate the exported database.</string>
|
||||
<string name="chat_database_exported_not_all_files">Some file(s) were not exported</string>
|
||||
<string name="chat_database_exported_continue">Continue</string>
|
||||
<string name="error_saving_database">Error saving database</string>
|
||||
|
||||
<!-- DatabaseEncryptionView.kt -->
|
||||
<string name="save_passphrase_in_keychain">Save passphrase in Keystore</string>
|
||||
@@ -2402,7 +2408,7 @@
|
||||
<string name="servers_info_messages_sent">Messages sent</string>
|
||||
<string name="servers_info_messages_received">Messages received</string>
|
||||
<string name="servers_info_details">Details</string>
|
||||
<string name="servers_info_private_data_disclaimer">Starting from %s.\nAll data is private to your device.</string>
|
||||
<string name="servers_info_private_data_disclaimer">Starting from %s.\nAll data is kept private on your device..</string>
|
||||
<string name="servers_info_subscriptions_section_header">Message reception</string>
|
||||
<string name="servers_info_subscriptions_connections_subscribed">Active connections</string>
|
||||
<string name="servers_info_subscriptions_connections_pending">Pending</string>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="22px" viewBox="0 -960 960 960" width="22px" fill="#5f6368"><path d="M262.5-285q11.5 0 20.25-8.5t8.75-20q0-11.5-8.75-20.25t-20.25-8.75q-11.5 0-20 8.75T234-313.5q0 11.5 8.5 20t20 8.5Zm-29-168.5H291v-227h-57.5v227Zm217.5 174h275.5V-337H451v57.5Zm0-174h275.5V-511H451v57.5Zm0-169.5h275.5v-57.5H451v57.5ZM134.5-124.5q-22.97 0-40.23-17.27Q77-159.03 77-182v-596q0-22.97 17.27-40.23 17.26-17.27 40.23-17.27h691q22.97 0 40.23 17.27Q883-800.97 883-778v596q0 22.97-17.27 40.23-17.26 17.27-40.23 17.27h-691Zm0-57.5h691v-596h-691v596Zm0 0v-596 596Z"/></svg>
|
||||
|
After Width: | Height: | Size: 592 B |
+4
-3
@@ -67,7 +67,7 @@ object NtfManager {
|
||||
ntf.second.close()
|
||||
} catch (e: Exception) {
|
||||
// Can be java.lang.UnsupportedOperationException, for example. May do nothing
|
||||
println("Failed to close notification: ${e.stackTraceToString()}")
|
||||
Log.e(TAG, "Failed to close notification: ${e.stackTraceToString()}")
|
||||
}*/
|
||||
}
|
||||
}
|
||||
@@ -85,7 +85,8 @@ object NtfManager {
|
||||
}
|
||||
|
||||
fun cancelAllNotifications() {
|
||||
// prevNtfs.forEach { try { it.second.close() } catch (e: Exception) { println("Failed to close notification: ${e.stackTraceToString()}") } }
|
||||
// prevNtfs.forEach { try { it.second.close() } catch (e: Exception) { Log.e(TAG, "Failed to close notification: ${e
|
||||
// .stackTraceToString()}") } }
|
||||
withBGApi {
|
||||
prevNtfsMutex.withLock {
|
||||
prevNtfs.clear()
|
||||
@@ -153,7 +154,7 @@ object NtfManager {
|
||||
ImageIO.write(icon.toAwtImage(), "PNG", newFile.outputStream())
|
||||
newFile.absolutePath
|
||||
} catch (e: Exception) {
|
||||
println("Failed to write an icon to tmpDir: ${e.stackTraceToString()}")
|
||||
Log.e(TAG, "Failed to write an icon to tmpDir: ${e.stackTraceToString()}")
|
||||
null
|
||||
}
|
||||
} else null
|
||||
|
||||
+6
-4
@@ -1,8 +1,10 @@
|
||||
package chat.simplex.common.platform
|
||||
|
||||
import chat.simplex.common.model.ChatController.appPrefs
|
||||
|
||||
actual object Log {
|
||||
actual fun d(tag: String, text: String) = println("D: $text")
|
||||
actual fun e(tag: String, text: String) = println("E: $text")
|
||||
actual fun i(tag: String, text: String) = println("I: $text")
|
||||
actual fun w(tag: String, text: String) = println("W: $text")
|
||||
actual fun d(tag: String, text: String) { if (appPrefs.logLevel.get() <= LogLevel.DEBUG && appPrefs.developerTools.get()) println("D: $text") }
|
||||
actual fun e(tag: String, text: String) { if (appPrefs.logLevel.get() <= LogLevel.ERROR || !appPrefs.developerTools.get()) println("E: $text") }
|
||||
actual fun i(tag: String, text: String) { if (appPrefs.logLevel.get() <= LogLevel.INFO && appPrefs.developerTools.get()) println("I: $text") }
|
||||
actual fun w(tag: String, text: String) { if (appPrefs.logLevel.get() <= LogLevel.WARNING || !appPrefs.developerTools.get()) println("W: $text") }
|
||||
}
|
||||
|
||||
@@ -24,11 +24,11 @@ android.nonTransitiveRClass=true
|
||||
kotlin.mpp.androidSourceSetLayoutVersion=2
|
||||
kotlin.jvm.target=11
|
||||
|
||||
android.version_name=6.2
|
||||
android.version_code=259
|
||||
android.version_name=6.2.1
|
||||
android.version_code=261
|
||||
|
||||
desktop.version_name=6.2
|
||||
desktop.version_code=82
|
||||
desktop.version_name=6.2.1
|
||||
desktop.version_code=83
|
||||
|
||||
kotlin.version=1.9.23
|
||||
gradle.plugin.version=8.2.0
|
||||
|
||||
Reference in New Issue
Block a user