Compute WidgetUrl "onDemand" to properly handle the theme value

This commit is contained in:
Benoit Marty 2021-02-10 14:44:33 +01:00 committed by Benoit Marty
parent 463f2a7ad7
commit 1978a180ff
12 changed files with 73 additions and 30 deletions

View file

@ -56,6 +56,11 @@ interface WidgetService {
excludedTypes: Set<String>? = null
): List<Widget>
/**
* Return the computed URL of a widget
*/
fun getWidgetComputedUrl(widget: Widget, isLightTheme: Boolean): String?
/**
* Returns the live room widgets so you can listen to them.
* Some widgets can be deactivated, so be sure to check for isActive.

View file

@ -25,7 +25,6 @@ data class Widget(
val widgetId: String,
val senderInfo: SenderInfo?,
val isAddedByMe: Boolean,
val computedUrl: String?,
val type: WidgetType
) {

View file

@ -37,7 +37,6 @@ import org.matrix.android.sdk.internal.session.user.accountdata.AccountDataDataS
import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask
import org.matrix.android.sdk.internal.session.widgets.helper.WidgetFactory
import org.matrix.android.sdk.internal.session.widgets.helper.extractWidgetSequence
import org.matrix.android.sdk.internal.task.TaskExecutor
import timber.log.Timber
import javax.inject.Inject
@ -55,7 +54,6 @@ import javax.inject.Inject
*/
@SessionScope
internal class IntegrationManager @Inject constructor(matrixConfiguration: MatrixConfiguration,
private val taskExecutor: TaskExecutor,
@SessionDatabase private val monarchy: Monarchy,
private val updateUserAccountDataTask: UpdateUserAccountDataTask,
private val accountDataDataSource: AccountDataDataSource,

View file

@ -50,6 +50,10 @@ internal class DefaultWidgetService @Inject constructor(private val widgetManage
return widgetManager.getRoomWidgets(roomId, widgetId, widgetTypes, excludedTypes)
}
override fun getWidgetComputedUrl(widget: Widget, isLightTheme: Boolean): String? {
return widgetManager.getWidgetComputedUrl(widget, isLightTheme)
}
override fun getRoomWidgetsLive(
roomId: String,
widgetId: QueryStringValue,

View file

@ -104,6 +104,10 @@ internal class WidgetManager @Inject constructor(private val integrationManager:
return widgetEvents.mapEventsToWidgets(widgetTypes, excludedTypes)
}
fun getWidgetComputedUrl(widget: Widget, isLightTheme: Boolean): String? {
return widgetFactory.computeURL(widget, isLightTheme)
}
private fun List<Event>.mapEventsToWidgets(widgetTypes: Set<String>? = null,
excludedTypes: Set<String>? = null): List<Widget> {
val widgetEvents = this

View file

@ -55,30 +55,29 @@ internal class WidgetFactory @Inject constructor(private val userDataSource: Use
}
}
val isAddedByMe = widgetEvent.senderId == userId
val computedUrl = widgetContent.computeURL(widgetEvent.roomId, widgetId)
return Widget(
widgetContent = widgetContent,
event = widgetEvent,
widgetId = widgetId,
senderInfo = senderInfo,
isAddedByMe = isAddedByMe,
computedUrl = computedUrl,
type = WidgetType.fromString(type)
)
}
// Ref: https://github.com/matrix-org/matrix-widget-api/blob/master/src/templating/url-template.ts#L29-L33
private fun WidgetContent.computeURL(roomId: String?, widgetId: String): String? {
var computedUrl = url ?: return null
fun computeURL(widget: Widget, isLightTheme: Boolean): String? {
var computedUrl = widget.widgetContent.url ?: return null
val myUser = userDataSource.getUser(userId)
val keyValue = data.mapKeys { "\$${it.key}" }.toMutableMap()
val keyValue = widget.widgetContent.data.mapKeys { "\$${it.key}" }.toMutableMap()
keyValue[WIDGET_PATTERN_MATRIX_USER_ID] = userId
keyValue[WIDGET_PATTERN_MATRIX_DISPLAY_NAME] = myUser?.getBestName() ?: userId
keyValue[WIDGET_PATTERN_MATRIX_AVATAR_URL] = urlResolver.resolveFullSize(myUser?.avatarUrl) ?: ""
keyValue[WIDGET_PATTERN_MATRIX_WIDGET_ID] = widgetId
keyValue[WIDGET_PATTERN_MATRIX_ROOM_ID] = roomId ?: ""
keyValue[WIDGET_PATTERN_MATRIX_WIDGET_ID] = widget.widgetId
keyValue[WIDGET_PATTERN_MATRIX_ROOM_ID] = widget.event.roomId ?: ""
keyValue[WIDGET_PATTERN_THEME] = getTheme(isLightTheme)
for ((key, value) in keyValue) {
computedUrl = computedUrl.replace(key, URLEncoder.encode(value.toString(), "utf-8"))
@ -86,6 +85,10 @@ internal class WidgetFactory @Inject constructor(private val userDataSource: Use
return computedUrl
}
private fun getTheme(isLightTheme: Boolean): String {
return if (isLightTheme) "light" else "dark"
}
companion object {
// Value to be replaced in URLS
const val WIDGET_PATTERN_MATRIX_USER_ID = "\$matrix_user_id"
@ -93,5 +96,6 @@ internal class WidgetFactory @Inject constructor(private val userDataSource: Use
const val WIDGET_PATTERN_MATRIX_AVATAR_URL = "\$matrix_avatar_url"
const val WIDGET_PATTERN_MATRIX_WIDGET_ID = "\$matrix_widget_id"
const val WIDGET_PATTERN_MATRIX_ROOM_ID = "\$matrix_room_id"
const val WIDGET_PATTERN_THEME = "\$theme"
}
}

View file

@ -25,6 +25,7 @@ import dagger.assisted.AssistedInject
import dagger.assisted.AssistedFactory
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.themes.ThemeProvider
import org.jitsi.meet.sdk.JitsiMeetUserInfo
import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.Session
@ -37,7 +38,8 @@ class JitsiCallViewModel @AssistedInject constructor(
@Assisted initialState: JitsiCallViewState,
@Assisted val args: VectorJitsiActivity.Args,
private val session: Session,
private val stringProvider: StringProvider
private val stringProvider: StringProvider,
private val themeProvider: ThemeProvider
) : VectorViewModel<JitsiCallViewState, JitsiCallViewActions, JitsiCallViewEvents>(initialState) {
@AssistedFactory
@ -45,6 +47,8 @@ class JitsiCallViewModel @AssistedInject constructor(
fun create(initialState: JitsiCallViewState, args: VectorJitsiActivity.Args): JitsiCallViewModel
}
private val widgetService = session.widgetService()
init {
val me = session.getRoomMember(session.myUserId, args.roomId)?.toMatrixItem()
val userInfo = JitsiMeetUserInfo().apply {
@ -57,13 +61,14 @@ class JitsiCallViewModel @AssistedInject constructor(
copy(userInfo = userInfo)
}
session.widgetService().getRoomWidgetsLive(args.roomId, QueryStringValue.Equals(args.widgetId), WidgetType.Jitsi.values())
widgetService.getRoomWidgetsLive(args.roomId, QueryStringValue.Equals(args.widgetId), WidgetType.Jitsi.values())
.asObservable()
.distinctUntilChanged()
.subscribe {
val jitsiWidget = it.firstOrNull()
if (jitsiWidget != null) {
val ppt = jitsiWidget.computedUrl?.let { url -> JitsiWidgetProperties(url, stringProvider) }
val ppt = widgetService.getWidgetComputedUrl(jitsiWidget, themeProvider.isLightTheme())
?.let { url -> JitsiWidgetProperties(url, stringProvider) }
setState {
copy(
widget = Success(jitsiWidget),

View file

@ -32,7 +32,7 @@ class StickerPickerActionHandler @Inject constructor(private val session: Sessio
return@withContext RoomDetailViewEvents.DisplayEnableIntegrationsWarning
}
val stickerWidget = session.widgetService().getUserWidgets(WidgetType.StickerPicker.values()).firstOrNull { it.isActive }
if (stickerWidget == null || stickerWidget.computedUrl.isNullOrBlank()) {
if (stickerWidget == null || stickerWidget.widgetContent.url.isNullOrBlank()) {
RoomDetailViewEvents.DisplayPromptForIntegrationManager
} else {
RoomDetailViewEvents.OpenStickerPicker(

View file

@ -43,7 +43,7 @@ abstract class RoomWidgetItem : EpoxyModelWithHolder<RoomWidgetItem.Holder>() {
override fun bind(holder: Holder) {
super.bind(holder)
holder.widgetName.text = widget.name
holder.widgetUrl.text = tryOrNull { URL(widget.computedUrl) }?.host ?: widget.computedUrl
holder.widgetUrl.text = tryOrNull { URL(widget.widgetContent.url) }?.host ?: widget.widgetContent.url
if (iconRes != null) {
holder.iconImage.isVisible = true
holder.iconImage.setImageResource(iconRes!!)

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2021 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.themes
import android.content.Context
import javax.inject.Inject
/**
* Injectable class to encapsulate ThemeUtils call...
*/
class ThemeProvider @Inject constructor(
private val context: Context
) {
fun isLightTheme() = ThemeUtils.isLightTheme(context)
}

View file

@ -16,15 +16,14 @@
package im.vector.app.features.widgets
import android.content.Context
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.features.themes.ThemeUtils
import im.vector.app.features.themes.ThemeProvider
import org.matrix.android.sdk.api.session.widgets.model.Widget
import javax.inject.Inject
class WidgetArgsBuilder @Inject constructor(
private val sessionHolder: ActiveSessionHolder,
private val context: Context
private val themeProvider: ThemeProvider
) {
@Suppress("UNCHECKED_CAST")
@ -52,7 +51,8 @@ class WidgetArgsBuilder @Inject constructor(
@Suppress("UNCHECKED_CAST")
fun buildStickerPickerArgs(roomId: String, widget: Widget): WidgetArgs {
val widgetId = widget.widgetId
val baseUrl = widget.computedUrl ?: throw IllegalStateException()
val baseUrl = sessionHolder.getActiveSession().widgetService()
.getWidgetComputedUrl(widget, themeProvider.isLightTheme()) ?: throw IllegalStateException()
return WidgetArgs(
baseUrl = baseUrl,
kind = WidgetKind.STICKER_PICKER,
@ -68,15 +68,13 @@ class WidgetArgsBuilder @Inject constructor(
fun buildRoomWidgetArgs(roomId: String, widget: Widget): WidgetArgs {
val widgetId = widget.widgetId
val baseUrl = widget.computedUrl ?: throw IllegalStateException()
val baseUrl = sessionHolder.getActiveSession().widgetService()
.getWidgetComputedUrl(widget, themeProvider.isLightTheme()) ?: throw IllegalStateException()
return WidgetArgs(
baseUrl = baseUrl,
kind = WidgetKind.ROOM,
roomId = roomId,
widgetId = widgetId,
urlParams = mapOf(
"theme" to getTheme()
).filterNotNull()
widgetId = widgetId
)
}
@ -86,7 +84,7 @@ class WidgetArgsBuilder @Inject constructor(
}
private fun getTheme(): String {
return if (ThemeUtils.isLightTheme(context)) {
return if (themeProvider.isLightTheme()) {
"light"
} else {
"dark"

View file

@ -27,6 +27,7 @@ import im.vector.app.R
import im.vector.app.core.platform.VectorViewModel
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.widgets.model.WidgetType
@ -52,11 +53,7 @@ class RoomWidgetPermissionViewModel @AssistedInject constructor(@Assisted val in
.filter { it.isNotEmpty() }
.map {
val widget = it.first()
val domain = try {
URL(widget.computedUrl).host
} catch (e: Throwable) {
null
}
val domain = tryOrNull { URL(widget.widgetContent.url) }?.host
// TODO check from widget urls the perms that should be shown?
// For now put all
if (widget.type == WidgetType.Jitsi) {