Compare commits
11 commits
develop
...
t3chguy/fe
Author | SHA1 | Date | |
---|---|---|---|
|
ad15747c21 | ||
|
55e6ff9b2d | ||
|
c75a4add42 | ||
|
4d8fe1f7f3 | ||
|
22e3286b96 | ||
|
97b3a4a3e0 | ||
|
b66ab3991b | ||
|
2ed497bcf6 | ||
|
b12936447f | ||
|
66bd562c21 | ||
|
eb9a29cec5 |
9 changed files with 376 additions and 5 deletions
11
src/vector/dummy_widget/index.html
Normal file
11
src/vector/dummy_widget/index.html
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Dummy Widget</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<button id="demoBtn">Click me to show a modal widget</button>
|
||||||
|
<p id="answer"><!-- populated with js --></p>
|
||||||
|
</body>
|
||||||
|
</html>
|
50
src/vector/dummy_widget/index.scss
Normal file
50
src/vector/dummy_widget/index.scss
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 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/riot-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');
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: Nunito, Arial, Helvetica, sans-serif;
|
||||||
|
background-color: #181b21;
|
||||||
|
color: #edf3ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
body, html {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
// A mix of AccessibleButton styles
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 7px 18px;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 4px;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: #03b381;
|
||||||
|
border: 0;
|
||||||
|
}
|
70
src/vector/dummy_widget/index.ts
Normal file
70
src/vector/dummy_widget/index.ts
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 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 {
|
||||||
|
ModalButtonKind,
|
||||||
|
WidgetApi,
|
||||||
|
WidgetApiToWidgetAction,
|
||||||
|
IModalWidgetCloseNotificationRequest,
|
||||||
|
} from 'matrix-widget-api';
|
||||||
|
|
||||||
|
let widgetApi: WidgetApi;
|
||||||
|
(async function() {
|
||||||
|
try {
|
||||||
|
// 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 query = Object.assign({}, qs.parse(window.location.search.substring(1)), widgetQuery);
|
||||||
|
const qsParam = (name: string, optional = false): string => {
|
||||||
|
if (!optional && (!query[name] || typeof (query[name]) !== 'string')) {
|
||||||
|
throw new Error(`Expected singular ${name} in query string`);
|
||||||
|
}
|
||||||
|
return <string>query[name];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set this up as early as possible because Element will be hitting it almost immediately.
|
||||||
|
const parentOrigin = new URL(qsParam('parentUrl')).origin;
|
||||||
|
widgetApi = new WidgetApi(qsParam("widgetId"), parentOrigin);
|
||||||
|
|
||||||
|
widgetApi.on(`action:${WidgetApiToWidgetAction.CloseModalWidget}`,
|
||||||
|
(ev: CustomEvent<IModalWidgetCloseNotificationRequest>) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
document.getElementById("answer").innerText = "Response from Modal: " + JSON.stringify(ev.detail);
|
||||||
|
widgetApi.transport.reply(ev.detail, {}); // ack
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
widgetApi.start();
|
||||||
|
|
||||||
|
document.getElementById("demoBtn").onclick = () => {
|
||||||
|
const url = new URL(window.location.href);
|
||||||
|
url.pathname = url.pathname.substr(0, url.pathname.lastIndexOf("/")) + "/theme_widget.html";
|
||||||
|
url.search = "";
|
||||||
|
widgetApi.openModalWidget(url.toString(), "Test Modal Widget", [
|
||||||
|
{id: "m.close", kind: ModalButtonKind.Danger, label: "Danger"},
|
||||||
|
{id: "org.secondary", kind: ModalButtonKind.Secondary, label: "Secondary"},
|
||||||
|
{id: "org.primary", kind: ModalButtonKind.Primary, label: "Primary"},
|
||||||
|
], {question: "Answer to everything?"});
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error setting up Jitsi widget", e);
|
||||||
|
document.getElementById("widgetActionContainer").innerText = "Failed to load Jitsi widget";
|
||||||
|
}
|
||||||
|
})();
|
|
@ -23,11 +23,19 @@ limitations under the License.
|
||||||
src: url('~matrix-react-sdk/res/fonts/Nunito/Nunito-Regular.ttf') format('truetype');
|
src: url('~matrix-react-sdk/res/fonts/Nunito/Nunito-Regular.ttf') format('truetype');
|
||||||
}
|
}
|
||||||
|
|
||||||
$fg-color: #edf3ff;
|
$dark-fg: #edf3ff;
|
||||||
|
$dark-bg: rgba(141, 151, 165, 0.2);
|
||||||
|
$light-fg: #2e2f32;
|
||||||
|
$light-bg: #fff;
|
||||||
body {
|
body {
|
||||||
font-family: Nunito, Arial, Helvetica, sans-serif;
|
font-family: Nunito, Arial, Helvetica, sans-serif;
|
||||||
background-color: #181b21;
|
background-color: $dark-bg;
|
||||||
color: $fg-color;
|
color: $dark-fg;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.theme-light {
|
||||||
|
background-color: $light-bg;
|
||||||
|
color: $light-fg;
|
||||||
}
|
}
|
||||||
|
|
||||||
body, html {
|
body, html {
|
||||||
|
@ -82,7 +90,7 @@ body, html {
|
||||||
&::before {
|
&::before {
|
||||||
content: '';
|
content: '';
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
background-color: $fg-color;
|
background-color: $dark-fg;
|
||||||
mask-repeat: no-repeat;
|
mask-repeat: no-repeat;
|
||||||
mask-position: center;
|
mask-position: center;
|
||||||
mask-image: url("~matrix-react-sdk/res/img/element-icons/call/video-call.svg");
|
mask-image: url("~matrix-react-sdk/res/img/element-icons/call/video-call.svg");
|
||||||
|
@ -93,3 +101,7 @@ body, html {
|
||||||
margin: 0 auto; // center
|
margin: 0 auto; // center
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.theme-light .icon::before {
|
||||||
|
background-color: $light-fg;
|
||||||
|
}
|
||||||
|
|
|
@ -67,6 +67,11 @@ let meetApi: any; // JitsiMeetExternalAPI
|
||||||
// out into a browser.
|
// out into a browser.
|
||||||
const parentUrl = qsParam('parentUrl', true);
|
const parentUrl = qsParam('parentUrl', true);
|
||||||
const widgetId = qsParam('widgetId', true);
|
const widgetId = qsParam('widgetId', true);
|
||||||
|
const theme = qsParam('theme', true);
|
||||||
|
|
||||||
|
if (theme) {
|
||||||
|
document.body.classList.add(`theme-${theme}`);
|
||||||
|
}
|
||||||
|
|
||||||
// Set this up as early as possible because Element will be hitting it almost immediately.
|
// Set this up as early as possible because Element will be hitting it almost immediately.
|
||||||
let readyPromise: Promise<[void, void]>;
|
let readyPromise: Promise<[void, void]>;
|
||||||
|
|
25
src/vector/theme_widget/index.html
Normal file
25
src/vector/theme_widget/index.html
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Theme Widget</title>
|
||||||
|
</head>
|
||||||
|
<body class="client-element-web">
|
||||||
|
<h1>Example Theme Widget</h1>
|
||||||
|
<div>
|
||||||
|
<h2>Button Decorations</h2>
|
||||||
|
<button type="button" class="primary">Primary</button>
|
||||||
|
<button type="button" class="danger">Danger</button>
|
||||||
|
<button type="button" class="cancel">Cancel</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2>Actual Dialog Controls (closes the dialog)</h2>
|
||||||
|
<button type="button" id="closeButton">Close</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2>Metadata</h2>
|
||||||
|
<p id="question"><!-- populated with js --></p>
|
||||||
|
<p id="button"><!-- populated with js --></p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
103
src/vector/theme_widget/index.scss
Normal file
103
src/vector/theme_widget/index.scss
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 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/riot-web/issues/12794
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Inter';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
unicode-range: U+0000-20e2,U+20e4-23ce,U+23d0-24c1,U+24c3-259f,U+25c2-2664,U+2666-2763,U+2765-2b05,U+2b07-2b1b,U+2b1d-10FFFF;
|
||||||
|
src: url("~matrix-react-sdk/res/fonts/Inter/Inter-Regular.woff2?v=3.13") format("woff2"),
|
||||||
|
url("~matrix-react-sdk/res/fonts/Inter/Inter-Regular.woff?v=3.13") format("woff");
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: Inter, Arial, Helvetica, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
body, html {
|
||||||
|
padding: 10px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.client-element-web {
|
||||||
|
&.is-dark {
|
||||||
|
background-color: #181b21;
|
||||||
|
color: #edf3ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(.is-dark) {
|
||||||
|
background-color: #edf3ff;
|
||||||
|
color: #181b21;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 7px 18px;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 4px;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: var(--accent-color, #03b381);
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.danger {
|
||||||
|
background-color: #ff4b55;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.cancel {
|
||||||
|
background-color: transparent;
|
||||||
|
color: #03b381;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body.client-element-android {
|
||||||
|
&.is-dark {
|
||||||
|
background-color: #062600;
|
||||||
|
color: #c6ffc6;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(.is-dark) {
|
||||||
|
background-color: #c6ffc6;
|
||||||
|
color: #181b21;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 12px 24px;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 12px;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: var(--accent-color, #03b381);
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.danger {
|
||||||
|
background-color: #ff4b55;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.cancel {
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--accent-color, #03b381);
|
||||||
|
}
|
||||||
|
}
|
77
src/vector/theme_widget/index.ts
Normal file
77
src/vector/theme_widget/index.ts
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 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 {
|
||||||
|
WidgetApi,
|
||||||
|
WidgetApiToWidgetAction,
|
||||||
|
IModalWidgetButtonClickedRequest,
|
||||||
|
IModalWidgetOpenRequest,
|
||||||
|
} from 'matrix-widget-api';
|
||||||
|
|
||||||
|
let widgetApi: WidgetApi;
|
||||||
|
(async function() {
|
||||||
|
try {
|
||||||
|
// 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 query = Object.assign({}, qs.parse(window.location.search.substring(1)), widgetQuery);
|
||||||
|
const qsParam = (name: string, optional = false): string => {
|
||||||
|
if (!optional && (!query[name] || typeof (query[name]) !== 'string')) {
|
||||||
|
throw new Error(`Expected singular ${name} in query string`);
|
||||||
|
}
|
||||||
|
return <string>query[name];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set this up as early as possible because Element will be hitting it almost immediately.
|
||||||
|
const parentOrigin = new URL(qsParam('parentUrl')).origin;
|
||||||
|
widgetApi = new WidgetApi(qsParam("widgetId"), parentOrigin);
|
||||||
|
|
||||||
|
widgetApi.on(
|
||||||
|
`action:${WidgetApiToWidgetAction.ButtonClicked}`,
|
||||||
|
(ev: CustomEvent<IModalWidgetButtonClickedRequest>) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
console.log("@@ clickety", ev.detail);
|
||||||
|
document.getElementById("button").innerText = "BUTTON CLICKED: " + JSON.stringify(ev.detail.data);
|
||||||
|
setTimeout(() => {
|
||||||
|
widgetApi.closeModalWidget(ev.detail.data);
|
||||||
|
}, 3000);
|
||||||
|
widgetApi.transport.reply(ev.detail, {}); // ack
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
widgetApi.on(
|
||||||
|
`action:${WidgetApiToWidgetAction.WidgetConfig}`,
|
||||||
|
(ev: CustomEvent<IModalWidgetOpenRequest>) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
console.log("Got widget config: ", ev.detail.data);
|
||||||
|
document.getElementById("question").innerText = "INIT PARAMS: " + JSON.stringify(ev.detail.data);
|
||||||
|
widgetApi.transport.reply(ev.detail, {}); // ack
|
||||||
|
},
|
||||||
|
);
|
||||||
|
widgetApi.start();
|
||||||
|
|
||||||
|
document.getElementById("closeButton").onclick = () => {
|
||||||
|
widgetApi.closeModalWidget({answer: 42});
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error setting up Jitsi widget", e);
|
||||||
|
document.getElementById("widgetActionContainer").innerText = "Failed to load Jitsi widget";
|
||||||
|
}
|
||||||
|
})();
|
|
@ -39,6 +39,8 @@ module.exports = (env, argv) => {
|
||||||
"indexeddb-worker": "./src/vector/indexeddb-worker.js",
|
"indexeddb-worker": "./src/vector/indexeddb-worker.js",
|
||||||
"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",
|
||||||
|
"theme_widget": "./src/vector/theme_widget/index.ts",
|
||||||
|
"dummy_widget": "./src/vector/dummy_widget/index.ts",
|
||||||
"usercontent": "./node_modules/matrix-react-sdk/src/usercontent/index.js",
|
"usercontent": "./node_modules/matrix-react-sdk/src/usercontent/index.js",
|
||||||
|
|
||||||
// CSS themes
|
// CSS themes
|
||||||
|
@ -312,7 +314,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', 'theme_widget', 'dummy_widget'],
|
||||||
minify: argv.mode === 'production',
|
minify: argv.mode === 'production',
|
||||||
vars: {
|
vars: {
|
||||||
og_image_url: og_image_url,
|
og_image_url: og_image_url,
|
||||||
|
@ -327,6 +329,22 @@ module.exports = (env, argv) => {
|
||||||
chunks: ['jitsi'],
|
chunks: ['jitsi'],
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
// This is the theme_widget wrapper (embedded, so isolated stack)
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
template: './src/vector/theme_widget/index.html',
|
||||||
|
filename: 'theme_widget.html',
|
||||||
|
minify: argv.mode === 'production',
|
||||||
|
chunks: ['theme_widget'],
|
||||||
|
}),
|
||||||
|
|
||||||
|
// This is the dummy_widget wrapper (embedded, so isolated stack)
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
template: './src/vector/dummy_widget/index.html',
|
||||||
|
filename: 'dummy_widget.html',
|
||||||
|
minify: argv.mode === 'production',
|
||||||
|
chunks: ['dummy_widget'],
|
||||||
|
}),
|
||||||
|
|
||||||
// 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