Mercury-IM/domain/src/main/java/org/mercury_im/messenger/core/viewmodel/account/LoginViewModel.java

190 lines
7.3 KiB
Java

package org.mercury_im.messenger.core.viewmodel.account;
import org.jivesoftware.smack.util.StringUtils;
import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.stringprep.XmppStringprepException;
import org.mercury_im.messenger.core.SchedulersFacade;
import org.mercury_im.messenger.core.account.error.PasswordError;
import org.mercury_im.messenger.core.account.error.UsernameError;
import org.mercury_im.messenger.core.connection.MercuryConnectionManager;
import org.mercury_im.messenger.core.connection.exception.InvalidCredentialsException;
import org.mercury_im.messenger.core.connection.exception.ServerUnreachableException;
import org.mercury_im.messenger.core.connection.state.ConnectionState;
import org.mercury_im.messenger.core.connection.state.ConnectivityState;
import org.mercury_im.messenger.core.data.repository.AccountRepository;
import org.mercury_im.messenger.core.util.Optional;
import org.mercury_im.messenger.core.viewmodel.MercuryViewModel;
import org.mercury_im.messenger.entity.Account;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.inject.Inject;
import io.reactivex.Observable;
import io.reactivex.subjects.BehaviorSubject;
public class LoginViewModel implements MercuryViewModel {
private static final Logger LOGGER = Logger.getLogger(LoginViewModel.class.getName());
private final MercuryConnectionManager connectionManager;
private final AccountRepository accountRepository;
private final SchedulersFacade schedulers;
private BehaviorSubject<Optional<UsernameError>> loginUsernameError;
private BehaviorSubject<Optional<PasswordError>> loginPasswordError;
private BehaviorSubject<Boolean> isLoginPossible;
private BehaviorSubject<Optional<Account>> isLoginSuccessful;
private BehaviorSubject<Boolean> displayProgressBar;
private EntityBareJid loginUsernameValue;
private String loginPasswordValue;
private Account account;
@Inject
public LoginViewModel(MercuryConnectionManager connectionManager,
AccountRepository accountRepository,
SchedulersFacade schedulers) {
this.connectionManager = connectionManager;
this.accountRepository = accountRepository;
this.schedulers = schedulers;
reset();
}
public void reset() {
loginUsernameError = BehaviorSubject.createDefault(new Optional<>());
loginPasswordError = BehaviorSubject.createDefault(new Optional<>());
isLoginPossible = BehaviorSubject.createDefault(false);
isLoginSuccessful = BehaviorSubject.createDefault(new Optional<>());
displayProgressBar = BehaviorSubject.createDefault(false);
loginUsernameValue = null;
loginPasswordValue = null;
}
public Observable<Optional<UsernameError>> getLoginUsernameError() {
return loginUsernameError;
}
public Observable<Optional<PasswordError>> getLoginPasswordError() {
return loginPasswordError;
}
public Observable<Boolean> isLoginPossible() {
return isLoginPossible;
}
public Observable<Optional<Account>> isLoginSuccessful() {
return isLoginSuccessful;
}
public synchronized void onLoginUsernameChanged(String username) {
if (StringUtils.isNullOrEmpty(username)) {
loginUsernameValue = null;
loginUsernameError.onNext(new Optional<>(UsernameError.emptyUsername));
} else {
try {
loginUsernameValue = JidCreate.entityBareFrom(username);
loginUsernameError.onNext(new Optional<>());
} catch (XmppStringprepException e) {
loginUsernameValue = null;
loginUsernameError.onNext(new Optional<>(UsernameError.invalidUsername));
}
}
updateLoginPossible();
}
public synchronized void onLoginPasswordChanged(String password) {
if (StringUtils.isNullOrEmpty(password)) {
loginPasswordValue = null;
loginPasswordError.onNext(new Optional<>(PasswordError.emptyPassword));
} else {
loginPasswordValue = password;
loginPasswordError.onNext(new Optional<>());
}
updateLoginPossible();
}
private synchronized void updateLoginPossible() {
isLoginPossible.onNext(loginUsernameValue != null && loginPasswordValue != null);
}
public synchronized Account login() {
displayProgressBar.onNext(true);
if (!isLoginPossible.getValue()) {
// Prevent race condition where account would be logged in twice
return account;
}
isLoginPossible.onNext(false);
account = createAccountEntity();
//MercuryConnection connection = connectionManager.createConnection(account);
addDisposable(accountRepository.upsertAccount(account)
//.andThen(connection.connect())
//.andThen(connection.login())
//.andThen(connectionManager.registerConnection(connection))
.subscribeOn(schedulers.getNewThread())
.observeOn(schedulers.getUiScheduler())
.subscribe(
a -> LOGGER.log(Level.INFO, "Account " + a + " successfully inserted."),
this::onLoginFailed
));
addDisposable(connectionManager.observeConnectionPool()
.map(cp -> {
ConnectionState state = cp.getConnectionStates().get(account.getId());
if (state == null) {
return ConnectivityState.disconnected;
} else {
return state.getConnectivity();
}
})
.filter(s -> s == ConnectivityState.disconnectedOnError || s == ConnectivityState.authenticated)
.firstOrError()
.compose(schedulers.executeUiSafeSingle())
.subscribe(connectivityState -> {
displayProgressBar.onNext(false);
if (connectivityState == ConnectivityState.disconnectedOnError) {
onLoginFailed(new Exception());
} else {
onLoginSuccessful(account);
}
}));
return account;
}
private Account createAccountEntity() {
Account account = new Account();
account.setAddress(loginUsernameValue.asEntityBareJidString());
account.setPassword(loginPasswordValue);
account.setEnabled(true);
return account;
}
private void onLoginSuccessful(Account account) {
LOGGER.log(Level.INFO, "Login successful.");
isLoginSuccessful.onNext(new Optional<>(account));
}
private void onLoginFailed(Throwable error) {
LOGGER.log(Level.INFO, "Login failed!");
isLoginPossible.onNext(true);
if (error instanceof InvalidCredentialsException) {
loginPasswordError.onNext(new Optional<>(PasswordError.incorrectPassword));
} else if (error instanceof ServerUnreachableException) {
loginUsernameError.onNext(new Optional<>(UsernameError.unreachableServer));
}
}
public Observable<Boolean> isDisplayProgressBar() {
return displayProgressBar;
}
}