diff --git a/changelog.d/7762.feature b/changelog.d/7762.feature
index 48d7fb0d2f..485acf9415 100644
--- a/changelog.d/7762.feature
+++ b/changelog.d/7762.feature
@@ -1 +1 @@
-Added lab feature to pin/unpin messages
\ No newline at end of file
+Added lab feature to pin/unpin messages
diff --git a/gradle.properties b/gradle.properties
index 2c999af35d..4e3c90491f 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -8,7 +8,7 @@
# The setting is particularly useful for tweaking memory settings.
# Build Time Optimizations
-org.gradle.jvmargs=-Xmx4g -Xms512M -XX:MaxPermSize=2048m -XX:MaxMetaspaceSize=1g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:+UseParallelGC
+org.gradle.jvmargs=-Xmx4g -Xms512M -XX:MaxMetaspaceSize=1g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:+UseParallelGC
org.gradle.configureondemand=true
org.gradle.parallel=true
org.gradle.vfs.watch=true
diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index a964cb7d6a..59980c0e3e 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -77,6 +77,10 @@
• Servers matching %s are allowed.
• Servers matching IP literals are allowed.
• Servers matching IP literals are banned.
+ %1$s pinned a message.
+ %1$s unpinned a message.
+ You pinned a message.
+ You unpinned a message.
%s changed the server ACLs for this room.
You changed the server ACLs for this room.
@@ -373,7 +377,7 @@
Are you sure you want to sign out?
Voice Call
Video Call
- Open Pinned Messages
+ Open Pinned Messages
View Threads
Mark all as read
Quick reply
@@ -803,11 +807,9 @@
Your homeserver does not currently support threads, so this feature may be unreliable. Some threaded messages may not be reliably available. %sDo you want to enable threads anyway?
- Pin
- Unpin
- Pinned Messages
- %1$s pinned a message.
- %1$s unpinned a message.
+ Pin
+ Unpin
+ Pinned Messages
Search
@@ -3039,7 +3041,7 @@
Auto Report Decryption Errors.
Your system will automatically send logs when an unable to decrypt error occurs
- Enable Pinned Messages
+ Enable Pinned Messages
Enable Thread Messages
Note: app will be restarted
Show latest user info
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt
index d5c0ad46c5..6fe608fd6d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt
@@ -449,10 +449,10 @@ fun Event.supportsNotification() =
fun Event.isContentReportable() =
this.getClearType() in EventType.MESSAGE + EventType.STATE_ROOM_BEACON_INFO.values
-fun Event.getIdsOfPinnedEvents(): MutableList? {
+fun Event.getIdsOfPinnedEvents(): List? {
return getClearContent()?.toModel()?.eventIds
}
-fun Event.getPreviousIdsOfPinnedEvents(): MutableList? {
+fun Event.getPreviousIdsOfPinnedEvents(): List? {
return resolvedPrevContent()?.toModel()?.eventIds
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/pinnedmessages/PinnedEventsStateContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/pinnedmessages/PinnedEventsStateContent.kt
index 0475ee0fc4..646cf62cda 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/pinnedmessages/PinnedEventsStateContent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/pinnedmessages/PinnedEventsStateContent.kt
@@ -24,5 +24,5 @@ import com.squareup.moshi.JsonClass
*/
@JsonClass(generateAdapter = true)
data class PinnedEventsStateContent(
- @Json(name = "pinned") val eventIds: MutableList
+ @Json(name = "pinned") val eventIds: List
)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt
index 2b008ab732..851dea8b9f 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/state/StateService.kt
@@ -67,9 +67,14 @@ interface StateService {
suspend fun deleteAvatar()
/**
- * Pin a message of the room.
+ * Pin an event of the room.
*/
- suspend fun pinMessage(eventIds: MutableList)
+ suspend fun pinEvent(eventId: String)
+
+ /**
+ * Unpin an event of the room.
+ */
+ suspend fun unpinEvent(eventId: String)
/**
* Send a state event to the room.
@@ -108,16 +113,6 @@ interface StateService {
*/
fun getStateEventsLive(eventTypes: Set, stateKey: QueryStateEventValue): LiveData>
- /**
- * Get state event containing the IDs of pinned events of the room
- */
- fun getPinnedEventsState(): Event?
-
- /**
- * Tells if an event is a pinned message
- */
- fun isPinned(eventId: String): Boolean?
-
suspend fun setJoinRulePublic()
suspend fun setJoinRuleInviteOnly()
suspend fun setJoinRuleRestricted(allowList: List)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/Timeline.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/Timeline.kt
index 2e9b87b797..f49bae1b9b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/Timeline.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/Timeline.kt
@@ -43,7 +43,7 @@ interface Timeline {
/**
* This must be called before any other method after creating the timeline. It ensures the underlying database is open
*/
- fun start(rootThreadEventId: String? = null, rootPinnedMessageEventId: String? = null)
+ fun start(rootThreadEventId: String? = null, isFromPinnedEventsTimeline: Boolean = false)
/**
* This must be called when you don't need the timeline. It ensures the underlying database get closed.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineSettings.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineSettings.kt
index 3d9f4e3dc7..64c6a8f068 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineSettings.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineSettings.kt
@@ -33,9 +33,9 @@ data class TimelineSettings(
*/
val rootThreadEventId: String? = null,
/**
- * The root pinned message eventId if this is a pinned messages timeline, or null if this is NOT a pinned messages timeline.
+ * True if the timeline is a pinned messages timeline.
*/
- val rootPinnedMessageEventId: String? = null,
+ val isFromPinnedEventsTimeline: Boolean = false,
/**
* If true Sender Info shown in room will get the latest data information (avatar + displayName).
*/
@@ -50,5 +50,5 @@ data class TimelineSettings(
/**
* Returns true if this is a pinned messages timeline or false otherwise.
*/
- fun isPinnedMessagesTimeline() = rootPinnedMessageEventId != null
+ fun isPinnedEventsTimeline() = isFromPinnedEventsTimeline
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt
index bf0f482c13..e80c860eb0 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt
@@ -33,7 +33,6 @@ import org.matrix.android.sdk.internal.session.room.membership.RoomMembersRespon
import org.matrix.android.sdk.internal.session.room.membership.admin.UserIdAndReason
import org.matrix.android.sdk.internal.session.room.membership.joining.InviteBody
import org.matrix.android.sdk.internal.session.room.membership.threepid.ThreePidInviteBody
-import org.matrix.android.sdk.internal.session.room.pinnedmessages.PinnedEventsStateResponse
import org.matrix.android.sdk.internal.session.room.read.ReadBody
import org.matrix.android.sdk.internal.session.room.relation.RelationsResponse
import org.matrix.android.sdk.internal.session.room.reporting.ReportContentBody
@@ -249,7 +248,7 @@ internal interface RoomAPI {
@Path("roomId") roomId: String,
@Path("eventType") eventType: String,
@Path("state_key") stateKey: String
- ): PinnedEventsStateResponse
+ ): Content
/**
* Paginate relations for event based in normal topological order.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/pinnedmessages/PinnedEventsStateResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/pinnedmessages/PinnedEventsStateResponse.kt
deleted file mode 100644
index c964f1c769..0000000000
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/pinnedmessages/PinnedEventsStateResponse.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2022 The Matrix.org Foundation C.I.C.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.matrix.android.sdk.internal.session.room.pinnedmessages
-
-import com.squareup.moshi.Json
-import com.squareup.moshi.JsonClass
-
-@JsonClass(generateAdapter = true)
-internal data class PinnedEventsStateResponse(
- /**
- * A unique identifier for the event.
- */
- @Json(name = "pinned") val pinned: List
-)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt
index 51cf975574..7a12bf8896 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt
@@ -173,7 +173,25 @@ internal class DefaultStateService @AssistedInject constructor(
)
}
- override suspend fun pinMessage(eventIds: MutableList) {
+ override suspend fun pinEvent(eventId: String) {
+ val pinnedEvents = getStateEvent(EventType.STATE_ROOM_PINNED_EVENT, QueryStringValue.Equals(""))
+ ?.getIdsOfPinnedEvents()
+ ?.toMutableList()
+ pinnedEvents?.add(eventId)
+ val newListOfPinnedEvents = pinnedEvents?.toList() ?: return
+ setPinnedEvents(newListOfPinnedEvents)
+ }
+
+ override suspend fun unpinEvent(eventId: String) {
+ val pinnedEvents = getStateEvent(EventType.STATE_ROOM_PINNED_EVENT, QueryStringValue.Equals(""))
+ ?.getIdsOfPinnedEvents()
+ ?.toMutableList()
+ pinnedEvents?.remove(eventId)
+ val newListOfPinnedEvents = pinnedEvents?.toList() ?: return
+ setPinnedEvents(newListOfPinnedEvents)
+ }
+
+ private suspend fun setPinnedEvents(eventIds: List) {
sendStateEvent(
eventType = EventType.STATE_ROOM_PINNED_EVENT,
body = PinnedEventsStateContent(eventIds).toContent(),
@@ -181,15 +199,6 @@ internal class DefaultStateService @AssistedInject constructor(
)
}
- override fun getPinnedEventsState(): Event? {
- return getStateEvent(EventType.STATE_ROOM_PINNED_EVENT, QueryStringValue.Equals(""))
- }
-
- override fun isPinned(eventId: String): Boolean? {
- val idsOfPinnedEvents: MutableList = getPinnedEventsState()?.getIdsOfPinnedEvents() ?: return null
- return idsOfPinnedEvents.contains(eventId)
- }
-
override suspend fun setJoinRulePublic() {
updateJoinRule(RoomJoinRules.PUBLIC, null)
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt
index 458d2b8af0..469c387ccc 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt
@@ -99,8 +99,7 @@ internal class DefaultTimeline(
private var isFromThreadTimeline = false
private var rootThreadEventId: String? = null
- private var isFromPinnedMessagesTimeline = false
- private var rootPinnedMessageEventId: String? = null
+ private var isFromPinnedEventsTimeline = false
private val strategyDependencies = LoadTimelineStrategy.Dependencies(
timelineSettings = settings,
@@ -132,7 +131,7 @@ internal class DefaultTimeline(
override fun addListener(listener: Timeline.Listener): Boolean {
listeners.add(listener)
timelineScope.launch {
- val snapshot = if (isFromPinnedMessagesTimeline) {
+ val snapshot = if (isFromPinnedEventsTimeline) {
getPinnedEvents()
} else {
strategy.buildSnapshot()
@@ -152,7 +151,7 @@ internal class DefaultTimeline(
listeners.clear()
}
- override fun start(rootThreadEventId: String?, rootPinnedMessageEventId: String?) {
+ override fun start(rootThreadEventId: String?, isFromPinnedEventsTimeline: Boolean) {
timelineScope.launch {
loadRoomMembersIfNeeded()
}
@@ -161,8 +160,7 @@ internal class DefaultTimeline(
if (isStarted.compareAndSet(false, true)) {
isFromThreadTimeline = rootThreadEventId != null
this@DefaultTimeline.rootThreadEventId = rootThreadEventId
- isFromPinnedMessagesTimeline = rootPinnedMessageEventId != null
- this@DefaultTimeline.rootPinnedMessageEventId = rootPinnedMessageEventId
+ this@DefaultTimeline.isFromPinnedEventsTimeline = isFromPinnedEventsTimeline
// /
val realm = Realm.getInstance(realmConfiguration)
ensureReadReceiptAreLoaded(realm)
@@ -267,8 +265,8 @@ internal class DefaultTimeline(
}
}
Timber.v("$baseLogMessage: result $loadMoreResult")
- val hasMoreToLoad = if (isFromPinnedMessagesTimeline) {
- !areAllPinnedMessagesLoaded()
+ val hasMoreToLoad = if (isFromPinnedEventsTimeline) {
+ !areAllPinnedEventsLoaded()
} else {
loadMoreResult != LoadMoreResult.REACHED_END
}
@@ -352,7 +350,7 @@ internal class DefaultTimeline(
}
private suspend fun postSnapshot() {
- val snapshot = if (isFromPinnedMessagesTimeline) {
+ val snapshot = if (isFromPinnedEventsTimeline) {
getPinnedEvents()
} else {
strategy.buildSnapshot()
@@ -371,25 +369,22 @@ internal class DefaultTimeline(
}
}
- private fun getIdsOfPinnedEvents(): MutableList {
+ private fun getIdsOfPinnedEvents(): List {
return stateEventDataSource
.getStateEvent(roomId, EventType.STATE_ROOM_PINNED_EVENT, QueryStringValue.Equals(""))
- ?.getIdsOfPinnedEvents() ?: mutableListOf("")
+ ?.getIdsOfPinnedEvents()
+ .orEmpty()
}
private fun getPinnedEvents(): List {
- val idsOfPinnedEvents = getIdsOfPinnedEvents()
- val pinnedEvents = ArrayList()
- for (id in idsOfPinnedEvents) {
- val timelineEvent = timelineEventDataSource.getTimelineEvent(roomId, id)
- if (timelineEvent != null) {
- pinnedEvents.add(timelineEvent)
- }
- }
- return pinnedEvents.reversed()
+ return getIdsOfPinnedEvents()
+ .mapNotNull { id ->
+ timelineEventDataSource.getTimelineEvent(roomId, id)
+ }
+ .reversed()
}
- private fun areAllPinnedMessagesLoaded(): Boolean {
+ private fun areAllPinnedEventsLoaded(): Boolean {
return getIdsOfPinnedEvents().size == getPinnedEvents().size
}
diff --git a/vector-config/src/main/res/values/config-settings.xml b/vector-config/src/main/res/values/config-settings.xml
index caa60af797..f32b1fd95b 100755
--- a/vector-config/src/main/res/values/config-settings.xml
+++ b/vector-config/src/main/res/values/config-settings.xml
@@ -39,7 +39,7 @@
true
true
- false
+ false
false
true
false
diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml
index 9d62904fc4..60c072c8ad 100644
--- a/vector/src/main/AndroidManifest.xml
+++ b/vector/src/main/AndroidManifest.xml
@@ -149,7 +149,7 @@
-
+
, val compressBeforeSending: Boolean) : RoomDetailAction()
data class TimelineEventTurnsVisible(val event: TimelineEvent) : RoomDetailAction()
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt
index b60154192e..1e4aafc255 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt
@@ -71,7 +71,6 @@ data class RoomDetailViewState(
val isAllowedToManageWidgets: Boolean = false,
val isAllowedToStartWebRTCCall: Boolean = true,
val isAllowedToSetupEncryption: Boolean = true,
- val rootPinnedMessageEventId: String?,
val hasFailedSending: Boolean = false,
val jitsiState: JitsiState = JitsiState(),
val switchToParentSpace: Boolean = false,
@@ -81,6 +80,7 @@ data class RoomDetailViewState(
val isSharingLiveLocation: Boolean = false,
val showKeyboardWhenPresented: Boolean = false,
val sharedData: SharedData? = null,
+ val isFromPinnedEventsTimeline: Boolean = false,
) : MavericksState {
constructor(args: TimelineArgs) : this(
@@ -93,7 +93,7 @@ data class RoomDetailViewState(
rootThreadEventId = args.threadTimelineArgs?.rootThreadEventId,
showKeyboardWhenPresented = args.threadTimelineArgs?.showKeyboard.orFalse(),
sharedData = args.sharedData,
- rootPinnedMessageEventId = args.pinnedMessagesTimelineArgs?.rootPinnedMessageEventId,
+ isFromPinnedEventsTimeline = args.pinnedEventsTimelineArgs != null,
)
fun isCallOptionAvailable(): Boolean {
@@ -115,7 +115,7 @@ data class RoomDetailViewState(
fun isThreadTimeline() = rootThreadEventId != null
- fun isPinnedMessagesTimeline() = rootPinnedMessageEventId != null
+ fun isPinnedEventsTimeline() = isFromPinnedEventsTimeline
fun isLocalRoom() = RoomLocalEcho.isLocalEchoId(roomId)
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt
index 90b9929762..14f7a2ca93 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt
@@ -159,7 +159,7 @@ import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever
import im.vector.app.features.home.room.detail.upgrade.MigrateRoomBottomSheet
import im.vector.app.features.home.room.detail.views.RoomDetailLazyLoadedViews
import im.vector.app.features.home.room.detail.widget.RoomWidgetsBottomSheet
-import im.vector.app.features.home.room.pinnedmessages.arguments.PinnedMessagesTimelineArgs
+import im.vector.app.features.home.room.pinnedmessages.arguments.PinnedEventsTimelineArgs
import im.vector.app.features.home.room.threads.ThreadsManager
import im.vector.app.features.home.room.threads.arguments.ThreadTimelineArgs
import im.vector.app.features.html.EventHtmlRenderer
@@ -379,9 +379,8 @@ class TimelineFragment :
)
}
- if (isPinnedMessagesTimeline()) {
- views.composerContainer.isVisible = false
- views.voiceMessageRecorderContainer.isVisible = false
+ if (isPinnedEventsTimeline()) {
+ views.hideComposerViews()
}
timelineViewModel.observeViewEvents {
@@ -883,8 +882,8 @@ class TimelineFragment :
callActionsHandler.onVideoCallClicked()
true
}
- R.id.open_pinned_messages -> {
- navigateToPinnedMessages()
+ R.id.open_pinned_events -> {
+ navigateToPinnedEvents()
true
}
R.id.menu_timeline_thread_list -> {
@@ -1116,7 +1115,7 @@ class TimelineFragment :
}
private fun updateJumpToReadMarkerViewVisibility() {
- if (isThreadTimeLine() || isPinnedMessagesTimeline()) return
+ if (isThreadTimeLine() || isPinnedEventsTimeline()) return
viewLifecycleOwner.lifecycleScope.launchWhenResumed {
val state = timelineViewModel.awaitState()
val showJumpToUnreadBanner = when (state.unreadState) {
@@ -1197,6 +1196,9 @@ class TimelineFragment :
vectorBaseActivity.finish()
}
updateLiveLocationIndicator(mainState.isSharingLiveLocation)
+ if (isPinnedEventsTimeline()) {
+ views.hideComposerViews()
+ }
}
private fun handleRoomSummaryFailure(asyncRoomSummary: Fail) {
@@ -1245,16 +1247,18 @@ class TimelineFragment :
}
views.includeThreadToolbar.roomToolbarThreadTitleTextView.text = resources.getText(R.string.thread_timeline_title)
}
- isPinnedMessagesTimeline() -> {
+ isPinnedEventsTimeline() -> {
+ withState(timelineViewModel) { state ->
+ timelineArgs.let {
+ val matrixItem = MatrixItem.RoomItem(it.roomId, state.asyncRoomSummary()?.displayName, state.asyncRoomSummary()?.avatarUrl)
+ avatarRenderer.render(matrixItem, views.includeThreadToolbar.roomToolbarThreadImageView)
+ views.includeThreadToolbar.roomToolbarThreadShieldImageView.render(state.asyncRoomSummary()?.roomEncryptionTrustLevel)
+ views.includeThreadToolbar.roomToolbarThreadSubtitleTextView.text = state.asyncRoomSummary()?.displayName
+ }
+ }
views.includeRoomToolbar.roomToolbarContentView.isVisible = false
views.includeThreadToolbar.roomToolbarThreadConstraintLayout.isVisible = true
- timelineArgs.pinnedMessagesTimelineArgs?.let {
- val matrixItem = MatrixItem.RoomItem(it.roomId, it.displayName, it.avatarUrl)
- avatarRenderer.render(matrixItem, views.includeThreadToolbar.roomToolbarThreadImageView)
- views.includeThreadToolbar.roomToolbarThreadShieldImageView.render(it.roomEncryptionTrustLevel)
- views.includeThreadToolbar.roomToolbarThreadSubtitleTextView.text = it.displayName
- }
- views.includeThreadToolbar.roomToolbarThreadTitleTextView.text = resources.getText(R.string.pinned_messages_timeline_title)
+ views.includeThreadToolbar.roomToolbarThreadTitleTextView.text = resources.getText(R.string.pinned_events_timeline_title)
}
else -> {
views.includeRoomToolbar.roomToolbarContentView.isVisible = true
@@ -1564,7 +1568,7 @@ class TimelineFragment :
this.view?.hideKeyboard()
MessageActionsBottomSheet
- .newInstance(roomId, informationData, isThreadTimeLine(), isPinnedMessagesTimeline())
+ .newInstance(roomId, informationData, isThreadTimeLine(), isPinnedEventsTimeline())
.show(requireActivity().supportFragmentManager, "MESSAGE_CONTEXTUAL_ACTIONS")
return true
@@ -1816,13 +1820,13 @@ class TimelineFragment :
requireActivity().toast(R.string.error_voice_message_cannot_reply_or_edit)
}
}
- is EventSharedAction.PinMessage -> {
- timelineViewModel.handle(RoomDetailAction.PinMessage(action.eventId))
+ is EventSharedAction.PinEvent -> {
+ timelineViewModel.handle(RoomDetailAction.PinEvent(action.eventId))
}
- is EventSharedAction.UnpinMessage -> {
- timelineViewModel.handle(RoomDetailAction.UnpinMessage(action.eventId))
+ is EventSharedAction.UnpinEvent -> {
+ timelineViewModel.handle(RoomDetailAction.UnpinEvent(action.eventId))
}
- is EventSharedAction.ViewPinnedMessageInRoom -> {
+ is EventSharedAction.ViewPinnedEventInRoom -> {
handleViewInRoomAction(action.eventId)
}
is EventSharedAction.ReplyInThread -> {
@@ -2005,24 +2009,19 @@ class TimelineFragment :
}
/**
- * Navigate to pinned messages for the current room using the PinnedMessagesActivity.
+ * Navigate to pinned events for the current room using the PinnedEventsActivity.
*/
- private fun navigateToPinnedMessages() = withState(timelineViewModel) { state ->
- val pinnedEventId = timelineViewModel.getIdOfLastPinnedEvent()
+ private fun navigateToPinnedEvents() {
context?.let {
- val pinnedMessagesTimelineArgs = PinnedMessagesTimelineArgs(
+ val pinnedEventsTimelineArgs = PinnedEventsTimelineArgs(
roomId = timelineArgs.roomId,
- displayName = state.asyncRoomSummary()?.displayName,
- roomEncryptionTrustLevel = state.asyncRoomSummary()?.roomEncryptionTrustLevel,
- avatarUrl = state.asyncRoomSummary()?.avatarUrl,
- rootPinnedMessageEventId = pinnedEventId
)
- navigator.openPinnedMessages(it, pinnedMessagesTimelineArgs)
+ navigator.openPinnedEvents(it, pinnedEventsTimelineArgs)
}
}
private fun handleViewInRoomAction(eventId: String) {
- val newRoom = timelineArgs.copy(threadTimelineArgs = null, pinnedMessagesTimelineArgs = null, eventId = eventId)
+ val newRoom = timelineArgs.copy(threadTimelineArgs = null, pinnedEventsTimelineArgs = null, eventId = eventId)
context?.let { con ->
val intent = RoomDetailActivity.newIntent(con, newRoom, false)
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
@@ -2086,7 +2085,7 @@ class TimelineFragment :
/**
* Returns true if the current room is a Pinned Messages room, false otherwise.
*/
- private fun isPinnedMessagesTimeline(): Boolean = withState(timelineViewModel) { it.isPinnedMessagesTimeline() }
+ private fun isPinnedEventsTimeline(): Boolean = withState(timelineViewModel) { it.isPinnedEventsTimeline() }
/**
* Returns true if the current room is a local room, false otherwise.
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt
index 8044845301..3dc3839051 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/TimelineViewModel.kt
@@ -204,10 +204,10 @@ class TimelineViewModel @AssistedInject constructor(
}
private fun initSafe(room: Room, timeline: Timeline) {
- timeline.start(initialState.rootThreadEventId, initialState.rootPinnedMessageEventId)
+ timeline.start(initialState.rootThreadEventId, initialState.isFromPinnedEventsTimeline)
timeline.addListener(this)
observeMembershipChanges()
- if (!initialState.isPinnedMessagesTimeline()) {
+ if (!initialState.isPinnedEventsTimeline()) {
observeSummaryState()
}
getUnreadState()
@@ -451,8 +451,8 @@ class TimelineViewModel @AssistedInject constructor(
override fun handle(action: RoomDetailAction) {
when (action) {
- is RoomDetailAction.PinMessage -> handlePinMessage(action)
- is RoomDetailAction.UnpinMessage -> handleUnpinMessage(action)
+ is RoomDetailAction.PinEvent -> handlePinEvent(action)
+ is RoomDetailAction.UnpinEvent -> handleUnpinEvent(action)
is RoomDetailAction.ComposerFocusChange -> handleComposerFocusChange(action)
is RoomDetailAction.SendMedia -> handleSendMedia(action)
is RoomDetailAction.SendSticker -> handleSendSticker(action)
@@ -762,14 +762,6 @@ class TimelineViewModel @AssistedInject constructor(
return room?.membershipService()?.getRoomMember(userId)
}
- fun getIdOfLastPinnedEvent(): String? {
- return room
- ?.stateService()
- ?.getPinnedEventsState()
- ?.getIdsOfPinnedEvents()
- ?.last()
- }
-
private fun handleComposerFocusChange(action: RoomDetailAction.ComposerFocusChange) {
if (room == null) return
// Ensure outbound session keys
@@ -840,7 +832,7 @@ class TimelineViewModel @AssistedInject constructor(
else -> false
}
}
- initialState.isPinnedMessagesTimeline() -> false
+ initialState.isPinnedEventsTimeline() -> false
else -> {
when (itemId) {
R.id.timeline_setting -> true
@@ -851,7 +843,7 @@ class TimelineViewModel @AssistedInject constructor(
// Show Join conference button only if there is an active conf id not joined. Otherwise fallback to default video disabled. ^
R.id.join_conference -> !state.isCallOptionAvailable() && state.jitsiState.confId != null && !state.jitsiState.hasJoined
R.id.search -> state.isSearchAvailable()
- R.id.open_pinned_messages -> vectorPreferences.arePinnedMessagesEnabled() && areTherePinnedMessages()
+ R.id.open_pinned_events -> vectorPreferences.arePinnedEventsEnabled() && areTherePinnedEvents()
R.id.menu_timeline_thread_list -> vectorPreferences.areThreadMessagesEnabled()
R.id.dev_tools -> vectorPreferences.developerMode()
else -> false
@@ -1038,40 +1030,12 @@ class TimelineViewModel @AssistedInject constructor(
_viewEvents.post(RoomDetailViewEvents.NavigateToEvent(targetEventId))
}
- private fun handlePinMessage(action: RoomDetailAction.PinMessage) {
- if (room == null) return
- val idsOfPinnedMessages = getIdsOfPinnedEvents()
- if (idsOfPinnedMessages == null) return
- idsOfPinnedMessages.add(action.eventId)
- sendPinnedStateEvent(idsOfPinnedMessages, action)
- }
-
- private fun handleUnpinMessage(action: RoomDetailAction.UnpinMessage) {
- if (room == null) return
- val idsOfPinnedMessages = getIdsOfPinnedEvents()
- if (idsOfPinnedMessages == null) return
- idsOfPinnedMessages.remove(action.eventId)
- sendPinnedStateEvent(idsOfPinnedMessages, action)
- }
-
- private fun getIdsOfPinnedEvents(): MutableList? {
- return room
- ?.stateService()
- ?.getPinnedEventsState()
- ?.getIdsOfPinnedEvents()
- }
-
- private fun areTherePinnedMessages(): Boolean {
- val idsOfPinnedMessages = getIdsOfPinnedEvents() ?: return false
- return idsOfPinnedMessages.isNotEmpty()
- }
-
- private fun sendPinnedStateEvent(eventIds: MutableList, action: RoomDetailAction) {
+ private fun handlePinEvent(action: RoomDetailAction.PinEvent) {
viewModelScope.launch(Dispatchers.IO) {
try {
room
?.stateService()
- ?.pinMessage(eventIds)
+ ?.pinEvent(action.eventId)
_viewEvents.post(RoomDetailViewEvents.ActionSuccess(action))
} catch (failure: Throwable) {
_viewEvents.post(RoomDetailViewEvents.ActionFailure(action, failure))
@@ -1079,6 +1043,31 @@ class TimelineViewModel @AssistedInject constructor(
}
}
+ private fun handleUnpinEvent(action: RoomDetailAction.UnpinEvent) {
+ viewModelScope.launch(Dispatchers.IO) {
+ try {
+ room
+ ?.stateService()
+ ?.unpinEvent(action.eventId)
+ _viewEvents.post(RoomDetailViewEvents.ActionSuccess(action))
+ } catch (failure: Throwable) {
+ _viewEvents.post(RoomDetailViewEvents.ActionFailure(action, failure))
+ }
+ }
+ }
+
+ private fun getIdsOfPinnedEvents(): List? {
+ return room
+ ?.stateService()
+ ?.getStateEvent(EventType.STATE_ROOM_PINNED_EVENT, QueryStringValue.Equals(""))
+ ?.getIdsOfPinnedEvents()
+ }
+
+ private fun areTherePinnedEvents(): Boolean {
+ val idsOfPinnedEvents = getIdsOfPinnedEvents() ?: return false
+ return idsOfPinnedEvents.isNotEmpty()
+ }
+
private fun handleResendEvent(action: RoomDetailAction.ResendMessage) {
if (room == null) return
val targetEventId = action.eventId
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/arguments/TimelineArgs.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/arguments/TimelineArgs.kt
index d71f8ae7a9..ba99331a00 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/arguments/TimelineArgs.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/arguments/TimelineArgs.kt
@@ -17,7 +17,7 @@
package im.vector.app.features.home.room.detail.arguments
import android.os.Parcelable
-import im.vector.app.features.home.room.pinnedmessages.arguments.PinnedMessagesTimelineArgs
+import im.vector.app.features.home.room.pinnedmessages.arguments.PinnedEventsTimelineArgs
import im.vector.app.features.home.room.threads.arguments.ThreadTimelineArgs
import im.vector.app.features.share.SharedData
import kotlinx.parcelize.Parcelize
@@ -29,7 +29,7 @@ data class TimelineArgs(
val sharedData: SharedData? = null,
val openShareSpaceForId: String? = null,
val threadTimelineArgs: ThreadTimelineArgs? = null,
- val pinnedMessagesTimelineArgs: PinnedMessagesTimelineArgs? = null,
+ val pinnedEventsTimelineArgs: PinnedEventsTimelineArgs? = null,
val switchToParentSpace: Boolean = false,
val isInviteAlreadyAccepted: Boolean = false
) : Parcelable
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/EventSharedAction.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/EventSharedAction.kt
index 1e7b7c99ef..6679aa1d08 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/EventSharedAction.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/EventSharedAction.kt
@@ -53,13 +53,13 @@ sealed class EventSharedAction(
data class ReplyInThread(val eventId: String, val startsThread: Boolean) :
EventSharedAction(R.string.reply_in_thread, R.drawable.ic_reply_in_thread)
- data class PinMessage(val eventId: String) :
- EventSharedAction(R.string.pinning_message, R.drawable.ic_pin_message)
+ data class PinEvent(val eventId: String) :
+ EventSharedAction(R.string.pinning_event, R.drawable.ic_pin_event)
- data class UnpinMessage(val eventId: String) :
- EventSharedAction(R.string.unpinning_message, R.drawable.ic_unpin_message)
+ data class UnpinEvent(val eventId: String) :
+ EventSharedAction(R.string.unpinning_event, R.drawable.ic_unpin_event)
- data class ViewPinnedMessageInRoom(val eventId: String) :
+ data class ViewPinnedEventInRoom(val eventId: String) :
EventSharedAction(R.string.view_in_room, R.drawable.ic_threads_view_in_room_24)
object ViewInRoom :
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionState.kt
index fa03877219..9d76410129 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionState.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionState.kt
@@ -36,7 +36,7 @@ data class ActionPermissions(
val canSendMessage: Boolean = false,
val canReact: Boolean = false,
val canRedact: Boolean = false,
- val canPinMessage: Boolean = false
+ val canPinEvent: Boolean = false
)
data class MessageActionState(
@@ -52,7 +52,7 @@ data class MessageActionState(
val expendedReportContentMenu: Boolean = false,
val actionPermissions: ActionPermissions = ActionPermissions(),
val isFromThreadTimeline: Boolean = false,
- val isFromPinnedMessagesTimeline: Boolean = false
+ val isFromPinnedEventsTimeline: Boolean = false
) : MavericksState {
constructor(args: TimelineEventFragmentArgs) : this(
@@ -60,7 +60,7 @@ data class MessageActionState(
eventId = args.eventId,
informationData = args.informationData,
isFromThreadTimeline = args.isFromThreadTimeline,
- isFromPinnedMessagesTimeline = args.isFromPinnedMessagesTimeline
+ isFromPinnedEventsTimeline = args.isFromPinnedEventsTimeline
)
fun senderName(): String = informationData.memberName?.toString() ?: ""
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt
index c0740f0905..9f6a117dfd 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt
@@ -93,7 +93,7 @@ class MessageActionsBottomSheet :
}
companion object {
- fun newInstance(roomId: String, informationData: MessageInformationData, isFromThreadTimeline: Boolean, isFromPinnedMessagesTimeline: Boolean): MessageActionsBottomSheet {
+ fun newInstance(roomId: String, informationData: MessageInformationData, isFromThreadTimeline: Boolean, isFromPinnedEventsTimeline: Boolean): MessageActionsBottomSheet {
return MessageActionsBottomSheet().apply {
setArguments(
TimelineEventFragmentArgs(
@@ -101,7 +101,7 @@ class MessageActionsBottomSheet :
roomId,
informationData,
isFromThreadTimeline,
- isFromPinnedMessagesTimeline
+ isFromPinnedEventsTimeline
)
)
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt
index 341fa83987..feb7e7a444 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt
@@ -42,9 +42,11 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import org.matrix.android.sdk.api.extensions.orFalse
+import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState
import org.matrix.android.sdk.api.session.events.model.EventType
+import org.matrix.android.sdk.api.session.events.model.getIdsOfPinnedEvents
import org.matrix.android.sdk.api.session.events.model.isAttachmentMessage
import org.matrix.android.sdk.api.session.events.model.isContentReportable
import org.matrix.android.sdk.api.session.events.model.isTextMessage
@@ -131,8 +133,8 @@ class MessageActionsViewModel @AssistedInject constructor(
val canReact = powerLevelsHelper.isUserAllowedToSend(session.myUserId, false, EventType.REACTION)
val canRedact = powerLevelsHelper.isUserAbleToRedact(session.myUserId)
val canSendMessage = powerLevelsHelper.isUserAllowedToSend(session.myUserId, false, EventType.MESSAGE)
- val canPinMessage = powerLevelsHelper.isUserAllowedToSend(session.myUserId, false, EventType.STATE_ROOM_PINNED_EVENT)
- val permissions = ActionPermissions(canSendMessage = canSendMessage, canRedact = canRedact, canReact = canReact, canPinMessage = canPinMessage)
+ val canPinEvent = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_PINNED_EVENT)
+ val permissions = ActionPermissions(canSendMessage = canSendMessage, canRedact = canRedact, canReact = canReact, canPinEvent = canPinEvent)
setState {
copy(actionPermissions = permissions)
}
@@ -334,91 +336,95 @@ class MessageActionsViewModel @AssistedInject constructor(
) {
val eventId = timelineEvent.eventId
if (!timelineEvent.root.isRedacted()) {
- if (initialState.isFromPinnedMessagesTimeline) {
- if (actionPermissions.canPinMessage && vectorPreferences.arePinnedMessagesEnabled()) {
- add(EventSharedAction.UnpinMessage(eventId))
- add(EventSharedAction.ViewPinnedMessageInRoom(eventId))
+ if (initialState.isFromPinnedEventsTimeline && vectorPreferences.arePinnedEventsEnabled()) {
+ add(EventSharedAction.ViewPinnedEventInRoom(eventId))
+ if (actionPermissions.canPinEvent) {
+ add(EventSharedAction.UnpinEvent(eventId))
}
- return
- }
- if (canReply(timelineEvent, messageContent, actionPermissions)) {
- add(EventSharedAction.Reply(eventId))
- }
-
- if (canReplyInThread(timelineEvent, messageContent, actionPermissions)) {
- add(EventSharedAction.ReplyInThread(eventId, !timelineEvent.isRootThread()))
- }
-
- if (canViewInRoom(timelineEvent, messageContent, actionPermissions)) {
- add(EventSharedAction.ViewInRoom)
- }
-
- if (canEndPoll(timelineEvent, actionPermissions)) {
- add(EventSharedAction.EndPoll(timelineEvent.eventId))
- }
-
- if (canEdit(timelineEvent, session.myUserId, actionPermissions)) {
- add(EventSharedAction.Edit(eventId, timelineEvent.root.getClearType()))
- }
-
- if (canCopy(msgType)) {
- // TODO copy images? html? see ClipBoard
- add(EventSharedAction.Copy(messageContent!!.body))
- }
-
- if (timelineEvent.canReact() && actionPermissions.canReact) {
- add(EventSharedAction.AddReaction(eventId))
- }
-
- if (actionPermissions.canPinMessage && vectorPreferences.arePinnedMessagesEnabled()) {
- val id: String = timelineEvent.root.eventId ?: return
- val isPinned: Boolean = room?.stateService()?.isPinned(id) ?: return
- if (isPinned) {
- add(EventSharedAction.UnpinMessage(eventId))
- } else {
- add(EventSharedAction.PinMessage(eventId))
+ } else {
+ if (canReply(timelineEvent, messageContent, actionPermissions)) {
+ add(EventSharedAction.Reply(eventId))
}
- }
- if (canViewReactions(timelineEvent)) {
- add(EventSharedAction.ViewReactions(informationData))
- }
+ if (canReplyInThread(timelineEvent, messageContent, actionPermissions)) {
+ add(EventSharedAction.ReplyInThread(eventId, !timelineEvent.isRootThread()))
+ }
- if (canQuote(timelineEvent, messageContent, actionPermissions)) {
- add(EventSharedAction.Quote(eventId))
- }
+ if (canViewInRoom(timelineEvent, messageContent, actionPermissions)) {
+ add(EventSharedAction.ViewInRoom)
+ }
- if (timelineEvent.hasBeenEdited()) {
- add(EventSharedAction.ViewEditHistory(informationData))
- }
+ if (canEndPoll(timelineEvent, actionPermissions)) {
+ add(EventSharedAction.EndPoll(timelineEvent.eventId))
+ }
- if (canSave(msgType) && messageContent is MessageWithAttachmentContent) {
- add(EventSharedAction.Save(timelineEvent.eventId, messageContent))
- }
+ if (canEdit(timelineEvent, session.myUserId, actionPermissions)) {
+ add(EventSharedAction.Edit(eventId, timelineEvent.root.getClearType()))
+ }
- if (canShare(msgType)) {
- add(EventSharedAction.Share(timelineEvent.eventId, messageContent!!))
- }
+ if (canCopy(msgType)) {
+ // TODO copy images? html? see ClipBoard
+ add(EventSharedAction.Copy(messageContent!!.body))
+ }
- if (canRedact(timelineEvent, actionPermissions)) {
- if (timelineEvent.root.getClearType() in EventType.POLL_START.values) {
- add(
- EventSharedAction.Redact(
- eventId,
- askForReason = informationData.senderId != session.myUserId,
- dialogTitleRes = R.string.delete_poll_dialog_title,
- dialogDescriptionRes = R.string.delete_poll_dialog_content
- )
- )
- } else {
- add(
- EventSharedAction.Redact(
- eventId,
- askForReason = informationData.senderId != session.myUserId,
- dialogTitleRes = R.string.delete_event_dialog_title,
- dialogDescriptionRes = R.string.delete_event_dialog_content
- )
- )
+ if (timelineEvent.canReact() && actionPermissions.canReact) {
+ add(EventSharedAction.AddReaction(eventId))
+ }
+
+ if (actionPermissions.canPinEvent && vectorPreferences.arePinnedEventsEnabled()) {
+ val isPinned = room
+ ?.stateService()
+ ?.getStateEvent(EventType.STATE_ROOM_PINNED_EVENT, QueryStringValue.Equals(""))
+ ?.getIdsOfPinnedEvents()
+ ?.contains(eventId)
+ .orFalse()
+ if (isPinned) {
+ add(EventSharedAction.UnpinEvent(eventId))
+ } else {
+ add(EventSharedAction.PinEvent(eventId))
+ }
+ }
+
+ if (canViewReactions(timelineEvent)) {
+ add(EventSharedAction.ViewReactions(informationData))
+ }
+
+ if (canQuote(timelineEvent, messageContent, actionPermissions)) {
+ add(EventSharedAction.Quote(eventId))
+ }
+
+ if (timelineEvent.hasBeenEdited()) {
+ add(EventSharedAction.ViewEditHistory(informationData))
+ }
+
+ if (canSave(msgType) && messageContent is MessageWithAttachmentContent) {
+ add(EventSharedAction.Save(timelineEvent.eventId, messageContent))
+ }
+
+ if (canShare(msgType)) {
+ add(EventSharedAction.Share(timelineEvent.eventId, messageContent!!))
+ }
+
+ if (canRedact(timelineEvent, actionPermissions)) {
+ if (timelineEvent.root.getClearType() in EventType.POLL_START.values) {
+ add(
+ EventSharedAction.Redact(
+ eventId,
+ askForReason = informationData.senderId != session.myUserId,
+ dialogTitleRes = R.string.delete_poll_dialog_title,
+ dialogDescriptionRes = R.string.delete_poll_dialog_content
+ )
+ )
+ } else {
+ add(
+ EventSharedAction.Redact(
+ eventId,
+ askForReason = informationData.senderId != session.myUserId,
+ dialogTitleRes = R.string.delete_event_dialog_title,
+ dialogDescriptionRes = R.string.delete_event_dialog_content
+ )
+ )
+ }
}
}
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/TimelineEventFragmentArgs.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/TimelineEventFragmentArgs.kt
index 17d55ac8b9..e6d14bdc7f 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/TimelineEventFragmentArgs.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/TimelineEventFragmentArgs.kt
@@ -26,5 +26,5 @@ data class TimelineEventFragmentArgs(
val roomId: String,
val informationData: MessageInformationData,
val isFromThreadTimeline: Boolean = false,
- val isFromPinnedMessagesTimeline: Boolean = false
+ val isFromPinnedEventsTimeline: Boolean = false
) : Parcelable
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt
index e2956d9d98..efc139a7af 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt
@@ -122,16 +122,24 @@ class NoticeEventFormatter @Inject constructor(
}
private fun formatPinnedEvent(event: Event, disambiguatedDisplayName: String): CharSequence? {
- val idsOfPinnedEvents: MutableList = event.getIdsOfPinnedEvents() ?: return null
- val previousIdsOfPinnedEvents: MutableList? = event.getPreviousIdsOfPinnedEvents()
- // A message was pinned
- val pinnedMessageString = if (event.resolvedPrevContent() == null || previousIdsOfPinnedEvents != null && previousIdsOfPinnedEvents.size < idsOfPinnedEvents.size) {
- sp.getString(R.string.user_pinned_message, disambiguatedDisplayName)
- // A message was unpinned
+ val idsOfPinnedEvents: List = event.getIdsOfPinnedEvents() ?: return null
+ val previousIdsOfPinnedEvents: List? = event.getPreviousIdsOfPinnedEvents()
+ // An event was pinned
+ val pinnedEventString = if (event.resolvedPrevContent() == null || previousIdsOfPinnedEvents != null && previousIdsOfPinnedEvents.size < idsOfPinnedEvents.size) {
+ if (event.isSentByCurrentUser()) {
+ sp.getString(R.string.notice_user_pinned_event_by_you, disambiguatedDisplayName)
+ } else {
+ sp.getString(R.string.notice_user_pinned_event, disambiguatedDisplayName)
+ }
+ // An event was unpinned
} else {
- sp.getString(R.string.user_unpinned_message, disambiguatedDisplayName)
+ if (event.isSentByCurrentUser()) {
+ sp.getString(R.string.notice_user_unpinned_event_by_you, disambiguatedDisplayName)
+ } else {
+ sp.getString(R.string.notice_user_unpinned_event, disambiguatedDisplayName)
+ }
}
- return pinnedMessageString
+ return pinnedEventString
}
private fun formatRoomPowerLevels(event: Event, disambiguatedDisplayName: String): CharSequence? {
@@ -194,7 +202,6 @@ class NoticeEventFormatter @Inject constructor(
}
fun format(event: Event, senderName: String?, isDm: Boolean): CharSequence? {
- Timber.v("°°°°°°°°°°°°°°°°°°°format(event: Event, senderName: String?, isDm: Boolean)")
return when (val type = event.getClearType()) {
EventType.STATE_ROOM_JOIN_RULES -> formatJoinRulesEvent(event, senderName, isDm)
EventType.STATE_ROOM_NAME -> formatRoomNameEvent(event, senderName)
@@ -889,7 +896,6 @@ class NoticeEventFormatter @Inject constructor(
}
fun formatRedactedEvent(event: Event): String {
- Timber.v("°°°°°°°formatRedactedEvent°°°°°°")
return (event
.unsignedData
?.redactedEvent
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/merged/MergedTimelines.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/merged/MergedTimelines.kt
index 55d82e9e4f..76ceb4b688 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/merged/MergedTimelines.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/merged/MergedTimelines.kt
@@ -113,7 +113,7 @@ class MergedTimelines(
secondaryTimeline.removeAllListeners()
}
- override fun start(rootThreadEventId: String?, rootPinnedMessageEventId: String?) {
+ override fun start(rootThreadEventId: String?, isFromPinnedEventsTimeline: Boolean) {
mainTimeline.start()
secondaryTimeline.start()
}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/pinnedevents/PinnedEventsActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/pinnedevents/PinnedEventsActivity.kt
new file mode 100644
index 0000000000..ca673a4177
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/home/room/pinnedevents/PinnedEventsActivity.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.home.room.pinnedmessages
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import dagger.hilt.android.AndroidEntryPoint
+import im.vector.app.core.extensions.replaceFragment
+import im.vector.app.core.platform.VectorBaseActivity
+import im.vector.app.databinding.ActivityPinnedEventsBinding
+import im.vector.app.features.home.AvatarRenderer
+import im.vector.app.features.home.room.detail.TimelineFragment
+import im.vector.app.features.home.room.detail.arguments.TimelineArgs
+import im.vector.app.features.home.room.pinnedmessages.arguments.PinnedEventsTimelineArgs
+import im.vector.lib.core.utils.compat.getParcelableCompat
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class PinnedEventsActivity : VectorBaseActivity() {
+
+ @Inject lateinit var avatarRenderer: AvatarRenderer
+
+ override fun getBinding() = ActivityPinnedEventsBinding.inflate(layoutInflater)
+
+ override fun getCoordinatorLayout() = views.coordinatorLayout
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ initFragment()
+ }
+
+ private fun initFragment() {
+ if (isFirstCreation()) {
+ val args = getPinnedEventsTimelineArgs()
+ if (args == null) {
+ finish()
+ } else {
+ initPinnedEventsTimelineFragment(args)
+ }
+ }
+ }
+
+ private fun initPinnedEventsTimelineFragment(pinnedEventsTimelineArgs: PinnedEventsTimelineArgs) =
+ replaceFragment(
+ views.pinnedEventsActivityFragmentContainer,
+ TimelineFragment::class.java,
+ TimelineArgs(
+ roomId = pinnedEventsTimelineArgs.roomId,
+ pinnedEventsTimelineArgs = pinnedEventsTimelineArgs
+ )
+ )
+
+ private fun getPinnedEventsTimelineArgs(): PinnedEventsTimelineArgs? = intent?.extras?.getParcelableCompat(PINNED_EVENTS_TIMELINE_ARGS)
+
+ companion object {
+ const val PINNED_EVENTS_TIMELINE_ARGS = "PINNED_EVENTS_TIMELINE_ARGS"
+
+ fun newIntent(
+ context: Context,
+ pinnedEventsTimelineArgs: PinnedEventsTimelineArgs?,
+ ): Intent {
+ return Intent(context, PinnedEventsActivity::class.java).apply {
+ putExtra(PINNED_EVENTS_TIMELINE_ARGS, pinnedEventsTimelineArgs)
+ }
+ }
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/pinnedmessages/arguments/PinnedMessagesTimelineArgs.kt b/vector/src/main/java/im/vector/app/features/home/room/pinnedevents/arguments/PinnedEventsTimelineArgs.kt
similarity index 70%
rename from vector/src/main/java/im/vector/app/features/home/room/pinnedmessages/arguments/PinnedMessagesTimelineArgs.kt
rename to vector/src/main/java/im/vector/app/features/home/room/pinnedevents/arguments/PinnedEventsTimelineArgs.kt
index daf6bb9240..2c81c2f4d5 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/pinnedmessages/arguments/PinnedMessagesTimelineArgs.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/pinnedevents/arguments/PinnedEventsTimelineArgs.kt
@@ -18,13 +18,8 @@ package im.vector.app.features.home.room.pinnedmessages.arguments
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
-import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
@Parcelize
-data class PinnedMessagesTimelineArgs(
- val roomId: String,
- val displayName: String?,
- val avatarUrl: String?,
- val roomEncryptionTrustLevel: RoomEncryptionTrustLevel?,
- val rootPinnedMessageEventId: String?
+data class PinnedEventsTimelineArgs(
+ val roomId: String
) : Parcelable
diff --git a/vector/src/main/java/im/vector/app/features/home/room/pinnedmessages/PinnedMessagesActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/pinnedmessages/PinnedMessagesActivity.kt
deleted file mode 100644
index 3c5c305a20..0000000000
--- a/vector/src/main/java/im/vector/app/features/home/room/pinnedmessages/PinnedMessagesActivity.kt
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (c) 2022 New Vector Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package im.vector.app.features.home.room.pinnedmessages
-
-import android.content.Context
-import android.content.Intent
-import android.os.Bundle
-import dagger.hilt.android.AndroidEntryPoint
-import im.vector.app.core.extensions.replaceFragment
-import im.vector.app.core.platform.VectorBaseActivity
-import im.vector.app.databinding.ActivityPinnedMessagesBinding
-import im.vector.app.features.home.AvatarRenderer
-import im.vector.app.features.home.room.detail.TimelineFragment
-import im.vector.app.features.home.room.detail.arguments.TimelineArgs
-import im.vector.app.features.home.room.pinnedmessages.arguments.PinnedMessagesTimelineArgs
-import im.vector.lib.core.utils.compat.getParcelableCompat
-import javax.inject.Inject
-
-@AndroidEntryPoint
-class PinnedMessagesActivity : VectorBaseActivity() {
-
- @Inject lateinit var avatarRenderer: AvatarRenderer
-
- override fun getBinding() = ActivityPinnedMessagesBinding.inflate(layoutInflater)
-
- override fun getCoordinatorLayout() = views.coordinatorLayout
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- initFragment()
- }
-
- private fun initFragment() {
- if (isFirstCreation()) {
- when (val fragment = fragmentToNavigate()) {
- is DisplayFragment.PinnedMessagesTimeLine -> {
- initPinnedMessagesTimelineFragment(fragment.pinnedMessagesTimelineArgs)
- }
- is DisplayFragment.ErrorFragment -> {
- finish()
- }
- }
- }
- }
-
- private fun initPinnedMessagesTimelineFragment(pinnedMessagesTimelineArgs: PinnedMessagesTimelineArgs) =
- replaceFragment(
- views.pinnedMessagesActivityFragmentContainer,
- TimelineFragment::class.java,
- TimelineArgs(
- roomId = pinnedMessagesTimelineArgs.roomId,
- pinnedMessagesTimelineArgs = pinnedMessagesTimelineArgs
- )
- )
-
- /**
- * Determine in witch fragment we should navigate.
- */
- private fun fragmentToNavigate(): DisplayFragment {
- getPinnedMessagesTimelineArgs()?.let {
- return DisplayFragment.PinnedMessagesTimeLine(it)
- }
- return DisplayFragment.ErrorFragment
- }
-
- private fun getPinnedMessagesTimelineArgs(): PinnedMessagesTimelineArgs? = intent?.extras?.getParcelableCompat(PINNED_MESSAGES_TIMELINE_ARGS)
-
- companion object {
- const val PINNED_MESSAGES_TIMELINE_ARGS = "PINNED_MESSAGES_TIMELINE_ARGS"
-
- fun newIntent(
- context: Context,
- pinnedMessagesTimelineArgs: PinnedMessagesTimelineArgs?,
- ): Intent {
- return Intent(context, PinnedMessagesActivity::class.java).apply {
- putExtra(PINNED_MESSAGES_TIMELINE_ARGS, pinnedMessagesTimelineArgs)
- }
- }
- }
-
- sealed class DisplayFragment {
- data class PinnedMessagesTimeLine(val pinnedMessagesTimelineArgs: PinnedMessagesTimelineArgs) : DisplayFragment()
- object ErrorFragment : DisplayFragment()
- }
-}
diff --git a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt
index 58a28386fd..7333154776 100644
--- a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt
+++ b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt
@@ -58,8 +58,8 @@ import im.vector.app.features.home.room.detail.arguments.TimelineArgs
import im.vector.app.features.home.room.detail.search.SearchActivity
import im.vector.app.features.home.room.detail.search.SearchArgs
import im.vector.app.features.home.room.filtered.FilteredRoomsActivity
-import im.vector.app.features.home.room.pinnedmessages.arguments.PinnedMessagesTimelineArgs
-import im.vector.app.features.home.room.pinnedmessages.PinnedMessagesActivity
+import im.vector.app.features.home.room.pinnedmessages.arguments.PinnedEventsTimelineArgs
+import im.vector.app.features.home.room.pinnedmessages.PinnedEventsActivity
import im.vector.app.features.home.room.threads.ThreadsActivity
import im.vector.app.features.home.room.threads.arguments.ThreadListArgs
import im.vector.app.features.home.room.threads.arguments.ThreadTimelineArgs
@@ -601,11 +601,11 @@ class DefaultNavigator @Inject constructor(
)
}
- override fun openPinnedMessages(context: Context, pinnedMessagesTimelineArgs: PinnedMessagesTimelineArgs) {
+ override fun openPinnedEvents(context: Context, pinnedEventsTimelineArgs: PinnedEventsTimelineArgs) {
context.startActivity(
- PinnedMessagesActivity.newIntent(
+ PinnedEventsActivity.newIntent(
context = context,
- pinnedMessagesTimelineArgs = pinnedMessagesTimelineArgs
+ pinnedEventsTimelineArgs = pinnedEventsTimelineArgs
)
)
}
diff --git a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt
index 6cba3a298f..a7f736549d 100644
--- a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt
+++ b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt
@@ -27,7 +27,7 @@ import androidx.fragment.app.FragmentActivity
import im.vector.app.features.analytics.plan.ViewRoom
import im.vector.app.features.crypto.recover.SetupMode
import im.vector.app.features.displayname.getBestName
-import im.vector.app.features.home.room.pinnedmessages.arguments.PinnedMessagesTimelineArgs
+import im.vector.app.features.home.room.pinnedmessages.arguments.PinnedEventsTimelineArgs
import im.vector.app.features.home.room.threads.arguments.ThreadTimelineArgs
import im.vector.app.features.location.LocationData
import im.vector.app.features.location.LocationSharingMode
@@ -199,7 +199,7 @@ interface Navigator {
fun openThreadList(context: Context, threadTimelineArgs: ThreadTimelineArgs)
- fun openPinnedMessages(context: Context, pinnedMessagesTimelineArgs: PinnedMessagesTimelineArgs)
+ fun openPinnedEvents(context: Context, pinnedEventsTimelineArgs: PinnedEventsTimelineArgs)
fun openScreenSharingPermissionDialog(
screenCaptureIntent: Intent,
diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt
index 0278ff91c2..0f9b2c03f0 100755
--- a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt
@@ -234,7 +234,7 @@ class VectorPreferences @Inject constructor(
private const val SETTINGS_LABS_ENABLE_ELEMENT_CALL_PERMISSION_SHORTCUTS = "SETTINGS_LABS_ENABLE_ELEMENT_CALL_PERMISSION_SHORTCUTS"
- private const val SETTINGS_LABS_ENABLE_PINNED_MESSAGES = "SETTINGS_LABS_ENABLE_PINNED_MESSAGES"
+ private const val SETTINGS_LABS_ENABLE_PINNED_EVENTS = "SETTINGS_LABS_ENABLE_PINNED_EVENTS"
// This key will be used to identify clients with the old thread support enabled io.element.thread
const val SETTINGS_LABS_ENABLE_THREAD_MESSAGES_OLD_CLIENTS = "SETTINGS_LABS_ENABLE_THREAD_MESSAGES"
@@ -1114,8 +1114,8 @@ class VectorPreferences @Inject constructor(
return defaultPrefs.getBoolean(SETTINGS_LABS_ENABLE_ELEMENT_CALL_PERMISSION_SHORTCUTS, false)
}
- fun arePinnedMessagesEnabled(): Boolean {
- return defaultPrefs.getBoolean(SETTINGS_LABS_ENABLE_PINNED_MESSAGES, getDefault(R.bool.settings_labs_pinned_messages_default))
+ fun arePinnedEventsEnabled(): Boolean {
+ return defaultPrefs.getBoolean(SETTINGS_LABS_ENABLE_PINNED_EVENTS, getDefault(R.bool.settings_labs_pinned_events_default))
}
/**
diff --git a/vector/src/main/res/drawable/ic_open_pinned_messages.xml b/vector/src/main/res/drawable/ic_open_pinned_events.xml
similarity index 100%
rename from vector/src/main/res/drawable/ic_open_pinned_messages.xml
rename to vector/src/main/res/drawable/ic_open_pinned_events.xml
diff --git a/vector/src/main/res/drawable/ic_pin_event.xml b/vector/src/main/res/drawable/ic_pin_event.xml
new file mode 100644
index 0000000000..b0341a8aa8
--- /dev/null
+++ b/vector/src/main/res/drawable/ic_pin_event.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/vector/src/main/res/drawable/ic_pin_message.xml b/vector/src/main/res/drawable/ic_pin_message.xml
deleted file mode 100644
index 9fc7b8cecc..0000000000
--- a/vector/src/main/res/drawable/ic_pin_message.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/vector/src/main/res/drawable/ic_unpin_event.xml b/vector/src/main/res/drawable/ic_unpin_event.xml
new file mode 100644
index 0000000000..514d21ec17
--- /dev/null
+++ b/vector/src/main/res/drawable/ic_unpin_event.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/vector/src/main/res/drawable/ic_unpin_message.xml b/vector/src/main/res/drawable/ic_unpin_message.xml
deleted file mode 100644
index 0cad148ca7..0000000000
--- a/vector/src/main/res/drawable/ic_unpin_message.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/vector/src/main/res/layout/activity_pinned_messages.xml b/vector/src/main/res/layout/activity_pinned_events.xml
similarity index 88%
rename from vector/src/main/res/layout/activity_pinned_messages.xml
rename to vector/src/main/res/layout/activity_pinned_events.xml
index e7b0ef00c9..93a75fe2e3 100644
--- a/vector/src/main/res/layout/activity_pinned_messages.xml
+++ b/vector/src/main/res/layout/activity_pinned_events.xml
@@ -11,7 +11,7 @@
android:layout_height="match_parent">
- -->
+ android:defaultValue="@bool/settings_labs_pinned_events_default"
+ android:key="SETTINGS_LABS_ENABLE_PINNED_EVENTS"
+ android:title="@string/labs_enable_pinned_events" />