Friends Screen + fix string resources format
continuous-integration/drone/push Build is passing Details

pull/5/head
avalin 8 months ago
parent e29c5d7152
commit 9d7061fa62

@ -5,10 +5,11 @@ import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithTag
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import fr.iut.alldev.allin.data.model.User
import fr.iut.alldev.allin.test.TestTags
import fr.iut.alldev.allin.test.mock.Bets
import fr.iut.alldev.allin.ui.MainActivity
import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.MainActivity
import fr.iut.alldev.allin.vo.bet.displayer.BetTestDisplayer
import org.junit.Before
import org.junit.Rule
@ -33,13 +34,22 @@ class BetVOTest {
}
@Test
fun testDisplayer_shouldDisplayYesNoBetUI(){
fun testDisplayer_shouldDisplayYesNoBetUI() {
//Given
val currentUser = User(
id = "1",
username = "test",
email = "test@test.fr",
coins = 120
)
//When
composeTestRule.activity.setContent {
AllInTheme{
displayer.DisplayBet(Bets.bets[0])
AllInTheme {
displayer.DisplayBet(
Bets.bets[0],
currentUser
)
}
}
//Expect
@ -49,13 +59,22 @@ class BetVOTest {
}
@Test
fun testDisplayer_shouldDisplayMatchUI(){
fun testDisplayer_shouldDisplayMatchUI() {
//Given
val currentUser = User(
id = "1",
username = "test",
email = "test@test.fr",
coins = 120
)
//When
composeTestRule.activity.setContent {
AllInTheme{
displayer.DisplayBet(Bets.bets[1])
AllInTheme {
displayer.DisplayBet(
Bets.bets[1],
currentUser
)
}
}
//Expect
@ -65,13 +84,22 @@ class BetVOTest {
}
@Test
fun testDisplayer_shouldDisplayCustomBetUI(){
fun testDisplayer_shouldDisplayCustomBetUI() {
//Given
val currentUser = User(
id = "1",
username = "test",
email = "test@test.fr",
coins = 120
)
//When
composeTestRule.activity.setContent {
AllInTheme{
displayer.DisplayBet(Bets.bets[2])
AllInTheme {
displayer.DisplayBet(
Bets.bets[2],
currentUser
)
}
}
//Expect

@ -4,23 +4,24 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import fr.iut.alldev.allin.data.model.User
import fr.iut.alldev.allin.data.model.bet.vo.BetDetail
import fr.iut.alldev.allin.test.TestTags
import fr.iut.alldev.allin.vo.bet.BetDisplayer
class BetTestDisplayer : BetDisplayer {
@Composable
override fun DisplayYesNoBet(betDetail: BetDetail) {
Text("This is a YesNo Bet", Modifier.testTag(TestTags.YES_NO_BET.tag))
override fun DisplayBinaryBet(betDetail: BetDetail, currentUser: User) {
Text("This is a Binary Bet", Modifier.testTag(TestTags.YES_NO_BET.tag))
}
@Composable
override fun DisplayMatchBet(betDetail: BetDetail) {
override fun DisplayMatchBet(betDetail: BetDetail, currentUser: User) {
Text("This is a Match Bet", Modifier.testTag(TestTags.MATCH_BET.tag))
}
@Composable
override fun DisplayCustomBet(betDetail: BetDetail) {
override fun DisplayCustomBet(betDetail: BetDetail, currentUser: User) {
Text("This is a Custom Bet", Modifier.testTag(TestTags.CUSTOM_BET.tag))
}
}

@ -19,3 +19,9 @@ fun String.toFloatOrNull(locale: Locale): Float? {
val format = DecimalFormat("0.##", DecimalFormatSymbols.getInstance(locale))
return format.parse(this)?.toFloat()
}
fun String.asFallbackProfileUsername() = buildString {
this@asFallbackProfileUsername.trim().split("\\s+".toRegex(), limit = 2).forEach {
append(it.first().uppercase())
}
}

@ -22,7 +22,6 @@ import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
@ -34,6 +33,7 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import fr.iut.alldev.allin.data.ext.formatToMediumDateNoYear
import fr.iut.alldev.allin.data.ext.formatToTime
import fr.iut.alldev.allin.data.model.bet.Bet
@ -50,10 +50,10 @@ fun BetScreen(
viewModel: BetViewModel = hiltViewModel(),
selectBet: (Bet, Boolean) -> Unit,
) {
val bets by viewModel.bets.collectAsState()
val filters by viewModel.filters.collectAsState()
val bets by viewModel.bets.collectAsStateWithLifecycle()
val filters by viewModel.filters.collectAsStateWithLifecycle()
val refreshing by viewModel.isRefreshing.collectAsState()
val refreshing by viewModel.isRefreshing.collectAsStateWithLifecycle()
val pullRefreshState = rememberPullRefreshState(refreshing, { viewModel.refresh() })
val progressAnimation by animateFloatAsState(pullRefreshState.progress * 15, label = "")
@ -123,7 +123,7 @@ fun BetScreen(
title = it.phrase,
date = it.endRegisterDate.formatToMediumDateNoYear(),
time = it.endRegisterDate.formatToTime(),
players = List(3) { null },
players = emptyList(), // TODO : Players
onClickParticipate = { selectBet(it, true) },
onClickCard = { selectBet(it, false) },
modifier = Modifier

@ -24,8 +24,7 @@ class BetViewModel @Inject constructor(
) : ViewModel() {
private val _isRefreshing by lazy { MutableStateFlow(false) }
val isRefreshing: StateFlow<Boolean>
get() = _isRefreshing.asStateFlow()
val isRefreshing: StateFlow<Boolean> get() = _isRefreshing.asStateFlow()
private val _bets: MutableStateFlow<List<Bet>> by lazy {
MutableStateFlow(emptyList())
@ -45,7 +44,7 @@ class BetViewModel @Inject constructor(
MutableStateFlow(BetFilter.entries)
}
val filters by lazy { _filters.asStateFlow() }
val filters get() = _filters.asStateFlow()
init {
viewModelScope.launch {

@ -15,12 +15,12 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.res.pluralStringResource
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.theme.AllInTheme
import fr.iut.alldev.allin.ui.core.AllInBouncyCard
import fr.iut.alldev.allin.ui.core.RainbowButton
@ -35,7 +35,7 @@ fun BetScreenCard(
title: String,
date: String,
time: String,
players: List<Painter?>,
players: List<User>,
modifier: Modifier = Modifier,
onClickParticipate: () -> Unit,
onClickCard: () -> Unit,
@ -71,7 +71,7 @@ fun BetScreenCard(
.padding(7.dp),
verticalAlignment = Alignment.CenterVertically
) {
BetProfilePictureRow(pictures = players)
BetProfilePictureRow(pictures = players.map { it.username to null })
Spacer(modifier = Modifier.width(12.dp))
Text(
text = pluralStringResource(
@ -103,7 +103,9 @@ private fun BetScreenCardPreview() {
title = "Emre va réussir son TP de CI/CD mercredi?",
date = "12 Sept.",
time = "13:00",
players = List(3) { null },
players = listOf(
User(id = "", username = "Lucas D", email = "", coins = 0),
),
onClickParticipate = {},
onClickCard = {}
)

@ -48,7 +48,7 @@ import fr.iut.alldev.allin.data.model.bet.CustomBet
import fr.iut.alldev.allin.data.model.bet.MatchBet
import fr.iut.alldev.allin.data.model.bet.NO_VALUE
import fr.iut.alldev.allin.data.model.bet.YES_VALUE
import fr.iut.alldev.allin.data.model.bet.YesNoBet
import fr.iut.alldev.allin.data.model.bet.BinaryBet
import fr.iut.alldev.allin.data.model.bet.vo.BetAnswerDetail
import fr.iut.alldev.allin.data.model.bet.vo.BetDetail
import fr.iut.alldev.allin.ext.formatToSimple
@ -153,7 +153,7 @@ fun ConfirmationAnswers(
when (val bet = betDetail.bet) {
is CustomBet -> bet.possibleAnswers
is MatchBet -> listOf(bet.nameTeam1, bet.nameTeam2)
is YesNoBet -> listOf(YES_VALUE, NO_VALUE)
is BinaryBet -> listOf(YES_VALUE, NO_VALUE)
}
}
@ -295,7 +295,7 @@ private fun BetConfirmationBottomSheetContentPreview() {
AllInTheme {
BetConfirmationBottomSheetContent(
betDetail = BetDetail(
bet = YesNoBet(
bet = BinaryBet(
id = "1",
theme = "Theme",
phrase = "Phrase",

@ -96,7 +96,7 @@ class BetCreationViewModel @Inject constructor(
if (!hasError.value) {
try {
userRepository.currentUser.value?.let { currentUser ->
userRepository.currentUserState.value?.let { currentUser ->
val bet = BetFactory.createBet(
id = "",
betType = selectedBetType.value,

@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@ -16,6 +15,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import fr.iut.alldev.allin.ext.asFallbackProfileUsername
import fr.iut.alldev.allin.theme.AllInColorToken
import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.core.AllInCoinCount
@ -45,7 +45,10 @@ fun BetCreationScreenFriendLine(
checked = isSelected,
modifier = Modifier.padding(end = 7.dp)
)
ProfilePicture(modifier = Modifier.size(25.dp))
ProfilePicture(
fallback = username.asFallbackProfileUsername(),
size = 25.dp
)
Text(
text = username,
fontWeight = FontWeight.Bold,

@ -1,10 +1,10 @@
package fr.iut.alldev.allin.ui.betHistory
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import fr.iut.alldev.allin.R
import fr.iut.alldev.allin.data.ext.formatToMediumDateNoYear
import fr.iut.alldev.allin.data.ext.formatToTime
@ -16,7 +16,7 @@ fun BetCurrentScreen(
selectBet: (Bet, Boolean) -> Unit,
viewModel: BetCurrentViewModel = hiltViewModel()
) {
val bets by viewModel.bets.collectAsState()
val bets by viewModel.bets.collectAsStateWithLifecycle()
GenericHistory(
title = stringResource(id = R.string.bet_history_current_title),
bets = bets,

@ -1,10 +1,10 @@
package fr.iut.alldev.allin.ui.betHistory
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import fr.iut.alldev.allin.R
import fr.iut.alldev.allin.data.ext.formatToMediumDateNoYear
import fr.iut.alldev.allin.data.ext.formatToTime
@ -16,7 +16,7 @@ fun BetHistoryScreen(
selectBet: (Bet, Boolean) -> Unit,
viewModel: BetHistoryViewModel = hiltViewModel()
) {
val bets by viewModel.bets.collectAsState()
val bets by viewModel.bets.collectAsStateWithLifecycle()
GenericHistory(
title = stringResource(id = R.string.bet_history_title),
bets = bets,

@ -21,7 +21,7 @@ import fr.iut.alldev.allin.data.ext.formatToMediumDateNoYear
import fr.iut.alldev.allin.data.ext.formatToTime
import fr.iut.alldev.allin.data.model.bet.Bet
import fr.iut.alldev.allin.data.model.bet.BetStatus
import fr.iut.alldev.allin.data.model.bet.YesNoBet
import fr.iut.alldev.allin.data.model.bet.BinaryBet
import fr.iut.alldev.allin.theme.AllInColorToken
import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.betResult.components.BetResultBottomSheetBetCard
@ -133,7 +133,7 @@ private fun BetResultBottomSheetContentPreview() {
BetResultBottomSheetContent(
username = "Pseudo",
coinAmount = 3976,
bet = YesNoBet(
bet = BinaryBet(
id = "1",
theme = "Theme",
phrase = "Phrase",

@ -25,7 +25,7 @@ import fr.iut.alldev.allin.data.model.bet.CustomBet
import fr.iut.alldev.allin.data.model.bet.MatchBet
import fr.iut.alldev.allin.data.model.bet.NO_VALUE
import fr.iut.alldev.allin.data.model.bet.YES_VALUE
import fr.iut.alldev.allin.data.model.bet.YesNoBet
import fr.iut.alldev.allin.data.model.bet.BinaryBet
import fr.iut.alldev.allin.data.model.bet.vo.BetDetail
import fr.iut.alldev.allin.ext.formatToSimple
import fr.iut.alldev.allin.theme.AllInColorToken
@ -77,7 +77,7 @@ fun BetDetail.getParticipationAnswers(): List<@Composable RowScope.() -> Unit> {
}
}
is YesNoBet -> buildList {
is BinaryBet -> buildList {
add {
this@getParticipationAnswers.getAnswerOfResponse(YES_VALUE)?.let {
ParticipationAnswerLine(
@ -144,7 +144,7 @@ fun Bet.getAnswerFromParticipationIdx(idx: Int) =
else -> ""
}
is YesNoBet -> when (idx) {
is BinaryBet -> when (idx) {
0 -> YES_VALUE
1 -> NO_VALUE
else -> ""

@ -55,6 +55,7 @@ import fr.iut.alldev.allin.data.model.bet.NO_VALUE
import fr.iut.alldev.allin.data.model.bet.YES_VALUE
import fr.iut.alldev.allin.data.model.bet.vo.BetAnswerDetail
import fr.iut.alldev.allin.data.model.bet.vo.BetDetail
import fr.iut.alldev.allin.ext.asFallbackProfileUsername
import fr.iut.alldev.allin.ext.asPaddingValues
import fr.iut.alldev.allin.ext.bottomSheetNavigationBarsInsets
import fr.iut.alldev.allin.ext.formatToSimple
@ -295,7 +296,7 @@ class BetStatusBottomSheetBetDisplayer(
}
@Composable
override fun DisplayYesNoBet(betDetail: BetDetail, currentUser: User) {
override fun DisplayBinaryBet(betDetail: BetDetail, currentUser: User) {
DisplayBetDail(
betDetail = betDetail,
currentUser = currentUser,
@ -390,7 +391,10 @@ fun BetStatusParticipant(
horizontalArrangement = Arrangement.spacedBy(7.dp),
verticalAlignment = Alignment.CenterVertically
) {
ProfilePicture(modifier = Modifier.size(25.dp))
ProfilePicture(
fallback = username.asFallbackProfileUsername(),
size = 25.dp
)
Text(
text = username,
fontWeight = FontWeight.Bold,

@ -6,17 +6,17 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.PhotoCamera
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Icon
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.graphics.painter.Painter
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
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.dp
@ -26,6 +26,7 @@ import fr.iut.alldev.allin.theme.AllInTheme
@Composable
fun ProfilePicture(
fallback: String,
modifier: Modifier = Modifier,
image: Painter? = null,
borderWidth: Dp? = null,
@ -49,13 +50,14 @@ fun ProfilePicture(
.clip(shape)
)
} ?: run {
Icon(
imageVector = Icons.Default.PhotoCamera,
tint = AllInColorToken.white,
contentDescription = null,
Text(
text = fallback,
style = AllInTheme.typography.p2,
textAlign = TextAlign.Center,
fontSize = with(LocalDensity.current) { (size / 2).toSp() },
color = AllInColorToken.white,
modifier = Modifier
.align(Alignment.Center)
.fillMaxSize(0.5f)
.clip(shape)
)
}
@ -68,7 +70,7 @@ fun ProfilePicture(
@Composable
private fun ProfilePictureDefaultPreview() {
AllInTheme {
ProfilePicture()
ProfilePicture("LS")
}
}
@ -77,6 +79,7 @@ private fun ProfilePictureDefaultPreview() {
private fun ProfilePicturePreview() {
AllInTheme {
ProfilePicture(
fallback = "LS",
image = painterResource(id = R.drawable.money_with_wings)
)
}

@ -1,6 +1,7 @@
package fr.iut.alldev.allin.ui.core.bet
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
@ -13,24 +14,22 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import fr.iut.alldev.allin.R
import fr.iut.alldev.allin.ui.core.ProfilePicture
import fr.iut.alldev.allin.ext.asFallbackProfileUsername
import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.core.ProfilePicture
@Composable
fun BetProfilePictureRow(
pictures: List<Painter?>,
maxLength: Int = 5,
modifier: Modifier = Modifier
pictures: List<Pair<String, Painter?>>,
modifier: Modifier = Modifier,
maxLength: Int = 5
) {
val nRepeat = remember{
if (pictures.size > maxLength) maxLength else pictures.size
}
val nRepeat = remember{ pictures.size.coerceAtMost(maxLength) }
Box(
modifier.width((nRepeat*17).dp)
){
pictures.forEachIndexed { index, painter ->
Box(modifier.width((35 + (nRepeat - 1) * 17).dp)){
pictures.take(nRepeat).forEachIndexed { index, (username, painter) ->
ProfilePicture(
fallback = username.asFallbackProfileUsername(),
image = painter,
size = 35.dp,
modifier = Modifier
@ -46,24 +45,34 @@ fun BetProfilePictureRow(
@Composable
private fun BetProfilePictureRowPreview() {
AllInTheme {
BetProfilePictureRow(pictures = listOf(
painterResource(id = R.drawable.money_with_wings),
null,
painterResource(id = R.drawable.money_with_wings)
))
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center
) {
BetProfilePictureRow(
pictures = listOf("lucas" to null)
)
}
}
}
@Preview
@Composable
private fun BetProfilePictureRowMaxPreview() {
AllInTheme {
BetProfilePictureRow(pictures = listOf(
painterResource(id = R.drawable.money_with_wings),
null,
painterResource(id = R.drawable.money_with_wings),
null,
painterResource(id = R.drawable.money_with_wings),
null
))
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center
) {
BetProfilePictureRow(
pictures = listOf(
"lucas" to painterResource(id = R.drawable.money_with_wings),
"lucas" to null,
"lucas" to painterResource(id = R.drawable.money_with_wings),
"lucas" to null,
"lucas" to painterResource(id = R.drawable.money_with_wings),
"lucas" to null
)
)
}
}
}

@ -6,6 +6,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import fr.iut.alldev.allin.data.model.User
import fr.iut.alldev.allin.ui.core.AllInLoading
import fr.iut.alldev.allin.ui.friends.components.FriendsScreenContent
@ -15,7 +16,7 @@ fun FriendsScreen(
viewModel: FriendsScreenViewModel = hiltViewModel()
) {
var search by remember { viewModel.search }
val state by remember { viewModel.state }
val state by viewModel.state.collectAsStateWithLifecycle()
when (val s = state) {
is FriendsScreenViewModel.State.Loaded -> {
@ -33,8 +34,10 @@ fun FriendsScreen(
setSearch = { search = it },
onToggleDeleteFriend = {
deleted = if (deleted.contains(it)) {
viewModel.addFriend(it.username)
deleted - it
} else {
viewModel.removeFriend(it.username)
deleted + it
}
}

@ -5,150 +5,69 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import fr.iut.alldev.allin.data.model.User
import fr.iut.alldev.allin.data.repository.FriendRepository
import fr.iut.alldev.allin.keystore.AllInKeystoreManager
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@HiltViewModel
class FriendsScreenViewModel @Inject constructor(
private val friendRepository: FriendRepository,
private val keystoreManager: AllInKeystoreManager
) : ViewModel() {
val search by lazy { mutableStateOf("") }
val state by lazy { mutableStateOf<State>(State.Loading) }
private val _state by lazy { MutableStateFlow<State>(State.Loading) }
val state get() = _state.asStateFlow()
init {
viewModelScope.launch {
state.value = State.Loaded(mockFriends)
try {
_state.emit(
State.Loaded(
friends = friendRepository.getFriends(
token = keystoreManager.getTokenOrEmpty()
)
)
)
} catch (e: Exception) {
Timber.e(e)
}
}
sealed class State {
data object Loading: State()
data class Loaded(val friends: List<User>): State()
}
}
private val mockFriends 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 = "12",
username = "Owen",
email = "",
coins = 8533
),
User(
id = "22",
username = "Dave",
email = "",
coins = 6942
),
User(
id = "32",
username = "Lucas",
email = "",
coins = 3333
),
User(
id = "42",
username = "Louison",
email = "",
coins = 1970
),
User(
id = "52",
username = "Imri",
email = "",
coins = 1
),
User(
id = "13",
username = "Owen",
email = "",
coins = 8533
),
User(
id = "23",
username = "Dave",
email = "",
coins = 6942
),
User(
id = "33",
username = "Lucas",
email = "",
coins = 3333
),
User(
id = "43",
username = "Louison",
email = "",
coins = 1970
),
User(
id = "53",
username = "Imri",
email = "",
coins = 1
),
User(
id = "14",
username = "Owen",
email = "",
coins = 8533
),
User(
id = "24",
username = "Dave",
email = "",
coins = 6942
),
User(
id = "34",
username = "Lucas",
email = "",
coins = 3333
),
User(
id = "44",
username = "Louison",
email = "",
coins = 1970
),
User(
id = "54",
username = "Imri",
email = "",
coins = 1
fun addFriend(username: String) {
viewModelScope.launch {
try {
friendRepository.add(
token = keystoreManager.getTokenOrEmpty(),
username = username
)
} catch (e: Exception) {
Timber.e(e)
}
}
}
fun removeFriend(username: String) {
viewModelScope.launch {
try {
friendRepository.remove(
token = keystoreManager.getTokenOrEmpty(),
username = username
)
} catch (e: Exception) {
Timber.e(e)
}
}
}
sealed interface State {
data object Loading : State
data class Loaded(val friends: List<User>) : State
}
}

@ -13,6 +13,7 @@ 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.ext.asFallbackProfileUsername
import fr.iut.alldev.allin.theme.AllInColorToken
import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.core.AllInButton
@ -31,6 +32,7 @@ fun FriendsScreenLine(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
ProfilePicture(
fallback = username.asFallbackProfileUsername(),
size = 50.dp
)

@ -32,7 +32,7 @@ class MainViewModel @Inject constructor(
var loading = mutableStateOf(false)
val currentUser = userRepository.currentUser
val currentUser = userRepository.currentUserState
val selectedBet = mutableStateOf<BetDetail?>(null)
val dismissedEvents = mutableStateListOf<AllInEvent>()
val events = mutableStateListOf<AllInEvent>()

@ -48,6 +48,10 @@ object Routes {
const val RANKING = "RANKING"
}
object Arguments {
const val USER_ID = "USER_ID"
}
private val fadingRoutes
get() = listOf(
Routes.WELCOME,

@ -17,6 +17,7 @@ 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.ext.asFallbackProfileUsername
import fr.iut.alldev.allin.theme.AllInColorToken
import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.core.ProfilePicture
@ -34,6 +35,7 @@ fun DrawerHeader(
horizontalAlignment = Alignment.CenterHorizontally
) {
ProfilePicture(
fallback = username.asFallbackProfileUsername(),
borderWidth = 1.dp
)
Spacer(modifier = Modifier.height(12.dp))

@ -6,7 +6,7 @@ import fr.iut.alldev.allin.data.model.bet.MatchBet
import fr.iut.alldev.allin.data.model.bet.NO_VALUE
import fr.iut.alldev.allin.data.model.bet.Participation
import fr.iut.alldev.allin.data.model.bet.YES_VALUE
import fr.iut.alldev.allin.data.model.bet.YesNoBet
import fr.iut.alldev.allin.data.model.bet.BinaryBet
import fr.iut.alldev.allin.data.model.bet.vo.BetAnswerDetail
import fr.iut.alldev.allin.data.model.bet.vo.BetDetail
@ -60,7 +60,7 @@ class BetDetailPreviewProvider : PreviewParameterProvider<BetDetail> {
odds = 2.0f
)
)
is YesNoBet -> listOf(
is BinaryBet -> listOf(
BetAnswerDetail(
response = YES_VALUE,
totalStakes = 300,

@ -5,12 +5,12 @@ import fr.iut.alldev.allin.data.model.bet.Bet
import fr.iut.alldev.allin.data.model.bet.BetStatus
import fr.iut.alldev.allin.data.model.bet.CustomBet
import fr.iut.alldev.allin.data.model.bet.MatchBet
import fr.iut.alldev.allin.data.model.bet.YesNoBet
import fr.iut.alldev.allin.data.model.bet.BinaryBet
import java.time.ZonedDateTime
class BetPreviewProvider : PreviewParameterProvider<Bet> {
override val values = sequenceOf(
YesNoBet(
BinaryBet(
id = "1",
theme = "Theme",
phrase = "Phrase",

@ -5,14 +5,14 @@ import fr.iut.alldev.allin.data.model.bet.Bet
import fr.iut.alldev.allin.data.model.bet.BetStatus
import fr.iut.alldev.allin.data.model.bet.CustomBet
import fr.iut.alldev.allin.data.model.bet.MatchBet
import fr.iut.alldev.allin.data.model.bet.YesNoBet
import fr.iut.alldev.allin.data.model.bet.BinaryBet
import java.time.ZonedDateTime
class BetWithStatusPreviewProvider : PreviewParameterProvider<Bet> {
override val values = BetStatus.entries.asSequence().flatMap { status ->
sequenceOf(
YesNoBet(
BinaryBet(
id = "1",
theme = "Theme",
phrase = "Phrase",

@ -1,9 +0,0 @@
package fr.iut.alldev.allin.ui.profile
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@Composable
fun Profile(){
Text(text = "PROFILE")
}

@ -0,0 +1,32 @@
package fr.iut.alldev.allin.ui.profile
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import fr.iut.alldev.allin.ui.profile.components.ProfileScreenHeader
@Composable
fun ProfileScreen(
viewModel: ProfileViewModel = hiltViewModel()
) {
val state by viewModel.state.collectAsStateWithLifecycle()
Column {
when (val s = state) {
is ProfileViewModel.State.Loaded -> {
ProfileScreenHeader(
username = s.user.username,
totalBets = 333,
bestWin = 365,
friends = 3
)
}
ProfileViewModel.State.Loading -> {
}
}
}
}

@ -0,0 +1,42 @@
package fr.iut.alldev.allin.ui.profile
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import fr.iut.alldev.allin.data.model.User
import fr.iut.alldev.allin.data.repository.UserRepository
import fr.iut.alldev.allin.ui.navigation.Arguments
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class ProfileViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
private val userRepository: UserRepository
) : ViewModel() {
private val userId: String? = savedStateHandle[Arguments.USER_ID]
private val _state by lazy { MutableStateFlow<State>(State.Loading) }
val state: StateFlow<State> get() = _state.asStateFlow()
init {
viewModelScope.launch {
userRepository.currentUserState.value?.let { currentUser ->
if (userId == currentUser.id) {
_state.emit(State.Loaded(currentUser))
}
}
}
}
sealed interface State {
data object Loading : State
data class Loaded(val user: User) : State
}
}

@ -0,0 +1,105 @@
package fr.iut.alldev.allin.ui.profile.components
import android.content.res.Configuration
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.height
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.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.ext.asFallbackProfileUsername
import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.core.ProfilePicture
@Composable
fun ProfileScreenHeader(
username: String,
totalBets: Int,
bestWin: Int,
friends: Int
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
ProfilePicture(
fallback = username.asFallbackProfileUsername(),
size = 68.dp
)
Column(
verticalArrangement = Arrangement.SpaceEvenly,
modifier = Modifier.height(68.dp)
) {
Text(
text = username,
style = AllInTheme.typography.h2,
color = AllInTheme.colors.onMainSurface,
fontSize = 20.sp
)
Row(
horizontalArrangement = Arrangement.spacedBy(20.dp)
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(
text = totalBets.toString(),
style = AllInTheme.typography.h1,
color = AllInTheme.colors.onMainSurface,
fontSize = 14.sp,
)
Text(
text = stringResource(id = R.string.drawer_bets),
style = AllInTheme.typography.p1,
color = AllInTheme.colors.onBackground2
)
}
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(
text = bestWin.toString(),
style = AllInTheme.typography.h1,
color = AllInTheme.colors.onMainSurface,
fontSize = 14.sp
)
Text(
text = stringResource(id = R.string.drawer_best_win),
style = AllInTheme.typography.p1,
color = AllInTheme.colors.onBackground2
)
}
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(
text = friends.toString(),
style = AllInTheme.typography.h1,
color = AllInTheme.colors.onMainSurface,
fontSize = 14.sp
)
Text(
text = stringResource(id = R.string.drawer_friends),
style = AllInTheme.typography.p1,
color = AllInTheme.colors.onBackground2
)
}
}
}
}
}
@Preview(showBackground = true)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun ProfileScreenHeaderPreview() {
AllInTheme {
ProfileScreenHeader(
username = "User 1",
totalBets = 12,
bestWin = 365,
friends = 5
)
}
}

@ -22,6 +22,7 @@ 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.ext.asFallbackProfileUsername
import fr.iut.alldev.allin.theme.AllInColorToken
import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.core.AllInCard
@ -87,8 +88,9 @@ fun RankingScreenFirst(
}
}
ProfilePicture(
fallback = username.asFallbackProfileUsername(),
size = 70.dp,
modifier = Modifier
.size(70.dp)
.align(Alignment.TopCenter)
.zIndex(1f),
)

@ -15,6 +15,7 @@ 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.ext.asFallbackProfileUsername
import fr.iut.alldev.allin.theme.AllInColorToken
import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.core.AllInCard
@ -48,7 +49,10 @@ fun RankingScreenItem(
modifier = Modifier.size(width = 26.dp, height = 20.dp)
)
ProfilePicture(modifier = Modifier.size(38.dp))
ProfilePicture(
fallback = username.asFallbackProfileUsername(),
size = 38.dp
)
Spacer(modifier = Modifier.width(8.dp))

@ -22,6 +22,7 @@ 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.ext.asFallbackProfileUsername
import fr.iut.alldev.allin.theme.AllInColorToken
import fr.iut.alldev.allin.theme.AllInTheme
import fr.iut.alldev.allin.ui.core.AllInCard
@ -87,8 +88,9 @@ fun RankingScreenSecond(
}
}
ProfilePicture(
fallback = username.asFallbackProfileUsername(),
size = 56.dp,
modifier = Modifier
.size(56.dp)
.align(Alignment.TopCenter)
.zIndex(1f),
)

@ -2,9 +2,9 @@ package fr.iut.alldev.allin.vo.bet
import androidx.compose.runtime.Composable
import fr.iut.alldev.allin.data.model.User
import fr.iut.alldev.allin.data.model.bet.BinaryBet
import fr.iut.alldev.allin.data.model.bet.CustomBet
import fr.iut.alldev.allin.data.model.bet.MatchBet
import fr.iut.alldev.allin.data.model.bet.YesNoBet
import fr.iut.alldev.allin.data.model.bet.vo.BetDetail
interface BetDisplayer {
@ -13,12 +13,12 @@ interface BetDisplayer {
when (betDetail.bet) {
is CustomBet -> DisplayCustomBet(betDetail, currentUser)
is MatchBet -> DisplayMatchBet(betDetail, currentUser)
is YesNoBet -> DisplayYesNoBet(betDetail, currentUser)
is BinaryBet -> DisplayBinaryBet(betDetail, currentUser)
}
}
@Composable
fun DisplayYesNoBet(betDetail: BetDetail, currentUser: User)
fun DisplayBinaryBet(betDetail: BetDetail, currentUser: User)
@Composable
fun DisplayMatchBet(betDetail: BetDetail, currentUser: User)

@ -3,6 +3,7 @@ package fr.iut.alldev.allin.data.api
import fr.iut.alldev.allin.data.api.model.CheckUser
import fr.iut.alldev.allin.data.api.model.RequestBet
import fr.iut.alldev.allin.data.api.model.RequestBetFilters
import fr.iut.alldev.allin.data.api.model.RequestFriend
import fr.iut.alldev.allin.data.api.model.RequestParticipation
import fr.iut.alldev.allin.data.api.model.RequestUser
import fr.iut.alldev.allin.data.api.model.ResponseBet
@ -26,6 +27,9 @@ interface AllInApi {
this.toRequestBody("text/plain".toMediaTypeOrNull())
}
// USERS
// ---------------------
@POST("users/login")
suspend fun login(@Body body: CheckUser): ResponseUser
@ -35,12 +39,33 @@ interface AllInApi {
@GET("users/token")
suspend fun login(@Header("Authorization") token: String): ResponseUser
@POST("bets/add")
suspend fun createBet(@Header("Authorization") token: String, @Body body: RequestBet)
@GET("users/gift")
suspend fun dailyGift(@Header("Authorization") token: String): Int
// FRIENDS
// ---------------------
@GET("friends/add")
suspend fun getFriends(@Header("Authorization") token: String): List<ResponseUser>
@POST("friends/add")
suspend fun addFriend(
@Header("Authorization") token: String,
@Body request: RequestFriend
)
@POST("friends/delete")
suspend fun deleteFriend(
@Header("Authorization") token: String,
@Body request: RequestFriend
)
// BETS
// ---------------------
@POST("bets/add")
suspend fun createBet(@Header("Authorization") token: String, @Body body: RequestBet)
@POST("bets/gets")
suspend fun getAllBets(
@Header("Authorization") token: String,
@ -57,12 +82,6 @@ interface AllInApi {
@Body value: RequestBody
)
@GET("betdetail/get/{id}")
suspend fun getBet(
@Header("Authorization") token: String,
@Path("id") id: String
): ResponseBetDetail
@GET("bets/current")
suspend fun getBetCurrent(
@Header("Authorization") token: String
@ -78,6 +97,15 @@ interface AllInApi {
@Header("Authorization") token: String
): List<ResponseBetResultDetail>
@GET("betdetail/get/{id}")
suspend fun getBet(
@Header("Authorization") token: String,
@Path("id") id: String
): ResponseBetDetail
// PARTICIPATIONS
// ---------------------
@POST("participations/add")
suspend fun participateToBet(
@Header("Authorization") token: String,

@ -3,6 +3,7 @@ package fr.iut.alldev.allin.data.api
import fr.iut.alldev.allin.data.api.model.CheckUser
import fr.iut.alldev.allin.data.api.model.RequestBet
import fr.iut.alldev.allin.data.api.model.RequestBetFilters
import fr.iut.alldev.allin.data.api.model.RequestFriend
import fr.iut.alldev.allin.data.api.model.RequestParticipation
import fr.iut.alldev.allin.data.api.model.RequestUser
import fr.iut.alldev.allin.data.api.model.ResponseBet
@ -28,7 +29,6 @@ import java.util.UUID
class MockAllInApiException(override val message: String?) : Exception()
class MockAllInApi : AllInApi {
init {
CoroutineScope(Dispatchers.Default).launch {
while (true) {
@ -135,6 +135,27 @@ class MockAllInApi : AllInApi {
} else throw MockAllInApiException("Gift already taken today")
}
override suspend fun getFriends(token: String): List<ResponseUser> {
val user = getUserFromToken(token) ?: throw MockAllInApiException("Invalid login/password.")
return mockFriends
.filter { it.first == user.first.id }
.mapNotNull { mockUsers.find { usr -> usr.first.id == it.second }?.first }
}
override suspend fun addFriend(token: String, request: RequestFriend) {
val user = getUserFromToken(token) ?: throw MockAllInApiException("Invalid login/password.")
val requestUser =
mockUsers.find { it.first.username == request.username } ?: throw MockAllInApiException("Requested user not found")
mockFriends.add(user.first.id to requestUser.first.id)
}
override suspend fun deleteFriend(token: String, request: RequestFriend) {
val user = getUserFromToken(token) ?: throw MockAllInApiException("Invalid login/password.")
val requestUser =
mockUsers.find { it.first.username == request.username } ?: throw MockAllInApiException("Requested user not found")
mockFriends.remove(user.first.id to requestUser.first.id)
}
override suspend fun getAllBets(token: String, body: RequestBetFilters): List<ResponseBet> {
getUserFromToken(token) ?: throw MockAllInApiException("Invalid login/password.")
return mockBets
@ -399,6 +420,14 @@ class MockAllInApi : AllInApi {
private val mockWon by lazy { mutableMapOf<String, MutableList<String>>() }
private val mockGifts by lazy { mutableMapOf<String, ZonedDateTime>() }
private val mockFriends by lazy {
mutableSetOf(
"UUID 1" to "UUID 2",
"UUID 2" to "UUID 1",
"UUID 1" to "UUID 3"
)
}
}
}

@ -11,7 +11,7 @@ import fr.iut.alldev.allin.data.model.bet.CustomBet
import fr.iut.alldev.allin.data.model.bet.MatchBet
import fr.iut.alldev.allin.data.model.bet.NO_VALUE
import fr.iut.alldev.allin.data.model.bet.YES_VALUE
import fr.iut.alldev.allin.data.model.bet.YesNoBet
import fr.iut.alldev.allin.data.model.bet.BinaryBet
import fr.iut.alldev.allin.data.model.bet.vo.BetAnswerDetail
import fr.iut.alldev.allin.data.model.bet.vo.BetDetail
import fr.iut.alldev.allin.data.serialization.ZonedDateTimeSerializer
@ -34,7 +34,7 @@ data class ResponseBet(
) {
fun toBet(): Bet = when {
response.toSet() == setOf(YES_VALUE, NO_VALUE) -> {
YesNoBet(
BinaryBet(
id = id ?: "",
theme = theme,
phrase = sentenceBet,

@ -0,0 +1,8 @@
package fr.iut.alldev.allin.data.api.model
import androidx.annotation.Keep
import kotlinx.serialization.Serializable
@Keep
@Serializable
data class RequestFriend(val username: String)

@ -5,8 +5,10 @@ import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import fr.iut.alldev.allin.data.repository.BetRepository
import fr.iut.alldev.allin.data.repository.FriendRepository
import fr.iut.alldev.allin.data.repository.UserRepository
import fr.iut.alldev.allin.data.repository.impl.BetRepositoryImpl
import fr.iut.alldev.allin.data.repository.impl.FriendRepositoryImpl
import fr.iut.alldev.allin.data.repository.impl.UserRepositoryImpl
import javax.inject.Singleton
@ -20,4 +22,8 @@ abstract class RepositoryModule {
@Singleton
@Binds
abstract fun provideBetRepository(betRepositoryImpl: BetRepositoryImpl): BetRepository
@Singleton
@Binds
abstract fun provideFriendRepository(friendRepositoryImpl: FriendRepositoryImpl): FriendRepository
}

@ -20,7 +20,7 @@ class BetFactory {
): Bet =
when (betType) {
BetType.BINARY -> {
YesNoBet(
BinaryBet(
id = id,
theme = theme,
creator = creator,

@ -5,7 +5,7 @@ import java.time.ZonedDateTime
const val YES_VALUE = "Yes"
const val NO_VALUE = "No"
data class YesNoBet(
data class BinaryBet(
override val id: String,
override val creator: String,
override val theme: String,

@ -0,0 +1,9 @@
package fr.iut.alldev.allin.data.repository
import fr.iut.alldev.allin.data.model.User
abstract class FriendRepository {
abstract suspend fun getFriends(token: String): List<User>
abstract suspend fun add(token: String, username: String)
abstract suspend fun remove(token: String, username: String)
}

@ -6,16 +6,16 @@ import kotlinx.coroutines.flow.asStateFlow
abstract class UserRepository {
internal val _currentUser by lazy { MutableStateFlow<User?>(null) }
val currentUser by lazy { _currentUser.asStateFlow() }
internal val currentUser by lazy { MutableStateFlow<User?>(null) }
val currentUserState get() = currentUser.asStateFlow()
suspend fun resetCurrentUser() {
_currentUser.emit(null)
currentUser.emit(null)
}
suspend fun updateCurrentUserCoins(value: Int) {
currentUser.value?.let { user ->
_currentUser.emit(
currentUserState.value?.let { user ->
currentUser.emit(
user.copy(
coins = value
)
@ -24,9 +24,7 @@ abstract class UserRepository {
}
abstract suspend fun login(username: String, password: String): String?
abstract suspend fun login(token: String): String?
abstract suspend fun register(username: String, email: String, password: String): String?
abstract suspend fun dailyGift(token: String): Int
}

@ -0,0 +1,30 @@
package fr.iut.alldev.allin.data.repository.impl
import fr.iut.alldev.allin.data.api.AllInApi
import fr.iut.alldev.allin.data.api.AllInApi.Companion.formatBearerToken
import fr.iut.alldev.allin.data.api.model.RequestFriend
import fr.iut.alldev.allin.data.model.User
import fr.iut.alldev.allin.data.repository.FriendRepository
import javax.inject.Inject
class FriendRepositoryImpl @Inject constructor(
private val api: AllInApi
) : FriendRepository() {
override suspend fun getFriends(token: String): List<User> {
return api.getFriends(token.formatBearerToken()).map { it.toUser() }
}
override suspend fun add(token: String, username: String) {
return api.addFriend(
token = token.formatBearerToken(),
request = RequestFriend(username)
)
}
override suspend fun remove(token: String, username: String) {
return api.deleteFriend(
token = token.formatBearerToken(),
request = RequestFriend(username)
)
}
}

@ -18,17 +18,16 @@ class UserRepositoryImpl @Inject constructor(
password = password
)
)
_currentUser.emit(response.toUser())
currentUser.emit(response.toUser())
return response.token
}
override suspend fun login(token: String): String? {
val response = api.login(token = token.formatBearerToken())
_currentUser.emit(response.toUser())
currentUser.emit(response.toUser())
return response.token
}
override suspend fun register(username: String, email: String, password: String): String? {
val response = api.register(
RequestUser(
@ -37,7 +36,7 @@ class UserRepositoryImpl @Inject constructor(
password = password
)
)
_currentUser.emit(response.toUser())
currentUser.emit(response.toUser())
return response.token
}

@ -15,7 +15,7 @@ androidxSecurity = "1.1.0-alpha06"
composeBom = "2024.05.00"
composeCompiler = "1.5.5"
composePreview = "1.6.5"
composeNavigation = "2.7.6"
composeNavigation = "2.7.7"
hilt = "2.48"
hiltNavigation = "1.1.0"

Loading…
Cancel
Save