mirror of https://github.com/readrops/Readrops.git
Add account creation screens with global account state management
This commit is contained in:
parent
c9db21e881
commit
dac54a307f
|
@ -1,24 +1,26 @@
|
||||||
package com.readrops.app.compose
|
package com.readrops.app.compose
|
||||||
|
|
||||||
import com.readrops.app.compose.account.AccountViewModel
|
import com.readrops.app.compose.account.selection.AccountSelectionViewModel
|
||||||
import com.readrops.app.compose.feeds.FeedViewModel
|
import com.readrops.app.compose.feeds.FeedViewModel
|
||||||
import com.readrops.app.compose.repositories.BaseRepository
|
import com.readrops.app.compose.repositories.BaseRepository
|
||||||
import com.readrops.app.compose.repositories.LocalRSSRepository
|
import com.readrops.app.compose.repositories.LocalRSSRepository
|
||||||
import com.readrops.app.compose.timelime.TimelineViewModel
|
import com.readrops.app.compose.timelime.TimelineViewModel
|
||||||
import com.readrops.db.entities.account.Account
|
import com.readrops.db.entities.account.Account
|
||||||
import com.readrops.db.entities.account.AccountType
|
|
||||||
import org.koin.androidx.viewmodel.dsl.viewModel
|
import org.koin.androidx.viewmodel.dsl.viewModel
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
|
|
||||||
val composeAppModule = module {
|
val composeAppModule = module {
|
||||||
|
|
||||||
viewModel { TimelineViewModel(get(), get()) }
|
viewModel { TimelineViewModel(get()) }
|
||||||
|
|
||||||
viewModel { FeedViewModel(get(), get()) }
|
viewModel { FeedViewModel(get()) }
|
||||||
|
|
||||||
viewModel { AccountViewModel(get()) }
|
viewModel { AccountSelectionViewModel(get()) }
|
||||||
|
|
||||||
// repositories
|
// repositories
|
||||||
|
|
||||||
single<BaseRepository> { LocalRSSRepository(get(), get(), Account(id = 1, isCurrentAccount = true, accountType = AccountType.LOCAL)) }
|
factory<BaseRepository> { (account: Account) ->
|
||||||
|
LocalRSSRepository(get(), get(), account)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -4,26 +4,12 @@ import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.compose.animation.ExperimentalAnimationApi
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.filled.AccountBox
|
|
||||||
import androidx.compose.material.icons.filled.MoreVert
|
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.res.painterResource
|
|
||||||
import cafe.adriel.voyager.navigator.CurrentScreen
|
|
||||||
import cafe.adriel.voyager.navigator.Navigator
|
import cafe.adriel.voyager.navigator.Navigator
|
||||||
import cafe.adriel.voyager.navigator.tab.TabNavigator
|
|
||||||
import cafe.adriel.voyager.transitions.FadeTransition
|
import cafe.adriel.voyager.transitions.FadeTransition
|
||||||
import cafe.adriel.voyager.transitions.SlideTransition
|
import com.readrops.app.compose.account.selection.AccountSelectionScreen
|
||||||
import com.readrops.app.compose.account.AccountSelectionScreen
|
import com.readrops.app.compose.account.selection.AccountSelectionViewModel
|
||||||
import com.readrops.app.compose.account.AccountTab
|
|
||||||
import com.readrops.app.compose.account.AccountViewModel
|
|
||||||
import com.readrops.app.compose.feeds.FeedTab
|
|
||||||
import com.readrops.app.compose.home.HomeScreen
|
import com.readrops.app.compose.home.HomeScreen
|
||||||
import com.readrops.app.compose.more.MoreTab
|
|
||||||
import com.readrops.app.compose.timelime.TimelineTab
|
|
||||||
import org.koin.androidx.viewmodel.ext.android.getViewModel
|
import org.koin.androidx.viewmodel.ext.android.getViewModel
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
@ -32,7 +18,7 @@ class MainActivity : ComponentActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
val viewModel = getViewModel<AccountViewModel>()
|
val viewModel = getViewModel<AccountSelectionViewModel>()
|
||||||
val accountExists = viewModel.accountExists()
|
val accountExists = viewModel.accountExists()
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
package com.readrops.app.compose.account
|
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import cafe.adriel.voyager.androidx.AndroidScreen
|
|
||||||
|
|
||||||
class AccountSelectionScreen : AndroidScreen() {
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
override fun Content() {
|
|
||||||
Column {
|
|
||||||
Text(text = "account selection")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
package com.readrops.app.compose.account
|
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import com.readrops.db.Database
|
|
||||||
import kotlinx.coroutines.flow.callbackFlow
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
|
|
||||||
class AccountViewModel(
|
|
||||||
private val database: Database,
|
|
||||||
) : ViewModel() {
|
|
||||||
|
|
||||||
fun accountExists(): Boolean {
|
|
||||||
val accountCount = runBlocking {
|
|
||||||
database.newAccountDao().selectAccountCount()
|
|
||||||
}
|
|
||||||
|
|
||||||
return accountCount > 0
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
package com.readrops.app.compose.account.credentials
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import cafe.adriel.voyager.androidx.AndroidScreen
|
||||||
|
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||||
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
|
import com.readrops.app.compose.home.HomeScreen
|
||||||
|
import com.readrops.db.entities.account.Account
|
||||||
|
import com.readrops.db.entities.account.AccountType
|
||||||
|
|
||||||
|
class AccountCredentialsScreen(
|
||||||
|
private val accountType: AccountType,
|
||||||
|
private val account: Account? = null,
|
||||||
|
) : AndroidScreen() {
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
override fun Content() {
|
||||||
|
val navigator = LocalNavigator.currentOrThrow
|
||||||
|
|
||||||
|
Column {
|
||||||
|
Text(
|
||||||
|
text = "AccountCredentialsScreen"
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.size(16.dp))
|
||||||
|
|
||||||
|
Button(onClick = { navigator.replaceAll(HomeScreen()) }) {
|
||||||
|
Text(
|
||||||
|
text = "skip"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
package com.readrops.app.compose.account.selection
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import cafe.adriel.voyager.androidx.AndroidScreen
|
||||||
|
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||||
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
|
import com.readrops.app.compose.R
|
||||||
|
import com.readrops.app.compose.account.credentials.AccountCredentialsScreen
|
||||||
|
import com.readrops.app.compose.home.HomeScreen
|
||||||
|
import com.readrops.db.entities.account.AccountType
|
||||||
|
import org.koin.androidx.compose.getViewModel
|
||||||
|
|
||||||
|
class AccountSelectionScreen : AndroidScreen() {
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
override fun Content() {
|
||||||
|
val viewModel = getViewModel<AccountSelectionViewModel>()
|
||||||
|
val navState by viewModel.navState.collectAsState()
|
||||||
|
val navigator = LocalNavigator.currentOrThrow
|
||||||
|
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
) {
|
||||||
|
Text(text = "Choose an account")
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.size(8.dp))
|
||||||
|
|
||||||
|
AccountType.values().forEach { accountType ->
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.clickable { viewModel.createAccount(accountType) }
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(id = R.drawable.ic_freshrss),
|
||||||
|
contentDescription = accountType.name,
|
||||||
|
modifier = Modifier.size(24.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.size(4.dp))
|
||||||
|
|
||||||
|
Text(text = accountType.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.size(8.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when (navState) {
|
||||||
|
is AccountSelectionViewModel.NavState.GoToHomeScreen -> {
|
||||||
|
// using replace makes the app crash due to a screen key conflict
|
||||||
|
navigator.replaceAll(HomeScreen())
|
||||||
|
}
|
||||||
|
|
||||||
|
is AccountSelectionViewModel.NavState.GoToAccountCredentialsScreen -> {
|
||||||
|
val accountType = (navState as AccountSelectionViewModel.NavState.GoToAccountCredentialsScreen).accountType
|
||||||
|
|
||||||
|
navigator.push(AccountCredentialsScreen(accountType))
|
||||||
|
viewModel.resetNavState()
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
package com.readrops.app.compose.account.selection
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.readrops.db.Database
|
||||||
|
import com.readrops.db.entities.account.Account
|
||||||
|
import com.readrops.db.entities.account.AccountType
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.koin.core.component.KoinComponent
|
||||||
|
import org.koin.core.component.get
|
||||||
|
|
||||||
|
class AccountSelectionViewModel(
|
||||||
|
private val database: Database,
|
||||||
|
private val dispatcher: CoroutineDispatcher = Dispatchers.IO
|
||||||
|
) : ViewModel(), KoinComponent {
|
||||||
|
|
||||||
|
private val _navState = MutableStateFlow<NavState>(NavState.Idle)
|
||||||
|
val navState = _navState.asStateFlow()
|
||||||
|
|
||||||
|
fun accountExists(): Boolean {
|
||||||
|
val accountCount = runBlocking {
|
||||||
|
database.newAccountDao().selectAccountCount()
|
||||||
|
}
|
||||||
|
|
||||||
|
return accountCount > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createAccount(accountType: AccountType) {
|
||||||
|
if (accountType == AccountType.LOCAL) {
|
||||||
|
createLocalAccount()
|
||||||
|
} else {
|
||||||
|
_navState.update { NavState.GoToAccountCredentialsScreen(accountType) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resetNavState() {
|
||||||
|
_navState.update { NavState.Idle }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createLocalAccount() {
|
||||||
|
val context = get<Context>()
|
||||||
|
val account = Account(
|
||||||
|
url = null,
|
||||||
|
accountName = context.getString(AccountType.LOCAL.typeName),
|
||||||
|
accountType = AccountType.LOCAL,
|
||||||
|
isCurrentAccount = true
|
||||||
|
)
|
||||||
|
|
||||||
|
viewModelScope.launch(dispatcher) {
|
||||||
|
database.newAccountDao().insert(account)
|
||||||
|
|
||||||
|
_navState.update { NavState.GoToHomeScreen }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sealed class NavState {
|
||||||
|
object Idle : NavState()
|
||||||
|
object GoToHomeScreen : NavState()
|
||||||
|
class GoToAccountCredentialsScreen(val accountType: AccountType) : NavState()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package com.readrops.app.compose.base
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.readrops.app.compose.repositories.BaseRepository
|
||||||
|
import com.readrops.app.compose.repositories.LocalRSSRepository
|
||||||
|
import com.readrops.db.Database
|
||||||
|
import com.readrops.db.entities.account.Account
|
||||||
|
import kotlinx.coroutines.async
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.koin.core.component.KoinComponent
|
||||||
|
import org.koin.core.component.get
|
||||||
|
import org.koin.core.parameter.parametersOf
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom ViewModel for Tab screens handling account change
|
||||||
|
*/
|
||||||
|
abstract class TabViewModel(
|
||||||
|
private val database: Database,
|
||||||
|
) : ViewModel(), KoinComponent {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repository intended to be rebuilt when the current account changes
|
||||||
|
*/
|
||||||
|
protected var repository: BaseRepository? = null
|
||||||
|
|
||||||
|
protected var currentAccount: Account? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called when the repository has been rebuilt from the new current account
|
||||||
|
*/
|
||||||
|
abstract fun invalidate()
|
||||||
|
|
||||||
|
init {
|
||||||
|
viewModelScope.launch {
|
||||||
|
database.newAccountDao()
|
||||||
|
.selectCurrentAccount()
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.collect { account ->
|
||||||
|
currentAccount = account
|
||||||
|
repository = get(parameters = { parametersOf(account) })
|
||||||
|
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ package com.readrops.app.compose.feeds
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.readrops.app.compose.base.TabViewModel
|
||||||
import com.readrops.app.compose.repositories.BaseRepository
|
import com.readrops.app.compose.repositories.BaseRepository
|
||||||
import com.readrops.db.Database
|
import com.readrops.db.Database
|
||||||
import com.readrops.db.entities.Feed
|
import com.readrops.db.entities.Feed
|
||||||
|
@ -13,8 +14,7 @@ import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class FeedViewModel(
|
class FeedViewModel(
|
||||||
private val database: Database,
|
private val database: Database,
|
||||||
private val repository: BaseRepository,
|
) : TabViewModel(database) {
|
||||||
) : ViewModel() {
|
|
||||||
|
|
||||||
private val _feedsState = MutableStateFlow<FeedsState>(FeedsState.InitialState)
|
private val _feedsState = MutableStateFlow<FeedsState>(FeedsState.InitialState)
|
||||||
val feedsState = _feedsState.asStateFlow()
|
val feedsState = _feedsState.asStateFlow()
|
||||||
|
@ -29,9 +29,13 @@ class FeedViewModel(
|
||||||
|
|
||||||
fun insertFeed(url: String) {
|
fun insertFeed(url: String) {
|
||||||
viewModelScope.launch(context = Dispatchers.IO) {
|
viewModelScope.launch(context = Dispatchers.IO) {
|
||||||
repository.insertNewFeeds(listOf(url))
|
repository?.insertNewFeeds(listOf(url))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun invalidate() {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class FeedsState {
|
sealed class FeedsState {
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
package com.readrops.app.compose.timelime
|
package com.readrops.app.compose.timelime
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.readrops.app.compose.repositories.BaseRepository
|
import com.readrops.app.compose.base.TabViewModel
|
||||||
import com.readrops.db.Database
|
import com.readrops.db.Database
|
||||||
import com.readrops.db.entities.Item
|
import com.readrops.db.entities.Item
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -12,9 +11,8 @@ import kotlinx.coroutines.flow.catch
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class TimelineViewModel(
|
class TimelineViewModel(
|
||||||
private val database: Database,
|
private val database: Database,
|
||||||
private val repository: BaseRepository,
|
) : TabViewModel(database) {
|
||||||
) : ViewModel() {
|
|
||||||
|
|
||||||
private val _timelineState = MutableStateFlow<TimelineState>(TimelineState.InitialState)
|
private val _timelineState = MutableStateFlow<TimelineState>(TimelineState.InitialState)
|
||||||
val timelineState = _timelineState.asStateFlow()
|
val timelineState = _timelineState.asStateFlow()
|
||||||
|
@ -25,23 +23,27 @@ class TimelineViewModel(
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch(context = Dispatchers.IO) {
|
viewModelScope.launch(context = Dispatchers.IO) {
|
||||||
database.newItemDao().selectAll()
|
database.newItemDao().selectAll()
|
||||||
.catch { _timelineState.value = TimelineState.ErrorState(Exception(it)) }
|
.catch { _timelineState.value = TimelineState.ErrorState(Exception(it)) }
|
||||||
.collect {
|
.collect {
|
||||||
_timelineState.value = TimelineState.LoadedState(it)
|
_timelineState.value = TimelineState.LoadedState(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun refreshTimeline() {
|
fun refreshTimeline() {
|
||||||
_isRefreshing.value = true
|
_isRefreshing.value = true
|
||||||
viewModelScope.launch(context = Dispatchers.IO) {
|
viewModelScope.launch(context = Dispatchers.IO) {
|
||||||
repository.synchronize(null) {
|
repository?.synchronize(null) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_isRefreshing.value = false
|
_isRefreshing.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun invalidate() {
|
||||||
|
refreshTimeline()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class TimelineState {
|
sealed class TimelineState {
|
||||||
|
|
|
@ -2,11 +2,15 @@ package com.readrops.db.dao.newdao
|
||||||
|
|
||||||
import androidx.room.Dao
|
import androidx.room.Dao
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
|
import com.readrops.db.entities.account.Account
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface NewAccountDao {
|
interface NewAccountDao : NewBaseDao<Account> {
|
||||||
|
|
||||||
@Query("Select Count(*) From Account")
|
@Query("Select Count(*) From Account")
|
||||||
suspend fun selectAccountCount(): Int
|
suspend fun selectAccountCount(): Int
|
||||||
|
|
||||||
|
@Query("Select * From Account Where current_account = 1")
|
||||||
|
fun selectCurrentAccount(): Flow<Account>
|
||||||
}
|
}
|
Loading…
Reference in New Issue