mirror of
https://github.com/vector-im/hydrogen-web.git
synced 2024-12-23 03:25:12 +01:00
Merge branch 'master' into threading-fallback-reply
This commit is contained in:
commit
3c59004e72
23
doc/SDK.md
23
doc/SDK.md
@ -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
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 = {
|
||||
|
@ -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());
|
||||
|
@ -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`;
|
||||
}
|
||||
}
|
||||
|
@ -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%;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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":
|
||||
|
@ -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,
|
||||
|
27
src/platform/web/ui/session/room/timeline/LocationView.js
Normal file
27
src/platform/web/ui/session/room/timeline/LocationView.js
Normal 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)
|
||||
]);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user