Merge pull request #62 from SELab1/screens

Screens & profile
This commit is contained in:
Tibo De Peuter 2023-04-16 20:20:46 +02:00 committed by GitHub Enterprise
commit f82e83867c
20 changed files with 233 additions and 89 deletions

View file

@ -20,6 +20,7 @@ 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.profile.ProfileScreen
import be.ugent.sel.studeez.screens.sign_up.SignUpScreen
import be.ugent.sel.studeez.screens.splash.SplashScreen
import be.ugent.sel.studeez.ui.theme.StudeezTheme
@ -80,6 +81,10 @@ fun NavGraphBuilder.studeezGraph(appState: StudeezAppstate) {
route, popUp -> appState.navigateAndPopUp(route, popUp)
}
val open: (String) -> Unit = {
route -> appState.navigate(route)
}
composable(StudeezDestinations.SPLASH_SCREEN) {
SplashScreen(openAndPopUp)
}
@ -93,6 +98,16 @@ fun NavGraphBuilder.studeezGraph(appState: StudeezAppstate) {
}
composable(StudeezDestinations.HOME_SCREEN) {
HomeScreen(openAndPopUp)
HomeScreen(open, openAndPopUp)
}
// TODO Tasks screen
// TODO Sessions screen
composable(StudeezDestinations.PROFILE_SCREEN) {
ProfileScreen(open, openAndPopUp)
}
// TODO Timers screen
// TODO Settings screen
}

View file

@ -6,16 +6,19 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Menu
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.ui.theme.StudeezTheme
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@Composable
fun PrimaryScreenTemplate(
title: String,
open: (String) -> Unit,
openAndPopUp: (String, String) -> Unit,
content: @Composable (PaddingValues) -> Unit
) {
@ -40,10 +43,10 @@ fun PrimaryScreenTemplate(
) },
drawerContent = {
Drawer(openAndPopUp)
Drawer(open, openAndPopUp)
},
bottomBar = { NavigationBar(openAndPopUp) },
bottomBar = { NavigationBar(open) },
floatingActionButtonPosition = FabPosition.Center,
isFloatingActionButtonDocked = true,
floatingActionButton = { CollapsedAddButton() }
@ -52,13 +55,14 @@ fun PrimaryScreenTemplate(
}
}
//@Preview
//@Composable
//fun PrimaryScreenPreview() {
// StudeezTheme {
// PrimaryScreenTemplate(
// "Preview screen",
// {}
// ) {}
// }
//}
@Preview
@Composable
fun PrimaryScreenPreview() {
StudeezTheme {
PrimaryScreenTemplate(
"Preview screen",
{ _ -> {}},
{ _, _ -> {}}
) {}
}
}

View file

@ -0,0 +1,16 @@
package be.ugent.sel.studeez.common.composable
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.runtime.Composable
@Composable
fun SimpleScreenTemplate(
title: String,
content: @Composable (PaddingValues) -> Unit
) {
Scaffold( topBar = { TopAppBar ( title = { Text(text = title) } ) }
) { paddingValues -> content(paddingValues) }
}

View file

@ -0,0 +1,26 @@
package be.ugent.sel.studeez.common.composable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.sp
@Composable
fun Headline(
text: String
) {
Row (
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
) {
Text(
text = text,
fontSize = 34.sp
)
}
}

View file

@ -1,10 +1,7 @@
package be.ugent.sel.studeez.domain
import kotlinx.coroutines.flow.Flow
interface UserDAO {
suspend fun getUserName(): String?
suspend fun getUsername(): String?
suspend fun save(newUsername: String)
}

View file

