Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
7 changes: 1 addition & 6 deletions app/src/main/kotlin/io/element/android/x/di/AppModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ import io.element.android.x.BuildConfig
import io.element.android.x.R
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.plus
import java.io.File
Expand Down Expand Up @@ -107,11 +106,7 @@ object AppModule {
@Provides
@SingleIn(AppScope::class)
fun providesCoroutineDispatchers(): CoroutineDispatchers {
return CoroutineDispatchers(
io = Dispatchers.IO,
computation = Dispatchers.Default,
main = Dispatchers.Main,
)
return CoroutineDispatchers.Default
}

@Provides
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import io.element.android.libraries.androidutils.clipboard.ClipboardHelper
import io.element.android.libraries.architecture.AsyncData
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.extensions.flatMap
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.designsystem.components.avatar.AvatarData
Expand All @@ -70,6 +71,7 @@ import io.element.android.libraries.matrix.api.core.toThreadId
import io.element.android.libraries.matrix.api.encryption.EncryptionService
import io.element.android.libraries.matrix.api.encryption.identity.IdentityState
import io.element.android.libraries.matrix.api.permalink.PermalinkParser
import io.element.android.libraries.matrix.api.recentemojis.AddRecentEmoji
import io.element.android.libraries.matrix.api.room.JoinedRoom
import io.element.android.libraries.matrix.api.room.MessageEventType
import io.element.android.libraries.matrix.api.room.RoomInfo
Expand Down Expand Up @@ -121,6 +123,7 @@ class MessagesPresenter(
private val analyticsService: AnalyticsService,
private val encryptionService: EncryptionService,
private val featureFlagService: FeatureFlagService,
private val addRecentEmoji: AddRecentEmoji,
) : Presenter<MessagesState> {
@AssistedFactory
interface Factory {
Expand Down Expand Up @@ -398,6 +401,7 @@ class MessagesPresenter(
) = launch(dispatchers.io) {
timelineController.invokeOnCurrentTimeline {
toggleReaction(emoji, eventOrTransactionId)
.flatMap { added -> if (added) addRecentEmoji(emoji) else Result.success(Unit) }
.onFailure { Timber.e(it) }
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import io.element.android.libraries.matrix.api.room.tombstone.SuccessorRoom
import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.textcomposer.model.MessageComposerMode
import io.element.android.libraries.textcomposer.model.aTextEditorStateRich
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.persistentSetOf

Expand Down Expand Up @@ -186,9 +187,11 @@ fun aReactionSummaryState(

fun aCustomReactionState(
target: CustomReactionState.Target = CustomReactionState.Target.None,
recentEmojis: ImmutableList<String> = persistentListOf(),
eventSink: (CustomReactionEvents) -> Unit = {},
) = CustomReactionState(
target = target,
recentEmojis = recentEmojis,
selectedEmoji = persistentSetOf(),
eventSink = eventSink,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.recentemojis.GetRecentEmojis
import io.element.android.libraries.matrix.api.room.BaseRoom
import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
Expand Down Expand Up @@ -73,6 +74,7 @@ class DefaultActionListPresenter(
private val userSendFailureFactory: VerifiedUserSendFailureFactory,
private val dateFormatter: DateFormatter,
private val featureFlagService: FeatureFlagService,
private val getRecentEmojis: GetRecentEmojis,
) : ActionListPresenter {
@AssistedFactory
@ContributesBinding(RoomScope::class)
Expand Down Expand Up @@ -153,14 +155,15 @@ class DefaultActionListPresenter(
),
displayEmojiReactions = displayEmojiReactions,
verifiedUserSendFailure = verifiedUserSendFailure,
actions = actions.toImmutableList()
actions = actions.toImmutableList(),
recentEmojis = getRecentEmojis().getOrNull()?.toImmutableList() ?: persistentListOf()
)
} else {
target.value = ActionListState.Target.None
}
}

private suspend fun buildActions(
private fun buildActions(
timelineItem: TimelineItem.Event,
usersEventPermissions: UserEventPermissions,
isDeveloperModeEnabled: Boolean,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ data class ActionListState(
val event: TimelineItem.Event,
val sentTimeFull: String,
val displayEmojiReactions: Boolean,
val recentEmojis: ImmutableList<String>,
val verifiedUserSendFailure: VerifiedUserSendFailure,
val actions: ImmutableList<TimelineItemAction>,
) : Target
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import io.element.android.features.messages.impl.timeline.model.event.aTimelineI
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemVoiceContent
import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList

open class ActionListStateProvider : PreviewParameterProvider<ActionListState> {
Expand All @@ -41,6 +42,7 @@ open class ActionListStateProvider : PreviewParameterProvider<ActionListState> {
displayEmojiReactions = true,
verifiedUserSendFailure = VerifiedUserSendFailure.None,
actions = aTimelineItemActionList(),
recentEmojis = persistentListOf(),
)
),
anActionListState(
Expand All @@ -56,6 +58,7 @@ open class ActionListStateProvider : PreviewParameterProvider<ActionListState> {
actions = aTimelineItemActionList(
copyAction = TimelineItemAction.CopyCaption,
),
recentEmojis = persistentListOf(),
)
),
anActionListState(
Expand All @@ -70,6 +73,7 @@ open class ActionListStateProvider : PreviewParameterProvider<ActionListState> {
actions = aTimelineItemActionList(
copyAction = TimelineItemAction.CopyCaption,
),
recentEmojis = persistentListOf(),
)
),
anActionListState(
Expand All @@ -84,6 +88,7 @@ open class ActionListStateProvider : PreviewParameterProvider<ActionListState> {
actions = aTimelineItemActionList(
copyAction = null,
),
recentEmojis = persistentListOf(),
)
),
anActionListState(
Expand All @@ -98,6 +103,7 @@ open class ActionListStateProvider : PreviewParameterProvider<ActionListState> {
actions = aTimelineItemActionList(
copyAction = TimelineItemAction.CopyCaption,
),
recentEmojis = persistentListOf(),
)
),
anActionListState(
Expand All @@ -112,6 +118,7 @@ open class ActionListStateProvider : PreviewParameterProvider<ActionListState> {
actions = aTimelineItemActionList(
copyAction = null,
),
recentEmojis = persistentListOf(),
)
),
anActionListState(
Expand All @@ -124,6 +131,7 @@ open class ActionListStateProvider : PreviewParameterProvider<ActionListState> {
displayEmojiReactions = true,
verifiedUserSendFailure = VerifiedUserSendFailure.None,
actions = aTimelineItemActionList(),
recentEmojis = persistentListOf(),
)
),
anActionListState(
Expand All @@ -136,6 +144,7 @@ open class ActionListStateProvider : PreviewParameterProvider<ActionListState> {
displayEmojiReactions = false,
verifiedUserSendFailure = VerifiedUserSendFailure.None,
actions = aTimelineItemActionList(),
recentEmojis = persistentListOf(),
),
),
anActionListState(
Expand All @@ -148,6 +157,7 @@ open class ActionListStateProvider : PreviewParameterProvider<ActionListState> {
displayEmojiReactions = false,
verifiedUserSendFailure = VerifiedUserSendFailure.None,
actions = aTimelineItemPollActionList(),
recentEmojis = persistentListOf(),
),
),
anActionListState(
Expand All @@ -160,6 +170,7 @@ open class ActionListStateProvider : PreviewParameterProvider<ActionListState> {
displayEmojiReactions = true,
verifiedUserSendFailure = VerifiedUserSendFailure.None,
actions = aTimelineItemActionList(),
recentEmojis = persistentListOf(),
)
),
anActionListState(
Expand All @@ -169,6 +180,7 @@ open class ActionListStateProvider : PreviewParameterProvider<ActionListState> {
displayEmojiReactions = true,
verifiedUserSendFailure = anUnsignedDeviceSendFailure(),
actions = aTimelineItemActionList(),
recentEmojis = persistentListOf(),
)
),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,19 @@ import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.ExperimentalMaterial3Api
Expand All @@ -35,6 +38,10 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
Expand Down Expand Up @@ -90,6 +97,8 @@ import io.element.android.libraries.matrix.ui.messages.sender.SenderName
import io.element.android.libraries.matrix.ui.messages.sender.SenderNameMode
import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList

@OptIn(ExperimentalMaterial3Api::class)
@Composable
Expand Down Expand Up @@ -218,6 +227,7 @@ private fun ActionListViewContent(
if (target.displayEmojiReactions) {
item {
EmojiReactionsRow(
recentEmojis = target.recentEmojis,
highlightedEmojis = target.event.reactionsState.highlightedKeys,
onEmojiReactionClick = onEmojiReactionClick,
onCustomReactionClick = onCustomReactionClick,
Expand Down Expand Up @@ -335,43 +345,67 @@ private fun MessageSummary(
}

private val emojiRippleRadius = 24.dp
private val suggestedEmojis = persistentListOf("👍️", "👎️", "🔥", "❤️", "👏")

@Composable
private fun EmojiReactionsRow(
recentEmojis: ImmutableList<String>,
highlightedEmojis: ImmutableList<String>,
onEmojiReactionClick: (String) -> Unit,
onCustomReactionClick: () -> Unit,
modifier: Modifier = Modifier,
) {
Row(
horizontalArrangement = Arrangement.SpaceBetween,
modifier = modifier.padding(horizontal = 24.dp, vertical = 16.dp)
modifier = modifier.padding(end = 16.dp, top = 16.dp, bottom = 16.dp),
) {
// TODO use most recently used emojis here when available from the Rust SDK
val defaultEmojis = sequenceOf(
"👍️",
"👎️",
"🔥",
"❤️",
"👏"
)
for (emoji in defaultEmojis) {
val isHighlighted = highlightedEmojis.contains(emoji)
EmojiButton(
modifier = Modifier
// Make it appear after the more useful actions for the accessibility service
.semantics {
traversalIndex = 1f
},
emoji = emoji,
isHighlighted = isHighlighted,
onClick = onEmojiReactionClick
)
val backgroundColor = ElementTheme.colors.bgCanvasDefault

val emojis = remember(recentEmojis) {
(suggestedEmojis + recentEmojis.filter { it !in suggestedEmojis })
.take(100)
.toImmutableList()
}
Box(

LazyRow(
modifier = Modifier
.size(48.dp),
contentAlignment = Alignment.Center,
.weight(1f, fill = true)
.drawWithContent {
val gradientWidth = 24.dp.toPx()
val width = size.width
drawContent()

drawRect(
brush = Brush.horizontalGradient(
0.0f to Color.Transparent,
1.0f to backgroundColor,
startX = width - gradientWidth,
endX = width,
),
topLeft = Offset(width - gradientWidth, 0f),
size = Size(gradientWidth, size.height)
)
},
contentPadding = PaddingValues(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
items(emojis) { emoji ->
val isHighlighted = highlightedEmojis.contains(emoji)
EmojiButton(
modifier = Modifier
// Make it appear after the more useful actions for the accessibility service
.semantics {
traversalIndex = 1f
},
emoji = emoji,
isHighlighted = isHighlighted,
onClick = onEmojiReactionClick
)
}
}

Box(
modifier = Modifier.padding(end = 10.dp).requiredSize(48.dp),
contentAlignment = Alignment.CenterEnd,
) {
Icon(
imageVector = CompoundIcons.ReactionAdd(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import androidx.compose.ui.Modifier
import io.element.android.emojibasebindings.Emoji
import io.element.android.features.messages.impl.timeline.components.customreaction.picker.EmojiPicker
import io.element.android.features.messages.impl.timeline.components.customreaction.picker.EmojiPickerPresenter
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.designsystem.theme.components.ModalBottomSheet
import io.element.android.libraries.designsystem.theme.components.hide
import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId
Expand Down Expand Up @@ -50,7 +51,13 @@ fun CustomReactionBottomSheet(
sheetState = sheetState,
modifier = modifier
) {
val presenter = remember { EmojiPickerPresenter(target.emojibaseStore) }
val presenter = remember {
EmojiPickerPresenter(
emojibaseStore = target.emojibaseStore,
recentEmojis = state.recentEmojis,
coroutineDispatchers = CoroutineDispatchers.Default,
)
}
EmojiPicker(
onSelectEmoji = ::onEmojiSelectedDismiss,
state = presenter.present(),
Expand Down
Loading
Loading