kopia lustrzana https://github.com/ryukoposting/Signal-Android
Improve handling of network timeouts for donor badges.
rodzic
c4164b17a2
commit
482a10de02
|
@ -13,8 +13,11 @@ class BadgeRepository(context: Context) {
|
|||
|
||||
private val context = context.applicationContext
|
||||
|
||||
fun setVisibilityForAllBadges(displayBadgesOnProfile: Boolean): Completable = Completable.fromAction {
|
||||
val badges = Recipient.self().badges.map { it.copy(visible = displayBadgesOnProfile) }
|
||||
fun setVisibilityForAllBadges(
|
||||
displayBadgesOnProfile: Boolean,
|
||||
selfBadges: List<Badge> = Recipient.self().badges
|
||||
): Completable = Completable.fromAction {
|
||||
val badges = selfBadges.map { it.copy(visible = displayBadgesOnProfile) }
|
||||
ProfileUtil.uploadProfileWithBadges(context, badges)
|
||||
|
||||
val recipientDatabase: RecipientDatabase = DatabaseFactory.getRecipientDatabase(context)
|
||||
|
|
|
@ -154,18 +154,30 @@ class DonationPaymentRepository(activity: Activity) : StripeApi.PaymentIntentFet
|
|||
val subscriber = SignalStore.donationsValues().requireSubscriber()
|
||||
|
||||
Log.d(TAG, "Attempting to set user subscription level to $subscriptionLevel", true)
|
||||
|
||||
ApplicationDependencies.getDonationsService().updateSubscriptionLevel(
|
||||
subscriber.subscriberId,
|
||||
subscriptionLevel,
|
||||
subscriber.currencyCode,
|
||||
levelUpdateOperation.idempotencyKey.serialize()
|
||||
).flatMap(ServiceResponse<EmptyResponse>::flattenResult).ignoreElement().andThen {
|
||||
Log.d(TAG, "Successfully set user subscription to level $subscriptionLevel", true)
|
||||
SignalStore.donationsValues().clearUserManuallyCancelled()
|
||||
SignalStore.donationsValues().clearLevelOperation()
|
||||
LevelUpdate.updateProcessingState(false)
|
||||
it.onComplete()
|
||||
levelUpdateOperation.idempotencyKey.serialize(),
|
||||
SubscriptionReceiptRequestResponseJob.MUTEX
|
||||
).flatMapCompletable {
|
||||
if (it.status == 200 || it.status == 204) {
|
||||
Log.d(TAG, "Successfully set user subscription to level $subscriptionLevel with response code ${it.status}", true)
|
||||
SignalStore.donationsValues().clearUserManuallyCancelled()
|
||||
SignalStore.donationsValues().clearLevelOperations()
|
||||
LevelUpdate.updateProcessingState(false)
|
||||
Completable.complete()
|
||||
} else {
|
||||
if (it.applicationError.isPresent) {
|
||||
Log.w(TAG, "Failed to set user subscription to level $subscriptionLevel with response code ${it.status}", it.applicationError.get(), true)
|
||||
SignalStore.donationsValues().clearLevelOperations()
|
||||
} else {
|
||||
Log.w(TAG, "Failed to set user subscription to level $subscriptionLevel", it.executionError.orNull(), true)
|
||||
}
|
||||
|
||||
LevelUpdate.updateProcessingState(false)
|
||||
it.flattenResult().ignoreElement()
|
||||
}
|
||||
}.andThen {
|
||||
Log.d(TAG, "Enqueuing request response job chain.", true)
|
||||
val jobId = SubscriptionReceiptRequestResponseJob.enqueueSubscriptionContinuation()
|
||||
|
@ -205,14 +217,13 @@ class DonationPaymentRepository(activity: Activity) : StripeApi.PaymentIntentFet
|
|||
}
|
||||
}
|
||||
}.doOnError {
|
||||
SignalStore.donationsValues().clearLevelOperation()
|
||||
LevelUpdate.updateProcessingState(false)
|
||||
}.subscribeOn(Schedulers.io())
|
||||
}
|
||||
|
||||
private fun getOrCreateLevelUpdateOperation(subscriptionLevel: String): Single<LevelUpdateOperation> = Single.fromCallable {
|
||||
val levelUpdateOperation = SignalStore.donationsValues().getLevelOperation()
|
||||
if (levelUpdateOperation == null || subscriptionLevel != levelUpdateOperation.level) {
|
||||
val levelUpdateOperation = SignalStore.donationsValues().getLevelOperation(subscriptionLevel)
|
||||
if (levelUpdateOperation == null) {
|
||||
val newOperation = LevelUpdateOperation(
|
||||
idempotencyKey = IdempotencyKey.generate(),
|
||||
level = subscriptionLevel
|
||||
|
|
|
@ -145,7 +145,7 @@ class SubscribeViewModel(
|
|||
onComplete = {
|
||||
eventPublisher.onNext(DonationEvent.SubscriptionCancelled)
|
||||
SignalStore.donationsValues().setLastEndOfPeriod(0L)
|
||||
SignalStore.donationsValues().clearLevelOperation()
|
||||
SignalStore.donationsValues().clearLevelOperations()
|
||||
SignalStore.donationsValues().markUserManuallyCancelled()
|
||||
refreshActiveSubscription()
|
||||
store.update { it.copy(stage = SubscribeState.Stage.READY) }
|
||||
|
|
|
@ -150,7 +150,7 @@ class ThanksForYourSupportBottomSheetDialogFragment : FixedRoundedCornerBottomSh
|
|||
findNavController().popBackStack()
|
||||
} else {
|
||||
requireActivity().finish()
|
||||
requireActivity().startActivity(AppSettingsActivity.subscriptions(requireContext()))
|
||||
requireActivity().startActivity(AppSettingsActivity.manageSubscriptions(requireContext()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -224,14 +224,7 @@ public class RefreshOwnProfileJob extends BaseJob {
|
|||
Log.d(TAG, "Detected mixed visibility of badges. Telling the server to mark them all visible.", true);
|
||||
|
||||
BadgeRepository badgeRepository = new BadgeRepository(context);
|
||||
badgeRepository.setVisibilityForAllBadges(true);
|
||||
|
||||
DatabaseFactory.getRecipientDatabase(context)
|
||||
.setBadges(Recipient.self().getId(),
|
||||
appBadges.stream()
|
||||
.map(Badge::setVisible)
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
badgeRepository.setVisibilityForAllBadges(true, appBadges).blockingSubscribe();
|
||||
} else {
|
||||
DatabaseFactory.getRecipientDatabase(context)
|
||||
.setBadges(Recipient.self().getId(), appBadges);
|
||||
|
|
|
@ -41,6 +41,8 @@ public class SubscriptionReceiptRequestResponseJob extends BaseJob {
|
|||
private static final String DATA_REQUEST_BYTES = "data.request.bytes";
|
||||
private static final String DATA_SUBSCRIBER_ID = "data.subscriber.id";
|
||||
|
||||
public static final Object MUTEX = new Object();
|
||||
|
||||
private ReceiptCredentialRequestContext requestContext;
|
||||
|
||||
private final SubscriberId subscriberId;
|
||||
|
@ -107,6 +109,12 @@ public class SubscriptionReceiptRequestResponseJob extends BaseJob {
|
|||
|
||||
@Override
|
||||
protected void onRun() throws Exception {
|
||||
synchronized (MUTEX) {
|
||||
doRun();
|
||||
}
|
||||
}
|
||||
|
||||
private void doRun() throws Exception {
|
||||
ActiveSubscription.Subscription subscription = getLatestSubscriptionInformation();
|
||||
if (subscription == null || !subscription.isActive()) {
|
||||
Log.d(TAG, "User does not have an active subscription. Exiting.", true);
|
||||
|
|
|
@ -23,12 +23,12 @@ internal class DonationsValues internal constructor(store: KeyValueStore) : Sign
|
|||
private const val KEY_SUBSCRIPTION_CURRENCY_CODE = "donation.currency.code"
|
||||
private const val KEY_CURRENCY_CODE_BOOST = "donation.currency.code.boost"
|
||||
private const val KEY_SUBSCRIBER_ID_PREFIX = "donation.subscriber.id."
|
||||
private const val KEY_IDEMPOTENCY = "donation.idempotency.key"
|
||||
private const val KEY_LEVEL = "donation.level"
|
||||
private const val KEY_LAST_KEEP_ALIVE_LAUNCH = "donation.last.successful.ping"
|
||||
private const val KEY_LAST_END_OF_PERIOD = "donation.last.end.of.period"
|
||||
private const val EXPIRED_BADGE = "donation.expired.badge"
|
||||
private const val USER_MANUALLY_CANCELLED = "donation.user.manually.cancelled"
|
||||
private const val KEY_LEVEL_OPERATION_PREFIX = "donation.level.operation."
|
||||
private const val KEY_LEVEL_HISTORY = "donation.level.history"
|
||||
}
|
||||
|
||||
override fun onFirstEverAppLaunch() = Unit
|
||||
|
@ -114,29 +114,36 @@ internal class DonationsValues internal constructor(store: KeyValueStore) : Sign
|
|||
subscriptionCurrencyPublisher.onNext(Currency.getInstance(currencyCode))
|
||||
}
|
||||
|
||||
fun getLevelOperation(): LevelUpdateOperation? {
|
||||
val level = getString(KEY_LEVEL, null)
|
||||
val idempotencyKey = getBlob(KEY_IDEMPOTENCY, null)?.let { IdempotencyKey.fromBytes(it) }
|
||||
|
||||
return if (level == null || idempotencyKey == null) {
|
||||
null
|
||||
fun getLevelOperation(level: String): LevelUpdateOperation? {
|
||||
val idempotencyKey = getBlob("${KEY_LEVEL_OPERATION_PREFIX}$level", null)
|
||||
return if (idempotencyKey != null) {
|
||||
LevelUpdateOperation(IdempotencyKey.fromBytes(idempotencyKey), level)
|
||||
} else {
|
||||
LevelUpdateOperation(idempotencyKey, level)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun setLevelOperation(levelUpdateOperation: LevelUpdateOperation) {
|
||||
store.beginWrite()
|
||||
.putString(KEY_LEVEL, levelUpdateOperation.level)
|
||||
.putBlob(KEY_IDEMPOTENCY, levelUpdateOperation.idempotencyKey.bytes)
|
||||
.apply()
|
||||
addLevelToHistory(levelUpdateOperation.level)
|
||||
putBlob("$KEY_LEVEL_OPERATION_PREFIX${levelUpdateOperation.level}", levelUpdateOperation.idempotencyKey.bytes)
|
||||
}
|
||||
|
||||
fun clearLevelOperation() {
|
||||
store.beginWrite()
|
||||
.remove(KEY_LEVEL)
|
||||
.remove(KEY_IDEMPOTENCY)
|
||||
.apply()
|
||||
private fun getLevelHistory(): Set<String> {
|
||||
return getString(KEY_LEVEL_HISTORY, "").split(",").toSet()
|
||||
}
|
||||
|
||||
private fun addLevelToHistory(level: String) {
|
||||
val levels = getLevelHistory() + level
|
||||
putString(KEY_LEVEL_HISTORY, levels.joinToString(","))
|
||||
}
|
||||
|
||||
fun clearLevelOperations() {
|
||||
val levelHistory = getLevelHistory()
|
||||
val write = store.beginWrite()
|
||||
for (level in levelHistory) {
|
||||
write.remove("${KEY_LEVEL_OPERATION_PREFIX}$level")
|
||||
}
|
||||
write.apply()
|
||||
}
|
||||
|
||||
fun setExpiredBadge(badge: Badge?) {
|
||||
|
|
|
@ -427,7 +427,7 @@ public final class FeatureFlags {
|
|||
* Whether or not donor badges should be displayed throughout the app.
|
||||
*/
|
||||
public static boolean displayDonorBadges() {
|
||||
return getBoolean(DONOR_BADGES_DISPLAY, false);
|
||||
return getBoolean(DONOR_BADGES_DISPLAY, Environment.IS_STAGING);
|
||||
}
|
||||
|
||||
public static boolean cdsh() {
|
||||
|
|
|
@ -125,10 +125,18 @@ public class DonationsService {
|
|||
* @param level The new level to subscribe to
|
||||
* @param currencyCode The currencyCode the user is using for payment
|
||||
* @param idempotencyKey url-safe-base64-encoded random 16-byte value (see description)
|
||||
* @param mutex A mutex to lock on to avoid a situation where this subscription update happens *as* we are trying to get a credential receipt.
|
||||
*/
|
||||
public Single<ServiceResponse<EmptyResponse>> updateSubscriptionLevel(SubscriberId subscriberId, String level, String currencyCode, String idempotencyKey) {
|
||||
public Single<ServiceResponse<EmptyResponse>> updateSubscriptionLevel(SubscriberId subscriberId,
|
||||
String level,
|
||||
String currencyCode,
|
||||
String idempotencyKey,
|
||||
Object mutex
|
||||
) {
|
||||
return createServiceResponse(() -> {
|
||||
pushServiceSocket.updateSubscriptionLevel(subscriberId.serialize(), level, currencyCode, idempotencyKey);
|
||||
synchronized(mutex) {
|
||||
pushServiceSocket.updateSubscriptionLevel(subscriberId.serialize(), level, currencyCode, idempotencyKey);
|
||||
}
|
||||
return new Pair<>(EmptyResponse.INSTANCE, 200);
|
||||
});
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue