add map list tab
This commit is contained in:
parent
87a1471efe
commit
57d1273435
17 changed files with 527 additions and 82 deletions
|
|
@ -32,6 +32,7 @@ class LocationParser @Inject constructor() {
|
||||||
sheetIndex = index,
|
sheetIndex = index,
|
||||||
name = name,
|
name = name,
|
||||||
uri = uri,
|
uri = uri,
|
||||||
|
marquees = emptyList(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
package com.pixelized.rplexicon.ui.composable.error
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import com.pixelized.rplexicon.LocalSnack
|
||||||
|
import com.pixelized.rplexicon.R
|
||||||
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
sealed class FetchErrorUio {
|
||||||
|
@Stable
|
||||||
|
data class Permission(val intent: Intent) : FetchErrorUio()
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
object Structure : FetchErrorUio()
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
object Default : FetchErrorUio()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun HandleFetchError(
|
||||||
|
errors: SharedFlow<FetchErrorUio>,
|
||||||
|
onPermissionGranted: suspend () -> Unit,
|
||||||
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val snack = LocalSnack.current
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
val launcher = rememberLauncherForActivityResult(
|
||||||
|
contract = ActivityResultContracts.StartActivityForResult(),
|
||||||
|
) { result ->
|
||||||
|
if (result.resultCode == Activity.RESULT_OK) {
|
||||||
|
scope.launch {
|
||||||
|
onPermissionGranted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(key1 = "LexiconErrorManagement") {
|
||||||
|
errors.collect { error ->
|
||||||
|
when (error) {
|
||||||
|
is FetchErrorUio.Permission -> launcher.launch(error.intent)
|
||||||
|
is FetchErrorUio.Structure -> snack.showSnackbar(message = context.getString(R.string.error_structure))
|
||||||
|
is FetchErrorUio.Default -> snack.showSnackbar(message = context.getString(R.string.error_generic))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package com.pixelized.rplexicon.ui.navigation
|
package com.pixelized.rplexicon.ui.navigation
|
||||||
|
|
||||||
import androidx.compose.animation.ExperimentalAnimationApi
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
import androidx.compose.material3.BottomAppBar
|
import androidx.compose.material3.BottomAppBar
|
||||||
|
|
@ -33,8 +32,10 @@ import com.pixelized.rplexicon.LocalSnack
|
||||||
import com.pixelized.rplexicon.R
|
import com.pixelized.rplexicon.R
|
||||||
import com.pixelized.rplexicon.ui.navigation.pages.LEXICON_ROUTE
|
import com.pixelized.rplexicon.ui.navigation.pages.LEXICON_ROUTE
|
||||||
import com.pixelized.rplexicon.ui.navigation.pages.composableLexicon
|
import com.pixelized.rplexicon.ui.navigation.pages.composableLexicon
|
||||||
import com.pixelized.rplexicon.ui.navigation.pages.composableQuestList
|
import com.pixelized.rplexicon.ui.navigation.pages.composableLocations
|
||||||
|
import com.pixelized.rplexicon.ui.navigation.pages.composableQuests
|
||||||
import com.pixelized.rplexicon.ui.navigation.pages.navigateToLexicon
|
import com.pixelized.rplexicon.ui.navigation.pages.navigateToLexicon
|
||||||
|
import com.pixelized.rplexicon.ui.navigation.pages.navigateToLocation
|
||||||
import com.pixelized.rplexicon.ui.navigation.pages.navigateToQuestList
|
import com.pixelized.rplexicon.ui.navigation.pages.navigateToQuestList
|
||||||
|
|
||||||
val LocalPageNavHost = staticCompositionLocalOf<NavHostController> {
|
val LocalPageNavHost = staticCompositionLocalOf<NavHostController> {
|
||||||
|
|
@ -44,9 +45,11 @@ val LocalPageNavHost = staticCompositionLocalOf<NavHostController> {
|
||||||
@OptIn(ExperimentalAnimationApi::class, ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalAnimationApi::class, ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeNavHost(
|
fun HomeNavHost(
|
||||||
lexiconListState: LazyListState,
|
|
||||||
navHostController: NavHostController = rememberAnimatedNavController(),
|
navHostController: NavHostController = rememberAnimatedNavController(),
|
||||||
bottomBarItems: List<BottomBarItem> = rememberBottomBarItems(navHostController = navHostController),
|
bottomBarItems: List<BottomBarItem> = rememberBottomBarItems(navHostController),
|
||||||
|
lexiconListState: LazyListState,
|
||||||
|
questListState: LazyListState,
|
||||||
|
locationListState: LazyListState,
|
||||||
startDestination: String = LEXICON_ROUTE
|
startDestination: String = LEXICON_ROUTE
|
||||||
) {
|
) {
|
||||||
CompositionLocalProvider(
|
CompositionLocalProvider(
|
||||||
|
|
@ -73,7 +76,8 @@ fun HomeNavHost(
|
||||||
tonalElevation = 0.dp,
|
tonalElevation = 0.dp,
|
||||||
) {
|
) {
|
||||||
bottomBarItems.forEachIndexed { index, item ->
|
bottomBarItems.forEachIndexed { index, item ->
|
||||||
val selectedItem = remember { derivedStateOf { selectedIndex.value == index } }
|
val selectedItem =
|
||||||
|
remember { derivedStateOf { selectedIndex.value == index } }
|
||||||
NavigationBarItem(
|
NavigationBarItem(
|
||||||
selected = selectedItem.value,
|
selected = selectedItem.value,
|
||||||
onClick = {
|
onClick = {
|
||||||
|
|
@ -103,7 +107,8 @@ fun HomeNavHost(
|
||||||
startDestination = startDestination,
|
startDestination = startDestination,
|
||||||
) {
|
) {
|
||||||
composableLexicon(lazyListState = lexiconListState)
|
composableLexicon(lazyListState = lexiconListState)
|
||||||
composableQuestList()
|
composableQuests(lazyListState = questListState)
|
||||||
|
composableLocations(lazyListState = locationListState)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -124,17 +129,23 @@ private fun rememberBottomBarItems(
|
||||||
navHostController: NavHostController,
|
navHostController: NavHostController,
|
||||||
): List<BottomBarItem> {
|
): List<BottomBarItem> {
|
||||||
return remember(navHostController) {
|
return remember(navHostController) {
|
||||||
|
val option = navHostController.pageOption()
|
||||||
listOf(
|
listOf(
|
||||||
BottomBarItem(
|
BottomBarItem(
|
||||||
icon = R.drawable.ic_outline_account_circle_24,
|
icon = R.drawable.ic_outline_account_circle_24,
|
||||||
label = R.string.home_lexicon,
|
label = R.string.home_lexicon,
|
||||||
onClick = { navHostController.navigateToLexicon(navHostController.pageOption()) }
|
onClick = { navHostController.navigateToLexicon(option) }
|
||||||
|
),
|
||||||
|
BottomBarItem(
|
||||||
|
icon = R.drawable.ic_outline_scroll_24,
|
||||||
|
label = R.string.home_quest_log,
|
||||||
|
onClick = { navHostController.navigateToQuestList(option) }
|
||||||
),
|
),
|
||||||
BottomBarItem(
|
BottomBarItem(
|
||||||
icon = R.drawable.ic_outline_map_24,
|
icon = R.drawable.ic_outline_map_24,
|
||||||
label = R.string.home_quest_log,
|
label = R.string.home_location,
|
||||||
onClick = { navHostController.navigateToQuestList(navHostController.pageOption()) }
|
onClick = { navHostController.navigateToLocation(option) }
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -28,7 +28,9 @@ fun ScreenNavHost(
|
||||||
navHostController: NavHostController = rememberAnimatedNavController(),
|
navHostController: NavHostController = rememberAnimatedNavController(),
|
||||||
startDestination: String = AUTHENTICATION_ROUTE,
|
startDestination: String = AUTHENTICATION_ROUTE,
|
||||||
) {
|
) {
|
||||||
val lexiconListState: LazyListState = rememberLazyListState()
|
val lexiconListState =rememberLazyListState()
|
||||||
|
val questListState = rememberLazyListState()
|
||||||
|
val locationListState = rememberLazyListState()
|
||||||
|
|
||||||
CompositionLocalProvider(
|
CompositionLocalProvider(
|
||||||
LocalScreenNavHost provides navHostController,
|
LocalScreenNavHost provides navHostController,
|
||||||
|
|
@ -38,7 +40,11 @@ fun ScreenNavHost(
|
||||||
startDestination = startDestination,
|
startDestination = startDestination,
|
||||||
) {
|
) {
|
||||||
composableAuthentication()
|
composableAuthentication()
|
||||||
composableHome(lexiconListState)
|
composableHome(
|
||||||
|
lexiconListState = lexiconListState,
|
||||||
|
questListState = questListState,
|
||||||
|
locationListState = locationListState
|
||||||
|
)
|
||||||
|
|
||||||
composableLexiconDetail()
|
composableLexiconDetail()
|
||||||
composableLexiconSearch()
|
composableLexiconSearch()
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
package com.pixelized.rplexicon.ui.navigation.pages
|
||||||
|
|
||||||
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import androidx.navigation.NavGraphBuilder
|
||||||
|
import androidx.navigation.NavOptionsBuilder
|
||||||
|
import com.pixelized.rplexicon.ui.navigation.NavigationAnimation
|
||||||
|
import com.pixelized.rplexicon.ui.navigation.animatedComposable
|
||||||
|
import com.pixelized.rplexicon.ui.screens.location.list.LocationScreen
|
||||||
|
|
||||||
|
private const val ROUTE = "locations"
|
||||||
|
|
||||||
|
const val LOCATION_ROUTE = ROUTE
|
||||||
|
|
||||||
|
fun NavGraphBuilder.composableLocations(
|
||||||
|
lazyListState: LazyListState,
|
||||||
|
) {
|
||||||
|
animatedComposable(
|
||||||
|
route = LOCATION_ROUTE,
|
||||||
|
animation = NavigationAnimation.NONE,
|
||||||
|
) {
|
||||||
|
LocationScreen(
|
||||||
|
lazyListState = lazyListState,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun NavController.navigateToLocation(
|
||||||
|
option: NavOptionsBuilder.() -> Unit = {},
|
||||||
|
) {
|
||||||
|
navigate(route = ROUTE, builder = option)
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.pixelized.rplexicon.ui.navigation.pages
|
package com.pixelized.rplexicon.ui.navigation.pages
|
||||||
|
|
||||||
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
import androidx.navigation.NavGraphBuilder
|
import androidx.navigation.NavGraphBuilder
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.NavOptionsBuilder
|
import androidx.navigation.NavOptionsBuilder
|
||||||
|
|
@ -11,12 +12,14 @@ private const val ROUTE = "quests"
|
||||||
|
|
||||||
const val QUEST_LIST_ROUTE = ROUTE
|
const val QUEST_LIST_ROUTE = ROUTE
|
||||||
|
|
||||||
fun NavGraphBuilder.composableQuestList() {
|
fun NavGraphBuilder.composableQuests(
|
||||||
|
lazyListState: LazyListState,
|
||||||
|
) {
|
||||||
animatedComposable(
|
animatedComposable(
|
||||||
route = QUEST_LIST_ROUTE,
|
route = QUEST_LIST_ROUTE,
|
||||||
animation = NavigationAnimation.NONE,
|
animation = NavigationAnimation.NONE,
|
||||||
) {
|
) {
|
||||||
QuestListScreen()
|
QuestListScreen(lazyListState = lazyListState)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,18 @@ const val HOME_ROUTE = ROUTE
|
||||||
|
|
||||||
fun NavGraphBuilder.composableHome(
|
fun NavGraphBuilder.composableHome(
|
||||||
lexiconListState: LazyListState,
|
lexiconListState: LazyListState,
|
||||||
|
questListState: LazyListState,
|
||||||
|
locationListState: LazyListState,
|
||||||
) {
|
) {
|
||||||
animatedComposable(
|
animatedComposable(
|
||||||
route = HOME_ROUTE,
|
route = HOME_ROUTE,
|
||||||
animation = NavigationAnimation.Fade,
|
animation = NavigationAnimation.Fade,
|
||||||
) {
|
) {
|
||||||
HomeNavHost(lexiconListState = lexiconListState)
|
HomeNavHost(
|
||||||
|
lexiconListState = lexiconListState,
|
||||||
|
questListState = questListState,
|
||||||
|
locationListState = locationListState
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,7 @@
|
||||||
package com.pixelized.rplexicon.ui.screens.lexicon.list
|
package com.pixelized.rplexicon.ui.screens.lexicon.list
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.res.Configuration.UI_MODE_NIGHT_NO
|
import android.content.res.Configuration.UI_MODE_NIGHT_NO
|
||||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.compose.animation.AnimatedContent
|
import androidx.compose.animation.AnimatedContent
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.ExperimentalAnimationApi
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
|
|
@ -29,8 +25,6 @@ import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.Stable
|
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
|
@ -38,39 +32,23 @@ import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
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.LocalSnack
|
|
||||||
import com.pixelized.rplexicon.R
|
import com.pixelized.rplexicon.R
|
||||||
import com.pixelized.rplexicon.ui.composable.FloatingActionButton
|
import com.pixelized.rplexicon.ui.composable.FloatingActionButton
|
||||||
import com.pixelized.rplexicon.ui.composable.Loader
|
import com.pixelized.rplexicon.ui.composable.Loader
|
||||||
|
import com.pixelized.rplexicon.ui.composable.error.HandleFetchError
|
||||||
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
|
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
|
||||||
import com.pixelized.rplexicon.ui.navigation.screens.navigateToLexiconDetail
|
import com.pixelized.rplexicon.ui.navigation.screens.navigateToLexiconDetail
|
||||||
import com.pixelized.rplexicon.ui.navigation.screens.navigateToLexiconSearch
|
import com.pixelized.rplexicon.ui.navigation.screens.navigateToLexiconSearch
|
||||||
import com.pixelized.rplexicon.ui.screens.lexicon.list.LexiconErrorUio.Default
|
|
||||||
import com.pixelized.rplexicon.ui.screens.lexicon.list.LexiconErrorUio.Permission
|
|
||||||
import com.pixelized.rplexicon.ui.screens.lexicon.list.LexiconErrorUio.Structure
|
|
||||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
||||||
import com.pixelized.rplexicon.utilitary.extentions.cell
|
import com.pixelized.rplexicon.utilitary.extentions.cell
|
||||||
import com.pixelized.rplexicon.utilitary.extentions.lexicon
|
import com.pixelized.rplexicon.utilitary.extentions.lexicon
|
||||||
import kotlinx.coroutines.flow.SharedFlow
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@Stable
|
|
||||||
sealed class LexiconErrorUio {
|
|
||||||
@Stable
|
|
||||||
data class Permission(val intent: Intent) : LexiconErrorUio()
|
|
||||||
|
|
||||||
@Stable
|
|
||||||
object Structure : LexiconErrorUio()
|
|
||||||
|
|
||||||
@Stable
|
|
||||||
object Default : LexiconErrorUio()
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterialApi::class)
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -111,9 +89,9 @@ fun LexiconScreen(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
HandleError(
|
HandleFetchError(
|
||||||
errors = viewModel.error,
|
errors = viewModel.error,
|
||||||
onLexiconPermissionGranted = {
|
onPermissionGranted = {
|
||||||
viewModel.fetchLexicon()
|
viewModel.fetchLexicon()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -216,36 +194,6 @@ private fun LexiconScreenContent(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun HandleError(
|
|
||||||
errors: SharedFlow<LexiconErrorUio>,
|
|
||||||
onLexiconPermissionGranted: suspend () -> Unit,
|
|
||||||
) {
|
|
||||||
val context = LocalContext.current
|
|
||||||
val snack = LocalSnack.current
|
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
|
|
||||||
val launcher = rememberLauncherForActivityResult(
|
|
||||||
contract = ActivityResultContracts.StartActivityForResult(),
|
|
||||||
) { result ->
|
|
||||||
if (result.resultCode == Activity.RESULT_OK) {
|
|
||||||
scope.launch {
|
|
||||||
onLexiconPermissionGranted()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LaunchedEffect(key1 = "LexiconErrorManagement") {
|
|
||||||
errors.collect { error ->
|
|
||||||
when (error) {
|
|
||||||
is Permission -> launcher.launch(error.intent)
|
|
||||||
is Structure -> snack.showSnackbar(message = context.getString(R.string.error_structure))
|
|
||||||
is Default -> snack.showSnackbar(message = context.getString(R.string.error_generic))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterialApi::class)
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
@Preview(uiMode = UI_MODE_NIGHT_NO)
|
@Preview(uiMode = UI_MODE_NIGHT_NO)
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecovera
|
||||||
import com.pixelized.rplexicon.R
|
import com.pixelized.rplexicon.R
|
||||||
import com.pixelized.rplexicon.model.Lexicon
|
import com.pixelized.rplexicon.model.Lexicon
|
||||||
import com.pixelized.rplexicon.repository.LexiconRepository
|
import com.pixelized.rplexicon.repository.LexiconRepository
|
||||||
|
import com.pixelized.rplexicon.ui.composable.error.FetchErrorUio
|
||||||
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
|
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
|
@ -27,8 +28,8 @@ class LexiconViewModel @Inject constructor(
|
||||||
private val _items = mutableStateOf<List<LexiconItemUio>>(emptyList())
|
private val _items = mutableStateOf<List<LexiconItemUio>>(emptyList())
|
||||||
val items: State<List<LexiconItemUio>> get() = _items
|
val items: State<List<LexiconItemUio>> get() = _items
|
||||||
|
|
||||||
private val _error = MutableSharedFlow<LexiconErrorUio>()
|
private val _error = MutableSharedFlow<FetchErrorUio>()
|
||||||
val error: SharedFlow<LexiconErrorUio> get() = _error
|
val error: SharedFlow<FetchErrorUio> get() = _error
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
|
@ -77,15 +78,15 @@ class LexiconViewModel @Inject constructor(
|
||||||
// user need to accept OAuth2 permission.
|
// user need to accept OAuth2 permission.
|
||||||
catch (exception: UserRecoverableAuthIOException) {
|
catch (exception: UserRecoverableAuthIOException) {
|
||||||
Log.e(TAG, exception.message, exception)
|
Log.e(TAG, exception.message, exception)
|
||||||
_error.emit(LexiconErrorUio.Permission(intent = exception.intent))
|
_error.emit(FetchErrorUio.Permission(intent = exception.intent))
|
||||||
} catch (exception: IncompatibleSheetStructure) {
|
} catch (exception: IncompatibleSheetStructure) {
|
||||||
Log.e(TAG, exception.message, exception)
|
Log.e(TAG, exception.message, exception)
|
||||||
_error.emit(LexiconErrorUio.Structure)
|
_error.emit(FetchErrorUio.Structure)
|
||||||
}
|
}
|
||||||
// default exception
|
// default exception
|
||||||
catch (exception: Exception) {
|
catch (exception: Exception) {
|
||||||
Log.e(TAG, exception.message, exception)
|
Log.e(TAG, exception.message, exception)
|
||||||
_error.emit(LexiconErrorUio.Default)
|
_error.emit(FetchErrorUio.Default)
|
||||||
}
|
}
|
||||||
// clean the laoding state
|
// clean the laoding state
|
||||||
finally {
|
finally {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
package com.pixelized.rplexicon.ui.screens.location.list
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
||||||
|
import com.pixelized.rplexicon.utilitary.LOS_HOLLOW
|
||||||
|
import com.pixelized.rplexicon.utilitary.extentions.cell
|
||||||
|
import com.pixelized.rplexicon.utilitary.extentions.lexicon
|
||||||
|
import com.pixelized.rplexicon.utilitary.extentions.placeholder
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
data class LocationItemUio(
|
||||||
|
val id: Int,
|
||||||
|
val title: String,
|
||||||
|
val placeholder: Boolean = false,
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun preview(
|
||||||
|
id: Int = 0,
|
||||||
|
title: String = "Daggerfall",
|
||||||
|
placeHolder: Boolean = false,
|
||||||
|
): LocationItemUio {
|
||||||
|
return LocationItemUio(
|
||||||
|
id = id,
|
||||||
|
title = title,
|
||||||
|
placeholder = placeHolder,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LocationItem(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
item: LocationItemUio,
|
||||||
|
) {
|
||||||
|
val typography = MaterialTheme.lexicon.typography
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = modifier,
|
||||||
|
contentAlignment = Alignment.CenterStart,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
modifier = when (item.placeholder) {
|
||||||
|
true -> Modifier.placeholder { true }
|
||||||
|
else -> Modifier.alignByBaseline()
|
||||||
|
},
|
||||||
|
text = LOS_HOLLOW,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = when (item.placeholder) {
|
||||||
|
true -> Modifier.placeholder { true }
|
||||||
|
else -> Modifier.alignByBaseline()
|
||||||
|
},
|
||||||
|
text = remember(item.placeholder) {
|
||||||
|
AnnotatedString(
|
||||||
|
text = item.title,
|
||||||
|
spanStyles = when (item.placeholder) {
|
||||||
|
true -> emptyList()
|
||||||
|
else -> listOf(
|
||||||
|
AnnotatedString.Range(
|
||||||
|
item = typography.dropCapMediumSpan,
|
||||||
|
start = 0,
|
||||||
|
end = 1,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||||
|
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
private fun QuestItemPreview(
|
||||||
|
@PreviewParameter(QuestItemPreviewProvider::class) preview: LocationItemUio,
|
||||||
|
) {
|
||||||
|
LexiconTheme {
|
||||||
|
Surface {
|
||||||
|
LocationItem(
|
||||||
|
modifier = Modifier.cell(),
|
||||||
|
item = preview,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class QuestItemPreviewProvider : PreviewParameterProvider<LocationItemUio> {
|
||||||
|
override val values: Sequence<LocationItemUio> = sequenceOf(
|
||||||
|
LocationItemUio.preview(placeHolder = false),
|
||||||
|
LocationItemUio.preview(placeHolder = true),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,167 @@
|
||||||
|
package com.pixelized.rplexicon.ui.screens.location.list
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import androidx.compose.animation.AnimatedContent
|
||||||
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
import androidx.compose.material.ExperimentalMaterialApi
|
||||||
|
import androidx.compose.material.pullrefresh.PullRefreshState
|
||||||
|
import androidx.compose.material.pullrefresh.pullRefresh
|
||||||
|
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.State
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import com.pixelized.rplexicon.ui.composable.Loader
|
||||||
|
import com.pixelized.rplexicon.ui.composable.error.HandleFetchError
|
||||||
|
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
|
||||||
|
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
||||||
|
import com.pixelized.rplexicon.utilitary.extentions.cell
|
||||||
|
import com.pixelized.rplexicon.utilitary.extentions.lexicon
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
|
@Composable
|
||||||
|
fun LocationScreen(
|
||||||
|
viewModel: LocationViewModel = hiltViewModel(),
|
||||||
|
lazyListState: LazyListState,
|
||||||
|
) {
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val screen = LocalScreenNavHost.current
|
||||||
|
|
||||||
|
val refresh = rememberPullRefreshState(
|
||||||
|
refreshing = false,
|
||||||
|
onRefresh = {
|
||||||
|
scope.launch {
|
||||||
|
viewModel.fetchLocation()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
Surface {
|
||||||
|
LocationContent(
|
||||||
|
items = viewModel.items,
|
||||||
|
lazyColumnState = lazyListState,
|
||||||
|
refreshState = refresh,
|
||||||
|
refreshing = viewModel.isLoading,
|
||||||
|
onItem = {
|
||||||
|
// screen.navigateToQuestDetail(id = it.id)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
HandleFetchError(
|
||||||
|
errors = viewModel.error,
|
||||||
|
onPermissionGranted = {
|
||||||
|
viewModel.fetchLocation()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalAnimationApi::class, ExperimentalMaterialApi::class)
|
||||||
|
@Composable
|
||||||
|
private fun LocationContent(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
lazyColumnState: LazyListState,
|
||||||
|
refreshState: PullRefreshState,
|
||||||
|
refreshing: State<Boolean>,
|
||||||
|
items: State<List<LocationItemUio>>,
|
||||||
|
onItem: (LocationItemUio) -> Unit,
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = modifier,
|
||||||
|
contentAlignment = Alignment.TopCenter,
|
||||||
|
) {
|
||||||
|
AnimatedContent(
|
||||||
|
targetState = items.value.isEmpty(),
|
||||||
|
transitionSpec = MaterialTheme.lexicon.animation.itemList,
|
||||||
|
label = "AnimatedLocations"
|
||||||
|
) { empty ->
|
||||||
|
when (empty) {
|
||||||
|
true -> LazyColumn(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.pullRefresh(state = refreshState),
|
||||||
|
state = lazyColumnState,
|
||||||
|
contentPadding = MaterialTheme.lexicon.dimens.itemListPadding,
|
||||||
|
) {
|
||||||
|
items(count = 2) {
|
||||||
|
LocationItem(
|
||||||
|
modifier = Modifier.cell(),
|
||||||
|
item = LocationItemUio.preview(placeHolder = true),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> LazyColumn(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.pullRefresh(state = refreshState),
|
||||||
|
state = lazyColumnState,
|
||||||
|
contentPadding = MaterialTheme.lexicon.dimens.itemListPadding,
|
||||||
|
) {
|
||||||
|
items(
|
||||||
|
items = items.value,
|
||||||
|
key = { it.id },
|
||||||
|
contentType = { "Location" },
|
||||||
|
) {
|
||||||
|
LocationItem(
|
||||||
|
modifier = Modifier
|
||||||
|
.clickable { onItem(it) }
|
||||||
|
.cell(),
|
||||||
|
item = it,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader(
|
||||||
|
refreshState = refreshState,
|
||||||
|
refreshing = refreshing,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
|
@Composable
|
||||||
|
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||||
|
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
private fun QuestListPreview() {
|
||||||
|
LexiconTheme {
|
||||||
|
Surface {
|
||||||
|
LocationContent(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
lazyColumnState = rememberLazyListState(),
|
||||||
|
refreshState = rememberPullRefreshState(
|
||||||
|
refreshing = false,
|
||||||
|
onRefresh = {},
|
||||||
|
),
|
||||||
|
refreshing = remember { mutableStateOf(false) },
|
||||||
|
items = remember {
|
||||||
|
mutableStateOf(
|
||||||
|
listOf(
|
||||||
|
LocationItemUio.preview(id = 0, title = "Daggerfall"),
|
||||||
|
LocationItemUio.preview(id = 1, title = "Athkatla"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onItem = { },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
package com.pixelized.rplexicon.ui.screens.location.list
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.compose.runtime.State
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException
|
||||||
|
import com.pixelized.rplexicon.repository.LocationRepository
|
||||||
|
import com.pixelized.rplexicon.ui.composable.error.FetchErrorUio
|
||||||
|
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class LocationViewModel @Inject constructor(
|
||||||
|
private val repository: LocationRepository,
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
|
private val _isLoading = mutableStateOf(false)
|
||||||
|
val isLoading: State<Boolean> get() = _isLoading
|
||||||
|
|
||||||
|
private val _items = mutableStateOf<List<LocationItemUio>>(emptyList())
|
||||||
|
val items: State<List<LocationItemUio>> get() = _items
|
||||||
|
|
||||||
|
private val _error = MutableSharedFlow<FetchErrorUio>()
|
||||||
|
val error: SharedFlow<FetchErrorUio> get() = _error
|
||||||
|
|
||||||
|
init {
|
||||||
|
viewModelScope.launch {
|
||||||
|
launch {
|
||||||
|
repository.data.collect { items ->
|
||||||
|
_items.value = items.map { item ->
|
||||||
|
LocationItemUio(
|
||||||
|
id = item.id,
|
||||||
|
title = item.name,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
launch {
|
||||||
|
fetchLocation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun fetchLocation() {
|
||||||
|
try {
|
||||||
|
_isLoading.value = true
|
||||||
|
repository.fetchLocation()
|
||||||
|
}
|
||||||
|
// user need to accept OAuth2 permission.
|
||||||
|
catch (exception: UserRecoverableAuthIOException) {
|
||||||
|
Log.e(TAG, exception.message, exception)
|
||||||
|
_error.emit(FetchErrorUio.Permission(intent = exception.intent))
|
||||||
|
} catch (exception: IncompatibleSheetStructure) {
|
||||||
|
Log.e(TAG, exception.message, exception)
|
||||||
|
_error.emit(FetchErrorUio.Structure)
|
||||||
|
}
|
||||||
|
// default exception
|
||||||
|
catch (exception: Exception) {
|
||||||
|
Log.e(TAG, exception.message, exception)
|
||||||
|
_error.emit(FetchErrorUio.Default)
|
||||||
|
}
|
||||||
|
// clean the laoding state
|
||||||
|
finally {
|
||||||
|
_isLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "LocationViewModel"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -26,6 +26,7 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import com.pixelized.rplexicon.ui.composable.Loader
|
import com.pixelized.rplexicon.ui.composable.Loader
|
||||||
|
import com.pixelized.rplexicon.ui.composable.error.HandleFetchError
|
||||||
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
|
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
|
||||||
import com.pixelized.rplexicon.ui.navigation.screens.navigateToQuestDetail
|
import com.pixelized.rplexicon.ui.navigation.screens.navigateToQuestDetail
|
||||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
||||||
|
|
@ -38,9 +39,9 @@ import kotlinx.coroutines.launch
|
||||||
@Composable
|
@Composable
|
||||||
fun QuestListScreen(
|
fun QuestListScreen(
|
||||||
viewModel: QuestListViewModel = hiltViewModel(),
|
viewModel: QuestListViewModel = hiltViewModel(),
|
||||||
|
lazyListState: LazyListState,
|
||||||
) {
|
) {
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val lazyListState = rememberLazyListState()
|
|
||||||
val screen = LocalScreenNavHost.current
|
val screen = LocalScreenNavHost.current
|
||||||
|
|
||||||
val refresh = rememberPullRefreshState(
|
val refresh = rememberPullRefreshState(
|
||||||
|
|
@ -62,6 +63,13 @@ fun QuestListScreen(
|
||||||
screen.navigateToQuestDetail(id = it.id)
|
screen.navigateToQuestDetail(id = it.id)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
HandleFetchError(
|
||||||
|
errors = viewModel.error,
|
||||||
|
onPermissionGranted = {
|
||||||
|
viewModel.fetchQuests()
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -110,7 +118,7 @@ private fun QuestListContent(
|
||||||
items(
|
items(
|
||||||
items = items.value,
|
items = items.value,
|
||||||
key = { it.id },
|
key = { it.id },
|
||||||
contentType = { "Lexicon" },
|
contentType = { "Quest" },
|
||||||
) {
|
) {
|
||||||
QuestItem(
|
QuestItem(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,11 @@ import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException
|
import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException
|
||||||
import com.pixelized.rplexicon.repository.QuestRepository
|
import com.pixelized.rplexicon.repository.QuestRepository
|
||||||
|
import com.pixelized.rplexicon.ui.composable.error.FetchErrorUio
|
||||||
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
|
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
@ -23,8 +26,8 @@ class QuestListViewModel @Inject constructor(
|
||||||
private val _items = mutableStateOf<List<QuestItemUio>>(emptyList())
|
private val _items = mutableStateOf<List<QuestItemUio>>(emptyList())
|
||||||
val items: State<List<QuestItemUio>> get() = _items
|
val items: State<List<QuestItemUio>> get() = _items
|
||||||
|
|
||||||
// private val _error = MutableSharedFlow<LexiconErrorUio>()
|
private val _error = MutableSharedFlow<FetchErrorUio>()
|
||||||
// val error: SharedFlow<LexiconErrorUio> get() = _error
|
val error: SharedFlow<FetchErrorUio> get() = _error
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
|
@ -53,15 +56,15 @@ class QuestListViewModel @Inject constructor(
|
||||||
// user need to accept OAuth2 permission.
|
// user need to accept OAuth2 permission.
|
||||||
catch (exception: UserRecoverableAuthIOException) {
|
catch (exception: UserRecoverableAuthIOException) {
|
||||||
Log.e(TAG, exception.message, exception)
|
Log.e(TAG, exception.message, exception)
|
||||||
// _error.emit(LexiconErrorUio.Permission(intent = exception.intent))
|
_error.emit(FetchErrorUio.Permission(intent = exception.intent))
|
||||||
} catch (exception: IncompatibleSheetStructure) {
|
} catch (exception: IncompatibleSheetStructure) {
|
||||||
Log.e(TAG, exception.message, exception)
|
Log.e(TAG, exception.message, exception)
|
||||||
// _error.emit(LexiconErrorUio.Structure)
|
_error.emit(FetchErrorUio.Structure)
|
||||||
}
|
}
|
||||||
// default exception
|
// default exception
|
||||||
catch (exception: Exception) {
|
catch (exception: Exception) {
|
||||||
Log.e(TAG, exception.message, exception)
|
Log.e(TAG, exception.message, exception)
|
||||||
// _error.emit(LexiconErrorUio.Default)
|
_error.emit(FetchErrorUio.Default)
|
||||||
}
|
}
|
||||||
// clean the laoding state
|
// clean the laoding state
|
||||||
finally {
|
finally {
|
||||||
|
|
|
||||||
9
app/src/main/res/drawable/ic_outline_scroll_24.xml
Normal file
9
app/src/main/res/drawable/ic_outline_scroll_24.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M8,2C6.355,2 5,3.355 5,5L5,16L2,16L2,19C2,20.645 3.355,22 5,22L14,22L15,22C16.645,22 18,20.645 18,19L18,8L22,8L22,5C22,3.355 20.645,2 19,2L8,2zM8,4L16.188,4C16.074,4.315 16,4.648 16,5L16,19C16,19.56 15.572,19.991 15.014,19.998L15.014,19.986C14.45,19.994 14.008,19.563 14,19L13.973,16.004L13,16.014L13,16L7,16L7,5C7,4.435 7.435,4 8,4zM19,4C19.565,4 20,4.435 20,5L20,6L18,6L18,5C18,4.435 18.435,4 19,4zM4,18L11.99,18L12,19.025L12,19.027C12.005,19.37 12.081,19.694 12.193,20L5,20C4.435,20 4,19.565 4,19L4,18z"/>
|
||||||
|
</vector>
|
||||||
|
|
@ -30,6 +30,7 @@
|
||||||
|
|
||||||
<string name="home_lexicon">Lexique</string>
|
<string name="home_lexicon">Lexique</string>
|
||||||
<string name="home_quest_log">Journal de quêtes</string>
|
<string name="home_quest_log">Journal de quêtes</string>
|
||||||
|
<string name="home_location">Cartes</string>
|
||||||
|
|
||||||
<string name="lexicon_search">Rechercher</string>
|
<string name="lexicon_search">Rechercher</string>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@
|
||||||
|
|
||||||
<string name="home_lexicon">Lexicon</string>
|
<string name="home_lexicon">Lexicon</string>
|
||||||
<string name="home_quest_log">Quest logs</string>
|
<string name="home_quest_log">Quest logs</string>
|
||||||
|
<string name="home_location">Location</string>
|
||||||
|
|
||||||
<string name="lexicon_search">Search</string>
|
<string name="lexicon_search">Search</string>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue