Compare commits

...
This repository has been archived on 2022-08-19. You can view files and clone it, but cannot push or open issues or pull requests.

7 commits

Author SHA1 Message Date
Danila Fedorin
a7adc27a69 Try switching some files over to TypeScript 2021-08-05 17:03:46 -07:00
Danila Fedorin
d5503b2e6b Rely on snowpack to serve node modules 2021-08-04 16:31:05 -07:00
Danila Fedorin
c5153339c0 Ignore seemingly dead code 2021-08-04 16:19:14 -07:00
Danila Fedorin
b5b4d563ec Add index file to a subfolder to avoid exposing base directory 2021-08-02 17:19:56 -07:00
Danila Fedorin
59c1a1f401 Add a configuration file and 'migrate' deserialize to TS 2021-08-02 17:18:00 -07:00
Danila Fedorin
3687f1fd4a Copy instead of symlinking since it seems snowpack can't pick up symlinks 2021-08-02 17:00:40 -07:00
Danila Fedorin
32b308faa7 Add snowpack and typescript as dev dependencies 2021-08-02 16:59:04 -07:00
18 changed files with 2489 additions and 74 deletions

View file

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

View file

@ -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
View 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: {
/* ... */
},
};

View file

@ -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 {

View file

@ -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) {

View file

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

View file

@ -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");

View file

@ -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) {}
}

View file

@ -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
}
}

View file

@ -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;

View file

@ -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}) {

View file

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

View file

@ -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";

View file

@ -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) {

View file

@ -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) {

View file

@ -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) {

2367
yarn.lock

File diff suppressed because it is too large Load diff