Change to Toolbar layout
This commit is contained in:
parent
7af9eb8acc
commit
db7d6d8ab8
25 changed files with 544 additions and 157 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
package com.pixelized.biblib.model.user
|
package com.pixelized.biblib.model.user
|
||||||
|
|
||||||
import java.util.Date
|
import java.util.*
|
||||||
|
|
||||||
data class User(
|
data class User(
|
||||||
val id: String,
|
val id: String,
|
||||||
|
|
@ -10,4 +10,11 @@ data class User(
|
||||||
val firstname: String?,
|
val firstname: String?,
|
||||||
val lastname: String?,
|
val lastname: String?,
|
||||||
val amazonEmails: List<String>,
|
val amazonEmails: List<String>,
|
||||||
|
val downloadedBooks: List<DownloadedBooks>,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class DownloadedBooks(
|
||||||
|
val bookId: Int,
|
||||||
|
val date: Date,
|
||||||
|
val format: String,
|
||||||
)
|
)
|
||||||
|
|
@ -4,7 +4,8 @@ import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import androidx.room.Room
|
import androidx.room.Room
|
||||||
import com.pixelized.biblib.database.BibLibDatabase
|
import com.pixelized.biblib.database.BibLibDatabase
|
||||||
import com.pixelized.biblib.utils.CoverCache
|
import com.pixelized.biblib.utils.cache.BookCoverCache
|
||||||
|
import com.pixelized.biblib.utils.cache.ImageCache
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
|
|
@ -31,10 +32,22 @@ class PersistenceModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideBitmapCache(
|
fun provideBookCoverCache(
|
||||||
|
imageCache: ImageCache,
|
||||||
|
): BookCoverCache {
|
||||||
|
return BookCoverCache(
|
||||||
|
cache = imageCache
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideImageCache(
|
||||||
@ApplicationContext context: Context,
|
@ApplicationContext context: Context,
|
||||||
): CoverCache {
|
): ImageCache {
|
||||||
return CoverCache(context)
|
return ImageCache(
|
||||||
|
context = context
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|
|
||||||
|
|
@ -11,5 +11,6 @@ interface IBibLibClient {
|
||||||
const val THUMBNAIL_URL = "$BASE_URL/api/book/thumbnail"
|
const val THUMBNAIL_URL = "$BASE_URL/api/book/thumbnail"
|
||||||
const val COVER_URL = "$BASE_URL/api/book/cover"
|
const val COVER_URL = "$BASE_URL/api/book/cover"
|
||||||
const val REGISTER_URL = "$BASE_URL/signup"
|
const val REGISTER_URL = "$BASE_URL/signup"
|
||||||
|
const val EDIT_PROFILE = "$BASE_URL/profile"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.pixelized.biblib.network.factory
|
package com.pixelized.biblib.network.factory
|
||||||
|
|
||||||
|
import com.pixelized.biblib.model.user.DownloadedBooks
|
||||||
import com.pixelized.biblib.model.user.User
|
import com.pixelized.biblib.model.user.User
|
||||||
import com.pixelized.biblib.network.data.response.UserResponse
|
import com.pixelized.biblib.network.data.response.UserResponse
|
||||||
import com.pixelized.biblib.utils.exception.MandatoryFieldMissingException
|
import com.pixelized.biblib.utils.exception.MandatoryFieldMissingException
|
||||||
|
|
@ -23,6 +24,21 @@ class UserFactory {
|
||||||
val lastname = response.data?.local?.lastname
|
val lastname = response.data?.local?.lastname
|
||||||
val amazonEmails = response.data?.local?.amazonEmails
|
val amazonEmails = response.data?.local?.amazonEmails
|
||||||
|
|
||||||
|
val downloadedBooks = response.data?.history?.downloadedBooks?.mapNotNull { book ->
|
||||||
|
val bookId = book.id
|
||||||
|
val date = book.date?.let { parser.parse(it) }
|
||||||
|
val format = book.data?.data_format
|
||||||
|
if (bookId != null && date != null && format != null) {
|
||||||
|
DownloadedBooks(
|
||||||
|
bookId = bookId,
|
||||||
|
date = date,
|
||||||
|
format = format,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return User(
|
return User(
|
||||||
id = id ?: throw error("id"),
|
id = id ?: throw error("id"),
|
||||||
created = created ?: throw error("created"),
|
created = created ?: throw error("created"),
|
||||||
|
|
@ -31,7 +47,7 @@ class UserFactory {
|
||||||
firstname = firstname,
|
firstname = firstname,
|
||||||
lastname = lastname,
|
lastname = lastname,
|
||||||
amazonEmails = amazonEmails ?: listOf(),
|
amazonEmails = amazonEmails ?: listOf(),
|
||||||
|
downloadedBooks = downloadedBooks ?: listOf()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ package com.pixelized.biblib.repository.googleSignIn
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import com.google.android.gms.auth.api.signin.GoogleSignIn
|
import com.google.android.gms.auth.api.signin.GoogleSignIn
|
||||||
|
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
|
||||||
import com.google.android.gms.auth.api.signin.GoogleSignInClient
|
import com.google.android.gms.auth.api.signin.GoogleSignInClient
|
||||||
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
|
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
|
||||||
import com.pixelized.biblib.R
|
import com.pixelized.biblib.R
|
||||||
|
|
@ -19,6 +20,10 @@ class GoogleSingInRepository(application: Application) : IGoogleSingInRepository
|
||||||
GoogleSignIn.getClient(application, option)
|
GoogleSignIn.getClient(application, option)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val account: GoogleSignInAccount? by lazy {
|
||||||
|
GoogleSignIn.getLastSignedInAccount(application)
|
||||||
|
}
|
||||||
|
|
||||||
override val lastGoogleToken: String? by lazy {
|
override val lastGoogleToken: String? by lazy {
|
||||||
GoogleSignIn.getLastSignedInAccount(application)?.idToken
|
GoogleSignIn.getLastSignedInAccount(application)?.idToken
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
package com.pixelized.biblib.repository.googleSignIn
|
package com.pixelized.biblib.repository.googleSignIn
|
||||||
|
|
||||||
|
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
|
||||||
import com.google.android.gms.auth.api.signin.GoogleSignInClient
|
import com.google.android.gms.auth.api.signin.GoogleSignInClient
|
||||||
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
|
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
|
||||||
|
|
||||||
interface IGoogleSingInRepository {
|
interface IGoogleSingInRepository {
|
||||||
val option: GoogleSignInOptions
|
val option: GoogleSignInOptions
|
||||||
val client: GoogleSignInClient
|
val client: GoogleSignInClient
|
||||||
|
val account: GoogleSignInAccount?
|
||||||
val lastGoogleToken: String?
|
val lastGoogleToken: String?
|
||||||
}
|
}
|
||||||
|
|
@ -15,6 +15,11 @@ sealed class Screen(
|
||||||
route = "home"
|
route = "home"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
object Profile: Screen(
|
||||||
|
route = "profile"
|
||||||
|
)
|
||||||
|
|
||||||
|
@Deprecated(message = "User BottomDetailState instead.")
|
||||||
class BookDetail(id: Int) : Screen(
|
class BookDetail(id: Int) : Screen(
|
||||||
route = "$ROUTE/$id"
|
route = "$ROUTE/$id"
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import com.pixelized.biblib.ui.screen.authentication.AuthenticationScreen
|
import com.pixelized.biblib.ui.screen.authentication.AuthenticationScreen
|
||||||
import com.pixelized.biblib.ui.screen.home.HomeScreen
|
import com.pixelized.biblib.ui.screen.home.HomeScreen
|
||||||
|
import com.pixelized.biblib.ui.screen.profile.ProfileScreen
|
||||||
|
|
||||||
val LocalScreenNavHostController = compositionLocalOf<NavHostController> {
|
val LocalScreenNavHostController = compositionLocalOf<NavHostController> {
|
||||||
error("LocalFullScreenNavHostController is not ready yet.")
|
error("LocalFullScreenNavHostController is not ready yet.")
|
||||||
|
|
@ -35,6 +36,9 @@ fun ScreenNavHost(
|
||||||
composable(Screen.Home.route) {
|
composable(Screen.Home.route) {
|
||||||
HomeScreen()
|
HomeScreen()
|
||||||
}
|
}
|
||||||
|
composable(Screen.Profile.route) {
|
||||||
|
ProfileScreen()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -44,4 +48,8 @@ fun NavHostController.navigateToHome() {
|
||||||
launchSingleTop = true
|
launchSingleTop = true
|
||||||
popUpTo(0) { inclusive = true }
|
popUpTo(0) { inclusive = true }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun NavHostController.navigateToProfile() {
|
||||||
|
navigate(Screen.Profile.route)
|
||||||
}
|
}
|
||||||
|
|
@ -18,7 +18,7 @@ import com.pixelized.biblib.ui.navigation.screen.Screen
|
||||||
import com.pixelized.biblib.ui.screen.home.common.uio.BookUio
|
import com.pixelized.biblib.ui.screen.home.common.uio.BookUio
|
||||||
import com.pixelized.biblib.ui.screen.home.common.uio.CoverUio
|
import com.pixelized.biblib.ui.screen.home.common.uio.CoverUio
|
||||||
import com.pixelized.biblib.ui.screen.home.common.viewModel.ACoverViewModel
|
import com.pixelized.biblib.ui.screen.home.common.viewModel.ACoverViewModel
|
||||||
import com.pixelized.biblib.utils.CoverCache
|
import com.pixelized.biblib.utils.cache.BookCoverCache
|
||||||
import com.pixelized.biblib.utils.extention.capitalize
|
import com.pixelized.biblib.utils.extention.capitalize
|
||||||
import com.pixelized.biblib.utils.extention.context
|
import com.pixelized.biblib.utils.extention.context
|
||||||
import com.pixelized.biblib.utils.extention.shortDate
|
import com.pixelized.biblib.utils.extention.shortDate
|
||||||
|
|
@ -32,10 +32,10 @@ import javax.inject.Inject
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class BookDetailViewModel @Inject constructor(
|
class BookDetailViewModel @Inject constructor(
|
||||||
application: Application,
|
application: Application,
|
||||||
coverCache: CoverCache,
|
bookCoverCache: BookCoverCache,
|
||||||
savedStateHandle: SavedStateHandle,
|
savedStateHandle: SavedStateHandle,
|
||||||
private val client: IBibLibClient,
|
private val client: IBibLibClient,
|
||||||
) : ACoverViewModel(application, coverCache) {
|
) : ACoverViewModel(application, bookCoverCache) {
|
||||||
|
|
||||||
private val _state = mutableStateOf<StateUio<BookUio>>(StateUio.Progress())
|
private val _state = mutableStateOf<StateUio<BookUio>>(StateUio.Progress())
|
||||||
val state: State<StateUio<BookUio>> get() = _state
|
val state: State<StateUio<BookUio>> get() = _state
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,11 @@ import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.*
|
import androidx.compose.material.Button
|
||||||
|
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.Icons
|
||||||
import androidx.compose.material.icons.filled.Close
|
|
||||||
import androidx.compose.material.icons.filled.Download
|
import androidx.compose.material.icons.filled.Download
|
||||||
import androidx.compose.material.icons.filled.Send
|
import androidx.compose.material.icons.filled.Send
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
@ -39,6 +41,7 @@ import com.pixelized.biblib.ui.screen.home.common.uio.BookUio
|
||||||
import com.pixelized.biblib.ui.screen.home.common.uio.CoverUio
|
import com.pixelized.biblib.ui.screen.home.common.uio.CoverUio
|
||||||
import com.pixelized.biblib.ui.theme.BibLibTheme
|
import com.pixelized.biblib.ui.theme.BibLibTheme
|
||||||
import com.pixelized.biblib.utils.extention.bibLib
|
import com.pixelized.biblib.utils.extention.bibLib
|
||||||
|
import com.pixelized.biblib.utils.extention.todo
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DetailScreen(
|
fun DetailScreen(
|
||||||
|
|
@ -54,9 +57,6 @@ fun DetailScreen(
|
||||||
onClose = {
|
onClose = {
|
||||||
bottomDetailState.collapse()
|
bottomDetailState.collapse()
|
||||||
},
|
},
|
||||||
onMobi = {},
|
|
||||||
onEpub = {},
|
|
||||||
onSend = {},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -67,10 +67,10 @@ fun DetailScreen(
|
||||||
private fun DetailScreenContent(
|
private fun DetailScreenContent(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
book: BookUio,
|
book: BookUio,
|
||||||
onClose: () -> Unit,
|
onClose: () -> Unit = todo(),
|
||||||
onMobi: () -> Unit,
|
onMobi: () -> Unit = todo(),
|
||||||
onEpub: () -> Unit,
|
onEpub: () -> Unit = todo(),
|
||||||
onSend: () -> Unit,
|
onSend: () -> Unit = todo(),
|
||||||
) {
|
) {
|
||||||
AnimatedDelayer(delay = Delay(300)) {
|
AnimatedDelayer(delay = Delay(300)) {
|
||||||
Column(
|
Column(
|
||||||
|
|
@ -291,12 +291,6 @@ private fun DetailScreenContentPreview() {
|
||||||
cover = cover,
|
cover = cover,
|
||||||
)
|
)
|
||||||
BibLibTheme {
|
BibLibTheme {
|
||||||
DetailScreenContent(
|
DetailScreenContent(book = book)
|
||||||
book = book,
|
|
||||||
onClose = {},
|
|
||||||
onMobi = {},
|
|
||||||
onEpub = {},
|
|
||||||
onSend = {},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,115 +1,40 @@
|
||||||
package com.pixelized.biblib.ui.screen.home
|
package com.pixelized.biblib.ui.screen.home
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.RowScope
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.systemBarsPadding
|
import androidx.compose.foundation.layout.systemBarsPadding
|
||||||
import androidx.compose.material.*
|
import androidx.compose.material.ExperimentalMaterialApi
|
||||||
|
import androidx.compose.material.Scaffold
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.navigation.NavDestination.Companion.hierarchy
|
|
||||||
import androidx.navigation.NavGraph.Companion.findStartDestination
|
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import com.pixelized.biblib.R
|
|
||||||
import com.pixelized.biblib.ui.navigation.BottomDetailScaffold
|
import com.pixelized.biblib.ui.navigation.BottomDetailScaffold
|
||||||
import com.pixelized.biblib.ui.navigation.page.Page
|
|
||||||
import com.pixelized.biblib.ui.navigation.page.PageNavHost
|
import com.pixelized.biblib.ui.navigation.page.PageNavHost
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterialApi::class)
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeScreen() {
|
fun HomeScreen(
|
||||||
val pageNavHostController = rememberNavController()
|
accountViewModel: HomeViewModel = hiltViewModel(),
|
||||||
|
navHostController: NavHostController = rememberNavController(),
|
||||||
|
) {
|
||||||
|
|
||||||
BottomDetailScaffold {
|
BottomDetailScaffold {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.systemBarsPadding(),
|
modifier = Modifier.systemBarsPadding(),
|
||||||
topBar = {
|
topBar = {
|
||||||
Column {
|
Column {
|
||||||
TopAppBar(
|
HomeSearchBar()
|
||||||
title = {
|
HomeTarBar(homePageNavController = navHostController)
|
||||||
Text(text = stringResource(id = R.string.app_name))
|
|
||||||
}
|
|
||||||
)
|
|
||||||
BottomBarNavigation(
|
|
||||||
homePageNavController = pageNavHostController
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
content = {
|
content = {
|
||||||
PageNavHost(
|
PageNavHost(
|
||||||
modifier = Modifier.padding(it),
|
modifier = Modifier.padding(it),
|
||||||
navHostController = pageNavHostController,
|
navHostController = navHostController,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun BottomBarNavigation(
|
|
||||||
homePageNavController: NavHostController,
|
|
||||||
pages: List<Page> = Page.all
|
|
||||||
) {
|
|
||||||
BottomNavigation(
|
|
||||||
backgroundColor = MaterialTheme.colors.background,
|
|
||||||
) {
|
|
||||||
val navBackStackEntry by homePageNavController.currentBackStackEntryAsState()
|
|
||||||
val currentDestination = navBackStackEntry?.destination
|
|
||||||
|
|
||||||
pages.forEach { page ->
|
|
||||||
val selected = currentDestination?.hierarchy?.any { it.route == page.route }
|
|
||||||
BottomNavigationIcon(
|
|
||||||
page = page,
|
|
||||||
selected = selected ?: false,
|
|
||||||
onClick = {
|
|
||||||
homePageNavController.navigate(page.route) {
|
|
||||||
// Pop up to the start destination of the graph to avoid building up a
|
|
||||||
// large stack of destinations on the back stack as users select items
|
|
||||||
popUpTo(homePageNavController.graph.findStartDestination().id) {
|
|
||||||
saveState = true
|
|
||||||
}
|
|
||||||
// Avoid multiple copies of the same destination when re-selecting the same item
|
|
||||||
launchSingleTop = true
|
|
||||||
// Restore state when re-selecting a previously selected item
|
|
||||||
restoreState = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun RowScope.BottomNavigationIcon(
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
page: Page,
|
|
||||||
selected: Boolean,
|
|
||||||
selectedColor: Color = MaterialTheme.colors.primary,
|
|
||||||
defaultColor: Color = MaterialTheme.colors.onSurface,
|
|
||||||
onClick: () -> Unit = {},
|
|
||||||
) {
|
|
||||||
BottomNavigationItem(
|
|
||||||
modifier = modifier,
|
|
||||||
icon = {
|
|
||||||
Icon(
|
|
||||||
imageVector = page.icon,
|
|
||||||
contentDescription = null,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
label = {
|
|
||||||
Text(
|
|
||||||
style = MaterialTheme.typography.caption,
|
|
||||||
text = stringResource(id = page.label),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
selectedContentColor = selectedColor,
|
|
||||||
unselectedContentColor = defaultColor,
|
|
||||||
selected = selected,
|
|
||||||
onClick = onClick,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
package com.pixelized.biblib.ui.screen.home
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import androidx.compose.foundation.layout.RowScope
|
||||||
|
import androidx.compose.material.*
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.navigation.NavDestination.Companion.hierarchy
|
||||||
|
import androidx.navigation.NavGraph.Companion.findStartDestination
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
|
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||||
|
import androidx.navigation.compose.rememberNavController
|
||||||
|
import com.pixelized.biblib.ui.navigation.page.Page
|
||||||
|
import com.pixelized.biblib.ui.theme.BibLibTheme
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun HomeTarBar(
|
||||||
|
homePageNavController: NavHostController = rememberNavController(),
|
||||||
|
pages: List<Page> = Page.all
|
||||||
|
) {
|
||||||
|
BottomNavigation(
|
||||||
|
backgroundColor = MaterialTheme.colors.background,
|
||||||
|
) {
|
||||||
|
val navBackStackEntry by homePageNavController.currentBackStackEntryAsState()
|
||||||
|
val currentDestination = navBackStackEntry?.destination
|
||||||
|
|
||||||
|
pages.forEach { page ->
|
||||||
|
val selected = currentDestination?.hierarchy?.any { it.route == page.route }
|
||||||
|
BottomNavigationIcon(
|
||||||
|
page = page,
|
||||||
|
selected = selected ?: false,
|
||||||
|
onClick = {
|
||||||
|
homePageNavController.navigate(page.route) {
|
||||||
|
// Pop up to the start destination of the graph to avoid building up a
|
||||||
|
// large stack of destinations on the back stack as users select items
|
||||||
|
popUpTo(homePageNavController.graph.findStartDestination().id) {
|
||||||
|
saveState = true
|
||||||
|
}
|
||||||
|
// Avoid multiple copies of the same destination when re-selecting the same item
|
||||||
|
launchSingleTop = true
|
||||||
|
// Restore state when re-selecting a previously selected item
|
||||||
|
restoreState = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun RowScope.BottomNavigationIcon(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
page: Page,
|
||||||
|
selected: Boolean,
|
||||||
|
selectedColor: Color = MaterialTheme.colors.primary,
|
||||||
|
defaultColor: Color = MaterialTheme.colors.onSurface,
|
||||||
|
onClick: () -> Unit = {},
|
||||||
|
) {
|
||||||
|
BottomNavigationItem(
|
||||||
|
modifier = modifier,
|
||||||
|
icon = {
|
||||||
|
Icon(
|
||||||
|
imageVector = page.icon,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
label = {
|
||||||
|
Text(
|
||||||
|
style = MaterialTheme.typography.caption,
|
||||||
|
text = stringResource(id = page.label),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
selectedContentColor = selectedColor,
|
||||||
|
unselectedContentColor = defaultColor,
|
||||||
|
selected = selected,
|
||||||
|
onClick = onClick,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||||
|
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
private fun BottomBarNavigationPreview() {
|
||||||
|
BibLibTheme {
|
||||||
|
HomeTarBar()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
package com.pixelized.biblib.ui.screen.home
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import com.pixelized.biblib.R
|
||||||
|
import com.pixelized.biblib.repository.googleSignIn.IGoogleSingInRepository
|
||||||
|
import com.pixelized.biblib.ui.screen.home.common.uio.CoverUio
|
||||||
|
import com.pixelized.biblib.ui.screen.home.common.viewModel.ACoverViewModel
|
||||||
|
import com.pixelized.biblib.utils.cache.BookCoverCache
|
||||||
|
import com.pixelized.biblib.utils.extention.context
|
||||||
|
import com.pixelized.biblib.utils.painterResource
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import java.net.URL
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class HomeViewModel @Inject constructor(
|
||||||
|
application: Application,
|
||||||
|
cacheBook: BookCoverCache,
|
||||||
|
private val account: IGoogleSingInRepository,
|
||||||
|
) : ACoverViewModel(application, cacheBook) {
|
||||||
|
|
||||||
|
private val _avatar = mutableStateOf(
|
||||||
|
painterResource(context, R.drawable.ic_launcher_foreground)
|
||||||
|
)
|
||||||
|
val avatar by _avatar
|
||||||
|
|
||||||
|
init {
|
||||||
|
updateAvatar()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateAvatar() {
|
||||||
|
account.account?.photoUrl?.let {
|
||||||
|
_avatar.value = cover(
|
||||||
|
placeHolder = CoverUio(
|
||||||
|
type = CoverUio.Type.PLACE_HOLDER,
|
||||||
|
painter = painterResource(context, R.drawable.ic_launcher_foreground),
|
||||||
|
),
|
||||||
|
type = CoverUio.Type.DETAIL,
|
||||||
|
contentScale = ContentScale.None,
|
||||||
|
url = URL("$it"),
|
||||||
|
).value.painter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
package com.pixelized.biblib.ui.screen.home.common.uio
|
package com.pixelized.biblib.ui.screen.home.common.uio
|
||||||
|
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import com.pixelized.biblib.network.client.IBibLibClient.Companion.COVER_URL
|
|
||||||
import java.net.URL
|
|
||||||
|
|
||||||
data class BookUio(
|
data class BookUio(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
|
|
|
||||||
|
|
@ -6,25 +6,25 @@ import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.pixelized.biblib.ui.screen.home.common.uio.CoverUio
|
import com.pixelized.biblib.ui.screen.home.common.uio.CoverUio
|
||||||
import com.pixelized.biblib.utils.CoverCache
|
import com.pixelized.biblib.utils.cache.BookCoverCache
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
|
|
||||||
abstract class ACoverViewModel(
|
abstract class ACoverViewModel(
|
||||||
application: Application,
|
application: Application,
|
||||||
private val cache: CoverCache,
|
private val cacheBook: BookCoverCache,
|
||||||
) : AndroidViewModel(application) {
|
) : AndroidViewModel(application) {
|
||||||
|
|
||||||
fun cover(
|
fun cover(
|
||||||
cache: CoverCache = this.cache,
|
cacheBook: BookCoverCache = this.cacheBook,
|
||||||
coroutineScope: CoroutineScope = viewModelScope,
|
coroutineScope: CoroutineScope = viewModelScope,
|
||||||
placeHolder: CoverUio,
|
placeHolder: CoverUio,
|
||||||
type: CoverUio.Type,
|
type: CoverUio.Type,
|
||||||
contentScale: ContentScale = ContentScale.FillBounds,
|
contentScale: ContentScale = ContentScale.FillBounds,
|
||||||
tint: ColorFilter? = null,
|
tint: ColorFilter? = null,
|
||||||
url: URL,
|
url: URL,
|
||||||
) = cache.cover(
|
) = cacheBook.cover(
|
||||||
placeHolder = placeHolder,
|
placeHolder = placeHolder,
|
||||||
coroutineScope = coroutineScope,
|
coroutineScope = coroutineScope,
|
||||||
type = type,
|
type = type,
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import com.pixelized.biblib.repository.book.IBookRepository
|
||||||
import com.pixelized.biblib.ui.screen.home.common.uio.BookThumbnailUio
|
import com.pixelized.biblib.ui.screen.home.common.uio.BookThumbnailUio
|
||||||
import com.pixelized.biblib.ui.screen.home.common.uio.CoverUio
|
import com.pixelized.biblib.ui.screen.home.common.uio.CoverUio
|
||||||
import com.pixelized.biblib.ui.screen.home.common.viewModel.ACoverViewModel
|
import com.pixelized.biblib.ui.screen.home.common.viewModel.ACoverViewModel
|
||||||
import com.pixelized.biblib.utils.CoverCache
|
import com.pixelized.biblib.utils.cache.BookCoverCache
|
||||||
import com.pixelized.biblib.utils.extention.context
|
import com.pixelized.biblib.utils.extention.context
|
||||||
import com.pixelized.biblib.utils.extention.longDate
|
import com.pixelized.biblib.utils.extention.longDate
|
||||||
import com.pixelized.biblib.utils.painterResource
|
import com.pixelized.biblib.utils.painterResource
|
||||||
|
|
@ -27,8 +27,8 @@ import javax.inject.Inject
|
||||||
class BooksViewModel @Inject constructor(
|
class BooksViewModel @Inject constructor(
|
||||||
application: Application,
|
application: Application,
|
||||||
bookRepository: IBookRepository,
|
bookRepository: IBookRepository,
|
||||||
cache: CoverCache,
|
cacheBook: BookCoverCache,
|
||||||
) : ACoverViewModel(application, cache) {
|
) : ACoverViewModel(application, cacheBook) {
|
||||||
|
|
||||||
private val booksSource = Pager(
|
private val booksSource = Pager(
|
||||||
config = PagingConfig(pageSize = PAGING_SIZE),
|
config = PagingConfig(pageSize = PAGING_SIZE),
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import com.pixelized.biblib.repository.book.IBookRepository
|
||||||
import com.pixelized.biblib.ui.screen.home.common.uio.BookThumbnailUio
|
import com.pixelized.biblib.ui.screen.home.common.uio.BookThumbnailUio
|
||||||
import com.pixelized.biblib.ui.screen.home.common.uio.CoverUio
|
import com.pixelized.biblib.ui.screen.home.common.uio.CoverUio
|
||||||
import com.pixelized.biblib.ui.screen.home.common.viewModel.ACoverViewModel
|
import com.pixelized.biblib.ui.screen.home.common.viewModel.ACoverViewModel
|
||||||
import com.pixelized.biblib.utils.CoverCache
|
import com.pixelized.biblib.utils.cache.BookCoverCache
|
||||||
import com.pixelized.biblib.utils.extention.context
|
import com.pixelized.biblib.utils.extention.context
|
||||||
import com.pixelized.biblib.utils.extention.longDate
|
import com.pixelized.biblib.utils.extention.longDate
|
||||||
import com.pixelized.biblib.utils.painterResource
|
import com.pixelized.biblib.utils.painterResource
|
||||||
|
|
@ -29,8 +29,8 @@ import javax.inject.Inject
|
||||||
class NewsBookViewModel @Inject constructor(
|
class NewsBookViewModel @Inject constructor(
|
||||||
application: Application,
|
application: Application,
|
||||||
bookRepository: IBookRepository,
|
bookRepository: IBookRepository,
|
||||||
cache: CoverCache,
|
cacheBook: BookCoverCache,
|
||||||
) : ACoverViewModel(application, cache) {
|
) : ACoverViewModel(application, cacheBook) {
|
||||||
|
|
||||||
private val newsSource: Flow<PagingData<BookThumbnailUio>> = Pager(
|
private val newsSource: Flow<PagingData<BookThumbnailUio>> = Pager(
|
||||||
config = PagingConfig(pageSize = PAGING_SIZE),
|
config = PagingConfig(pageSize = PAGING_SIZE),
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ class LauncherViewModel @Inject constructor(
|
||||||
loadAllBooks()
|
loadAllBooks()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//
|
// Update loading state.
|
||||||
_isLoading.value = false
|
_isLoading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
package com.pixelized.biblib.ui.screen.profile
|
||||||
|
|
||||||
|
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.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.rememberUpdatedState
|
||||||
|
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
|
||||||
|
|
||||||
|
@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)
|
||||||
|
}
|
||||||
|
|
||||||
|
FloatingActionButton(
|
||||||
|
onClick = {
|
||||||
|
context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(IBibLibClient.EDIT_PROFILE)))
|
||||||
|
},
|
||||||
|
content = { },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ProfileScreenContent(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
user: ProfileViewModel.UserUio,
|
||||||
|
) {
|
||||||
|
Column(modifier = modifier) {
|
||||||
|
Text(
|
||||||
|
style = MaterialTheme.typography.h5,
|
||||||
|
color = MaterialTheme.colors.onSurface,
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
user.amazonEmails.forEach {
|
||||||
|
Text(
|
||||||
|
style = MaterialTheme.typography.body1,
|
||||||
|
color = MaterialTheme.colors.onSurface,
|
||||||
|
text = it,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_NO)
|
||||||
|
@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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
package com.pixelized.biblib.ui.screen.profile
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.pixelized.biblib.model.user.User
|
||||||
|
import com.pixelized.biblib.network.client.IBibLibClient
|
||||||
|
import com.pixelized.biblib.network.factory.UserFactory
|
||||||
|
import com.pixelized.biblib.ui.composable.StateUio
|
||||||
|
import com.pixelized.biblib.ui.screen.home.common.viewModel.ACoverViewModel
|
||||||
|
import com.pixelized.biblib.utils.cache.BookCoverCache
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class ProfileViewModel @Inject constructor(
|
||||||
|
application: Application,
|
||||||
|
cacheBook: BookCoverCache,
|
||||||
|
private val client: IBibLibClient,
|
||||||
|
) : ACoverViewModel(application, cacheBook) {
|
||||||
|
|
||||||
|
private val _user = mutableStateOf<StateUio<UserUio>>(StateUio.Progress())
|
||||||
|
val user by _user
|
||||||
|
|
||||||
|
init {
|
||||||
|
updateUser()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateUser() {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
val factory = UserFactory()
|
||||||
|
val response = client.service.user()
|
||||||
|
val user = factory.fromUserResponseToUser(response)
|
||||||
|
_user.value = StateUio.Success(user.toUio())
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
Log.e("AccountViewModel", exception.message, exception)
|
||||||
|
_user.value = StateUio.Failure(exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class UserUio(
|
||||||
|
val username: String,
|
||||||
|
val firstname: String?,
|
||||||
|
val lastname: String?,
|
||||||
|
val amazonEmails: List<String>,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun User.toUio() = ProfileViewModel.UserUio(
|
||||||
|
username = username,
|
||||||
|
firstname = firstname,
|
||||||
|
lastname = lastname,
|
||||||
|
amazonEmails = amazonEmails.toList(),
|
||||||
|
)
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
package com.pixelized.biblib.utils
|
package com.pixelized.biblib.utils.cache
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
|
|
@ -14,16 +12,13 @@ import androidx.compose.ui.layout.ContentScale
|
||||||
import com.pixelized.biblib.ui.screen.home.common.uio.CoverUio
|
import com.pixelized.biblib.ui.screen.home.common.uio.CoverUio
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.io.File
|
|
||||||
import java.io.IOException
|
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class CoverCache @Inject constructor(context: Context) {
|
class BookCoverCache @Inject constructor(
|
||||||
private var cache: File? = context.cacheDir
|
private val cache: ImageCache
|
||||||
|
) {
|
||||||
fun cover(
|
fun cover(
|
||||||
coroutineScope: CoroutineScope,
|
coroutineScope: CoroutineScope,
|
||||||
placeHolder: CoverUio,
|
placeHolder: CoverUio,
|
||||||
|
|
@ -33,7 +28,7 @@ class CoverCache @Inject constructor(context: Context) {
|
||||||
tint: ColorFilter?,
|
tint: ColorFilter?,
|
||||||
): State<CoverUio> {
|
): State<CoverUio> {
|
||||||
// read the cache a convert it to a UIO.
|
// read the cache a convert it to a UIO.
|
||||||
val cache = readFromDisk(url)?.let {
|
val cache = cache.readFromDisk(url)?.let {
|
||||||
CoverUio(
|
CoverUio(
|
||||||
type = type,
|
type = type,
|
||||||
contentScale = contentScale,
|
contentScale = contentScale,
|
||||||
|
|
@ -65,7 +60,7 @@ class CoverCache @Inject constructor(context: Context) {
|
||||||
try {
|
try {
|
||||||
val bitmap = BitmapFactory.decodeStream(url.openStream())
|
val bitmap = BitmapFactory.decodeStream(url.openStream())
|
||||||
val painter = BitmapPainter(bitmap.asImageBitmap())
|
val painter = BitmapPainter(bitmap.asImageBitmap())
|
||||||
writeToDisk(url, bitmap)
|
cache.writeToDisk(url, bitmap)
|
||||||
value = CoverUio(
|
value = CoverUio(
|
||||||
type = type,
|
type = type,
|
||||||
contentScale = contentScale,
|
contentScale = contentScale,
|
||||||
|
|
@ -76,26 +71,4 @@ class CoverCache @Inject constructor(context: Context) {
|
||||||
Log.w("CoverCache", "Fail to download: {$url}", exception)
|
Log.w("CoverCache", "Fail to download: {$url}", exception)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun file(url: URL): File = File(cache?.absolutePath + url.file)
|
|
||||||
|
|
||||||
private fun writeToDisk(url: URL, bitmap: Bitmap) {
|
|
||||||
val file = file(url)
|
|
||||||
try {
|
|
||||||
file.mkdirs()
|
|
||||||
file.delete()
|
|
||||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, file.outputStream())
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e("BitmapCache", "bitmap?.compress() FAILED !", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readFromDisk(url: URL): Bitmap? {
|
|
||||||
val file = file(url)
|
|
||||||
return try {
|
|
||||||
if (file.exists()) BitmapFactory.decodeStream(file.inputStream()) else null
|
|
||||||
} catch (e: IOException) {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
37
app/src/main/java/com/pixelized/biblib/utils/cache/ImageCache.kt
vendored
Normal file
37
app/src/main/java/com/pixelized/biblib/utils/cache/ImageCache.kt
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
package com.pixelized.biblib.utils.cache
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.util.Log
|
||||||
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
import java.net.URL
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class ImageCache @Inject constructor(context: Context) {
|
||||||
|
|
||||||
|
private var cache: File? = context.cacheDir
|
||||||
|
|
||||||
|
private fun file(url: URL): File = File(cache?.absolutePath + url.file)
|
||||||
|
|
||||||
|
fun writeToDisk(url: URL, bitmap: Bitmap) {
|
||||||
|
val file = file(url)
|
||||||
|
try {
|
||||||
|
file.mkdirs()
|
||||||
|
file.delete()
|
||||||
|
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, file.outputStream())
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("BitmapCache", "bitmap?.compress() FAILED !", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readFromDisk(url: URL): Bitmap? {
|
||||||
|
val file = file(url)
|
||||||
|
return try {
|
||||||
|
if (file.exists()) BitmapFactory.decodeStream(file.inputStream()) else null
|
||||||
|
} catch (e: IOException) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,18 @@ package com.pixelized.biblib.utils.extention
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
|
||||||
fun Context.showToast(message: String, duration: Int = Toast.LENGTH_SHORT) =
|
fun Context.showToast(message: String, duration: Int = Toast.LENGTH_SHORT) =
|
||||||
Toast.makeText(this, message, duration).show()
|
Toast.makeText(this, message, duration).show()
|
||||||
|
|
||||||
|
fun Context.notImplemented() {
|
||||||
|
showToast("Not implemented yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun todo(): () -> Unit {
|
||||||
|
val context = LocalContext.current
|
||||||
|
return { context.notImplemented() }
|
||||||
|
}
|
||||||
|
|
@ -12,5 +12,6 @@ class UserMock {
|
||||||
firstname = "R. Daneel",
|
firstname = "R. Daneel",
|
||||||
lastname = "Olivaw",
|
lastname = "Olivaw",
|
||||||
amazonEmails = listOf("r.daneel.olivaw@biblib.com"),
|
amazonEmails = listOf("r.daneel.olivaw@biblib.com"),
|
||||||
|
downloadedBooks = listOf(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue