From 0ce06bd9ac462359494b0903a554a236795c4918 Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Sat, 27 May 2023 17:48:41 -0600 Subject: [PATCH] fix a problem with test data (divide by 0), and map full addresses to points --- project.task.yaml | 10 +- .../World/components/objects/landmarks.js | 85 +++++++++------ src/components/World/systems/renderer.js | 2 +- src/libs/veramo/appSlice.ts | 100 ------------------ 4 files changed, 60 insertions(+), 137 deletions(-) delete mode 100644 src/libs/veramo/appSlice.ts diff --git a/project.task.yaml b/project.task.yaml index fb0f0dfae..89b8974fb 100644 --- a/project.task.yaml +++ b/project.task.yaml @@ -1,9 +1,10 @@ + - top screens from img/screens.pdf milestone:2 : - view all : - add infinite scroll assignee:matthew 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 : - use all bits of randomness for location @@ -37,6 +38,7 @@ - Release Minimum Viable Product : - Turn off stats-world or ensure it's usable (eg. cannot zoom out too far and lose world, cannot screenshot). - Add disclaimers. + - Switch default server to the public server. - Deploy to a server. - Ensure public server has limits that work for group adoption. - Test PWA features on Android and iOS. @@ -52,7 +54,9 @@ - linking between projects or plans : - terminology: - 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) @@ -64,5 +68,7 @@ - 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 adding time to contacts https://youtu.be/7Yylczevp10 diff --git a/src/components/World/components/objects/landmarks.js b/src/components/World/components/objects/landmarks.js index 7a0555a23..0d0ff62aa 100644 --- a/src/components/World/components/objects/landmarks.js +++ b/src/components/World/components/objects/landmarks.js @@ -10,33 +10,33 @@ import { MASTER_SETTINGS_KEY } from "@/db/tables/settings"; import { accessToken } from "@/libs/crypto"; const ANIMATION_DURATION_SECS = 10; -const BASE32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"; const ENDORSER_ENTITY_PREFIX = "https://endorser.ch/entity/"; export async function loadLandmarks(vue, world, scene, loop) { - const endorserApiServer = AppString.DEFAULT_ENDORSER_API_SERVER; try { await db.open(); const settings = await db.settings.get(MASTER_SETTINGS_KEY); const activeDid = settings?.activeDid || ""; + const apiServer = settings?.apiServer; await accountsDB.open(); const accounts = await accountsDB.accounts.toArray(); const account = R.find((acc) => acc.did === activeDid, accounts); const identity = JSON.parse(account?.identity || "undefined"); const token = await accessToken(identity); - const url = - endorserApiServer + "/api/v2/report/claims?claimType=GiveAction"; + const url = apiServer + "/api/v2/report/claims?claimType=GiveAction"; const headers = { "Content-Type": "application/json", Authorization: "Bearer " + token, }; const resp = await axios.get(url, { headers: headers }); if (resp.status === 200) { - const minDate = resp.data.data[resp.data.data.length - 1].issuedAt; - const maxDate = resp.data.data[0].issuedAt; + const landmarks = resp.data.data; + const minDate = landmarks[landmarks.length - 1].issuedAt; + const maxDate = landmarks[0].issuedAt; 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 const fakeRealRatio = (ANIMATION_DURATION_SECS * 1000) / fullTimeMillis; @@ -53,7 +53,7 @@ export async function loadLandmarks(vue, world, scene, loop) { // modScale = 15; // 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) ); @@ -62,9 +62,9 @@ export async function loadLandmarks(vue, world, scene, loop) { modelLoc, function (gltf) { 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) - const claim = resp.data.data[i]; + const claim = landmarks[i]; const newPlant = SkeletonUtils.clone(gltf.scene); const loc = locations[i]; @@ -88,9 +88,9 @@ export async function loadLandmarks(vue, world, scene, loop) { ); // 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) - const claim = resp.data.data[i]; + const claim = landmarks[i]; const loc = locations[i]; const light = createLight(); @@ -157,7 +157,7 @@ function locForGive(giveClaim, platformWidth, borderWidth) { } } 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; loc = { 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. - * That's a bit over a trillion locations... should be enough for pretty - * unique locations for this fun kind of application within a network - * -- though they're not guaranteed unique without the full ID. - * (We're leaving 40 bits for other properties.) + * We'd like the location to fully map back to the original ID. + * This typically means we use half the ID for the x and half for the z. + * + * ... in this case: a ULID. + * 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 * @returns {x: float, z: float} where 0 <= x & z < 100 */ function locForUlid(ulid) { - // The random parts of a ULID come after the first 10 characters. - const randomness = ulid.substring(10); + const xChars = ulid.substring(0, 13).split("").reverse().join(""); + 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. + 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 * BASE32.indexOf(randomness.substring(0, 1))) / 32; - const z = (100 * BASE32.indexOf(randomness.substring(4, 5))) / 32; + const x = (100 * rawX) / 1024; + const z = (100 * rawZ) / 1024; return { x, z }; } /** - * See locForUlid + * See locForUlid. Similar, but for ethr DIDs. * @param did * @returns {x: float, z: float} where 0 <= x & z < 100 */ @@ -206,13 +221,15 @@ function locForEthrDid(did) { if (did.length < 51) { return { x: 0, z: 0 }; } else { - const randomness = did.substring(11); - // We'll take the first 4 bits for 16 possible x & z values. - const xOff = parseInt(Number("0x" + randomness.substring(0, 1)), 10); - const x = (xOff * 100) / 16; - // ... and since we're reserving 20 bits total for x, start with character 5. - const zOff = parseInt(Number("0x" + randomness.substring(5, 6)), 10); - const z = (zOff * 100) / 16; + const randomness = did.substring("did:ethr:0x".length); + // We'll use all the randomness for fully unique x & z values. + // But we'll only calculate this view with the first byte since our rendering resolution is low. + const xOff = parseInt(Number("0x" + randomness.substring(0, 2)), 10); + const x = (xOff * 100) / 256; + // ... and since we're reserving 20 bytes total for x, start z with character 20, + // again with one byte. + const zOff = parseInt(Number("0x" + randomness.substring(20, 22)), 10); + const z = (zOff * 100) / 256; return { x, z }; } } diff --git a/src/components/World/systems/renderer.js b/src/components/World/systems/renderer.js index ea423fced..a786e9939 100644 --- a/src/components/World/systems/renderer.js +++ b/src/components/World/systems/renderer.js @@ -4,7 +4,7 @@ function createRenderer() { const renderer = new WebGLRenderer({ antialias: true }); // 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; return renderer; diff --git a/src/libs/veramo/appSlice.ts b/src/libs/veramo/appSlice.ts deleted file mode 100644 index 2b6996a2f..000000000 --- a/src/libs/veramo/appSlice.ts +++ /dev/null @@ -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 { - 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 | 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 | null, - - viewServer: DEFAULT_ENDORSER_VIEW_SERVER, - - logMessage: "", - - advancedMode: false, - testMode: false, - }, - reducers: { - addIdentifier: (state, contents: Payload) => { - state.identifiers = state.identifiers.concat([contents.payload]); - }, - addLog: (state, contents: Payload) => { - if (state.logMessage.length > MAX_LOG_LENGTH) { - state.logMessage = - "\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) => { - state.advancedMode = contents.payload; - }, - setContacts: (state, contents: Payload>) => { - state.contacts = contents.payload; - }, - setContact: (state, contents: Payload) => { - const index = R.findIndex( - (c) => c.did === contents.payload.did, - state.contacts - ); - state.contacts[index] = contents.payload; - }, - setHomeScreen: (state, contents: Payload) => { - state.settings.homeScreen = contents.payload; - }, - setIdentifiers: (state, contents: Payload>) => { - state.identifiers = contents.payload; - }, - setSettings: (state, contents: Payload) => { - state.settings = contents.payload; - }, - setTestMode: (state, contents: Payload) => { - state.testMode = contents.payload; - }, - setViewServer: (state, contents: Payload) => { - state.viewServer = contents.payload; - }, - }, -}); - -export const appStore = configureStore({ reducer: appSlice.reducer }); - */