@yz-social/civildefense.io 4.5.8 → 4.5.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/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { P2PWebNetwork } from "./public/javascripts/p2pWebNetwork.js";
2
- export { agentTopic, alertTopic, canonicalTag } from "./public/javascripts/versions.js";
2
+ export { topicRegion, agentTopic, alertTopic, canonicalTag } from "./public/javascripts/versions.js";
3
3
  export { getContainingCells } from "./public/javascripts/s2.js";
4
4
  export { location } from "./server/getLocation.js";
5
+ export { deriveTopicIdBig } from "@axona/protocol";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@yz-social/civildefense.io",
3
3
  "description": "Browser app to safely share live sightings on a map.",
4
- "version": "4.5.8",
4
+ "version": "4.5.16",
5
5
  "keywords": [
6
6
  "map",
7
7
  "browser",
@@ -10,14 +10,16 @@
10
10
  "scripts": {
11
11
  "start": "npm stop; node ./server/app.js",
12
12
  "stop": "pkill yz.social",
13
- "postinstall": "touch server/location.json; chmod a+w server/location.json"
13
+ "postinstall": "touch server/location.json; chmod a+w server/location.json",
14
+ "prepack": "mv server/location.json server/location.save.json",
15
+ "postpack": "mv server/location.save.json server/location.json"
14
16
  },
15
17
  "type": "module",
16
18
  "exports": {
17
19
  ".": "./index.js"
18
20
  },
19
21
  "dependencies": {
20
- "@axona/protocol": "github:axona-net/axona-protocol#semver:^4.29.0",
22
+ "@axona/protocol": "github:axona-net/axona-protocol#semver:^4.59.2",
21
23
  "cors": "^2.8.6",
22
24
  "express": "^4.22.1",
23
25
  "leaflet": "^1.9.4",
@@ -128,7 +128,7 @@
128
128
  <!-- <hr class="rule"> -->
129
129
 
130
130
  <h1>The Bigger Picture</h1>
131
- <p>The Civil Defense software was created by a group of experienced system developers, running on the <a href="https://github.com/axona-net/axona-protocol" target="yz.other" rel="noopener">Axona network</a>. Both are free and open source.</p>
131
+ <p>The Civil Defense runs on the <a href="https://github.com/axona-net/axona-protocol" target="yz.other" rel="noopener">Axona network</a>. Both are free and open source.</p>
132
132
  <p>Axona is a modern descendant of the old <a href="https://en.wikipedia.org/wiki/Peer-to-peer" target="yz.other" rel="noopener">peer-to-peer networks</a> such as <a href="https://en.wikipedia.org/wiki/Napster">Napster</a>. It has a unique self-healing routing mechanism that uses neuron learning techniques to find the best way to distribute alerts. It is analogous to <a href="https://en.wikipedia.org/wiki/Tor_(network)" target="yz.other" rel="noopener">TOR</a> (The Onion Router), but designed from the ground up to run in mobile and desktop browsers.</p>
133
133
  <p>We are also developing a comprehensive quantum-ready cryptography system that will add private-groups for Civil Defense and any software on the network. We believe there are many applications where developers, users, and the public all benefit from open software and secure, decentralized networks — without app stores, servers, or data centers. Beyond Civil Defense, we are also developing large-scale <a href="https://en.wikipedia.org/wiki/Distributed_computing" target="yz.other" rel="noopener">multiuser collaboration</a> and <a href="https://poverty-action.org/inclusive-instant-payment-systems-evidence-based-approach-design-impact" target="yz.other" rel="noopener">Inclusive Instant Payment Systems</a>.</p>
134
134
 
@@ -113,7 +113,7 @@
113
113
  </ul>
114
114
 
115
115
  <h1>El Panorama General</h1>
116
- <p>El software de Defensa Civil fue creado por un grupo de desarrolladores de sistemas experimentados, que se ejecuta en el <a href="https://github.com/axona-net/axona-protocol" target="yz.other" rel="noopener">red Axona</a>. Ambos son gratuitos y de código abierto.</p>
116
+ <p>La Defensa Civil opera en la <a href="https://github.com/axona-net/axona-protocol" target="yz.other" rel="noopener">red Axona</a>. Ambos son gratuitos y de código abierto.</p>
117
117
  <p>Axona es un descendiente moderno de las antiguas <a href="https://en.wikipedia.org/wiki/Peer-to-peer" target="yz.other" rel="noopener">redes entre pares</a> como <a href="https://en.wikipedia.org/wiki/Napster">Napster</a>. Tiene un mecanismo único de enrutamiento autocurativo que utiliza técnicas de aprendizaje neuronal para encontrar la mejor manera de distribuir alertas. Es análogo a <a href="https://en.wikipedia.org/wiki/Tor_(network)" target="yz.other" rel="noopener">TOR</a> (The Onion Router), pero diseñado desde cero para funcionar en navegadores móviles y de escritorio.</p>
118
118
  <p>También estamos desarrollando un sistema integral de criptografía preparado para la era cuántica que añadirá grupos privados para Defensa Civil y cualquier software en la red. Creemos que hay muchas aplicaciones en las que los desarrolladores, los usuarios y el público en general se benefician del software abierto y las redes seguras y descentralizadas, sin tiendas de aplicaciones, servidores ni centros de datos. Más allá de Defensa Civil, también estamos desarrollando <a href="https://en.wikipedia.org/wiki/Distributed_computing" target="yz.other" rel="noopener">colaboración multiusuario a gran escala</a> y <a href="https://poverty-action.org/inclusive-instant-payment-systems-evidence-based-approach-design-impact" target="yz.other" rel="noopener">Sistemas de Pago Instantáneo Inclusivos</a>.</p>
119
119
 
Binary file
package/public/index.html CHANGED
@@ -25,12 +25,11 @@
25
25
  "uuid": "https://unpkg.com/uuid@13.0.0/dist/index.js",
26
26
  "@material/web/": "https://esm.run/@material/web/",
27
27
  "s2js": "./s2js/s2js.esm.js",
28
- "bigfloat": "./bigfloat/esm/index.js",
29
- "leaflet": "./leaflet/leaflet-src.esm.js",
30
- "minidenticons": "./minidenticons/minidenticons.min.js",
31
- "@axona/protocol": "./axona-protocol/src/index.js",
32
- "@axona/protocol/std": "./axona-protocol/std/index.js",
33
- "@axona/protocol/connect.js": "./axona-protocol/src/connect.js"
28
+ "bigfloat": "./bigfloat/esm/index.js",
29
+ "leaflet": "./leaflet/leaflet-src.esm.js",
30
+ "minidenticons": "./minidenticons/minidenticons.min.js",
31
+ "@axona/protocol": "./axona-protocol/src/index.js",
32
+ "@axona/protocol/std": "./axona-protocol/std/index.js"
34
33
  }
35
34
  }
36
35
  </script>
@@ -45,123 +44,123 @@
45
44
  </head>
46
45
  <body>
47
46
  <section data-nosnippet>
48
- <div id="mapCapture">
49
- <div id="map"></div>
50
- <img src="images/civil-defense-240.png" alt="civildefense" class="about-image">
51
- </div>
52
-
53
- <div id="subPopoverControls"> <!-- Our control, that appear within the map and the marker popups in stack order -->
47
+ <div id="mapCapture">
48
+ <div id="map"></div>
49
+ <img src="images/civil-defense-240.png" alt="civildefense" class="about-image">
50
+ </div>
54
51
 
55
- <button id="aboutButton" class="about-button">
56
- <img src="images/civil-defense-240.png" alt="civildefense" class="about-image"></img>
57
- </button>
52
+ <div id="subPopoverControls"> <!-- Our control, that appear within the map and the marker popups in stack order -->
58
53
 
59
- <div class="button-container">
60
- <button id="qrButton">
61
- <img src="images/qr-scan.svg" alt="qr code" class="button-image">
62
- </button>
63
- <button id="share">
64
- <img src="images/share.svg" alt="share" class="button-image">
54
+ <button id="aboutButton" class="about-button">
55
+ <img src="images/civil-defense-240.png" alt="civildefense" class="about-image"></img>
65
56
  </button>
