mirror of https://github.com/readrops/Readrops.git
Add initial FreshRSS login with new kotlin repository
This commit is contained in:
parent
a7c0749641
commit
f14ed7f331
|
@ -3,8 +3,13 @@ package com.readrops.api
|
|||
import com.readrops.api.localfeed.LocalRSSDataSource
|
||||
import com.readrops.api.services.Credentials
|
||||
import com.readrops.api.services.freshrss.FreshRSSDataSource
|
||||
import com.readrops.api.services.freshrss.FreshRSSService
|
||||
import com.readrops.api.services.freshrss.adapters.*
|
||||
import com.readrops.api.services.freshrss.NewFreshRSSDataSource
|
||||
import com.readrops.api.services.freshrss.NewFreshRSSService
|
||||
import com.readrops.api.services.freshrss.adapters.FreshRSSFeedsAdapter
|
||||
import com.readrops.api.services.freshrss.adapters.FreshRSSFoldersAdapter
|
||||
import com.readrops.api.services.freshrss.adapters.FreshRSSItemsAdapter
|
||||
import com.readrops.api.services.freshrss.adapters.FreshRSSItemsIdsAdapter
|
||||
import com.readrops.api.services.freshrss.adapters.FreshRSSUserInfoAdapter
|
||||
import com.readrops.api.services.nextcloudnews.NextNewsDataSource
|
||||
import com.readrops.api.services.nextcloudnews.NextNewsService
|
||||
import com.readrops.api.services.nextcloudnews.adapters.NextNewsFeedsAdapter
|
||||
|
@ -27,12 +32,12 @@ val apiModule = module {
|
|||
|
||||
single {
|
||||
OkHttpClient.Builder()
|
||||
.callTimeout(1, TimeUnit.MINUTES)
|
||||
.readTimeout(1, TimeUnit.HOURS)
|
||||
.addInterceptor(get<AuthInterceptor>())
|
||||
.addInterceptor(get<ErrorInterceptor>())
|
||||
//.addInterceptor(NiddlerOkHttpInterceptor(get(), "niddler"))
|
||||
.build()
|
||||
.callTimeout(1, TimeUnit.MINUTES)
|
||||
.readTimeout(1, TimeUnit.HOURS)
|
||||
.addInterceptor(get<AuthInterceptor>())
|
||||
.addInterceptor(get<ErrorInterceptor>())
|
||||
//.addInterceptor(NiddlerOkHttpInterceptor(get(), "niddler"))
|
||||
.build()
|
||||
}
|
||||
|
||||
single { AuthInterceptor() }
|
||||
|
@ -45,14 +50,15 @@ val apiModule = module {
|
|||
|
||||
factory { params -> FreshRSSDataSource(get(parameters = { params })) }
|
||||
|
||||
factory { params -> NewFreshRSSDataSource(get(parameters = { params })) }
|
||||
|
||||
factory { (credentials: Credentials) ->
|
||||
Retrofit.Builder()
|
||||
.baseUrl(credentials.url)
|
||||
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
|
||||
.client(get())
|
||||
.addConverterFactory(MoshiConverterFactory.create(get(named("freshrssMoshi"))))
|
||||
.build()
|
||||
.create(FreshRSSService::class.java)
|
||||
.baseUrl(credentials.url)
|
||||
.client(get())
|
||||
.addConverterFactory(MoshiConverterFactory.create(get(named("freshrssMoshi"))))
|
||||
.build()
|
||||
.create(NewFreshRSSService::class.java)
|
||||
}
|
||||
|
||||
single(named("freshrssMoshi")) {
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
package com.readrops.api.utils.exceptions
|
||||
|
||||
import okhttp3.Response
|
||||
import java.io.IOException
|
||||
|
||||
|
||||
class HttpException(val response: Response) : Exception() {
|
||||
class HttpException(val response: Response) : IOException() {
|
||||
|
||||
val code: Int
|
||||
get() = response.code
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
package com.readrops.app.compose
|
||||
|
||||
import com.readrops.api.services.Credentials
|
||||
import com.readrops.app.compose.account.AccountScreenModel
|
||||
import com.readrops.app.compose.account.credentials.AccountCredentialsScreenModel
|
||||
import com.readrops.app.compose.account.selection.AccountSelectionScreenModel
|
||||
import com.readrops.app.compose.feeds.FeedScreenModel
|
||||
import com.readrops.app.compose.item.ItemScreenModel
|
||||
import com.readrops.app.compose.repositories.BaseRepository
|
||||
import com.readrops.app.compose.repositories.FreshRSSRepository
|
||||
import com.readrops.app.compose.repositories.GetFoldersWithFeeds
|
||||
import com.readrops.app.compose.repositories.LocalRSSRepository
|
||||
import com.readrops.app.compose.timelime.TimelineScreenModel
|
||||
import com.readrops.db.entities.account.Account
|
||||
import com.readrops.db.entities.account.AccountType
|
||||
import org.koin.core.parameter.parametersOf
|
||||
import org.koin.dsl.module
|
||||
|
||||
val composeAppModule = module {
|
||||
|
@ -23,18 +26,22 @@ val composeAppModule = module {
|
|||
|
||||
factory { AccountScreenModel(get()) }
|
||||
|
||||
factory { (itemId: Int) ->
|
||||
ItemScreenModel(get(), itemId)
|
||||
}
|
||||
factory { (itemId: Int) -> ItemScreenModel(get(), itemId) }
|
||||
|
||||
factory { (accountType: AccountType) -> AccountCredentialsScreenModel(accountType) }
|
||||
factory { (accountType: AccountType) -> AccountCredentialsScreenModel(accountType, get()) }
|
||||
|
||||
single { GetFoldersWithFeeds(get()) }
|
||||
|
||||
// repositories
|
||||
|
||||
factory<BaseRepository> { (account: Account) ->
|
||||
LocalRSSRepository(get(), get(), account)
|
||||
when (account.accountType) {
|
||||
AccountType.LOCAL -> LocalRSSRepository(get(), get(), account)
|
||||
AccountType.FRESHRSS -> FreshRSSRepository(
|
||||
get(), account,
|
||||
get(parameters = { parametersOf(Credentials.toCredentials(account)) })
|
||||
)
|
||||
else -> throw IllegalArgumentException("Unknown account type")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -35,7 +35,9 @@ import cafe.adriel.voyager.koin.getScreenModel
|
|||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import com.readrops.app.compose.R
|
||||
import com.readrops.app.compose.home.HomeScreen
|
||||
import com.readrops.app.compose.util.components.AndroidScreen
|
||||
import com.readrops.app.compose.util.components.errorText
|
||||
import com.readrops.app.compose.util.theme.ShortSpacer
|
||||
import com.readrops.app.compose.util.theme.VeryLargeSpacer
|
||||
import com.readrops.app.compose.util.theme.spacing
|
||||
|
@ -54,6 +56,10 @@ class AccountCredentialsScreen(
|
|||
|
||||
val state by screenModel.state.collectAsStateWithLifecycle()
|
||||
|
||||
if (state.goToHomeScreen) {
|
||||
navigator.replaceAll(HomeScreen())
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier.imePadding()
|
||||
) {
|
||||
|
@ -129,9 +135,9 @@ class AccountCredentialsScreen(
|
|||
) {
|
||||
Icon(
|
||||
painter = painterResource(
|
||||
id = if (state.isPasswordVisible)
|
||||
id = if (state.isPasswordVisible) {
|
||||
R.drawable.ic_visible_off
|
||||
else R.drawable.ic_visible
|
||||
} else R.drawable.ic_visible
|
||||
),
|
||||
contentDescription = null
|
||||
)
|
||||
|
@ -157,7 +163,7 @@ class AccountCredentialsScreen(
|
|||
onClick = { screenModel.login() },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
if (state.isLoginStarted) {
|
||||
if (state.isLoginOnGoing) {
|
||||
CircularProgressIndicator(
|
||||
color = MaterialTheme.colorScheme.onPrimary,
|
||||
strokeWidth = 2.dp,
|
||||
|
@ -167,6 +173,16 @@ class AccountCredentialsScreen(
|
|||
Text(text = stringResource(id = R.string.validate))
|
||||
}
|
||||
}
|
||||
|
||||
if (state.loginException != null) {
|
||||
ShortSpacer()
|
||||
|
||||
Text(
|
||||
text = errorText(exception = state.loginException!!),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.error
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,20 +2,38 @@ package com.readrops.app.compose.account.credentials
|
|||
|
||||
import android.util.Patterns
|
||||
import cafe.adriel.voyager.core.model.StateScreenModel
|
||||
import cafe.adriel.voyager.core.model.screenModelScope
|
||||
import com.readrops.app.compose.repositories.BaseRepository
|
||||
import com.readrops.app.compose.util.components.TextFieldError
|
||||
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.update
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.get
|
||||
import org.koin.core.parameter.parametersOf
|
||||
|
||||
class AccountCredentialsScreenModel(
|
||||
private val accountType: AccountType
|
||||
) : StateScreenModel<AccountCredentialsState>(AccountCredentialsState(name = accountType.name)) {
|
||||
private val accountType: AccountType,
|
||||
private val database: Database,
|
||||
private val dispatcher: CoroutineDispatcher = Dispatchers.IO
|
||||
) : StateScreenModel<AccountCredentialsState>(AccountCredentialsState(name = accountType.name)),
|
||||
KoinComponent {
|
||||
|
||||
fun onEvent(event: Event): Unit = with(mutableState) {
|
||||
when (event) {
|
||||
is Event.LoginEvent -> update { it.copy(login = event.value, loginError = null) }
|
||||
is Event.NameEvent -> update { it.copy(name = event.value, nameError = null) }
|
||||
is Event.PasswordEvent -> update { it.copy(password = event.value, passwordError = null) }
|
||||
is Event.PasswordEvent -> update {
|
||||
it.copy(
|
||||
password = event.value,
|
||||
passwordError = null
|
||||
)
|
||||
}
|
||||
|
||||
is Event.URLEvent -> update { it.copy(url = event.value, urlError = null) }
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +44,7 @@ class AccountCredentialsScreenModel(
|
|||
|
||||
fun login() {
|
||||
if (validateFields()) {
|
||||
mutableState.update { it.copy(isLoginStarted = true) }
|
||||
mutableState.update { it.copy(isLoginOnGoing = true) }
|
||||
|
||||
with(state.value) {
|
||||
val account = Account(
|
||||
|
@ -34,8 +52,29 @@ class AccountCredentialsScreenModel(
|
|||
accountName = name,
|
||||
login = login,
|
||||
password = password,
|
||||
accountType = accountType
|
||||
accountType = accountType,
|
||||
isCurrentAccount = true
|
||||
)
|
||||
|
||||
val repository = get<BaseRepository> { parametersOf(account) }
|
||||
|
||||
screenModelScope.launch(dispatcher) {
|
||||
try {
|
||||
repository.login(account)
|
||||
} catch (e: Exception) {
|
||||
mutableState.update {
|
||||
it.copy(
|
||||
loginException = e,
|
||||
isLoginOnGoing = false
|
||||
)
|
||||
}
|
||||
|
||||
return@launch
|
||||
}
|
||||
|
||||
database.newAccountDao().insert(account)
|
||||
mutableState.update { it.copy(goToHomeScreen = true) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -82,7 +121,9 @@ data class AccountCredentialsState(
|
|||
val password: String = "",
|
||||
val passwordError: TextFieldError? = null,
|
||||
val isPasswordVisible: Boolean = false,
|
||||
val isLoginStarted: Boolean = false
|
||||
val isLoginOnGoing: Boolean = false,
|
||||
val goToHomeScreen: Boolean = false,
|
||||
val loginException: Exception? = null
|
||||
) {
|
||||
val isUrlError = urlError != null
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ abstract class ARepository(
|
|||
/**
|
||||
* This method is intended for remote accounts.
|
||||
*/
|
||||
abstract suspend fun login()
|
||||
abstract suspend fun login(account: Account)
|
||||
|
||||
/**
|
||||
* Global synchronization for the local account.
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
package com.readrops.app.compose.repositories
|
||||
|
||||
import com.readrops.api.services.Credentials
|
||||
import com.readrops.api.services.SyncResult
|
||||
import com.readrops.api.services.freshrss.NewFreshRSSDataSource
|
||||
import com.readrops.api.utils.AuthInterceptor
|
||||
import com.readrops.db.Database
|
||||
import com.readrops.db.entities.Feed
|
||||
import com.readrops.db.entities.account.Account
|
||||
import org.koin.core.component.KoinComponent
|
||||
|
||||
class FreshRSSRepository(
|
||||
database: Database,
|
||||
account: Account,
|
||||
private val dataSource: NewFreshRSSDataSource,
|
||||
) : BaseRepository(database, account), KoinComponent {
|
||||
|
||||
override suspend fun login(account: Account) {
|
||||
val authInterceptor = getKoin().get<AuthInterceptor>()
|
||||
authInterceptor.credentials = Credentials.toCredentials(account)
|
||||
|
||||
val authToken = dataSource.login(account.login!!, account.password!!)
|
||||
account.token = authToken
|
||||
// we got the authToken, time to provide it to make real calls
|
||||
authInterceptor.credentials = Credentials.toCredentials(account)
|
||||
|
||||
val userInfo = dataSource.getUserInfo()
|
||||
account.displayedName = userInfo.userName
|
||||
}
|
||||
|
||||
override suspend fun synchronize(
|
||||
selectedFeeds: List<Feed>,
|
||||
onUpdate: (Feed) -> Unit
|
||||
): Pair<SyncResult, ErrorResult> {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun synchronize(): SyncResult {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun insertNewFeeds(
|
||||
newFeeds: List<Feed>,
|
||||
onUpdate: (Feed) -> Unit
|
||||
): ErrorResult {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
|
@ -24,7 +24,7 @@ class LocalRSSRepository(
|
|||
account: Account
|
||||
) : BaseRepository(database, account), KoinComponent {
|
||||
|
||||
override suspend fun login() { /* useless here */
|
||||
override suspend fun login(account: Account) { /* useless here */
|
||||
}
|
||||
|
||||
override suspend fun synchronize(
|
||||
|
|
Loading…
Reference in New Issue