on main help sign in
fix app/ally subscription and use monkey
2d2698cJohn Hyde
base-desk/mar/kelvin.hoondeleted−28
@@ -1,28 +0,0 @@
1−|_ kal=waft:clay
2−++ grow
3− |%
4− ++ mime `^mime`[/text/x-kelvin (as-octs:mimes:html hoon)]
5− ++ noun kal
6− ++ hoon
7− %+ rap 3
8− %+ turn
9− %+ sort
10− ~(tap in (waft-to-wefts:clay kal))
11− |= [a=weft b=weft]
12− ?: =(lal.a lal.b)
13− (gte num.a num.b)
14− (gte lal.a lal.b)
15− |= =weft
16− (rap 3 '[%' (scot %tas lal.weft) ' ' (scot %ud num.weft) ']\0a' ~)
17− ::
18− ++ txt (to-wain:format hoon)
19− --
20−++ grab
21− |%
22− ++ noun waft:clay
23− ++ mime
24− |= [=mite len=@ud tex=@]
25− (cord-to-waft:clay tex)
26− --
27−++ grad %noun
28−--
desk/app/astrolabe.hoon+5−1
@@ -59,7 +59,11 @@
59 ?. ?=(?(%0 %1 %2) -.old) `old
59 ?. ?=(?(%0 %1 %2) -.old) `old
60 (on-naive-state:hc get-nas:hc)
60 (on-naive-state:hc get-nas:hc)
61 ?> ?=(%3 -.old)
61 ?> ?=(%3 -.old)
62+ =/ cards=(list card)
63+ =/ ug-vase !>([/astrolabe '/apps/astrolabe/#'])
64+ [%pass /monkey-ug %agent [our.bowl %monkey] %poke [%bind-ug ug-vase]]~
65+ =. cards (weld cards-01 cards)
62− [cards-01 this(state old)]
66+ [cards this(state old)]
63++ on-poke on-poke:def
67++ on-poke on-poke:def
64++ on-watch on-watch:def
68++ on-watch on-watch:def
65++ on-leave on-leave:def
69++ on-leave on-leave:def
ui/scraps/scry thoughtsnew file+5
@@ -0,0 +1,5 @@
1+:: get list of group ids
2+.^((set [ship term]) %gy /(scot %p our)/group-store/(scot %da now)/groups)
3+.^(* %gx /(scot %p our)/group-store/(scot %da now)/groups/ship/~poster-hoster-midlev-mindyr/astrolabe/noun)
4+
5+web+urbitgraph://astrolabe/ship/~midlev-mindyr
ui/src/components/extensions/AppIcon.svelte+8−8
@@ -9,6 +9,8 @@
9
9
10 $: desk = path.split('/')[1];
10 $: desk = path.split('/')[1];
11 $: appPromise = getAppByPath(path);
11 $: appPromise = getAppByPath(path);
12+ $: appUrbitGraphLink = `web+urbitgraph://${path}`;
13+ $: appLink = `/apps/grid/perma?ext=${appUrbitGraphLink}`;
12
14
13 let overrideText = null;
15 let overrideText = null;
14
16
@@ -25,25 +27,24 @@
25</script>
27</script>
26
28
27<div class="w-20 text-sm text-center">
29<div class="w-20 text-sm text-center">
28− <div class="w-20 h-20 border border-gray-300 bg-gray-600 text-white rounded-lg overflow-hidden">
30+ <a class="block w-20 h-20 border border-gray-300 bg-gray-600 text-white rounded-lg overflow-hidden"
31+ href={appLink} target="_blank">
29 {#await appPromise}
32 {#await appPromise}
30 <div class="flex h-full items-center">
33 <div class="flex h-full items-center">
31 <LoadingSpinner />
34 <LoadingSpinner />
32 </div>
35 </div>
33 {:then app}
36 {:then app}
34− <a style:background-color={uxToHex(app.color)} class="block h-full w-full text-sm"
37+ <div style:background-color={uxToHex(app.color)} class="h-full w-full text-sm">
35− href="web+urbitgraph://{path}" target="_blank"
36− >
37 {#if app.image}
38 {#if app.image}
38 <img src={app.image} alt="app icon"
39 <img src={app.image} alt="app icon"
39 class="aspect-square object-cover w-full h-full"
40 class="aspect-square object-cover w-full h-full"
40 />
41 />
41 {/if}
42 {/if}
42− </a>
43+ </div>
43 {:catch}
44 {:catch}
44− {path}
45+ {path}
45 {/await}
46 {/await}
46− </div>
47+ </a>
47 <CopyToClipboard text={path} on:copy={handleSuccessfullyCopied} on:fail={handleFailedCopy} let:copy>
48 <CopyToClipboard text={path} on:copy={handleSuccessfullyCopied} on:fail={handleFailedCopy} let:copy>
48 <span on:click={copy}>
49 <span on:click={copy}>
49 {#if overrideText}
50 {#if overrideText}
@@ -52,7 +53,6 @@
52 {#await appPromise}
53 {#await appPromise}
53 {desk}
54 {desk}
54 {:then app}
55 {:then app}
55− <!-- {desk} -->
56 {app.title}
56 {app.title}
57 {/await}
57 {/await}
58 {/if}
58 {/if}
ui/src/components/extensions/GroupsShipViewExtension.svelte+3−40
@@ -1,52 +1,15 @@
1<script lang="ts">
1<script lang="ts">
2− import SvelteMarkdown from 'svelte-markdown';
3 import { uxToHex } from 'lib/utils';
2 import { uxToHex } from 'lib/utils';
4 import { normalizeId } from 'lib/id';
3 import { normalizeId } from 'lib/id';
5 import store from 'stores/store';
4 import store from 'stores/store';
6− import Sigil from "@/common/Sigil.svelte";
7 import ExtensionWindow from '@/ExtensionWindow.svelte';
5 import ExtensionWindow from '@/ExtensionWindow.svelte';
8
6
9 export let patp: string;
7 export let patp: string;
10− $: contact = $store.contacts[normalizeId(patp)];
11− $: color = contact && uxToHex(contact.color);
12− $: somethingToShow = contact && (contact.nickname || contact.cover || contact.avatar || contact.bio);
13</script>
8</script>
14
9
15−{#if somethingToShow}
10+{#if patp}
16− <ExtensionWindow name="Groups">
11+ <ExtensionWindow name="Shared Groups">
17− <svelte:fragment slot="top-bar">
12+ TODO: implement for Groups 2, don't bother with Groups 1
18− {#if contact.nickname}
19− <div class="grow m-1 flex justify-center align-center">
20− {contact.nickname}
21− </div>
22− {/if}
23− </svelte:fragment>
24− <div class="flex flex-wrap py-2">
25− {#if contact.cover}
26− <img src={contact.cover} alt="profile cover image"
27− class="m-1 -mb-16 w-full h-48 border border-black rounded-lg object-cover min-w-0"
28− />
29− {/if}
30− <div class="flex flex-wrap md:flex-nowrap justify-center">
31− <div class="mx-8 max-w-[128px] min-w-[128px] h-fit grow overflow-hidden rounded-lg">
32− {#if contact.avatar}
33− <img src={contact.avatar} alt="custom avatar"
34− class="aspect-square object-cover max-w-[128px] min-w-0"
35− />
36− {:else}
37− <Sigil patp={patp} size={128} bgColor={color} useNew />
38− {/if}
39− </div>
40− {#if contact.bio}
41− <div class="text-sm p-3 bg-white rounded-tl-lg rounded-br-lg whitespace-pre-line">
42− <SvelteMarkdown source={contact.bio} />
43− </div>
44− {/if}
45− </div>
46− <!-- <p class="whitespace-pre-line">
47− {contact.bio}
48− </p> -->
49− </div>
50 </ExtensionWindow>
13 </ExtensionWindow>
51{/if}
14{/if}
52
15
ui/src/components/extensions/ProfileShipViewExtension.sveltenew file+52
@@ -0,0 +1,52 @@
1+<script lang="ts">
2+ import SvelteMarkdown from 'svelte-markdown';
3+ import { uxToHex } from 'lib/utils';
4+ import { normalizeId } from 'lib/id';
5+ import store from 'stores/store';
6+ import Sigil from "@/common/Sigil.svelte";
7+ import ExtensionWindow from '@/ExtensionWindow.svelte';
8+
9+ export let patp: string;
10+ $: contact = $store.contacts[normalizeId(patp)];
11+ $: color = contact && uxToHex(contact.color);
12+ $: somethingToShow = contact && (contact.nickname || contact.cover || contact.avatar || contact.bio);
13+</script>
14+
15+{#if somethingToShow}
16+ <ExtensionWindow name="Groups Profile">
17+ <svelte:fragment slot="top-bar">
18+ {#if contact.nickname}
19+ <div class="grow m-1 flex justify-center align-center">
20+ {contact.nickname}
21+ </div>
22+ {/if}
23+ </svelte:fragment>
24+ <div class="flex flex-wrap py-2">
25+ {#if contact.cover}
26+ <img src={contact.cover} alt="profile cover image"
27+ class="m-1 -mb-16 w-full h-48 border border-black rounded-lg object-cover min-w-0"
28+ />
29+ {/if}
30+ <div class="flex flex-wrap md:flex-nowrap justify-center">
31+ <div class="mx-8 max-w-[128px] min-w-[128px] h-fit grow overflow-hidden rounded-lg">
32+ {#if contact.avatar}
33+ <img src={contact.avatar} alt="custom avatar"
34+ class="aspect-square object-cover max-w-[128px] min-w-0"
35+ />
36+ {:else}
37+ <Sigil patp={patp} size={128} bgColor={color} useNew />
38+ {/if}
39+ </div>
40+ {#if contact.bio}
41+ <div class="text-sm p-3 bg-white rounded-tl-lg rounded-br-lg whitespace-pre-line">
42+ <SvelteMarkdown source={contact.bio} />
43+ </div>
44+ {/if}
45+ </div>
46+ <!-- <p class="whitespace-pre-line">
47+ {contact.bio}
48+ </p> -->
49+ </div>
50+ </ExtensionWindow>
51+{/if}
52+
ui/src/components/pals/PalsShipViewExtension.svelte+2−1
@@ -10,9 +10,10 @@
10 $: pal = getPal($store.pals, patp);
10 $: pal = getPal($store.pals, patp);
11 $: palAdded = pal.status == 'outgoing' || pal.status == 'mutual';
11 $: palAdded = pal.status == 'outgoing' || pal.status == 'mutual';
12 $: [buttonText, buttonFunc] = palAdded ? ['Remove Pal', removePal] : ['Add Pal', addPal];
12 $: [buttonText, buttonFunc] = palAdded ? ['Remove Pal', removePal] : ['Add Pal', addPal];
13+ $: itsUs = patp.substring(1) === window.ship;
13</script>
14</script>
14
15
15−{#if $store.palsInstalled}
16+{#if $store.palsInstalled && !itsUs}
16 <ExtensionWindow name="%pals" halfWidth>
17 <ExtensionWindow name="%pals" halfWidth>
17 <svelte:fragment slot="top-bar">
18 <svelte:fragment slot="top-bar">
18 <div class="grow ml-1 my-2 flex justify-center align-center">
19 <div class="grow ml-1 my-2 flex justify-center align-center">
ui/src/components/ship_view/ShipChildrenTabs.svelte+3−3
@@ -16,7 +16,7 @@
16 $: shipClass = clan(patp);
16 $: shipClass = clan(patp);
17 $: azPoint = ['galaxy', 'star', 'planet'].includes(shipClass);
17 $: azPoint = ['galaxy', 'star', 'planet'].includes(shipClass);
18 $: canSpawnPoints = ['galaxy', 'star'].includes(shipClass);
18 $: canSpawnPoints = ['galaxy', 'star'].includes(shipClass);
19− $: spawnableClassPlural = !canSpawnPoints ? '' : (shipClass === 'galaxy' ? 'Stars' : 'Planets');
19+ $: spawnableClassPlural = !canSpawnPoints ? '' : (shipClass === 'galaxy' ? 'Star' : 'Planet');
20 $: {
20 $: {
21 rawSpawnedPoints = null;
21 rawSpawnedPoints = null;
22 if (canSpawnPoints) {
22 if (canSpawnPoints) {
@@ -37,7 +37,7 @@
37 $: trueSpawnedCount = spawnedCount || spawnedPoints.length;
37 $: trueSpawnedCount = spawnedCount || spawnedPoints.length;
38 $: spawnedTab = {
38 $: spawnedTab = {
39 name: 'Spawned',
39 name: 'Spawned',
40− plural: 'Spawned ' + spawnableClassPlural + (trueSpawnedCount > 1 ? 's' : ''),
40+ plural: 'Spawned ' + spawnableClassPlural + (trueSpawnedCount == 1 ? '' : 's'),
41 promise: rawSpawnedPointsPromise,
41 promise: rawSpawnedPointsPromise,
42 points: spawnedPoints,
42 points: spawnedPoints,
43 count: trueSpawnedCount,
43 count: trueSpawnedCount,
@@ -45,7 +45,7 @@
45 $: moons = filterIdsForChildMoons($store.peers, patp).map(patpToShip);
45 $: moons = filterIdsForChildMoons($store.peers, patp).map(patpToShip);
46 $: moonsTab = {
46 $: moonsTab = {
47 name: 'Moons',
47 name: 'Moons',
48− plural: 'Known Moon' + (moons.length > 1 ? 's' : ''),
48+ plural: 'Known Moon' + (moons.length == 1 ? '' : 's'),
49 promise: true,
49 promise: true,
50 points: moons,
50 points: moons,
51 count: moons.length,
51 count: moons.length,
ui/src/components/ship_view/ShipView.svelte+5−3
@@ -14,9 +14,10 @@
14 import CollapsibleContent from '@/CollapsibleContent.svelte';
14 import CollapsibleContent from '@/CollapsibleContent.svelte';
15 import ShipChain from './ShipChain.svelte';
15 import ShipChain from './ShipChain.svelte';
16 import ShipChildrenTabs from './ShipChildrenTabs.svelte';
16 import ShipChildrenTabs from './ShipChildrenTabs.svelte';
17− import GroupsShipViewExtension from '@/extensions/GroupsShipViewExtension.svelte';
17+ import ProfileShipViewExtension from '~/components/extensions/ProfileShipViewExtension.svelte';
18− import AppsShipViewExtension from '@/extensions/AppsShipViewExtension.svelte';
19 import PalsShipViewExtension from '@/pals/PalsShipViewExtension.svelte';
18 import PalsShipViewExtension from '@/pals/PalsShipViewExtension.svelte';
19+ import AppsShipViewExtension from '@/extensions/AppsShipViewExtension.svelte';
20+ // import GroupsShipViewExtension from '~/components/extensions/GroupsShipViewExtension.svelte';
20
21
21 export let patp: string;
22 export let patp: string;
22
23
@@ -200,9 +201,10 @@
200 </div>
201 </div>
201 </div>
202 </div>
202 <div class="flex flex-wrap shrink min-h-0 h-fit -m-1 mt-2 justify-evenly justify-items-center">
203 <div class="flex flex-wrap shrink min-h-0 h-fit -m-1 mt-2 justify-evenly justify-items-center">
203− <GroupsShipViewExtension {patp} />
204+ <ProfileShipViewExtension {patp} />
204 <PalsShipViewExtension {patp} />
205 <PalsShipViewExtension {patp} />
205 <AppsShipViewExtension {patp} />
206 <AppsShipViewExtension {patp} />
207+ <!-- <GroupsShipViewExtension {patp} /> -->
206 </div>
208 </div>
207 {#if azPoint}
209 {#if azPoint}
208 <div class="mt-5 border-t">
210 <div class="mt-5 border-t">
ui/src/lib/api.ts+1−1
@@ -88,7 +88,7 @@ export function subscribeToContacts(e: (data: any) => void): Promise<any> {
88 console.error(`Subscription to contact-store/all just get "err"`);
88 console.error(`Subscription to contact-store/all just get "err"`);
89 },
89 },
90 quit: () => {
90 quit: () => {
91− console.error(`Subscription to contact-store/all just get "quit"`)
91+ console.error(`Subscription to contact-store/all just get "quit"`);
92 }
92 }
93 })
93 })
94}
94}
ui/src/lib/apps.ts+106−6
@@ -1,5 +1,8 @@
1+import _get from 'lodash/get';
2+import { compose } from 'lodash/fp';
1import type { Apps } from 'types/store';
3import type { Apps } from 'types/store';
2import store from 'stores/store';
4import store from 'stores/store';
5+import { sleep } from 'lib/utils';
3import { api } from 'lib/api';
6import { api } from 'lib/api';
4import { normalizeId } from 'lib/id';
7import { normalizeId } from 'lib/id';
5
8
@@ -7,17 +10,26 @@ export function getProvidedApps(patp: string): Promise<any> {
7 return api.scry<any>({ app: 'treaty', path: `/treaties/${patp}` });
10 return api.scry<any>({ app: 'treaty', path: `/treaties/${patp}` });
8}
11}
9
12
13+// Unused, use subscription
10export function getKnownApps(): Promise<any> {
14export function getKnownApps(): Promise<any> {
11 return api.scry<any>({ app: 'treaty', path: `/allies` }).then(processAllies);
15 return api.scry<any>({ app: 'treaty', path: `/allies` }).then(processAllies);
12}
16}
13
17
14export function getApp(ship: string, desk: string): Promise<any> {
18export function getApp(ship: string, desk: string): Promise<any> {
15− return api.scry<any>({ app: 'treaty', path: `/treaty/${normalizeId(ship)}/${desk}` });
19+ return getAppByPath(`${normalizeId(ship)}/${desk}`);
16}
20}
17
21
18// @param path - ship/desk e.g. ~mister-dister-dozzod-dozzod/webterm
22// @param path - ship/desk e.g. ~mister-dister-dozzod-dozzod/webterm
19−export function getAppByPath(path: string): Promise<any> {
23+export async function getAppByPath(path: string, retry: number = 2): Promise<any> {
24+ try {
20− return api.scry<any>({ app: 'treaty', path: `/treaty/${path}` });
25+ return await api.scry<any>({ app: 'treaty', path: `/treaty/${path}` });
26+ } catch {
27+ if (retry > 0) {
28+ console.log(`Got error when fetching app: ${path}.\nTrying at most ${retry} more time(s)`);
29+ await sleep(750);
30+ return getAppByPath(path, retry - 1);
31+ }
32+ }
21}
33}
22
34
23export function getInstalledApps(): Promise<any> {
35export function getInstalledApps(): Promise<any> {
@@ -30,7 +42,6 @@ export async function sendAllyUpdate(cmd: string, ship: string) {
30 mark: 'ally-update-0',
42 mark: 'ally-update-0',
31 json: { [cmd]: normalizeId(ship) },
43 json: { [cmd]: normalizeId(ship) },
32 });
44 });
33− // store.refreshKnownApps();
34 return result;
45 return result;
35}
46}
36
47
@@ -49,7 +60,6 @@ export async function addAllies(ships: string[]) {
49 results = results.filter(r => r.status === 'fulfilled').map((r) => {
60 results = results.filter(r => r.status === 'fulfilled').map((r) => {
50 return (r as PromiseFulfilledResult<any>).value;
61 return (r as PromiseFulfilledResult<any>).value;
51 });
62 });
52− store.refreshKnownApps();
53 return results;
63 return results;
54}
64}
55
65
@@ -60,7 +70,6 @@ export async function delAllies(ships: string[], retry: number = 0) {
60 results = results.filter(r => r.status === 'fulfilled').map((r) => {
70 results = results.filter(r => r.status === 'fulfilled').map((r) => {
61 return (r as PromiseFulfilledResult<any>).value;
71 return (r as PromiseFulfilledResult<any>).value;
62 });
72 });
63− store.refreshKnownApps();
64 return results;
73 return results;
65}
74}
66
75
@@ -73,3 +82,94 @@ export function processAllies(rawPals: any): Apps {
73 set,
82 set,
74 };
83 };
75}
84}
85+
86+export type AllyUpdate = AllyUpdateIni | AllyUpdateAdd | AllyUpdateDel | AllyUpdateNew;
87+
88+export interface AllyUpdateIni {
89+ ini: {
90+ [ship: string]: string[];
91+ }
92+}
93+
94+export interface AllyUpdateNew {
95+ new: {
96+ ship: string;
97+ alliance: string[];
98+ }
99+}
100+
101+export interface AllyUpdateAdd {
102+ add: string;
103+}
104+
105+export interface AllyUpdateDel {
106+ del: string;
107+}
108+
109+export function handleAllyUpdateIni(json: AllyUpdate, apps: Apps): Apps {
110+ const data = _get(json, 'ini', false);
111+ if (data) {
112+ apps = processAllies(json)
113+ }
114+ return apps;
115+};
116+
117+export function handleAllyUpdateNew(json: AllyUpdate, apps: Apps): Apps {
118+ const data = _get(json, 'new', false);
119+ if (data) {
120+ apps = processAllies({
121+ ini: {
122+ ...apps.allies,
123+ [data.ship]: data.alliance,
124+ }
125+ });
126+ }
127+ return apps;
128+};
129+
130+export function handleAllyUpdateAdd(json: AllyUpdate, apps: Apps): Apps {
131+ const data = _get(json, 'add', false);
132+ if (data) {
133+ apps = processAllies({
134+ ini: {
135+ ...apps.allies,
136+ [data]: [],
137+ }
138+ });
139+ }
140+ return apps;
141+};
142+
143+export function handleAllyUpdateDel(json: AllyUpdate, apps: Apps): Apps {
144+ const data = _get(json, 'del', false);
145+ if (data && (data in apps.allies)) {
146+ delete apps.allies[data];
147+ apps = processAllies({
148+ ini: apps.allies,
149+ });
150+ }
151+ return apps;
152+};
153+
154+export function handleAllyUpdateEvent(updateApps) {
155+ return (allyUpdate: AllyUpdate) => {
156+ console.log(`received ally-update: ${Object.keys(allyUpdate)}`);
157+ const reducers = [handleAllyUpdateIni, handleAllyUpdateNew, handleAllyUpdateAdd, handleAllyUpdateDel];
158+ const reducer = compose(reducers.map(r => sta => r(allyUpdate, sta)));
159+ updateApps(reducer);
160+ };
161+}
162+
163+export function subscribeToApps(e: (data: any) => void): Promise<any> {
164+ return api.subscribe({
165+ app: 'treaty',
166+ path: '/allies',
167+ event: handleAllyUpdateEvent(e),
168+ err: () => {
169+ console.error(`Subscription to treaty/all just got "err"`);
170+ },
171+ quit: () => {
172+ console.error(`Subscription to treaty/all just got "quit"`);
173+ }
174+ })
175+}
ui/src/lib/utils.ts+5−1
@@ -54,4 +54,8 @@ export function uxToHex(ux: string) {
54
54
55 value = value.replace('.', '').padStart(6, '0');
55 value = value.replace('.', '').padStart(6, '0');
56 return '#' + value;
56 return '#' + value;
57−}
57+}
58+
59+export function sleep(ms) {
60+ return new Promise(resolve => setTimeout(resolve, ms));
61+}
ui/src/stores/store.ts+11−7
@@ -2,11 +2,11 @@ import { writable } from 'svelte/store';
2
2
3import type { Rolodex } from '@urbit/api';
3import type { Rolodex } from '@urbit/api';
4import { subscribeToContacts, getPeers } from 'lib/api';
4import { subscribeToContacts, getPeers } from 'lib/api';
5−import type { StoreState } from 'types/store';
5+import type { StoreState, Apps } from 'types/store';
6import { normalizeId } from 'lib/id';
6import { normalizeId } from 'lib/id';
7import { setStoreKey } from 'lib/utils';
7import { setStoreKey } from 'lib/utils';
8import { getPals } from 'lib/pals';
8import { getPals } from 'lib/pals';
9−import { getKnownApps } from 'lib/apps';
9+import { subscribeToApps } from 'lib/apps';
10
10
11const initStore: StoreState = {
11const initStore: StoreState = {
12 contacts: {},
12 contacts: {},
@@ -68,11 +68,15 @@ function refreshPals() {
68}
68}
69refreshPals();
69refreshPals();
70
70
71−function refreshKnownApps() {
71+function updateApps(callback: (apps) => Apps): void {
72− return getKnownApps().then((apps) => {
72+ update($store => {
73− setStoreKey(store, 'apps', apps);
73+ return {
74+ ...$store,
75+ apps: callback($store.apps),
76+ };
74 });
77 });
75}
78}
76−refreshKnownApps();
77
79
80+subscribeToApps(updateApps);
81+
78−export default { subscribe, set, searchPatp, searchSigil, refreshPals, refreshKnownApps, reset };
82+export default { subscribe, set, searchPatp, searchSigil, refreshPals, reset };