@monimesl/dot-simulator 0.1.0-alpha.14 → 0.1.0-alpha.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/service-bridge.js +109 -35
- package/dist/client/service-bridge.js.map +1 -1
- package/dist/client/simulator.js +345 -85
- package/dist/client/simulator.js.map +1 -1
- package/dist/client/webview-bridge.js.map +1 -1
- package/dist/node/index.d.ts +1 -1
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +36 -3
- package/dist/node/index.js.map +1 -1
- package/package.json +3 -3
package/dist/client/simulator.js
CHANGED
|
@@ -76,6 +76,49 @@
|
|
|
76
76
|
"warning",
|
|
77
77
|
"danger"
|
|
78
78
|
];
|
|
79
|
+
const TEXT_TONES = {
|
|
80
|
+
words: [...TONES, "onPrimary"],
|
|
81
|
+
or: "themeColor"
|
|
82
|
+
};
|
|
83
|
+
const BACKGROUNDS = {
|
|
84
|
+
words: [
|
|
85
|
+
"background",
|
|
86
|
+
"surface",
|
|
87
|
+
"primary",
|
|
88
|
+
"muted",
|
|
89
|
+
"info",
|
|
90
|
+
"success",
|
|
91
|
+
"warning",
|
|
92
|
+
"danger"
|
|
93
|
+
],
|
|
94
|
+
or: "themeColor"
|
|
95
|
+
};
|
|
96
|
+
const BORDERS = [
|
|
97
|
+
"none",
|
|
98
|
+
"subtle",
|
|
99
|
+
"strong"
|
|
100
|
+
];
|
|
101
|
+
const RADII = [
|
|
102
|
+
"none",
|
|
103
|
+
"small",
|
|
104
|
+
"medium",
|
|
105
|
+
"large",
|
|
106
|
+
"full"
|
|
107
|
+
];
|
|
108
|
+
const LENGTH = {
|
|
109
|
+
words: ["fill"],
|
|
110
|
+
or: "number"
|
|
111
|
+
};
|
|
112
|
+
const BOX_PROPS = {
|
|
113
|
+
background: BACKGROUNDS,
|
|
114
|
+
border: BORDERS,
|
|
115
|
+
radius: RADII,
|
|
116
|
+
width: LENGTH,
|
|
117
|
+
height: LENGTH,
|
|
118
|
+
minHeight: "number",
|
|
119
|
+
maxWidth: "number",
|
|
120
|
+
aspect: ASPECTS
|
|
121
|
+
};
|
|
79
122
|
const STATUS_TONES = [
|
|
80
123
|
"default",
|
|
81
124
|
"info",
|
|
@@ -462,11 +505,13 @@
|
|
|
462
505
|
name: "Text",
|
|
463
506
|
props: {
|
|
464
507
|
variant: [
|
|
508
|
+
"display",
|
|
465
509
|
"title",
|
|
510
|
+
"subtitle",
|
|
466
511
|
"body",
|
|
467
512
|
"caption"
|
|
468
513
|
],
|
|
469
|
-
tone:
|
|
514
|
+
tone: TEXT_TONES,
|
|
470
515
|
weight: [
|
|
471
516
|
"regular",
|
|
472
517
|
"medium",
|
|
@@ -478,6 +523,12 @@
|
|
|
478
523
|
"end"
|
|
479
524
|
],
|
|
480
525
|
maxLines: "number",
|
|
526
|
+
decoration: [
|
|
527
|
+
"none",
|
|
528
|
+
"underline",
|
|
529
|
+
"strikethrough"
|
|
530
|
+
],
|
|
531
|
+
numeric: "boolean",
|
|
481
532
|
...PADDING_PROPS
|
|
482
533
|
},
|
|
483
534
|
children: "text",
|
|
@@ -528,6 +579,46 @@
|
|
|
528
579
|
events: ["change"],
|
|
529
580
|
children: "elements"
|
|
530
581
|
});
|
|
582
|
+
const Radio = defineComponent({
|
|
583
|
+
name: "Radio",
|
|
584
|
+
props: {
|
|
585
|
+
value: "string",
|
|
586
|
+
label: "string",
|
|
587
|
+
disabled: "boolean"
|
|
588
|
+
},
|
|
589
|
+
children: "none"
|
|
590
|
+
});
|
|
591
|
+
const View = defineComponent({
|
|
592
|
+
name: "View",
|
|
593
|
+
props: {
|
|
594
|
+
gap: SPACES,
|
|
595
|
+
direction: ["column", "row"],
|
|
596
|
+
align: [
|
|
597
|
+
"stretch",
|
|
598
|
+
"start",
|
|
599
|
+
"center",
|
|
600
|
+
"end"
|
|
601
|
+
],
|
|
602
|
+
justify: [
|
|
603
|
+
"start",
|
|
604
|
+
"center",
|
|
605
|
+
"end",
|
|
606
|
+
"between"
|
|
607
|
+
],
|
|
608
|
+
wrap: "boolean",
|
|
609
|
+
grow: "boolean",
|
|
610
|
+
...PADDING_PROPS,
|
|
611
|
+
variant: ["plain", "card"],
|
|
612
|
+
...BOX_PROPS
|
|
613
|
+
},
|
|
614
|
+
events: ["tap"],
|
|
615
|
+
children: "elements",
|
|
616
|
+
overrides: [{
|
|
617
|
+
prop: "aspect",
|
|
618
|
+
ignores: "height",
|
|
619
|
+
because: "the aspect sets the height"
|
|
620
|
+
}]
|
|
621
|
+
});
|
|
531
622
|
const builtinComponents = {
|
|
532
623
|
AmountInput,
|
|
533
624
|
Badge,
|
|
@@ -552,52 +643,35 @@
|
|
|
552
643
|
Option,
|
|
553
644
|
ProgressBar,
|
|
554
645
|
QRCode,
|
|
555
|
-
Radio
|
|
556
|
-
name: "Radio",
|
|
557
|
-
props: {
|
|
558
|
-
value: "string",
|
|
559
|
-
label: "string",
|
|
560
|
-
disabled: "boolean"
|
|
561
|
-
},
|
|
562
|
-
children: "none"
|
|
563
|
-
}),
|
|
646
|
+
Radio,
|
|
564
647
|
RadioGroup,
|
|
565
648
|
Scroll,
|
|
566
649
|
Select,
|
|
567
650
|
Sheet,
|
|
568
651
|
Skeleton,
|
|
569
652
|
Spinner,
|
|
653
|
+
Stack: defineComponent({
|
|
654
|
+
name: "Stack",
|
|
655
|
+
props: {
|
|
656
|
+
radius: BOX_PROPS.radius,
|
|
657
|
+
width: BOX_PROPS.width,
|
|
658
|
+
height: BOX_PROPS.height,
|
|
659
|
+
aspect: BOX_PROPS.aspect
|
|
660
|
+
},
|
|
661
|
+
children: "elements",
|
|
662
|
+
overrides: [{
|
|
663
|
+
prop: "aspect",
|
|
664
|
+
ignores: "height",
|
|
665
|
+
because: "the aspect sets the height"
|
|
666
|
+
}]
|
|
667
|
+
}),
|
|
570
668
|
Switch,
|
|
571
669
|
Tab,
|
|
572
670
|
Tabs,
|
|
573
671
|
Text,
|
|
574
672
|
TextArea,
|
|
575
673
|
Toast,
|
|
576
|
-
View
|
|
577
|
-
name: "View",
|
|
578
|
-
props: {
|
|
579
|
-
gap: SPACES,
|
|
580
|
-
direction: ["column", "row"],
|
|
581
|
-
align: [
|
|
582
|
-
"stretch",
|
|
583
|
-
"start",
|
|
584
|
-
"center",
|
|
585
|
-
"end"
|
|
586
|
-
],
|
|
587
|
-
justify: [
|
|
588
|
-
"start",
|
|
589
|
-
"center",
|
|
590
|
-
"end",
|
|
591
|
-
"between"
|
|
592
|
-
],
|
|
593
|
-
wrap: "boolean",
|
|
594
|
-
grow: "boolean",
|
|
595
|
-
...PADDING_PROPS,
|
|
596
|
-
variant: ["plain", "card"]
|
|
597
|
-
},
|
|
598
|
-
events: ["tap"],
|
|
599
|
-
children: "elements"
|
|
600
|
-
})
|
|
674
|
+
View
|
|
601
675
|
};
|
|
602
676
|
componentRegistry(Object.values(builtinComponents));
|
|
603
677
|
var HostError = class extends Error {
|
|
@@ -678,7 +752,7 @@
|
|
|
678
752
|
limit;
|
|
679
753
|
#pages = [];
|
|
680
754
|
#nextInstanceId = 1;
|
|
681
|
-
constructor(limit =
|
|
755
|
+
constructor(limit = 16) {
|
|
682
756
|
this.limit = limit;
|
|
683
757
|
}
|
|
684
758
|
get pages() {
|
|
@@ -779,7 +853,8 @@
|
|
|
779
853
|
this.#succeed(id, await entry(call));
|
|
780
854
|
return;
|
|
781
855
|
}
|
|
782
|
-
|
|
856
|
+
const permission = typeof entry.permission === "function" ? entry.permission(call.params) : entry.permission;
|
|
857
|
+
await this.#permissions.check(call.method, permission, call.signal);
|
|
783
858
|
this.#succeed(id, await entry.handle(call));
|
|
784
859
|
} catch (error) {
|
|
785
860
|
this.#fail(id, error);
|
|
@@ -979,7 +1054,7 @@
|
|
|
979
1054
|
}
|
|
980
1055
|
//#endregion
|
|
981
1056
|
//#region src/versions.ts
|
|
982
|
-
const SIMULATOR_VERSIONS = { runtimeVersion: "0.1.0-alpha.
|
|
1057
|
+
const SIMULATOR_VERSIONS = { runtimeVersion: "0.1.0-alpha.16" };
|
|
983
1058
|
//#endregion
|
|
984
1059
|
//#region src/paths.ts
|
|
985
1060
|
const CLIENT_PATH = "/__dot/";
|
|
@@ -988,6 +1063,7 @@
|
|
|
988
1063
|
const FILE_PATH = `${CLIENT_PATH}storage/file/`;
|
|
989
1064
|
const CACHE_PATH = `${CLIENT_PATH}storage/cache/`;
|
|
990
1065
|
const SECRET_PATH = `${CLIENT_PATH}storage/secret/`;
|
|
1066
|
+
const MEDIA_PATH = `${CLIENT_PATH}media/`;
|
|
991
1067
|
const DOT_PATH = "/dot/";
|
|
992
1068
|
const WEBVIEW_URL = `${DOT_PATH}ui/index.html`;
|
|
993
1069
|
const MANIFEST_URL = `${DOT_PATH}manifest.json`;
|
|
@@ -1003,6 +1079,196 @@
|
|
|
1003
1079
|
if ("error" in answer) throw new HostError(answer.error.code, answer.error.message);
|
|
1004
1080
|
return answer.result;
|
|
1005
1081
|
}
|
|
1082
|
+
function tooLargeMessage(name) {
|
|
1083
|
+
return `"${name}" is larger than 25 MB`;
|
|
1084
|
+
}
|
|
1085
|
+
//#endregion
|
|
1086
|
+
//#region src/client/dom.ts
|
|
1087
|
+
function element(selector) {
|
|
1088
|
+
const node = document.querySelector(selector);
|
|
1089
|
+
if (!node) throw new Error(`The simulator page is missing ${selector}`);
|
|
1090
|
+
return node;
|
|
1091
|
+
}
|
|
1092
|
+
function createElement(tag, className, text) {
|
|
1093
|
+
const node = document.createElement(tag);
|
|
1094
|
+
node.className = className;
|
|
1095
|
+
if (text !== void 0) node.textContent = text;
|
|
1096
|
+
return node;
|
|
1097
|
+
}
|
|
1098
|
+
//#endregion
|
|
1099
|
+
//#region src/client/dash/ui.ts
|
|
1100
|
+
const DEFAULT_ACTIONS = [{
|
|
1101
|
+
id: "ok",
|
|
1102
|
+
label: "OK"
|
|
1103
|
+
}];
|
|
1104
|
+
function alert(phone, options, signal, field) {
|
|
1105
|
+
return new Promise((resolve, reject) => {
|
|
1106
|
+
const layer = createElement("div", "dialog-layer");
|
|
1107
|
+
const dialog = createElement("div", "dialog");
|
|
1108
|
+
dialog.setAttribute("role", "alertdialog");
|
|
1109
|
+
const body = createElement("div", "dialog-body");
|
|
1110
|
+
body.append(createElement("div", "dialog-title", options.title));
|
|
1111
|
+
if (options.message) body.append(createElement("div", "dialog-message", options.message));
|
|
1112
|
+
if (field) body.append(field);
|
|
1113
|
+
const actions = options.actions ?? DEFAULT_ACTIONS;
|
|
1114
|
+
const buttons = createElement("div", actions.length === 2 ? "dialog-actions" : "dialog-actions stacked");
|
|
1115
|
+
for (const action of actions) {
|
|
1116
|
+
const button = createElement("button", action.role ?? "default", action.label);
|
|
1117
|
+
button.addEventListener("click", () => {
|
|
1118
|
+
layer.remove();
|
|
1119
|
+
resolve({ action: action.id });
|
|
1120
|
+
});
|
|
1121
|
+
buttons.append(button);
|
|
1122
|
+
}
|
|
1123
|
+
dialog.append(body, buttons);
|
|
1124
|
+
layer.append(dialog);
|
|
1125
|
+
field?.addEventListener("keydown", (event) => {
|
|
1126
|
+
if (event.key === "Enter") buttons.lastElementChild?.click();
|
|
1127
|
+
});
|
|
1128
|
+
signal.addEventListener("abort", () => {
|
|
1129
|
+
layer.remove();
|
|
1130
|
+
reject(new HostError("cancelled", "The alert was dismissed"));
|
|
1131
|
+
});
|
|
1132
|
+
phone.append(layer);
|
|
1133
|
+
field?.focus();
|
|
1134
|
+
});
|
|
1135
|
+
}
|
|
1136
|
+
//#endregion
|
|
1137
|
+
//#region src/client/dash/media.ts
|
|
1138
|
+
const MAX_PHOTO_SIDE = 2048;
|
|
1139
|
+
const JPEG_QUALITY = .85;
|
|
1140
|
+
function photoPermission(params) {
|
|
1141
|
+
return photoSource(params) === "camera" ? "camera" : "photoLibrary";
|
|
1142
|
+
}
|
|
1143
|
+
async function pickPhoto(phone, note, params, signal) {
|
|
1144
|
+
const source = photoSource(params);
|
|
1145
|
+
await allowPicker(phone, source === "camera" ? "Take a photo" : "Choose a photo", signal);
|
|
1146
|
+
const file = await chooseFile("image/*", signal);
|
|
1147
|
+
const jpeg = await toJpeg(file);
|
|
1148
|
+
const name = jpegName(file.name);
|
|
1149
|
+
note(`photo (${source}): ${name}`);
|
|
1150
|
+
return keepOnDevServer("photo", {
|
|
1151
|
+
name,
|
|
1152
|
+
data: await base64(jpeg)
|
|
1153
|
+
}, signal);
|
|
1154
|
+
}
|
|
1155
|
+
async function pickFile(phone, note, params, signal) {
|
|
1156
|
+
const types = fileTypes(params);
|
|
1157
|
+
await allowPicker(phone, "Choose a file", signal);
|
|
1158
|
+
const file = await chooseFile(types.join(","), signal);
|
|
1159
|
+
if (file.size > 26214400) throw new HostError("invalid", tooLargeMessage(file.name));
|
|
1160
|
+
note(`file: ${file.name}`);
|
|
1161
|
+
const contentType = file.type || "application/octet-stream";
|
|
1162
|
+
return keepOnDevServer("file", {
|
|
1163
|
+
name: file.name,
|
|
1164
|
+
contentType,
|
|
1165
|
+
data: await base64(file)
|
|
1166
|
+
}, signal);
|
|
1167
|
+
}
|
|
1168
|
+
async function scanCode(phone, note, signal) {
|
|
1169
|
+
const field = createElement("input", "dialog-input");
|
|
1170
|
+
field.placeholder = "https://monime.io/pay/…";
|
|
1171
|
+
const { action } = await alert(phone, {
|
|
1172
|
+
title: "Scan a QR code",
|
|
1173
|
+
message: "Type or paste the text the code holds.",
|
|
1174
|
+
actions: [{
|
|
1175
|
+
id: "cancel",
|
|
1176
|
+
label: "Cancel",
|
|
1177
|
+
role: "cancel"
|
|
1178
|
+
}, {
|
|
1179
|
+
id: "scan",
|
|
1180
|
+
label: "Scan"
|
|
1181
|
+
}]
|
|
1182
|
+
}, signal, field);
|
|
1183
|
+
const text = field.value.trim();
|
|
1184
|
+
if (action !== "scan" || text === "") throw new HostError("cancelled", "No code was scanned");
|
|
1185
|
+
note(`scanned: ${text}`);
|
|
1186
|
+
return {
|
|
1187
|
+
text,
|
|
1188
|
+
format: "qr"
|
|
1189
|
+
};
|
|
1190
|
+
}
|
|
1191
|
+
function jpegName(fileName) {
|
|
1192
|
+
const dot = fileName.lastIndexOf(".");
|
|
1193
|
+
return `${(dot > 0 ? fileName.slice(0, dot) : fileName) || "photo"}.jpg`;
|
|
1194
|
+
}
|
|
1195
|
+
function photoSource(params) {
|
|
1196
|
+
const source = params?.source;
|
|
1197
|
+
if (source !== "library" && source !== "camera") throw new HostError("invalid", "\"source\" must be \"library\" or \"camera\"");
|
|
1198
|
+
return source;
|
|
1199
|
+
}
|
|
1200
|
+
function fileTypes(params) {
|
|
1201
|
+
const types = params?.types ?? [];
|
|
1202
|
+
if (!Array.isArray(types) || types.some((type) => typeof type !== "string" || !type.includes("/"))) throw new HostError("invalid", "\"types\" must list content types, such as \"application/pdf\" or \"image/*\"");
|
|
1203
|
+
return types;
|
|
1204
|
+
}
|
|
1205
|
+
async function allowPicker(phone, title, signal) {
|
|
1206
|
+
if (navigator.userActivation.isActive) return;
|
|
1207
|
+
const { action } = await alert(phone, {
|
|
1208
|
+
title,
|
|
1209
|
+
message: "The simulator picks a file from this computer.",
|
|
1210
|
+
actions: [{
|
|
1211
|
+
id: "cancel",
|
|
1212
|
+
label: "Cancel",
|
|
1213
|
+
role: "cancel"
|
|
1214
|
+
}, {
|
|
1215
|
+
id: "choose",
|
|
1216
|
+
label: "Choose"
|
|
1217
|
+
}]
|
|
1218
|
+
}, signal);
|
|
1219
|
+
if (action !== "choose") throw new HostError("cancelled", "Nothing was chosen");
|
|
1220
|
+
}
|
|
1221
|
+
function chooseFile(accept, signal) {
|
|
1222
|
+
return new Promise((resolve, reject) => {
|
|
1223
|
+
const input = document.createElement("input");
|
|
1224
|
+
input.type = "file";
|
|
1225
|
+
input.accept = accept;
|
|
1226
|
+
input.addEventListener("change", () => {
|
|
1227
|
+
const file = input.files?.[0];
|
|
1228
|
+
if (file) resolve(file);
|
|
1229
|
+
else reject(new HostError("cancelled", "Nothing was chosen"));
|
|
1230
|
+
});
|
|
1231
|
+
input.addEventListener("cancel", () => {
|
|
1232
|
+
reject(new HostError("cancelled", "Nothing was chosen"));
|
|
1233
|
+
});
|
|
1234
|
+
signal.addEventListener("abort", () => {
|
|
1235
|
+
reject(new HostError("cancelled", "The call ended before anything was chosen"));
|
|
1236
|
+
});
|
|
1237
|
+
input.click();
|
|
1238
|
+
});
|
|
1239
|
+
}
|
|
1240
|
+
async function toJpeg(file) {
|
|
1241
|
+
let image;
|
|
1242
|
+
try {
|
|
1243
|
+
image = await createImageBitmap(file, { imageOrientation: "from-image" });
|
|
1244
|
+
} catch {
|
|
1245
|
+
throw new HostError("failed", `"${file.name}" isn't a photo the simulator can read`);
|
|
1246
|
+
}
|
|
1247
|
+
const scale = Math.min(1, MAX_PHOTO_SIDE / Math.max(image.width, image.height));
|
|
1248
|
+
const canvas = new OffscreenCanvas(Math.round(image.width * scale), Math.round(image.height * scale));
|
|
1249
|
+
const context = canvas.getContext("2d");
|
|
1250
|
+
if (!context) throw new HostError("failed", "The browser couldn't draw the photo");
|
|
1251
|
+
context.drawImage(image, 0, 0, canvas.width, canvas.height);
|
|
1252
|
+
image.close();
|
|
1253
|
+
return canvas.convertToBlob({
|
|
1254
|
+
type: "image/jpeg",
|
|
1255
|
+
quality: JPEG_QUALITY
|
|
1256
|
+
});
|
|
1257
|
+
}
|
|
1258
|
+
function keepOnDevServer(kind, params, signal) {
|
|
1259
|
+
return callDevServer(`${MEDIA_PATH}${kind}`, params, signal);
|
|
1260
|
+
}
|
|
1261
|
+
function base64(blob) {
|
|
1262
|
+
return new Promise((resolve, reject) => {
|
|
1263
|
+
const reader = new FileReader();
|
|
1264
|
+
reader.addEventListener("load", () => {
|
|
1265
|
+
const url = reader.result;
|
|
1266
|
+
resolve(url.slice(url.indexOf(",") + 1));
|
|
1267
|
+
});
|
|
1268
|
+
reader.addEventListener("error", () => reject(new HostError("failed", "The file couldn't be read")));
|
|
1269
|
+
reader.readAsDataURL(blob);
|
|
1270
|
+
});
|
|
1271
|
+
}
|
|
1006
1272
|
//#endregion
|
|
1007
1273
|
//#region src/client/dash/system.ts
|
|
1008
1274
|
const SHAKE_DURATION_MS = 300;
|
|
@@ -1059,52 +1325,6 @@
|
|
|
1059
1325
|
});
|
|
1060
1326
|
}
|
|
1061
1327
|
//#endregion
|
|
1062
|
-
//#region src/client/dom.ts
|
|
1063
|
-
function element(selector) {
|
|
1064
|
-
const node = document.querySelector(selector);
|
|
1065
|
-
if (!node) throw new Error(`The simulator page is missing ${selector}`);
|
|
1066
|
-
return node;
|
|
1067
|
-
}
|
|
1068
|
-
function createElement(tag, className, text) {
|
|
1069
|
-
const node = document.createElement(tag);
|
|
1070
|
-
node.className = className;
|
|
1071
|
-
if (text !== void 0) node.textContent = text;
|
|
1072
|
-
return node;
|
|
1073
|
-
}
|
|
1074
|
-
//#endregion
|
|
1075
|
-
//#region src/client/dash/ui.ts
|
|
1076
|
-
const DEFAULT_ACTIONS = [{
|
|
1077
|
-
id: "ok",
|
|
1078
|
-
label: "OK"
|
|
1079
|
-
}];
|
|
1080
|
-
function alert(phone, options, signal) {
|
|
1081
|
-
return new Promise((resolve, reject) => {
|
|
1082
|
-
const layer = createElement("div", "dialog-layer");
|
|
1083
|
-
const dialog = createElement("div", "dialog");
|
|
1084
|
-
dialog.setAttribute("role", "alertdialog");
|
|
1085
|
-
const body = createElement("div", "dialog-body");
|
|
1086
|
-
body.append(createElement("div", "dialog-title", options.title));
|
|
1087
|
-
if (options.message) body.append(createElement("div", "dialog-message", options.message));
|
|
1088
|
-
const actions = options.actions ?? DEFAULT_ACTIONS;
|
|
1089
|
-
const buttons = createElement("div", actions.length === 2 ? "dialog-actions" : "dialog-actions stacked");
|
|
1090
|
-
for (const action of actions) {
|
|
1091
|
-
const button = createElement("button", action.role ?? "default", action.label);
|
|
1092
|
-
button.addEventListener("click", () => {
|
|
1093
|
-
layer.remove();
|
|
1094
|
-
resolve({ action: action.id });
|
|
1095
|
-
});
|
|
1096
|
-
buttons.append(button);
|
|
1097
|
-
}
|
|
1098
|
-
dialog.append(body, buttons);
|
|
1099
|
-
layer.append(dialog);
|
|
1100
|
-
signal.addEventListener("abort", () => {
|
|
1101
|
-
layer.remove();
|
|
1102
|
-
reject(new HostError("cancelled", "The alert was dismissed"));
|
|
1103
|
-
});
|
|
1104
|
-
phone.append(layer);
|
|
1105
|
-
});
|
|
1106
|
-
}
|
|
1107
|
-
//#endregion
|
|
1108
1328
|
//#region src/client/dash/index.ts
|
|
1109
1329
|
const HTTP_METHODS = [
|
|
1110
1330
|
"request",
|
|
@@ -1147,6 +1367,18 @@
|
|
|
1147
1367
|
"dash.system.share": ({ params }) => share(note, params),
|
|
1148
1368
|
"dash.system.openUrl": ({ params }) => openUrl(note, params.url),
|
|
1149
1369
|
"dash.system.delay": ({ params, signal }) => delay(params.milliseconds, signal),
|
|
1370
|
+
"dash.media.pickPhoto": {
|
|
1371
|
+
permission: photoPermission,
|
|
1372
|
+
handle: ({ params, signal }) => pickPhoto(phone, note, params, signal)
|
|
1373
|
+
},
|
|
1374
|
+
"dash.media.scanCode": {
|
|
1375
|
+
permission: "camera",
|
|
1376
|
+
handle: ({ signal }) => scanCode(phone, note, signal)
|
|
1377
|
+
},
|
|
1378
|
+
"dash.storage.file.pick": {
|
|
1379
|
+
permission: "files",
|
|
1380
|
+
handle: ({ params, signal }) => pickFile(phone, note, params, signal)
|
|
1381
|
+
},
|
|
1150
1382
|
...devServerHandlers("dash.network.http", HTTP_PATH, HTTP_METHODS),
|
|
1151
1383
|
...devServerHandlers("dash.storage.file", FILE_PATH, FILE_OPERATIONS),
|
|
1152
1384
|
...devServerHandlers("dash.storage.cache", CACHE_PATH, CACHE_OPERATIONS),
|
|
@@ -1158,7 +1390,12 @@
|
|
|
1158
1390
|
}
|
|
1159
1391
|
//#endregion
|
|
1160
1392
|
//#region src/client/dash/permissions.ts
|
|
1161
|
-
const PERMISSION_SUMMARIES = {
|
|
1393
|
+
const PERMISSION_SUMMARIES = {
|
|
1394
|
+
account: "see your Dash account: your name, and your email or phone",
|
|
1395
|
+
camera: "use your camera to take photos and scan codes",
|
|
1396
|
+
photoLibrary: "use photos you choose from your library",
|
|
1397
|
+
files: "use files you choose from your phone"
|
|
1398
|
+
};
|
|
1162
1399
|
async function askPermission(phone, note, request, signal) {
|
|
1163
1400
|
const { action } = await alert(phone, {
|
|
1164
1401
|
title: `Allow “${request.dotName}” to ${PERMISSION_SUMMARIES[request.permission]}?`,
|
|
@@ -1205,8 +1442,11 @@
|
|
|
1205
1442
|
//#endregion
|
|
1206
1443
|
//#region src/client/simulator.ts
|
|
1207
1444
|
const MAX_LOG_ENTRIES = 500;
|
|
1445
|
+
const MESSAGES_SHOWN_KEY = "dot-simulator-messages";
|
|
1208
1446
|
const screen = element("#screen");
|
|
1209
1447
|
const log = element("#log");
|
|
1448
|
+
const messagesPanel = element("#messages-panel");
|
|
1449
|
+
const messagesButton = element("#messages");
|
|
1210
1450
|
const errorBanner = element("#error");
|
|
1211
1451
|
const phone = element(".phone");
|
|
1212
1452
|
const schemeSelect = element("#scheme");
|
|
@@ -1369,14 +1609,34 @@
|
|
|
1369
1609
|
hotReload();
|
|
1370
1610
|
});
|
|
1371
1611
|
events.addEventListener("build-error", (event) => showError(String(event.data)));
|
|
1612
|
+
function showMessages(shown) {
|
|
1613
|
+
messagesPanel.hidden = !shown;
|
|
1614
|
+
messagesButton.setAttribute("aria-pressed", String(shown));
|
|
1615
|
+
}
|
|
1616
|
+
function toggleMessages() {
|
|
1617
|
+
const shown = messagesPanel.hidden;
|
|
1618
|
+
showMessages(shown);
|
|
1619
|
+
try {
|
|
1620
|
+
localStorage.setItem(MESSAGES_SHOWN_KEY, String(shown));
|
|
1621
|
+
} catch {}
|
|
1622
|
+
}
|
|
1623
|
+
function messagesShownLastTime() {
|
|
1624
|
+
try {
|
|
1625
|
+
return localStorage.getItem(MESSAGES_SHOWN_KEY) !== "false";
|
|
1626
|
+
} catch {
|
|
1627
|
+
return true;
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1372
1630
|
element("#reload").addEventListener("click", () => restart());
|
|
1373
1631
|
element("#back").addEventListener("click", goBack);
|
|
1374
1632
|
navigationBack.addEventListener("click", goBack);
|
|
1375
1633
|
element("#clear").addEventListener("click", () => log.replaceChildren());
|
|
1634
|
+
messagesButton.addEventListener("click", toggleMessages);
|
|
1376
1635
|
schemeSelect.addEventListener("change", () => changeScheme(schemeSelect.value));
|
|
1377
1636
|
browserDark.addEventListener("change", () => changeScheme(browserScheme()));
|
|
1378
1637
|
document.addEventListener("visibilitychange", syncSystemContext);
|
|
1379
1638
|
changeScheme(browserScheme());
|
|
1639
|
+
showMessages(messagesShownLastTime());
|
|
1380
1640
|
restart();
|
|
1381
1641
|
//#endregion
|
|
1382
1642
|
})();
|