Apply prettier formatting
This commit is contained in:
parent
a32f12c8f3
commit
7921a6cbf8
104 changed files with 12169 additions and 11047 deletions
28
src/@types/global.d.ts
vendored
28
src/@types/global.d.ts
vendored
|
@ -19,20 +19,20 @@ import type { Renderer } from "react-dom";
|
|||
import type { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
type ElectronChannel =
|
||||
"app_onAction" |
|
||||
"before-quit" |
|
||||
"check_updates" |
|
||||
"install_update" |
|
||||
"ipcCall" |
|
||||
"ipcReply" |
|
||||
"loudNotification" |
|
||||
"preferences" |
|
||||
"seshat" |
|
||||
"seshatReply" |
|
||||
"setBadgeCount" |
|
||||
"update-downloaded" |
|
||||
"userDownloadCompleted" |
|
||||
"userDownloadAction";
|
||||
| "app_onAction"
|
||||
| "before-quit"
|
||||
| "check_updates"
|
||||
| "install_update"
|
||||
| "ipcCall"
|
||||
| "ipcReply"
|
||||
| "loudNotification"
|
||||
| "preferences"
|
||||
| "seshat"
|
||||
| "seshatReply"
|
||||
| "setBadgeCount"
|
||||
| "update-downloaded"
|
||||
| "userDownloadCompleted"
|
||||
| "userDownloadAction";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
|
|
2
src/@types/raw-loader.d.ts
vendored
2
src/@types/raw-loader.d.ts
vendored
|
@ -1,4 +1,4 @@
|
|||
declare module '!!raw-loader!*' {
|
||||
declare module "!!raw-loader!*" {
|
||||
const contents: string;
|
||||
export default contents;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
|||
|
||||
import * as React from "react";
|
||||
import { _t } from "matrix-react-sdk/src/languageHandler";
|
||||
import SdkConfig from 'matrix-react-sdk/src/SdkConfig';
|
||||
import SdkConfig from "matrix-react-sdk/src/SdkConfig";
|
||||
|
||||
// directly import the style here as this layer does not support rethemedex at this time so no matrix-react-sdk
|
||||
// PostCSS variables will be accessible.
|
||||
|
@ -32,115 +32,131 @@ const CompatibilityView: React.FC<IProps> = ({ onAccept }) => {
|
|||
|
||||
let ios = null;
|
||||
const iosCustomUrl = mobileBuilds?.ios;
|
||||
if (iosCustomUrl !== null) { // could be undefined or a string
|
||||
ios = <>
|
||||
<p><strong>iOS</strong> (iPhone or iPad)</p>
|
||||
<a
|
||||
href={iosCustomUrl || "https://apps.apple.com/app/vector/id1083446067"}
|
||||
target="_blank"
|
||||
className="mx_ClearDecoration"
|
||||
>
|
||||
<img height="48" src="themes/element/img/download/apple.svg" alt="Apple App Store" />
|
||||
</a>
|
||||
</>;
|
||||
if (iosCustomUrl !== null) {
|
||||
// could be undefined or a string
|
||||
ios = (
|
||||
<>
|
||||
<p>
|
||||
<strong>iOS</strong> (iPhone or iPad)
|
||||
</p>
|
||||
<a
|
||||
href={iosCustomUrl || "https://apps.apple.com/app/vector/id1083446067"}
|
||||
target="_blank"
|
||||
className="mx_ClearDecoration"
|
||||
>
|
||||
<img height="48" src="themes/element/img/download/apple.svg" alt="Apple App Store" />
|
||||
</a>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
let android = [<p className="mx_Spacer" key="header"><strong>Android</strong></p>];
|
||||
let android = [
|
||||
<p className="mx_Spacer" key="header">
|
||||
<strong>Android</strong>
|
||||
</p>,
|
||||
];
|
||||
const andCustomUrl = mobileBuilds?.android;
|
||||
const fdroidCustomUrl = mobileBuilds?.fdroid;
|
||||
if (andCustomUrl !== null) { // undefined or string
|
||||
android.push(<a
|
||||
href={andCustomUrl || "https://play.google.com/store/apps/details?id=im.vector.app"}
|
||||
target="_blank"
|
||||
className="mx_ClearDecoration"
|
||||
key="android"
|
||||
>
|
||||
<img height="48" src="themes/element/img/download/google.svg" alt="Google Play Store" />
|
||||
</a>);
|
||||
if (andCustomUrl !== null) {
|
||||
// undefined or string
|
||||
android.push(
|
||||
<a
|
||||
href={andCustomUrl || "https://play.google.com/store/apps/details?id=im.vector.app"}
|
||||
target="_blank"
|
||||
className="mx_ClearDecoration"
|
||||
key="android"
|
||||
>
|
||||
<img height="48" src="themes/element/img/download/google.svg" alt="Google Play Store" />
|
||||
</a>,
|
||||
);
|
||||
}
|
||||
if (fdroidCustomUrl !== null) { // undefined or string
|
||||
android.push(<a
|
||||
href={fdroidCustomUrl || "https://f-droid.org/repository/browse/?fdid=im.vector.app"}
|
||||
target="_blank"
|
||||
className="mx_ClearDecoration"
|
||||
key="fdroid"
|
||||
>
|
||||
<img height="48" src="themes/element/img/download/fdroid.svg" alt="F-Droid" />
|
||||
</a>);
|
||||
if (fdroidCustomUrl !== null) {
|
||||
// undefined or string
|
||||
android.push(
|
||||
<a
|
||||
href={fdroidCustomUrl || "https://f-droid.org/repository/browse/?fdid=im.vector.app"}
|
||||
target="_blank"
|
||||
className="mx_ClearDecoration"
|
||||
key="fdroid"
|
||||
>
|
||||
<img height="48" src="themes/element/img/download/fdroid.svg" alt="F-Droid" />
|
||||
</a>,
|
||||
);
|
||||
}
|
||||
if (android.length === 1) { // just a header, meaning no links
|
||||
if (android.length === 1) {
|
||||
// just a header, meaning no links
|
||||
android = [];
|
||||
}
|
||||
|
||||
let mobileHeader = <h2 id="step2_heading">{ _t("Use %(brand)s on mobile", { brand }) }</h2>;
|
||||
let mobileHeader = <h2 id="step2_heading">{_t("Use %(brand)s on mobile", { brand })}</h2>;
|
||||
if (!android.length && !ios) {
|
||||
mobileHeader = null;
|
||||
}
|
||||
|
||||
return <div className="mx_ErrorView">
|
||||
<div className="mx_ErrorView_container">
|
||||
<div className="mx_HomePage_header">
|
||||
<span className="mx_HomePage_logo">
|
||||
<img height="42" src="themes/element/img/logos/element-logo.svg" alt="Element" />
|
||||
</span>
|
||||
<h1>{ _t("Unsupported browser") }</h1>
|
||||
</div>
|
||||
return (
|
||||
<div className="mx_ErrorView">
|
||||
<div className="mx_ErrorView_container">
|
||||
<div className="mx_HomePage_header">
|
||||
<span className="mx_HomePage_logo">
|
||||
<img height="42" src="themes/element/img/logos/element-logo.svg" alt="Element" />
|
||||
</span>
|
||||
<h1>{_t("Unsupported browser")}</h1>
|
||||
</div>
|
||||
|
||||
<div className="mx_HomePage_col">
|
||||
<div className="mx_HomePage_row">
|
||||
<div>
|
||||
<h2 id="step1_heading">{ _t("Your browser can't run %(brand)s", { brand }) }</h2>
|
||||
<p>
|
||||
{ _t(
|
||||
"%(brand)s uses advanced browser features which aren't " +
|
||||
"supported by your current browser.",
|
||||
{ brand },
|
||||
) }
|
||||
</p>
|
||||
<p>
|
||||
{ _t(
|
||||
'Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, ' +
|
||||
'or <safariLink>Safari</safariLink> for the best experience.',
|
||||
{},
|
||||
{
|
||||
'chromeLink': (sub) => <a href="https://www.google.com/chrome">{ sub }</a>,
|
||||
'firefoxLink': (sub) => <a href="https://firefox.com">{ sub }</a>,
|
||||
'safariLink': (sub) => <a href="https://apple.com/safari">{ sub }</a>,
|
||||
},
|
||||
) }
|
||||
</p>
|
||||
<p>
|
||||
{ _t(
|
||||
"You can continue using your current browser, but some or all features may not work " +
|
||||
"and the look and feel of the application may be incorrect.",
|
||||
) }
|
||||
</p>
|
||||
<button onClick={onAccept}>
|
||||
{ _t("I understand the risks and wish to continue") }
|
||||
</button>
|
||||
<div className="mx_HomePage_col">
|
||||
<div className="mx_HomePage_row">
|
||||
<div>
|
||||
<h2 id="step1_heading">{_t("Your browser can't run %(brand)s", { brand })}</h2>
|
||||
<p>
|
||||
{_t(
|
||||
"%(brand)s uses advanced browser features which aren't " +
|
||||
"supported by your current browser.",
|
||||
{ brand },
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
{_t(
|
||||
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, " +
|
||||
"or <safariLink>Safari</safariLink> for the best experience.",
|
||||
{},
|
||||
{
|
||||
chromeLink: (sub) => <a href="https://www.google.com/chrome">{sub}</a>,
|
||||
firefoxLink: (sub) => <a href="https://firefox.com">{sub}</a>,
|
||||
safariLink: (sub) => <a href="https://apple.com/safari">{sub}</a>,
|
||||
},
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
{_t(
|
||||
"You can continue using your current browser, but some or all features may not work " +
|
||||
"and the look and feel of the application may be incorrect.",
|
||||
)}
|
||||
</p>
|
||||
<button onClick={onAccept}>{_t("I understand the risks and wish to continue")}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mx_HomePage_col">
|
||||
<div className="mx_HomePage_row">
|
||||
<div>
|
||||
{ mobileHeader }
|
||||
{ ios }
|
||||
{ android }
|
||||
<div className="mx_HomePage_col">
|
||||
<div className="mx_HomePage_row">
|
||||
<div>
|
||||
{mobileHeader}
|
||||
{ios}
|
||||
{android}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mx_HomePage_row mx_Center mx_Spacer">
|
||||
<p className="mx_Spacer">
|
||||
<a href="https://element.io" target="_blank" className="mx_FooterLink">
|
||||
{ _t("Go to element.io") }
|
||||
</a>
|
||||
</p>
|
||||
<div className="mx_HomePage_row mx_Center mx_Spacer">
|
||||
<p className="mx_Spacer">
|
||||
<a href="https://element.io" target="_blank" className="mx_FooterLink">
|
||||
{_t("Go to element.io")}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
);
|
||||
};
|
||||
|
||||
export default CompatibilityView;
|
||||
|
|
|
@ -28,34 +28,33 @@ interface IProps {
|
|||
}
|
||||
|
||||
const ErrorView: React.FC<IProps> = ({ title, messages }) => {
|
||||
return <div className="mx_ErrorView">
|
||||
<div className="mx_ErrorView_container">
|
||||
<div className="mx_HomePage_header">
|
||||
<span className="mx_HomePage_logo">
|
||||
<img height="42" src="themes/element/img/logos/element-logo.svg" alt="Element" />
|
||||
</span>
|
||||
<h1>{ _t("Failed to start") }</h1>
|
||||
</div>
|
||||
<div className="mx_HomePage_col">
|
||||
<div className="mx_HomePage_row">
|
||||
<div>
|
||||
<h2 id="step1_heading">{ title }</h2>
|
||||
{ messages && messages.map(msg => <p key={msg}>
|
||||
{ msg }
|
||||
</p>) }
|
||||
return (
|
||||
<div className="mx_ErrorView">
|
||||
<div className="mx_ErrorView_container">
|
||||
<div className="mx_HomePage_header">
|
||||
<span className="mx_HomePage_logo">
|
||||
<img height="42" src="themes/element/img/logos/element-logo.svg" alt="Element" />
|
||||
</span>
|
||||
<h1>{_t("Failed to start")}</h1>
|
||||
</div>
|
||||
<div className="mx_HomePage_col">
|
||||
<div className="mx_HomePage_row">
|
||||
<div>
|
||||
<h2 id="step1_heading">{title}</h2>
|
||||
{messages && messages.map((msg) => <p key={msg}>{msg}</p>)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx_HomePage_row mx_Center mx_Spacer">
|
||||
<p className="mx_Spacer">
|
||||
<a href="https://element.io" target="_blank" className="mx_FooterLink">
|
||||
{ _t("Go to element.io") }
|
||||
</a>
|
||||
</p>
|
||||
<div className="mx_HomePage_row mx_Center mx_Spacer">
|
||||
<p className="mx_Spacer">
|
||||
<a href="https://element.io" target="_blank" className="mx_FooterLink">
|
||||
{_t("Go to element.io")}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
);
|
||||
};
|
||||
|
||||
export default ErrorView;
|
||||
|
||||
|
|
|
@ -15,31 +15,33 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { ReactElement } from 'react';
|
||||
import SdkConfig from 'matrix-react-sdk/src/SdkConfig';
|
||||
import { _t } from 'matrix-react-sdk/src/languageHandler';
|
||||
import React, { ReactElement } from "react";
|
||||
import SdkConfig from "matrix-react-sdk/src/SdkConfig";
|
||||
import { _t } from "matrix-react-sdk/src/languageHandler";
|
||||
|
||||
const VectorAuthFooter = (): ReactElement => {
|
||||
const brandingConfig = SdkConfig.getObject("branding");
|
||||
const links = brandingConfig?.get("auth_footer_links") ?? [
|
||||
{ "text": "Blog", "url": "https://element.io/blog" },
|
||||
{ "text": "Twitter", "url": "https://twitter.com/element_hq" },
|
||||
{ "text": "GitHub", "url": "https://github.com/vector-im/element-web" },
|
||||
{ text: "Blog", url: "https://element.io/blog" },
|
||||
{ text: "Twitter", url: "https://twitter.com/element_hq" },
|
||||
{ text: "GitHub", url: "https://github.com/vector-im/element-web" },
|
||||
];
|
||||
|
||||
const authFooterLinks = [];
|
||||
for (const linkEntry of links) {
|
||||
authFooterLinks.push(
|
||||
<a href={linkEntry.url} key={linkEntry.text} target="_blank" rel="noreferrer noopener">
|
||||
{ linkEntry.text }
|
||||
{linkEntry.text}
|
||||
</a>,
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<footer className="mx_AuthFooter" role="contentinfo">
|
||||
{ authFooterLinks }
|
||||
<a href="https://matrix.org" target="_blank" rel="noreferrer noopener">{ _t('Powered by Matrix') }</a>
|
||||
{authFooterLinks}
|
||||
<a href="https://matrix.org" target="_blank" rel="noreferrer noopener">
|
||||
{_t("Powered by Matrix")}
|
||||
</a>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -15,8 +15,8 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import SdkConfig from 'matrix-react-sdk/src/SdkConfig';
|
||||
import * as React from "react";
|
||||
import SdkConfig from "matrix-react-sdk/src/SdkConfig";
|
||||
|
||||
export default class VectorAuthHeaderLogo extends React.PureComponent {
|
||||
public render(): React.ReactElement {
|
||||
|
|
|
@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import SdkConfig from 'matrix-react-sdk/src/SdkConfig';
|
||||
import * as React from "react";
|
||||
import SdkConfig from "matrix-react-sdk/src/SdkConfig";
|
||||
|
||||
import VectorAuthFooter from "./VectorAuthFooter";
|
||||
|
||||
|
@ -48,25 +48,25 @@ export default class VectorAuthPage extends React.PureComponent {
|
|||
};
|
||||
|
||||
const modalStyle: React.CSSProperties = {
|
||||
position: 'relative',
|
||||
background: 'initial',
|
||||
position: "relative",
|
||||
background: "initial",
|
||||
};
|
||||
|
||||
const blurStyle: React.CSSProperties = {
|
||||
position: 'absolute',
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
filter: 'blur(40px)',
|
||||
filter: "blur(40px)",
|
||||
background: pageStyle.background,
|
||||
};
|
||||
|
||||
const modalContentStyle: React.CSSProperties = {
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
zIndex: 1,
|
||||
background: 'rgba(255, 255, 255, 0.59)',
|
||||
borderRadius: '8px',
|
||||
background: "rgba(255, 255, 255, 0.59)",
|
||||
borderRadius: "8px",
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -74,7 +74,7 @@ export default class VectorAuthPage extends React.PureComponent {
|
|||
<div className="mx_AuthPage_modal" style={modalStyle}>
|
||||
<div className="mx_AuthPage_modalBlur" style={blurStyle} />
|
||||
<div className="mx_AuthPage_modalContent" style={modalContentStyle}>
|
||||
{ this.props.children }
|
||||
{this.props.children}
|
||||
</div>
|
||||
</div>
|
||||
<VectorAuthFooter />
|
||||
|
|
|
@ -70,8 +70,8 @@ export default class Favicon {
|
|||
this.baseImage.setAttribute("crossOrigin", "anonymous");
|
||||
this.baseImage.onload = (): void => {
|
||||
// get height and width of the favicon
|
||||
this.canvas.height = (this.baseImage.height > 0) ? this.baseImage.height : 32;
|
||||
this.canvas.width = (this.baseImage.width > 0) ? this.baseImage.width : 32;
|
||||
this.canvas.height = this.baseImage.height > 0 ? this.baseImage.height : 32;
|
||||
this.canvas.width = this.baseImage.width > 0 ? this.baseImage.width : 32;
|
||||
this.context = this.canvas.getContext("2d");
|
||||
this.ready();
|
||||
};
|
||||
|
@ -89,7 +89,10 @@ export default class Favicon {
|
|||
this.context.drawImage(this.baseImage, 0, 0, this.canvas.width, this.canvas.height);
|
||||
}
|
||||
|
||||
private options(n: number | string, params: IParams): {
|
||||
private options(
|
||||
n: number | string,
|
||||
params: IParams,
|
||||
): {
|
||||
n: string | number;
|
||||
len: number;
|
||||
x: number;
|
||||
|
@ -98,7 +101,7 @@ export default class Favicon {
|
|||
h: number;
|
||||
} {
|
||||
const opt = {
|
||||
n: ((typeof n) === "number") ? Math.abs(n as number | 0) : n,
|
||||
n: typeof n === "number" ? Math.abs(n as number | 0) : n,
|
||||
len: ("" + n).length,
|
||||
// badge positioning constants as percentages
|
||||
x: 0.4,
|
||||
|
@ -174,8 +177,8 @@ export default class Favicon {
|
|||
this.context.stroke();
|
||||
this.context.fillStyle = params.textColor;
|
||||
|
||||
if ((typeof opt.n) === "number" && opt.n > 999) {
|
||||
const count = ((opt.n > 9999) ? 9 : Math.floor(opt.n as number / 1000)) + "k+";
|
||||
if (typeof opt.n === "number" && opt.n > 999) {
|
||||
const count = (opt.n > 9999 ? 9 : Math.floor((opt.n as number) / 1000)) + "k+";
|
||||
this.context.fillText(count, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.2));
|
||||
} else {
|
||||
this.context.fillText("" + opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15));
|
||||
|
@ -209,7 +212,7 @@ export default class Favicon {
|
|||
newIcon.setAttribute("href", url);
|
||||
old.parentNode?.removeChild(old);
|
||||
} else {
|
||||
this.icons.forEach(icon => {
|
||||
this.icons.forEach((icon) => {
|
||||
icon.setAttribute("href", url);
|
||||
});
|
||||
}
|
||||
|
@ -236,7 +239,7 @@ export default class Favicon {
|
|||
const icons: HTMLLinkElement[] = [];
|
||||
const links = window.document.getElementsByTagName("head")[0].getElementsByTagName("link");
|
||||
for (const link of links) {
|
||||
if ((/(^|\s)icon(\s|$)/i).test(link.getAttribute("rel"))) {
|
||||
if (/(^|\s)icon(\s|$)/i.test(link.getAttribute("rel"))) {
|
||||
icons.push(link);
|
||||
}
|
||||
}
|
||||
|
@ -252,7 +255,7 @@ export default class Favicon {
|
|||
window.document.getElementsByTagName("head")[0].appendChild(elms[0]);
|
||||
}
|
||||
|
||||
elms.forEach(item => {
|
||||
elms.forEach((item) => {
|
||||
item.setAttribute("type", "image/png");
|
||||
});
|
||||
return elms;
|
||||
|
|
|
@ -21,10 +21,10 @@ limitations under the License.
|
|||
// To ensure we load the browser-matrix version first
|
||||
import "matrix-js-sdk/src/browser-index";
|
||||
|
||||
import React, { ReactElement } from 'react';
|
||||
import PlatformPeg from 'matrix-react-sdk/src/PlatformPeg';
|
||||
import { _td, newTranslatableError } from 'matrix-react-sdk/src/languageHandler';
|
||||
import AutoDiscoveryUtils from 'matrix-react-sdk/src/utils/AutoDiscoveryUtils';
|
||||
import React, { ReactElement } from "react";
|
||||
import PlatformPeg from "matrix-react-sdk/src/PlatformPeg";
|
||||
import { _td, newTranslatableError } from "matrix-react-sdk/src/languageHandler";
|
||||
import AutoDiscoveryUtils from "matrix-react-sdk/src/utils/AutoDiscoveryUtils";
|
||||
import { AutoDiscovery } from "matrix-js-sdk/src/autodiscovery";
|
||||
import * as Lifecycle from "matrix-react-sdk/src/Lifecycle";
|
||||
import SdkConfig, { parseSsoRedirectOptions } from "matrix-react-sdk/src/SdkConfig";
|
||||
|
@ -34,7 +34,7 @@ import { createClient } from "matrix-js-sdk/src/matrix";
|
|||
import { SnakedObject } from "matrix-react-sdk/src/utils/SnakedObject";
|
||||
import MatrixChat from "matrix-react-sdk/src/components/structures/MatrixChat";
|
||||
|
||||
import { parseQs } from './url_utils';
|
||||
import { parseQs } from "./url_utils";
|
||||
import VectorBasePlatform from "./platform/VectorBasePlatform";
|
||||
import { getScreenFromLocation, init as initRouting, onNewScreen } from "./routing";
|
||||
|
||||
|
@ -58,25 +58,20 @@ window.matrixLogger = logger;
|
|||
function makeRegistrationUrl(params: object): string {
|
||||
let url;
|
||||
if (window.location.protocol === "vector:") {
|
||||
url = 'https://app.element.io/#/register';
|
||||
url = "https://app.element.io/#/register";
|
||||
} else {
|
||||
url = (
|
||||
window.location.protocol + '//' +
|
||||
window.location.host +
|
||||
window.location.pathname +
|
||||
'#/register'
|
||||
);
|
||||
url = window.location.protocol + "//" + window.location.host + window.location.pathname + "#/register";
|
||||
}
|
||||
|
||||
const keys = Object.keys(params);
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
if (i === 0) {
|
||||
url += '?';
|
||||
url += "?";
|
||||
} else {
|
||||
url += '&';
|
||||
url += "&";
|
||||
}
|
||||
const k = keys[i];
|
||||
url += k + '=' + encodeURIComponent(params[k]);
|
||||
url += k + "=" + encodeURIComponent(params[k]);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
@ -99,7 +94,7 @@ export async function loadApp(fragParams: {}): Promise<ReactElement> {
|
|||
|
||||
const params = parseQs(window.location);
|
||||
|
||||
const urlWithoutQuery = window.location.protocol + '//' + window.location.host + window.location.pathname;
|
||||
const urlWithoutQuery = window.location.protocol + "//" + window.location.host + window.location.pathname;
|
||||
logger.log("Vector starting at " + urlWithoutQuery);
|
||||
|
||||
(platform as VectorBasePlatform).startUpdater();
|
||||
|
@ -115,7 +110,7 @@ export async function loadApp(fragParams: {}): Promise<ReactElement> {
|
|||
const ssoRedirects = parseSsoRedirectOptions(config);
|
||||
let autoRedirect = ssoRedirects.immediate === true;
|
||||
// XXX: This path matching is a bit brittle, but better to do it early instead of in the app code.
|
||||
const isWelcomeOrLanding = window.location.hash === '#/welcome' || window.location.hash === '#';
|
||||
const isWelcomeOrLanding = window.location.hash === "#/welcome" || window.location.hash === "#";
|
||||
if (!autoRedirect && ssoRedirects.on_welcome_page && isWelcomeOrLanding) {
|
||||
autoRedirect = true;
|
||||
}
|
||||
|
@ -133,20 +128,21 @@ export async function loadApp(fragParams: {}): Promise<ReactElement> {
|
|||
return;
|
||||
}
|
||||
|
||||
const defaultDeviceName = snakedConfig.get("default_device_display_name")
|
||||
?? platform.getDefaultDeviceDisplayName();
|
||||
const defaultDeviceName = snakedConfig.get("default_device_display_name") ?? platform.getDefaultDeviceDisplayName();
|
||||
|
||||
return <MatrixChat
|
||||
onNewScreen={onNewScreen}
|
||||
makeRegistrationUrl={makeRegistrationUrl}
|
||||
config={config}
|
||||
realQueryParams={params}
|
||||
startingFragmentQueryParams={fragParams}
|
||||
enableGuest={!config.disable_guests}
|
||||
onTokenLoginCompleted={onTokenLoginCompleted}
|
||||
initialScreenAfterLogin={getScreenFromLocation(window.location)}
|
||||
defaultDeviceDisplayName={defaultDeviceName}
|
||||
/>;
|
||||
return (
|
||||
<MatrixChat
|
||||
onNewScreen={onNewScreen}
|
||||
makeRegistrationUrl={makeRegistrationUrl}
|
||||
config={config}
|
||||
realQueryParams={params}
|
||||
startingFragmentQueryParams={fragParams}
|
||||
enableGuest={!config.disable_guests}
|
||||
onTokenLoginCompleted={onTokenLoginCompleted}
|
||||
initialScreenAfterLogin={getScreenFromLocation(window.location)}
|
||||
defaultDeviceDisplayName={defaultDeviceName}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
async function verifyServerConfig(): Promise<IConfigOptions> {
|
||||
|
@ -164,18 +160,20 @@ async function verifyServerConfig(): Promise<IConfigOptions> {
|
|||
// validators for that purpose.
|
||||
|
||||
const config = SdkConfig.get();
|
||||
let wkConfig = config['default_server_config']; // overwritten later under some conditions
|
||||
const serverName = config['default_server_name'];
|
||||
const hsUrl = config['default_hs_url'];
|
||||
const isUrl = config['default_is_url'];
|
||||
let wkConfig = config["default_server_config"]; // overwritten later under some conditions
|
||||
const serverName = config["default_server_name"];
|
||||
const hsUrl = config["default_hs_url"];
|
||||
const isUrl = config["default_is_url"];
|
||||
|
||||
const incompatibleOptions = [wkConfig, serverName, hsUrl].filter(i => !!i);
|
||||
const incompatibleOptions = [wkConfig, serverName, hsUrl].filter((i) => !!i);
|
||||
if (incompatibleOptions.length > 1) {
|
||||
// noinspection ExceptionCaughtLocallyJS
|
||||
throw newTranslatableError(_td(
|
||||
"Invalid configuration: can only specify one of default_server_config, default_server_name, " +
|
||||
"or default_hs_url.",
|
||||
));
|
||||
throw newTranslatableError(
|
||||
_td(
|
||||
"Invalid configuration: can only specify one of default_server_config, default_server_name, " +
|
||||
"or default_hs_url.",
|
||||
),
|
||||
);
|
||||
}
|
||||
if (incompatibleOptions.length < 1) {
|
||||
// noinspection ExceptionCaughtLocallyJS
|
||||
|
@ -186,17 +184,17 @@ async function verifyServerConfig(): Promise<IConfigOptions> {
|
|||
logger.log("Config uses a default_hs_url - constructing a default_server_config using this information");
|
||||
logger.warn(
|
||||
"DEPRECATED CONFIG OPTION: In the future, default_hs_url will not be accepted. Please use " +
|
||||
"default_server_config instead.",
|
||||
"default_server_config instead.",
|
||||
);
|
||||
|
||||
wkConfig = {
|
||||
"m.homeserver": {
|
||||
"base_url": hsUrl,
|
||||
base_url: hsUrl,
|
||||
},
|
||||
};
|
||||
if (isUrl) {
|
||||
wkConfig["m.identity_server"] = {
|
||||
"base_url": isUrl,
|
||||
base_url: isUrl,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -211,7 +209,7 @@ async function verifyServerConfig(): Promise<IConfigOptions> {
|
|||
logger.log("Config uses a default_server_name - doing .well-known lookup");
|
||||
logger.warn(
|
||||
"DEPRECATED CONFIG OPTION: In the future, default_server_name will not be accepted. Please " +
|
||||
"use default_server_config instead.",
|
||||
"use default_server_config instead.",
|
||||
);
|
||||
discoveryResult = await AutoDiscovery.findClientConfig(serverName);
|
||||
}
|
||||
|
@ -238,7 +236,7 @@ async function verifyServerConfig(): Promise<IConfigOptions> {
|
|||
|
||||
// Add the newly built config to the actual config for use by the app
|
||||
logger.log("Updating SdkConfig with validated discovery information");
|
||||
SdkConfig.add({ "validated_server_config": validatedConfig });
|
||||
SdkConfig.add({ validated_server_config: validatedConfig });
|
||||
|
||||
return SdkConfig.get();
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ limitations under the License.
|
|||
*
|
||||
* For more details, see webpack.config.js:184 (string-replace-loader)
|
||||
*/
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
("use theming");
|
||||
/**
|
||||
* Clean up old hot-module script injections as they hog up memory
|
||||
|
@ -40,8 +40,7 @@ if (process.env.NODE_ENV === 'development') {
|
|||
const elements = Array.from(document.querySelectorAll("script[src*=hot-update]"));
|
||||
if (elements.length > 1) {
|
||||
const oldInjects = elements.slice(0, elements.length - 1);
|
||||
oldInjects.forEach(e => e.remove());
|
||||
oldInjects.forEach((e) => e.remove());
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,8 +18,8 @@ import type { IConfigOptions } from "matrix-react-sdk/src/IConfigOptions";
|
|||
|
||||
// Load the config file. First try to load up a domain-specific config of the
|
||||
// form "config.$domain.json" and if that fails, fall back to config.json.
|
||||
export async function getVectorConfig(relativeLocation=''): Promise<IConfigOptions> {
|
||||
if (relativeLocation !== '' && !relativeLocation.endsWith('/')) relativeLocation += '/';
|
||||
export async function getVectorConfig(relativeLocation = ""): Promise<IConfigOptions> {
|
||||
if (relativeLocation !== "" && !relativeLocation.endsWith("/")) relativeLocation += "/";
|
||||
|
||||
const specificConfigPromise = getConfig(`${relativeLocation}config.${document.domain}.json`);
|
||||
const generalConfigPromise = getConfig(relativeLocation + "config.json");
|
||||
|
|
|
@ -22,13 +22,13 @@ import { logger } from "matrix-js-sdk/src/logger";
|
|||
|
||||
// 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';
|
||||
import "./modernizr";
|
||||
|
||||
// Require common CSS here; this will make webpack process it into bundle.css.
|
||||
// Our own CSS (which is themed) is imported via separate webpack entry points
|
||||
// in webpack.config.js
|
||||
require('gfm.css/gfm.css');
|
||||
require('katex/dist/katex.css');
|
||||
require("gfm.css/gfm.css");
|
||||
require("katex/dist/katex.css");
|
||||
|
||||
/**
|
||||
* This require is necessary only for purposes of CSS hot-reload, as otherwise
|
||||
|
@ -37,8 +37,8 @@ require('katex/dist/katex.css');
|
|||
*
|
||||
* On production build it's going to be an empty module, so don't worry about that.
|
||||
*/
|
||||
require('./devcss');
|
||||
require('./localstorage-fix');
|
||||
require("./devcss");
|
||||
require("./localstorage-fix");
|
||||
|
||||
async function settled(...promises: Array<Promise<any>>): Promise<void> {
|
||||
for (const prom of promises) {
|
||||
|
@ -60,19 +60,16 @@ function checkBrowserFeatures(): boolean {
|
|||
// in it for some features we depend on.
|
||||
// Modernizr requires rules to be lowercase with no punctuation.
|
||||
// ES2018: http://262.ecma-international.org/9.0/#sec-promise.prototype.finally
|
||||
window.Modernizr.addTest("promiseprototypefinally", () =>
|
||||
typeof window.Promise?.prototype?.finally === "function");
|
||||
window.Modernizr.addTest("promiseprototypefinally", () => typeof window.Promise?.prototype?.finally === "function");
|
||||
// ES2020: http://262.ecma-international.org/#sec-promise.allsettled
|
||||
window.Modernizr.addTest("promiseallsettled", () =>
|
||||
typeof window.Promise?.allSettled === "function");
|
||||
window.Modernizr.addTest("promiseallsettled", () => typeof window.Promise?.allSettled === "function");
|
||||
// ES2018: https://262.ecma-international.org/9.0/#sec-get-regexp.prototype.dotAll
|
||||
window.Modernizr.addTest("regexpdotall", () => (
|
||||
window.RegExp?.prototype &&
|
||||
!!Object.getOwnPropertyDescriptor(window.RegExp.prototype, "dotAll")?.get
|
||||
));
|
||||
window.Modernizr.addTest(
|
||||
"regexpdotall",
|
||||
() => window.RegExp?.prototype && !!Object.getOwnPropertyDescriptor(window.RegExp.prototype, "dotAll")?.get,
|
||||
);
|
||||
// ES2019: http://262.ecma-international.org/10.0/#sec-object.fromentries
|
||||
window.Modernizr.addTest("objectfromentries", () =>
|
||||
typeof window.Object?.fromEntries === "function");
|
||||
window.Modernizr.addTest("objectfromentries", () => typeof window.Object?.fromEntries === "function");
|
||||
|
||||
const featureList = Object.keys(window.Modernizr);
|
||||
|
||||
|
@ -80,8 +77,8 @@ function checkBrowserFeatures(): boolean {
|
|||
for (const feature of featureList) {
|
||||
if (window.Modernizr[feature] === undefined) {
|
||||
logger.error(
|
||||
"Looked for feature '%s' but Modernizr has no results for this. " +
|
||||
"Has it been configured correctly?", feature,
|
||||
"Looked for feature '%s' but Modernizr has no results for this. " + "Has it been configured correctly?",
|
||||
feature,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
@ -120,7 +117,8 @@ async function start(): Promise<void> {
|
|||
} = await import(
|
||||
/* webpackChunkName: "init" */
|
||||
/* webpackPreload: true */
|
||||
"./init");
|
||||
"./init"
|
||||
);
|
||||
|
||||
try {
|
||||
// give rageshake a chance to load/fail, we don't actually assert rageshake loads, we allow it to fail if no IDB
|
||||
|
@ -178,12 +176,12 @@ async function start(): Promise<void> {
|
|||
// error handling begins here
|
||||
// ##########################
|
||||
if (!acceptBrowser) {
|
||||
await new Promise<void>(resolve => {
|
||||
await new Promise<void>((resolve) => {
|
||||
logger.error("Browser is missing required features.");
|
||||
// take to a different landing page to AWOOOOOGA at the user
|
||||
showIncompatibleBrowser(() => {
|
||||
if (window.localStorage) {
|
||||
window.localStorage.setItem('mx_accepts_unsupported_browser', String(true));
|
||||
window.localStorage.setItem("mx_accepts_unsupported_browser", String(true));
|
||||
}
|
||||
logger.log("User accepts the compatibility risks.");
|
||||
resolve();
|
||||
|
@ -199,12 +197,13 @@ async function start(): Promise<void> {
|
|||
if (error.err && error.err instanceof SyntaxError) {
|
||||
// This uses the default brand since the app config is unavailable.
|
||||
return showError(_t("Your Element is misconfigured"), [
|
||||
_t("Your Element configuration contains invalid JSON. " +
|
||||
"Please correct the problem and reload the page."),
|
||||
_t(
|
||||
"The message from the parser is: %(message)s",
|
||||
{ message: error.err.message || _t("Invalid JSON") },
|
||||
"Your Element configuration contains invalid JSON. " +
|
||||
"Please correct the problem and reload the page.",
|
||||
),
|
||||
_t("The message from the parser is: %(message)s", {
|
||||
message: error.err.message || _t("Invalid JSON"),
|
||||
}),
|
||||
]);
|
||||
}
|
||||
return showError(_t("Unable to load config file: please refresh the page to try again."));
|
||||
|
@ -237,7 +236,7 @@ async function start(): Promise<void> {
|
|||
}
|
||||
}
|
||||
|
||||
start().catch(err => {
|
||||
start().catch((err) => {
|
||||
logger.error(err);
|
||||
// show the static error in an iframe to not lose any context / console data
|
||||
// with some basic styling to make the iframe full page
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { IndexedDBStoreWorker } from 'matrix-js-sdk/src/indexeddb-worker';
|
||||
import { IndexedDBStoreWorker } from "matrix-js-sdk/src/indexeddb-worker";
|
||||
|
||||
const remoteWorker = new IndexedDBStoreWorker(postMessage as InstanceType<typeof Worker>["postMessage"]);
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ limitations under the License.
|
|||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
import olmWasmPath from "@matrix-org/olm/olm.wasm";
|
||||
import Olm from '@matrix-org/olm';
|
||||
import Olm from "@matrix-org/olm";
|
||||
import * as ReactDOM from "react-dom";
|
||||
import * as React from "react";
|
||||
import * as languageHandler from "matrix-react-sdk/src/languageHandler";
|
||||
|
@ -45,7 +45,7 @@ export function preparePlatform(): void {
|
|||
if (window.electron) {
|
||||
logger.log("Using Electron platform");
|
||||
PlatformPeg.set(new ElectronPlatform());
|
||||
} else if (window.matchMedia('(display-mode: standalone)').matches) {
|
||||
} else if (window.matchMedia("(display-mode: standalone)").matches) {
|
||||
logger.log("Using PWA platform");
|
||||
PlatformPeg.set(new PWAPlatform());
|
||||
} else {
|
||||
|
@ -90,30 +90,35 @@ export function loadOlm(): Promise<void> {
|
|||
*/
|
||||
return Olm.init({
|
||||
locateFile: () => olmWasmPath,
|
||||
}).then(() => {
|
||||
logger.log("Using WebAssembly Olm");
|
||||
}).catch((wasmLoadError) => {
|
||||
logger.log("Failed to load Olm: trying legacy version", wasmLoadError);
|
||||
return new Promise((resolve, reject) => {
|
||||
const s = document.createElement('script');
|
||||
s.src = 'olm_legacy.js'; // XXX: This should be cache-busted too
|
||||
s.onload = resolve;
|
||||
s.onerror = reject;
|
||||
document.body.appendChild(s);
|
||||
}).then(() => {
|
||||
// Init window.Olm, ie. the one just loaded by the script tag,
|
||||
// not 'Olm' which is still the failed wasm version.
|
||||
return window.Olm.init();
|
||||
}).then(() => {
|
||||
logger.log("Using legacy Olm");
|
||||
}).catch((legacyLoadError) => {
|
||||
logger.log("Both WebAssembly and asm.js Olm failed!", legacyLoadError);
|
||||
})
|
||||
.then(() => {
|
||||
logger.log("Using WebAssembly Olm");
|
||||
})
|
||||
.catch((wasmLoadError) => {
|
||||
logger.log("Failed to load Olm: trying legacy version", wasmLoadError);
|
||||
return new Promise((resolve, reject) => {
|
||||
const s = document.createElement("script");
|
||||
s.src = "olm_legacy.js"; // XXX: This should be cache-busted too
|
||||
s.onload = resolve;
|
||||
s.onerror = reject;
|
||||
document.body.appendChild(s);
|
||||
})
|
||||
.then(() => {
|
||||
// Init window.Olm, ie. the one just loaded by the script tag,
|
||||
// not 'Olm' which is still the failed wasm version.
|
||||
return window.Olm.init();
|
||||
})
|
||||
.then(() => {
|
||||
logger.log("Using legacy Olm");
|
||||
})
|
||||
.catch((legacyLoadError) => {
|
||||
logger.log("Both WebAssembly and asm.js Olm failed!", legacyLoadError);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export async function loadLanguage(): Promise<void> {
|
||||
const prefLang = SettingsStore.getValue("language", null, /*excludeDefault=*/true);
|
||||
const prefLang = SettingsStore.getValue("language", null, /*excludeDefault=*/ true);
|
||||
let langs = [];
|
||||
|
||||
if (!prefLang) {
|
||||
|
@ -140,25 +145,35 @@ export async function loadApp(fragParams: {}): Promise<void> {
|
|||
const module = await import(
|
||||
/* webpackChunkName: "element-web-app" */
|
||||
/* webpackPreload: true */
|
||||
"./app");
|
||||
window.matrixChat = ReactDOM.render(await module.loadApp(fragParams),
|
||||
document.getElementById('matrixchat'));
|
||||
"./app"
|
||||
);
|
||||
window.matrixChat = ReactDOM.render(await module.loadApp(fragParams), document.getElementById("matrixchat"));
|
||||
}
|
||||
|
||||
export async function showError(title: string, messages?: string[]): Promise<void> {
|
||||
const ErrorView = (await import(
|
||||
/* webpackChunkName: "error-view" */
|
||||
"../async-components/structures/ErrorView")).default;
|
||||
window.matrixChat = ReactDOM.render(<ErrorView title={title} messages={messages} />,
|
||||
document.getElementById('matrixchat'));
|
||||
const ErrorView = (
|
||||
await import(
|
||||
/* webpackChunkName: "error-view" */
|
||||
"../async-components/structures/ErrorView"
|
||||
)
|
||||
).default;
|
||||
window.matrixChat = ReactDOM.render(
|
||||
<ErrorView title={title} messages={messages} />,
|
||||
document.getElementById("matrixchat"),
|
||||
);
|
||||
}
|
||||
|
||||
export async function showIncompatibleBrowser(onAccept): Promise<void> {
|
||||
const CompatibilityView = (await import(
|
||||
/* webpackChunkName: "compatibility-view" */
|
||||
"../async-components/structures/CompatibilityView")).default;
|
||||
window.matrixChat = ReactDOM.render(<CompatibilityView onAccept={onAccept} />,
|
||||
document.getElementById('matrixchat'));
|
||||
const CompatibilityView = (
|
||||
await import(
|
||||
/* webpackChunkName: "compatibility-view" */
|
||||
"../async-components/structures/CompatibilityView"
|
||||
)
|
||||
).default;
|
||||
window.matrixChat = ReactDOM.render(
|
||||
<CompatibilityView onAccept={onAccept} />,
|
||||
document.getElementById("matrixchat"),
|
||||
);
|
||||
}
|
||||
|
||||
export async function loadModules(): Promise<void> {
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Jitsi Widget</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="jitsiContainer"><!-- the js will put the conference here --></div>
|
||||
<div id="joinButtonContainer">
|
||||
<div class="joinConferenceFloating">
|
||||
<div class="joinConferencePrompt">
|
||||
<span class="icon"><!-- managed by CSS --></span>
|
||||
<!-- TODO: i18n -->
|
||||
<h2>Jitsi Video Conference</h2>
|
||||
<div id="widgetActionContainer">
|
||||
<button type="button" id="joinButton">Join Conference</button>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Jitsi Widget</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="jitsiContainer"><!-- the js will put the conference here --></div>
|
||||
<div id="joinButtonContainer">
|
||||
<div class="joinConferenceFloating">
|
||||
<div class="joinConferencePrompt">
|
||||
<span class="icon"><!-- managed by CSS --></span>
|
||||
<!-- TODO: i18n -->
|
||||
<h2>Jitsi Video Conference</h2>
|
||||
<div id="widgetActionContainer">
|
||||
<button type="button" id="joinButton">Join Conference</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- This script is not webpacked, and the script is downloaded at build time -->
|
||||
<script src="./jitsi_external_api.min.js"></script>
|
||||
</body>
|
||||
<!-- This script is not webpacked, and the script is downloaded at build time -->
|
||||
<script src="./jitsi_external_api.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -17,10 +17,10 @@ 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-family: "Nunito";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
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");
|
||||
}
|
||||
|
||||
$dark-fg: #edf3ff;
|
||||
|
@ -38,7 +38,8 @@ body.theme-light {
|
|||
color: $light-fg;
|
||||
}
|
||||
|
||||
body, html {
|
||||
body,
|
||||
html {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
@ -92,7 +93,7 @@ body, html {
|
|||
margin-top: -$icon-size; /* to visually center the form */
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
content: "";
|
||||
background-size: contain;
|
||||
background-color: $dark-fg;
|
||||
mask-repeat: no-repeat;
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { KJUR } from 'jsrsasign';
|
||||
import { KJUR } from "jsrsasign";
|
||||
import {
|
||||
IOpenIDCredentials,
|
||||
IWidgetApiRequest,
|
||||
|
@ -34,7 +34,7 @@ import { getVectorConfig } from "../getconfig";
|
|||
// We have to trick webpack into loading our CSS for us.
|
||||
require("./index.pcss");
|
||||
|
||||
const JITSI_OPENIDTOKEN_JWT_AUTH = 'openidtoken-jwt';
|
||||
const JITSI_OPENIDTOKEN_JWT_AUTH = "openidtoken-jwt";
|
||||
|
||||
// Dev note: we use raw JS without many dependencies to reduce bundle size.
|
||||
// We do not need all of React to render a Jitsi conference.
|
||||
|
@ -82,9 +82,9 @@ const setupCompleted = (async (): Promise<string | void> => {
|
|||
// If we have these params, expect a widget API to be available (ie. to be in an iframe
|
||||
// inside a matrix client). Otherwise, assume we're on our own, eg. have been popped
|
||||
// out into a browser.
|
||||
const parentUrl = qsParam('parentUrl', true);
|
||||
const widgetId = qsParam('widgetId', true);
|
||||
const theme = qsParam('theme', true);
|
||||
const parentUrl = qsParam("parentUrl", true);
|
||||
const widgetId = qsParam("widgetId", true);
|
||||
const theme = qsParam("theme", true);
|
||||
|
||||
if (theme) {
|
||||
document.body.classList.add(`theme-${theme.replace(" ", "_")}`);
|
||||
|
@ -93,10 +93,10 @@ const setupCompleted = (async (): Promise<string | void> => {
|
|||
// Set this up as early as possible because Element will be hitting it almost immediately.
|
||||
let widgetApiReady: Promise<void>;
|
||||
if (parentUrl && widgetId) {
|
||||
const parentOrigin = new URL(qsParam('parentUrl')).origin;
|
||||
const parentOrigin = new URL(qsParam("parentUrl")).origin;
|
||||
widgetApi = new WidgetApi(qsParam("widgetId"), parentOrigin);
|
||||
|
||||
widgetApiReady = new Promise<void>(resolve => widgetApi.once("ready", resolve));
|
||||
widgetApiReady = new Promise<void>((resolve) => widgetApi.once("ready", resolve));
|
||||
widgetApi.requestCapabilities(VideoConferenceCapabilities);
|
||||
widgetApi.start();
|
||||
|
||||
|
@ -134,39 +134,39 @@ const setupCompleted = (async (): Promise<string | void> => {
|
|||
meetApi = null;
|
||||
closeConference();
|
||||
} else {
|
||||
meetApi?.executeCommand('hangup');
|
||||
meetApi?.executeCommand("hangup");
|
||||
}
|
||||
});
|
||||
handleAction(ElementWidgetActions.MuteAudio, async () => {
|
||||
if (meetApi && !await meetApi.isAudioMuted()) {
|
||||
meetApi.executeCommand('toggleAudio');
|
||||
if (meetApi && !(await meetApi.isAudioMuted())) {
|
||||
meetApi.executeCommand("toggleAudio");
|
||||
}
|
||||
});
|
||||
handleAction(ElementWidgetActions.UnmuteAudio, async () => {
|
||||
if (meetApi && await meetApi.isAudioMuted()) {
|
||||
meetApi.executeCommand('toggleAudio');
|
||||
if (meetApi && (await meetApi.isAudioMuted())) {
|
||||
meetApi.executeCommand("toggleAudio");
|
||||
}
|
||||
});
|
||||
handleAction(ElementWidgetActions.MuteVideo, async () => {
|
||||
if (meetApi && !await meetApi.isVideoMuted()) {
|
||||
meetApi.executeCommand('toggleVideo');
|
||||
if (meetApi && !(await meetApi.isVideoMuted())) {
|
||||
meetApi.executeCommand("toggleVideo");
|
||||
}
|
||||
});
|
||||
handleAction(ElementWidgetActions.UnmuteVideo, async () => {
|
||||
if (meetApi && await meetApi.isVideoMuted()) {
|
||||
meetApi.executeCommand('toggleVideo');
|
||||
if (meetApi && (await meetApi.isVideoMuted())) {
|
||||
meetApi.executeCommand("toggleVideo");
|
||||
}
|
||||
});
|
||||
handleAction(ElementWidgetActions.TileLayout, async () => {
|
||||
meetApi?.executeCommand('setTileView', true);
|
||||
meetApi?.executeCommand("setTileView", true);
|
||||
});
|
||||
handleAction(ElementWidgetActions.SpotlightLayout, async () => {
|
||||
meetApi?.executeCommand('setTileView', false);
|
||||
meetApi?.executeCommand("setTileView", false);
|
||||
});
|
||||
handleAction(ElementWidgetActions.StartLiveStream, async ({ rtmpStreamKey }) => {
|
||||
if (!meetApi) throw new Error("Conference not joined");
|
||||
meetApi.executeCommand('startRecording', {
|
||||
mode: 'stream',
|
||||
meetApi.executeCommand("startRecording", {
|
||||
mode: "stream",
|
||||
// this looks like it should be rtmpStreamKey but we may be on too old
|
||||
// a version of jitsi meet
|
||||
//rtmpStreamKey,
|
||||
|
@ -178,23 +178,23 @@ const setupCompleted = (async (): Promise<string | void> => {
|
|||
}
|
||||
|
||||
// Populate the Jitsi params now
|
||||
jitsiDomain = qsParam('conferenceDomain');
|
||||
conferenceId = qsParam('conferenceId');
|
||||
displayName = qsParam('displayName', true);
|
||||
avatarUrl = qsParam('avatarUrl', true); // http not mxc
|
||||
userId = qsParam('userId');
|
||||
jitsiAuth = qsParam('auth', true);
|
||||
roomId = qsParam('roomId', true);
|
||||
roomName = qsParam('roomName', true);
|
||||
startAudioOnly = qsParam('isAudioOnly', true) === "true";
|
||||
isVideoChannel = qsParam('isVideoChannel', true) === "true";
|
||||
supportsScreensharing = qsParam('supportsScreensharing', true) === "true";
|
||||
jitsiDomain = qsParam("conferenceDomain");
|
||||
conferenceId = qsParam("conferenceId");
|
||||
displayName = qsParam("displayName", true);
|
||||
avatarUrl = qsParam("avatarUrl", true); // http not mxc
|
||||
userId = qsParam("userId");
|
||||
jitsiAuth = qsParam("auth", true);
|
||||
roomId = qsParam("roomId", true);
|
||||
roomName = qsParam("roomName", true);
|
||||
startAudioOnly = qsParam("isAudioOnly", true) === "true";
|
||||
isVideoChannel = qsParam("isVideoChannel", true) === "true";
|
||||
supportsScreensharing = qsParam("supportsScreensharing", true) === "true";
|
||||
|
||||
// We've reached the point where we have to wait for the config, so do that then parse it.
|
||||
const instanceConfig = new SnakedObject<IConfigOptions>((await configPromise) ?? <IConfigOptions>{});
|
||||
const jitsiConfig = instanceConfig.get("jitsi_widget") ?? {};
|
||||
skipOurWelcomeScreen = (new SnakedObject<IConfigOptions["jitsi_widget"]>(jitsiConfig))
|
||||
.get("skip_built_in_welcome_screen") ?? false;
|
||||
skipOurWelcomeScreen =
|
||||
new SnakedObject<IConfigOptions["jitsi_widget"]>(jitsiConfig).get("skip_built_in_welcome_screen") ?? false;
|
||||
|
||||
// Either reveal the prejoin screen, or skip straight to Jitsi depending on the config.
|
||||
// We don't set up the call yet though as this might lead to failure without the widget API.
|
||||
|
@ -238,10 +238,10 @@ function switchVisibleContainers(): void {
|
|||
}
|
||||
|
||||
function toggleConferenceVisibility(inConference: boolean): void {
|
||||
document.getElementById("jitsiContainer").style.visibility = inConference ? 'unset' : 'hidden';
|
||||
document.getElementById("jitsiContainer").style.visibility = inConference ? "unset" : "hidden";
|
||||
// Video rooms have a separate UI for joining, so they should never show our join button
|
||||
document.getElementById("joinButtonContainer").style.visibility =
|
||||
(inConference || isVideoChannel) ? 'hidden' : 'unset';
|
||||
inConference || isVideoChannel ? "hidden" : "unset";
|
||||
}
|
||||
|
||||
function skipToJitsiSplashScreen(): void {
|
||||
|
@ -256,7 +256,7 @@ function skipToJitsiSplashScreen(): void {
|
|||
*/
|
||||
function createJWTToken(): string {
|
||||
// Header
|
||||
const header = { alg: 'HS256', typ: 'JWT' };
|
||||
const header = { alg: "HS256", typ: "JWT" };
|
||||
// Payload
|
||||
const payload = {
|
||||
// As per Jitsi token auth, `iss` needs to be set to something agreed between
|
||||
|
@ -281,12 +281,7 @@ function createJWTToken(): string {
|
|||
// Sign JWT
|
||||
// The secret string here is irrelevant, we're only using the JWT
|
||||
// to transport data to Prosody in the Jitsi stack.
|
||||
return KJUR.jws.JWS.sign(
|
||||
'HS256',
|
||||
JSON.stringify(header),
|
||||
JSON.stringify(payload),
|
||||
'notused',
|
||||
);
|
||||
return KJUR.jws.JWS.sign("HS256", JSON.stringify(header), JSON.stringify(payload), "notused");
|
||||
}
|
||||
|
||||
async function notifyHangup(errorMessage?: string): Promise<void> {
|
||||
|
@ -318,9 +313,10 @@ function closeConference(): void {
|
|||
function joinConference(audioInput?: string | null, videoInput?: string | null): void {
|
||||
let jwt;
|
||||
if (jitsiAuth === JITSI_OPENIDTOKEN_JWT_AUTH) {
|
||||
if (!openIdToken?.access_token) { // eslint-disable-line camelcase
|
||||
if (!openIdToken?.access_token) {
|
||||
// eslint-disable-line camelcase
|
||||
// We've failing to get a token, don't try to init conference
|
||||
logger.warn('Expected to have an OpenID credential, cannot initialize widget.');
|
||||
logger.warn("Expected to have an OpenID credential, cannot initialize widget.");
|
||||
document.getElementById("widgetActionContainer").innerText = "Failed to load Jitsi widget";
|
||||
return;
|
||||
}
|
||||
|
@ -331,8 +327,8 @@ function joinConference(audioInput?: string | null, videoInput?: string | null):
|
|||
|
||||
logger.warn(
|
||||
"[Jitsi Widget] The next few errors about failing to parse URL parameters are fine if " +
|
||||
"they mention 'external_api' or 'jitsi' in the stack. They're just Jitsi Meet trying to parse " +
|
||||
"our fragment values and not recognizing the options.",
|
||||
"they mention 'external_api' or 'jitsi' in the stack. They're just Jitsi Meet trying to parse " +
|
||||
"our fragment values and not recognizing the options.",
|
||||
);
|
||||
|
||||
const options = {
|
||||
|
@ -400,7 +396,7 @@ function joinConference(audioInput?: string | null, videoInput?: string | null):
|
|||
meetApi.on("audioMuteStatusChanged", onAudioMuteStatusChanged);
|
||||
meetApi.on("videoMuteStatusChanged", onVideoMuteStatusChanged);
|
||||
|
||||
["videoConferenceJoined", "participantJoined", "participantLeft"].forEach(event => {
|
||||
["videoConferenceJoined", "participantJoined", "participantLeft"].forEach((event) => {
|
||||
meetApi.on(event, updateParticipants);
|
||||
});
|
||||
|
||||
|
@ -472,5 +468,4 @@ const updateParticipants = (): void => {
|
|||
});
|
||||
};
|
||||
|
||||
const onLog = ({ logLevel, args }): void =>
|
||||
(parent as unknown as typeof global).mx_rage_logger?.log(logLevel, ...args);
|
||||
const onLog = ({ logLevel, args }): void => (parent as unknown as typeof global).mx_rage_logger?.log(logLevel, ...args);
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
* */
|
||||
|
||||
if (window.localStorage) {
|
||||
Object.keys(window.localStorage).forEach(key => {
|
||||
if (key.indexOf('loglevel:') === 0) {
|
||||
Object.keys(window.localStorage).forEach((key) => {
|
||||
if (key.indexOf("loglevel:") === 0) {
|
||||
window.localStorage.removeItem(key);
|
||||
}
|
||||
});
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,17 +1,17 @@
|
|||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
import { getVectorConfig } from '../getconfig';
|
||||
import { getVectorConfig } from "../getconfig";
|
||||
|
||||
function onBackToElementClick(): void {
|
||||
// Cookie should expire in 4 hours
|
||||
document.cookie = 'element_mobile_redirect_to_guide=false;path=/;max-age=14400';
|
||||
window.location.href = '../';
|
||||
document.cookie = "element_mobile_redirect_to_guide=false;path=/;max-age=14400";
|
||||
window.location.href = "../";
|
||||
}
|
||||
|
||||
// NEVER pass user-controlled content to this function! Hardcoded strings only please.
|
||||
function renderConfigError(message: string): void {
|
||||
const contactMsg = "If this is unexpected, please contact your system administrator " +
|
||||
"or technical support representative.";
|
||||
const contactMsg =
|
||||
"If this is unexpected, please contact your system administrator " + "or technical support representative.";
|
||||
message = `<h2>Error loading Element</h2><p>${message}</p><p>${contactMsg}</p>`;
|
||||
|
||||
const toHide = document.getElementsByClassName("mx_HomePage_container");
|
||||
|
@ -22,46 +22,46 @@ function renderConfigError(message: string): void {
|
|||
for (const e of toHide) {
|
||||
// We have to clear the content because .style.display='none'; doesn't work
|
||||
// due to an !important in the CSS.
|
||||
e.innerHTML = '';
|
||||
e.innerHTML = "";
|
||||
}
|
||||
for (const e of errorContainers) {
|
||||
e.style.display = 'block';
|
||||
e.style.display = "block";
|
||||
e.innerHTML = message;
|
||||
}
|
||||
}
|
||||
|
||||
async function initPage(): Promise<void> {
|
||||
document.getElementById('back_to_element_button').onclick = onBackToElementClick;
|
||||
document.getElementById("back_to_element_button").onclick = onBackToElementClick;
|
||||
|
||||
const config = await getVectorConfig('..');
|
||||
const config = await getVectorConfig("..");
|
||||
|
||||
// We manually parse the config similar to how validateServerConfig works because
|
||||
// calling that function pulls in roughly 4mb of JS we don't use.
|
||||
|
||||
const wkConfig = config['default_server_config']; // overwritten later under some conditions
|
||||
const serverName = config['default_server_name'];
|
||||
const defaultHsUrl = config['default_hs_url'];
|
||||
const defaultIsUrl = config['default_is_url'];
|
||||
const wkConfig = config["default_server_config"]; // overwritten later under some conditions
|
||||
const serverName = config["default_server_name"];
|
||||
const defaultHsUrl = config["default_hs_url"];
|
||||
const defaultIsUrl = config["default_is_url"];
|
||||
|
||||
const incompatibleOptions = [wkConfig, serverName, defaultHsUrl].filter(i => !!i);
|
||||
const incompatibleOptions = [wkConfig, serverName, defaultHsUrl].filter((i) => !!i);
|
||||
if (incompatibleOptions.length > 1) {
|
||||
return renderConfigError(
|
||||
"Invalid configuration: can only specify one of default_server_config, default_server_name, " +
|
||||
"or default_hs_url.",
|
||||
"or default_hs_url.",
|
||||
);
|
||||
}
|
||||
if (incompatibleOptions.length < 1) {
|
||||
return renderConfigError("Invalid configuration: no default server specified.");
|
||||
}
|
||||
|
||||
let hsUrl = '';
|
||||
let isUrl = '';
|
||||
let hsUrl = "";
|
||||
let isUrl = "";
|
||||
|
||||
if (wkConfig && wkConfig['m.homeserver']) {
|
||||
hsUrl = wkConfig['m.homeserver']['base_url'];
|
||||
if (wkConfig && wkConfig["m.homeserver"]) {
|
||||
hsUrl = wkConfig["m.homeserver"]["base_url"];
|
||||
|
||||
if (wkConfig['m.identity_server']) {
|
||||
isUrl = wkConfig['m.identity_server']['base_url'];
|
||||
if (wkConfig["m.identity_server"]) {
|
||||
isUrl = wkConfig["m.identity_server"]["base_url"];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,11 +70,11 @@ async function initPage(): Promise<void> {
|
|||
try {
|
||||
const result = await fetch(`https://${serverName}/.well-known/matrix/client`);
|
||||
const wkConfig = await result.json();
|
||||
if (wkConfig && wkConfig['m.homeserver']) {
|
||||
hsUrl = wkConfig['m.homeserver']['base_url'];
|
||||
if (wkConfig && wkConfig["m.homeserver"]) {
|
||||
hsUrl = wkConfig["m.homeserver"]["base_url"];
|
||||
|
||||
if (wkConfig['m.identity_server']) {
|
||||
isUrl = wkConfig['m.identity_server']['base_url'];
|
||||
if (wkConfig["m.identity_server"]) {
|
||||
isUrl = wkConfig["m.identity_server"]["base_url"];
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
|
@ -92,21 +92,20 @@ async function initPage(): Promise<void> {
|
|||
return renderConfigError("Unable to locate homeserver");
|
||||
}
|
||||
|
||||
if (hsUrl && !hsUrl.endsWith('/')) hsUrl += '/';
|
||||
if (isUrl && !isUrl.endsWith('/')) isUrl += '/';
|
||||
if (hsUrl && !hsUrl.endsWith("/")) hsUrl += "/";
|
||||
if (isUrl && !isUrl.endsWith("/")) isUrl += "/";
|
||||
|
||||
if (hsUrl !== 'https://matrix.org/') {
|
||||
(document.getElementById('configure_element_button') as HTMLAnchorElement).href =
|
||||
"https://mobile.element.io?hs_url=" + encodeURIComponent(hsUrl) +
|
||||
"&is_url=" + encodeURIComponent(isUrl);
|
||||
document.getElementById('step1_heading').innerHTML= '1: Install the app';
|
||||
document.getElementById('step2_container').style.display = 'block';
|
||||
document.getElementById('hs_url').innerText = hsUrl;
|
||||
if (hsUrl !== "https://matrix.org/") {
|
||||
(document.getElementById("configure_element_button") as HTMLAnchorElement).href =
|
||||
"https://mobile.element.io?hs_url=" + encodeURIComponent(hsUrl) + "&is_url=" + encodeURIComponent(isUrl);
|
||||
document.getElementById("step1_heading").innerHTML = "1: Install the app";
|
||||
document.getElementById("step2_container").style.display = "block";
|
||||
document.getElementById("hs_url").innerText = hsUrl;
|
||||
|
||||
if (isUrl) {
|
||||
document.getElementById('custom_is').style.display = 'block';
|
||||
document.getElementById('is_url').style.display = 'block';
|
||||
document.getElementById('is_url').innerText = isUrl;
|
||||
document.getElementById("custom_is").style.display = "block";
|
||||
document.getElementById("is_url").style.display = "block";
|
||||
document.getElementById("is_url").innerText = isUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -19,12 +19,12 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import { UpdateCheckStatus, UpdateStatus } from "matrix-react-sdk/src/BasePlatform";
|
||||
import BaseEventIndexManager from 'matrix-react-sdk/src/indexing/BaseEventIndexManager';
|
||||
import dis from 'matrix-react-sdk/src/dispatcher/dispatcher';
|
||||
import { _t } from 'matrix-react-sdk/src/languageHandler';
|
||||
import SdkConfig from 'matrix-react-sdk/src/SdkConfig';
|
||||
import BaseEventIndexManager from "matrix-react-sdk/src/indexing/BaseEventIndexManager";
|
||||
import dis from "matrix-react-sdk/src/dispatcher/dispatcher";
|
||||
import { _t } from "matrix-react-sdk/src/languageHandler";
|
||||
import SdkConfig from "matrix-react-sdk/src/SdkConfig";
|
||||
import { IConfigOptions } from "matrix-react-sdk/src/IConfigOptions";
|
||||
import * as rageshake from 'matrix-react-sdk/src/rageshake/rageshake';
|
||||
import * as rageshake from "matrix-react-sdk/src/rageshake/rageshake";
|
||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||
import { Room } from "matrix-js-sdk/src/models/room";
|
||||
import Modal from "matrix-react-sdk/src/Modal";
|
||||
|
@ -41,35 +41,35 @@ import GenericExpiringToast from "matrix-react-sdk/src/components/views/toasts/G
|
|||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||
|
||||
import VectorBasePlatform from './VectorBasePlatform';
|
||||
import VectorBasePlatform from "./VectorBasePlatform";
|
||||
import { SeshatIndexManager } from "./SeshatIndexManager";
|
||||
import { IPCManager } from "./IPCManager";
|
||||
|
||||
const isMac = navigator.platform.toUpperCase().includes('MAC');
|
||||
const isMac = navigator.platform.toUpperCase().includes("MAC");
|
||||
|
||||
function platformFriendlyName(): string {
|
||||
// used to use window.process but the same info is available here
|
||||
if (navigator.userAgent.includes('Macintosh')) {
|
||||
return 'macOS';
|
||||
} else if (navigator.userAgent.includes('FreeBSD')) {
|
||||
return 'FreeBSD';
|
||||
} else if (navigator.userAgent.includes('OpenBSD')) {
|
||||
return 'OpenBSD';
|
||||
} else if (navigator.userAgent.includes('SunOS')) {
|
||||
return 'SunOS';
|
||||
} else if (navigator.userAgent.includes('Windows')) {
|
||||
return 'Windows';
|
||||
} else if (navigator.userAgent.includes('Linux')) {
|
||||
return 'Linux';
|
||||
if (navigator.userAgent.includes("Macintosh")) {
|
||||
return "macOS";
|
||||
} else if (navigator.userAgent.includes("FreeBSD")) {
|
||||
return "FreeBSD";
|
||||
} else if (navigator.userAgent.includes("OpenBSD")) {
|
||||
return "OpenBSD";
|
||||
} else if (navigator.userAgent.includes("SunOS")) {
|
||||
return "SunOS";
|
||||
} else if (navigator.userAgent.includes("Windows")) {
|
||||
return "Windows";
|
||||
} else if (navigator.userAgent.includes("Linux")) {
|
||||
return "Linux";
|
||||
} else {
|
||||
return 'Unknown';
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
function onAction(payload: ActionPayload): void {
|
||||
// Whitelist payload actions, no point sending most across
|
||||
if (['call_state'].includes(payload.action)) {
|
||||
window.electron.send('app_onAction', payload);
|
||||
if (["call_state"].includes(payload.action)) {
|
||||
window.electron.send("app_onAction", payload);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,7 +102,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
|||
false if there is not
|
||||
or the error if one is encountered
|
||||
*/
|
||||
window.electron.on('check_updates', (event, status) => {
|
||||
window.electron.on("check_updates", (event, status) => {
|
||||
dis.dispatch<CheckUpdatesPayload>({
|
||||
action: Action.CheckUpdates,
|
||||
...getUpdateCheckStatus(status),
|
||||
|
@ -110,27 +110,27 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
|||
});
|
||||
|
||||
// try to flush the rageshake logs to indexeddb before quit.
|
||||
window.electron.on('before-quit', function() {
|
||||
logger.log('element-desktop closing');
|
||||
window.electron.on("before-quit", function () {
|
||||
logger.log("element-desktop closing");
|
||||
rageshake.flush();
|
||||
});
|
||||
|
||||
window.electron.on('update-downloaded', this.onUpdateDownloaded);
|
||||
window.electron.on("update-downloaded", this.onUpdateDownloaded);
|
||||
|
||||
window.electron.on('preferences', () => {
|
||||
window.electron.on("preferences", () => {
|
||||
dis.fire(Action.ViewUserSettings);
|
||||
});
|
||||
|
||||
window.electron.on('userDownloadCompleted', (ev, { id, name }) => {
|
||||
window.electron.on("userDownloadCompleted", (ev, { id, name }) => {
|
||||
const key = `DOWNLOAD_TOAST_${id}`;
|
||||
|
||||
const onAccept = (): void => {
|
||||
window.electron.send('userDownloadAction', { id, open: true });
|
||||
window.electron.send("userDownloadAction", { id, open: true });
|
||||
ToastStore.sharedInstance().dismissToast(key);
|
||||
};
|
||||
|
||||
const onDismiss = (): void => {
|
||||
window.electron.send('userDownloadAction', { id });
|
||||
window.electron.send("userDownloadAction", { id });
|
||||
};
|
||||
|
||||
ToastStore.sharedInstance().addOrReplaceToast({
|
||||
|
@ -153,7 +153,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
|||
}
|
||||
|
||||
public async getConfig(): Promise<IConfigOptions> {
|
||||
return this.ipc.call('getConfig');
|
||||
return this.ipc.call("getConfig");
|
||||
}
|
||||
|
||||
private onUpdateDownloaded = async (ev, { releaseNotes, releaseName }): Promise<void> => {
|
||||
|
@ -167,7 +167,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
|||
};
|
||||
|
||||
public getHumanReadableName(): string {
|
||||
return 'Electron Platform'; // no translation required: only used for analytics
|
||||
return "Electron Platform"; // no translation required: only used for analytics
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -186,7 +186,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
|||
if (this.notificationCount === count) return;
|
||||
super.setNotificationCount(count);
|
||||
|
||||
window.electron.send('setBadgeCount', count);
|
||||
window.electron.send("setBadgeCount", count);
|
||||
}
|
||||
|
||||
public supportsNotifications(): boolean {
|
||||
|
@ -210,29 +210,23 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
|||
// maybe we should pass basic styling (italics, bold, underline) through from MD
|
||||
// we only have to strip out < and > as the spec doesn't include anything about things like &
|
||||
// so we shouldn't assume that all implementations will treat those properly. Very basic tag parsing is done.
|
||||
if (navigator.userAgent.includes('Linux')) {
|
||||
msg = msg.replace(/</g, '<').replace(/>/g, '>');
|
||||
if (navigator.userAgent.includes("Linux")) {
|
||||
msg = msg.replace(/</g, "<").replace(/>/g, ">");
|
||||
}
|
||||
|
||||
const notification = super.displayNotification(
|
||||
title,
|
||||
msg,
|
||||
avatarUrl,
|
||||
room,
|
||||
ev,
|
||||
);
|
||||
const notification = super.displayNotification(title, msg, avatarUrl, room, ev);
|
||||
|
||||
const handler = notification.onclick as Function;
|
||||
notification.onclick = (): void => {
|
||||
handler?.();
|
||||
this.ipc.call('focusWindow');
|
||||
this.ipc.call("focusWindow");
|
||||
};
|
||||
|
||||
return notification;
|
||||
}
|
||||
|
||||
public loudNotification(ev: MatrixEvent, room: Room): void {
|
||||
window.electron.send('loudNotification');
|
||||
window.electron.send("loudNotification");
|
||||
}
|
||||
|
||||
public needsUrlTooltips(): boolean {
|
||||
|
@ -240,7 +234,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
|||
}
|
||||
|
||||
public async getAppVersion(): Promise<string> {
|
||||
return this.ipc.call('getAppVersion');
|
||||
return this.ipc.call("getAppVersion");
|
||||
}
|
||||
|
||||
public supportsSetting(settingName?: string): boolean {
|
||||
|
@ -262,32 +256,32 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
|||
}
|
||||
|
||||
public async canSelfUpdate(): Promise<boolean> {
|
||||
const feedUrl = await this.ipc.call('getUpdateFeedUrl');
|
||||
const feedUrl = await this.ipc.call("getUpdateFeedUrl");
|
||||
return Boolean(feedUrl);
|
||||
}
|
||||
|
||||
public startUpdateCheck(): void {
|
||||
super.startUpdateCheck();
|
||||
window.electron.send('check_updates');
|
||||
window.electron.send("check_updates");
|
||||
}
|
||||
|
||||
public installUpdate(): void {
|
||||
// IPC to the main process to install the update, since quitAndInstall
|
||||
// doesn't fire the before-quit event so the main process needs to know
|
||||
// it should exit.
|
||||
window.electron.send('install_update');
|
||||
window.electron.send("install_update");
|
||||
}
|
||||
|
||||
public getDefaultDeviceDisplayName(): string {
|
||||
const brand = SdkConfig.get().brand;
|
||||
return _t('%(brand)s Desktop: %(platformName)s', {
|
||||
return _t("%(brand)s Desktop: %(platformName)s", {
|
||||
brand,
|
||||
platformName: platformFriendlyName(),
|
||||
});
|
||||
}
|
||||
|
||||
public requestNotificationPermission(): Promise<string> {
|
||||
return Promise.resolve('granted');
|
||||
return Promise.resolve("granted");
|
||||
}
|
||||
|
||||
public reload(): void {
|
||||
|
@ -299,33 +293,33 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
|||
}
|
||||
|
||||
public async setLanguage(preferredLangs: string[]): Promise<any> {
|
||||
return this.ipc.call('setLanguage', preferredLangs);
|
||||
return this.ipc.call("setLanguage", preferredLangs);
|
||||
}
|
||||
|
||||
public setSpellCheckEnabled(enabled: boolean): void {
|
||||
this.ipc.call('setSpellCheckEnabled', enabled).catch(error => {
|
||||
this.ipc.call("setSpellCheckEnabled", enabled).catch((error) => {
|
||||
logger.log("Failed to send setSpellCheckEnabled IPC to Electron");
|
||||
logger.error(error);
|
||||
});
|
||||
}
|
||||
|
||||
public async getSpellCheckEnabled(): Promise<boolean> {
|
||||
return this.ipc.call('getSpellCheckEnabled');
|
||||
return this.ipc.call("getSpellCheckEnabled");
|
||||
}
|
||||
|
||||
public setSpellCheckLanguages(preferredLangs: string[]): void {
|
||||
this.ipc.call('setSpellCheckLanguages', preferredLangs).catch(error => {
|
||||
this.ipc.call("setSpellCheckLanguages", preferredLangs).catch((error) => {
|
||||
logger.log("Failed to send setSpellCheckLanguages IPC to Electron");
|
||||
logger.error(error);
|
||||
});
|
||||
}
|
||||
|
||||
public async getSpellCheckLanguages(): Promise<string[]> {
|
||||
return this.ipc.call('getSpellCheckLanguages');
|
||||
return this.ipc.call("getSpellCheckLanguages");
|
||||
}
|
||||
|
||||
public async getDesktopCapturerSources(options: GetSourcesOptions): Promise<Array<DesktopCapturerSource>> {
|
||||
return this.ipc.call('getDesktopCapturerSources', options);
|
||||
return this.ipc.call("getDesktopCapturerSources", options);
|
||||
}
|
||||
|
||||
public supportsDesktopCapturer(): boolean {
|
||||
|
@ -338,7 +332,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
|||
}
|
||||
|
||||
public async getAvailableSpellCheckLanguages(): Promise<string[]> {
|
||||
return this.ipc.call('getAvailableSpellCheckLanguages');
|
||||
return this.ipc.call("getAvailableSpellCheckLanguages");
|
||||
}
|
||||
|
||||
public getSSOCallbackUrl(fragmentAfterLogin: string): URL {
|
||||
|
@ -372,7 +366,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
|||
|
||||
public async getPickleKey(userId: string, deviceId: string): Promise<string | null> {
|
||||
try {
|
||||
return await this.ipc.call('getPickleKey', userId, deviceId);
|
||||
return await this.ipc.call("getPickleKey", userId, deviceId);
|
||||
} catch (e) {
|
||||
// if we can't connect to the password storage, assume there's no
|
||||
// pickle key
|
||||
|
@ -382,7 +376,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
|||
|
||||
public async createPickleKey(userId: string, deviceId: string): Promise<string | null> {
|
||||
try {
|
||||
return await this.ipc.call('createPickleKey', userId, deviceId);
|
||||
return await this.ipc.call("createPickleKey", userId, deviceId);
|
||||
} catch (e) {
|
||||
// if we can't connect to the password storage, assume there's no
|
||||
// pickle key
|
||||
|
@ -392,7 +386,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
|||
|
||||
public async destroyPickleKey(userId: string, deviceId: string): Promise<void> {
|
||||
try {
|
||||
await this.ipc.call('destroyPickleKey', userId, deviceId);
|
||||
await this.ipc.call("destroyPickleKey", userId, deviceId);
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { defer, IDeferred } from 'matrix-js-sdk/src/utils';
|
||||
import { defer, IDeferred } from "matrix-js-sdk/src/utils";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
import { ElectronChannel } from "../../@types/global";
|
||||
|
|
|
@ -24,7 +24,7 @@ export default class PWAPlatform extends WebPlatform {
|
|||
if (this.notificationCount === count) return;
|
||||
this.notificationCount = count;
|
||||
|
||||
navigator.setAppBadge(count).catch(e => {
|
||||
navigator.setAppBadge(count).catch((e) => {
|
||||
logger.error("Failed to update PWA app badge", e);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ import BaseEventIndexManager, {
|
|||
IEventAndProfile,
|
||||
IIndexStats,
|
||||
ISearchArgs,
|
||||
} from 'matrix-react-sdk/src/indexing/BaseEventIndexManager';
|
||||
} from "matrix-react-sdk/src/indexing/BaseEventIndexManager";
|
||||
import { IMatrixProfile, IEventWithRoomId as IMatrixEvent, IResultRoomEvents } from "matrix-js-sdk/src/@types/search";
|
||||
|
||||
import { IPCManager } from "./IPCManager";
|
||||
|
@ -28,35 +28,35 @@ export class SeshatIndexManager extends BaseEventIndexManager {
|
|||
private readonly ipc = new IPCManager("seshat", "seshatReply");
|
||||
|
||||
public async supportsEventIndexing(): Promise<boolean> {
|
||||
return this.ipc.call('supportsEventIndexing');
|
||||
return this.ipc.call("supportsEventIndexing");
|
||||
}
|
||||
|
||||
public async initEventIndex(userId: string, deviceId: string): Promise<void> {
|
||||
return this.ipc.call('initEventIndex', userId, deviceId);
|
||||
return this.ipc.call("initEventIndex", userId, deviceId);
|
||||
}
|
||||
|
||||
public async addEventToIndex(ev: IMatrixEvent, profile: IMatrixProfile): Promise<void> {
|
||||
return this.ipc.call('addEventToIndex', ev, profile);
|
||||
return this.ipc.call("addEventToIndex", ev, profile);
|
||||
}
|
||||
|
||||
public async deleteEvent(eventId: string): Promise<boolean> {
|
||||
return this.ipc.call('deleteEvent', eventId);
|
||||
return this.ipc.call("deleteEvent", eventId);
|
||||
}
|
||||
|
||||
public async isEventIndexEmpty(): Promise<boolean> {
|
||||
return this.ipc.call('isEventIndexEmpty');
|
||||
return this.ipc.call("isEventIndexEmpty");
|
||||
}
|
||||
|
||||
public async isRoomIndexed(roomId: string): Promise<boolean> {
|
||||
return this.ipc.call('isRoomIndexed', roomId);
|
||||
return this.ipc.call("isRoomIndexed", roomId);
|
||||
}
|
||||
|
||||
public async commitLiveEvents(): Promise<void> {
|
||||
return this.ipc.call('commitLiveEvents');
|
||||
return this.ipc.call("commitLiveEvents");
|
||||
}
|
||||
|
||||
public async searchEventIndex(searchConfig: ISearchArgs): Promise<IResultRoomEvents> {
|
||||
return this.ipc.call('searchEventIndex', searchConfig);
|
||||
return this.ipc.call("searchEventIndex", searchConfig);
|
||||
}
|
||||
|
||||
public async addHistoricEvents(
|
||||
|
@ -64,42 +64,42 @@ export class SeshatIndexManager extends BaseEventIndexManager {
|
|||
checkpoint: ICrawlerCheckpoint | null,
|
||||
oldCheckpoint: ICrawlerCheckpoint | null,
|
||||
): Promise<boolean> {
|
||||
return this.ipc.call('addHistoricEvents', events, checkpoint, oldCheckpoint);
|
||||
return this.ipc.call("addHistoricEvents", events, checkpoint, oldCheckpoint);
|
||||
}
|
||||
|
||||
public async addCrawlerCheckpoint(checkpoint: ICrawlerCheckpoint): Promise<void> {
|
||||
return this.ipc.call('addCrawlerCheckpoint', checkpoint);
|
||||
return this.ipc.call("addCrawlerCheckpoint", checkpoint);
|
||||
}
|
||||
|
||||
public async removeCrawlerCheckpoint(checkpoint: ICrawlerCheckpoint): Promise<void> {
|
||||
return this.ipc.call('removeCrawlerCheckpoint', checkpoint);
|
||||
return this.ipc.call("removeCrawlerCheckpoint", checkpoint);
|
||||
}
|
||||
|
||||
public async loadFileEvents(args): Promise<IEventAndProfile[]> {
|
||||
return this.ipc.call('loadFileEvents', args);
|
||||
return this.ipc.call("loadFileEvents", args);
|
||||
}
|
||||
|
||||
public async loadCheckpoints(): Promise<ICrawlerCheckpoint[]> {
|
||||
return this.ipc.call('loadCheckpoints');
|
||||
return this.ipc.call("loadCheckpoints");
|
||||
}
|
||||
|
||||
public async closeEventIndex(): Promise<void> {
|
||||
return this.ipc.call('closeEventIndex');
|
||||
return this.ipc.call("closeEventIndex");
|
||||
}
|
||||
|
||||
public async getStats(): Promise<IIndexStats> {
|
||||
return this.ipc.call('getStats');
|
||||
return this.ipc.call("getStats");
|
||||
}
|
||||
|
||||
public async getUserVersion(): Promise<number> {
|
||||
return this.ipc.call('getUserVersion');
|
||||
return this.ipc.call("getUserVersion");
|
||||
}
|
||||
|
||||
public async setUserVersion(version: number): Promise<void> {
|
||||
return this.ipc.call('setUserVersion', version);
|
||||
return this.ipc.call("setUserVersion", version);
|
||||
}
|
||||
|
||||
public async deleteEventIndex(): Promise<void> {
|
||||
return this.ipc.call('deleteEventIndex');
|
||||
return this.ipc.call("deleteEventIndex");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import BasePlatform from 'matrix-react-sdk/src/BasePlatform';
|
||||
import { _t } from 'matrix-react-sdk/src/languageHandler';
|
||||
import BasePlatform from "matrix-react-sdk/src/BasePlatform";
|
||||
import { _t } from "matrix-react-sdk/src/languageHandler";
|
||||
|
||||
import type { IConfigOptions } from "matrix-react-sdk/src/IConfigOptions";
|
||||
import { getVectorConfig } from "../getconfig";
|
||||
|
@ -35,7 +35,7 @@ export default abstract class VectorBasePlatform extends BasePlatform {
|
|||
}
|
||||
|
||||
public getHumanReadableName(): string {
|
||||
return 'Vector Base Platform'; // no translation required: only used for analytics
|
||||
return "Vector Base Platform"; // no translation required: only used for analytics
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -78,8 +78,7 @@ export default abstract class VectorBasePlatform extends BasePlatform {
|
|||
/**
|
||||
* Begin update polling, if applicable
|
||||
*/
|
||||
public startUpdater(): void {
|
||||
}
|
||||
public startUpdater(): void {}
|
||||
|
||||
/**
|
||||
* Get a sensible default display name for the
|
||||
|
|
|
@ -17,15 +17,15 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import { UpdateCheckStatus, UpdateStatus } from "matrix-react-sdk/src/BasePlatform";
|
||||
import dis from 'matrix-react-sdk/src/dispatcher/dispatcher';
|
||||
import { _t } from 'matrix-react-sdk/src/languageHandler';
|
||||
import dis from "matrix-react-sdk/src/dispatcher/dispatcher";
|
||||
import { _t } from "matrix-react-sdk/src/languageHandler";
|
||||
import { hideToast as hideUpdateToast, showToast as showUpdateToast } from "matrix-react-sdk/src/toasts/UpdateToast";
|
||||
import { Action } from "matrix-react-sdk/src/dispatcher/actions";
|
||||
import { CheckUpdatesPayload } from 'matrix-react-sdk/src/dispatcher/payloads/CheckUpdatesPayload';
|
||||
import UAParser from 'ua-parser-js';
|
||||
import { CheckUpdatesPayload } from "matrix-react-sdk/src/dispatcher/payloads/CheckUpdatesPayload";
|
||||
import UAParser from "ua-parser-js";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
import VectorBasePlatform from './VectorBasePlatform';
|
||||
import VectorBasePlatform from "./VectorBasePlatform";
|
||||
import { parseQs } from "../url_utils";
|
||||
|
||||
const POKE_RATE_MS = 10 * 60 * 1000; // 10 min
|
||||
|
@ -43,13 +43,13 @@ export default class WebPlatform extends VectorBasePlatform {
|
|||
public constructor() {
|
||||
super();
|
||||
// Register service worker if available on this platform
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.register('sw.js');
|
||||
if ("serviceWorker" in navigator) {
|
||||
navigator.serviceWorker.register("sw.js");
|
||||
}
|
||||
}
|
||||
|
||||
public getHumanReadableName(): string {
|
||||
return 'Web Platform'; // no translation required: only used for analytics
|
||||
return "Web Platform"; // no translation required: only used for analytics
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -65,7 +65,7 @@ export default class WebPlatform extends VectorBasePlatform {
|
|||
* to display notifications. Otherwise false.
|
||||
*/
|
||||
public maySendNotifications(): boolean {
|
||||
return window.Notification.permission === 'granted';
|
||||
return window.Notification.permission === "granted";
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -79,7 +79,7 @@ export default class WebPlatform extends VectorBasePlatform {
|
|||
// annoyingly, the latest spec says this returns a
|
||||
// promise, but this is only supported in Chrome 46
|
||||
// and Firefox 47, so adapt the callback API.
|
||||
return new Promise(function(resolve) {
|
||||
return new Promise(function (resolve) {
|
||||
window.Notification.requestPermission((result) => {
|
||||
resolve(result);
|
||||
});
|
||||
|
@ -142,30 +142,33 @@ export default class WebPlatform extends VectorBasePlatform {
|
|||
showUpdate: (currentVersion: string, mostRecentVersion: string) => void,
|
||||
showNoUpdate?: () => void,
|
||||
): Promise<UpdateStatus> => {
|
||||
return this.getMostRecentVersion().then((mostRecentVersion) => {
|
||||
const currentVersion = getNormalizedAppVersion(process.env.VERSION);
|
||||
return this.getMostRecentVersion().then(
|
||||
(mostRecentVersion) => {
|
||||
const currentVersion = getNormalizedAppVersion(process.env.VERSION);
|
||||
|
||||
if (currentVersion !== mostRecentVersion) {
|
||||
if (this.shouldShowUpdate(mostRecentVersion)) {
|
||||
console.log("Update available to " + mostRecentVersion + ", will notify user");
|
||||
showUpdate(currentVersion, mostRecentVersion);
|
||||
if (currentVersion !== mostRecentVersion) {
|
||||
if (this.shouldShowUpdate(mostRecentVersion)) {
|
||||
console.log("Update available to " + mostRecentVersion + ", will notify user");
|
||||
showUpdate(currentVersion, mostRecentVersion);
|
||||
} else {
|
||||
console.log("Update available to " + mostRecentVersion + " but won't be shown");
|
||||
}
|
||||
return { status: UpdateCheckStatus.Ready };
|
||||
} else {
|
||||
console.log("Update available to " + mostRecentVersion + " but won't be shown");
|
||||
console.log("No update available, already on " + mostRecentVersion);
|
||||
showNoUpdate?.();
|
||||
}
|
||||
return { status: UpdateCheckStatus.Ready };
|
||||
} else {
|
||||
console.log("No update available, already on " + mostRecentVersion);
|
||||
showNoUpdate?.();
|
||||
}
|
||||
|
||||
return { status: UpdateCheckStatus.NotAvailable };
|
||||
}, (err) => {
|
||||
logger.error("Failed to poll for update", err);
|
||||
return {
|
||||
status: UpdateCheckStatus.Error,
|
||||
detail: err.message || err.status ? err.status.toString() : 'Unknown Error',
|
||||
};
|
||||
});
|
||||
return { status: UpdateCheckStatus.NotAvailable };
|
||||
},
|
||||
(err) => {
|
||||
logger.error("Failed to poll for update", err);
|
||||
return {
|
||||
status: UpdateCheckStatus.Error,
|
||||
detail: err.message || err.status ? err.status.toString() : "Unknown Error",
|
||||
};
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
public startUpdateCheck(): void {
|
||||
|
@ -197,7 +200,7 @@ export default class WebPlatform extends VectorBasePlatform {
|
|||
let osName = ua.getOS().name || "unknown OS";
|
||||
// Stylise the value from the parser to match Apple's current branding.
|
||||
if (osName === "Mac OS") osName = "macOS";
|
||||
return _t('%(appName)s: %(browserName)s on %(osName)s', {
|
||||
return _t("%(appName)s: %(browserName)s on %(osName)s", {
|
||||
appName,
|
||||
browserName,
|
||||
osName,
|
||||
|
|
|
@ -33,22 +33,26 @@ import { logger } from "matrix-js-sdk/src/logger";
|
|||
|
||||
export function initRageshake(): Promise<void> {
|
||||
// we manually check persistence for rageshakes ourselves
|
||||
const prom = rageshake.init(/*setUpPersistence=*/false);
|
||||
prom.then(() => {
|
||||
logger.log("Initialised rageshake.");
|
||||
logger.log("To fix line numbers in Chrome: " +
|
||||
"Meatball menu → Settings → Ignore list → Add /rageshake\\.js$");
|
||||
const prom = rageshake.init(/*setUpPersistence=*/ false);
|
||||
prom.then(
|
||||
() => {
|
||||
logger.log("Initialised rageshake.");
|
||||
logger.log(
|
||||
"To fix line numbers in Chrome: " + "Meatball menu → Settings → Ignore list → Add /rageshake\\.js$",
|
||||
);
|
||||
|
||||
window.addEventListener('beforeunload', () => {
|
||||
logger.log('element-web closing');
|
||||
// try to flush the logs to indexeddb
|
||||
rageshake.flush();
|
||||
});
|
||||
window.addEventListener("beforeunload", () => {
|
||||
logger.log("element-web closing");
|
||||
// try to flush the logs to indexeddb
|
||||
rageshake.flush();
|
||||
});
|
||||
|
||||
rageshake.cleanup();
|
||||
}, (err) => {
|
||||
logger.error("Failed to initialise rageshake: " + err);
|
||||
});
|
||||
rageshake.cleanup();
|
||||
},
|
||||
(err) => {
|
||||
logger.error("Failed to initialise rageshake: " + err);
|
||||
},
|
||||
);
|
||||
return prom;
|
||||
}
|
||||
|
||||
|
@ -56,7 +60,7 @@ export function initRageshakeStore(): Promise<void> {
|
|||
return rageshake.tryInitStorage();
|
||||
}
|
||||
|
||||
window.mxSendRageshake = function(text: string, withLogs?: boolean): void {
|
||||
window.mxSendRageshake = function (text: string, withLogs?: boolean): void {
|
||||
const url = SdkConfig.get().bug_report_endpoint_url;
|
||||
if (!url) {
|
||||
logger.error("Cannot send a rageshake - no bug_report_endpoint_url configured");
|
||||
|
@ -72,9 +76,12 @@ window.mxSendRageshake = function(text: string, withLogs?: boolean): void {
|
|||
userText: text,
|
||||
sendLogs: withLogs,
|
||||
progressCallback: logger.log.bind(console),
|
||||
}).then(() => {
|
||||
logger.log("Bug report sent!");
|
||||
}, (err) => {
|
||||
logger.error(err);
|
||||
});
|
||||
}).then(
|
||||
() => {
|
||||
logger.log("Bug report sent!");
|
||||
},
|
||||
(err) => {
|
||||
logger.error(err);
|
||||
},
|
||||
);
|
||||
};
|
||||
|
|
|
@ -24,7 +24,7 @@ import { parseQsFromFragment } from "./url_utils";
|
|||
|
||||
let lastLocationHashSet: string = null;
|
||||
|
||||
export function getScreenFromLocation(location: Location): { screen: string, params: QueryDict } {
|
||||
export function getScreenFromLocation(location: Location): { screen: string; params: QueryDict } {
|
||||
const fragparts = parseQsFromFragment(location);
|
||||
return {
|
||||
screen: fragparts.location.substring(1),
|
||||
|
@ -54,11 +54,12 @@ function onHashChange(): void {
|
|||
// so a web page can update the URL bar appropriately.
|
||||
export function onNewScreen(screen: string, replaceLast = false): void {
|
||||
logger.log("newscreen " + screen);
|
||||
const hash = '#/' + screen;
|
||||
const hash = "#/" + screen;
|
||||
lastLocationHashSet = hash;
|
||||
|
||||
// if the new hash is a substring of the old one then we are stripping fields e.g `via` so replace history
|
||||
if (screen.startsWith("room/") &&
|
||||
if (
|
||||
screen.startsWith("room/") &&
|
||||
window.location.hash.includes("/$") === hash.includes("/$") && // only if both did or didn't contain event link
|
||||
window.location.hash.startsWith(hash)
|
||||
) {
|
||||
|
@ -73,5 +74,5 @@ export function onNewScreen(screen: string, replaceLast = false): void {
|
|||
}
|
||||
|
||||
export function init(): void {
|
||||
window.addEventListener('hashchange', onHashChange);
|
||||
window.addEventListener("hashchange", onHashChange);
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,11 +1,10 @@
|
|||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<head>
|
||||
<style type="text/css">
|
||||
|
||||
/* By default, hide the custom IS stuff - enabled in JS */
|
||||
#custom_is, #is_url {
|
||||
#custom_is,
|
||||
#is_url {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
@ -17,7 +16,8 @@
|
|||
background: #f9fafb;
|
||||
max-width: 680px;
|
||||
margin: auto;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif,
|
||||
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
}
|
||||
|
||||
.mx_Button {
|
||||
|
@ -27,7 +27,7 @@
|
|||
margin-left: 4px;
|
||||
margin-right: 4px;
|
||||
min-width: 80px;
|
||||
background-color: #03B381;
|
||||
background-color: #03b381;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
padding: 12px 22px;
|
||||
|
@ -44,7 +44,7 @@
|
|||
}
|
||||
|
||||
.mx_HomePage_header {
|
||||
color: #2E2F32;
|
||||
color: #2e2f32;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
@ -72,7 +72,7 @@
|
|||
}
|
||||
|
||||
.mx_HomePage_container {
|
||||
display: block ! important;
|
||||
display: block !important;
|
||||
margin: 10px 20px;
|
||||
}
|
||||
|
||||
|
@ -137,43 +137,58 @@
|
|||
}
|
||||
</style>
|
||||
|
||||
<meta name="apple-itunes-app" content="app-id=id1083446067">
|
||||
|
||||
<meta name="apple-itunes-app" content="app-id=id1083446067" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="mx_HomePage_errorContainer">
|
||||
<!-- populated by JS if needed -->
|
||||
</div>
|
||||
|
||||
<div class="mx_HomePage_container">
|
||||
<div class="mx_HomePage_header">
|
||||
<span class="mx_HomePage_logo">
|
||||
<svg width="34" height="42" viewBox="0 0 34 42" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.08 8.68C13.08 7.75216 13.8321 7 14.76 7C20.9456 7 25.96 12.0144 25.96 18.2C25.96 19.1278 25.2078 19.88 24.28 19.88C23.3521 19.88 22.6 19.1278 22.6 18.2C22.6 13.8701 19.0899 10.36 14.76 10.36C13.8321 10.36 13.08 9.60784 13.08 8.68Z" fill="#0DBD8B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.92 33.32C20.92 34.2478 20.1679 35 19.24 35C13.0544 35 8.04001 29.9856 8.04001 23.8C8.04001 22.8722 8.79217 22.12 9.72001 22.12C10.6478 22.12 11.4 22.8722 11.4 23.8C11.4 28.1299 14.9101 31.64 19.24 31.64C20.1679 31.64 20.92 32.3922 20.92 33.32Z" fill="#0DBD8B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.68 24.9199C3.75216 24.9199 3 24.1678 3 23.2399C3 17.0543 8.01441 12.0399 14.2 12.0399C15.1278 12.0399 15.88 12.7921 15.88 13.7199C15.88 14.6478 15.1278 15.3999 14.2 15.3999C9.87009 15.3999 6.36 18.91 6.36 23.2399C6.36 24.1678 5.60784 24.9199 4.68 24.9199Z" fill="#0DBD8B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M29.32 17.0801C30.2478 17.0801 31 17.8322 31 18.7601C31 24.9457 25.9856 29.9601 19.8 29.9601C18.8722 29.9601 18.12 29.2079 18.12 28.2801C18.12 27.3522 18.8722 26.6001 19.8 26.6001C24.1299 26.6001 27.64 23.09 27.64 18.7601C27.64 17.8322 28.3922 17.0801 29.32 17.0801Z" fill="#0DBD8B"/>
|
||||
</svg>
|
||||
</span>
|
||||
<h1>Unable to load</h1>
|
||||
<div class="mx_HomePage_errorContainer">
|
||||
<!-- populated by JS if needed -->
|
||||
</div>
|
||||
<div class="mx_HomePage_col">
|
||||
<div class="mx_HomePage_row">
|
||||
<div>
|
||||
<h2 id="step1_heading">Element can't load</h2>
|
||||
<p>Something went wrong and Element was unable to load.</p>
|
||||
|
||||
<div class="mx_HomePage_container">
|
||||
<div class="mx_HomePage_header">
|
||||
<span class="mx_HomePage_logo">
|
||||
<svg width="34" height="42" viewBox="0 0 34 42" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M13.08 8.68C13.08 7.75216 13.8321 7 14.76 7C20.9456 7 25.96 12.0144 25.96 18.2C25.96 19.1278 25.2078 19.88 24.28 19.88C23.3521 19.88 22.6 19.1278 22.6 18.2C22.6 13.8701 19.0899 10.36 14.76 10.36C13.8321 10.36 13.08 9.60784 13.08 8.68Z"
|
||||
fill="#0DBD8B"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M20.92 33.32C20.92 34.2478 20.1679 35 19.24 35C13.0544 35 8.04001 29.9856 8.04001 23.8C8.04001 22.8722 8.79217 22.12 9.72001 22.12C10.6478 22.12 11.4 22.8722 11.4 23.8C11.4 28.1299 14.9101 31.64 19.24 31.64C20.1679 31.64 20.92 32.3922 20.92 33.32Z"
|
||||
fill="#0DBD8B"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M4.68 24.9199C3.75216 24.9199 3 24.1678 3 23.2399C3 17.0543 8.01441 12.0399 14.2 12.0399C15.1278 12.0399 15.88 12.7921 15.88 13.7199C15.88 14.6478 15.1278 15.3999 14.2 15.3999C9.87009 15.3999 6.36 18.91 6.36 23.2399C6.36 24.1678 5.60784 24.9199 4.68 24.9199Z"
|
||||
fill="#0DBD8B"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M29.32 17.0801C30.2478 17.0801 31 17.8322 31 18.7601C31 24.9457 25.9856 29.9601 19.8 29.9601C18.8722 29.9601 18.12 29.2079 18.12 28.2801C18.12 27.3522 18.8722 26.6001 19.8 26.6001C24.1299 26.6001 27.64 23.09 27.64 18.7601C27.64 17.8322 28.3922 17.0801 29.32 17.0801Z"
|
||||
fill="#0DBD8B"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<h1>Unable to load</h1>
|
||||
</div>
|
||||
<div class="mx_HomePage_col">
|
||||
<div class="mx_HomePage_row">
|
||||
<div>
|
||||
<h2 id="step1_heading">Element can't load</h2>
|
||||
<p>Something went wrong and Element was unable to load.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mx_HomePage_row mx_Center mx_Spacer">
|
||||
<p class="mx_Spacer">
|
||||
<a href="https://element.io" target="_blank" class="mx_FooterLink"> Go to element.io </a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mx_HomePage_row mx_Center mx_Spacer">
|
||||
<p class="mx_Spacer">
|
||||
<a href="https://element.io" target="_blank" class="mx_FooterLink">
|
||||
Go to element.io
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -19,7 +19,7 @@ import { QueryDict, decodeParams } from "matrix-js-sdk/src/utils";
|
|||
// We want to support some name / value pairs in the fragment
|
||||
// so we're re-using query string like format
|
||||
//
|
||||
export function parseQsFromFragment(location: Location): { location: string, params: QueryDict } {
|
||||
export function parseQsFromFragment(location: Location): { location: string; params: QueryDict } {
|
||||
// if we have a fragment, it will start with '#', which we need to drop.
|
||||
// (if we don't, this will return '').
|
||||
const fragment = location.hash.substring(1);
|
||||
|
@ -27,7 +27,7 @@ export function parseQsFromFragment(location: Location): { location: string, par
|
|||
// our fragment may contain a query-param-like section. we need to fish
|
||||
// this out *before* URI-decoding because the params may contain ? and &
|
||||
// characters which are only URI-encoded once.
|
||||
const hashparts = fragment.split('?');
|
||||
const hashparts = fragment.split("?");
|
||||
|
||||
const result = {
|
||||
location: decodeURIComponent(hashparts[0]),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue