Merge pull request #84 from SELab1/screenrefactor

Screenrefactor
This commit is contained in:
brreynie 2023-04-24 16:31:12 +02:00 committed by GitHub Enterprise
commit c73c06b11c
21 changed files with 854 additions and 372 deletions

2
.idea/compiler.xml generated
View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="11" />
<bytecodeTargetLevel target="17" />
</component>
</project>

2
.idea/kotlinc.xml generated
View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.8.0-release" />
<option name="version" value="1.7.0" />
</component>
</project>

3
.idea/misc.xml generated
View file

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="Android Studio default JDK" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View file

@ -2,7 +2,13 @@ package be.ugent.sel.studeez
import android.content.res.Resources
import androidx.compose.foundation.layout.padding
import androidx.compose.material.*
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
import androidx.compose.material.ScaffoldState
import androidx.compose.material.Snackbar
import androidx.compose.material.SnackbarHost
import androidx.compose.material.Surface
import androidx.compose.material.rememberScaffoldState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.remember
@ -11,22 +17,24 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.navigation.NavGraphBuilder
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import be.ugent.sel.studeez.common.composable.drawer.DrawerViewModel
import be.ugent.sel.studeez.common.composable.navbar.NavigationBarViewModel
import be.ugent.sel.studeez.common.snackbar.SnackbarManager
import be.ugent.sel.studeez.navigation.StudeezDestinations
import be.ugent.sel.studeez.screens.home.HomeScreen
import be.ugent.sel.studeez.screens.log_in.LoginScreen
import be.ugent.sel.studeez.screens.session.SessionScreen
import be.ugent.sel.studeez.screens.profile.EditProfileScreen
import be.ugent.sel.studeez.screens.profile.ProfileScreen
import be.ugent.sel.studeez.screens.sign_up.SignUpScreen
import be.ugent.sel.studeez.screens.splash.SplashScreen
import be.ugent.sel.studeez.screens.timer_overview.TimerOverviewScreen
import be.ugent.sel.studeez.screens.timer_selection.TimerSelectionScreen
import be.ugent.sel.studeez.screens.home.HomeRoute
import be.ugent.sel.studeez.screens.log_in.LoginRoute
import be.ugent.sel.studeez.screens.profile.EditProfileRoute
import be.ugent.sel.studeez.screens.profile.ProfileRoute
import be.ugent.sel.studeez.screens.session.SessionRoute
import be.ugent.sel.studeez.screens.sign_up.SignUpRoute
import be.ugent.sel.studeez.screens.splash.SplashRoute
import be.ugent.sel.studeez.screens.timer_overview.TimerOverviewRoute
import be.ugent.sel.studeez.screens.timer_selection.TimerSelectionRoute
import be.ugent.sel.studeez.ui.theme.StudeezTheme
import kotlinx.coroutines.CoroutineScope
@ -48,13 +56,7 @@ fun StudeezApp() {
},
scaffoldState = appState.scaffoldState
) { innerPaddingModifier ->
NavHost(
navController = appState.navController,
startDestination = StudeezDestinations.SPLASH_SCREEN,
modifier = Modifier.padding(innerPaddingModifier)
) {
studeezGraph(appState)
}
StudeezNavGraph(appState, Modifier.padding(innerPaddingModifier))
}
}
}
@ -79,60 +81,91 @@ fun resources(): Resources {
return LocalContext.current.resources
}
fun NavGraphBuilder.studeezGraph(appState: StudeezAppstate) {
@Composable
fun StudeezNavGraph(
appState: StudeezAppstate,
modifier: Modifier,
) {
val drawerViewModel: DrawerViewModel = hiltViewModel()
val navBarViewModel: NavigationBarViewModel = hiltViewModel()
val goBack: () -> Unit = {
appState.popUp()
NavHost(
navController = appState.navController,
startDestination = StudeezDestinations.SPLASH_SCREEN,
modifier = modifier,
) {
val goBack: () -> Unit = {
appState.popUp()
}
val open: (String) -> Unit = { route ->
appState.navigate(route)
}
val openAndPopUp: (String, String) -> Unit = { route, popUp ->
appState.navigateAndPopUp(route, popUp)
}
composable(StudeezDestinations.SPLASH_SCREEN) {
SplashRoute(openAndPopUp, viewModel = hiltViewModel())
}
composable(StudeezDestinations.LOGIN_SCREEN) {
LoginRoute(openAndPopUp, viewModel = hiltViewModel())
}
composable(StudeezDestinations.SIGN_UP_SCREEN) {
SignUpRoute(openAndPopUp, viewModel = hiltViewModel())
}
composable(StudeezDestinations.HOME_SCREEN) {
HomeRoute(
open,
openAndPopUp,
viewModel = hiltViewModel(),
drawerViewModel = drawerViewModel,
navBarViewModel = navBarViewModel,
)
}
// TODO Tasks screen
// TODO Sessions screen
composable(StudeezDestinations.PROFILE_SCREEN) {
ProfileRoute(open, openAndPopUp, viewModel = hiltViewModel())
}
composable(StudeezDestinations.TIMER_OVERVIEW_SCREEN) {
TimerOverviewRoute(
open,
openAndPopUp,
viewModel = hiltViewModel(),
drawerViewModel = drawerViewModel,
navBarViewModel = navBarViewModel,
)
}
composable(StudeezDestinations.SESSION_SCREEN) {
SessionRoute(open, viewModel = hiltViewModel())
}
// TODO Timers screen
// TODO Settings screen
// Edit screens
composable(StudeezDestinations.EDIT_PROFILE_SCREEN) {
EditProfileRoute(goBack, openAndPopUp, viewModel = hiltViewModel())
}
composable(StudeezDestinations.TIMER_SELECTION_SCREEN) {
TimerSelectionRoute(
open,
openAndPopUp,
viewModel = hiltViewModel(),
drawerViewModel = drawerViewModel,
navBarViewModel = navBarViewModel,
)
}
}
val open: (String) -> Unit = {
route -> appState.navigate(route)
}
val openAndPopUp: (String, String) -> Unit = {
route, popUp -> appState.navigateAndPopUp(route, popUp)
}
composable(StudeezDestinations.SPLASH_SCREEN) {
SplashScreen(openAndPopUp)
}
composable(StudeezDestinations.LOGIN_SCREEN) {
LoginScreen(openAndPopUp)
}
composable(StudeezDestinations.SIGN_UP_SCREEN) {
SignUpScreen(openAndPopUp)
}
composable(StudeezDestinations.HOME_SCREEN) {
HomeScreen(open, openAndPopUp)
}
// TODO Tasks screen
// TODO Sessions screen
composable(StudeezDestinations.PROFILE_SCREEN) {
ProfileScreen(open, openAndPopUp)
}
composable(StudeezDestinations.TIMER_OVERVIEW_SCREEN) {
TimerOverviewScreen(open, openAndPopUp)
}
composable(StudeezDestinations.SESSION_SCREEN) {
SessionScreen(open, openAndPopUp)
}
// TODO Timers screen
// TODO Settings screen
// Edit screens
composable(StudeezDestinations.EDIT_PROFILE_SCREEN) {
EditProfileScreen(goBack, openAndPopUp)
}
composable(StudeezDestinations.TIMER_SELECTION_SCREEN) {
TimerSelectionScreen(open, openAndPopUp)
}
}
}

View file

@ -2,17 +2,26 @@ package be.ugent.sel.studeez.common.composable
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.RowScope
import androidx.compose.material.*
import androidx.compose.material.FabPosition
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.Scaffold
import androidx.compose.material.ScaffoldState
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material.rememberScaffoldState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.tooling.preview.Preview
import be.ugent.sel.studeez.R
import be.ugent.sel.studeez.resources
import be.ugent.sel.studeez.screens.drawer.Drawer
import be.ugent.sel.studeez.screens.navbar.NavigationBar
import be.ugent.sel.studeez.common.composable.drawer.Drawer
import be.ugent.sel.studeez.common.composable.drawer.DrawerActions
import be.ugent.sel.studeez.common.composable.navbar.NavigationBar
import be.ugent.sel.studeez.common.composable.navbar.NavigationBarActions
import be.ugent.sel.studeez.ui.theme.StudeezTheme
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@ -20,8 +29,8 @@ import kotlinx.coroutines.launch
@Composable
fun PrimaryScreenTemplate(
title: String,
open: (String) -> Unit,
openAndPopUp: (String, String) -> Unit,
drawerActions: DrawerActions,
navigationBarActions: NavigationBarActions,
action: @Composable RowScope.() -> Unit = {},
content: @Composable (PaddingValues) -> Unit
) {
@ -31,26 +40,28 @@ fun PrimaryScreenTemplate(
Scaffold(
scaffoldState = scaffoldState,
topBar = { TopAppBar(
title = { Text(text = title) },
navigationIcon = {
IconButton(onClick = {
coroutineScope.launch { scaffoldState.drawerState.open() }
}) {
Icon(
imageVector = Icons.Default.Menu,
contentDescription = resources().getString(R.string.menu)
)
}
},
actions = action
) },
drawerContent = {
Drawer(open, openAndPopUp)
topBar = {
TopAppBar(
title = { Text(text = title) },
navigationIcon = {
IconButton(onClick = {
coroutineScope.launch { scaffoldState.drawerState.open() }
}) {
Icon(
imageVector = Icons.Default.Menu,
contentDescription = resources().getString(R.string.menu)
)
}
},
actions = action
)
},
bottomBar = { NavigationBar(open) },
drawerContent = {
Drawer(drawerActions)
},
bottomBar = { NavigationBar(navigationBarActions) },
floatingActionButtonPosition = FabPosition.Center,
isFloatingActionButtonDocked = true,
floatingActionButton = { CollapsedAddButton() }
@ -65,14 +76,16 @@ fun PrimaryScreenPreview() {
StudeezTheme {
PrimaryScreenTemplate(
"Preview screen",
{ _ -> {}},
{ _, _ -> {}},
{ IconButton(onClick = { /*TODO*/ }) {
Icon(
imageVector = Icons.Default.Edit,
contentDescription = "Edit"
)
}}
DrawerActions({}, {}, {}, {}, {}),
NavigationBarActions({}, {}, {}, {}),
{
IconButton(onClick = { /*TODO*/ }) {
Icon(
imageVector = Icons.Default.Edit,
contentDescription = "Edit"
)
}
},
) {}
}
}

View file

@ -0,0 +1,62 @@
package be.ugent.sel.studeez.common.composable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
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 be.ugent.sel.studeez.R
import be.ugent.sel.studeez.data.local.models.timer_info.CustomTimerInfo
import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo
@Composable
fun TimerEntry(
timerInfo: TimerInfo,
button: @Composable () -> Unit,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Column(
Modifier.padding(horizontal = 10.dp)
) {
Text(
text = timerInfo.name, fontWeight = FontWeight.Bold, fontSize = 20.sp
)
Text(
text = timerInfo.description, fontWeight = FontWeight.Light, fontSize = 15.sp
)
}
button()
}
}
@Preview
@Composable
fun TimerEntryPreview() {
val timerInfo = CustomTimerInfo(
"my preview timer", "This is the description of the timer", 60
)
TimerEntry(timerInfo = timerInfo) {
StealthButton(text = R.string.edit) {}
}
}
@Preview
@Composable
fun TimerDefaultEntryPreview() {
val timerInfo = CustomTimerInfo(
"Default preview timer", "This is the description of the timer", 60
)
TimerEntry(timerInfo = timerInfo) {}
}

View file

@ -1,7 +1,12 @@
package be.ugent.sel.studeez.screens.drawer
package be.ugent.sel.studeez.common.composable.drawer
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
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.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
@ -14,57 +19,71 @@ import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.viewmodel.compose.viewModel
import be.ugent.sel.studeez.R
import be.ugent.sel.studeez.resources
import be.ugent.sel.studeez.ui.theme.StudeezTheme
data class DrawerActions(
val onHomeButtonClick: () -> Unit,
val onTimersClick: () -> Unit,
val onSettingsClick: () -> Unit,
val onLogoutClick: () -> Unit,
val onAboutClick: () -> Unit,
)
fun getDrawerActions(
drawerViewModel: DrawerViewModel,
open: (String) -> Unit,
openAndPopUp: (String, String) -> Unit,
): DrawerActions {
return DrawerActions(
onHomeButtonClick = { drawerViewModel.onHomeButtonClick(open) },
onTimersClick = { drawerViewModel.onTimersClick(open) },
onSettingsClick = { drawerViewModel.onSettingsClick(open) },
onLogoutClick = { drawerViewModel.onLogoutClick(openAndPopUp) },
onAboutClick = { drawerViewModel.onAboutClick(open) },
)
}
@Composable
fun Drawer(
open: (String) -> Unit,
openAndPopUp: (String, String) -> Unit,
viewModel: DrawerViewModel = hiltViewModel()
drawerActions: DrawerActions,
) {
Column (
Column(
modifier = Modifier.fillMaxWidth()
) {
Column (
modifier = Modifier.fillMaxWidth().weight(1f)
Column(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
) {
DrawerEntry(
icon = Icons.Default.Home,
text = resources().getString(R.string.home)
) {
viewModel.onHomeButtonClick(open)
}
text = resources().getString(R.string.home),
onClick = drawerActions.onHomeButtonClick,
)
DrawerEntry(
icon = ImageVector.vectorResource(id = R.drawable.ic_timer),
text = resources().getString(R.string.timers)
) {
viewModel.onTimersClick(open)
}
text = resources().getString(R.string.timers),
onClick = drawerActions.onTimersClick,
)
DrawerEntry(
icon = Icons.Default.Settings,
text = resources().getString(R.string.settings)
) {
viewModel.onSettingsClick(open)
}
text = resources().getString(R.string.settings),
onClick = drawerActions.onSettingsClick,
)
DrawerEntry(
icon = ImageVector.vectorResource(id = R.drawable.ic_logout),
text = resources().getString(R.string.log_out)
) {
viewModel.onLogoutClick(openAndPopUp)
}
text = resources().getString(R.string.log_out),
onClick = drawerActions.onLogoutClick,
)
}
DrawerEntry(
icon = Icons.Outlined.Info,
text = resources().getString(R.string.about)
) {
viewModel.onAboutClick(open)
}
text = resources().getString(R.string.about),
onClick = drawerActions.onAboutClick,
)
}
}
@ -100,11 +119,8 @@ fun DrawerEntry(
@Preview
@Composable
fun DrawerPreview() {
val drawerActions = DrawerActions({}, {}, {}, {}, {})
StudeezTheme {
Drawer(
{ _, -> {} },
{ _, _ -> {} },
hiltViewModel()
)
Drawer(drawerActions)
}
}

View file

@ -1,4 +1,4 @@
package be.ugent.sel.studeez.screens.drawer
package be.ugent.sel.studeez.common.composable.drawer
import be.ugent.sel.studeez.domain.AccountDAO
import be.ugent.sel.studeez.domain.LogService

View file

@ -1,4 +1,4 @@
package be.ugent.sel.studeez.screens.navbar
package be.ugent.sel.studeez.common.composable.navbar
import androidx.compose.material.BottomNavigation
import androidx.compose.material.BottomNavigationItem
@ -12,16 +12,32 @@ import androidx.compose.material.icons.outlined.DateRange
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import be.ugent.sel.studeez.resources
import be.ugent.sel.studeez.ui.theme.StudeezTheme
import be.ugent.sel.studeez.R.string as AppText
data class NavigationBarActions(
val onHomeClick: () -> Unit,
val onTasksClick: () -> Unit,
val onSessionsClick: () -> Unit,
val onProfileClick: () -> Unit,
)
fun getNavigationBarActions(
navigationBarViewModel: NavigationBarViewModel,
open: (String) -> Unit,
): NavigationBarActions {
return NavigationBarActions(
onHomeClick = { navigationBarViewModel.onHomeClick(open) },
onTasksClick = { navigationBarViewModel.onTasksClick(open) },
onSessionsClick = { navigationBarViewModel.onSessionsClick(open) },
onProfileClick = { navigationBarViewModel.onProfileClick(open) },
)
}
@Composable
fun NavigationBar(
open: (String) -> Unit,
viewModel: NavigationBarViewModel = hiltViewModel()
navigationBarActions: NavigationBarActions,
) {
// TODO Pass functions and new screens.
// TODO Pass which screen is selected.
@ -33,31 +49,43 @@ fun NavigationBar(
icon = { Icon(imageVector = Icons.Default.List, resources().getString(AppText.home)) },
label = { Text(text = resources().getString(AppText.home)) },
selected = false, // TODO
onClick = { viewModel.onHomeClick(open) }
onClick = navigationBarActions.onHomeClick
)
BottomNavigationItem(
icon = { Icon(imageVector = Icons.Default.Check, resources().getString(AppText.tasks)) },
icon = {
Icon(
imageVector = Icons.Default.Check, resources().getString(AppText.tasks)
)
},
label = { Text(text = resources().getString(AppText.tasks)) },
selected = false, // TODO
onClick = { viewModel.onTasksClick(open) }
onClick = navigationBarActions.onTasksClick
)
// Hack to space the entries in the navigation bar, make space for fab
BottomNavigationItem(icon = {}, onClick = {}, selected = false)
BottomNavigationItem(
icon = { Icon(imageVector = Icons.Outlined.DateRange, resources().getString(AppText.sessions)) },
icon = {
Icon(
imageVector = Icons.Outlined.DateRange, resources().getString(AppText.sessions)
)
},
label = { Text(text = resources().getString(AppText.sessions)) },
selected = false, // TODO
onClick = { viewModel.onSessionsClick(open) }
onClick = navigationBarActions.onSessionsClick
)
BottomNavigationItem(
icon = { Icon(imageVector = Icons.Default.Person, resources().getString(AppText.profile)) },
icon = {
Icon(
imageVector = Icons.Default.Person, resources().getString(AppText.profile)
)
},
label = { Text(text = resources().getString(AppText.profile)) },
selected = false, // TODO
onClick = { viewModel.onProfileClick(open) }
onClick = navigationBarActions.onProfileClick
)
}
@ -67,9 +95,6 @@ fun NavigationBar(
@Composable
fun NavigationBarPreview() {
StudeezTheme {
NavigationBar(
{ _ -> {} },
hiltViewModel()
)
NavigationBar(NavigationBarActions({}, {}, {}, {}))
}
}

View file

@ -1,4 +1,4 @@
package be.ugent.sel.studeez.screens.navbar
package be.ugent.sel.studeez.common.composable.navbar
import be.ugent.sel.studeez.domain.AccountDAO
import be.ugent.sel.studeez.domain.LogService

View file

@ -6,37 +6,69 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Person
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.compose.ui.tooling.preview.Preview
import be.ugent.sel.studeez.R
import be.ugent.sel.studeez.common.composable.BasicButton
import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate
import be.ugent.sel.studeez.common.composable.drawer.DrawerActions
import be.ugent.sel.studeez.common.composable.drawer.DrawerViewModel
import be.ugent.sel.studeez.common.composable.drawer.getDrawerActions
import be.ugent.sel.studeez.common.composable.navbar.NavigationBarActions
import be.ugent.sel.studeez.common.composable.navbar.NavigationBarViewModel
import be.ugent.sel.studeez.common.composable.navbar.getNavigationBarActions
import be.ugent.sel.studeez.common.ext.basicButton
import be.ugent.sel.studeez.resources
@Composable
fun HomeScreen(
fun HomeRoute(
open: (String) -> Unit,
openAndPopUp: (String, String) -> Unit,
viewModel: HomeViewModel = hiltViewModel()
viewModel: HomeViewModel,
drawerViewModel: DrawerViewModel,
navBarViewModel: NavigationBarViewModel,
) {
HomeScreen(
onStartSessionClick = { viewModel.onStartSessionClick(open) },
drawerActions = getDrawerActions(drawerViewModel, open, openAndPopUp),
navigationBarActions = getNavigationBarActions(navBarViewModel, open),
)
}
@Composable
fun HomeScreen(
onStartSessionClick: () -> Unit,
drawerActions: DrawerActions,
navigationBarActions: NavigationBarActions,
) {
PrimaryScreenTemplate(
title = resources().getString(R.string.home),
open = open,
openAndPopUp = openAndPopUp,
drawerActions = drawerActions,
navigationBarActions = navigationBarActions,
action = { FriendsAction() }
) {
BasicButton(R.string.start_session, Modifier.basicButton()) {
viewModel.onStartSessionClick(open)
onStartSessionClick()
}
}
}
@Composable
fun FriendsAction () {
fun FriendsAction() {
IconButton(onClick = { /*TODO*/ }) {
Icon(
imageVector = Icons.Default.Person,
contentDescription = resources().getString(R.string.friends)
)
}
}
}
@Preview
@Composable
fun HomeScreenPreview() {
HomeScreen(
onStartSessionClick = {},
drawerActions = DrawerActions({}, {}, {}, {}, {}),
navigationBarActions = NavigationBarActions({}, {}, {}, {})
)
}

View file

@ -10,7 +10,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.compose.ui.tooling.preview.Preview
import be.ugent.sel.studeez.common.composable.*
import be.ugent.sel.studeez.common.ext.basicButton
import be.ugent.sel.studeez.common.ext.fieldModifier
@ -18,14 +18,48 @@ import be.ugent.sel.studeez.common.ext.textButton
import be.ugent.sel.studeez.resources
import be.ugent.sel.studeez.R.string as AppText
data class LoginScreenActions(
val onEmailChange: (String) -> Unit,
val onPasswordChange: (String) -> Unit,
val onSignUpClick: () -> Unit,
val onSignInClick: () -> Unit,
val onForgotPasswordClick: () -> Unit,
)
fun getLoginScreenActions(
viewModel: LoginViewModel,
openAndPopUp: (String, String) -> Unit,
): LoginScreenActions {
return LoginScreenActions(
onEmailChange = { viewModel.onEmailChange(it) },
onPasswordChange = { viewModel.onPasswordChange(it) },
onSignUpClick = { viewModel.onSignUpClick(openAndPopUp) },
onSignInClick = { viewModel.onSignInClick(openAndPopUp) },
onForgotPasswordClick = { viewModel.onForgotPasswordClick() }
)
}
@Composable
fun LoginScreen(
fun LoginRoute(
openAndPopUp: (String, String) -> Unit,
modifier: Modifier = Modifier,
viewModel: LoginViewModel = hiltViewModel()
viewModel: LoginViewModel,
) {
val uiState by viewModel.uiState
LoginScreen(
modifier = modifier,
uiState = uiState,
loginScreenActions = getLoginScreenActions(viewModel = viewModel, openAndPopUp)
)
}
@Composable
fun LoginScreen(
modifier: Modifier = Modifier,
uiState: LoginUiState,
loginScreenActions: LoginScreenActions,
) {
SimpleScreenTemplate(title = resources().getString(AppText.sign_in)) {
Column(
modifier = modifier
@ -35,18 +69,42 @@ fun LoginScreen(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
EmailField(uiState.email, viewModel::onEmailChange, Modifier.fieldModifier())
PasswordField(uiState.password, viewModel::onPasswordChange, Modifier.fieldModifier())
EmailField(
uiState.email,
loginScreenActions.onEmailChange,
Modifier.fieldModifier()
)
PasswordField(
uiState.password,
loginScreenActions.onPasswordChange,
Modifier.fieldModifier()
)
BasicButton(
AppText.sign_in,
Modifier.basicButton(),
onClick = loginScreenActions.onSignInClick,
)
BasicButton(AppText.sign_in, Modifier.basicButton()) { viewModel.onSignInClick(openAndPopUp) }
BasicTextButton(
AppText.not_already_user,
Modifier.textButton(),
action = loginScreenActions.onSignUpClick,
)
BasicTextButton(AppText.not_already_user, Modifier.textButton()) {
viewModel.onNotAlreadyUser(openAndPopUp)
}
BasicTextButton(AppText.forgot_password, Modifier.textButton()) {
viewModel.onForgotPasswordClick()
}
BasicTextButton(
AppText.forgot_password,
Modifier.textButton(),
action = loginScreenActions.onForgotPasswordClick,
)
}
}
}
@Preview
@Composable
fun LoginScreenPreview() {
LoginScreen(
uiState = LoginUiState(),
loginScreenActions = LoginScreenActions({}, {}, {}, {}, {})
)
}

View file

@ -63,7 +63,7 @@ class LoginViewModel @Inject constructor(
}
}
fun onNotAlreadyUser(openAndPopUp: (String, String) -> Unit) {
fun onSignUpClick(openAndPopUp: (String, String) -> Unit) {
openAndPopUp(SIGN_UP_SCREEN, LOGIN_SCREEN)
}
}

View file

@ -14,14 +14,43 @@ import be.ugent.sel.studeez.common.ext.textButton
import be.ugent.sel.studeez.resources
import be.ugent.sel.studeez.ui.theme.StudeezTheme
data class EditProfileActions(
val onUserNameChange: (String) -> Unit,
val onSaveClick: () -> Unit,
val onDeleteClick: () -> Unit
)
fun getEditProfileActions(
viewModel: ProfileEditViewModel,
openAndPopUp: (String, String) -> Unit,
): EditProfileActions {
return EditProfileActions(
onUserNameChange = { viewModel.onUsernameChange(it) },
onSaveClick = { viewModel.onSaveClick() },
onDeleteClick = { viewModel.onDeleteClick(openAndPopUp) },
)
}
@Composable
fun EditProfileRoute(
goBack: () -> Unit,
openAndPopUp: (String, String) -> Unit,
viewModel: ProfileEditViewModel,
) {
val uiState by viewModel.uiState
EditProfileScreen(
goBack = goBack,
uiState = uiState,
editProfileActions = getEditProfileActions(viewModel, openAndPopUp)
)
}
@Composable
fun EditProfileScreen(
goBack: () -> Unit,
openAndPopUp: (String, String) -> Unit,
viewModel: ProfileEditViewModel = hiltViewModel()
uiState: ProfileEditUiState,
editProfileActions: EditProfileActions,
) {
val uiState by viewModel.uiState
SecondaryScreenTemplate(
title = resources().getString(R.string.editing_profile),
popUp = goBack
@ -29,16 +58,19 @@ fun EditProfileScreen(
Column {
LabelledInputField(
value = uiState.username,
onNewValue = viewModel::onUsernameChange,
onNewValue = editProfileActions.onUserNameChange,
label = R.string.username
)
BasicTextButton(text = R.string.save, Modifier.textButton()) {
viewModel.onSaveClick()
}
BasicTextButton(text = R.string.delete_profile, Modifier.textButton()) {
viewModel.onDeleteClick(openAndPopUp)
}
BasicTextButton(
text = R.string.save,
Modifier.textButton(),
action = editProfileActions.onSaveClick
)
BasicTextButton(
text = R.string.delete_profile,
Modifier.textButton(),
action = editProfileActions.onDeleteClick
)
}
}
}
@ -47,9 +79,6 @@ fun EditProfileScreen(
@Composable
fun EditProfileScreenComposable() {
StudeezTheme {
EditProfileScreen (
{},
{_, _ -> {}}
)
EditProfileScreen({}, ProfileEditUiState(), EditProfileActions({}, {}, {}))
}
}

View file

@ -4,30 +4,68 @@ import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Edit
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.tooling.preview.Preview
import androidx.hilt.navigation.compose.hiltViewModel
import be.ugent.sel.studeez.R
import be.ugent.sel.studeez.common.composable.Headline
import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate
import be.ugent.sel.studeez.resources
import be.ugent.sel.studeez.common.composable.drawer.DrawerActions
import be.ugent.sel.studeez.common.composable.drawer.getDrawerActions
import be.ugent.sel.studeez.common.composable.navbar.NavigationBarActions
import be.ugent.sel.studeez.common.composable.navbar.getNavigationBarActions
import kotlinx.coroutines.CoroutineScope
import be.ugent.sel.studeez.R.string as AppText
data class ProfileActions(
val getUsername: suspend CoroutineScope.() -> String?,
val onEditProfileClick: () -> Unit,
)
fun getProfileActions(
viewModel: ProfileViewModel,
open: (String) -> Unit,
): ProfileActions {
return ProfileActions(
getUsername = { viewModel.getUsername() },
onEditProfileClick = { viewModel.onEditProfileClick(open) },
)
}
@Composable
fun ProfileRoute(
open: (String) -> Unit,
openAndPopUp: (String, String) -> Unit,
viewModel: ProfileViewModel,
) {
ProfileScreen(
profileActions = getProfileActions(viewModel, open),
drawerActions = getDrawerActions(hiltViewModel(), open, openAndPopUp),
navigationBarActions = getNavigationBarActions(hiltViewModel(), open),
)
}
@Composable
fun ProfileScreen(
open: (String) -> Unit,
openAndPopUp: (String, String) -> Unit,
viewModel: ProfileViewModel = hiltViewModel()
profileActions: ProfileActions,
drawerActions: DrawerActions,
navigationBarActions: NavigationBarActions,
) {
var username: String? by remember { mutableStateOf("") }
LaunchedEffect(key1 = Unit) {
username = viewModel.getUsername()
username = profileActions.getUsername(this)
}
PrimaryScreenTemplate(
title = resources().getString(AppText.profile),
open = open,
openAndPopUp = openAndPopUp,
action = { EditAction { viewModel.onEditProfileClick(open) } }
drawerActions = drawerActions,
navigationBarActions = navigationBarActions,
action = { EditAction(onClick = profileActions.onEditProfileClick) }
) {
Headline(text = (username ?: resources().getString(R.string.no_username)))
}
@ -44,4 +82,14 @@ fun EditAction(
)
}
}
@Preview
@Composable
fun ProfileScreenPreview() {
ProfileScreen(
profileActions = ProfileActions({ null }, {}),
drawerActions = DrawerActions({}, {}, {}, {}, {}),
navigationBarActions = NavigationBarActions({}, {}, {}, {})
)
}

View file

@ -12,69 +12,98 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.ui.Alignment
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import be.ugent.sel.studeez.navigation.StudeezDestinations
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import be.ugent.sel.studeez.R
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalEndlessTimer
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer.StudyState
import be.ugent.sel.studeez.navigation.StudeezDestinations
import be.ugent.sel.studeez.resources
import kotlinx.coroutines.delay
import kotlin.time.Duration.Companion.seconds
var timerEnd = false
data class SessionActions(
val getTimer: () -> FunctionalTimer,
val getTask: () -> String,
val prepareMediaPlayer: () -> Unit,
val releaseMediaPlayer: () -> Unit,
)
fun getSessionActions(
viewModel: SessionViewModel,
mediaplayer: MediaPlayer,
): SessionActions {
return SessionActions(
getTimer = viewModel::getTimer,
getTask = viewModel::getTask,
prepareMediaPlayer = mediaplayer::prepareAsync,
releaseMediaPlayer = mediaplayer::release,
)
}
@Composable
fun SessionScreen(
fun SessionRoute(
open: (String) -> Unit,
openAndPopUp: (String, String) -> Unit,
viewModel: SessionViewModel = hiltViewModel()
viewModel: SessionViewModel,
) {
val context = LocalContext.current
val uri: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val mediaplayer = MediaPlayer.create(context, uri)
val mediaplayer = MediaPlayer()
mediaplayer.setDataSource(context, uri)
mediaplayer.setOnCompletionListener {
if (!mediaplayer.isPlaying) {
mediaplayer.stop()
}
mediaplayer.stop()
if (timerEnd) {
mediaplayer.release()
// mediaplayer.release()
}
}
mediaplayer.setOnPreparedListener {
mediaplayer.start()
// mediaplayer.start()
}
SessionScreen(
open = open,
sessionActions = getSessionActions(viewModel, mediaplayer),
)
}
@Composable
fun SessionScreen(
open: (String) -> Unit,
sessionActions: SessionActions,
) {
Column(
modifier = Modifier.padding(10.dp)
) {
Timer(viewModel, mediaplayer)
modifier = Modifier.padding(10.dp)
) {
Timer(
sessionActions = sessionActions,
)
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
contentAlignment = Alignment.Center, modifier = Modifier
.fillMaxWidth()
.padding(50.dp)
) {
TextButton(
onClick = {
mediaplayer.release()
sessionActions.releaseMediaPlayer
open(StudeezDestinations.HOME_SCREEN)
// Vanaf hier ook naar report gaan als "end session" knop word ingedrukt
},
},
modifier = Modifier
.padding(horizontal = 20.dp)
.border(1.dp, Color.Red, RoundedCornerShape(32.dp))
@ -93,24 +122,30 @@ fun SessionScreen(
}
@Composable
private fun Timer(viewModel: SessionViewModel = hiltViewModel(), mediaplayer: MediaPlayer) {
private fun Timer(
sessionActions: SessionActions,
) {
var tikker by remember { mutableStateOf(false) }
LaunchedEffect(tikker) {
delay(1.seconds)
viewModel.getTimer().tick()
sessionActions.getTimer().tick()
tikker = !tikker
}
if (viewModel.getTimer().hasCurrentCountdownEnded() && !viewModel.getTimer().hasEnded()) {
mediaplayer.prepare()
if (
sessionActions.getTimer().hasCurrentCountdownEnded() && !sessionActions.getTimer()
.hasEnded()
) {
// sessionActions.prepareMediaPlayer()
}
if (!timerEnd && viewModel.getTimer().hasEnded()) {
mediaplayer.prepare()
timerEnd = true // Placeholder, vanaf hier moet het report opgestart worden en de sessie afgesloten
if (!timerEnd && sessionActions.getTimer().hasEnded()) {
// sessionActions.prepareMediaPlayer()
timerEnd =
true // Placeholder, vanaf hier moet het report opgestart worden en de sessie afgesloten
}
val hms = viewModel.getTimer().getHoursMinutesSeconds()
val hms = sessionActions.getTimer().getHoursMinutesSeconds()
Column {
Text(
text = "${hms.hours} : ${hms.minutes} : ${hms.seconds}",
@ -121,13 +156,12 @@ private fun Timer(viewModel: SessionViewModel = hiltViewModel(), mediaplayer: Me
fontWeight = FontWeight.Bold,
fontSize = 40.sp,
)
val stateString: String = when (viewModel.getTimer().view) {
val stateString: String = when (sessionActions.getTimer().view) {
StudyState.DONE -> resources().getString(R.string.state_done)
StudyState.FOCUS -> resources().getString(R.string.state_focus)
StudyState.BREAK -> resources().getString(R.string.state_take_a_break)
StudyState.FOCUS_REMAINING ->
(viewModel.getTimer() as FunctionalPomodoroTimer?)?.breaksRemaining?.let {
resources().getQuantityString(R.plurals.state_focus_remaining, it, it)
StudyState.FOCUS_REMAINING -> (sessionActions.getTimer() as FunctionalPomodoroTimer?)?.breaksRemaining?.let {
resources().getQuantityString(R.plurals.state_focus_remaining, it, it)
}.toString()
}
@ -140,8 +174,7 @@ private fun Timer(viewModel: SessionViewModel = hiltViewModel(), mediaplayer: Me
)
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
contentAlignment = Alignment.Center, modifier = Modifier
.fillMaxWidth()
.padding(50.dp)
) {
@ -152,16 +185,28 @@ private fun Timer(viewModel: SessionViewModel = hiltViewModel(), mediaplayer: Me
.background(Color.Blue, RoundedCornerShape(32.dp))
) {
Text(
text = viewModel.getTask(),
text = sessionActions.getTask(),
color = Color.White,
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier
.padding(vertical = 4.dp, horizontal = 20.dp)
modifier = Modifier.padding(vertical = 4.dp, horizontal = 20.dp)
)
}
}
}
}
@Preview
@Composable
fun TimerPreview() {
Timer(sessionActions = SessionActions({ FunctionalEndlessTimer() }, { "Preview" }, {}, {}))
}
}
@Preview
@Composable
fun SessionPreview() {
SessionScreen(
open = {},
sessionActions = SessionActions({ FunctionalEndlessTimer() }, { "Preview" }, {}, {})
)
}

View file

@ -10,23 +10,64 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.hilt.navigation.compose.hiltViewModel
import be.ugent.sel.studeez.common.composable.*
import androidx.compose.ui.tooling.preview.Preview
import be.ugent.sel.studeez.common.composable.BasicButton
import be.ugent.sel.studeez.common.composable.BasicTextButton
import be.ugent.sel.studeez.common.composable.EmailField
import be.ugent.sel.studeez.common.composable.PasswordField
import be.ugent.sel.studeez.common.composable.RepeatPasswordField
import be.ugent.sel.studeez.common.composable.SimpleScreenTemplate
import be.ugent.sel.studeez.common.composable.UsernameField
import be.ugent.sel.studeez.common.ext.basicButton
import be.ugent.sel.studeez.common.ext.fieldModifier
import be.ugent.sel.studeez.common.ext.textButton
import be.ugent.sel.studeez.resources
import be.ugent.sel.studeez.R.string as AppText
data class SignUpActions(
val onUserNameChange: (String) -> Unit,
val onEmailChange: (String) -> Unit,
val onPasswordChange: (String) -> Unit,
val onRepeatPasswordChange: (String) -> Unit,
val onSignUpClick: () -> Unit,
val onLoginClick: () -> Unit,
)
fun getSignUpActions(
viewModel: SignUpViewModel,
openAndPopUp: (String, String) -> Unit,
): SignUpActions {
return SignUpActions(
onUserNameChange = { viewModel.onUsernameChange(it) },
onEmailChange = { viewModel.onEmailChange(it) },
onPasswordChange = { viewModel.onPasswordChange(it) },
onRepeatPasswordChange = { viewModel.onRepeatPasswordChange(it) },
onSignUpClick = { viewModel.onSignUpClick(openAndPopUp) },
onLoginClick = { viewModel.onLoginClick(openAndPopUp) },
)
}
@Composable
fun SignUpScreen(
fun SignUpRoute(
openAndPopUp: (String, String) -> Unit,
modifier: Modifier = Modifier,
viewModel: SignUpViewModel = hiltViewModel()
viewModel: SignUpViewModel,
) {
val uiState by viewModel.uiState
val fieldModifier = Modifier.fieldModifier()
SignUpScreen(
modifier = modifier,
uiState,
getSignUpActions(viewModel, openAndPopUp)
)
}
@Composable
fun SignUpScreen(
modifier: Modifier = Modifier,
uiState: SignUpUiState,
signUpActions: SignUpActions,
) {
val fieldModifier = Modifier.fieldModifier()
SimpleScreenTemplate(title = resources().getString(AppText.create_account)) {
Column(
modifier = modifier
@ -36,40 +77,45 @@ fun SignUpScreen(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
UsernameField(
uiState.username,
viewModel::onUsernameChange,
signUpActions.onUserNameChange,
fieldModifier
)
EmailField(
uiState.email,
viewModel::onEmailChange,
signUpActions.onEmailChange,
fieldModifier
)
PasswordField(
uiState.password,
viewModel::onPasswordChange,
signUpActions.onPasswordChange,
fieldModifier
)
RepeatPasswordField(
uiState.repeatPassword,
viewModel::onRepeatPasswordChange,
signUpActions.onRepeatPasswordChange,
fieldModifier
)
BasicButton(AppText.create_account, Modifier.basicButton()) {
viewModel.onSignUpClick(openAndPopUp)
}
BasicTextButton(AppText.already_user, Modifier.textButton()) {
viewModel.onLoginScreenClick(openAndPopUp)
}
BasicButton(
AppText.create_account,
Modifier.basicButton(),
onClick = signUpActions.onSignUpClick
)
BasicTextButton(
AppText.already_user,
Modifier.textButton(),
action = signUpActions.onLoginClick
)
}
}
}
@Preview
@Composable
fun SignUpPreview() {
SignUpScreen(
uiState = SignUpUiState(),
signUpActions = SignUpActions({}, {}, {}, {}, {}, {})
)
}

View file

@ -71,7 +71,7 @@ class SignUpViewModel @Inject constructor(
}
}
fun onLoginScreenClick(openAndPopUp: (String, String) -> Unit) {
fun onLoginClick(openAndPopUp: (String, String) -> Unit) {
openAndPopUp(LOGIN_SCREEN, SIGN_UP_SCREEN)
}
}

View file

@ -1,5 +1,6 @@
package be.ugent.sel.studeez.screens.splash
import android.window.SplashScreen
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@ -15,7 +16,7 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.compose.ui.tooling.preview.Preview
import be.ugent.sel.studeez.common.composable.BasicButton
import be.ugent.sel.studeez.common.ext.basicButton
import kotlinx.coroutines.delay
@ -24,14 +25,26 @@ import be.ugent.sel.studeez.R.string as AppText
private const val SPLASH_TIMEOUT = 500L
@Composable
fun SplashScreen(
fun SplashRoute(
openAndPopUp: (String, String) -> Unit,
modifier: Modifier = Modifier,
viewModel: SplashViewModel = hiltViewModel()
viewModel: SplashViewModel,
) {
SplashScreen(
modifier = modifier,
onAppStart = { viewModel.onAppStart(openAndPopUp) },
showError = viewModel.showError.value
)
}
@Composable
fun SplashScreen(
modifier: Modifier = Modifier,
onAppStart: () -> Unit,
showError: Boolean,
) {
Column(
modifier =
modifier
modifier = modifier
.fillMaxWidth()
.fillMaxHeight()
.background(color = MaterialTheme.colors.background)
@ -39,17 +52,37 @@ fun SplashScreen(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
if (viewModel.showError.value) {
if (showError) {
Text(text = stringResource(AppText.generic_error))
BasicButton(AppText.try_again, Modifier.basicButton()) { viewModel.onAppStart(openAndPopUp) }
BasicButton(
AppText.try_again,
Modifier.basicButton(),
onClick = onAppStart,
)
} else {
CircularProgressIndicator(color = MaterialTheme.colors.onBackground)
}
}
LaunchedEffect(true) {
delay(SPLASH_TIMEOUT)
viewModel.onAppStart(openAndPopUp)
onAppStart()
}
}
@Preview
@Composable
fun SplashPreview() {
SplashScreen(
onAppStart = {},
showError = false,
)
}
@Preview
@Composable
fun SplashErrorPreview() {
SplashScreen(
onAppStart = {},
showError = true,
)
}

View file

@ -1,122 +1,118 @@
package be.ugent.sel.studeez.screens.timer_overview
import android.annotation.SuppressLint
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
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 be.ugent.sel.studeez.R
import be.ugent.sel.studeez.common.composable.BasicButton
import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate
import be.ugent.sel.studeez.common.composable.StealthButton
import be.ugent.sel.studeez.common.composable.TimerEntry
import be.ugent.sel.studeez.common.composable.drawer.DrawerActions
import be.ugent.sel.studeez.common.composable.drawer.DrawerViewModel
import be.ugent.sel.studeez.common.composable.drawer.getDrawerActions
import be.ugent.sel.studeez.common.composable.navbar.NavigationBarActions
import be.ugent.sel.studeez.common.composable.navbar.NavigationBarViewModel
import be.ugent.sel.studeez.common.composable.navbar.getNavigationBarActions
import be.ugent.sel.studeez.common.ext.basicButton
import be.ugent.sel.studeez.data.local.models.timer_info.CustomTimerInfo
import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo
import be.ugent.sel.studeez.resources
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
data class TimerOverviewActions(
val getUserTimers: () -> Flow<List<TimerInfo>>,
val getDefaultTimers: () -> List<TimerInfo>,
val onEditClick: (TimerInfo) -> Unit,
)
fun getTimerOverviewActions(
viewModel: TimerOverviewViewModel,
): TimerOverviewActions {
return TimerOverviewActions(
getUserTimers = viewModel::getUserTimers,
getDefaultTimers = viewModel::getDefaultTimers,
onEditClick = { viewModel.update(it) },
)
}
@Composable
fun TimerOverviewRoute(
open: (String) -> Unit,
openAndPopUp: (String, String) -> Unit,
viewModel: TimerOverviewViewModel,
drawerViewModel: DrawerViewModel,
navBarViewModel: NavigationBarViewModel,
) {
TimerOverviewScreen(
timerOverviewActions = getTimerOverviewActions(viewModel),
drawerActions = getDrawerActions(drawerViewModel, open, openAndPopUp),
navigationBarActions = getNavigationBarActions(navBarViewModel, open),
)
}
@Composable
fun TimerOverviewScreen(
open: (String) -> Unit,
openAndPopUp: (String, String) -> Unit,
viewModel: TimerOverviewViewModel = hiltViewModel()
timerOverviewActions: TimerOverviewActions,
drawerActions: DrawerActions,
navigationBarActions: NavigationBarActions,
) {
val timers = viewModel.getUserTimers().collectAsState(initial = emptyList())
val timers = timerOverviewActions.getUserTimers().collectAsState(initial = emptyList())
// TODO moet geen primary screen zijn: geen navbar nodig
PrimaryScreenTemplate(
title = resources().getString(R.string.timers),
open = open,
openAndPopUp = openAndPopUp
drawerActions = drawerActions,
navigationBarActions = navigationBarActions,
) {
Column {
LazyColumn(
verticalArrangement = Arrangement.spacedBy(7.dp)
) {
// Default Timers, cannot be edited
items(viewModel.getDefaultTimers()) {
TimerEntry(timerInfo = it, canDisplay = false)
items(timerOverviewActions.getDefaultTimers()) {
TimerEntry(timerInfo = it) {}
}
// User timers, can be edited
items(timers.value) {
TimerEntry(timerInfo = it, true, R.string.edit) { timerInfo ->
viewModel.update(timerInfo)
items(timers.value) { timerInfo ->
TimerEntry(
timerInfo = timerInfo,
) {
StealthButton(
text = R.string.edit,
onClick = { timerOverviewActions.onEditClick(timerInfo) }
)
}
}
}
BasicButton(R.string.add_timer, Modifier.basicButton()) {
// TODO
}
}
}
}
@Composable
fun TimerEntry(
timerInfo: TimerInfo,
canDisplay: Boolean,
@StringRes buttonName: Int = -1,
buttonFunction: (TimerInfo) -> Unit = {}
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Column(
Modifier.padding(horizontal = 10.dp)
) {
Text(
text = timerInfo.name,
fontWeight = FontWeight.Bold,
fontSize = 20.sp
)
Text(
text = timerInfo.description,
fontWeight = FontWeight.Light,
fontSize = 15.sp
)
}
if (canDisplay) {
StealthButton(buttonName) {
buttonFunction(timerInfo)
}
}
}
}
@SuppressLint("UnusedMaterialScaffoldPaddingParameter")
@Preview
@Composable
fun TimerEntryPreview() {
val timerInfo = CustomTimerInfo(
"my preview timer",
"This is the description of the timer",
60
fun TimerOverviewPreview() {
val customTimer = CustomTimerInfo(
"my preview timer", "This is the description of the timer", 60
)
Scaffold() {
Column() {
TimerEntry(timerInfo = timerInfo, true, buttonName = R.string.edit) { }
TimerEntry(timerInfo = timerInfo, true, buttonName = R.string.edit) { }
TimerEntry(timerInfo = timerInfo, true, buttonName = R.string.edit) { }
}
}
}
TimerOverviewScreen(
timerOverviewActions = TimerOverviewActions(
{ flowOf() },
{ listOf(customTimer, customTimer) },
{}),
drawerActions = DrawerActions({}, {}, {}, {}, {}),
navigationBarActions = NavigationBarActions({}, {}, {}, {})
)
}

View file

@ -3,42 +3,89 @@ package be.ugent.sel.studeez.screens.timer_selection
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import be.ugent.sel.studeez.R
import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate
import be.ugent.sel.studeez.common.composable.StealthButton
import be.ugent.sel.studeez.common.composable.TimerEntry
import be.ugent.sel.studeez.common.composable.drawer.DrawerActions
import be.ugent.sel.studeez.common.composable.drawer.DrawerViewModel
import be.ugent.sel.studeez.common.composable.drawer.getDrawerActions
import be.ugent.sel.studeez.common.composable.navbar.NavigationBarActions
import be.ugent.sel.studeez.common.composable.navbar.NavigationBarViewModel
import be.ugent.sel.studeez.common.composable.navbar.getNavigationBarActions
import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo
import be.ugent.sel.studeez.resources
import be.ugent.sel.studeez.screens.timer_overview.TimerEntry
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
data class TimerSelectionActions(
val getAllTimers: () -> Flow<List<TimerInfo>>,
val startSession: (TimerInfo) -> Unit,
)
fun getTimerSelectionActions(
viewModel: TimerSelectionViewModel,
open: (String) -> Unit,
): TimerSelectionActions {
return TimerSelectionActions(
getAllTimers = viewModel::getAllTimers,
startSession = { viewModel.startSession(open, it) },
)
}
@Composable
fun TimerSelectionRoute(
open: (String) -> Unit,
openAndPopUp: (String, String) -> Unit,
viewModel: TimerSelectionViewModel,
drawerViewModel: DrawerViewModel,
navBarViewModel: NavigationBarViewModel,
) {
TimerSelectionScreen(
timerSelectionActions = getTimerSelectionActions(viewModel, open),
drawerActions = getDrawerActions(drawerViewModel, open, openAndPopUp),
navigationBarActions = getNavigationBarActions(navBarViewModel, open),
)
}
@Composable
fun TimerSelectionScreen(
open: (String) -> Unit,
openAndPopUp: (String, String) -> Unit,
viewModel: TimerSelectionViewModel = hiltViewModel()
timerSelectionActions: TimerSelectionActions,
drawerActions: DrawerActions,
navigationBarActions: NavigationBarActions,
) {
val timers = viewModel.getAllTimers().collectAsState(initial = emptyList())
val timers = timerSelectionActions.getAllTimers().collectAsState(initial = emptyList())
PrimaryScreenTemplate(
title = resources().getString(R.string.timers),
open = open,
openAndPopUp = openAndPopUp,
drawerActions = drawerActions,
navigationBarActions = navigationBarActions,
) {
LazyColumn(verticalArrangement = Arrangement.spacedBy(7.dp)) {
// All timers
items(timers.value) {
items(timers.value) { timerInfo ->
TimerEntry(
timerInfo = it,
canDisplay = true,
buttonName = R.string.start
) { timerInfo ->
viewModel.startSession(open, timerInfo)
timerInfo = timerInfo,
) {
StealthButton(
text = R.string.start,
onClick = { timerSelectionActions.startSession(timerInfo) }
)
}
}
}
}
}
@Preview
@Composable
fun TimerSelectionPreview() {
TimerSelectionScreen(
timerSelectionActions = TimerSelectionActions({ flowOf() }, {}),
drawerActions = DrawerActions({}, {}, {}, {}, {}),
navigationBarActions = NavigationBarActions({}, {}, {}, {}),
)
}