diff --git a/CHANGES.md b/CHANGES.md index 42fb2cc291..dd7f47de03 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,7 @@ Improvements: - Persist active tab between sessions (#503) - Do not upload file too big for the homeserver (#587) - Handle read markers (#84) + - Mark all messages as read (#396) Other changes: - Accessibility improvements to read receipts in the room timeline and reactions emoji chooser diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/RoomService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/RoomService.kt index 175d393c86..c7fedb2627 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/RoomService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/RoomService.kt @@ -53,4 +53,9 @@ interface RoomService { * @return the [LiveData] of [RoomSummary] */ fun liveRoomSummaries(): LiveData> + + /** + * Mark all rooms as read + */ + fun markAllAsRead(roomIds: List, callback: MatrixCallback): Cancelable } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt index c64676c8c2..962b7b54d6 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt @@ -32,6 +32,7 @@ import im.vector.matrix.android.internal.database.model.RoomSummaryEntityFields import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.session.room.create.CreateRoomTask import im.vector.matrix.android.internal.session.room.membership.joining.JoinRoomTask +import im.vector.matrix.android.internal.session.room.read.MarkAllRoomsReadTask import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.configureWith import io.realm.Realm @@ -41,6 +42,7 @@ internal class DefaultRoomService @Inject constructor(private val monarchy: Mona private val roomSummaryMapper: RoomSummaryMapper, private val createRoomTask: CreateRoomTask, private val joinRoomTask: JoinRoomTask, + private val markAllRoomsReadTask: MarkAllRoomsReadTask, private val roomFactory: RoomFactory, private val taskExecutor: TaskExecutor) : RoomService { @@ -80,4 +82,12 @@ internal class DefaultRoomService @Inject constructor(private val monarchy: Mona } .executeBy(taskExecutor) } + + override fun markAllAsRead(roomIds: List, callback: MatrixCallback): Cancelable { + return markAllRoomsReadTask + .configureWith(MarkAllRoomsReadTask.Params(roomIds)) { + this.callback = callback + } + .executeBy(taskExecutor) + } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt index b3db84c9c6..5755d6b46e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt @@ -40,22 +40,14 @@ import im.vector.matrix.android.internal.session.room.membership.leaving.Default import im.vector.matrix.android.internal.session.room.membership.leaving.LeaveRoomTask import im.vector.matrix.android.internal.session.room.prune.DefaultPruneEventTask import im.vector.matrix.android.internal.session.room.prune.PruneEventTask +import im.vector.matrix.android.internal.session.room.read.DefaultMarkAllRoomsReadTask import im.vector.matrix.android.internal.session.room.read.DefaultSetReadMarkersTask +import im.vector.matrix.android.internal.session.room.read.MarkAllRoomsReadTask import im.vector.matrix.android.internal.session.room.read.SetReadMarkersTask -import im.vector.matrix.android.internal.session.room.relation.DefaultFetchEditHistoryTask -import im.vector.matrix.android.internal.session.room.relation.DefaultFindReactionEventForUndoTask -import im.vector.matrix.android.internal.session.room.relation.DefaultUpdateQuickReactionTask -import im.vector.matrix.android.internal.session.room.relation.FetchEditHistoryTask -import im.vector.matrix.android.internal.session.room.relation.FindReactionEventForUndoTask -import im.vector.matrix.android.internal.session.room.relation.UpdateQuickReactionTask +import im.vector.matrix.android.internal.session.room.relation.* import im.vector.matrix.android.internal.session.room.state.DefaultSendStateTask import im.vector.matrix.android.internal.session.room.state.SendStateTask import im.vector.matrix.android.internal.session.room.timeline.* -import im.vector.matrix.android.internal.session.room.timeline.ClearUnlinkedEventsTask -import im.vector.matrix.android.internal.session.room.timeline.DefaultGetContextOfEventTask -import im.vector.matrix.android.internal.session.room.timeline.DefaultPaginationTask -import im.vector.matrix.android.internal.session.room.timeline.GetContextOfEventTask -import im.vector.matrix.android.internal.session.room.timeline.PaginationTask import retrofit2.Retrofit @Module @@ -110,6 +102,9 @@ internal abstract class RoomModule { @Binds abstract fun bindSetReadMarkersTask(setReadMarkersTask: DefaultSetReadMarkersTask): SetReadMarkersTask + @Binds + abstract fun bindMarkAllRoomsReadTask(markAllRoomsReadTask: DefaultMarkAllRoomsReadTask): MarkAllRoomsReadTask + @Binds abstract fun bindFindReactionEventForUndoTask(findReactionEventForUndoTask: DefaultFindReactionEventForUndoTask): FindReactionEventForUndoTask diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/MarkAllRoomsReadTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/MarkAllRoomsReadTask.kt new file mode 100644 index 0000000000..99376a981a --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/MarkAllRoomsReadTask.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2019 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.matrix.android.internal.session.room.read + +import im.vector.matrix.android.internal.task.Task +import javax.inject.Inject + +internal interface MarkAllRoomsReadTask : Task { + data class Params( + val roomIds: List + ) +} + +internal class DefaultMarkAllRoomsReadTask @Inject constructor(private val readMarkersTask: SetReadMarkersTask) : MarkAllRoomsReadTask { + + override suspend fun execute(params: MarkAllRoomsReadTask.Params) { + params.roomIds.forEach { roomId -> + readMarkersTask.execute(SetReadMarkersTask.Params(roomId, markAllAsRead = true)) + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/ReadReceiptHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/ReadReceiptHandler.kt index 4dbcc7168f..62fbd42ed5 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/ReadReceiptHandler.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/ReadReceiptHandler.kt @@ -25,7 +25,7 @@ import io.realm.Realm import timber.log.Timber import javax.inject.Inject -// the receipts dictionnaries +// the receipts dictionaries // key : $EventId // value : dict key $UserId // value dict key ts diff --git a/matrix-sdk-android/src/test/java/im/vector/matrix/android/api/pushrules/PushrulesConditionTest.kt b/matrix-sdk-android/src/test/java/im/vector/matrix/android/api/pushrules/PushrulesConditionTest.kt index 36aded79ad..882e171e4a 100644 --- a/matrix-sdk-android/src/test/java/im/vector/matrix/android/api/pushrules/PushrulesConditionTest.kt +++ b/matrix-sdk-android/src/test/java/im/vector/matrix/android/api/pushrules/PushrulesConditionTest.kt @@ -32,6 +32,7 @@ import im.vector.matrix.android.api.session.room.timeline.Timeline import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineSettings import im.vector.matrix.android.api.util.Cancelable +import im.vector.matrix.android.api.util.Optional import org.junit.Assert import org.junit.Test @@ -172,7 +173,6 @@ class PushrulesConditionTest { } class MockRoomService() : RoomService { - override fun createRoom(createRoomParams: CreateRoomParams, callback: MatrixCallback): Cancelable { TODO("not implemented") // To change body of created functions use File | Settings | File Templates. } @@ -192,9 +192,21 @@ class PushrulesConditionTest { override fun liveRoomSummaries(): LiveData> { return MutableLiveData() } + + override fun markAllAsRead(roomIds: List, callback: MatrixCallback): Cancelable { + TODO("not implemented") // To change body of created functions use File | Settings | File Templates. + } } class MockRoom(override val roomId: String, val _numberOfJoinedMembers: Int) : Room { + override fun getReadMarkerLive(): LiveData> { + TODO("not implemented") // To change body of created functions use File | Settings | File Templates. + } + + override fun getMyReadReceiptLive(): LiveData> { + TODO("not implemented") // To change body of created functions use File | Settings | File Templates. + } + override fun resendTextMessage(localEcho: TimelineEvent): Cancelable? { TODO("not implemented") // To change body of created functions use File | Settings | File Templates. } @@ -242,7 +254,7 @@ class PushrulesConditionTest { override fun fetchEditHistory(eventId: String, callback: MatrixCallback>) { } - override fun liveTimeLineEvent(eventId: String): LiveData { + override fun getTimeLineEventLive(eventId: String): LiveData> { TODO("not implemented") // To change body of created functions use File | Settings | File Templates. } @@ -250,7 +262,7 @@ class PushrulesConditionTest { return _numberOfJoinedMembers } - override fun liveRoomSummary(): LiveData { + override fun getRoomSummaryLive(): LiveData> { TODO("not implemented") // To change body of created functions use File | Settings | File Templates. } @@ -347,7 +359,7 @@ class PushrulesConditionTest { TODO("not implemented") // To change body of created functions use File | Settings | File Templates. } - override fun getEventSummaryLive(eventId: String): LiveData { + override fun getEventSummaryLive(eventId: String): LiveData> { TODO("not implemented") // To change body of created functions use File | Settings | File Templates. } diff --git a/vector/src/debug/java/im/vector/riotx/features/debug/DebugMaterialThemeActivity.kt b/vector/src/debug/java/im/vector/riotx/features/debug/DebugMaterialThemeActivity.kt index ab6b86801a..542b0a1cbb 100644 --- a/vector/src/debug/java/im/vector/riotx/features/debug/DebugMaterialThemeActivity.kt +++ b/vector/src/debug/java/im/vector/riotx/features/debug/DebugMaterialThemeActivity.kt @@ -59,7 +59,7 @@ abstract class DebugMaterialThemeActivity : AppCompatActivity() { } override fun onCreateOptionsMenu(menu: Menu): Boolean { - menuInflater.inflate(R.menu.vector_home, menu) + menuInflater.inflate(R.menu.home, menu) return true } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/riotx/features/home/HomeActivity.kt index 9071b51acf..1e0121a500 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/HomeActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/HomeActivity.kt @@ -182,7 +182,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable { } } - return true + return super.onOptionsItemSelected(item) } override fun onBackPressed() { diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListActions.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListActions.kt index 7302d9d2b8..8271086421 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListActions.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListActions.kt @@ -19,14 +19,10 @@ package im.vector.riotx.features.home.room.list import im.vector.matrix.android.api.session.room.model.RoomSummary sealed class RoomListActions { - data class SelectRoom(val roomSummary: RoomSummary) : RoomListActions() - data class ToggleCategory(val category: RoomCategory) : RoomListActions() - data class AcceptInvitation(val roomSummary: RoomSummary) : RoomListActions() - data class RejectInvitation(val roomSummary: RoomSummary) : RoomListActions() - data class FilterWith(val filter: String) : RoomListActions() + object MarkAllRoomsRead : RoomListActions() } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt index d0957f752b..75bf15efa7 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt @@ -18,6 +18,7 @@ package im.vector.riotx.features.home.room.list import android.os.Bundle import android.os.Parcelable +import android.view.MenuItem import androidx.annotation.StringRes import androidx.core.content.ContextCompat import androidx.core.view.isVisible @@ -78,6 +79,19 @@ class RoomListFragment : VectorBaseFragment(), RoomSummaryController.Listener, O injector.inject(this) } + override fun getMenuRes() = R.menu.room_list + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.menu_home_mark_all_as_read -> { + roomListViewModel.accept(RoomListActions.MarkAllRoomsRead) + return true + } + } + + return super.onOptionsItemSelected(item) + } + override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) setupCreateRoomButton() diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt index 0edf3b72e0..292e5405c4 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListViewModel.kt @@ -78,6 +78,7 @@ class RoomListViewModel @AssistedInject constructor(@Assisted initialState: Room is RoomListActions.AcceptInvitation -> handleAcceptInvitation(action) is RoomListActions.RejectInvitation -> handleRejectInvitation(action) is RoomListActions.FilterWith -> handleFilter(action) + is RoomListActions.MarkAllRoomsRead -> handleMarkAllRoomsRead() } } @@ -193,6 +194,16 @@ class RoomListViewModel @AssistedInject constructor(@Assisted initialState: Room }) } + private fun handleMarkAllRoomsRead() = withState { state -> + state.asyncFilteredRooms.invoke() + ?.flatMap { it.value } + ?.filter { it.membership == Membership.JOIN } + ?.map { it.roomId } + ?.toList() + ?.let { session.markAllAsRead(it, object : MatrixCallback {}) } + } + + private fun buildRoomSummaries(rooms: List): RoomSummaries { val invites = ArrayList() val favourites = ArrayList() diff --git a/vector/src/main/res/menu/room_list.xml b/vector/src/main/res/menu/room_list.xml new file mode 100644 index 0000000000..60ffdcd87b --- /dev/null +++ b/vector/src/main/res/menu/room_list.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/menu/vector_home.xml b/vector/src/main/res/menu/vector_home.xml deleted file mode 100755 index fe24e6fdf5..0000000000 --- a/vector/src/main/res/menu/vector_home.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - -