Add share highwater timestamp protection to CFv2.

This commit is contained in:
Cody Henthorne 2023-07-18 13:58:13 -04:00 committed by Nicholas
parent e6c9449e3c
commit 67d4f666ce
5 changed files with 40 additions and 10 deletions

View file

@ -62,8 +62,8 @@ class DraftRepository(
val TAG = Log.tag(DraftRepository::class.java)
}
fun getShareOrDraftData(): Maybe<Pair<ShareOrDraftData?, Drafts?>> {
return MaybeCompat.fromCallable { getShareOrDraftDataInternal() }
fun getShareOrDraftData(lastShareDataTimestamp: Long): Maybe<Pair<ShareOrDraftData?, Drafts?>> {
return MaybeCompat.fromCallable { getShareOrDraftDataInternal(lastShareDataTimestamp) }
.observeOn(Schedulers.io())
}
@ -73,7 +73,17 @@ class DraftRepository(
*
* Note: Voice note drafts are handled differently and via the [DraftViewModel.state]
*/
private fun getShareOrDraftDataInternal(): Pair<ShareOrDraftData?, Drafts?>? {
@Suppress("ConvertTwoComparisonsToRangeCheck")
private fun getShareOrDraftDataInternal(lastShareDataTimestamp: Long): Pair<ShareOrDraftData?, Drafts?>? {
val sharedDataTimestamp: Long = conversationArguments?.shareDataTimestamp ?: -1
Log.d(TAG, "Shared this data at $sharedDataTimestamp and last processed share data at $lastShareDataTimestamp")
if (sharedDataTimestamp > 0 && sharedDataTimestamp <= lastShareDataTimestamp) {
Log.d(TAG, "Already processed this share data. Skipping.")
return null
} else {
Log.d(TAG, "Have not processed this share data. Proceeding.")
}
val shareText = conversationArguments?.draftText
val shareMedia = conversationArguments?.draftMedia
val shareContentType = conversationArguments?.draftContentType

View file

@ -168,8 +168,8 @@ class DraftViewModel @JvmOverloads constructor(
.observeOn(AndroidSchedulers.mainThread())
}
fun loadShareOrDraftData(): Maybe<DraftRepository.ShareOrDraftData> {
return repository.getShareOrDraftData()
fun loadShareOrDraftData(lastShareDataTimestamp: Long): Maybe<DraftRepository.ShareOrDraftData> {
return repository.getShareOrDraftData(lastShareDataTimestamp)
.doOnSuccess { (_, drafts) ->
if (drafts != null) {
store.update { saveDrafts(it.copyAndSetDrafts(drafts = drafts)) }

View file

@ -28,7 +28,6 @@ class ConversationActivity : PassphraseRequiredActivity(), VoiceNoteMediaControl
private val theme = DynamicNoActionBarTheme()
private val transitionDebouncer: Debouncer = Debouncer(150, TimeUnit.MILLISECONDS)
private var shareDataTimestamp: Long = -1L
override val voiceNoteMediaController = VoiceNoteMediaController(this, true)
@ -36,6 +35,7 @@ class ConversationActivity : PassphraseRequiredActivity(), VoiceNoteMediaControl
override val googlePayResultPublisher: Subject<DonationPaymentComponent.GooglePayResult> = PublishSubject.create()
private val motionEventRelay: MotionEventRelay by viewModels()
private val shareDataTimestampViewModel: ShareDataTimestampViewModel by viewModels()
override fun onPreCreate() {
theme.onCreate(this)
@ -47,9 +47,9 @@ class ConversationActivity : PassphraseRequiredActivity(), VoiceNoteMediaControl
window.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)
if (savedInstanceState != null) {
shareDataTimestamp = savedInstanceState.getLong(STATE_WATERMARK, -1L)
shareDataTimestampViewModel.timestamp = savedInstanceState.getLong(STATE_WATERMARK, -1L)
} else if (intent.flags and Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY != 0) {
shareDataTimestamp = System.currentTimeMillis()
shareDataTimestampViewModel.timestamp = System.currentTimeMillis()
}
setContentView(R.layout.fragment_container)
@ -66,7 +66,7 @@ class ConversationActivity : PassphraseRequiredActivity(), VoiceNoteMediaControl
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putLong(STATE_WATERMARK, shareDataTimestamp)
outState.putLong(STATE_WATERMARK, shareDataTimestampViewModel.timestamp)
}
override fun onDestroy() {

View file

@ -407,6 +407,8 @@ class ConversationFragment :
InlineQueryViewModelV2(recipientRepository = conversationRecipientRepository)
}
private val shareDataTimestampViewModel: ShareDataTimestampViewModel by activityViewModels()
private val inlineQueryController: InlineQueryResultsControllerV2 by lazy {
InlineQueryResultsControllerV2(
this,
@ -907,7 +909,7 @@ class ConversationFragment :
this::handleReplyToMessage
).attachToRecyclerView(binding.conversationItemRecycler)
draftViewModel.loadShareOrDraftData()
draftViewModel.loadShareOrDraftData(shareDataTimestampViewModel.timestamp)
.subscribeBy { data -> handleShareOrDraftData(data) }
.addTo(disposables)
@ -1298,6 +1300,8 @@ class ConversationFragment :
}
private fun handleShareOrDraftData(data: ShareOrDraftData) {
shareDataTimestampViewModel.timestamp = args.shareDataTimestamp
when (data) {
is ShareOrDraftData.SendKeyboardImage -> sendMessageWithoutComposeInput(slide = data.slide, clearCompose = false)
is ShareOrDraftData.SendSticker -> sendMessageWithoutComposeInput(slide = data.slide, clearCompose = true)

View file

@ -0,0 +1,16 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.conversation.v2
import androidx.lifecycle.ViewModel
/**
* Hold the last share timestamp in an activity scoped view model for sharing between
* the activity and fragments.
*/
class ShareDataTimestampViewModel : ViewModel() {
var timestamp: Long = -1L
}