Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Qol updates #113

Merged
merged 12 commits into from
May 25, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import coil.ImageLoader
import coil.request.ImageRequest
import coil.size.Precision
import com.crisiscleanup.core.designsystem.component.ViewImageViewState
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
Expand Down Expand Up @@ -81,15 +80,15 @@ class MultiImageViewModel @Inject constructor(
callbackFlow {
val request = ImageRequest.Builder(context)
.data(url)
.size(Int.MAX_VALUE)
.precision(Precision.INEXACT)
.target(
onStart = {
channel.trySend(ViewImageViewState.Loading)
},
onSuccess = { result ->
val bitmap = (result as BitmapDrawable).bitmap.asImageBitmap()
ensureActive()
bitmap.prepareToDraw()
ensureActive()
channel.trySend(ViewImageViewState.Image(url, bitmap))
},
onError = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import coil.ImageLoader
import coil.request.ImageRequest
import coil.size.Precision
import com.crisiscleanup.core.designsystem.component.ViewImageViewState
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
Expand Down Expand Up @@ -37,14 +36,13 @@ class SingleImageViewModel @Inject constructor(
callbackFlow {
val request = ImageRequest.Builder(context)
.data(url)
.size(Int.MAX_VALUE)
.precision(Precision.INEXACT)
.target(
onStart = {
channel.trySend(ViewImageViewState.Loading)
},
onSuccess = { result ->
val bitmap = (result as BitmapDrawable).bitmap.asImageBitmap()
bitmap.prepareToDraw()
channel.trySend(ViewImageViewState.Image(url, bitmap))
},
onError = {
Expand Down
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ plugins {

android {
defaultConfig {
val buildVersion = 201
val buildVersion = 203
applicationId = "com.crisiscleanup"
versionCode = buildVersion
versionName = "0.9.${buildVersion - 168}"
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/java/com/crisiscleanup/MainActivityViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ class MainActivityViewModel @Inject constructor(
syncPuller.appPullIncident(incidentSelector.incidentId.first())
accountDataRefresher.updateMyOrganization(true)
accountDataRefresher.updateApprovedIncidents()

if (!it.hasAcceptedTerms && !it.areTokensValid) {
authEventBus.onLogout()
}
}
.launchIn(viewModelScope)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ private fun CaseView(
with(caseSummary) {
icon?.let {
Image(
// TODO Cache image bitmap, prepareToDraw() as well
bitmap = it.asImageBitmap(),
contentDescription = summary.workType?.workTypeLiteral,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ interface LanguageTranslationsRepository : KeyTranslator {
fun setLanguageFromSystem()

suspend fun getLanguageOptions(): List<LanguageIdName>

fun getRecommendedLanguage(languageOptions: List<LanguageIdName>): LanguageIdName
}

@Singleton
Expand Down Expand Up @@ -216,6 +218,26 @@ class OfflineFirstLanguageTranslationsRepository @Inject constructor(
return emptyList()
}

override fun getRecommendedLanguage(languageOptions: List<LanguageIdName>): LanguageIdName {
val systemLocale = Locale.getDefault().toLanguageTag()
val languageLookup = languageOptions
.associateBy {
it.name.split(".").last()
}
val localeLower = systemLocale.lowercase()
languageLookup[localeLower]?.let {
return it
}

languageOptions.firstOrNull {
it.name.contains("en-us")
}?.let {
return it
}

return languageOptions.first()
}

override fun translate(phraseKey: String): String? {
translations.value[phraseKey]?.let {
return it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ class OrgPersistentInviteViewModel @Inject constructor(
languageOptions
.onEach {
if (it.isNotEmpty() && userInfo.language.name.isBlank()) {
userInfo.language = it.first()
userInfo.language = languageRepository.getRecommendedLanguage(it)
}
}
.launchIn(viewModelScope)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ class RequestOrgAccessViewModel @Inject constructor(
languageOptions
.onEach {
if (it.isNotEmpty() && userInfo.language.name.isBlank()) {
userInfo.language = it.first()
userInfo.language = languageRepository.getRecommendedLanguage(it)
}
}
.launchIn(viewModelScope)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,11 @@ private fun LoginWithPhoneScreen(
onBack: () -> Unit = {},
viewModel: LoginWithPhoneViewModel = hiltViewModel(),
) {
val translator = LocalAppTranslator.current
val t = LocalAppTranslator.current

Text(
modifier = listItemModifier.testTag("phoneLoginHeaderText"),
text = translator("actions.login", R.string.login),
text = t("actions.login", R.string.login),
style = LocalFontStyles.current.header1,
)

Expand All @@ -161,7 +161,7 @@ private fun LoginWithPhoneScreen(
}
OutlinedClearableTextField(
modifier = fillWidthPadded.testTag("loginPhoneTextField"),
label = translator("loginWithPhone.enter_cell"),
label = t("loginWithPhone.enter_cell"),
value = phoneNumber,
onValueChange = updateEmailInput,
keyboardType = KeyboardType.Phone,
Expand All @@ -174,7 +174,7 @@ private fun LoginWithPhoneScreen(

// TODO Hide if device does not have a SIM/phone number
LinkAction(
"~~Use phone's number",
t("loginWithPhone.use_phones_number"),
modifier = Modifier
.listItemPadding()
.testTag("phoneLoginRequestPhoneNumber"),
Expand All @@ -188,7 +188,7 @@ private fun LoginWithPhoneScreen(
modifier = fillWidthPadded.testTag("phoneLoginAction"),
onClick = requestPhoneCode,
enabled = isNotBusy,
text = translator("loginForm.login_with_cell"),
text = t("loginForm.login_with_cell"),
indicateBusy = isRequestingCode,
)

Expand Down Expand Up @@ -271,7 +271,7 @@ private fun ColumnScope.VerifyPhoneCodeScreen(
val updatePhoneCode = remember(viewModel) { { s: String -> viewModel.phoneCode = s.trim() } }
OutlinedClearableTextField(
modifier = listItemModifier.testTag("loginPhoneCodeTextField"),
label = t("~~Phone login code"),
label = t("loginWithPhone.phone_login_code"),
value = viewModel.phoneCode.trim(),
onValueChange = updatePhoneCode,
keyboardType = KeyboardType.Number,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,18 +211,29 @@ class LocationInputData(
referenceWorksite = referenceWorksite.copy(
latitude = worksite.latitude,
longitude = worksite.longitude,
address = worksite.address,
city = worksite.city,
county = worksite.county,
postalCode = worksite.postalCode,
state = worksite.state,
)
coordinates.value = LatLng(worksite.latitude, worksite.longitude)
streetAddress = worksite.address
zipCode = worksite.postalCode
city = worksite.city
county = worksite.county
state = worksite.state

if (
worksite.address.isNotBlank() ||
worksite.city.isNotBlank() ||
worksite.county.isNotBlank() ||
worksite.postalCode.isNotBlank() ||
worksite.state.isNotBlank()
) {
referenceWorksite = referenceWorksite.copy(
address = worksite.address,
city = worksite.city,
county = worksite.county,
postalCode = worksite.postalCode,
state = worksite.state,
)
streetAddress = worksite.address
zipCode = worksite.postalCode
city = worksite.city
county = worksite.county
state = worksite.state
}

if (isIncompleteAddress) {
isEditingAddress = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,6 @@ private fun LocationAddressFormView(
keyboardCapitalization = KeyboardCapitalization.Words,
isError = isStateError,
hasFocus = focusState,
imeAction = ImeAction.Done,
onEnter = onStateEnd,
enabled = isEditable,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,8 @@ private fun CaseInfoView(

val distanceAwayText by viewModel.distanceAwayText.collectAsStateWithLifecycle()

LazyColumn {
val listState = rememberLazyListState()
LazyColumn(state = listState) {
item(key = "incident-info") {
val caseData by viewModel.caseData.collectAsStateWithLifecycle()
caseData?.let { caseState ->
Expand All @@ -597,6 +598,10 @@ private fun CaseInfoView(
isSyncing = isSyncing,
scheduleSync = scheduleSync,
)

LaunchedEffect(Unit) {
listState.scrollToItem(0)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.crisiscleanup.feature.cases

import android.graphics.Bitmap
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.crisiscleanup.core.common.combine
Expand Down Expand Up @@ -31,14 +34,18 @@ import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import javax.inject.Inject
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.flow.combine as kCombine

@OptIn(FlowPreview::class)
@HiltViewModel
class CasesSearchViewModel @Inject constructor(
private val incidentSelector: IncidentSelector,
Expand Down Expand Up @@ -297,10 +304,21 @@ class CasesSearchViewModel @Inject constructor(
started = SharingStarted.WhileSubscribed(),
)

var focusOnSearchInput by mutableStateOf(false)
private set

init {
viewModelScope.launch(ioDispatcher) {
databaseManagementRepository.rebuildFts()
}

recentWorksites
.debounce(0.6.seconds)
.filter { it.isEmpty() }
.onEach {
focusOnSearchInput = true
}
.launchIn(viewModelScope)
}

private fun getIcon(workType: WorkType?) = workType?.let {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
Expand Down Expand Up @@ -48,6 +50,14 @@ internal fun CasesSearchRoute(
openCase: (Long, Long) -> Boolean = { _, _ -> false },
viewModel: CasesSearchViewModel = hiltViewModel(),
) {
// Guard due to navigation animations
var onBackCount by remember { mutableIntStateOf(0) }
val onSingleBack = {
if (onBackCount++ == 0) {
onBackClick()
}
}

val selectedWorksite by viewModel.selectedWorksite.collectAsStateWithLifecycle()
if (selectedWorksite.second != EmptyWorksite.id) {
openCase(selectedWorksite.first, selectedWorksite.second)
Expand Down Expand Up @@ -78,16 +88,18 @@ internal fun CasesSearchRoute(
val recentCases by viewModel.recentWorksites.collectAsStateWithLifecycle()
val searchResults by viewModel.searchResults.collectAsStateWithLifecycle()

val closeKeyboard = rememberCloseKeyboard(viewModel)
val focusOnSearchInput = viewModel.focusOnSearchInput

val closeKeyboard = rememberCloseKeyboard(viewModel)
val isEditable = !isSelectingResult
if (isListDetailLayout) {
Row {
SearchCasesView(
onBackClick,
onSingleBack,
q,
updateQuery,
isEditable,
hasFocus = focusOnSearchInput,
closeKeyboard,
onCaseSelect,
emptyList(),
Expand Down Expand Up @@ -117,10 +129,11 @@ internal fun CasesSearchRoute(
}
} else {
SearchCasesView(
onBackClick,
onSingleBack,
q,
updateQuery,
isEditable,
hasFocus = focusOnSearchInput,
closeKeyboard,
onCaseSelect,
recentCases,
Expand All @@ -139,6 +152,7 @@ private fun SearchCasesView(
q: String,
updateQuery: (String) -> Unit,
isEditable: Boolean,
hasFocus: Boolean,
closeKeyboard: () -> Unit,
onCaseSelect: (CaseSummaryResult) -> Unit,
recentCases: List<CaseSummaryResult>,
Expand All @@ -155,6 +169,7 @@ private fun SearchCasesView(
q,
updateQuery,
isEditable,
hasFocus = hasFocus,
closeKeyboard,
)

Expand All @@ -179,6 +194,7 @@ private fun SearchBar(
q: String,
updateQuery: (String) -> Unit,
isEditable: Boolean,
hasFocus: Boolean,
closeKeyboard: () -> Unit,
) {
val t = LocalAppTranslator.current
Expand Down Expand Up @@ -210,6 +226,7 @@ private fun SearchBar(
enabled = isEditable,
imeAction = ImeAction.Done,
onEnter = closeKeyboard,
hasFocus = hasFocus,
isError = false,
)
}
Expand Down
Loading
Loading