Move friends overview to new package and new stuff
This commit is contained in:
		
							parent
							
								
									71b9550bd0
								
							
						
					
					
						commit
						bc7753fce5
					
				
					 3 changed files with 155 additions and 35 deletions
				
			
		|  | @ -1,20 +1,21 @@ | |||
| package be.ugent.sel.studeez.screens.friend | ||||
| package be.ugent.sel.studeez.screens.friends.friends_overview | ||||
| 
 | ||||
| import androidx.compose.foundation.background | ||||
| import androidx.compose.foundation.layout.* | ||||
| import androidx.compose.foundation.lazy.LazyColumn | ||||
| import androidx.compose.foundation.lazy.items | ||||
| import androidx.compose.foundation.shape.CircleShape | ||||
| import androidx.compose.material.Icon | ||||
| import androidx.compose.material.IconButton | ||||
| import androidx.compose.material.MaterialTheme | ||||
| import androidx.compose.material.Text | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.collectAsState | ||||
| import androidx.compose.material.* | ||||
| import androidx.compose.material.icons.Icons | ||||
| import androidx.compose.material.icons.filled.ArrowBack | ||||
| import androidx.compose.material.icons.filled.Delete | ||||
| import androidx.compose.material.icons.filled.Person | ||||
| import androidx.compose.runtime.* | ||||
| import androidx.compose.ui.Alignment | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.graphics.vector.ImageVector | ||||
| import androidx.compose.ui.res.painterResource | ||||
| import androidx.compose.ui.res.stringResource | ||||
| import androidx.compose.ui.res.vectorResource | ||||
| import androidx.compose.ui.text.style.TextOverflow | ||||
| import androidx.compose.ui.tooling.preview.Preview | ||||
|  | @ -22,7 +23,8 @@ import androidx.compose.ui.unit.dp | |||
| import androidx.compose.ui.unit.sp | ||||
| import be.ugent.sel.studeez.R | ||||
| import be.ugent.sel.studeez.common.composable.BasicButton | ||||
| import be.ugent.sel.studeez.common.composable.SecondaryScreenTemplate | ||||
| import be.ugent.sel.studeez.common.composable.SearchField | ||||
| import be.ugent.sel.studeez.common.composable.drawer.DrawerEntry | ||||
| import be.ugent.sel.studeez.common.ext.basicButton | ||||
| import be.ugent.sel.studeez.data.local.models.Friendship | ||||
| import be.ugent.sel.studeez.data.local.models.User | ||||
|  | @ -35,7 +37,11 @@ import be.ugent.sel.studeez.R.string as AppText | |||
| 
 | ||||
| data class FriendsOverviewActions( | ||||
|     val getFriendsFlow: () -> Flow<List<Pair<User, Friendship>>>, | ||||
|     val searchFriends: () -> Unit | ||||
|     val searchFriends: () -> Unit, | ||||
|     val onQueryStringChange: (String) -> Unit, | ||||
|     val onSubmit: () -> Unit, | ||||
|     val viewProfile: (String) -> Unit, | ||||
|     val removeFriend: (Friendship) -> Unit | ||||
| ) | ||||
| 
 | ||||
| fun getFriendsOverviewActions( | ||||
|  | @ -44,7 +50,13 @@ fun getFriendsOverviewActions( | |||
| ): FriendsOverviewActions { | ||||
|     return FriendsOverviewActions( | ||||
|         getFriendsFlow = viewModel::getAllFriends, | ||||
|         searchFriends = { viewModel.searchFriends(open) } | ||||
|         searchFriends = { viewModel.searchFriends(open) }, | ||||
|         onQueryStringChange = viewModel::onQueryStringChange, | ||||
|         onSubmit = { viewModel.onSubmit(open) }, | ||||
|         viewProfile = { userId -> | ||||
|             viewModel.viewProfile(userId, open) | ||||
|         }, | ||||
|         removeFriend = viewModel::removeFriend | ||||
|     ) | ||||
| } | ||||
| 
 | ||||
|  | @ -54,27 +66,52 @@ fun FriendsOveriewRoute( | |||
|     popUp: () -> Unit, | ||||
|     viewModel: FriendsOverviewViewModel | ||||
| ) { | ||||
|     val uiState by viewModel.uiState | ||||
|     FriendsOverviewScreen( | ||||
|         popUp = popUp, | ||||
|         uiState = uiState, | ||||
|         friendsOverviewActions = getFriendsOverviewActions( | ||||
|             viewModel = viewModel, | ||||
|             open = open | ||||
|         ), | ||||
|         popUp = popUp | ||||
|         ) | ||||
|     ) | ||||
| } | ||||
| 
 | ||||
