Compare commits
6 commits
develop
...
travis/thi
Author | SHA1 | Date | |
---|---|---|---|
|
bdd34120f0 | ||
|
0c6c0f32dd | ||
|
447db3df62 | ||
|
24a695e9a3 | ||
|
8c4b8f281c | ||
|
3456b4c58a |
7 changed files with 266 additions and 17 deletions
|
@ -28,16 +28,7 @@ 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";
|
||||||
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) {
|
||||||
|
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
45
src/vector/thin_widget/app.tsx
Normal file
45
src/vector/thin_widget/app.tsx
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
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';
|
||||||
|
import AppTile from "matrix-react-sdk/src/components/views/elements/AppTile";
|
||||||
|
import { IWidget } from "matrix-widget-api";
|
||||||
|
import MatrixClientContext from "matrix-react-sdk/src/contexts/MatrixClientContext";
|
||||||
|
import { MatrixClientPeg } from "matrix-react-sdk/src/MatrixClientPeg";
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
export interface IStartOpts {
|
||||||
|
widgetId: string;
|
||||||
|
roomId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function loadApp(widget: IWidget) {
|
||||||
|
return (
|
||||||
|
<MatrixClientContext.Provider value={MatrixClientPeg.get()}>
|
||||||
|
<div id="mx_ThinWrapper_container">
|
||||||
|
<AppTile
|
||||||
|
app={widget}
|
||||||
|
fullWidth={true}
|
||||||
|
userId={MatrixClientPeg.get().getUserId()}
|
||||||
|
userWidget={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</MatrixClientContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
12
src/vector/thin_widget/index.html
Normal file
12
src/vector/thin_widget/index.html
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Thin Widget</title>
|
||||||
|
<meta name="theme-color" content="#ffffff">
|
||||||
|
</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>
|
58
src/vector/thin_widget/index.scss
Normal file
58
src/vector/thin_widget/index.scss
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mx_ThinWrapper_container {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
.mx_AppTileFullWidth {
|
||||||
|
width: unset !important;
|
||||||
|
height: calc(100% - 10px); // 5px top + bottom borders on the AppTile
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
105
src/vector/thin_widget/index.ts
Normal file
105
src/vector/thin_widget/index.ts
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
/*
|
||||||
|
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';
|
||||||
|
import { StopGapWidgetDriver, WidgetRenderMode } from "matrix-react-sdk/src/stores/widgets/StopGapWidgetDriver";
|
||||||
|
import WidgetUtils from "matrix-react-sdk/src/utils/WidgetUtils";
|
||||||
|
import { MatrixClientPeg } from "matrix-react-sdk/src/MatrixClientPeg";
|
||||||
|
|
||||||
|
// 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(2));
|
||||||
|
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 homeserverUrl = qsParam("hsUrl");
|
||||||
|
const roomId = qsParam("roomId", true);
|
||||||
|
const widgetId = qsParam("widgetId"); // state_key or account data key
|
||||||
|
|
||||||
|
// TODO: clear href so people don't accidentally copy/paste it
|
||||||
|
//window.location.hash = '';
|
||||||
|
|
||||||
|
(async function() {
|
||||||
|
const {
|
||||||
|
rageshakePromise,
|
||||||
|
preparePlatform,
|
||||||
|
loadSkin,
|
||||||
|
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...");
|
||||||
|
StopGapWidgetDriver.RENDER_MODE = WidgetRenderMode.ThinWrapper;
|
||||||
|
await loadSkin();
|
||||||
|
await loadOlm();
|
||||||
|
preparePlatform();
|
||||||
|
await MatrixClientPeg.shim(homeserverUrl, accessToken);
|
||||||
|
await loadTheme();
|
||||||
|
await loadLanguage();
|
||||||
|
|
||||||
|
console.log("Locating widget...");
|
||||||
|
const stateEvent = await MatrixClientPeg.get()._http.authedRequest(
|
||||||
|
undefined, "GET",
|
||||||
|
`/rooms/${encodeURIComponent(roomId)}/state/im.vector.modular.widgets/${encodeURIComponent(widgetId)}`,
|
||||||
|
undefined, undefined, {},
|
||||||
|
);
|
||||||
|
if (!stateEvent?.url) {
|
||||||
|
throw new Error("Invalid widget");
|
||||||
|
}
|
||||||
|
const app = WidgetUtils.makeAppConfig(
|
||||||
|
widgetId,
|
||||||
|
stateEvent,
|
||||||
|
MatrixClientPeg.get().getUserId(), // assume we are the sender
|
||||||
|
roomId,
|
||||||
|
widgetId);
|
||||||
|
|
||||||
|
// 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(app),
|
||||||
|
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."),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
})();
|
|
@ -31,6 +31,15 @@ module.exports = (env, argv) => {
|
||||||
const reactSdkSrcDir = path.resolve(require.resolve("matrix-react-sdk/package.json"), '..', 'src');
|
const reactSdkSrcDir = path.resolve(require.resolve("matrix-react-sdk/package.json"), '..', 'src');
|
||||||
const jsSdkSrcDir = path.resolve(require.resolve("matrix-js-sdk/package.json"), '..', 'src');
|
const jsSdkSrcDir = path.resolve(require.resolve("matrix-js-sdk/package.json"), '..', 'src');
|
||||||
|
|
||||||
|
const themeBundles = {
|
||||||
|
"theme-legacy": "./node_modules/matrix-react-sdk/res/themes/legacy-light/css/legacy-light.scss",
|
||||||
|
"theme-legacy-dark": "./node_modules/matrix-react-sdk/res/themes/legacy-dark/css/legacy-dark.scss",
|
||||||
|
"theme-light": "./node_modules/matrix-react-sdk/res/themes/light/css/light.scss",
|
||||||
|
"theme-dark": "./node_modules/matrix-react-sdk/res/themes/dark/css/dark.scss",
|
||||||
|
"theme-light-custom": "./node_modules/matrix-react-sdk/res/themes/light-custom/css/light-custom.scss",
|
||||||
|
"theme-dark-custom": "./node_modules/matrix-react-sdk/res/themes/dark-custom/css/dark-custom.scss",
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...development,
|
...development,
|
||||||
|
|
||||||
|
@ -40,14 +49,10 @@ 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",
|
...themeBundles,
|
||||||
"theme-legacy-dark": "./node_modules/matrix-react-sdk/res/themes/legacy-dark/css/legacy-dark.scss",
|
|
||||||
"theme-light": "./node_modules/matrix-react-sdk/res/themes/light/css/light.scss",
|
|
||||||
"theme-dark": "./node_modules/matrix-react-sdk/res/themes/dark/css/dark.scss",
|
|
||||||
"theme-light-custom": "./node_modules/matrix-react-sdk/res/themes/light-custom/css/light-custom.scss",
|
|
||||||
"theme-dark-custom": "./node_modules/matrix-react-sdk/res/themes/dark-custom/css/dark-custom.scss",
|
|
||||||
},
|
},
|
||||||
|
|
||||||
optimization: {
|
optimization: {
|
||||||
|
@ -313,7 +318,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,
|
||||||
|
@ -328,6 +333,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', ...Object.keys(themeBundles)],
|
||||||
|
}),
|
||||||
|
|
||||||
// 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