Skip to content

Instantly share code, notes, and snippets.

@hlnstepanova
Created December 14, 2020 18:56
Show Gist options
  • Save hlnstepanova/1dabb07ce34d9d927be54074ad701b8c to your computer and use it in GitHub Desktop.
Save hlnstepanova/1dabb07ce34d9d927be54074ad701b8c to your computer and use it in GitHub Desktop.
LoginModel.kt
package de.comp.proj.shared.models
import co.touchlab.kermit.Kermit
import co.touchlab.stately.ensureNeverFrozen
import com.russhwolf.settings.Settings
import de.comp.proj.shared.*
import de.comp.proj.shared.resources.MR
import de.comp.proj.shared.cache.DatabaseHelper
import de.comp.proj.shared.entity.Email
import de.comp.proj.shared.entity.LoginResult
import de.comp.proj.shared.entity.User
import de.comp.proj.shared.ktor.KtorApi
import dev.icerock.moko.mvvm.dispatcher.EventsDispatcher
import dev.icerock.moko.mvvm.dispatcher.EventsDispatcherOwner
import dev.icerock.moko.mvvm.livedata.LiveData
import dev.icerock.moko.mvvm.livedata.MutableLiveData
import dev.icerock.moko.mvvm.viewmodel.ViewModel
import org.koin.core.KoinComponent
import org.koin.core.inject
import org.koin.core.parameter.parametersOf
import dev.icerock.moko.resources.StringResource
import dev.icerock.moko.resources.desc.ResourceFormatted
import dev.icerock.moko.resources.desc.StringDesc
import dev.icerock.moko.resources.desc.Resource
import dev.icerock.moko.resources.desc.desc
import kotlinx.coroutines.launch
class LoginModel(override val eventsDispatcher: EventsDispatcher<EventsListener>) : ViewModel(),
EventsDispatcherOwner<LoginModel.EventsListener>, KoinComponent {
private val dbHelper: DatabaseHelper by inject()
private val settings: Settings by inject()
private val ktorApi: KtorApi by inject()
private val log: Kermit by inject { parametersOf("LoginModel") }
private val _cloudLogin = MutableLiveData(false)
private val _logged = MutableLiveData(false)
private val _loading = MutableLiveData(false)
val username = MutableLiveData("")
val password = MutableLiveData("")
val url = MutableLiveData("")
val logged: LiveData<Boolean>
get() = _logged
val loading: LiveData<Boolean>
get() = _loading
val cloudLogin: LiveData<Boolean>
get() = _cloudLogin
companion object {
internal const val EXPIRATION_TIME_KEY = "tokenExpiredKey"
internal const val ACCESS_TOKEN_KEY = "accessTokenKey"
}
init {
ensureNeverFrozen()
}
fun onLogin() {
val user = User(username.value, password.value)
// regardless of login type (cloud or local), validate username and password
validateUserData(user)?.let {
eventsDispatcher.dispatchEvent { showError(it) }
return
}
// if cloud login, try login with api
if (cloudLogin.value) {
viewModelScope.launch {
startLoading()
login(user)?.let { errorString ->
eventsDispatcher.dispatchEvent { showError(errorString) }
stopLoading()
return@launch
}
eventsDispatcher.dispatchEvent { navigateToDeviceList(username.value)}
setLogged(true)
}
} else {
// if local login, validate ip
validateIpData(url.value)?.let {
eventsDispatcher.dispatchEvent { showError(it) }
return
}
eventsDispatcher.dispatchEvent { navigateToFavouritesList(url.value)}
setLogged(true)
}
}
fun onLogout(){
startLoading()
viewModelScope.launch {
logout()?.let { errorString ->
eventsDispatcher.dispatchEvent { showError(errorString) }
return@launch
}
setLogged(false)
}
stopLoading()
}
private fun setLogged(logged: Boolean){
_logged.value = logged
}
//TODO: move to repository
suspend fun login(user: User): StringDesc? {
val currentTimeMS = currentTimeMillis()
if (isExpired(currentTimeMS)) {
try {
val loginResult = ktorApi.login(user)
val expiresIn = loginResult.expiresIn.toLong()
settings.putLong(EXPIRATION_TIME_KEY, currentTimeMS + expiresIn)
settings.putString(ACCESS_TOKEN_KEY, loginResult.accessToken)
log.i { "New token granted" }
} catch (e: Exception) {
log.i { e.toString() }
return StringDesc.Resource(MR.strings.access_denied)
}
} else {
log.i { "Access token still valid" }
}
return null
}
//TODO: move to repository
suspend fun logout(): StringDesc? {
try {
ktorApi.logout()
settings.putLong(EXPIRATION_TIME_KEY, 0)
settings.putString(ACCESS_TOKEN_KEY, "")
} catch (e: Exception) {
log.i { e.toString() }
return StringDesc.Resource(MR.strings.logout_failed)
}
return null
}
private fun isExpired(currentTimeMS: Long): Boolean {
val expirationTime = settings.getLong(EXPIRATION_TIME_KEY, 0)
return (expirationTime < currentTimeMS)
}
fun getAccessToken(): String {
return settings.getString(ACCESS_TOKEN_KEY)
}
fun setAccessTokenCookie(url: String) {
// cookie string should be url encoded
if (cloudLogin.value) {
setCookie(url, getAccessToken())
}
}
// StringDesc is a mutable class for Strings from moko resources
fun validateUserData(user: User): StringDesc?{
if (!validateEmail(user.email)){
return StringDesc.Resource(MR.strings.enter_valid_email)
}
if (!validatePassword(user.password)){
return StringDesc.Resource(MR.strings.enter_valid_password)
}
return null
}
fun validateIpData(url: String): StringDesc?{
if (!validateIp(url)){
return StringDesc.Resource(MR.strings.enter_valid_ip)
}
return null
}
fun toggleCloudLogin(value: Boolean) {
_cloudLogin.value = value
}
fun startLoading(){
_loading.value = true
}
fun stopLoading(){
_loading.value = false
}
interface EventsListener {
fun navigateToDeviceList(username: String)
fun navigateToFavouritesList(url: String)
fun showError(error: StringDesc)
}
}
comp.proj
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment