Browse Source

fix a problem with test data (divide by 0), and map full addresses to points

kb/add-usage-guide
Trent Larson 2 years ago
parent
commit
0ce06bd9ac
  1. 10
      project.task.yaml
  2. 85
      src/components/World/components/objects/landmarks.js
  3. 2
      src/components/World/systems/renderer.js
  4. 100
      src/libs/veramo/appSlice.ts

10
project.task.yaml

@ -1,9 +1,10 @@
- top screens from img/screens.pdf milestone:2 : - top screens from img/screens.pdf milestone:2 :
- view all : - view all :
- add infinite scroll assignee:matthew - add infinite scroll assignee:matthew
blocks: ref:https://raw.githubusercontent.com/trentlarson/lives-of-gifts/master/project.yaml#kickstarter%20for%20time blocks: ref:https://raw.githubusercontent.com/trentlarson/lives-of-gifts/master/project.yaml#kickstarter%20for%20time
- replace user-affecting console.logs with error messages (eg. catches) - replace user-affecting console.log & console.error with error messages (eg. catches)
- stats v1 : - stats v1 :
- use all bits of randomness for location - use all bits of randomness for location
@ -37,6 +38,7 @@
- Release Minimum Viable Product : - Release Minimum Viable Product :
- Turn off stats-world or ensure it's usable (eg. cannot zoom out too far and lose world, cannot screenshot). - Turn off stats-world or ensure it's usable (eg. cannot zoom out too far and lose world, cannot screenshot).
- Add disclaimers. - Add disclaimers.
- Switch default server to the public server.
- Deploy to a server. - Deploy to a server.
- Ensure public server has limits that work for group adoption. - Ensure public server has limits that work for group adoption.
- Test PWA features on Android and iOS. - Test PWA features on Android and iOS.
@ -52,7 +54,9 @@
- linking between projects or plans : - linking between projects or plans :
- terminology: - terminology:
- Fulfills, Feeds, contributes to, supplies, boosts, advances - Fulfills, Feeds, contributes to, supplies, boosts, advances
- Precedes, comes before, is sought by vs follows, seeks, builds on ("contributes to" isn't specific enough, "succeeds" has different confusing meaning) - Precedes, comes before, is sought by -- vs follows, seeks, builds on ("contributes to" isn't specific enough, "succeeds" has different, possibly confusing meaning)
- automated tests, eg. cypress
- Notifications (wake on the phone, push notifications) - Notifications (wake on the phone, push notifications)
@ -64,5 +68,7 @@
- DIDComm - DIDComm
- Write to or read from a different ledger (eg. private ACDC, attest.sh)
- v video for multiple identities https://youtu.be/p8L87AeD76w - v video for multiple identities https://youtu.be/p8L87AeD76w
- v video for adding time to contacts https://youtu.be/7Yylczevp10 - v video for adding time to contacts https://youtu.be/7Yylczevp10

85
src/components/World/components/objects/landmarks.js

@ -10,33 +10,33 @@ import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
import { accessToken } from "@/libs/crypto"; import { accessToken } from "@/libs/crypto";
const ANIMATION_DURATION_SECS = 10; const ANIMATION_DURATION_SECS = 10;
const BASE32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
const ENDORSER_ENTITY_PREFIX = "https://endorser.ch/entity/"; const ENDORSER_ENTITY_PREFIX = "https://endorser.ch/entity/";
export async function loadLandmarks(vue, world, scene, loop) { export async function loadLandmarks(vue, world, scene, loop) {
const endorserApiServer = AppString.DEFAULT_ENDORSER_API_SERVER;
try { try {
await db.open(); await db.open();
const settings = await db.settings.get(MASTER_SETTINGS_KEY); const settings = await db.settings.get(MASTER_SETTINGS_KEY);
const activeDid = settings?.activeDid || ""; const activeDid = settings?.activeDid || "";
const apiServer = settings?.apiServer;
await accountsDB.open(); await accountsDB.open();
const accounts = await accountsDB.accounts.toArray(); const accounts = await accountsDB.accounts.toArray();
const account = R.find((acc) => acc.did === activeDid, accounts); const account = R.find((acc) => acc.did === activeDid, accounts);
const identity = JSON.parse(account?.identity || "undefined"); const identity = JSON.parse(account?.identity || "undefined");
const token = await accessToken(identity); const token = await accessToken(identity);
const url = const url = apiServer + "/api/v2/report/claims?claimType=GiveAction";
endorserApiServer + "/api/v2/report/claims?claimType=GiveAction";
const headers = { const headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
Authorization: "Bearer " + token, Authorization: "Bearer " + token,
}; };
const resp = await axios.get(url, { headers: headers }); const resp = await axios.get(url, { headers: headers });
if (resp.status === 200) { if (resp.status === 200) {
const minDate = resp.data.data[resp.data.data.length - 1].issuedAt; const landmarks = resp.data.data;
const maxDate = resp.data.data[0].issuedAt; const minDate = landmarks[landmarks.length - 1].issuedAt;
const maxDate = landmarks[0].issuedAt;
const minTimeMillis = new Date(minDate).getTime(); const minTimeMillis = new Date(minDate).getTime();
const fullTimeMillis = new Date(maxDate).getTime() - minTimeMillis; const fullTimeMillis =
maxDate > minDate ? new Date(maxDate).getTime() - minTimeMillis : 1; // avoid divide by zero
// ratio of animation time to real time // ratio of animation time to real time
const fakeRealRatio = (ANIMATION_DURATION_SECS * 1000) / fullTimeMillis; const fakeRealRatio = (ANIMATION_DURATION_SECS * 1000) / fullTimeMillis;
@ -53,7 +53,7 @@ export async function loadLandmarks(vue, world, scene, loop) {
// modScale = 15; // modScale = 15;
// calculate positions for each claim, especially because some are random // calculate positions for each claim, especially because some are random
const locations = resp.data.data.map((claim) => const locations = landmarks.map((claim) =>
locForGive(claim, world.PLATFORM_SIZE, world.PLATFORM_EDGE_FOR_UNKNOWNS) locForGive(claim, world.PLATFORM_SIZE, world.PLATFORM_EDGE_FOR_UNKNOWNS)
); );
@ -62,9 +62,9 @@ export async function loadLandmarks(vue, world, scene, loop) {
modelLoc, modelLoc,
function (gltf) { function (gltf) {
gltf.scene.scale.set(0, 0, 0); gltf.scene.scale.set(0, 0, 0);
for (let i = 0; i < resp.data.data.length; i++) { for (let i = 0; i < landmarks.length; i++) {
// claim is a GiveServerRecord (see endorserServer.ts) // claim is a GiveServerRecord (see endorserServer.ts)
const claim = resp.data.data[i]; const claim = landmarks[i];
const newPlant = SkeletonUtils.clone(gltf.scene); const newPlant = SkeletonUtils.clone(gltf.scene);
const loc = locations[i]; const loc = locations[i];
@ -88,9 +88,9 @@ export async function loadLandmarks(vue, world, scene, loop) {
); );
// calculate when lights shine on appearing claim area // calculate when lights shine on appearing claim area
for (let i = 0; i < resp.data.data.length; i++) { for (let i = 0; i < landmarks.length; i++) {
// claim is a GiveServerRecord (see endorserServer.ts) // claim is a GiveServerRecord (see endorserServer.ts)
const claim = resp.data.data[i]; const claim = landmarks[i];
const loc = locations[i]; const loc = locations[i];
const light = createLight(); const light = createLight();
@ -157,7 +157,7 @@ function locForGive(giveClaim, platformWidth, borderWidth) {
} }
} }
if (!loc) { if (!loc) {
// it must be outside our known addresses so let's put it somewhere random // it must be outside our known addresses so let's put it somewhere random on the side
const leftSide = Math.random() < 0.5; const leftSide = Math.random() < 0.5;
loc = { loc = {
x: leftSide x: leftSide
@ -170,34 +170,49 @@ function locForGive(giveClaim, platformWidth, borderWidth) {
} }
/** /**
* Generate a deterministic x & z location based on the randomness in a ULID. * Generate a deterministic x & z location based on the randomness of an ID.
* *
* We'll use the first 20 bits for the x coordinate and next 20 for the z. * We'd like the location to fully map back to the original ID.
* That's a bit over a trillion locations... should be enough for pretty * This typically means we use half the ID for the x and half for the z.
* unique locations for this fun kind of application within a network *
* -- though they're not guaranteed unique without the full ID. * ... in this case: a ULID.
* (We're leaving 40 bits for other properties.) * We'll use the first half (13 characters) for the x coordinate and next 13 for the z.
* We recognize that this is only 3 characters = 15 bits = 32768 unique values
* for the random part for the first half. We also recognize that those random
* bits may be shared with previous ULIDs if they were generated in the same
* millisecond, and therefore much of the evenness of the distribution depends
* on the other dimension.
*
* Also: since the first 10 characters are time-based, we're going to reverse
* the order of the characters to make the randomness more evenly distributed.
* This is reversing the order of the 5-bit characters, not each of the bits.
* Also wik: the first characters of the second half might be the same as
* previous ULIDs if they were generated in the same millisecond. So it's
* best to have that last character be the most significant bit so that there
* is a more even distribution in that dimension.
* *
* @param ulid * @param ulid
* @returns {x: float, z: float} where 0 <= x & z < 100 * @returns {x: float, z: float} where 0 <= x & z < 100
*/ */
function locForUlid(ulid) { function locForUlid(ulid) {
// The random parts of a ULID come after the first 10 characters. const xChars = ulid.substring(0, 13).split("").reverse().join("");
const randomness = ulid.substring(10); const zChars = ulid.substring(13, 26).split("").reverse().join("");
// That leaves 16 characters of randomness, or 80 bits. // from https://github.com/ulid/javascript/blob/5e9727b527aec5b841737c395a20085c4361e971/lib/index.ts#L21
const BASE32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"; // Crockford's Base32
// We're currently only using 32 possible x and z values // We're currently only using 1024 possible x and z values
// because the display is pretty low-fidelity at this point. // because the display is pretty low-fidelity at this point.
const rawX = BASE32.indexOf(xChars[1]) * 32 + BASE32.indexOf(xChars[0]);
const rawZ = BASE32.indexOf(zChars[1]) * 32 + BASE32.indexOf(zChars[0]);
// Similar code is below. const x = (100 * rawX) / 1024;
const x = (100 * BASE32.indexOf(randomness.substring(0, 1))) / 32; const z = (100 * rawZ) / 1024;
const z = (100 * BASE32.indexOf(randomness.substring(4, 5))) / 32;
return { x, z }; return { x, z };
} }
/** /**
* See locForUlid * See locForUlid. Similar, but for ethr DIDs.
* @param did * @param did
* @returns {x: float, z: float} where 0 <= x & z < 100 * @returns {x: float, z: float} where 0 <= x & z < 100
*/ */
@ -206,13 +221,15 @@ function locForEthrDid(did) {
if (did.length < 51) { if (did.length < 51) {
return { x: 0, z: 0 }; return { x: 0, z: 0 };
} else { } else {
const randomness = did.substring(11); const randomness = did.substring("did:ethr:0x".length);
// We'll take the first 4 bits for 16 possible x & z values. // We'll use all the randomness for fully unique x & z values.
const xOff = parseInt(Number("0x" + randomness.substring(0, 1)), 10); // But we'll only calculate this view with the first byte since our rendering resolution is low.
const x = (xOff * 100) / 16; const xOff = parseInt(Number("0x" + randomness.substring(0, 2)), 10);
// ... and since we're reserving 20 bits total for x, start with character 5. const x = (xOff * 100) / 256;
const zOff = parseInt(Number("0x" + randomness.substring(5, 6)), 10); // ... and since we're reserving 20 bytes total for x, start z with character 20,
const z = (zOff * 100) / 16; // again with one byte.
const zOff = parseInt(Number("0x" + randomness.substring(20, 22)), 10);
const z = (zOff * 100) / 256;
return { x, z }; return { x, z };
} }
} }

2
src/components/World/systems/renderer.js

@ -4,7 +4,7 @@ function createRenderer() {
const renderer = new WebGLRenderer({ antialias: true }); const renderer = new WebGLRenderer({ antialias: true });
// turn on the physically correct lighting model // turn on the physically correct lighting model
// (The browser complains: "THREE.WebGLRenderer: .physicallyCorrectLights has been removed. Set enderer.useLegacyLights instead." However, that changes the lighting in a way that doesn't look better.) // (The browser complains: "THREE.WebGLRenderer: the property .physicallyCorrectLights has been removed. Set renderer.useLegacyLights instead." However, that changes the lighting in a way that doesn't look better.)
renderer.physicallyCorrectLights = true; renderer.physicallyCorrectLights = true;
return renderer; return renderer;

100
src/libs/veramo/appSlice.ts

@ -1,100 +0,0 @@
/* import * as R from "ramda";
import { configureStore, createSlice } from "@reduxjs/toolkit";
import { IIdentifier } from "@veramo/core";
import { Contact } from "../entity/contact";
import { Settings } from "../entity/settings";
import * as utility from "../utility/utility";
const MAX_LOG_LENGTH = 2000000;
export const DEFAULT_ENDORSER_API_SERVER = "https://endorser.ch:3000";
export const DEFAULT_ENDORSER_VIEW_SERVER = "https://endorser.ch";
export const LOCAL_ENDORSER_API_SERVER = "http://127.0.0.1:3000";
export const LOCAL_ENDORSER_VIEW_SERVER = "http://127.0.0.1:3001";
export const TEST_ENDORSER_API_SERVER = "https://test.endorser.ch:8000";
export const TEST_ENDORSER_VIEW_SERVER = "https://test.endorser.ch:8080";
// for contents set in reducers
interface Payload<T> {
type: string;
payload: T;
}
interface LogMsg {
log: boolean;
msg: string;
}
export const appSlice = createSlice({
name: "app",
initialState: {
// This is nullable because it is cached state from the DB...
// it'll be null if we haven't even loaded from the DB yet.
settings: null as Settings,
// This is nullable because it is cached state from the DB...
// it'll be null if we haven't even loaded from the DB yet.
identifiers: null as Array<IIdentifier> | null,
// This is nullable because it is cached state from the DB...
// it'll be null if we haven't even loaded from the DB yet.
contacts: null as Array<Contact> | null,
viewServer: DEFAULT_ENDORSER_VIEW_SERVER,
logMessage: "",
advancedMode: false,
testMode: false,
},
reducers: {
addIdentifier: (state, contents: Payload<IIdentifier>) => {
state.identifiers = state.identifiers.concat([contents.payload]);
},
addLog: (state, contents: Payload<LogMsg>) => {
if (state.logMessage.length > MAX_LOG_LENGTH) {
state.logMessage =
"<truncated>\n..." +
state.logMessage.substring(
state.logMessage.length - MAX_LOG_LENGTH / 2
);
}
if (contents.payload.log) {
console.log(contents.payload.msg);
state.logMessage += "\n" + contents.payload.msg;
}
},
setAdvancedMode: (state, contents: Payload<boolean>) => {
state.advancedMode = contents.payload;
},
setContacts: (state, contents: Payload<Array<Contact>>) => {
state.contacts = contents.payload;
},
setContact: (state, contents: Payload<Contact>) => {
const index = R.findIndex(
(c) => c.did === contents.payload.did,
state.contacts
);
state.contacts[index] = contents.payload;
},
setHomeScreen: (state, contents: Payload<string>) => {
state.settings.homeScreen = contents.payload;
},
setIdentifiers: (state, contents: Payload<Array<IIdentifier>>) => {
state.identifiers = contents.payload;
},
setSettings: (state, contents: Payload<Settings>) => {
state.settings = contents.payload;
},
setTestMode: (state, contents: Payload<boolean>) => {
state.testMode = contents.payload;
},
setViewServer: (state, contents: Payload<string>) => {
state.viewServer = contents.payload;
},
},
});
export const appStore = configureStore({ reducer: appSlice.reducer });
*/
Loading…
Cancel
Save