66
- <button id="recenterButton">
67
- <img src="images/recenter.svg" alt="recenter on position" class="button-image">
68
- </button>
69
- </div>
70
57
 
71
- <md-chip-set class="watching-hashtags"></md-chip-set>
72
- </div>
58
+ <div class="button-container">
59
+ <button id="qrButton">
60
+ <img src="images/qr-scan.svg" alt="qr code" class="button-image">
61
+ </button>
62
+ <button id="share">
63
+ <img src="images/share.svg" alt="share" class="button-image">
64
+ </button>
65
+ <button id="recenterButton">
66
+ <img src="images/recenter.svg" alt="recenter on position" class="button-image">
67
+ </button>
68
+ </div>
73
69
 
74
- <div id="info" class="info-banner loading"></div>
70
+ <md-chip-set class="watching-hashtags"></md-chip-set>
71
+ </div>
75
72
 
76
- <div id="qrContainer" class="hidden">
77
- <div class="display-content"></div>
78
- </div>
73
+ <div id="info" class="info-banner loading"></div>
79
74
 
80
- <div id="aboutContainer" class="hidden">
81
- <div class="display-content">
82
- <h1>Civil Defense</h1>
83
- <ul>
84
- <li id="aboutReport"></li>
85
- <li id="aboutShared"></li>
86
- <li id="aboutFade"></li>
87
- </ul>
88
- <p> <a id="learnMore" href="about/index.html" target="yz.sidebar">Learn More</a> </p>
89
- <p>
90
- <span id="aboutAnyone1"></span>
91
- <a id="aboutAnyone2" href="https://github.com/YZ-social/Yz.social" target="yz.sidebar"></a>
92
- <span id="aboutAnyone3"></span>
93
- </p>
94
- <hr>
95
- <div>
96
- <label>
97
- <md-checkbox id="showNotifications" touch-target="wrapper"></md-checkbox>
98
- <span id="showNotificationsLabel"></span>
99
- </label>
100
- </div>
101
- <div class="handle-avatar">
102
- <md-outlined-text-field id="myHandle" placeholder="anonymous"></md-outlined-text-field>
103
- <md-outlined-icon-button id="myAvatar"></md-outlined-icon-button>
104
- </div>
105
- <hr>
106
- <div class="source-updates">
107
- <md-elevated-button id="checkForUpdates"></md-elevated-button>
108
- <md-elevated-button id="downloadUpdates" class="hidden"></md-elevated-button>
109
- <span id="updateStatus"></span>
110
- </div>
111
- <p><small>
112
- <label for="scriptChooser"> <span id="version">Version</span></label>
113
- <span id="appVersion"></span>,
114
- kernel: <span id="kernelVersion"></span>.
115
- </small>
116
- <small><a id="wipe" href="#"></a></small></p>
117
- <input id="scriptChooser" type="file" accept="text/javascript"></input>
75
+ <div id="qrContainer" class="hidden">
76
+ <div class="display-content"></div>
118
77
  </div>
119
- </div>
120
78
 
121
- <div id="correspondentContainer" class="hidden">
122
- <div class="display-content">
123
- <div class="private-label">
124
- <div class="describe">
125
- <span id="describePrivate1"></span>
126
- <span class="handle"></span>
127
- <md-icon class="avatar"></md-icon>
128
- <span id="describePrivate2"></span>
79
+ <div id="aboutContainer" class="hidden">
80
+ <div class="display-content">
81
+ <h1>Civil Defense</h1>
82
+ <ul>
83
+ <li id="aboutReport"></li>
84
+ <li id="aboutShared"></li>
85
+ <li id="aboutFade"></li>
86
+ </ul>
87
+ <p> <a id="learnMore" href="about/index.html" target="yz.sidebar">Learn More</a> </p>
88
+ <p>
89
+ <span id="aboutAnyone1"></span>
90
+ <a id="aboutAnyone2" href="https://github.com/YZ-social/Yz.social" target="yz.sidebar"></a>
91
+ <span id="aboutAnyone3"></span>
92
+ </p>
93
+ <hr>
94
+ <div>
95
+ <label>
96
+ <md-checkbox id="showNotifications" touch-target="wrapper"></md-checkbox>
97
+ <span id="showNotificationsLabel"></span>
98
+ </label>
129
99
  </div>
