Skip to content

Commit

Permalink
♻️ Move user mention search to CheckIn (#354)
Browse files Browse the repository at this point in the history
fixes #351
  • Loading branch information
jheubuch authored Apr 14, 2024
1 parent 52dc792 commit 205cfab
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 185 deletions.
9 changes: 2 additions & 7 deletions app/src/main/kotlin/de/hbch/traewelling/navigation/NavHost.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import androidx.navigation.compose.composable
import com.jcloquell.androidsecurestorage.SecureStorage
import de.hbch.traewelling.R
import de.hbch.traewelling.api.models.status.Status
import de.hbch.traewelling.shared.BottomSearchViewModel
import de.hbch.traewelling.shared.CheckInViewModel
import de.hbch.traewelling.shared.EventViewModel
import de.hbch.traewelling.shared.LoggedInUserViewModel
Expand Down Expand Up @@ -57,7 +56,6 @@ fun TraewelldroidNavHost(
eventViewModel: EventViewModel,
checkInViewModel: CheckInViewModel,
notificationsViewModel: NotificationsViewModel,
bottomSearchViewModel: BottomSearchViewModel,
snackbarHostState: SnackbarHostState,
modifier: Modifier = Modifier,
onFloatingActionButtonChange: (Int, Int, () -> Unit) -> Unit = { _, _, _ -> },
Expand Down Expand Up @@ -137,7 +135,6 @@ fun TraewelldroidNavHost(

Dashboard(
loggedInUserViewModel = loggedInUserViewModel,
bottomSearchViewModel = bottomSearchViewModel,
searchConnectionsAction = navToSearchConnections,
statusSelectedAction = navToStatusDetails,
userSelectedAction = navToUserProfile,
Expand Down Expand Up @@ -391,8 +388,7 @@ fun TraewelldroidNavHost(
)
shortcutManager.requestPinShortcut(shortcut, successCallback.intentSender)
}
},
bottomSearchViewModel = bottomSearchViewModel
}
)
onResetFloatingActionButton()
}
Expand Down Expand Up @@ -487,8 +483,7 @@ fun TraewelldroidNavHost(
) {
launchSingleTop = true
}
},
bottomSearchViewModel = bottomSearchViewModel
}
)
onResetFloatingActionButton()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,37 +1,15 @@
package de.hbch.traewelling.shared

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import de.hbch.traewelling.api.TraewellingApi
import de.hbch.traewelling.api.models.user.User

