Skip to content

Commit

Permalink
Refactor account repo and data source
Browse files Browse the repository at this point in the history
  • Loading branch information
albin-mullvad committed Oct 16, 2023
1 parent 05a0a17 commit 989f57e
Show file tree
Hide file tree
Showing 7 changed files with 35 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import net.mullvad.mullvadvpn.repository.ChangelogRepository
import net.mullvad.mullvadvpn.repository.DeviceRepository
import net.mullvad.mullvadvpn.repository.PrivacyDisclaimerRepository
import net.mullvad.mullvadvpn.repository.SettingsRepository
import net.mullvad.mullvadvpn.ui.serviceconnection.MessageHandler
import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager
import net.mullvad.mullvadvpn.ui.serviceconnection.SplitTunneling
import net.mullvad.mullvadvpn.util.ChangelogDataProvider
Expand All @@ -40,6 +41,7 @@ import org.koin.android.ext.koin.androidApplication
import org.koin.android.ext.koin.androidContext
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.core.qualifier.named
import org.koin.dsl.bind
import org.koin.dsl.module
import org.koin.dsl.onClose

Expand All @@ -59,7 +61,7 @@ val uiModule = module {
SplitTunneling(messenger, dispatcher)
}

single { ServiceConnectionManager(androidContext()) }
single { ServiceConnectionManager(androidContext()) } bind MessageHandler::class
single { InetAddressValidator.getInstance() }
single { androidContext().resources }
single { androidContext().assets }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,97 +10,93 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.withContext
import net.mullvad.mullvadvpn.lib.ipc.Event
import net.mullvad.mullvadvpn.lib.ipc.Request
import net.mullvad.mullvadvpn.model.AccountCreationResult
import net.mullvad.mullvadvpn.model.AccountExpiry
import net.mullvad.mullvadvpn.model.AccountHistory
import net.mullvad.mullvadvpn.model.LoginResult
import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager
import net.mullvad.mullvadvpn.ui.serviceconnection.accountDataSource
import net.mullvad.mullvadvpn.util.flatMapReadyConnectionOrDefault
import net.mullvad.mullvadvpn.ui.serviceconnection.MessageHandler
import net.mullvad.mullvadvpn.ui.serviceconnection.events

class AccountRepository(
private val serviceConnectionManager: ServiceConnectionManager,
val dispatcher: CoroutineDispatcher = Dispatchers.IO
private val messageHandler: MessageHandler,
private val dispatcher: CoroutineDispatcher = Dispatchers.IO
) {
private val _cachedCreatedAccount = MutableStateFlow<String?>(null)
val cachedCreatedAccount = _cachedCreatedAccount.asStateFlow()

private val accountCreationEvents: SharedFlow<AccountCreationResult> =
serviceConnectionManager.connectionState
.flatMapReadyConnectionOrDefault(flowOf()) { state ->
state.container.accountDataSource.accountCreationResult
}
messageHandler
.events<Event.AccountCreationEvent>()
.map { it.result }
.onEach {
_cachedCreatedAccount.value = (it as? AccountCreationResult.Success)?.accountToken
}
.shareIn(CoroutineScope(dispatcher), SharingStarted.WhileSubscribed())

val accountExpiryState: StateFlow<AccountExpiry> =
serviceConnectionManager.connectionState
.flatMapReadyConnectionOrDefault(flowOf()) { state ->
state.container.accountDataSource.accountExpiry
}
messageHandler
.events<Event.AccountExpiryEvent>()
.map { it.expiry }
.stateIn(
CoroutineScope(dispatcher),
SharingStarted.WhileSubscribed(),
AccountExpiry.Missing
)

val accountHistory: StateFlow<AccountHistory> =
serviceConnectionManager.connectionState
.flatMapReadyConnectionOrDefault(flowOf()) { state ->
state.container.accountDataSource.accountHistory
}
messageHandler
.events<Event.AccountHistoryEvent>()
.map { it.history }
.onStart { fetchAccountHistory() }
.stateIn(
CoroutineScope(dispatcher),
SharingStarted.WhileSubscribed(),
AccountHistory.Missing
)

private val loginEvents: SharedFlow<Event.LoginEvent> =
serviceConnectionManager.connectionState
.flatMapReadyConnectionOrDefault(flowOf()) { state ->
state.container.accountDataSource.loginEvents
}
private val loginEvents: SharedFlow<LoginResult> =
messageHandler
.events<Event.LoginEvent>()
.map { it.result }
.shareIn(CoroutineScope(dispatcher), SharingStarted.WhileSubscribed())

suspend fun createAccount(): AccountCreationResult =
withContext(dispatcher) {
val deferred = async { accountCreationEvents.first() }
serviceConnectionManager.accountDataSource()?.createAccount()
messageHandler.trySendRequest(Request.CreateAccount)
deferred.await()
}

suspend fun login(accountToken: String): LoginResult =
withContext(Dispatchers.IO) {
val deferred = async { loginEvents.first().result }
serviceConnectionManager.accountDataSource()?.login(accountToken)
val deferred = async { loginEvents.first() }
messageHandler.trySendRequest(Request.Login(accountToken))
deferred.await()
}

fun logout() {
clearCreatedAccountCache()
serviceConnectionManager.accountDataSource()?.logout()
messageHandler.trySendRequest(Request.Logout)
}

fun fetchAccountExpiry() {
serviceConnectionManager.accountDataSource()?.fetchAccountExpiry()
messageHandler.trySendRequest(Request.FetchAccountExpiry)
}

fun fetchAccountHistory() {
serviceConnectionManager.accountDataSource()?.fetchAccountHistory()
messageHandler.trySendRequest(Request.FetchAccountHistory)
}

fun clearAccountHistory() {
serviceConnectionManager.accountDataSource()?.clearAccountHistory()
messageHandler.trySendRequest(Request.ClearAccountHistory)
}

private fun clearCreatedAccountCache() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onSubscription
import kotlinx.coroutines.launch
import kotlinx.coroutines.withTimeoutOrNull
import net.mullvad.mullvadvpn.BuildConfig
Expand Down Expand Up @@ -312,6 +313,7 @@ open class MainActivity : FragmentActivity() {
private suspend fun isExpired(timeoutMillis: Long): Boolean {
return withTimeoutOrNull(timeoutMillis) {
accountRepository.accountExpiryState
.onSubscription { accountRepository.fetchAccountExpiry() }
.filter { it is AccountExpiry.Available }
.map { it.date()?.isBeforeNow }
.first()
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ class ServiceConnectionContainer(

val events = dispatcher.parsedMessages.filterIsInstance<Event>()

val accountDataSource = ServiceConnectionAccountDataSource(connection, dispatcher)
val authTokenCache = AuthTokenCache(connection, dispatcher)
val connectionProxy = ConnectionProxy(connection, dispatcher)
val deviceDataSource = ServiceConnectionDeviceDataSource(connection, dispatcher)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
package net.mullvad.mullvadvpn.ui.serviceconnection

fun ServiceConnectionManager.accountDataSource() =
this.connectionState.value.readyContainer()?.accountDataSource

fun ServiceConnectionManager.appVersionInfoCache() =
this.connectionState.value.readyContainer()?.appVersionInfoCache

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ class AccountViewModel(
@Suppress("konsist.ensure public properties use permitted names")
val enterTransitionEndAction = _enterTransitionEndAction.asSharedFlow()

init {
accountRepository.fetchAccountExpiry()
}

fun onManageAccountClick() {
viewModelScope.launch {
_uiSideEffect.tryEmit(
Expand Down

0 comments on commit 989f57e

Please sign in to comment.