mobile: show skipped messages in the UI (#707)

* mobile: show skipped messages in the UI

* ios: skipped messages alert and translations

* android: skipped messages alert

* android translation keys

* more keys

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>
This commit is contained in:
Evgeny Poberezkin
2022-05-29 08:06:56 +01:00
committed by GitHub
parent 89908ef5dc
commit 7c1d573a17
24 changed files with 658 additions and 45 deletions
@@ -159,7 +159,7 @@ class MainActivity: FragmentActivity(), LifecycleEventObserver {
private fun enableLA() {
val m = vm.chatModel
authenticate(
generalGetString(R.string.auth_enable),
generalGetString(R.string.auth_enable_simplex_lock),
generalGetString(R.string.auth_confirm_credential),
this@MainActivity,
completed = { laResult ->
@@ -193,7 +193,7 @@ class MainActivity: FragmentActivity(), LifecycleEventObserver {
private fun disableLA() {
val m = vm.chatModel
authenticate(
generalGetString(R.string.auth_disable),
generalGetString(R.string.auth_disable_simplex_lock),
generalGetString(R.string.auth_confirm_credential),
this@MainActivity,
completed = { laResult ->
@@ -843,10 +843,11 @@ sealed class CIContent: ItemContent {
@Serializable @SerialName("sndMsgContent") class SndMsgContent(override val msgContent: MsgContent): CIContent()
@Serializable @SerialName("rcvMsgContent") class RcvMsgContent(override val msgContent: MsgContent): CIContent()
@Serializable @SerialName("sndDeleted") class SndDeleted(val deleteMode: CIDeleteMode): CIContent() { override val msgContent get() = null }
@Serializable @SerialName("rcvDeleted") class RcvDeleted(val deleteMode: CIDeleteMode): CIContent() { override val msgContent get() = null }
@Serializable @SerialName("sndCall") class SndCall(val status: CICallStatus, val duration: Int): CIContent() { override val msgContent get() = null }
@Serializable @SerialName("rcvCall") class RcvCall(val status: CICallStatus, val duration: Int): CIContent() { override val msgContent get() = null }
@Serializable @SerialName("sndDeleted") class SndDeleted(val deleteMode: CIDeleteMode): CIContent() { override val msgContent: MsgContent? get() = null }
@Serializable @SerialName("rcvDeleted") class RcvDeleted(val deleteMode: CIDeleteMode): CIContent() { override val msgContent: MsgContent? get() = null }
@Serializable @SerialName("sndCall") class SndCall(val status: CICallStatus, val duration: Int): CIContent() { override val msgContent: MsgContent? get() = null }
@Serializable @SerialName("rcvCall") class RcvCall(val status: CICallStatus, val duration: Int): CIContent() { override val msgContent: MsgContent? get() = null }
@Serializable @SerialName("rcvIntegrityError") class RcvIntegrityError(val msgError: MsgErrorType): CIContent() { override val msgContent: MsgContent? get() = null }
override val text: String get() = when(this) {
is SndMsgContent -> msgContent.text
@@ -855,6 +856,7 @@ sealed class CIContent: ItemContent {
is RcvDeleted -> generalGetString(R.string.deleted_description)
is SndCall -> status.text(duration)
is RcvCall -> status.text(duration)
is RcvIntegrityError -> msgError.text
}
}
@@ -1118,3 +1120,18 @@ enum class CICallStatus {
fun duration(sec: Int): String = "%02d:%02d".format(sec / 60, sec % 60)
}
@Serializable
sealed class MsgErrorType() {
@Serializable @SerialName("msgSkipped") class MsgSkipped(val fromMsgId: Long, val toMsgId: Long): MsgErrorType()
@Serializable @SerialName("msgBadId") class MsgBadId(val msgId: Long): MsgErrorType()
@Serializable @SerialName("msgBadHash") class MsgBadHash(): MsgErrorType()
@Serializable @SerialName("msgDuplicate") class MsgDuplicate(): MsgErrorType()
val text: String get() = when (this) {
is MsgSkipped -> String.format(generalGetString(R.string.integrity_msg_skipped), toMsgId - fromMsgId + 1)
is MsgBadHash -> generalGetString(R.string.integrity_msg_bad_hash) // not used now
is MsgBadId -> generalGetString(R.string.integrity_msg_bad_id) // not used now
is MsgDuplicate -> generalGetString(R.string.integrity_msg_duplicate) // not used now
}
}
@@ -762,12 +762,12 @@ open class ChatController(private val ctrl: ChatCtrl, val ntfManager: NtfManager
if (!appPrefs.laNoticeShown.get()) {
appPrefs.laNoticeShown.set(true)
AlertManager.shared.showAlertDialog(
title = generalGetString(R.string.la_notice_title),
text = generalGetString(R.string.la_notice_text),
title = generalGetString(R.string.la_notice_title_simplex_lock),
text = generalGetString(R.string.la_notice_to_protect_your_information_turn_on_simplex_lock_you_will_be_prompted_to_complete_authentication_before_this_feature_is_enabled),
confirmText = generalGetString(R.string.la_notice_turn_on),
onConfirm = {
authenticate(
generalGetString(R.string.auth_enable),
generalGetString(R.string.auth_enable_simplex_lock),
generalGetString(R.string.auth_confirm_credential),
activity,
completed = { laResult ->
@@ -144,6 +144,7 @@ fun ChatItemView(
is CIContent.RcvDeleted -> DeletedItem()
is CIContent.SndCall -> CallItem(c.status, c.duration)
is CIContent.RcvCall -> CallItem(c.status, c.duration)
is CIContent.RcvIntegrityError -> IntegrityErrorItemView(cItem, showMember = showMember)
}
}
}
@@ -0,0 +1,67 @@
package chat.simplex.app.views.chat.item
import android.content.res.Configuration
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.*
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import chat.simplex.app.R
import chat.simplex.app.model.ChatItem
import chat.simplex.app.ui.theme.HighOrLowlight
import chat.simplex.app.ui.theme.SimpleXTheme
import chat.simplex.app.views.helpers.AlertManager
import chat.simplex.app.views.helpers.generalGetString
@Composable
fun IntegrityErrorItemView(ci: ChatItem, showMember: Boolean = false) {
Surface(
Modifier.clickable(onClick = {
AlertManager.shared.showAlertMsg(
title = generalGetString(R.string.alert_title_skipped_messages),
text = generalGetString(R.string.alert_text_skipped_messages_it_can_happen_when)
)
}),
shape = RoundedCornerShape(18.dp),
color = ReceivedColorLight,
) {
Row(
Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
verticalAlignment = Alignment.Bottom
) {
Text(
buildAnnotatedString {
appendSender(this, if (showMember) ci.memberDisplayName else null, true)
withStyle(SpanStyle(fontStyle = FontStyle.Italic, color = Color.Red)) { append(ci.content.text) }
},
style = MaterialTheme.typography.body1.copy(lineHeight = 22.sp),
modifier = Modifier.padding(end = 8.dp)
)
CIMetaView(ci)
}
}
}
@Preview(showBackground = true)
@Preview(
uiMode = Configuration.UI_MODE_NIGHT_YES,
name = "Dark Mode"
)
@Composable
fun IntegrityErrorItemViewView() {
SimpleXTheme {
IntegrityErrorItemView(
ChatItem.getDeletedContentSampleData()
)
}
}
@@ -85,8 +85,8 @@ private fun authenticateWithBiometricManager(
}
fun laTurnedOnAlert() = AlertManager.shared.showAlertMsg(
generalGetString(R.string.auth_turned_on),
generalGetString(R.string.auth_turned_on_desc)
generalGetString(R.string.auth_simplex_lock_turned_on),
generalGetString(R.string.auth_you_will_be_required_to_authenticate_when_you_start_or_resume)
)
fun laErrorToast(context: Context, errString: CharSequence) = Toast.makeText(
@@ -103,10 +103,10 @@ fun laFailedToast(context: Context) = Toast.makeText(
fun laUnavailableInstructionAlert() = AlertManager.shared.showAlertMsg(
generalGetString(R.string.auth_unavailable),
generalGetString(R.string.auth_unavailable_instruction_desc)
generalGetString(R.string.auth_device_authentication_is_not_enabled_you_can_turn_on_in_settings_once_enabled)
)
fun laUnavailableTurningOffAlert() = AlertManager.shared.showAlertMsg(
generalGetString(R.string.auth_unavailable),
generalGetString(R.string.auth_unavailable_turning_off_desc)
generalGetString(R.string.auth_device_authentication_is_disabled_turning_off)
)
@@ -65,24 +65,24 @@
<string name="simplex_service_notification_text">Приём сообщений…</string>
<!-- local authentication notice - SimpleXAPI.kt -->
<string name="la_notice_title">Блокировка SimpleX</string>
<string name="la_notice_text">Чтобы защитить вашу информацию, включите блокировку <xliff:g id="appNameFull">SimpleX Chat</xliff:g>.\nВам будет нужно пройти аутентификацию для включения блокировки.</string>
<string name="la_notice_title_simplex_lock">Блокировка SimpleX</string>
<string name="la_notice_to_protect_your_information_turn_on_simplex_lock_you_will_be_prompted_to_complete_authentication_before_this_feature_is_enabled">Чтобы защитить вашу информацию, включите блокировку <xliff:g id="appNameFull">SimpleX Chat</xliff:g>.\nВам будет нужно пройти аутентификацию для включения блокировки.</string>
<string name="la_notice_turn_on">Включить</string>
<!-- LocalAuthentication.kt -->
<string name="auth_turned_on">Блокировка SimpleX включена</string>
<string name="auth_turned_on_desc">Вы будете аутентифицированы при запуске и возобновлении приложения, которое было 30 секунд в фоновом режиме.</string>
<string name="auth_simplex_lock_turned_on">Блокировка SimpleX включена</string>
<string name="auth_you_will_be_required_to_authenticate_when_you_start_or_resume">Вы будете аутентифицированы при запуске и возобновлении приложения, которое было 30 секунд в фоновом режиме.</string>
<string name="auth_unlock">Разблокировать</string>
<string name="auth_log_in_using_credential">Пройдите аутентификацию</string>
<string name="auth_enable">Включить блокировку SimpleX</string>
<string name="auth_disable">Отключить блокировку SimpleX</string>
<string name="auth_enable_simplex_lock">Включить блокировку SimpleX</string>
<string name="auth_disable_simplex_lock">Отключить блокировку SimpleX</string>
<string name="auth_confirm_credential">Пройдите аутентификацию</string>
<string name="auth_error">Ошибка аутентификации</string>
<string name="auth_error_w_desc">Ошибка аутентификации: <xliff:g id="desc">%1$s</xliff:g></string>
<string name="auth_failed">Ошибка аутентификации</string>
<string name="auth_unavailable">Аутентификация недоступна</string>
<string name="auth_unavailable_instruction_desc">На устройстве не включена аутентификация. Вы можете включить блокировку SimpleX в Настройках после включения аутентификации.</string>
<string name="auth_unavailable_turning_off_desc">На устройстве выключена аутентификация. Отключение блокировки SimpleX Chat.</string>
<string name="auth_device_authentication_is_not_enabled_you_can_turn_on_in_settings_once_enabled">Аутентификация устройства не включена. Вы можете включить блокировку SimpleX в Настройках после включения аутентификации.</string>
<string name="auth_device_authentication_is_disabled_turning_off">Аутентификация устройства выключена. Отключение блокировки SimpleX Chat.</string>
<string name="auth_retry">Повторить</string>
<!-- Chat Actions - ChatItemView.kt (and general) -->
@@ -413,4 +413,12 @@
<string name="icon_descr_call_progress">Текущий звонок</string>
<string name="icon_descr_call_ended">Звонок завершен</string>
<string name="answer_call">Принять звонок</string>
<!-- Message integrity -->
<string name="integrity_msg_skipped"><xliff:g id="connection ID" example="1">%1$d</xliff:g> пропущенных сообщений"</string>
<string name="integrity_msg_bad_hash">ошибка хэш сообщения</string>
<string name="integrity_msg_bad_id">ошибка ID сообщения</string>
<string name="integrity_msg_duplicate">повторное сообщение</string>
<string name="alert_title_skipped_messages">Пропущенные сообщения</string>
<string name="alert_text_skipped_messages_it_can_happen_when">Это может случится, когда:\n1. Сервер удалил сообщения, если они не были доставлены в течение 30 дней.\n2. Сервер, через который вы получаете сообщения от контакта, был обновлён и перезапущен.\n3. Соединение компроментировано.\nПожалуйста, соединитесь с девелоперами через Настройки, чтобы получать уведомления о серверах.\nМы планируем добавить избыточную доставку сообщений, чтобы не терять сообщения.</string>
</resources>
@@ -65,24 +65,24 @@
<string name="simplex_service_notification_text">Receiving messages…</string>
<!-- local authentication notice - SimpleXAPI.kt -->
<string name="la_notice_title">SimpleX Lock</string>
<string name="la_notice_text">To protect your information, turn on SimpleX Lock.\nYou will be prompted to complete authentication before this feature is enabled.</string>
<string name="la_notice_title_simplex_lock">SimpleX Lock</string>
<string name="la_notice_to_protect_your_information_turn_on_simplex_lock_you_will_be_prompted_to_complete_authentication_before_this_feature_is_enabled">To protect your information, turn on SimpleX Lock.\nYou will be prompted to complete authentication before this feature is enabled.</string>
<string name="la_notice_turn_on">Turn on</string>
<!-- LocalAuthentication.kt -->
<string name="auth_turned_on">SimpleX Lock turned on</string>
<string name="auth_turned_on_desc">You will be required to authenticate when you start or resume the app after 30 seconds in background.</string>
<string name="auth_simplex_lock_turned_on">SimpleX Lock turned on</string>
<string name="auth_you_will_be_required_to_authenticate_when_you_start_or_resume">You will be required to authenticate when you start or resume the app after 30 seconds in background.</string>
<string name="auth_unlock">Unlock</string>
<string name="auth_log_in_using_credential">Log in using your credential</string>
<string name="auth_enable">Enable SimpleX Lock</string>
<string name="auth_disable">Disable SimpleX Lock</string>
<string name="auth_enable_simplex_lock">Enable SimpleX Lock</string>
<string name="auth_disable_simplex_lock">Disable SimpleX Lock</string>
<string name="auth_confirm_credential">Confirm your credential</string>
<string name="auth_error">Authentication error</string>
<string name="auth_error_w_desc">Authentication error: <xliff:g id="desc">%1$s</xliff:g></string>
<string name="auth_failed">Authentication failed</string>
<string name="auth_unavailable">Authentication unavailable</string>
<string name="auth_unavailable_instruction_desc">Device authentication is not enabled. You can turn on SimpleX Lock via Settings, once you enable device authentication.</string>
<string name="auth_unavailable_turning_off_desc">Device authentication is disabled. Turning off SimpleX Lock.</string>
<string name="auth_device_authentication_is_not_enabled_you_can_turn_on_in_settings_once_enabled">Device authentication is not enabled. You can turn on SimpleX Lock via Settings, once you enable device authentication.</string>
<string name="auth_device_authentication_is_disabled_turning_off">Device authentication is disabled. Turning off SimpleX Lock.</string>
<string name="auth_retry">Retry</string>
<!-- Chat Actions - ChatItemView.kt (and general) -->
@@ -415,4 +415,12 @@
<string name="icon_descr_call_progress">Call in progress</string>
<string name="icon_descr_call_ended">Call ended</string>
<string name="answer_call">Answer call</string>
<!-- Message integrity -->
<string name="integrity_msg_skipped"><xliff:g id="connection ID" example="1">%1$d</xliff:g> skipped message(s)"</string>
<string name="integrity_msg_bad_hash">bad message hash</string>
<string name="integrity_msg_bad_id">bad message ID</string>
<string name="integrity_msg_duplicate">duplicate message</string>
<string name="alert_title_skipped_messages">Skipped messages</string>
<string name="alert_text_skipped_messages_it_can_happen_when">It can happen when:\n1. The messages expire on the server if they were not received for 30 days,\n2. The server you use to receive the messages from this contact was updated and restarted.\n3. The connection is compromised.\nPlease connect to the developers via Settings to receive the updates about the servers.\nWe will be adding server redundancy to prevent lost messages.</string>
</resources>