Skip to content

Commit

Permalink
refactor: do not pass current account id to manage contact screen + c…
Browse files Browse the repository at this point in the history
…apitalize first letter for message text (#95)
  • Loading branch information
migulyaev authored Oct 4, 2023
1 parent 38614be commit f14c482
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 22 deletions.
1 change: 1 addition & 0 deletions app/lint.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<issue id="NewerVersionAvailable" severity="ignore" />
<issue id="GradleDependency" severity="ignore" />
<issue id="MonochromeLauncherIcon" severity="ignore" />
<issue id="RestrictedApi" severity="ignore" />
<issue id="LogConditional">
<ignore path="**"/>
</issue>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,42 +1,46 @@
package tech.relaycorp.letro.contacts

import android.util.Log
import androidx.annotation.IntDef
import androidx.annotation.StringRes
import androidx.compose.runtime.Immutable
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import tech.relaycorp.letro.R
import tech.relaycorp.letro.account.storage.repository.AccountRepository
import tech.relaycorp.letro.contacts.ManageContactScreenContent.Companion.REQUEST_SENT
import tech.relaycorp.letro.contacts.ManageContactViewModel.Type.Companion.EDIT_CONTACT
import tech.relaycorp.letro.contacts.ManageContactViewModel.Type.Companion.NEW_CONTACT
import tech.relaycorp.letro.contacts.model.Contact
import tech.relaycorp.letro.contacts.model.ContactPairingStatus
import tech.relaycorp.letro.contacts.storage.repository.ContactsRepository
import tech.relaycorp.letro.ui.navigation.Route
import tech.relaycorp.letro.utils.ext.decodeFromUTF
import tech.relaycorp.letro.utils.ext.nullIfBlankOrEmpty
import javax.inject.Inject

@OptIn(FlowPreview::class)
@OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class)
@HiltViewModel
class ManageContactViewModel @Inject constructor(
private val contactsRepository: ContactsRepository,
private val accountRepository: AccountRepository,
savedStateHandle: SavedStateHandle,
) : ViewModel() {

@Type
private val screenType: Int = savedStateHandle[Route.ManageContact.KEY_SCREEN_TYPE]!!
private val currentAccountId: String? = (savedStateHandle.get(Route.ManageContact.KEY_CURRENT_ACCOUNT_ID_ENCODED) as? String)?.decodeFromUTF() // by default Android decode strings inside navigation library, but just in case we decode it here
private val contactIdToEdit: Long? = savedStateHandle[Route.ManageContact.KEY_CONTACT_ID_TO_EDIT]

private val _uiState = MutableStateFlow(
Expand Down Expand Up @@ -66,6 +70,7 @@ class ManageContactViewModel @Inject constructor(
val showPermissionGoToSettingsSignal: SharedFlow<Unit>
get() = _showPermissionGoToSettingsSignal

private var currentAccountId: String? = null
private val contacts: HashSet<Contact> = hashSetOf()

private var editingContact: Contact? = null
Expand All @@ -86,12 +91,19 @@ class ManageContactViewModel @Inject constructor(
}
}
viewModelScope.launch {
currentAccountId?.let { currentAccountId ->
contactsRepository.getContacts(currentAccountId).collect {
accountRepository.currentAccount
.flatMapLatest {
currentAccountId = it?.accountId
if (it != null) {
contactsRepository.getContacts(it.accountId)
} else {
emptyFlow()
}
}
.collect {
contacts.clear()
contacts.addAll(it)
}
}
}
viewModelScope.launch {
checkActionButtonAvailabilityFlow
Expand Down Expand Up @@ -138,6 +150,10 @@ class ManageContactViewModel @Inject constructor(
}
}
EDIT_CONTACT -> {
if (contacts.any { it.id == contactIdToEdit }) {
Log.w(TAG, IllegalStateException("You cannot edit this contact. Contact belongs to ${contacts.firstOrNull()?.ownerVeraId}, but yours is $currentAccountId")) // TODO: log?
return
}
updateContact()
viewModelScope.launch {
_onEditContactCompleted.emit(uiState.value.accountId)
Expand Down Expand Up @@ -211,6 +227,7 @@ class ManageContactViewModel @Inject constructor(
}

private companion object {
private const val TAG = "ManageContactViewModel"
private const val CHECK_ID_DEBOUNCE_DELAY_MS = 1_500L
private val CORRECT_ID_REGEX = """^([^@]+@)?\p{L}{1,63}(\.\p{L}{1,63})+$""".toRegex()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
Expand Down Expand Up @@ -307,6 +308,9 @@ fun ComposeNewMessageScreen(
placeHolderText = stringResource(id = R.string.new_message_body_hint),
singleLine = false,
placeholderColor = MaterialTheme.colorScheme.onSurfaceVariant,
keyboardOptions = KeyboardOptions.Default.copy(
capitalization = KeyboardCapitalization.Sentences,
),
modifier = Modifier
.then(Modifier)
.applyIf(uiState.messageExceedsLimitTextError != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ import tech.relaycorp.letro.ui.theme.LetroColor
import tech.relaycorp.letro.ui.utils.StringsProvider
import tech.relaycorp.letro.utils.compose.navigation.navigateSingleTop
import tech.relaycorp.letro.utils.compose.navigation.navigateWithPoppingAllBackStack
import tech.relaycorp.letro.utils.compose.navigation.popBackStackSafe
import tech.relaycorp.letro.utils.compose.showSnackbar
import tech.relaycorp.letro.utils.ext.encodeToUTF

@Composable
fun LetroNavHost(
Expand Down Expand Up @@ -214,7 +214,6 @@ fun LetroNavHost(
navController.navigate(
Route.ManageContact.getRouteName(
screenType = ManageContactViewModel.Type.NEW_CONTACT,
currentAccountIdEncoded = uiState.currentAccount?.encodeToUTF(),
),
)
},
Expand All @@ -234,7 +233,6 @@ fun LetroNavHost(
navController.navigate(
Route.ManageContact.getRouteName(
screenType = ManageContactViewModel.Type.NEW_CONTACT,
currentAccountIdEncoded = uiState.currentAccount?.encodeToUTF(),
),
)
},
Expand All @@ -250,7 +248,7 @@ fun LetroNavHost(
)
}
composable(
route = "${Route.ManageContact.name}/{${Route.ManageContact.KEY_CURRENT_ACCOUNT_ID_ENCODED}}&{${Route.ManageContact.KEY_SCREEN_TYPE}}&{${Route.ManageContact.KEY_CONTACT_ID_TO_EDIT}}",
route = "${Route.ManageContact.name}/{${Route.ManageContact.KEY_SCREEN_TYPE}}&{${Route.ManageContact.KEY_CONTACT_ID_TO_EDIT}}",
arguments = listOf(
navArgument(Route.ManageContact.KEY_CURRENT_ACCOUNT_ID_ENCODED) {
type = NavType.StringType
Expand All @@ -269,12 +267,12 @@ fun LetroNavHost(
) { entry ->
ManageContactScreen(
onBackClick = {
navController.popBackStack()
navController.popBackStackSafe()
},
onEditContactCompleted = {
when (val type = entry.arguments?.getInt(Route.ManageContact.KEY_SCREEN_TYPE)) {
ManageContactViewModel.Type.EDIT_CONTACT -> {
navController.popBackStack()
navController.popBackStackSafe()
snackbarHostState.showSnackbar(scope, stringsProvider.snackbar.contactEdited)
}
else -> throw IllegalStateException("Unknown screen type: $type")
Expand Down Expand Up @@ -310,7 +308,6 @@ fun LetroNavHost(
navController.navigate(
Route.ManageContact.getRouteName(
screenType = ManageContactViewModel.Type.EDIT_CONTACT,
currentAccountIdEncoded = uiState.currentAccount?.encodeToUTF(),
contactIdToEdit = contact.id,
),
)
Expand Down Expand Up @@ -343,7 +340,7 @@ fun LetroNavHost(
val screenType = it.arguments?.getInt(Route.CreateNewMessage.KEY_SCREEN_TYPE)
ComposeNewMessageScreen(
conversationsStringsProvider = stringsProvider.conversations,
onBackClicked = { navController.popBackStack() },
onBackClicked = { navController.popBackStackSafe() },
onMessageSent = {
when (screenType) {
ComposeNewMessageViewModel.ScreenType.REPLY_TO_EXISTING_CONVERSATION -> {
Expand All @@ -353,7 +350,7 @@ fun LetroNavHost(
)
}
ComposeNewMessageViewModel.ScreenType.NEW_CONVERSATION -> {
navController.popBackStack()
navController.popBackStackSafe()
}
}
snackbarHostState.showSnackbar(scope, stringsProvider.snackbar.messageSent)
Expand All @@ -373,11 +370,11 @@ fun LetroNavHost(
ConversationScreen(
conversationsStringsProvider = stringsProvider.conversations,
onConversationDeleted = {
navController.popBackStack()
navController.popBackStackSafe()
snackbarHostState.showSnackbar(scope, stringsProvider.snackbar.conversationDeleted)
},
onConversationArchived = { isArchived ->
navController.popBackStack()
navController.popBackStackSafe()
snackbarHostState.showSnackbar(scope, if (isArchived) stringsProvider.snackbar.conversationArchived else stringsProvider.snackbar.conversationUnarchived)
},
onReplyClick = {
Expand All @@ -389,7 +386,7 @@ fun LetroNavHost(
)
},
onBackClicked = {
navController.popBackStack()
navController.popBackStackSafe()
},
onAttachmentClick = { fileId ->
mainViewModel.onAttachmentClick(fileId)
Expand All @@ -401,7 +398,7 @@ fun LetroNavHost(
onAddAccountClick = { navController.navigate(Route.Registration.name) },
onNotificationsClick = onGoToNotificationsSettingsClick,
onTermsAndConditionsClick = { mainViewModel.onTermsAndConditionsClick() },
onBackClick = { navController.popBackStack() },
onBackClick = { navController.popBackStackSafe() },
onAccountDeleted = {
snackbarHostState.showSnackbar(scope, stringsProvider.snackbar.accountDeleted)
},
Expand All @@ -421,7 +418,6 @@ fun LetroNavHost(
navController.navigate(
Route.ManageContact.getRouteName(
screenType = ManageContactViewModel.Type.NEW_CONTACT,
currentAccountIdEncoded = uiState.currentAccount?.encodeToUTF(),
),
)
homeViewModel.onOptionFromContactsFloatingMenuClicked()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,8 @@ sealed class Route(

fun getRouteName(
@ManageContactViewModel.Type screenType: Int,
currentAccountIdEncoded: String?,
contactIdToEdit: Long = NO_ID,
) = "${ManageContact.name}/$currentAccountIdEncoded&$screenType&$contactIdToEdit"
) = "${ManageContact.name}/$screenType&$contactIdToEdit"
}

object Home : Route(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ fun NavController.navigateSingleTop(route: Route) {
}
}

fun NavController.popBackStackSafe() {
if (currentBackStack.value.size > 2) { // StartDestination (Splash) + Root screen.
popBackStack()
}
}

fun NavController.navigateWithDropCurrentScreen(route: String) {
navigate(route) {
popBackStack()
Expand Down

0 comments on commit f14c482

Please sign in to comment.