Login logic
This commit is contained in:
@@ -3,6 +3,7 @@ package com.ericampire.android.androidstudycase
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
import androidx.compose.material.ExperimentalMaterialApi
|
import androidx.compose.material.ExperimentalMaterialApi
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.material.Surface
|
import androidx.compose.material.Surface
|
||||||
@@ -14,10 +15,11 @@ import dagger.hilt.android.AndroidEntryPoint
|
|||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
|
||||||
@ExperimentalMaterialApi
|
@ExperimentalMaterialApi
|
||||||
@ExperimentalPagerApi
|
@ExperimentalPagerApi
|
||||||
@ExperimentalPermissionsApi
|
@ExperimentalPermissionsApi
|
||||||
|
@ExperimentalAnimationApi
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContent {
|
setContent {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package com.ericampire.android.androidstudycase.app
|
package com.ericampire.android.androidstudycase.app
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
import dagger.hilt.android.HiltAndroidApp
|
import dagger.hilt.android.HiltAndroidApp
|
||||||
|
|
||||||
@HiltAndroidApp
|
@HiltAndroidApp
|
||||||
@@ -8,4 +10,15 @@ class App : Application() {
|
|||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun restart(context: Context) {
|
||||||
|
context.packageManager.getLaunchIntentForPackage(context.packageName)?.apply {
|
||||||
|
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||||
|
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
||||||
|
addCategory(Intent.CATEGORY_HOME)
|
||||||
|
context.startActivity(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.ericampire.android.androidstudycase.app
|
package com.ericampire.android.androidstudycase.app
|
||||||
|
|
||||||
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
import androidx.compose.material.ExperimentalMaterialApi
|
import androidx.compose.material.ExperimentalMaterialApi
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.NavGraphBuilder
|
import androidx.navigation.NavGraphBuilder
|
||||||
@@ -11,6 +12,7 @@ import com.ericampire.android.androidstudycase.util.Destination
|
|||||||
import com.google.accompanist.pager.ExperimentalPagerApi
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||||
|
|
||||||
|
@ExperimentalAnimationApi
|
||||||
@ExperimentalMaterialApi
|
@ExperimentalMaterialApi
|
||||||
fun NavGraphBuilder.addHomeScreen(navController: NavController) {
|
fun NavGraphBuilder.addHomeScreen(navController: NavController) {
|
||||||
composable(Destination.Home.route) {
|
composable(Destination.Home.route) {
|
||||||
|
|||||||
+67
@@ -0,0 +1,67 @@
|
|||||||
|
package com.ericampire.android.androidstudycase.presentation.custom
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.material.*
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.rounded.Facebook
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
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.compose.ui.unit.dp
|
||||||
|
import com.ericampire.android.androidstudycase.R
|
||||||
|
import com.ericampire.android.androidstudycase.presentation.theme.AppColor
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CustomFacebookButton(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
onClick: () -> Unit
|
||||||
|
) {
|
||||||
|
Button(
|
||||||
|
enabled = enabled,
|
||||||
|
colors = ButtonDefaults.buttonColors(backgroundColor = AppColor.BlueFacebook),
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(50.dp),
|
||||||
|
onClick = onClick,
|
||||||
|
content = {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
content = {
|
||||||
|
Icon(
|
||||||
|
tint = Color.White,
|
||||||
|
imageVector = Icons.Rounded.Facebook,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
color = Color.White,
|
||||||
|
style = MaterialTheme.typography.h5,
|
||||||
|
text = stringResource(R.string.txt_connect_with_facebook)
|
||||||
|
)
|
||||||
|
Icon(
|
||||||
|
tint = Color.Transparent,
|
||||||
|
imageVector = Icons.Rounded.Facebook,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
fun CustomFacebookButtonPreview() {
|
||||||
|
CustomFacebookButton() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+67
@@ -0,0 +1,67 @@
|
|||||||
|
package com.ericampire.android.androidstudycase.presentation.custom
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.material.*
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.rounded.Facebook
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.ericampire.android.androidstudycase.R
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CustomGoogleButton(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
onClick: () -> Unit
|
||||||
|
) {
|
||||||
|
OutlinedButton(
|
||||||
|
enabled = enabled,
|
||||||
|
colors = ButtonDefaults.buttonColors(backgroundColor = Color.White),
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(50.dp),
|
||||||
|
onClick = onClick,
|
||||||
|
content = {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
content = {
|
||||||
|
Icon(
|
||||||
|
tint = Color.Unspecified,
|
||||||
|
painter = painterResource(id = R.drawable.ic_google),
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
style = MaterialTheme.typography.h5,
|
||||||
|
color = MaterialTheme.colors.surface,
|
||||||
|
text = stringResource(R.string.txt_connect_with_google)
|
||||||
|
)
|
||||||
|
Icon(
|
||||||
|
tint = Color.Transparent,
|
||||||
|
imageVector = Icons.Rounded.Facebook,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
fun CustomGoogleButtonPreview() {
|
||||||
|
CustomGoogleButton {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
@@ -140,10 +140,18 @@ fun ExploreScreen(
|
|||||||
}
|
}
|
||||||
is Success -> {
|
is Success -> {
|
||||||
val animations = it.invoke()
|
val animations = it.invoke()
|
||||||
|
if (animations.isEmpty()) {
|
||||||
|
// Todo: Empty instead of Loading View
|
||||||
|
LoadingAnimation(
|
||||||
|
waveColor = MaterialTheme.colors.primary.copy(alpha = 0.5f),
|
||||||
|
arcColor = MaterialTheme.colors.primaryVariant
|
||||||
|
)
|
||||||
|
} else {
|
||||||
HorizontalPager(state = pagerState) {
|
HorizontalPager(state = pagerState) {
|
||||||
ExploreContent(files = animations)
|
ExploreContent(files = animations)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
is Fail -> {
|
is Fail -> {
|
||||||
Timber.e(it.error.localizedMessage)
|
Timber.e(it.error.localizedMessage)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -2,4 +2,6 @@ package com.ericampire.android.androidstudycase.presentation.screen.home.busines
|
|||||||
|
|
||||||
sealed interface HomeAction {
|
sealed interface HomeAction {
|
||||||
object FetchData : HomeAction
|
object FetchData : HomeAction
|
||||||
|
object Login : HomeAction
|
||||||
|
object FetchCurrentUser : HomeAction
|
||||||
}
|
}
|
||||||
+30
-9
@@ -3,17 +3,17 @@ package com.ericampire.android.androidstudycase.presentation.screen.home.busines
|
|||||||
import com.airbnb.mvrx.MavericksViewModelFactory
|
import com.airbnb.mvrx.MavericksViewModelFactory
|
||||||
import com.ericampire.android.androidstudycase.app.hilt.AssistedViewModelFactory
|
import com.ericampire.android.androidstudycase.app.hilt.AssistedViewModelFactory
|
||||||
import com.ericampire.android.androidstudycase.app.hilt.hiltMavericksViewModelFactory
|
import com.ericampire.android.androidstudycase.app.hilt.hiltMavericksViewModelFactory
|
||||||
import com.ericampire.android.androidstudycase.domain.usecase.FindFeaturedAnimatorUseCase
|
import com.ericampire.android.androidstudycase.domain.usecase.*
|
||||||
import com.ericampire.android.androidstudycase.domain.usecase.FindFeaturedBlogUseCase
|
import com.ericampire.android.androidstudycase.util.PreviewData
|
||||||
import com.ericampire.android.androidstudycase.domain.usecase.FindFeaturedLottieFileUseCase
|
|
||||||
import com.ericampire.android.androidstudycase.domain.usecase.FindUsersUseCase
|
|
||||||
import com.ericampire.android.androidstudycase.util.data
|
import com.ericampire.android.androidstudycase.util.data
|
||||||
import com.ericampire.android.androidstudycase.util.mvi.BaseViewModel
|
import com.ericampire.android.androidstudycase.util.mvi.BaseViewModel
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class HomeViewModel @AssistedInject constructor(
|
class HomeViewModel @AssistedInject constructor(
|
||||||
@@ -21,7 +21,8 @@ class HomeViewModel @AssistedInject constructor(
|
|||||||
private val findFeaturedBlogUseCase: FindFeaturedBlogUseCase,
|
private val findFeaturedBlogUseCase: FindFeaturedBlogUseCase,
|
||||||
private val findFeaturedAnimatorUseCase: FindFeaturedAnimatorUseCase,
|
private val findFeaturedAnimatorUseCase: FindFeaturedAnimatorUseCase,
|
||||||
private val findUsersUseCase: FindUsersUseCase,
|
private val findUsersUseCase: FindUsersUseCase,
|
||||||
private val findFeaturedLottieFileUseCase: FindFeaturedLottieFileUseCase
|
private val findFeaturedLottieFileUseCase: FindFeaturedLottieFileUseCase,
|
||||||
|
private val saveUserUseCase: SaveUserUseCase
|
||||||
) : BaseViewModel<HomeViewState, HomeAction>(initialState) {
|
) : BaseViewModel<HomeViewState, HomeAction>(initialState) {
|
||||||
|
|
||||||
|
|
||||||
@@ -30,25 +31,45 @@ class HomeViewModel @AssistedInject constructor(
|
|||||||
pendingAction.collectLatest { action ->
|
pendingAction.collectLatest { action ->
|
||||||
when (action) {
|
when (action) {
|
||||||
HomeAction.FetchData -> fetchData()
|
HomeAction.FetchData -> fetchData()
|
||||||
|
HomeAction.Login -> login()
|
||||||
|
HomeAction.FetchCurrentUser -> fetchCurrentUser()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun login() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
suspend {
|
||||||
|
delay(2000)
|
||||||
|
saveUserUseCase(PreviewData.User.data.first()).data!!
|
||||||
|
}.execute {
|
||||||
|
copy(login = it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fetchCurrentUser() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
findUsersUseCase(Unit).map {
|
||||||
|
it.data?.firstOrNull()
|
||||||
|
}.execute {
|
||||||
|
copy(currentUser = it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun fetchData() {
|
private fun fetchData() {
|
||||||
val userFlow = findUsersUseCase(Unit)
|
|
||||||
val storiesFlow = findFeaturedBlogUseCase(Unit)
|
val storiesFlow = findFeaturedBlogUseCase(Unit)
|
||||||
val animatorsFlow = findFeaturedAnimatorUseCase(Unit)
|
val animatorsFlow = findFeaturedAnimatorUseCase(Unit)
|
||||||
val animationsFlow = findFeaturedLottieFileUseCase(Unit)
|
val animationsFlow = findFeaturedLottieFileUseCase(Unit)
|
||||||
|
|
||||||
combine(
|
combine(
|
||||||
userFlow,
|
|
||||||
storiesFlow,
|
storiesFlow,
|
||||||
animationsFlow,
|
animationsFlow,
|
||||||
animatorsFlow
|
animatorsFlow
|
||||||
) { user, stories, anim, animators ->
|
) { stories, anim, animators ->
|
||||||
HomeContentData(
|
HomeContentData(
|
||||||
user = user.data?.firstOrNull(),
|
|
||||||
blog = stories.data ?: emptyList(),
|
blog = stories.data ?: emptyList(),
|
||||||
featuredAnimators = animators.data ?: emptyList(),
|
featuredAnimators = animators.data ?: emptyList(),
|
||||||
featuredLottieFile = anim.data ?: emptyList(),
|
featuredLottieFile = anim.data ?: emptyList(),
|
||||||
|
|||||||
+3
-2
@@ -9,12 +9,13 @@ import com.ericampire.android.androidstudycase.domain.entity.Lottiefile
|
|||||||
import com.ericampire.android.androidstudycase.domain.entity.User
|
import com.ericampire.android.androidstudycase.domain.entity.User
|
||||||
|
|
||||||
data class HomeViewState(
|
data class HomeViewState(
|
||||||
val contentData: Async<HomeContentData> = Uninitialized
|
val contentData: Async<HomeContentData> = Uninitialized,
|
||||||
|
val login: Async<Unit> = Uninitialized,
|
||||||
|
val currentUser: Async<User?> = Uninitialized,
|
||||||
) : MavericksState
|
) : MavericksState
|
||||||
|
|
||||||
data class HomeContentData(
|
data class HomeContentData(
|
||||||
val blog: List<Blog> = emptyList(),
|
val blog: List<Blog> = emptyList(),
|
||||||
val featuredAnimators: List<Animator> = emptyList(),
|
val featuredAnimators: List<Animator> = emptyList(),
|
||||||
val featuredLottieFile: List<Lottiefile> = emptyList(),
|
val featuredLottieFile: List<Lottiefile> = emptyList(),
|
||||||
val user: User? = null,
|
|
||||||
) : MavericksState
|
) : MavericksState
|
||||||
|
|||||||
+44
-8
@@ -1,6 +1,7 @@
|
|||||||
package com.ericampire.android.androidstudycase.presentation.screen.home.ui
|
package com.ericampire.android.androidstudycase.presentation.screen.home.ui
|
||||||
|
|
||||||
import androidx.compose.animation.Crossfade
|
import androidx.compose.animation.Crossfade
|
||||||
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
import androidx.compose.animation.animateContentSize
|
import androidx.compose.animation.animateContentSize
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
@@ -9,16 +10,16 @@ import androidx.compose.foundation.lazy.LazyRow
|
|||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.material.ExperimentalMaterialApi
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.*
|
||||||
import androidx.compose.material.Scaffold
|
|
||||||
import androidx.compose.material.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
@@ -29,6 +30,8 @@ import com.airbnb.mvrx.Uninitialized
|
|||||||
import com.airbnb.mvrx.compose.collectAsState
|
import com.airbnb.mvrx.compose.collectAsState
|
||||||
import com.airbnb.mvrx.compose.mavericksViewModel
|
import com.airbnb.mvrx.compose.mavericksViewModel
|
||||||
import com.ericampire.android.androidstudycase.R
|
import com.ericampire.android.androidstudycase.R
|
||||||
|
import com.ericampire.android.androidstudycase.app.App
|
||||||
|
import com.ericampire.android.androidstudycase.domain.entity.User
|
||||||
import com.ericampire.android.androidstudycase.presentation.custom.CustomImageView
|
import com.ericampire.android.androidstudycase.presentation.custom.CustomImageView
|
||||||
import com.ericampire.android.androidstudycase.presentation.custom.LoadingAnimation
|
import com.ericampire.android.androidstudycase.presentation.custom.LoadingAnimation
|
||||||
import com.ericampire.android.androidstudycase.presentation.custom.TopActionBar
|
import com.ericampire.android.androidstudycase.presentation.custom.TopActionBar
|
||||||
@@ -37,9 +40,10 @@ import com.ericampire.android.androidstudycase.presentation.screen.home.business
|
|||||||
import com.ericampire.android.androidstudycase.presentation.screen.home.business.HomeViewModel
|
import com.ericampire.android.androidstudycase.presentation.screen.home.business.HomeViewModel
|
||||||
import com.ericampire.android.androidstudycase.presentation.screen.home.business.HomeViewState
|
import com.ericampire.android.androidstudycase.presentation.screen.home.business.HomeViewState
|
||||||
import com.ericampire.android.androidstudycase.presentation.theme.AppColor
|
import com.ericampire.android.androidstudycase.presentation.theme.AppColor
|
||||||
import com.ericampire.android.androidstudycase.util.Destination
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
|
@ExperimentalAnimationApi
|
||||||
@ExperimentalMaterialApi
|
@ExperimentalMaterialApi
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeScreen(
|
fun HomeScreen(
|
||||||
@@ -48,11 +52,37 @@ fun HomeScreen(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
val state by viewModel.collectAsState(HomeViewState::contentData)
|
val state by viewModel.collectAsState(HomeViewState::contentData)
|
||||||
|
val loginState by viewModel.collectAsState(HomeViewState::login)
|
||||||
|
val userState by viewModel.collectAsState(HomeViewState::currentUser)
|
||||||
|
|
||||||
|
val bottomSheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
LaunchedEffect(viewModel) {
|
LaunchedEffect(viewModel) {
|
||||||
viewModel.submitAction(HomeAction.FetchData)
|
viewModel.submitAction(HomeAction.FetchData)
|
||||||
|
viewModel.submitAction(HomeAction.FetchCurrentUser)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(loginState) {
|
||||||
|
if (loginState is Success) {
|
||||||
|
App.restart(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ModalBottomSheetLayout(
|
||||||
|
sheetShape = RoundedCornerShape(topEnd = 24.dp, topStart = 24.dp),
|
||||||
|
sheetState = bottomSheetState,
|
||||||
|
sheetContent = {
|
||||||
|
LoginBottomSheet(
|
||||||
|
onLoginClick = {
|
||||||
|
viewModel.submitAction(HomeAction.Login)
|
||||||
|
},
|
||||||
|
isLoading = loginState is Loading
|
||||||
|
)
|
||||||
|
},
|
||||||
|
content = {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
Column(
|
Column(
|
||||||
@@ -86,8 +116,11 @@ fun HomeScreen(
|
|||||||
is Success -> {
|
is Success -> {
|
||||||
HomeContent(
|
HomeContent(
|
||||||
state = it.invoke(),
|
state = it.invoke(),
|
||||||
|
currentUser = userState.invoke(),
|
||||||
onLoginClick = {
|
onLoginClick = {
|
||||||
navController.navigate(Destination.Login.route)
|
coroutineScope.launch {
|
||||||
|
bottomSheetState.animateTo(targetValue = ModalBottomSheetValue.Expanded)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -101,12 +134,15 @@ fun HomeScreen(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@ExperimentalMaterialApi
|
@ExperimentalMaterialApi
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeContent(
|
fun HomeContent(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
state: HomeContentData,
|
state: HomeContentData,
|
||||||
|
currentUser: User? = null,
|
||||||
onLoginClick: () -> Unit
|
onLoginClick: () -> Unit
|
||||||
) {
|
) {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
@@ -117,10 +153,10 @@ fun HomeContent(
|
|||||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
content = {
|
content = {
|
||||||
item {
|
item {
|
||||||
if (state.user == null) {
|
if (currentUser == null) {
|
||||||
UnLoggedUserHeaderView(onLoginClick = onLoginClick)
|
UnLoggedUserHeaderView(onLoginClick = onLoginClick)
|
||||||
} else {
|
} else {
|
||||||
LoggedUserHeaderView(user = state.user)
|
LoggedUserHeaderView(user = currentUser)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -42,7 +42,7 @@ fun LoggedUserHeaderView(
|
|||||||
content = {
|
content = {
|
||||||
Text(
|
Text(
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
text = "Monday September 4",
|
text = "Monday 4 September",
|
||||||
style = MaterialTheme.typography.button.copy(
|
style = MaterialTheme.typography.button.copy(
|
||||||
color = Color.Gray
|
color = Color.Gray
|
||||||
),
|
),
|
||||||
|
|||||||
+117
@@ -0,0 +1,117 @@
|
|||||||
|
package com.ericampire.android.androidstudycase.presentation.screen.home.ui
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.material.ExperimentalMaterialApi
|
||||||
|
import androidx.compose.material.LinearProgressIndicator
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.airbnb.lottie.compose.*
|
||||||
|
import com.ericampire.android.androidstudycase.R
|
||||||
|
import com.ericampire.android.androidstudycase.presentation.custom.CustomFacebookButton
|
||||||
|
import com.ericampire.android.androidstudycase.presentation.custom.CustomGoogleButton
|
||||||
|
import com.ericampire.android.androidstudycase.presentation.theme.AndroidStudyCaseTheme
|
||||||
|
import com.ericampire.android.androidstudycase.presentation.theme.AppColor
|
||||||
|
|
||||||
|
@ExperimentalAnimationApi
|
||||||
|
@Composable
|
||||||
|
fun LoginBottomSheet(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
isLoading: Boolean = false,
|
||||||
|
onLoginClick: () -> Unit,
|
||||||
|
) {
|
||||||
|
|
||||||
|
val composition by rememberLottieComposition(
|
||||||
|
spec = LottieCompositionSpec.RawRes(R.raw.people_communicating)
|
||||||
|
)
|
||||||
|
val progress by animateLottieCompositionAsState(
|
||||||
|
composition = composition,
|
||||||
|
iterations = LottieConstants.IterateForever,
|
||||||
|
)
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = modifier
|
||||||
|
.clip(MaterialTheme.shapes.medium)
|
||||||
|
.background(AppColor.Black001),
|
||||||
|
content = {
|
||||||
|
AnimatedVisibility(isLoading) {
|
||||||
|
LinearProgressIndicator(modifier = Modifier.fillMaxWidth())
|
||||||
|
}
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(24.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
content = {
|
||||||
|
Text(
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
text = stringResource(id = R.string.txt_login_title),
|
||||||
|
style = MaterialTheme.typography.h4.copy(
|
||||||
|
color = MaterialTheme.colors.onSurface
|
||||||
|
),
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
text = stringResource(id = R.string.txt_login_description),
|
||||||
|
style = MaterialTheme.typography.body1.copy(
|
||||||
|
color = MaterialTheme.colors.onSurface
|
||||||
|
),
|
||||||
|
)
|
||||||
|
LottieAnimation(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(200.dp),
|
||||||
|
composition = composition,
|
||||||
|
progress = progress,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
text = stringResource(id = R.string.txt_get_started),
|
||||||
|
style = MaterialTheme.typography.body1.copy(
|
||||||
|
color = MaterialTheme.colors.onSurface
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
CustomFacebookButton(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
onClick = onLoginClick
|
||||||
|
)
|
||||||
|
|
||||||
|
CustomGoogleButton(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
onClick = onLoginClick
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExperimentalAnimationApi
|
||||||
|
@ExperimentalMaterialApi
|
||||||
|
@Preview()
|
||||||
|
@Composable
|
||||||
|
fun LoginDialogViewPreview() {
|
||||||
|
AndroidStudyCaseTheme(darkTheme = true) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
content = {
|
||||||
|
LoginBottomSheet(
|
||||||
|
onLoginClick = {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -44,7 +44,7 @@ fun UnLoggedUserHeaderView(
|
|||||||
content = {
|
content = {
|
||||||
Text(
|
Text(
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
text = "Monday September 4",
|
text = "Monday 4 September",
|
||||||
style = MaterialTheme.typography.button.copy(
|
style = MaterialTheme.typography.button.copy(
|
||||||
color = Color.Gray
|
color = Color.Gray
|
||||||
),
|
),
|
||||||
|
|||||||
+2
-1
@@ -1,5 +1,6 @@
|
|||||||
package com.ericampire.android.androidstudycase.presentation.screen.main.ui
|
package com.ericampire.android.androidstudycase.presentation.screen.main.ui
|
||||||
|
|
||||||
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.*
|
import androidx.compose.material.*
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
@@ -29,7 +30,7 @@ import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
|||||||
@ExperimentalMaterialApi
|
@ExperimentalMaterialApi
|
||||||
@ExperimentalPagerApi
|
@ExperimentalPagerApi
|
||||||
@ExperimentalPermissionsApi
|
@ExperimentalPermissionsApi
|
||||||
|
@ExperimentalAnimationApi
|
||||||
@Composable
|
@Composable
|
||||||
fun MainScreen() {
|
fun MainScreen() {
|
||||||
val navController = rememberNavController()
|
val navController = rememberNavController()
|
||||||
|
|||||||
+2
-1
@@ -12,7 +12,8 @@ object AppColor {
|
|||||||
val Purple700 = Color(0xFF3700B3)
|
val Purple700 = Color(0xFF3700B3)
|
||||||
val Teal200 = Color(0xFF03DAC5)
|
val Teal200 = Color(0xFF03DAC5)
|
||||||
val Black001 = Color(0xFF222222)
|
val Black001 = Color(0xFF222222)
|
||||||
val BlackOverlay = Color(0x4D000000)
|
val BlueFacebook = Color(0xFF3B5998)
|
||||||
|
val BlackOverlay = Color(0x4DFFFFFF)
|
||||||
val BlackOverlay001 = Color(0x1A000000)
|
val BlackOverlay001 = Color(0x1A000000)
|
||||||
val WhiteTransparent = Color(0x80FFFFFF)
|
val WhiteTransparent = Color(0x80FFFFFF)
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M23.745,12.27C23.745,11.48 23.675,10.73 23.555,10L12.255,10L12.255,14.51L18.725,14.51C18.435,15.99 17.585,17.24 16.325,18.09L16.325,21.09L20.185,21.09C22.445,19 23.745,15.92 23.745,12.27Z"
|
||||||
|
android:fillColor="#4285F4" />
|
||||||
|
<path
|
||||||
|
android:pathData="M12.255,24C15.495,24 18.205,22.92 20.185,21.09L16.325,18.09C15.245,18.81 13.875,19.25 12.255,19.25C9.125,19.25 6.475,17.14 5.525,14.29L1.545,14.29L1.545,17.38C3.515,21.3 7.565,24 12.255,24Z"
|
||||||
|
android:fillColor="#34A853" />
|
||||||
|
<path
|
||||||
|
android:pathData="M5.525,14.29C5.275,13.57 5.145,12.8 5.145,12C5.145,11.2 5.285,10.43 5.525,9.71L5.525,6.62L1.545,6.62C0.725,8.24 0.255,10.06 0.255,12C0.255,13.94 0.725,15.76 1.545,17.38L5.525,14.29Z"
|
||||||
|
android:fillColor="#FBBC05" />
|
||||||
|
<path
|
||||||
|
android:pathData="M12.255,4.75C14.025,4.75 15.605,5.36 16.855,6.55L20.275,3.13C18.205,1.19 15.495,0 12.255,0C7.565,0 3.515,2.7 1.545,6.62L5.525,9.71C6.475,6.86 9.125,4.75 12.255,4.75Z"
|
||||||
|
android:fillColor="#EA4335" />
|
||||||
|
</vector>
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -2,4 +2,7 @@
|
|||||||
<string name="txt_featured_animation">Featured Animations</string>
|
<string name="txt_featured_animation">Featured Animations</string>
|
||||||
<string name="txt_open_setting">Open Settings</string>
|
<string name="txt_open_setting">Open Settings</string>
|
||||||
<string name="txt_request">Request permission</string>
|
<string name="txt_request">Request permission</string>
|
||||||
|
<string name="txt_login_title">1000s of lottie animations</string>
|
||||||
|
<string name="txt_login_description">from top creators waiting to be discover, save and share from the palm of your hand</string>
|
||||||
|
<string name="txt_connect_with_facebook">Continue with Facebook</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -2,6 +2,7 @@ package com.ericampire.android.androidstudycase.data.room
|
|||||||
|
|
||||||
import androidx.room.Dao
|
import androidx.room.Dao
|
||||||
import androidx.room.Insert
|
import androidx.room.Insert
|
||||||
|
import androidx.room.OnConflictStrategy.REPLACE
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import com.ericampire.android.androidstudycase.domain.entity.User
|
import com.ericampire.android.androidstudycase.domain.entity.User
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
@@ -9,7 +10,7 @@ import kotlinx.coroutines.flow.Flow
|
|||||||
@Dao
|
@Dao
|
||||||
interface UserDao {
|
interface UserDao {
|
||||||
|
|
||||||
@Insert
|
@Insert(onConflict = REPLACE)
|
||||||
fun save(user: User)
|
fun save(user: User)
|
||||||
|
|
||||||
@Query("SELECT * FROM User")
|
@Query("SELECT * FROM User")
|
||||||
|
|||||||
@@ -21,4 +21,6 @@
|
|||||||
<string name="txt_feature_not_available">Feature not available</string>
|
<string name="txt_feature_not_available">Feature not available</string>
|
||||||
<string name="txt_camera_permssion_required">The camera is important for this app. Please grant the permission.</string>
|
<string name="txt_camera_permssion_required">The camera is important for this app. Please grant the permission.</string>
|
||||||
<string name="txt_permission_denied">Camera permission denied. See this FAQ with information about why we need this permission. Please, grant us access on the Settings screen.</string>
|
<string name="txt_permission_denied">Camera permission denied. See this FAQ with information about why we need this permission. Please, grant us access on the Settings screen.</string>
|
||||||
|
<string name="txt_connect_with_google">Continue with Google</string>
|
||||||
|
<string name="txt_get_started">Get started for free</string>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user