mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-27 06:29:54 +00:00
Add ability to turn off and delete backups.
This commit is contained in:
committed by
Greyson Parrelli
parent
6659700a1c
commit
5ecf60a306
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.thoughtcrime.securesms.backup.v2
|
||||
|
||||
import androidx.annotation.WorkerThread
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
@@ -41,6 +42,7 @@ import org.thoughtcrime.securesms.backup.v2.stream.PlainTextBackupReader
|
||||
import org.thoughtcrime.securesms.backup.v2.stream.PlainTextBackupWriter
|
||||
import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsType
|
||||
import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsTypeFeature
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.RecurringInAppPaymentRepository
|
||||
import org.thoughtcrime.securesms.database.DistributionListTables
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.database.model.InAppPaymentSubscriberRecord
|
||||
@@ -88,6 +90,7 @@ object BackupRepository {
|
||||
Log.i(TAG, "Resetting initialized state due to 401.")
|
||||
SignalStore.backup().backupsInitialized = false
|
||||
}
|
||||
|
||||
403 -> {
|
||||
Log.i(TAG, "Bad auth credential. Clearing stored credentials.")
|
||||
SignalStore.backup().clearAllCredentials()
|
||||
@@ -95,6 +98,13 @@ object BackupRepository {
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun turnOffAndDeleteBackup() {
|
||||
RecurringInAppPaymentRepository.cancelActiveSubscriptionSync(InAppPaymentSubscriberRecord.Type.BACKUP)
|
||||
SignalStore.backup().areBackupsEnabled = false
|
||||
SignalStore.backup().backupTier = null
|
||||
}
|
||||
|
||||
fun export(outputStream: OutputStream, append: (ByteArray) -> Unit, plaintext: Boolean = false) {
|
||||
val eventTimer = EventTimer()
|
||||
val writer: BackupExportWriter = if (plaintext) {
|
||||
|
||||
@@ -12,18 +12,12 @@ import org.thoughtcrime.securesms.components.settings.DSLSettingsAdapter
|
||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsBottomSheetFragment
|
||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsText
|
||||
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.RecurringInAppPaymentRepository
|
||||
import org.thoughtcrime.securesms.components.settings.configure
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.util.BottomSheetUtil
|
||||
|
||||
class BecomeASustainerFragment : DSLSettingsBottomSheetFragment() {
|
||||
|
||||
private val viewModel: BecomeASustainerViewModel by viewModels(
|
||||
factoryProducer = {
|
||||
BecomeASustainerViewModel.Factory(RecurringInAppPaymentRepository(AppDependencies.donationsService))
|
||||
}
|
||||
)
|
||||
private val viewModel: BecomeASustainerViewModel by viewModels()
|
||||
|
||||
override fun bindAdapter(adapter: DSLSettingsAdapter) {
|
||||
BadgePreview.register(adapter)
|
||||
|
||||
@@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.badges.self.none
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import io.reactivex.rxjava3.kotlin.plusAssign
|
||||
import io.reactivex.rxjava3.kotlin.subscribeBy
|
||||
@@ -10,7 +9,7 @@ import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.RecurringInAppPaymentRepository
|
||||
import org.thoughtcrime.securesms.util.livedata.Store
|
||||
|
||||
class BecomeASustainerViewModel(subscriptionsRepository: RecurringInAppPaymentRepository) : ViewModel() {
|
||||
class BecomeASustainerViewModel : ViewModel() {
|
||||
|
||||
private val store = Store(BecomeASustainerState())
|
||||
|
||||
@@ -19,7 +18,7 @@ class BecomeASustainerViewModel(subscriptionsRepository: RecurringInAppPaymentRe
|
||||
private val disposables = CompositeDisposable()
|
||||
|
||||
init {
|
||||
disposables += subscriptionsRepository.getSubscriptions().subscribeBy(
|
||||
disposables += RecurringInAppPaymentRepository.getSubscriptions().subscribeBy(
|
||||
onError = { Log.w(TAG, "Could not load subscriptions.") },
|
||||
onSuccess = { subscriptions ->
|
||||
store.update {
|
||||
@@ -36,10 +35,4 @@ class BecomeASustainerViewModel(subscriptionsRepository: RecurringInAppPaymentRe
|
||||
companion object {
|
||||
private val TAG = Log.tag(BecomeASustainerViewModel::class.java)
|
||||
}
|
||||
|
||||
class Factory(private val subscriptionsRepository: RecurringInAppPaymentRepository) : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return modelClass.cast(BecomeASustainerViewModel(subscriptionsRepository))!!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,7 @@ import org.thoughtcrime.securesms.badges.view.ViewBadgeBottomSheetDialogFragment
|
||||
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
|
||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment
|
||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsText
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.RecurringInAppPaymentRepository
|
||||
import org.thoughtcrime.securesms.components.settings.configure
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
|
||||
import org.thoughtcrime.securesms.util.navigation.safeNavigate
|
||||
@@ -31,7 +29,7 @@ class BadgesOverviewFragment : DSLSettingsFragment(
|
||||
private val lifecycleDisposable = LifecycleDisposable()
|
||||
private val viewModel: BadgesOverviewViewModel by viewModels(
|
||||
factoryProducer = {
|
||||
BadgesOverviewViewModel.Factory(BadgeRepository(requireContext()), RecurringInAppPaymentRepository(AppDependencies.donationsService))
|
||||
BadgesOverviewViewModel.Factory(BadgeRepository(requireContext()))
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -23,8 +23,7 @@ import java.util.Optional
|
||||
private val TAG = Log.tag(BadgesOverviewViewModel::class.java)
|
||||
|
||||
class BadgesOverviewViewModel(
|
||||
private val badgeRepository: BadgeRepository,
|
||||
private val subscriptionsRepository: RecurringInAppPaymentRepository
|
||||
private val badgeRepository: BadgeRepository
|
||||
) : ViewModel() {
|
||||
private val store = Store(BadgesOverviewState())
|
||||
private val eventSubject = PublishSubject.create<BadgesOverviewEvent>()
|
||||
@@ -51,8 +50,8 @@ class BadgesOverviewViewModel(
|
||||
}
|
||||
|
||||
disposables += Single.zip(
|
||||
subscriptionsRepository.getActiveSubscription(InAppPaymentSubscriberRecord.Type.DONATION),
|
||||
subscriptionsRepository.getSubscriptions()
|
||||
RecurringInAppPaymentRepository.getActiveSubscription(InAppPaymentSubscriberRecord.Type.DONATION),
|
||||
RecurringInAppPaymentRepository.getSubscriptions()
|
||||
) { active, all ->
|
||||
if (!active.isActive && active.activeSubscription?.willCancelAtPeriodEnd() == true) {
|
||||
Optional.ofNullable<String>(all.firstOrNull { it.level == active.activeSubscription?.level }?.badge?.id)
|
||||
@@ -89,11 +88,10 @@ class BadgesOverviewViewModel(
|
||||
}
|
||||
|
||||
class Factory(
|
||||
private val badgeRepository: BadgeRepository,
|
||||
private val subscriptionsRepository: RecurringInAppPaymentRepository
|
||||
private val badgeRepository: BadgeRepository
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return requireNotNull(modelClass.cast(BadgesOverviewViewModel(badgeRepository, subscriptionsRepository)))
|
||||
return requireNotNull(modelClass.cast(BadgesOverviewViewModel(badgeRepository)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,9 +15,7 @@ import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.thoughtcrime.securesms.util.livedata.Store
|
||||
|
||||
class AppSettingsViewModel(
|
||||
recurringInAppPaymentRepository: RecurringInAppPaymentRepository = RecurringInAppPaymentRepository(AppDependencies.donationsService)
|
||||
) : ViewModel() {
|
||||
class AppSettingsViewModel : ViewModel() {
|
||||
|
||||
private val store = Store(
|
||||
AppSettingsState(
|
||||
@@ -40,7 +38,7 @@ class AppSettingsViewModel(
|
||||
store.update(unreadPaymentsLiveData) { payments, state -> state.copy(unreadPaymentsCount = payments.map { it.unreadCount }.orElse(0)) }
|
||||
store.update(selfLiveData) { self, state -> state.copy(self = self) }
|
||||
|
||||
disposables += recurringInAppPaymentRepository.getActiveSubscription(InAppPaymentSubscriberRecord.Type.DONATION).subscribeBy(
|
||||
disposables += RecurringInAppPaymentRepository.getActiveSubscription(InAppPaymentSubscriberRecord.Type.DONATION).subscribeBy(
|
||||
onSuccess = { activeSubscription ->
|
||||
store.update { state ->
|
||||
state.copy(allowUserToGoToDonationManagementScreen = activeSubscription.isActive || InAppDonations.hasAtLeastOnePaymentMethodAvailable())
|
||||
|
||||
@@ -12,12 +12,17 @@ import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.AlertDialogDefaults
|
||||
import androidx.compose.material3.BasicAlertDialog
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LinearProgressIndicator
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
@@ -27,17 +32,19 @@ import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
@@ -83,7 +90,7 @@ class RemoteBackupsSettingsFragment : ComposeFragment() {
|
||||
|
||||
@Composable
|
||||
override fun FragmentContent() {
|
||||
val state by viewModel.state
|
||||
val state by viewModel.state.collectAsState()
|
||||
val callbacks = remember { Callbacks() }
|
||||
|
||||
RemoteBackupsSettingsContent(
|
||||
@@ -142,7 +149,7 @@ class RemoteBackupsSettingsFragment : ComposeFragment() {
|
||||
}
|
||||
|
||||
override fun onTurnOffAndDeleteBackupsConfirm() {
|
||||
// TODO [alex] CheckoutFlowStartFragment.launchForBackupsCancellation(childFragmentManager)
|
||||
viewModel.turnOffAndDeleteBackups()
|
||||
}
|
||||
|
||||
override fun onBackupsTypeClick() {
|
||||
@@ -164,12 +171,6 @@ class RemoteBackupsSettingsFragment : ComposeFragment() {
|
||||
super.onResume()
|
||||
viewModel.refresh()
|
||||
}
|
||||
|
||||
// override fun onCheckoutFlowResult(result: CheckoutFlowStartFragment.Result) {
|
||||
// if (result is CheckoutFlowStartFragment.Result.CancelationSuccess) {
|
||||
// Snackbar.make(requireView(), R.string.SubscribeFragment__your_subscription_has_been_cancelled, Snackbar.LENGTH_LONG).show()
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -331,6 +332,13 @@ private fun RemoteBackupsSettingsContent(
|
||||
onDismiss = contentCallbacks::onDialogDismissed
|
||||
)
|
||||
}
|
||||
|
||||
RemoteBackupsSettingsState.Dialog.DELETING_BACKUP, RemoteBackupsSettingsState.Dialog.BACKUP_DELETED -> {
|
||||
DeletingBackupDialog(
|
||||
backupDeleted = requestedDialog == RemoteBackupsSettingsState.Dialog.BACKUP_DELETED,
|
||||
onDismiss = contentCallbacks::onDialogDismissed
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(requestedSnackbar) {
|
||||
@@ -509,6 +517,60 @@ private fun TurnOffAndDeleteBackupsDialog(
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun DeletingBackupDialog(
|
||||
backupDeleted: Boolean,
|
||||
onDismiss: () -> Unit
|
||||
) {
|
||||
BasicAlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
properties = DialogProperties(
|
||||
dismissOnBackPress = false,
|
||||
dismissOnClickOutside = false
|
||||
)
|
||||
) {
|
||||
Surface(
|
||||
shape = AlertDialogDefaults.shape,
|
||||
color = AlertDialogDefaults.containerColor
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.defaultMinSize(minWidth = 232.dp)
|
||||
.padding(bottom = 60.dp)
|
||||
) {
|
||||
if (backupDeleted) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.symbol_check_light_24),
|
||||
contentDescription = null,
|
||||
tint = Color(0xFF09B37B),
|
||||
modifier = Modifier
|
||||
.padding(top = 58.dp, bottom = 9.dp)
|
||||
.size(48.dp)
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = R.string.RemoteBackupsSettingsFragment__backup_deleted),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
} else {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier
|
||||
.padding(top = 64.dp, bottom = 20.dp)
|
||||
.size(48.dp)
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = R.string.RemoteBackupsSettingsFragment__deleting_backup),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun BackupFrequencyDialog(
|
||||
@@ -642,6 +704,17 @@ private fun TurnOffAndDeleteBackupsDialogPreview() {
|
||||
}
|
||||
}
|
||||
|
||||
@SignalPreview
|
||||
@Composable
|
||||
private fun DeleteBackupDialogPreview() {
|
||||
Previews.Preview {
|
||||
DeletingBackupDialog(
|
||||
backupDeleted = true,
|
||||
onDismiss = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@SignalPreview
|
||||
@Composable
|
||||
private fun BackupFrequencyDialogPreview() {
|
||||
|
||||
@@ -22,7 +22,9 @@ data class RemoteBackupsSettingsState(
|
||||
enum class Dialog {
|
||||
NONE,
|
||||
TURN_OFF_AND_DELETE_BACKUPS,
|
||||
BACKUP_FREQUENCY
|
||||
BACKUP_FREQUENCY,
|
||||
DELETING_BACKUP,
|
||||
BACKUP_DELETED
|
||||
}
|
||||
|
||||
enum class Snackbar {
|
||||
|
||||
@@ -5,11 +5,16 @@
|
||||
|
||||
package org.thoughtcrime.securesms.components.settings.app.chats.backups
|
||||
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.thoughtcrime.securesms.backup.v2.BackupFrequency
|
||||
import org.thoughtcrime.securesms.backup.v2.BackupRepository
|
||||
import org.thoughtcrime.securesms.backup.v2.BackupV2Event
|
||||
@@ -17,12 +22,13 @@ import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.jobs.BackupMessagesJob
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.service.MessageBackupListener
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
/**
|
||||
* ViewModel for state management of RemoteBackupsSettingsFragment
|
||||
*/
|
||||
class RemoteBackupsSettingsViewModel : ViewModel() {
|
||||
private val internalState = mutableStateOf(
|
||||
private val internalState = MutableStateFlow(
|
||||
RemoteBackupsSettingsState(
|
||||
messageBackupsType = null,
|
||||
lastBackupTimestamp = SignalStore.backup().lastBackupTime,
|
||||
@@ -31,7 +37,7 @@ class RemoteBackupsSettingsViewModel : ViewModel() {
|
||||
)
|
||||
)
|
||||
|
||||
val state: State<RemoteBackupsSettingsState> = internalState
|
||||
val state: StateFlow<RemoteBackupsSettingsState> = internalState
|
||||
|
||||
init {
|
||||
refresh()
|
||||
@@ -39,22 +45,22 @@ class RemoteBackupsSettingsViewModel : ViewModel() {
|
||||
|
||||
fun setCanBackUpUsingCellular(canBackUpUsingCellular: Boolean) {
|
||||
SignalStore.backup().backupWithCellular = canBackUpUsingCellular
|
||||
internalState.value = state.value.copy(canBackUpUsingCellular = canBackUpUsingCellular)
|
||||
internalState.update { it.copy(canBackUpUsingCellular = canBackUpUsingCellular) }
|
||||
}
|
||||
|
||||
fun setBackupsFrequency(backupsFrequency: BackupFrequency) {
|
||||
SignalStore.backup().backupFrequency = backupsFrequency
|
||||
internalState.value = state.value.copy(backupsFrequency = backupsFrequency)
|
||||
internalState.update { it.copy(backupsFrequency = backupsFrequency) }
|
||||
MessageBackupListener.setNextBackupTimeToIntervalFromNow()
|
||||
MessageBackupListener.schedule(AppDependencies.application)
|
||||
}
|
||||
|
||||
fun requestDialog(dialog: RemoteBackupsSettingsState.Dialog) {
|
||||
internalState.value = state.value.copy(dialog = dialog)
|
||||
internalState.update { it.copy(dialog = dialog) }
|
||||
}
|
||||
|
||||
fun requestSnackbar(snackbar: RemoteBackupsSettingsState.Snackbar) {
|
||||
internalState.value = state.value.copy(snackbar = snackbar)
|
||||
internalState.update { it.copy(snackbar = snackbar) }
|
||||
}
|
||||
|
||||
fun refresh() {
|
||||
@@ -62,35 +68,49 @@ class RemoteBackupsSettingsViewModel : ViewModel() {
|
||||
val tier = SignalStore.backup().backupTier
|
||||
val backupType = if (tier != null) BackupRepository.getBackupsType(tier) else null
|
||||
|
||||
internalState.value = state.value.copy(
|
||||
messageBackupsType = backupType,
|
||||
lastBackupTimestamp = SignalStore.backup().lastBackupTime,
|
||||
backupSize = SignalStore.backup().totalBackupSize,
|
||||
backupsFrequency = SignalStore.backup().backupFrequency
|
||||
)
|
||||
internalState.update {
|
||||
it.copy(
|
||||
messageBackupsType = backupType,
|
||||
lastBackupTimestamp = SignalStore.backup().lastBackupTime,
|
||||
backupSize = SignalStore.backup().totalBackupSize,
|
||||
backupsFrequency = SignalStore.backup().backupFrequency
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun turnOffAndDeleteBackups() {
|
||||
// TODO [message-backups] -- Delete.
|
||||
SignalStore.backup().areBackupsEnabled = false
|
||||
internalState.value = state.value.copy(snackbar = RemoteBackupsSettingsState.Snackbar.BACKUP_DELETED_AND_TURNED_OFF)
|
||||
viewModelScope.launch {
|
||||
requestDialog(RemoteBackupsSettingsState.Dialog.DELETING_BACKUP)
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
BackupRepository.turnOffAndDeleteBackup()
|
||||
}
|
||||
|
||||
if (isActive) {
|
||||
requestDialog(RemoteBackupsSettingsState.Dialog.BACKUP_DELETED)
|
||||
delay(2000.milliseconds)
|
||||
requestDialog(RemoteBackupsSettingsState.Dialog.NONE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateBackupProgress(backupEvent: BackupV2Event?) {
|
||||
internalState.value = state.value.copy(backupProgress = backupEvent)
|
||||
internalState.update { it.copy(backupProgress = backupEvent) }
|
||||
refreshBackupState()
|
||||
}
|
||||
|
||||
private fun refreshBackupState() {
|
||||
internalState.value = state.value.copy(
|
||||
lastBackupTimestamp = SignalStore.backup().lastBackupTime,
|
||||
backupSize = SignalStore.backup().totalBackupSize
|
||||
)
|
||||
internalState.update {
|
||||
it.copy(
|
||||
lastBackupTimestamp = SignalStore.backup().lastBackupTime,
|
||||
backupSize = SignalStore.backup().totalBackupSize
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onBackupNowClick() {
|
||||
if (state.value.backupProgress == null || state.value.backupProgress?.type == BackupV2Event.Type.FINISHED) {
|
||||
if (internalState.value.backupProgress == null || internalState.value.backupProgress?.type == BackupV2Event.Type.FINISHED) {
|
||||
BackupMessagesJob.enqueue()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,8 +31,6 @@ class PayPalRepository(private val donationsService: DonationsService) {
|
||||
private val TAG = Log.tag(PayPalRepository::class.java)
|
||||
}
|
||||
|
||||
private val recurringInAppPaymentRepository = RecurringInAppPaymentRepository(donationsService)
|
||||
|
||||
fun createOneTimePaymentIntent(
|
||||
amount: FiatMoney,
|
||||
badgeRecipient: RecipientId,
|
||||
@@ -88,7 +86,7 @@ class PayPalRepository(private val donationsService: DonationsService) {
|
||||
)
|
||||
}.flatMap { serviceResponse ->
|
||||
if (retryOn409 && serviceResponse.status == 409) {
|
||||
recurringInAppPaymentRepository.rotateSubscriberId(subscriberType).andThen(createPaymentMethod(subscriberType, retryOn409 = false))
|
||||
RecurringInAppPaymentRepository.rotateSubscriberId(subscriberType).andThen(createPaymentMethod(subscriberType, retryOn409 = false))
|
||||
} else {
|
||||
serviceResponse.flattenResult()
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.thoughtcrime.securesms.components.settings.app.subscription
|
||||
|
||||
import androidx.annotation.CheckResult
|
||||
import io.reactivex.rxjava3.core.Completable
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
@@ -25,7 +26,6 @@ import org.thoughtcrime.securesms.storage.StorageSyncHelper
|
||||
import org.thoughtcrime.securesms.subscription.LevelUpdate
|
||||
import org.thoughtcrime.securesms.subscription.LevelUpdateOperation
|
||||
import org.thoughtcrime.securesms.subscription.Subscription
|
||||
import org.whispersystems.signalservice.api.services.DonationsService
|
||||
import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription
|
||||
import org.whispersystems.signalservice.api.subscriptions.IdempotencyKey
|
||||
import org.whispersystems.signalservice.api.subscriptions.SubscriberId
|
||||
@@ -39,7 +39,11 @@ import kotlin.time.Duration.Companion.milliseconds
|
||||
* Repository which can query for the user's active subscription as well as a list of available subscriptions,
|
||||
* in the currency indicated.
|
||||
*/
|
||||
class RecurringInAppPaymentRepository(private val donationsService: DonationsService) {
|
||||
object RecurringInAppPaymentRepository {
|
||||
|
||||
private val TAG = Log.tag(RecurringInAppPaymentRepository::class.java)
|
||||
|
||||
private val donationsService = AppDependencies.donationsService
|
||||
|
||||
fun getActiveSubscription(type: InAppPaymentSubscriberRecord.Type): Single<ActiveSubscription> {
|
||||
val localSubscription = InAppPaymentsRepository.getSubscriber(type)
|
||||
@@ -129,29 +133,29 @@ class RecurringInAppPaymentRepository(private val donationsService: DonationsSer
|
||||
}
|
||||
}
|
||||
|
||||
fun cancelActiveSubscription(subscriberType: InAppPaymentSubscriberRecord.Type): Completable {
|
||||
fun cancelActiveSubscriptionSync(subscriberType: InAppPaymentSubscriberRecord.Type) {
|
||||
Log.d(TAG, "Canceling active subscription...", true)
|
||||
return Single
|
||||
.fromCallable {
|
||||
val localSubscriber = InAppPaymentsRepository.requireSubscriber(subscriberType)
|
||||
val localSubscriber = InAppPaymentsRepository.requireSubscriber(subscriberType)
|
||||
|
||||
donationsService.cancelSubscription(localSubscriber.subscriberId)
|
||||
}
|
||||
val serviceResponse: ServiceResponse<EmptyResponse> = donationsService.cancelSubscription(localSubscriber.subscriberId)
|
||||
serviceResponse.resultOrThrow
|
||||
|
||||
Log.d(TAG, "Cancelled active subscription.", true)
|
||||
SignalStore.donationsValues().updateLocalStateForManualCancellation(subscriberType)
|
||||
MultiDeviceSubscriptionSyncRequestJob.enqueue()
|
||||
InAppPaymentsRepository.scheduleSyncForAccountRecordChange()
|
||||
}
|
||||
|
||||
@CheckResult
|
||||
fun cancelActiveSubscription(subscriberType: InAppPaymentSubscriberRecord.Type): Completable {
|
||||
return Completable
|
||||
.fromAction { cancelActiveSubscriptionSync(subscriberType) }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.flatMap(ServiceResponse<EmptyResponse>::flattenResult)
|
||||
.ignoreElement()
|
||||
.doOnComplete {
|
||||
Log.d(TAG, "Cancelled active subscription.", true)
|
||||
SignalStore.donationsValues().updateLocalStateForManualCancellation(subscriberType)
|
||||
MultiDeviceSubscriptionSyncRequestJob.enqueue()
|
||||
InAppPaymentsRepository.scheduleSyncForAccountRecordChange()
|
||||
}
|
||||
}
|
||||
|
||||
fun cancelActiveSubscriptionIfNecessary(subscriberType: InAppPaymentSubscriberRecord.Type): Completable {
|
||||
return Single.fromCallable { InAppPaymentsRepository.getShouldCancelSubscriptionBeforeNextSubscribeAttempt(subscriberType) }.flatMapCompletable {
|
||||
if (it) {
|
||||
Log.d(TAG, "Cancelling active subscription...", true)
|
||||
cancelActiveSubscription(subscriberType).doOnComplete {
|
||||
SignalStore.donationsValues().updateLocalStateForManualCancellation(subscriberType)
|
||||
MultiDeviceSubscriptionSyncRequestJob.enqueue()
|
||||
@@ -250,27 +254,23 @@ class RecurringInAppPaymentRepository(private val donationsService: DonationsSer
|
||||
getOrCreateLevelUpdateOperation(TAG, subscriptionLevel)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = Log.tag(RecurringInAppPaymentRepository::class.java)
|
||||
fun getOrCreateLevelUpdateOperation(tag: String, subscriptionLevel: String): LevelUpdateOperation {
|
||||
Log.d(tag, "Retrieving level update operation for $subscriptionLevel")
|
||||
val levelUpdateOperation = SignalStore.donationsValues().getLevelOperation(subscriptionLevel)
|
||||
return if (levelUpdateOperation == null) {
|
||||
val newOperation = LevelUpdateOperation(
|
||||
idempotencyKey = IdempotencyKey.generate(),
|
||||
level = subscriptionLevel
|
||||
)
|
||||
|
||||
fun getOrCreateLevelUpdateOperation(tag: String, subscriptionLevel: String): LevelUpdateOperation {
|
||||
Log.d(tag, "Retrieving level update operation for $subscriptionLevel")
|
||||
val levelUpdateOperation = SignalStore.donationsValues().getLevelOperation(subscriptionLevel)
|
||||
return if (levelUpdateOperation == null) {
|
||||
val newOperation = LevelUpdateOperation(
|
||||
idempotencyKey = IdempotencyKey.generate(),
|
||||
level = subscriptionLevel
|
||||
)
|
||||
|
||||
SignalStore.donationsValues().setLevelOperation(newOperation)
|
||||
LevelUpdate.updateProcessingState(true)
|
||||
Log.d(tag, "Created a new operation for $subscriptionLevel")
|
||||
newOperation
|
||||
} else {
|
||||
LevelUpdate.updateProcessingState(true)
|
||||
Log.d(tag, "Reusing operation for $subscriptionLevel")
|
||||
levelUpdateOperation
|
||||
}
|
||||
SignalStore.donationsValues().setLevelOperation(newOperation)
|
||||
LevelUpdate.updateProcessingState(true)
|
||||
Log.d(tag, "Created a new operation for $subscriptionLevel")
|
||||
newOperation
|
||||
} else {
|
||||
LevelUpdate.updateProcessingState(true)
|
||||
Log.d(tag, "Reusing operation for $subscriptionLevel")
|
||||
levelUpdateOperation
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,6 @@ class StripeRepository(
|
||||
|
||||
private val googlePayApi = GooglePayApi(activity, StripeApi.Gateway(Environment.Donations.STRIPE_CONFIGURATION), Environment.Donations.GOOGLE_PAY_CONFIGURATION)
|
||||
private val stripeApi = StripeApi(Environment.Donations.STRIPE_CONFIGURATION, this, this, AppDependencies.okHttpClient)
|
||||
private val recurringInAppPaymentRepository = RecurringInAppPaymentRepository(AppDependencies.donationsService)
|
||||
|
||||
fun isGooglePayAvailable(): Completable {
|
||||
return googlePayApi.queryIsReadyToPay()
|
||||
@@ -169,7 +168,7 @@ class StripeRepository(
|
||||
}
|
||||
.flatMap { serviceResponse ->
|
||||
if (retryOn409 && serviceResponse.status == 409) {
|
||||
recurringInAppPaymentRepository.rotateSubscriberId(subscriberType).andThen(createPaymentMethod(subscriberType, paymentSourceType, retryOn409 = false))
|
||||
RecurringInAppPaymentRepository.rotateSubscriberId(subscriberType).andThen(createPaymentMethod(subscriberType, paymentSourceType, retryOn409 = false))
|
||||
} else {
|
||||
serviceResponse.flattenResult()
|
||||
}
|
||||
|
||||
@@ -52,7 +52,6 @@ import java.util.Optional
|
||||
*/
|
||||
class DonateToSignalViewModel(
|
||||
startType: InAppPaymentType,
|
||||
private val subscriptionsRepository: RecurringInAppPaymentRepository,
|
||||
private val oneTimeInAppPaymentRepository: OneTimeInAppPaymentRepository
|
||||
) : ViewModel() {
|
||||
|
||||
@@ -75,7 +74,7 @@ class DonateToSignalViewModel(
|
||||
|
||||
init {
|
||||
initializeOneTimeDonationState(oneTimeInAppPaymentRepository)
|
||||
initializeMonthlyDonationState(subscriptionsRepository)
|
||||
initializeMonthlyDonationState(RecurringInAppPaymentRepository)
|
||||
|
||||
networkDisposable += InternetConnectionObserver
|
||||
.observe()
|
||||
@@ -91,7 +90,7 @@ class DonateToSignalViewModel(
|
||||
fun retryMonthlyDonationState() {
|
||||
if (!monthlyDonationDisposables.isDisposed && store.state.monthlyDonationState.donationStage == DonateToSignalState.DonationStage.FAILURE) {
|
||||
store.update { it.copy(monthlyDonationState = it.monthlyDonationState.copy(donationStage = DonateToSignalState.DonationStage.INIT)) }
|
||||
initializeMonthlyDonationState(subscriptionsRepository)
|
||||
initializeMonthlyDonationState(RecurringInAppPaymentRepository)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +180,7 @@ class DonateToSignalViewModel(
|
||||
}
|
||||
|
||||
fun refreshActiveSubscription() {
|
||||
subscriptionsRepository
|
||||
RecurringInAppPaymentRepository
|
||||
.getActiveSubscription(InAppPaymentSubscriberRecord.Type.DONATION)
|
||||
.subscribeBy(
|
||||
onSuccess = {
|
||||
@@ -395,7 +394,7 @@ class DonateToSignalViewModel(
|
||||
val usd = PlatformCurrencyUtil.USD
|
||||
val newSubscriber = InAppPaymentsRepository.getSubscriber(usd, InAppPaymentSubscriberRecord.Type.DONATION) ?: InAppPaymentSubscriberRecord(SubscriberId.generate(), usd, InAppPaymentSubscriberRecord.Type.DONATION, false, InAppPaymentData.PaymentMethodType.UNKNOWN)
|
||||
InAppPaymentsRepository.setSubscriber(newSubscriber)
|
||||
subscriptionsRepository.syncAccountRecord().subscribe()
|
||||
RecurringInAppPaymentRepository.syncAccountRecord().subscribe()
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -422,11 +421,10 @@ class DonateToSignalViewModel(
|
||||
|
||||
class Factory(
|
||||
private val startType: InAppPaymentType,
|
||||
private val subscriptionsRepository: RecurringInAppPaymentRepository = RecurringInAppPaymentRepository(AppDependencies.donationsService),
|
||||
private val oneTimeInAppPaymentRepository: OneTimeInAppPaymentRepository = OneTimeInAppPaymentRepository(AppDependencies.donationsService)
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return modelClass.cast(DonateToSignalViewModel(startType, subscriptionsRepository, oneTimeInAppPaymentRepository)) as T
|
||||
return modelClass.cast(DonateToSignalViewModel(startType, oneTimeInAppPaymentRepository)) as T
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ import org.whispersystems.signalservice.api.util.Preconditions
|
||||
|
||||
class PayPalPaymentInProgressViewModel(
|
||||
private val payPalRepository: PayPalRepository,
|
||||
private val recurringInAppPaymentRepository: RecurringInAppPaymentRepository,
|
||||
private val oneTimeInAppPaymentRepository: OneTimeInAppPaymentRepository
|
||||
) : ViewModel() {
|
||||
|
||||
@@ -86,7 +85,7 @@ class PayPalPaymentInProgressViewModel(
|
||||
Log.d(TAG, "Beginning subscription update...", true)
|
||||
|
||||
store.update { DonationProcessorStage.PAYMENT_PIPELINE }
|
||||
disposables += recurringInAppPaymentRepository.cancelActiveSubscriptionIfNecessary(inAppPayment.type.requireSubscriberType()).andThen(recurringInAppPaymentRepository.setSubscriptionLevel(inAppPayment, PaymentSourceType.PayPal))
|
||||
disposables += RecurringInAppPaymentRepository.cancelActiveSubscriptionIfNecessary(inAppPayment.type.requireSubscriberType()).andThen(RecurringInAppPaymentRepository.setSubscriptionLevel(inAppPayment, PaymentSourceType.PayPal))
|
||||
.subscribeBy(
|
||||
onComplete = {
|
||||
Log.w(TAG, "Completed subscription update", true)
|
||||
@@ -104,12 +103,12 @@ class PayPalPaymentInProgressViewModel(
|
||||
Log.d(TAG, "Beginning cancellation...", true)
|
||||
|
||||
store.update { DonationProcessorStage.CANCELLING }
|
||||
disposables += recurringInAppPaymentRepository.cancelActiveSubscription(subscriberType).subscribeBy(
|
||||
disposables += RecurringInAppPaymentRepository.cancelActiveSubscription(subscriberType).subscribeBy(
|
||||
onComplete = {
|
||||
Log.d(TAG, "Cancellation succeeded", true)
|
||||
SignalStore.donationsValues().updateLocalStateForManualCancellation(subscriberType)
|
||||
MultiDeviceSubscriptionSyncRequestJob.enqueue()
|
||||
recurringInAppPaymentRepository.syncAccountRecord().subscribe()
|
||||
RecurringInAppPaymentRepository.syncAccountRecord().subscribe()
|
||||
store.update { DonationProcessorStage.COMPLETE }
|
||||
},
|
||||
onError = { throwable ->
|
||||
@@ -172,14 +171,14 @@ class PayPalPaymentInProgressViewModel(
|
||||
private fun proceedMonthly(inAppPayment: InAppPaymentTable.InAppPayment, routeToPaypalConfirmation: (PayPalCreatePaymentMethodResponse) -> Single<PayPalPaymentMethodId>) {
|
||||
Log.d(TAG, "Proceeding with monthly payment pipeline for InAppPayment::${inAppPayment.id} of type ${inAppPayment.type}...", true)
|
||||
|
||||
val setup = recurringInAppPaymentRepository.ensureSubscriberId(inAppPayment.type.requireSubscriberType())
|
||||
.andThen(recurringInAppPaymentRepository.cancelActiveSubscriptionIfNecessary(inAppPayment.type.requireSubscriberType()))
|
||||
val setup = RecurringInAppPaymentRepository.ensureSubscriberId(inAppPayment.type.requireSubscriberType())
|
||||
.andThen(RecurringInAppPaymentRepository.cancelActiveSubscriptionIfNecessary(inAppPayment.type.requireSubscriberType()))
|
||||
.andThen(payPalRepository.createPaymentMethod(inAppPayment.type.requireSubscriberType()))
|
||||
.flatMap(routeToPaypalConfirmation)
|
||||
.flatMapCompletable { payPalRepository.setDefaultPaymentMethod(inAppPayment.type.requireSubscriberType(), it.paymentId) }
|
||||
.onErrorResumeNext { Completable.error(DonationError.getPaymentSetupError(DonationErrorSource.MONTHLY, it, PaymentSourceType.PayPal)) }
|
||||
|
||||
disposables += setup.andThen(recurringInAppPaymentRepository.setSubscriptionLevel(inAppPayment, PaymentSourceType.PayPal))
|
||||
disposables += setup.andThen(RecurringInAppPaymentRepository.setSubscriptionLevel(inAppPayment, PaymentSourceType.PayPal))
|
||||
.subscribeBy(
|
||||
onError = { throwable ->
|
||||
Log.w(TAG, "Failure in monthly payment pipeline...", throwable, true)
|
||||
@@ -195,11 +194,10 @@ class PayPalPaymentInProgressViewModel(
|
||||
|
||||
class Factory(
|
||||
private val payPalRepository: PayPalRepository = PayPalRepository(AppDependencies.donationsService),
|
||||
private val recurringInAppPaymentRepository: RecurringInAppPaymentRepository = RecurringInAppPaymentRepository(AppDependencies.donationsService),
|
||||
private val oneTimeInAppPaymentRepository: OneTimeInAppPaymentRepository = OneTimeInAppPaymentRepository(AppDependencies.donationsService)
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return modelClass.cast(PayPalPaymentInProgressViewModel(payPalRepository, recurringInAppPaymentRepository, oneTimeInAppPaymentRepository)) as T
|
||||
return modelClass.cast(PayPalPaymentInProgressViewModel(payPalRepository, oneTimeInAppPaymentRepository)) as T
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,6 @@ import org.whispersystems.signalservice.internal.push.exceptions.DonationProcess
|
||||
|
||||
class StripePaymentInProgressViewModel(
|
||||
private val stripeRepository: StripeRepository,
|
||||
private val recurringInAppPaymentRepository: RecurringInAppPaymentRepository,
|
||||
private val oneTimeInAppPaymentRepository: OneTimeInAppPaymentRepository
|
||||
) : ViewModel() {
|
||||
|
||||
@@ -144,18 +143,18 @@ class StripePaymentInProgressViewModel(
|
||||
}
|
||||
|
||||
private fun proceedMonthly(inAppPayment: InAppPaymentTable.InAppPayment, paymentSourceProvider: PaymentSourceProvider, nextActionHandler: StripeNextActionHandler) {
|
||||
val ensureSubscriberId: Completable = recurringInAppPaymentRepository.ensureSubscriberId(inAppPayment.type.requireSubscriberType())
|
||||
val ensureSubscriberId: Completable = RecurringInAppPaymentRepository.ensureSubscriberId(inAppPayment.type.requireSubscriberType())
|
||||
val createAndConfirmSetupIntent: Single<StripeApi.Secure3DSAction> = paymentSourceProvider.paymentSource.flatMap {
|
||||
stripeRepository.createAndConfirmSetupIntent(inAppPayment.type, it, paymentSourceProvider.paymentSourceType as PaymentSourceType.Stripe)
|
||||
}
|
||||
|
||||
val setLevel: Completable = recurringInAppPaymentRepository.setSubscriptionLevel(inAppPayment, paymentSourceProvider.paymentSourceType)
|
||||
val setLevel: Completable = RecurringInAppPaymentRepository.setSubscriptionLevel(inAppPayment, paymentSourceProvider.paymentSourceType)
|
||||
|
||||
Log.d(TAG, "Starting subscription payment pipeline...", true)
|
||||
store.update { DonationProcessorStage.PAYMENT_PIPELINE }
|
||||
|
||||
val setup: Completable = ensureSubscriberId
|
||||
.andThen(recurringInAppPaymentRepository.cancelActiveSubscriptionIfNecessary(inAppPayment.type.requireSubscriberType()))
|
||||
.andThen(RecurringInAppPaymentRepository.cancelActiveSubscriptionIfNecessary(inAppPayment.type.requireSubscriberType()))
|
||||
.andThen(createAndConfirmSetupIntent)
|
||||
.flatMap { secure3DSAction ->
|
||||
nextActionHandler.handle(
|
||||
@@ -255,7 +254,7 @@ class StripePaymentInProgressViewModel(
|
||||
Log.d(TAG, "Beginning cancellation...", true)
|
||||
|
||||
store.update { DonationProcessorStage.CANCELLING }
|
||||
disposables += recurringInAppPaymentRepository.cancelActiveSubscription(subscriberType).subscribeBy(
|
||||
disposables += RecurringInAppPaymentRepository.cancelActiveSubscription(subscriberType).subscribeBy(
|
||||
onComplete = {
|
||||
Log.d(TAG, "Cancellation succeeded", true)
|
||||
store.update { DonationProcessorStage.COMPLETE }
|
||||
@@ -270,10 +269,10 @@ class StripePaymentInProgressViewModel(
|
||||
fun updateSubscription(inAppPayment: InAppPaymentTable.InAppPayment) {
|
||||
Log.d(TAG, "Beginning subscription update...", true)
|
||||
store.update { DonationProcessorStage.PAYMENT_PIPELINE }
|
||||
disposables += recurringInAppPaymentRepository
|
||||
disposables += RecurringInAppPaymentRepository
|
||||
.cancelActiveSubscriptionIfNecessary(inAppPayment.type.requireSubscriberType())
|
||||
.andThen(recurringInAppPaymentRepository.getPaymentSourceTypeOfLatestSubscription(inAppPayment.type.requireSubscriberType()))
|
||||
.flatMapCompletable { paymentSourceType -> recurringInAppPaymentRepository.setSubscriptionLevel(inAppPayment, paymentSourceType) }
|
||||
.andThen(RecurringInAppPaymentRepository.getPaymentSourceTypeOfLatestSubscription(inAppPayment.type.requireSubscriberType()))
|
||||
.flatMapCompletable { paymentSourceType -> RecurringInAppPaymentRepository.setSubscriptionLevel(inAppPayment, paymentSourceType) }
|
||||
.subscribeBy(
|
||||
onComplete = {
|
||||
Log.w(TAG, "Completed subscription update", true)
|
||||
@@ -304,11 +303,10 @@ class StripePaymentInProgressViewModel(
|
||||
|
||||
class Factory(
|
||||
private val stripeRepository: StripeRepository,
|
||||
private val recurringInAppPaymentRepository: RecurringInAppPaymentRepository = RecurringInAppPaymentRepository(AppDependencies.donationsService),
|
||||
private val oneTimeInAppPaymentRepository: OneTimeInAppPaymentRepository = OneTimeInAppPaymentRepository(AppDependencies.donationsService)
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return modelClass.cast(StripePaymentInProgressViewModel(stripeRepository, recurringInAppPaymentRepository, oneTimeInAppPaymentRepository)) as T
|
||||
return modelClass.cast(StripePaymentInProgressViewModel(stripeRepository, oneTimeInAppPaymentRepository)) as T
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,14 +20,12 @@ import org.thoughtcrime.securesms.components.settings.DSLSettingsIcon
|
||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsText
|
||||
import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.DonationSerializationHelper.toFiatMoney
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.RecurringInAppPaymentRepository
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.completed.TerminalDonationDelegate
|
||||
import org.thoughtcrime.securesms.components.settings.app.subscription.models.NetworkFailure
|
||||
import org.thoughtcrime.securesms.components.settings.configure
|
||||
import org.thoughtcrime.securesms.components.settings.models.IndeterminateLoadingCircle
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.DonationErrorValue
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.PendingOneTimeDonation
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.help.HelpFragment
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.payments.FiatMoneyUtil
|
||||
@@ -66,11 +64,7 @@ class ManageDonationsFragment :
|
||||
)
|
||||
}
|
||||
|
||||
private val viewModel: ManageDonationsViewModel by viewModels(
|
||||
factoryProducer = {
|
||||
ManageDonationsViewModel.Factory(RecurringInAppPaymentRepository(AppDependencies.donationsService))
|
||||
}
|
||||
)
|
||||
private val viewModel: ManageDonationsViewModel by viewModels()
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
viewLifecycleOwner.lifecycle.addObserver(TerminalDonationDelegate(childFragmentManager, viewLifecycleOwner))
|
||||
|
||||
@@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.components.settings.app.subscription.manage
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import io.reactivex.rxjava3.core.Observable
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
@@ -25,9 +24,7 @@ import org.thoughtcrime.securesms.util.livedata.Store
|
||||
import org.whispersystems.signalservice.api.subscriptions.ActiveSubscription
|
||||
import java.util.Optional
|
||||
|
||||
class ManageDonationsViewModel(
|
||||
private val subscriptionsRepository: RecurringInAppPaymentRepository
|
||||
) : ViewModel() {
|
||||
class ManageDonationsViewModel : ViewModel() {
|
||||
|
||||
private val store = Store(ManageDonationsState())
|
||||
private val disposables = CompositeDisposable()
|
||||
@@ -65,7 +62,7 @@ class ManageDonationsViewModel(
|
||||
disposables.clear()
|
||||
|
||||
val levelUpdateOperationEdges: Observable<Boolean> = LevelUpdate.isProcessing.distinctUntilChanged()
|
||||
val activeSubscription: Single<ActiveSubscription> = subscriptionsRepository.getActiveSubscription(InAppPaymentSubscriberRecord.Type.DONATION)
|
||||
val activeSubscription: Single<ActiveSubscription> = RecurringInAppPaymentRepository.getActiveSubscription(InAppPaymentSubscriberRecord.Type.DONATION)
|
||||
|
||||
disposables += Single.fromCallable {
|
||||
InAppPaymentsRepository.getShouldCancelSubscriptionBeforeNextSubscribeAttempt(InAppPaymentSubscriberRecord.Type.DONATION)
|
||||
@@ -134,7 +131,7 @@ class ManageDonationsViewModel(
|
||||
}
|
||||
)
|
||||
|
||||
disposables += subscriptionsRepository.getSubscriptions().subscribeBy(
|
||||
disposables += RecurringInAppPaymentRepository.getSubscriptions().subscribeBy(
|
||||
onSuccess = { subs ->
|
||||
store.update { it.copy(availableSubscriptions = subs) }
|
||||
},
|
||||
@@ -155,14 +152,6 @@ class ManageDonationsViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
class Factory(
|
||||
private val subscriptionsRepository: RecurringInAppPaymentRepository
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return modelClass.cast(ManageDonationsViewModel(subscriptionsRepository))!!
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = Log.tag(ManageDonationsViewModel::class.java)
|
||||
}
|
||||
|
||||
9
app/src/main/res/drawable/symbol_check_light_24.xml
Normal file
9
app/src/main/res/drawable/symbol_check_light_24.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M18.9 5.37c0.35 0.22 0.46 0.69 0.23 1.03l-8.32 13c-0.13 0.2-0.35 0.34-0.6 0.35-0.24 0.01-0.47-0.1-0.62-0.29L4.9 13.48c-0.26-0.32-0.2-0.8 0.13-1.05 0.32-0.26 0.8-0.2 1.05 0.13l4.03 5.14 7.75-12.1c0.22-0.35 0.69-0.45 1.03-0.23Z"/>
|
||||
</vector>
|
||||
@@ -7031,5 +7031,11 @@
|
||||
<!-- Educational bottom sheet confirm/dismiss button text shown to notify about delete syncs causing deletes to happen across all devices -->
|
||||
<string name="DeleteSyncEducation_acknowledge_button">OK</string>
|
||||
|
||||
<!-- RemoteBackupsSettingsFragment -->
|
||||
<!-- Text on dialog while user backup is being deleted -->
|
||||
<string name="RemoteBackupsSettingsFragment__deleting_backup">Deleting backup…</string>
|
||||
<!-- Text on dialog when user backup is deleted -->
|
||||
<string name="RemoteBackupsSettingsFragment__backup_deleted">Backup deleted</string>
|
||||
|
||||
<!-- EOF -->
|
||||
</resources>
|
||||
|
||||
@@ -84,6 +84,18 @@ public final class ServiceResponse<Result> {
|
||||
}
|
||||
}
|
||||
|
||||
public Result getResultOrThrow() throws Throwable {
|
||||
if (result.isPresent()) {
|
||||
return result.get();
|
||||
} else if (applicationError.isPresent()) {
|
||||
throw applicationError.get();
|
||||
} else if (executionError.isPresent()) {
|
||||
throw executionError.get();
|
||||
} else {
|
||||
throw new AssertionError("Should never get here");
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> ServiceResponse<T> forResult(T result, WebsocketResponse response) {
|
||||
return new ServiceResponse<>(result, response);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user