merged with current dev
This commit is contained in:
		
						commit
						b30edf6538
					
				
					 22 changed files with 849 additions and 373 deletions
				
			
		|  | @ -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) | ||||
|     } | ||||
| } | ||||
| } | ||||
|  |  | |||
|  | @ -1,8 +1,5 @@ | |||
| package be.ugent.sel.studeez.activities | ||||
| 
 | ||||
| import android.media.MediaPlayer | ||||
| import android.media.RingtoneManager | ||||
| import android.net.Uri | ||||
| import android.os.Bundle | ||||
| import androidx.activity.ComponentActivity | ||||
| import androidx.activity.compose.setContent | ||||
|  | @ -25,13 +22,7 @@ var onTimerInvisible: Job? = null | |||
| 
 | ||||
| @AndroidEntryPoint | ||||
| class MainActivity : ComponentActivity() { | ||||
| 
 | ||||
|     private var mediaPlayer: MediaPlayer? = null | ||||
|     override fun onCreate(savedInstanceState: Bundle?) { | ||||
|         val uri: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) | ||||
|         mediaPlayer = MediaPlayer.create(this, uri) | ||||
|         mediaPlayer?.isLooping = false | ||||
| 
 | ||||
|         super.onCreate(savedInstanceState) | ||||
|         setContent { | ||||
|             StudeezTheme { | ||||
|  | @ -54,16 +45,9 @@ class MainActivity : ComponentActivity() { | |||
|     } | ||||
| 
 | ||||
|     override fun onStart() { | ||||
|         mediaPlayer?.stop() | ||||
|         onTimerInvisible?.cancel() | ||||
|         super.onStart() | ||||
|     } | ||||
| 
 | ||||
|     override fun onDestroy() { | ||||
|         mediaPlayer?.stop() | ||||
|         mediaPlayer?.release() | ||||
|         super.onDestroy() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @Composable | ||||
|  |  | |||
|  | @ -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" | ||||
|                     ) | ||||
|                 } | ||||
|             }, | ||||
|         ) {} | ||||
|     } | ||||
| } | ||||
|  | @ -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) {} | ||||
| } | ||||
|  | @ -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) | ||||
|     } | ||||
| } | ||||
|  | @ -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 | ||||
|  | @ -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({}, {}, {}, {})) | ||||
|     } | ||||
| } | ||||
|  | @ -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 | ||||
|  | @ -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({}, {}, {}, {}) | ||||
|     ) | ||||
| } | ||||
|  |  | |||
|  | @ -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({}, {}, {}, {}, {}) | ||||
|     ) | ||||
| } | ||||
|  | @ -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) | ||||
|     } | ||||
| } | ||||
|  | @ -5,7 +5,6 @@ import androidx.compose.runtime.Composable | |||
| import androidx.compose.runtime.getValue | ||||
| import androidx.compose.ui.Modifier | ||||
| 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.BasicTextButton | ||||
| import be.ugent.sel.studeez.common.composable.LabelledInputField | ||||
|  | @ -14,14 +13,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 +57,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 +78,6 @@ fun EditProfileScreen( | |||
| @Composable | ||||
| fun EditProfileScreenComposable() { | ||||
|     StudeezTheme { | ||||
|         EditProfileScreen ( | ||||
|             {}, | ||||
|             {_, _ -> {}} | ||||
|         ) | ||||
|         EditProfileScreen({}, ProfileEditUiState(), EditProfileActions({}, {}, {})) | ||||
|     } | ||||
| } | ||||
|  | @ -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({}, {}, {}, {}) | ||||
|     ) | ||||
| } | ||||
|  | @ -24,34 +24,71 @@ 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.resources | ||||
| import kotlinx.coroutines.delay | ||||
| import javax.inject.Singleton | ||||
| import kotlin.time.Duration.Companion.seconds | ||||
| 
 | ||||
| data class SessionActions( | ||||
|     val getTimer: () -> FunctionalTimer, | ||||
|     val getTask: () -> String, | ||||
|     val releaseMediaPlayer: () -> Unit, | ||||
|     val startMediaPlayer: () -> Unit, | ||||
| ) | ||||
| 
 | ||||
