diff --git a/app/src/main/java/tech/relaycorp/letro/contacts/ManageContactViewModel.kt b/app/src/main/java/tech/relaycorp/letro/contacts/ManageContactViewModel.kt index fd0c164a..5cf38949 100644 --- a/app/src/main/java/tech/relaycorp/letro/contacts/ManageContactViewModel.kt +++ b/app/src/main/java/tech/relaycorp/letro/contacts/ManageContactViewModel.kt @@ -7,10 +7,12 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +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.update import kotlinx.coroutines.launch import tech.relaycorp.letro.R @@ -24,6 +26,7 @@ import tech.relaycorp.letro.utils.ext.decodeFromUTF import tech.relaycorp.letro.utils.ext.nullIfBlankOrEmpty import javax.inject.Inject +@OptIn(FlowPreview::class) @HiltViewModel class ManageContactViewModel @Inject constructor( private val contactsRepository: ContactsRepository, @@ -42,14 +45,21 @@ class ManageContactViewModel @Inject constructor( EDIT_CONTACT -> ManageContactTexts.EditContact() else -> throw IllegalStateException("Unknown screen type: $screenType") }, + isActionButtonEnabled = screenType == EDIT_CONTACT, ), ) val uiState: StateFlow get() = _uiState - private val _onActionCompleted = MutableSharedFlow() - val onActionCompleted: SharedFlow - get() = _onActionCompleted + private val checkActionButtonAvailabilityFlow = MutableSharedFlow() + + private val _onEditContactCompleted = MutableSharedFlow() + val onEditContactCompleted: SharedFlow + get() = _onEditContactCompleted + + private val _goBackSignal = MutableSharedFlow() + val goBackSignal: SharedFlow + get() = _goBackSignal private val contacts: HashSet = hashSetOf() @@ -78,6 +88,13 @@ class ManageContactViewModel @Inject constructor( } } } + viewModelScope.launch { + checkActionButtonAvailabilityFlow + .debounce(CHECK_ID_DEBOUNCE_DELAY_MS) + .collect { + checkIfIdIsCorrect(it) + } + } } fun onIdChanged(id: String) { @@ -87,9 +104,9 @@ class ManageContactViewModel @Inject constructor( it.copy( veraId = trimmedId, isSentRequestAgainHintVisible = contacts.any { it.contactVeraId == trimmedId && it.status == ContactPairingStatus.REQUEST_SENT }, - pairingErrorCaption = getPairingErrorMessage(trimmedId), ) } + checkActionButtonAvailabilityFlow.emit(trimmedId) } } @@ -103,14 +120,45 @@ class ManageContactViewModel @Inject constructor( } } - fun onActionButtonClick() { + fun onUpdateContactButtonClick() { when (screenType) { - NEW_CONTACT -> sendNewContactRequest() - EDIT_CONTACT -> updateContact() + NEW_CONTACT -> { + sendNewContactRequest() + viewModelScope.launch { + _uiState.update { + it.copy( + showRequestSentScreen = true, + ) + } + } + } + EDIT_CONTACT -> { + updateContact() + viewModelScope.launch { + _onEditContactCompleted.emit(uiState.value.veraId) + } + } else -> throw IllegalStateException("Unknown screen type: $screenType") } + } + + fun onGotItClick() { + contactsRepository.saveRequestWasOnceSent() + viewModelScope.launch { + _goBackSignal.emit(Unit) + } + } + + private fun checkIfIdIsCorrect(id: String) { viewModelScope.launch { - _onActionCompleted.emit(uiState.value.veraId) + val isValidId = id.matches(CORRECT_ID_REGEX) + val errorMessage = getPairingErrorMessage(id, isValidId) + _uiState.update { + it.copy( + isActionButtonEnabled = isValidId && errorMessage == null, + pairingErrorCaption = errorMessage, + ) + } } } @@ -137,9 +185,10 @@ class ManageContactViewModel @Inject constructor( } } - private fun getPairingErrorMessage(contactId: String): PairingErrorCaption? { + private fun getPairingErrorMessage(contactId: String, isValidId: Boolean): PairingErrorCaption? { val contact = contacts.find { it.contactVeraId == contactId } return when { + !isValidId -> PairingErrorCaption(R.string.pair_request_invalid_id) contact == null -> null contact.status == ContactPairingStatus.COMPLETED -> PairingErrorCaption(R.string.pair_request_already_paired) contact.status >= ContactPairingStatus.MATCH -> PairingErrorCaption(R.string.pair_request_already_in_progress) @@ -147,6 +196,11 @@ class ManageContactViewModel @Inject constructor( } } + private companion object { + private const val CHECK_ID_DEBOUNCE_DELAY_MS = 1_000L + private val CORRECT_ID_REGEX = """^([^@]+@)?\p{L}{1,63}(\.\p{L}{1,63})+$""".toRegex() + } + @IntDef(NEW_CONTACT, EDIT_CONTACT) annotation class Type { companion object { @@ -160,9 +214,11 @@ data class PairWithOthersUiState( val manageContactTexts: ManageContactTexts, val veraId: String = "", val alias: String? = null, + val isActionButtonEnabled: Boolean = false, val isSentRequestAgainHintVisible: Boolean = false, val isVeraIdInputEnabled: Boolean = true, val pairingErrorCaption: PairingErrorCaption? = null, + val showRequestSentScreen: Boolean = false, ) @Immutable diff --git a/app/src/main/java/tech/relaycorp/letro/contacts/storage/ContactsRepository.kt b/app/src/main/java/tech/relaycorp/letro/contacts/storage/ContactsRepository.kt index dae778de..14ffcef7 100644 --- a/app/src/main/java/tech/relaycorp/letro/contacts/storage/ContactsRepository.kt +++ b/app/src/main/java/tech/relaycorp/letro/contacts/storage/ContactsRepository.kt @@ -17,31 +17,34 @@ import tech.relaycorp.letro.awala.message.MessageType import tech.relaycorp.letro.contacts.model.Contact import tech.relaycorp.letro.contacts.model.ContactPairingStatus import tech.relaycorp.letro.main.MainViewModel +import tech.relaycorp.letro.storage.Preferences import javax.inject.Inject interface ContactsRepository { - val isPairedContactsExist: StateFlow + val contactsState: StateFlow fun getContacts(ownerVeraId: String): Flow> fun getContactById(id: Long): Contact? fun addNewContact(contact: Contact) fun deleteContact(contact: Contact) fun updateContact(contact: Contact) + fun saveRequestWasOnceSent() } class ContactsRepositoryImpl @Inject constructor( private val contactsDao: ContactsDao, private val accountRepository: AccountRepository, private val awalaManager: AwalaManager, + private val preferences: Preferences, ) : ContactsRepository { private val scope = CoroutineScope(Dispatchers.IO) private val contacts = MutableStateFlow>(emptyList()) private var currentAccount: Account? = null - private val _isPairedContactsExist: MutableStateFlow = MutableStateFlow(false) - override val isPairedContactsExist: StateFlow - get() = _isPairedContactsExist + private val _contactsState: MutableStateFlow = MutableStateFlow(ContactsState()) + override val contactsState: StateFlow + get() = _contactsState init { scope.launch { @@ -49,7 +52,7 @@ class ContactsRepositoryImpl @Inject constructor( contacts.emit(it) startCollectAccountFlow() if (currentAccount != null) { - updatePairedContactExist(currentAccount) + updateContactsState(currentAccount) } } } @@ -108,31 +111,54 @@ class ContactsRepositoryImpl @Inject constructor( } } + override fun saveRequestWasOnceSent() { + val currentAccount = currentAccount ?: return + scope.launch { + preferences.putBoolean(getContactRequestHasEverBeenSentKey(currentAccount.veraId), true) + updateContactsState(currentAccount) + } + } + private fun startCollectAccountFlow() { scope.launch { accountRepository.currentAccount.collect { currentAccount = it - updatePairedContactExist(it) + updateContactsState(it) } } } - private suspend fun updatePairedContactExist(account: Account?) { + private suspend fun updateContactsState(account: Account?) { Log.d(MainViewModel.TAG, "ContactsRepository.emit(pairedContactExist)") account ?: run { - _isPairedContactsExist.emit(false) + _contactsState.emit(ContactsState()) return } - _isPairedContactsExist.emit( - contacts - .value - .any { - it.ownerVeraId == account.veraId && it.status == ContactPairingStatus.COMPLETED - }, + val isPairedContactExist = contacts + .value + .any { + it.ownerVeraId == account.veraId && it.status == ContactPairingStatus.COMPLETED + } + val isPairRequestWasEverSent = preferences.getBoolean(getContactRequestHasEverBeenSentKey(account.veraId), false) + _contactsState.emit( + ContactsState( + isPairedContactExist = isPairedContactExist, + isPairRequestWasEverSent = isPairRequestWasEverSent, + ), ) } + private fun getContactRequestHasEverBeenSentKey( + veraId: String, + ) = "${KEY_CONTACT_REQUEST_HAS_EVER_BEEN_SENT_PREFIX}$veraId" + private companion object { private const val TAG = "ContactsRepository" + private const val KEY_CONTACT_REQUEST_HAS_EVER_BEEN_SENT_PREFIX = "contact_request_has_ever_been_sent_" } } + +data class ContactsState( + val isPairedContactExist: Boolean = false, + val isPairRequestWasEverSent: Boolean = false, +) diff --git a/app/src/main/java/tech/relaycorp/letro/contacts/ui/ContactsScreen.kt b/app/src/main/java/tech/relaycorp/letro/contacts/ui/ContactsScreen.kt index 6171498a..9149ff4f 100644 --- a/app/src/main/java/tech/relaycorp/letro/contacts/ui/ContactsScreen.kt +++ b/app/src/main/java/tech/relaycorp/letro/contacts/ui/ContactsScreen.kt @@ -65,6 +65,7 @@ fun ContactsScreen( Box { if (editBottomSheet.isShown && editBottomSheet.contact != null) { ModalBottomSheet( + containerColor = MaterialTheme.colorScheme.surface, onDismissRequest = { viewModel.onEditBottomSheetDismissed() }, ) { EditContactBottomSheet( @@ -147,11 +148,12 @@ private fun EditContactBottomSheet( onDeleteClick: () -> Unit, ) { Column( - modifier = Modifier.padding( - PaddingValues( - bottom = 44.dp, + modifier = Modifier + .padding( + PaddingValues( + bottom = 44.dp, + ), ), - ), ) { Text( text = title, diff --git a/app/src/main/java/tech/relaycorp/letro/contacts/ui/ManageContactScreen.kt b/app/src/main/java/tech/relaycorp/letro/contacts/ui/ManageContactScreen.kt index 472e7f17..32a30fc7 100644 --- a/app/src/main/java/tech/relaycorp/letro/contacts/ui/ManageContactScreen.kt +++ b/app/src/main/java/tech/relaycorp/letro/contacts/ui/ManageContactScreen.kt @@ -23,6 +23,9 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import tech.relaycorp.letro.R import tech.relaycorp.letro.contacts.ManageContactViewModel +import tech.relaycorp.letro.contacts.PairWithOthersUiState +import tech.relaycorp.letro.onboarding.actionTaking.ActionTakingScreen +import tech.relaycorp.letro.onboarding.actionTaking.ActionTakingScreenUIStateModel import tech.relaycorp.letro.ui.common.LetroButtonMaxWidthFilled import tech.relaycorp.letro.ui.common.LetroInfoView import tech.relaycorp.letro.ui.common.LetroOutlinedTextField @@ -32,17 +35,47 @@ import tech.relaycorp.letro.ui.theme.LetroTheme @Composable fun ManageContactScreen( onBackClick: () -> Unit, - onActionCompleted: (String) -> Unit, + onEditContactCompleted: (String) -> Unit, viewModel: ManageContactViewModel = hiltViewModel(), ) { val uiState by viewModel.uiState.collectAsState() LaunchedEffect(Unit) { - viewModel.onActionCompleted.collect { - onActionCompleted(it) + viewModel.onEditContactCompleted.collect { + onEditContactCompleted(it) } } + LaunchedEffect(Unit) { + viewModel.goBackSignal.collect { + onBackClick() + } + } + + if (!uiState.showRequestSentScreen) { + ManageContactView( + onBackClick, + uiState, + viewModel, + ) + } else { + ActionTakingScreen( + actionTakingScreenUIStateModel = ActionTakingScreenUIStateModel.PairingRequestSent( + boldPartOfMessage = uiState.veraId, + onGotItClicked = { + viewModel.onGotItClick() + }, + ), + ) + } +} + +@Composable +private fun ManageContactView( + onBackClick: () -> Unit, + uiState: PairWithOthersUiState, + viewModel: ManageContactViewModel, +) { val errorCaption = uiState.pairingErrorCaption Column( @@ -119,8 +152,8 @@ fun ManageContactScreen( ) LetroButtonMaxWidthFilled( text = stringResource(id = uiState.manageContactTexts.button), - onClick = { viewModel.onActionButtonClick() }, - isEnabled = errorCaption == null, + onClick = { viewModel.onUpdateContactButtonClick() }, + isEnabled = uiState.isActionButtonEnabled, ) } } @@ -131,7 +164,7 @@ private fun UseExistingAccountPreview() { LetroTheme { ManageContactScreen( onBackClick = {}, - onActionCompleted = {}, + onEditContactCompleted = {}, viewModel = hiltViewModel(), ) } diff --git a/app/src/main/java/tech/relaycorp/letro/main/MainViewModel.kt b/app/src/main/java/tech/relaycorp/letro/main/MainViewModel.kt index 05ee2214..57ec4adc 100644 --- a/app/src/main/java/tech/relaycorp/letro/main/MainViewModel.kt +++ b/app/src/main/java/tech/relaycorp/letro/main/MainViewModel.kt @@ -50,7 +50,6 @@ class MainViewModel @Inject constructor( val rootNavigationScreen: StateFlow get() = _rootNavigationScreen private var currentAccount: Account? = null - private var isRegistration = false init { viewModelScope.launch { @@ -72,31 +71,28 @@ class MainViewModel @Inject constructor( viewModelScope.launch { combine( accountRepository.currentAccount, - contactsRepository.isPairedContactsExist, - ) { currentAccount, isPairedContactExist -> - Pair(currentAccount, isPairedContactExist) + contactsRepository.contactsState, + ) { currentAccount, contactsState -> + Pair(currentAccount, contactsState) } .distinctUntilChanged() .onStart { Log.d(TAG, "Start collecting the combined Flow") } .collect { val currentAccount = it.first - val isPairedContactExist = it.second - Log.d(TAG, "$currentAccount; $isPairedContactExist") + val contactsState = it.second + Log.d(TAG, "$currentAccount; $contactsState") if (currentAccount != null) { when { !currentAccount.isCreated -> { - isRegistration = true _rootNavigationScreen.emit(RootNavigationScreen.RegistrationWaiting) } - !isPairedContactExist -> { - if (isRegistration) { - _rootNavigationScreen.emit(RootNavigationScreen.WelcomeToLetro) - } else { - _rootNavigationScreen.emit(RootNavigationScreen.NoContactsScreen) - } - isRegistration = false + !contactsState.isPairRequestWasEverSent -> { + _rootNavigationScreen.emit(RootNavigationScreen.WelcomeToLetro) } - isPairedContactExist -> _rootNavigationScreen.emit(RootNavigationScreen.Home) + !contactsState.isPairedContactExist -> { + _rootNavigationScreen.emit(RootNavigationScreen.NoContactsScreen) + } + else -> _rootNavigationScreen.emit(RootNavigationScreen.Home) } } else { _rootNavigationScreen.emit(RootNavigationScreen.Registration) @@ -121,13 +117,16 @@ class MainViewModel @Inject constructor( fun onShareIdClick() { currentAccount?.veraId?.let { accountId -> viewModelScope.launch { - _joinMeOnLetroSignal.emit(accountId) + _joinMeOnLetroSignal.emit(getJoinMeLink(accountId)) } } } + private fun getJoinMeLink(accountId: String) = "$JOIN_ME_ON_LETRO_COMMON_PART_OF_LINK$accountId" + companion object { const val TAG = "MainViewModel" + private const val JOIN_ME_ON_LETRO_COMMON_PART_OF_LINK = "https://letro.app/connect/#u=" private const val AWALA_GOOGLE_PLAY_LINK = "https://play.google.com/store/apps/details?id=tech.relaycorp.gateway" } } diff --git a/app/src/main/java/tech/relaycorp/letro/ui/MainActivity.kt b/app/src/main/java/tech/relaycorp/letro/ui/MainActivity.kt index 5fc555c8..ad0ca447 100644 --- a/app/src/main/java/tech/relaycorp/letro/ui/MainActivity.kt +++ b/app/src/main/java/tech/relaycorp/letro/ui/MainActivity.kt @@ -72,9 +72,9 @@ class MainActivity : ComponentActivity() { } } lifecycleScope.launch(Dispatchers.Main) { - viewModel.joinMeOnLetroSignal.collect { id -> + viewModel.joinMeOnLetroSignal.collect { link -> try { - val text = getString(R.string.join_me_on_letro, id) + val text = getString(R.string.join_me_on_letro, link) startActivity( Intent( Intent.ACTION_SEND, diff --git a/app/src/main/java/tech/relaycorp/letro/ui/common/LetroButton.kt b/app/src/main/java/tech/relaycorp/letro/ui/common/LetroButton.kt index 04aae1a6..2a7a3577 100644 --- a/app/src/main/java/tech/relaycorp/letro/ui/common/LetroButton.kt +++ b/app/src/main/java/tech/relaycorp/letro/ui/common/LetroButton.kt @@ -15,6 +15,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp +import tech.relaycorp.letro.ui.theme.LargeProminent import tech.relaycorp.letro.ui.theme.LetroColor @Composable @@ -68,7 +69,7 @@ fun LetroButton( } Text( text = text, - style = MaterialTheme.typography.labelLarge, + style = MaterialTheme.typography.LargeProminent, ) } } diff --git a/app/src/main/java/tech/relaycorp/letro/ui/common/text/HyperlinkText.kt b/app/src/main/java/tech/relaycorp/letro/ui/common/text/HyperlinkText.kt index 0a49e850..6f6f86b6 100644 --- a/app/src/main/java/tech/relaycorp/letro/ui/common/text/HyperlinkText.kt +++ b/app/src/main/java/tech/relaycorp/letro/ui/common/text/HyperlinkText.kt @@ -6,11 +6,15 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalUriHandler +import androidx.compose.ui.text.ParagraphStyle import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.text.withStyle import androidx.compose.ui.unit.TextUnit +import androidx.compose.ui.unit.TextUnitType +import androidx.compose.ui.unit.sp @Composable fun HyperlinkText( @@ -24,34 +28,42 @@ fun HyperlinkText( fontSize: TextUnit = TextUnit.Unspecified, ) { val annotatedString = buildAnnotatedString { - append(fullText) - addStyle( - style = SpanStyle( - fontSize = fontSize, - color = textColor, + withStyle( + style = ParagraphStyle( + lineHeight = 18.sp, ), - start = 0, - end = fullText.length, - ) - for ((key, value) in hyperLinks) { - val startIndex = fullText.indexOf(key) - val endIndex = startIndex + key.length + ) { + append(fullText) addStyle( style = SpanStyle( - color = linkTextColor, fontSize = fontSize, - fontWeight = linkTextFontWeight, - textDecoration = linkTextDecoration, + color = textColor, + letterSpacing = TextUnit(0.2f, TextUnitType.Sp), ), - start = startIndex, - end = endIndex, - ) - addStringAnnotation( - tag = "URL", - annotation = value, - start = startIndex, - end = endIndex, + start = 0, + end = fullText.length, ) + for ((key, value) in hyperLinks) { + val startIndex = fullText.indexOf(key) + val endIndex = startIndex + key.length + addStyle( + style = SpanStyle( + color = linkTextColor, + fontSize = fontSize, + fontWeight = linkTextFontWeight, + textDecoration = linkTextDecoration, + letterSpacing = TextUnit(0.2f, TextUnitType.Sp), + ), + start = startIndex, + end = endIndex, + ) + addStringAnnotation( + tag = "URL", + annotation = value, + start = startIndex, + end = endIndex, + ) + } } } diff --git a/app/src/main/java/tech/relaycorp/letro/ui/navigation/LetroNavHost.kt b/app/src/main/java/tech/relaycorp/letro/ui/navigation/LetroNavHost.kt index a1d26169..201fde3d 100644 --- a/app/src/main/java/tech/relaycorp/letro/ui/navigation/LetroNavHost.kt +++ b/app/src/main/java/tech/relaycorp/letro/ui/navigation/LetroNavHost.kt @@ -51,7 +51,6 @@ import tech.relaycorp.letro.ui.theme.LetroColor import tech.relaycorp.letro.ui.utils.SnackbarStringsProvider import tech.relaycorp.letro.utils.compose.rememberLifecycleEvent import tech.relaycorp.letro.utils.ext.encodeToUTF -import tech.relaycorp.letro.utils.navigation.navigateWithDropCurrentScreen import tech.relaycorp.letro.utils.navigation.navigateWithPoppingAllBackStack @Composable @@ -182,24 +181,6 @@ fun LetroNavHost( actionTakingScreenUIStateModel = ActionTakingScreenUIStateModel.RegistrationWaiting, ) } - composable( - route = "${Route.PairingRequestSent.name}/{${Route.PairingRequestSent.RECEIVER_ARGUMENT_VERA_ID}}", - arguments = listOf( - navArgument(Route.PairingRequestSent.RECEIVER_ARGUMENT_VERA_ID) { - type = NavType.StringType - nullable = false - }, - ), - ) { - ActionTakingScreen( - actionTakingScreenUIStateModel = ActionTakingScreenUIStateModel.PairingRequestSent( - boldPartOfMessage = it.arguments?.getString(Route.PairingRequestSent.RECEIVER_ARGUMENT_VERA_ID)!!, - onGotItClicked = { - navController.popBackStack() - }, - ), - ) - } composable( route = "${Route.ManageContact.name}/{${Route.ManageContact.KEY_CURRENT_ACCOUNT_ID_ENCODED}}&{${Route.ManageContact.KEY_SCREEN_TYPE}}&{${Route.ManageContact.KEY_CONTACT_ID_TO_EDIT}}", arguments = listOf( @@ -222,15 +203,8 @@ fun LetroNavHost( onBackClick = { navController.popBackStack() }, - onActionCompleted = { + onEditContactCompleted = { when (val type = entry.arguments?.getInt(Route.ManageContact.KEY_SCREEN_TYPE)) { - ManageContactViewModel.Type.NEW_CONTACT -> { - navController.navigateWithDropCurrentScreen( - Route.PairingRequestSent.getRouteName( - receiverVeraId = it, - ), - ) - } ManageContactViewModel.Type.EDIT_CONTACT -> { navController.popBackStack() scope.launch { diff --git a/app/src/main/java/tech/relaycorp/letro/ui/navigation/Route.kt b/app/src/main/java/tech/relaycorp/letro/ui/navigation/Route.kt index 7d7607e9..24c483f7 100644 --- a/app/src/main/java/tech/relaycorp/letro/ui/navigation/Route.kt +++ b/app/src/main/java/tech/relaycorp/letro/ui/navigation/Route.kt @@ -46,19 +46,6 @@ sealed class Route( isStatusBarPrimaryColor = true, ) - object PairingRequestSent : Route( - name = "pairing_request_sent_route", - showTopBar = true, - isStatusBarPrimaryColor = true, - ) { - - const val RECEIVER_ARGUMENT_VERA_ID = "receiver_vera_id" - - fun getRouteName(receiverVeraId: String): String { - return "$name/$receiverVeraId" - } - } - object ManageContact : Route( name = "manage_contact_route", showTopBar = true, @@ -99,7 +86,6 @@ fun String?.toRoute(): Route { it.startsWith(Route.WelcomeToLetro.name) -> Route.WelcomeToLetro it.startsWith(Route.NoContacts.name) -> Route.NoContacts it.startsWith(Route.ManageContact.name) -> Route.ManageContact - it.startsWith(Route.PairingRequestSent.name) -> Route.PairingRequestSent it.startsWith(Route.Home.name) -> Route.Home it.startsWith(Route.CreateNewMessage.name) -> Route.CreateNewMessage else -> throw IllegalArgumentException("Define the Route by the name of the Route $it") diff --git a/app/src/main/java/tech/relaycorp/letro/ui/theme/Color.kt b/app/src/main/java/tech/relaycorp/letro/ui/theme/Color.kt index 79afe792..9842623c 100644 --- a/app/src/main/java/tech/relaycorp/letro/ui/theme/Color.kt +++ b/app/src/main/java/tech/relaycorp/letro/ui/theme/Color.kt @@ -42,7 +42,11 @@ object LetroColor { val OnSurfaceContainer: Color @Composable - get() = if (isSystemInDarkTheme()) NeutralVariant5 else Neutral4 + get() = if (isSystemInDarkTheme()) NeutralVariant6 else Neutral4 + + val OnSurfaceVariant: Color + @Composable + get() = if (isSystemInDarkTheme()) NeutralVariant5 else Neutral3 @Composable fun statusBarUnderDialogOverlay(): Color { @@ -56,6 +60,6 @@ object LetroColor { @Composable fun disabledButtonTextColor(): Color { - return if (isSystemInDarkTheme()) Color(0x1AEFF0F3) else Color(0x5C0C1B44) + return if (isSystemInDarkTheme()) Color(0xFF706F73) else Color(0x5C0C1B44) } } diff --git a/app/src/main/java/tech/relaycorp/letro/ui/theme/Type.kt b/app/src/main/java/tech/relaycorp/letro/ui/theme/Type.kt index c346606a..bb501881 100644 --- a/app/src/main/java/tech/relaycorp/letro/ui/theme/Type.kt +++ b/app/src/main/java/tech/relaycorp/letro/ui/theme/Type.kt @@ -38,29 +38,29 @@ val Typography = Typography( headlineSmall = TextStyle( fontFamily = Inter, fontWeight = FontWeight.SemiBold, - fontSize = 24.sp, - lineHeight = 32.sp, - letterSpacing = (-0.6).sp, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = (-0.3).sp, ), titleLarge = TextStyle( fontFamily = Inter, fontWeight = FontWeight.SemiBold, - fontSize = 22.sp, - lineHeight = 28.sp, + fontSize = 23.sp, + lineHeight = 29.sp, letterSpacing = (-0.3).sp, ), titleMedium = TextStyle( fontFamily = Inter, fontWeight = FontWeight.Medium, - fontSize = 16.sp, - lineHeight = 24.sp, + fontSize = 17.sp, + lineHeight = 25.sp, letterSpacing = (-0.2).sp, ), titleSmall = TextStyle( fontFamily = Inter, fontWeight = FontWeight.Medium, - fontSize = 14.sp, - lineHeight = 20.sp, + fontSize = 15.sp, + lineHeight = 21.sp, letterSpacing = (-0.2).sp, ), labelLarge = TextStyle( @@ -68,7 +68,7 @@ val Typography = Typography( fontWeight = FontWeight.Medium, fontSize = 14.sp, lineHeight = 20.sp, - letterSpacing = (-0.2).sp, + letterSpacing = (-0.25).sp, ), labelMedium = TextStyle( fontFamily = Inter, @@ -87,23 +87,22 @@ val Typography = Typography( bodyLarge = TextStyle( fontFamily = Inter, fontWeight = FontWeight.Normal, - fontSize = 16.sp, + fontSize = 18.sp, lineHeight = 24.sp, - letterSpacing = (-0.15).sp, + letterSpacing = 0.05.sp, ), bodyMedium = TextStyle( fontFamily = Inter, fontWeight = FontWeight.Normal, - fontSize = 14.sp, + fontSize = 16.sp, lineHeight = 20.sp, letterSpacing = (-0.25).sp, ), bodySmall = TextStyle( fontFamily = Inter, fontWeight = FontWeight.Normal, - fontSize = 12.sp, + fontSize = 14.sp, lineHeight = 16.sp, - letterSpacing = 0.sp, ), ) @@ -112,23 +111,23 @@ val Typography.LargeProminent: TextStyle fontFamily = Inter, fontWeight = FontWeight.SemiBold, fontSize = 16.sp, - lineHeight = 24.sp, - letterSpacing = (-0.15).sp, + lineHeight = 20.sp, + letterSpacing = 0.1.sp, ) val Typography.MediumProminent: TextStyle get() = TextStyle( fontFamily = Inter, fontWeight = FontWeight.SemiBold, - fontSize = 14.sp, + fontSize = 16.sp, lineHeight = 20.sp, - letterSpacing = (-0.15).sp, + letterSpacing = 0.1.sp, ) val Typography.SmallProminent: TextStyle get() = TextStyle( fontFamily = Inter, fontWeight = FontWeight.SemiBold, - fontSize = 11.sp, + fontSize = 14.sp, lineHeight = 16.sp, ) diff --git a/app/src/main/res/drawable-night-hdpi/letro_icon.webp b/app/src/main/res/drawable-night-hdpi/letro_icon.webp new file mode 100644 index 00000000..ac12eeaf Binary files /dev/null and b/app/src/main/res/drawable-night-hdpi/letro_icon.webp differ diff --git a/app/src/main/res/drawable-night-ldpi/letro_icon.webp b/app/src/main/res/drawable-night-ldpi/letro_icon.webp new file mode 100644 index 00000000..4a5e4d19 Binary files /dev/null and b/app/src/main/res/drawable-night-ldpi/letro_icon.webp differ diff --git a/app/src/main/res/drawable-night-mdpi/letro_icon.webp b/app/src/main/res/drawable-night-mdpi/letro_icon.webp new file mode 100644 index 00000000..4fc17996 Binary files /dev/null and b/app/src/main/res/drawable-night-mdpi/letro_icon.webp differ diff --git a/app/src/main/res/drawable-night-xhdpi/letro_icon.webp b/app/src/main/res/drawable-night-xhdpi/letro_icon.webp new file mode 100644 index 00000000..590616cc Binary files /dev/null and b/app/src/main/res/drawable-night-xhdpi/letro_icon.webp differ diff --git a/app/src/main/res/drawable-night-xxhdpi/letro_icon.webp b/app/src/main/res/drawable-night-xxhdpi/letro_icon.webp new file mode 100644 index 00000000..b557f113 Binary files /dev/null and b/app/src/main/res/drawable-night-xxhdpi/letro_icon.webp differ diff --git a/app/src/main/res/drawable-night-xxxhdpi/letro_icon.webp b/app/src/main/res/drawable-night-xxxhdpi/letro_icon.webp new file mode 100644 index 00000000..654ccca4 Binary files /dev/null and b/app/src/main/res/drawable-night-xxxhdpi/letro_icon.webp differ diff --git a/app/src/main/res/drawable-night/letro_logo.xml b/app/src/main/res/drawable-night/letro_logo.xml new file mode 100644 index 00000000..36dba325 --- /dev/null +++ b/app/src/main/res/drawable-night/letro_logo.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-es-rVE/strings.xml b/app/src/main/res/values-es-rVE/strings.xml index e6c66deb..ae5b281c 100644 --- a/app/src/main/res/values-es-rVE/strings.xml +++ b/app/src/main/res/values-es-rVE/strings.xml @@ -47,7 +47,7 @@ ¡Cambios guardados! Eliminiar contacto ¿Estás seguro de que quieres borrar a %s de tus contactos? - Conecta conmigo en Letro: https://letro.app/connect/#u=%1$s + Conecta conmigo en Letro: %s Regresar teresa.carreño@pianistas.org.ve Teresa Carreño diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 990446b2..d907fa6f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -41,6 +41,7 @@ You already tried to connect with them, but we can send another request if you want. You two are already connected. Your pairing with this user is already under way. + Enter an id like “alice@example.com“ or “example.com“. Conversations Contacts @@ -65,7 +66,7 @@ Delete contact Are you sure you want to delete %s from your contact list? - Join me on Letro: https://letro.app/connect/#u=%1$s + Join me on Letro: %s Go back