diff --git a/.idea/compiler.xml b/.idea/compiler.xml index fb7f4a8..b589d56 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 7e42cec..ff9696e 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index bdd9278..8978d23 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,7 +1,6 @@ - - + diff --git a/app/src/main/java/be/ugent/sel/studeez/StudeezApp.kt b/app/src/main/java/be/ugent/sel/studeez/StudeezApp.kt index 04b4914..a6830a5 100644 --- a/app/src/main/java/be/ugent/sel/studeez/StudeezApp.kt +++ b/app/src/main/java/be/ugent/sel/studeez/StudeezApp.kt @@ -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) - } -} \ No newline at end of file +} diff --git a/app/src/main/java/be/ugent/sel/studeez/activities/MainActivity.kt b/app/src/main/java/be/ugent/sel/studeez/activities/MainActivity.kt index 02656bd..b020cc8 100644 --- a/app/src/main/java/be/ugent/sel/studeez/activities/MainActivity.kt +++ b/app/src/main/java/be/ugent/sel/studeez/activities/MainActivity.kt @@ -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 diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/PrimaryScreenComposable.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/PrimaryScreenComposable.kt index df9b346..5af2788 100644 --- a/app/src/main/java/be/ugent/sel/studeez/common/composable/PrimaryScreenComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/PrimaryScreenComposable.kt @@ -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" + ) + } + }, ) {} } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/TimerEntry.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/TimerEntry.kt new file mode 100644 index 0000000..bfa2711 --- /dev/null +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/TimerEntry.kt @@ -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) {} +} \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/drawer/DrawerComposable.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/drawer/DrawerComposable.kt similarity index 51% rename from app/src/main/java/be/ugent/sel/studeez/screens/drawer/DrawerComposable.kt rename to app/src/main/java/be/ugent/sel/studeez/common/composable/drawer/DrawerComposable.kt index 296fd94..47571f5 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/drawer/DrawerComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/drawer/DrawerComposable.kt @@ -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) } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/drawer/DrawerViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/drawer/DrawerViewModel.kt similarity index 95% rename from app/src/main/java/be/ugent/sel/studeez/screens/drawer/DrawerViewModel.kt rename to app/src/main/java/be/ugent/sel/studeez/common/composable/drawer/DrawerViewModel.kt index 7e03b9b..d16d930 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/drawer/DrawerViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/drawer/DrawerViewModel.kt @@ -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 diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/navbar/NavigationBarComposable.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/navbar/NavigationBarComposable.kt similarity index 56% rename from app/src/main/java/be/ugent/sel/studeez/screens/navbar/NavigationBarComposable.kt rename to app/src/main/java/be/ugent/sel/studeez/common/composable/navbar/NavigationBarComposable.kt index 5c5dee1..21311ef 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/navbar/NavigationBarComposable.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/navbar/NavigationBarComposable.kt @@ -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({}, {}, {}, {})) } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/navbar/NavigationBarViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/navbar/NavigationBarViewModel.kt similarity index 93% rename from app/src/main/java/be/ugent/sel/studeez/screens/navbar/NavigationBarViewModel.kt rename to app/src/main/java/be/ugent/sel/studeez/common/composable/navbar/NavigationBarViewModel.kt index 75613d5..1e4bd0d 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/navbar/NavigationBarViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/navbar/NavigationBarViewModel.kt @@ -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 diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt index 7ca2559..e318655 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/home/HomeScreen.kt @@ -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) ) } -} \ No newline at end of file +} + +@Preview +@Composable +fun HomeScreenPreview() { + HomeScreen( + onStartSessionClick = {}, + drawerActions = DrawerActions({}, {}, {}, {}, {}), + navigationBarActions = NavigationBarActions({}, {}, {}, {}) + ) +} diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/log_in/LoginScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/log_in/LoginScreen.kt index 2762abc..fe7524b 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/log_in/LoginScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/log_in/LoginScreen.kt @@ -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({}, {}, {}, {}, {}) + ) } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/log_in/LoginViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/log_in/LoginViewModel.kt index cf3a72d..c7e88fa 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/log_in/LoginViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/log_in/LoginViewModel.kt @@ -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) } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditScreen.kt index eb12ac7..3dbe270 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditScreen.kt @@ -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({}, {}, {})) } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileScreen.kt index 6ec4a01..a12dd08 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileScreen.kt @@ -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({}, {}, {}, {}) + ) } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionScreen.kt index ac2d225..3a32b37 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/session/SessionScreen.kt @@ -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" }, {}, {}) + ) +} + diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpScreen.kt index 7515117..038bee2 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpScreen.kt @@ -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({}, {}, {}, {}, {}, {}) + ) } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpViewModel.kt index 91dde13..a08d063 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpViewModel.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/sign_up/SignUpViewModel.kt @@ -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) } } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/splash/SplashScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/splash/SplashScreen.kt index 38146c0..e70c67b 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/splash/SplashScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/splash/SplashScreen.kt @@ -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, + ) } \ No newline at end of file diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_overview/TimerOverviewScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_overview/TimerOverviewScreen.kt index fcff7fe..fafdf02 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_overview/TimerOverviewScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_overview/TimerOverviewScreen.kt @@ -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>, + val getDefaultTimers: () -> List, + 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) { } - } - } -} \ No newline at end of file + TimerOverviewScreen( + timerOverviewActions = TimerOverviewActions( + { flowOf() }, + { listOf(customTimer, customTimer) }, + {}), + drawerActions = DrawerActions({}, {}, {}, {}, {}), + navigationBarActions = NavigationBarActions({}, {}, {}, {}) + ) +} diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/timer_selection/TimerSelectionScreen.kt b/app/src/main/java/be/ugent/sel/studeez/screens/timer_selection/TimerSelectionScreen.kt index 8e8df37..47e7f91 100644 --- a/app/src/main/java/be/ugent/sel/studeez/screens/timer_selection/TimerSelectionScreen.kt +++ b/app/src/main/java/be/ugent/sel/studeez/screens/timer_selection/TimerSelectionScreen.kt @@ -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>, + 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({}, {}, {}, {}), + ) } \ No newline at end of file