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 d112db1..ff46729 100644
--- a/app/src/main/java/be/ugent/sel/studeez/StudeezApp.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/StudeezApp.kt
@@ -21,6 +21,7 @@ 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
@@ -79,12 +80,16 @@ fun resources(): Resources {
fun NavGraphBuilder.studeezGraph(appState: StudeezAppstate) {
- val openAndPopUp: (String, String) -> Unit = {
- route, popUp -> appState.navigateAndPopUp(route, popUp)
+ val goBack: () -> Unit = {
+ appState.popUp()
}
val open: (String) -> Unit = {
- route -> appState.navigate(route)
+ route -> appState.navigate(route)
+ }
+
+ val openAndPopUp: (String, String) -> Unit = {
+ route, popUp -> appState.navigateAndPopUp(route, popUp)
}
composable(StudeezDestinations.SPLASH_SCREEN) {
@@ -120,4 +125,9 @@ fun NavGraphBuilder.studeezGraph(appState: StudeezAppstate) {
// TODO Timers screen
// TODO Settings screen
+
+ // Edit screens
+ composable(StudeezDestinations.EDIT_PROFILE_SCREEN) {
+ EditProfileScreen(goBack, openAndPopUp)
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/be/ugent/sel/studeez/common/composable/ButtonComposable.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/ButtonComposable.kt
index e174ef2..64c7352 100644
--- a/app/src/main/java/be/ugent/sel/studeez/common/composable/ButtonComposable.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/ButtonComposable.kt
@@ -8,6 +8,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.sp
@Composable
+
fun BasicTextButton(@StringRes text: Int, modifier: Modifier, action: () -> Unit) {
TextButton(onClick = action, modifier = modifier) { Text(text = stringResource(text)) }
}
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 5983d37..009281f 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
@@ -1,8 +1,10 @@
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.icons.Icons
+import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.Menu
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
@@ -20,6 +22,7 @@ fun PrimaryScreenTemplate(
title: String,
open: (String) -> Unit,
openAndPopUp: (String, String) -> Unit,
+ action: @Composable RowScope.() -> Unit,
content: @Composable (PaddingValues) -> Unit
) {
val scaffoldState: ScaffoldState = rememberScaffoldState()
@@ -39,7 +42,8 @@ fun PrimaryScreenTemplate(
contentDescription = resources().getString(R.string.menu)
)
}
- }
+ },
+ actions = action
) },
drawerContent = {
@@ -62,7 +66,13 @@ fun PrimaryScreenPreview() {
PrimaryScreenTemplate(
"Preview screen",
{ _ -> {}},
- { _, _ -> {}}
+ { _, _ -> {}},
+ { 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/TextFieldComposable.kt b/app/src/main/java/be/ugent/sel/studeez/common/composable/TextFieldComposable.kt
index 5766607..2c0b450 100644
--- a/app/src/main/java/be/ugent/sel/studeez/common/composable/TextFieldComposable.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/common/composable/TextFieldComposable.kt
@@ -11,14 +11,15 @@ import androidx.compose.material.icons.filled.Email
import androidx.compose.material.icons.filled.Lock
import androidx.compose.material.icons.filled.Person
import androidx.compose.runtime.*
-import be.ugent.sel.studeez.R.string as AppText
-import be.ugent.sel.studeez.R.drawable as AppIcon
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
+import be.ugent.sel.studeez.common.ext.fieldModifier
+import be.ugent.sel.studeez.R.drawable as AppIcon
+import be.ugent.sel.studeez.R.string as AppText
@Composable
fun BasicField(
@@ -36,6 +37,20 @@ fun BasicField(
)
}
+@Composable
+fun LabelledInputField(
+ value: String,
+ onNewValue: (String) -> Unit,
+ @StringRes label: Int
+) {
+ OutlinedTextField(
+ value = value,
+ onValueChange = onNewValue,
+ label = { Text(text = stringResource(id = label)) },
+ modifier = Modifier.fieldModifier()
+ )
+}
+
@Composable
fun UsernameField(
value: String,
diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/AccountDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/AccountDAO.kt
index 96ecb74..c813ec6 100644
--- a/app/src/main/java/be/ugent/sel/studeez/domain/AccountDAO.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/domain/AccountDAO.kt
@@ -29,5 +29,6 @@ interface AccountDAO {
suspend fun sendRecoveryEmail(email: String)
suspend fun signUpWithEmailAndPassword(email: String, password: String)
suspend fun deleteAccount()
+
suspend fun signOut()
}
diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt
index 8b6f357..b96cf17 100644
--- a/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/domain/UserDAO.kt
@@ -4,4 +4,10 @@ interface UserDAO {
suspend fun getUsername(): String?
suspend fun save(newUsername: String)
+
+ /**
+ * Delete all references to this user in the database. Similar to the deleteCascade in
+ * relational databases.
+ */
+ suspend fun deleteUserReferences()
}
\ No newline at end of file
diff --git a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt
index 8ac779b..3158b88 100644
--- a/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/domain/implementation/FirebaseUserDAO.kt
@@ -1,19 +1,13 @@
package be.ugent.sel.studeez.domain.implementation
-import androidx.compose.runtime.rememberCoroutineScope
+import be.ugent.sel.studeez.R
+import be.ugent.sel.studeez.common.snackbar.SnackbarManager
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,
@@ -34,4 +28,10 @@ class FirebaseUserDAO @Inject constructor(
companion object {
private const val USER_COLLECTION = "users"
}
+
+ override suspend fun deleteUserReferences() {
+ currentUserDocument().delete()
+ .addOnSuccessListener { SnackbarManager.showMessage(R.string.success) }
+ .addOnFailureListener { SnackbarManager.showMessage(R.string.generic_error) }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt
index 51cb871..144cd45 100644
--- a/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/navigation/StudeezDestinations.kt
@@ -13,4 +13,7 @@ object StudeezDestinations {
// const val TIMERS_SCREEN = "timers"
// const val SETTINGS_SCREEN = "settings"
+
+ // Edit screens
+ const val EDIT_PROFILE_SCREEN = "edit_profile"
}
\ No newline at end of file
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 e8c88c1..7ca2559 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
@@ -1,5 +1,9 @@
package be.ugent.sel.studeez.screens.home
+import androidx.compose.material.Icon
+import androidx.compose.material.IconButton
+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
@@ -18,10 +22,21 @@ fun HomeScreen(
PrimaryScreenTemplate(
title = resources().getString(R.string.home),
open = open,
- openAndPopUp = openAndPopUp
+ openAndPopUp = openAndPopUp,
+ action = { FriendsAction() }
) {
BasicButton(R.string.start_session, Modifier.basicButton()) {
viewModel.onStartSessionClick(open)
}
}
+}
+
+@Composable
+fun FriendsAction () {
+ IconButton(onClick = { /*TODO*/ }) {
+ Icon(
+ imageVector = Icons.Default.Person,
+ contentDescription = resources().getString(R.string.friends)
+ )
+ }
}
\ 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/screens/navbar/NavigationBarViewModel.kt
index 1814d84..75613d5 100644
--- a/app/src/main/java/be/ugent/sel/studeez/screens/navbar/NavigationBarViewModel.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/screens/navbar/NavigationBarViewModel.kt
@@ -2,7 +2,6 @@ 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
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
new file mode 100644
index 0000000..eb12ac7
--- /dev/null
+++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditScreen.kt
@@ -0,0 +1,55 @@
+package be.ugent.sel.studeez.screens.profile
+
+import androidx.compose.foundation.layout.Column
+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
+import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate
+import be.ugent.sel.studeez.common.ext.textButton
+import be.ugent.sel.studeez.resources
+import be.ugent.sel.studeez.ui.theme.StudeezTheme
+
+@Composable
+fun EditProfileScreen(
+ goBack: () -> Unit,
+ openAndPopUp: (String, String) -> Unit,
+ viewModel: ProfileEditViewModel = hiltViewModel()
+) {
+ val uiState by viewModel.uiState
+
+ SecondaryScreenTemplate(
+ title = resources().getString(R.string.editing_profile),
+ popUp = goBack
+ ) {
+ Column {
+ LabelledInputField(
+ value = uiState.username,
+ onNewValue = viewModel::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)
+ }
+ }
+ }
+}
+
+@Preview
+@Composable
+fun EditProfileScreenComposable() {
+ StudeezTheme {
+ EditProfileScreen (
+ {},
+ {_, _ -> {}}
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditUiState.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditUiState.kt
new file mode 100644
index 0000000..9ecaba3
--- /dev/null
+++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditUiState.kt
@@ -0,0 +1,5 @@
+package be.ugent.sel.studeez.screens.profile
+
+data class ProfileEditUiState (
+ val username: String = ""
+)
\ No newline at end of file
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt
new file mode 100644
index 0000000..cb270be
--- /dev/null
+++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileEditViewModel.kt
@@ -0,0 +1,48 @@
+package be.ugent.sel.studeez.screens.profile
+
+import androidx.compose.runtime.mutableStateOf
+import be.ugent.sel.studeez.R
+import be.ugent.sel.studeez.common.snackbar.SnackbarManager
+import be.ugent.sel.studeez.domain.AccountDAO
+import be.ugent.sel.studeez.domain.LogService
+import be.ugent.sel.studeez.domain.UserDAO
+import be.ugent.sel.studeez.navigation.StudeezDestinations
+import be.ugent.sel.studeez.screens.StudeezViewModel
+import dagger.hilt.android.lifecycle.HiltViewModel
+import javax.inject.Inject
+
+@HiltViewModel
+class ProfileEditViewModel @Inject constructor(
+ private val accountDAO: AccountDAO,
+ private val userDAO: UserDAO,
+ logService: LogService
+) : StudeezViewModel(logService) {
+
+ var uiState = mutableStateOf(ProfileEditUiState())
+ private set
+
+ init {
+ launchCatching {
+ uiState.value = uiState.value.copy(username = userDAO.getUsername()!!)
+ }
+ }
+
+ fun onUsernameChange(newValue: String) {
+ uiState.value = uiState.value.copy(username = newValue)
+ }
+
+ fun onSaveClick() {
+ launchCatching {
+ userDAO.save(uiState.value.username)
+ SnackbarManager.showMessage(R.string.success)
+ }
+ }
+
+ fun onDeleteClick(openAndPopUp: (String, String) -> Unit) {
+ launchCatching {
+ userDAO.deleteUserReferences() // Delete references
+ accountDAO.deleteAccount() // Delete authentication
+ }
+ openAndPopUp(StudeezDestinations.SIGN_UP_SCREEN, StudeezDestinations.EDIT_PROFILE_SCREEN)
+ }
+}
\ 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 56568c4..6ec4a01 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
@@ -1,5 +1,9 @@
package be.ugent.sel.studeez.screens.profile
+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.hilt.navigation.compose.hiltViewModel
import be.ugent.sel.studeez.R
@@ -22,8 +26,22 @@ fun ProfileScreen(
PrimaryScreenTemplate(
title = resources().getString(AppText.profile),
open = open,
- openAndPopUp = openAndPopUp
+ openAndPopUp = openAndPopUp,
+ action = { EditAction { viewModel.onEditProfileClick(open) } }
) {
Headline(text = (username ?: resources().getString(R.string.no_username)))
}
+}
+
+@Composable
+fun EditAction(
+ onClick: () -> Unit
+) {
+ IconButton(onClick = onClick) {
+ Icon(
+ imageVector = Icons.Default.Edit,
+ contentDescription = resources().getString(AppText.edit_profile)
+ )
+
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt
index 1f6b1a2..e24defd 100644
--- a/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt
+++ b/app/src/main/java/be/ugent/sel/studeez/screens/profile/ProfileViewModel.kt
@@ -1,20 +1,10 @@
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.navigation.StudeezDestinations
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
@@ -27,4 +17,8 @@ class ProfileViewModel @Inject constructor(
return userDAO.getUsername()
}
+ fun onEditProfileClick(open: (String) -> Unit) {
+ open(StudeezDestinations.EDIT_PROFILE_SCREEN)
+ }
+
}
\ 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 dacb7db..91dde13 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
@@ -13,11 +13,8 @@ import be.ugent.sel.studeez.navigation.StudeezDestinations.LOGIN_SCREEN
import be.ugent.sel.studeez.navigation.StudeezDestinations.SIGN_UP_SCREEN
import be.ugent.sel.studeez.screens.StudeezViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
-import kotlinx.coroutines.flow.FlowCollector
-import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.flow.take
-import be.ugent.sel.studeez.R.string as AppText
import javax.inject.Inject
+import be.ugent.sel.studeez.R.string as AppText
@HiltViewModel
class SignUpViewModel @Inject constructor(
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index b17c518..b48d7f8 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -5,13 +5,21 @@
Email
Password
Repeat password
- Something wrong happened. Please try again.
- Please insert a valid email.
- Cancel
- Try again
- Go back
Menu
+
+ Confirm
+ Save
+ Cancel
+ Go back
+ Next
+
+
+ Success!
+ Try again
+ Something wrong happened. Please try again.
+ Please insert a valid email.
+
Create account
Your password should have at least six characters and include one digit, one lower case letter and one upper case letter.
@@ -39,6 +47,12 @@
Profile
Unknown username
+ Edit profile
+ Editing profile
+ Delete profile
+
+
+ Friends
Log out