Merge branch 'master' into threading-fallback-reply

This commit is contained in:
Bruno Windels 2022-01-14 15:43:24 +01:00
commit 3c59004e72
13 changed files with 171 additions and 35 deletions

View File

@ -33,7 +33,20 @@ import {
RoomViewModel,
TimelineView
} from "hydrogen-view-sdk";
import assetPaths from "hydrogen-view-sdk/paths/vite";
import downloadSandboxPath from 'hydrogen-view-sdk/download-sandbox.html?url';
import workerPath from 'hydrogen-view-sdk/main.js?url';
import olmWasmPath from '@matrix-org/olm/olm.wasm?url';
import olmJsPath from '@matrix-org/olm/olm.js?url';
import olmLegacyJsPath from '@matrix-org/olm/olm_legacy.js?url';
const assetPaths = {
downloadSandbox: downloadSandboxPath,
worker: workerPath,
olm: {
wasm: olmWasmPath,
legacyBundle: olmLegacyJsPath,
wasmBundle: olmJsPath
}
};
import "hydrogen-view-sdk/style.css";
async function main() {
@ -84,7 +97,13 @@ main();
## Typescript support
There is partial typescript support while we are still in the process of converting the Hydrogen codebase to typesccript.
Typescript support is not yet available while we're converting the Hydrogen codebase to Typescript.
In your `src` directory, you'll need to add a `.d.ts` (can be called anything, e.g. `deps.d.ts`)
containing this snippet to make Typescript not complain that `hydrogen-view-sdk` doesn't have types:
```ts
declare module "hydrogen-view-sdk";
```
## API Stability

View File

@ -2,8 +2,37 @@
## Use `type` rather than `interface` for named parameters and POJO return values.
`type` and `interface` can be used somewhat interchangebly used, but let's use `type` to describe data and `interface` to describe (polymorphic) behaviour.
`type` and `interface` can be used somewhat interchangeably, but let's use `type` to describe data and `interface` to describe (polymorphic) behaviour.
Good examples of data are option objects to have named parameters, and POJO (plain old javascript objects) without any methods, just fields.
Also see [this playground](https://www.typescriptlang.org/play?#code/C4TwDgpgBACghgJwgO2AeTMAlge2QZygF4oBvAKCiqmTgFsIAuKfYBLZAcwG5LqATCABs4IAPzNkAVzoAjCAl4BfcuVCQoAYQAWWIfwzY8hEvCSpDuAlABkZPlQDGOITgTNW7LstWOR+QjMUYHtqKGcCNilHYDcAChxMK3xmIIsk4wBKewcoFRVyPzgArV19KAgAD2AUfkDEYNDqCM9o2IQEjIJmHT0DLvxsijCw-ClIDsSjAkzeEebjEIYAuE5oEgADABJSKeSAOloGJSgsQh29433nVwQlDbnqfKA)
## Use `type foo = { [key: string]: any }` for types that you intend to fill in later.
For instance, if you have a method such as:
```js
function load(options) {
// ...
}
```
and you intend to type options at some later point, do:
```ts
type Options = { [key: string]: any}
```
This makes it much easier to add the necessary type information at a later time.
## Use `object` or `Record<string, any>` to describe a type that accepts any javascript object.
Sometimes a function or method may genuinely need to accept any object; eg:
```js
function encodeBody(body) {
// ...
}
```
In this scenario:
- Use `object` if you know that you will not access any property
- Use `Record<string, any>` if you need to access some property
Both usages prevent the type from accepting primitives (eg: string, boolean...).
If using `Record`, ensure that you have guards to check that the properties really do exist.

View File

@ -45,13 +45,13 @@
"text-encoding": "^0.7.0",
"typescript": "^4.3.5",
"vite": "^2.6.14",
"xxhashjs": "^0.2.2"
"xxhashjs": "^0.2.2",
"bs58": "^4.0.1"
},
"dependencies": {
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz",
"another-json": "^0.2.0",
"base64-arraybuffer": "^0.2.0",
"bs58": "^4.0.1",
"dompurify": "^2.3.0"
}
}

View File

@ -1,15 +1,7 @@
{
"name": "hydrogen-view-sdk",
"description": "Embeddable matrix client library, including view components",
"version": "0.0.2",
"main": "./hydrogen.cjs.js",
"exports": {
".": {
"import": "./hydrogen.es.js",
"require": "./hydrogen.cjs.js"
},
"./paths/vite": "./paths/vite.js",
"./style.css": "./style.css"
},
"types": "types/lib.d.ts"
"version": "0.0.4",
"main": "./hydrogen.es.js",
"type": "module"
}

View File

@ -1,10 +1,12 @@
#!/bin/bash
rm -rf target
yarn run vite build -c vite.sdk-assets-config.js
yarn run vite build -c vite.sdk-lib-config.js
yarn tsc -p tsconfig-declaration.json
./scripts/sdk/create-manifest.js ./target/package.json
mkdir target/paths
./scripts/sdk/transform-paths.js ./src/platform/web/sdk/paths/vite.js ./target/paths/vite.js
# this doesn't work, the ?url imports need to be in the consuming project, so disable for now
# ./scripts/sdk/transform-paths.js ./src/platform/web/sdk/paths/vite.js ./target/paths/vite.js
cp doc/SDK.md target/README.md
pushd target
pushd asset-build/assets

View File