| fun getSessionActions( | ||||
|     viewModel: SessionViewModel, | ||||
|     mediaplayer: MediaPlayer, | ||||
| ): SessionActions { | ||||
|     return SessionActions( | ||||
|         getTimer = viewModel::getTimer, | ||||
|         getTask = viewModel::getTask, | ||||
|         releaseMediaPlayer = mediaplayer::release, | ||||
|         startMediaPlayer = mediaplayer::start, | ||||
|     ) | ||||
| } | ||||
| 
 | ||||
| @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) | ||||
|     mediaplayer.isLooping = false | ||||
| 
 | ||||
|     InvisibleSessionManager.setParameters(viewModel = viewModel, mediaplayer = mediaplayer) | ||||
|     InvisibleSessionManager.setParameters( | ||||
|         viewModel = viewModel, | ||||
|         mediaplayer = mediaplayer | ||||
|     ) | ||||
| 
 | ||||
|     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, | ||||
|  | @ -61,12 +98,11 @@ fun SessionScreen( | |||
|         ) { | ||||
|             TextButton( | ||||
|                 onClick = { | ||||
|                     mediaplayer.stop() | ||||
|                     mediaplayer.release() | ||||
|                     sessionActions.releaseMediaPlayer | ||||
| 
 | ||||
|                     open(StudeezDestinations.HOME_SCREEN) | ||||
|                     InvisibleSessionManager.isSession = false | ||||
|                     // 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)) | ||||
|  | @ -77,7 +113,8 @@ fun SessionScreen( | |||
|                     color = Color.Red, | ||||
|                     fontWeight = FontWeight.Bold, | ||||
|                     fontSize = 18.sp, | ||||
|                     modifier = Modifier.padding(1.dp) | ||||
|                     modifier = Modifier | ||||
|                         .padding(1.dp) | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
|  | @ -85,19 +122,21 @@ 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.start() | ||||
|     if (sessionActions.getTimer().hasCurrentCountdownEnded() && !sessionActions.getTimer().hasEnded()) { | ||||
|         sessionActions.startMediaPlayer | ||||
|     } | ||||
| 
 | ||||
|     val hms = viewModel.getTimer().getHoursMinutesSeconds() | ||||
|     val hms = sessionActions.getTimer().getHoursMinutesSeconds() | ||||
|     Column { | ||||
|         Text( | ||||
|             text = "${hms.hours} : ${hms.minutes} : ${hms.seconds}", | ||||
|  | @ -108,14 +147,14 @@ 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 { | ||||
|                 (sessionActions.getTimer() as FunctionalPomodoroTimer?)?.breaksRemaining?.let { | ||||
|                     resources().getQuantityString(R.plurals.state_focus_remaining, it, it) | ||||
|             }.toString() | ||||
|                 }.toString() | ||||
|         } | ||||
| 
 | ||||
|         Text( | ||||
|  | @ -139,7 +178,7 @@ 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, | ||||
|  | @ -151,6 +190,8 @@ private fun Timer(viewModel: SessionViewModel = hiltViewModel(), mediaplayer: Me | |||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| @Singleton | ||||
| object InvisibleSessionManager { | ||||
|     private lateinit var viewModel: SessionViewModel | ||||
|     private lateinit var mediaplayer: MediaPlayer | ||||
|  | @ -174,3 +215,19 @@ object InvisibleSessionManager { | |||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @Preview | ||||
| @Composable | ||||
| fun TimerPreview() { | ||||
|     Timer(sessionActions = SessionActions({ FunctionalEndlessTimer() }, { "Preview" }, {}, {})) | ||||
| } | ||||
| 
 | ||||
| @Preview | ||||
| @Composable | ||||
| fun SessionPreview() { | ||||
|     SessionScreen( | ||||
|         open = {}, | ||||
|         sessionActions = SessionActions({ FunctionalEndlessTimer() }, { "Preview" }, {}, {}) | ||||
|     ) | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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({}, {}, {}, {}, {}, {}) | ||||
|     ) | ||||
| } | ||||
|  | @ -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) | ||||
|     } | ||||
| } | ||||
|  | @ -15,7 +15,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 +24,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 +51,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, | ||||
|     ) | ||||
| } | ||||
|  | @ -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({}, {}, {}, {}) | ||||
|     ) | ||||
| } | ||||
|  |  | |||
|  | @ -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({}, {}, {}, {}), | ||||
|     ) | ||||
| } | ||||
		Reference in a new issue
	
	 Rune Dyselinck
						Rune Dyselinck