@yz-social/civildefense.io 4.5.22 → 4.5.28

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.
@@ -254,6 +254,7 @@ button, .leaflet-control-zoom > a, .leaflet-popup-close-button, md-outlined-icon
254
254
  .alert-marker {
255
255
  text-shadow: 8px -3px 10px rgb(0 0 0 / 60%);
256
256
  animation: pulse ease 0.8s 1;
257
+ transition: transform 0.8s, opacity 0.4s;
257
258
  }
258
259
  .alert-commented {
259
260
  display: none;
@@ -271,6 +272,30 @@ button, .leaflet-control-zoom > a, .leaflet-popup-close-button, md-outlined-icon
271
272
  left: 5px;
272
273
  text-align: center;
273
274
  }
275
+ .aggregate.alert-pin {
276
+ font-size: 40px;
277
+ line-height: 40px;
278
+ top: 0px;
279
+ left: 0px;
280
+ transition: font-size 0.8s;
281
+ }
282
+ .aggregate.alert-pin:before {
283
+ content: "";
284
+ width: 50px;
285
+ height: 50px;
286
+ position: absolute;
287
+ top: -5px;
288
+ left: -5px;
289
+ background-image: radial-gradient(transparent 30%, var(--md-sys-color-primary), transparent 70%);
290
+ transition: opacity 0.8s;
291
+ animation: pulse ease 0.8s 1;
292
+ }
293
+ .aggregate.alert-pin.starting {
294
+ font-size: 0px;
295
+ }
296
+ .aggregate.alert-pin.starting:after {
297
+ opacity: 0;
298
+ }
274
299
  @keyframes pulse {
275
300
  0% { filter: saturate(1); }
276
301
  25% { filter: saturate(4); }
@@ -409,9 +434,93 @@ md-menu {
409
434
  display: flex;
410
435
  align-items: center;
411
436
  }
437
+
438
+ .teach {
439
+ position: absolute;
440
+ z-index: 1000;
441
+ background: linear-gradient(color-mix(in srgb, var(--md-sys-color-primary), white 40%), var(--md-sys-color-primary));
442
+ color: white;
443
+ padding: 10px;
444
+ border-radius: 12px;
445
+ }
446
+ .changeHashtag {
447
+ anchor-name: --changeHashtag;
448
+ }
449
+ .correspondent.avatar {
450
+ anchor-name: --correspondentAvatar;
451
+ }
452
+ .attribution > .share {
453
+ anchor-name: --share;
454
+ }
455
+ .reply-input [slot="leading-icon"] {
456
+ anchor-name: --attach;
457
+ }
458
+ md-filter-chip[label="🆘 help"], md-filter-chip[label="🆘 ayuda"] {
459
+ anchor-name: --topicToggle;
460
+ }
461
+ .leaflet-popup-content-wrapper {
462
+ anchor-name: --popup;
463
+ }
464
+ body.firstPublish.firstConversation .firstConversation .correspondent,
465
+ body:not(.firstPublish) .firstPublish,
466
+ body:not(.firstConversation) .firstConversation {
467
+ display: none;
468
+ }
469
+ .teach.share {
470
+ position-anchor: --share;
471
+ bottom: calc(anchor(top) + 60px);
472
+ left: anchor(--popup left);
473
+ max-width: min(45vw, 310px);
474
+ }
475
+ .teach.correspondent {
476
+ position-anchor: --correspondentAvatar;
477
+ bottom: calc(anchor(top) + 15px);
478
+ left: anchor(left);
479
+ }
480
+ .teach.changeHashtag {
481
+ position-anchor: --changeHashtag;
482
+ bottom: calc(anchor(top) + 60px);
483
+ right: anchor(--popup right);
484
+ width: min(45vw, 310px);
485
+ }
486
+ .teach.attach {
487
+ position-anchor: --attach;
488
+ top: calc(anchor(bottom) + 15px);
489
+ left: anchor(--popup left);
490
+ background: linear-gradient(var(--md-sys-color-primary), color-mix(in srgb, var(--md-sys-color-primary), white 40%));
491
+ }
492
+ .teach:after {
493
+ content: "";
494
+ position: absolute;
495
+ display: block;
496
+ width: 0;
497
+ border-style: solid;
498
+ border-color: var(--md-sys-color-primary) transparent;
499
+ }
500
+ .teach.share:after {
501
+ bottom: -60px;
502
+ left: 30px;
503
+ border-width: 60px 15px 0;
504
+ }
505
+ .teach.correspondent:after {
506
+ bottom: -13px;
507
+ left: 6px;
508
+ border-width: 15px 15px 0;
509
+ }
510
+ .teach.changeHashtag:after {
511
+ bottom: -60px;
512
+ right: 45px;
513
+ border-width: 60px 15px 0;
514
+ }
515
+ .teach.attach:after {
516
+ top: -15px;
517
+ left: 32px;
518
+ border-width: 0 15px 15px;
519
+ }
412
520
  md-outlined-button {
413
521
  padding: 10px;
414
522
  }
523
+
415
524
  /* .attribution > div:last-child > span { */
416
525
  /* text-wrap: nowrap; */
417
526
  /* } */
package/server/app.js CHANGED
@@ -14,18 +14,33 @@ const argv = yargs(hideBin(process.argv))
14
14
  .option('nPortals', {
15
15
  alias: 'p',
16
16
  type: 'number',
17
- default: Math.max(2, logicalCores - 3),
17
+ default: process.env.PORTALS ? parseInt(process.env.PORTALS) : Math.max(2, logicalCores - 3),
18
18
  description: "The number of steady nodes that handle initial connections."
19
19
  })
20
+ .option('regionStartIndex', {
21
+ type: 'number',
22
+ default: -1,
23
+ description: "If non-negative, place each of the nPortals in successive regions (excluding excludedRegionFromSeries) starting with the region at regionStaartIndex."
24
+ })
25
+ .option('excludedRegionFromSeries', {
26
+ type: 'string', array: true,
27
+ default: ['80', '88', '89'],
28
+ description: "If regionStartIndex is used, the ordered list of regions to occupy is the list of canonical regions less these."
29
+ })
20
30
  .option('baseURL', {
21
31
  type: 'string',
22
- default: 'http://localhost:3000',
32
+ default: process.env.BRIDGE_URL || 'wss://bridge.axona.net',
23
33
  description: "The base URL of the portal server through which to bootstrap."
24
34
  })
25
- .option('externalBaseURL', {
26
- type: 'string',
27
- default: '',
28
- description: "The base URL of the some other portal server to which we should connect ours, if any."
35
+ // .option('externalBaseURL', {
36
+ // type: 'string',
37
+ // default: '',
38
+ // description: "The base URL of the some other portal server to which we should connect ours, if any."
39
+ // })
40
+ .option('port', {
41
+ type: 'number',
42
+ default: 3000,
43
+ description: "Port for the server to listen on."
29
44
  })
30
45
  .option('announce', {
31
46
  type: 'boolean',
@@ -61,7 +76,6 @@ if (cluster.isPrimary) { // Parent process with portal webserver through which c
61
76
  const logger = (await import('morgan')).default;
62
77
  const { configureWebsocket } = await import('./websocket.js');
63
78
 
64
- const port = parseInt((new URL(argv.baseURL)).port || '80');
65
79
  process.title = 'yz.social';
66
80
  const app = express();
67
81
  app.use(logger(':date[iso] :status :method :url :res[content-length] - :response-time ms'));
@@ -101,8 +115,8 @@ if (cluster.isPrimary) { // Parent process with portal webserver through which c
101
115
  extensions: ['js'] // Some dependencies refer to .js files as relative pathnames, with the .js missing.
102
116
  }));
103
117
 
104
- server.listen(port);
105
- log(`Listening on ${port} and starting ${argv.nPortals} nodes on ${logicalCores} ${cpus()[0].model} logical cores.`);
118
+ server.listen(argv.port);
119
+ log(`Listening on ${argv.port} and starting ${argv.nPortals} nodes on ${logicalCores} ${cpus()[0].model} logical cores.`);
106
120
  for (let i = 0; i < argv.nPortals; i++) {
107
121
  cluster.fork();
108
122
  await delay(); // Number chosen to give an unspikey rise in packets/s.
@@ -118,8 +132,14 @@ if (cluster.isPrimary) { // Parent process with portal webserver through which c
118
132
  } else {
119
133
  process.title = 'axona-starting';
120
134
  const { P2PWebNetwork, location } = await import('../index.js');
135
+ const { MAJORS, geoCellCenter } = await import('@axona/protocol');
136
+ const excluded = argv.excludedRegionFromSeries.map(hex => parseInt(hex, 16).toString());
137
+ const regions = Object.keys(MAJORS).filter(r => !excluded.includes(r));
138
+ const index = (cluster.worker.id - 1 + argv.regionStartIndex) % regions.length;
139
+ const region = (argv.regionStartIndex >= 0) && regions[index];
140
+ const loc = region ? geoCellCenter(parseInt(region)) : location;
121
141
  const network = await P2PWebNetwork.create({
122
- location,
142
+ location: loc,
123
143
  infoLogger: log,
124
144
  debugLogger: debug
125
145
  });
@@ -1,4 +1,4 @@
1
- {
1
+ {
2
2
  "uswest/80": "37,-122",
3
3
  "useast/89": "40,-75",
4
4
  "uscentle/88": "35,-83",
@@ -5,7 +5,7 @@ import * as operator from '../public/javascripts/pubsub.js';
5
5
  const sockets = {};
6
6
  operator.setReceiver((nodeId, id, envelope) => {
7
7
  const socket = sockets[nodeId];
8
- socket.send(JSON.stringify([id, envelope]));
8
+ socket?.send(JSON.stringify([id, envelope]));
9
9
  });
10
10
 
11
11
  function heartbeat() {
@@ -22,7 +22,7 @@ export function configureWebsocket(server) {
22
22
  ws.on('message', async message => {
23
23
  const [id, methodName, ...rest] = JSON.parse(message);
24
24
  const result = await operator[methodName](...rest);
25
- ws.send(JSON.stringify([id, result]));
25
+ if (ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify([id, result]));
26
26
  });
27
27
 
28
28
  ws.isAlive = true;
@@ -31,6 +31,7 @@ export function configureWebsocket(server) {
31
31
  ws.on('error', console.error);
32
32
  ws.on('close', () => {
33
33
  console.log('Disconnected', nodeTag);
34
+ delete sockets[nodeTag];
34
35
  operator.deleteSubscriber(nodeTag);
35
36
  });
36
37
  });
@@ -38,9 +39,11 @@ export function configureWebsocket(server) {
38
39
  const interval = setInterval(function ping() { // Keep-alive ping/pong on interval
39
40
  wss.clients.forEach(function each(ws) {
40
41
  if (!ws.isAlive) return ws.terminate();
41
- ws.isAlive = false;
42
- ws.ping();
42
+ if (ws.readyState == WebSocket.OPEN) {
43
+ ws.isAlive = false;
44
+ ws.ping();
45
+ }
43
46
  return null;
44
47
  });
45
- }, 30e3);
48
+ }, 20e3);
46
49
  }