Add profile screen to the search bar.
This commit is contained in:
parent
5ac6d6b352
commit
f6739f60ee
26 changed files with 390 additions and 262 deletions
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
|
||||
|
||||
<application
|
||||
android:name=".BibLibApplication"
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
package com.pixelized.biblib.module
|
||||
|
||||
import android.content.Context
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.pixelized.biblib.network.client.BibLibClient
|
||||
import com.pixelized.biblib.network.client.IBibLibClient
|
||||
import com.pixelized.biblib.repository.connectivity.ConnectivityRepository
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
|
@ -29,4 +32,12 @@ class NetworkModule {
|
|||
gson = gson
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideConnectivityRepository(
|
||||
@ApplicationContext context: Context,
|
||||
): ConnectivityRepository {
|
||||
return ConnectivityRepository(context)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
package com.pixelized.biblib.repository.connectivity
|
||||
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Network
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.NetworkRequest
|
||||
import androidx.core.content.getSystemService
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
/**
|
||||
* This class is responsible to check for internet availability.
|
||||
* https://developer.android.com/training/monitoring-device-state/connectivity-status-type
|
||||
*/
|
||||
class ConnectivityRepository constructor(
|
||||
@ApplicationContext context: Context,
|
||||
) {
|
||||
|
||||
private val _networkAvailabilityFlow = MutableStateFlow(true)
|
||||
|
||||
val networkAvailabilityFlow: Flow<Boolean> get() = _networkAvailabilityFlow
|
||||
val isNetworkAvailable: Boolean get() = _networkAvailabilityFlow.value
|
||||
|
||||
init {
|
||||
context.getSystemService<ConnectivityManager>()?.let { connectivityManager ->
|
||||
|
||||
val networkRequest = NetworkRequest.Builder()
|
||||
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
|
||||
.build()
|
||||
|
||||
val networkCallback = object : ConnectivityManager.NetworkCallback() {
|
||||
override fun onAvailable(network: Network) {
|
||||
super.onAvailable(network)
|
||||
_networkAvailabilityFlow.value = true
|
||||
}
|
||||
|
||||
override fun onLost(network: Network) {
|
||||
super.onLost(network)
|
||||
_networkAvailabilityFlow.value = false
|
||||
}
|
||||
}
|
||||
|
||||
connectivityManager.requestNetwork(networkRequest, networkCallback)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,12 +2,13 @@ package com.pixelized.biblib.ui.composable
|
|||
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_NO
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
|
|
@ -24,6 +25,7 @@ import com.pixelized.biblib.R
|
|||
import com.pixelized.biblib.ui.scaffold.SearchScaffoldState
|
||||
import com.pixelized.biblib.ui.scaffold.rememberSearchScaffoldState
|
||||
import com.pixelized.biblib.ui.theme.BibLibTheme
|
||||
import com.pixelized.biblib.utils.extention.bibLib
|
||||
import com.pixelized.biblib.utils.extention.todo
|
||||
|
||||
|
||||
|
|
@ -35,7 +37,13 @@ fun Search(
|
|||
onAvatar: () -> Unit = todo(),
|
||||
onSearch: () -> Unit = todo(),
|
||||
) {
|
||||
var test by remember { mutableStateOf("") }
|
||||
val horizontalPadding by animateDpAsState(
|
||||
targetValue = when (state.isCollapsed()) {
|
||||
true -> MaterialTheme.bibLib.dimen.dp8
|
||||
else -> MaterialTheme.bibLib.dimen.dp16
|
||||
}
|
||||
)
|
||||
var search by remember { mutableStateOf("") }
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clickable(onClick = onSearch)
|
||||
|
|
@ -44,8 +52,8 @@ fun Search(
|
|||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(42.dp)
|
||||
.padding(horizontal = 8.dp),
|
||||
.padding(start = horizontalPadding)
|
||||
.size(24.dp),
|
||||
imageVector = Icons.Default.Search,
|
||||
tint = MaterialTheme.colors.onSurface,
|
||||
contentDescription = null
|
||||
|
|
@ -58,24 +66,23 @@ fun Search(
|
|||
text = "Rechercher sur BibLib"
|
||||
)
|
||||
},
|
||||
value = test,
|
||||
value = if (state.isCollapsed()) "" else search,
|
||||
singleLine = true,
|
||||
enabled = state.isCollapsed().not(),
|
||||
readOnly = state.isCollapsed(),
|
||||
colors = TextFieldDefaults.outlinedTextFieldColors(
|
||||
focusedBorderColor = Color.Transparent,
|
||||
unfocusedBorderColor = Color.Transparent,
|
||||
disabledBorderColor = Color.Transparent,
|
||||
unfocusedBorderColor = Color.Transparent,
|
||||
disabledBorderColor = Color.Transparent,
|
||||
),
|
||||
onValueChange = { test = it }
|
||||
onValueChange = { search = it }
|
||||
)
|
||||
IconButton(
|
||||
modifier = Modifier.padding(horizontal = 8.dp),
|
||||
modifier = Modifier.padding(end = horizontalPadding),
|
||||
onClick = onAvatar,
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.size(32.dp)
|
||||
.clip(RoundedCornerShape(50)),
|
||||
modifier = Modifier.clip(CircleShape),
|
||||
painter = avatar,
|
||||
contentDescription = null
|
||||
)
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ fun ErrorCard(
|
|||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(MaterialTheme.bibLib.dimen.medium)
|
||||
.padding(MaterialTheme.bibLib.dimen.dp16)
|
||||
.sizeIn(
|
||||
minWidth = MaterialTheme.bibLib.dimen.dialog.minimum.width,
|
||||
minHeight = MaterialTheme.bibLib.dimen.dialog.minimum.height,
|
||||
|
|
@ -49,7 +49,7 @@ fun ErrorCard(
|
|||
)
|
||||
if (message.isNotEmpty()) {
|
||||
Text(
|
||||
modifier = Modifier.padding(top = MaterialTheme.bibLib.dimen.medium),
|
||||
modifier = Modifier.padding(top = MaterialTheme.bibLib.dimen.dp16),
|
||||
style = MaterialTheme.typography.body1,
|
||||
textAlign = TextAlign.Center,
|
||||
text = message
|
||||
|
|
@ -57,7 +57,7 @@ fun ErrorCard(
|
|||
}
|
||||
if (exception != null) {
|
||||
Text(
|
||||
modifier = Modifier.padding(top = MaterialTheme.bibLib.dimen.small),
|
||||
modifier = Modifier.padding(top = MaterialTheme.bibLib.dimen.dp8),
|
||||
style = MaterialTheme.typography.caption,
|
||||
textAlign = TextAlign.Center,
|
||||
text = exception.message ?: exception::class.java.simpleName
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ fun LoadingCard(
|
|||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(MaterialTheme.bibLib.dimen.medium)
|
||||
.padding(MaterialTheme.bibLib.dimen.dp16)
|
||||
.sizeIn(
|
||||
minWidth = MaterialTheme.bibLib.dimen.dialog.minimum.width,
|
||||
minHeight = MaterialTheme.bibLib.dimen.dialog.minimum.height,
|
||||
|
|
@ -50,7 +50,7 @@ fun LoadingCard(
|
|||
if (message?.isNotEmpty() == true) {
|
||||
val typography = MaterialTheme.typography
|
||||
Text(
|
||||
modifier = Modifier.padding(top = MaterialTheme.bibLib.dimen.medium),
|
||||
modifier = Modifier.padding(top = MaterialTheme.bibLib.dimen.dp16),
|
||||
style = typography.body1,
|
||||
textAlign = TextAlign.Center,
|
||||
text = message
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ fun SuccessCard(
|
|||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(MaterialTheme.bibLib.dimen.medium)
|
||||
.padding(MaterialTheme.bibLib.dimen.dp16)
|
||||
.sizeIn(
|
||||
minWidth = MaterialTheme.bibLib.dimen.dialog.minimum.width,
|
||||
minHeight = MaterialTheme.bibLib.dimen.dialog.minimum.height,
|
||||
|
|
@ -48,7 +48,7 @@ fun SuccessCard(
|
|||
Text(
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterHorizontally)
|
||||
.padding(top = MaterialTheme.bibLib.dimen.medium),
|
||||
.padding(top = MaterialTheme.bibLib.dimen.dp16),
|
||||
style = MaterialTheme.typography.body1,
|
||||
textAlign = TextAlign.Center,
|
||||
text = message
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.pixelized.biblib.ui.navigation.screen
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
|
|
@ -16,6 +17,7 @@ val LocalScreenNavHostController = compositionLocalOf<NavHostController> {
|
|||
error("LocalFullScreenNavHostController is not ready yet.")
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalComposeUiApi::class)
|
||||
@Composable
|
||||
fun ScreenNavHost(
|
||||
modifier: Modifier = Modifier,
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@ package com.pixelized.biblib.ui.scaffold
|
|||
|
||||
import android.content.Context
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.material.BottomSheetScaffold
|
||||
import androidx.compose.material.BottomSheetScaffoldState
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.rememberBottomSheetScaffoldState
|
||||
import androidx.compose.material.ModalBottomSheetLayout
|
||||
import androidx.compose.material.ModalBottomSheetState
|
||||
import androidx.compose.material.ModalBottomSheetValue.Hidden
|
||||
import androidx.compose.material.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.pixelized.biblib.R
|
||||
import com.pixelized.biblib.ui.composable.StateUio
|
||||
|
|
@ -20,60 +20,62 @@ import com.pixelized.biblib.utils.extention.showToast
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
val LocalBottomDetailController = staticCompositionLocalOf<BottomDetailStateController> {
|
||||
val LocalBottomDetailController = staticCompositionLocalOf<BottomDetailState> {
|
||||
error("LocalBottomDetailController is not ready yet")
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun BottomDetailScaffold(
|
||||
bottomStateController: BottomDetailStateController = rememberBottomDetailStateController(),
|
||||
content: @Composable (PaddingValues) -> Unit,
|
||||
bottomDetailState: BottomDetailState = rememberBottomDetailState(),
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
CompositionLocalProvider(
|
||||
LocalBottomDetailController provides bottomStateController
|
||||
LocalBottomDetailController provides bottomDetailState
|
||||
) {
|
||||
BottomSheetScaffold(
|
||||
scaffoldState = bottomStateController.scaffoldState,
|
||||
sheetPeekHeight = 0.dp,
|
||||
sheetGesturesEnabled = false,
|
||||
ModalBottomSheetLayout(
|
||||
scrimColor = Color.Black.copy(alpha = 0.37f),
|
||||
sheetState = bottomDetailState.bottomSheetState,
|
||||
sheetContent = {
|
||||
val detail by remember { bottomStateController.bookDetail }
|
||||
val detail by remember { bottomDetailState.bookDetail }
|
||||
DetailScreen(detail = detail)
|
||||
},
|
||||
content = content
|
||||
content = content,
|
||||
)
|
||||
|
||||
BackHandler(bottomStateController.scaffoldState.bottomSheetState.isExpanded) {
|
||||
bottomStateController.collapse()
|
||||
BackHandler(bottomDetailState.bottomSheetState.isVisible) {
|
||||
bottomDetailState.collapse()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun rememberBottomDetailStateController(
|
||||
fun rememberBottomDetailState(
|
||||
viewModel: BookDetailViewModel = hiltViewModel(),
|
||||
scope: CoroutineScope = rememberCoroutineScope(),
|
||||
scaffoldState: BottomSheetScaffoldState = rememberBottomSheetScaffoldState(),
|
||||
): BottomDetailStateController {
|
||||
bottomSheetState: ModalBottomSheetState = rememberModalBottomSheetState(
|
||||
initialValue = Hidden,
|
||||
skipHalfExpanded = true,
|
||||
),
|
||||
): BottomDetailState {
|
||||
val context: Context = LocalContext.current
|
||||
val controller = BottomDetailStateController(
|
||||
val controller = BottomDetailState(
|
||||
context = context,
|
||||
viewModel = viewModel,
|
||||
scope = scope,
|
||||
scaffoldState = scaffoldState
|
||||
bottomSheetState = bottomSheetState
|
||||
)
|
||||
return remember(scope, viewModel, scaffoldState) { controller }
|
||||
return remember(scope, viewModel, bottomSheetState) { controller }
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Stable
|
||||
class BottomDetailStateController constructor(
|
||||
class BottomDetailState constructor(
|
||||
private val context: Context,
|
||||
private val viewModel: BookDetailViewModel,
|
||||
private val scope: CoroutineScope,
|
||||
val scaffoldState: BottomSheetScaffoldState,
|
||||
val bottomSheetState: ModalBottomSheetState,
|
||||
) {
|
||||
var bookDetail = mutableStateOf<BookUio?>(null)
|
||||
private set
|
||||
|
|
@ -87,7 +89,8 @@ class BottomDetailStateController constructor(
|
|||
}
|
||||
is StateUio.Success -> {
|
||||
bookDetail.value = book.value
|
||||
scaffoldState.bottomSheetState.expand()
|
||||
// bottomSheetState.animateTo(ModalBottomSheetValue.Expanded)
|
||||
bottomSheetState.show()
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
|
|
@ -96,7 +99,7 @@ class BottomDetailStateController constructor(
|
|||
|
||||
fun collapse() {
|
||||
scope.launch {
|
||||
scaffoldState.bottomSheetState.collapse()
|
||||
bottomSheetState.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,8 @@
|
|||
package com.pixelized.biblib.ui.scaffold
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.core.animateDp
|
||||
import androidx.compose.animation.core.updateTransition
|
||||
import androidx.compose.foundation.ScrollState
|
||||
import androidx.compose.foundation.ScrollState.Companion.Saver
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
|
|
@ -36,13 +34,13 @@ fun SearchScaffold(
|
|||
)
|
||||
val horizontal by transition.animateDp(label = "horizontal") {
|
||||
when (it) {
|
||||
true -> MaterialTheme.bibLib.dimen.medium
|
||||
true -> MaterialTheme.bibLib.dimen.dp16
|
||||
else -> MaterialTheme.bibLib.dimen.default
|
||||
}
|
||||
}
|
||||
val vertical by transition.animateDp(label = "vertical") {
|
||||
when (it) {
|
||||
true -> MaterialTheme.bibLib.dimen.small
|
||||
true -> MaterialTheme.bibLib.dimen.dp8
|
||||
else -> MaterialTheme.bibLib.dimen.default
|
||||
}
|
||||
}
|
||||
|
|
@ -53,7 +51,11 @@ fun SearchScaffold(
|
|||
) {
|
||||
Column {
|
||||
topBar()
|
||||
AnimatedVisibility(visible = state.isCollapsed().not()) {
|
||||
AnimatedVisibility(
|
||||
visible = state.isCollapsed().not(),
|
||||
enter = fadeIn() + expandVertically(),
|
||||
exit = fadeOut() + shrinkVertically(),
|
||||
) {
|
||||
search()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ private fun AuthenticationScreenContent(
|
|||
|
||||
AnimatedOffset(
|
||||
modifier = Modifier
|
||||
.padding(all = MaterialTheme.bibLib.dimen.medium)
|
||||
.padding(all = MaterialTheme.bibLib.dimen.dp16)
|
||||
.align(alignment = Alignment.CenterHorizontally),
|
||||
) {
|
||||
Text(
|
||||
|
|
@ -127,7 +127,7 @@ private fun AuthenticationScreenContent(
|
|||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
AnimatedOffset(
|
||||
modifier = Modifier.padding(horizontal = MaterialTheme.bibLib.dimen.medium),
|
||||
modifier = Modifier.padding(horizontal = MaterialTheme.bibLib.dimen.dp16),
|
||||
) {
|
||||
LoginField(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
|
|
@ -137,10 +137,10 @@ private fun AuthenticationScreenContent(
|
|||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(MaterialTheme.bibLib.dimen.small))
|
||||
Spacer(modifier = Modifier.height(MaterialTheme.bibLib.dimen.dp8))
|
||||
|
||||
AnimatedOffset(
|
||||
modifier = Modifier.padding(horizontal = MaterialTheme.bibLib.dimen.medium),
|
||||
modifier = Modifier.padding(horizontal = MaterialTheme.bibLib.dimen.dp16),
|
||||
) {
|
||||
PasswordField(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
|
|
@ -151,10 +151,10 @@ private fun AuthenticationScreenContent(
|
|||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(MaterialTheme.bibLib.dimen.medium))
|
||||
Spacer(modifier = Modifier.height(MaterialTheme.bibLib.dimen.dp16))
|
||||
|
||||
AnimatedOffset(
|
||||
modifier = Modifier.padding(horizontal = MaterialTheme.bibLib.dimen.medium),
|
||||
modifier = Modifier.padding(horizontal = MaterialTheme.bibLib.dimen.dp16),
|
||||
) {
|
||||
CredentialRemember(
|
||||
value = rememberPassword,
|
||||
|
|
@ -162,11 +162,11 @@ private fun AuthenticationScreenContent(
|
|||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(MaterialTheme.bibLib.dimen.medium))
|
||||
Spacer(modifier = Modifier.height(MaterialTheme.bibLib.dimen.dp16))
|
||||
|
||||
AnimatedOffset(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = MaterialTheme.bibLib.dimen.medium)
|
||||
.padding(horizontal = MaterialTheme.bibLib.dimen.dp16)
|
||||
.align(Alignment.End),
|
||||
) {
|
||||
Row {
|
||||
|
|
@ -177,7 +177,7 @@ private fun AuthenticationScreenContent(
|
|||
Text(text = stringResource(id = R.string.action_register))
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.width(MaterialTheme.bibLib.dimen.small))
|
||||
Spacer(modifier = Modifier.width(MaterialTheme.bibLib.dimen.dp8))
|
||||
|
||||
Button(
|
||||
colors = ButtonDefaults.buttonColors(),
|
||||
|
|
@ -193,7 +193,7 @@ private fun AuthenticationScreenContent(
|
|||
AnimatedOffset {
|
||||
Button(
|
||||
modifier = Modifier
|
||||
.padding(all = MaterialTheme.bibLib.dimen.medium)
|
||||
.padding(all = MaterialTheme.bibLib.dimen.dp16)
|
||||
.fillMaxWidth(),
|
||||
colors = ButtonDefaults.outlinedButtonColors(),
|
||||
onClick = onGoogleSignIn,
|
||||
|
|
@ -299,7 +299,7 @@ private fun CredentialRemember(
|
|||
onCheckedChange = null
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(MaterialTheme.bibLib.dimen.small))
|
||||
Spacer(modifier = Modifier.width(MaterialTheme.bibLib.dimen.dp8))
|
||||
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.CenterVertically),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
package com.pixelized.biblib.ui.screen.connectivity
|
||||
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.pixelized.biblib.repository.connectivity.ConnectivityRepository
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class ConnectivityViewModel @Inject constructor(
|
||||
private val connectivityRepository: ConnectivityRepository,
|
||||
) : ViewModel() {
|
||||
|
||||
private val _isNetworkAvailable = mutableStateOf(connectivityRepository.isNetworkAvailable)
|
||||
val isNetworkAvailable: State<Boolean> get() = _isNetworkAvailable
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
connectivityRepository.networkAvailabilityFlow.collect {
|
||||
_isNetworkAvailable.value = it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ package com.pixelized.biblib.ui.screen.detail
|
|||
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_NO
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
|
|
@ -68,13 +67,13 @@ private fun DetailScreenContent(
|
|||
modifier = Modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
.systemBarsPadding()
|
||||
.padding(all = MaterialTheme.bibLib.dimen.medium)
|
||||
.padding(all = MaterialTheme.bibLib.dimen.dp16)
|
||||
.then(modifier)
|
||||
) {
|
||||
AnimatedOffset(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = MaterialTheme.bibLib.dimen.medium)
|
||||
.padding(vertical = MaterialTheme.bibLib.dimen.dp16)
|
||||
.height(MaterialTheme.bibLib.dimen.detail.cover),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
|
|
@ -89,7 +88,7 @@ private fun DetailScreenContent(
|
|||
)
|
||||
}
|
||||
|
||||
Row(modifier = Modifier.padding(vertical = MaterialTheme.bibLib.dimen.medium)) {
|
||||
Row(modifier = Modifier.padding(vertical = MaterialTheme.bibLib.dimen.dp16)) {
|
||||
AnimatedOffset(
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
|
|
@ -98,12 +97,12 @@ private fun DetailScreenContent(
|
|||
onClick = onMobi,
|
||||
) {
|
||||
Icon(imageVector = Icons.Default.Download, contentDescription = null)
|
||||
Spacer(modifier = Modifier.width(MaterialTheme.bibLib.dimen.extraSmall))
|
||||
Spacer(modifier = Modifier.width(MaterialTheme.bibLib.dimen.dp4))
|
||||
Text(text = stringResource(id = R.string.action_mobi))
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.padding(all = MaterialTheme.bibLib.dimen.extraSmall))
|
||||
Spacer(modifier = Modifier.padding(all = MaterialTheme.bibLib.dimen.dp4))
|
||||
|
||||
AnimatedOffset(
|
||||
modifier = Modifier.weight(1f),
|
||||
|
|
@ -113,12 +112,12 @@ private fun DetailScreenContent(
|
|||
onClick = onEpub,
|
||||
) {
|
||||
Icon(imageVector = Icons.Default.Download, contentDescription = null)
|
||||
Spacer(modifier = Modifier.width(MaterialTheme.bibLib.dimen.extraSmall))
|
||||
Spacer(modifier = Modifier.width(MaterialTheme.bibLib.dimen.dp4))
|
||||
Text(text = stringResource(id = R.string.action_epub))
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.padding(all = MaterialTheme.bibLib.dimen.extraSmall))
|
||||
Spacer(modifier = Modifier.padding(all = MaterialTheme.bibLib.dimen.dp4))
|
||||
|
||||
AnimatedOffset(
|
||||
modifier = Modifier.weight(1f),
|
||||
|
|
@ -128,7 +127,7 @@ private fun DetailScreenContent(
|
|||
onClick = onSend,
|
||||
) {
|
||||
Icon(imageVector = Icons.Default.Send, contentDescription = "")
|
||||
Spacer(modifier = Modifier.width(MaterialTheme.bibLib.dimen.extraSmall))
|
||||
Spacer(modifier = Modifier.width(MaterialTheme.bibLib.dimen.dp4))
|
||||
Text(text = stringResource(id = R.string.action_send))
|
||||
}
|
||||
}
|
||||
|
|
@ -149,7 +148,7 @@ private fun DetailScreenContent(
|
|||
AnimatedOffset(
|
||||
modifier = Modifier
|
||||
.align(alignment = Alignment.CenterHorizontally)
|
||||
.padding(bottom = MaterialTheme.bibLib.dimen.medium),
|
||||
.padding(bottom = MaterialTheme.bibLib.dimen.dp16),
|
||||
) {
|
||||
Text(
|
||||
fontWeight = FontWeight.Bold,
|
||||
|
|
@ -160,7 +159,7 @@ private fun DetailScreenContent(
|
|||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier.padding(bottom = MaterialTheme.bibLib.dimen.small),
|
||||
modifier = Modifier.padding(bottom = MaterialTheme.bibLib.dimen.dp8),
|
||||
) {
|
||||
AnimatedOffset(modifier = Modifier.weight(1f)) {
|
||||
TitleLabel(
|
||||
|
|
@ -185,7 +184,7 @@ private fun DetailScreenContent(
|
|||
}
|
||||
|
||||
AnimatedOffset(
|
||||
modifier = Modifier.padding(bottom = MaterialTheme.bibLib.dimen.medium),
|
||||
modifier = Modifier.padding(bottom = MaterialTheme.bibLib.dimen.dp16),
|
||||
) {
|
||||
TitleLabel(
|
||||
title = stringResource(id = R.string.detail_series),
|
||||
|
|
|
|||
|
|
@ -1,15 +1,13 @@
|
|||
package com.pixelized.biblib.ui.screen.home
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.clipToBounds
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.platform.SoftwareKeyboardController
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
|
|
@ -19,14 +17,18 @@ import com.pixelized.biblib.ui.scaffold.BottomDetailScaffold
|
|||
import com.pixelized.biblib.ui.scaffold.SearchScaffold
|
||||
import com.pixelized.biblib.ui.scaffold.SearchScaffoldState
|
||||
import com.pixelized.biblib.ui.scaffold.rememberSearchScaffoldState
|
||||
import com.pixelized.biblib.ui.screen.profile.ProfileScreen
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@OptIn(ExperimentalComposeUiApi::class)
|
||||
@Composable
|
||||
fun HomeScreen(
|
||||
accountViewModel: HomeViewModel = hiltViewModel(),
|
||||
keyboard: SoftwareKeyboardController? = LocalSoftwareKeyboardController.current,
|
||||
navHostController: NavHostController = rememberNavController(),
|
||||
searchScaffoldState: SearchScaffoldState = rememberSearchScaffoldState(),
|
||||
) {
|
||||
var pouet: String by remember { mutableStateOf("aucun") }
|
||||
|
||||
BottomDetailScaffold {
|
||||
SearchScaffold(
|
||||
state = searchScaffoldState,
|
||||
|
|
@ -39,6 +41,16 @@ fun HomeScreen(
|
|||
avatar = accountViewModel.avatar,
|
||||
onSearch = {
|
||||
if (searchScaffoldState.isCollapsed()) {
|
||||
pouet = "search"
|
||||
searchScaffoldState.expand()
|
||||
} else {
|
||||
keyboard?.hide()
|
||||
searchScaffoldState.collapse()
|
||||
}
|
||||
},
|
||||
onAvatar = {
|
||||
if (searchScaffoldState.isCollapsed()) {
|
||||
pouet = "profile"
|
||||
searchScaffoldState.expand()
|
||||
} else {
|
||||
searchScaffoldState.collapse()
|
||||
|
|
@ -47,7 +59,12 @@ fun HomeScreen(
|
|||
)
|
||||
},
|
||||
search = {
|
||||
Box(modifier = Modifier.fillMaxSize())
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
when (pouet) {
|
||||
"profile" -> ProfileScreen()
|
||||
"search" -> Unit
|
||||
}
|
||||
}
|
||||
},
|
||||
content = {
|
||||
Column {
|
||||
|
|
@ -55,12 +72,12 @@ fun HomeScreen(
|
|||
homePageNavController = navHostController
|
||||
)
|
||||
PageNavHost(
|
||||
modifier = Modifier.padding(it),
|
||||
navHostController = navHostController,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
BackHandler(searchScaffoldState.isCollapsed().not()) {
|
||||
searchScaffoldState.collapse()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,100 +0,0 @@
|
|||
package com.pixelized.biblib.ui.screen.home
|
||||
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_NO
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.pixelized.biblib.R
|
||||
import com.pixelized.biblib.ui.navigation.screen.LocalScreenNavHostController
|
||||
import com.pixelized.biblib.ui.navigation.screen.navigateToProfile
|
||||
import com.pixelized.biblib.ui.theme.BibLibTheme
|
||||
import com.pixelized.biblib.utils.extention.todo
|
||||
|
||||
@Composable
|
||||
fun HomeSearchBar(
|
||||
accountViewModel: HomeViewModel = hiltViewModel()
|
||||
) {
|
||||
val screenNavHostController = LocalScreenNavHostController.current
|
||||
val avatar by rememberUpdatedState(accountViewModel.avatar)
|
||||
|
||||
HomeSearchBarContent(
|
||||
modifier = Modifier.padding(vertical = 8.dp, horizontal = 16.dp),
|
||||
avatar = avatar,
|
||||
onAvatar = {
|
||||
screenNavHostController.navigateToProfile()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun HomeSearchBarContent(
|
||||
modifier: Modifier = Modifier,
|
||||
avatar: Painter,
|
||||
onAvatar: () -> Unit = todo(),
|
||||
onSearch: () -> Unit = todo(),
|
||||
) {
|
||||
Card(
|
||||
modifier = modifier,
|
||||
elevation = 8.dp
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clickable(onClick = onSearch)
|
||||
.fillMaxWidth()
|
||||
.height(48.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(42.dp)
|
||||
.padding(horizontal = 8.dp),
|
||||
imageVector = Icons.Default.Search,
|
||||
contentDescription = null
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
color = MaterialTheme.colors.onSurface,
|
||||
text = "Rechercher sur BibLib"
|
||||
)
|
||||
IconButton(
|
||||
modifier = Modifier.padding(horizontal = 8.dp),
|
||||
onClick = onAvatar,
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.size(32.dp)
|
||||
.clip(RoundedCornerShape(50)),
|
||||
painter = avatar,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(uiMode = UI_MODE_NIGHT_NO)
|
||||
@Preview(uiMode = UI_MODE_NIGHT_YES)
|
||||
private fun HomeSearchBarContentPreview() {
|
||||
BibLibTheme {
|
||||
HomeSearchBarContent(
|
||||
avatar = painterResource(R.drawable.ic_baseline_auto_stories_24)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -23,12 +23,13 @@ import com.pixelized.biblib.utils.extention.bibLib
|
|||
|
||||
@Composable
|
||||
fun BookThumbnail(
|
||||
modifier: Modifier = Modifier,
|
||||
thumbnail: BookThumbnailUio?,
|
||||
onClick: (BookThumbnailUio) -> Unit = { },
|
||||
) {
|
||||
val currentOnClick by rememberUpdatedState(newValue = onClick)
|
||||
Card(
|
||||
modifier = Modifier
|
||||
modifier = modifier
|
||||
.clickable(enabled = thumbnail != null) {
|
||||
thumbnail?.let { currentOnClick(it) }
|
||||
}
|
||||
|
|
@ -47,7 +48,7 @@ fun BookThumbnail(
|
|||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(MaterialTheme.bibLib.dimen.small)
|
||||
.padding(MaterialTheme.bibLib.dimen.dp8)
|
||||
) {
|
||||
Text(
|
||||
style = MaterialTheme.typography.body1,
|
||||
|
|
|
|||
|
|
@ -1,16 +1,26 @@
|
|||
package com.pixelized.biblib.ui.screen.home.common.composable
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.ButtonDefaults
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.CloudOff
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.compose.LazyPagingItems
|
||||
|
|
@ -23,12 +33,14 @@ import com.pixelized.biblib.ui.theme.BibLibTheme
|
|||
import com.pixelized.biblib.utils.extention.bibLib
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun LazyBookThumbnailColumn(
|
||||
modifier: Modifier = Modifier,
|
||||
verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(MaterialTheme.bibLib.dimen.medium),
|
||||
contentPadding: PaddingValues = PaddingValues(all = MaterialTheme.bibLib.dimen.medium),
|
||||
verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(MaterialTheme.bibLib.dimen.dp16),
|
||||
contentPadding: PaddingValues = PaddingValues(),
|
||||
state: LazyListState = rememberLazyListState(),
|
||||
isNetworkAvailable: () -> State<Boolean>,
|
||||
books: LazyPagingItems<BookThumbnailUio>,
|
||||
onItemClick: (BookThumbnailUio) -> Unit = {},
|
||||
) {
|
||||
|
|
@ -39,11 +51,17 @@ fun LazyBookThumbnailColumn(
|
|||
contentPadding = contentPadding,
|
||||
state = state,
|
||||
) {
|
||||
stickyHeader {
|
||||
ConnectivityHeader(
|
||||
isNetworkAvailable = isNetworkAvailable,
|
||||
)
|
||||
}
|
||||
items(
|
||||
items = books,
|
||||
key = { it.id },
|
||||
) { thumbnail ->
|
||||
BookThumbnail(
|
||||
modifier = Modifier.padding(horizontal = MaterialTheme.bibLib.dimen.thumbnail.padding),
|
||||
thumbnail = thumbnail,
|
||||
onClick = currentOnItemClick,
|
||||
)
|
||||
|
|
@ -51,12 +69,51 @@ fun LazyBookThumbnailColumn(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ConnectivityHeader(
|
||||
modifier : Modifier = Modifier,
|
||||
isNetworkAvailable: () -> State<Boolean>
|
||||
) {
|
||||
val showHideHeader by isNetworkAvailable()
|
||||
AnimatedVisibility(
|
||||
visible = showHideHeader.not(),
|
||||
enter = expandVertically(),
|
||||
exit = shrinkVertically(),
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.background(color = MaterialTheme.colors.error)
|
||||
.fillMaxWidth()
|
||||
.sizeIn(minHeight = ButtonDefaults.MinHeight)
|
||||
.padding(
|
||||
horizontal = MaterialTheme.bibLib.dimen.thumbnail.padding,
|
||||
vertical = MaterialTheme.bibLib.dimen.thumbnail.arrangement,
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.padding(end = MaterialTheme.bibLib.dimen.dp8),
|
||||
imageVector = Icons.Default.CloudOff,
|
||||
tint = MaterialTheme.colors.onError,
|
||||
contentDescription = null
|
||||
)
|
||||
Text(
|
||||
style = MaterialTheme.typography.caption,
|
||||
color = MaterialTheme.colors.onError,
|
||||
text = stringResource(id = R.string.error_offline),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
private fun LazyBookThumbnailColumnPreview() {
|
||||
val isNetworkAvailable = remember { mutableStateOf(false) }
|
||||
BibLibTheme {
|
||||
LazyBookThumbnailColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
isNetworkAvailable = { isNetworkAvailable },
|
||||
books = previewResources(),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@ package com.pixelized.biblib.ui.screen.home.page.books
|
|||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.pixelized.biblib.ui.scaffold.LocalBottomDetailController
|
||||
import com.pixelized.biblib.ui.screen.connectivity.ConnectivityViewModel
|
||||
import com.pixelized.biblib.ui.screen.home.common.composable.LazyBookThumbnailColumn
|
||||
import com.pixelized.biblib.ui.screen.home.page.news.NewsPage
|
||||
import com.pixelized.biblib.ui.theme.BibLibTheme
|
||||
|
|
@ -15,13 +15,14 @@ import com.pixelized.biblib.utils.extention.bibLib
|
|||
|
||||
@Composable
|
||||
fun BooksPage(
|
||||
connectivityViewModel: ConnectivityViewModel = hiltViewModel(),
|
||||
booksViewModel: BooksViewModel = hiltViewModel()
|
||||
) {
|
||||
val bottomDetailState = LocalBottomDetailController.current
|
||||
|
||||
LazyBookThumbnailColumn(
|
||||
verticalArrangement = Arrangement.spacedBy(MaterialTheme.bibLib.dimen.thumbnail.arrangement),
|
||||
contentPadding = PaddingValues(all = MaterialTheme.bibLib.dimen.thumbnail.padding),
|
||||
isNetworkAvailable = { connectivityViewModel.isNetworkAvailable },
|
||||
books = booksViewModel.books,
|
||||
onItemClick = {
|
||||
bottomDetailState.expandBookDetail(id = it.id)
|
||||
|
|
|
|||
|
|
@ -2,25 +2,26 @@ package com.pixelized.biblib.ui.screen.home.page.news
|
|||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.pixelized.biblib.ui.scaffold.LocalBottomDetailController
|
||||
import com.pixelized.biblib.ui.screen.connectivity.ConnectivityViewModel
|
||||
import com.pixelized.biblib.ui.screen.home.common.composable.LazyBookThumbnailColumn
|
||||
import com.pixelized.biblib.ui.theme.BibLibTheme
|
||||
import com.pixelized.biblib.utils.extention.bibLib
|
||||
|
||||
@Composable
|
||||
fun NewsPage(
|
||||
connectivityViewModel: ConnectivityViewModel = hiltViewModel(),
|
||||
booksViewModel: NewsBookViewModel = hiltViewModel()
|
||||
) {
|
||||
val bottomDetail = LocalBottomDetailController.current
|
||||
|
||||
LazyBookThumbnailColumn(
|
||||
verticalArrangement = Arrangement.spacedBy(MaterialTheme.bibLib.dimen.thumbnail.arrangement),
|
||||
contentPadding = PaddingValues(all = MaterialTheme.bibLib.dimen.thumbnail.padding),
|
||||
isNetworkAvailable = { connectivityViewModel.isNetworkAvailable },
|
||||
books = booksViewModel.news,
|
||||
onItemClick = {
|
||||
bottomDetail.expandBookDetail(id = it.id)
|
||||
|
|
|
|||
|
|
@ -4,66 +4,85 @@ import android.content.Intent
|
|||
import android.content.res.Configuration.UI_MODE_NIGHT_NO
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||
import android.net.Uri
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.material.FloatingActionButton
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.navigation.NavHostController
|
||||
import com.pixelized.biblib.network.client.IBibLibClient
|
||||
import com.pixelized.biblib.ui.composable.StateUio
|
||||
import com.pixelized.biblib.ui.theme.BibLibTheme
|
||||
import com.pixelized.biblib.utils.extention.bibLib
|
||||
import com.pixelized.biblib.utils.extention.todo
|
||||
|
||||
@Composable
|
||||
fun ProfileScreen(
|
||||
viewModel: ProfileViewModel = hiltViewModel(),
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val user by rememberUpdatedState(newValue = viewModel.user)
|
||||
|
||||
when (val unwrap = user) {
|
||||
is StateUio.Success -> ProfileScreenContent(user = unwrap.value)
|
||||
when (val user = viewModel.user) {
|
||||
is StateUio.Progress -> Unit
|
||||
is StateUio.Success -> ProfileScreenContent(
|
||||
modifier = Modifier.padding(MaterialTheme.bibLib.dimen.dp16),
|
||||
user = user.value,
|
||||
onEditClick = {
|
||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(IBibLibClient.EDIT_PROFILE))
|
||||
context.startActivity(intent)
|
||||
}
|
||||
)
|
||||
is StateUio.Failure -> Unit
|
||||
}
|
||||
|
||||
FloatingActionButton(
|
||||
onClick = {
|
||||
context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(IBibLibClient.EDIT_PROFILE)))
|
||||
},
|
||||
content = { },
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ProfileScreenContent(
|
||||
modifier: Modifier = Modifier,
|
||||
user: ProfileViewModel.UserUio,
|
||||
user: UserUio,
|
||||
onEditClick: () -> Unit = todo()
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
Column(modifier = modifier.fillMaxWidth()) {
|
||||
Text(
|
||||
style = MaterialTheme.typography.h5,
|
||||
style = MaterialTheme.typography.body1,
|
||||
color = MaterialTheme.colors.onSurface,
|
||||
text = "Welcome"
|
||||
)
|
||||
Text(
|
||||
style = MaterialTheme.typography.h4,
|
||||
color = MaterialTheme.colors.primary,
|
||||
text = user.username,
|
||||
)
|
||||
user.firstname?.let {
|
||||
Text(
|
||||
style = MaterialTheme.typography.body1,
|
||||
color = MaterialTheme.colors.onSurface,
|
||||
text = it,
|
||||
)
|
||||
}
|
||||
user.lastname?.let {
|
||||
Text(
|
||||
style = MaterialTheme.typography.body1,
|
||||
color = MaterialTheme.colors.onSurface,
|
||||
text = it,
|
||||
)
|
||||
|
||||
if (user.firstname?.isNotEmpty() == true || user.lastname?.isNotEmpty() == true) {
|
||||
Row {
|
||||
user.firstname?.let {
|
||||
Text(
|
||||
modifier = Modifier.padding(end = MaterialTheme.bibLib.dimen.dp4),
|
||||
style = MaterialTheme.typography.body1,
|
||||
color = MaterialTheme.colors.onSurface,
|
||||
text = it,
|
||||
)
|
||||
}
|
||||
user.lastname?.let {
|
||||
Text(
|
||||
style = MaterialTheme.typography.body1,
|
||||
color = MaterialTheme.colors.onSurface,
|
||||
text = it,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text(
|
||||
modifier = Modifier.padding(top = MaterialTheme.bibLib.dimen.dp16),
|
||||
style = MaterialTheme.typography.body1,
|
||||
color = MaterialTheme.colors.onSurface,
|
||||
text = "Linked emails:"
|
||||
)
|
||||
user.amazonEmails.forEach {
|
||||
Text(
|
||||
style = MaterialTheme.typography.body1,
|
||||
|
|
@ -71,7 +90,25 @@ private fun ProfileScreenContent(
|
|||
text = it,
|
||||
)
|
||||
}
|
||||
|
||||
Button(
|
||||
modifier = Modifier
|
||||
.padding(top = MaterialTheme.bibLib.dimen.dp16)
|
||||
.align(Alignment.End),
|
||||
colors = ButtonDefaults.outlinedButtonColors(),
|
||||
onClick = onEditClick,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Edit,
|
||||
contentDescription = null
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(start = MaterialTheme.bibLib.dimen.dp8),
|
||||
text = "Edit profile"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -79,14 +116,19 @@ private fun ProfileScreenContent(
|
|||
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_YES)
|
||||
private fun ProfileScreenContentPreview() {
|
||||
BibLibTheme {
|
||||
val user = ProfileViewModel.UserUio(
|
||||
username = "R. Daneel Olivaw",
|
||||
firstname = "R. Daneel",
|
||||
lastname = "Olivaw",
|
||||
amazonEmails = listOf("r.daneel.olivaw@biblib.com"),
|
||||
)
|
||||
ProfileScreenContent(
|
||||
user = user
|
||||
)
|
||||
Box {
|
||||
ProfileScreenContent(
|
||||
modifier = Modifier.padding(MaterialTheme.bibLib.dimen.dp16),
|
||||
user = UserUio(
|
||||
username = "DefinitelyNotARobot",
|
||||
firstname = "R. Daneel",
|
||||
lastname = "Olivaw",
|
||||
amazonEmails = listOf(
|
||||
"r.daneel.olivaw@robot.com",
|
||||
"r.daneel.olivaw@biblib.com",
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import android.app.Application
|
|||
import android.util.Log
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.pixelized.biblib.model.user.User
|
||||
import com.pixelized.biblib.network.client.IBibLibClient
|
||||
|
|
@ -23,8 +24,8 @@ class ProfileViewModel @Inject constructor(
|
|||
private val client: IBibLibClient,
|
||||
) : ACoverViewModel(application, cacheBook) {
|
||||
|
||||
private val _user = mutableStateOf<StateUio<UserUio>>(StateUio.Progress())
|
||||
val user by _user
|
||||
var user by mutableStateOf<StateUio<UserUio>>(StateUio.Progress())
|
||||
private set
|
||||
|
||||
init {
|
||||
updateUser()
|
||||
|
|
@ -32,29 +33,22 @@ class ProfileViewModel @Inject constructor(
|
|||
|
||||
private fun updateUser() {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
user = try {
|
||||
val factory = UserFactory()
|
||||
val response = client.service.user()
|
||||
val user = factory.fromUserResponseToUser(response)
|
||||
_user.value = StateUio.Success(user.toUio())
|
||||
val data = factory.fromUserResponseToUser(response)
|
||||
StateUio.Success(data.toUio())
|
||||
} catch (exception: Exception) {
|
||||
Log.e("AccountViewModel", exception.message, exception)
|
||||
_user.value = StateUio.Failure(exception)
|
||||
StateUio.Failure(exception)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class UserUio(
|
||||
val username: String,
|
||||
val firstname: String?,
|
||||
val lastname: String?,
|
||||
val amazonEmails: List<String>,
|
||||
private fun User.toUio() = UserUio(
|
||||
username = username,
|
||||
firstname = firstname,
|
||||
lastname = lastname,
|
||||
amazonEmails = amazonEmails.toList(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun User.toUio() = ProfileViewModel.UserUio(
|
||||
username = username,
|
||||
firstname = firstname,
|
||||
lastname = lastname,
|
||||
amazonEmails = amazonEmails.toList(),
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.pixelized.biblib.ui.screen.profile
|
||||
|
||||
|
||||
data class UserUio(
|
||||
val username: String,
|
||||
val firstname: String?,
|
||||
val lastname: String?,
|
||||
val amazonEmails: List<String>,
|
||||
)
|
||||
|
|
@ -21,6 +21,7 @@ fun bibLibDarkColors(
|
|||
onPrimary = Color.White,
|
||||
onSecondary = Color.White,
|
||||
error = BibLibColorPalette.Red,
|
||||
onError = Color.White,
|
||||
),
|
||||
) = BibLibColor(
|
||||
base = base,
|
||||
|
|
@ -33,6 +34,7 @@ fun bibLibLightColors(
|
|||
onPrimary = Color.White,
|
||||
onSecondary = Color.White,
|
||||
error = BibLibColorPalette.Red,
|
||||
onError = Color.White,
|
||||
)
|
||||
) = BibLibColor(
|
||||
base = base,
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@ import androidx.compose.ui.unit.dp
|
|||
@Immutable
|
||||
data class BibLibDimen(
|
||||
val default: Dp = 0.dp,
|
||||
val extraSmall: Dp = 4.dp,
|
||||
val small: Dp = 8.dp,
|
||||
val medium: Dp = 16.dp,
|
||||
val large: Dp = 32.dp,
|
||||
val extraLarge: Dp = 64.dp,
|
||||
val dp4: Dp = 4.dp,
|
||||
val dp8: Dp = 8.dp,
|
||||
val dp16: Dp = 16.dp,
|
||||
val dp32: Dp = 32.dp,
|
||||
val dp64: Dp = 64.dp,
|
||||
val dialog: Dialog = Dialog(),
|
||||
val thumbnail: BookThumbnail = BookThumbnail(),
|
||||
val detail: BookDetail = BookDetail(),
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
<string name="error_generic">Oops!</string>
|
||||
<string name="error_authentication">Oops, connection failed!</string>
|
||||
<string name="error_book">Oops! library download failed!</string>
|
||||
<string name="error_offline">Vous êtes hors ligne.</string>
|
||||
|
||||
<string name="loading_authentication">Entering the Imperial Library of Trantor.</string>
|
||||
<string name="loading_book">Downloading the Imperial Library of Trantor.</string>
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
<string name="error_generic">Oops!</string>
|
||||
<string name="error_authentication">Oops, connection failed!</string>
|
||||
<string name="error_book">Oops! library download failed!</string>
|
||||
<string name="error_offline">You are offline.</string>
|
||||
|
||||
<string name="loading_authentication">Entering the Imperial Library of Trantor.</string>
|
||||
<string name="loading_book">Downloading the Imperial Library of Trantor.</string>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue