android, desktop: settings navigation reorganization

Restructure the root Settings screen into two top-level sections and
fold previously-scattered items into three new sub-screens.

Root:
- Appearance, Your privacy, Chat data
- Help & support, Migrate to another device, Advanced settings

Your privacy (renamed from Privacy & security): keeps Device,
link previews / remove tracking, auto-accept images, blur media,
contact requests from groups. Adds a "More privacy" sub-screen.

More privacy (new): show last messages, message draft, encrypt local
files, protect IP address (with original dynamic footer preserved),
notification preview mode (moved from Notifications), and delivery
receipts.

Help & support (new): merges Help and Support SimpleX Chat sections
into Help / About (with App version) / Contact / Support the project.

Advanced settings (new): Network & servers, Notifications (Android),
Audio & video calls; then Developer tools, Restart and Shutdown
(Android), App update channel (desktop). Notifications is hidden on
desktop because the screen is empty after Show preview moves to
More privacy.

Chat data is the new top-level menu label for the existing
Database passphrase & export screen.
This commit is contained in:
Narasimha-sc
2026-05-23 12:40:06 +00:00
parent 2b48b55190
commit 4cf683dda1
8 changed files with 279 additions and 181 deletions
@@ -11,20 +11,17 @@ import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource
@Composable
actual fun SettingsSectionApp(
actual fun AdvancedSettingsAppSection(
showSettingsModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit),
showVersion: () -> Unit,
withAuth: (title: String, desc: String, block: () -> Unit) -> Unit
withAuth: (title: String, desc: String, block: () -> Unit) -> Unit,
) {
SectionView(stringResource(MR.strings.settings_section_title_app)) {
SectionView {
SettingsActionItem(painterResource(MR.images.ic_restart_alt), stringResource(MR.strings.settings_restart_app), ::restartApp)
SettingsActionItem(painterResource(MR.images.ic_power_settings_new), stringResource(MR.strings.settings_shutdown), { shutdownAppAlert(::shutdownApp) })
SettingsActionItem(painterResource(MR.images.ic_code), stringResource(MR.strings.settings_developer_tools), showSettingsModal { DeveloperView(withAuth) })
AppVersionItem(showVersion)
}
}
fun restartApp() {
ProcessPhoenix.triggerRebirth(androidAppContext)
shutdownApp()
@@ -0,0 +1,47 @@
package chat.simplex.common.views.usersettings
import SectionBottomSpacer
import SectionDividerSpaced
import SectionView
import androidx.compose.runtime.*
import chat.simplex.common.model.ChatModel
import chat.simplex.common.model.NotificationsMode
import chat.simplex.common.platform.*
import chat.simplex.common.views.helpers.*
import chat.simplex.common.views.usersettings.networkAndServers.NetworkAndServersView
import chat.simplex.res.MR
import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource
@Composable
fun AdvancedSettingsView(
chatModel: ChatModel,
showModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit),
showSettingsModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit),
showCustomModal: (@Composable ModalData.(ChatModel, () -> Unit) -> Unit) -> (() -> Unit),
withAuth: (title: String, desc: String, block: () -> Unit) -> Unit,
) {
val stopped = chatModel.chatRunning.value == false
val notificationsMode = remember { chatModel.controller.appPrefs.notificationsMode.state }
ColumnWithScrollBar {
AppBarTitle(stringResource(MR.strings.advanced_settings))
SectionView {
SettingsActionItem(painterResource(MR.images.ic_wifi_tethering), stringResource(MR.strings.network_and_servers), showCustomModal { _, close -> NetworkAndServersView(close) }, disabled = stopped)
if (appPlatform == AppPlatform.ANDROID) {
SettingsActionItem(painterResource(if (notificationsMode.value == NotificationsMode.OFF) MR.images.ic_bolt_off else MR.images.ic_bolt), stringResource(MR.strings.notifications), showSettingsModal { NotificationsSettingsView(it) }, disabled = stopped)
}
SettingsActionItem(painterResource(MR.images.ic_videocam), stringResource(MR.strings.settings_audio_video_calls), showSettingsModal { CallSettingsView(it, showModal) }, disabled = stopped)
}
SectionDividerSpaced()
AdvancedSettingsAppSection(showSettingsModal, withAuth)
SectionBottomSpacer()
}
}
@Composable
expect fun AdvancedSettingsAppSection(
showSettingsModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit),
withAuth: (title: String, desc: String, block: () -> Unit) -> Unit,
)
@@ -0,0 +1,104 @@
package chat.simplex.common.views.usersettings
import SectionBottomSpacer
import SectionDividerSpaced
import SectionItemView
import SectionView
import TextIconSpaced
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.platform.UriHandler
import chat.simplex.common.BuildConfigCommon
import chat.simplex.common.model.ChatModel
import chat.simplex.common.platform.ColumnWithScrollBar
import chat.simplex.common.views.helpers.*
import chat.simplex.common.views.onboarding.SimpleXInfo
import chat.simplex.common.views.onboarding.WhatsNewView
import chat.simplex.res.MR
import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource
@Composable
fun HelpAndSupportView(
chatModel: ChatModel,
userDisplayName: String?,
showModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit),
showCustomModal: (@Composable ModalData.(ChatModel, () -> Unit) -> Unit) -> (() -> Unit),
showVersion: () -> Unit,
) {
val uriHandler = LocalUriHandler.current
val stopped = chatModel.chatRunning.value == false
ColumnWithScrollBar {
AppBarTitle(stringResource(MR.strings.help_and_support))
SectionView(stringResource(MR.strings.settings_section_title_help)) {
SettingsActionItem(painterResource(MR.images.ic_help), stringResource(MR.strings.how_to_use_simplex_chat), showModal { HelpView(userDisplayName ?: "") }, disabled = stopped)
SettingsActionItem(painterResource(MR.images.ic_add), stringResource(MR.strings.whats_new), showCustomModal { _, close -> WhatsNewView(viaSettings = true, close = close) }, disabled = stopped)
}
SectionDividerSpaced()
SectionView(stringResource(MR.strings.settings_section_title_about)) {
SettingsActionItem(painterResource(MR.images.ic_info), stringResource(MR.strings.about_simplex_chat), showModal { SimpleXInfo(it, onboarding = false) })
AppVersionItem(showVersion)
}
SectionDividerSpaced()
SectionView(stringResource(MR.strings.settings_section_title_contact)) {
if (!chatModel.desktopNoUserNoRemote) {
SettingsActionItem(painterResource(MR.images.ic_tag), stringResource(MR.strings.chat_with_the_founder), { uriHandler.openVerifiedSimplexUri(simplexTeamUri) }, textColor = MaterialTheme.colors.primary, disabled = stopped)
}
SettingsActionItem(painterResource(MR.images.ic_mail), stringResource(MR.strings.send_us_an_email), { uriHandler.openUriCatching("mailto:chat@simplex.chat") }, textColor = MaterialTheme.colors.primary)
}
SectionDividerSpaced()
SectionView(stringResource(MR.strings.settings_section_title_support_project)) {
if (!BuildConfigCommon.ANDROID_BUNDLE) {
ContributeItem(uriHandler)
}
RateAppItem(uriHandler)
StarOnGithubItem(uriHandler)
}
SectionBottomSpacer()
}
}
@Composable private fun ContributeItem(uriHandler: UriHandler) {
SectionItemView({ uriHandler.openExternalLink("https://github.com/simplex-chat/simplex-chat#contribute") }) {
Icon(
painterResource(MR.images.ic_keyboard),
contentDescription = "GitHub",
tint = MaterialTheme.colors.secondary,
)
TextIconSpaced()
Text(generalGetString(MR.strings.contribute), color = MaterialTheme.colors.primary)
}
}
@Composable private fun RateAppItem(uriHandler: UriHandler) {
SectionItemView({
runCatching { uriHandler.openUriCatching("market://details?id=chat.simplex.app") }
.onFailure { uriHandler.openUriCatching("https://play.google.com/store/apps/details?id=chat.simplex.app") }
}
) {
Icon(
painterResource(MR.images.ic_star),
contentDescription = "Google Play",
tint = MaterialTheme.colors.secondary,
)
TextIconSpaced()
Text(generalGetString(MR.strings.rate_the_app), color = MaterialTheme.colors.primary)
}
}
@Composable private fun StarOnGithubItem(uriHandler: UriHandler) {
SectionItemView({ uriHandler.openExternalLink("https://github.com/simplex-chat/simplex-chat") }) {
Icon(
painter = painterResource(MR.images.ic_github),
contentDescription = "GitHub",
tint = MaterialTheme.colors.secondary,
)
TextIconSpaced()
Text(generalGetString(MR.strings.star_on_github), color = MaterialTheme.colors.primary)
}
}
@@ -24,19 +24,12 @@ import kotlin.collections.ArrayList
fun NotificationsSettingsView(
chatModel: ChatModel,
) {
val onNotificationPreviewModeSelected = { mode: NotificationPreviewMode ->
chatModel.controller.appPrefs.notificationPreviewMode.set(mode.name)
chatModel.notificationPreviewMode.value = mode
}
NotificationsSettingsLayout(
notificationsMode = remember { chatModel.controller.appPrefs.notificationsMode.state },
notificationPreviewMode = chatModel.notificationPreviewMode,
showPage = { page ->
ModalManager.start.showModalCloseable(true) {
when (page) {
CurrentPage.NOTIFICATIONS_MODE -> NotificationsModeView(chatModel.controller.appPrefs.notificationsMode.state) { changeNotificationsMode(it, chatModel) }
CurrentPage.NOTIFICATION_PREVIEW_MODE -> NotificationPreviewView(chatModel.notificationPreviewMode, onNotificationPreviewModeSelected)
}
}
},
@@ -44,17 +37,15 @@ fun NotificationsSettingsView(
}
enum class CurrentPage {
NOTIFICATIONS_MODE, NOTIFICATION_PREVIEW_MODE
NOTIFICATIONS_MODE
}
@Composable
fun NotificationsSettingsLayout(
notificationsMode: State<NotificationsMode>,
notificationPreviewMode: State<NotificationPreviewMode>,
showPage: (CurrentPage) -> Unit,
) {
val modes = remember { notificationModes() }
val previewModes = remember { notificationPreviewModes() }
ColumnWithScrollBar {
AppBarTitle(stringResource(MR.strings.notifications))
@@ -69,14 +60,6 @@ fun NotificationsSettingsLayout(
)
}
}
SettingsActionItemWithContent(null, stringResource(MR.strings.settings_notification_preview_mode_title), { showPage(CurrentPage.NOTIFICATION_PREVIEW_MODE) }) {
Text(
previewModes.firstOrNull { it.value == notificationPreviewMode.value }?.title ?: "",
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = MaterialTheme.colors.secondary
)
}
}
if (platform.androidIsXiaomiDevice() && (notificationsMode.value == NotificationsMode.PERIODIC || notificationsMode.value == NotificationsMode.SERVICE)) {
SectionTextFooter(annotatedStringResource(MR.strings.xiaomi_ignore_battery_optimization))
@@ -15,6 +15,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.*
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource
import androidx.compose.ui.unit.dp
@@ -73,6 +74,48 @@ fun PrivacySettingsView(
stringResource(MR.strings.sanitize_links_toggle),
chatModel.controller.appPrefs.privacySanitizeLinks
)
}
SectionDividerSpaced()
SectionView(stringResource(MR.strings.settings_section_title_files)) {
SettingsPreferenceItem(painterResource(MR.images.ic_image), stringResource(MR.strings.auto_accept_images), chatModel.controller.appPrefs.privacyAcceptImages)
BlurRadiusOptions(remember { appPrefs.privacyMediaBlurRadius.state }) {
appPrefs.privacyMediaBlurRadius.set(it)
}
}
val currentUser = chatModel.currentUser.value
if (currentUser != null && !chatModel.desktopNoUserNoRemote) {
SectionDividerSpaced()
ContacRequestsFromGroupsSection(
currentUser = currentUser,
setAutoAcceptGrpDirectInvs = { enable ->
withApi {
chatModel.controller.apiSetUserAutoAcceptMemberContacts(currentUser, enable)
chatModel.currentUser.value = currentUser.copy(autoAcceptMemberContacts = enable)
}
}
)
}
SectionDividerSpaced()
SectionView {
SettingsActionItem(
painterResource(MR.images.ic_more_horiz),
stringResource(MR.strings.more_privacy),
showSettingsModal { MorePrivacyView(it) }
)
}
SectionBottomSpacer()
}
}
@Composable
fun MorePrivacyView(chatModel: ChatModel) {
ColumnWithScrollBar {
AppBarTitle(stringResource(MR.strings.more_privacy))
SectionView(stringResource(MR.strings.settings_section_title_chats)) {
SettingsPreferenceItem(
painterResource(MR.images.ic_chat_bubble),
stringResource(MR.strings.privacy_show_last_messages),
@@ -98,10 +141,6 @@ fun PrivacySettingsView(
SettingsPreferenceItem(painterResource(MR.images.ic_lock), stringResource(MR.strings.encrypt_local_files), chatModel.controller.appPrefs.privacyEncryptLocalFiles, onChange = { enable ->
withBGApi { chatModel.controller.apiSetEncryptLocalFiles(enable) }
})
SettingsPreferenceItem(painterResource(MR.images.ic_image), stringResource(MR.strings.auto_accept_images), chatModel.controller.appPrefs.privacyAcceptImages)
BlurRadiusOptions(remember { appPrefs.privacyMediaBlurRadius.state }) {
appPrefs.privacyMediaBlurRadius.set(it)
}
SettingsPreferenceItem(painterResource(MR.images.ic_security), stringResource(MR.strings.protect_ip_address), chatModel.controller.appPrefs.privacyAskToApproveRelays)
}
SectionTextFooter(
@@ -111,9 +150,34 @@ fun PrivacySettingsView(
stringResource(MR.strings.without_tor_or_vpn_ip_address_will_be_visible_to_file_servers)
}
)
SectionDividerSpaced()
SectionView(stringResource(MR.strings.notifications)) {
val previewModes = remember { notificationPreviewModes() }
val notificationPreviewMode = remember { chatModel.notificationPreviewMode }
SettingsActionItemWithContent(
painterResource(MR.images.ic_visibility_off),
stringResource(MR.strings.settings_notification_preview_mode_title),
click = {
ModalManager.start.showModalCloseable(true) {
NotificationPreviewView(notificationPreviewMode) { mode ->
chatModel.controller.appPrefs.notificationPreviewMode.set(mode.name)
chatModel.notificationPreviewMode.value = mode
}
}
}
) {
Text(
previewModes.firstOrNull { it.value == notificationPreviewMode.value }?.title ?: "",
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = MaterialTheme.colors.secondary
)
}
}
val currentUser = chatModel.currentUser.value
if (currentUser != null) {
if (currentUser != null && !chatModel.desktopNoUserNoRemote) {
fun setSendReceiptsContacts(enable: Boolean, clearOverrides: Boolean) {
withLongRunningApi(slow = 60_000) {
val mrs = UserMsgReceiptSettings(enable, clearOverrides)
@@ -164,57 +228,40 @@ fun PrivacySettingsView(
}
}
fun setAutoAcceptGrpDirectInvs(enable: Boolean) {
withApi {
chatModel.controller.apiSetUserAutoAcceptMemberContacts(currentUser, enable)
chatModel.currentUser.value = currentUser.copy(autoAcceptMemberContacts = enable)
SectionDividerSpaced()
DeliveryReceiptsSection(
currentUser = currentUser,
setOrAskSendReceiptsContacts = { enable ->
val contactReceiptsOverrides = chatModel.chats.value.fold(0) { count, chat ->
if (chat.chatInfo is ChatInfo.Direct) {
val sendRcpts = chat.chatInfo.contact.chatSettings.sendRcpts
count + (if (sendRcpts == null || sendRcpts == enable) 0 else 1)
} else {
count
}
}
if (contactReceiptsOverrides == 0) {
setSendReceiptsContacts(enable, clearOverrides = false)
} else {
showUserContactsReceiptsAlert(enable, contactReceiptsOverrides, ::setSendReceiptsContacts)
}
},
setOrAskSendReceiptsGroups = { enable ->
val groupReceiptsOverrides = chatModel.chats.value.fold(0) { count, chat ->
if (chat.chatInfo is ChatInfo.Group) {
val sendRcpts = chat.chatInfo.groupInfo.chatSettings.sendRcpts
count + (if (sendRcpts == null || sendRcpts == enable) 0 else 1)
} else {
count
}
}
if (groupReceiptsOverrides == 0) {
setSendReceiptsGroups(enable, clearOverrides = false)
} else {
showUserGroupsReceiptsAlert(enable, groupReceiptsOverrides, ::setSendReceiptsGroups)
}
}
}
if (!chatModel.desktopNoUserNoRemote) {
SectionDividerSpaced()
ContacRequestsFromGroupsSection(
currentUser = currentUser,
setAutoAcceptGrpDirectInvs = { enable ->
setAutoAcceptGrpDirectInvs(enable)
}
)
SectionDividerSpaced()
DeliveryReceiptsSection(
currentUser = currentUser,
setOrAskSendReceiptsContacts = { enable ->
val contactReceiptsOverrides = chatModel.chats.value.fold(0) { count, chat ->
if (chat.chatInfo is ChatInfo.Direct) {
val sendRcpts = chat.chatInfo.contact.chatSettings.sendRcpts
count + (if (sendRcpts == null || sendRcpts == enable) 0 else 1)
} else {
count
}
}
if (contactReceiptsOverrides == 0) {
setSendReceiptsContacts(enable, clearOverrides = false)
} else {
showUserContactsReceiptsAlert(enable, contactReceiptsOverrides, ::setSendReceiptsContacts)
}
},
setOrAskSendReceiptsGroups = { enable ->
val groupReceiptsOverrides = chatModel.chats.value.fold(0) { count, chat ->
if (chat.chatInfo is ChatInfo.Group) {
val sendRcpts = chat.chatInfo.groupInfo.chatSettings.sendRcpts
count + (if (sendRcpts == null || sendRcpts == enable) 0 else 1)
} else {
count
}
}
if (groupReceiptsOverrides == 0) {
setSendReceiptsGroups(enable, clearOverrides = false)
} else {
showUserGroupsReceiptsAlert(enable, groupReceiptsOverrides, ::setSendReceiptsGroups)
}
}
)
}
)
}
SectionBottomSpacer()
}
@@ -22,7 +22,6 @@ import dev.icerock.moko.resources.compose.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.*
import chat.simplex.common.BuildConfigCommon
import chat.simplex.common.model.*
import chat.simplex.common.model.ChatController.appPrefs
import chat.simplex.common.platform.*
@@ -30,9 +29,6 @@ import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.database.DatabaseView
import chat.simplex.common.views.helpers.*
import chat.simplex.common.views.migration.MigrateFromDeviceView
import chat.simplex.common.views.onboarding.SimpleXInfo
import chat.simplex.common.views.onboarding.WhatsNewView
import chat.simplex.common.views.usersettings.networkAndServers.NetworkAndServersView
import chat.simplex.res.MR
@Composable
@@ -43,7 +39,6 @@ fun SettingsView(chatModel: ChatModel, setPerformLA: (Boolean) -> Unit, close: (
stopped,
chatModel.chatDbEncrypted.value == true,
remember { chatModel.controller.appPrefs.storeDBPassphrase.state }.value,
remember { chatModel.controller.appPrefs.notificationsMode.state },
user?.displayName,
setPerformLA = setPerformLA,
showModal = { modalView -> { ModalManager.start.showModal { modalView(chatModel) } } },
@@ -84,7 +79,6 @@ fun SettingsLayout(
stopped: Boolean,
encrypted: Boolean,
passphraseSaved: Boolean,
notificationsMode: State<NotificationsMode>,
userDisplayName: String?,
setPerformLA: (Boolean) -> Unit,
showModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit),
@@ -98,58 +92,25 @@ fun SettingsLayout(
LaunchedEffect(Unit) {
hideKeyboard(view)
}
val uriHandler = LocalUriHandler.current
ColumnWithScrollBar {
AppBarTitle(stringResource(MR.strings.your_settings))
SectionView(stringResource(MR.strings.settings_section_title_settings)) {
SettingsActionItem(painterResource(if (notificationsMode.value == NotificationsMode.OFF) MR.images.ic_bolt_off else MR.images.ic_bolt), stringResource(MR.strings.notifications), showSettingsModal { NotificationsSettingsView(it) }, disabled = stopped)
SettingsActionItem(painterResource(MR.images.ic_wifi_tethering), stringResource(MR.strings.network_and_servers), showCustomModal { _, close -> NetworkAndServersView(close) }, disabled = stopped)
SettingsActionItem(painterResource(MR.images.ic_videocam), stringResource(MR.strings.settings_audio_video_calls), showSettingsModal { CallSettingsView(it, showModal) }, disabled = stopped)
SettingsActionItem(painterResource(MR.images.ic_lock), stringResource(MR.strings.privacy_and_security), showSettingsModal { PrivacySettingsView(it, showSettingsModal, setPerformLA) }, disabled = stopped)
SectionView {
SettingsActionItem(painterResource(MR.images.ic_light_mode), stringResource(MR.strings.appearance_settings), showSettingsModal { AppearanceView(it) })
}
SectionDividerSpaced()
SectionView(stringResource(MR.strings.settings_section_title_chat_database)) {
SettingsActionItem(painterResource(MR.images.ic_lock), stringResource(MR.strings.your_privacy), showSettingsModal { PrivacySettingsView(it, showSettingsModal, setPerformLA) }, disabled = stopped)
DatabaseItem(encrypted, passphraseSaved, showSettingsModal { DatabaseView() }, stopped)
}
SectionDividerSpaced()
SectionView {
SettingsActionItem(painterResource(MR.images.ic_help), stringResource(MR.strings.help_and_support), showSettingsModal { HelpAndSupportView(it, userDisplayName, showModal, showCustomModal, showVersion) })
SettingsActionItem(painterResource(MR.images.ic_ios_share), stringResource(MR.strings.migrate_from_device_to_another_device), { withAuth(generalGetString(MR.strings.auth_open_migration_to_another_device), generalGetString(MR.strings.auth_log_in_using_credential)) { ModalManager.fullscreen.showCustomModal { close -> MigrateFromDeviceView(close) } } }, disabled = stopped)
SettingsActionItem(painterResource(MR.images.ic_code), stringResource(MR.strings.advanced_settings), showSettingsModal { AdvancedSettingsView(it, showModal, showSettingsModal, showCustomModal, withAuth) })
}
SectionDividerSpaced()
SectionView(stringResource(MR.strings.settings_section_title_help)) {
SettingsActionItem(painterResource(MR.images.ic_help), stringResource(MR.strings.how_to_use_simplex_chat), showModal { HelpView(userDisplayName ?: "") }, disabled = stopped)
SettingsActionItem(painterResource(MR.images.ic_add), stringResource(MR.strings.whats_new), showCustomModal { _, close -> WhatsNewView(viaSettings = true, close = close) }, disabled = stopped)
SettingsActionItem(painterResource(MR.images.ic_info), stringResource(MR.strings.about_simplex_chat), showModal { SimpleXInfo(it, onboarding = false) })
if (!chatModel.desktopNoUserNoRemote) {
SettingsActionItem(painterResource(MR.images.ic_tag), stringResource(MR.strings.chat_with_the_founder), { uriHandler.openVerifiedSimplexUri(simplexTeamUri) }, textColor = MaterialTheme.colors.primary, disabled = stopped)
}
SettingsActionItem(painterResource(MR.images.ic_mail), stringResource(MR.strings.send_us_an_email), { uriHandler.openUriCatching("mailto:chat@simplex.chat") }, textColor = MaterialTheme.colors.primary)
}
SectionDividerSpaced()
SectionView(stringResource(MR.strings.settings_section_title_support)) {
if (!BuildConfigCommon.ANDROID_BUNDLE) {
ContributeItem(uriHandler)
}
RateAppItem(uriHandler)
StarOnGithubItem(uriHandler)
}
SectionDividerSpaced()
SettingsSectionApp(showSettingsModal, showVersion, withAuth)
SectionBottomSpacer()
}
}
@Composable
expect fun SettingsSectionApp(
showSettingsModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit),
showVersion: () -> Unit,
withAuth: (title: String, desc: String, block: () -> Unit) -> Unit
)
@Composable private fun DatabaseItem(encrypted: Boolean, saved: Boolean, openDatabaseView: () -> Unit, stopped: Boolean) {
SectionItemView(openDatabaseView) {
Row(
@@ -160,11 +121,11 @@ expect fun SettingsSectionApp(
Row(Modifier.weight(1f), verticalAlignment = Alignment.CenterVertically) {
Icon(
painterResource(MR.images.ic_database),
contentDescription = stringResource(MR.strings.database_passphrase_and_export),
contentDescription = stringResource(MR.strings.chat_data),
tint = if (encrypted && (appPlatform.isAndroid || !saved)) MaterialTheme.colors.secondary else WarningOrange,
)
TextIconSpaced(false)
Text(stringResource(MR.strings.database_passphrase_and_export))
Text(stringResource(MR.strings.chat_data))
}
if (stopped) {
Icon(
@@ -208,46 +169,6 @@ fun ChatLockItem(
}
}
@Composable private fun ContributeItem(uriHandler: UriHandler) {
SectionItemView({ uriHandler.openExternalLink("https://github.com/simplex-chat/simplex-chat#contribute") }) {
Icon(
painterResource(MR.images.ic_keyboard),
contentDescription = "GitHub",
tint = MaterialTheme.colors.secondary,
)
TextIconSpaced()
Text(generalGetString(MR.strings.contribute), color = MaterialTheme.colors.primary)
}
}
@Composable private fun RateAppItem(uriHandler: UriHandler) {
SectionItemView({
runCatching { uriHandler.openUriCatching("market://details?id=chat.simplex.app") }
.onFailure { uriHandler.openUriCatching("https://play.google.com/store/apps/details?id=chat.simplex.app") }
}
) {
Icon(
painterResource(MR.images.ic_star),
contentDescription = "Google Play",
tint = MaterialTheme.colors.secondary,
)
TextIconSpaced()
Text(generalGetString(MR.strings.rate_the_app), color = MaterialTheme.colors.primary)
}
}
@Composable private fun StarOnGithubItem(uriHandler: UriHandler) {
SectionItemView({ uriHandler.openExternalLink("https://github.com/simplex-chat/simplex-chat") }) {
Icon(
painter = painterResource(MR.images.ic_github),
contentDescription = "GitHub",
tint = MaterialTheme.colors.secondary,
)
TextIconSpaced()
Text(generalGetString(MR.strings.star_on_github), color = MaterialTheme.colors.primary)
}
}
@Composable fun ChatConsoleItem(showTerminal: () -> Unit) {
SectionItemView(showTerminal) {
Icon(
@@ -486,7 +407,6 @@ fun PreviewSettingsLayout() {
stopped = false,
encrypted = false,
passphraseSaved = false,
notificationsMode = remember { mutableStateOf(NotificationsMode.OFF) },
userDisplayName = "Alice",
setPerformLA = { _ -> },
showModal = { {} },
@@ -1548,6 +1548,13 @@
<string name="settings_section_title_files">Files</string>
<string name="settings_section_title_delivery_receipts">Send delivery receipts to</string>
<string name="settings_section_title_contact_requests_from_groups">Contact requests from groups</string>
<string name="settings_section_title_about">About</string>
<string name="settings_section_title_contact">Contact</string>
<string name="settings_section_title_support_project">Support the project</string>
<string name="chat_data">Chat data</string>
<string name="help_and_support">Help &amp; support</string>
<string name="more_privacy">More privacy</string>
<string name="advanced_settings">Advanced settings</string>
<string name="settings_restart_app">Restart</string>
<string name="settings_shutdown">Shutdown</string>
<string name="settings_developer_tools">Developer tools</string>
@@ -1,27 +1,21 @@
package chat.simplex.common.views.usersettings
import SectionView
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.runtime.*
import chat.simplex.common.model.ChatController.appPrefs
import chat.simplex.common.model.ChatModel
import chat.simplex.common.platform.AppUpdatesChannel
import chat.simplex.common.ui.theme.DEFAULT_PADDING_HALF
import chat.simplex.common.platform.*
import chat.simplex.common.views.helpers.*
import chat.simplex.res.MR
import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource
@Composable
actual fun SettingsSectionApp(
actual fun AdvancedSettingsAppSection(
showSettingsModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit),
showVersion: () -> Unit,
withAuth: (title: String, desc: String, block: () -> Unit) -> Unit
withAuth: (title: String, desc: String, block: () -> Unit) -> Unit,
) {
SectionView(stringResource(MR.strings.settings_section_title_app)) {
SectionView {
SettingsActionItem(painterResource(MR.images.ic_code), stringResource(MR.strings.settings_developer_tools), showSettingsModal { DeveloperView(withAuth) })
val selectedChannel = remember { appPrefs.appUpdateChannel.state }
val values = AppUpdatesChannel.entries.map { it to it.text }
@@ -29,6 +23,5 @@ actual fun SettingsSectionApp(
appPrefs.appUpdateChannel.set(it)
setupUpdateChecker()
}
AppVersionItem(showVersion)
}
}