Compare commits

...

13 Commits

Author SHA1 Message Date
Travis Ralston
bdd34120f0 Render the widget fully from room state 2021-02-14 22:16:43 -07:00
Travis Ralston
0c6c0f32dd Debugging 2021-02-14 21:23:27 -07:00
Travis Ralston
447db3df62 Fix theme support 2021-02-14 21:23:23 -07:00
Travis Ralston
24a695e9a3 Fix merge 2021-02-14 21:16:03 -07:00
Travis Ralston
8c4b8f281c Merge branch 'develop' into travis/thin-widget-wrapper 2021-02-14 21:15:34 -07:00
Michael Telatynski
19a07bc4a2 Merge pull request #16415 from bekliev/fix/sso-redirect
fix / sso: make sure to delete only loginToken after redirect
2021-02-11 16:57:28 +00:00
Michael Telatynski
438eef0acd Update src/vector/app.tsx 2021-02-11 16:27:55 +00:00
Michael Telatynski
e15d1c1501 Update src/vector/platform/WebPlatform.ts 2021-02-11 16:27:30 +00:00
Ibragim/Parviz Bekliev
92a5787528 Migrating from Node's deprecated url API to browser's URL
Related to https://github.com/vector-im/element-web/pull/16292

Signed-off-by: Bekliev Parviz <nightkon95@gmail.com>
2021-02-11 18:59:59 +03:00
J. Ryan Stinnett
af2da9d371 Merge pull request #16433 from vector-im/jryans/disable-countly
Disable Countly
2021-02-11 15:26:50 +00:00
J. Ryan Stinnett
68c36ebb49 Disable Countly
The Countly experiment has ended, so this removes the configuration to enable
it.
2021-02-11 15:16:24 +00:00
Ibragim/Parviz Bekliev
3e57378631 fix / sso: after complete login via token make sure to delete only loginToken query-param from the window.location api.
Related to https://github.com/vector-im/element-web/pull/16292

Signed-off-by: Bekliev Parviz <nightkon95@gmail.com>
2021-02-09 16:40:52 +03:00
Travis Ralston
3456b4c58a WIP concept for a "thin widget" wrapper 2021-01-11 23:42:49 -07:00
12 changed files with 280 additions and 50 deletions

View File

