mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-03-30 20:45:49 +00:00
android: show what is new in the latest version (#1651)
This commit is contained in:
@@ -132,6 +132,8 @@ class AppPreferences(val context: Context) {
|
||||
val currentTheme = mkStrPreference(SHARED_PREFS_CURRENT_THEME, DefaultTheme.SYSTEM.name)
|
||||
val primaryColor = mkIntPreference(SHARED_PREFS_PRIMARY_COLOR, LightColorPalette.primary.toArgb())
|
||||
|
||||
val whatsNewVersion = mkStrPreference(SHARED_PREFS_WHATS_NEW_VERSION, null)
|
||||
|
||||
private fun mkIntPreference(prefName: String, default: Int) =
|
||||
SharedPreference(
|
||||
get = fun() = sharedPreferences.getInt(prefName, default),
|
||||
@@ -223,6 +225,7 @@ class AppPreferences(val context: Context) {
|
||||
private const val SHARED_PREFS_ENCRYPTION_STARTED_AT = "EncryptionStartedAt"
|
||||
private const val SHARED_PREFS_CURRENT_THEME = "CurrentTheme"
|
||||
private const val SHARED_PREFS_PRIMARY_COLOR = "PrimaryColor"
|
||||
private const val SHARED_PREFS_WHATS_NEW_VERSION = "WhatsNewVersion"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import chat.simplex.app.views.helpers.annotatedStringResource
|
||||
import chat.simplex.app.views.usersettings.MarkdownHelpView
|
||||
import chat.simplex.app.views.usersettings.simplexTeamUri
|
||||
|
||||
val bold = SpanStyle(fontWeight = FontWeight.Bold)
|
||||
@@ -76,6 +77,15 @@ fun ChatHelpView(addContact: (() -> Unit)? = null) {
|
||||
Text(annotatedStringResource(R.string.desktop_scan_QR_code_from_app_via_scan_QR_code), lineHeight = 22.sp)
|
||||
Text(annotatedStringResource(R.string.mobile_tap_open_in_mobile_app_then_tap_connect_in_app), lineHeight = 22.sp)
|
||||
}
|
||||
|
||||
Column(
|
||||
Modifier.padding(vertical = 24.dp),
|
||||
horizontalAlignment = Alignment.Start,
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp)
|
||||
) {
|
||||
Text(stringResource(R.string.markdown_in_messages), style = MaterialTheme.typography.h2)
|
||||
MarkdownHelpView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,11 @@ import chat.simplex.app.model.*
|
||||
import chat.simplex.app.ui.theme.*
|
||||
import chat.simplex.app.views.helpers.*
|
||||
import chat.simplex.app.views.newchat.NewChatSheet
|
||||
import chat.simplex.app.views.onboarding.WhatsNewView
|
||||
import chat.simplex.app.views.onboarding.shouldShowWhatsNew
|
||||
import chat.simplex.app.views.usersettings.SettingsView
|
||||
import chat.simplex.app.views.usersettings.simplexTeamUri
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@@ -42,6 +45,12 @@ fun ChatListView(chatModel: ChatModel, setPerformLA: (Boolean) -> Unit, stopped:
|
||||
if (animated) newChatSheetState.value = NewChatSheetState.HIDING
|
||||
else newChatSheetState.value = NewChatSheetState.GONE
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
if (shouldShowWhatsNew(chatModel)) {
|
||||
delay(1000L)
|
||||
ModalManager.shared.showCustomModal { close -> WhatsNewView(close = close) }
|
||||
}
|
||||
}
|
||||
LaunchedEffect(chatModel.clearOverlays.value) {
|
||||
if (chatModel.clearOverlays.value && newChatSheetState.value.isVisible()) hideNewChatSheet(false)
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ fun CloseSheetBar(close: () -> Unit) {
|
||||
@Composable
|
||||
fun AppBarTitle(title: String, withPadding: Boolean = true) {
|
||||
val padding = if (withPadding)
|
||||
PaddingValues(start = DEFAULT_PADDING, end = DEFAULT_PADDING, bottom = DEFAULT_PADDING )
|
||||
PaddingValues(start = DEFAULT_PADDING, end = DEFAULT_PADDING, bottom = DEFAULT_PADDING)
|
||||
else
|
||||
PaddingValues(bottom = DEFAULT_PADDING)
|
||||
Text(
|
||||
|
||||
@@ -35,6 +35,9 @@ fun CreateProfile(chatModel: ChatModel) {
|
||||
.padding(20.dp)
|
||||
) {
|
||||
CreateProfilePanel(chatModel)
|
||||
LaunchedEffect(Unit) {
|
||||
setLastVersionDefault(chatModel)
|
||||
}
|
||||
if (savedKeyboardState != keyboardState) {
|
||||
LaunchedEffect(keyboardState) {
|
||||
scope.launch {
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
package chat.simplex.app.views.onboarding
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.ChatModel
|
||||
import chat.simplex.app.ui.theme.*
|
||||
import chat.simplex.app.views.helpers.*
|
||||
|
||||
@Composable
|
||||
fun WhatsNewView(viaSettings: Boolean = false, close: () -> Unit) {
|
||||
val currentVersion = remember { mutableStateOf(versionDescriptions.lastIndex) }
|
||||
|
||||
@Composable
|
||||
fun featureDescription(icon: ImageVector, titleId: Int, descrId: Int, link: String?) {
|
||||
@Composable
|
||||
fun linkButton(link: String) {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
Icon(
|
||||
Icons.Outlined.OpenInNew, stringResource(titleId), tint = MaterialTheme.colors.primary,
|
||||
modifier = Modifier
|
||||
.clickable { uriHandler.openUri(link) }
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.Start
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Icon(icon, stringResource(titleId), tint = HighOrLowlight)
|
||||
Text(
|
||||
generalGetString(titleId),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = MaterialTheme.typography.h3,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
if (link != null) {
|
||||
linkButton(link)
|
||||
}
|
||||
}
|
||||
Text(generalGetString(descrId))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun pagination() {
|
||||
Row(
|
||||
Modifier
|
||||
.padding(bottom = 16.dp)
|
||||
) {
|
||||
if (currentVersion.value > 0) {
|
||||
val prev = currentVersion.value - 1
|
||||
Surface(shape = RoundedCornerShape(20.dp)) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier = Modifier
|
||||
.clickable { currentVersion.value = prev }
|
||||
.padding(8.dp)
|
||||
) {
|
||||
Icon(Icons.Outlined.ArrowBackIosNew, "previous", tint = MaterialTheme.colors.primary)
|
||||
Text(versionDescriptions[prev].version, color = MaterialTheme.colors.primary)
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.fillMaxWidth().weight(1f))
|
||||
if (currentVersion.value < versionDescriptions.lastIndex) {
|
||||
val next = currentVersion.value + 1
|
||||
Surface(shape = RoundedCornerShape(20.dp)) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier = Modifier
|
||||
.clickable { currentVersion.value = next }
|
||||
.padding(8.dp)
|
||||
) {
|
||||
Text(versionDescriptions[next].version, color = MaterialTheme.colors.primary)
|
||||
Icon(Icons.Outlined.ArrowForwardIos, "next", tint = MaterialTheme.colors.primary)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val v = versionDescriptions[currentVersion.value]
|
||||
|
||||
ModalView(close = close) {
|
||||
Column(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = DEFAULT_PADDING),
|
||||
horizontalAlignment = Alignment.Start,
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
Text(
|
||||
String.format(generalGetString(R.string.new_in_version), v.version),
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(DEFAULT_PADDING),
|
||||
textAlign = TextAlign.Center,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = MaterialTheme.typography.h1,
|
||||
fontWeight = FontWeight.Normal,
|
||||
color = HighOrLowlight
|
||||
)
|
||||
|
||||
v.features.forEach { feature ->
|
||||
featureDescription(feature.icon, feature.titleId, feature.descrId, feature.link)
|
||||
}
|
||||
|
||||
if (!viaSettings) {
|
||||
Spacer(Modifier.fillMaxHeight().weight(1f))
|
||||
Box(
|
||||
Modifier.fillMaxWidth(), contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
generalGetString(R.string.ok),
|
||||
modifier = Modifier.clickable(onClick = close),
|
||||
style = MaterialTheme.typography.h3,
|
||||
color = MaterialTheme.colors.primary
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.fillMaxHeight().weight(1f))
|
||||
}
|
||||
|
||||
Spacer(Modifier.fillMaxHeight().weight(1f))
|
||||
|
||||
pagination()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private data class FeatureDescription(
|
||||
val icon: ImageVector,
|
||||
val titleId: Int,
|
||||
val descrId: Int,
|
||||
val link: String? = null
|
||||
)
|
||||
|
||||
private data class VersionDescription(
|
||||
val version: String,
|
||||
val features: List<FeatureDescription>
|
||||
)
|
||||
|
||||
private val versionDescriptions: List<VersionDescription> = listOf(
|
||||
VersionDescription(
|
||||
version = "v4.2",
|
||||
features = listOf(
|
||||
FeatureDescription(
|
||||
icon = Icons.Outlined.VerifiedUser,
|
||||
titleId = R.string.v4_2_security_assessment,
|
||||
descrId = R.string.v4_2_security_assessment_desc,
|
||||
link = "https://simplex.chat/blog/20221108-simplex-chat-v4.2-security-audit-new-website.html"
|
||||
),
|
||||
FeatureDescription(
|
||||
icon = Icons.Outlined.Group,
|
||||
titleId = R.string.v4_2_group_links,
|
||||
descrId = R.string.v4_2_group_links_desc
|
||||
),
|
||||
FeatureDescription(
|
||||
icon = Icons.Outlined.Check,
|
||||
titleId = R.string.v4_2_auto_accept_contact_requests,
|
||||
descrId = R.string.v4_2_auto_accept_contact_requests_desc
|
||||
),
|
||||
)
|
||||
),
|
||||
VersionDescription(
|
||||
version = "v4.3",
|
||||
features = listOf(
|
||||
FeatureDescription(
|
||||
icon = Icons.Outlined.Mic,
|
||||
titleId = R.string.v4_3_voice_messages,
|
||||
descrId = R.string.v4_3_voice_messages_desc
|
||||
),
|
||||
FeatureDescription(
|
||||
icon = Icons.Outlined.DeleteForever,
|
||||
titleId = R.string.v4_3_irreversible_message_deletion,
|
||||
descrId = R.string.v4_3_irreversible_message_deletion_desc
|
||||
),
|
||||
FeatureDescription(
|
||||
icon = Icons.Outlined.WifiTethering,
|
||||
titleId = R.string.v4_3_improved_server_configuration,
|
||||
descrId = R.string.v4_3_improved_server_configuration_desc
|
||||
),
|
||||
FeatureDescription(
|
||||
icon = Icons.Outlined.VisibilityOff,
|
||||
titleId = R.string.v4_3_improved_privacy_and_security,
|
||||
descrId = R.string.v4_3_improved_privacy_and_security_desc
|
||||
),
|
||||
)
|
||||
),
|
||||
VersionDescription(
|
||||
version = "v4.4",
|
||||
features = listOf(
|
||||
FeatureDescription(
|
||||
icon = Icons.Outlined.Timer,
|
||||
titleId = R.string.v4_4_disappearing_messages,
|
||||
descrId = R.string.v4_4_disappearing_messages_desc
|
||||
),
|
||||
FeatureDescription(
|
||||
icon = Icons.Outlined.Pending,
|
||||
titleId = R.string.v4_4_live_messages,
|
||||
descrId = R.string.v4_4_live_messages_desc
|
||||
),
|
||||
FeatureDescription(
|
||||
icon = Icons.Outlined.VerifiedUser,
|
||||
titleId = R.string.v4_4_verify_connection_security,
|
||||
descrId = R.string.v4_4_verify_connection_security_desc
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
private val lastVersion = versionDescriptions.last().version
|
||||
|
||||
fun setLastVersionDefault(m: ChatModel) {
|
||||
m.controller.appPrefs.whatsNewVersion.set(lastVersion)
|
||||
}
|
||||
|
||||
fun shouldShowWhatsNew(m: ChatModel): Boolean {
|
||||
val v = m.controller.appPrefs.whatsNewVersion.get()
|
||||
setLastVersionDefault(m)
|
||||
return v != lastVersion
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Preview(
|
||||
uiMode = Configuration.UI_MODE_NIGHT_YES,
|
||||
showBackground = true,
|
||||
name = "Dark Mode"
|
||||
)
|
||||
@Composable
|
||||
fun PreviewWhatsNewView() {
|
||||
SimpleXTheme {
|
||||
WhatsNewView(
|
||||
viaSettings = true,
|
||||
close = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -17,18 +17,13 @@ import chat.simplex.app.model.Format
|
||||
import chat.simplex.app.model.FormatColor
|
||||
import chat.simplex.app.ui.theme.DEFAULT_PADDING
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import chat.simplex.app.views.helpers.AppBarTitle
|
||||
import chat.simplex.app.views.helpers.generalGetString
|
||||
|
||||
@Composable
|
||||
fun MarkdownHelpView() {
|
||||
Column(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(horizontal = DEFAULT_PADDING)
|
||||
) {
|
||||
AppBarTitle(stringResource(R.string.how_to_use_markdown), false)
|
||||
Text(stringResource(R.string.you_can_use_markdown_to_format_messages__prompt))
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
val bold = stringResource(R.string.bold)
|
||||
|
||||
@@ -34,6 +34,7 @@ import chat.simplex.app.views.helpers.*
|
||||
import chat.simplex.app.views.newchat.CreateLinkTab
|
||||
import chat.simplex.app.views.newchat.CreateLinkView
|
||||
import chat.simplex.app.views.onboarding.SimpleXInfo
|
||||
import chat.simplex.app.views.onboarding.WhatsNewView
|
||||
|
||||
@Composable
|
||||
fun SettingsView(chatModel: ChatModel, setPerformLA: (Boolean) -> Unit) {
|
||||
@@ -138,9 +139,9 @@ fun SettingsLayout(
|
||||
SectionView(stringResource(R.string.settings_section_title_help)) {
|
||||
SettingsActionItem(Icons.Outlined.HelpOutline, stringResource(R.string.how_to_use_simplex_chat), showModal { HelpView(userDisplayName) }, disabled = stopped)
|
||||
SectionDivider()
|
||||
SettingsActionItem(Icons.Outlined.Info, stringResource(R.string.about_simplex_chat), showModal { SimpleXInfo(it, onboarding = false) })
|
||||
SettingsActionItem(Icons.Outlined.Add, stringResource(R.string.whats_new), showCustomModal { _, close -> WhatsNewView(viaSettings = true, close) }, disabled = stopped)
|
||||
SectionDivider()
|
||||
SettingsActionItem(Icons.Outlined.TextFormat, stringResource(R.string.markdown_in_messages), showModal { MarkdownHelpView() })
|
||||
SettingsActionItem(Icons.Outlined.Info, stringResource(R.string.about_simplex_chat), showModal { SimpleXInfo(it, onboarding = false) })
|
||||
SectionDivider()
|
||||
SettingsActionItem(Icons.Outlined.Tag, stringResource(R.string.chat_with_the_founder), { uriHandler.openUri(simplexTeamUri) }, textColor = MaterialTheme.colors.primary, disabled = stopped)
|
||||
SectionDivider()
|
||||
@@ -488,7 +489,7 @@ fun PreviewSettingsLayout() {
|
||||
setPerformLA = {},
|
||||
showModal = { {} },
|
||||
showSettingsModal = { {} },
|
||||
showCustomModal = { {}},
|
||||
showCustomModal = { {} },
|
||||
showTerminal = {},
|
||||
// showVideoChatPrototype = {}
|
||||
)
|
||||
|
||||
@@ -1055,4 +1055,28 @@
|
||||
<string name="ttl_week">%d week</string>
|
||||
<string name="ttl_weeks">%d weeks</string>
|
||||
<string name="ttl_w">%dw</string>
|
||||
|
||||
<!-- WhatsNewView.kt -->
|
||||
<string name="whats_new">What\'s new</string>
|
||||
<string name="new_in_version">New in %s</string>
|
||||
<string name="v4_2_security_assessment">Security assessment</string>
|
||||
<string name="v4_2_security_assessment_desc">SimpleX Chat security was audited by Trail of Bits.</string>
|
||||
<string name="v4_2_group_links">Group links</string>
|
||||
<string name="v4_2_group_links_desc">Admins can create the links to join groups.</string>
|
||||
<string name="v4_2_auto_accept_contact_requests">Auto-accept contact requests</string>
|
||||
<string name="v4_2_auto_accept_contact_requests_desc">With optional welcome message.</string>
|
||||
<string name="v4_3_voice_messages">Voice messages</string>
|
||||
<string name="v4_3_voice_messages_desc">Max 40 seconds, received instantly.</string>
|
||||
<string name="v4_3_irreversible_message_deletion">Irreversible message deletion</string>
|
||||
<string name="v4_3_irreversible_message_deletion_desc">Your contacts can allow full message deletion.</string>
|
||||
<string name="v4_3_improved_server_configuration">Improved server configuration</string>
|
||||
<string name="v4_3_improved_server_configuration_desc">Add servers by scanning QR codes.</string>
|
||||
<string name="v4_3_improved_privacy_and_security">Improved privacy and security</string>
|
||||
<string name="v4_3_improved_privacy_and_security_desc">Hide app screen in the recent apps.</string>
|
||||
<string name="v4_4_disappearing_messages">Disappearing messages</string>
|
||||
<string name="v4_4_disappearing_messages_desc">Sent messages will be deleted after set time.</string>
|
||||
<string name="v4_4_live_messages">Live messages</string>
|
||||
<string name="v4_4_live_messages_desc">Recipients see updates as you type them.</string>
|
||||
<string name="v4_4_verify_connection_security">Verify connection security</string>
|
||||
<string name="v4_4_verify_connection_security_desc">Compare security codes with your contacts.</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user