130
- <md-outlined-text-field></md-outlined-text-field>
131
- <md-outlined-icon-button></md-outlined-icon-button>
132
- </div>
133
- <div id="pickLabels"></div>
134
- <div class="public-label">
135
- <div id="describePublic" class="describe"></div>
136
- <md-outlined-button></md-outlined-button>
137
- <md-outlined-icon-button></md-outlined-icon-button>
138
- </div>
139
- <div class="system-label">
140
- <div id="describeSystem" class="describe"></div>
141
- <md-outlined-button></md-outlined-button>
142
- <md-outlined-icon-button></md-outlined-icon-button>
100
+ <div class="handle-avatar">
101
+ <md-outlined-text-field id="myHandle" placeholder="anonymous"></md-outlined-text-field>
102
+ <md-outlined-icon-button id="myAvatar"></md-outlined-icon-button>
103
+ </div>
104
+ <hr>
105
+ <div class="source-updates">
106
+ <md-elevated-button id="checkForUpdates"></md-elevated-button>
107
+ <md-elevated-button id="downloadUpdates" class="hidden"></md-elevated-button>
108
+ <span id="updateStatus"></span>
109
+ </div>
110
+ <p><small>
111
+ <label for="scriptChooser"> <span id="version">Version</span></label>
112
+ <span id="appVersion"></span>,
113
+ kernel: <span id="kernelVersion"></span>.
114
+ </small>
115
+ <small><a id="wipe" href="#"></a></small></p>
116
+ <input id="scriptChooser" type="file" accept="text/javascript"></input>
143
117
  </div>
144
- <input type="file" accept="image/*"></input>
145
- <div>
146
- <md-text-button id="correspondentCancel">Cancel</md-text-button>
147
- <md-text-button id="correspondentOK">OK</md-text-button>
118
+ </div>
119
+
120
+ <div id="correspondentContainer" class="hidden">
121
+ <div class="display-content">
122
+ <div class="private-label">
123
+ <div class="describe">
124
+ <span id="describePrivate1"></span>
125
+ <span class="handle"></span>
126
+ <md-icon class="avatar"></md-icon>
127
+ <span id="describePrivate2"></span>
128
+ </div>
129
+ <md-outlined-text-field></md-outlined-text-field>
130
+ <md-outlined-icon-button></md-outlined-icon-button>
131
+ </div>
132
+ <div id="pickLabels"></div>
133
+ <div class="public-label">
134
+ <div id="describePublic" class="describe"></div>
135
+ <md-outlined-button></md-outlined-button>
136
+ <md-outlined-icon-button></md-outlined-icon-button>
137
+ </div>
138
+ <div class="system-label">
139
+ <div id="describeSystem" class="describe"></div>
140
+ <md-outlined-button></md-outlined-button>
141
+ <md-outlined-icon-button></md-outlined-icon-button>
142
+ </div>
143
+ <input type="file" accept="image/*"></input>
144
+ <div>
145
+ <md-text-button id="correspondentCancel">Cancel</md-text-button>
146
+ <md-text-button id="correspondentOK">OK</md-text-button>
147
+ </div>
148
148
  </div>
149
149
  </div>
150
- </div>
151
150
 
152
- <div id="updateContainer" class="hidden">
153
- <div class="display-content">
154
- <h1 id="newVersionHeader">New version available</h1>
155
- <p id="updateNowQuestion"></p>
156
- <p id="updateReload"></p>
157
- <p id="updateDefer"></p>
158
- <md-elevated-button id="downloadUpdates2"></md-elevated-button>
159
- <md-elevated-button id="downloadDefer"></md-elevated-button>
151
+ <div id="updateContainer" class="hidden">
152
+ <div class="display-content">
153
+ <h1 id="newVersionHeader">New version available</h1>
154
+ <p id="updateNowQuestion"></p>
155
+ <p id="updateReload"></p>
156
+ <p id="updateDefer"></p>
157
+ <md-elevated-button id="downloadUpdates2"></md-elevated-button>
158
+ <md-elevated-button id="downloadDefer"></md-elevated-button>
159
+ </div>
160
160
  </div>
