forked from mystiq/hydrogen-web
Merge branch 'master' into madlittlemods/686-682-local-friendly-development-and-commonjs
This commit is contained in:
commit
75098b4712
10 changed files with 83 additions and 27 deletions
|
@ -31,7 +31,8 @@ import {
|
||||||
createNavigation,
|
createNavigation,
|
||||||
createRouter,
|
createRouter,
|
||||||
RoomViewModel,
|
RoomViewModel,
|
||||||
TimelineView
|
TimelineView,
|
||||||
|
viewClassForTile
|
||||||
} from "hydrogen-view-sdk";
|
} from "hydrogen-view-sdk";
|
||||||
import downloadSandboxPath from 'hydrogen-view-sdk/download-sandbox.html?url';
|
import downloadSandboxPath from 'hydrogen-view-sdk/download-sandbox.html?url';
|
||||||
import workerPath from 'hydrogen-view-sdk/main.js?url';
|
import workerPath from 'hydrogen-view-sdk/main.js?url';
|
||||||
|
@ -53,7 +54,7 @@ import "hydrogen-view-sdk/theme-element-light.css";
|
||||||
async function main() {
|
async function main() {
|
||||||
const app = document.querySelector<HTMLDivElement>('#app')!
|
const app = document.querySelector<HTMLDivElement>('#app')!
|
||||||
const config = {};
|
const config = {};
|
||||||
const platform = new Platform(app, assetPaths, config, { development: import.meta.env.DEV });
|
const platform = new Platform({container: app, assetPaths, config, options: { development: import.meta.env.DEV }});
|
||||||
const navigation = createNavigation();
|
const navigation = createNavigation();
|
||||||
platform.setNavigation(navigation);
|
platform.setNavigation(navigation);
|
||||||
const urlRouter = createRouter({
|
const urlRouter = createRouter({
|
||||||
|
@ -88,7 +89,7 @@ async function main() {
|
||||||
navigation,
|
navigation,
|
||||||
});
|
});
|
||||||
await vm.load();
|
await vm.load();
|
||||||
const view = new TimelineView(vm.timelineViewModel);
|
const view = new TimelineView(vm.timelineViewModel, viewClassForTile);
|
||||||
app.appendChild(view.mount());
|
app.appendChild(view.mount());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,14 +132,15 @@ export class Client {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async startRegistration(homeserver, username, password, initialDeviceDisplayName) {
|
async startRegistration(homeserver, username, password, initialDeviceDisplayName, flowSelector) {
|
||||||
const request = this._platform.request;
|
const request = this._platform.request;
|
||||||
const hsApi = new HomeServerApi({homeserver, request});
|
const hsApi = new HomeServerApi({homeserver, request});
|
||||||
const registration = new Registration(hsApi, {
|
const registration = new Registration(hsApi, {
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
initialDeviceDisplayName,
|
initialDeviceDisplayName,
|
||||||
});
|
},
|
||||||
|
flowSelector);
|
||||||
return registration;
|
return registration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,6 @@ import {hkdf} from "../../utils/crypto/hkdf";
|
||||||
|
|
||||||
import {Platform as ModernPlatform} from "./Platform.js";
|
import {Platform as ModernPlatform} from "./Platform.js";
|
||||||
|
|
||||||
export function Platform(container, assetPaths, config, options = null) {
|
export function Platform({ container, assetPaths, config, configURL, options = null }) {
|
||||||
return new ModernPlatform(container, assetPaths, config, options, {aesjs, hkdf});
|
return new ModernPlatform({ container, assetPaths, config, configURL, options, cryptoExtras: { aesjs, hkdf }});
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,10 +126,11 @@ function adaptUIOnVisualViewportResize(container) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Platform {
|
export class Platform {
|
||||||
constructor(container, assetPaths, config, options = null, cryptoExtras = null) {
|
constructor({ container, assetPaths, config, configURL, options = null, cryptoExtras = null }) {
|
||||||
this._container = container;
|
this._container = container;
|
||||||
this._assetPaths = assetPaths;
|
this._assetPaths = assetPaths;
|
||||||
this._config = config;
|
this._config = config;
|
||||||
|
this._configURL = configURL;
|
||||||
this.settingsStorage = new SettingsStorage("hydrogen_setting_v1_");
|
this.settingsStorage = new SettingsStorage("hydrogen_setting_v1_");
|
||||||
this.clock = new Clock();
|
this.clock = new Clock();
|
||||||
this.encoding = new Encoding();
|
this.encoding = new Encoding();
|
||||||
|
@ -142,7 +143,7 @@ export class Platform {
|
||||||
this._serviceWorkerHandler = new ServiceWorkerHandler();
|
this._serviceWorkerHandler = new ServiceWorkerHandler();
|
||||||
this._serviceWorkerHandler.registerAndStart(assetPaths.serviceWorker);
|
this._serviceWorkerHandler.registerAndStart(assetPaths.serviceWorker);
|
||||||
}
|
}
|
||||||
this.notificationService = new NotificationService(this._serviceWorkerHandler, config.push);
|
this.notificationService = undefined;
|
||||||
// Only try to use crypto when olm is provided
|
// Only try to use crypto when olm is provided
|
||||||
if(this._assetPaths.olm) {
|
if(this._assetPaths.olm) {
|
||||||
this.crypto = new Crypto(cryptoExtras);
|
this.crypto = new Crypto(cryptoExtras);
|
||||||
|
@ -165,6 +166,20 @@ export class Platform {
|
||||||
this._workerPromise = undefined;
|
this._workerPromise = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
if (!this._config) {
|
||||||
|
if (!this._configURL) {
|
||||||
|
throw new Error("Neither config nor configURL was provided!");
|
||||||
|
}
|
||||||
|
const {body}= await this.request(this._configURL, {method: "GET", format: "json", cache: true}).response();
|
||||||
|
this._config = body;
|
||||||
|
}
|
||||||
|
this.notificationService = new NotificationService(
|
||||||
|
this._serviceWorkerHandler,
|
||||||
|
this._config.push
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
_createLogger(isDevelopment) {
|
_createLogger(isDevelopment) {
|
||||||
// Make sure that loginToken does not end up in the logs
|
// Make sure that loginToken does not end up in the logs
|
||||||
const transformer = (item) => {
|
const transformer = (item) => {
|
||||||
|
|
|
@ -17,17 +17,17 @@
|
||||||
<script id="main" type="module">
|
<script id="main" type="module">
|
||||||
import {main} from "./main";
|
import {main} from "./main";
|
||||||
import {Platform} from "./Platform";
|
import {Platform} from "./Platform";
|
||||||
import configJSON from "./assets/config.json?raw";
|
import configURL from "./assets/config.json?url";
|
||||||
import assetPaths from "./sdk/paths/vite";
|
import assetPaths from "./sdk/paths/vite";
|
||||||
if (import.meta.env.PROD) {
|
if (import.meta.env.PROD) {
|
||||||
assetPaths.serviceWorker = "sw.js";
|
assetPaths.serviceWorker = "sw.js";
|
||||||
}
|
}
|
||||||
const platform = new Platform(
|
const platform = new Platform({
|
||||||
document.body,
|
container: document.body,
|
||||||
assetPaths,
|
assetPaths,
|
||||||
JSON.parse(configJSON),
|
configURL,
|
||||||
{development: import.meta.env.DEV}
|
options: {development: import.meta.env.DEV}
|
||||||
);
|
});
|
||||||
main(platform);
|
main(platform);
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -32,6 +32,7 @@ export async function main(platform) {
|
||||||
// const recorder = new RecordRequester(createFetchRequest(clock.createTimeout));
|
// const recorder = new RecordRequester(createFetchRequest(clock.createTimeout));
|
||||||
// const request = recorder.request;
|
// const request = recorder.request;
|
||||||
// window.getBrawlFetchLog = () => recorder.log();
|
// window.getBrawlFetchLog = () => recorder.log();
|
||||||
|
await platform.init();
|
||||||
const navigation = createNavigation();
|
const navigation = createNavigation();
|
||||||
platform.setNavigation(navigation);
|
platform.setNavigation(navigation);
|
||||||
const urlRouter = createRouter({navigation, history: platform.history});
|
const urlRouter = createRouter({navigation, history: platform.history});
|
||||||
|
|
|
@ -92,8 +92,12 @@ function isCacheableThumbnail(url) {
|
||||||
|
|
||||||
const baseURL = new URL(self.registration.scope);
|
const baseURL = new URL(self.registration.scope);
|
||||||
let pendingFetchAbortController = new AbortController();
|
let pendingFetchAbortController = new AbortController();
|
||||||
|
|
||||||
async function handleRequest(request) {
|
async function handleRequest(request) {
|
||||||
try {
|
try {
|
||||||
|
if (request.url.includes("config.json")) {
|
||||||
|
return handleConfigRequest(request);
|
||||||
|
}
|
||||||
const url = new URL(request.url);
|
const url = new URL(request.url);
|
||||||
// rewrite / to /index.html so it hits the cache
|
// rewrite / to /index.html so it hits the cache
|
||||||
if (url.origin === baseURL.origin && url.pathname === baseURL.pathname) {
|
if (url.origin === baseURL.origin && url.pathname === baseURL.pathname) {
|
||||||
|
@ -119,6 +123,27 @@ async function handleRequest(request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleConfigRequest(request) {
|
||||||
|
let response = await readCache(request);
|
||||||
|
const networkResponsePromise = fetchAndUpdateConfig(request);
|
||||||
|
if (response) {
|
||||||
|
return response;
|
||||||
|
} else {
|
||||||
|
return await networkResponsePromise;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchAndUpdateConfig(request) {
|
||||||
|
const response = await fetch(request, {
|
||||||
|
signal: pendingFetchAbortController.signal,
|
||||||
|
headers: {
|
||||||
|
"Cache-Control": "no-cache",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
updateCache(request, response.clone());
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
async function updateCache(request, response) {
|
async function updateCache(request, response) {
|
||||||
// don't write error responses to the cache
|
// don't write error responses to the cache
|
||||||
if (response.status >= 400) {
|
if (response.status >= 400) {
|
||||||
|
|
|
@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { setAttribute, text, isChildren, classNames, TAG_NAMES, HTML_NS, ClassNames, Child} from "./html";
|
import { setAttribute, text, isChildren, classNames, TAG_NAMES, HTML_NS, ClassNames, Child as NonBoundChild} from "./html";
|
||||||
import {mountView} from "./utils";
|
import {mountView} from "./utils";
|
||||||
import {BaseUpdateView, IObservableValue} from "./BaseUpdateView";
|
import {BaseUpdateView, IObservableValue} from "./BaseUpdateView";
|
||||||
import {IMountArgs, ViewNode, IView} from "./types";
|
import {IMountArgs, ViewNode, IView} from "./types";
|
||||||
|
@ -30,12 +30,15 @@ function objHasFns(obj: ClassNames<unknown>): obj is { [className: string]: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RenderFn<T> = (t: Builder<T>, vm: T) => ViewNode;
|
export type RenderFn<T> = (t: Builder<T>, vm: T) => ViewNode;
|
||||||
|
type TextBinding<T> = (T) => string | number | boolean | undefined | null;
|
||||||
|
type Child<T> = NonBoundChild | TextBinding<T>;
|
||||||
|
type Children<T> = Child<T> | Child<T>[];
|
||||||
type EventHandler = ((event: Event) => void);
|
type EventHandler = ((event: Event) => void);
|
||||||
type AttributeStaticValue = string | boolean;
|
type AttributeStaticValue = string | boolean;
|
||||||
type AttributeBinding<T> = (value: T) => AttributeStaticValue;
|
type AttributeBinding<T> = (value: T) => AttributeStaticValue;
|
||||||
export type AttrValue<T> = AttributeStaticValue | AttributeBinding<T> | EventHandler | ClassNames<T>;
|
export type AttrValue<T> = AttributeStaticValue | AttributeBinding<T> | EventHandler | ClassNames<T>;
|
||||||
export type Attributes<T> = { [attribute: string]: AttrValue<T> };
|
export type Attributes<T> = { [attribute: string]: AttrValue<T> };
|
||||||
type ElementFn<T> = (attributes?: Attributes<T> | Child | Child[], children?: Child | Child[]) => Element;
|
type ElementFn<T> = (attributes?: Attributes<T> | Children<T>, children?: Children<T>) => Element;
|
||||||
export type Builder<T> = TemplateBuilder<T> & { [tagName in typeof TAG_NAMES[string][number]]: ElementFn<T> };
|
export type Builder<T> = TemplateBuilder<T> & { [tagName in typeof TAG_NAMES[string][number]]: ElementFn<T> };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -195,15 +198,15 @@ export class TemplateBuilder<T extends IObservableValue> {
|
||||||
this._addAttributeBinding(node, "className", value => classNames(obj, value));
|
this._addAttributeBinding(node, "className", value => classNames(obj, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
_addTextBinding(fn: (value: T) => string): Text {
|
_addTextBinding(fn: (value: T) => ReturnType<TextBinding<T>>): Text {
|
||||||
const initialValue = fn(this._value);
|
const initialValue = fn(this._value)+"";
|
||||||
const node = text(initialValue);
|
const node = text(initialValue);
|
||||||
let prevValue = initialValue;
|
let prevValue = initialValue;
|
||||||
const binding = () => {
|
const binding = () => {
|
||||||
const newValue = fn(this._value);
|
const newValue = fn(this._value)+"";
|
||||||
if (prevValue !== newValue) {
|
if (prevValue !== newValue) {
|
||||||
prevValue = newValue;
|
prevValue = newValue;
|
||||||
node.textContent = newValue+"";
|
node.textContent = newValue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -242,7 +245,7 @@ export class TemplateBuilder<T extends IObservableValue> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_setNodeChildren(node: Element, children: Child | Child[]): void{
|
_setNodeChildren(node: Element, children: Children<T>): void{
|
||||||
if (!Array.isArray(children)) {
|
if (!Array.isArray(children)) {
|
||||||
children = [children];
|
children = [children];
|
||||||
}
|
}
|
||||||
|
@ -276,14 +279,18 @@ export class TemplateBuilder<T extends IObservableValue> {
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
el(name: string, attributes?: Attributes<T> | Child | Child[], children?: Child | Child[]): ViewNode {
|
el(name: string, attributes?: Attributes<T> | Children<T>, children?: Children<T>): ViewNode {
|
||||||
return this.elNS(HTML_NS, name, attributes, children);
|
return this.elNS(HTML_NS, name, attributes, children);
|
||||||
}
|
}
|
||||||
|
|
||||||
elNS(ns: string, name: string, attributes?: Attributes<T> | Child | Child[], children?: Child | Child[]): ViewNode {
|
elNS(ns: string, name: string, attributesOrChildren?: Attributes<T> | Children<T>, children?: Children<T>): ViewNode {
|
||||||
if (attributes !== undefined && isChildren(attributes)) {
|
let attributes: Attributes<T> | undefined;
|
||||||
children = attributes;
|
if (attributesOrChildren) {
|
||||||
attributes = undefined;
|
if (isChildren(attributesOrChildren)) {
|
||||||
|
children = attributesOrChildren as Children<T>;
|
||||||
|
} else {
|
||||||
|
attributes = attributesOrChildren as Attributes<T>;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const node = document.createElementNS(ns, name);
|
const node = document.createElementNS(ns, name);
|
||||||
|
|
|
@ -31,6 +31,7 @@ const commonOptions = {
|
||||||
assetsInlineLimit: 0,
|
assetsInlineLimit: 0,
|
||||||
polyfillModulePreload: false,
|
polyfillModulePreload: false,
|
||||||
},
|
},
|
||||||
|
assetsInclude: ['**/config.json'],
|
||||||
define: {
|
define: {
|
||||||
DEFINE_VERSION: JSON.stringify(version),
|
DEFINE_VERSION: JSON.stringify(version),
|
||||||
DEFINE_GLOBAL_HASH: JSON.stringify(null),
|
DEFINE_GLOBAL_HASH: JSON.stringify(null),
|
||||||
|
|
|
@ -14,6 +14,11 @@ export default defineConfig(({mode}) => {
|
||||||
outDir: "../../../target",
|
outDir: "../../../target",
|
||||||
minify: true,
|
minify: true,
|
||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
|
rollupOptions: {
|
||||||
|
output: {
|
||||||
|
assetFileNames: (asset) => asset.name.includes("config.json") ? "assets/[name][extname]": "assets/[name].[hash][extname]",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
themeBuilder({
|
themeBuilder({
|
||||||
|
|
Loading…
Reference in a new issue