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

feat: enter to sent message part-1 [WPB-15369] #3839

Merged
merged 5 commits into from
Feb 4, 2025
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class GlobalDataStore @Inject constructor(@ApplicationContext private val contex
private val IS_LOGGING_ENABLED = booleanPreferencesKey("is_logging_enabled")
private val APP_LOCK_PASSCODE = stringPreferencesKey("app_lock_passcode")
private val APP_LOCK_SOURCE = intPreferencesKey("app_lock_source")
private val ENTER_TO_SENT = booleanPreferencesKey("enter_to_sent")

val APP_THEME_OPTION = stringPreferencesKey("app_theme_option")
val RECORD_AUDIO_EFFECTS_CHECKBOX = booleanPreferencesKey("record_audio_effects_checkbox")
Expand Down Expand Up @@ -232,4 +233,9 @@ class GlobalDataStore @Inject constructor(@ApplicationContext private val contex
fun selectedThemeOptionFlow(): Flow<ThemeOption> =
getStringPreference(APP_THEME_OPTION, ThemeOption.SYSTEM.toString())
.map { ThemeOption.valueOf(it) }

fun enterToSendFlow(): Flow<Boolean> = getBooleanPreference(ENTER_TO_SENT, false)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as far as it's used only as enterToSendFlow().first() maybe make sens to change it to suspend fun isEnterToSend(): Boolean

suspend fun setEnterToSend(enabled: Boolean) {
context.dataStore.edit { it[ENTER_TO_SENT] = enabled }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ internal fun WireTextField(
textState,
onSelectedLineIndexChanged,
onLineBottomYCoordinateChanged
)
),
)
}
)
Expand Down Expand Up @@ -185,6 +185,24 @@ val KeyboardOptions.Companion.DefaultText: KeyboardOptions
capitalization = KeyboardCapitalization.Sentences,
)

@Stable
val KeyboardOptions.Companion.MessageComposerEnterToSend: KeyboardOptions
get() = Default.copy(
keyboardType = KeyboardType.Text,
imeAction = ImeAction.Send,
autoCorrectEnabled = true,
capitalization = KeyboardCapitalization.Sentences,
)

@Stable
val KeyboardOptions.Companion.MessageComposerDefault: KeyboardOptions
get() = Default.copy(
keyboardType = KeyboardType.Text,
imeAction = ImeAction.None,
autoCorrectEnabled = true,
capitalization = KeyboardCapitalization.Sentences,
)

@Stable
val KeyboardOptions.Companion.DefaultEmailDone: KeyboardOptions
get() = defaultEmail(ImeAction.Done)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ data class MessageComposerViewState(
val isFileSharingEnabled: Boolean = true,
val interactionAvailability: InteractionAvailability = InteractionAvailability.ENABLED,
val mentionSearchResult: List<Contact> = listOf(),
val mentionSearchQuery: String = String.EMPTY
val mentionSearchQuery: String = String.EMPTY,
val enterToSend: Boolean = false,
)

