Do not upload file to big for the homeserver (#587)

Also create a HomeServerCapabilitiesService which provide configuration of the homeserver.
Data are retrieved every 8 hours (as RiotWeb?)
This commit is contained in:
Benoit Marty 2019-09-25 16:59:50 +02:00
parent 1b66d1f746
commit a0b1ef3216
22 changed files with 463 additions and 7 deletions

View File

@ -6,12 +6,13 @@ Features:
Improvements:
- Persist active tab between sessions (#503)
- Do not upload file to big for the homeserver (#587)
Other changes:
-
Bugfix:
-
- Fix issue on upload error in loop (#587)
Translations:
-

View File

@ -26,6 +26,7 @@ import im.vector.matrix.android.api.session.content.ContentUrlResolver
import im.vector.matrix.android.api.session.crypto.CryptoService
import im.vector.matrix.android.api.session.file.FileService
import im.vector.matrix.android.api.session.group.GroupService
import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilitiesService
import im.vector.matrix.android.api.session.pushers.PushersService
import im.vector.matrix.android.api.session.room.RoomDirectoryService
import im.vector.matrix.android.api.session.room.RoomService
@ -52,6 +53,7 @@ interface Session :
PushRuleService,
PushersService,
InitialSyncProgressService,
HomeServerCapabilitiesService,
SecureStorageService {
/**

View File

@ -0,0 +1,28 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.api.session.homeserver
data class HomeServerCapabilities(
/**
* Max size of file which can be uploaded to the homeserver in bytes. [MAX_UPLOAD_FILE_SIZE_UNKNOWN] if unknown or not retrieved yet
*/
val maxUploadFileSize: Long
) {
companion object {
const val MAX_UPLOAD_FILE_SIZE_UNKNOWN = -1L
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.api.session.homeserver
/**
* This interface defines a method to sign out. It's implemented at the session level.
*/
interface HomeServerCapabilitiesService {
/**
* Get the HomeServer capabilities
*/
fun getHomeServerCapabilities(): HomeServerCapabilities
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.internal.database.mapper
import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilities
import im.vector.matrix.android.internal.database.model.HomeServerCapabilitiesEntity
/**
* HomeServerCapabilitiesEntity <-> HomeSeverCapabilities
*/
internal object HomeServerCapabilitiesMapper {
fun map(entity: HomeServerCapabilitiesEntity): HomeServerCapabilities {
return HomeServerCapabilities(
entity.maxUploadFileSize
)
}
fun map(domain: HomeServerCapabilities): HomeServerCapabilitiesEntity {
return HomeServerCapabilitiesEntity(
domain.maxUploadFileSize
)
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.internal.database.model
import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilities
import io.realm.RealmObject
internal open class HomeServerCapabilitiesEntity(
var maxUploadFileSize: Long = HomeServerCapabilities.MAX_UPLOAD_FILE_SIZE_UNKNOWN,
var lastUpdatedTimestamp: Long = 0L
) : RealmObject() {
companion object
}

View File

@ -45,6 +45,7 @@ import io.realm.annotations.RealmModule
PusherDataEntity::class,
ReadReceiptsSummaryEntity::class,
UserDraftsEntity::class,
DraftEntity::class
DraftEntity::class,
HomeServerCapabilitiesEntity::class
])
internal class SessionRealmModule

View File

@ -0,0 +1,37 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.internal.database.query
import im.vector.matrix.android.internal.database.model.HomeServerCapabilitiesEntity
import io.realm.Realm
import io.realm.kotlin.createObject
import io.realm.kotlin.where
/**
* Get the current HomeServerCapabilitiesEntity, create one if it does not exist
*/
internal fun HomeServerCapabilitiesEntity.Companion.getOrCreate(realm: Realm): HomeServerCapabilitiesEntity {
var homeServerCapabilitiesEntity = realm.where<HomeServerCapabilitiesEntity>().findFirst()
if (homeServerCapabilitiesEntity == null) {
realm.executeTransaction {
realm.createObject<HomeServerCapabilitiesEntity>()
}
homeServerCapabilitiesEntity = realm.where<HomeServerCapabilitiesEntity>().findFirst()!!
}
return homeServerCapabilitiesEntity
}

View File

@ -22,4 +22,9 @@ internal object NetworkConstants {
const val URI_API_PREFIX_PATH_R0 = "$URI_API_PREFIX_PATH/r0/"
const val URI_API_PREFIX_PATH_UNSTABLE = "$URI_API_PREFIX_PATH/unstable/"
// Media
private const val URI_API_MEDIA_PREFIX_PATH = "_matrix/media"
const val URI_API_MEDIA_PREFIX_PATH_R0 = "$URI_API_MEDIA_PREFIX_PATH/r0/"
}

View File

@ -32,6 +32,7 @@ import im.vector.matrix.android.api.session.content.ContentUrlResolver
import im.vector.matrix.android.api.session.crypto.CryptoService
import im.vector.matrix.android.api.session.file.FileService
import im.vector.matrix.android.api.session.group.GroupService
import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilitiesService
import im.vector.matrix.android.api.session.pushers.PushersService
import im.vector.matrix.android.api.session.room.RoomDirectoryService
import im.vector.matrix.android.api.session.room.RoomService
@ -68,7 +69,8 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se
private val syncThreadProvider: Provider<SyncThread>,
private val contentUrlResolver: ContentUrlResolver,
private val contentUploadProgressTracker: ContentUploadStateTracker,
private val initialSyncProgressService: Lazy<InitialSyncProgressService>)
private val initialSyncProgressService: Lazy<InitialSyncProgressService>,
private val homeServerCapabilitiesService: Lazy<HomeServerCapabilitiesService>)
: Session,
RoomService by roomService.get(),
RoomDirectoryService by roomDirectoryService.get(),
@ -81,7 +83,8 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se
PushersService by pushersService.get(),
FileService by fileService.get(),
InitialSyncProgressService by initialSyncProgressService.get(),
SecureStorageService by secureStorageService.get() {
SecureStorageService by secureStorageService.get(),
HomeServerCapabilitiesService by homeServerCapabilitiesService.get() {
private var isOpen = false

View File

@ -30,6 +30,7 @@ import im.vector.matrix.android.internal.session.content.UploadContentWorker
import im.vector.matrix.android.internal.session.filter.FilterModule
import im.vector.matrix.android.internal.session.group.GetGroupDataWorker
import im.vector.matrix.android.internal.session.group.GroupModule
import im.vector.matrix.android.internal.session.homeserver.HomeServerCapabilitiesModule
import im.vector.matrix.android.internal.session.pushers.AddHttpPusherWorker
import im.vector.matrix.android.internal.session.pushers.PushersModule
import im.vector.matrix.android.internal.session.room.RoomModule
@ -51,6 +52,7 @@ import im.vector.matrix.android.internal.task.TaskExecutor
SessionModule::class,
RoomModule::class,
SyncModule::class,
HomeServerCapabilitiesModule::class,
SignOutModule::class,
GroupModule::class,
UserModule::class,

View File

@ -27,6 +27,7 @@ import im.vector.matrix.android.api.auth.data.HomeServerConnectionConfig
import im.vector.matrix.android.api.auth.data.SessionParams
import im.vector.matrix.android.api.session.InitialSyncProgressService
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilitiesService
import im.vector.matrix.android.api.session.securestorage.SecureStorageService
import im.vector.matrix.android.internal.database.LiveEntityObserver
import im.vector.matrix.android.internal.database.RealmKeysUtils
@ -36,6 +37,7 @@ import im.vector.matrix.android.internal.network.AccessTokenInterceptor
import im.vector.matrix.android.internal.network.RetrofitFactory
import im.vector.matrix.android.internal.network.interceptors.CurlLoggingInterceptor
import im.vector.matrix.android.internal.session.group.GroupSummaryUpdater
import im.vector.matrix.android.internal.session.homeserver.DefaultHomeServerCapabilitiesService
import im.vector.matrix.android.internal.session.room.EventRelationsAggregationUpdater
import im.vector.matrix.android.internal.session.room.create.RoomCreateEventLiveObserver
import im.vector.matrix.android.internal.session.room.prune.EventsPruner
@ -178,4 +180,7 @@ internal abstract class SessionModule {
@Binds
abstract fun bindSecureStorageService(secureStorageService: DefaultSecureStorageService): SecureStorageService
@Binds
abstract fun bindHomeServerCapabilitiesService(homeServerCapabilitiesService: DefaultHomeServerCapabilitiesService): HomeServerCapabilitiesService
}

View File

@ -0,0 +1,31 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.internal.session.homeserver
import im.vector.matrix.android.internal.network.NetworkConstants
import retrofit2.Call
import retrofit2.http.GET
internal interface CapabilitiesAPI {
/**
* Request the upload capabilities
*/
@GET(NetworkConstants.URI_API_MEDIA_PREFIX_PATH_R0 + "config")
fun getUploadCapabilities(): Call<GetUploadCapabilitiesResult>
}

View File

@ -0,0 +1,74 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.internal.session.homeserver
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilities
import im.vector.matrix.android.internal.database.model.HomeServerCapabilitiesEntity
import im.vector.matrix.android.internal.database.query.getOrCreate
import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.task.Task
import java.util.*
import javax.inject.Inject
internal interface GetHomeServerCapabilitiesTask : Task<Unit, Unit>
internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
private val capabilitiesAPI: CapabilitiesAPI,
private val monarchy: Monarchy
) : GetHomeServerCapabilitiesTask {
override suspend fun execute(params: Unit) {
var doRequest = false
monarchy.doWithRealm { realm ->
val homeServerCapabilitiesEntity = HomeServerCapabilitiesEntity.getOrCreate(realm)
doRequest = homeServerCapabilitiesEntity.lastUpdatedTimestamp + MIN_DELAY_BETWEEN_TWO_REQUEST_MILLIS < Date().time
}
if (!doRequest) {
return
}
val uploadCapabilities = executeRequest<GetUploadCapabilitiesResult> {
apiCall = capabilitiesAPI.getUploadCapabilities()
}
// TODO Add other call here (get version, etc.)
insertInDb(uploadCapabilities)
}
private fun insertInDb(getUploadCapabilitiesResult: GetUploadCapabilitiesResult) {
monarchy
.writeAsync { realm ->
val homeServerCapabilitiesEntity = HomeServerCapabilitiesEntity.getOrCreate(realm)
homeServerCapabilitiesEntity.maxUploadFileSize = getUploadCapabilitiesResult.maxUploadSize
?: HomeServerCapabilities.MAX_UPLOAD_FILE_SIZE_UNKNOWN
homeServerCapabilitiesEntity.lastUpdatedTimestamp = Date().time
}
}
companion object {
// 8 hours like on Riot Web
private const val MIN_DELAY_BETWEEN_TWO_REQUEST_MILLIS = 8 * 60 * 60 * 1000
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.internal.session.homeserver
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilities
import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilitiesService
import im.vector.matrix.android.internal.database.mapper.HomeServerCapabilitiesMapper
import im.vector.matrix.android.internal.database.model.HomeServerCapabilitiesEntity
import im.vector.matrix.android.internal.database.query.getOrCreate
import javax.inject.Inject
internal class DefaultHomeServerCapabilitiesService @Inject constructor(private val monarchy: Monarchy) : HomeServerCapabilitiesService {
override fun getHomeServerCapabilities(): HomeServerCapabilities {
var entity: HomeServerCapabilitiesEntity? = null
monarchy.doWithRealm { realm ->
entity = HomeServerCapabilitiesEntity.getOrCreate(realm)
}
return entity?.let { HomeServerCapabilitiesMapper.map(it) } ?: HomeServerCapabilities(HomeServerCapabilities.MAX_UPLOAD_FILE_SIZE_UNKNOWN)
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.internal.session.homeserver
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class GetUploadCapabilitiesResult(
/**
* The maximum size an upload can be in bytes. Clients SHOULD use this as a guide when uploading content.
* If not listed or null, the size limit should be treated as unknown.
*/
@Json(name = "m.upload.size")
val maxUploadSize: Long? = null
)

View File

@ -0,0 +1,41 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.internal.session.homeserver
import dagger.Binds
import dagger.Module
import dagger.Provides
import im.vector.matrix.android.internal.session.SessionScope
import retrofit2.Retrofit
@Module
internal abstract class HomeServerCapabilitiesModule {
@Module
companion object {
@Provides
@JvmStatic
@SessionScope
fun providesCapabilitiesAPI(retrofit: Retrofit): CapabilitiesAPI {
return retrofit.create(CapabilitiesAPI::class.java)
}
}
@Binds
abstract fun bindGetHomeServerCapabilitiesTask(getHomeServerCapabilitiesTask: DefaultGetHomeServerCapabilitiesTask): GetHomeServerCapabilitiesTask
}

View File

@ -16,7 +16,6 @@
package im.vector.matrix.android.internal.session.sync
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.R
import im.vector.matrix.android.api.failure.Failure
import im.vector.matrix.android.api.failure.MatrixError
@ -25,6 +24,7 @@ import im.vector.matrix.android.internal.di.UserId
import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.session.DefaultInitialSyncProgressService
import im.vector.matrix.android.internal.session.filter.FilterRepository
import im.vector.matrix.android.internal.session.homeserver.GetHomeServerCapabilitiesTask
import im.vector.matrix.android.internal.session.sync.model.SyncResponse
import im.vector.matrix.android.internal.task.Task
import javax.inject.Inject
@ -42,11 +42,14 @@ internal class DefaultSyncTask @Inject constructor(private val syncAPI: SyncAPI,
private val sessionParamsStore: SessionParamsStore,
private val initialSyncProgressService: DefaultInitialSyncProgressService,
private val syncTokenStore: SyncTokenStore,
private val monarchy: Monarchy
private val getHomeServerCapabilitiesTask: GetHomeServerCapabilitiesTask
) : SyncTask {
override suspend fun execute(params: SyncTask.Params) {
// Maybe refresh the home server capabilities data we know
getHomeServerCapabilitiesTask.execute(Unit)
val requestParams = HashMap<String, String>()
var timeout = 0L
val token = syncTokenStore.getLastToken()

View File

@ -0,0 +1,23 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotx.features.home.room.detail
data class FileTooBigError(
val filename: String,
val fileSizeInBytes: Long,
val homeServerLimitInBytes: Long
)

View File

@ -28,6 +28,7 @@ import android.os.Parcelable
import android.text.Editable
import android.text.Spannable
import android.text.TextUtils
import android.text.format.Formatter
import android.view.*
import android.view.inputmethod.InputMethodManager
import android.widget.TextView
@ -227,6 +228,10 @@ class RoomDetailFragment :
scrollOnHighlightedEventCallback.scheduleScrollTo(it)
}
roomDetailViewModel.fileTooBigEvent.observeEvent(this) {
displayFileTooBigWarning(it)
}
roomDetailViewModel.selectSubscribe(this, RoomDetailViewState::tombstoneEventHandling, uniqueOnly("tombstoneEventHandling")) {
renderTombstoneEventHandling(it)
}
@ -254,6 +259,18 @@ class RoomDetailFragment :
}
}
private fun displayFileTooBigWarning(error: FileTooBigError) {
AlertDialog.Builder(requireActivity())
.setTitle(R.string.dialog_title_error)
.setMessage(getString(R.string.error_file_too_big,
error.filename,
Formatter.formatFileSize(requireContext(), error.homeServerLimitInBytes),
Formatter.formatFileSize(requireContext(), error.fileSizeInBytes)
))
.setPositiveButton(R.string.ok, null)
.show()
}
private fun setupNotificationView() {
notificationAreaView.delegate = object : NotificationAreaView.Delegate {

View File

@ -37,6 +37,7 @@ import im.vector.matrix.android.api.session.events.model.isImageMessage
import im.vector.matrix.android.api.session.events.model.isTextMessage
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.file.FileService
import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilities
import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.model.message.MessageContent
import im.vector.matrix.android.api.session.room.model.message.MessageType
@ -228,6 +229,10 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
val navigateToEvent: LiveData<LiveEvent<String>>
get() = _navigateToEvent
private val _fileTooBigEvent = MutableLiveData<LiveEvent<FileTooBigError>>()
val fileTooBigEvent: LiveData<LiveEvent<FileTooBigError>>
get() = _fileTooBigEvent
private val _downloadedFileEvent = MutableLiveData<LiveEvent<DownloadFileState>>()
val downloadedFileEvent: LiveData<LiveEvent<DownloadFileState>>
get() = _downloadedFileEvent
@ -466,7 +471,20 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
type = ContentAttachmentData.Type.values()[it.mediaType]
)
}
room.sendMedias(attachments)
val homeServerCapabilities = session.getHomeServerCapabilities()
val maxUploadFileSize = homeServerCapabilities.maxUploadFileSize
if (maxUploadFileSize == HomeServerCapabilities.MAX_UPLOAD_FILE_SIZE_UNKNOWN) {
// Unknown limitation
room.sendMedias(attachments)
} else {
attachments.find { it.size > maxUploadFileSize }
?.let {
_fileTooBigEvent.postValue(LiveEvent(FileTooBigError(it.name ?: it.path, it.size, maxUploadFileSize)))
} ?: run { room.sendMedias(attachments) }
}
}
private fun handleEventDisplayed(action: RoomDetailActions.EventDisplayed) {

View File

@ -22,4 +22,6 @@
<string name="a11y_create_room">Create a new room</string>
<string name="a11y_close_keys_backup_banner">Close keys backup banner</string>
<string name="error_file_too_big">The file %1$s is too large to upload. The file size limit is %2$s but this file is %3$s.</string>
</resources>