diff --git a/ui/package-lock.json b/ui/package-lock.json index 7a33ab63..93ef648f 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -23,7 +23,8 @@ "@tlon/sigil-js": "^1.4.4", "@tloncorp/mock-http-api": "^1.2.0", "@types/lodash": "^4.14.172", - "@urbit/http-api": "^2.4.5-debug", + "@urbit/aura": "^1.0.0", + "@urbit/js-http-api": "git+https://github.com/urbit/js-http-api", "big-integer": "^1.6.48", "browser-cookies": "^1.2.0", "classnames": "^2.3.1", @@ -48,7 +49,6 @@ "react-hook-form": "^7.38.0", "react-router-dom": "^5.2.0", "slugify": "^1.6.0", - "urbit-ob": "^5.0.1", "zustand": "^3.7.2" }, "devDependencies": { @@ -2401,10 +2401,23 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@urbit/http-api": { - "version": "2.4.5-debug", - "resolved": "https://registry.npmjs.org/@urbit/http-api/-/http-api-2.4.5-debug.tgz", - "integrity": "sha512-kMzQwNlrObEz4tEg90nlPRW69Tf+9rD3iIK/JgvsG3423x9oa4rFXobnFKBphXR/5bqd5vR41RoMFN7mqEfDgQ==", + "node_modules/@urbit/aura": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@urbit/aura/-/aura-1.0.0.tgz", + "integrity": "sha512-IeP3uoDzZ0Rpn345auXK0y/BCcXTmpgAlOPbgf7n4eD35h56OnSoit1kuXKA21sWE19gFjK/wqZcz5ULjz2ADg==", + "engines": { + "node": ">=16", + "npm": ">=8" + }, + "peerDependencies": { + "big-integer": "^1.6.51" + } + }, + "node_modules/@urbit/js-http-api": { + "name": "@urbit/http-api", + "version": "3.0.0", + "resolved": "git+ssh://git@github.com/urbit/js-http-api.git#25c3f8f73bd8d6edfd4d5b9feffcfe27585e1ddf", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.12.5", "browser-or-node": "^1.3.0", @@ -2882,8 +2895,9 @@ "license": "MIT" }, "node_modules/big-integer": { - "version": "1.6.49", - "license": "Unlicense", + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", "engines": { "node": ">=0.6" } @@ -2896,10 +2910,6 @@ "node": ">=8" } }, - "node_modules/bn.js": { - "version": "4.12.0", - "license": "MIT" - }, "node_modules/brace-expansion": { "version": "1.1.11", "dev": true, @@ -6094,19 +6104,11 @@ "version": "4.17.21", "license": "MIT" }, - "node_modules/lodash.chunk": { - "version": "4.2.0", - "license": "MIT" - }, "node_modules/lodash.clonedeep": { "version": "4.5.0", "dev": true, "license": "MIT" }, - "node_modules/lodash.isequal": { - "version": "4.5.0", - "license": "MIT" - }, "node_modules/lodash.merge": { "version": "4.6.2", "dev": true, @@ -8437,15 +8439,6 @@ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, - "node_modules/urbit-ob": { - "version": "5.0.1", - "license": "MIT", - "dependencies": { - "bn.js": "^4.11.8", - "lodash.chunk": "^4.2.0", - "lodash.isequal": "^4.5.0" - } - }, "node_modules/uri-js": { "version": "4.4.1", "dev": true, @@ -10507,10 +10500,15 @@ "eslint-visitor-keys": "^2.0.0" } }, - "@urbit/http-api": { - "version": "2.4.5-debug", - "resolved": "https://registry.npmjs.org/@urbit/http-api/-/http-api-2.4.5-debug.tgz", - "integrity": "sha512-kMzQwNlrObEz4tEg90nlPRW69Tf+9rD3iIK/JgvsG3423x9oa4rFXobnFKBphXR/5bqd5vR41RoMFN7mqEfDgQ==", + "@urbit/aura": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@urbit/aura/-/aura-1.0.0.tgz", + "integrity": "sha512-IeP3uoDzZ0Rpn345auXK0y/BCcXTmpgAlOPbgf7n4eD35h56OnSoit1kuXKA21sWE19gFjK/wqZcz5ULjz2ADg==", + "requires": {} + }, + "@urbit/js-http-api": { + "version": "git+ssh://git@github.com/urbit/js-http-api.git#25c3f8f73bd8d6edfd4d5b9feffcfe27585e1ddf", + "from": "@urbit/js-http-api@git+https://github.com/urbit/js-http-api", "requires": { "@babel/runtime": "^7.12.5", "browser-or-node": "^1.3.0", @@ -10810,15 +10808,14 @@ "dev": true }, "big-integer": { - "version": "1.6.49" + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==" }, "binary-extensions": { "version": "2.2.0", "dev": true }, - "bn.js": { - "version": "4.12.0" - }, "brace-expansion": { "version": "1.1.11", "dev": true, @@ -12885,16 +12882,10 @@ "lodash": { "version": "4.17.21" }, - "lodash.chunk": { - "version": "4.2.0" - }, "lodash.clonedeep": { "version": "4.5.0", "dev": true }, - "lodash.isequal": { - "version": "4.5.0" - }, "lodash.merge": { "version": "4.6.2", "dev": true @@ -14325,14 +14316,6 @@ } } }, - "urbit-ob": { - "version": "5.0.1", - "requires": { - "bn.js": "^4.11.8", - "lodash.chunk": "^4.2.0", - "lodash.isequal": "^4.5.0" - } - }, "uri-js": { "version": "4.4.1", "dev": true, diff --git a/ui/package.json b/ui/package.json index 2367e7cf..33a16304 100644 --- a/ui/package.json +++ b/ui/package.json @@ -33,7 +33,8 @@ "@tlon/sigil-js": "^1.4.4", "@tloncorp/mock-http-api": "^1.2.0", "@types/lodash": "^4.14.172", - "@urbit/http-api": "^2.4.5-debug", + "@urbit/aura": "^1.0.0", + "@urbit/js-http-api": "git+https://github.com/urbit/js-http-api", "big-integer": "^1.6.48", "browser-cookies": "^1.2.0", "classnames": "^2.3.1", @@ -58,7 +59,6 @@ "react-hook-form": "^7.38.0", "react-router-dom": "^5.2.0", "slugify": "^1.6.0", - "urbit-ob": "^5.0.1", "zustand": "^3.7.2" }, "devDependencies": { diff --git a/ui/src/api.ts b/ui/src/api.ts index bc7815f4..381d819d 100644 --- a/ui/src/api.ts +++ b/ui/src/api.ts @@ -6,7 +6,7 @@ import Urbit, { Thread, UrbitHttpApiEvent, UrbitHttpApiEventType, -} from '@urbit/http-api'; +} from '@urbit/js-http-api'; import _ from 'lodash'; import { useLocalState } from '@/state/local'; import useSchedulerStore from './state/scheduler'; diff --git a/ui/src/components/AppInfo.tsx b/ui/src/components/AppInfo.tsx index b0003299..ad3a9c84 100644 --- a/ui/src/components/AppInfo.tsx +++ b/ui/src/components/AppInfo.tsx @@ -1,4 +1,4 @@ -import { chadIsRunning, Pike, Treaty } from '@urbit/api'; +import { chadIsRunning, Pike, Treaty } from '@/gear'; import clipboardCopy from 'clipboard-copy'; import React, { FC, useCallback, useState } from 'react'; import cn from 'classnames'; diff --git a/ui/src/components/Avatar.tsx b/ui/src/components/Avatar.tsx index 35e7174e..dbea5e1b 100644 --- a/ui/src/components/Avatar.tsx +++ b/ui/src/components/Avatar.tsx @@ -1,7 +1,7 @@ import classNames from 'classnames'; import React, { useMemo } from 'react'; import { sigil, reactRenderer } from '@tlon/sigil-js'; -import { deSig } from '@urbit/api'; +import { deSig } from '@urbit/aura'; import { darken, lighten, parseToHsla } from 'color2k'; import { useCurrentTheme } from '../state/local'; import { normalizeUrbitColor } from '@/logic/utils'; diff --git a/ui/src/components/DocketImage.tsx b/ui/src/components/DocketImage.tsx index 68212342..8ee86237 100644 --- a/ui/src/components/DocketImage.tsx +++ b/ui/src/components/DocketImage.tsx @@ -1,5 +1,5 @@ import React, { useState } from 'react'; -import { Docket } from '@urbit/api'; +import { Docket } from '@/gear'; import cn from 'classnames'; import { useTileColor } from '../tiles/useTileColor'; diff --git a/ui/src/components/PikeMeta.tsx b/ui/src/components/PikeMeta.tsx index 5877fbfa..4e3d9a54 100644 --- a/ui/src/components/PikeMeta.tsx +++ b/ui/src/components/PikeMeta.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Pike } from '@urbit/api'; +import { Pike } from '@/gear'; import { Attribute } from './Attribute'; diff --git a/ui/src/components/ProviderLink.tsx b/ui/src/components/ProviderLink.tsx index 702ff7a4..2e2a179f 100644 --- a/ui/src/components/ProviderLink.tsx +++ b/ui/src/components/ProviderLink.tsx @@ -1,7 +1,7 @@ import classNames from 'classnames'; import React from 'react'; import { Link, LinkProps } from 'react-router-dom'; -import { Provider } from '@urbit/api'; +import { Provider } from '@/gear'; import { ShipName } from './ShipName'; import { Avatar, AvatarSizes } from './Avatar'; import { Contact } from '@/types/contact'; diff --git a/ui/src/components/ProviderList.tsx b/ui/src/components/ProviderList.tsx index 61674b13..abd5e29b 100644 --- a/ui/src/components/ProviderList.tsx +++ b/ui/src/components/ProviderList.tsx @@ -1,5 +1,5 @@ import React, { MouseEvent, useCallback } from 'react'; -import { Provider } from '@urbit/api'; +import { Provider } from '@/gear'; import classNames from 'classnames'; import { MatchItem } from '../nav/Nav'; import { useRecentsStore } from '../nav/search/Home'; diff --git a/ui/src/components/ShipName.tsx b/ui/src/components/ShipName.tsx index 0342508a..b767c69a 100644 --- a/ui/src/components/ShipName.tsx +++ b/ui/src/components/ShipName.tsx @@ -1,4 +1,4 @@ -import { cite } from '@urbit/api'; +import { cite } from '@urbit/aura'; import React, { HTMLAttributes } from 'react'; import { useCalm } from '../state/settings'; import { useContact } from '../state/contact'; diff --git a/ui/src/components/TreatyMeta.tsx b/ui/src/components/TreatyMeta.tsx index 00974bb7..36185d3a 100644 --- a/ui/src/components/TreatyMeta.tsx +++ b/ui/src/components/TreatyMeta.tsx @@ -1,5 +1,6 @@ import React from 'react'; -import { daToDate, Treaty } from '@urbit/api'; +import { daToUnix, parseDa } from '@urbit/aura' +import { Treaty } from '@/gear'; import moment from 'moment'; import { Attribute } from './Attribute'; @@ -15,7 +16,7 @@ export function TreatyMeta(props: { treaty: Treaty }) { {ship}/{desk} - {moment(daToDate(cass.da)).format('YYYY.MM.DD')} + {moment(daToUnix(parseDa(cass.da))).format('YYYY.MM.DD')} {meta.map((d) => ( diff --git a/ui/src/gear/contacts/lib.ts b/ui/src/gear/contacts/lib.ts index 26b08c6b..7c024a2c 100644 --- a/ui/src/gear/contacts/lib.ts +++ b/ui/src/gear/contacts/lib.ts @@ -1,5 +1,5 @@ -import { Patp, Poke, Scry } from '../lib'; +import { Patp, Poke, Scry } from '@urbit/js-http-api'; import { Contact, ContactUpdateAdd, diff --git a/ui/src/gear/contacts/types.ts b/ui/src/gear/contacts/types.ts index 201aa243..75aa943c 100644 --- a/ui/src/gear/contacts/types.ts +++ b/ui/src/gear/contacts/types.ts @@ -1,4 +1,4 @@ -import { Path, Patp } from '../lib'; +import { Path, Patp } from '@urbit/js-http-api'; import { Resource } from '../groups'; export type ContactUpdate = diff --git a/ui/src/gear/deps.d.ts b/ui/src/gear/deps.d.ts index 0099f33f..e69de29b 100644 --- a/ui/src/gear/deps.d.ts +++ b/ui/src/gear/deps.d.ts @@ -1,8 +0,0 @@ - -declare module 'urbit-ob' { - - /** - * Convert a @p-encoded string to a decimal-encoded string. - */ - function patp2dec(name: string): string -} diff --git a/ui/src/gear/docket/lib.ts b/ui/src/gear/docket/lib.ts index f5c3e255..ebdce211 100644 --- a/ui/src/gear/docket/lib.ts +++ b/ui/src/gear/docket/lib.ts @@ -1,4 +1,4 @@ -import { Poke, Scry } from '../lib'; +import { Poke, Scry } from '@urbit/js-http-api'; import { Chad } from './types'; export function chadIsRunning(chad: Chad) { diff --git a/ui/src/gear/groups/lib.ts b/ui/src/gear/groups/lib.ts index 7e93ad3a..2d5d2d50 100644 --- a/ui/src/gear/groups/lib.ts +++ b/ui/src/gear/groups/lib.ts @@ -1,5 +1,6 @@ -import { deSig } from '../index'; -import { Enc, Path, Patp, PatpNoSig, Poke, Thread } from '../lib/types'; +import { deSig } from '@urbit/aura'; +import { Path, Patp, PatpNoSig, Poke, Thread } from '@urbit/js-http-api'; +import { Enc } from '@/gear'; import { Group, GroupPolicy, GroupPolicyDiff, GroupUpdateAddMembers, GroupUpdateAddTag, GroupUpdateChangePolicy, GroupUpdateRemoveGroup, GroupUpdateRemoveMembers, GroupUpdateRemoveTag, Resource, RoleTags, Tag } from './types'; import { GroupUpdate } from './update'; diff --git a/ui/src/gear/groups/update.ts b/ui/src/gear/groups/update.ts index a25c9a63..a5f025a1 100644 --- a/ui/src/gear/groups/update.ts +++ b/ui/src/gear/groups/update.ts @@ -1,4 +1,5 @@ -import { PatpNoSig, Path, ShipRank, Enc } from '../lib'; +import { PatpNoSig, Path } from '@urbit/js-http-api'; +import { ShipRank, Enc } from '@/gear'; import { roleTags } from './index'; export type RoleTags = typeof roleTags[number]; diff --git a/ui/src/gear/groups/view.ts b/ui/src/gear/groups/view.ts index c6e432b2..dced81d1 100644 --- a/ui/src/gear/groups/view.ts +++ b/ui/src/gear/groups/view.ts @@ -1,5 +1,5 @@ import { joinError, joinProgress, joinResult } from "."; -import {Patp} from "../lib"; +import { Patp } from '@urbit/js-http-api'; export type JoinError = typeof joinError[number]; diff --git a/ui/src/gear/hark/lib.ts b/ui/src/gear/hark/lib.ts index a470c671..e7f081db 100644 --- a/ui/src/gear/hark/lib.ts +++ b/ui/src/gear/hark/lib.ts @@ -1,6 +1,6 @@ import { BigInteger } from 'big-integer'; -import { Poke } from '../lib/types'; +import { Poke } from '@urbit/js-http-api'; import { HarkBin, HarkBinId, @@ -8,7 +8,7 @@ import { HarkLid, HarkPlace } from './types'; -import { decToUd } from '../lib'; +import { parseUd } from '@urbit/aura'; export const harkAction = (data: T): Poke => ({ app: 'hark-store', @@ -39,7 +39,7 @@ export const actOnNotification = ( ): Poke => harkAction({ [frond]: { - time: decToUd(intTime.toString()), + time: parseUd(intTime.toString()), bin } }); diff --git a/ui/src/gear/hood/lib.ts b/ui/src/gear/hood/lib.ts index 02d86868..346980a6 100644 --- a/ui/src/gear/hood/lib.ts +++ b/ui/src/gear/hood/lib.ts @@ -1,4 +1,4 @@ -import { Poke, Scry } from '../lib'; +import { Poke, Scry } from '@urbit/js-http-api'; import { Pike } from './types'; export const getPikes: Scry = { diff --git a/ui/src/gear/index.ts b/ui/src/gear/index.ts index 9ea47738..50825f78 100644 --- a/ui/src/gear/index.ts +++ b/ui/src/gear/index.ts @@ -17,6 +17,8 @@ export * as hood from './hood'; export * from './hood'; export * as docket from './docket'; export * from './docket'; +export * as utils from './utils'; +export * from './utils'; // TODO: Userspace Permissions // export * from './permissions'; diff --git a/ui/src/gear/invite/lib.ts b/ui/src/gear/invite/lib.ts index a5a5ca94..885ef787 100644 --- a/ui/src/gear/invite/lib.ts +++ b/ui/src/gear/invite/lib.ts @@ -1,4 +1,5 @@ -import { Poke, Serial } from "../lib"; +import { Poke } from '@urbit/js-http-api'; +import { Serial } from '@/gear'; import { InviteUpdate, InviteUpdateAccept, InviteUpdateDecline } from "./types"; export const inviteAction = (data: T): Poke => ({ diff --git a/ui/src/gear/invite/types.ts b/ui/src/gear/invite/types.ts index b3515bb4..e0e95745 100644 --- a/ui/src/gear/invite/types.ts +++ b/ui/src/gear/invite/types.ts @@ -1,4 +1,5 @@ -import { Serial, PatpNoSig, Path } from '../lib'; +import { PatpNoSig, Path } from '@urbit/js-http-api'; +import { Serial } from '@/gear'; import { Resource } from "../groups"; export type InviteUpdate = diff --git a/ui/src/gear/metadata/lib.ts b/ui/src/gear/metadata/lib.ts index ace81e8a..64b91fc7 100644 --- a/ui/src/gear/metadata/lib.ts +++ b/ui/src/gear/metadata/lib.ts @@ -1,4 +1,5 @@ -import { Path, Poke, uxToHex, PatpNoSig } from '../lib'; +import { parseUx } from '@urbit/aura'; +import { Path, Poke, PatpNoSig } from '@urbit/js-http-api'; import { MdAppName, Association, Metadata, MetadataUpdate, MetadataUpdateAdd, MetadataUpdateRemove, MetadataEditField, MetadataUpdateEdit } from './types'; export const METADATA_UPDATE_VERSION = 2; @@ -83,7 +84,7 @@ export const update = ( newMetadata: Partial ): Poke => { const metadata = { ...association.metadata, ...newMetadata }; - metadata.color = uxToHex(metadata.color); + metadata.color = parseUx(metadata.color); return metadataAction({ add: { group: association.group, diff --git a/ui/src/gear/metadata/types.ts b/ui/src/gear/metadata/types.ts index 828c83b1..0d83135c 100644 --- a/ui/src/gear/metadata/types.ts +++ b/ui/src/gear/metadata/types.ts @@ -1,4 +1,4 @@ -import { Path, Patp } from '../lib'; +import { Path, Patp } from '@urbit/js-http-api'; export type MdAppName = 'groups' | 'graph'; diff --git a/ui/src/gear/settings/lib.ts b/ui/src/gear/settings/lib.ts index 94fca9d5..7a868ec2 100644 --- a/ui/src/gear/settings/lib.ts +++ b/ui/src/gear/settings/lib.ts @@ -1,4 +1,4 @@ -import { Poke, Scry } from '../lib'; +import { Poke, Scry } from '@urbit/js-http-api'; import { PutBucket, Key, Bucket, DelBucket, Value, PutEntry, DelEntry, SettingsUpdate } from './types'; export const action = (data: T): Poke => ({ diff --git a/ui/src/gear/storage/lib.ts b/ui/src/gear/storage/lib.ts index 0a11d8db..dd8106b9 100644 --- a/ui/src/gear/storage/lib.ts +++ b/ui/src/gear/storage/lib.ts @@ -1,4 +1,4 @@ -import { Poke } from '@urbit/http-api'; +import { Poke } from '@urbit/js-http-api'; import { StorageUpdate, StorageUpdateCurrentBucket, StorageUpdateAddBucket, StorageUpdateRemoveBucket, StorageUpdateEndpoint, StorageUpdateAccessKeyId, StorageUpdateSecretAccessKey } from './types'; const s3Action = ( diff --git a/ui/src/gear/utils/BigIntArrayOrderedMap.ts b/ui/src/gear/utils/BigIntArrayOrderedMap.ts new file mode 100644 index 00000000..dcd1ec37 --- /dev/null +++ b/ui/src/gear/utils/BigIntArrayOrderedMap.ts @@ -0,0 +1,151 @@ +import produce, { immerable, castDraft, setAutoFreeze, enablePatches } from 'immer'; +import bigInt, { BigInteger } from 'big-integer'; + +setAutoFreeze(false); + +enablePatches(); + +export function stringToArr(str: string) { + return str.split('/').slice(1).map((ind) => { + return bigInt(ind); + }); +} + +export function arrToString(arr: BigInteger[]) { + let string = ''; + arr.forEach((key) => { + string = string + `/${key.toString()}`; + }); + return string; +} + +function sorted(a: BigInteger[], b: BigInteger[], reversed = false) { + const getSort = sortBigIntArr(a, b); + if (reversed) { + return getSort * -1; + } else { + return getSort; + } +} + +export function sortBigIntArr(a: BigInteger[], b: BigInteger[]) { + const aLen = a.length; + const bLen = b.length; + + const aCop = a.slice(0); + const bCop = b.slice(0); + aCop.reverse(); + bCop.reverse(); + + let i = 0; + while (i < aLen && i < bLen) { + if (aCop[i].lt(bCop[i])) { + return 1; + } else if (aCop[i].gt(bCop[i])) { + return -1; + } else { + i++; + } + } + + return bLen - aLen; +} + +export class BigIntArrayOrderedMap implements Iterable<[BigInteger[], V]> { + root: Record = {} + cachedIter: [BigInteger[], V][] | null = null; + [immerable] = true; + reversed = false; + + constructor(items: [BigInteger[], V][] = [], reversed = false) { + items.forEach(([key, val]) => { + this.set(key, val); + }); + this.reversed = reversed; + } + + get size() { + return Object.keys(this.root).length; + } + + get(key: BigInteger[]) { + return this.root[arrToString(key)] ?? null; + } + + gas(items: [BigInteger[], V][]) { + return produce(this, (draft) => { + items.forEach(([key, value]) => { + draft.root[arrToString(key)] = castDraft(value); + }); + draft.generateCachedIter(); + }, + (patches) => { + // console.log(`gassed with ${JSON.stringify(patches, null, 2)}`); + }); + } + + set(key: BigInteger[], value: V) { + return produce(this, (draft) => { + draft.root[arrToString(key)] = castDraft(value); + draft.cachedIter = null; + }); + } + + clear() { + return produce(this, (draft) => { + draft.cachedIter = []; + draft.root = {}; + }); + } + + has(key: BigInteger[]) { + return arrToString(key) in this.root; + } + + delete(key: BigInteger[]) { + const result = produce(this, (draft) => { + delete draft.root[arrToString(key)]; + draft.cachedIter = null; + }); + return result; + } + + [Symbol.iterator](): IterableIterator<[BigInteger[], V]> { + let idx = 0; + const result = this.generateCachedIter(); + return { + [Symbol.iterator]: this[Symbol.iterator], + next: (): IteratorResult<[BigInteger[], V]> => { + if (idx < result.length) { + return { value: result[idx++], done: false }; + } + return { done: true, value: null }; + } + }; + } + + peekLargest() { + const sorted = Array.from(this); + return sorted[0] as [BigInteger[], V] | null; + } + + peekSmallest() { + const sorted = Array.from(this); + return sorted[sorted.length - 1] as [BigInteger[], V] | null; + } + + keys() { + return Array.from(this).map(([k,v]) => k); + } + + generateCachedIter() { + if(this.cachedIter) { + return [...this.cachedIter]; + } + const result = Object.keys(this.root).map((key) => { + return [stringToArr(key), this.root[key]] as [BigInteger[], V]; + }).sort(([a], [b]) => sorted(a, b, this.reversed)); + this.cachedIter = result; + return [...result]; + } +} diff --git a/ui/src/gear/utils/BigIntOrderedMap.ts b/ui/src/gear/utils/BigIntOrderedMap.ts new file mode 100644 index 00000000..621c2e11 --- /dev/null +++ b/ui/src/gear/utils/BigIntOrderedMap.ts @@ -0,0 +1,116 @@ +import produce, { immerable, castDraft, setAutoFreeze, enablePatches } from 'immer'; +import bigInt, { BigInteger } from 'big-integer'; + +setAutoFreeze(false); + +enablePatches(); + +function sortBigInt(a: BigInteger, b: BigInteger) { + if (a.lt(b)) { + return 1; + } else if (a.eq(b)) { + return 0; + } else { + return -1; + } +} +export class BigIntOrderedMap implements Iterable<[BigInteger, V]> { + root: Record = {} + cachedIter: [BigInteger, V][] | null = null; + [immerable] = true; + + constructor(items: [BigInteger, V][] = []) { + items.forEach(([key, val]) => { + this.set(key, val); + }); + } + + get size() { + if(this.cachedIter) { + return this.cachedIter.length; + } + return this.generateCachedIter().length; + } + + get(key: BigInteger) { + return this.root[key.toString()] ?? null; + } + + gas(items: [BigInteger, V][]) { + return produce(this, (draft) => { + items.forEach(([key, value]) => { + draft.root[key.toString()] = castDraft(value); + }); + draft.cachedIter = null; + }, + (patches) => { + // console.log(`gassed with ${JSON.stringify(patches, null, 2)}`); + }); + } + + set(key: BigInteger, value: V) { + return produce(this, (draft) => { + draft.root[key.toString()] = castDraft(value); + draft.cachedIter = null; + }); + } + + clear() { + return produce(this, (draft) => { + draft.cachedIter = []; + draft.root = {}; + }); + } + + has(key: BigInteger) { + return key.toString() in this.root; + } + + delete(key: BigInteger) { + const result = produce(this, (draft) => { + delete draft.root[key.toString()]; + draft.cachedIter = null; + }); + return result; + } + + [Symbol.iterator](): IterableIterator<[BigInteger, V]> { + let idx = 0; + const result = this.generateCachedIter(); + return { + [Symbol.iterator]: this[Symbol.iterator], + next: (): IteratorResult<[BigInteger, V]> => { + if (idx < result.length) { + return { value: result[idx++], done: false }; + } + return { done: true, value: null }; + } + }; + } + + peekLargest() { + const sorted = Array.from(this); + return sorted[0] as [BigInteger, V] | null; + } + + peekSmallest() { + const sorted = Array.from(this); + return sorted[sorted.length - 1] as [BigInteger, V] | null; + } + + keys() { + return Array.from(this).map(([k,v]) => k); + } + + generateCachedIter() { + if(this.cachedIter) { + return [...this.cachedIter]; + } + const result = Object.keys(this.root).map((key) => { + const num = bigInt(key); + return [num, this.root[key]] as [BigInteger, V]; + }).sort(([a], [b]) => sortBigInt(a,b)); + this.cachedIter = result; + return [...result]; + } +} diff --git a/ui/src/gear/utils/index.ts b/ui/src/gear/utils/index.ts new file mode 100644 index 00000000..4fed660f --- /dev/null +++ b/ui/src/gear/utils/index.ts @@ -0,0 +1,2 @@ +export * from './lib'; +export * from './types'; \ No newline at end of file diff --git a/ui/src/gear/utils/lib.ts b/ui/src/gear/utils/lib.ts new file mode 100644 index 00000000..693da49f --- /dev/null +++ b/ui/src/gear/utils/lib.ts @@ -0,0 +1 @@ +export {} \ No newline at end of file diff --git a/ui/src/gear/utils/types.ts b/ui/src/gear/utils/types.ts new file mode 100644 index 00000000..84116f36 --- /dev/null +++ b/ui/src/gear/utils/types.ts @@ -0,0 +1,25 @@ +import { BigIntOrderedMap } from "./BigIntOrderedMap"; + +export type SetElement = S extends Set<(infer T)> ? T : never; +export type MapKey = M extends Map<(infer K), any> ? K : never; +export type MapValue = M extends Map ? V : never; + +/** + * Turns sets into arrays and maps into objects so we can send them over the wire + */ +export type Enc = + S extends Set ? + Enc>[] : + S extends Map ? + { [s: string]: Enc> } : + S extends object ? + { [K in keyof S]: Enc } : + S extends BigIntOrderedMap ? + { [index: string]: T } : + S; + + +export type ShipRank = 'czar' | 'king' | 'duke' | 'earl' | 'pawn'; + +// @uvH encoded string +export type Serial = string; diff --git a/ui/src/global.d.ts b/ui/src/global.d.ts index ce5215a8..ecefe09c 100644 --- a/ui/src/global.d.ts +++ b/ui/src/global.d.ts @@ -1,7 +1,3 @@ -declare module 'urbit-ob' { - export function isValidPatp(patp: string): boolean; -} - type Stringified = string & { [P in keyof T]: { '_ value': T[P] }; diff --git a/ui/src/logic/useSystemUpdate.tsx b/ui/src/logic/useSystemUpdate.tsx index 223f78b2..3ebad797 100644 --- a/ui/src/logic/useSystemUpdate.tsx +++ b/ui/src/logic/useSystemUpdate.tsx @@ -1,4 +1,4 @@ -import { kilnBump, Pike } from '@urbit/api'; +import { kilnBump, Pike } from '@/gear'; import { partition, pick } from 'lodash'; import { useCallback } from 'react'; import { useHistory } from 'react-router-dom'; diff --git a/ui/src/logic/utils.ts b/ui/src/logic/utils.ts index 6138b104..7db872ff 100644 --- a/ui/src/logic/utils.ts +++ b/ui/src/logic/utils.ts @@ -1,6 +1,6 @@ import { Yarn, isYarnEmph, isYarnShip } from '@/types/hark'; import { findLast } from 'lodash'; -import { Docket, DocketHref, Treaty } from '@urbit/api'; +import { Docket, DocketHref, Treaty } from '@/gear'; import { hsla, parseToHsla, parseToRgba } from 'color2k'; import _ from 'lodash'; import { differenceInDays, endOfToday, format } from 'date-fns'; @@ -44,13 +44,6 @@ export function handleDropdownLink( }; } -export function deSig(ship: string): string { - if (!ship) { - return ''; - } - return ship.replace('~', ''); -} - export function normalizeUrbitColor(color: string): string { if (color.startsWith('#')) { return color; diff --git a/ui/src/main.tsx b/ui/src/main.tsx index 736b6dad..8444e279 100644 --- a/ui/src/main.tsx +++ b/ui/src/main.tsx @@ -1,4 +1,4 @@ -import { preSig } from '@urbit/api'; +import { preSig } from '@urbit/aura'; import cookies from 'browser-cookies'; import React from 'react'; import ReactDOM from 'react-dom'; diff --git a/ui/src/mocks/mockContacts.ts b/ui/src/mocks/mockContacts.ts index d4a93607..3270acfe 100644 --- a/ui/src/mocks/mockContacts.ts +++ b/ui/src/mocks/mockContacts.ts @@ -1,4 +1,4 @@ -import { Rolodex } from '@urbit/api'; +import { Rolodex } from '@/gear'; const mockContacts: Rolodex = { '~finned-palmer': { diff --git a/ui/src/nav/SystemMenu.tsx b/ui/src/nav/SystemMenu.tsx index 5dd8259a..0884f762 100644 --- a/ui/src/nav/SystemMenu.tsx +++ b/ui/src/nav/SystemMenu.tsx @@ -3,7 +3,7 @@ import classNames from 'classnames'; import clipboardCopy from 'clipboard-copy'; import React, { HTMLAttributes, useCallback, useState } from 'react'; import { Link, Route, useHistory } from 'react-router-dom'; -import { Pike } from '@urbit/api'; +import { Pike } from '@/gear'; import { Adjust } from '../components/icons/Adjust'; import { usePike } from '../state/kiln'; import { disableDefault, handleDropdownLink } from '@/logic/utils'; diff --git a/ui/src/nav/notifications/Notification.tsx b/ui/src/nav/notifications/Notification.tsx index 683d81ba..2808e691 100644 --- a/ui/src/nav/notifications/Notification.tsx +++ b/ui/src/nav/notifications/Notification.tsx @@ -12,7 +12,7 @@ import { ShipName } from '../../components/ShipName'; import { DeskLink } from '../../components/DeskLink'; import { DocketImage } from '../../components/DocketImage'; import GroupAvatar from '../../components/GroupAvatar'; -import { Charge } from '@urbit/api'; +import { Charge } from '@/gear'; import { useSawRopeMutation } from '@/state/hark'; interface NotificationProps { diff --git a/ui/src/nav/search/Apps.tsx b/ui/src/nav/search/Apps.tsx index 7bcb7d64..5b255bf7 100644 --- a/ui/src/nav/search/Apps.tsx +++ b/ui/src/nav/search/Apps.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useEffect, useMemo } from 'react'; import { RouteComponentProps } from 'react-router-dom'; import fuzzy from 'fuzzy'; -import { Treaty } from '@urbit/api'; +import { Treaty } from '@/gear'; import { ShipName } from '../../components/ShipName'; import { useAllyTreaties } from '../../state/docket'; import { useAppSearchStore } from '../Nav'; diff --git a/ui/src/nav/search/Providers.tsx b/ui/src/nav/search/Providers.tsx index f861844c..39748166 100644 --- a/ui/src/nav/search/Providers.tsx +++ b/ui/src/nav/search/Providers.tsx @@ -1,8 +1,8 @@ import React, { useEffect, useMemo } from 'react'; import { RouteComponentProps } from 'react-router-dom'; import fuzzy from 'fuzzy'; -import { Provider, deSig } from '@urbit/api'; -import * as ob from 'urbit-ob'; +import { deSig, isValidPatp } from '@urbit/aura'; +import { Provider } from '@/gear'; import { MatchItem, useAppSearchStore } from '../Nav'; import { useAllies, useCharges } from '../../state/docket'; import { ProviderList } from '../../components/ProviderList'; @@ -59,14 +59,14 @@ export const Providers = ({ match }: ProvidersProps) => { ); const patp = `~${deSig(search) || ''}`; - const isValidPatp = ob.isValidPatp(patp); + const isValid = isValidPatp(patp); const results = useMemo(() => { if (!allies) { return []; } const exact = - isValidPatp && !Object.keys(allies).includes(patp) + isValid && !Object.keys(allies).includes(patp) ? [ { shipName: patp, @@ -109,7 +109,7 @@ export const Providers = ({ match }: ProvidersProps) => { })) : []; - const newProviderMatches = isValidPatp + const newProviderMatches = isValid ? [ { url: `/search/${patp}/apps`, @@ -128,7 +128,7 @@ export const Providers = ({ match }: ProvidersProps) => { ), }); } - }, [results, patp, isValidPatp]); + }, [results, patp, isValid]); return (
; -const credentials = (json: S3Update, state: StorageState): StorageState => { +const credentials = (json: StorageUpdate, state: StorageState): StorageState => { const data = _.get(json, 'credentials', false); if (data) { state.s3.credentials = data; @@ -13,7 +13,7 @@ const credentials = (json: S3Update, state: StorageState): StorageState => { return state; }; -const configuration = (json: S3Update, state: StorageState): StorageState => { +const configuration = (json: StorageUpdate, state: StorageState): StorageState => { const data = _.get(json, 'configuration', false); if (data) { state.s3.configuration = { @@ -25,7 +25,7 @@ const configuration = (json: S3Update, state: StorageState): StorageState => { return state; }; -const currentBucket = (json: S3Update, state: StorageState): StorageState => { +const currentBucket = (json: StorageUpdate, state: StorageState): StorageState => { const data = _.get(json, 'setCurrentBucket', false); if (data && state.s3) { state.s3.configuration.currentBucket = data; @@ -33,7 +33,7 @@ const currentBucket = (json: S3Update, state: StorageState): StorageState => { return state; }; -const region = (json: S3Update, state: StorageState): StorageState => { +const region = (json: StorageUpdate, state: StorageState): StorageState => { const data = _.get(json, 'setRegion', false); if (data && state.s3) { state.s3.configuration.region = data; @@ -41,7 +41,7 @@ const region = (json: S3Update, state: StorageState): StorageState => { return state; }; -const addBucket = (json: S3Update, state: StorageState): StorageState => { +const addBucket = (json: StorageUpdate, state: StorageState): StorageState => { const data = _.get(json, 'addBucket', false); if (data) { state.s3.configuration.buckets = state.s3.configuration.buckets.add(data); @@ -49,7 +49,7 @@ const addBucket = (json: S3Update, state: StorageState): StorageState => { return state; }; -const removeBucket = (json: S3Update, state: StorageState): StorageState => { +const removeBucket = (json: StorageUpdate, state: StorageState): StorageState => { const data = _.get(json, 'removeBucket', false); if (data) { state.s3.configuration.buckets.delete(data); @@ -57,7 +57,7 @@ const removeBucket = (json: S3Update, state: StorageState): StorageState => { return state; }; -const endpoint = (json: S3Update, state: StorageState): StorageState => { +const endpoint = (json: StorageUpdate, state: StorageState): StorageState => { const data = _.get(json, 'setEndpoint', false); if (data && state.s3.credentials) { state.s3.credentials.endpoint = data; @@ -65,7 +65,7 @@ const endpoint = (json: S3Update, state: StorageState): StorageState => { return state; }; -const accessKeyId = (json: S3Update, state: StorageState): StorageState => { +const accessKeyId = (json: StorageUpdate, state: StorageState): StorageState => { const data = _.get(json, 'setAccessKeyId', false); if (data && state.s3.credentials) { state.s3.credentials.accessKeyId = data; @@ -73,7 +73,7 @@ const accessKeyId = (json: S3Update, state: StorageState): StorageState => { return state; }; -const secretAccessKey = (json: S3Update, state: StorageState): StorageState => { +const secretAccessKey = (json: StorageUpdate, state: StorageState): StorageState => { const data = _.get(json, 'setSecretAccessKey', false); if (data && state.s3.credentials) { state.s3.credentials.secretAccessKey = data; diff --git a/ui/src/tiles/Tile.tsx b/ui/src/tiles/Tile.tsx index e9d5848f..2fcce08d 100644 --- a/ui/src/tiles/Tile.tsx +++ b/ui/src/tiles/Tile.tsx @@ -1,7 +1,7 @@ import classNames from 'classnames'; import React, { FunctionComponent } from 'react'; import { useDrag } from 'react-dnd'; -import { chadIsRunning } from '@urbit/api'; +import { chadIsRunning } from '@/gear'; import { TileMenu } from './TileMenu'; import { Spinner } from '../components/Spinner'; import { getAppHref } from '@/logic/utils'; diff --git a/ui/src/tiles/TileMenu.tsx b/ui/src/tiles/TileMenu.tsx index bd6d1f90..2df238aa 100644 --- a/ui/src/tiles/TileMenu.tsx +++ b/ui/src/tiles/TileMenu.tsx @@ -2,7 +2,7 @@ import React, { ReactElement, useCallback, useState } from 'react'; import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; import classNames from 'classnames'; import { Link } from 'react-router-dom'; -import { Chad, chadIsRunning } from '@urbit/api'; +import { Chad, chadIsRunning } from '@/gear'; import useDocketState from '../state/docket'; import { disableDefault, handleDropdownLink } from '@/logic/utils'; import { useMedia } from '../logic/useMedia';