@@ -18,10 +18,6 @@
"siteId": 1,
"policyUrl": "https://element.io/cookie-policy"
},
"countly": {
"url": "https://try.count.ly",
"appKey": "8abf1ee15646bc884556b82e5053857904264b66"
},
"roomDirectory": {
"servers": [
"matrix.org",

View File

@@ -66,8 +66,7 @@
"react": "^16.14.0",
"react-dom": "^16.14.0",
"sanitize-html": "github:apostrophecms/sanitize-html#3c7f93f2058f696f5359e3e58d464161647226db",
"ua-parser-js": "^0.7.23",
"url": "^0.11.0"
"ua-parser-js": "^0.7.23"
},
"devDependencies": {
"@babel/core": "^7.12.10",

View File

@@ -23,7 +23,6 @@ import React from 'react';
// this incidentally means we can forget our React imports in JSX files without penalty.
window.React = React;
import url from 'url';
import * as sdk from 'matrix-react-sdk';
import PlatformPeg from 'matrix-react-sdk/src/PlatformPeg';
import {_td, newTranslatableError} from 'matrix-react-sdk/src/languageHandler';
@@ -120,11 +119,12 @@ function onTokenLoginCompleted() {
// if we did a token login, we're now left with the token, hs and is
// url as query params in the url; a little nasty but let's redirect to
// clear them.
const parsedUrl = url.parse(window.location.href);
parsedUrl.search = "";
const formatted = url.format(parsedUrl);
console.log(`Redirecting to ${formatted} to drop loginToken from queryparams`);
window.history.replaceState(null, "", formatted);
const url = new URL(window.location.href);
url.searchParams.delete("loginToken");
console.log(`Redirecting to ${url.href} to drop loginToken from queryparams`);
window.history.replaceState(null, "", url.href);
}
export async function loadApp(fragParams: {}) {

View File

@@ -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.
import {parseQsFromFragment} from "./url_utils";
import './modernizr';
async function settled(...promises: Array<Promise<any>>) {
for (const prom of promises) {
try {
await prom;
} catch (e) {
console.error(e);
}
}
}
import {settled} from "./promise_utils";
function checkBrowserFeatures() {
if (!window.Modernizr) {

View File

@@ -26,7 +26,6 @@ import {hideToast as hideUpdateToast, showToast as showUpdateToast} from "matrix
import {Action} from "matrix-react-sdk/src/dispatcher/actions";
import { CheckUpdatesPayload } from 'matrix-react-sdk/src/dispatcher/payloads/CheckUpdatesPayload';
import url from 'url';
import UAParser from 'ua-parser-js';
const POKE_RATE_MS = 10 * 60 * 1000; // 10 min
@@ -184,17 +183,13 @@ export default class WebPlatform extends VectorBasePlatform {
getDefaultDeviceDisplayName(): string {
// strip query-string and fragment from uri
const u = url.parse(window.location.href);
u.protocol = "";
u.search = "";
u.hash = "";
// Remove trailing slash if present
u.pathname = u.pathname.replace(/\/$/, "");
const url = new URL(window.location.href);
let appName = u.format();
// Remove leading slashes if present
appName = appName.replace(/^\/\//, "");
// `appName` is now in the format `develop.element.io`.
// `appName` in the format `develop.element.io/abc/xyz`
const appName = [
url.host,
url.pathname.replace(/\/$/, ""), // Remove trailing slash if present
].join("");
const ua = new UAParser();
const browserName = ua.getBrowser().name || "unknown browser";

View 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);
}
}
}

View 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>
);
}

View 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>

View 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;
}
}

View 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."),
]);
}
})();

View File

@@ -31,6 +31,15 @@ module.exports = (env, argv) => {
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 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 {
...development,
@@ -40,14 +49,10 @@ module.exports = (env, argv) => {
"mobileguide": "./src/vector/mobile_guide/index.js",
"jitsi": "./src/vector/jitsi/index.ts",
"usercontent": "./node_modules/matrix-react-sdk/src/usercontent/index.js",
"thinwidget": "./src/vector/thin_widget/index.ts",
// CSS themes
"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",
...themeBundles,
},
optimization: {
@@ -313,7 +318,7 @@ module.exports = (env, argv) => {
// HtmlWebpackPlugin will screw up our formatting like the names
// of the themes and which chunks we actually care about.
inject: false,
excludeChunks: ['mobileguide', 'usercontent', 'jitsi'],
excludeChunks: ['mobileguide', 'usercontent', 'jitsi', 'thinwidget'],
minify: argv.mode === 'production',
vars: {
og_image_url: og_image_url,
@@ -328,6 +333,14 @@ module.exports = (env, argv) => {
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)
new HtmlWebpackPlugin({
template: './src/vector/mobile_guide/index.html',

View File

@@ -8890,16 +8890,7 @@ postcss-attribute-case-insensitive@^4.0.1:
postcss "^7.0.2"
postcss-selector-parser "^6.0.2"
postcss-calc@^7.0.1:
version "7.0.5"
resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.5.tgz#f8a6e99f12e619c2ebc23cf6c486fdc15860933e"
integrity sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==
dependencies:
postcss "^7.0.27"
postcss-selector-parser "^6.0.2"
postcss-value-parser "^4.0.2"
postcss-calc@^7.0.5:
postcss-calc@^7.0.1, postcss-calc@^7.0.5:
version "7.0.5"
resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.5.tgz#f8a6e99f12e619c2ebc23cf6c486fdc15860933e"
integrity sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==