| @Composable | ||||
| fun FriendsOverviewScreen( | ||||
|     friendsOverviewActions: FriendsOverviewActions, | ||||
|     popUp: () -> Unit | ||||
|     popUp: () -> Unit, | ||||
|     uiState: FriendsOverviewUiState, | ||||
|     friendsOverviewActions: FriendsOverviewActions | ||||
| ) { | ||||
|     val friends = friendsOverviewActions.getFriendsFlow().collectAsState(initial = emptyList()) | ||||
|      | ||||
|     SecondaryScreenTemplate( | ||||
|         title = "TODO there needs to be a search field here", // TODO | ||||
|         popUp = popUp | ||||
|     ) { | ||||
|         LazyColumn { | ||||
| 
 | ||||
|     Scaffold( | ||||
|         topBar = { | ||||
|             TopAppBar( | ||||
|                 title = { | ||||
|                     // TODO Link to each other | ||||
|                     SearchField( | ||||
|                         value = uiState.queryString, | ||||
|                         onValueChange = friendsOverviewActions.onQueryStringChange, | ||||
|                         onSubmit = friendsOverviewActions.onSubmit, | ||||
|                         label = AppText.search_friends | ||||
|                     ) | ||||
|                 }, | ||||
|                 navigationIcon = { | ||||
|                     IconButton(onClick = popUp) { | ||||
|                         Icon( | ||||
|                             imageVector = Icons.Default.ArrowBack, | ||||
|                             contentDescription = resources().getString(R.string.go_back) | ||||
|                         ) | ||||
|                     } | ||||
|                 } | ||||
|                 // TODO Add inbox action | ||||
|             ) | ||||
|         } | ||||
|     ) { paddingValues -> | ||||
|         LazyColumn ( | ||||
|             modifier = Modifier.padding(paddingValues) | ||||
|         ) { | ||||
|             if (friends.value.isEmpty()) { | ||||
|                 // Show a quick button to search friends when the user does not have any friends yet. | ||||
|                 item { | ||||
|  | @ -90,7 +127,9 @@ fun FriendsOverviewScreen( | |||
|             items(friends.value) { friend -> | ||||
|                 FriendsEntry( | ||||
|                     user = friend.first, | ||||
|                     friendship = friend.second | ||||
|                     friendship = friend.second, | ||||
|                     viewProfile = { userId -> friendsOverviewActions.viewProfile(userId) }, | ||||
|                     removeFriend = friendsOverviewActions.removeFriend | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
|  | @ -102,11 +141,16 @@ fun FriendsOverviewScreen( | |||
| fun FriendsOverviewPreview() { | ||||
|     StudeezTheme { | ||||
|         FriendsOverviewScreen( | ||||
|             popUp = {}, | ||||
|             uiState = FriendsOverviewUiState(""), | ||||
|             friendsOverviewActions = FriendsOverviewActions( | ||||
|                 getFriendsFlow = { emptyFlow() }, | ||||
|                 searchFriends = {} | ||||
|             ), | ||||
|             popUp = {} | ||||
|                 searchFriends = {}, | ||||
|                 onQueryStringChange = {}, | ||||
|                 onSubmit = {}, | ||||
|                 viewProfile = {}, | ||||
|                 removeFriend = {} | ||||
|             ) | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|  | @ -114,7 +158,9 @@ fun FriendsOverviewPreview() { | |||
| @Composable | ||||
| fun FriendsEntry( | ||||
|     user: User, | ||||
|     friendship: Friendship | ||||
|     friendship: Friendship, | ||||
|     viewProfile: (String) -> Unit, | ||||
|     removeFriend: (Friendship) -> Unit | ||||
| ) { | ||||
|     // TODO Styling | ||||
|     Row ( | ||||
|  | @ -162,7 +208,11 @@ fun FriendsEntry( | |||
|         Box( | ||||
|             modifier = Modifier.fillMaxWidth(0.15f) | ||||
|         ) { | ||||
|             ThreeDots(friendship = friendship) | ||||
|             FriendsOverviewDropDown( | ||||
|                 friendship = friendship, | ||||
|                 viewProfile = viewProfile, | ||||
|                 removeFriend = removeFriend | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -182,17 +232,23 @@ fun FriendsEntryPreview() { | |||
|                 friendId = "someId", | ||||
|                 friendsSince = Timestamp.now(), | ||||
|                 accepted = true | ||||
|             ) | ||||
|             ), | ||||
|             viewProfile = {}, | ||||
|             removeFriend = {} | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @Composable | ||||
| fun ThreeDots( | ||||
|     friendship: Friendship | ||||
| fun FriendsOverviewDropDown( | ||||
|     friendship: Friendship, | ||||
|     viewProfile: (String) -> Unit, | ||||
|     removeFriend: (Friendship) -> Unit | ||||
| ) { | ||||
|     var expanded by remember { mutableStateOf(false) } | ||||
| 
 | ||||
|     IconButton( | ||||
|         onClick = { /* TODO Open dropdown */ } | ||||
|         onClick = { expanded = true } | ||||
|     ) { | ||||
|         Icon( | ||||
|             imageVector = ImageVector.vectorResource(id = R.drawable.ic_more_horizontal), | ||||
|  | @ -200,19 +256,40 @@ fun ThreeDots( | |||
|             modifier = Modifier.fillMaxSize() | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     DropdownMenu( | ||||
|         expanded = expanded, | ||||
|         onDismissRequest = { expanded = false } | ||||
|     ) { | ||||
|         DrawerEntry( | ||||
|             icon = Icons.Default.Person, | ||||
|             text = stringResource(id = AppText.show_profile) | ||||
|         ) { | ||||
|             viewProfile(friendship.friendId) | ||||
|         } | ||||
|         DrawerEntry( | ||||
|             icon = Icons.Default.Delete, | ||||
|             text = stringResource(id = AppText.remove_friend) | ||||
|         ) { | ||||
|             removeFriend(friendship) | ||||
|             expanded = false | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @Preview | ||||
| @Composable | ||||
| fun ThreeDotsPreview() { | ||||
| fun FriendsOverviewDropDownPreview() { | ||||
|     StudeezTheme { | ||||
|         ThreeDots( | ||||
|         FriendsOverviewDropDown( | ||||
|             friendship = Friendship( | ||||
|                 id = "", | ||||
|                 friendId = "someId", | ||||
|                 friendsSince = Timestamp.now(), | ||||
|                 accepted = true | ||||
|             ) | ||||
|             ), | ||||
|             viewProfile = {}, | ||||
|             removeFriend = { } | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,6 @@ | |||
| package be.ugent.sel.studeez.screens.friends.friends_overview | ||||
| 
 | ||||
| data class FriendsOverviewUiState( | ||||
|     val userId: String, | ||||
|     val queryString: String = "" | ||||
| ) | ||||
|  | @ -1,5 +1,6 @@ | |||
| package be.ugent.sel.studeez.screens.friend | ||||
| package be.ugent.sel.studeez.screens.friends.friends_overview | ||||
| 
 | ||||
| import androidx.compose.runtime.mutableStateOf | ||||
| import be.ugent.sel.studeez.data.local.models.Friendship | ||||
| import be.ugent.sel.studeez.data.local.models.User | ||||
| import be.ugent.sel.studeez.domain.FriendshipDAO | ||||
|  | @ -7,6 +8,7 @@ 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 be.ugent.sel.studeez.screens.profile.public_profile.SelectedProfileState | ||||
| import dagger.hilt.android.lifecycle.HiltViewModel | ||||
| import kotlinx.coroutines.flow.Flow | ||||
| import kotlinx.coroutines.flow.combine | ||||
|  | @ -17,11 +19,19 @@ import javax.inject.Inject | |||
| class FriendsOverviewViewModel @Inject constructor( | ||||
|     private val userDAO: UserDAO, | ||||
|     private val friendshipDAO: FriendshipDAO, | ||||
|     private val selectedProfileState: SelectedProfileState, | ||||
|     logService: LogService | ||||
| ) : StudeezViewModel(logService) { | ||||
| 
 | ||||
|     var uiState = mutableStateOf(FriendsOverviewUiState( | ||||
|         userId = selectedProfileState.selectedUserId | ||||
|     )) | ||||
|         private set | ||||
| 
 | ||||
|     fun getAllFriends(): Flow<List<Pair<User, Friendship>>> { | ||||
|         return friendshipDAO.getAllFriendships() | ||||
|         return friendshipDAO.getAllFriendships( | ||||
|                 userId = uiState.value.userId | ||||
|             ) | ||||
|             .flatMapConcat { friendships -> | ||||
|                 val userFlows = friendships.map { friendship -> | ||||
|                     userDAO.getUserDetails(friendship.friendId) | ||||
|  | @ -38,4 +48,31 @@ class FriendsOverviewViewModel @Inject constructor( | |||
|         open(StudeezDestinations.SEARCH_FRIENDS_SCREEN) | ||||
|     } | ||||
| 
 | ||||
|     fun onQueryStringChange(newValue: String) { | ||||
|         uiState.value = uiState.value.copy( | ||||
|             queryString = newValue | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     fun onSubmit(open: (String) -> Unit) { | ||||
|         val query = uiState.value.queryString // TODO Pass as argument | ||||
|         open(StudeezDestinations.SEARCH_FRIENDS_SCREEN) | ||||
|     } | ||||
| 
 | ||||
|     fun viewProfile( | ||||
|         userId: String, | ||||
|         open: (String) -> Unit | ||||
|     ) { | ||||
|         selectedProfileState.selectedUserId = userId | ||||
|         open(StudeezDestinations.PUBLIC_PROFILE_SCREEN) | ||||
|     } | ||||
| 
 | ||||
|     fun removeFriend( | ||||
|         friendship: Friendship | ||||
|     ) { | ||||
|         friendshipDAO.removeFriendship( | ||||
|             friendship = friendship | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
		Reference in a new issue