Merge pull request #2975 from vector-im/feature/bma/nsfw_filter

Basic filtering for "nsfw" keyword
This commit is contained in:
Benoit Marty 2021-03-09 15:41:27 +01:00 committed by GitHub
commit eaf27a01e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 103 additions and 69 deletions

View File

@ -16,11 +16,9 @@
package org.matrix.android.sdk.api.session.room
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsResponse
import org.matrix.android.sdk.api.util.Cancelable
/**
* This interface defines methods to get and join public rooms. It's implemented at the session level.
@ -30,9 +28,8 @@ interface RoomDirectoryService {
/**
* Get rooms from directory
*/
fun getPublicRooms(server: String?,
publicRoomsParams: PublicRoomsParams,
callback: MatrixCallback<PublicRoomsResponse>): Cancelable
suspend fun getPublicRooms(server: String?,
publicRoomsParams: PublicRoomsParams): PublicRoomsResponse
/**
* Get the visibility of a room in the directory

View File

@ -16,33 +16,24 @@
package org.matrix.android.sdk.internal.session.room
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.session.room.RoomDirectoryService
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsResponse
import org.matrix.android.sdk.api.util.Cancelable
import org.matrix.android.sdk.internal.session.room.directory.GetPublicRoomTask
import org.matrix.android.sdk.internal.session.room.directory.GetRoomDirectoryVisibilityTask
import org.matrix.android.sdk.internal.session.room.directory.SetRoomDirectoryVisibilityTask
import org.matrix.android.sdk.internal.task.TaskExecutor
import org.matrix.android.sdk.internal.task.configureWith
import javax.inject.Inject
internal class DefaultRoomDirectoryService @Inject constructor(
private val getPublicRoomTask: GetPublicRoomTask,
private val getRoomDirectoryVisibilityTask: GetRoomDirectoryVisibilityTask,
private val setRoomDirectoryVisibilityTask: SetRoomDirectoryVisibilityTask,
private val taskExecutor: TaskExecutor) : RoomDirectoryService {
private val setRoomDirectoryVisibilityTask: SetRoomDirectoryVisibilityTask
) : RoomDirectoryService {
override fun getPublicRooms(server: String?,
publicRoomsParams: PublicRoomsParams,
callback: MatrixCallback<PublicRoomsResponse>): Cancelable {
return getPublicRoomTask
.configureWith(GetPublicRoomTask.Params(server, publicRoomsParams)) {
this.callback = callback
}
.executeBy(taskExecutor)
override suspend fun getPublicRooms(server: String?,
publicRoomsParams: PublicRoomsParams): PublicRoomsResponse {
return getPublicRoomTask.execute(GetPublicRoomTask.Params(server, publicRoomsParams))
}
override suspend fun getRoomDirectoryVisibility(roomId: String): RoomDirectoryVisibility {

View File

@ -29,7 +29,7 @@ data class PublicRoomsViewState(
// Store cumul of pagination result
val publicRooms: List<PublicRoom> = emptyList(),
// Current pagination request
val asyncPublicRoomsRequest: Async<List<PublicRoom>> = Uninitialized,
val asyncPublicRoomsRequest: Async<Unit> = Uninitialized,
// True if more result are available server side
val hasMore: Boolean = false,
// Set of joined roomId,

View File

@ -16,6 +16,7 @@
package im.vector.app.features.roomdirectory
import androidx.lifecycle.viewModelScope
import com.airbnb.mvrx.ActivityViewModelContext
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
@ -24,9 +25,12 @@ import com.airbnb.mvrx.Success
import com.airbnb.mvrx.ViewModelContext
import com.airbnb.mvrx.appendAt
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.settings.VectorPreferences
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.failure.Failure
@ -34,18 +38,17 @@ import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsFilter
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsParams
import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoomsResponse
import org.matrix.android.sdk.api.session.room.model.thirdparty.RoomDirectoryData
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
import org.matrix.android.sdk.api.util.Cancelable
import org.matrix.android.sdk.rx.rx
import timber.log.Timber
import java.util.Locale
private const val PUBLIC_ROOMS_LIMIT = 20
class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState: PublicRoomsViewState,
private val session: Session)
: VectorViewModel<PublicRoomsViewState, RoomDirectoryAction, RoomDirectoryViewEvents>(initialState) {
class RoomDirectoryViewModel @AssistedInject constructor(
@Assisted initialState: PublicRoomsViewState,
vectorPreferences: VectorPreferences,
private val session: Session
) : VectorViewModel<PublicRoomsViewState, RoomDirectoryAction, RoomDirectoryViewEvents>(initialState) {
@AssistedFactory
interface Factory {
@ -53,6 +56,12 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
}
companion object : MvRxViewModelFactory<RoomDirectoryViewModel, PublicRoomsViewState> {
private const val PUBLIC_ROOMS_LIMIT = 20
// List of forbidden terms, in lower case
private val explicitContentTerms = listOf(
"nsfw"
)
@JvmStatic
override fun create(viewModelContext: ViewModelContext, state: PublicRoomsViewState): RoomDirectoryViewModel? {
@ -61,9 +70,11 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
}
}
private val showAllRooms = vectorPreferences.showAllPublicRooms()
private var since: String? = null
private var currentTask: Cancelable? = null
private var currentJob: Job? = null
init {
// Observe joined room (from the sync)
@ -122,7 +133,7 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
private fun filterWith(action: RoomDirectoryAction.FilterWith) = withState { state ->
if (state.currentFilter != action.filter) {
currentTask?.cancel()
currentJob?.cancel()
reset(action.filter)
load(action.filter, state.roomDirectoryData)
@ -144,7 +155,7 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
}
private fun loadMore() = withState { state ->
if (currentTask == null) {
if (currentJob == null) {
setState {
copy(
asyncPublicRoomsRequest = Loading()
@ -155,47 +166,60 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
}
private fun load(filter: String, roomDirectoryData: RoomDirectoryData) {
currentTask = session.getPublicRooms(roomDirectoryData.homeServer,
PublicRoomsParams(
limit = PUBLIC_ROOMS_LIMIT,
filter = PublicRoomsFilter(searchTerm = filter),
includeAllNetworks = roomDirectoryData.includeAllNetworks,
since = since,
thirdPartyInstanceId = roomDirectoryData.thirdPartyInstanceId
),
object : MatrixCallback<PublicRoomsResponse> {
override fun onSuccess(data: PublicRoomsResponse) {
currentTask = null
currentJob = viewModelScope.launch {
val data = try {
session.getPublicRooms(roomDirectoryData.homeServer,
PublicRoomsParams(
limit = PUBLIC_ROOMS_LIMIT,
filter = PublicRoomsFilter(searchTerm = filter),
includeAllNetworks = roomDirectoryData.includeAllNetworks,
since = since,
thirdPartyInstanceId = roomDirectoryData.thirdPartyInstanceId
)
)
} catch (failure: Throwable) {
if (failure is Failure.Cancelled) {
// Ignore, another request should be already started
return@launch
}
since = data.nextBatch
currentJob = null
setState {
copy(
asyncPublicRoomsRequest = Success(data.chunk!!),
// It's ok to append at the end of the list, so I use publicRooms.size()
publicRooms = publicRooms.appendAt(data.chunk!!, publicRooms.size)
// Rageshake #8206 tells that we can have several times the same room
.distinctBy { it.roomId },
hasMore = since != null
)
}
setState {
copy(
asyncPublicRoomsRequest = Fail(failure)
)
}
null
}
data ?: return@launch
currentJob = null
since = data.nextBatch
// Filter
val newPublicRooms = data.chunk.orEmpty()
.filter {
showAllRooms
|| "${it.name.orEmpty()} ${it.topic.orEmpty()} ${it.canonicalAlias.orEmpty()}".toLowerCase(Locale.ROOT)
.let { str ->
explicitContentTerms.all { term -> term !in str }
}
}
override fun onFailure(failure: Throwable) {
if (failure is Failure.Cancelled) {
// Ignore, another request should be already started
return
}
currentTask = null
setState {
copy(
asyncPublicRoomsRequest = Fail(failure)
)
}
}
})
setState {
copy(
asyncPublicRoomsRequest = Success(Unit),
// It's ok to append at the end of the list, so I use publicRooms.size()
publicRooms = publicRooms.appendAt(newPublicRooms, publicRooms.size)
// Rageshake #8206 tells that we can have several times the same room
.distinctBy { it.roomId },
hasMore = since != null
)
}
}
}
private fun joinRoom(action: RoomDirectoryAction.JoinRoom) = withState { state ->
@ -222,7 +246,7 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
}
override fun onCleared() {
currentTask?.cancel()
currentJob?.cancel()
super.onCleared()
}
}

View File

@ -100,6 +100,9 @@ class VectorPreferences @Inject constructor(private val context: Context) {
private const val SETTINGS_ENABLE_CHAT_EFFECTS = "SETTINGS_ENABLE_CHAT_EFFECTS"
private const val SETTINGS_SHOW_EMOJI_KEYBOARD = "SETTINGS_SHOW_EMOJI_KEYBOARD"
// Room directory
private const val SETTINGS_ROOM_DIRECTORY_SHOW_ALL_PUBLIC_ROOMS = "SETTINGS_ROOM_DIRECTORY_SHOW_ALL_PUBLIC_ROOMS"
// Help
private const val SETTINGS_SHOULD_SHOW_HELP_ON_ROOM_LIST_KEY = "SETTINGS_SHOULD_SHOW_HELP_ON_ROOM_LIST_KEY"
@ -399,6 +402,13 @@ class VectorPreferences @Inject constructor(private val context: Context) {
return defaultPrefs.getBoolean(SETTINGS_ENABLE_SEND_VOICE_FEATURE_PREFERENCE_KEY, false)
}
/**
* Show all rooms in room directory
*/
fun showAllPublicRooms(): Boolean {
return defaultPrefs.getBoolean(SETTINGS_ROOM_DIRECTORY_SHOW_ALL_PUBLIC_ROOMS, false)
}
/**
* Tells which compression level to use by default
*

View File

@ -497,6 +497,10 @@
<item quantity="other">%d users</item>
</plurals>
<string name="settings_category_room_directory">Room directory</string>
<string name="settings_room_directory_show_all_rooms">Show rooms with explicit content</string>
<string name="settings_room_directory_show_all_rooms_summary">Show all rooms in the room directory, including rooms with explicit content.</string>
<!-- Groups fragment -->
<string name="groups_invite_header">Invite</string>
<string name="groups_header">Communities</string>

View File

@ -177,4 +177,12 @@
</im.vector.app.core.preference.VectorPreferenceCategory>
<im.vector.app.core.preference.VectorPreferenceCategory android:title="@string/settings_category_room_directory">
<im.vector.app.core.preference.VectorSwitchPreference
android:key="SETTINGS_ROOM_DIRECTORY_SHOW_ALL_PUBLIC_ROOMS"
android:summary="@string/settings_room_directory_show_all_rooms_summary"
android:title="@string/settings_room_directory_show_all_rooms" />
</im.vector.app.core.preference.VectorPreferenceCategory>
</androidx.preference.PreferenceScreen>