@ -1,18 +1,26 @@
package be.ugent.sel.studeez.domain.implementation
import androidx.compose.runtime.rememberCoroutineScope
import be.ugent.sel.studeez.domain.AccountDAO
import be.ugent.sel.studeez.domain.UserDAO
import com.google.firebase.firestore.DocumentReference
import com.google.firebase.firestore.FirebaseFirestore
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.tasks.await
import javax.inject.Inject
import kotlin.coroutines.coroutineContext
class FirebaseUserDAO @Inject constructor(
private val firestore: FirebaseFirestore,
private val auth: AccountDAO
) : UserDAO {
override suspend fun getUserName(): String? {
override suspend fun getUsername(): String? {
return currentUserDocument().get().await().getString("username")
}

View file

@ -4,6 +4,12 @@ object StudeezDestinations {
const val SPLASH_SCREEN = "splash"
const val SIGN_UP_SCREEN = "signup"
const val LOGIN_SCREEN = "login"
const val HOME_SCREEN = "home"
const val HOME_SCREEN = "home"
// const val TASKS_SCREEN = "tasks"
// const val SESSIONS_SCREEN = "sessions"
const val PROFILE_SCREEN = "profile"
// const val TIMERS_SCREEN = "timers"
// const val SETTINGS_SCREEN = "settings"
}

View file

@ -23,6 +23,7 @@ import be.ugent.sel.studeez.ui.theme.StudeezTheme
@Composable
fun Drawer(
open: (String) -> Unit,
openAndPopUp: (String, String) -> Unit,
viewModel: DrawerViewModel = hiltViewModel()
) {
@ -36,19 +37,19 @@ fun Drawer(
icon = Icons.Default.Home,
text = resources().getString(R.string.home)
) {
// TODO Go to home
viewModel.onHomeButtonClick(open)
}
DrawerEntry(
icon = ImageVector.vectorResource(id = R.drawable.ic_timer),
text = resources().getString(R.string.timers)
) {
viewModel.onTimersClick(openAndPopUp)
viewModel.onTimersClick(open)
}
DrawerEntry(
icon = Icons.Default.Settings,
text = resources().getString(R.string.settings)
) {
viewModel.onSettingsClick(openAndPopUp)
viewModel.onSettingsClick(open)
}
DrawerEntry(
icon = ImageVector.vectorResource(id = R.drawable.ic_logout),
@ -62,7 +63,7 @@ fun Drawer(
icon = Icons.Outlined.Info,
text = resources().getString(R.string.about)
) {
viewModel.onAboutClick(openAndPopUp)
viewModel.onAboutClick(open)
}
}
}
@ -101,7 +102,9 @@ fun DrawerEntry(
fun DrawerPreview() {
StudeezTheme {
Drawer(
{a, b -> {}}, hiltViewModel()
{ _, -> {} },
{ _, _ -> {} },
hiltViewModel()
)
}
}

View file

@ -2,7 +2,8 @@ package be.ugent.sel.studeez.screens.drawer
import be.ugent.sel.studeez.domain.AccountDAO
import be.ugent.sel.studeez.domain.LogService
import be.ugent.sel.studeez.navigation.StudeezDestinations
import be.ugent.sel.studeez.navigation.StudeezDestinations.HOME_SCREEN
import be.ugent.sel.studeez.navigation.StudeezDestinations.LOGIN_SCREEN
import be.ugent.sel.studeez.screens.StudeezViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
@ -13,26 +14,26 @@ class DrawerViewModel @Inject constructor(
logService: LogService
) : StudeezViewModel(logService) {
fun onLogoutClick(openAndPopup: (String, String) -> Unit) {
fun onHomeButtonClick(open: (String) -> Unit) {
open(HOME_SCREEN)
}
fun onTimersClick(open: (String) -> Unit) {
// TODO
}
fun onSettingsClick(open: (String) -> Unit) {
// TODO
}
fun onLogoutClick(openAndPopUp: (String, String) -> Unit) {
launchCatching {
accountDAO.signOut()
openAndPopup(StudeezDestinations.LOGIN_SCREEN, StudeezDestinations.HOME_SCREEN)
openAndPopUp(LOGIN_SCREEN, HOME_SCREEN)
}
}
fun onHomeButtonClick(openAndPopup: (String, String) -> Unit) {
// TODO
}
fun onTimersClick(openAndPopup: (String, String) -> Unit) {
// TODO
}
fun onSettingsClick(openAndPopup: (String, String) -> Unit) {
// TODO
}
fun onAboutClick(openAndPopup: (String, String) -> Unit) {
fun onAboutClick(open: (String) -> Unit) {
// TODO
}
}

View file

@ -11,15 +11,17 @@ import be.ugent.sel.studeez.resources
@Composable
fun HomeScreen(
open: (String) -> Unit,
openAndPopUp: (String, String) -> Unit,
viewModel: HomeViewModel = hiltViewModel()
) {
PrimaryScreenTemplate(
title = resources().getString(R.string.home),
open = open,
openAndPopUp = openAndPopUp
) {
BasicButton(R.string.start_session, Modifier.basicButton()) {
viewModel.onStartSessionClick(openAndPopUp)
viewModel.onStartSessionClick(open)
}
}
}

View file

@ -1,15 +1,9 @@
package be.ugent.sel.studeez.screens.home
import androidx.compose.material.ScaffoldState
import androidx.compose.material.rememberScaffoldState
import be.ugent.sel.studeez.data.local.models.User
import be.ugent.sel.studeez.domain.AccountDAO
import be.ugent.sel.studeez.domain.LogService
import be.ugent.sel.studeez.navigation.StudeezDestinations.HOME_SCREEN
import be.ugent.sel.studeez.navigation.StudeezDestinations.LOGIN_SCREEN
import be.ugent.sel.studeez.screens.StudeezViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
@HiltViewModel
@ -18,14 +12,7 @@ class HomeViewModel @Inject constructor(
logService: LogService
) : StudeezViewModel(logService) {
fun onStartSessionClick(openAndPopUp: (String, String) -> Unit) {
// TODO openAndPopUp(StudeezDestinations.xxx, StudeezDestinations.HOME_SCREEN)
}
fun onLogoutClick(openAndPopup: (String, String) -> Unit) {
launchCatching {
accountDAO.signOut()
openAndPopup(LOGIN_SCREEN, HOME_SCREEN)
}
fun onStartSessionClick(open: (String) -> Unit) {
// TODO open(StudeezDestinations.xxx, StudeezDestinations.HOME_SCREEN)
}
}

View file

@ -26,8 +26,7 @@ fun LoginScreen(
) {
val uiState by viewModel.uiState
// TODO Make this a separate kind of screen?
SecondaryScreenTemplate(title = resources().getString(AppText.sign_in), {}) {
SimpleScreenTemplate(title = resources().getString(AppText.sign_in)) {
Column(
modifier = modifier
.fillMaxWidth()

View file

@ -13,11 +13,14 @@ 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
@Composable
fun NavigationBar(
popUpAndOpen: (String, String) -> Unit,
open: (String) -> Unit,
viewModel: NavigationBarViewModel = hiltViewModel()
) {
// TODO Pass functions and new screens.
@ -27,41 +30,46 @@ fun NavigationBar(
elevation = 10.dp
) {
BottomNavigationItem(
icon = { Icon(imageVector = Icons.Default.List, "Home") },
label = { Text(text = "Home") },
icon = { Icon(imageVector = Icons.Default.List, resources().getString(AppText.home)) },
label = { Text(text = resources().getString(AppText.home)) },
selected = false, // TODO
onClick = { viewModel.onHomeClick(popUpAndOpen) }
onClick = { viewModel.onHomeClick(open) }
)
BottomNavigationItem(
icon = { Icon(imageVector = Icons.Default.Check, "Tasks") },
label = { Text(text = "Tasks") },
icon = { Icon(imageVector = Icons.Default.Check, resources().getString(AppText.tasks)) },
label = { Text(text = resources().getString(AppText.tasks)) },
selected = false, // TODO
onClick = { viewModel.onTasksClick(popUpAndOpen) }
onClick = { viewModel.onTasksClick(open) }
)
// 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, "Sessions") },
label = { Text(text = "Sessions") },
icon = { Icon(imageVector = Icons.Outlined.DateRange, resources().getString(AppText.sessions)) },
label = { Text(text = resources().getString(AppText.sessions)) },
selected = false, // TODO
onClick = { viewModel.onSessionsClick(popUpAndOpen) }
onClick = { viewModel.onSessionsClick(open) }
)
BottomNavigationItem(
icon = { Icon(imageVector = Icons.Default.Person, "Profile") },
label = { Text(text = "Profile") },
icon = { Icon(imageVector = Icons.Default.Person, resources().getString(AppText.profile)) },
label = { Text(text = resources().getString(AppText.profile)) },
selected = false, // TODO
onClick = { viewModel.onProfileClick(popUpAndOpen) }
onClick = { viewModel.onProfileClick(open) }
)
}
}
//@Preview(showBackground = true)
//@Composable
//fun NavigationBarPreview() {
// StudeezTheme { NavigationBar() }
//}
@Preview(showBackground = true)
@Composable
fun NavigationBarPreview() {
StudeezTheme {
NavigationBar(
{ _ -> {} },
hiltViewModel()
)
}
}

View file

@ -3,6 +3,8 @@ package be.ugent.sel.studeez.screens.navbar
import be.ugent.sel.studeez.domain.AccountDAO
import be.ugent.sel.studeez.domain.LogService
import be.ugent.sel.studeez.navigation.StudeezDestinations
import be.ugent.sel.studeez.navigation.StudeezDestinations.HOME_SCREEN
import be.ugent.sel.studeez.navigation.StudeezDestinations.PROFILE_SCREEN
import be.ugent.sel.studeez.screens.StudeezViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
@ -13,19 +15,19 @@ class NavigationBarViewModel @Inject constructor(
logService: LogService
) : StudeezViewModel(logService) {
fun onHomeClick(openAndPopup: (String, String) -> Unit) {
fun onHomeClick(open: (String) -> Unit) {
open(HOME_SCREEN)
}
fun onTasksClick(open: (String) -> Unit) {
// TODO
}
fun onTasksClick(openAndPopup: (String, String) -> Unit) {
fun onSessionsClick(open: (String) -> Unit) {
// TODO
}
fun onSessionsClick(openAndPopup: (String, String) -> Unit) {
// TODO
}
fun onProfileClick(openAndPopup: (String, String) -> Unit) {
// TODO
fun onProfileClick(open: (String) -> Unit) {
open(PROFILE_SCREEN)
}
}

View file

@ -0,0 +1,29 @@
package be.ugent.sel.studeez.screens.profile
import androidx.compose.runtime.*
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.R.string as AppText
@Composable
fun ProfileScreen(
open: (String) -> Unit,
openAndPopUp: (String, String) -> Unit,
viewModel: ProfileViewModel = hiltViewModel()
) {
var username: String? by remember { mutableStateOf("") }
LaunchedEffect(key1 = Unit) {
username = viewModel.getUsername()
}
PrimaryScreenTemplate(
title = resources().getString(AppText.profile),
open = open,
openAndPopUp = openAndPopUp
) {
Headline(text = (username ?: resources().getString(R.string.no_username)))
}
}

View file

@ -0,0 +1,30 @@
package be.ugent.sel.studeez.screens.profile
import androidx.compose.runtime.rememberCoroutineScope
import androidx.lifecycle.viewModelScope
import be.ugent.sel.studeez.R
import be.ugent.sel.studeez.domain.LogService
import be.ugent.sel.studeez.domain.UserDAO
import be.ugent.sel.studeez.resources
import be.ugent.sel.studeez.screens.StudeezViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import javax.inject.Inject
@HiltViewModel
class ProfileViewModel @Inject constructor(
private val userDAO: UserDAO,
logService: LogService
) : StudeezViewModel(logService) {
suspend fun getUsername(): String? {
return userDAO.getUsername()
}
}

View file

@ -6,8 +6,6 @@ import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Person
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
@ -29,7 +27,7 @@ fun SignUpScreen(
val uiState by viewModel.uiState
val fieldModifier = Modifier.fieldModifier()
SecondaryScreenTemplate(title = resources().getString(AppText.create_account), {}) {
SimpleScreenTemplate(title = resources().getString(AppText.create_account)) {
Column(
modifier = modifier
.fillMaxWidth()

View file

@ -21,7 +21,7 @@ import be.ugent.sel.studeez.common.ext.basicButton
import kotlinx.coroutines.delay
import be.ugent.sel.studeez.R.string as AppText
private const val SPLASH_TIMEOUT = 1000L
private const val SPLASH_TIMEOUT = 500L
@Composable
fun SplashScreen(

View file

@ -0,0 +1,4 @@
package be.ugent.sel.studeez.screens.timers
class TimerScreen {
}

View file

@ -18,7 +18,6 @@
<string name="password_match_error">Passwords do not match.</string>
<string name="already_user">Already have an account? Log in.</string>
<!-- LoginScreen -->
<string name="not_already_user">Don\'t have an account yet? Sign up.</string>
<string name="sign_in">Sign in</string>
@ -31,6 +30,16 @@
<string name="home">Home</string>
<string name="start_session">Start session</string>
<!-- Tasks -->
<string name="tasks">Tasks</string>
<!-- Sessions -->
<string name="sessions">Sessions</string>
<!-- Profile -->
<string name="profile">Profile</string>
<string name="no_username">Unknown username</string>
<!-- Drawer / SideMenu -->
<string name="log_out">Log out</string>
<string name="profile_picture_description">Profile Picture</string>