Link the spreadsheet API to the dataSource.
This commit is contained in:
parent
6cfd673335
commit
6167999001
10 changed files with 164 additions and 125 deletions
|
|
@ -3,7 +3,8 @@ package com.pixelized.rplexicon.model
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
|
||||||
data class Lexicon(
|
data class Lexicon(
|
||||||
val name: String?,
|
val id: Int,
|
||||||
|
val name: String,
|
||||||
val diminutive: String?,
|
val diminutive: String?,
|
||||||
val gender: Gender = Gender.UNDETERMINED,
|
val gender: Gender = Gender.UNDETERMINED,
|
||||||
val race: String?,
|
val race: String?,
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
package com.pixelized.rplexicon.repository
|
package com.pixelized.rplexicon.repository
|
||||||
|
|
||||||
|
import android.accounts.Account
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import com.google.android.gms.auth.api.signin.GoogleSignIn
|
import com.google.android.gms.auth.api.identity.SignInCredential
|
||||||
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
|
|
||||||
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential
|
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential
|
||||||
import com.google.api.client.util.ExponentialBackOff
|
import com.google.api.client.util.ExponentialBackOff
|
||||||
import com.google.api.services.sheets.v4.SheetsScopes
|
import com.google.api.services.sheets.v4.SheetsScopes
|
||||||
|
|
@ -16,11 +17,8 @@ import javax.inject.Singleton
|
||||||
class AuthenticationRepository @Inject constructor(
|
class AuthenticationRepository @Inject constructor(
|
||||||
@ApplicationContext private val context: Context,
|
@ApplicationContext private val context: Context,
|
||||||
) {
|
) {
|
||||||
private val _isAuthenticated = mutableStateOf(account != null)
|
private val signInCredential = mutableStateOf<SignInCredential?>(null)
|
||||||
val isAuthenticated: State<Boolean> get() = _isAuthenticated
|
val isAuthenticated: State<Boolean> = derivedStateOf { signInCredential.value != null }
|
||||||
|
|
||||||
private val account: GoogleSignInAccount?
|
|
||||||
get() = GoogleSignIn.getLastSignedInAccount(context)
|
|
||||||
|
|
||||||
val credential: GoogleAccountCredential
|
val credential: GoogleAccountCredential
|
||||||
get() {
|
get() {
|
||||||
|
|
@ -33,12 +31,16 @@ class AuthenticationRepository @Inject constructor(
|
||||||
)
|
)
|
||||||
.setBackOff(ExponentialBackOff())
|
.setBackOff(ExponentialBackOff())
|
||||||
|
|
||||||
credential.selectedAccount = account?.account
|
credential.selectedAccount = signInCredential.value?.let {
|
||||||
|
Account(it.id, "google")
|
||||||
|
}
|
||||||
|
|
||||||
return credential
|
return credential
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateAuthenticationState() {
|
fun updateAuthenticationState(
|
||||||
_isAuthenticated.value = account != null
|
credential: SignInCredential? = null,
|
||||||
|
) {
|
||||||
|
signInCredential.value = credential
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ package com.pixelized.rplexicon.repository
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.core.net.toUri
|
||||||
import com.google.api.client.extensions.android.http.AndroidHttp
|
import com.google.api.client.extensions.android.http.AndroidHttp
|
||||||
import com.google.api.client.json.gson.GsonFactory
|
import com.google.api.client.json.gson.GsonFactory
|
||||||
import com.google.api.services.sheets.v4.Sheets
|
import com.google.api.services.sheets.v4.Sheets
|
||||||
|
|
@ -37,79 +38,102 @@ class LexiconRepository @Inject constructor(
|
||||||
private val _data = MutableStateFlow<List<Lexicon>>(emptyList())
|
private val _data = MutableStateFlow<List<Lexicon>>(emptyList())
|
||||||
val data: StateFlow<List<Lexicon>> get() = _data
|
val data: StateFlow<List<Lexicon>> get() = _data
|
||||||
|
|
||||||
@Throws(ServiceNotReady::class, Exception::class)
|
@Throws(ServiceNotReady::class, IncompatibleSheetStructure::class, Exception::class)
|
||||||
suspend fun fetchLexicon(): ValueRange? {
|
suspend fun fetchLexicon() {
|
||||||
val service = sheetService
|
val service = sheetService
|
||||||
return if (service == null) {
|
if (service == null) {
|
||||||
throw ServiceNotReady()
|
throw ServiceNotReady()
|
||||||
} else {
|
} else {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
val request = service.spreadsheets().values().get(ID, LEXIQUE)
|
val request = service.spreadsheets().values().get(ID, LEXIQUE)
|
||||||
request.execute()
|
val data = request.execute()
|
||||||
|
updateData(data = data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
@Throws(IncompatibleSheetStructure::class)
|
||||||
const val HOST = "https://docs.google.com/"
|
private fun updateData(data: ValueRange?) {
|
||||||
const val ID = "1oL9Nu5y37BPEbKxHre4TN9o8nrgy2JQoON4RRkdAHMs"
|
val sheet = data?.values?.sheet()
|
||||||
const val LEXICON_GID = 0
|
val lexicon: List<Lexicon> = sheet?.mapIndexedNotNull { index, row ->
|
||||||
const val META_DATA_GID = "957635233"
|
if (index == 0) {
|
||||||
const val LEXIQUE = "Lexique"
|
checkSheetStructure(firstRow = row)
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
parseCharacterRow(index = index - 1, row = row as? List<Any>?)
|
||||||
|
}
|
||||||
|
} ?: emptyList()
|
||||||
|
_data.tryEmit(lexicon)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IncompatibleSheetStructure::class)
|
||||||
|
private fun checkSheetStructure(firstRow: Any?) {
|
||||||
|
when {
|
||||||
|
firstRow !is ArrayList<*> -> throw IncompatibleSheetStructure("First row is not a List: $firstRow")
|
||||||
|
firstRow.size < 7 -> throw IncompatibleSheetStructure("First row have not enough column: ${firstRow.size}, $firstRow")
|
||||||
|
firstRow.size > 7 -> throw IncompatibleSheetStructure("First row have too mush columns: ${firstRow.size}, $firstRow")
|
||||||
|
else -> {
|
||||||
|
for (index in 0..6) {
|
||||||
|
if (columns[index] != firstRow[index]) {
|
||||||
|
throw IncompatibleSheetStructure("Column at index:$index should be ${columns[index]} but was ${firstRow[index]}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseCharacterRow(index: Int, row: List<Any>?): Lexicon? {
|
||||||
|
val name = row?.getOrNull(0) as? String
|
||||||
|
val diminutive = row?.getOrNull(1) as? String?
|
||||||
|
val gender = row?.getOrNull(2) as? String?
|
||||||
|
val race = row?.getOrNull(3) as? String?
|
||||||
|
val portrait = row?.getOrNull(4) as? String?
|
||||||
|
val description = row?.getOrNull(5) as? String?
|
||||||
|
val history = row?.getOrNull(6) as? String?
|
||||||
|
|
||||||
|
return if (name != null) {
|
||||||
|
Lexicon(
|
||||||
|
id = index,
|
||||||
|
name = name,
|
||||||
|
diminutive = diminutive,
|
||||||
|
gender = when (gender) {
|
||||||
|
"Male" -> Lexicon.Gender.MALE
|
||||||
|
"Femelle" -> Lexicon.Gender.FEMALE
|
||||||
|
else -> Lexicon.Gender.UNDETERMINED
|
||||||
|
},
|
||||||
|
race = race,
|
||||||
|
portrait = portrait?.split("\n")?.mapNotNull { it.toUriOrNull() } ?: emptyList(),
|
||||||
|
description = description,
|
||||||
|
history = history,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MutableCollection<Any>?.sheet(): List<*>? {
|
||||||
|
return this?.firstOrNull {
|
||||||
|
val sheet = it as? ArrayList<*>
|
||||||
|
sheet != null
|
||||||
|
} as List<*>?
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String?.toUriOrNull(): Uri? = try {
|
||||||
|
this?.toUri()
|
||||||
|
} catch (_: Exception) {
|
||||||
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
class ServiceNotReady : Exception()
|
class ServiceNotReady : Exception()
|
||||||
}
|
|
||||||
|
|
||||||
|
class IncompatibleSheetStructure(message: String?) : Exception(message)
|
||||||
|
|
||||||
private fun sample(): List<Lexicon> {
|
companion object {
|
||||||
return listOf(
|
const val TAG = "LexiconRepository"
|
||||||
Lexicon(
|
const val ID = "1oL9Nu5y37BPEbKxHre4TN9o8nrgy2JQoON4RRkdAHMs"
|
||||||
name = "Brulkhai",
|
const val LEXIQUE = "Lexique"
|
||||||
diminutive = "Bru",
|
|
||||||
gender = Lexicon.Gender.FEMALE,
|
val columns =
|
||||||
race = "Demi-Orc",
|
listOf("Nom", "Diminutif", "Sexe", "Race", "Portrait", "Description", "Histoire")
|
||||||
portrait = listOf(
|
}
|
||||||
Uri.parse("https://drive.google.com/file/d/1a31xJ6DQnzqmGBndG-uo65HNQHPEUJnI/view?usp=sharing"),
|
|
||||||
),
|
|
||||||
description = "Brulkhai, ou plus simplement Bru, est solidement bâti. Elle mesure 192 cm pour 110 kg de muscles lorsqu’elle est en bonne santé. Elle a les cheveux châtains, les yeux noisettes et la peau couleur gris-vert typique de son espèce. D’un tempérament taciturne, elle parle peu et de façon concise. Elle est parfois brutale, aussi bien physiquement que verbalement, Elle ne prend cependant aucun plaisir à malmener ceux qu’elle considère plus faibles qu’elle. D’une nature simple et honnête, elle ne mâche pas ses mots et ne dissimule généralement pas ses pensées. Son intelligence modeste est plus le reflet d’un manque d’éducation et d’une capacité limitée à gérer ses émotions qu’à une débilité congénitale. Elle voue à la force un culte car c’est par son expression qu’elle se sent vraiment vivante et éprouve de grandes difficultés vis à vis de ceux qu’elle nomme foshnu (bébé, chouineur en commun).",
|
|
||||||
history = null,
|
|
||||||
),
|
|
||||||
Lexicon(
|
|
||||||
name = "Léandre",
|
|
||||||
diminutive = null,
|
|
||||||
gender = Lexicon.Gender.MALE,
|
|
||||||
race = "Humain",
|
|
||||||
portrait = emptyList(),
|
|
||||||
description = null,
|
|
||||||
history = null,
|
|
||||||
),
|
|
||||||
Lexicon(
|
|
||||||
name = "Nelia",
|
|
||||||
diminutive = null,
|
|
||||||
gender = Lexicon.Gender.FEMALE,
|
|
||||||
race = "Elfe",
|
|
||||||
portrait = emptyList(),
|
|
||||||
description = null,
|
|
||||||
history = null,
|
|
||||||
),
|
|
||||||
Lexicon(
|
|
||||||
name = "Tigrane",
|
|
||||||
diminutive = null,
|
|
||||||
gender = Lexicon.Gender.MALE,
|
|
||||||
race = "Tieffelin",
|
|
||||||
portrait = emptyList(),
|
|
||||||
description = null,
|
|
||||||
history = null,
|
|
||||||
),
|
|
||||||
Lexicon(
|
|
||||||
name = "Unathana",
|
|
||||||
diminutive = "Una",
|
|
||||||
gender = Lexicon.Gender.FEMALE,
|
|
||||||
race = "Demi-Elfe",
|
|
||||||
portrait = emptyList(),
|
|
||||||
description = null,
|
|
||||||
history = null,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
@ -18,7 +18,7 @@ val CHARACTER_DETAIL_ROUTE = "$ROUTE?${ARG_ID.ARG}"
|
||||||
@Stable
|
@Stable
|
||||||
@Immutable
|
@Immutable
|
||||||
data class CharacterDetailArgument(
|
data class CharacterDetailArgument(
|
||||||
val id: String,
|
val id: Int,
|
||||||
)
|
)
|
||||||
|
|
||||||
val SavedStateHandle.characterDetailArgument: CharacterDetailArgument
|
val SavedStateHandle.characterDetailArgument: CharacterDetailArgument
|
||||||
|
|
@ -31,7 +31,7 @@ fun NavGraphBuilder.composableCharacterDetail() {
|
||||||
route = CHARACTER_DETAIL_ROUTE,
|
route = CHARACTER_DETAIL_ROUTE,
|
||||||
arguments = listOf(
|
arguments = listOf(
|
||||||
navArgument(name = ARG_ID) {
|
navArgument(name = ARG_ID) {
|
||||||
type = NavType.StringType
|
type = NavType.IntType
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
animation = NavigationAnimation.Push,
|
animation = NavigationAnimation.Push,
|
||||||
|
|
@ -41,7 +41,7 @@ fun NavGraphBuilder.composableCharacterDetail() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun NavHostController.navigateToCharacterDetail(
|
fun NavHostController.navigateToCharacterDetail(
|
||||||
id: String,
|
id: Int,
|
||||||
option: NavOptionsBuilder.() -> Unit = {},
|
option: NavOptionsBuilder.() -> Unit = {},
|
||||||
) {
|
) {
|
||||||
val route = "$ROUTE?$ARG_ID=$id"
|
val route = "$ROUTE?$ARG_ID=$id"
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@ import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import com.google.android.gms.auth.api.identity.GetSignInIntentRequest
|
import com.google.android.gms.auth.api.identity.GetSignInIntentRequest
|
||||||
import com.google.android.gms.auth.api.identity.Identity
|
import com.google.android.gms.auth.api.identity.Identity
|
||||||
|
import com.google.android.gms.auth.api.identity.SignInCredential
|
||||||
|
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
|
||||||
import com.pixelized.rplexicon.R
|
import com.pixelized.rplexicon.R
|
||||||
import com.pixelized.rplexicon.repository.AuthenticationRepository
|
import com.pixelized.rplexicon.repository.AuthenticationRepository
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
|
@ -43,8 +45,12 @@ class AuthenticationViewModel @Inject constructor(
|
||||||
contract = ActivityResultContracts.StartIntentSenderForResult(),
|
contract = ActivityResultContracts.StartIntentSenderForResult(),
|
||||||
onResult = {
|
onResult = {
|
||||||
if (it.resultCode == Activity.RESULT_OK) {
|
if (it.resultCode == Activity.RESULT_OK) {
|
||||||
|
val credential: SignInCredential = Identity
|
||||||
|
.getSignInClient(context)
|
||||||
|
.getSignInCredentialFromIntent(it.data)
|
||||||
|
|
||||||
state.value = Authentication.Success
|
state.value = Authentication.Success
|
||||||
repository.updateAuthenticationState()
|
repository.updateAuthenticationState(credential)
|
||||||
} else {
|
} else {
|
||||||
state.value = Authentication.Failure
|
state.value = Authentication.Failure
|
||||||
repository.updateAuthenticationState()
|
repository.updateAuthenticationState()
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import com.pixelized.rplexicon.R
|
import com.pixelized.rplexicon.R
|
||||||
|
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
|
||||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
||||||
import com.skydoves.landscapist.ImageOptions
|
import com.skydoves.landscapist.ImageOptions
|
||||||
import com.skydoves.landscapist.glide.GlideImage
|
import com.skydoves.landscapist.glide.GlideImage
|
||||||
|
|
@ -73,11 +74,13 @@ data class CharacterDetailUio(
|
||||||
fun CharacterDetailScreen(
|
fun CharacterDetailScreen(
|
||||||
viewModel: CharacterDetailViewModel = hiltViewModel(),
|
viewModel: CharacterDetailViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
|
val screen = LocalScreenNavHost.current
|
||||||
|
|
||||||
Surface {
|
Surface {
|
||||||
CharacterDetailScreenContent(
|
CharacterDetailScreenContent(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
item = viewModel.character,
|
item = viewModel.character,
|
||||||
onBack = { },
|
onBack = { screen.popBackStack() },
|
||||||
onImage = { },
|
onImage = { },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,36 @@
|
||||||
package com.pixelized.rplexicon.ui.screens.detail
|
package com.pixelized.rplexicon.ui.screens.detail
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.lifecycle.SavedStateHandle
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.pixelized.rplexicon.model.Lexicon
|
||||||
|
import com.pixelized.rplexicon.repository.LexiconRepository
|
||||||
|
import com.pixelized.rplexicon.ui.navigation.characterDetailArgument
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class CharacterDetailViewModel @Inject constructor() : ViewModel() {
|
class CharacterDetailViewModel @Inject constructor(
|
||||||
|
private val savedStateHandle: SavedStateHandle,
|
||||||
|
private val repository: LexiconRepository,
|
||||||
|
) : ViewModel() {
|
||||||
|
val character: State<CharacterDetailUio> = mutableStateOf(init())
|
||||||
|
|
||||||
val character: State<CharacterDetailUio> = mutableStateOf(
|
private fun init(): CharacterDetailUio {
|
||||||
CharacterDetailUio(
|
val source = repository.data.value[savedStateHandle.characterDetailArgument.id]
|
||||||
name = "Brulkhai",
|
return CharacterDetailUio(
|
||||||
diminutive = "./ Bru",
|
name = source.name,
|
||||||
gender = "female",
|
diminutive = source.diminutive?.let { "./ $it" },
|
||||||
race = "Demi-Orc",
|
gender = when (source.gender) {
|
||||||
portrait = listOf(
|
Lexicon.Gender.MALE -> "homme"
|
||||||
Uri.parse("https://cdnb.artstation.com/p/assets/images/images/003/024/889/large/bayard-wu-0716.jpg?1468642855"),
|
Lexicon.Gender.FEMALE -> "femme"
|
||||||
Uri.parse("https://cdnb.artstation.com/p/assets/images/images/003/024/877/large/bayard-wu-0714.jpg?1468642665"),
|
Lexicon.Gender.UNDETERMINED -> "inconnu"
|
||||||
Uri.parse("https://cdnb.artstation.com/p/assets/images/images/003/024/887/large/bayard-wu-0715.jpg?1468642839"),
|
},
|
||||||
Uri.parse("https://cdnb.artstation.com/p/assets/images/images/003/024/891/large/bayard-wu-0623-03.jpg?1468642872"),
|
race = source.race,
|
||||||
Uri.parse("https://cdna.artstation.com/p/assets/images/images/002/869/868/large/bayard-wu-0622-03.jpg?1466664135"),
|
portrait = source.portrait,
|
||||||
Uri.parse("https://cdnb.artstation.com/p/assets/images/images/002/869/871/large/bayard-wu-0622-04.jpg?1466664153"),
|
description = source.description,
|
||||||
Uri.parse("https://cdnb.artstation.com/p/assets/images/images/004/347/181/large/bayard-wu-1217.jpg?1482770883"),
|
history = source.history,
|
||||||
Uri.parse("https://cdnb.artstation.com/p/assets/images/images/004/297/635/large/bayard-wu-1215.jpg?1482166826"),
|
|
||||||
Uri.parse("https://cdnb.artstation.com/p/assets/images/images/004/297/631/large/bayard-wu-1209.jpg?1482166803"),
|
|
||||||
Uri.parse("https://cdnb.artstation.com/p/assets/images/images/004/297/641/large/bayard-wu-1212.jpg?1482166838"),
|
|
||||||
),
|
|
||||||
description = "Brulkhai, ou plus simplement Bru, est solidement bâti. Elle mesure 192 cm pour 110 kg de muscles lorsqu’elle est en bonne santé. Elle a les cheveux châtains, les yeux noisettes et la peau couleur gris-vert typique de son espèce. D’un tempérament taciturne, elle parle peu et de façon concise. Elle est parfois brutale, aussi bien physiquement que verbalement, Elle ne prend cependant aucun plaisir à malmener ceux qu’elle considère plus faibles qu’elle. D’une nature simple et honnête, elle ne mâche pas ses mots et ne dissimule généralement pas ses pensées. Son intelligence modeste est plus le reflet d’un manque d’éducation et d’une capacité limitée à gérer ses émotions qu’à une débilité congénitale. Elle voue à la force un culte car c’est par son expression qu’elle se sent vraiment vivante et éprouve de grandes difficultés vis à vis de ceux qu’elle nomme foshnu (bébé, chouineur en commun).",
|
|
||||||
history = null,
|
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -21,6 +21,7 @@ import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
data class LexiconItemUio(
|
data class LexiconItemUio(
|
||||||
|
val id: Int,
|
||||||
val name: String,
|
val name: String,
|
||||||
val diminutive: String?,
|
val diminutive: String?,
|
||||||
val gender: String?,
|
val gender: String?,
|
||||||
|
|
@ -77,6 +78,7 @@ private fun LexiconItemContentPreview() {
|
||||||
LexiconItem(
|
LexiconItem(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
item = LexiconItemUio(
|
item = LexiconItemUio(
|
||||||
|
id = 0,
|
||||||
name = "Brulkhai",
|
name = "Brulkhai",
|
||||||
diminutive = "Bru",
|
diminutive = "Bru",
|
||||||
gender = "f.",
|
gender = "f.",
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ fun LexiconScreen(
|
||||||
LexiconScreenContent(
|
LexiconScreenContent(
|
||||||
items = viewModel.items,
|
items = viewModel.items,
|
||||||
onItem = {
|
onItem = {
|
||||||
screen.navigateToCharacterDetail(id = "")
|
screen.navigateToCharacterDetail(id = it.id)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -128,6 +128,7 @@ private fun LexiconScreenContentPreview() {
|
||||||
mutableStateOf(
|
mutableStateOf(
|
||||||
listOf(
|
listOf(
|
||||||
LexiconItemUio(
|
LexiconItemUio(
|
||||||
|
id = 2,
|
||||||
name = "Brulkhai",
|
name = "Brulkhai",
|
||||||
diminutive = "Bru",
|
diminutive = "Bru",
|
||||||
gender = "f.",
|
gender = "f.",
|
||||||
|
|
|
||||||
|
|
@ -28,40 +28,39 @@ class LexiconViewModel @Inject constructor(
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
launch {
|
repository.data.collect { items ->
|
||||||
repository.data.collect { items ->
|
_items.value = items.map { item ->
|
||||||
_items.value = items.mapNotNull { item ->
|
LexiconItemUio(
|
||||||
item.name?.let {
|
id = item.id,
|
||||||
LexiconItemUio(
|
name = item.name,
|
||||||
name = item.name,
|
diminutive = item.diminutive?.takeIf { it.isNotBlank() }?.let { "./ $it" },
|
||||||
diminutive = item.diminutive?.let { "./ $it" },
|
gender = when (item.gender) {
|
||||||
gender = when (item.gender) {
|
Lexicon.Gender.MALE -> "h."
|
||||||
Lexicon.Gender.MALE -> "m."
|
Lexicon.Gender.FEMALE -> "f."
|
||||||
Lexicon.Gender.FEMALE -> "f."
|
Lexicon.Gender.UNDETERMINED -> "u."
|
||||||
Lexicon.Gender.UNDETERMINED -> "u."
|
},
|
||||||
},
|
race = item.race,
|
||||||
race = item.race,
|
)
|
||||||
)
|
}.sortedBy { it.name }
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
launch {
|
viewModelScope.launch {
|
||||||
delay(100)
|
fetchLexicon()
|
||||||
fetchLexicon()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun fetchLexicon() {
|
suspend fun fetchLexicon() {
|
||||||
try {
|
try {
|
||||||
repository.fetchLexicon()
|
repository.fetchLexicon()
|
||||||
} catch (exception: UserRecoverableAuthIOException) {
|
}
|
||||||
|
// user need to accept OAuth2 permission.
|
||||||
|
catch (exception: UserRecoverableAuthIOException) {
|
||||||
Log.e(TAG, exception.message, exception)
|
Log.e(TAG, exception.message, exception)
|
||||||
// user need to accept OAuth2 permission.
|
|
||||||
_error.emit(LexiconErrorUio.Permission(intent = exception.intent))
|
_error.emit(LexiconErrorUio.Permission(intent = exception.intent))
|
||||||
} catch (exception: Exception) {
|
}
|
||||||
|
// default exception
|
||||||
|
catch (exception: Exception) {
|
||||||
Log.e(TAG, exception.message, exception)
|
Log.e(TAG, exception.message, exception)
|
||||||
_error.emit(LexiconErrorUio.Default)
|
_error.emit(LexiconErrorUio.Default)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue