mirror of
https://github.com/vector-im/element-android.git
synced 2024-11-16 02:05:06 +08:00
Merge develop into feature/fre/apply_push_rules_after_decryption
This commit is contained in:
commit
4146b5511a
@ -26,10 +26,10 @@ buildscript {
|
|||||||
classpath libs.gradle.hiltPlugin
|
classpath libs.gradle.hiltPlugin
|
||||||
classpath 'com.google.firebase:firebase-appdistribution-gradle:3.2.0'
|
classpath 'com.google.firebase:firebase-appdistribution-gradle:3.2.0'
|
||||||
classpath 'com.google.gms:google-services:4.3.15'
|
classpath 'com.google.gms:google-services:4.3.15'
|
||||||
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.5.0.2730'
|
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:4.0.0.2929'
|
||||||
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.6'
|
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.6'
|
||||||
classpath "com.likethesalad.android:stem-plugin:2.3.0"
|
classpath "com.likethesalad.android:stem-plugin:2.3.0"
|
||||||
classpath 'org.owasp:dependency-check-gradle:8.1.0'
|
classpath 'org.owasp:dependency-check-gradle:8.1.2'
|
||||||
classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.7.20"
|
classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.7.20"
|
||||||
classpath "org.jetbrains.kotlinx:kotlinx-knit:0.4.0"
|
classpath "org.jetbrains.kotlinx:kotlinx-knit:0.4.0"
|
||||||
classpath 'com.jakewharton:butterknife-gradle-plugin:10.2.3'
|
classpath 'com.jakewharton:butterknife-gradle-plugin:10.2.3'
|
||||||
|
1
changelog.d/8157.feature
Normal file
1
changelog.d/8157.feature
Normal file
@ -0,0 +1 @@
|
|||||||
|
Add aggregated unread indicator for spaces in the new layout
|
1
changelog.d/8168.bugfix
Normal file
1
changelog.d/8168.bugfix
Normal file
@ -0,0 +1 @@
|
|||||||
|
Fix timeline loading a wrong room on permalink if a matching event id is found in a different room
|
1
changelog.d/8171.bugfix
Normal file
1
changelog.d/8171.bugfix
Normal file
@ -0,0 +1 @@
|
|||||||
|
[Rich text editor] Fix code appearance
|
1
changelog.d/8190.bugfix
Normal file
1
changelog.d/8190.bugfix
Normal file
@ -0,0 +1 @@
|
|||||||
|
[Poll history] Fixing small issue about font style
|
@ -11,17 +11,17 @@ def gradle = "7.4.1"
|
|||||||
def kotlin = "1.8.10"
|
def kotlin = "1.8.10"
|
||||||
def kotlinCoroutines = "1.6.4"
|
def kotlinCoroutines = "1.6.4"
|
||||||
def dagger = "2.45"
|
def dagger = "2.45"
|
||||||
def firebaseBom = "31.2.1"
|
def firebaseBom = "31.2.2"
|
||||||
def appDistribution = "16.0.0-beta05"
|
def appDistribution = "16.0.0-beta05"
|
||||||
def retrofit = "2.9.0"
|
def retrofit = "2.9.0"
|
||||||
def markwon = "4.6.2"
|
def markwon = "4.6.2"
|
||||||
def moshi = "1.14.0"
|
def moshi = "1.14.0"
|
||||||
def lifecycle = "2.5.1"
|
def lifecycle = "2.5.1"
|
||||||
def flowBinding = "1.2.0"
|
def flowBinding = "1.2.0"
|
||||||
def flipper = "0.182.0"
|
def flipper = "0.183.0"
|
||||||
def epoxy = "5.0.0"
|
def epoxy = "5.0.0"
|
||||||
def mavericks = "3.0.1"
|
def mavericks = "3.0.1"
|
||||||
def glide = "4.14.2"
|
def glide = "4.15.0"
|
||||||
def bigImageViewer = "1.8.1"
|
def bigImageViewer = "1.8.1"
|
||||||
def jjwt = "0.11.5"
|
def jjwt = "0.11.5"
|
||||||
// Temporary version to unblock #6929. Once 0.16.0 is released we should use it, and revert
|
// Temporary version to unblock #6929. Once 0.16.0 is released we should use it, and revert
|
||||||
@ -59,7 +59,7 @@ ext.libs = [
|
|||||||
'fragmentTesting' : "androidx.fragment:fragment-testing:$fragment",
|
'fragmentTesting' : "androidx.fragment:fragment-testing:$fragment",
|
||||||
'fragmentTestingManifest' : "androidx.fragment:fragment-testing-manifest:$fragment",
|
'fragmentTestingManifest' : "androidx.fragment:fragment-testing-manifest:$fragment",
|
||||||
'constraintLayout' : "androidx.constraintlayout:constraintlayout:2.1.4",
|
'constraintLayout' : "androidx.constraintlayout:constraintlayout:2.1.4",
|
||||||
'work' : "androidx.work:work-runtime-ktx:2.7.1",
|
'work' : "androidx.work:work-runtime-ktx:2.8.0",
|
||||||
'autoFill' : "androidx.autofill:autofill:1.1.0",
|
'autoFill' : "androidx.autofill:autofill:1.1.0",
|
||||||
'preferenceKtx' : "androidx.preference:preference-ktx:1.2.0",
|
'preferenceKtx' : "androidx.preference:preference-ktx:1.2.0",
|
||||||
'junit' : "androidx.test.ext:junit:1.1.5",
|
'junit' : "androidx.test.ext:junit:1.1.5",
|
||||||
@ -70,7 +70,7 @@ ext.libs = [
|
|||||||
'datastore' : "androidx.datastore:datastore:1.0.0",
|
'datastore' : "androidx.datastore:datastore:1.0.0",
|
||||||
'datastorepreferences' : "androidx.datastore:datastore-preferences:1.0.0",
|
'datastorepreferences' : "androidx.datastore:datastore-preferences:1.0.0",
|
||||||
'pagingRuntimeKtx' : "androidx.paging:paging-runtime-ktx:2.1.2",
|
'pagingRuntimeKtx' : "androidx.paging:paging-runtime-ktx:2.1.2",
|
||||||
'coreTesting' : "androidx.arch.core:core-testing:2.1.0",
|
'coreTesting' : "androidx.arch.core:core-testing:2.2.0",
|
||||||
'testCore' : "androidx.test:core:$androidxTest",
|
'testCore' : "androidx.test:core:$androidxTest",
|
||||||
'orchestrator' : "androidx.test:orchestrator:$androidxOrchestrator",
|
'orchestrator' : "androidx.test:orchestrator:$androidxOrchestrator",
|
||||||
'testRunner' : "androidx.test:runner:$androidxTest",
|
'testRunner' : "androidx.test:runner:$androidxTest",
|
||||||
@ -79,7 +79,7 @@ ext.libs = [
|
|||||||
'espressoContrib' : "androidx.test.espresso:espresso-contrib:$espresso",
|
'espressoContrib' : "androidx.test.espresso:espresso-contrib:$espresso",
|
||||||
'espressoIntents' : "androidx.test.espresso:espresso-intents:$espresso",
|
'espressoIntents' : "androidx.test.espresso:espresso-intents:$espresso",
|
||||||
'viewpager2' : "androidx.viewpager2:viewpager2:1.0.0",
|
'viewpager2' : "androidx.viewpager2:viewpager2:1.0.0",
|
||||||
'transition' : "androidx.transition:transition:1.2.0",
|
'transition' : "androidx.transition:transition:1.4.1",
|
||||||
],
|
],
|
||||||
google : [
|
google : [
|
||||||
'material' : "com.google.android.material:material:1.8.0",
|
'material' : "com.google.android.material:material:1.8.0",
|
||||||
@ -88,7 +88,7 @@ ext.libs = [
|
|||||||
'appdistributionApi' : "com.google.firebase:firebase-appdistribution-api-ktx:$appDistribution",
|
'appdistributionApi' : "com.google.firebase:firebase-appdistribution-api-ktx:$appDistribution",
|
||||||
'appdistribution' : "com.google.firebase:firebase-appdistribution:$appDistribution",
|
'appdistribution' : "com.google.firebase:firebase-appdistribution:$appDistribution",
|
||||||
// Phone number https://github.com/google/libphonenumber
|
// Phone number https://github.com/google/libphonenumber
|
||||||
'phonenumber' : "com.googlecode.libphonenumber:libphonenumber:8.13.6"
|
'phonenumber' : "com.googlecode.libphonenumber:libphonenumber:8.13.7"
|
||||||
],
|
],
|
||||||
dagger : [
|
dagger : [
|
||||||
'dagger' : "com.google.dagger:dagger:$dagger",
|
'dagger' : "com.google.dagger:dagger:$dagger",
|
||||||
@ -103,7 +103,7 @@ ext.libs = [
|
|||||||
],
|
],
|
||||||
element : [
|
element : [
|
||||||
'opusencoder' : "io.element.android:opusencoder:1.1.0",
|
'opusencoder' : "io.element.android:opusencoder:1.1.0",
|
||||||
'wysiwyg' : "io.element.android:wysiwyg:1.0.0"
|
'wysiwyg' : "io.element.android:wysiwyg:1.1.1"
|
||||||
],
|
],
|
||||||
squareup : [
|
squareup : [
|
||||||
'moshi' : "com.squareup.moshi:moshi:$moshi",
|
'moshi' : "com.squareup.moshi:moshi:$moshi",
|
||||||
|
2
library/external/jsonviewer/build.gradle
vendored
2
library/external/jsonviewer/build.gradle
vendored
@ -65,7 +65,7 @@ dependencies {
|
|||||||
implementation libs.jetbrains.coroutinesCore
|
implementation libs.jetbrains.coroutinesCore
|
||||||
implementation libs.jetbrains.coroutinesAndroid
|
implementation libs.jetbrains.coroutinesAndroid
|
||||||
|
|
||||||
testImplementation 'org.json:json:20220924'
|
testImplementation 'org.json:json:20230227'
|
||||||
testImplementation libs.tests.junit
|
testImplementation libs.tests.junit
|
||||||
androidTestImplementation libs.androidx.junit
|
androidTestImplementation libs.androidx.junit
|
||||||
androidTestImplementation libs.androidx.espressoCore
|
androidTestImplementation libs.androidx.espressoCore
|
||||||
|
@ -297,7 +297,7 @@ internal fun updateThreadNotifications(roomId: String, realm: Realm, currentUser
|
|||||||
val readReceipt = findMyReadReceipt(realm, roomId, currentUserId, threadId = rootThreadEventId) ?: return
|
val readReceipt = findMyReadReceipt(realm, roomId, currentUserId, threadId = rootThreadEventId) ?: return
|
||||||
|
|
||||||
val readReceiptChunk = ChunkEntity
|
val readReceiptChunk = ChunkEntity
|
||||||
.findIncludingEvent(realm, readReceipt) ?: return
|
.findIncludingEvent(realm, roomId, readReceipt) ?: return
|
||||||
|
|
||||||
val readReceiptChunkThreadEvents = readReceiptChunk
|
val readReceiptChunkThreadEvents = readReceiptChunk
|
||||||
.timelineEvents
|
.timelineEvents
|
||||||
|
@ -72,15 +72,16 @@ internal fun ChunkEntity.Companion.findEventInThreadChunk(realm: Realm, roomId:
|
|||||||
.findFirst()
|
.findFirst()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun ChunkEntity.Companion.findAllIncludingEvents(realm: Realm, eventIds: List<String>): RealmResults<ChunkEntity> {
|
internal fun ChunkEntity.Companion.findAllIncludingEvents(realm: Realm, roomId: String, eventIds: List<String>): RealmResults<ChunkEntity> {
|
||||||
return realm.where<ChunkEntity>()
|
return realm.where<ChunkEntity>()
|
||||||
|
.equalTo(ChunkEntityFields.ROOM.ROOM_ID, roomId)
|
||||||
.`in`(ChunkEntityFields.TIMELINE_EVENTS.EVENT_ID, eventIds.toTypedArray())
|
.`in`(ChunkEntityFields.TIMELINE_EVENTS.EVENT_ID, eventIds.toTypedArray())
|
||||||
.isNull(ChunkEntityFields.ROOT_THREAD_EVENT_ID)
|
.isNull(ChunkEntityFields.ROOT_THREAD_EVENT_ID)
|
||||||
.findAll()
|
.findAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun ChunkEntity.Companion.findIncludingEvent(realm: Realm, eventId: String): ChunkEntity? {
|
internal fun ChunkEntity.Companion.findIncludingEvent(realm: Realm, roomId: String, eventId: String): ChunkEntity? {
|
||||||
return findAllIncludingEvents(realm, listOf(eventId)).firstOrNull()
|
return findAllIncludingEvents(realm, roomId, listOf(eventId)).firstOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun ChunkEntity.Companion.create(
|
internal fun ChunkEntity.Companion.create(
|
||||||
|
@ -76,11 +76,11 @@ private fun hasReadMissingEvent(realm: Realm,
|
|||||||
userId: String,
|
userId: String,
|
||||||
eventId: String,
|
eventId: String,
|
||||||
threadId: String? = ReadService.THREAD_ID_MAIN): Boolean {
|
threadId: String? = ReadService.THREAD_ID_MAIN): Boolean {
|
||||||
return realm.doesEventExistInChunkHistory(eventId) && realm.hasReadReceiptInLatestChunk(latestChunkEntity, roomId, userId, threadId)
|
return realm.doesEventExistInChunkHistory(roomId, eventId) && realm.hasReadReceiptInLatestChunk(latestChunkEntity, roomId, userId, threadId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Realm.doesEventExistInChunkHistory(eventId: String): Boolean {
|
private fun Realm.doesEventExistInChunkHistory(roomId: String, eventId: String): Boolean {
|
||||||
return ChunkEntity.findIncludingEvent(this, eventId) != null
|
return ChunkEntity.findIncludingEvent(this, roomId, eventId) != null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Realm.hasReadReceiptInLatestChunk(latestChunkEntity: ChunkEntity, roomId: String, userId: String, threadId: String?): Boolean {
|
private fun Realm.hasReadReceiptInLatestChunk(latestChunkEntity: ChunkEntity, roomId: String, userId: String, threadId: String?): Boolean {
|
||||||
|
@ -59,7 +59,7 @@ internal class DefaultFetchTokenAndPaginateTask @Inject constructor(
|
|||||||
?: throw IllegalStateException("No token found")
|
?: throw IllegalStateException("No token found")
|
||||||
|
|
||||||
monarchy.awaitTransaction { realm ->
|
monarchy.awaitTransaction { realm ->
|
||||||
val chunkToUpdate = ChunkEntity.findIncludingEvent(realm, params.lastKnownEventId)
|
val chunkToUpdate = ChunkEntity.findIncludingEvent(realm, params.roomId, params.lastKnownEventId)
|
||||||
if (params.direction == PaginationDirection.FORWARDS) {
|
if (params.direction == PaginationDirection.FORWARDS) {
|
||||||
chunkToUpdate?.nextToken = fromToken
|
chunkToUpdate?.nextToken = fromToken
|
||||||
} else {
|
} else {
|
||||||
|
@ -278,7 +278,7 @@ internal class LoadTimelineStrategy constructor(
|
|||||||
.findAll()
|
.findAll()
|
||||||
}
|
}
|
||||||
is Mode.Permalink -> {
|
is Mode.Permalink -> {
|
||||||
ChunkEntity.findAllIncludingEvents(realm, listOf(mode.originEventId))
|
ChunkEntity.findAllIncludingEvents(realm, roomId, listOf(mode.originEventId))
|
||||||
}
|
}
|
||||||
is Mode.Thread -> {
|
is Mode.Thread -> {
|
||||||
recreateThreadChunkEntity(realm, mode.rootThreadEventId)
|
recreateThreadChunkEntity(realm, mode.rootThreadEventId)
|
||||||
|
@ -199,7 +199,7 @@ dependencies {
|
|||||||
implementation 'com.github.hyuwah:DraggableView:1.0.0'
|
implementation 'com.github.hyuwah:DraggableView:1.0.0'
|
||||||
|
|
||||||
// Custom Tab
|
// Custom Tab
|
||||||
implementation 'androidx.browser:browser:1.4.0'
|
implementation 'androidx.browser:browser:1.5.0'
|
||||||
|
|
||||||
// Passphrase strength helper
|
// Passphrase strength helper
|
||||||
implementation 'com.nulab-inc:zxcvbn:1.7.0'
|
implementation 'com.nulab-inc:zxcvbn:1.7.0'
|
||||||
|
@ -40,6 +40,7 @@ import im.vector.app.features.discovery.DiscoverySettingsViewModel
|
|||||||
import im.vector.app.features.discovery.change.SetIdentityServerViewModel
|
import im.vector.app.features.discovery.change.SetIdentityServerViewModel
|
||||||
import im.vector.app.features.home.HomeActivityViewModel
|
import im.vector.app.features.home.HomeActivityViewModel
|
||||||
import im.vector.app.features.home.HomeDetailViewModel
|
import im.vector.app.features.home.HomeDetailViewModel
|
||||||
|
import im.vector.app.features.home.NewHomeDetailViewModel
|
||||||
import im.vector.app.features.home.UnknownDeviceDetectorSharedViewModel
|
import im.vector.app.features.home.UnknownDeviceDetectorSharedViewModel
|
||||||
import im.vector.app.features.home.UnreadMessagesSharedViewModel
|
import im.vector.app.features.home.UnreadMessagesSharedViewModel
|
||||||
import im.vector.app.features.home.UserColorAccountDataViewModel
|
import im.vector.app.features.home.UserColorAccountDataViewModel
|
||||||
@ -717,4 +718,9 @@ interface MavericksViewModelModule {
|
|||||||
@IntoMap
|
@IntoMap
|
||||||
@MavericksViewModelKey(RoomPollDetailViewModel::class)
|
@MavericksViewModelKey(RoomPollDetailViewModel::class)
|
||||||
fun roomPollDetailViewModelFactory(factory: RoomPollDetailViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
fun roomPollDetailViewModelFactory(factory: RoomPollDetailViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@MavericksViewModelKey(NewHomeDetailViewModel::class)
|
||||||
|
fun newHomeDetailViewModelFactory(factory: NewHomeDetailViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 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
|
||||||
|
|
||||||
|
import im.vector.app.features.home.room.list.UnreadCounterBadgeView
|
||||||
|
import im.vector.app.features.spaces.GetSpacesUseCase
|
||||||
|
import im.vector.app.features.spaces.notification.GetNotificationCountForSpacesUseCase
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||||
|
import org.matrix.android.sdk.api.query.SpaceFilter
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
import org.matrix.android.sdk.api.session.room.spaceSummaryQueryParams
|
||||||
|
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class GetSpacesNotificationBadgeStateUseCase @Inject constructor(
|
||||||
|
private val getNotificationCountForSpacesUseCase: GetNotificationCountForSpacesUseCase,
|
||||||
|
private val getSpacesUseCase: GetSpacesUseCase,
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun execute(): Flow<UnreadCounterBadgeView.State> {
|
||||||
|
val params = spaceSummaryQueryParams {
|
||||||
|
memberships = listOf(Membership.INVITE)
|
||||||
|
displayName = QueryStringValue.IsNotEmpty
|
||||||
|
}
|
||||||
|
return combine(
|
||||||
|
getNotificationCountForSpacesUseCase.execute(SpaceFilter.NoFilter),
|
||||||
|
getSpacesUseCase.execute(params),
|
||||||
|
) { spacesNotificationCount, spaceInvites ->
|
||||||
|
computeSpacesNotificationCounterBadgeState(spacesNotificationCount, spaceInvites)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun computeSpacesNotificationCounterBadgeState(
|
||||||
|
spacesNotificationCount: RoomAggregateNotificationCount,
|
||||||
|
spaceInvites: List<RoomSummary>,
|
||||||
|
): UnreadCounterBadgeView.State {
|
||||||
|
val hasPendingSpaceInvites = spaceInvites.isNotEmpty()
|
||||||
|
return if (hasPendingSpaceInvites && spacesNotificationCount.notificationCount == 0) {
|
||||||
|
UnreadCounterBadgeView.State.Text(
|
||||||
|
text = "!",
|
||||||
|
highlighted = true,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
UnreadCounterBadgeView.State.Count(
|
||||||
|
count = spacesNotificationCount.notificationCount,
|
||||||
|
highlighted = spacesNotificationCount.isHighlight || hasPendingSpaceInvites,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -47,6 +47,7 @@ import im.vector.app.features.call.SharedKnownCallsViewModel
|
|||||||
import im.vector.app.features.call.VectorCallActivity
|
import im.vector.app.features.call.VectorCallActivity
|
||||||
import im.vector.app.features.call.dialpad.PstnDialActivity
|
import im.vector.app.features.call.dialpad.PstnDialActivity
|
||||||
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
||||||
|
import im.vector.app.features.home.room.list.UnreadCounterBadgeView
|
||||||
import im.vector.app.features.home.room.list.actions.RoomListSharedAction
|
import im.vector.app.features.home.room.list.actions.RoomListSharedAction
|
||||||
import im.vector.app.features.home.room.list.actions.RoomListSharedActionViewModel
|
import im.vector.app.features.home.room.list.actions.RoomListSharedActionViewModel
|
||||||
import im.vector.app.features.home.room.list.home.HomeRoomListFragment
|
import im.vector.app.features.home.room.list.home.HomeRoomListFragment
|
||||||
@ -82,6 +83,7 @@ class NewHomeDetailFragment :
|
|||||||
@Inject lateinit var buildMeta: BuildMeta
|
@Inject lateinit var buildMeta: BuildMeta
|
||||||
|
|
||||||
private val viewModel: HomeDetailViewModel by fragmentViewModel()
|
private val viewModel: HomeDetailViewModel by fragmentViewModel()
|
||||||
|
private val newHomeDetailViewModel: NewHomeDetailViewModel by fragmentViewModel()
|
||||||
private val unknownDeviceDetectorSharedViewModel: UnknownDeviceDetectorSharedViewModel by activityViewModel()
|
private val unknownDeviceDetectorSharedViewModel: UnknownDeviceDetectorSharedViewModel by activityViewModel()
|
||||||
private val serverBackupStatusViewModel: ServerBackupStatusViewModel by activityViewModel()
|
private val serverBackupStatusViewModel: ServerBackupStatusViewModel by activityViewModel()
|
||||||
|
|
||||||
@ -180,6 +182,10 @@ class NewHomeDetailFragment :
|
|||||||
currentCallsViewPresenter.updateCall(callManager.getCurrentCall(), callManager.getCalls())
|
currentCallsViewPresenter.updateCall(callManager.getCurrentCall(), callManager.getCalls())
|
||||||
invalidateOptionsMenu()
|
invalidateOptionsMenu()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newHomeDetailViewModel.onEach { viewState ->
|
||||||
|
refreshUnreadCounterBadge(viewState.spacesNotificationCounterBadgeState)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupObservers() {
|
private fun setupObservers() {
|
||||||
@ -379,6 +385,10 @@ class NewHomeDetailFragment :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun refreshUnreadCounterBadge(badgeState: UnreadCounterBadgeView.State) {
|
||||||
|
views.spacesUnreadCounterBadge.render(badgeState)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onTapToReturnToCall() {
|
override fun onTapToReturnToCall() {
|
||||||
callManager.getCurrentCall()?.let { call ->
|
callManager.getCurrentCall()?.let { call ->
|
||||||
VectorCallActivity.newIntent(
|
VectorCallActivity.newIntent(
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 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
|
||||||
|
|
||||||
|
import com.airbnb.mvrx.MavericksViewModelFactory
|
||||||
|
import dagger.assisted.Assisted
|
||||||
|
import dagger.assisted.AssistedFactory
|
||||||
|
import dagger.assisted.AssistedInject
|
||||||
|
import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
||||||
|
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
||||||
|
import im.vector.app.core.platform.EmptyAction
|
||||||
|
import im.vector.app.core.platform.EmptyViewEvents
|
||||||
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
|
||||||
|
class NewHomeDetailViewModel @AssistedInject constructor(
|
||||||
|
@Assisted initialState: NewHomeDetailViewState,
|
||||||
|
private val getSpacesNotificationBadgeStateUseCase: GetSpacesNotificationBadgeStateUseCase,
|
||||||
|
) : VectorViewModel<NewHomeDetailViewState, EmptyAction, EmptyViewEvents>(initialState) {
|
||||||
|
|
||||||
|
@AssistedFactory
|
||||||
|
interface Factory : MavericksAssistedViewModelFactory<NewHomeDetailViewModel, NewHomeDetailViewState> {
|
||||||
|
override fun create(initialState: NewHomeDetailViewState): NewHomeDetailViewModel
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object : MavericksViewModelFactory<NewHomeDetailViewModel, NewHomeDetailViewState> by hiltMavericksViewModelFactory()
|
||||||
|
|
||||||
|
init {
|
||||||
|
observeSpacesNotificationBadgeState()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun observeSpacesNotificationBadgeState() {
|
||||||
|
getSpacesNotificationBadgeStateUseCase.execute()
|
||||||
|
.onEach { badgeState -> setState { copy(spacesNotificationCounterBadgeState = badgeState) } }
|
||||||
|
.launchIn(viewModelScope)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun handle(action: EmptyAction) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 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
|
||||||
|
|
||||||
|
import com.airbnb.mvrx.MavericksState
|
||||||
|
import im.vector.app.features.home.room.list.UnreadCounterBadgeView
|
||||||
|
|
||||||
|
data class NewHomeDetailViewState(
|
||||||
|
val spacesNotificationCounterBadgeState: UnreadCounterBadgeView.State = UnreadCounterBadgeView.State.Count(count = 0, highlighted = false),
|
||||||
|
) : MavericksState
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 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.spaces
|
||||||
|
|
||||||
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.emptyFlow
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
import org.matrix.android.sdk.api.session.space.SpaceSummaryQueryParams
|
||||||
|
import org.matrix.android.sdk.flow.flow
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class GetSpacesUseCase @Inject constructor(
|
||||||
|
private val activeSessionHolder: ActiveSessionHolder,
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun execute(params: SpaceSummaryQueryParams): Flow<List<RoomSummary>> {
|
||||||
|
val session = activeSessionHolder.getSafeActiveSession()
|
||||||
|
|
||||||
|
return session?.flow()?.liveSpaceSummaries(params) ?: emptyFlow()
|
||||||
|
}
|
||||||
|
}
|
@ -29,16 +29,13 @@ import im.vector.app.core.di.hiltMavericksViewModelFactory
|
|||||||
import im.vector.app.core.platform.VectorViewModel
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
import im.vector.app.features.analytics.AnalyticsTracker
|
import im.vector.app.features.analytics.AnalyticsTracker
|
||||||
import im.vector.app.features.analytics.plan.Interaction
|
import im.vector.app.features.analytics.plan.Interaction
|
||||||
import im.vector.app.features.invite.AutoAcceptInvites
|
|
||||||
import im.vector.app.features.session.coroutineScope
|
import im.vector.app.features.session.coroutineScope
|
||||||
import im.vector.app.features.settings.VectorPreferences
|
import im.vector.app.features.settings.VectorPreferences
|
||||||
import kotlinx.coroutines.Dispatchers
|
import im.vector.app.features.spaces.notification.GetNotificationCountForSpacesUseCase
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.flow.sample
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||||
@ -48,25 +45,22 @@ import org.matrix.android.sdk.api.session.events.model.toContent
|
|||||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
import org.matrix.android.sdk.api.session.getRoom
|
import org.matrix.android.sdk.api.session.getRoom
|
||||||
import org.matrix.android.sdk.api.session.getUserOrDefault
|
import org.matrix.android.sdk.api.session.getUserOrDefault
|
||||||
import org.matrix.android.sdk.api.session.room.RoomSortOrder
|
|
||||||
import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataTypes
|
import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataTypes
|
||||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||||
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
|
||||||
import org.matrix.android.sdk.api.session.room.spaceSummaryQueryParams
|
import org.matrix.android.sdk.api.session.room.spaceSummaryQueryParams
|
||||||
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
|
||||||
import org.matrix.android.sdk.api.session.space.SpaceOrderUtils
|
import org.matrix.android.sdk.api.session.space.SpaceOrderUtils
|
||||||
import org.matrix.android.sdk.api.session.space.model.SpaceOrderContent
|
import org.matrix.android.sdk.api.session.space.model.SpaceOrderContent
|
||||||
import org.matrix.android.sdk.api.session.space.model.TopLevelSpaceComparator
|
import org.matrix.android.sdk.api.session.space.model.TopLevelSpaceComparator
|
||||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||||
import org.matrix.android.sdk.flow.flow
|
|
||||||
|
|
||||||
class SpaceListViewModel @AssistedInject constructor(
|
class SpaceListViewModel @AssistedInject constructor(
|
||||||
@Assisted initialState: SpaceListViewState,
|
@Assisted initialState: SpaceListViewState,
|
||||||
private val spaceStateHandler: SpaceStateHandler,
|
private val spaceStateHandler: SpaceStateHandler,
|
||||||
private val session: Session,
|
private val session: Session,
|
||||||
private val vectorPreferences: VectorPreferences,
|
private val vectorPreferences: VectorPreferences,
|
||||||
private val autoAcceptInvites: AutoAcceptInvites,
|
|
||||||
private val analyticsTracker: AnalyticsTracker,
|
private val analyticsTracker: AnalyticsTracker,
|
||||||
|
getNotificationCountForSpacesUseCase: GetNotificationCountForSpacesUseCase,
|
||||||
|
private val getSpacesUseCase: GetSpacesUseCase,
|
||||||
) : VectorViewModel<SpaceListViewState, SpaceListAction, SpaceListViewEvents>(initialState) {
|
) : VectorViewModel<SpaceListViewState, SpaceListAction, SpaceListViewEvents>(initialState) {
|
||||||
|
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
@ -92,39 +86,14 @@ class SpaceListViewModel @AssistedInject constructor(
|
|||||||
copy(selectedSpace = selectedSpaceOption.orNull())
|
copy(selectedSpace = selectedSpaceOption.orNull())
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX there should be a way to refactor this and share it
|
getNotificationCountForSpacesUseCase.execute(roomsInSpaceFilter())
|
||||||
session.roomService().getPagedRoomSummariesLive(
|
.onEach { counts ->
|
||||||
roomSummaryQueryParams {
|
|
||||||
this.memberships = listOf(Membership.JOIN)
|
|
||||||
this.spaceFilter = roomsInSpaceFilter()
|
|
||||||
}, sortOrder = RoomSortOrder.NONE
|
|
||||||
).asFlow()
|
|
||||||
.sample(300)
|
|
||||||
.onEach {
|
|
||||||
val inviteCount = if (autoAcceptInvites.hideInvites) {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
session.roomService().getRoomSummaries(
|
|
||||||
roomSummaryQueryParams { this.memberships = listOf(Membership.INVITE) }
|
|
||||||
).size
|
|
||||||
}
|
|
||||||
val totalCount = session.roomService().getNotificationCountForRooms(
|
|
||||||
roomSummaryQueryParams {
|
|
||||||
this.memberships = listOf(Membership.JOIN)
|
|
||||||
this.spaceFilter = roomsInSpaceFilter()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
val counts = RoomAggregateNotificationCount(
|
|
||||||
totalCount.notificationCount + inviteCount,
|
|
||||||
totalCount.highlightCount + inviteCount
|
|
||||||
)
|
|
||||||
setState {
|
setState {
|
||||||
copy(
|
copy(
|
||||||
homeAggregateCount = counts
|
homeAggregateCount = counts
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.flowOn(Dispatchers.Default)
|
|
||||||
.launchIn(viewModelScope)
|
.launchIn(viewModelScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,7 +236,7 @@ class SpaceListViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
combine(
|
combine(
|
||||||
session.flow().liveSpaceSummaries(params),
|
getSpacesUseCase.execute(params),
|
||||||
session.accountDataService()
|
session.accountDataService()
|
||||||
.getLiveRoomAccountDataEvents(setOf(RoomAccountDataTypes.EVENT_TYPE_SPACE_ORDER))
|
.getLiveRoomAccountDataEvents(setOf(RoomAccountDataTypes.EVENT_TYPE_SPACE_ORDER))
|
||||||
.asFlow()
|
.asFlow()
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 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.spaces.notification
|
||||||
|
|
||||||
|
import androidx.lifecycle.asFlow
|
||||||
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
|
import im.vector.app.features.invite.AutoAcceptInvites
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.emptyFlow
|
||||||
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
import kotlinx.coroutines.flow.mapLatest
|
||||||
|
import kotlinx.coroutines.flow.sample
|
||||||
|
import org.matrix.android.sdk.api.query.SpaceFilter
|
||||||
|
import org.matrix.android.sdk.api.session.room.RoomSortOrder
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||||
|
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
||||||
|
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class GetNotificationCountForSpacesUseCase @Inject constructor(
|
||||||
|
private val activeSessionHolder: ActiveSessionHolder,
|
||||||
|
private val autoAcceptInvites: AutoAcceptInvites,
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun execute(spaceFilter: SpaceFilter): Flow<RoomAggregateNotificationCount> {
|
||||||
|
val session = activeSessionHolder.getSafeActiveSession()
|
||||||
|
|
||||||
|
val spaceQueryParams = roomSummaryQueryParams {
|
||||||
|
this.memberships = listOf(Membership.JOIN)
|
||||||
|
this.spaceFilter = spaceFilter
|
||||||
|
}
|
||||||
|
return session
|
||||||
|
?.roomService()
|
||||||
|
?.getPagedRoomSummariesLive(queryParams = spaceQueryParams, sortOrder = RoomSortOrder.NONE)
|
||||||
|
?.asFlow()
|
||||||
|
?.sample(300)
|
||||||
|
?.mapLatest {
|
||||||
|
val inviteCount = if (autoAcceptInvites.hideInvites) {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
session.roomService().getRoomSummaries(
|
||||||
|
roomSummaryQueryParams { this.memberships = listOf(Membership.INVITE) }
|
||||||
|
).size
|
||||||
|
}
|
||||||
|
val totalCount = session.roomService().getNotificationCountForRooms(spaceQueryParams)
|
||||||
|
RoomAggregateNotificationCount(
|
||||||
|
notificationCount = totalCount.notificationCount + inviteCount,
|
||||||
|
highlightCount = totalCount.highlightCount + inviteCount,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
?.flowOn(session.coroutineDispatchers.main)
|
||||||
|
?: emptyFlow()
|
||||||
|
}
|
||||||
|
}
|
22
vector/src/main/res/drawable/bg_code_block.xml
Normal file
22
vector/src/main/res/drawable/bg_code_block.xml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright 2018 The Android Open Source Project
|
||||||
|
~ Modifications Copyright 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.
|
||||||
|
-->
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="?vctr_system"/>
|
||||||
|
<stroke android:width="@dimen/code_block_border_width" android:color="?vctr_content_quinary"/>
|
||||||
|
<corners android:radius="@dimen/code_block_border_radius"/>
|
||||||
|
</shape>
|
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright 2018 The Android Open Source Project
|
||||||
|
~ Modifications Copyright 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.
|
||||||
|
-->
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="?vctr_system"/>
|
||||||
|
<stroke android:width="@dimen/inline_code_border_width" android:color="?vctr_content_quinary"/>
|
||||||
|
<corners android:topLeftRadius="@dimen/inline_code_border_radius"
|
||||||
|
android:bottomLeftRadius="@dimen/inline_code_border_radius"/>
|
||||||
|
</shape>
|
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright 2018 The Android Open Source Project
|
||||||
|
~ Modifications Copyright 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.
|
||||||
|
-->
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="?vctr_system"/>
|
||||||
|
<stroke android:width="@dimen/inline_code_border_width" android:color="?vctr_content_quinary"/>
|
||||||
|
</shape>
|
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright 2018 The Android Open Source Project
|
||||||
|
~ Modifications Copyright 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.
|
||||||
|
-->
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="?vctr_system"/>
|
||||||
|
<stroke android:width="@dimen/inline_code_border_width" android:color="?vctr_content_quinary"/>
|
||||||
|
<corners android:topRightRadius="@dimen/inline_code_border_radius"
|
||||||
|
android:bottomRightRadius="@dimen/inline_code_border_radius"/>
|
||||||
|
</shape>
|
22
vector/src/main/res/drawable/bg_inline_code_single_line.xml
Normal file
22
vector/src/main/res/drawable/bg_inline_code_single_line.xml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright 2018 The Android Open Source Project
|
||||||
|
~ Modifications Copyright 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.
|
||||||
|
-->
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="?vctr_system"/>
|
||||||
|
<stroke android:width="@dimen/inline_code_border_width" android:color="?vctr_content_quinary"/>
|
||||||
|
<corners android:radius="@dimen/inline_code_border_radius"/>
|
||||||
|
</shape>
|
@ -126,6 +126,11 @@
|
|||||||
app:layout_constraintTop_toBottomOf="@id/composerModeBarrier"
|
app:layout_constraintTop_toBottomOf="@id/composerModeBarrier"
|
||||||
app:bulletRadius="4sp"
|
app:bulletRadius="4sp"
|
||||||
app:bulletGap="8sp"
|
app:bulletGap="8sp"
|
||||||
|
app:codeBlockBackgroundDrawable="@drawable/bg_code_block"
|
||||||
|
app:inlineCodeSingleLineBg="@drawable/bg_inline_code_single_line"
|
||||||
|
app:inlineCodeMultiLineBgLeft="@drawable/bg_inline_code_multi_line_left"
|
||||||
|
app:inlineCodeMultiLineBgMid="@drawable/bg_inline_code_multi_line_mid"
|
||||||
|
app:inlineCodeMultiLineBgRight="@drawable/bg_inline_code_multi_line_right"
|
||||||
tools:text="@tools:sample/lorem/random" />
|
tools:text="@tools:sample/lorem/random" />
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
@ -120,6 +120,28 @@
|
|||||||
tools:targetApi="lollipop_mr1"
|
tools:targetApi="lollipop_mr1"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<im.vector.app.features.home.room.list.UnreadCounterBadgeView
|
||||||
|
android:id="@+id/spacesUnreadCounterBadge"
|
||||||
|
style="@style/Widget.Vector.TextView.Micro"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:elevation="15dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:minWidth="16dp"
|
||||||
|
android:minHeight="16dp"
|
||||||
|
android:paddingStart="4dp"
|
||||||
|
android:paddingEnd="4dp"
|
||||||
|
android:textColor="?colorOnError"
|
||||||
|
app:layout_constraintCircle="@id/newLayoutOpenSpacesButton"
|
||||||
|
app:layout_constraintCircleAngle="45"
|
||||||
|
app:layout_constraintCircleRadius="24dp"
|
||||||
|
tools:background="@drawable/bg_unread_highlight"
|
||||||
|
tools:ignore="MissingConstraints"
|
||||||
|
tools:text="147"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
android:id="@+id/newLayoutCreateChatButton"
|
android:id="@+id/newLayoutCreateChatButton"
|
||||||
style="@style/Widget.Vector.FloatingActionButton"
|
style="@style/Widget.Vector.FloatingActionButton"
|
||||||
|
@ -54,7 +54,8 @@
|
|||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:textAppearance="@style/TextAppearance.Vector.Body"
|
android:textAppearance="@style/TextAppearance.Vector.Body"
|
||||||
android:textColor="?vctr_content_secondary"
|
android:textColor="?vctr_content_secondary"
|
||||||
android:textSize="17sp"
|
android:textSize="15sp"
|
||||||
|
android:lineSpacingMultiplier="1.2"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
@ -8,4 +8,10 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
android:textColor="?vctr_content_primary"
|
android:textColor="?vctr_content_primary"
|
||||||
|
app:codeBlockBackgroundDrawable="@drawable/bg_code_block"
|
||||||
|
app:inlineCodeSingleLineBg="@drawable/bg_inline_code_single_line"
|
||||||
|
app:inlineCodeMultiLineBgLeft="@drawable/bg_inline_code_multi_line_left"
|
||||||
|
app:inlineCodeMultiLineBgMid="@drawable/bg_inline_code_multi_line_mid"
|
||||||
|
app:inlineCodeMultiLineBgRight="@drawable/bg_inline_code_multi_line_right"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
tools:text="@sample/messages.json/data/message" />
|
tools:text="@sample/messages.json/data/message" />
|
||||||
|
@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 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
|
||||||
|
|
||||||
|
import im.vector.app.features.home.room.list.UnreadCounterBadgeView
|
||||||
|
import im.vector.app.features.spaces.GetSpacesUseCase
|
||||||
|
import im.vector.app.features.spaces.notification.GetNotificationCountForSpacesUseCase
|
||||||
|
import im.vector.app.test.test
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.verify
|
||||||
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
import kotlinx.coroutines.test.advanceUntilIdle
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.Test
|
||||||
|
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||||
|
import org.matrix.android.sdk.api.query.SpaceFilter
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
||||||
|
|
||||||
|
internal class GetSpacesNotificationBadgeStateUseCaseTest {
|
||||||
|
|
||||||
|
private val fakeGetNotificationCountForSpacesUseCase = mockk<GetNotificationCountForSpacesUseCase>()
|
||||||
|
private val fakeGetSpacesUseCase = mockk<GetSpacesUseCase>()
|
||||||
|
|
||||||
|
private val getSpacesNotificationBadgeStateUseCase = GetSpacesNotificationBadgeStateUseCase(
|
||||||
|
getNotificationCountForSpacesUseCase = fakeGetNotificationCountForSpacesUseCase,
|
||||||
|
getSpacesUseCase = fakeGetSpacesUseCase,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given flow of spaces invite and notification count then flow of state is correct`() = runTest {
|
||||||
|
// Given
|
||||||
|
val noSpacesInvite = emptyList<RoomSummary>()
|
||||||
|
val existingSpaceInvite = listOf<RoomSummary>(mockk())
|
||||||
|
val noNotification = RoomAggregateNotificationCount(
|
||||||
|
notificationCount = 0,
|
||||||
|
highlightCount = 0,
|
||||||
|
)
|
||||||
|
val existingNotificationNotHighlighted = RoomAggregateNotificationCount(
|
||||||
|
notificationCount = 1,
|
||||||
|
highlightCount = 0,
|
||||||
|
)
|
||||||
|
val existingNotificationHighlighted = RoomAggregateNotificationCount(
|
||||||
|
notificationCount = 1,
|
||||||
|
highlightCount = 1,
|
||||||
|
)
|
||||||
|
every { fakeGetSpacesUseCase.execute(any()) } returns
|
||||||
|
flowOf(noSpacesInvite, existingSpaceInvite, existingSpaceInvite, noSpacesInvite, noSpacesInvite)
|
||||||
|
every { fakeGetNotificationCountForSpacesUseCase.execute(any()) } returns
|
||||||
|
flowOf(noNotification, noNotification, existingNotificationNotHighlighted, existingNotificationNotHighlighted, existingNotificationHighlighted)
|
||||||
|
|
||||||
|
// When
|
||||||
|
val testObserver = getSpacesNotificationBadgeStateUseCase.execute().test(this)
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
// Then
|
||||||
|
val expectedState1 = UnreadCounterBadgeView.State.Count(count = 0, highlighted = false)
|
||||||
|
val expectedState2 = UnreadCounterBadgeView.State.Text(text = "!", highlighted = true)
|
||||||
|
val expectedState3 = UnreadCounterBadgeView.State.Count(count = 1, highlighted = true)
|
||||||
|
val expectedState4 = UnreadCounterBadgeView.State.Count(count = 1, highlighted = false)
|
||||||
|
val expectedState5 = UnreadCounterBadgeView.State.Count(count = 1, highlighted = true)
|
||||||
|
testObserver
|
||||||
|
.assertValues(expectedState1, expectedState2, expectedState3, expectedState4, expectedState5)
|
||||||
|
.finish()
|
||||||
|
verify {
|
||||||
|
fakeGetSpacesUseCase.execute(match {
|
||||||
|
it.memberships == listOf(Membership.INVITE) && it.displayName == QueryStringValue.IsNotEmpty
|
||||||
|
})
|
||||||
|
}
|
||||||
|
verify { fakeGetNotificationCountForSpacesUseCase.execute(SpaceFilter.NoFilter) }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 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
|
||||||
|
|
||||||
|
import com.airbnb.mvrx.test.MavericksTestRule
|
||||||
|
import im.vector.app.features.home.room.list.UnreadCounterBadgeView
|
||||||
|
import im.vector.app.test.test
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.verify
|
||||||
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
internal class NewHomeDetailViewModelTest {
|
||||||
|
|
||||||
|
@get:Rule
|
||||||
|
val mavericksTestRule = MavericksTestRule(testDispatcher = UnconfinedTestDispatcher())
|
||||||
|
|
||||||
|
private val initialState = NewHomeDetailViewState()
|
||||||
|
private val fakeGetSpacesNotificationBadgeStateUseCase = mockk<GetSpacesNotificationBadgeStateUseCase>()
|
||||||
|
|
||||||
|
private fun createViewModel(): NewHomeDetailViewModel {
|
||||||
|
return NewHomeDetailViewModel(
|
||||||
|
initialState = initialState,
|
||||||
|
getSpacesNotificationBadgeStateUseCase = fakeGetSpacesNotificationBadgeStateUseCase,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given the viewModel is created then viewState is updated with space notifications badge state`() {
|
||||||
|
// Given
|
||||||
|
val aBadgeState = UnreadCounterBadgeView.State.Count(count = 1, highlighted = false)
|
||||||
|
every { fakeGetSpacesNotificationBadgeStateUseCase.execute() } returns flowOf(aBadgeState)
|
||||||
|
|
||||||
|
// When
|
||||||
|
val viewModel = createViewModel()
|
||||||
|
val viewModelTest = viewModel.test()
|
||||||
|
|
||||||
|
// Then
|
||||||
|
val expectedViewState = initialState.copy(
|
||||||
|
spacesNotificationCounterBadgeState = aBadgeState,
|
||||||
|
)
|
||||||
|
viewModelTest
|
||||||
|
.assertLatestState(expectedViewState)
|
||||||
|
.finish()
|
||||||
|
verify {
|
||||||
|
fakeGetSpacesNotificationBadgeStateUseCase.execute()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 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.spaces
|
||||||
|
|
||||||
|
import im.vector.app.test.fakes.FakeActiveSessionHolder
|
||||||
|
import im.vector.app.test.fakes.FakeFlowLiveDataConversions
|
||||||
|
import im.vector.app.test.fakes.givenAsFlow
|
||||||
|
import im.vector.app.test.test
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.unmockkAll
|
||||||
|
import io.mockk.verify
|
||||||
|
import kotlinx.coroutines.test.advanceUntilIdle
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
import org.matrix.android.sdk.api.session.space.SpaceSummaryQueryParams
|
||||||
|
|
||||||
|
internal class GetSpacesUseCaseTest {
|
||||||
|
|
||||||
|
private val fakeActiveSessionHolder = FakeActiveSessionHolder()
|
||||||
|
private val fakeFlowLiveDataConversions = FakeFlowLiveDataConversions()
|
||||||
|
|
||||||
|
private val getSpacesUseCase = GetSpacesUseCase(
|
||||||
|
activeSessionHolder = fakeActiveSessionHolder.instance,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
fakeFlowLiveDataConversions.setup()
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
unmockkAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given params when execute then the list of summaries is returned`() = runTest {
|
||||||
|
// Given
|
||||||
|
val queryParams = givenSpaceQueryParams()
|
||||||
|
val firstSummaries = listOf<RoomSummary>(mockk())
|
||||||
|
val nextSummaries = listOf<RoomSummary>(mockk())
|
||||||
|
fakeActiveSessionHolder.fakeSession
|
||||||
|
.fakeSpaceService
|
||||||
|
.givenGetSpaceSummariesReturns(firstSummaries)
|
||||||
|
fakeActiveSessionHolder.fakeSession
|
||||||
|
.fakeSpaceService
|
||||||
|
.givenGetSpaceSummariesLiveReturns(nextSummaries)
|
||||||
|
.givenAsFlow()
|
||||||
|
|
||||||
|
// When
|
||||||
|
val testObserver = getSpacesUseCase.execute(queryParams).test(this)
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
// Then
|
||||||
|
testObserver
|
||||||
|
.assertValues(firstSummaries, nextSummaries)
|
||||||
|
.finish()
|
||||||
|
verify {
|
||||||
|
fakeActiveSessionHolder.fakeSession.fakeSpaceService.getSpaceSummaries(queryParams)
|
||||||
|
fakeActiveSessionHolder.fakeSession.fakeSpaceService.getSpaceSummariesLive(queryParams)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given no active session when execute then empty flow is returned`() = runTest {
|
||||||
|
// Given
|
||||||
|
fakeActiveSessionHolder.givenGetSafeActiveSessionReturns(null)
|
||||||
|
val queryParams = givenSpaceQueryParams()
|
||||||
|
|
||||||
|
// When
|
||||||
|
val testObserver = getSpacesUseCase.execute(queryParams).test(this)
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
// Then
|
||||||
|
testObserver
|
||||||
|
.assertNoValues()
|
||||||
|
.finish()
|
||||||
|
verify(inverse = true) {
|
||||||
|
fakeActiveSessionHolder.fakeSession.fakeSpaceService.getSpaceSummaries(queryParams)
|
||||||
|
fakeActiveSessionHolder.fakeSession.fakeSpaceService.getSpaceSummariesLive(queryParams)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun givenSpaceQueryParams(): SpaceSummaryQueryParams {
|
||||||
|
return mockk()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,174 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 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.spaces.notification
|
||||||
|
|
||||||
|
import androidx.paging.PagedList
|
||||||
|
import im.vector.app.test.fakes.FakeActiveSessionHolder
|
||||||
|
import im.vector.app.test.fakes.FakeAutoAcceptInvites
|
||||||
|
import im.vector.app.test.fakes.FakeFlowLiveDataConversions
|
||||||
|
import im.vector.app.test.fakes.givenAsFlow
|
||||||
|
import im.vector.app.test.test
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.mockkStatic
|
||||||
|
import io.mockk.unmockkAll
|
||||||
|
import io.mockk.verify
|
||||||
|
import kotlinx.coroutines.flow.sample
|
||||||
|
import kotlinx.coroutines.test.advanceUntilIdle
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.matrix.android.sdk.api.query.SpaceFilter
|
||||||
|
import org.matrix.android.sdk.api.session.room.RoomSortOrder
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
||||||
|
|
||||||
|
internal class GetNotificationCountForSpacesUseCaseTest {
|
||||||
|
|
||||||
|
private val fakeActiveSessionHolder = FakeActiveSessionHolder()
|
||||||
|
private val fakeAutoAcceptInvites = FakeAutoAcceptInvites()
|
||||||
|
private val fakeFlowLiveDataConversions = FakeFlowLiveDataConversions()
|
||||||
|
|
||||||
|
private val getNotificationCountForSpacesUseCase = GetNotificationCountForSpacesUseCase(
|
||||||
|
activeSessionHolder = fakeActiveSessionHolder.instance,
|
||||||
|
autoAcceptInvites = fakeAutoAcceptInvites,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
fakeFlowLiveDataConversions.setup()
|
||||||
|
mockkStatic("kotlinx.coroutines.flow.FlowKt")
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
unmockkAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given space filter and auto accept invites when execute then correct notification count is returned`() = runTest {
|
||||||
|
// given
|
||||||
|
val spaceFilter = SpaceFilter.NoFilter
|
||||||
|
val pagedList = mockk<PagedList<RoomSummary>>()
|
||||||
|
val pagedListFlow = fakeActiveSessionHolder.fakeSession
|
||||||
|
.fakeRoomService
|
||||||
|
.givenGetPagedRoomSummariesLiveReturns(pagedList)
|
||||||
|
.givenAsFlow()
|
||||||
|
every { pagedListFlow.sample(any<Long>()) } returns pagedListFlow
|
||||||
|
val expectedNotificationCount = RoomAggregateNotificationCount(
|
||||||
|
notificationCount = 1,
|
||||||
|
highlightCount = 0,
|
||||||
|
)
|
||||||
|
fakeActiveSessionHolder.fakeSession
|
||||||
|
.fakeRoomService
|
||||||
|
.givenGetNotificationCountForRoomsReturns(expectedNotificationCount)
|
||||||
|
fakeAutoAcceptInvites._isEnabled = true
|
||||||
|
|
||||||
|
// When
|
||||||
|
val testObserver = getNotificationCountForSpacesUseCase.execute(spaceFilter).test(this)
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
// Then
|
||||||
|
testObserver
|
||||||
|
.assertValues(expectedNotificationCount)
|
||||||
|
.finish()
|
||||||
|
verify {
|
||||||
|
fakeActiveSessionHolder.fakeSession.fakeRoomService.getNotificationCountForRooms(
|
||||||
|
queryParams = match { it.memberships == listOf(Membership.JOIN) && it.spaceFilter == spaceFilter }
|
||||||
|
)
|
||||||
|
fakeActiveSessionHolder.fakeSession.fakeRoomService.getPagedRoomSummariesLive(
|
||||||
|
queryParams = match { it.memberships == listOf(Membership.JOIN) && it.spaceFilter == spaceFilter },
|
||||||
|
pagedListConfig = any(),
|
||||||
|
sortOrder = RoomSortOrder.NONE,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given space filter and show invites when execute then correct notification count is returned`() = runTest {
|
||||||
|
// given
|
||||||
|
val spaceFilter = SpaceFilter.NoFilter
|
||||||
|
val pagedList = mockk<PagedList<RoomSummary>>()
|
||||||
|
val pagedListFlow = fakeActiveSessionHolder.fakeSession
|
||||||
|
.fakeRoomService
|
||||||
|
.givenGetPagedRoomSummariesLiveReturns(pagedList)
|
||||||
|
.givenAsFlow()
|
||||||
|
every { pagedListFlow.sample(any<Long>()) } returns pagedListFlow
|
||||||
|
val notificationCount = RoomAggregateNotificationCount(
|
||||||
|
notificationCount = 1,
|
||||||
|
highlightCount = 0,
|
||||||
|
)
|
||||||
|
fakeActiveSessionHolder.fakeSession
|
||||||
|
.fakeRoomService
|
||||||
|
.givenGetNotificationCountForRoomsReturns(notificationCount)
|
||||||
|
val invitedRooms = listOf<RoomSummary>(mockk())
|
||||||
|
fakeActiveSessionHolder.fakeSession
|
||||||
|
.fakeRoomService
|
||||||
|
.givenGetRoomSummaries(invitedRooms)
|
||||||
|
fakeAutoAcceptInvites._isEnabled = false
|
||||||
|
val expectedNotificationCount = RoomAggregateNotificationCount(
|
||||||
|
notificationCount = notificationCount.notificationCount + invitedRooms.size,
|
||||||
|
highlightCount = notificationCount.highlightCount + invitedRooms.size,
|
||||||
|
)
|
||||||
|
|
||||||
|
// When
|
||||||
|
val testObserver = getNotificationCountForSpacesUseCase.execute(spaceFilter).test(this)
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
// Then
|
||||||
|
testObserver
|
||||||
|
.assertValues(expectedNotificationCount)
|
||||||
|
.finish()
|
||||||
|
verify {
|
||||||
|
fakeActiveSessionHolder.fakeSession.fakeRoomService.getRoomSummaries(
|
||||||
|
queryParams = match { it.memberships == listOf(Membership.INVITE) }
|
||||||
|
)
|
||||||
|
fakeActiveSessionHolder.fakeSession.fakeRoomService.getNotificationCountForRooms(
|
||||||
|
queryParams = match { it.memberships == listOf(Membership.JOIN) && it.spaceFilter == spaceFilter }
|
||||||
|
)
|
||||||
|
fakeActiveSessionHolder.fakeSession.fakeRoomService.getPagedRoomSummariesLive(
|
||||||
|
queryParams = match { it.memberships == listOf(Membership.JOIN) && it.spaceFilter == spaceFilter },
|
||||||
|
pagedListConfig = any(),
|
||||||
|
sortOrder = RoomSortOrder.NONE,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given no active session when execute then empty flow is returned`() = runTest {
|
||||||
|
// given
|
||||||
|
val spaceFilter = SpaceFilter.NoFilter
|
||||||
|
fakeActiveSessionHolder.givenGetSafeActiveSessionReturns(null)
|
||||||
|
|
||||||
|
// When
|
||||||
|
val testObserver = getNotificationCountForSpacesUseCase.execute(spaceFilter).test(this)
|
||||||
|
|
||||||
|
// Then
|
||||||
|
testObserver
|
||||||
|
.assertNoValues()
|
||||||
|
.finish()
|
||||||
|
verify(inverse = true) {
|
||||||
|
fakeActiveSessionHolder.fakeSession.fakeRoomService.getPagedRoomSummariesLive(
|
||||||
|
queryParams = any(),
|
||||||
|
pagedListConfig = any(),
|
||||||
|
sortOrder = any(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -20,6 +20,7 @@ import androidx.lifecycle.LiveData
|
|||||||
import androidx.lifecycle.asFlow
|
import androidx.lifecycle.asFlow
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockkStatic
|
import io.mockk.mockkStatic
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.flowOf
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
|
||||||
class FakeFlowLiveDataConversions {
|
class FakeFlowLiveDataConversions {
|
||||||
@ -28,6 +29,8 @@ class FakeFlowLiveDataConversions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> LiveData<T>.givenAsFlow() {
|
fun <T> LiveData<T>.givenAsFlow(): Flow<T> {
|
||||||
every { asFlow() } returns flowOf(value!!)
|
return flowOf(value!!).also {
|
||||||
|
every { asFlow() } returns it
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,10 +16,14 @@
|
|||||||
|
|
||||||
package im.vector.app.test.fakes
|
package im.vector.app.test.fakes
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.paging.PagedList
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import org.matrix.android.sdk.api.session.room.RoomService
|
import org.matrix.android.sdk.api.session.room.RoomService
|
||||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount
|
||||||
|
|
||||||
class FakeRoomService(
|
class FakeRoomService(
|
||||||
private val fakeRoom: FakeRoom = FakeRoom()
|
private val fakeRoom: FakeRoom = FakeRoom()
|
||||||
@ -34,4 +38,18 @@ class FakeRoomService(
|
|||||||
fun set(roomSummary: RoomSummary?) {
|
fun set(roomSummary: RoomSummary?) {
|
||||||
every { getRoomSummary(any()) } returns roomSummary
|
every { getRoomSummary(any()) } returns roomSummary
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun givenGetPagedRoomSummariesLiveReturns(pagedList: PagedList<RoomSummary>): LiveData<PagedList<RoomSummary>> {
|
||||||
|
return MutableLiveData(pagedList).also {
|
||||||
|
every { getPagedRoomSummariesLive(queryParams = any(), pagedListConfig = any(), sortOrder = any()) } returns it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun givenGetNotificationCountForRoomsReturns(roomAggregateNotificationCount: RoomAggregateNotificationCount) {
|
||||||
|
every { getNotificationCountForRooms(queryParams = any()) } returns roomAggregateNotificationCount
|
||||||
|
}
|
||||||
|
|
||||||
|
fun givenGetRoomSummaries(roomSummaries: List<RoomSummary>) {
|
||||||
|
every { getRoomSummaries(any()) } returns roomSummaries
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,7 @@ class FakeSession(
|
|||||||
val fakeUserService: FakeUserService = FakeUserService(),
|
val fakeUserService: FakeUserService = FakeUserService(),
|
||||||
private val fakeEventService: FakeEventService = FakeEventService(),
|
private val fakeEventService: FakeEventService = FakeEventService(),
|
||||||
val fakeSessionAccountDataService: FakeSessionAccountDataService = FakeSessionAccountDataService(),
|
val fakeSessionAccountDataService: FakeSessionAccountDataService = FakeSessionAccountDataService(),
|
||||||
|
val fakeSpaceService: FakeSpaceService = FakeSpaceService(),
|
||||||
) : Session by mockk(relaxed = true) {
|
) : Session by mockk(relaxed = true) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -66,6 +67,7 @@ class FakeSession(
|
|||||||
override fun pushersService() = fakePushersService
|
override fun pushersService() = fakePushersService
|
||||||
override fun accountDataService() = fakeSessionAccountDataService
|
override fun accountDataService() = fakeSessionAccountDataService
|
||||||
override fun userService() = fakeUserService
|
override fun userService() = fakeUserService
|
||||||
|
override fun spaceService() = fakeSpaceService
|
||||||
|
|
||||||
fun givenVectorStore(vectorSessionStore: VectorSessionStore) {
|
fun givenVectorStore(vectorSessionStore: VectorSessionStore) {
|
||||||
coEvery {
|
coEvery {
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2023 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.test.fakes
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||||
|
import org.matrix.android.sdk.api.session.space.SpaceService
|
||||||
|
|
||||||
|
class FakeSpaceService : SpaceService by mockk() {
|
||||||
|
|
||||||
|
fun givenGetSpaceSummariesLiveReturns(roomSummaries: List<RoomSummary>): LiveData<List<RoomSummary>> {
|
||||||
|
return MutableLiveData(roomSummaries).also {
|
||||||
|
every { getSpaceSummariesLive(any()) } returns it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun givenGetSpaceSummariesReturns(roomSummaries: List<RoomSummary>) {
|
||||||
|
every { getSpaceSummaries(any()) } returns roomSummaries
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user