edits after first review on Github
This commit is contained in:
parent
adc9136386
commit
dd65c67f1f
|
@ -8,7 +8,7 @@
|
||||||
# The setting is particularly useful for tweaking memory settings.
|
# The setting is particularly useful for tweaking memory settings.
|
||||||
|
|
||||||
# Build Time Optimizations
|
# 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.configureondemand=true
|
||||||
org.gradle.parallel=true
|
org.gradle.parallel=true
|
||||||
org.gradle.vfs.watch=true
|
org.gradle.vfs.watch=true
|
||||||
|
|
|
@ -77,6 +77,10 @@
|
||||||
<string name="notice_room_server_acl_set_allowed">• Servers matching %s are allowed.</string>
|
<string name="notice_room_server_acl_set_allowed">• Servers matching %s are allowed.</string>
|
||||||
<string name="notice_room_server_acl_set_ip_literals_allowed">• Servers matching IP literals are allowed.</string>
|
<string name="notice_room_server_acl_set_ip_literals_allowed">• Servers matching IP literals are allowed.</string>
|
||||||
<string name="notice_room_server_acl_set_ip_literals_not_allowed">• Servers matching IP literals are banned.</string>
|
<string name="notice_room_server_acl_set_ip_literals_not_allowed">• Servers matching IP literals are banned.</string>
|
||||||
|
<string name="notice_user_pinned_event">%1$s pinned a message.</string>
|
||||||
|
<string name="notice_user_unpinned_event">%1$s unpinned a message.</string>
|
||||||
|
<string name="notice_user_pinned_event_by_you">You pinned a message.</string>
|
||||||
|
<string name="notice_user_unpinned_event_by_you">You unpinned a message.</string>
|
||||||
|
|
||||||
<string name="notice_room_server_acl_updated_title">%s changed the server ACLs for this room.</string>
|
<string name="notice_room_server_acl_updated_title">%s changed the server ACLs for this room.</string>
|
||||||
<string name="notice_room_server_acl_updated_title_by_you">You changed the server ACLs for this room.</string>
|
<string name="notice_room_server_acl_updated_title_by_you">You changed the server ACLs for this room.</string>
|
||||||
|
@ -373,7 +377,7 @@
|
||||||
<string name="action_sign_out_confirmation_simple">Are you sure you want to sign out?</string>
|
<string name="action_sign_out_confirmation_simple">Are you sure you want to sign out?</string>
|
||||||
<string name="action_voice_call">Voice Call</string>
|
<string name="action_voice_call">Voice Call</string>
|
||||||
<string name="action_video_call">Video Call</string>
|
<string name="action_video_call">Video Call</string>
|
||||||
<string name="action_open_pinned_messages">Open Pinned Messages</string>
|
<string name="action_open_pinned_events">Open Pinned Messages</string>
|
||||||
<string name="action_view_threads">View Threads</string>
|
<string name="action_view_threads">View Threads</string>
|
||||||
<string name="action_mark_all_as_read">Mark all as read</string>
|
<string name="action_mark_all_as_read">Mark all as read</string>
|
||||||
<string name="action_quick_reply">Quick reply</string>
|
<string name="action_quick_reply">Quick reply</string>
|
||||||
|
@ -803,11 +807,9 @@
|
||||||
<string name="threads_labs_enable_notice_message">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?</string>
|
<string name="threads_labs_enable_notice_message">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?</string>
|
||||||
|
|
||||||
<!-- Pinning -->
|
<!-- Pinning -->
|
||||||
<string name="pinning_message">Pin</string>
|
<string name="pinning_event">Pin</string>
|
||||||
<string name="unpinning_message">Unpin</string>
|
<string name="unpinning_event">Unpin</string>
|
||||||
<string name="pinned_messages_timeline_title">Pinned Messages</string>
|
<string name="pinned_events_timeline_title">Pinned Messages</string>
|
||||||
<string name="user_pinned_message">%1$s pinned a message.</string>
|
|
||||||
<string name="user_unpinned_message">%1$s unpinned a message.</string>
|
|
||||||
|
|
||||||
<!-- Search -->
|
<!-- Search -->
|
||||||
<string name="search_hint">Search</string>
|
<string name="search_hint">Search</string>
|
||||||
|
@ -3039,7 +3041,7 @@
|
||||||
|
|
||||||
<string name="labs_auto_report_uisi">Auto Report Decryption Errors.</string>
|
<string name="labs_auto_report_uisi">Auto Report Decryption Errors.</string>
|
||||||
<string name="labs_auto_report_uisi_desc">Your system will automatically send logs when an unable to decrypt error occurs</string>
|
<string name="labs_auto_report_uisi_desc">Your system will automatically send logs when an unable to decrypt error occurs</string>
|
||||||
<string name="labs_enable_pinned_messages">Enable Pinned Messages</string>
|
<string name="labs_enable_pinned_events">Enable Pinned Messages</string>
|
||||||
<string name="labs_enable_thread_messages">Enable Thread Messages</string>
|
<string name="labs_enable_thread_messages">Enable Thread Messages</string>
|
||||||
<string name="labs_enable_thread_messages_desc">Note: app will be restarted</string>
|
<string name="labs_enable_thread_messages_desc">Note: app will be restarted</string>
|
||||||
<string name="settings_show_latest_profile">Show latest user info</string>
|
<string name="settings_show_latest_profile">Show latest user info</string>
|
||||||
|
|
|
@ -449,10 +449,10 @@ fun Event.supportsNotification() =
|
||||||
fun Event.isContentReportable() =
|
fun Event.isContentReportable() =
|
||||||
this.getClearType() in EventType.MESSAGE + EventType.STATE_ROOM_BEACON_INFO.values
|
this.getClearType() in EventType.MESSAGE + EventType.STATE_ROOM_BEACON_INFO.values
|
||||||
|
|
||||||
fun Event.getIdsOfPinnedEvents(): MutableList<String>? {
|
fun Event.getIdsOfPinnedEvents(): List<String>? {
|
||||||
return getClearContent()?.toModel<PinnedEventsStateContent>()?.eventIds
|
return getClearContent()?.toModel<PinnedEventsStateContent>()?.eventIds
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Event.getPreviousIdsOfPinnedEvents(): MutableList<String>? {
|
fun Event.getPreviousIdsOfPinnedEvents(): List<String>? {
|
||||||
return resolvedPrevContent()?.toModel<PinnedEventsStateContent>()?.eventIds
|
return resolvedPrevContent()?.toModel<PinnedEventsStateContent>()?.eventIds
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,5 +24,5 @@ import com.squareup.moshi.JsonClass
|
||||||
*/
|
*/
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class PinnedEventsStateContent(
|
data class PinnedEventsStateContent(
|
||||||
@Json(name = "pinned") val eventIds: MutableList<String>
|
@Json(name = "pinned") val eventIds: List<String>
|
||||||
)
|
)
|
||||||
|
|
|
@ -67,9 +67,14 @@ interface StateService {
|
||||||
suspend fun deleteAvatar()
|
suspend fun deleteAvatar()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pin a message of the room.
|
* Pin an event of the room.
|
||||||
*/
|
*/
|
||||||
suspend fun pinMessage(eventIds: MutableList<String>)
|
suspend fun pinEvent(eventId: String)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unpin an event of the room.
|
||||||
|
*/
|
||||||
|
suspend fun unpinEvent(eventId: String)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a state event to the room.
|
* Send a state event to the room.
|
||||||
|
@ -108,16 +113,6 @@ interface StateService {
|
||||||
*/
|
*/
|
||||||
fun getStateEventsLive(eventTypes: Set<String>, stateKey: QueryStateEventValue): LiveData<List<Event>>
|
fun getStateEventsLive(eventTypes: Set<String>, stateKey: QueryStateEventValue): LiveData<List<Event>>
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 setJoinRulePublic()
|
||||||
suspend fun setJoinRuleInviteOnly()
|
suspend fun setJoinRuleInviteOnly()
|
||||||
suspend fun setJoinRuleRestricted(allowList: List<String>)
|
suspend fun setJoinRuleRestricted(allowList: List<String>)
|
||||||
|
|
|
@ -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
|
* 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.
|
* This must be called when you don't need the timeline. It ensures the underlying database get closed.
|
||||||
|
|
|
@ -33,9 +33,9 @@ data class TimelineSettings(
|
||||||
*/
|
*/
|
||||||
val rootThreadEventId: String? = null,
|
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).
|
* 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.
|
* Returns true if this is a pinned messages timeline or false otherwise.
|
||||||
*/
|
*/
|
||||||
fun isPinnedMessagesTimeline() = rootPinnedMessageEventId != null
|
fun isPinnedEventsTimeline() = isFromPinnedEventsTimeline
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.admin.UserIdAndReason
|
||||||
import org.matrix.android.sdk.internal.session.room.membership.joining.InviteBody
|
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.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.read.ReadBody
|
||||||
import org.matrix.android.sdk.internal.session.room.relation.RelationsResponse
|
import org.matrix.android.sdk.internal.session.room.relation.RelationsResponse
|
||||||
import org.matrix.android.sdk.internal.session.room.reporting.ReportContentBody
|
import org.matrix.android.sdk.internal.session.room.reporting.ReportContentBody
|
||||||
|
@ -249,7 +248,7 @@ internal interface RoomAPI {
|
||||||
@Path("roomId") roomId: String,
|
@Path("roomId") roomId: String,
|
||||||
@Path("eventType") eventType: String,
|
@Path("eventType") eventType: String,
|
||||||
@Path("state_key") stateKey: String
|
@Path("state_key") stateKey: String
|
||||||
): PinnedEventsStateResponse
|
): Content
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Paginate relations for event based in normal topological order.
|
* Paginate relations for event based in normal topological order.
|
||||||
|
|
|
@ -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<String>
|
|
||||||
)
|
|
|
@ -173,7 +173,25 @@ internal class DefaultStateService @AssistedInject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun pinMessage(eventIds: MutableList<String>) {
|
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<String>) {
|
||||||
sendStateEvent(
|
sendStateEvent(
|
||||||
eventType = EventType.STATE_ROOM_PINNED_EVENT,
|
eventType = EventType.STATE_ROOM_PINNED_EVENT,
|
||||||
body = PinnedEventsStateContent(eventIds).toContent(),
|
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<String> = getPinnedEventsState()?.getIdsOfPinnedEvents() ?: return null
|
|
||||||
return idsOfPinnedEvents.contains(eventId)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun setJoinRulePublic() {
|
override suspend fun setJoinRulePublic() {
|
||||||
updateJoinRule(RoomJoinRules.PUBLIC, null)
|
updateJoinRule(RoomJoinRules.PUBLIC, null)
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,8 +99,7 @@ internal class DefaultTimeline(
|
||||||
private var isFromThreadTimeline = false
|
private var isFromThreadTimeline = false
|
||||||
private var rootThreadEventId: String? = null
|
private var rootThreadEventId: String? = null
|
||||||
|
|
||||||
private var isFromPinnedMessagesTimeline = false
|
private var isFromPinnedEventsTimeline = false
|
||||||
private var rootPinnedMessageEventId: String? = null
|
|
||||||
|
|
||||||
private val strategyDependencies = LoadTimelineStrategy.Dependencies(
|
private val strategyDependencies = LoadTimelineStrategy.Dependencies(
|
||||||
timelineSettings = settings,
|
timelineSettings = settings,
|
||||||
|
@ -132,7 +131,7 @@ internal class DefaultTimeline(
|
||||||
override fun addListener(listener: Timeline.Listener): Boolean {
|
override fun addListener(listener: Timeline.Listener): Boolean {
|
||||||
listeners.add(listener)
|
listeners.add(listener)
|
||||||
timelineScope.launch {
|
timelineScope.launch {
|
||||||
val snapshot = if (isFromPinnedMessagesTimeline) {
|
val snapshot = if (isFromPinnedEventsTimeline) {
|
||||||
getPinnedEvents()
|
getPinnedEvents()
|
||||||
} else {
|
} else {
|
||||||
strategy.buildSnapshot()
|
strategy.buildSnapshot()
|
||||||
|
@ -152,7 +151,7 @@ internal class DefaultTimeline(
|
||||||
listeners.clear()
|
listeners.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun start(rootThreadEventId: String?, rootPinnedMessageEventId: String?) {
|
override fun start(rootThreadEventId: String?, isFromPinnedEventsTimeline: Boolean) {
|
||||||
timelineScope.launch {
|
timelineScope.launch {
|
||||||
loadRoomMembersIfNeeded()
|
loadRoomMembersIfNeeded()
|
||||||
}
|
}
|
||||||
|
@ -161,8 +160,7 @@ internal class DefaultTimeline(
|
||||||
if (isStarted.compareAndSet(false, true)) {
|
if (isStarted.compareAndSet(false, true)) {
|
||||||
isFromThreadTimeline = rootThreadEventId != null
|
isFromThreadTimeline = rootThreadEventId != null
|
||||||
this@DefaultTimeline.rootThreadEventId = rootThreadEventId
|
this@DefaultTimeline.rootThreadEventId = rootThreadEventId
|
||||||
isFromPinnedMessagesTimeline = rootPinnedMessageEventId != null
|
this@DefaultTimeline.isFromPinnedEventsTimeline = isFromPinnedEventsTimeline
|
||||||
this@DefaultTimeline.rootPinnedMessageEventId = rootPinnedMessageEventId
|
|
||||||
// /
|
// /
|
||||||
val realm = Realm.getInstance(realmConfiguration)
|
val realm = Realm.getInstance(realmConfiguration)
|
||||||
ensureReadReceiptAreLoaded(realm)
|
ensureReadReceiptAreLoaded(realm)
|
||||||
|
@ -267,8 +265,8 @@ internal class DefaultTimeline(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Timber.v("$baseLogMessage: result $loadMoreResult")
|
Timber.v("$baseLogMessage: result $loadMoreResult")
|
||||||
val hasMoreToLoad = if (isFromPinnedMessagesTimeline) {
|
val hasMoreToLoad = if (isFromPinnedEventsTimeline) {
|
||||||
!areAllPinnedMessagesLoaded()
|
!areAllPinnedEventsLoaded()
|
||||||
} else {
|
} else {
|
||||||
loadMoreResult != LoadMoreResult.REACHED_END
|
loadMoreResult != LoadMoreResult.REACHED_END
|
||||||
}
|
}
|
||||||
|
@ -352,7 +350,7 @@ internal class DefaultTimeline(
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun postSnapshot() {
|
private suspend fun postSnapshot() {
|
||||||
val snapshot = if (isFromPinnedMessagesTimeline) {
|
val snapshot = if (isFromPinnedEventsTimeline) {
|
||||||
getPinnedEvents()
|
getPinnedEvents()
|
||||||
} else {
|
} else {
|
||||||
strategy.buildSnapshot()
|
strategy.buildSnapshot()
|
||||||
|
@ -371,25 +369,22 @@ internal class DefaultTimeline(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getIdsOfPinnedEvents(): MutableList<String> {
|
private fun getIdsOfPinnedEvents(): List<String> {
|
||||||
return stateEventDataSource
|
return stateEventDataSource
|
||||||
.getStateEvent(roomId, EventType.STATE_ROOM_PINNED_EVENT, QueryStringValue.Equals(""))
|
.getStateEvent(roomId, EventType.STATE_ROOM_PINNED_EVENT, QueryStringValue.Equals(""))
|
||||||
?.getIdsOfPinnedEvents() ?: mutableListOf("")
|
?.getIdsOfPinnedEvents()
|
||||||
|
.orEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getPinnedEvents(): List<TimelineEvent> {
|
private fun getPinnedEvents(): List<TimelineEvent> {
|
||||||
val idsOfPinnedEvents = getIdsOfPinnedEvents()
|
return getIdsOfPinnedEvents()
|
||||||
val pinnedEvents = ArrayList<TimelineEvent>()
|
.mapNotNull { id ->
|
||||||
for (id in idsOfPinnedEvents) {
|
timelineEventDataSource.getTimelineEvent(roomId, id)
|
||||||
val timelineEvent = timelineEventDataSource.getTimelineEvent(roomId, id)
|
|
||||||
if (timelineEvent != null) {
|
|
||||||
pinnedEvents.add(timelineEvent)
|
|
||||||
}
|
}
|
||||||
}
|
.reversed()
|
||||||
return pinnedEvents.reversed()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun areAllPinnedMessagesLoaded(): Boolean {
|
private fun areAllPinnedEventsLoaded(): Boolean {
|
||||||
return getIdsOfPinnedEvents().size == getPinnedEvents().size
|
return getIdsOfPinnedEvents().size == getPinnedEvents().size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
<!-- Level 1: Labs -->
|
<!-- Level 1: Labs -->
|
||||||
<bool name="settings_labs_deferred_dm_visible">true</bool>
|
<bool name="settings_labs_deferred_dm_visible">true</bool>
|
||||||
<bool name="settings_labs_deferred_dm_default">true</bool>
|
<bool name="settings_labs_deferred_dm_default">true</bool>
|
||||||
<bool name="settings_labs_pinned_messages_default">false</bool>
|
<bool name="settings_labs_pinned_events_default">false</bool>
|
||||||
<bool name="settings_labs_thread_messages_default">false</bool>
|
<bool name="settings_labs_thread_messages_default">false</bool>
|
||||||
<bool name="settings_labs_new_app_layout_default">true</bool>
|
<bool name="settings_labs_new_app_layout_default">true</bool>
|
||||||
<bool name="settings_labs_new_session_manager_default">false</bool>
|
<bool name="settings_labs_new_session_manager_default">false</bool>
|
||||||
|
|
|
@ -149,7 +149,7 @@
|
||||||
<activity android:name=".features.roomdirectory.roompreview.RoomPreviewActivity" />
|
<activity android:name=".features.roomdirectory.roompreview.RoomPreviewActivity" />
|
||||||
<activity android:name=".features.home.room.filtered.FilteredRoomsActivity" />
|
<activity android:name=".features.home.room.filtered.FilteredRoomsActivity" />
|
||||||
<activity android:name=".features.home.room.threads.ThreadsActivity" />
|
<activity android:name=".features.home.room.threads.ThreadsActivity" />
|
||||||
<activity android:name=".features.home.room.pinnedmessages.PinnedMessagesActivity" />
|
<activity android:name=".features.home.room.pinnedmessages.PinnedEventsActivity" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".features.home.room.detail.RoomDetailActivity"
|
android:name=".features.home.room.detail.RoomDetailActivity"
|
||||||
|
|
|
@ -30,8 +30,8 @@ import org.matrix.android.sdk.api.session.widgets.model.Widget
|
||||||
import org.matrix.android.sdk.api.util.MatrixItem
|
import org.matrix.android.sdk.api.util.MatrixItem
|
||||||
|
|
||||||
sealed class RoomDetailAction : VectorViewModelAction {
|
sealed class RoomDetailAction : VectorViewModelAction {
|
||||||
data class PinMessage(val eventId: String) : RoomDetailAction()
|
data class PinEvent(val eventId: String) : RoomDetailAction()
|
||||||
data class UnpinMessage(val eventId: String) : RoomDetailAction()
|
data class UnpinEvent(val eventId: String) : RoomDetailAction()
|
||||||
data class SendSticker(val stickerContent: MessageStickerContent) : RoomDetailAction()
|
data class SendSticker(val stickerContent: MessageStickerContent) : RoomDetailAction()
|
||||||
data class SendMedia(val attachments: List<ContentAttachmentData>, val compressBeforeSending: Boolean) : RoomDetailAction()
|
data class SendMedia(val attachments: List<ContentAttachmentData>, val compressBeforeSending: Boolean) : RoomDetailAction()
|
||||||
data class TimelineEventTurnsVisible(val event: TimelineEvent) : RoomDetailAction()
|
data class TimelineEventTurnsVisible(val event: TimelineEvent) : RoomDetailAction()
|
||||||
|
|
|
@ -71,7 +71,6 @@ data class RoomDetailViewState(
|
||||||
val isAllowedToManageWidgets: Boolean = false,
|
val isAllowedToManageWidgets: Boolean = false,
|
||||||
val isAllowedToStartWebRTCCall: Boolean = true,
|
val isAllowedToStartWebRTCCall: Boolean = true,
|
||||||
val isAllowedToSetupEncryption: Boolean = true,
|
val isAllowedToSetupEncryption: Boolean = true,
|
||||||
val rootPinnedMessageEventId: String?,
|
|
||||||
val hasFailedSending: Boolean = false,
|
val hasFailedSending: Boolean = false,
|
||||||
val jitsiState: JitsiState = JitsiState(),
|
val jitsiState: JitsiState = JitsiState(),
|
||||||
val switchToParentSpace: Boolean = false,
|
val switchToParentSpace: Boolean = false,
|
||||||
|
@ -81,6 +80,7 @@ data class RoomDetailViewState(
|
||||||
val isSharingLiveLocation: Boolean = false,
|
val isSharingLiveLocation: Boolean = false,
|
||||||
val showKeyboardWhenPresented: Boolean = false,
|
val showKeyboardWhenPresented: Boolean = false,
|
||||||
val sharedData: SharedData? = null,
|
val sharedData: SharedData? = null,
|
||||||
|
val isFromPinnedEventsTimeline: Boolean = false,
|
||||||
) : MavericksState {
|
) : MavericksState {
|
||||||
|
|
||||||
constructor(args: TimelineArgs) : this(
|
constructor(args: TimelineArgs) : this(
|
||||||
|
@ -93,7 +93,7 @@ data class RoomDetailViewState(
|
||||||
rootThreadEventId = args.threadTimelineArgs?.rootThreadEventId,
|
rootThreadEventId = args.threadTimelineArgs?.rootThreadEventId,
|
||||||
showKeyboardWhenPresented = args.threadTimelineArgs?.showKeyboard.orFalse(),
|
showKeyboardWhenPresented = args.threadTimelineArgs?.showKeyboard.orFalse(),
|
||||||
sharedData = args.sharedData,
|
sharedData = args.sharedData,
|
||||||
rootPinnedMessageEventId = args.pinnedMessagesTimelineArgs?.rootPinnedMessageEventId,
|
isFromPinnedEventsTimeline = args.pinnedEventsTimelineArgs != null,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun isCallOptionAvailable(): Boolean {
|
fun isCallOptionAvailable(): Boolean {
|
||||||
|
@ -115,7 +115,7 @@ data class RoomDetailViewState(
|
||||||
|
|
||||||
fun isThreadTimeline() = rootThreadEventId != null
|
fun isThreadTimeline() = rootThreadEventId != null
|
||||||
|
|
||||||
fun isPinnedMessagesTimeline() = rootPinnedMessageEventId != null
|
fun isPinnedEventsTimeline() = isFromPinnedEventsTimeline
|
||||||
|
|
||||||
fun isLocalRoom() = RoomLocalEcho.isLocalEchoId(roomId)
|
fun isLocalRoom() = RoomLocalEcho.isLocalEchoId(roomId)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.upgrade.MigrateRoomBottomSheet
|
||||||
import im.vector.app.features.home.room.detail.views.RoomDetailLazyLoadedViews
|
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.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.ThreadsManager
|
||||||
import im.vector.app.features.home.room.threads.arguments.ThreadTimelineArgs
|
import im.vector.app.features.home.room.threads.arguments.ThreadTimelineArgs
|
||||||
import im.vector.app.features.html.EventHtmlRenderer
|
import im.vector.app.features.html.EventHtmlRenderer
|
||||||
|
@ -379,9 +379,8 @@ class TimelineFragment :
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPinnedMessagesTimeline()) {
|
if (isPinnedEventsTimeline()) {
|
||||||
views.composerContainer.isVisible = false
|
views.hideComposerViews()
|
||||||
views.voiceMessageRecorderContainer.isVisible = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
timelineViewModel.observeViewEvents {
|
timelineViewModel.observeViewEvents {
|
||||||
|
@ -883,8 +882,8 @@ class TimelineFragment :
|
||||||
callActionsHandler.onVideoCallClicked()
|
callActionsHandler.onVideoCallClicked()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.open_pinned_messages -> {
|
R.id.open_pinned_events -> {
|
||||||
navigateToPinnedMessages()
|
navigateToPinnedEvents()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.menu_timeline_thread_list -> {
|
R.id.menu_timeline_thread_list -> {
|
||||||
|
@ -1116,7 +1115,7 @@ class TimelineFragment :
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateJumpToReadMarkerViewVisibility() {
|
private fun updateJumpToReadMarkerViewVisibility() {
|
||||||
if (isThreadTimeLine() || isPinnedMessagesTimeline()) return
|
if (isThreadTimeLine() || isPinnedEventsTimeline()) return
|
||||||
viewLifecycleOwner.lifecycleScope.launchWhenResumed {
|
viewLifecycleOwner.lifecycleScope.launchWhenResumed {
|
||||||
val state = timelineViewModel.awaitState()
|
val state = timelineViewModel.awaitState()
|
||||||
val showJumpToUnreadBanner = when (state.unreadState) {
|
val showJumpToUnreadBanner = when (state.unreadState) {
|
||||||
|
@ -1197,6 +1196,9 @@ class TimelineFragment :
|
||||||
vectorBaseActivity.finish()
|
vectorBaseActivity.finish()
|
||||||
}
|
}
|
||||||
updateLiveLocationIndicator(mainState.isSharingLiveLocation)
|
updateLiveLocationIndicator(mainState.isSharingLiveLocation)
|
||||||
|
if (isPinnedEventsTimeline()) {
|
||||||
|
views.hideComposerViews()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleRoomSummaryFailure(asyncRoomSummary: Fail<RoomSummary>) {
|
private fun handleRoomSummaryFailure(asyncRoomSummary: Fail<RoomSummary>) {
|
||||||
|
@ -1245,16 +1247,18 @@ class TimelineFragment :
|
||||||
}
|
}
|
||||||
views.includeThreadToolbar.roomToolbarThreadTitleTextView.text = resources.getText(R.string.thread_timeline_title)
|
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.includeRoomToolbar.roomToolbarContentView.isVisible = false
|
||||||
views.includeThreadToolbar.roomToolbarThreadConstraintLayout.isVisible = true
|
views.includeThreadToolbar.roomToolbarThreadConstraintLayout.isVisible = true
|
||||||
timelineArgs.pinnedMessagesTimelineArgs?.let {
|
views.includeThreadToolbar.roomToolbarThreadTitleTextView.text = resources.getText(R.string.pinned_events_timeline_title)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
views.includeRoomToolbar.roomToolbarContentView.isVisible = true
|
views.includeRoomToolbar.roomToolbarContentView.isVisible = true
|
||||||
|
@ -1564,7 +1568,7 @@ class TimelineFragment :
|
||||||
this.view?.hideKeyboard()
|
this.view?.hideKeyboard()
|
||||||
|
|
||||||
MessageActionsBottomSheet
|
MessageActionsBottomSheet
|
||||||
.newInstance(roomId, informationData, isThreadTimeLine(), isPinnedMessagesTimeline())
|
.newInstance(roomId, informationData, isThreadTimeLine(), isPinnedEventsTimeline())
|
||||||
.show(requireActivity().supportFragmentManager, "MESSAGE_CONTEXTUAL_ACTIONS")
|
.show(requireActivity().supportFragmentManager, "MESSAGE_CONTEXTUAL_ACTIONS")
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
@ -1816,13 +1820,13 @@ class TimelineFragment :
|
||||||
requireActivity().toast(R.string.error_voice_message_cannot_reply_or_edit)
|
requireActivity().toast(R.string.error_voice_message_cannot_reply_or_edit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is EventSharedAction.PinMessage -> {
|
is EventSharedAction.PinEvent -> {
|
||||||
timelineViewModel.handle(RoomDetailAction.PinMessage(action.eventId))
|
timelineViewModel.handle(RoomDetailAction.PinEvent(action.eventId))
|
||||||
}
|
}
|
||||||
is EventSharedAction.UnpinMessage -> {
|
is EventSharedAction.UnpinEvent -> {
|
||||||
timelineViewModel.handle(RoomDetailAction.UnpinMessage(action.eventId))
|
timelineViewModel.handle(RoomDetailAction.UnpinEvent(action.eventId))
|
||||||
}
|
}
|
||||||
is EventSharedAction.ViewPinnedMessageInRoom -> {
|
is EventSharedAction.ViewPinnedEventInRoom -> {
|
||||||
handleViewInRoomAction(action.eventId)
|
handleViewInRoomAction(action.eventId)
|
||||||
}
|
}
|
||||||
is EventSharedAction.ReplyInThread -> {
|
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 ->
|
private fun navigateToPinnedEvents() {
|
||||||
val pinnedEventId = timelineViewModel.getIdOfLastPinnedEvent()
|
|
||||||
context?.let {
|
context?.let {
|
||||||
val pinnedMessagesTimelineArgs = PinnedMessagesTimelineArgs(
|
val pinnedEventsTimelineArgs = PinnedEventsTimelineArgs(
|
||||||
roomId = timelineArgs.roomId,
|
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) {
|
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 ->
|
context?.let { con ->
|
||||||
val intent = RoomDetailActivity.newIntent(con, newRoom, false)
|
val intent = RoomDetailActivity.newIntent(con, newRoom, false)
|
||||||
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
|
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.
|
* 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.
|
* Returns true if the current room is a local room, false otherwise.
|
||||||
|
|
|
@ -204,10 +204,10 @@ class TimelineViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initSafe(room: Room, timeline: Timeline) {
|
private fun initSafe(room: Room, timeline: Timeline) {
|
||||||
timeline.start(initialState.rootThreadEventId, initialState.rootPinnedMessageEventId)
|
timeline.start(initialState.rootThreadEventId, initialState.isFromPinnedEventsTimeline)
|
||||||
timeline.addListener(this)
|
timeline.addListener(this)
|
||||||
observeMembershipChanges()
|
observeMembershipChanges()
|
||||||
if (!initialState.isPinnedMessagesTimeline()) {
|
if (!initialState.isPinnedEventsTimeline()) {
|
||||||
observeSummaryState()
|
observeSummaryState()
|
||||||
}
|
}
|
||||||
getUnreadState()
|
getUnreadState()
|
||||||
|
@ -451,8 +451,8 @@ class TimelineViewModel @AssistedInject constructor(
|
||||||
|
|
||||||
override fun handle(action: RoomDetailAction) {
|
override fun handle(action: RoomDetailAction) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is RoomDetailAction.PinMessage -> handlePinMessage(action)
|
is RoomDetailAction.PinEvent -> handlePinEvent(action)
|
||||||
is RoomDetailAction.UnpinMessage -> handleUnpinMessage(action)
|
is RoomDetailAction.UnpinEvent -> handleUnpinEvent(action)
|
||||||
is RoomDetailAction.ComposerFocusChange -> handleComposerFocusChange(action)
|
is RoomDetailAction.ComposerFocusChange -> handleComposerFocusChange(action)
|
||||||
is RoomDetailAction.SendMedia -> handleSendMedia(action)
|
is RoomDetailAction.SendMedia -> handleSendMedia(action)
|
||||||
is RoomDetailAction.SendSticker -> handleSendSticker(action)
|
is RoomDetailAction.SendSticker -> handleSendSticker(action)
|
||||||
|
@ -762,14 +762,6 @@ class TimelineViewModel @AssistedInject constructor(
|
||||||
return room?.membershipService()?.getRoomMember(userId)
|
return room?.membershipService()?.getRoomMember(userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getIdOfLastPinnedEvent(): String? {
|
|
||||||
return room
|
|
||||||
?.stateService()
|
|
||||||
?.getPinnedEventsState()
|
|
||||||
?.getIdsOfPinnedEvents()
|
|
||||||
?.last()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleComposerFocusChange(action: RoomDetailAction.ComposerFocusChange) {
|
private fun handleComposerFocusChange(action: RoomDetailAction.ComposerFocusChange) {
|
||||||
if (room == null) return
|
if (room == null) return
|
||||||
// Ensure outbound session keys
|
// Ensure outbound session keys
|
||||||
|
@ -840,7 +832,7 @@ class TimelineViewModel @AssistedInject constructor(
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
initialState.isPinnedMessagesTimeline() -> false
|
initialState.isPinnedEventsTimeline() -> false
|
||||||
else -> {
|
else -> {
|
||||||
when (itemId) {
|
when (itemId) {
|
||||||
R.id.timeline_setting -> true
|
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. ^
|
// 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.join_conference -> !state.isCallOptionAvailable() && state.jitsiState.confId != null && !state.jitsiState.hasJoined
|
||||||
R.id.search -> state.isSearchAvailable()
|
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.menu_timeline_thread_list -> vectorPreferences.areThreadMessagesEnabled()
|
||||||
R.id.dev_tools -> vectorPreferences.developerMode()
|
R.id.dev_tools -> vectorPreferences.developerMode()
|
||||||
else -> false
|
else -> false
|
||||||
|
@ -1038,40 +1030,12 @@ class TimelineViewModel @AssistedInject constructor(
|
||||||
_viewEvents.post(RoomDetailViewEvents.NavigateToEvent(targetEventId))
|
_viewEvents.post(RoomDetailViewEvents.NavigateToEvent(targetEventId))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handlePinMessage(action: RoomDetailAction.PinMessage) {
|
private fun handlePinEvent(action: RoomDetailAction.PinEvent) {
|
||||||
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<String>? {
|
|
||||||
return room
|
|
||||||
?.stateService()
|
|
||||||
?.getPinnedEventsState()
|
|
||||||
?.getIdsOfPinnedEvents()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun areTherePinnedMessages(): Boolean {
|
|
||||||
val idsOfPinnedMessages = getIdsOfPinnedEvents() ?: return false
|
|
||||||
return idsOfPinnedMessages.isNotEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun sendPinnedStateEvent(eventIds: MutableList<String>, action: RoomDetailAction) {
|
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
room
|
room
|
||||||
?.stateService()
|
?.stateService()
|
||||||
?.pinMessage(eventIds)
|
?.pinEvent(action.eventId)
|
||||||
_viewEvents.post(RoomDetailViewEvents.ActionSuccess(action))
|
_viewEvents.post(RoomDetailViewEvents.ActionSuccess(action))
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
_viewEvents.post(RoomDetailViewEvents.ActionFailure(action, failure))
|
_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<String>? {
|
||||||
|
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) {
|
private fun handleResendEvent(action: RoomDetailAction.ResendMessage) {
|
||||||
if (room == null) return
|
if (room == null) return
|
||||||
val targetEventId = action.eventId
|
val targetEventId = action.eventId
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
package im.vector.app.features.home.room.detail.arguments
|
package im.vector.app.features.home.room.detail.arguments
|
||||||
|
|
||||||
import android.os.Parcelable
|
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.home.room.threads.arguments.ThreadTimelineArgs
|
||||||
import im.vector.app.features.share.SharedData
|
import im.vector.app.features.share.SharedData
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
@ -29,7 +29,7 @@ data class TimelineArgs(
|
||||||
val sharedData: SharedData? = null,
|
val sharedData: SharedData? = null,
|
||||||
val openShareSpaceForId: String? = null,
|
val openShareSpaceForId: String? = null,
|
||||||
val threadTimelineArgs: ThreadTimelineArgs? = null,
|
val threadTimelineArgs: ThreadTimelineArgs? = null,
|
||||||
val pinnedMessagesTimelineArgs: PinnedMessagesTimelineArgs? = null,
|
val pinnedEventsTimelineArgs: PinnedEventsTimelineArgs? = null,
|
||||||
val switchToParentSpace: Boolean = false,
|
val switchToParentSpace: Boolean = false,
|
||||||
val isInviteAlreadyAccepted: Boolean = false
|
val isInviteAlreadyAccepted: Boolean = false
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
|
@ -53,13 +53,13 @@ sealed class EventSharedAction(
|
||||||
data class ReplyInThread(val eventId: String, val startsThread: Boolean) :
|
data class ReplyInThread(val eventId: String, val startsThread: Boolean) :
|
||||||
EventSharedAction(R.string.reply_in_thread, R.drawable.ic_reply_in_thread)
|
EventSharedAction(R.string.reply_in_thread, R.drawable.ic_reply_in_thread)
|
||||||
|
|
||||||
data class PinMessage(val eventId: String) :
|
data class PinEvent(val eventId: String) :
|
||||||
EventSharedAction(R.string.pinning_message, R.drawable.ic_pin_message)
|
EventSharedAction(R.string.pinning_event, R.drawable.ic_pin_event)
|
||||||
|
|
||||||
data class UnpinMessage(val eventId: String) :
|
data class UnpinEvent(val eventId: String) :
|
||||||
EventSharedAction(R.string.unpinning_message, R.drawable.ic_unpin_message)
|
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)
|
EventSharedAction(R.string.view_in_room, R.drawable.ic_threads_view_in_room_24)
|
||||||
|
|
||||||
object ViewInRoom :
|
object ViewInRoom :
|
||||||
|
|
|
@ -36,7 +36,7 @@ data class ActionPermissions(
|
||||||
val canSendMessage: Boolean = false,
|
val canSendMessage: Boolean = false,
|
||||||
val canReact: Boolean = false,
|
val canReact: Boolean = false,
|
||||||
val canRedact: Boolean = false,
|
val canRedact: Boolean = false,
|
||||||
val canPinMessage: Boolean = false
|
val canPinEvent: Boolean = false
|
||||||
)
|
)
|
||||||
|
|
||||||
data class MessageActionState(
|
data class MessageActionState(
|
||||||
|
@ -52,7 +52,7 @@ data class MessageActionState(
|
||||||
val expendedReportContentMenu: Boolean = false,
|
val expendedReportContentMenu: Boolean = false,
|
||||||
val actionPermissions: ActionPermissions = ActionPermissions(),
|
val actionPermissions: ActionPermissions = ActionPermissions(),
|
||||||
val isFromThreadTimeline: Boolean = false,
|
val isFromThreadTimeline: Boolean = false,
|
||||||
val isFromPinnedMessagesTimeline: Boolean = false
|
val isFromPinnedEventsTimeline: Boolean = false
|
||||||
) : MavericksState {
|
) : MavericksState {
|
||||||
|
|
||||||
constructor(args: TimelineEventFragmentArgs) : this(
|
constructor(args: TimelineEventFragmentArgs) : this(
|
||||||
|
@ -60,7 +60,7 @@ data class MessageActionState(
|
||||||
eventId = args.eventId,
|
eventId = args.eventId,
|
||||||
informationData = args.informationData,
|
informationData = args.informationData,
|
||||||
isFromThreadTimeline = args.isFromThreadTimeline,
|
isFromThreadTimeline = args.isFromThreadTimeline,
|
||||||
isFromPinnedMessagesTimeline = args.isFromPinnedMessagesTimeline
|
isFromPinnedEventsTimeline = args.isFromPinnedEventsTimeline
|
||||||
)
|
)
|
||||||
|
|
||||||
fun senderName(): String = informationData.memberName?.toString() ?: ""
|
fun senderName(): String = informationData.memberName?.toString() ?: ""
|
||||||
|
|
|
@ -93,7 +93,7 @@ class MessageActionsBottomSheet :
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
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 {
|
return MessageActionsBottomSheet().apply {
|
||||||
setArguments(
|
setArguments(
|
||||||
TimelineEventFragmentArgs(
|
TimelineEventFragmentArgs(
|
||||||
|
@ -101,7 +101,7 @@ class MessageActionsBottomSheet :
|
||||||
roomId,
|
roomId,
|
||||||
informationData,
|
informationData,
|
||||||
isFromThreadTimeline,
|
isFromThreadTimeline,
|
||||||
isFromPinnedMessagesTimeline
|
isFromPinnedEventsTimeline
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,9 +42,11 @@ import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
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.Session
|
||||||
import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState
|
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.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.isAttachmentMessage
|
||||||
import org.matrix.android.sdk.api.session.events.model.isContentReportable
|
import org.matrix.android.sdk.api.session.events.model.isContentReportable
|
||||||
import org.matrix.android.sdk.api.session.events.model.isTextMessage
|
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 canReact = powerLevelsHelper.isUserAllowedToSend(session.myUserId, false, EventType.REACTION)
|
||||||
val canRedact = powerLevelsHelper.isUserAbleToRedact(session.myUserId)
|
val canRedact = powerLevelsHelper.isUserAbleToRedact(session.myUserId)
|
||||||
val canSendMessage = powerLevelsHelper.isUserAllowedToSend(session.myUserId, false, EventType.MESSAGE)
|
val canSendMessage = powerLevelsHelper.isUserAllowedToSend(session.myUserId, false, EventType.MESSAGE)
|
||||||
val canPinMessage = powerLevelsHelper.isUserAllowedToSend(session.myUserId, false, EventType.STATE_ROOM_PINNED_EVENT)
|
val canPinEvent = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_PINNED_EVENT)
|
||||||
val permissions = ActionPermissions(canSendMessage = canSendMessage, canRedact = canRedact, canReact = canReact, canPinMessage = canPinMessage)
|
val permissions = ActionPermissions(canSendMessage = canSendMessage, canRedact = canRedact, canReact = canReact, canPinEvent = canPinEvent)
|
||||||
setState {
|
setState {
|
||||||
copy(actionPermissions = permissions)
|
copy(actionPermissions = permissions)
|
||||||
}
|
}
|
||||||
|
@ -334,13 +336,12 @@ class MessageActionsViewModel @AssistedInject constructor(
|
||||||
) {
|
) {
|
||||||
val eventId = timelineEvent.eventId
|
val eventId = timelineEvent.eventId
|
||||||
if (!timelineEvent.root.isRedacted()) {
|
if (!timelineEvent.root.isRedacted()) {
|
||||||
if (initialState.isFromPinnedMessagesTimeline) {
|
if (initialState.isFromPinnedEventsTimeline && vectorPreferences.arePinnedEventsEnabled()) {
|
||||||
if (actionPermissions.canPinMessage && vectorPreferences.arePinnedMessagesEnabled()) {
|
add(EventSharedAction.ViewPinnedEventInRoom(eventId))
|
||||||
add(EventSharedAction.UnpinMessage(eventId))
|
if (actionPermissions.canPinEvent) {
|
||||||
add(EventSharedAction.ViewPinnedMessageInRoom(eventId))
|
add(EventSharedAction.UnpinEvent(eventId))
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
if (canReply(timelineEvent, messageContent, actionPermissions)) {
|
if (canReply(timelineEvent, messageContent, actionPermissions)) {
|
||||||
add(EventSharedAction.Reply(eventId))
|
add(EventSharedAction.Reply(eventId))
|
||||||
}
|
}
|
||||||
|
@ -370,13 +371,17 @@ class MessageActionsViewModel @AssistedInject constructor(
|
||||||
add(EventSharedAction.AddReaction(eventId))
|
add(EventSharedAction.AddReaction(eventId))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (actionPermissions.canPinMessage && vectorPreferences.arePinnedMessagesEnabled()) {
|
if (actionPermissions.canPinEvent && vectorPreferences.arePinnedEventsEnabled()) {
|
||||||
val id: String = timelineEvent.root.eventId ?: return
|
val isPinned = room
|
||||||
val isPinned: Boolean = room?.stateService()?.isPinned(id) ?: return
|
?.stateService()
|
||||||
|
?.getStateEvent(EventType.STATE_ROOM_PINNED_EVENT, QueryStringValue.Equals(""))
|
||||||
|
?.getIdsOfPinnedEvents()
|
||||||
|
?.contains(eventId)
|
||||||
|
.orFalse()
|
||||||
if (isPinned) {
|
if (isPinned) {
|
||||||
add(EventSharedAction.UnpinMessage(eventId))
|
add(EventSharedAction.UnpinEvent(eventId))
|
||||||
} else {
|
} else {
|
||||||
add(EventSharedAction.PinMessage(eventId))
|
add(EventSharedAction.PinEvent(eventId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -422,6 +427,7 @@ class MessageActionsViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (vectorPreferences.developerMode()) {
|
if (vectorPreferences.developerMode()) {
|
||||||
if (timelineEvent.isEncrypted() && timelineEvent.root.mCryptoError != null) {
|
if (timelineEvent.isEncrypted() && timelineEvent.root.mCryptoError != null) {
|
||||||
|
|
|
@ -26,5 +26,5 @@ data class TimelineEventFragmentArgs(
|
||||||
val roomId: String,
|
val roomId: String,
|
||||||
val informationData: MessageInformationData,
|
val informationData: MessageInformationData,
|
||||||
val isFromThreadTimeline: Boolean = false,
|
val isFromThreadTimeline: Boolean = false,
|
||||||
val isFromPinnedMessagesTimeline: Boolean = false
|
val isFromPinnedEventsTimeline: Boolean = false
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
|
@ -122,16 +122,24 @@ class NoticeEventFormatter @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun formatPinnedEvent(event: Event, disambiguatedDisplayName: String): CharSequence? {
|
private fun formatPinnedEvent(event: Event, disambiguatedDisplayName: String): CharSequence? {
|
||||||
val idsOfPinnedEvents: MutableList<String> = event.getIdsOfPinnedEvents() ?: return null
|
val idsOfPinnedEvents: List<String> = event.getIdsOfPinnedEvents() ?: return null
|
||||||
val previousIdsOfPinnedEvents: MutableList<String>? = event.getPreviousIdsOfPinnedEvents()
|
val previousIdsOfPinnedEvents: List<String>? = event.getPreviousIdsOfPinnedEvents()
|
||||||
// A message was pinned
|
// An event was pinned
|
||||||
val pinnedMessageString = if (event.resolvedPrevContent() == null || previousIdsOfPinnedEvents != null && previousIdsOfPinnedEvents.size < idsOfPinnedEvents.size) {
|
val pinnedEventString = if (event.resolvedPrevContent() == null || previousIdsOfPinnedEvents != null && previousIdsOfPinnedEvents.size < idsOfPinnedEvents.size) {
|
||||||
sp.getString(R.string.user_pinned_message, disambiguatedDisplayName)
|
if (event.isSentByCurrentUser()) {
|
||||||
// A message was unpinned
|
sp.getString(R.string.notice_user_pinned_event_by_you, disambiguatedDisplayName)
|
||||||
} else {
|
} else {
|
||||||
sp.getString(R.string.user_unpinned_message, disambiguatedDisplayName)
|
sp.getString(R.string.notice_user_pinned_event, disambiguatedDisplayName)
|
||||||
}
|
}
|
||||||
return pinnedMessageString
|
// An event was unpinned
|
||||||
|
} else {
|
||||||
|
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 pinnedEventString
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun formatRoomPowerLevels(event: Event, disambiguatedDisplayName: String): CharSequence? {
|
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? {
|
fun format(event: Event, senderName: String?, isDm: Boolean): CharSequence? {
|
||||||
Timber.v("°°°°°°°°°°°°°°°°°°°format(event: Event, senderName: String?, isDm: Boolean)")
|
|
||||||
return when (val type = event.getClearType()) {
|
return when (val type = event.getClearType()) {
|
||||||
EventType.STATE_ROOM_JOIN_RULES -> formatJoinRulesEvent(event, senderName, isDm)
|
EventType.STATE_ROOM_JOIN_RULES -> formatJoinRulesEvent(event, senderName, isDm)
|
||||||
EventType.STATE_ROOM_NAME -> formatRoomNameEvent(event, senderName)
|
EventType.STATE_ROOM_NAME -> formatRoomNameEvent(event, senderName)
|
||||||
|
@ -889,7 +896,6 @@ class NoticeEventFormatter @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun formatRedactedEvent(event: Event): String {
|
fun formatRedactedEvent(event: Event): String {
|
||||||
Timber.v("°°°°°°°formatRedactedEvent°°°°°°")
|
|
||||||
return (event
|
return (event
|
||||||
.unsignedData
|
.unsignedData
|
||||||
?.redactedEvent
|
?.redactedEvent
|
||||||
|
|
|
@ -113,7 +113,7 @@ class MergedTimelines(
|
||||||
secondaryTimeline.removeAllListeners()
|
secondaryTimeline.removeAllListeners()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun start(rootThreadEventId: String?, rootPinnedMessageEventId: String?) {
|
override fun start(rootThreadEventId: String?, isFromPinnedEventsTimeline: Boolean) {
|
||||||
mainTimeline.start()
|
mainTimeline.start()
|
||||||
secondaryTimeline.start()
|
secondaryTimeline.start()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<ActivityPinnedEventsBinding>() {
|
||||||
|
|
||||||
|
@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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,13 +18,8 @@ package im.vector.app.features.home.room.pinnedmessages.arguments
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
|
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class PinnedMessagesTimelineArgs(
|
data class PinnedEventsTimelineArgs(
|
||||||
val roomId: String,
|
val roomId: String
|
||||||
val displayName: String?,
|
|
||||||
val avatarUrl: String?,
|
|
||||||
val roomEncryptionTrustLevel: RoomEncryptionTrustLevel?,
|
|
||||||
val rootPinnedMessageEventId: String?
|
|
||||||
) : Parcelable
|
) : Parcelable
|
|
@ -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<ActivityPinnedMessagesBinding>() {
|
|
||||||
|
|
||||||
@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()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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.SearchActivity
|
||||||
import im.vector.app.features.home.room.detail.search.SearchArgs
|
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.filtered.FilteredRoomsActivity
|
||||||
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.pinnedmessages.PinnedMessagesActivity
|
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.ThreadsActivity
|
||||||
import im.vector.app.features.home.room.threads.arguments.ThreadListArgs
|
import im.vector.app.features.home.room.threads.arguments.ThreadListArgs
|
||||||
import im.vector.app.features.home.room.threads.arguments.ThreadTimelineArgs
|
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(
|
context.startActivity(
|
||||||
PinnedMessagesActivity.newIntent(
|
PinnedEventsActivity.newIntent(
|
||||||
context = context,
|
context = context,
|
||||||
pinnedMessagesTimelineArgs = pinnedMessagesTimelineArgs
|
pinnedEventsTimelineArgs = pinnedEventsTimelineArgs
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ import androidx.fragment.app.FragmentActivity
|
||||||
import im.vector.app.features.analytics.plan.ViewRoom
|
import im.vector.app.features.analytics.plan.ViewRoom
|
||||||
import im.vector.app.features.crypto.recover.SetupMode
|
import im.vector.app.features.crypto.recover.SetupMode
|
||||||
import im.vector.app.features.displayname.getBestName
|
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.home.room.threads.arguments.ThreadTimelineArgs
|
||||||
import im.vector.app.features.location.LocationData
|
import im.vector.app.features.location.LocationData
|
||||||
import im.vector.app.features.location.LocationSharingMode
|
import im.vector.app.features.location.LocationSharingMode
|
||||||
|
@ -199,7 +199,7 @@ interface Navigator {
|
||||||
|
|
||||||
fun openThreadList(context: Context, threadTimelineArgs: ThreadTimelineArgs)
|
fun openThreadList(context: Context, threadTimelineArgs: ThreadTimelineArgs)
|
||||||
|
|
||||||
fun openPinnedMessages(context: Context, pinnedMessagesTimelineArgs: PinnedMessagesTimelineArgs)
|
fun openPinnedEvents(context: Context, pinnedEventsTimelineArgs: PinnedEventsTimelineArgs)
|
||||||
|
|
||||||
fun openScreenSharingPermissionDialog(
|
fun openScreenSharingPermissionDialog(
|
||||||
screenCaptureIntent: Intent,
|
screenCaptureIntent: Intent,
|
||||||
|
|
|
@ -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_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
|
// 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"
|
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)
|
return defaultPrefs.getBoolean(SETTINGS_LABS_ENABLE_ELEMENT_CALL_PERMISSION_SHORTCUTS, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun arePinnedMessagesEnabled(): Boolean {
|
fun arePinnedEventsEnabled(): Boolean {
|
||||||
return defaultPrefs.getBoolean(SETTINGS_LABS_ENABLE_PINNED_MESSAGES, getDefault(R.bool.settings_labs_pinned_messages_default))
|
return defaultPrefs.getBoolean(SETTINGS_LABS_ENABLE_PINNED_EVENTS, getDefault(R.bool.settings_labs_pinned_events_default))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
<vector android:height="30dp" android:viewportHeight="24"
|
||||||
|
android:viewportWidth="24" android:width="30dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="#212121" android:pathData="m16.242,2.932 l4.826,4.826a2.75,2.75 0,0 1,-0.715 4.404l-4.87,2.435a0.75,0.75 0,0 0,-0.374 0.426l-1.44,4.166a1.25,1.25 0,0 1,-2.065 0.476L8.5,16.561 4.06,21L3,21v-1.06l4.44,-4.44 -3.105,-3.104a1.25,1.25 0,0 1,0.476 -2.066l4.166,-1.44a0.75,0.75 0,0 0,0.426 -0.373l2.435,-4.87a2.75,2.75 0,0 1,4.405 -0.715ZM20.008,8.818 L15.182,3.992a1.25,1.25 0,0 0,-2.002 0.325l-2.435,4.871a2.25,2.25 0,0 1,-1.278 1.12l-3.789,1.31 6.705,6.704 1.308,-3.789a2.25,2.25 0,0 1,1.12 -1.277l4.872,-2.436a1.25,1.25 0,0 0,0.325 -2.002Z"/>
|
||||||
|
</vector>
|
|
@ -1,9 +0,0 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24">
|
|
||||||
<path
|
|
||||||
android:pathData="m16.242,2.932 l4.826,4.826a2.75,2.75 0,0 1,-0.715 4.404l-4.87,2.435a0.75,0.75 0,0 0,-0.374 0.426l-1.44,4.166a1.25,1.25 0,0 1,-2.065 0.476L8.5,16.561 4.06,21L3,21v-1.06l4.44,-4.44 -3.105,-3.104a1.25,1.25 0,0 1,0.476 -2.066l4.166,-1.44a0.75,0.75 0,0 0,0.426 -0.373l2.435,-4.87a2.75,2.75 0,0 1,4.405 -0.715ZM20.008,8.818 L15.182,3.992a1.25,1.25 0,0 0,-2.002 0.325l-2.435,4.871a2.25,2.25 0,0 1,-1.278 1.12l-3.789,1.31 6.705,6.704 1.308,-3.789a2.25,2.25 0,0 1,1.12 -1.277l4.872,-2.436a1.25,1.25 0,0 0,0.325 -2.002Z"
|
|
||||||
android:fillColor="#aeb5bc"/>
|
|
||||||
</vector>
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
<vector android:height="30dp" android:viewportHeight="24"
|
||||||
|
android:viewportWidth="24" android:width="30dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="#212121" android:pathData="M3.28,2.22a0.75,0.75 0,0 0,-1.06 1.06l5.905,5.905L4.81,10.33a1.25,1.25 0,0 0,-0.476 2.065L7.439,15.5 3,19.94L3,21h1.06l4.44,-4.44 3.105,3.105a1.25,1.25 0,0 0,2.065 -0.476l1.145,-3.313 5.905,5.904a0.75,0.75 0,0 0,1.06 -1.06L3.28,2.22ZM13.635,14.696 L12.383,18.322 5.678,11.617 9.304,10.365 13.635,14.696ZM19.683,10.82 L15.896,12.714 17.014,13.832 20.354,12.162a2.75,2.75 0,0 0,0.714 -4.404l-4.825,-4.826a2.75,2.75 0,0 0,-4.405 0.715l-1.67,3.34 1.118,1.117 1.894,-3.787a1.25,1.25 0,0 1,2.002 -0.325l4.826,4.826a1.25,1.25 0,0 1,-0.325 2.002Z"/>
|
||||||
|
</vector>
|
|
@ -1,9 +0,0 @@
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24">
|
|
||||||
<path
|
|
||||||
android:pathData="M3.28,2.22a0.75,0.75 0,0 0,-1.06 1.06l5.905,5.905L4.81,10.33a1.25,1.25 0,0 0,-0.476 2.065L7.439,15.5 3,19.94L3,21h1.06l4.44,-4.44 3.105,3.105a1.25,1.25 0,0 0,2.065 -0.476l1.145,-3.313 5.905,5.904a0.75,0.75 0,0 0,1.06 -1.06L3.28,2.22ZM13.635,14.696 L12.383,18.322 5.678,11.617 9.304,10.365 13.635,14.696ZM19.683,10.82 L15.896,12.714 17.014,13.832 20.354,12.162a2.75,2.75 0,0 0,0.714 -4.404l-4.825,-4.826a2.75,2.75 0,0 0,-4.405 0.715l-1.67,3.34 1.118,1.117 1.894,-3.787a1.25,1.25 0,0 1,2.002 -0.325l4.826,4.826a1.25,1.25 0,0 1,-0.325 2.002Z"
|
|
||||||
android:fillColor="#aeb5bc"/>
|
|
||||||
</vector>
|
|
|
@ -11,7 +11,7 @@
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<androidx.fragment.app.FragmentContainerView
|
<androidx.fragment.app.FragmentContainerView
|
||||||
android:id="@+id/pinnedMessagesActivityFragmentContainer"
|
android:id="@+id/pinnedEventsActivityFragmentContainer"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
|
@ -42,9 +42,9 @@
|
||||||
|
|
||||||
<!-- We always want to show this item as an icon -->
|
<!-- We always want to show this item as an icon -->
|
||||||
<item
|
<item
|
||||||
android:id="@+id/open_pinned_messages"
|
android:id="@+id/open_pinned_events"
|
||||||
android:icon="@drawable/ic_open_pinned_messages"
|
android:icon="@drawable/ic_open_pinned_events"
|
||||||
android:title="@string/action_open_pinned_messages"
|
android:title="@string/action_open_pinned_events"
|
||||||
android:visible="true"
|
android:visible="true"
|
||||||
app:iconTint="?colorPrimary"
|
app:iconTint="?colorPrimary"
|
||||||
app:showAsAction="always"
|
app:showAsAction="always"
|
||||||
|
|
|
@ -60,9 +60,9 @@
|
||||||
<!--</im.vector.app.core.preference.VectorPreferenceCategory>-->
|
<!--</im.vector.app.core.preference.VectorPreferenceCategory>-->
|
||||||
|
|
||||||
<im.vector.app.core.preference.VectorSwitchPreference
|
<im.vector.app.core.preference.VectorSwitchPreference
|
||||||
android:defaultValue="@bool/settings_labs_pinned_messages_default"
|
android:defaultValue="@bool/settings_labs_pinned_events_default"
|
||||||
android:key="SETTINGS_LABS_ENABLE_PINNED_MESSAGES"
|
android:key="SETTINGS_LABS_ENABLE_PINNED_EVENTS"
|
||||||
android:title="@string/labs_enable_pinned_messages" />
|
android:title="@string/labs_enable_pinned_events" />
|
||||||
|
|
||||||
<im.vector.app.core.preference.VectorSwitchPreference
|
<im.vector.app.core.preference.VectorSwitchPreference
|
||||||
android:defaultValue="@bool/settings_labs_thread_messages_default"
|
android:defaultValue="@bool/settings_labs_thread_messages_default"
|
||||||
|
|
Loading…
Reference in New Issue