161
- </div>
162
161
 
163
- <md-menu positioning="popover" id="popoverMenu"></md-menu>
164
- <script type="module" src="javascripts/main.js"></script>
162
+ <md-menu positioning="popover" id="popoverMenu"></md-menu>
165
163
  </section>
164
+ <script type="module" src="javascripts/main.js"></script>
166
165
  </body>
167
166
  </html>
@@ -1,7 +1,6 @@
1
1
  const { localStorage } = globalThis;
2
2
  import { v4 as uuidv4 } from 'uuid';
3
3
  import { minidenticonSvg } from 'minidenticons';
4
- import { createAuthorIdentity } from '@axona/protocol';
5
4
  import { agentTopic, agentPersistKey } from './versions.js';
6
5
  import { Int } from './translations.js';
7
6
  import { consume, openDisplay } from './display.js';
@@ -65,7 +64,7 @@ export class Agent {
65
64
  const owner = this.tag;
66
65
  ['handle', 'avatar'].forEach(type => {
67
66
  const eventName = this.networkPersistKey(type);
68
- contact.subscribe({eventName, region, owner, /*since: 'latest',*/ handler: data => this.setPublicData({...data, type})});
67
+ contact.subscribe({eventName, region, owner, since: 'latest', handler: data => this.setPublicData({...data, type})});
69
68
  });
70
69
  });
71
70
  }
@@ -129,23 +128,25 @@ export class Agent {
129
128
  const eventName = this.networkPersistKey(type);
130
129
  const region = this.currentRegion;
131
130
  const owner = this.tag;
132
- // TODO: set owner as well.
133
131
  const contact = await networkPromise;
134
- if (value) {
135
- let payload = value;
136
- // Our downsampling is such that we do not need to chunkify.
137
- // if (type === 'avatar') {
138
- // const blob = await P2PWebNetwork.dataURL2blob(value);
139
- // payload = (await contact.chunkifyBlob({blob, region})).topic;
140
- // }
141
- return contact.publish({eventName, region, owner, payload});
142
- }
143
- // For now, until since:'latest' works, supply a null payload and no subject.
144
- await contact.publish({eventName, region, owner, payload: null});
145
- // const subject = this.publicMsgId[type];
146
- // if (!subject) return null; // We have not published a value, so nothing to kill.
147
- // await contact.publish({eventName, region, owner, subject, payload: null});
148
- // return null;
132
+
133
+ // First kill previous, if any.
134
+ // Publish doesn't know whether subscribers will be since all or latest, so it must retain all unkilled.
135
+ // Thus if we only kill the last one when there is no value,
136
+ // a subscribe since:latest will produce the PREVIOUS value -- the last unkilled one.
137
+
138
+ const killTag = this.publicMsgId[type];
139
+ console.log('persist', {type, killTag, value: value && value.slice(0, 15)});
140
+ if (killTag) await contact.publish({eventName, region, owner, killTag, payload: null});
141
+
142
+ if (!value) return null;
143
+ let payload = value;
144
+ // Our downsampling is such that we do not need to chunkify.
145
+ // if (type === 'avatar') {
146
+ // const blob = await P2PWebNetwork.dataURL2blob(value);
147
+ // payload = (await contact.chunkifyBlob({blob, region})).topic;
148
+ // }
149
+ return contact.publish({eventName, region, owner, payload});
149
150
  }
150
151
 
151
152
  // We represent handles and avatars by inserting stuff into given elements.
@@ -156,8 +157,9 @@ export class Agent {
156
157
  element.textContent = value || '(none)';
157
158
  element.value = value || ''; // Hack: handle input[type="text"] as well. Must be property assignment, not attribute.
158
159
  }
