Compare commits
7 commits
master
...
snowpack-t
Author | SHA1 | Date | |
---|---|---|---|
|
a7adc27a69 | ||
|
d5503b2e6b | ||
|
c5153339c0 | ||
|
b5b4d563ec | ||
|
59c1a1f401 | ||
|
3687f1fd4a | ||
|
32b308faa7 |
18 changed files with 2489 additions and 74 deletions
|
@ -51,6 +51,8 @@
|
|||
"rollup": "^2.26.4",
|
||||
"rollup-plugin-cleanup": "^3.1.1",
|
||||
"serve-static": "^1.13.2",
|
||||
"snowpack": "^3.8.3",
|
||||
"typescript": "^4.3.5",
|
||||
"xxhashjs": "^0.2.2"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
41
public/index.html
Normal file
41
public/index.html
Normal file
|
@ -0,0 +1,41 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no">
|
||||
<meta name="application-name" content="Hydrogen Chat"/>
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
<meta name="apple-mobile-web-app-title" content="Hydrogen Chat">
|
||||
<meta name="description" content="A matrix chat application">
|
||||
<link rel="apple-touch-icon" href="assets/icon-maskable.png">
|
||||
<link rel="stylesheet" type="text/css" href="src/platform/web/ui/css/main.css">
|
||||
<link rel="stylesheet" type="text/css" href="src/platform/web/ui/css/themes/element/theme.css" title="Element Theme">
|
||||
<link rel="alternate stylesheet" type="text/css" href="src/platform/web/ui/css/themes/bubbles/theme.css" title="Bubbles Theme">
|
||||
</head>
|
||||
<body class="hydrogen">
|
||||
<script id="version" type="disabled">
|
||||
window.HYDROGEN_VERSION = "%%VERSION%%";
|
||||
window.HYDROGEN_GLOBAL_HASH = "%%GLOBAL_HASH%%";
|
||||
</script>
|
||||
<script id="main" type="module">
|
||||
import {main} from "./src/main.js";
|
||||
import {Platform} from "./src/platform/web/Platform.js";
|
||||
main(new Platform(document.body, {
|
||||
worker: "src/worker.js",
|
||||
downloadSandbox: "assets/download-sandbox.html",
|
||||
defaultHomeServer: "matrix.org",
|
||||
// NOTE: uncomment this if you want the service worker for local development
|
||||
// serviceWorker: "sw.js",
|
||||
// NOTE: provide push config if you want push notifs for local development
|
||||
// see assets/config.json for what the config looks like
|
||||
// push: {...},
|
||||
olm: {
|
||||
wasm: "lib/olm/olm.wasm",
|
||||
legacyBundle: "lib/olm/olm_legacy.js",
|
||||
wasmBundle: "lib/olm/olm.js",
|
||||
}
|
||||
}, null, {development: true}));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -75,7 +75,7 @@ async function populateLib() {
|
|||
const olmDstDir = path.join(libDir, "olm/");
|
||||
await fs.mkdir(olmDstDir);
|
||||
for (const file of ["olm.js", "olm.wasm", "olm_legacy.js"]) {
|
||||
await fs.symlink(path.join(olmSrcDir, file), path.join(olmDstDir, file));
|
||||
await fs.copyFile(path.join(olmSrcDir, file), path.join(olmDstDir, file));
|
||||
}
|
||||
// transpile node-html-parser to esm
|
||||
await fs.mkdir(path.join(libDir, "node-html-parser/"));
|
||||
|
@ -85,7 +85,7 @@ async function populateLib() {
|
|||
);
|
||||
// Symlink dompurify
|
||||
await fs.mkdir(path.join(libDir, "dompurify/"));
|
||||
await fs.symlink(
|
||||
await fs.copyFile(
|
||||
require.resolve('dompurify/dist/purify.es.js'),
|
||||
path.join(libDir, "dompurify/index.js")
|
||||
);
|
||||
|
|
35
snowpack.config.js
Normal file
35
snowpack.config.js
Normal file
|
@ -0,0 +1,35 @@
|
|||
// Snowpack Configuration File
|
||||
// See all supported options: https://www.snowpack.dev/reference/configuration
|
||||
|
||||
/** @type {import("snowpack").SnowpackUserConfig } */
|
||||
module.exports = {
|
||||
mount: {
|
||||
"src": "/src",
|
||||
"public": "/",
|
||||
"lib": {url: "/lib", static: true },
|
||||
/* ... */
|
||||
},
|
||||
exclude: [
|
||||
/* Avoid scanning scripts which use dev-dependencies and pull in babel, rollup, etc. */
|
||||
'**/node_modules/**/*', '**/scripts/**', '**/target/**', '**/prototypes/**', '**/src/matrix/storage/memory/**'
|
||||
],
|
||||
plugins: [
|
||||
/* ... */
|
||||
],
|
||||
packageOptions: {
|
||||
/* ... */
|
||||
external: [
|
||||
/* Olm seems to import these but not use them. */
|
||||
"path",
|
||||
"crypto",
|
||||
"fs",
|
||||
],
|
||||
},
|
||||
devOptions: {
|
||||
open: "none",
|
||||
/* ... */
|
||||
},
|
||||
buildOptions: {
|
||||
/* ... */
|
||||
},
|
||||
};
|
|
@ -343,7 +343,7 @@ export function parseHTMLBody(platform, mediaRepository, html) {
|
|||
return new MessageBody(html, parts);
|
||||
}
|
||||
|
||||
import parse from '../../../../../lib/node-html-parser/index.js';
|
||||
import parse from 'node-html-parser';
|
||||
|
||||
export function tests() {
|
||||
class HTMLParseResult {
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
|||
|
||||
import {BaseTextTile, BodyFormat} from "./BaseTextTile.js";
|
||||
import {parsePlainBody} from "../MessageBody.js";
|
||||
import {parseHTMLBody} from "../deserialize.js";
|
||||
import {parseHTMLBody} from "../deserialize.ts";
|
||||
|
||||
export class TextTile extends BaseTextTile {
|
||||
_getContentString(key) {
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import anotherjson from "../../../lib/another-json/index.js";
|
||||
import anotherjson from "another-json";
|
||||
import {SESSION_KEY_PREFIX, OLM_ALGORITHM, MEGOLM_ALGORITHM} from "./common.js";
|
||||
|
||||
// use common prefix so it's easy to clear properties that are not e2ee related during session clear
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import anotherjson from "../../../lib/another-json/index.js";
|
||||
import anotherjson from "another-json";
|
||||
import {createEnum} from "../../utils/enum.js";
|
||||
|
||||
export const DecryptionSource = createEnum("Sync", "Timeline", "Retry");
|
||||
|
|
|
@ -18,20 +18,26 @@ limitations under the License.
|
|||
import {EventKey} from "../EventKey.js";
|
||||
export const PENDING_FRAGMENT_ID = Number.MAX_SAFE_INTEGER;
|
||||
|
||||
export class BaseEntry {
|
||||
export interface FragmentIdComparer {
|
||||
compare(a: number, b: number): number;
|
||||
}
|
||||
|
||||
export abstract class BaseEntry {
|
||||
protected _fragmentIdComparer: FragmentIdComparer
|
||||
|
||||
constructor(fragmentIdComparer) {
|
||||
this._fragmentIdComparer = fragmentIdComparer;
|
||||
}
|
||||
|
||||
get fragmentId() {
|
||||
get fragmentId(): number {
|
||||
throw new Error("unimplemented");
|
||||
}
|
||||
|
||||
get entryIndex() {
|
||||
get entryIndex(): number {
|
||||
throw new Error("unimplemented");
|
||||
}
|
||||
|
||||
compare(otherEntry) {
|
||||
compare(otherEntry: BaseEntry): number {
|
||||
if (this.fragmentId === otherEntry.fragmentId) {
|
||||
return this.entryIndex - otherEntry.entryIndex;
|
||||
} else if (this.fragmentId === PENDING_FRAGMENT_ID) {
|
||||
|
@ -44,9 +50,9 @@ export class BaseEntry {
|
|||
}
|
||||
}
|
||||
|
||||
asEventKey() {
|
||||
asEventKey(): EventKey {
|
||||
return new EventKey(this.fragmentId, this.entryIndex);
|
||||
}
|
||||
|
||||
updateFrom() {}
|
||||
updateFrom(other: BaseEntry) {}
|
||||
}
|
|
@ -14,33 +14,48 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {BaseEntry} from "./BaseEntry.js";
|
||||
import {FragmentIdComparer, BaseEntry} from "./BaseEntry";
|
||||
import {PendingEventEntry} from "./PendingEventEntry.js"
|
||||
import {REDACTION_TYPE} from "../../common.js";
|
||||
import {createAnnotation, ANNOTATION_RELATION_TYPE, getRelationFromContent} from "../relations.js";
|
||||
import {PendingAnnotation} from "../PendingAnnotation.js";
|
||||
|
||||
export interface Annotation {
|
||||
count: number,
|
||||
me: boolean,
|
||||
firstTimestamp: number
|
||||
}
|
||||
|
||||
/** Deals mainly with local echo for relations and redactions,
|
||||
* so it is shared between PendingEventEntry and EventEntry */
|
||||
export class BaseEventEntry extends BaseEntry {
|
||||
constructor(fragmentIdComparer) {
|
||||
export abstract class BaseEventEntry extends BaseEntry {
|
||||
private _pendingRedactions: Array<PendingEventEntry> | null
|
||||
private _pendingAnnotations: Map<string, PendingAnnotation> | null
|
||||
|
||||
abstract id: string
|
||||
abstract relatedEventId: string
|
||||
abstract eventType: string
|
||||
abstract content: any
|
||||
|
||||
constructor(fragmentIdComparer: FragmentIdComparer) {
|
||||
super(fragmentIdComparer);
|
||||
this._pendingRedactions = null;
|
||||
this._pendingAnnotations = null;
|
||||
}
|
||||
|
||||
get isRedacting() {
|
||||
get isRedacting(): boolean {
|
||||
return !!this._pendingRedactions;
|
||||
}
|
||||
|
||||
get isRedacted() {
|
||||
get isRedacted(): boolean {
|
||||
return this.isRedacting;
|
||||
}
|
||||
|
||||
get isRedaction() {
|
||||
get isRedaction(): boolean {
|
||||
return this.eventType === REDACTION_TYPE;
|
||||
}
|
||||
|
||||
get redactionReason() {
|
||||
get redactionReason(): string | null {
|
||||
if (this._pendingRedactions) {
|
||||
return this._pendingRedactions[0].content?.reason;
|
||||
}
|
||||
|
@ -51,7 +66,7 @@ export class BaseEventEntry extends BaseEntry {
|
|||
aggregates local relation or local redaction of remote relation.
|
||||
@return [string] returns the name of the field that has changed, if any
|
||||
*/
|
||||
addLocalRelation(entry) {
|
||||
addLocalRelation(entry: PendingEventEntry): string | undefined {
|
||||
if (entry.eventType === REDACTION_TYPE && entry.isRelatedToId(this.id)) {
|
||||
if (!this._pendingRedactions) {
|
||||
this._pendingRedactions = [];
|
||||
|
@ -76,7 +91,7 @@ export class BaseEventEntry extends BaseEntry {
|
|||
deaggregates local relation or a local redaction of a remote relation.
|
||||
@return [string] returns the name of the field that has changed, if any
|
||||
*/
|
||||
removeLocalRelation(entry) {
|
||||
removeLocalRelation(entry: PendingEventEntry): string | undefined {
|
||||
if (entry.eventType === REDACTION_TYPE && entry.isRelatedToId(this.id) && this._pendingRedactions) {
|
||||
const countBefore = this._pendingRedactions.length;
|
||||
this._pendingRedactions = this._pendingRedactions.filter(e => e !== entry);
|
||||
|
@ -98,7 +113,7 @@ export class BaseEventEntry extends BaseEntry {
|
|||
}
|
||||
}
|
||||
|
||||
_addPendingAnnotation(entry) {
|
||||
_addPendingAnnotation(entry: PendingEventEntry): boolean {
|
||||
if (!this._pendingAnnotations) {
|
||||
this._pendingAnnotations = new Map();
|
||||
}
|
||||
|
@ -115,14 +130,14 @@ export class BaseEventEntry extends BaseEntry {
|
|||
return false;
|
||||
}
|
||||
|
||||
_removePendingAnnotation(entry) {
|
||||
_removePendingAnnotation(entry: PendingEventEntry) {
|
||||
const {key} = (entry.redactingEntry || entry).relation;
|
||||
if (key) {
|
||||
let annotation = this._pendingAnnotations.get(key);
|
||||
let annotation = this._pendingAnnotations?.get(key);
|
||||
if (annotation.remove(entry) && annotation.isEmpty) {
|
||||
this._pendingAnnotations.delete(key);
|
||||
this._pendingAnnotations?.delete(key);
|
||||
}
|
||||
if (this._pendingAnnotations.size === 0) {
|
||||
if (this._pendingAnnotations?.size === 0) {
|
||||
this._pendingAnnotations = null;
|
||||
}
|
||||
return true;
|
||||
|
@ -140,23 +155,23 @@ export class BaseEventEntry extends BaseEntry {
|
|||
}
|
||||
}
|
||||
|
||||
get pendingRedaction() {
|
||||
get pendingRedaction(): PendingEventEntry | null {
|
||||
if (this._pendingRedactions) {
|
||||
return this._pendingRedactions[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
annotate(key) {
|
||||
annotate(key: string): any {
|
||||
return createAnnotation(this.id, key);
|
||||
}
|
||||
|
||||
/** takes both remote event id and local txn id into account, see overriding in PendingEventEntry */
|
||||
isRelatedToId(id) {
|
||||
return id && this.relatedEventId === id;
|
||||
isRelatedToId(id: string | null): boolean {
|
||||
return !!id && this.relatedEventId === id;
|
||||
}
|
||||
|
||||
haveAnnotation(key) {
|
||||
haveAnnotation(key: string): boolean {
|
||||
const haveRemoteReaction = this.annotations?.[key]?.me || false;
|
||||
const pendingAnnotation = this.pendingAnnotations?.get(key);
|
||||
const willAnnotate = pendingAnnotation?.willAnnotate || false;
|
||||
|
@ -170,15 +185,15 @@ export class BaseEventEntry extends BaseEntry {
|
|||
(!haveRemoteReaction && willAnnotate);
|
||||
}
|
||||
|
||||
get relation() {
|
||||
get relation(): any {
|
||||
return getRelationFromContent(this.content);
|
||||
}
|
||||
|
||||
get pendingAnnotations() {
|
||||
get pendingAnnotations(): Map<string, PendingAnnotation> | null {
|
||||
return this._pendingAnnotations;
|
||||
}
|
||||
|
||||
get annotations() {
|
||||
get annotations(): { [key: string]: Annotation } | null {
|
||||
return null; //overwritten in EventEntry
|
||||
}
|
||||
}
|
|
@ -14,25 +14,30 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {FragmentIdComparer} from "./BaseEntry.js"
|
||||
import {BaseEventEntry} from "./BaseEventEntry.js";
|
||||
import {getPrevContentFromStateEvent, isRedacted} from "../../common.js";
|
||||
import {getRelatedEventId} from "../relations.js";
|
||||
|
||||
export class EventEntry extends BaseEventEntry {
|
||||
constructor(eventEntry, fragmentIdComparer) {
|
||||
private _eventEntry: any // TODO Need type
|
||||
private _decryptionError: Error | null
|
||||
private _decryptionResult: any | null // TODO Need type
|
||||
|
||||
constructor(eventEntry: any, fragmentIdComparer: FragmentIdComparer = { compare: (x, y) => 0 }) {
|
||||
super(fragmentIdComparer);
|
||||
this._eventEntry = eventEntry;
|
||||
this._decryptionError = null;
|
||||
this._decryptionResult = null;
|
||||
}
|
||||
|
||||
clone() {
|
||||
clone(): EventEntry {
|
||||
const clone = new EventEntry(this._eventEntry, this._fragmentIdComparer);
|
||||
clone.updateFrom(this);
|
||||
return clone;
|
||||
}
|
||||
|
||||
updateFrom(other) {
|
||||
updateFrom(other: EventEntry) {
|
||||
super.updateFrom(other);
|
||||
if (other._decryptionResult && !this._decryptionResult) {
|
||||
this._decryptionResult = other._decryptionResult;
|
|
@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {PENDING_FRAGMENT_ID} from "./BaseEntry.js";
|
||||
import {BaseEventEntry} from "./BaseEventEntry.js";
|
||||
import {PENDING_FRAGMENT_ID} from "./BaseEntry";
|
||||
import {BaseEventEntry} from "./BaseEventEntry";
|
||||
|
||||
export class PendingEventEntry extends BaseEventEntry {
|
||||
constructor({pendingEvent, member, clock, redactingEntry}) {
|
||||
|
|
|
@ -14,9 +14,9 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {FDBFactory, FDBKeyRange} from "../../lib/fake-indexeddb/index.js";
|
||||
import fdb from "fake-indexeddb";
|
||||
import {StorageFactory} from "../matrix/storage/idb/StorageFactory.js";
|
||||
|
||||
export function createMockStorage() {
|
||||
return new StorageFactory(null, new FDBFactory(), FDBKeyRange).create(1);
|
||||
return new StorageFactory(null, new fdb.FDBFactory(), fdb.FDBKeyRange).create(1);
|
||||
}
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import aesjs from "../../../lib/aes-js/index.js";
|
||||
import aesjs from "aes-js";
|
||||
import {hkdf} from "../../utils/crypto/hkdf.js";
|
||||
import {Platform as ModernPlatform} from "./Platform.js";
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import base64 from "../../../../lib/base64-arraybuffer/index.js";
|
||||
import base64 from "base64-arraybuffer";
|
||||
|
||||
// turn IE11 result into promise
|
||||
function subtleCryptoResult(promiseOrOp, method) {
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import DOMPurify from "../../../lib/dompurify/index.js"
|
||||
import DOMPurify from "dompurify"
|
||||
|
||||
class HTMLParseResult {
|
||||
constructor(bodyNode) {
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import base64 from "../../../../lib/base64-arraybuffer/index.js";
|
||||
import base64 from "base64-arraybuffer";
|
||||
|
||||
export class Base64 {
|
||||
encodeUnpadded(buffer) {
|
||||
|
|
Reference in a new issue