Skip to content

Commit

Permalink
refactor: Redesigned home screen
Browse files Browse the repository at this point in the history
Redesigned the home screen with a new layout and components.
- Added new icons for search and profile.
- Created new components for FloatingTabRow and TextLogo.
- Changed the default font to DM Sans.
- Added Ktor client logging.
- Implemented TokenStore, CredentialsProvider and RegionProvider for CinemaCity feature.
  • Loading branch information
diareuse committed Dec 18, 2024
1 parent 479c5c7 commit ccbf97b
Show file tree
Hide file tree
Showing 25 changed files with 343 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@ package movie.metropolis.app.feature.cinemacity

import movie.cinema.city.Credentials
import movie.cinema.city.CredentialsProvider
import movie.cinema.city.Region
import movie.cinema.city.RegionProvider
import movie.cinema.city.TokenStore
import movie.core.auth.UserAccount
import movie.settings.GlobalPreferences

class CredentialsProviderImpl(
private val user: UserAccount
Expand All @@ -15,30 +11,4 @@ class CredentialsProviderImpl(
username = user.email.let(::requireNotNull),
password = user.password.let(::requireNotNull)
)
}

class RegionProviderImpl(
private val prefs: GlobalPreferences
) : RegionProvider {
override val region: Region?
get() = prefs.regionId?.let(Region.Companion::by)

override fun setRegion(region: Region) {
prefs.regionId = region.id
}
}

