ganfra
6 months ago
12 changed files with 411 additions and 0 deletions
@ -0,0 +1,36 @@
@@ -0,0 +1,36 @@
|
||||
/* |
||||
* Copyright (c) 2024 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 io.element.android.libraries.matrix.api.roomdirectory |
||||
|
||||
import io.element.android.libraries.matrix.api.core.RoomId |
||||
|
||||
data class RoomDescription( |
||||
val roomId: RoomId, |
||||
val name: String?, |
||||
val topic: String?, |
||||
val alias: String?, |
||||
val avatarUrl: String?, |
||||
val joinRule: JoinRule, |
||||
val isWorldReadable: Boolean, |
||||
val joinedMembers: Long |
||||
) { |
||||
enum class JoinRule { |
||||
PUBLIC, |
||||
KNOCK, |
||||
UNKNOWN |
||||
} |
||||
} |
@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
|
||||
/* |
||||
* Copyright (c) 2024 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 io.element.android.libraries.matrix.api.roomdirectory |
||||
|
||||
import kotlinx.coroutines.flow.SharedFlow |
||||
|
||||
interface RoomDirectorySearch { |
||||
suspend fun updateQuery(query: String?, batchSize: Int) |
||||
suspend fun loadMore() |
||||
suspend fun hasMoreToLoad(): Boolean |
||||
val results: SharedFlow<List<RoomDescription>> |
||||
} |
@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
|
||||
/* |
||||
* Copyright (c) 2024 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 io.element.android.libraries.matrix.api.roomdirectory |
||||
|
||||
interface RoomDirectoryService { |
||||
fun search(): RoomDirectorySearch |
||||
} |
@ -0,0 +1,42 @@
@@ -0,0 +1,42 @@
|
||||
/* |
||||
* Copyright (c) 2024 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 io.element.android.libraries.matrix.impl.roomdirectory |
||||
|
||||
import io.element.android.libraries.matrix.api.core.RoomId |
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDescription |
||||
import org.matrix.rustcomponents.sdk.PublicRoomJoinRule |
||||
import org.matrix.rustcomponents.sdk.RoomDescription as RustRoomDescription |
||||
|
||||
class RoomDescriptionMapper { |
||||
|
||||
fun map(roomDescription: RustRoomDescription): RoomDescription { |
||||
return RoomDescription( |
||||
roomId = RoomId(roomDescription.roomId), |
||||
name = roomDescription.name, |
||||
topic = roomDescription.topic, |
||||
avatarUrl = roomDescription.avatarUrl, |
||||
alias = roomDescription.alias, |
||||
joinRule = when (roomDescription.joinRule) { |
||||
PublicRoomJoinRule.PUBLIC -> RoomDescription.JoinRule.PUBLIC |
||||
PublicRoomJoinRule.KNOCK -> RoomDescription.JoinRule.KNOCK |
||||
null -> RoomDescription.JoinRule.UNKNOWN |
||||
}, |
||||
isWorldReadable = roomDescription.isWorldReadable, |
||||
joinedMembers = roomDescription.joinedMembers.toLong(), |
||||
) |
||||
} |
||||
} |
@ -0,0 +1,45 @@
@@ -0,0 +1,45 @@
|
||||
/* |
||||
* Copyright (c) 2024 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 io.element.android.libraries.matrix.impl.roomdirectory |
||||
|
||||
import io.element.android.libraries.matrix.impl.util.cancelAndDestroy |
||||
import kotlinx.coroutines.channels.Channel |
||||
import kotlinx.coroutines.channels.awaitClose |
||||
import kotlinx.coroutines.channels.trySendBlocking |
||||
import kotlinx.coroutines.flow.Flow |
||||
import kotlinx.coroutines.flow.buffer |
||||
import kotlinx.coroutines.flow.callbackFlow |
||||
import kotlinx.coroutines.flow.catch |
||||
import org.matrix.rustcomponents.sdk.RoomDirectorySearch |
||||
import org.matrix.rustcomponents.sdk.RoomDirectorySearchEntriesListener |
||||
import org.matrix.rustcomponents.sdk.RoomDirectorySearchEntryUpdate |
||||
import timber.log.Timber |
||||
|
||||
internal fun RoomDirectorySearch.resultsFlow(): Flow<List<RoomDirectorySearchEntryUpdate>> = |
||||
callbackFlow { |
||||
val listener = object : RoomDirectorySearchEntriesListener { |
||||
override fun onUpdate(roomEntriesUpdate: List<RoomDirectorySearchEntryUpdate>) { |
||||
trySendBlocking(roomEntriesUpdate) |
||||
} |
||||
} |
||||
val result = results(listener) |
||||
awaitClose { |
||||
result.cancelAndDestroy() |
||||
} |
||||
}.catch { |
||||
Timber.d(it, "timelineDiffFlow() failed") |
||||
}.buffer(Channel.UNLIMITED) |
@ -0,0 +1,97 @@
@@ -0,0 +1,97 @@
|
||||
/* |
||||
* Copyright (c) 2024 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 io.element.android.libraries.matrix.impl.roomdirectory |
||||
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDescription |
||||
import kotlinx.coroutines.flow.MutableSharedFlow |
||||
import kotlinx.coroutines.sync.Mutex |
||||
import kotlinx.coroutines.sync.withLock |
||||
import kotlinx.coroutines.withContext |
||||
import org.matrix.rustcomponents.sdk.RoomDirectorySearchEntryUpdate |
||||
import timber.log.Timber |
||||
import kotlin.coroutines.CoroutineContext |
||||
|
||||
class RoomDirectorySearchProcessor( |
||||
private val roomDescriptions: MutableSharedFlow<List<RoomDescription>>, |
||||
private val coroutineContext: CoroutineContext, |
||||
private val roomDescriptionMapper: RoomDescriptionMapper, |
||||
) { |
||||
|
||||
private val mutex = Mutex() |
||||
|
||||
suspend fun postUpdates(updates: List<RoomDirectorySearchEntryUpdate>) { |
||||
updateRoomDescriptions { |
||||
Timber.v("Update room descriptions from postUpdates (with ${updates.size} items) on ${Thread.currentThread()}") |
||||
updates.forEach { update -> |
||||
applyUpdate(update) |
||||
} |
||||
} |
||||
} |
||||
|
||||
private suspend fun MutableList<RoomDescription>.applyUpdate(update: RoomDirectorySearchEntryUpdate) { |
||||
when (update) { |
||||
is RoomDirectorySearchEntryUpdate.Append -> { |
||||
val roomSummaries = update.values.map(roomDescriptionMapper::map) |
||||
addAll(roomSummaries) |
||||
} |
||||
is RoomDirectorySearchEntryUpdate.PushBack -> { |
||||
val roomSummary = roomDescriptionMapper.map(update.value) |
||||
add(roomSummary) |
||||
} |
||||
is RoomDirectorySearchEntryUpdate.PushFront -> { |
||||
val roomSummary = roomDescriptionMapper.map(update.value) |
||||
add(0, roomSummary) |
||||
} |
||||
is RoomDirectorySearchEntryUpdate.Set -> { |
||||
val roomSummary = roomDescriptionMapper.map(update.value) |
||||
this[update.index.toInt()] = roomSummary |
||||
} |
||||
is RoomDirectorySearchEntryUpdate.Insert -> { |
||||
val roomSummary = roomDescriptionMapper.map(update.value) |
||||
add(update.index.toInt(), roomSummary) |
||||
} |
||||
is RoomDirectorySearchEntryUpdate.Remove -> { |
||||
removeAt(update.index.toInt()) |
||||
} |
||||
is RoomDirectorySearchEntryUpdate.Reset -> { |
||||
clear() |
||||
addAll(update.values.map(roomDescriptionMapper::map)) |
||||
} |
||||
RoomDirectorySearchEntryUpdate.PopBack -> { |
||||
removeLastOrNull() |
||||
} |
||||
RoomDirectorySearchEntryUpdate.PopFront -> { |
||||
removeFirstOrNull() |
||||
} |
||||
RoomDirectorySearchEntryUpdate.Clear -> { |
||||
clear() |
||||
} |
||||
is RoomDirectorySearchEntryUpdate.Truncate -> { |
||||
subList(update.length.toInt(), size).clear() |
||||
} |
||||
} |
||||
} |
||||
|
||||
private suspend fun updateRoomDescriptions(block: suspend MutableList<RoomDescription>.() -> Unit) = withContext(coroutineContext) { |
||||
mutex.withLock { |
||||
val current = roomDescriptions.replayCache.lastOrNull() |
||||
val mutableRoomSummaries = current.orEmpty().toMutableList() |
||||
block(mutableRoomSummaries) |
||||
roomDescriptions.emit(mutableRoomSummaries) |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,65 @@
@@ -0,0 +1,65 @@
|
||||
/* |
||||
* Copyright (c) 2024 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 io.element.android.libraries.matrix.impl.roomdirectory |
||||
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDescription |
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectorySearch |
||||
import kotlinx.coroutines.CoroutineDispatcher |
||||
import kotlinx.coroutines.CoroutineScope |
||||
import kotlinx.coroutines.flow.MutableStateFlow |
||||
import kotlinx.coroutines.flow.SharedFlow |
||||
import kotlinx.coroutines.flow.launchIn |
||||
import kotlinx.coroutines.flow.onEach |
||||
import kotlinx.coroutines.launch |
||||
import org.matrix.rustcomponents.sdk.RoomDirectorySearch as InnerRoomDirectorySearch |
||||
|
||||
class RustRoomDirectorySearch( |
||||
private val inner: InnerRoomDirectorySearch, |
||||
private val sessionCoroutineScope: CoroutineScope, |
||||
private val sessionDispatcher: CoroutineDispatcher, |
||||
) : RoomDirectorySearch { |
||||
|
||||
private val _results: MutableStateFlow<List<RoomDescription>> = |
||||
MutableStateFlow(emptyList()) |
||||
|
||||
private val processor = RoomDirectorySearchProcessor(_results, sessionDispatcher, RoomDescriptionMapper()) |
||||
|
||||
init { |
||||
sessionCoroutineScope.launch(sessionDispatcher) { |
||||
inner |
||||
.resultsFlow() |
||||
.onEach { updates -> |
||||
processor.postUpdates(updates) |
||||
} |
||||
.launchIn(this) |
||||
} |
||||
} |
||||
|
||||
override suspend fun updateQuery(query: String?, batchSize: Int) { |
||||
inner.search(query, batchSize.toUInt()) |
||||
} |
||||
|
||||
override suspend fun loadMore() { |
||||
inner.nextPage() |
||||
} |
||||
|
||||
override suspend fun hasMoreToLoad(): Boolean { |
||||
return !inner.isAtLastPage() |
||||
} |
||||
|
||||
override val results: SharedFlow<List<RoomDescription>> = _results |
||||
} |
@ -0,0 +1,35 @@
@@ -0,0 +1,35 @@
|
||||
/* |
||||
* Copyright (c) 2024 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 io.element.android.libraries.matrix.impl.roomdirectory |
||||
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectorySearch |
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService |
||||
import kotlinx.coroutines.CoroutineDispatcher |
||||
import kotlinx.coroutines.CoroutineScope |
||||
import org.matrix.rustcomponents.sdk.Client |
||||
|
||||
class RustRoomDirectoryService( |
||||
private val client: Client, |
||||
private val sessionCoroutineScope: CoroutineScope, |
||||
private val sessionDispatcher: CoroutineDispatcher, |
||||
) : RoomDirectoryService { |
||||
|
||||
override fun search(): RoomDirectorySearch { |
||||
return RustRoomDirectorySearch(client.roomDirectorySearch(), sessionCoroutineScope, sessionDispatcher) |
||||
} |
||||
|
||||
} |
@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
|
||||
/* |
||||
* Copyright (c) 2024 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 io.element.android.libraries.matrix.test.roomdirectory |
||||
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectorySearch |
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService |
||||
|
||||
class FakeRoomDirectoryService : RoomDirectoryService { |
||||
override fun search(): RoomDirectorySearch { |
||||
TODO("Not yet implemented") |
||||
} |
||||
} |
Loading…
Reference in new issue