Skip to content

Commit 02d6fa7

Browse files
jmartinespElementBot
and
ElementBot
authored
Add timeline item for m.call.notify events (#2986)
* Add timeline item for `m.call.notify` events * Update screenshots --------- Co-authored-by: ElementBot <[email protected]>
1 parent 30a1367 commit 02d6fa7

File tree

291 files changed

+369
-68
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

291 files changed

+369
-68
lines changed

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ import io.element.android.features.messages.impl.timeline.components.receipt.bot
4949
import io.element.android.features.messages.impl.timeline.components.retrysendmenu.RetrySendMenuPresenter
5050
import io.element.android.features.messages.impl.timeline.model.TimelineItem
5151
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent
52+
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemCallNotifyContent
5253
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent
5354
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent
5455
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
@@ -392,6 +393,7 @@ class MessagesPresenter @AssistedInject constructor(
392393
is TimelineItemStateContent,
393394
is TimelineItemEncryptedContent,
394395
is TimelineItemLegacyCallInviteContent,
396+
is TimelineItemCallNotifyContent,
395397
is TimelineItemUnknownContent -> null
396398
}
397399
val composerMode = MessageComposerMode.Reply(

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt

Lines changed: 22 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import androidx.compose.foundation.clickable
2121
import androidx.compose.foundation.layout.Arrangement
2222
import androidx.compose.foundation.layout.Box
2323
import androidx.compose.foundation.layout.Column
24-
import androidx.compose.foundation.layout.PaddingValues
2524
import androidx.compose.foundation.layout.Row
2625
import androidx.compose.foundation.layout.Spacer
2726
import androidx.compose.foundation.layout.WindowInsets
@@ -32,10 +31,8 @@ import androidx.compose.foundation.layout.heightIn
3231
import androidx.compose.foundation.layout.imePadding
3332
import androidx.compose.foundation.layout.navigationBarsPadding
3433
import androidx.compose.foundation.layout.padding
35-
import androidx.compose.foundation.layout.size
3634
import androidx.compose.foundation.layout.statusBars
3735
import androidx.compose.foundation.layout.width
38-
import androidx.compose.material3.ButtonDefaults
3936
import androidx.compose.material3.ExperimentalMaterial3Api
4037
import androidx.compose.material3.MaterialTheme
4138
import androidx.compose.runtime.Composable
@@ -70,6 +67,7 @@ import io.element.android.features.messages.impl.messagecomposer.AttachmentsStat
7067
import io.element.android.features.messages.impl.messagecomposer.MessageComposerEvents
7168
import io.element.android.features.messages.impl.messagecomposer.MessageComposerView
7269
import io.element.android.features.messages.impl.timeline.TimelineView
70+
import io.element.android.features.messages.impl.timeline.components.JoinCallMenuItem
7371
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionBottomSheet
7472
import io.element.android.features.messages.impl.timeline.components.customreaction.CustomReactionEvents
7573
import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryEvents
@@ -110,7 +108,6 @@ import io.element.android.libraries.ui.strings.CommonStrings
110108
import kotlinx.collections.immutable.ImmutableList
111109
import timber.log.Timber
112110
import kotlin.random.Random
113-
import androidx.compose.material3.Button as Material3Button
114111

115112
@Composable
116113
fun MessagesView(
@@ -232,6 +229,7 @@ fun MessagesView(
232229
state.eventSink(MessagesEvents.HandleAction(TimelineItemAction.Reply, targetEvent))
233230
},
234231
forceJumpToBottomVisibility = forceJumpToBottomVisibility,
232+
onJoinCallClick = onJoinCallClick,
235233
)
236234
},
237235
snackbarHost = {
@@ -324,6 +322,7 @@ private fun MessagesViewContent(
324322
onTimestampClick: (TimelineItem.Event) -> Unit,
325323
onSendLocationClick: () -> Unit,
326324
onCreatePollClick: () -> Unit,
325+
onJoinCallClick: () -> Unit,
327326
forceJumpToBottomVisibility: Boolean,
328327
modifier: Modifier = Modifier,
329328
onSwipeToReply: (TimelineItem.Event) -> Unit,
@@ -396,6 +395,7 @@ private fun MessagesViewContent(
396395
onReadReceiptClick = onReadReceiptClick,
397396
modifier = Modifier.padding(paddingValues),
398397
forceJumpToBottomVisibility = forceJumpToBottomVisibility,
398+
onJoinCallClick = onJoinCallClick,
399399
)
400400
},
401401
sheetContent = { subcomposing: Boolean ->
@@ -478,46 +478,32 @@ private fun MessagesViewTopBar(
478478
}
479479
},
480480
actions = {
481-
if (callState == RoomCallState.ONGOING) {
482-
JoinCallMenuItem(onJoinCallClick = onJoinCallClick)
483-
} else {
484-
IconButton(onClick = onJoinCallClick, enabled = callState != RoomCallState.DISABLED) {
485-
Icon(
486-
imageVector = CompoundIcons.VideoCallSolid(),
487-
contentDescription = stringResource(CommonStrings.a11y_start_call),
488-
)
489-
}
490-
}
481+
CallMenuItem(
482+
isCallOngoing = callState == RoomCallState.ONGOING,
483+
onClick = onJoinCallClick,
484+
enabled = callState != RoomCallState.DISABLED
485+
)
491486
Spacer(Modifier.width(8.dp))
492487
},
493488
windowInsets = WindowInsets(0.dp)
494489
)
495490
}
496491

497492
@Composable
498-
private fun JoinCallMenuItem(
499-
onJoinCallClick: () -> Unit,
493+
private fun CallMenuItem(
494+
isCallOngoing: Boolean,
495+
enabled: Boolean = true,
496+
onClick: () -> Unit,
500497
) {
501-
Material3Button(
502-
onClick = onJoinCallClick,
503-
colors = ButtonDefaults.buttonColors(
504-
contentColor = ElementTheme.colors.bgCanvasDefault,
505-
containerColor = ElementTheme.colors.iconAccentTertiary
506-
),
507-
contentPadding = PaddingValues(horizontal = 10.dp, vertical = 0.dp),
508-
modifier = Modifier.heightIn(min = 36.dp),
509-
) {
510-
Icon(
511-
modifier = Modifier.size(20.dp),
512-
imageVector = CompoundIcons.VideoCallSolid(),
513-
contentDescription = null
514-
)
515-
Spacer(Modifier.width(8.dp))
516-
Text(
517-
text = stringResource(CommonStrings.action_join),
518-
style = ElementTheme.typography.fontBodyMdMedium
519-
)
520-
Spacer(Modifier.width(8.dp))
498+
if (isCallOngoing) {
499+
JoinCallMenuItem(onJoinCallClick = onClick)
500+
} else {
501+
IconButton(onClick = onClick, enabled = enabled) {
502+
Icon(
503+
imageVector = CompoundIcons.VideoCallSolid(),
504+
contentDescription = stringResource(CommonStrings.a11y_start_call),
505+
)
506+
}
521507
}
522508
}
523509

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import androidx.compose.runtime.remember
2525
import androidx.compose.runtime.rememberCoroutineScope
2626
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
2727
import io.element.android.features.messages.impl.timeline.model.TimelineItem
28+
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemCallNotifyContent
2829
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLegacyCallInviteContent
2930
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent
3031
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent
@@ -86,6 +87,13 @@ class ActionListPresenter @Inject constructor(
8687
val canRedact = timelineItem.isMine && userCanRedactOwn || !timelineItem.isMine && userCanRedactOther
8788
val actions =
8889
when (timelineItem.content) {
90+
is TimelineItemCallNotifyContent -> {
91+
if (isDeveloperModeEnabled) {
92+
listOf(TimelineItemAction.ViewSource)
93+
} else {
94+
emptyList()
95+
}
96+
}
8997
is TimelineItemRedactedContent -> {
9098
if (isDeveloperModeEnabled) {
9199
listOf(TimelineItemAction.ViewSource)

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListView.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import io.element.android.features.messages.impl.sender.SenderName
5959
import io.element.android.features.messages.impl.sender.SenderNameMode
6060
import io.element.android.features.messages.impl.timeline.model.TimelineItem
6161
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemAudioContent
62+
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemCallNotifyContent
6263
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent
6364
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent
6465
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
@@ -265,6 +266,9 @@ private fun MessageSummary(event: TimelineItem.Event, modifier: Modifier = Modif
265266
is TimelineItemLegacyCallInviteContent -> {
266267
content = { ContentForBody(textContent) }
267268
}
269+
is TimelineItemCallNotifyContent -> {
270+
content = { ContentForBody(stringResource(CommonStrings.common_call_started)) }
271+
}
268272
}
269273
Row(modifier = modifier) {
270274
icon()

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import io.element.android.features.poll.api.actions.EndPollAction
3838
import io.element.android.features.poll.api.actions.SendPollResponseAction
3939
import io.element.android.features.preferences.api.store.SessionPreferencesStore
4040
import io.element.android.libraries.architecture.Presenter
41+
import io.element.android.libraries.core.bool.orFalse
4142
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
4243
import io.element.android.libraries.matrix.api.core.EventId
4344
import io.element.android.libraries.matrix.api.room.MatrixRoom
@@ -85,6 +86,7 @@ class TimelinePresenter @AssistedInject constructor(
8586
val lastReadReceiptId = rememberSaveable { mutableStateOf<EventId?>(null) }
8687

8788
val timelineItems by timelineItemsFactory.collectItemsAsState()
89+
val roomInfo by room.roomInfoFlow.collectAsState(initial = null)
8890

8991
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
9092

@@ -196,6 +198,7 @@ class TimelinePresenter @AssistedInject constructor(
196198
isDm = room.isDm,
197199
userHasPermissionToSendMessage = userHasPermissionToSendMessage,
198200
userHasPermissionToSendReaction = userHasPermissionToSendReaction,
201+
isCallOngoing = roomInfo?.hasRoomCall.orFalse(),
199202
)
200203
}
201204
}

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineState.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,5 @@ data class TimelineRoomInfo(
5151
val name: String?,
5252
val userHasPermissionToSendMessage: Boolean,
5353
val userHasPermissionToSendReaction: Boolean,
54+
val isCallOngoing: Boolean,
5455
)

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,4 +232,5 @@ internal fun aTimelineRoomInfo(
232232
name = name,
233233
userHasPermissionToSendMessage = userHasPermissionToSendMessage,
234234
userHasPermissionToSendReaction = true,
235+
isCallOngoing = false,
235236
)

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ fun TimelineView(
8989
onReactionLongClick: (emoji: String, TimelineItem.Event) -> Unit,
9090
onMoreReactionsClick: (TimelineItem.Event) -> Unit,
9191
onReadReceiptClick: (TimelineItem.Event) -> Unit,
92+
onJoinCallClick: () -> Unit,
9293
modifier: Modifier = Modifier,
9394
forceJumpToBottomVisibility: Boolean = false
9495
) {
@@ -150,6 +151,7 @@ fun TimelineView(
150151
onTimestampClick = onTimestampClick,
151152
eventSink = state.eventSink,
152153
onSwipeToReply = onSwipeToReply,
154+
onJoinCallClick = onJoinCallClick,
153155
)
154156
}
155157
}
@@ -305,6 +307,7 @@ internal fun TimelineViewPreview(
305307
onReactionLongClick = { _, _ -> },
306308
onMoreReactionsClick = {},
307309
onReadReceiptClick = {},
310+
onJoinCallClick = {},
308311
forceJumpToBottomVisibility = true,
309312
)
310313
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright (c) 2024 New Vector Ltd
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.element.android.features.messages.impl.timeline.components
18+
19+
import androidx.compose.foundation.layout.PaddingValues
20+
import androidx.compose.foundation.layout.Spacer
21+
import androidx.compose.foundation.layout.heightIn
22+
import androidx.compose.foundation.layout.size
23+
import androidx.compose.foundation.layout.width
24+
import androidx.compose.material3.Button
25+
import androidx.compose.material3.ButtonDefaults
26+
import androidx.compose.runtime.Composable
27+
import androidx.compose.ui.Modifier
28+
import androidx.compose.ui.res.stringResource
29+
import androidx.compose.ui.unit.dp
30+
import io.element.android.compound.theme.ElementTheme
31+
import io.element.android.compound.tokens.generated.CompoundIcons
32+
import io.element.android.libraries.designsystem.theme.components.Icon
33+
import io.element.android.libraries.designsystem.theme.components.Text
34+
import io.element.android.libraries.ui.strings.CommonStrings
35+
36+
@Composable
37+
internal fun JoinCallMenuItem(
38+
onJoinCallClick: () -> Unit,
39+
) {
40+
Button(
41+
onClick = onJoinCallClick,
42+
colors = ButtonDefaults.buttonColors(
43+
contentColor = ElementTheme.colors.bgCanvasDefault,
44+
containerColor = ElementTheme.colors.iconAccentTertiary
45+
),
46+
contentPadding = PaddingValues(horizontal = 10.dp, vertical = 0.dp),
47+
modifier = Modifier.heightIn(min = 36.dp),
48+
) {
49+
Icon(
50+
modifier = Modifier.size(20.dp),
51+
imageVector = CompoundIcons.VideoCallSolid(),
52+
contentDescription = null
53+
)
54+
Spacer(Modifier.width(8.dp))
55+
Text(
56+
text = stringResource(CommonStrings.action_join),
57+
style = ElementTheme.typography.fontBodyMdMedium
58+
)
59+
Spacer(Modifier.width(8.dp))
60+
}
61+
}

0 commit comments

Comments
 (0)