mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2025-06-28 20:29:53 +00:00
Merge branch 'master' into ab/diff-subs
This commit is contained in:
commit
0d62d7ddfb
11 changed files with 237 additions and 41 deletions
|
@ -37,7 +37,7 @@ kotlin {
|
||||||
api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
|
api("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
|
||||||
api("org.jetbrains.kotlinx:kotlinx-datetime:0.5.0")
|
api("org.jetbrains.kotlinx:kotlinx-datetime:0.5.0")
|
||||||
api("com.russhwolf:multiplatform-settings:1.1.1")
|
api("com.russhwolf:multiplatform-settings:1.1.1")
|
||||||
api("com.charleskorn.kaml:kaml:0.58.0")
|
api("com.charleskorn.kaml:kaml:0.59.0")
|
||||||
api("org.jetbrains.compose.ui:ui-text:${rootProject.extra["compose.version"] as String}")
|
api("org.jetbrains.compose.ui:ui-text:${rootProject.extra["compose.version"] as String}")
|
||||||
implementation("org.jetbrains.compose.components:components-animatedimage:${rootProject.extra["compose.version"] as String}")
|
implementation("org.jetbrains.compose.components:components-animatedimage:${rootProject.extra["compose.version"] as String}")
|
||||||
//Barcode
|
//Barcode
|
||||||
|
|
|
@ -4104,6 +4104,8 @@ val jsonShort = Json {
|
||||||
val yaml = Yaml(configuration = YamlConfiguration(
|
val yaml = Yaml(configuration = YamlConfiguration(
|
||||||
strictMode = false,
|
strictMode = false,
|
||||||
encodeDefaults = false,
|
encodeDefaults = false,
|
||||||
|
/** ~5.5 MB limitation since wallpaper is limited by 5 MB, see [saveWallpaperFile] */
|
||||||
|
codePointLimit = 5500000,
|
||||||
))
|
))
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
|
|
@ -506,6 +506,76 @@ data class ThemeModeOverride (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun removeSameColors(base: DefaultTheme): ThemeModeOverride {
|
||||||
|
val c = when (base) {
|
||||||
|
DefaultTheme.LIGHT -> LightColorPalette
|
||||||
|
DefaultTheme.DARK -> DarkColorPalette
|
||||||
|
DefaultTheme.SIMPLEX -> SimplexColorPalette
|
||||||
|
DefaultTheme.BLACK -> BlackColorPalette
|
||||||
|
}
|
||||||
|
val ac = when (base) {
|
||||||
|
DefaultTheme.LIGHT -> LightColorPaletteApp
|
||||||
|
DefaultTheme.DARK -> DarkColorPaletteApp
|
||||||
|
DefaultTheme.SIMPLEX -> SimplexColorPaletteApp
|
||||||
|
DefaultTheme.BLACK -> BlackColorPaletteApp
|
||||||
|
}
|
||||||
|
val w = when (val wallpaperType = WallpaperType.from(wallpaper)) {
|
||||||
|
is WallpaperType.Preset -> {
|
||||||
|
val p = PresetWallpaper.from(wallpaperType.filename)
|
||||||
|
ThemeWallpaper(
|
||||||
|
preset = wallpaperType.filename,
|
||||||
|
scale = p?.scale ?: wallpaper?.scale,
|
||||||
|
scaleType = null,
|
||||||
|
background = p?.background?.get(base)?.toReadableHex(),
|
||||||
|
tint = p?.tint?.get(base)?.toReadableHex(),
|
||||||
|
image = null,
|
||||||
|
imageFile = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is WallpaperType.Image -> {
|
||||||
|
ThemeWallpaper(
|
||||||
|
preset = null,
|
||||||
|
scale = null,
|
||||||
|
scaleType = WallpaperScaleType.FILL,
|
||||||
|
background = Color.Transparent.toReadableHex(),
|
||||||
|
tint = Color.Transparent.toReadableHex(),
|
||||||
|
image = null,
|
||||||
|
imageFile = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
ThemeWallpaper()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return copy(
|
||||||
|
colors = ThemeColors(
|
||||||
|
primary = if (colors.primary?.colorFromReadableHex() != c.primary) colors.primary else null,
|
||||||
|
primaryVariant = if (colors.primaryVariant?.colorFromReadableHex() != c.primaryVariant) colors.primaryVariant else null,
|
||||||
|
secondary = if (colors.secondary?.colorFromReadableHex() != c.secondary) colors.secondary else null,
|
||||||
|
secondaryVariant = if (colors.secondaryVariant?.colorFromReadableHex() != c.secondaryVariant) colors.secondaryVariant else null,
|
||||||
|
background = if (colors.background?.colorFromReadableHex() != c.background) colors.background else null,
|
||||||
|
surface = if (colors.surface?.colorFromReadableHex() != c.surface) colors.surface else null,
|
||||||
|
title = if (colors.title?.colorFromReadableHex() != ac.title) colors.title else null,
|
||||||
|
primaryVariant2 = if (colors.primaryVariant2?.colorFromReadableHex() != ac.primaryVariant2) colors.primary else null,
|
||||||
|
sentMessage = if (colors.sentMessage?.colorFromReadableHex() != ac.sentMessage) colors.sentMessage else null,
|
||||||
|
sentQuote = if (colors.sentQuote?.colorFromReadableHex() != ac.sentQuote) colors.sentQuote else null,
|
||||||
|
receivedMessage = if (colors.receivedMessage?.colorFromReadableHex() != ac.receivedMessage) colors.receivedMessage else null,
|
||||||
|
receivedQuote = if (colors.receivedQuote?.colorFromReadableHex() != ac.receivedQuote) colors.receivedQuote else null,
|
||||||
|
),
|
||||||
|
wallpaper = wallpaper?.copy(
|
||||||
|
preset = wallpaper.preset,
|
||||||
|
scale = if (wallpaper.scale != w.scale) wallpaper.scale else null,
|
||||||
|
scaleType = if (wallpaper.scaleType != w.scaleType) wallpaper.scaleType else null,
|
||||||
|
background = if (wallpaper.background != w.background) wallpaper.background else null,
|
||||||
|
tint = if (wallpaper.tint != w.tint) wallpaper.tint else null,
|
||||||
|
image = wallpaper.image,
|
||||||
|
imageFile = wallpaper.imageFile,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun withFilledAppDefaults(mode: DefaultThemeMode, base: DefaultTheme): ThemeModeOverride =
|
fun withFilledAppDefaults(mode: DefaultThemeMode, base: DefaultTheme): ThemeModeOverride =
|
||||||
ThemeModeOverride(
|
ThemeModeOverride(
|
||||||
|
|
|
@ -42,6 +42,7 @@ import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.datetime.Clock
|
import kotlinx.datetime.Clock
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ChatInfoView(
|
fun ChatInfoView(
|
||||||
|
@ -719,7 +720,7 @@ fun ModalData.ChatWallpaperEditorModal(chat: Chat) {
|
||||||
|
|
||||||
suspend fun save(applyToMode: DefaultThemeMode?, newTheme: ThemeModeOverride?, chat: Chat) {
|
suspend fun save(applyToMode: DefaultThemeMode?, newTheme: ThemeModeOverride?, chat: Chat) {
|
||||||
val unchangedThemes: ThemeModeOverrides = ((chat.chatInfo as? ChatInfo.Direct)?.contact?.uiThemes ?: (chat.chatInfo as? ChatInfo.Group)?.groupInfo?.uiThemes) ?: ThemeModeOverrides()
|
val unchangedThemes: ThemeModeOverrides = ((chat.chatInfo as? ChatInfo.Direct)?.contact?.uiThemes ?: (chat.chatInfo as? ChatInfo.Group)?.groupInfo?.uiThemes) ?: ThemeModeOverrides()
|
||||||
val wallpaperFiles = listOf(unchangedThemes.light?.wallpaper?.imageFile, unchangedThemes.dark?.wallpaper?.imageFile)
|
val wallpaperFiles = setOf(unchangedThemes.light?.wallpaper?.imageFile, unchangedThemes.dark?.wallpaper?.imageFile)
|
||||||
var changedThemes: ThemeModeOverrides? = unchangedThemes
|
var changedThemes: ThemeModeOverrides? = unchangedThemes
|
||||||
val changed = newTheme?.copy(wallpaper = newTheme.wallpaper?.withFilledWallpaperPath())
|
val changed = newTheme?.copy(wallpaper = newTheme.wallpaper?.withFilledWallpaperPath())
|
||||||
changedThemes = when (applyToMode) {
|
changedThemes = when (applyToMode) {
|
||||||
|
@ -727,7 +728,28 @@ suspend fun save(applyToMode: DefaultThemeMode?, newTheme: ThemeModeOverride?, c
|
||||||
DefaultThemeMode.LIGHT -> changedThemes?.copy(light = changed?.copy(mode = applyToMode))
|
DefaultThemeMode.LIGHT -> changedThemes?.copy(light = changed?.copy(mode = applyToMode))
|
||||||
DefaultThemeMode.DARK -> changedThemes?.copy(dark = changed?.copy(mode = applyToMode))
|
DefaultThemeMode.DARK -> changedThemes?.copy(dark = changed?.copy(mode = applyToMode))
|
||||||
}
|
}
|
||||||
changedThemes = if (changedThemes?.light != null || changedThemes?.dark != null) changedThemes else null
|
changedThemes = if (changedThemes?.light != null || changedThemes?.dark != null) {
|
||||||
|
val light = changedThemes.light
|
||||||
|
val dark = changedThemes.dark
|
||||||
|
val currentMode = CurrentColors.value.base.mode
|
||||||
|
// same image file for both modes, copy image to make them as different files
|
||||||
|
if (light?.wallpaper?.imageFile != null && dark?.wallpaper?.imageFile != null && light.wallpaper.imageFile == dark.wallpaper.imageFile) {
|
||||||
|
val imageFile = if (currentMode == DefaultThemeMode.LIGHT) {
|
||||||
|
dark.wallpaper.imageFile
|
||||||
|
} else {
|
||||||
|
light.wallpaper.imageFile
|
||||||
|
}
|
||||||
|
val filePath = saveWallpaperFile(File(getWallpaperFilePath(imageFile)).toURI())
|
||||||
|
changedThemes = if (currentMode == DefaultThemeMode.LIGHT) {
|
||||||
|
changedThemes.copy(dark = dark.copy(wallpaper = dark.wallpaper.copy(imageFile = filePath)))
|
||||||
|
} else {
|
||||||
|
changedThemes.copy(light = light.copy(wallpaper = light.wallpaper.copy(imageFile = filePath)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
changedThemes
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
val wallpaperFilesToDelete = wallpaperFiles - changedThemes?.light?.wallpaper?.imageFile - changedThemes?.dark?.wallpaper?.imageFile
|
val wallpaperFilesToDelete = wallpaperFiles - changedThemes?.light?.wallpaper?.imageFile - changedThemes?.dark?.wallpaper?.imageFile
|
||||||
wallpaperFilesToDelete.forEach(::removeWallpaperFile)
|
wallpaperFilesToDelete.forEach(::removeWallpaperFile)
|
||||||
|
|
||||||
|
|
|
@ -339,6 +339,10 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: suspend (chatId:
|
||||||
openDirectChat(chatRh, contactId, chatModel)
|
openDirectChat(chatRh, contactId, chatModel)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
forwardItem = { cItem, cInfo ->
|
||||||
|
chatModel.chatId.value = null
|
||||||
|
chatModel.sharedContent.value = SharedContent.Forward(cInfo, cItem)
|
||||||
|
},
|
||||||
updateContactStats = { contact ->
|
updateContactStats = { contact ->
|
||||||
withBGApi {
|
withBGApi {
|
||||||
val r = chatModel.controller.apiContactInfo(chatRh, chat.chatInfo.apiId)
|
val r = chatModel.controller.apiContactInfo(chatRh, chat.chatInfo.apiId)
|
||||||
|
@ -537,6 +541,7 @@ fun ChatLayout(
|
||||||
acceptCall: (Contact) -> Unit,
|
acceptCall: (Contact) -> Unit,
|
||||||
acceptFeature: (Contact, ChatFeature, Int?) -> Unit,
|
acceptFeature: (Contact, ChatFeature, Int?) -> Unit,
|
||||||
openDirectChat: (Long) -> Unit,
|
openDirectChat: (Long) -> Unit,
|
||||||
|
forwardItem: (ChatInfo, ChatItem) -> Unit,
|
||||||
updateContactStats: (Contact) -> Unit,
|
updateContactStats: (Contact) -> Unit,
|
||||||
updateMemberStats: (GroupInfo, GroupMember) -> Unit,
|
updateMemberStats: (GroupInfo, GroupMember) -> Unit,
|
||||||
syncContactConnection: (Contact) -> Unit,
|
syncContactConnection: (Contact) -> Unit,
|
||||||
|
@ -619,7 +624,7 @@ fun ChatLayout(
|
||||||
ChatItemsList(
|
ChatItemsList(
|
||||||
chat, unreadCount, composeState, searchValue,
|
chat, unreadCount, composeState, searchValue,
|
||||||
useLinkPreviews, linkMode, showMemberInfo, loadPrevMessages, deleteMessage, deleteMessages,
|
useLinkPreviews, linkMode, showMemberInfo, loadPrevMessages, deleteMessage, deleteMessages,
|
||||||
receiveFile, cancelFile, joinGroup, acceptCall, acceptFeature, openDirectChat,
|
receiveFile, cancelFile, joinGroup, acceptCall, acceptFeature, openDirectChat, forwardItem,
|
||||||
updateContactStats, updateMemberStats, syncContactConnection, syncMemberConnection, findModelChat, findModelMember,
|
updateContactStats, updateMemberStats, syncContactConnection, syncMemberConnection, findModelChat, findModelMember,
|
||||||
setReaction, showItemDetails, markRead, setFloatingButton, onComposed, developerTools, showViaProxy,
|
setReaction, showItemDetails, markRead, setFloatingButton, onComposed, developerTools, showViaProxy,
|
||||||
)
|
)
|
||||||
|
@ -888,6 +893,7 @@ fun BoxWithConstraintsScope.ChatItemsList(
|
||||||
acceptCall: (Contact) -> Unit,
|
acceptCall: (Contact) -> Unit,
|
||||||
acceptFeature: (Contact, ChatFeature, Int?) -> Unit,
|
acceptFeature: (Contact, ChatFeature, Int?) -> Unit,
|
||||||
openDirectChat: (Long) -> Unit,
|
openDirectChat: (Long) -> Unit,
|
||||||
|
forwardItem: (ChatInfo, ChatItem) -> Unit,
|
||||||
updateContactStats: (Contact) -> Unit,
|
updateContactStats: (Contact) -> Unit,
|
||||||
updateMemberStats: (GroupInfo, GroupMember) -> Unit,
|
updateMemberStats: (GroupInfo, GroupMember) -> Unit,
|
||||||
syncContactConnection: (Contact) -> Unit,
|
syncContactConnection: (Contact) -> Unit,
|
||||||
|
@ -991,7 +997,7 @@ fun BoxWithConstraintsScope.ChatItemsList(
|
||||||
tryOrShowError("${cItem.id}ChatItem", error = {
|
tryOrShowError("${cItem.id}ChatItem", error = {
|
||||||
CIBrokenComposableView(if (cItem.chatDir.sent) Alignment.CenterEnd else Alignment.CenterStart)
|
CIBrokenComposableView(if (cItem.chatDir.sent) Alignment.CenterEnd else Alignment.CenterStart)
|
||||||
}) {
|
}) {
|
||||||
ChatItemView(chat.remoteHostId, chat.chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, linkMode = linkMode, revealed = revealed, range = range, deleteMessage = deleteMessage, deleteMessages = deleteMessages, receiveFile = receiveFile, cancelFile = cancelFile, joinGroup = joinGroup, acceptCall = acceptCall, acceptFeature = acceptFeature, openDirectChat = openDirectChat, updateContactStats = updateContactStats, updateMemberStats = updateMemberStats, syncContactConnection = syncContactConnection, syncMemberConnection = syncMemberConnection, findModelChat = findModelChat, findModelMember = findModelMember, scrollToItem = scrollToItem, setReaction = setReaction, showItemDetails = showItemDetails, developerTools = developerTools, showViaProxy = showViaProxy)
|
ChatItemView(chat.remoteHostId, chat.chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, linkMode = linkMode, revealed = revealed, range = range, 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, setReaction = setReaction, showItemDetails = showItemDetails, developerTools = developerTools, showViaProxy = showViaProxy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1544,6 +1550,7 @@ fun PreviewChatLayout() {
|
||||||
acceptCall = { _ -> },
|
acceptCall = { _ -> },
|
||||||
acceptFeature = { _, _, _ -> },
|
acceptFeature = { _, _, _ -> },
|
||||||
openDirectChat = { _ -> },
|
openDirectChat = { _ -> },
|
||||||
|
forwardItem = { _, _ -> },
|
||||||
updateContactStats = { },
|
updateContactStats = { },
|
||||||
updateMemberStats = { _, _ -> },
|
updateMemberStats = { _, _ -> },
|
||||||
syncContactConnection = { },
|
syncContactConnection = { },
|
||||||
|
@ -1617,6 +1624,7 @@ fun PreviewGroupChatLayout() {
|
||||||
acceptCall = { _ -> },
|
acceptCall = { _ -> },
|
||||||
acceptFeature = { _, _, _ -> },
|
acceptFeature = { _, _, _ -> },
|
||||||
openDirectChat = { _ -> },
|
openDirectChat = { _ -> },
|
||||||
|
forwardItem = { _, _ -> },
|
||||||
updateContactStats = { },
|
updateContactStats = { },
|
||||||
updateMemberStats = { _, _ -> },
|
updateMemberStats = { _, _ -> },
|
||||||
syncContactConnection = { },
|
syncContactConnection = { },
|
||||||
|
|
|
@ -59,6 +59,7 @@ fun ChatItemView(
|
||||||
scrollToItem: (Long) -> Unit,
|
scrollToItem: (Long) -> Unit,
|
||||||
acceptFeature: (Contact, ChatFeature, Int?) -> Unit,
|
acceptFeature: (Contact, ChatFeature, Int?) -> Unit,
|
||||||
openDirectChat: (Long) -> Unit,
|
openDirectChat: (Long) -> Unit,
|
||||||
|
forwardItem: (ChatInfo, ChatItem) -> Unit,
|
||||||
updateContactStats: (Contact) -> Unit,
|
updateContactStats: (Contact) -> Unit,
|
||||||
updateMemberStats: (GroupInfo, GroupMember) -> Unit,
|
updateMemberStats: (GroupInfo, GroupMember) -> Unit,
|
||||||
syncContactConnection: (Contact) -> Unit,
|
syncContactConnection: (Contact) -> Unit,
|
||||||
|
@ -68,7 +69,8 @@ fun ChatItemView(
|
||||||
setReaction: (ChatInfo, ChatItem, Boolean, MsgReaction) -> Unit,
|
setReaction: (ChatInfo, ChatItem, Boolean, MsgReaction) -> Unit,
|
||||||
showItemDetails: (ChatInfo, ChatItem) -> Unit,
|
showItemDetails: (ChatInfo, ChatItem) -> Unit,
|
||||||
developerTools: Boolean,
|
developerTools: Boolean,
|
||||||
showViaProxy: Boolean
|
showViaProxy: Boolean,
|
||||||
|
preview: Boolean = false,
|
||||||
) {
|
) {
|
||||||
val uriHandler = LocalUriHandler.current
|
val uriHandler = LocalUriHandler.current
|
||||||
val sent = cItem.chatDir.sent
|
val sent = cItem.chatDir.sent
|
||||||
|
@ -260,8 +262,7 @@ fun ChatItemView(
|
||||||
!cItem.isLiveDummy && !live
|
!cItem.isLiveDummy && !live
|
||||||
) {
|
) {
|
||||||
ItemAction(stringResource(MR.strings.forward_chat_item), painterResource(MR.images.ic_forward), onClick = {
|
ItemAction(stringResource(MR.strings.forward_chat_item), painterResource(MR.images.ic_forward), onClick = {
|
||||||
chatModel.chatId.value = null
|
forwardItem(cInfo, cItem)
|
||||||
chatModel.sharedContent.value = SharedContent.Forward(cItem, cInfo)
|
|
||||||
showMenu.value = false
|
showMenu.value = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -272,7 +273,7 @@ fun ChatItemView(
|
||||||
if (cItem.meta.itemDeleted == null && cItem.file != null && cItem.file.cancelAction != null && !cItem.localNote) {
|
if (cItem.meta.itemDeleted == null && cItem.file != null && cItem.file.cancelAction != null && !cItem.localNote) {
|
||||||
CancelFileItemAction(cItem.file.fileId, showMenu, cancelFile = cancelFile, cancelAction = cItem.file.cancelAction)
|
CancelFileItemAction(cItem.file.fileId, showMenu, cancelFile = cancelFile, cancelAction = cItem.file.cancelAction)
|
||||||
}
|
}
|
||||||
if (!(live && cItem.meta.isLive)) {
|
if (!(live && cItem.meta.isLive) && !preview) {
|
||||||
DeleteItemAction(cItem, revealed, showMenu, questionText = deleteMessageQuestionText(), deleteMessage, deleteMessages)
|
DeleteItemAction(cItem, revealed, showMenu, questionText = deleteMessageQuestionText(), deleteMessage, deleteMessages)
|
||||||
}
|
}
|
||||||
val groupInfo = cItem.memberToModerate(cInfo)?.first
|
val groupInfo = cItem.memberToModerate(cInfo)?.first
|
||||||
|
@ -840,6 +841,7 @@ fun PreviewChatItemView(
|
||||||
scrollToItem = {},
|
scrollToItem = {},
|
||||||
acceptFeature = { _, _, _ -> },
|
acceptFeature = { _, _, _ -> },
|
||||||
openDirectChat = { _ -> },
|
openDirectChat = { _ -> },
|
||||||
|
forwardItem = { _, _ -> },
|
||||||
updateContactStats = { },
|
updateContactStats = { },
|
||||||
updateMemberStats = { _, _ -> },
|
updateMemberStats = { _, _ -> },
|
||||||
syncContactConnection = { },
|
syncContactConnection = { },
|
||||||
|
@ -850,6 +852,7 @@ fun PreviewChatItemView(
|
||||||
showItemDetails = { _, _ -> },
|
showItemDetails = { _, _ -> },
|
||||||
developerTools = false,
|
developerTools = false,
|
||||||
showViaProxy = false,
|
showViaProxy = false,
|
||||||
|
preview = true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -875,6 +878,7 @@ fun PreviewChatItemViewDeletedContent() {
|
||||||
scrollToItem = {},
|
scrollToItem = {},
|
||||||
acceptFeature = { _, _, _ -> },
|
acceptFeature = { _, _, _ -> },
|
||||||
openDirectChat = { _ -> },
|
openDirectChat = { _ -> },
|
||||||
|
forwardItem = { _, _ -> },
|
||||||
updateContactStats = { },
|
updateContactStats = { },
|
||||||
updateMemberStats = { _, _ -> },
|
updateMemberStats = { _, _ -> },
|
||||||
syncContactConnection = { },
|
syncContactConnection = { },
|
||||||
|
@ -884,7 +888,8 @@ fun PreviewChatItemViewDeletedContent() {
|
||||||
setReaction = { _, _, _, _ -> },
|
setReaction = { _, _, _, _ -> },
|
||||||
showItemDetails = { _, _ -> },
|
showItemDetails = { _, _ -> },
|
||||||
developerTools = false,
|
developerTools = false,
|
||||||
showViaProxy = false
|
showViaProxy = false,
|
||||||
|
preview = true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -379,6 +379,9 @@ fun DrawScope.chatViewBackground(image: ImageBitmap, imageType: WallpaperType, b
|
||||||
val scale = scaleType.contentScale.computeScaleFactor(Size(image.width.toFloat(), image.height.toFloat()), Size(size.width, size.height))
|
val scale = scaleType.contentScale.computeScaleFactor(Size(image.width.toFloat(), image.height.toFloat()), Size(size.width, size.height))
|
||||||
val scaledWidth = (image.width * scale.scaleX).roundToInt()
|
val scaledWidth = (image.width * scale.scaleX).roundToInt()
|
||||||
val scaledHeight = (image.height * scale.scaleY).roundToInt()
|
val scaledHeight = (image.height * scale.scaleY).roundToInt()
|
||||||
|
// Large image will cause freeze
|
||||||
|
if (image.width > 4320 || image.height > 4320) return@clipRect
|
||||||
|
|
||||||
drawImage(image, dstOffset = IntOffset(x = ((size.width - scaledWidth) / 2).roundToInt(), y = ((size.height - scaledHeight) / 2).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
|
drawImage(image, dstOffset = IntOffset(x = ((size.width - scaledWidth) / 2).roundToInt(), y = ((size.height - scaledHeight) / 2).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
|
||||||
if (scaleType == WallpaperScaleType.FIT) {
|
if (scaleType == WallpaperScaleType.FIT) {
|
||||||
if (scaledWidth < size.width) {
|
if (scaledWidth < size.width) {
|
||||||
|
|
|
@ -3,13 +3,16 @@ package chat.simplex.common.views.helpers
|
||||||
import SectionBottomSpacer
|
import SectionBottomSpacer
|
||||||
import SectionItemView
|
import SectionItemView
|
||||||
import SectionSpacer
|
import SectionSpacer
|
||||||
|
import SectionView
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.material.MaterialTheme.colors
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import chat.simplex.common.model.ChatController.appPrefs
|
import chat.simplex.common.model.ChatController.appPrefs
|
||||||
|
import chat.simplex.common.model.yaml
|
||||||
import chat.simplex.common.platform.*
|
import chat.simplex.common.platform.*
|
||||||
import chat.simplex.common.ui.theme.*
|
import chat.simplex.common.ui.theme.*
|
||||||
import chat.simplex.common.views.usersettings.*
|
import chat.simplex.common.views.usersettings.*
|
||||||
|
@ -18,6 +21,7 @@ import chat.simplex.common.views.usersettings.AppearanceScope.editColor
|
||||||
import chat.simplex.res.MR
|
import chat.simplex.res.MR
|
||||||
import dev.icerock.moko.resources.compose.painterResource
|
import dev.icerock.moko.resources.compose.painterResource
|
||||||
import dev.icerock.moko.resources.compose.stringResource
|
import dev.icerock.moko.resources.compose.stringResource
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -134,7 +138,7 @@ fun ModalData.UserWallpaperEditor(
|
||||||
SectionSpacer()
|
SectionSpacer()
|
||||||
|
|
||||||
if (!globalThemeUsed.value) {
|
if (!globalThemeUsed.value) {
|
||||||
ResetToGlobalThemeButton {
|
ResetToGlobalThemeButton(true) {
|
||||||
themeModeOverride.value = ThemeManager.defaultActiveTheme(chatModel.currentUser.value?.uiThemes, appPrefs.themeOverrides.get())
|
themeModeOverride.value = ThemeManager.defaultActiveTheme(chatModel.currentUser.value?.uiThemes, appPrefs.themeOverrides.get())
|
||||||
globalThemeUsed.value = true
|
globalThemeUsed.value = true
|
||||||
withBGApi { save(applyToMode.value, null) }
|
withBGApi { save(applyToMode.value, null) }
|
||||||
|
@ -202,6 +206,14 @@ fun ModalData.UserWallpaperEditor(
|
||||||
SectionSpacer()
|
SectionSpacer()
|
||||||
|
|
||||||
AppearanceScope.CustomizeThemeColorsSection(currentTheme, editColor = editColor)
|
AppearanceScope.CustomizeThemeColorsSection(currentTheme, editColor = editColor)
|
||||||
|
|
||||||
|
SectionSpacer()
|
||||||
|
ImportExportThemeSection(null, remember { chatModel.currentUser }.value?.uiThemes) {
|
||||||
|
withBGApi {
|
||||||
|
themeModeOverride.value = it
|
||||||
|
save(applyToMode.value, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
AdvancedSettingsButton { showMore = true }
|
AdvancedSettingsButton { showMore = true }
|
||||||
}
|
}
|
||||||
|
@ -226,7 +238,7 @@ fun ModalData.ChatWallpaperEditor(
|
||||||
val themeModeOverride = remember { stateGetOrPut("themeModeOverride") { theme } }
|
val themeModeOverride = remember { stateGetOrPut("themeModeOverride") { theme } }
|
||||||
val currentTheme by remember(themeModeOverride.value, CurrentColors.collectAsState().value) {
|
val currentTheme by remember(themeModeOverride.value, CurrentColors.collectAsState().value) {
|
||||||
mutableStateOf(
|
mutableStateOf(
|
||||||
ThemeManager.currentColors(null, if (themeModeOverride.value == ThemeModeOverride()) null else themeModeOverride.value, chatModel.currentUser.value?.uiThemes, appPreferences.themeOverrides.get())
|
ThemeManager.currentColors(null, if (globalThemeUsed.value) null else themeModeOverride.value, chatModel.currentUser.value?.uiThemes, appPreferences.themeOverrides.get())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,7 +375,7 @@ fun ModalData.ChatWallpaperEditor(
|
||||||
SectionSpacer()
|
SectionSpacer()
|
||||||
|
|
||||||
if (!globalThemeUsed.value) {
|
if (!globalThemeUsed.value) {
|
||||||
ResetToGlobalThemeButton {
|
ResetToGlobalThemeButton(remember { chatModel.currentUser }.value?.uiThemes?.preferredMode(isInDarkTheme()) == null) {
|
||||||
themeModeOverride.value = ThemeManager.defaultActiveTheme(chatModel.currentUser.value?.uiThemes, appPrefs.themeOverrides.get())
|
themeModeOverride.value = ThemeManager.defaultActiveTheme(chatModel.currentUser.value?.uiThemes, appPrefs.themeOverrides.get())
|
||||||
globalThemeUsed.value = true
|
globalThemeUsed.value = true
|
||||||
withBGApi { save(applyToMode.value, null) }
|
withBGApi { save(applyToMode.value, null) }
|
||||||
|
@ -431,6 +443,14 @@ fun ModalData.ChatWallpaperEditor(
|
||||||
SectionSpacer()
|
SectionSpacer()
|
||||||
|
|
||||||
AppearanceScope.CustomizeThemeColorsSection(currentTheme, editColor = editColor)
|
AppearanceScope.CustomizeThemeColorsSection(currentTheme, editColor = editColor)
|
||||||
|
|
||||||
|
SectionSpacer()
|
||||||
|
ImportExportThemeSection(themeModeOverride.value, remember { chatModel.currentUser }.value?.uiThemes) {
|
||||||
|
withBGApi {
|
||||||
|
themeModeOverride.value = it
|
||||||
|
save(applyToMode.value, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
AdvancedSettingsButton { showMore = true }
|
AdvancedSettingsButton { showMore = true }
|
||||||
}
|
}
|
||||||
|
@ -440,9 +460,46 @@ fun ModalData.ChatWallpaperEditor(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ResetToGlobalThemeButton(onClick: () -> Unit) {
|
private fun ImportExportThemeSection(perChat: ThemeModeOverride?, perUser: ThemeModeOverrides?, save: (ThemeModeOverride) -> Unit) {
|
||||||
|
SectionView {
|
||||||
|
val theme = remember { mutableStateOf(null as String?) }
|
||||||
|
val exportThemeLauncher = rememberFileChooserLauncher(false) { to: URI? ->
|
||||||
|
val themeValue = theme.value
|
||||||
|
if (themeValue != null && to != null) {
|
||||||
|
copyBytesToFile(themeValue.byteInputStream(), to) {
|
||||||
|
theme.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SectionItemView({
|
||||||
|
val overrides = ThemeManager.currentThemeOverridesForExport(perChat, perUser)
|
||||||
|
val lines = yaml.encodeToString<ThemeOverrides>(overrides).lines()
|
||||||
|
// Removing theme id without using custom serializer or data class
|
||||||
|
theme.value = lines.subList(1, lines.size).joinToString("\n")
|
||||||
|
withLongRunningApi { exportThemeLauncher.launch("simplex.theme") }
|
||||||
|
}) {
|
||||||
|
Text(generalGetString(MR.strings.export_theme), color = colors.primary)
|
||||||
|
}
|
||||||
|
val importThemeLauncher = rememberFileChooserLauncher(true) { to: URI? ->
|
||||||
|
if (to != null) {
|
||||||
|
val theme = getThemeFromUri(to)
|
||||||
|
if (theme != null) {
|
||||||
|
val res = ThemeModeOverride(mode = theme.base.mode, colors = theme.colors, wallpaper = theme.wallpaper?.importFromString()).removeSameColors(theme.base)
|
||||||
|
save(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Can not limit to YAML mime type since it's unsupported by Android
|
||||||
|
SectionItemView({ withLongRunningApi { importThemeLauncher.launch("*/*") } }) {
|
||||||
|
Text(generalGetString(MR.strings.import_theme), color = colors.primary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ResetToGlobalThemeButton(app: Boolean, onClick: () -> Unit) {
|
||||||
SectionItemView(onClick) {
|
SectionItemView(onClick) {
|
||||||
Text(stringResource(MR.strings.chat_theme_reset_to_global_theme), color = MaterialTheme.colors.primary)
|
Text(stringResource(if (app) MR.strings.chat_theme_reset_to_app_theme else MR.strings.chat_theme_reset_to_user_theme), color = MaterialTheme.colors.primary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -146,6 +146,7 @@ fun getThemeFromUri(uri: URI, withAlertOnException: Boolean = true): ThemeOverri
|
||||||
runCatching {
|
runCatching {
|
||||||
return yaml.decodeFromStream<ThemeOverrides>(it!!)
|
return yaml.decodeFromStream<ThemeOverrides>(it!!)
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
|
Log.e(TAG, "Error while decoding theme: ${it.stackTraceToString()}")
|
||||||
if (withAlertOnException) {
|
if (withAlertOnException) {
|
||||||
AlertManager.shared.showAlertMsg(
|
AlertManager.shared.showAlertMsg(
|
||||||
title = generalGetString(MR.strings.import_theme_error),
|
title = generalGetString(MR.strings.import_theme_error),
|
||||||
|
|
|
@ -40,6 +40,7 @@ import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.datetime.Clock
|
import kotlinx.datetime.Clock
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
|
import java.io.File
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
|
@ -277,7 +278,7 @@ object AppearanceScope {
|
||||||
if (themeUserDestination.value == null) {
|
if (themeUserDestination.value == null) {
|
||||||
ThemeManager.saveAndApplyWallpaper(baseTheme, type)
|
ThemeManager.saveAndApplyWallpaper(baseTheme, type)
|
||||||
} else {
|
} else {
|
||||||
val wallpaperFiles = listOf(perUserTheme.value.wallpaper?.imageFile)
|
val wallpaperFiles = setOf(perUserTheme.value.wallpaper?.imageFile)
|
||||||
ThemeManager.copyFromSameThemeOverrides(type, null, perUserTheme)
|
ThemeManager.copyFromSameThemeOverrides(type, null, perUserTheme)
|
||||||
val wallpaperFilesToDelete = wallpaperFiles - perUserTheme.value.wallpaper?.imageFile
|
val wallpaperFilesToDelete = wallpaperFiles - perUserTheme.value.wallpaper?.imageFile
|
||||||
wallpaperFilesToDelete.forEach(::removeWallpaperFile)
|
wallpaperFilesToDelete.forEach(::removeWallpaperFile)
|
||||||
|
@ -297,17 +298,15 @@ object AppearanceScope {
|
||||||
saveThemeToDatabase(themeUserDestination.value)
|
saveThemeToDatabase(themeUserDestination.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
val importWallpaperLauncher = rememberFileChooserLauncher(true) { to: URI? ->
|
val onImport = { to: URI ->
|
||||||
if (to != null) {
|
val filename = saveWallpaperFile(to)
|
||||||
val filename = saveWallpaperFile(to)
|
if (filename != null) {
|
||||||
if (filename != null) {
|
if (themeUserDestination.value == null) {
|
||||||
if (themeUserDestination.value == null) {
|
removeWallpaperFile((currentTheme.wallpaper.type as? WallpaperType.Image)?.filename)
|
||||||
removeWallpaperFile((currentTheme.wallpaper.type as? WallpaperType.Image)?.filename)
|
} else {
|
||||||
} else {
|
removeWallpaperFile((perUserTheme.value.type as? WallpaperType.Image)?.filename)
|
||||||
removeWallpaperFile((perUserTheme.value.type as? WallpaperType.Image)?.filename)
|
|
||||||
}
|
|
||||||
onTypeChange(WallpaperType.Image(filename, 1f, WallpaperScaleType.FILL))
|
|
||||||
}
|
}
|
||||||
|
onTypeChange(WallpaperType.Image(filename, 1f, WallpaperScaleType.FILL))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,18 +318,18 @@ object AppearanceScope {
|
||||||
ThemeManager.currentColors(type, null, perUserOverride, appPrefs.themeOverrides.get())
|
ThemeManager.currentColors(type, null, perUserOverride, appPrefs.themeOverrides.get())
|
||||||
}
|
}
|
||||||
|
|
||||||
val onChooseType: (WallpaperType?) -> Unit = { type: WallpaperType? ->
|
val onChooseType: (WallpaperType?, FileChooserLauncher) -> Unit = { type: WallpaperType?, importWallpaperLauncher: FileChooserLauncher ->
|
||||||
when {
|
when {
|
||||||
// don't have image in parent or already selected wallpaper with custom image
|
// don't have image in parent or already selected wallpaper with custom image
|
||||||
type is WallpaperType.Image &&
|
type is WallpaperType.Image &&
|
||||||
((wallpaperType is WallpaperType.Image && themeUserDestination.value?.second != null && chatModel.remoteHostId() == null) ||
|
((wallpaperType is WallpaperType.Image && themeUserDestination.value?.second != null && chatModel.remoteHostId() == null) ||
|
||||||
currentColors(type).wallpaper.type.image == null ||
|
currentColors(type).wallpaper.type.image == null ||
|
||||||
(currentColors(type).wallpaper.type.image != null && wallpaperType is WallpaperType.Image && themeUserDestination.value == null)) ->
|
(currentColors(type).wallpaper.type.image != null && CurrentColors.value.wallpaper.type is WallpaperType.Image && themeUserDestination.value == null)) ->
|
||||||
withLongRunningApi { importWallpaperLauncher.launch("image/*") }
|
withLongRunningApi { importWallpaperLauncher.launch("image/*") }
|
||||||
type is WallpaperType.Image && themeUserDestination.value == null -> onTypeChange(currentColors(type).wallpaper.type)
|
type is WallpaperType.Image && themeUserDestination.value == null -> onTypeChange(currentColors(type).wallpaper.type)
|
||||||
type is WallpaperType.Image && chatModel.remoteHostId() != null -> { /* do nothing when remote host connected */ }
|
type is WallpaperType.Image && chatModel.remoteHostId() != null -> { /* do nothing when remote host connected */ }
|
||||||
type is WallpaperType.Image -> onTypeCopyFromSameTheme(currentColors(type).wallpaper.type)
|
type is WallpaperType.Image -> onTypeCopyFromSameTheme(currentColors(type).wallpaper.type)
|
||||||
(themeUserDestination.value != null && themeUserDestination.value?.second?.preferredMode(!CurrentColors.value.colors.isLight)?.type != type) || currentTheme.wallpaper.type != type -> onTypeCopyFromSameTheme(type)
|
(themeUserDestination.value != null && themeUserDestination.value?.second?.preferredMode(!CurrentColors.value.colors.isLight)?.type != type) || CurrentColors.value.wallpaper.type != type -> onTypeCopyFromSameTheme(type)
|
||||||
else -> onTypeChange(type)
|
else -> onTypeChange(type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -340,13 +339,17 @@ object AppearanceScope {
|
||||||
ThemeDestinationPicker(themeUserDestination)
|
ThemeDestinationPicker(themeUserDestination)
|
||||||
Spacer(Modifier.height(DEFAULT_PADDING_HALF))
|
Spacer(Modifier.height(DEFAULT_PADDING_HALF))
|
||||||
|
|
||||||
|
val importWallpaperLauncher = rememberFileChooserLauncher(true) { to: URI? ->
|
||||||
|
if (to != null) onImport(to)
|
||||||
|
}
|
||||||
|
|
||||||
WallpaperPresetSelector(
|
WallpaperPresetSelector(
|
||||||
selectedWallpaper = wallpaperType,
|
selectedWallpaper = wallpaperType,
|
||||||
baseTheme = currentTheme.base,
|
baseTheme = currentTheme.base,
|
||||||
currentColors = { type ->
|
currentColors = { type ->
|
||||||
currentColors(type)
|
currentColors(type)
|
||||||
},
|
},
|
||||||
onChooseType = onChooseType,
|
onChooseType = { onChooseType(it, importWallpaperLauncher) },
|
||||||
)
|
)
|
||||||
val type = MaterialTheme.wallpaper.type
|
val type = MaterialTheme.wallpaper.type
|
||||||
if (type is WallpaperType.Image && (themeUserDestination.value == null || perUserTheme.value.wallpaper?.imageFile != null)) {
|
if (type is WallpaperType.Image && (themeUserDestination.value == null || perUserTheme.value.wallpaper?.imageFile != null)) {
|
||||||
|
@ -400,7 +403,10 @@ object AppearanceScope {
|
||||||
val user = themeUserDestination.value
|
val user = themeUserDestination.value
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
ModalManager.start.showModal {
|
ModalManager.start.showModal {
|
||||||
CustomizeThemeView(onChooseType)
|
val importWallpaperLauncher = rememberFileChooserLauncher(true) { to: URI? ->
|
||||||
|
if (to != null) onImport(to)
|
||||||
|
}
|
||||||
|
CustomizeThemeView { onChooseType(it, importWallpaperLauncher) }
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ModalManager.start.showModalCloseable { close ->
|
ModalManager.start.showModalCloseable { close ->
|
||||||
|
@ -586,19 +592,19 @@ object AppearanceScope {
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ModalData.UserWallpaperEditorModal(remoteHostId: Long?, userId: Long, close: () -> Unit) {
|
fun ModalData.UserWallpaperEditorModal(remoteHostId: Long?, userId: Long, close: () -> Unit) {
|
||||||
val themes = remember(chatModel.currentUser.value) { chatModel.currentUser.value?.uiThemes ?: ThemeModeOverrides() }
|
val themes = remember(chatModel.currentUser.value) { mutableStateOf(chatModel.currentUser.value?.uiThemes ?: ThemeModeOverrides()) }
|
||||||
val globalThemeUsed = remember { stateGetOrPut("globalThemeUsed") { false } }
|
val globalThemeUsed = remember { stateGetOrPut("globalThemeUsed") { false } }
|
||||||
val initialTheme = remember(CurrentColors.collectAsState().value.base) {
|
val initialTheme = remember(CurrentColors.collectAsState().value.base) {
|
||||||
val preferred = themes.preferredMode(!CurrentColors.value.colors.isLight)
|
val preferred = themes.value.preferredMode(!CurrentColors.value.colors.isLight)
|
||||||
globalThemeUsed.value = preferred == null
|
globalThemeUsed.value = preferred == null
|
||||||
preferred ?: ThemeManager.defaultActiveTheme(chatModel.currentUser.value?.uiThemes, appPrefs.themeOverrides.get())
|
preferred ?: ThemeManager.defaultActiveTheme(chatModel.currentUser.value?.uiThemes, appPrefs.themeOverrides.get())
|
||||||
}
|
}
|
||||||
UserWallpaperEditor(
|
UserWallpaperEditor(
|
||||||
initialTheme,
|
initialTheme,
|
||||||
applyToMode = if (themes.light == themes.dark) null else initialTheme.mode,
|
applyToMode = if (themes.value.light == themes.value.dark) null else initialTheme.mode,
|
||||||
globalThemeUsed = globalThemeUsed,
|
globalThemeUsed = globalThemeUsed,
|
||||||
save = { applyToMode, newTheme ->
|
save = { applyToMode, newTheme ->
|
||||||
save(applyToMode, newTheme, themes, userId, remoteHostId)
|
save(applyToMode, newTheme, themes.value, userId, remoteHostId)
|
||||||
})
|
})
|
||||||
KeyChangeEffect(chatModel.currentUser.value?.userId, chatModel.remoteHostId) {
|
KeyChangeEffect(chatModel.currentUser.value?.userId, chatModel.remoteHostId) {
|
||||||
close()
|
close()
|
||||||
|
@ -613,7 +619,7 @@ object AppearanceScope {
|
||||||
remoteHostId: Long?
|
remoteHostId: Long?
|
||||||
) {
|
) {
|
||||||
val unchangedThemes: ThemeModeOverrides = themes ?: ThemeModeOverrides()
|
val unchangedThemes: ThemeModeOverrides = themes ?: ThemeModeOverrides()
|
||||||
val wallpaperFiles = listOf(unchangedThemes.light?.wallpaper?.imageFile, unchangedThemes.dark?.wallpaper?.imageFile)
|
val wallpaperFiles = setOf(unchangedThemes.light?.wallpaper?.imageFile, unchangedThemes.dark?.wallpaper?.imageFile)
|
||||||
var changedThemes: ThemeModeOverrides? = unchangedThemes
|
var changedThemes: ThemeModeOverrides? = unchangedThemes
|
||||||
val changed = newTheme?.copy(wallpaper = newTheme.wallpaper?.withFilledWallpaperPath())
|
val changed = newTheme?.copy(wallpaper = newTheme.wallpaper?.withFilledWallpaperPath())
|
||||||
changedThemes = when (applyToMode) {
|
changedThemes = when (applyToMode) {
|
||||||
|
@ -621,7 +627,28 @@ object AppearanceScope {
|
||||||
DefaultThemeMode.LIGHT -> changedThemes?.copy(light = changed?.copy(mode = applyToMode))
|
DefaultThemeMode.LIGHT -> changedThemes?.copy(light = changed?.copy(mode = applyToMode))
|
||||||
DefaultThemeMode.DARK -> changedThemes?.copy(dark = changed?.copy(mode = applyToMode))
|
DefaultThemeMode.DARK -> changedThemes?.copy(dark = changed?.copy(mode = applyToMode))
|
||||||
}
|
}
|
||||||
changedThemes = if (changedThemes?.light != null || changedThemes?.dark != null) changedThemes else null
|
changedThemes = if (changedThemes?.light != null || changedThemes?.dark != null) {
|
||||||
|
val light = changedThemes.light
|
||||||
|
val dark = changedThemes.dark
|
||||||
|
val currentMode = CurrentColors.value.base.mode
|
||||||
|
// same image file for both modes, copy image to make them as different files
|
||||||
|
if (light?.wallpaper?.imageFile != null && dark?.wallpaper?.imageFile != null && light.wallpaper.imageFile == dark.wallpaper.imageFile) {
|
||||||
|
val imageFile = if (currentMode == DefaultThemeMode.LIGHT) {
|
||||||
|
dark.wallpaper.imageFile
|
||||||
|
} else {
|
||||||
|
light.wallpaper.imageFile
|
||||||
|
}
|
||||||
|
val filePath = saveWallpaperFile(File(getWallpaperFilePath(imageFile)).toURI())
|
||||||
|
changedThemes = if (currentMode == DefaultThemeMode.LIGHT) {
|
||||||
|
changedThemes.copy(dark = dark.copy(wallpaper = dark.wallpaper.copy(imageFile = filePath)))
|
||||||
|
} else {
|
||||||
|
changedThemes.copy(light = light.copy(wallpaper = light.wallpaper.copy(imageFile = filePath)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
changedThemes
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
val wallpaperFilesToDelete = wallpaperFiles - changedThemes?.light?.wallpaper?.imageFile - changedThemes?.dark?.wallpaper?.imageFile
|
val wallpaperFilesToDelete = wallpaperFiles - changedThemes?.light?.wallpaper?.imageFile - changedThemes?.dark?.wallpaper?.imageFile
|
||||||
wallpaperFilesToDelete.forEach(::removeWallpaperFile)
|
wallpaperFilesToDelete.forEach(::removeWallpaperFile)
|
||||||
|
@ -662,9 +689,9 @@ object AppearanceScope {
|
||||||
}
|
}
|
||||||
|
|
||||||
val values by remember(chatModel.users.toList()) { mutableStateOf(
|
val values by remember(chatModel.users.toList()) { mutableStateOf(
|
||||||
listOf(null as Long? to generalGetString(MR.strings.theme_destination_all_profiles))
|
listOf(null as Long? to generalGetString(MR.strings.theme_destination_app_theme))
|
||||||
+
|
+
|
||||||
chatModel.users.filter { it.user.activeUser || it.user.viewPwdHash == null }.map {
|
chatModel.users.filter { it.user.activeUser }.map {
|
||||||
it.user.userId to it.user.chatViewName
|
it.user.userId to it.user.chatViewName
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -1570,7 +1570,7 @@
|
||||||
<string name="export_theme">Export theme</string>
|
<string name="export_theme">Export theme</string>
|
||||||
<string name="reset_color">Reset colors</string>
|
<string name="reset_color">Reset colors</string>
|
||||||
<string name="reset_single_color">Reset color</string>
|
<string name="reset_single_color">Reset color</string>
|
||||||
<string name="theme_destination_all_profiles">All chat profiles</string>
|
<string name="theme_destination_app_theme">App theme</string>
|
||||||
<string name="color_primary">Accent</string>
|
<string name="color_primary">Accent</string>
|
||||||
<string name="color_primary_variant">Additional accent</string>
|
<string name="color_primary_variant">Additional accent</string>
|
||||||
<string name="color_secondary">Secondary</string>
|
<string name="color_secondary">Secondary</string>
|
||||||
|
@ -1601,7 +1601,8 @@
|
||||||
<string name="wallpaper_scale_fill">Fill</string>
|
<string name="wallpaper_scale_fill">Fill</string>
|
||||||
<string name="wallpaper_scale_fit">Fit</string>
|
<string name="wallpaper_scale_fit">Fit</string>
|
||||||
<string name="wallpaper_advanced_settings">Advanced settings</string>
|
<string name="wallpaper_advanced_settings">Advanced settings</string>
|
||||||
<string name="chat_theme_reset_to_global_theme">Reset to global theme</string>
|
<string name="chat_theme_reset_to_app_theme">Reset to app theme</string>
|
||||||
|
<string name="chat_theme_reset_to_user_theme">Reset to user theme</string>
|
||||||
<string name="chat_theme_set_default_theme">Set default theme</string>
|
<string name="chat_theme_set_default_theme">Set default theme</string>
|
||||||
<string name="chat_theme_apply_to_mode">Apply to</string>
|
<string name="chat_theme_apply_to_mode">Apply to</string>
|
||||||
<string name="chat_theme_apply_to_all_modes">All color modes</string>
|
<string name="chat_theme_apply_to_all_modes">All color modes</string>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue