Skip to content

Commit

Permalink
feat: Add EmailAddressTextField (#258)
Browse files Browse the repository at this point in the history
  • Loading branch information
sirambd authored Dec 23, 2024
2 parents c748c8a + 32331ef commit 0ab0cab
Show file tree
Hide file tree
Showing 14 changed files with 554 additions and 114 deletions.
2 changes: 1 addition & 1 deletion Core2/src/main/java/com/infomaniak/core2/EmailUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ package com.infomaniak.core2

import android.util.Patterns

fun String.isEmail(): Boolean = Patterns.EMAIL_ADDRESS.matcher(this).matches()
fun String.isValidEmail(): Boolean = Patterns.EMAIL_ADDRESS.matcher(trim()).matches()

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ fun EmailsFlowRow(
horizontalArrangement = horizontalArrangement,
) {
emails.forEach {
EmailAddressChip(
SwissTransferSuggestionChip(
text = it,
modifier = Modifier.padding(horizontal = Margin.Micro),
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
* Infomaniak SwissTransfer - Android
* Copyright (C) 2024 Infomaniak Network SA
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.infomaniak.swisstransfer.ui.components

import android.content.res.Configuration
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.datasource.LoremIpsum
import com.infomaniak.swisstransfer.R
import com.infomaniak.swisstransfer.ui.images.AppImages
import com.infomaniak.swisstransfer.ui.images.icons.CrossThick
import com.infomaniak.swisstransfer.ui.theme.CustomShapes
import com.infomaniak.swisstransfer.ui.theme.Dimens
import com.infomaniak.swisstransfer.ui.theme.Margin
import com.infomaniak.swisstransfer.ui.theme.SwissTransferTheme

@Composable
fun SwissTransferSuggestionChip(text: String, modifier: Modifier = Modifier) {
SuggestionChip(
modifier = modifier,
onClick = {},
label = { ChipLabel(text) },
enabled = false,
shape = CustomShapes.ROUNDED,
colors = SuggestionChipDefaults.suggestionChipColors(
disabledContainerColor = SwissTransferTheme.colors.emailAddressChipColor,
disabledLabelColor = SwissTransferTheme.colors.onEmailAddressChipColor,
),
border = null,
)
}

@Composable
fun SwissTransferInputChip(
modifier: Modifier = Modifier,
text: String,
isSelected: () -> Boolean,
onClick: () -> Unit,
onDismiss: () -> Unit,
) {
InputChip(
modifier = modifier.widthIn(min = Dimens.InputChipMinWidth),
selected = isSelected(),
onClick = onClick,
label = { ChipLabel(text) },
shape = CustomShapes.ROUNDED,
colors = InputChipDefaults.inputChipColors(
containerColor = SwissTransferTheme.colors.emailAddressChipColor,
labelColor = SwissTransferTheme.colors.onEmailAddressChipColor,
selectedLabelColor = SwissTransferTheme.colors.emailAddressChipColor,
selectedContainerColor = SwissTransferTheme.colors.onEmailAddressChipColor,
selectedTrailingIconColor = SwissTransferTheme.colors.emailAddressChipColor,
trailingIconColor = SwissTransferTheme.colors.onEmailAddressChipColor,
),
border = null,
trailingIcon = {
IconButton(modifier = Modifier.size(Dimens.IconSize), onClick = onDismiss) {
Icon(
modifier = Modifier.size(Dimens.MicroIconSize),
imageVector = AppImages.AppIcons.CrossThick,
contentDescription = stringResource(R.string.contentDescriptionButtonRemoveRecipient, text),
)
}
},
)
}

@Composable
private fun ChipLabel(text: String) {
Text(
text = text,
style = SwissTransferTheme.typography.bodyRegular,
maxLines = 1,
overflow = TextOverflow.MiddleEllipsis,
)
}

@Preview(name = "Light mode")
@Preview(name = "Dark mode", uiMode = Configuration.UI_MODE_NIGHT_YES or Configuration.UI_MODE_TYPE_NORMAL)
@Composable
private fun EmailAddressChipPreview() {

fun getLoremText(words: Int) = LoremIpsum(words).values.joinToString(separator = " ")

SwissTransferTheme {
Surface {
Column(Modifier.padding(Margin.Small)) {
SwissTransferSuggestionChip(text = LoremIpsum(2).values.joinToString(separator = " "))

Row(
modifier = Modifier.horizontalScroll(rememberScrollState()),
horizontalArrangement = Arrangement.spacedBy(Margin.Mini),
) {
SwissTransferInputChip(text = getLoremText(2), isSelected = { true }, onClick = {}, onDismiss = { })
SwissTransferInputChip(text = getLoremText(1), isSelected = { false }, onClick = {}, onDismiss = { })
SwissTransferInputChip(text = getLoremText(4), isSelected = { false }, onClick = {}, onDismiss = { })
SwissTransferInputChip(text = getLoremText(1), isSelected = { false }, onClick = {}, onDismiss = { })
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,6 @@ fun SwissTransferTextField(
var text by rememberSaveable { mutableStateOf(initialValue) }

val displayLabel = if (isRequired) label else "$label ${stringResource(R.string.textFieldOptional)}"
val textFieldColors = OutlinedTextFieldDefaults.colors(
unfocusedLabelColor = SwissTransferTheme.colors.tertiaryTextColor,
unfocusedSupportingTextColor = SwissTransferTheme.colors.tertiaryTextColor,
disabledBorderColor = SwissTransferTheme.materialColors.outline,
unfocusedTrailingIconColor = SwissTransferTheme.colors.iconColor,
disabledTrailingIconColor = SwissTransferTheme.colors.iconColor,
)

OutlinedTextField(
modifier = modifier,
Expand All @@ -92,7 +85,7 @@ fun SwissTransferTextField(
label = displayLabel?.let { { Text(it) } },
minLines = minLineNumber,
maxLines = maxLineNumber,
colors = textFieldColors,
colors = SwissTransferTextFieldDefaults.colors(),
textStyle = TextStyle(color = SwissTransferTheme.colors.primaryTextColor),
onValueChange = {
text = it
Expand Down Expand Up @@ -129,6 +122,18 @@ private fun getShowPasswordButton(shouldShowPassword: Boolean, onClick: () -> Un
}
}

object SwissTransferTextFieldDefaults {

@Composable
fun colors() = OutlinedTextFieldDefaults.colors(
unfocusedLabelColor = SwissTransferTheme.colors.tertiaryTextColor,
unfocusedSupportingTextColor = SwissTransferTheme.colors.tertiaryTextColor,
disabledBorderColor = SwissTransferTheme.materialColors.outline,
unfocusedTrailingIconColor = SwissTransferTheme.colors.iconColor,
disabledTrailingIconColor = SwissTransferTheme.colors.iconColor,
)
}

@Composable
@Preview
private fun Preview() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import androidx.compose.runtime.setValue
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.infomaniak.core2.isValidEmail
import com.infomaniak.multiplatform_swisstransfer.common.interfaces.upload.RemoteUploadFile
import com.infomaniak.multiplatform_swisstransfer.common.interfaces.upload.UploadFileSession
import com.infomaniak.multiplatform_swisstransfer.common.utils.mapToList
Expand All @@ -40,6 +41,7 @@ import com.infomaniak.swisstransfer.ui.screen.main.settings.EmailLanguageOption.
import com.infomaniak.swisstransfer.ui.screen.main.settings.ValidityPeriodOption
import com.infomaniak.swisstransfer.ui.screen.main.settings.ValidityPeriodOption.Companion.toTransferOption
import com.infomaniak.swisstransfer.ui.screen.main.settings.components.SettingOption
import com.infomaniak.swisstransfer.ui.screen.newtransfer.importfiles.EmailTextFieldCallbacks
import com.infomaniak.swisstransfer.ui.screen.newtransfer.importfiles.PasswordTransferOption
import com.infomaniak.swisstransfer.ui.screen.newtransfer.importfiles.TransferOptionState
import com.infomaniak.swisstransfer.ui.screen.newtransfer.importfiles.TransferOptionsCallbacks
Expand Down Expand Up @@ -81,13 +83,19 @@ class ImportFilesViewModel @Inject constructor(
val currentSessionFilesCount = importationFilesManager.currentSessionFilesCount

//region Transfer Author Email
private var _transferAuthorEmail by mutableStateOf("")
val transferAuthorEmail = GetSetCallbacks(get = { _transferAuthorEmail }, set = { _transferAuthorEmail = it })
private var transferAuthorEmail by mutableStateOf("")
private val isAuthorEmailInvalid by derivedStateOf { !transferAuthorEmail.isValidEmail() }
//endregion

//region Recipient Email
private var recipientEmail by mutableStateOf("")
private val isRecipientEmailInvalid by derivedStateOf { !recipientEmail.isValidEmail() }
private var validatedRecipientsEmails by mutableStateOf<Set<String>>(emptySet())
//endregion

//region Transfer Message
private var _transferMessage by mutableStateOf("")
val transferMessage = GetSetCallbacks(get = { _transferMessage }, set = { _transferMessage = it })
private var transferMessage by mutableStateOf("")
val transferMessageCallbacks = GetSetCallbacks(get = { transferMessage }, set = { transferMessage = it })
//endregion

//region Password
Expand All @@ -96,7 +104,7 @@ class ImportFilesViewModel @Inject constructor(
//endregion

private var isFirstViewModelCreation: Boolean
get() = savedStateHandle.get<Boolean>(IS_VIEW_MODEL_RESTORED_KEY) ?: true
get() = savedStateHandle[IS_VIEW_MODEL_RESTORED_KEY] ?: true
set(value) {
savedStateHandle[IS_VIEW_MODEL_RESTORED_KEY] = value
}
Expand Down Expand Up @@ -146,13 +154,13 @@ class ImportFilesViewModel @Inject constructor(
private fun generateNewUploadSession(): NewUploadSession {
return NewUploadSession(
duration = selectedValidityPeriodOption.value.apiValue,
authorEmail = if (selectedTransferType.value == TransferTypeUi.MAIL) _transferAuthorEmail else "",
authorEmail = if (selectedTransferType.value == TransferTypeUi.MAIL) transferAuthorEmail.trim() else "",
authorEmailToken = null,
password = if (selectedPasswordOption.value == PasswordTransferOption.ACTIVATED) transferPassword else NO_PASSWORD,
message = _transferMessage,
message = transferMessage,
numberOfDownload = selectedDownloadLimitOption.value.apiValue,
language = selectedLanguageOption.value.apiValue,
recipientsEmails = emptySet(),
recipientsEmails = validatedRecipientsEmails,
files = importationFilesManager.importedFiles.value.mapToList { fileUi ->
object : UploadFileSession {
override val path: String? = null
Expand Down Expand Up @@ -212,6 +220,19 @@ class ImportFilesViewModel @Inject constructor(
savedStateHandle[SELECTED_LANGUAGE_KEY] = language
}

fun getEmailTextFieldCallbacks(): EmailTextFieldCallbacks {
return EmailTextFieldCallbacks(
transferAuthorEmail = GetSetCallbacks(get = { transferAuthorEmail }, set = { transferAuthorEmail = it }),
isAuthorEmailInvalid = { isAuthorEmailInvalid },
recipientEmail = GetSetCallbacks(get = { recipientEmail }, set = { recipientEmail = it }),
isRecipientEmailInvalid = { isRecipientEmailInvalid },
validatedRecipientsEmails = GetSetCallbacks(
get = { validatedRecipientsEmails },
set = { validatedRecipientsEmails = it }
),
)
}

fun initTransferOptionsValues() {
viewModelScope.launch(ioDispatcher) {
appSettingsManager.getAppSettings()?.let {
Expand Down
Loading

0 comments on commit 0ab0cab

Please sign in to comment.