class BottomSearchViewModel : ViewModel() {
private var _clickHandler: ((String) -> Unit)? = null

private val _userResults = MutableLiveData<List<User>?>(null)
val userResults: LiveData<List<User>?> get() = _userResults

private val _displayResults = MutableLiveData(false)
val displayResults: LiveData<Boolean> get() = _displayResults

suspend fun searchUsers(query: String) {
_displayResults.postValue(true)
val data = try { TraewellingApi.userService.searchUsers(query).data } catch (_: Exception) { listOf() }
_userResults.postValue(data)
}

fun reset() {
_displayResults.postValue(false)
_userResults.postValue(null)
_clickHandler = null
}

fun registerClickHandler(handler: (String) -> Unit) {
_clickHandler = handler
}

fun onClick(selection: String) {
_clickHandler?.invoke(selection)
}
}
suspend fun searchUsers(query: String): List<User> {
return try {
TraewellingApi.userService.searchUsers(query).data
} catch (_: Exception) {
listOf()
}
}
}
73 changes: 63 additions & 10 deletions app/src/main/kotlin/de/hbch/traewelling/ui/checkIn/CheckIn.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@ package de.hbch.traewelling.ui.checkIn

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.clickable
import androidx.compose.foundation.horizontalScroll
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.foundation.layout.size
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.AssistChip
import androidx.compose.material3.Card
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.ExperimentalMaterial3Api
Expand All @@ -21,6 +24,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
Expand All @@ -37,11 +41,13 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.jcloquell.androidsecurestorage.SecureStorage
import de.hbch.traewelling.R
import de.hbch.traewelling.api.models.event.Event
import de.hbch.traewelling.api.models.status.StatusBusiness
import de.hbch.traewelling.api.models.status.StatusVisibility
import de.hbch.traewelling.api.models.user.User
import de.hbch.traewelling.shared.BottomSearchViewModel
import de.hbch.traewelling.shared.CheckInViewModel
import de.hbch.traewelling.shared.EventViewModel
Expand All @@ -53,6 +59,7 @@ import de.hbch.traewelling.ui.composables.ButtonWithIconAndText
import de.hbch.traewelling.ui.composables.DateTimeSelection
import de.hbch.traewelling.ui.composables.Dialog
import de.hbch.traewelling.ui.composables.OutlinedButtonWithIconAndText
import de.hbch.traewelling.ui.composables.ProfilePicture
import de.hbch.traewelling.ui.composables.SwitchWithIconAndText
import de.hbch.traewelling.ui.selectDestination.FromToTextRow
import de.hbch.traewelling.util.checkAnyUsernames
Expand All @@ -67,14 +74,14 @@ fun CheckIn(
modifier: Modifier = Modifier,
checkInViewModel: CheckInViewModel,
eventViewModel: EventViewModel,
bottomSearchViewModel: BottomSearchViewModel,
checkInAction: (Boolean, Boolean) -> Unit = { _, _ -> },
initText: String = "",
isEditMode: Boolean = false,
changeDestinationAction: () -> Unit = { }
) {
val secureStorage = SecureStorage(LocalContext.current)
val coroutineScope = rememberCoroutineScope()
val bottomSearchViewModel: BottomSearchViewModel = viewModel()

var enableTrwlCheckIn by rememberSaveable { mutableStateOf(secureStorage.getObject(SharedValues.SS_TRWL_AUTO_LOGIN, Boolean::class.java) ?: true) }
val travelynxConfigured = secureStorage.getObject(SharedValues.SS_TRAVELYNX_TOKEN, String::class.java)?.isNotBlank() ?: false
Expand All @@ -89,20 +96,22 @@ fun CheckIn(
val matches = statusText.text.checkAnyUsernames()
matches.firstOrNull { it.range.contains(statusText.selection.min - 1) || it.range.contains(statusText.selection.max + 1) }?.value?.replace("@", "")
} }
val userResults = remember { mutableStateListOf<User>() }
var usersQuerying by remember { mutableStateOf(false) }
var displayUserResults by remember { mutableStateOf(false) }
userSearchQuery.useDebounce(
onChange = { query ->
if (query == null) {
bottomSearchViewModel.reset()
usersQuerying = false
displayUserResults = false
} else {
bottomSearchViewModel.registerClickHandler { selection ->
val firstMatch = statusText.text.checkAnyUsernames().first { it.range.contains(statusText.selection.min - 1) || it.range.contains(statusText.selection.max + 1) }
statusText = statusText.copy(
text = statusText.text.replaceRange(firstMatch.range.first, firstMatch.range.last + 1, selection),
selection = TextRange(firstMatch.range.first + selection.length)
)
}
usersQuerying = true
displayUserResults = true
coroutineScope.launch {
bottomSearchViewModel.searchUsers(query)
val users = bottomSearchViewModel.searchUsers(query)
usersQuerying = false
userResults.clear()
userResults.addAll(users)
}
}
},
Expand Down Expand Up @@ -216,6 +225,50 @@ fun CheckIn(
text = "${statusText.text.count()}/280",
style = AppTypography.labelSmall
)
AnimatedVisibility(displayUserResults) {
Row(
modifier = Modifier
.fillMaxWidth()
.horizontalScroll(rememberScrollState()),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
if (usersQuerying) {
Text(
text = stringResource(id = R.string.data_loading)
)
} else {
if (userResults.isEmpty()) {
Text(
text = stringResource(id = R.string.no_results_found)
)
} else {
userResults.forEach {
val username = "@${it.username}"
AssistChip(
onClick = {
val firstMatch = statusText.text.checkAnyUsernames().first { it.range.contains(statusText.selection.min - 1) || it.range.contains(statusText.selection.max + 1) }
statusText = statusText.copy(
text = statusText.text.replaceRange(firstMatch.range.first, firstMatch.range.last + 1, "@${it.username} "),
selection = TextRange(firstMatch.range.first + it.username.length + 2)
)
},
label = {
Text(
text = username
)
},
leadingIcon = {
ProfilePicture(
user = it,
modifier = Modifier.size(24.dp)
)
}
)
}
}
}
}
}
}

if (travelynxConfigured && !isEditMode) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import de.hbch.traewelling.api.models.status.Status
import de.hbch.traewelling.shared.BottomSearchViewModel
import de.hbch.traewelling.shared.LoggedInUserViewModel
import de.hbch.traewelling.ui.composables.NotificationsAvailableHint
import de.hbch.traewelling.ui.include.cardSearchStation.CardSearch
Expand All @@ -33,7 +32,6 @@ import java.time.ZonedDateTime
@Composable
fun Dashboard(
loggedInUserViewModel: LoggedInUserViewModel,
bottomSearchViewModel: BottomSearchViewModel,
searchConnectionsAction: (String, ZonedDateTime?) -> Unit = { _, _ -> },
userSelectedAction: (String) -> Unit = { },
statusSelectedAction: (Int) -> Unit = { },
Expand Down Expand Up @@ -86,8 +84,7 @@ fun Dashboard(
recentStationsData = loggedInUserViewModel.lastVisitedStations,
onUserSelected = {
userSelectedAction(it.username)
},
bottomSearchViewModel = bottomSearchViewModel
}
)
}
if (!knowsAboutNotifications) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.LiveData
import de.hbch.traewelling.api.models.station.Station
import de.hbch.traewelling.api.models.user.User
import de.hbch.traewelling.shared.BottomSearchViewModel
import de.hbch.traewelling.theme.AppTypography
import de.hbch.traewelling.ui.search.Search
import de.hbch.traewelling.util.getGreeting
Expand All @@ -23,7 +22,6 @@ fun CardSearch(
modifier: Modifier = Modifier,
homelandStationData: LiveData<Station?>,
recentStationsData: LiveData<List<Station>?>,
bottomSearchViewModel: BottomSearchViewModel,
onStationSelected: (String) -> Unit = { },
onUserSelected: (User) -> Unit = { },
queryStations: Boolean = true,
Expand All @@ -50,8 +48,7 @@ fun CardSearch(
onUserSelected = onUserSelected,
queryStations = queryStations,
queryUsers = queryUsers,
modifier = Modifier.fillMaxWidth(),
bottomSearchViewModel = bottomSearchViewModel
modifier = Modifier.fillMaxWidth()
)
}
}
Expand Down
Loading

0 comments on commit 205cfab

Please sign in to comment.