Merge branch 'master' into bwindels/calls-wip

This commit is contained in:
Bruno Windels 2022-04-25 12:43:01 +02:00
commit 8b16782270
12 changed files with 80 additions and 23 deletions

View file

@ -47,7 +47,8 @@ const assetPaths = {
wasmBundle: olmJsPath wasmBundle: olmJsPath
} }
}; };
import "hydrogen-view-sdk/style.css"; import "hydrogen-view-sdk/theme-element-light.css";
// OR import "hydrogen-view-sdk/theme-element-dark.css";
async function main() { async function main() {
const app = document.querySelector<HTMLDivElement>('#app')! const app = document.querySelector<HTMLDivElement>('#app')!

View file

@ -39,7 +39,7 @@ function colorsFromURL(url, colorMap) {
function processURL(decl, replacer, colorMap) { function processURL(decl, replacer, colorMap) {
const value = decl.value; const value = decl.value;
const parsed = valueParser(value); const parsed = valueParser(value);
parsed.walk(async node => { parsed.walk(node => {
if (node.type !== "function" || node.value !== "url") { if (node.type !== "function" || node.value !== "url") {
return; return;
} }

View file

@ -37,7 +37,7 @@ module.exports.buildColorizedSVG = function (svgLocation, primaryColor, secondar
if (svgCode === coloredSVGCode) { if (svgCode === coloredSVGCode) {
throw new Error("svg-colorizer made no color replacements! The input svg should only contain colors #ff00ff (primary, case-sensitive) and #00ffff (secondary, case-sensitive)."); throw new Error("svg-colorizer made no color replacements! The input svg should only contain colors #ff00ff (primary, case-sensitive) and #00ffff (secondary, case-sensitive).");
} }
const fileName = svgLocation.match(/.+\/(.+\.svg)/)[1]; const fileName = svgLocation.match(/.+[/\\](.+\.svg)/)[1];
const outputName = `${fileName.substring(0, fileName.length - 4)}-${createHash(coloredSVGCode)}.svg`; const outputName = `${fileName.substring(0, fileName.length - 4)}-${createHash(coloredSVGCode)}.svg`;
const outputPath = path.resolve(__dirname, "../../.tmp"); const outputPath = path.resolve(__dirname, "../../.tmp");
try { try {

View file

@ -1,4 +1,8 @@
#!/bin/bash #!/bin/bash
# Exit whenever one of the commands fail with a non-zero exit code
set -e
set -o pipefail
rm -rf target rm -rf target
yarn run vite build -c vite.sdk-assets-config.js yarn run vite build -c vite.sdk-assets-config.js
yarn run vite build -c vite.sdk-lib-config.js yarn run vite build -c vite.sdk-lib-config.js

View file

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

View file

@ -128,10 +128,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();
@ -144,7 +145,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);
@ -169,6 +170,20 @@ export class Platform {
this.webRTC = new DOMWebRTC(); this.webRTC = new DOMWebRTC();
} }
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) => {

View file

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

View file

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

View file

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

View file

@ -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,16 @@ 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 {
let attributes: Attributes<T> | undefined;
if (attributes !== undefined && isChildren(attributes)) { if (attributes !== undefined && isChildren(attributes)) {
children = attributes; children = attributes;
attributes = undefined; } else {
attributes = attributesOrChildren as Attributes<T>;
} }
const node = document.createElementNS(ns, name); const node = document.createElementNS(ns, name);

View file

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

View file

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