signalk-p2p-plugin 0.3.0 → 0.4.1
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/CHANGELOG.md +26 -0
- package/README.md +66 -14
- package/deploy/README.md +24 -11
- package/deploy/policy.json +30 -0
- package/dist/advanced-config.d.ts +11 -0
- package/dist/advanced-config.d.ts.map +1 -0
- package/dist/advanced-config.js +40 -0
- package/dist/advanced-config.js.map +1 -0
- package/dist/api.d.ts +14 -1
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +41 -3
- package/dist/api.js.map +1 -1
- package/dist/config.d.ts +5 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +12 -273
- package/dist/config.js.map +1 -1
- package/dist/data/envelope.d.ts.map +1 -1
- package/dist/data/envelope.js +2 -0
- package/dist/data/envelope.js.map +1 -1
- package/dist/data/types.d.ts +1 -1
- package/dist/data/types.d.ts.map +1 -1
- package/dist/groups/control.d.ts +8 -0
- package/dist/groups/control.d.ts.map +1 -1
- package/dist/groups/control.js +30 -0
- package/dist/groups/control.js.map +1 -1
- package/dist/groups/manager.d.ts +2 -0
- package/dist/groups/manager.d.ts.map +1 -1
- package/dist/groups/manager.js +29 -2
- package/dist/groups/manager.js.map +1 -1
- package/dist/groups/types.d.ts +12 -0
- package/dist/groups/types.d.ts.map +1 -1
- package/dist/groups/types.js +23 -0
- package/dist/groups/types.js.map +1 -1
- package/dist/live/relay.d.ts +3 -0
- package/dist/live/relay.d.ts.map +1 -1
- package/dist/live/relay.js +11 -0
- package/dist/live/relay.js.map +1 -1
- package/dist/peer/cli.js +16 -0
- package/dist/peer/cli.js.map +1 -1
- package/dist/peer/config.d.ts +3 -0
- package/dist/peer/config.d.ts.map +1 -1
- package/dist/peer/config.js +5 -0
- package/dist/peer/config.js.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +136 -46
- package/dist/plugin.js.map +1 -1
- package/dist/policy/operator.d.ts +13 -0
- package/dist/policy/operator.d.ts.map +1 -0
- package/dist/policy/operator.js +53 -0
- package/dist/policy/operator.js.map +1 -0
- package/dist/policy/policy.d.ts +49 -0
- package/dist/policy/policy.d.ts.map +1 -0
- package/dist/policy/policy.js +147 -0
- package/dist/policy/policy.js.map +1 -0
- package/docs/plans/README.md +2 -2
- package/docs/plans/architecture.md +2 -2
- package/docs/plans/networks.md +36 -8
- package/docs/plans/peer-node.md +7 -2
- package/docs/plans/security.md +1 -1
- package/docs/plans/shared-data.md +4 -3
- package/package.json +3 -2
- package/policy/default.json +30 -0
- package/policy/operator.pub +2 -0
- package/public/index.html +299 -83
package/public/index.html
CHANGED
|
@@ -40,6 +40,32 @@
|
|
|
40
40
|
.network-card input[type="checkbox"] {
|
|
41
41
|
accent-color: #0fb9e9;
|
|
42
42
|
}
|
|
43
|
+
.sharing-wrap {
|
|
44
|
+
overflow-x: auto;
|
|
45
|
+
margin-top: 0.5rem;
|
|
46
|
+
}
|
|
47
|
+
.sharing-table {
|
|
48
|
+
width: 100%;
|
|
49
|
+
border-collapse: collapse;
|
|
50
|
+
}
|
|
51
|
+
.sharing-table th,
|
|
52
|
+
.sharing-table td {
|
|
53
|
+
padding: 0.4rem 0.5rem;
|
|
54
|
+
border-bottom: 1px solid #172a45;
|
|
55
|
+
text-align: left;
|
|
56
|
+
vertical-align: top;
|
|
57
|
+
}
|
|
58
|
+
.sharing-table th {
|
|
59
|
+
color: #9fb3c8;
|
|
60
|
+
font-size: 0.8rem;
|
|
61
|
+
text-transform: uppercase;
|
|
62
|
+
letter-spacing: 0.04em;
|
|
63
|
+
white-space: nowrap;
|
|
64
|
+
}
|
|
65
|
+
.sharing-table td small {
|
|
66
|
+
color: #9fb3c8;
|
|
67
|
+
display: block;
|
|
68
|
+
}
|
|
43
69
|
section {
|
|
44
70
|
margin: 1.25rem 0;
|
|
45
71
|
}
|
|
@@ -75,6 +101,25 @@
|
|
|
75
101
|
button:hover {
|
|
76
102
|
background: #7dd3fc;
|
|
77
103
|
}
|
|
104
|
+
details {
|
|
105
|
+
margin: 1rem 0;
|
|
106
|
+
}
|
|
107
|
+
summary {
|
|
108
|
+
cursor: pointer;
|
|
109
|
+
color: #9fb3c8;
|
|
110
|
+
font-weight: 600;
|
|
111
|
+
}
|
|
112
|
+
input[type="number"],
|
|
113
|
+
select {
|
|
114
|
+
background: #101f38;
|
|
115
|
+
color: #e8eef8;
|
|
116
|
+
border: 1px solid #27405f;
|
|
117
|
+
border-radius: 0.35rem;
|
|
118
|
+
padding: 0.2rem 0.35rem;
|
|
119
|
+
}
|
|
120
|
+
input[type="number"] {
|
|
121
|
+
width: 5.5rem;
|
|
122
|
+
}
|
|
78
123
|
textarea {
|
|
79
124
|
width: 100%;
|
|
80
125
|
min-height: 4rem;
|
|
@@ -103,15 +148,18 @@
|
|
|
103
148
|
<h1>Signal K P2P</h1>
|
|
104
149
|
<div id="message"></div>
|
|
105
150
|
|
|
106
|
-
<
|
|
107
|
-
<
|
|
108
|
-
<
|
|
109
|
-
|
|
151
|
+
<details>
|
|
152
|
+
<summary>Advanced</summary>
|
|
153
|
+
<section>
|
|
154
|
+
<h2>Identity</h2>
|
|
155
|
+
<div id="identity">loading…</div>
|
|
156
|
+
</section>
|
|
110
157
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
158
|
+
<section>
|
|
159
|
+
<h2>Claims</h2>
|
|
160
|
+
<div id="claims"></div>
|
|
161
|
+
</section>
|
|
162
|
+
</details>
|
|
115
163
|
|
|
116
164
|
<section>
|
|
117
165
|
<h2>Groups & networks</h2>
|
|
@@ -120,9 +168,7 @@
|
|
|
120
168
|
<div id="groups"></div>
|
|
121
169
|
<div class="actions">
|
|
122
170
|
<button onclick="createGroup()">Create group</button>
|
|
123
|
-
<button onclick="invite()">Create invite</button>
|
|
124
171
|
<button onclick="joinGroup()">Join with token</button>
|
|
125
|
-
<button onclick="setThreshold()">Set threshold</button>
|
|
126
172
|
</div>
|
|
127
173
|
<textarea
|
|
128
174
|
id="token"
|
|
@@ -130,12 +176,12 @@
|
|
|
130
176
|
></textarea>
|
|
131
177
|
</section>
|
|
132
178
|
|
|
133
|
-
<section>
|
|
179
|
+
<section id="requests-section">
|
|
134
180
|
<h2>Join requests</h2>
|
|
135
181
|
<div id="requests"></div>
|
|
136
182
|
</section>
|
|
137
183
|
|
|
138
|
-
<section>
|
|
184
|
+
<section id="proposals-section">
|
|
139
185
|
<h2>Pending proposals</h2>
|
|
140
186
|
<div id="proposals"></div>
|
|
141
187
|
</section>
|
|
@@ -212,7 +258,13 @@
|
|
|
212
258
|
call("/sharing"),
|
|
213
259
|
]);
|
|
214
260
|
renderIdentity(results[0], results[1]);
|
|
215
|
-
|
|
261
|
+
renderSharing(
|
|
262
|
+
results[0],
|
|
263
|
+
results[6],
|
|
264
|
+
(results[3].groups || []).filter(function (group) {
|
|
265
|
+
return !group.removed;
|
|
266
|
+
}),
|
|
267
|
+
);
|
|
216
268
|
renderClaims(results[2].claims || []);
|
|
217
269
|
renderGroups(results[3].groups || []);
|
|
218
270
|
renderRequests(results[4].requests || []);
|
|
@@ -244,6 +296,27 @@
|
|
|
244
296
|
);
|
|
245
297
|
root.appendChild(text("h3", "Status"));
|
|
246
298
|
root.appendChild(identityRow("Version", status.version));
|
|
299
|
+
if (status.policy) {
|
|
300
|
+
root.appendChild(
|
|
301
|
+
identityRow(
|
|
302
|
+
"Network policy",
|
|
303
|
+
"v" +
|
|
304
|
+
status.policy.version +
|
|
305
|
+
" · issued " +
|
|
306
|
+
new Date(status.policy.issuedAt * 1000)
|
|
307
|
+
.toISOString()
|
|
308
|
+
.slice(0, 10),
|
|
309
|
+
),
|
|
310
|
+
);
|
|
311
|
+
if (status.policy.operator) {
|
|
312
|
+
root.appendChild(
|
|
313
|
+
identityRow(
|
|
314
|
+
"Policy operator",
|
|
315
|
+
status.policy.operator.slice(0, 16) + "…",
|
|
316
|
+
),
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
247
320
|
root.appendChild(identityRow("Started", status.started ? "yes" : "no"));
|
|
248
321
|
root.appendChild(identityRow("Peers", status.peers));
|
|
249
322
|
root.appendChild(identityRow("Injected vessels", status.injected));
|
|
@@ -278,13 +351,28 @@
|
|
|
278
351
|
}
|
|
279
352
|
}
|
|
280
353
|
|
|
281
|
-
function
|
|
354
|
+
function sharingCheckbox(checked, onchange, disabled) {
|
|
355
|
+
var cell = document.createElement("td");
|
|
356
|
+
var input = document.createElement("input");
|
|
357
|
+
input.type = "checkbox";
|
|
358
|
+
input.checked = !!checked;
|
|
359
|
+
input.disabled = Boolean(disabled);
|
|
360
|
+
input.onchange = function () {
|
|
361
|
+
onchange(input.checked);
|
|
362
|
+
};
|
|
363
|
+
cell.appendChild(input);
|
|
364
|
+
return cell;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function renderSharing(status, sharing, groups) {
|
|
282
368
|
clear("public-network");
|
|
283
369
|
var root = document.getElementById("public-network");
|
|
284
370
|
root.className = "network-card";
|
|
285
371
|
status = status || {};
|
|
286
372
|
sharing = sharing || {};
|
|
373
|
+
groups = groups || [];
|
|
287
374
|
var network = sharing.network;
|
|
375
|
+
var publicEnabled = Boolean(network && network.enabled);
|
|
288
376
|
root.appendChild(
|
|
289
377
|
identityRow(
|
|
290
378
|
"Public network",
|
|
@@ -293,52 +381,121 @@
|
|
|
293
381
|
);
|
|
294
382
|
if (network) {
|
|
295
383
|
root.appendChild(
|
|
296
|
-
identityRow(
|
|
297
|
-
"Status",
|
|
298
|
-
(network.enabled ? "enabled" : "disabled") +
|
|
299
|
-
" · " +
|
|
300
|
-
(network.consent || "opt-out"),
|
|
301
|
-
),
|
|
384
|
+
identityRow("Status", network.enabled ? "enabled" : "disabled"),
|
|
302
385
|
);
|
|
386
|
+
var consentRow = document.createElement("div");
|
|
387
|
+
consentRow.className = "row";
|
|
388
|
+
consentRow.appendChild(text("code", "Sharing"));
|
|
389
|
+
var consent = document.createElement("select");
|
|
390
|
+
[
|
|
391
|
+
["receive-only", "Receive only (don't share my vessel)"],
|
|
392
|
+
["underway-gated", "Share only while underway"],
|
|
393
|
+
["coarse-position", "Share an approximate position"],
|
|
394
|
+
["opt-out", "Share my vessel"],
|
|
395
|
+
].forEach(function (entry) {
|
|
396
|
+
var option = document.createElement("option");
|
|
397
|
+
option.value = entry[0];
|
|
398
|
+
option.textContent = entry[1];
|
|
399
|
+
consent.appendChild(option);
|
|
400
|
+
});
|
|
401
|
+
consent.value = network.consent || "receive-only";
|
|
402
|
+
consent.onchange = function () {
|
|
403
|
+
setConsent(consent.value);
|
|
404
|
+
};
|
|
405
|
+
consentRow.appendChild(consent);
|
|
406
|
+
root.appendChild(consentRow);
|
|
303
407
|
}
|
|
304
408
|
root.appendChild(identityRow("Peers", status.peers));
|
|
305
|
-
|
|
409
|
+
|
|
410
|
+
var wrap = document.createElement("div");
|
|
411
|
+
wrap.className = "sharing-wrap";
|
|
412
|
+
var table = document.createElement("table");
|
|
413
|
+
table.className = "sharing-table";
|
|
414
|
+
|
|
415
|
+
var head = document.createElement("tr");
|
|
416
|
+
head.appendChild(text("th", "Data set"));
|
|
417
|
+
head.appendChild(text("th", "Public"));
|
|
418
|
+
groups.forEach(function (group) {
|
|
419
|
+
var th = text("th", group.name || group.id.slice(0, 8));
|
|
420
|
+
th.title = group.id;
|
|
421
|
+
head.appendChild(th);
|
|
422
|
+
});
|
|
423
|
+
table.appendChild(head);
|
|
424
|
+
|
|
425
|
+
(sharing.dataSets || []).forEach(function (set) {
|
|
426
|
+
var row = document.createElement("tr");
|
|
427
|
+
var name = document.createElement("td");
|
|
428
|
+
name.appendChild(text("code", set.title));
|
|
429
|
+
name.appendChild(text("small", set.description));
|
|
430
|
+
row.appendChild(name);
|
|
431
|
+
row.appendChild(
|
|
432
|
+
sharingCheckbox(
|
|
433
|
+
set.public,
|
|
434
|
+
function (checked) {
|
|
435
|
+
setSharing(set.id, { public: checked });
|
|
436
|
+
},
|
|
437
|
+
!publicEnabled,
|
|
438
|
+
),
|
|
439
|
+
);
|
|
440
|
+
groups.forEach(function (group) {
|
|
441
|
+
var checked =
|
|
442
|
+
set.groups === "all" ||
|
|
443
|
+
(Array.isArray(set.groups) && set.groups.indexOf(group.id) >= 0);
|
|
444
|
+
row.appendChild(
|
|
445
|
+
sharingCheckbox(checked, function (value) {
|
|
446
|
+
var current =
|
|
447
|
+
set.groups === "all"
|
|
448
|
+
? groups.map(function (entry) {
|
|
449
|
+
return entry.id;
|
|
450
|
+
})
|
|
451
|
+
: set.groups.slice();
|
|
452
|
+
if (value && current.indexOf(group.id) < 0) {
|
|
453
|
+
current.push(group.id);
|
|
454
|
+
}
|
|
455
|
+
if (!value) {
|
|
456
|
+
current = current.filter(function (id) {
|
|
457
|
+
return id !== group.id;
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
setSharing(set.id, { groups: current });
|
|
461
|
+
}),
|
|
462
|
+
);
|
|
463
|
+
});
|
|
464
|
+
table.appendChild(row);
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
wrap.appendChild(table);
|
|
468
|
+
root.appendChild(wrap);
|
|
469
|
+
if (!groups.length) {
|
|
306
470
|
root.appendChild(
|
|
307
|
-
|
|
308
|
-
"
|
|
309
|
-
|
|
310
|
-
" (capacity " +
|
|
311
|
-
status.electedPeer.capacity +
|
|
312
|
-
")",
|
|
471
|
+
text(
|
|
472
|
+
"p",
|
|
473
|
+
"No private groups yet. Create or join one to add group columns.",
|
|
313
474
|
),
|
|
314
475
|
);
|
|
315
476
|
}
|
|
316
|
-
(sharing.dataSets || []).forEach(function (set) {
|
|
317
|
-
var label = document.createElement("label");
|
|
318
|
-
label.className = "row";
|
|
319
|
-
var input = document.createElement("input");
|
|
320
|
-
input.type = "checkbox";
|
|
321
|
-
input.checked = !!set.public;
|
|
322
|
-
input.disabled = !(network && network.enabled);
|
|
323
|
-
input.onchange = function () {
|
|
324
|
-
setPublic(set.id, input.checked);
|
|
325
|
-
};
|
|
326
|
-
label.appendChild(input);
|
|
327
|
-
label.appendChild(text("code", set.title));
|
|
328
|
-
label.appendChild(text("span", set.description));
|
|
329
|
-
root.appendChild(label);
|
|
330
|
-
});
|
|
331
477
|
root.appendChild(
|
|
332
478
|
text(
|
|
333
479
|
"p",
|
|
334
|
-
"
|
|
480
|
+
"Check a group column to share that data set with that group. Public membership is open. Changes apply immediately.",
|
|
335
481
|
),
|
|
336
482
|
);
|
|
337
483
|
}
|
|
338
484
|
|
|
339
|
-
async function
|
|
485
|
+
async function setConsent(consent) {
|
|
486
|
+
try {
|
|
487
|
+
await call("/network/consent", { consent: consent });
|
|
488
|
+
say("", false);
|
|
489
|
+
return refresh();
|
|
490
|
+
} catch (error) {
|
|
491
|
+
say(String(error), true);
|
|
492
|
+
return refresh();
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
async function setSharing(dataSet, update) {
|
|
340
497
|
try {
|
|
341
|
-
await call("/sharing", { dataSet: dataSet,
|
|
498
|
+
await call("/sharing", Object.assign({ dataSet: dataSet }, update));
|
|
342
499
|
say("", false);
|
|
343
500
|
return refresh();
|
|
344
501
|
} catch (error) {
|
|
@@ -404,8 +561,6 @@
|
|
|
404
561
|
text(
|
|
405
562
|
"span",
|
|
406
563
|
group.role +
|
|
407
|
-
" · epoch " +
|
|
408
|
-
group.epoch +
|
|
409
564
|
" · " +
|
|
410
565
|
group.members +
|
|
411
566
|
" member(s)" +
|
|
@@ -415,22 +570,29 @@
|
|
|
415
570
|
: ""),
|
|
416
571
|
),
|
|
417
572
|
);
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
"
|
|
421
|
-
|
|
422
|
-
return removeMember(group.id);
|
|
423
|
-
}),
|
|
424
|
-
),
|
|
425
|
-
);
|
|
426
|
-
row.appendChild(
|
|
427
|
-
button(
|
|
428
|
-
"Promote",
|
|
429
|
-
action(function () {
|
|
430
|
-
return promoteMember(group.id);
|
|
573
|
+
if (group.role === "owner" || group.role === "admin") {
|
|
574
|
+
row.appendChild(
|
|
575
|
+
button("Invite", function () {
|
|
576
|
+
createInvite(group.id);
|
|
431
577
|
}),
|
|
432
|
-
)
|
|
433
|
-
|
|
578
|
+
);
|
|
579
|
+
row.appendChild(
|
|
580
|
+
button(
|
|
581
|
+
"Remove",
|
|
582
|
+
action(function () {
|
|
583
|
+
return removeMember(group.id);
|
|
584
|
+
}),
|
|
585
|
+
),
|
|
586
|
+
);
|
|
587
|
+
row.appendChild(
|
|
588
|
+
button(
|
|
589
|
+
"Promote",
|
|
590
|
+
action(function () {
|
|
591
|
+
return promoteMember(group.id);
|
|
592
|
+
}),
|
|
593
|
+
),
|
|
594
|
+
);
|
|
595
|
+
}
|
|
434
596
|
row.appendChild(
|
|
435
597
|
button(
|
|
436
598
|
"Leave",
|
|
@@ -440,10 +602,47 @@
|
|
|
440
602
|
),
|
|
441
603
|
);
|
|
442
604
|
root.appendChild(row);
|
|
605
|
+
|
|
606
|
+
if (!group.removed && group.role !== "viewer") {
|
|
607
|
+
var rateRow = document.createElement("div");
|
|
608
|
+
rateRow.className = "row";
|
|
609
|
+
rateRow.appendChild(text("code", "Publish rate"));
|
|
610
|
+
var input = document.createElement("input");
|
|
611
|
+
input.type = "number";
|
|
612
|
+
input.min = "0.01";
|
|
613
|
+
input.max = String(group.rateCapHz);
|
|
614
|
+
input.step = "0.01";
|
|
615
|
+
input.value = String(group.publishRateHz);
|
|
616
|
+
rateRow.appendChild(input);
|
|
617
|
+
rateRow.appendChild(
|
|
618
|
+
text("span", "/ " + group.rateCapHz + " Hz maximum"),
|
|
619
|
+
);
|
|
620
|
+
rateRow.appendChild(
|
|
621
|
+
button(
|
|
622
|
+
"Set",
|
|
623
|
+
action(function () {
|
|
624
|
+
return setGroupRate(group.id, Number(input.value));
|
|
625
|
+
}),
|
|
626
|
+
),
|
|
627
|
+
);
|
|
628
|
+
if (group.role === "owner" || group.role === "admin") {
|
|
629
|
+
rateRow.appendChild(
|
|
630
|
+
button(
|
|
631
|
+
"Set cap",
|
|
632
|
+
action(function () {
|
|
633
|
+
return setGroupRateCap(group.id);
|
|
634
|
+
}),
|
|
635
|
+
),
|
|
636
|
+
);
|
|
637
|
+
}
|
|
638
|
+
root.appendChild(rateRow);
|
|
639
|
+
}
|
|
443
640
|
});
|
|
444
641
|
}
|
|
445
642
|
|
|
446
643
|
function renderRequests(requests) {
|
|
644
|
+
document.getElementById("requests-section").style.display =
|
|
645
|
+
requests.length ? "" : "none";
|
|
447
646
|
clear("requests");
|
|
448
647
|
var root = document.getElementById("requests");
|
|
449
648
|
if (!requests.length) {
|
|
@@ -477,6 +676,8 @@
|
|
|
477
676
|
}
|
|
478
677
|
|
|
479
678
|
function renderProposals(proposals) {
|
|
679
|
+
document.getElementById("proposals-section").style.display =
|
|
680
|
+
proposals.length ? "" : "none";
|
|
480
681
|
clear("proposals");
|
|
481
682
|
var root = document.getElementById("proposals");
|
|
482
683
|
if (!proposals.length) {
|
|
@@ -538,26 +739,38 @@
|
|
|
538
739
|
await call("/groups", { name: name });
|
|
539
740
|
}
|
|
540
741
|
|
|
541
|
-
async function
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
742
|
+
async function createInvite(groupId) {
|
|
743
|
+
try {
|
|
744
|
+
var result = await call("/groups/invite", { groupId: groupId });
|
|
745
|
+
var token = result.token || "";
|
|
746
|
+
var field = document.getElementById("token");
|
|
747
|
+
field.value = token;
|
|
748
|
+
field.focus();
|
|
749
|
+
field.select();
|
|
750
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
751
|
+
try {
|
|
752
|
+
await navigator.clipboard.writeText(token);
|
|
753
|
+
say("Invite token copied. Send it to the person joining.", false);
|
|
754
|
+
return;
|
|
755
|
+
} catch (error) {}
|
|
756
|
+
}
|
|
757
|
+
say("Invite token created. Copy it from the box below.", false);
|
|
758
|
+
} catch (error) {
|
|
759
|
+
say(String(error), true);
|
|
760
|
+
}
|
|
553
761
|
}
|
|
554
762
|
|
|
555
763
|
async function joinGroup() {
|
|
556
764
|
var token =
|
|
557
|
-
prompt("
|
|
765
|
+
prompt("Paste the group invite token") ||
|
|
766
|
+
document.getElementById("token").value;
|
|
558
767
|
if (!token) return;
|
|
559
|
-
|
|
560
|
-
|
|
768
|
+
try {
|
|
769
|
+
await call("/groups/join", { token: token });
|
|
770
|
+
say("Join request sent. A group admin must approve it.", false);
|
|
771
|
+
} catch (error) {
|
|
772
|
+
say(String(error), true);
|
|
773
|
+
}
|
|
561
774
|
}
|
|
562
775
|
|
|
563
776
|
async function approveRequest(entry) {
|
|
@@ -600,13 +813,16 @@
|
|
|
600
813
|
await call("/groups/leave", { groupId: groupId });
|
|
601
814
|
}
|
|
602
815
|
|
|
603
|
-
async function
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
816
|
+
async function setGroupRate(groupId, rateHz) {
|
|
817
|
+
await call("/groups/rate", { groupId: groupId, rateHz: rateHz });
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
async function setGroupRateCap(groupId) {
|
|
821
|
+
var value = prompt("Group publish rate cap (Hz)", "1");
|
|
822
|
+
if (value === null) return;
|
|
823
|
+
await call("/groups/rate-cap", {
|
|
608
824
|
groupId: groupId,
|
|
609
|
-
|
|
825
|
+
rateHz: Number(value),
|
|
610
826
|
});
|
|
611
827
|
}
|
|
612
828
|
|