webxdc.js

  1// Based on GPLv3 code from deltachat-android
  2// https://github.com/deltachat/deltachat-android/blob/master/res/raw/webxdc.js
  3
  4window.webxdc = (() => {
  5	let setUpdateListenerPromise = null
  6	var update_listener = () => {};
  7	var last_serial = 0;
  8
  9	window.__webxdcUpdate = () => {
 10		var updates = JSON.parse(InternalJSApi.getStatusUpdates(last_serial));
 11		updates.forEach((update) => {
 12				update_listener(update);
 13				last_serial = update.serial;
 14		});
 15		if (setUpdateListenerPromise) {
 16			setUpdateListenerPromise();
 17			setUpdateListenerPromise = null;
 18		}
 19	};
 20
 21	return {
 22		selfAddr: InternalJSApi.selfAddr(),
 23
 24		selfName: InternalJSApi.selfName(),
 25
 26		setUpdateListener: (cb, serial) => {
 27				last_serial = typeof serial === "undefined" ? 0 : parseInt(serial);
 28				update_listener = cb;
 29				var promise = new Promise((res, _rej) => {
 30					setUpdateListenerPromise = res;
 31				});
 32				window.__webxdcUpdate();
 33				return promise;
 34		},
 35
 36		sendUpdate: (payload, descr) => {
 37			InternalJSApi.sendStatusUpdate(JSON.stringify(payload), descr);
 38		},
 39
 40		importFiles: (filters) => {
 41			var element = document.createElement("input");
 42			element.type = "file";
 43			element.accept = [
 44					...(filters.extensions || []),
 45					...(filters.mimeTypes || []),
 46			].join(",");
 47			element.multiple = filters.multiple || false;
 48			const promise = new Promise((resolve, _reject) => {
 49					element.onchange = (_ev) => {
 50							const files = Array.from(element.files || []);
 51							document.body.removeChild(element);
 52							resolve(files);
 53					};
 54			});
 55			element.style.display = "none";
 56			document.body.appendChild(element);
 57			element.click();
 58			return promise;
 59		},
 60
 61		sendToChat: async (message) => {
 62			const data = {};
 63			if (!message.file && !message.text) {
 64				return Promise.reject("sendToChat() error: file or text missing");
 65			}
 66			const blobToBase64 = (file) => {
 67				const dataStart = ";base64,";
 68				return new Promise((resolve, reject) => {
 69					const reader = new FileReader();
 70					reader.readAsDataURL(file);
 71					reader.onload = () => {
 72						let data = reader.result;
 73						resolve(data.slice(data.indexOf(dataStart) + dataStart.length));
 74					};
 75					reader.onerror = () => reject(reader.error);
 76				});
 77			};
 78			if (message.text) {
 79				data.text = message.text;
 80			}
 81
 82			if (message.file) {
 83				let base64content;
 84				if (!message.file.name) {
 85					return Promise.reject("sendToChat() error: file name missing");
 86				}
 87				if (
 88					Object.keys(message.file).filter((key) =>
 89						["blob", "base64", "plainText"].includes(key)
 90					).length > 1
 91				) {
 92					return Promise.reject("sendToChat() error: only one of blob, base64 or plainText allowed");
 93				}
 94
 95				if (message.file.blob instanceof Blob) {
 96					base64content = await blobToBase64(message.file.blob);
 97				} else if (typeof message.file.base64 === "string") {
 98					base64content = message.file.base64;
 99				} else if (typeof message.file.plainText === "string") {
100					base64content = await blobToBase64(
101						new Blob([message.file.plainText])
102					);
103				} else {
104					return Promise.reject("sendToChat() error: none of blob, base64 or plainText set correctly");
105				}
106				data.base64 = base64content;
107				data.name = message.file.name;
108			}
109
110			const errorMsg = InternalJSApi.sendToChat(JSON.stringify(data));
111			if (errorMsg) {
112				return Promise.reject(errorMsg);
113			}
114		},
115
116	};
117})();