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