WIP concept for a "thin widget" wrapper
This commit is contained in:
parent
66080b5751
commit
3456b4c58a
7 changed files with 213 additions and 11 deletions
|
@ -28,22 +28,13 @@ require('katex/dist/katex.css');
|
||||||
// These are things that can run before the skin loads - be careful not to reference the react-sdk though.
|
// These are things that can run before the skin loads - be careful not to reference the react-sdk though.
|
||||||
import {parseQsFromFragment} from "./url_utils";
|
import {parseQsFromFragment} from "./url_utils";
|
||||||
import './modernizr';
|
import './modernizr';
|
||||||
|
import {settled} from "./promise_utils";
|
||||||
|
|
||||||
// load service worker if available on this platform
|
// load service worker if available on this platform
|
||||||
if ('serviceWorker' in navigator) {
|
if ('serviceWorker' in navigator) {
|
||||||
navigator.serviceWorker.register('sw.js');
|
navigator.serviceWorker.register('sw.js');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function settled(...promises: Array<Promise<any>>) {
|
|
||||||
for (const prom of promises) {
|
|
||||||
try {
|
|
||||||
await prom;
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkBrowserFeatures() {
|
function checkBrowserFeatures() {
|
||||||
if (!window.Modernizr) {
|
if (!window.Modernizr) {
|
||||||
console.error("Cannot check features - Modernizr global is missing.");
|
console.error("Cannot check features - Modernizr global is missing.");
|
||||||
|
|
25
src/vector/promise_utils.ts
Normal file
25
src/vector/promise_utils.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
Copyright 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export async function settled(...promises: Array<Promise<any>>) {
|
||||||
|
for (const prom of promises) {
|
||||||
|
try {
|
||||||
|
await prom;
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
src/vector/thin_widget/app.tsx
Normal file
44
src/vector/thin_widget/app.tsx
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
Copyright 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
// add React and ReactPerf to the global namespace, to make them easier to access via the console
|
||||||
|
// this incidentally means we can forget our React imports in JSX files without penalty.
|
||||||
|
window.React = React;
|
||||||
|
|
||||||
|
import AppTile from "matrix-react-sdk/src/components/views/elements/AppTile";
|
||||||
|
|
||||||
|
export interface IStartOpts {
|
||||||
|
accessToken: string;
|
||||||
|
widgetId: string;
|
||||||
|
roomId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function loadApp(opts: IStartOpts) {
|
||||||
|
// TODO: Actually use `opts` to populate the widget
|
||||||
|
return <AppTile
|
||||||
|
app={{
|
||||||
|
id: "test1234",
|
||||||
|
url: "http://localhost:8081/index.html#/?widgetId=$matrix_widget_id",
|
||||||
|
name: "Test Widget",
|
||||||
|
type: "m.custom",
|
||||||
|
data: {},
|
||||||
|
}}
|
||||||
|
fullWidth={true}
|
||||||
|
userId={"@test:example.org"}
|
||||||
|
userWidget={true}
|
||||||
|
/>;
|
||||||
|
}
|
11
src/vector/thin_widget/index.html
Normal file
11
src/vector/thin_widget/index.html
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Thin Widget</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>Sorry, this requires JavaScript to be enabled.</noscript> <!-- TODO: Translate this? -->
|
||||||
|
<section id="matrixchat" style="height: 100%; overflow: auto;" class="notranslate"></section>
|
||||||
|
</body>
|
||||||
|
</html>
|
44
src/vector/thin_widget/index.scss
Normal file
44
src/vector/thin_widget/index.scss
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
Copyright 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TODO: Match the user's theme: https://github.com/vector-im/element-web/issues/12794
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Nunito';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: url('~matrix-react-sdk/res/fonts/Nunito/Nunito-Regular.ttf') format('truetype');
|
||||||
|
}
|
||||||
|
|
||||||
|
$dark-fg: #edf3ff;
|
||||||
|
$dark-bg: #363c43;
|
||||||
|
$light-fg: #2e2f32;
|
||||||
|
$light-bg: #fff;
|
||||||
|
body {
|
||||||
|
font-family: Nunito, Arial, Helvetica, sans-serif;
|
||||||
|
background-color: $dark-bg;
|
||||||
|
color: $dark-fg;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.theme-light {
|
||||||
|
background-color: $light-bg;
|
||||||
|
color: $light-fg;
|
||||||
|
}
|
||||||
|
|
||||||
|
body, html {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
78
src/vector/thin_widget/index.ts
Normal file
78
src/vector/thin_widget/index.ts
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
Copyright 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// We have to trick webpack into loading our CSS for us.
|
||||||
|
require("./index.scss");
|
||||||
|
import * as qs from 'querystring';
|
||||||
|
import { settled } from "../promise_utils";
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
|
||||||
|
// The widget's options are encoded into the fragment to avoid leaking info to the server. The widget
|
||||||
|
// spec on the other hand requires the widgetId and parentUrl to show up in the regular query string.
|
||||||
|
const widgetQuery = qs.parse(window.location.hash.substring(1));
|
||||||
|
const qsParam = (name: string, optional = false): string => {
|
||||||
|
if (!optional && (!widgetQuery[name] || typeof (widgetQuery[name]) !== 'string')) {
|
||||||
|
throw new Error(`Expected singular ${name} in query string`);
|
||||||
|
}
|
||||||
|
return widgetQuery[name] as string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const accessToken = qsParam("accessToken");
|
||||||
|
const roomId = qsParam("roomId", true);
|
||||||
|
const widgetId = qsParam("widgetId"); // state_key or account data key
|
||||||
|
|
||||||
|
(async function() {
|
||||||
|
const {
|
||||||
|
rageshakePromise,
|
||||||
|
preparePlatform,
|
||||||
|
loadOlm, // to handle timelines
|
||||||
|
loadLanguage,
|
||||||
|
loadTheme,
|
||||||
|
showError,
|
||||||
|
_t,
|
||||||
|
} = await import(
|
||||||
|
/* webpackChunkName: "thin-wrapper-init" */
|
||||||
|
/* webpackPreload: true */
|
||||||
|
"../init");
|
||||||
|
|
||||||
|
try {
|
||||||
|
// give rageshake a chance to load/fail, we don't actually assert rageshake loads, we allow it to fail if no IDB
|
||||||
|
console.log("Waiting for rageshake...");
|
||||||
|
await settled(rageshakePromise);
|
||||||
|
|
||||||
|
console.log("Running startup...");
|
||||||
|
await loadOlm();
|
||||||
|
preparePlatform();
|
||||||
|
await loadTheme();
|
||||||
|
await loadLanguage();
|
||||||
|
|
||||||
|
// Now we can start our custom code
|
||||||
|
console.log("Loading app...");
|
||||||
|
const module = await import(
|
||||||
|
/* webpackChunkName: "thin-wrapper-app" */
|
||||||
|
/* webpackPreload: true */
|
||||||
|
"./app");
|
||||||
|
window.matrixChat = ReactDOM.render(await module.loadApp({accessToken, roomId, widgetId}),
|
||||||
|
document.getElementById('matrixchat'));
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
// Like the compatibility page, AWOOOOOGA at the user
|
||||||
|
// This uses the default brand since the app config is unavailable.
|
||||||
|
await showError(_t("Your Element is misconfigured"), [
|
||||||
|
err.translatedMessage || _t("Unexpected error preparing the app. See console for details."),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
})();
|
|
@ -40,6 +40,7 @@ module.exports = (env, argv) => {
|
||||||
"mobileguide": "./src/vector/mobile_guide/index.js",
|
"mobileguide": "./src/vector/mobile_guide/index.js",
|
||||||
"jitsi": "./src/vector/jitsi/index.ts",
|
"jitsi": "./src/vector/jitsi/index.ts",
|
||||||
"usercontent": "./node_modules/matrix-react-sdk/src/usercontent/index.js",
|
"usercontent": "./node_modules/matrix-react-sdk/src/usercontent/index.js",
|
||||||
|
"thinwidget": "./src/vector/thin_widget/index.ts",
|
||||||
|
|
||||||
// CSS themes
|
// CSS themes
|
||||||
"theme-legacy": "./node_modules/matrix-react-sdk/res/themes/legacy-light/css/legacy-light.scss",
|
"theme-legacy": "./node_modules/matrix-react-sdk/res/themes/legacy-light/css/legacy-light.scss",
|
||||||
|
@ -312,7 +313,7 @@ module.exports = (env, argv) => {
|
||||||
// HtmlWebpackPlugin will screw up our formatting like the names
|
// HtmlWebpackPlugin will screw up our formatting like the names
|
||||||
// of the themes and which chunks we actually care about.
|
// of the themes and which chunks we actually care about.
|
||||||
inject: false,
|
inject: false,
|
||||||
excludeChunks: ['mobileguide', 'usercontent', 'jitsi'],
|
excludeChunks: ['mobileguide', 'usercontent', 'jitsi', 'thinwidget'],
|
||||||
minify: argv.mode === 'production',
|
minify: argv.mode === 'production',
|
||||||
vars: {
|
vars: {
|
||||||
og_image_url: og_image_url,
|
og_image_url: og_image_url,
|
||||||
|
@ -327,6 +328,14 @@ module.exports = (env, argv) => {
|
||||||
chunks: ['jitsi'],
|
chunks: ['jitsi'],
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
// This is a small thin wrapper for widgets (popout; isolated stack)
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
template: './src/vector/thin_widget/index.html',
|
||||||
|
filename: 'thin_widget.html',
|
||||||
|
minify: argv.mode === 'production',
|
||||||
|
chunks: ['thinwidget'],
|
||||||
|
}),
|
||||||
|
|
||||||
// This is the mobile guide's entry point (separate for faster mobile loading)
|
// This is the mobile guide's entry point (separate for faster mobile loading)
|
||||||
new HtmlWebpackPlugin({
|
new HtmlWebpackPlugin({
|
||||||
template: './src/vector/mobile_guide/index.html',
|
template: './src/vector/mobile_guide/index.html',
|
||||||
|
|
Loading…
Add table
Reference in a new issue