mirror of
https://github.com/element-hq/element-android
synced 2024-11-24 10:25:35 +03:00
Compute WidgetUrl "onDemand" to properly handle the theme value
This commit is contained in:
parent
463f2a7ad7
commit
1978a180ff
12 changed files with 73 additions and 30 deletions
|
@ -56,6 +56,11 @@ interface WidgetService {
|
||||||
excludedTypes: Set<String>? = null
|
excludedTypes: Set<String>? = null
|
||||||
): List<Widget>
|
): 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.
|
* Returns the live room widgets so you can listen to them.
|
||||||
* Some widgets can be deactivated, so be sure to check for isActive.
|
* Some widgets can be deactivated, so be sure to check for isActive.
|
||||||
|
|
|
@ -25,7 +25,6 @@ data class Widget(
|
||||||
val widgetId: String,
|
val widgetId: String,
|
||||||
val senderInfo: SenderInfo?,
|
val senderInfo: SenderInfo?,
|
||||||
val isAddedByMe: Boolean,
|
val isAddedByMe: Boolean,
|
||||||
val computedUrl: String?,
|
|
||||||
val type: WidgetType
|
val type: WidgetType
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
|
@ -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.user.accountdata.UpdateUserAccountDataTask
|
||||||
import org.matrix.android.sdk.internal.session.widgets.helper.WidgetFactory
|
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.session.widgets.helper.extractWidgetSequence
|
||||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -55,7 +54,6 @@ import javax.inject.Inject
|
||||||
*/
|
*/
|
||||||
@SessionScope
|
@SessionScope
|
||||||
internal class IntegrationManager @Inject constructor(matrixConfiguration: MatrixConfiguration,
|
internal class IntegrationManager @Inject constructor(matrixConfiguration: MatrixConfiguration,
|
||||||
private val taskExecutor: TaskExecutor,
|
|
||||||
@SessionDatabase private val monarchy: Monarchy,
|
@SessionDatabase private val monarchy: Monarchy,
|
||||||
private val updateUserAccountDataTask: UpdateUserAccountDataTask,
|
private val updateUserAccountDataTask: UpdateUserAccountDataTask,
|
||||||
private val accountDataDataSource: AccountDataDataSource,
|
private val accountDataDataSource: AccountDataDataSource,
|
||||||
|
|
|
@ -50,6 +50,10 @@ internal class DefaultWidgetService @Inject constructor(private val widgetManage
|
||||||
return widgetManager.getRoomWidgets(roomId, widgetId, widgetTypes, excludedTypes)
|
return widgetManager.getRoomWidgets(roomId, widgetId, widgetTypes, excludedTypes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getWidgetComputedUrl(widget: Widget, isLightTheme: Boolean): String? {
|
||||||
|
return widgetManager.getWidgetComputedUrl(widget, isLightTheme)
|
||||||
|
}
|
||||||
|
|
||||||
override fun getRoomWidgetsLive(
|
override fun getRoomWidgetsLive(
|
||||||
roomId: String,
|
roomId: String,
|
||||||
widgetId: QueryStringValue,
|
widgetId: QueryStringValue,
|
||||||
|
|
|
@ -104,6 +104,10 @@ internal class WidgetManager @Inject constructor(private val integrationManager:
|
||||||
return widgetEvents.mapEventsToWidgets(widgetTypes, excludedTypes)
|
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,
|
private fun List<Event>.mapEventsToWidgets(widgetTypes: Set<String>? = null,
|
||||||
excludedTypes: Set<String>? = null): List<Widget> {
|
excludedTypes: Set<String>? = null): List<Widget> {
|
||||||
val widgetEvents = this
|
val widgetEvents = this
|
||||||
|
|
|
@ -55,30 +55,29 @@ internal class WidgetFactory @Inject constructor(private val userDataSource: Use
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val isAddedByMe = widgetEvent.senderId == userId
|
val isAddedByMe = widgetEvent.senderId == userId
|
||||||
val computedUrl = widgetContent.computeURL(widgetEvent.roomId, widgetId)
|
|
||||||
return Widget(
|
return Widget(
|
||||||
widgetContent = widgetContent,
|
widgetContent = widgetContent,
|
||||||
event = widgetEvent,
|
event = widgetEvent,
|
||||||
widgetId = widgetId,
|
widgetId = widgetId,
|
||||||
senderInfo = senderInfo,
|
senderInfo = senderInfo,
|
||||||
isAddedByMe = isAddedByMe,
|
isAddedByMe = isAddedByMe,
|
||||||
computedUrl = computedUrl,
|
|
||||||
type = WidgetType.fromString(type)
|
type = WidgetType.fromString(type)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ref: https://github.com/matrix-org/matrix-widget-api/blob/master/src/templating/url-template.ts#L29-L33
|
// 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? {
|
fun computeURL(widget: Widget, isLightTheme: Boolean): String? {
|
||||||
var computedUrl = url ?: return null
|
var computedUrl = widget.widgetContent.url ?: return null
|
||||||
val myUser = userDataSource.getUser(userId)
|
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_USER_ID] = userId
|
||||||
keyValue[WIDGET_PATTERN_MATRIX_DISPLAY_NAME] = myUser?.getBestName() ?: userId
|
keyValue[WIDGET_PATTERN_MATRIX_DISPLAY_NAME] = myUser?.getBestName() ?: userId
|
||||||
keyValue[WIDGET_PATTERN_MATRIX_AVATAR_URL] = urlResolver.resolveFullSize(myUser?.avatarUrl) ?: ""
|
keyValue[WIDGET_PATTERN_MATRIX_AVATAR_URL] = urlResolver.resolveFullSize(myUser?.avatarUrl) ?: ""
|
||||||
keyValue[WIDGET_PATTERN_MATRIX_WIDGET_ID] = widgetId
|
keyValue[WIDGET_PATTERN_MATRIX_WIDGET_ID] = widget.widgetId
|
||||||
keyValue[WIDGET_PATTERN_MATRIX_ROOM_ID] = roomId ?: ""
|
keyValue[WIDGET_PATTERN_MATRIX_ROOM_ID] = widget.event.roomId ?: ""
|
||||||
|
keyValue[WIDGET_PATTERN_THEME] = getTheme(isLightTheme)
|
||||||
|
|
||||||
for ((key, value) in keyValue) {
|
for ((key, value) in keyValue) {
|
||||||
computedUrl = computedUrl.replace(key, URLEncoder.encode(value.toString(), "utf-8"))
|
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
|
return computedUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getTheme(isLightTheme: Boolean): String {
|
||||||
|
return if (isLightTheme) "light" else "dark"
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// Value to be replaced in URLS
|
// Value to be replaced in URLS
|
||||||
const val WIDGET_PATTERN_MATRIX_USER_ID = "\$matrix_user_id"
|
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_AVATAR_URL = "\$matrix_avatar_url"
|
||||||
const val WIDGET_PATTERN_MATRIX_WIDGET_ID = "\$matrix_widget_id"
|
const val WIDGET_PATTERN_MATRIX_WIDGET_ID = "\$matrix_widget_id"
|
||||||
const val WIDGET_PATTERN_MATRIX_ROOM_ID = "\$matrix_room_id"
|
const val WIDGET_PATTERN_MATRIX_ROOM_ID = "\$matrix_room_id"
|
||||||
|
const val WIDGET_PATTERN_THEME = "\$theme"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import dagger.assisted.AssistedInject
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import im.vector.app.core.platform.VectorViewModel
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
import im.vector.app.core.resources.StringProvider
|
import im.vector.app.core.resources.StringProvider
|
||||||
|
import im.vector.app.features.themes.ThemeProvider
|
||||||
import org.jitsi.meet.sdk.JitsiMeetUserInfo
|
import org.jitsi.meet.sdk.JitsiMeetUserInfo
|
||||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
@ -37,7 +38,8 @@ class JitsiCallViewModel @AssistedInject constructor(
|
||||||
@Assisted initialState: JitsiCallViewState,
|
@Assisted initialState: JitsiCallViewState,
|
||||||
@Assisted val args: VectorJitsiActivity.Args,
|
@Assisted val args: VectorJitsiActivity.Args,
|
||||||
private val session: Session,
|
private val session: Session,
|
||||||
private val stringProvider: StringProvider
|
private val stringProvider: StringProvider,
|
||||||
|
private val themeProvider: ThemeProvider
|
||||||
) : VectorViewModel<JitsiCallViewState, JitsiCallViewActions, JitsiCallViewEvents>(initialState) {
|
) : VectorViewModel<JitsiCallViewState, JitsiCallViewActions, JitsiCallViewEvents>(initialState) {
|
||||||
|
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
|
@ -45,6 +47,8 @@ class JitsiCallViewModel @AssistedInject constructor(
|
||||||
fun create(initialState: JitsiCallViewState, args: VectorJitsiActivity.Args): JitsiCallViewModel
|
fun create(initialState: JitsiCallViewState, args: VectorJitsiActivity.Args): JitsiCallViewModel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val widgetService = session.widgetService()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val me = session.getRoomMember(session.myUserId, args.roomId)?.toMatrixItem()
|
val me = session.getRoomMember(session.myUserId, args.roomId)?.toMatrixItem()
|
||||||
val userInfo = JitsiMeetUserInfo().apply {
|
val userInfo = JitsiMeetUserInfo().apply {
|
||||||
|
@ -57,13 +61,14 @@ class JitsiCallViewModel @AssistedInject constructor(
|
||||||
copy(userInfo = userInfo)
|
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()
|
.asObservable()
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
.subscribe {
|
.subscribe {
|
||||||
val jitsiWidget = it.firstOrNull()
|
val jitsiWidget = it.firstOrNull()
|
||||||
if (jitsiWidget != null) {
|
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 {
|
setState {
|
||||||
copy(
|
copy(
|
||||||
widget = Success(jitsiWidget),
|
widget = Success(jitsiWidget),
|
||||||
|
|
|
@ -32,7 +32,7 @@ class StickerPickerActionHandler @Inject constructor(private val session: Sessio
|
||||||
return@withContext RoomDetailViewEvents.DisplayEnableIntegrationsWarning
|
return@withContext RoomDetailViewEvents.DisplayEnableIntegrationsWarning
|
||||||
}
|
}
|
||||||
val stickerWidget = session.widgetService().getUserWidgets(WidgetType.StickerPicker.values()).firstOrNull { it.isActive }
|
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
|
RoomDetailViewEvents.DisplayPromptForIntegrationManager
|
||||||
} else {
|
} else {
|
||||||
RoomDetailViewEvents.OpenStickerPicker(
|
RoomDetailViewEvents.OpenStickerPicker(
|
||||||
|
|
|
@ -43,7 +43,7 @@ abstract class RoomWidgetItem : EpoxyModelWithHolder<RoomWidgetItem.Holder>() {
|
||||||
override fun bind(holder: Holder) {
|
override fun bind(holder: Holder) {
|
||||||
super.bind(holder)
|
super.bind(holder)
|
||||||
holder.widgetName.text = widget.name
|
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) {
|
if (iconRes != null) {
|
||||||
holder.iconImage.isVisible = true
|
holder.iconImage.isVisible = true
|
||||||
holder.iconImage.setImageResource(iconRes!!)
|
holder.iconImage.setImageResource(iconRes!!)
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -16,15 +16,14 @@
|
||||||
|
|
||||||
package im.vector.app.features.widgets
|
package im.vector.app.features.widgets
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
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 org.matrix.android.sdk.api.session.widgets.model.Widget
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class WidgetArgsBuilder @Inject constructor(
|
class WidgetArgsBuilder @Inject constructor(
|
||||||
private val sessionHolder: ActiveSessionHolder,
|
private val sessionHolder: ActiveSessionHolder,
|
||||||
private val context: Context
|
private val themeProvider: ThemeProvider
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
@ -52,7 +51,8 @@ class WidgetArgsBuilder @Inject constructor(
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
fun buildStickerPickerArgs(roomId: String, widget: Widget): WidgetArgs {
|
fun buildStickerPickerArgs(roomId: String, widget: Widget): WidgetArgs {
|
||||||
val widgetId = widget.widgetId
|
val widgetId = widget.widgetId
|
||||||
val baseUrl = widget.computedUrl ?: throw IllegalStateException()
|
val baseUrl = sessionHolder.getActiveSession().widgetService()
|
||||||
|
.getWidgetComputedUrl(widget, themeProvider.isLightTheme()) ?: throw IllegalStateException()
|
||||||
return WidgetArgs(
|
return WidgetArgs(
|
||||||
baseUrl = baseUrl,
|
baseUrl = baseUrl,
|
||||||
kind = WidgetKind.STICKER_PICKER,
|
kind = WidgetKind.STICKER_PICKER,
|
||||||
|
@ -68,15 +68,13 @@ class WidgetArgsBuilder @Inject constructor(
|
||||||
|
|
||||||
fun buildRoomWidgetArgs(roomId: String, widget: Widget): WidgetArgs {
|
fun buildRoomWidgetArgs(roomId: String, widget: Widget): WidgetArgs {
|
||||||
val widgetId = widget.widgetId
|
val widgetId = widget.widgetId
|
||||||
val baseUrl = widget.computedUrl ?: throw IllegalStateException()
|
val baseUrl = sessionHolder.getActiveSession().widgetService()
|
||||||
|
.getWidgetComputedUrl(widget, themeProvider.isLightTheme()) ?: throw IllegalStateException()
|
||||||
return WidgetArgs(
|
return WidgetArgs(
|
||||||
baseUrl = baseUrl,
|
baseUrl = baseUrl,
|
||||||
kind = WidgetKind.ROOM,
|
kind = WidgetKind.ROOM,
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
widgetId = widgetId,
|
widgetId = widgetId
|
||||||
urlParams = mapOf(
|
|
||||||
"theme" to getTheme()
|
|
||||||
).filterNotNull()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +84,7 @@ class WidgetArgsBuilder @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getTheme(): String {
|
private fun getTheme(): String {
|
||||||
return if (ThemeUtils.isLightTheme(context)) {
|
return if (themeProvider.isLightTheme()) {
|
||||||
"light"
|
"light"
|
||||||
} else {
|
} else {
|
||||||
"dark"
|
"dark"
|
||||||
|
|
|
@ -27,6 +27,7 @@ import im.vector.app.R
|
||||||
import im.vector.app.core.platform.VectorViewModel
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
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.query.QueryStringValue
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.widgets.model.WidgetType
|
import org.matrix.android.sdk.api.session.widgets.model.WidgetType
|
||||||
|
@ -52,11 +53,7 @@ class RoomWidgetPermissionViewModel @AssistedInject constructor(@Assisted val in
|
||||||
.filter { it.isNotEmpty() }
|
.filter { it.isNotEmpty() }
|
||||||
.map {
|
.map {
|
||||||
val widget = it.first()
|
val widget = it.first()
|
||||||
val domain = try {
|
val domain = tryOrNull { URL(widget.widgetContent.url) }?.host
|
||||||
URL(widget.computedUrl).host
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
// TODO check from widget urls the perms that should be shown?
|
// TODO check from widget urls the perms that should be shown?
|
||||||
// For now put all
|
// For now put all
|
||||||
if (widget.type == WidgetType.Jitsi) {
|
if (widget.type == WidgetType.Jitsi) {
|
||||||
|
|
Loading…
Reference in a new issue