kopia lustrzana https://github.com/ryukoposting/Signal-Android
280 wiersze
12 KiB
Java
280 wiersze
12 KiB
Java
package org.thoughtcrime.securesms.payments.preferences;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
import androidx.lifecycle.LiveData;
|
|
import androidx.lifecycle.Transformations;
|
|
import androidx.lifecycle.ViewModel;
|
|
import androidx.lifecycle.ViewModelProvider;
|
|
|
|
import com.annimon.stream.Stream;
|
|
|
|
import org.signal.core.util.logging.Log;
|
|
import org.signal.core.util.money.FiatMoney;
|
|
import org.thoughtcrime.securesms.R;
|
|
import org.thoughtcrime.securesms.components.settings.SettingHeader;
|
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
|
import org.thoughtcrime.securesms.keyvalue.PaymentsAvailability;
|
|
import org.thoughtcrime.securesms.keyvalue.PaymentsValues;
|
|
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
|
import org.thoughtcrime.securesms.payments.Balance;
|
|
import org.thoughtcrime.securesms.payments.Payment;
|
|
import org.thoughtcrime.securesms.payments.UnreadPaymentsRepository;
|
|
import org.thoughtcrime.securesms.payments.currency.CurrencyExchange;
|
|
import org.thoughtcrime.securesms.payments.currency.CurrencyExchangeRepository;
|
|
import org.thoughtcrime.securesms.payments.preferences.model.InProgress;
|
|
import org.thoughtcrime.securesms.payments.preferences.model.InfoCard;
|
|
import org.thoughtcrime.securesms.payments.preferences.model.IntroducingPayments;
|
|
import org.thoughtcrime.securesms.payments.preferences.model.NoRecentActivity;
|
|
import org.thoughtcrime.securesms.payments.preferences.model.PaymentItem;
|
|
import org.thoughtcrime.securesms.payments.preferences.model.SeeAll;
|
|
import org.thoughtcrime.securesms.util.AsynchronousCallback;
|
|
import org.thoughtcrime.securesms.util.SingleLiveEvent;
|
|
import org.thoughtcrime.securesms.util.adapter.mapping.MappingModelList;
|
|
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
|
|
import org.thoughtcrime.securesms.util.livedata.Store;
|
|
import org.whispersystems.signalservice.api.payments.Money;
|
|
|
|
import java.util.List;
|
|
import java.util.Optional;
|
|
|
|
public class PaymentsHomeViewModel extends ViewModel {
|
|
|
|
private static final String TAG = Log.tag(PaymentsHomeViewModel.class);
|
|
|
|
private static final int MAX_PAYMENT_ITEMS = 4;
|
|
|
|
private final Store<PaymentsHomeState> store;
|
|
private final LiveData<MappingModelList> list;
|
|
private final LiveData<Boolean> paymentsEnabled;
|
|
private final LiveData<Money> balance;
|
|
private final LiveData<FiatMoney> exchange;
|
|
private final SingleLiveEvent<PaymentStateEvent> paymentStateEvents;
|
|
private final SingleLiveEvent<ErrorEnabling> errorEnablingPayments;
|
|
|
|
private final PaymentsHomeRepository paymentsHomeRepository;
|
|
private final CurrencyExchangeRepository currencyExchangeRepository;
|
|
private final UnreadPaymentsRepository unreadPaymentsRepository;
|
|
private final LiveData<LoadState> exchangeLoadState;
|
|
|
|
PaymentsHomeViewModel(@NonNull PaymentsHomeRepository paymentsHomeRepository,
|
|
@NonNull PaymentsRepository paymentsRepository,
|
|
@NonNull CurrencyExchangeRepository currencyExchangeRepository)
|
|
{
|
|
this.paymentsHomeRepository = paymentsHomeRepository;
|
|
this.currencyExchangeRepository = currencyExchangeRepository;
|
|
this.unreadPaymentsRepository = new UnreadPaymentsRepository();
|
|
this.store = new Store<>(new PaymentsHomeState(getPaymentsState()));
|
|
this.balance = LiveDataUtil.mapDistinct(SignalStore.paymentsValues().liveMobileCoinBalance(), Balance::getFullAmount);
|
|
this.list = Transformations.map(store.getStateLiveData(), this::createList);
|
|
this.paymentsEnabled = LiveDataUtil.mapDistinct(store.getStateLiveData(), state -> state.getPaymentsState() == PaymentsHomeState.PaymentsState.ACTIVATED);
|
|
this.exchange = LiveDataUtil.mapDistinct(store.getStateLiveData(), PaymentsHomeState::getExchangeAmount);
|
|
this.exchangeLoadState = LiveDataUtil.mapDistinct(store.getStateLiveData(), PaymentsHomeState::getExchangeRateLoadState);
|
|
this.paymentStateEvents = new SingleLiveEvent<>();
|
|
this.errorEnablingPayments = new SingleLiveEvent<>();
|
|
|
|
this.store.update(paymentsRepository.getRecentPayments(), this::updateRecentPayments);
|
|
|
|
LiveData<CurrencyExchange.ExchangeRate> liveExchangeRate = LiveDataUtil.combineLatest(SignalStore.paymentsValues().liveCurrentCurrency(),
|
|
LiveDataUtil.mapDistinct(store.getStateLiveData(), PaymentsHomeState::getCurrencyExchange),
|
|
(currency, exchange) -> exchange.getExchangeRate(currency));
|
|
|
|
LiveData<Optional<FiatMoney>> liveExchangeAmount = LiveDataUtil.combineLatest(this.balance,
|
|
liveExchangeRate,
|
|
(balance, exchangeRate) -> exchangeRate.exchange(balance));
|
|
this.store.update(liveExchangeAmount, (amount, state) -> state.updateCurrencyAmount(amount.orElse(null)));
|
|
|
|
refreshExchangeRates(true);
|
|
}
|
|
|
|
private static PaymentsHomeState.PaymentsState getPaymentsState() {
|
|
PaymentsValues paymentsValues = SignalStore.paymentsValues();
|
|
|
|
PaymentsAvailability paymentsAvailability = paymentsValues.getPaymentsAvailability();
|
|
|
|
if (paymentsAvailability.canRegister()) {
|
|
return PaymentsHomeState.PaymentsState.NOT_ACTIVATED;
|
|
} else if (paymentsAvailability.isEnabled()) {
|
|
return PaymentsHomeState.PaymentsState.ACTIVATED;
|
|
} else {
|
|
return PaymentsHomeState.PaymentsState.ACTIVATE_NOT_ALLOWED;
|
|
}
|
|
}
|
|
|
|
@NonNull LiveData<PaymentStateEvent> getPaymentStateEvents() {
|
|
return paymentStateEvents;
|
|
}
|
|
|
|
@NonNull LiveData<ErrorEnabling> getErrorEnablingPayments() {
|
|
return errorEnablingPayments;
|
|
}
|
|
|
|
@NonNull LiveData<MappingModelList> getList() {
|
|
return list;
|
|
}
|
|
|
|
@NonNull LiveData<Boolean> getPaymentsEnabled() {
|
|
return paymentsEnabled;
|
|
}
|
|
|
|
@NonNull LiveData<Money> getBalance() {
|
|
return balance;
|
|
}
|
|
|
|
@NonNull LiveData<FiatMoney> getExchange() {
|
|
return exchange;
|
|
}
|
|
|
|
@NonNull LiveData<LoadState> getExchangeLoadState() {
|
|
return exchangeLoadState;
|
|
}
|
|
|
|
void markAllPaymentsSeen() {
|
|
unreadPaymentsRepository.markAllPaymentsSeen();
|
|
}
|
|
|
|
void checkPaymentActivationState() {
|
|
PaymentsHomeState.PaymentsState storedState = store.getState().getPaymentsState();
|
|
boolean paymentsEnabled = SignalStore.paymentsValues().mobileCoinPaymentsEnabled();
|
|
|
|
if (storedState.equals(PaymentsHomeState.PaymentsState.ACTIVATED) && !paymentsEnabled) {
|
|
store.update(s -> s.updatePaymentsEnabled(PaymentsHomeState.PaymentsState.NOT_ACTIVATED));
|
|
paymentStateEvents.setValue(PaymentStateEvent.DEACTIVATED);
|
|
} else if (storedState.equals(PaymentsHomeState.PaymentsState.NOT_ACTIVATED) && paymentsEnabled) {
|
|
store.update(s -> s.updatePaymentsEnabled(PaymentsHomeState.PaymentsState.ACTIVATED));
|
|
paymentStateEvents.setValue(PaymentStateEvent.ACTIVATED);
|
|
}
|
|
}
|
|
|
|
private @NonNull MappingModelList createList(@NonNull PaymentsHomeState state) {
|
|
MappingModelList list = new MappingModelList();
|
|
|
|
if (state.getPaymentsState() == PaymentsHomeState.PaymentsState.ACTIVATED) {
|
|
if (state.getTotalPayments() > 0) {
|
|
list.add(new SettingHeader.Item(R.string.PaymentsHomeFragment__recent_activity));
|
|
list.addAll(state.getPayments());
|
|
if (state.getTotalPayments() > MAX_PAYMENT_ITEMS) {
|
|
list.add(new SeeAll(PaymentType.PAYMENT));
|
|
}
|
|
}
|
|
|
|
if (!state.isRecentPaymentsLoaded()) {
|
|
list.add(new InProgress());
|
|
} else if (state.getRequests().isEmpty() &&
|
|
state.getPayments().isEmpty() &&
|
|
state.isRecentPaymentsLoaded())
|
|
{
|
|
list.add(new NoRecentActivity());
|
|
}
|
|
} else if (state.getPaymentsState() == PaymentsHomeState.PaymentsState.ACTIVATE_NOT_ALLOWED) {
|
|
Log.w(TAG, "Payments remotely disabled or not in region");
|
|
} else {
|
|
list.add(new IntroducingPayments(state.getPaymentsState()));
|
|
}
|
|
|
|
list.addAll(InfoCard.getInfoCards());
|
|
|
|
return list;
|
|
}
|
|
|
|
private @NonNull PaymentsHomeState updateRecentPayments(@NonNull List<Payment> payments,
|
|
@NonNull PaymentsHomeState state)
|
|
{
|
|
List<PaymentItem> paymentItems = Stream.of(payments)
|
|
.limit(MAX_PAYMENT_ITEMS)
|
|
.map(PaymentItem::fromPayment)
|
|
.toList();
|
|
|
|
return state.updatePayments(paymentItems, payments.size());
|
|
}
|
|
|
|
public void onInfoCardDismissed() {
|
|
store.update(s -> s);
|
|
}
|
|
|
|
public void activatePayments() {
|
|
if (store.getState().getPaymentsState() != PaymentsHomeState.PaymentsState.NOT_ACTIVATED) {
|
|
return;
|
|
}
|
|
|
|
store.update(state -> state.updatePaymentsEnabled(PaymentsHomeState.PaymentsState.ACTIVATING));
|
|
|
|
paymentsHomeRepository.activatePayments(new AsynchronousCallback.WorkerThread<Void, PaymentsHomeRepository.Error>() {
|
|
@Override
|
|
public void onComplete(@Nullable Void result) {
|
|
store.update(state -> state.updatePaymentsEnabled(PaymentsHomeState.PaymentsState.ACTIVATED));
|
|
}
|
|
|
|
@Override
|
|
public void onError(@Nullable PaymentsHomeRepository.Error error) {
|
|
store.update(state -> state.updatePaymentsEnabled(PaymentsHomeState.PaymentsState.NOT_ACTIVATED));
|
|
if (error == PaymentsHomeRepository.Error.NetworkError) {
|
|
errorEnablingPayments.postValue(ErrorEnabling.NETWORK);
|
|
} else if (error == PaymentsHomeRepository.Error.RegionError) {
|
|
errorEnablingPayments.postValue(ErrorEnabling.REGION);
|
|
} else {
|
|
throw new AssertionError();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
public void deactivatePayments() {
|
|
Money money = balance.getValue();
|
|
if (money == null) {
|
|
paymentStateEvents.setValue(PaymentStateEvent.NO_BALANCE);
|
|
} else if (money.isPositive()) {
|
|
paymentStateEvents.setValue(PaymentStateEvent.DEACTIVATE_WITH_BALANCE);
|
|
} else {
|
|
paymentStateEvents.setValue(PaymentStateEvent.DEACTIVATE_WITHOUT_BALANCE);
|
|
}
|
|
}
|
|
|
|
public void confirmDeactivatePayments() {
|
|
if (store.getState().getPaymentsState() != PaymentsHomeState.PaymentsState.ACTIVATED) {
|
|
return;
|
|
}
|
|
|
|
store.update(state -> state.updatePaymentsEnabled(PaymentsHomeState.PaymentsState.DEACTIVATING));
|
|
|
|
paymentsHomeRepository.deactivatePayments(result -> {
|
|
store.update(state -> state.updatePaymentsEnabled(result ? PaymentsHomeState.PaymentsState.NOT_ACTIVATED : PaymentsHomeState.PaymentsState.ACTIVATED));
|
|
|
|
if (result) {
|
|
paymentStateEvents.postValue(PaymentStateEvent.DEACTIVATED);
|
|
}
|
|
});
|
|
}
|
|
|
|
public void refreshExchangeRates(boolean refreshIfAble) {
|
|
store.update(state -> state.updateExchangeRateLoadState(LoadState.LOADING));
|
|
currencyExchangeRepository.getCurrencyExchange(new AsynchronousCallback.WorkerThread<CurrencyExchange, Throwable>() {
|
|
@Override
|
|
public void onComplete(@Nullable CurrencyExchange result) {
|
|
store.update(state -> state.updateCurrencyExchange(result, LoadState.LOADED));
|
|
}
|
|
|
|
@Override
|
|
public void onError(@Nullable Throwable error) {
|
|
Log.w(TAG, error);
|
|
store.update(state -> state.updateExchangeRateLoadState(LoadState.ERROR));
|
|
}
|
|
}, refreshIfAble);
|
|
}
|
|
|
|
public static final class Factory implements ViewModelProvider.Factory {
|
|
@Override
|
|
public @NonNull <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
|
|
//noinspection ConstantConditions
|
|
return modelClass.cast(new PaymentsHomeViewModel(new PaymentsHomeRepository(),
|
|
new PaymentsRepository(),
|
|
new CurrencyExchangeRepository(ApplicationDependencies.getPayments())));
|
|
}
|
|
}
|
|
|
|
public enum ErrorEnabling {
|
|
REGION,
|
|
NETWORK
|
|
}
|
|
}
|