Merge branch 'release/0.18.1'
This commit is contained in:
commit
367f793929
13
CHANGES.md
13
CHANGES.md
|
@ -1,3 +1,16 @@
|
||||||
|
Changes in RiotX 0.18.1 (2020-03-17)
|
||||||
|
===================================================
|
||||||
|
|
||||||
|
Improvements 🙌:
|
||||||
|
- Implementation of /join command
|
||||||
|
|
||||||
|
Bugfix 🐛:
|
||||||
|
- Message transitions in encrypted rooms are jarring #518
|
||||||
|
- Images that failed to send are waiting to be sent forever #1145
|
||||||
|
- Fix / Crashed when trying to send a gif from the Gboard #1136
|
||||||
|
- Fix / Cannot click on key backup banner when new keys are available
|
||||||
|
|
||||||
|
|
||||||
Changes in RiotX 0.18.0 (2020-03-11)
|
Changes in RiotX 0.18.0 (2020-03-11)
|
||||||
===================================================
|
===================================================
|
||||||
|
|
||||||
|
|
|
@ -193,6 +193,7 @@ internal class DefaultSession @Inject constructor(
|
||||||
stopAnyBackgroundSync()
|
stopAnyBackgroundSync()
|
||||||
liveEntityObservers.forEach { it.cancelProcess() }
|
liveEntityObservers.forEach { it.cancelProcess() }
|
||||||
cacheService.get().clearCache(callback)
|
cacheService.get().clearCache(callback)
|
||||||
|
workManagerProvider.cancelAllWorks()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
|
|
|
@ -77,6 +77,16 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter
|
||||||
return Result.success(inputData)
|
return Result.success(inputData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Just defensive code to ensure that we never have an uncaught exception that could break the queue
|
||||||
|
return try {
|
||||||
|
internalDoWork(params)
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
Timber.e(failure)
|
||||||
|
handleFailure(params, failure)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun internalDoWork(params: Params): Result {
|
||||||
val sessionComponent = getSessionComponent(params.sessionId) ?: return Result.success()
|
val sessionComponent = getSessionComponent(params.sessionId) ?: return Result.success()
|
||||||
sessionComponent.inject(this)
|
sessionComponent.inject(this)
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,10 @@ import com.squareup.moshi.JsonClass
|
||||||
import im.vector.matrix.android.api.failure.Failure
|
import im.vector.matrix.android.api.failure.Failure
|
||||||
import im.vector.matrix.android.api.session.crypto.CryptoService
|
import im.vector.matrix.android.api.session.crypto.CryptoService
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
|
import im.vector.matrix.android.api.session.events.model.toContent
|
||||||
import im.vector.matrix.android.api.session.room.send.SendState
|
import im.vector.matrix.android.api.session.room.send.SendState
|
||||||
|
import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
||||||
|
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
|
||||||
import im.vector.matrix.android.internal.crypto.model.MXEncryptEventContentResult
|
import im.vector.matrix.android.internal.crypto.model.MXEncryptEventContentResult
|
||||||
import im.vector.matrix.android.internal.util.awaitCallback
|
import im.vector.matrix.android.internal.util.awaitCallback
|
||||||
import im.vector.matrix.android.internal.worker.SessionWorkerParams
|
import im.vector.matrix.android.internal.worker.SessionWorkerParams
|
||||||
|
@ -96,6 +99,22 @@ internal class EncryptEventWorker(context: Context, params: WorkerParameters)
|
||||||
type = safeResult.eventType,
|
type = safeResult.eventType,
|
||||||
content = safeResult.eventContent
|
content = safeResult.eventContent
|
||||||
)
|
)
|
||||||
|
// Better handling of local echo, to avoid decrypting transition on remote echo
|
||||||
|
// Should I only do it for text messages?
|
||||||
|
if (result.eventContent["algorithm"] == MXCRYPTO_ALGORITHM_MEGOLM) {
|
||||||
|
val decryptionLocalEcho = MXEventDecryptionResult(
|
||||||
|
clearEvent = Event(
|
||||||
|
type = localEvent.type,
|
||||||
|
content = localEvent.content,
|
||||||
|
roomId = localEvent.roomId
|
||||||
|
).toContent(),
|
||||||
|
forwardingCurve25519KeyChain = emptyList(),
|
||||||
|
senderCurve25519Key = result.eventContent["sender_key"] as? String,
|
||||||
|
claimedEd25519Key = crypto.getMyDevice().fingerprint()
|
||||||
|
)
|
||||||
|
localEchoUpdater.updateEncryptedEcho(localEvent.eventId, safeResult.eventContent, decryptionLocalEcho)
|
||||||
|
}
|
||||||
|
|
||||||
val nextWorkerParams = SendEventWorker.Params(params.sessionId, encryptedEvent)
|
val nextWorkerParams = SendEventWorker.Params(params.sessionId, encryptedEvent)
|
||||||
return Result.success(WorkerParamsFactory.toData(nextWorkerParams))
|
return Result.success(WorkerParamsFactory.toData(nextWorkerParams))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -17,7 +17,11 @@
|
||||||
package im.vector.matrix.android.internal.session.room.send
|
package im.vector.matrix.android.internal.session.room.send
|
||||||
|
|
||||||
import com.zhuinden.monarchy.Monarchy
|
import com.zhuinden.monarchy.Monarchy
|
||||||
|
import im.vector.matrix.android.api.session.events.model.Content
|
||||||
|
import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
import im.vector.matrix.android.api.session.room.send.SendState
|
import im.vector.matrix.android.api.session.room.send.SendState
|
||||||
|
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
|
||||||
|
import im.vector.matrix.android.internal.database.mapper.ContentMapper
|
||||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
@ -38,4 +42,15 @@ internal class LocalEchoUpdater @Inject constructor(private val monarchy: Monarc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun updateEncryptedEcho(eventId: String, encryptedContent: Content, mxEventDecryptionResult: MXEventDecryptionResult) {
|
||||||
|
monarchy.writeAsync { realm ->
|
||||||
|
val sendingEventEntity = EventEntity.where(realm, eventId).findFirst()
|
||||||
|
if (sendingEventEntity != null) {
|
||||||
|
sendingEventEntity.type = EventType.ENCRYPTED
|
||||||
|
sendingEventEntity.content = ContentMapper.map(encryptedContent)
|
||||||
|
sendingEventEntity.setDecryptionResult(mxEventDecryptionResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import androidx.work.OneTimeWorkRequest
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
import com.squareup.moshi.JsonClass
|
import com.squareup.moshi.JsonClass
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
|
import im.vector.matrix.android.api.session.room.send.SendState
|
||||||
import im.vector.matrix.android.internal.di.WorkManagerProvider
|
import im.vector.matrix.android.internal.di.WorkManagerProvider
|
||||||
import im.vector.matrix.android.internal.session.room.timeline.TimelineSendEventWorkCommon
|
import im.vector.matrix.android.internal.session.room.timeline.TimelineSendEventWorkCommon
|
||||||
import im.vector.matrix.android.internal.worker.SessionWorkerParams
|
import im.vector.matrix.android.internal.worker.SessionWorkerParams
|
||||||
|
@ -49,6 +50,7 @@ internal class MultipleEventSendingDispatcherWorker(context: Context, params: Wo
|
||||||
|
|
||||||
@Inject lateinit var workManagerProvider: WorkManagerProvider
|
@Inject lateinit var workManagerProvider: WorkManagerProvider
|
||||||
@Inject lateinit var timelineSendEventWorkCommon: TimelineSendEventWorkCommon
|
@Inject lateinit var timelineSendEventWorkCommon: TimelineSendEventWorkCommon
|
||||||
|
@Inject lateinit var localEchoUpdater: LocalEchoUpdater
|
||||||
|
|
||||||
override suspend fun doWork(): Result {
|
override suspend fun doWork(): Result {
|
||||||
Timber.v("Start dispatch sending multiple event work")
|
Timber.v("Start dispatch sending multiple event work")
|
||||||
|
@ -57,14 +59,17 @@ internal class MultipleEventSendingDispatcherWorker(context: Context, params: Wo
|
||||||
Timber.e("Work cancelled due to input error from parent")
|
Timber.e("Work cancelled due to input error from parent")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.lastFailureMessage != null) {
|
|
||||||
// Transmit the error
|
|
||||||
return Result.success(inputData)
|
|
||||||
}
|
|
||||||
|
|
||||||
val sessionComponent = getSessionComponent(params.sessionId) ?: return Result.success()
|
val sessionComponent = getSessionComponent(params.sessionId) ?: return Result.success()
|
||||||
sessionComponent.inject(this)
|
sessionComponent.inject(this)
|
||||||
|
|
||||||
|
if (params.lastFailureMessage != null) {
|
||||||
|
params.events.forEach { event ->
|
||||||
|
event.eventId?.let { localEchoUpdater.updateSendState(it, SendState.UNDELIVERED) }
|
||||||
|
}
|
||||||
|
// Transmit the error if needed?
|
||||||
|
return Result.success(inputData)
|
||||||
|
}
|
||||||
|
|
||||||
// Create a work for every event
|
// Create a work for every event
|
||||||
params.events.forEach { event ->
|
params.events.forEach { event ->
|
||||||
if (params.isEncrypted) {
|
if (params.isEncrypted) {
|
||||||
|
|
|
@ -25,6 +25,8 @@ import im.vector.matrix.android.api.session.room.model.RoomMemberContent
|
||||||
import im.vector.matrix.android.api.session.room.model.tag.RoomTagContent
|
import im.vector.matrix.android.api.session.room.model.tag.RoomTagContent
|
||||||
import im.vector.matrix.android.api.session.room.send.SendState
|
import im.vector.matrix.android.api.session.room.send.SendState
|
||||||
import im.vector.matrix.android.internal.crypto.DefaultCryptoService
|
import im.vector.matrix.android.internal.crypto.DefaultCryptoService
|
||||||
|
import im.vector.matrix.android.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
||||||
|
import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult
|
||||||
import im.vector.matrix.android.internal.database.helper.addOrUpdate
|
import im.vector.matrix.android.internal.database.helper.addOrUpdate
|
||||||
import im.vector.matrix.android.internal.database.helper.addTimelineEvent
|
import im.vector.matrix.android.internal.database.helper.addTimelineEvent
|
||||||
import im.vector.matrix.android.internal.database.mapper.ContentMapper
|
import im.vector.matrix.android.internal.database.mapper.ContentMapper
|
||||||
|
@ -38,6 +40,7 @@ import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoo
|
||||||
import im.vector.matrix.android.internal.database.query.getOrCreate
|
import im.vector.matrix.android.internal.database.query.getOrCreate
|
||||||
import im.vector.matrix.android.internal.database.query.getOrNull
|
import im.vector.matrix.android.internal.database.query.getOrNull
|
||||||
import im.vector.matrix.android.internal.database.query.where
|
import im.vector.matrix.android.internal.database.query.where
|
||||||
|
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||||
import im.vector.matrix.android.internal.session.DefaultInitialSyncProgressService
|
import im.vector.matrix.android.internal.session.DefaultInitialSyncProgressService
|
||||||
import im.vector.matrix.android.internal.session.mapWithProgress
|
import im.vector.matrix.android.internal.session.mapWithProgress
|
||||||
import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater
|
import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater
|
||||||
|
@ -260,6 +263,14 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
|
||||||
if (sendingEventEntity != null) {
|
if (sendingEventEntity != null) {
|
||||||
Timber.v("Remove local echo for tx:$it")
|
Timber.v("Remove local echo for tx:$it")
|
||||||
roomEntity.sendingTimelineEvents.remove(sendingEventEntity)
|
roomEntity.sendingTimelineEvents.remove(sendingEventEntity)
|
||||||
|
if (event.isEncrypted() && event.content?.get("algorithm") as? String == MXCRYPTO_ALGORITHM_MEGOLM) {
|
||||||
|
// updated with echo decryption, to avoid seeing it decrypt again
|
||||||
|
val adapter = MoshiProvider.providesMoshi().adapter<OlmDecryptionResult>(OlmDecryptionResult::class.java)
|
||||||
|
sendingEventEntity.root?.decryptionResultJson?.let { json ->
|
||||||
|
eventEntity.decryptionResultJson = json
|
||||||
|
event.mxDecryptionResult = adapter.fromJson(json)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Timber.v("Can't find corresponding local echo for tx:$it")
|
Timber.v("Can't find corresponding local echo for tx:$it")
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ androidExtensions {
|
||||||
|
|
||||||
ext.versionMajor = 0
|
ext.versionMajor = 0
|
||||||
ext.versionMinor = 18
|
ext.versionMinor = 18
|
||||||
ext.versionPatch = 0
|
ext.versionPatch = 1
|
||||||
|
|
||||||
static def getGitTimestamp() {
|
static def getGitTimestamp() {
|
||||||
def cmd = 'git show -s --format=%ct'
|
def cmd = 'git show -s --format=%ct'
|
||||||
|
|
|
@ -123,6 +123,7 @@ class KeysBackupBanner @JvmOverloads constructor(
|
||||||
is State.Setup -> {
|
is State.Setup -> {
|
||||||
delegate?.setupKeysBackup()
|
delegate?.setupKeysBackup()
|
||||||
}
|
}
|
||||||
|
is State.Update,
|
||||||
is State.Recover -> {
|
is State.Recover -> {
|
||||||
delegate?.recoverKeysBackup()
|
delegate?.recoverKeysBackup()
|
||||||
}
|
}
|
||||||
|
|
|
@ -177,19 +177,28 @@ class AttachmentsHelper private constructor(private val context: Context,
|
||||||
fun handleShareIntent(intent: Intent): Boolean {
|
fun handleShareIntent(intent: Intent): Boolean {
|
||||||
val type = intent.resolveType(context) ?: return false
|
val type = intent.resolveType(context) ?: return false
|
||||||
if (type.startsWith("image")) {
|
if (type.startsWith("image")) {
|
||||||
imagePicker.submit(IntentUtils.getPickerIntentForSharing(intent))
|
imagePicker.submit(safeShareIntent(intent))
|
||||||
} else if (type.startsWith("video")) {
|
} else if (type.startsWith("video")) {
|
||||||
videoPicker.submit(IntentUtils.getPickerIntentForSharing(intent))
|
videoPicker.submit(safeShareIntent(intent))
|
||||||
} else if (type.startsWith("audio")) {
|
} else if (type.startsWith("audio")) {
|
||||||
videoPicker.submit(IntentUtils.getPickerIntentForSharing(intent))
|
videoPicker.submit(safeShareIntent(intent))
|
||||||
} else if (type.startsWith("application") || type.startsWith("file") || type.startsWith("*")) {
|
} else if (type.startsWith("application") || type.startsWith("file") || type.startsWith("*")) {
|
||||||
filePicker.submit(IntentUtils.getPickerIntentForSharing(intent))
|
filePicker.submit(safeShareIntent(intent))
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun safeShareIntent(intent: Intent): Intent {
|
||||||
|
// Work around for getPickerIntentForSharing doing NPE in android 10
|
||||||
|
return try {
|
||||||
|
IntentUtils.getPickerIntentForSharing(intent)
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
intent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun getPickerManagerForRequestCode(requestCode: Int): PickerManager? {
|
private fun getPickerManagerForRequestCode(requestCode: Int): PickerManager? {
|
||||||
return when (requestCode) {
|
return when (requestCode) {
|
||||||
PICK_IMAGE_DEVICE -> imagePicker
|
PICK_IMAGE_DEVICE -> imagePicker
|
||||||
|
|
|
@ -31,10 +31,12 @@ import im.vector.riotx.core.epoxy.loadingItem
|
||||||
import im.vector.riotx.core.resources.StringProvider
|
import im.vector.riotx.core.resources.StringProvider
|
||||||
import im.vector.riotx.core.ui.list.GenericItem
|
import im.vector.riotx.core.ui.list.GenericItem
|
||||||
import im.vector.riotx.core.ui.list.genericItem
|
import im.vector.riotx.core.ui.list.genericItem
|
||||||
|
import im.vector.riotx.features.settings.VectorPreferences
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class KeysBackupSettingsRecyclerViewController @Inject constructor(private val stringProvider: StringProvider,
|
class KeysBackupSettingsRecyclerViewController @Inject constructor(private val stringProvider: StringProvider,
|
||||||
|
private val vectorPreferences: VectorPreferences,
|
||||||
private val session: Session) : TypedEpoxyController<KeysBackupSettingViewState>() {
|
private val session: Session) : TypedEpoxyController<KeysBackupSettingViewState>() {
|
||||||
|
|
||||||
var listener: Listener? = null
|
var listener: Listener? = null
|
||||||
|
@ -149,8 +151,10 @@ class KeysBackupSettingsRecyclerViewController @Inject constructor(private val s
|
||||||
description(keyVersionResult?.algorithm ?: "")
|
description(keyVersionResult?.algorithm ?: "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (vectorPreferences.developerMode()) {
|
||||||
buildKeysBackupTrust(data.keysBackupVersionTrust)
|
buildKeysBackupTrust(data.keysBackupVersionTrust)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Footer
|
// Footer
|
||||||
keysBackupSettingFooterItem {
|
keysBackupSettingFooterItem {
|
||||||
|
|
|
@ -297,11 +297,18 @@ class RoomDetailFragment @Inject constructor(
|
||||||
is RoomDetailViewEvents.NavigateToEvent -> navigateToEvent(it)
|
is RoomDetailViewEvents.NavigateToEvent -> navigateToEvent(it)
|
||||||
is RoomDetailViewEvents.FileTooBigError -> displayFileTooBigError(it)
|
is RoomDetailViewEvents.FileTooBigError -> displayFileTooBigError(it)
|
||||||
is RoomDetailViewEvents.DownloadFileState -> handleDownloadFileState(it)
|
is RoomDetailViewEvents.DownloadFileState -> handleDownloadFileState(it)
|
||||||
|
is RoomDetailViewEvents.JoinRoomCommandSuccess -> handleJoinedToAnotherRoom(it)
|
||||||
is RoomDetailViewEvents.SendMessageResult -> renderSendMessageResult(it)
|
is RoomDetailViewEvents.SendMessageResult -> renderSendMessageResult(it)
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleJoinedToAnotherRoom(action: RoomDetailViewEvents.JoinRoomCommandSuccess) {
|
||||||
|
updateComposerText("")
|
||||||
|
lockSendButton = false
|
||||||
|
navigator.openRoom(vectorBaseActivity, action.roomId)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
|
|
|
@ -50,6 +50,7 @@ sealed class RoomDetailViewEvents : VectorViewEvents {
|
||||||
abstract class SendMessageResult : RoomDetailViewEvents()
|
abstract class SendMessageResult : RoomDetailViewEvents()
|
||||||
|
|
||||||
object MessageSent : SendMessageResult()
|
object MessageSent : SendMessageResult()
|
||||||
|
data class JoinRoomCommandSuccess(val roomId: String) : SendMessageResult()
|
||||||
class SlashCommandError(val command: Command) : SendMessageResult()
|
class SlashCommandError(val command: Command) : SendMessageResult()
|
||||||
class SlashCommandUnknown(val command: String) : SendMessageResult()
|
class SlashCommandUnknown(val command: String) : SendMessageResult()
|
||||||
data class SlashCommandHandled(@StringRes val messageRes: Int? = null) : SendMessageResult()
|
data class SlashCommandHandled(@StringRes val messageRes: Int? = null) : SendMessageResult()
|
||||||
|
|
|
@ -379,8 +379,8 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
_viewEvents.post(RoomDetailViewEvents.SlashCommandNotImplemented)
|
_viewEvents.post(RoomDetailViewEvents.SlashCommandNotImplemented)
|
||||||
}
|
}
|
||||||
is ParsedCommand.JoinRoom -> {
|
is ParsedCommand.JoinRoom -> {
|
||||||
// TODO
|
handleJoinToAnotherRoomSlashCommand(slashCommandResult)
|
||||||
_viewEvents.post(RoomDetailViewEvents.SlashCommandNotImplemented)
|
popDraft()
|
||||||
}
|
}
|
||||||
is ParsedCommand.PartRoom -> {
|
is ParsedCommand.PartRoom -> {
|
||||||
// TODO
|
// TODO
|
||||||
|
@ -512,6 +512,22 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
room.deleteDraft(NoOpMatrixCallback())
|
room.deleteDraft(NoOpMatrixCallback())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleJoinToAnotherRoomSlashCommand(command: ParsedCommand.JoinRoom) {
|
||||||
|
session.joinRoom(command.roomAlias, command.reason, object : MatrixCallback<Unit> {
|
||||||
|
override fun onSuccess(data: Unit) {
|
||||||
|
session.getRoomSummary(command.roomAlias)
|
||||||
|
?.roomId
|
||||||
|
?.let {
|
||||||
|
_viewEvents.post(RoomDetailViewEvents.JoinRoomCommandSuccess(it))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(failure: Throwable) {
|
||||||
|
_viewEvents.post(RoomDetailViewEvents.SlashCommandResultError(failure))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
private fun legacyRiotQuoteText(quotedText: String?, myText: String): String {
|
private fun legacyRiotQuoteText(quotedText: String?, myText: String): String {
|
||||||
val messageParagraphs = quotedText?.split("\n\n".toRegex())?.dropLastWhile { it.isEmpty() }?.toTypedArray()
|
val messageParagraphs = quotedText?.split("\n\n".toRegex())?.dropLastWhile { it.isEmpty() }?.toTypedArray()
|
||||||
return buildString {
|
return buildString {
|
||||||
|
|
|
@ -1461,7 +1461,7 @@ Why choose Riot.im?
|
||||||
<string name="keys_backup_banner_recover_line1">Never lose encrypted messages</string>
|
<string name="keys_backup_banner_recover_line1">Never lose encrypted messages</string>
|
||||||
<string name="keys_backup_banner_recover_line2">Use Key Backup</string>
|
<string name="keys_backup_banner_recover_line2">Use Key Backup</string>
|
||||||
|
|
||||||
<string name="keys_backup_banner_update_line1">New encrypted messages keys</string>
|
<string name="keys_backup_banner_update_line1">New secure message keys</string>
|
||||||
<string name="keys_backup_banner_update_line2">Manage in Key Backup</string>
|
<string name="keys_backup_banner_update_line2">Manage in Key Backup</string>
|
||||||
|
|
||||||
<string name="keys_backup_banner_in_progress">Backing up keys…</string>
|
<string name="keys_backup_banner_in_progress">Backing up keys…</string>
|
||||||
|
|
Loading…
Reference in New Issue