sealed class AssetTooLargeDialogState {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import com.wire.android.datastore.GlobalDataStore
import com.wire.android.mapper.ContactMapper
import com.wire.android.navigation.SavedStateViewModel
import com.wire.android.ui.home.conversations.ConversationNavArgs
Expand Down Expand Up @@ -56,6 +57,7 @@ import com.wire.kalium.logic.feature.session.CurrentSessionResult
import com.wire.kalium.logic.feature.user.IsFileSharingEnabledUseCase
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.mapLatest
Expand All @@ -80,6 +82,7 @@ class MessageComposerViewModel @Inject constructor(
private val fileManager: FileManager,
private val kaliumFileSystem: KaliumFileSystem,
private val currentSessionFlowUseCase: CurrentSessionFlowUseCase,
private val globalDataStore: GlobalDataStore,
) : SavedStateViewModel(savedStateHandle) {

var messageComposerViewState = mutableStateOf(MessageComposerViewState())
Expand Down Expand Up @@ -107,6 +110,15 @@ class MessageComposerViewModel @Inject constructor(
initTempWritableImageUri()
observeIsTypingAvailable()
setFileSharingStatus()
getEnterToSendState()
}

private fun getEnterToSendState() {
viewModelScope.launch(dispatchers.io()) {
globalDataStore.enterToSendFlow().first().also {
messageComposerViewState.value = messageComposerViewState.value.copy(enterToSend = it)
}
}
}

private fun initTempWritableVideoUri() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,12 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.shape.GenericShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.text.input.KeyboardActionHandler
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
Expand All @@ -65,6 +68,12 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.drawscope.Fill
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.KeyEventType
import androidx.compose.ui.input.key.isShiftPressed
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.onPreviewKeyEvent
import androidx.compose.ui.input.key.type
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Density
Expand All @@ -77,6 +86,8 @@ import com.wire.android.ui.common.banner.SecurityClassificationBannerForConversa
import com.wire.android.ui.common.bottombar.bottomNavigationBarHeight
import com.wire.android.ui.common.colorsScheme
import com.wire.android.ui.common.dimensions
import com.wire.android.ui.common.textfield.MessageComposerDefault
import com.wire.android.ui.common.textfield.MessageComposerEnterToSend
import com.wire.android.ui.home.conversations.ConversationActionPermissionType
import com.wire.android.ui.home.conversations.UsersTypingIndicatorForConversation
import com.wire.android.ui.home.conversations.model.UriAsset
Expand Down Expand Up @@ -209,10 +220,39 @@ fun EnabledMessageComposer(
Box(fillRemainingSpaceOrWrapContent, contentAlignment = Alignment.BottomCenter) {
var currentSelectedLineIndex by remember { mutableStateOf(0) }
var cursorCoordinateY by remember { mutableStateOf(0F) }
val canSendMessage by remember {
derivedStateOf {
messageCompositionInputStateHolder.inputType is InputType.Composing &&
(messageCompositionInputStateHolder.inputType as InputType.Composing).isSendButtonEnabled
}
}
val keyboardOptions by remember {
derivedStateOf {
if (messageComposerStateHolder.messageComposerViewState.value.enterToSend) {
KeyboardOptions.Companion.MessageComposerEnterToSend
} else {
KeyboardOptions.Companion.MessageComposerDefault
}
}
}
val keyboardActionHandler by remember {
derivedStateOf {
KeyboardActionHandler {
if (canSendMessage) {
onSendButtonClicked()
} else {
Unit
}
}
}
}

ActiveMessageComposerInput(
conversationId = conversationId,
messageComposition = messageComposition.value,
keyboardOptions = keyboardOptions,
onKeyboardAction = keyboardActionHandler,
canSendMessage = canSendMessage,
messageTextState = inputStateHolder.messageTextState,
isTextExpanded = inputStateHolder.isTextExpanded,
inputType = messageCompositionInputStateHolder.inputType,
Expand Down Expand Up @@ -270,7 +310,27 @@ fun EnabledMessageComposer(
transferableContent
}
}
),
)
.onPreviewKeyEvent { keyEvent ->
if (keyEvent.type != KeyEventType.KeyDown) {
return@onPreviewKeyEvent false
}
if (keyEvent.isShiftPressed && keyEvent.key == Key.Enter) {
messageComposerStateHolder.messageCompositionInputStateHolder.messageTextState.edit {
append("\n")
}
true
} else if (keyEvent.key == Key.Enter) {
if (canSendMessage) {
onSendButtonClicked()
} else {
Unit
}
true
} else {
false
}
},
)

val mentionSearchResult = messageComposerViewState.value.mentionSearchResult
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.text.input.TextFieldLineLimits
import androidx.compose.foundation.text.input.KeyboardActionHandler
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
Expand All @@ -52,7 +53,6 @@ import androidx.compose.ui.input.key.nativeKeyCode
import androidx.compose.ui.input.key.onPreInterceptKeyBeforeSoftKeyboard
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.Dimension
Expand All @@ -62,7 +62,7 @@ import com.wire.android.di.hiltViewModelScoped
import com.wire.android.ui.common.colorsScheme
import com.wire.android.ui.common.dimensions
import com.wire.android.ui.common.spacers.VerticalSpace
import com.wire.android.ui.common.textfield.DefaultText
import com.wire.android.ui.common.textfield.MessageComposerDefault
import com.wire.android.ui.common.textfield.WireTextField
import com.wire.android.ui.common.textfield.WireTextFieldColors
import com.wire.android.ui.common.textfield.WireTextFieldState
Expand All @@ -89,6 +89,9 @@ fun ActiveMessageComposerInput(
isTextExpanded: Boolean,
inputType: InputType,
focusRequester: FocusRequester,
keyboardOptions: KeyboardOptions,
onKeyboardAction: KeyboardActionHandler?,
canSendMessage: Boolean,
onSendButtonClicked: () -> Unit,
onEditButtonClicked: () -> Unit,
onChangeSelfDeletionClicked: (currentlySelected: SelfDeletionTimer) -> Unit,
Expand All @@ -102,7 +105,7 @@ fun ActiveMessageComposerInput(
showOptions: Boolean,
optionsSelected: Boolean,
onPlusClick: () -> Unit,
modifier: Modifier = Modifier
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier
Expand Down Expand Up @@ -133,6 +136,9 @@ fun ActiveMessageComposerInput(
inputType = inputType,
focusRequester = focusRequester,
onSendButtonClicked = onSendButtonClicked,
keyboardOptions = keyboardOptions,
onKeyboardAction = onKeyboardAction,
canSendMessage = canSendMessage,
onChangeSelfDeletionClicked = onChangeSelfDeletionClicked,
onFocused = onFocused,
onSelectedLineIndexChanged = onSelectedLineIndexChanged,
Expand Down Expand Up @@ -169,6 +175,9 @@ private fun InputContent(
isTextExpanded: Boolean,
inputType: InputType,
focusRequester: FocusRequester,
keyboardOptions: KeyboardOptions,
onKeyboardAction: KeyboardActionHandler?,
canSendMessage: Boolean,
onSendButtonClicked: () -> Unit,
onChangeSelfDeletionClicked: (currentlySelected: SelfDeletionTimer) -> Unit,
onFocused: () -> Unit,
Expand Down Expand Up @@ -215,6 +224,8 @@ private fun InputContent(
onSelectedLineIndexChanged = onSelectedLineIndexChanged,
onLineBottomYCoordinateChanged = onLineBottomYCoordinateChanged,
onTextCollapse = onTextCollapse,
keyboardOptions = keyboardOptions,
onKeyBoardAction = onKeyboardAction,
modifier = Modifier
.fillMaxWidth()
.constrainAs(input) {
Expand Down Expand Up @@ -247,15 +258,13 @@ private fun InputContent(
UsersTypingIndicatorForConversation(conversationId = conversationId)
}
}
if (inputType is InputType.Composing && (showOptions || inputType.isSendButtonEnabled)) {
MessageSendActions(
onSendButtonClicked = onSendButtonClicked,
sendButtonEnabled = inputType.isSendButtonEnabled,
selfDeletionTimer = viewModel.state(),
onChangeSelfDeletionClicked = onChangeSelfDeletionClicked,
modifier = Modifier.padding(end = dimensions().spacing8x)
)
}
MessageSendActions(
onSendButtonClicked = onSendButtonClicked,
sendButtonEnabled = canSendMessage,
selfDeletionTimer = viewModel.state(),
onChangeSelfDeletionClicked = onChangeSelfDeletionClicked,
modifier = Modifier.padding(end = dimensions().spacing8x)
)
}
}
}
Expand All @@ -270,9 +279,11 @@ private fun MessageComposerTextInput(
placeHolderText: String,
onTextCollapse: () -> Unit,
onFocused: () -> Unit,
keyboardOptions: KeyboardOptions,
onKeyBoardAction: KeyboardActionHandler?,
modifier: Modifier = Modifier,
onSelectedLineIndexChanged: (Int) -> Unit = { },
onLineBottomYCoordinateChanged: (Float) -> Unit = { }
onLineBottomYCoordinateChanged: (Float) -> Unit = { },
) {
val interactionSource = remember { MutableInteractionSource() }
val isPressed by interactionSource.collectIsPressedAsState()
Expand All @@ -290,7 +301,8 @@ private fun MessageComposerTextInput(
// Add an extra space so that the cursor is placed one space before "Type a message"
placeholderText = " $placeHolderText",
state = WireTextFieldState.Default,
keyboardOptions = KeyboardOptions.DefaultText.copy(imeAction = ImeAction.None),
keyboardOptions = keyboardOptions,
onKeyboardAction = onKeyBoardAction,
modifier = modifier
.focusable(true)
.focusRequester(focusRequester)
Expand Down Expand Up @@ -356,6 +368,9 @@ private fun PreviewActiveMessageComposerInput(inputType: InputType, isTextExpand
messageTextState = TextFieldState(""),
isTextExpanded = isTextExpanded,
inputType = inputType,
keyboardOptions = KeyboardOptions.Companion.MessageComposerDefault,
onKeyboardAction = null,
canSendMessage = true,
focusRequester = FocusRequester(),
onSendButtonClicked = {},
onEditButtonClicked = {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import android.net.Uri
import androidx.lifecycle.SavedStateHandle
import com.wire.android.config.TestDispatcherProvider
import com.wire.android.config.mockUri
import com.wire.android.datastore.GlobalDataStore
import com.wire.android.framework.FakeKaliumFileSystem
import com.wire.android.framework.TestConversation
import com.wire.android.framework.TestUser
Expand Down Expand Up @@ -97,6 +98,7 @@ internal class MessageComposerViewModelArrangement {
coEvery {
currentSessionFlowUseCase()
} returns flowOf(CurrentSessionResult.Success(AccountInfo.Valid(TestUser.USER_ID)))
coEvery { globalDataStore.enterToSendFlow() } returns flowOf(false)
}

@MockK
Expand Down Expand Up @@ -144,6 +146,9 @@ internal class MessageComposerViewModelArrangement {
@MockK
lateinit var currentSessionFlowUseCase: CurrentSessionFlowUseCase

@MockK
lateinit var globalDataStore: GlobalDataStore

private val fakeKaliumFileSystem = FakeKaliumFileSystem()

private val viewModel by lazy {
Expand All @@ -162,6 +167,7 @@ internal class MessageComposerViewModelArrangement {
kaliumFileSystem = fakeKaliumFileSystem,
fileManager = fileManager,
currentSessionFlowUseCase = currentSessionFlowUseCase,
globalDataStore = globalDataStore,
)
}

Expand Down
2 changes: 1 addition & 1 deletion kalium
Submodule kalium updated 32 files
+3 −0 logic/src/commonMain/kotlin/com/wire/kalium/logic/GlobalKaliumScope.kt
+2 −2 network/src/commonMain/kotlin/com/wire/kalium/network/BackendMetaDataUtil.kt
+26 −0 network/src/commonMain/kotlin/com/wire/kalium/network/api/v8/authenticated/AccessTokenApiV8.kt
+28 −0 network/src/commonMain/kotlin/com/wire/kalium/network/api/v8/authenticated/AssetApiV8.kt
+26 −0 network/src/commonMain/kotlin/com/wire/kalium/network/api/v8/authenticated/CallApiV8.kt
+26 −0 network/src/commonMain/kotlin/com/wire/kalium/network/api/v8/authenticated/ClientApiV8.kt
+26 −0 network/src/commonMain/kotlin/com/wire/kalium/network/api/v8/authenticated/ConnectionApiV8.kt
+26 −0 network/src/commonMain/kotlin/com/wire/kalium/network/api/v8/authenticated/ConversationApiV8.kt
+25 −0 network/src/commonMain/kotlin/com/wire/kalium/network/api/v8/authenticated/E2EIApiV8.kt
+26 −0 network/src/commonMain/kotlin/com/wire/kalium/network/api/v8/authenticated/FeatureConfigApiV8.kt
+26 −0 network/src/commonMain/kotlin/com/wire/kalium/network/api/v8/authenticated/KeyPackageApiV8.kt
+28 −0 network/src/commonMain/kotlin/com/wire/kalium/network/api/v8/authenticated/LogoutApiV8.kt
+26 −0 network/src/commonMain/kotlin/com/wire/kalium/network/api/v8/authenticated/MLSMessageApiV8.kt
+26 −0 network/src/commonMain/kotlin/com/wire/kalium/network/api/v8/authenticated/MLSPublicKeyApiV8.kt
+28 −0 network/src/commonMain/kotlin/com/wire/kalium/network/api/v8/authenticated/MessageApiV8.kt
+30 −0 network/src/commonMain/kotlin/com/wire/kalium/network/api/v8/authenticated/NotificationApiV8.kt
+26 −0 network/src/commonMain/kotlin/com/wire/kalium/network/api/v8/authenticated/PreKeyApiV8.kt
+26 −0 network/src/commonMain/kotlin/com/wire/kalium/network/api/v8/authenticated/PropertiesApiV8.kt
+28 −0 network/src/commonMain/kotlin/com/wire/kalium/network/api/v8/authenticated/SelfApiV8.kt
+26 −0 network/src/commonMain/kotlin/com/wire/kalium/network/api/v8/authenticated/TeamsApiV8.kt
+26 −0 network/src/commonMain/kotlin/com/wire/kalium/network/api/v8/authenticated/UpgradePersonalToTeamApiV8.kt
+26 −0 network/src/commonMain/kotlin/com/wire/kalium/network/api/v8/authenticated/UserDetailsApiV8.kt
+26 −0 network/src/commonMain/kotlin/com/wire/kalium/network/api/v8/authenticated/UserSearchApiV8.kt
+158 −0 ...ain/kotlin/com/wire/kalium/network/api/v8/authenticated/networkContainer/AuthenticatedNetworkContainerV8.kt
+25 −0 network/src/commonMain/kotlin/com/wire/kalium/network/api/v8/unauthenticated/DomainLookupApiV8.kt
+26 −0 network/src/commonMain/kotlin/com/wire/kalium/network/api/v8/unauthenticated/LoginApiV8.kt
+26 −0 network/src/commonMain/kotlin/com/wire/kalium/network/api/v8/unauthenticated/RegisterApiV8.kt
+26 −0 network/src/commonMain/kotlin/com/wire/kalium/network/api/v8/unauthenticated/SSOLoginApiV8.kt
+25 −0 network/src/commonMain/kotlin/com/wire/kalium/network/api/v8/unauthenticated/VerificationCodeApiV8.kt
+85 −0 ...kotlin/com/wire/kalium/network/api/v8/unauthenticated/networkContainer/UnauthenticatedNetworkContainerV8.kt
+11 −1 network/src/commonMain/kotlin/com/wire/kalium/network/networkContainer/AuthenticatedNetworkContainer.kt
+10 −1 network/src/commonMain/kotlin/com/wire/kalium/network/networkContainer/UnauthenticatedNetworkContainer.kt
Loading