class TokenStoreImpl(
private val user: UserAccount
) : TokenStore {
override var token: String
get() = user.token
set(value) {
user.token = value
}
override var refreshToken: String
get() = user.refreshToken
set(value) {
user.refreshToken = value
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package movie.metropolis.app.feature.cinemacity

import movie.cinema.city.Region
import movie.cinema.city.RegionProvider
import movie.settings.GlobalPreferences

class RegionProviderImpl(
private val prefs: GlobalPreferences
) : RegionProvider {
override val region: Region?
get() = prefs.regionId?.let(Region.Companion::by)

override fun setRegion(region: Region) {
prefs.regionId = region.id
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package movie.metropolis.app.feature.cinemacity

import movie.cinema.city.TokenStore
import movie.core.auth.UserAccount

class TokenStoreImpl(
private val user: UserAccount
) : TokenStore {
override var token: String
get() = user.token
set(value) {
user.token = value
}
override var refreshToken: String
get() = user.refreshToken
set(value) {
user.refreshToken = value
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package movie.metropolis.app.screen2.component

import androidx.compose.animation.*
import androidx.compose.animation.core.*
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.*
import androidx.compose.ui.draw.*
import androidx.compose.ui.geometry.*
import androidx.compose.ui.graphics.*
import androidx.compose.ui.semantics.*
import androidx.compose.ui.text.font.*
import androidx.compose.ui.text.style.*
import androidx.compose.ui.tooling.preview.*
import androidx.compose.ui.unit.*
import movie.style.layout.PreviewLayout
import kotlin.math.hypot

@Composable
fun FloatingTabRow(
selectedTabIndex: Int,
modifier: Modifier = Modifier,
tabs: @Composable () -> Unit,
) = TabRow(
modifier = Modifier
.clip(MaterialTheme.shapes.medium)
.then(modifier),
selectedTabIndex = selectedTabIndex,
containerColor = Color.Transparent,
contentColor = MaterialTheme.colorScheme.onSurface,
tabs = tabs,
divider = {},
indicator = {}
)

@Composable
fun FloatingTab(
selected: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
selectedColor: Color = MaterialTheme.colorScheme.onPrimary,
unselectedColor: Color = MaterialTheme.colorScheme.onSurface,
brushColors: List<Color> = listOf(
MaterialTheme.colorScheme.primary,
MaterialTheme.colorScheme.tertiary
),
shape: Shape = MaterialTheme.shapes.small,
label: @Composable () -> Unit
) = Box(
modifier = modifier
.fillMaxSize()
.clickable(enabled = enabled, role = Role.Tab, onClick = onClick)
.heightIn(min = 48.dp),
contentAlignment = Alignment.Center
) {
val alpha by animateFloatAsState(if (selected) 1f else 0f, tween(1000))
val blur by animateIntAsState(if (selected) 0 else 32, tween(500))
val scale by animateFloatAsState(if (selected) 1f else 2f, tween(500))
val color by animateColorAsState(if (selected) selectedColor else unselectedColor, tween(1000))
Spacer(
modifier = Modifier
.padding(8.dp)
.matchParentSize()
.scale(scale)
.blur(blur.dp, edgeTreatment = BlurredEdgeTreatment.Unbounded)
.graphicsLayer {
compositingStrategy = CompositingStrategy.Offscreen
this.alpha = alpha
}
.drawBehind {
val outline = shape.createOutline(size, layoutDirection, this)
val brush = Brush.radialGradient(
colors = brushColors,
center = Offset(0f, size.height),
radius = hypot(size.width, size.height)
)
drawOutline(outline, brush)
}
)
ProvideTextStyle(
MaterialTheme.typography.labelMedium.copy(
textAlign = TextAlign.Center,
fontWeight = FontWeight.Bold
)
) {
CompositionLocalProvider(LocalContentColor provides color) {
label()
}
}
}

@PreviewLightDark
@PreviewFontScale
@Composable
private fun FloatingTabRowPreview() = PreviewLayout {
var selected by remember { mutableIntStateOf(0) }
FloatingTabRow(
modifier = Modifier.background(Color.Gray),
selectedTabIndex = selected
) {
FloatingTab(
selected = selected == 0,
onClick = { selected = 0 }
) { Text("Movies", maxLines = 1, overflow = TextOverflow.Ellipsis) }
FloatingTab(
selected = selected == 1,
onClick = { selected = 1 }
) { Text("Upcoming", maxLines = 1, overflow = TextOverflow.Ellipsis) }
FloatingTab(
selected = selected == 2,
onClick = { selected = 2 }
) { Text("Tickets", maxLines = 1, overflow = TextOverflow.Ellipsis) }
}
}
10 changes: 10 additions & 0 deletions app/src/main/java/movie/metropolis/app/screen2/component/Icons.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package movie.metropolis.app.screen2.component

import androidx.compose.runtime.*
import androidx.compose.ui.res.*
import movie.metropolis.app.R

object Icons {
inline val Search @Composable get() = painterResource(R.drawable.ic_search)
inline val Profile @Composable get() = painterResource(R.drawable.ic_profile_active)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package movie.metropolis.app.screen2.component

import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.*
import androidx.compose.ui.graphics.*
import androidx.compose.ui.text.font.*
import androidx.compose.ui.tooling.preview.*
import androidx.compose.ui.unit.*
import movie.style.layout.PreviewLayout

@Composable
fun TextLogo(
modifier: Modifier = Modifier,
) = Column(
modifier = modifier,
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
/*Text(
"MOVIE",
fontWeight = FontWeight.Black,
fontSize = 16.sp,
lineHeight = 16.sp,
maxLines = 1,
letterSpacing = 10.5.sp,
modifier = Modifier.offset(y = 3.dp)
)
Text(
"METROPOLIS",
fontWeight = FontWeight.Light,
fontSize = 16.sp,
lineHeight = 16.sp,
maxLines = 1,
letterSpacing = 0.sp,
modifier = Modifier.offset(y = (-3).dp)
)*/
Text(
"MOVIE",
fontWeight = FontWeight.Black,
fontSize = 24.sp,
lineHeight = 24.sp,
maxLines = 1,
letterSpacing = 0.sp,
modifier = Modifier.offset(y = 3.dp)
)
Text(
"METROPOLIS",
fontWeight = FontWeight.Light,
fontSize = 13.sp,
lineHeight = 13.sp,
maxLines = 1,
letterSpacing = 0.sp,
modifier = Modifier.offset(y = (-3).dp)
)
}

@PreviewFontScale
@Composable
private fun TextLogoPreview() = PreviewLayout {
TextLogo(Modifier.background(Color.White))
}
91 changes: 91 additions & 0 deletions app/src/main/java/movie/metropolis/app/screen2/home/HomeScreen.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package movie.metropolis.app.screen2.home

import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.*
import androidx.compose.ui.tooling.preview.*
import androidx.compose.ui.unit.*
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import dev.chrisbanes.haze.HazeState
import dev.chrisbanes.haze.HazeStyle
import dev.chrisbanes.haze.haze
import dev.chrisbanes.haze.hazeChild
import movie.metropolis.app.screen2.component.FloatingTab
import movie.metropolis.app.screen2.component.FloatingTabRow
import movie.metropolis.app.screen2.component.Icons
import movie.metropolis.app.screen2.component.TextLogo
import movie.style.layout.PreviewLayout

@Composable
fun HomeScreen(
loggedIn: Boolean,
modifier: Modifier = Modifier,
haze: HazeState = remember { HazeState() },
navController: NavHostController = rememberNavController()
) = Scaffold(
modifier = modifier,
topBar = {
val entry by navController.currentBackStackEntryAsState()
val route = entry?.destination?.route
val index = when (route) {
HomeRoute.Movies -> 0
HomeRoute.Upcoming -> 1
HomeRoute.Tickets -> 2
else -> 0
}
val style = HazeStyle(MaterialTheme.colorScheme.surfaceTint.copy(.1f))
Column(
modifier = Modifier.padding(24.dp),
verticalArrangement = Arrangement.spacedBy(24.dp)
) {
Row {
IconButton({}, Modifier.hazeChild(haze, MaterialTheme.shapes.medium, style)) {
Icon(Icons.Search, null)
}
TextLogo(Modifier.weight(1f))
IconButton({}, Modifier.hazeChild(haze, MaterialTheme.shapes.medium, style)) {
Icon(Icons.Profile, null)
}
}
FloatingTabRow(
modifier = Modifier.hazeChild(haze, MaterialTheme.shapes.medium, style),
selectedTabIndex = index
) {
FloatingTab(route == null || route == HomeRoute.Movies, {}) { Text("Movies") }
FloatingTab(route == HomeRoute.Upcoming, {}) { Text("Upcoming") }
FloatingTab(route == HomeRoute.Tickets, {}, enabled = loggedIn) { Text("Tickets") }
}
}
},
bottomBar = {

}
) { padding ->
NavHost(
modifier = Modifier.haze(haze),
navController = navController,
startDestination = HomeRoute.Movies
) {
composable(HomeRoute.Movies) {}
composable(HomeRoute.Upcoming) {}
composable(HomeRoute.Tickets) {}
}
}

object HomeRoute {
const val Movies = "movies"
const val Upcoming = "upcoming"
const val Tickets = "tickets"
}

@PreviewLightDark
@PreviewFontScale
@Composable
private fun HomeScreenPreview() = PreviewLayout {
HomeScreen(false)
}
12 changes: 12 additions & 0 deletions app/src/main/res/drawable/ic_search.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#000000"
android:viewportWidth="24"
android:viewportHeight="24">

<path
android:fillColor="@android:color/white"
android:pathData="M15.5,14h-0.79l-0.28,-0.27c1.2,-1.4 1.82,-3.31 1.48,-5.34 -0.47,-2.78 -2.79,-5 -5.59,-5.34 -4.23,-0.52 -7.79,3.04 -7.27,7.27 0.34,2.8 2.56,5.12 5.34,5.59 2.03,0.34 3.94,-0.28 5.34,-1.48l0.27,0.28v0.79l4.25,4.25c0.41,0.41 1.08,0.41 1.49,0 0.41,-0.41 0.41,-1.08 0,-1.49L15.5,14zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z" />

</vector>
2 changes: 2 additions & 0 deletions feature-cinema-city/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ dependencies {
implementation libs.ktor.client.okhttp
implementation libs.ktor.client.content.negotiation
implementation libs.ktor.client.core
implementation libs.ktor.client.logging
implementation libs.slf4j.android
implementation libs.ktor.client.auth
implementation libs.ktor.serialization.kotlinx.json
implementation libs.androidx.room.ktx
Expand Down
Loading

0 comments on commit ccbf97b

Please sign in to comment.