SplashScreen and ranking screen

pull/5/head
avalin 10 months ago
parent e2d119242c
commit 78b4ab2e05

@ -9,6 +9,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import fr.iut.alldev.allin.R
import fr.iut.alldev.allin.data.model.User
import fr.iut.alldev.allin.data.model.bet.BetType
import fr.iut.alldev.allin.ext.getIcon
import fr.iut.alldev.allin.ext.getTitleId
@ -25,7 +26,6 @@ import java.time.ZonedDateTime
fun BetCreationScreen(
viewModel: BetCreationViewModel = hiltViewModel(),
setLoading: (Boolean) -> Unit,
openDrawer: () -> Unit,
onCreation: () -> Unit
) {
val betTypes = remember { BetType.entries }
@ -43,7 +43,21 @@ fun BetCreationScreen(
val registerDateError by remember { viewModel.registerDateError }
val betDateError by remember { viewModel.betDateError }
val selectedFriends = remember { mutableListOf<Int>() }
val friends = remember {
buildList {
repeat(10) {
add(
User(
id = "$it",
username = "Dave",
email = "",
coins = 420
)
)
}
}
}
val selectedFriends = remember { mutableListOf<String>() }
var selectionElements by remember { mutableStateOf(listOf<SelectionElement>()) }
var selectedBetTypeElement by remember { mutableStateOf<SelectionElement?>(null) }
@ -71,7 +85,6 @@ fun BetCreationScreen(
val (showEndTimePicker, setEndTimePicker) = remember { mutableStateOf(false) }
BetCreationScreenContent(
nbFriends = 42,
betTheme = theme,
betThemeError = themeError.errorResource(),
setBetTheme = { theme = it },
@ -84,6 +97,7 @@ fun BetCreationScreen(
registerDateError = registerDateError.errorResource(),
betDate = betDate,
betDateError = betDateError.errorResource(),
friends = friends,
selectedFriends = selectedFriends,
setRegisterDateDialog = setRegisterDatePicker,
setEndDateDialog = setEndDatePicker,
@ -93,7 +107,6 @@ fun BetCreationScreen(
selectedBetType = selectedBetType,
setSelectedBetTypeElement = { selectedBetTypeElement = it },
selectionBetType = selectionElements,
openDrawer = openDrawer,
onCreateBet = {
viewModel.createBet(
themeFieldName = themeFieldName,

@ -17,6 +17,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import fr.iut.alldev.allin.R
import fr.iut.alldev.allin.data.model.User
import fr.iut.alldev.allin.data.model.bet.BetType
import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.betCreation.tabs.BetCreationScreenAnswerTab
@ -29,7 +30,6 @@ import java.time.ZonedDateTime
@Composable
fun BetCreationScreenContent(
nbFriends: Int,
betTheme: String,
betThemeError: String?,
setBetTheme: (String) -> Unit,
@ -42,7 +42,8 @@ fun BetCreationScreenContent(
registerDateError: String?,
betDate: ZonedDateTime,
betDateError: String?,
selectedFriends: MutableList<Int>,
friends: List<User>,
selectedFriends: MutableList<String>,
setRegisterDateDialog: (Boolean) -> Unit,
setEndDateDialog: (Boolean) -> Unit,
setRegisterTimeDialog: (Boolean) -> Unit,
@ -51,7 +52,6 @@ fun BetCreationScreenContent(
selectedBetType: BetType,
setSelectedBetTypeElement: (SelectionElement) -> Unit,
selectionBetType: List<SelectionElement>,
openDrawer: () -> Unit,
onCreateBet: () -> Unit
) {
val interactionSource = remember { MutableInteractionSource() }
@ -59,7 +59,6 @@ fun BetCreationScreenContent(
Box(Modifier.fillMaxSize()) {
AllInSections(
openDrawer = openDrawer,
onLoadSection = { focus.clearFocus() },
modifier = Modifier.align(Alignment.TopCenter),
sections = listOf(
@ -71,7 +70,7 @@ fun BetCreationScreenContent(
setBetPhrase = setBetPhrase,
betTheme = betTheme,
setBetTheme = setBetTheme,
nbFriends = nbFriends,
friends = friends,
selectedFriends = selectedFriends,
registerDate = registerDate,
betDate = betDate,
@ -124,7 +123,7 @@ fun BetCreationScreenContent(
private fun BetCreationScreenContentPreview() {
AllInTheme {
BetCreationScreenContent(
nbFriends = 8900,
friends = emptyList(),
betTheme = "Ina",
betThemeError = null,
setBetTheme = { },
@ -146,8 +145,7 @@ private fun BetCreationScreenContentPreview() {
selectedBetType = BetType.BINARY,
setSelectedBetTypeElement = { },
selectionBetType = listOf(),
onCreateBet = { },
openDrawer = { }
onCreateBet = { }
)
}
}

@ -1,10 +1,8 @@
package fr.iut.alldev.allin.ui.betCreation.tabs
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
@ -12,17 +10,18 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import fr.iut.alldev.allin.data.ext.formatToMediumDate
import fr.iut.alldev.allin.data.ext.formatToTime
import fr.iut.alldev.allin.data.model.User
import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.betCreation.tabs.sections.QuestionTabDateTimeSection
import fr.iut.alldev.allin.ui.betCreation.tabs.sections.QuestionTabPrivacySection
import fr.iut.alldev.allin.ui.betCreation.tabs.sections.QuestionTabThemePhraseSection
import java.time.ZonedDateTime
import fr.iut.alldev.allin.data.ext.formatToTime as formatToTime1
@Composable
fun BetCreationScreenQuestionTab(
modifier: Modifier = Modifier,
nbFriends: Int,
friends: List<User>,
betTheme: String,
betThemeError: String?,
setBetTheme: (String) -> Unit,
@ -35,7 +34,7 @@ fun BetCreationScreenQuestionTab(
registerDateError: String?,
betDate: ZonedDateTime,
betDateError: String?,
selectedFriends: MutableList<Int>,
selectedFriends: MutableList<String>,
setRegisterDateDialog: (Boolean) -> Unit,
setEndDateDialog: (Boolean) -> Unit,
setRegisterTimeDialog: (Boolean) -> Unit,
@ -55,11 +54,11 @@ fun BetCreationScreenQuestionTab(
Spacer(modifier = Modifier.height(35.dp))
QuestionTabDateTimeSection(
registerDate = registerDate.formatToMediumDate(),
registerTime = registerDate.formatToTime(),
registerTime = registerDate.formatToTime1(),
registerDateError = registerDateError,
betDateError = betDateError,
endDate = betDate.formatToMediumDate(),
endTime = betDate.formatToTime(),
endTime = betDate.formatToTime1(),
setEndDateDialog = setEndDateDialog,
setRegisterDateDialog = setRegisterDateDialog,
setRegisterTimeDialog = setRegisterTimeDialog,
@ -70,7 +69,7 @@ fun BetCreationScreenQuestionTab(
QuestionTabPrivacySection(
isPublic = isPublic,
setIsPublic = setIsPublic,
nbFriends = nbFriends,
friends = friends,
selectedFriends = selectedFriends,
interactionSource = interactionSource
)
@ -83,7 +82,7 @@ fun BetCreationScreenQuestionTab(
private fun BetCreationScreenQuestionTabPreview() {
AllInTheme {
BetCreationScreenQuestionTab(
nbFriends = 4651,
friends = emptyList(),
betTheme = "Elly",
betThemeError = null,
setBetTheme = { },
@ -91,16 +90,16 @@ private fun BetCreationScreenQuestionTabPreview() {
betPhraseError = null,
setBetPhrase = { },
isPublic = true,
setIsPublic = { },
setIsPublic = { },
registerDate = ZonedDateTime.now(),
registerDateError = null,
betDate = ZonedDateTime.now(),
betDateError = null,
selectedFriends = mutableListOf(),
setRegisterDateDialog = { },
setEndDateDialog = { },
setRegisterTimeDialog = { },
setEndTimeDialog = { },
setRegisterDateDialog = { },
setEndDateDialog = { },
setRegisterTimeDialog = { },
setEndTimeDialog = { },
interactionSource = remember { MutableInteractionSource() }
)
}

@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.HelpOutline
import androidx.compose.material.icons.filled.Lock
@ -18,9 +19,11 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import fr.iut.alldev.allin.R
import fr.iut.alldev.allin.data.model.User
import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.betCreation.components.BetCreationScreenBottomText
import fr.iut.alldev.allin.ui.betCreation.components.BetCreationScreenFriendLine
@ -32,8 +35,8 @@ import fr.iut.alldev.allin.ui.core.AllInTitleInfo
fun QuestionTabPrivacySection(
isPublic: Boolean,
setIsPublic: (Boolean) -> Unit,
nbFriends: Int,
selectedFriends: MutableList<Int>,
friends: List<User>,
selectedFriends: MutableList<String>,
interactionSource: MutableInteractionSource,
) {
AllInTitleInfo(
@ -82,25 +85,25 @@ fun QuestionTabPrivacySection(
}
} else {
AllInRetractableCard(
text = stringResource(
id = R.string.n_friends_available,
nbFriends,
nbFriends
text = pluralStringResource(
id = R.plurals.n_friends_available,
friends.size,
friends.size
),
borderWidth = 1.dp,
boldText = nbFriends.toString(),
boldText = friends.size.toString(),
isOpen = isOpen,
setIsOpen = { isOpen = it }
) {
LazyColumn(
modifier = Modifier.height(165.dp)
modifier = Modifier.height(440.dp)
) {
items(nbFriends) {
itemsIndexed(friends, key = { _, it -> it.id }) { idx, it ->
var isSelected by remember {
mutableStateOf(selectedFriends.contains(it))
mutableStateOf(selectedFriends.contains(it.id))
}
if (it != 0) {
if (idx != 0) {
HorizontalDivider(color = AllInTheme.themeColors.border)
}
BetCreationScreenFriendLine(
@ -109,9 +112,9 @@ fun QuestionTabPrivacySection(
isSelected = isSelected
) {
if (isSelected) {
selectedFriends.remove(it)
selectedFriends.remove(it.id)
} else {
selectedFriends.add(it)
selectedFriends.add(it.id)
}
isSelected = !isSelected
}

@ -11,8 +11,8 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
@ -20,6 +20,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.unit.Dp
@ -27,12 +28,12 @@ import androidx.compose.ui.unit.dp
import fr.iut.alldev.allin.theme.AllInTheme
import racra.compose.smooth_corner_rect_library.AbsoluteSmoothCornerShape
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AllInCard(
modifier: Modifier = Modifier,
onClick: (() -> Unit)? = null,
radius: Dp = 15.dp,
shape: Shape? = null,
enabled: Boolean = true,
backgroundColor: Color = AllInTheme.themeColors.background,
disabledBackgroundColor: Color = AllInTheme.themeColors.disabled,
@ -44,7 +45,7 @@ fun AllInCard(
content: @Composable () -> Unit,
) {
val cardShape = AbsoluteSmoothCornerShape(radius, smoothnessAsPercent = 100)
val cardShape = shape ?: AbsoluteSmoothCornerShape(radius, smoothnessAsPercent = 100)
val cardModifier = modifier
.run {
backgroundBrush?.let {
@ -111,15 +112,19 @@ fun AllInBouncyCard(
Spring.StiffnessMediumLow
), label = ""
)
LaunchedEffect(key1 = scale < .97f) {
if (scale < .97f) {
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
}
}
AllInCard(
modifier = modifier
.combinedClickable(
interactionSource = interactionSource,
indication = null,
onClick = { onClick?.let { it() } },
onLongClick = {
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
}
onClick = { onClick?.let { it() } }
)
.scale(scale),
onClick = null,

@ -39,22 +39,11 @@ fun AllInSections(
sections: List<SectionElement>,
modifier: Modifier = Modifier,
interSectionsPadding: Dp = 56.dp,
openDrawer: () -> Unit,
onLoadSection: () -> Unit = { }
) {
val pagerState = rememberPagerState(pageCount = { sections.size })
val scope = rememberCoroutineScope()
LaunchedEffect(key1 = pagerState.isScrollInProgress) {
if (
pagerState.isScrollInProgress &&
!pagerState.canScrollBackward &&
pagerState.currentPage == pagerState.targetPage
) {
openDrawer()
}
}
Box(modifier = modifier) {
HorizontalPager(state = pagerState) { page ->
LaunchedEffect(key1 = page) { onLoadSection() }
@ -106,7 +95,6 @@ fun AllInSections(
private fun AllInSectionsPreview() {
AllInTheme {
AllInSections(
openDrawer = { },
sections = listOf(
SectionElement("Page 1") {
Text("This is page 1")

@ -2,16 +2,11 @@ package fr.iut.alldev.allin.ui.core.topbar
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
@ -75,6 +70,6 @@ fun AllInTopBar(
@Composable
private fun AllInTopBarPreview() {
AllInTheme {
AllInTopBar(onMenuClicked = { }, coinAmount = 541)
AllInTopBar(onMenuClicked = { }, coinAmount = 541)
}
}

@ -147,20 +147,14 @@ fun MainScreen(
setStatusVisibility(true)
},
setLoading = setLoading,
putSnackbarContent = { mainViewModel.putSnackbarContent(it) },
backHandlers = {
BackHandler(enabled = drawerState.isOpen) {
scope.launch {
drawerState.close()
}
}
},
openDrawer = {
putSnackbarContent = { mainViewModel.putSnackbarContent(it) }
) {
BackHandler(enabled = drawerState.isOpen) {
scope.launch {
drawerState.open()
drawerState.close()
}
}
)
}
}
}
}

@ -28,10 +28,13 @@ import fr.iut.alldev.allin.ui.core.snackbar.SnackbarType
import fr.iut.alldev.allin.ui.login.LoginScreen
import fr.iut.alldev.allin.ui.main.MainScreen
import fr.iut.alldev.allin.ui.main.MainViewModel
import fr.iut.alldev.allin.ui.ranking.RankingScreen
import fr.iut.alldev.allin.ui.register.RegisterScreen
import fr.iut.alldev.allin.ui.splash.SplashScreen
import fr.iut.alldev.allin.ui.welcome.WelcomeScreen
object Routes {
const val SPLASH = "SPLASH"
const val WELCOME = "WELCOME"
const val REGISTER = "REGISTER"
const val LOGIN = "LOGIN"
@ -41,9 +44,15 @@ object Routes {
const val BET_HISTORY = "BET_HISTORY"
const val BET_CURRENT = "BET_CURRENT"
const val FRIENDS = "FRIENDS"
}
private val fadingRoutes
get() = listOf(
Routes.WELCOME,
Routes.DASHBOARD,
Routes.SPLASH
)
internal fun NavHostController.popUpTo(route: String, baseRoute: String) {
this.navigate(route) {
launchSingleTop = true
@ -57,22 +66,18 @@ internal fun NavHostController.popUpTo(route: String, baseRoute: String) {
fun AllInNavHost(
modifier: Modifier = Modifier,
navController: NavHostController = rememberNavController(),
startDestination: String = Routes.WELCOME,
startDestination: String = Routes.SPLASH,
) {
NavHost(
navController = navController,
startDestination = startDestination,
enterTransition = {
if (navController.currentDestination?.route != Routes.DASHBOARD &&
navController.currentDestination?.route != Routes.WELCOME
) {
if (navController.currentDestination?.route !in fadingRoutes) {
slideInHorizontally(initialOffsetX = { it })
} else fadeIn(animationSpec = tween(1500))
},
exitTransition = {
if (navController.currentDestination?.route != Routes.DASHBOARD &&
navController.currentDestination?.route != Routes.WELCOME
) {
if (navController.currentDestination?.route !in fadingRoutes) {
slideOutHorizontally(targetOffsetX = { -it / 2 })
} else fadeOut(animationSpec = tween(1500))
},
@ -80,6 +85,7 @@ fun AllInNavHost(
.fillMaxSize()
.background(AllInTheme.themeColors.mainSurface),
) {
allInSplashScreen(navController)
allInWelcomeScreen(navController)
allInRegisterScreen(navController)
allInLoginScreen(navController)
@ -90,7 +96,6 @@ fun AllInNavHost(
@Composable
internal fun AllInDrawerNavHost(
modifier: Modifier = Modifier,
openDrawer: () -> Unit,
navController: NavHostController,
selectBet: (Bet, Boolean) -> Unit,
startDestination: String = Routes.PUBLIC_BETS,
@ -116,18 +121,16 @@ internal fun AllInDrawerNavHost(
backHandlers()
val creationSuccessMessage = stringResource(id = R.string.bet_creation_success_message)
BetCreationScreen(
setLoading = setLoading,
openDrawer = openDrawer,
onCreation = {
putSnackbarContent(
MainViewModel.SnackbarContent(
text = creationSuccessMessage,
type = SnackbarType.SUCCESS
)
setLoading = setLoading
) {
putSnackbarContent(
MainViewModel.SnackbarContent(
text = creationSuccessMessage,
type = SnackbarType.SUCCESS
)
navController.popUpTo(Routes.PUBLIC_BETS, Routes.BET_CREATION)
}
)
)
navController.popUpTo(Routes.PUBLIC_BETS, Routes.BET_CREATION)
}
}
composable(
@ -137,6 +140,13 @@ internal fun AllInDrawerNavHost(
BetHistoryScreen()
}
composable(
route = Routes.FRIENDS
) {
backHandlers()
RankingScreen()
}
composable(
route = Routes.BET_CURRENT
) {
@ -146,6 +156,21 @@ internal fun AllInDrawerNavHost(
}
}
private fun NavGraphBuilder.allInSplashScreen(
navController: NavHostController,
) {
composable(route = Routes.SPLASH) {
SplashScreen(
navigateToWelcomeScreen = {
navController.popUpTo(Routes.WELCOME, Routes.SPLASH)
},
navigateToDashboard = {
navController.popUpTo(Routes.DASHBOARD, Routes.SPLASH)
}
)
}
}
private fun NavGraphBuilder.allInWelcomeScreen(
navController: NavHostController,
) {
@ -156,9 +181,6 @@ private fun NavGraphBuilder.allInWelcomeScreen(
},
navigateToLogin = {
navController.popUpTo(Routes.LOGIN, Routes.WELCOME)
},
navigateToDashboard = {
navController.popUpTo(Routes.DASHBOARD, Routes.WELCOME)
}
)
}

@ -0,0 +1,110 @@
package fr.iut.alldev.allin.ui.ranking
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import fr.iut.alldev.allin.data.model.User
import fr.iut.alldev.allin.ui.ranking.components.RankingScreenContent
@Composable
fun RankingScreen() {
val users = remember { mockRanking }
RankingScreenContent(
users = users.sortedByDescending { it.coins }
)
}
private val mockRanking by lazy {
listOf(
User(
id = "1",
username = "Owen",
email = "",
coins = 8533
),
User(
id = "2",
username = "Dave",
email = "",
coins = 6942
),
User(
id = "3",
username = "Lucas",
email = "",
coins = 3333
),
User(
id = "4",
username = "Louison",
email = "",
coins = 1970
),
User(
id = "5",
username = "Imri",
email = "",
coins = 1
),
User(
id = "21",
username = "Owen",
email = "",
coins = 8533
),
User(
id = "22",
username = "Dave",
email = "",
coins = 6942
),
User(
id = "23",
username = "Lucas",
email = "",
coins = 3333
),
User(
id = "24",
username = "Louison",
email = "",
coins = 1970
),
User(
id = "25",
username = "Imri",
email = "",
coins = 1
),
User(
id = "31",
username = "Owen",
email = "",
coins = 8533
),
User(
id = "32",
username = "Dave",
email = "",
coins = 6942
),
User(
id = "33",
username = "Lucas",
email = "",
coins = 3333
),
User(
id = "34",
username = "Louison",
email = "",
coins = 1970
),
User(
id = "35",
username = "Imri",
email = "",
coins = 1
)
)
}

@ -0,0 +1,121 @@
package fr.iut.alldev.allin.ui.ranking.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
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 androidx.compose.ui.unit.sp
import fr.iut.alldev.allin.R
import fr.iut.alldev.allin.data.model.User
import fr.iut.alldev.allin.theme.AllInTheme
@Composable
fun RankingScreenContent(
users: List<User>
) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(start = 24.dp, end = 24.dp, top = 18.dp),
verticalArrangement = Arrangement.spacedBy(11.dp),
) {
item {
Text(
text = stringResource(id = R.string.ranking_title),
style = AllInTheme.typography.h1,
color = AllInTheme.colors.allInGrey,
fontSize = 24.sp,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
)
}
item {
Row(
verticalAlignment = Alignment.Bottom,
horizontalArrangement = Arrangement.spacedBy(11.dp)
) {
users.firstOrNull()?.let { first ->
RankingScreenFirst(
username = first.username,
coins = first.coins,
modifier = Modifier.weight(1f)
)
}
users.getOrNull(1)?.let { second ->
RankingScreenSecond(
username = second.username,
coins = second.coins,
modifier = Modifier.weight(1f)
)
}
}
}
itemsIndexed(users.subList(1, users.size), key = { _, user -> user.id }) { idx, user ->
RankingScreenItem(
position = idx + 3,
username = user.username,
coins = user.coins
)
}
item {
Spacer(modifier = Modifier.navigationBarsPadding())
}
}
}
@Preview
@Composable
private fun RankingScreenContentPreview() {
AllInTheme {
RankingScreenContent(
users = listOf(
User(
id = "1",
username = "Owen",
email = "",
coins = 8533
),
User(
id = "2",
username = "Dave",
email = "",
coins = 6942
),
User(
id = "3",
username = "Lucas",
email = "",
coins = 3333
),
User(
id = "4",
username = "Louison",
email = "",
coins = 1970
),
User(
id = "5",
username = "Imri",
email = "",
coins = 1
)
)
)
}
}

@ -0,0 +1,126 @@
package fr.iut.alldev.allin.ui.ranking.components
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.zIndex
import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.core.AllInCard
import fr.iut.alldev.allin.ui.core.AllInCoinCount
import fr.iut.alldev.allin.ui.core.ProfilePicture
import racra.compose.smooth_corner_rect_library.AbsoluteSmoothCornerShape
@Composable
fun RankingScreenFirst(
username: String,
coins: Int,
modifier: Modifier = Modifier
) {
Box(modifier) {
AllInCard(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(top = 35.dp),
shape = AbsoluteSmoothCornerShape(
cornerRadiusTL = 41.dp,
smoothnessAsPercentTL = 100,
cornerRadiusTR = 8.dp,
smoothnessAsPercentTR = 100,
cornerRadiusBR = 19.dp,
smoothnessAsPercentBR = 100,
cornerRadiusBL = 19.dp,
smoothnessAsPercentBL = 100
)
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(modifier = Modifier.height(50.dp))
Text(
text = username,
color = AllInTheme.themeColors.onBackground,
style = AllInTheme.typography.h1,
textAlign = TextAlign.Center,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
fontSize = 20.sp,
modifier = Modifier.padding(8.dp)
)
HorizontalDivider(color = AllInTheme.themeColors.border)
Box(
modifier = Modifier
.fillMaxWidth()
.background(AllInTheme.themeColors.background2)
.padding(16.dp),
contentAlignment = Alignment.Center
) {
AllInCoinCount(
amount = coins,
color = AllInTheme.colors.allInPurple,
size = 20
)
}
}
}
ProfilePicture(
modifier = Modifier
.size(70.dp)
.align(Alignment.TopCenter)
.zIndex(1f),
)
Box(
modifier = Modifier
.align(Alignment.TopCenter)
.padding(vertical = 55.dp)
.zIndex(500f)
.clip(CircleShape)
.background(AllInTheme.colors.allInPurple)
.size(30.dp),
contentAlignment = Alignment.Center
) {
Text(
text = "1",
color = AllInTheme.colors.white,
style = AllInTheme.typography.h1,
fontSize = 20.sp,
modifier = Modifier.padding(4.dp)
)
}
}
}
@Preview
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun RankingScreenFirstPreview() {
AllInTheme {
RankingScreenFirst(
username = "Username",
coins = 420
)
}
}

@ -0,0 +1,96 @@
package fr.iut.alldev.allin.ui.ranking.components
import android.content.res.Configuration
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.core.AllInCard
import fr.iut.alldev.allin.ui.core.AllInCoinCount
import fr.iut.alldev.allin.ui.core.ProfilePicture
@Composable
fun RankingScreenItem(
position: Int,
username: String,
coins: Int,
modifier: Modifier = Modifier
) {
AllInCard(
modifier = modifier,
radius = 8.dp
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 8.dp, vertical = 16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "$position",
color = AllInTheme.colors.allInPurple,
style = AllInTheme.typography.h1,
fontSize = 15.sp,
softWrap = false,
modifier = Modifier.size(width = 26.dp, height = 20.dp)
)
ProfilePicture(modifier = Modifier.size(38.dp))
Spacer(modifier = Modifier.width(8.dp))
Text(
text = username,
color = AllInTheme.themeColors.onBackground,
style = AllInTheme.typography.sm2,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
fontSize = 15.sp,
modifier = Modifier.weight(1f)
)
AllInCoinCount(
amount = coins,
color = AllInTheme.colors.allInPurple
)
}
}
}
@Preview
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun RankingScreenItemPreview() {
AllInTheme {
RankingScreenItem(
position = 3,
username = "Username",
coins = 420
)
}
}
@Preview
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun RankingScreenItemTenthPreview() {
AllInTheme {
RankingScreenItem(
position = 10,
username = "Username",
coins = 420
)
}
}

@ -0,0 +1,126 @@
package fr.iut.alldev.allin.ui.ranking.components
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.zIndex
import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.core.AllInCard
import fr.iut.alldev.allin.ui.core.AllInCoinCount
import fr.iut.alldev.allin.ui.core.ProfilePicture
import racra.compose.smooth_corner_rect_library.AbsoluteSmoothCornerShape
@Composable
fun RankingScreenSecond(
username: String,
coins: Int,
modifier: Modifier = Modifier
) {
Box(modifier) {
AllInCard(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(top = 28.dp),
shape = AbsoluteSmoothCornerShape(
cornerRadiusTL = 8.dp,
smoothnessAsPercentTL = 100,
cornerRadiusTR = 41.dp,
smoothnessAsPercentTR = 100,
cornerRadiusBR = 19.dp,
smoothnessAsPercentBR = 100,
cornerRadiusBL = 19.dp,
smoothnessAsPercentBL = 100
)
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(modifier = Modifier.height(30.dp))
Text(
text = username,
color = AllInTheme.themeColors.onBackground,
style = AllInTheme.typography.h1,
textAlign = TextAlign.Center,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
fontSize = 20.sp,
modifier = Modifier.padding(8.dp)
)
HorizontalDivider(color = AllInTheme.themeColors.border)
Box(
modifier = Modifier
.fillMaxWidth()
.background(AllInTheme.themeColors.background2)
.padding(16.dp),
contentAlignment = Alignment.Center
) {
AllInCoinCount(
amount = coins,
color = AllInTheme.colors.allInPurple,
size = 20
)
}
}
}
ProfilePicture(
modifier = Modifier
.size(56.dp)
.align(Alignment.TopCenter)
.zIndex(1f),
)
Box(
modifier = Modifier
.align(Alignment.TopCenter)
.padding(vertical = 44.dp)
.zIndex(500f)
.clip(CircleShape)
.background(AllInTheme.colors.allInPurple)
.size(25.dp),
contentAlignment = Alignment.Center
) {
Text(
text = "2",
color = AllInTheme.colors.white,
style = AllInTheme.typography.h1,
fontSize = 15.sp,
modifier = Modifier.padding(4.dp)
)
}
}
}
@Preview
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun RankingScreenSecondPreview() {
AllInTheme {
RankingScreenSecond(
username = "Username",
coins = 420
)
}
}

@ -0,0 +1,30 @@
package fr.iut.alldev.allin.ui.splash
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import fr.iut.alldev.allin.ui.splash.components.SplashScreenContent
@Composable
fun SplashScreen(
viewModel: SplashScreenViewModel = hiltViewModel(),
navigateToWelcomeScreen: () -> Unit,
navigateToDashboard: () -> Unit
) {
val state by viewModel.state.collectAsStateWithLifecycle()
LaunchedEffect(key1 = state) {
(state as? SplashScreenViewModel.State.Loaded)?.let { loadedState ->
if (loadedState.isLoggedIn) {
navigateToDashboard()
} else {
navigateToWelcomeScreen()
}
}
}
SplashScreenContent()
}

@ -0,0 +1,51 @@
package fr.iut.alldev.allin.ui.splash
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import fr.iut.alldev.allin.data.repository.UserRepository
import fr.iut.alldev.allin.keystore.AllInKeystoreManager
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.stateIn
import timber.log.Timber
import javax.inject.Inject
@HiltViewModel
class SplashScreenViewModel @Inject constructor(
private val keystoreManager: AllInKeystoreManager,
private val userRepository: UserRepository
) : ViewModel() {
val state: StateFlow<State> by lazy {
flow {
delay(1_000L)
keystoreManager.getToken()?.let { token ->
runCatching {
userRepository
.login(token)
?.let { newToken ->
keystoreManager.putToken(newToken)
Timber.d("Put token $newToken")
}
}.onSuccess {
emit(State.Loaded(true))
}.onFailure {
emit(State.Loaded(false))
}
} ?: emit(State.Loaded(false))
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), State.Loading)
}
init {
keystoreManager.createKeystore()
}
sealed interface State {
data object Loading : State
data class Loaded(val isLoggedIn: Boolean) : State
}
}

@ -0,0 +1,38 @@
package fr.iut.alldev.allin.ui.splash.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import fr.iut.alldev.allin.R
import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.core.Icon
@Composable
fun SplashScreenContent() {
Box(
modifier = Modifier
.fillMaxSize()
.background(AllInTheme.colors.allInLoginGradient),
contentAlignment = Alignment.Center
) {
Icon(
painter = painterResource(R.drawable.allin),
contentDescription = null,
tint = AllInTheme.colors.white,
modifier = Modifier.fillMaxSize(.25f)
)
}
}
@Preview
@Composable
private fun SplashScreenContentPreview() {
AllInTheme {
SplashScreenContent()
}
}

@ -1,56 +1,15 @@
package fr.iut.alldev.allin.ui.welcome
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import fr.iut.alldev.allin.R
import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.core.AllInButton
import fr.iut.alldev.allin.ui.core.AllInLoading
import fr.iut.alldev.allin.ui.welcome.components.WelcomeScreenContent
@Composable
fun WelcomeScreen(
navigateToRegister: () -> Unit,
navigateToLogin: () -> Unit,
navigateToDashboard: () -> Unit,
viewModel: WelcomeScreenViewModel = hiltViewModel()
) {
val loading by remember { viewModel.loading }
LaunchedEffect(viewModel) {
viewModel.tryAutoLogin(navigateToDashboard)
}
WelcomeScreenContent(
navigateToRegister = navigateToRegister,
navigateToLogin = navigateToLogin,
loading = loading
navigateToLogin = navigateToLogin
)
}

@ -1,38 +0,0 @@
package fr.iut.alldev.allin.ui.welcome
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import fr.iut.alldev.allin.data.repository.UserRepository
import fr.iut.alldev.allin.keystore.AllInKeystoreManager
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class WelcomeScreenViewModel @Inject constructor(
private val keystoreManager: AllInKeystoreManager,
private val userRepository: UserRepository
) : ViewModel() {
var loading = mutableStateOf(false)
fun tryAutoLogin(onSuccess: () -> Unit) {
viewModelScope.launch {
loading.value = true
keystoreManager.createKeystore()
keystoreManager.getToken()?.let { token ->
runCatching {
userRepository
.login(token)
?.let { newToken ->
keystoreManager.putToken(newToken)
}
onSuccess()
}
}
loading.value = false
}
}
}

@ -13,8 +13,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeContentPadding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@ -31,13 +29,11 @@ import androidx.compose.ui.unit.sp
import fr.iut.alldev.allin.R
import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.core.AllInButton
import fr.iut.alldev.allin.ui.core.AllInLoading
@Composable
fun WelcomeScreenContent(
navigateToRegister: () -> Unit,
navigateToLogin: () -> Unit,
loading: Boolean
navigateToLogin: () -> Unit
) {
Box(
Modifier
@ -115,8 +111,6 @@ fun WelcomeScreenContent(
}
}
}
AllInLoading(visible = loading)
}
@Preview
@ -126,8 +120,7 @@ private fun WelcomeScreenContentPreview() {
AllInTheme {
WelcomeScreenContent(
navigateToRegister = { },
navigateToLogin = { },
loading = false
navigateToLogin = { }
)
}
}

@ -71,7 +71,11 @@
<string name="End_registration_date">Date de fin des inscriptions</string>
<string name="End_bet_date">Date de fin du BET</string>
<string name="Bet_privacy">Confidentialité du bet</string>
<string name="n_friends_available">%d amis disponibles</string>
<plurals name="n_friends_available">
<item quantity="one">%d ami disponibles</item>
<item quantity="other">%d amis disponibles</item>
<item quantity="many">%d amis disponibles</item>
</plurals>
<string name="public_bottom_text_1">Votre bet sera visible par tous les utilisateurs.</string>
<string name="public_bottom_text_2">Tout le monde pourra rejoindre le BET.</string>
<string name="private_bottom_text_1">Votre bet sera visible uniquement par vos amis.</string>
@ -136,4 +140,7 @@
<string name="bet_result_odds">Cote totale</string>
<string name="bet_result_stake">Mise</string>
<string name="bet_result_winnings">Gains</string>
<!--Ranking-->
<string name="ranking_title">Classement</string>
</resources>

@ -74,7 +74,10 @@
<string name="End_registration_date">Registration end date</string>
<string name="End_bet_date">Bet end date</string>
<string name="Bet_privacy">Bet privacy</string>
<string name="n_friends_available">%d friends available</string>
<plurals name="n_friends_available">
<item quantity="one">%d friend available</item>
<item quantity="other">%d friends available</item>
</plurals>
<string name="public_bottom_text_1">Your bet will be visible by all the users.</string>
<string name="public_bottom_text_2">Everyone will be able to join the bet.</string>
<string name="private_bottom_text_1">Your bet will only be visible by your friends.</string>
@ -137,5 +140,7 @@
<string name="bet_result_stake">Stake</string>
<string name="bet_result_winnings">Winnings</string>
<!--Ranking-->
<string name="ranking_title">Ranking</string>
</resources>
Loading…
Cancel
Save