@ -1,7 +1,29 @@
#!/usr/bin/env node
const fs = require("fs");
const appManifest = require("../../package.json")
const baseSDKManifest = require("./base-manifest.json")
const appManifest = require("../../package.json");
const baseSDKManifest = require("./base-manifest.json");
/*
need to leave exports out of base-manifest.json because of #vite-bug,
with the downside that we can't support environments that support
both esm and commonjs modules, so we pick just esm.
```
"exports": {
".": {
"import": "./hydrogen.es.js",
"require": "./hydrogen.cjs.js"
},
"./paths/vite": "./paths/vite.js",
"./style.css": "./style.css"
},
```
Also need to leave typescript type definitions out until the
typescript conversion is complete and all imports in the d.ts files
exists.
```
"types": "types/lib.d.ts"
```
*/
const mergeOptions = require('merge-options');
const manifestExtension = {

View File

@ -25,10 +25,8 @@ export class BaseMediaTile extends BaseMessageTile {
super(options);
this._decryptedThumbnail = null;
this._decryptedFile = null;
this._isVisible = false;
this._error = null;
if (!this.isPending) {
this._tryLoadEncryptedThumbnail();
}
}
get isUploading() {
@ -60,6 +58,9 @@ export class BaseMediaTile extends BaseMessageTile {
}
get thumbnailUrl() {
if (!this._isVisible) {
return "";
}
if (this._decryptedThumbnail) {
return this._decryptedThumbnail.url;
} else {
@ -85,6 +86,15 @@ export class BaseMediaTile extends BaseMessageTile {
return "";
}
notifyVisible() {
super.notifyVisible();
this._isVisible = true;
this.emitChange("thumbnailUrl");
if (!this.isPending) {
this._tryLoadEncryptedThumbnail();
}
}
get width() {
const info = this._getContent()?.info;
return Math.round(info?.w * this._scaleFactor());

View File

@ -16,21 +16,43 @@ limitations under the License.
import {BaseMessageTile} from "./BaseMessageTile.js";
/*
map urls:
apple: https://developer.apple.com/library/archive/featuredarticles/iPhoneURLScheme_Reference/MapLinks/MapLinks.html
android: https://developers.google.com/maps/documentation/urls/guide
wp: maps:49.275267 -122.988617
https://www.habaneroconsulting.com/stories/insights/2011/opening-native-map-apps-from-the-mobile-browser
*/
export class LocationTile extends BaseMessageTile {
get shape() {
return "location";
}
get mapsLink() {
const geoUri = this._getContent().geo_uri;
const [lat, long] = geoUri.split(":")[1].split(",");
return `maps:${lat} ${long}`;
try {
const url = new URL(this._getContent().geo_uri);
if (url.protocol !== "geo:") {
return "";
}
const [locationStr, ...namedParams] = url.pathname.split(";");
const [latStr, longStr] = locationStr.split(",");
const lat = parseFloat(latStr);
const long = parseFloat(longStr);
let uncertainty;
for (const namedParam of namedParams) {
const [name, value] = namedParam.split("=");
if (name === "u") {
uncertainty = parseFloat(value);
}
}
if (this.platform.isIOS) {
return `http://maps.apple.com/?ll=${lat},${long}`;
} else {
let uri = `geo:${lat},${long}`;
if (uncertainty) {
uri = uri + `;u=${uncertainty}`;
}
return uri;
}
} catch {
return "";
}
}
get label() {
return `${this.sender} sent their location, click to see it in maps.`;
return this.i18n`${this.displayName} sent their location`;
}
}

View File

@ -374,6 +374,18 @@ only loads when the top comes into view*/
animation-timing-function: linear;
}
.Timeline_locationLink {
padding: 0px 8px;
border-radius: 16px;
border: 1px solid #e9edf1;
background-color: #f3f8fd;
text-decoration: none;
display: inline-block;
line-height: 2rem;
vertical-align: top;
margin: 1px 4px;
}
.AnnouncementView {
margin: 5px 0;
padding: 5px 10%;

View File

@ -39,7 +39,6 @@ export interface TimelineViewModel extends IObservableValue {
export type TileView = GapView | AnnouncementView | TextMessageView |
ImageView | VideoView | FileView | MissingAttachmentView | RedactedView;
function bottom(node: HTMLElement): number {
return node.offsetTop + node.clientHeight;
}

View File

@ -18,6 +18,7 @@ import {TextMessageView} from "./timeline/TextMessageView.js";
import {ImageView} from "./timeline/ImageView.js";
import {VideoView} from "./timeline/VideoView.js";
import {FileView} from "./timeline/FileView.js";
import {LocationView} from "./timeline/LocationView.js";
import {MissingAttachmentView} from "./timeline/MissingAttachmentView.js";
import {AnnouncementView} from "./timeline/AnnouncementView.js";
import {RedactedView} from "./timeline/RedactedView.js";
@ -41,6 +42,8 @@ export function viewClassForEntry(entry: SimpleTile): TileViewConstructor | unde
return VideoView;
case "file":
return FileView;
case "location":
return LocationView;
case "missing-attachment":
return MissingAttachmentView;
case "redacted":

View File

@ -19,7 +19,6 @@ import {BaseMediaView} from "./BaseMediaView.js";
export class ImageView extends BaseMediaView {
renderMedia(t, vm) {
const img = t.img({
loading: "lazy",
src: vm => vm.thumbnailUrl,
alt: vm => vm.label,
title: vm => vm.label,

View File

@ -0,0 +1,27 @@
/*
Copyright 2020 Bruno Windels <bruno@windels.cloud>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import {BaseMessageView} from "./BaseMessageView.js";
export class LocationView extends BaseMessageView {
renderMessageBody(t, vm) {
return t.p({className: "Timeline_messageBody statusMessage"}, [
t.span(vm.label),
t.a({className: "Timeline_locationLink", href: vm.mapsLink, target: "_blank", rel: "noopener"}, vm.i18n`Click to open in maps`),
t.time(vm.date + " " + vm.time)
]);
}
}