160
+ static spinner = 'images/loading.gif';
159
161
  static avatar(element, value) { // Update avatar element with value.
160
- element.innerHTML = value === null ? '(none)' : (value.startsWith('data') ? this.makeImage(value) : Agent.makeIdenticon(value));
162
+ element.innerHTML = value === null ? '(none)' : (value === this.spinner || value.startsWith('data') ? this.makeImage(value) : Agent.makeIdenticon(value));
161
163
  }
162
164
  static downsampleResolution = 128; // max height or width
163
165
  static makeImage(url) {
@@ -237,11 +239,8 @@ export class Agent {
237
239
  this.updateValue(null, 'private', 'avatar');
238
240
  };
239
241
  fileChooser.onchange = async event => {
240
- consume(event);
241
- if (!fileChooser.files.length) return;
242
- const blob = await P2PWebNetwork.downsampledBlob({blob: fileChooser.files[0], maxDimension: Agent.downsampleResolution});
243
- this.updateValue(await P2PWebNetwork.blob2dataURL(blob), 'private', 'avatar');
244
- console.log('clearing avatar selection');
242
+ await this.fileEvent(fileChooser, event, 'private');
243
+ console.log('cleared avatar selection');
245
244
  };
246
245
  fileChooser.click();
247
246
  });
@@ -263,6 +262,16 @@ export class Agent {
263
262
  content.parentElement.classList.toggle('hidden', true);
264
263
  };
265
264
  }
265
+ async fileEvent(fileChooser, event, scope) { // Update value from fileChooser change event, return dataURL;
266
+ consume(event);
267
+ if (!fileChooser.files.length) return '';
268
+ const file = fileChooser.files[0];
269
+ const maxDimension = Agent.downsampleResolution;
270
+ const blob = await P2PWebNetwork.downsampledBlob({blob: file, maxDimension});
271
+ const dataURL = await P2PWebNetwork.blob2dataURL(blob);
272
+ await this.updateValue(dataURL, scope, 'avatar');
273
+ return dataURL;
274
+ }
266
275
  static current = null;
267
276
  static tag = null;
268
277
  static identity = null;
@@ -275,7 +284,7 @@ export class Agent {
275
284
  static async initialize() { // Initialize what the agent needs from the about screen
276
285
  let tag = localStorage.getItem('usertag');
277
286
  const persistAs = tag || uuidv4(); // Give it SOMETHING to persistAs.
278
- const myIdentity = await createAuthorIdentity({persistAs});
287
+ const myIdentity = await P2PWebNetwork.createAuthorIdentity({persistAs});
279
288
  if (tag !== persistAs) { // Fix up persistence by moving it to where it need to go.
280
289
  // WARNING: This relies on undocumented behavior of createAuthorIdentity().
281
290
  const realTag = myIdentity.authorId;
@@ -304,17 +313,15 @@ export class Agent {
304
313
  fileChooser.oncancel = event => {
305
314
  consume(event);
306
315
  console.log('cancel my avatar');
316
+ myAgent.updateValue(this.spinner, 'public', 'avatar', false);
307
317
  myAgent.updateValue(null, 'public', 'avatar');
308
318
  myAgent.persistPrivate(null, 'avatar'); // So that we'll have it next session.
309
319
  console.log('clearing avatar selection');
310
320
  };
311
321
  fileChooser.onchange = async event => {
312
- consume(event);
313
- if (!fileChooser.files.length) return;
314
- const blob = await P2PWebNetwork.downsampledBlob({blob: fileChooser.files[0], maxDimension: Agent.downsampleResolution});
315
- const url = await P2PWebNetwork.blob2dataURL(blob);
322
+ myAgent.updateValue(this.spinner, 'public', 'avatar', false);
323
+ const url = await myAgent.fileEvent(fileChooser, event, 'public');
316
324
  console.log('set my avatar', url.slice(0, 50));
317
- myAgent.updateValue(url, 'public', 'avatar');
318
325
  myAgent.persistPrivate(url, 'avatar'); // So that we'll have it next session.
319
326
  };
320
327
  fileChooser.click();