Merge pull request #16 from urbit/t/merge-agent-clay

grid: migrate upstream changes for compatibility with 417 clay + kiln
This commit is contained in:
Hunter Miller 2022-12-06 08:15:46 -06:00 committed by GitHub
commit d882b0dce0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 585 additions and 492 deletions

View File

@ -3,7 +3,7 @@
|%
+$ card card:agent:gall
+$ app-state
$: %2
$: %3
:: local
charges=(map desk charge)
==
@ -42,7 +42,7 @@
++ on-init
^- (quip card _this)
:_ this
:~ (~(watch-our pass /kiln) %hood /kiln/vats)
:~ ~(tire pass /tire)
(~(connect pass /eyre) [~ /] %docket)
(~(wait pass /init) (add 1 now.bowl))
(~(connect pass /eyre) [~ /apps] %docket)
@ -54,20 +54,21 @@
|^
=+ !<(old=app-states vase)
=? old ?=(?(~ ^) -.old) [%1 old]
=^ cards old
=^ cards-1 old
?. ?=(%1 -.old) `old
=/ rein=cage kiln-rein+!>([%base %.y ~ ~])
=/ nuke=cage kiln-uninstall+!>(%hodl)
:_ old(- %2)
:~ [%pass /rein %agent [our.bowl %hood] %poke rein]
[%pass /nuke %agent [our.bowl %hood] %poke nuke]
==
?> ?=(%2 -.old)
`old(- %2)
=^ cards-2 old
?. ?=(%2 -.old) `old
:_ old(- %3) :_ ~
~(tire pass /tire)
?> ?=(%3 -.old)
=/ cards-tire [~(tire pass /tire) ~]
=. -.state old
:: inflate-cache needs to be called after the state is set
::
=. +.state inflate-cache
[cards this]
[:(weld cards-1 cards-2 cards-tire) this]
::
++ inflate-cache
^- cache
@ -81,9 +82,11 @@
$^ state-0-ket
$% state-0-sig
state-1
state-2
app-state
==
::
+$ state-2 [%2 (map desk charge)]
+$ state-1 [%1 (map desk charge)]
+$ state-0-sig
$: ~
@ -94,6 +97,7 @@
==
--
::
++ on-save !>(-.state)
++ on-poke
|= [=mark =vase]
@ -189,13 +193,21 @@
==
::
[%x %charges ~]
=/ tyr
.^(rock:tire:clay %cx /(scot %p our.bowl)//(scot %da now.bowl)/tire)
:- ~ :- ~
%- charge-update:cg
:- %initial
%- ~(gas by *(map desk charge))
%+ turn ~(tap by charges)
%+ murn ~(tap by charges)
|= [=desk =charge]
[desk (get-light-charge charge)]
?~ got=(~(get by tyr) desk)
~
?: ?& ?=(%dead zest.u.got)
?=(~ (get-apps-have:hood our.bowl desk now.bowl))
==
~
`u=[desk (get-light-charge charge)]
::
[%x %charges @ %version ~]
?~ charge=(~(get by charges) i.t.t.path)
@ -212,114 +224,11 @@
~ `state
[%rein ~] ~&(%reined `state)
[%nuke ~] ~&(%nuked `state)
[%kiln ~] take-kiln
[%kiln ~] `state
[%charge @ *] (take-charge i.t.wire t.t.wire)
==
[cards this]
::
++ take-kiln
^- (quip card _state)
?+ -.sign (on-agent:def:cc wire sign)
%kick [(~(watch-our pass /kiln) %hood /kiln/vats)^~ state]
%fact
|^ ^- (quip card _state)
?+ p.cage.sign ~|(take-kiln-mark/p.cage.sign !!)
%kiln-vats-snap-0 (on-snap !<(snap:hood q.cage.sign))
%kiln-vats-diff-0 (on-diff !<(diff:hood q.cage.sign))
==
::
++ on-snap
|= =snap:hood
^- (quip card _state)
=| fex=(list card)
=/ ark ~(tap by snap)
|- ^- (quip card _state)
?~ ark [(flop fex) state]
=^ caz state (on-commit i.ark)
$(ark t.ark, fex (weld (flop caz) fex))
::
++ on-diff
|= =diff:hood
=+ !<(=diff:hood q.cage.sign)
?- -.diff
%commit (on-commit [desk arak]:diff)
%suspend (on-suspend [desk arak]:diff)
%revive (on-revive [desk arak]:diff)
?(%block %reset %merge-sunk %merge-fail)
`state
==
::
++ on-commit
|= [=desk =arak:hood]
^- (quip card _state)
=* cha ~(. ch desk)
?. docket-exists:cha
~? ?& !=(%base desk)
!=(%kids desk)
==
[dap.bowl %no-docket-file-for desk]
`state
:: always update the docket in state to match clay's
::
=/ =docket docket:cha
=/ pre=(unit charge) (~(get by charges) desk)
=. charges (new-docket:cha docket)
:: if the new chad is a site, we're instantly done
::
?: ?=(%site -.href.docket)
=. charges (new-chad:cha %site ~)
:- ~[add-fact:cha]
state
::
=. by-base (~(put by by-base) base.href.docket desk)
:: if the glob specification is unchanged, keep it
::
?: &(?=(^ pre) =(href.docket.u.pre href.docket) ?=(%glob -.chad.u.pre))
[~[add-fact:cha] state]
:: if the glob spec changed, but we already host it, keep it
:: (this is the "just locally uploaded" case)
::
?: ?& ?=(^ pre)
?=(%glob -.chad.u.pre)
::
.= [(sham glob.chad.u.pre) %ames our.bowl]
glob-reference.href.docket
==
[~[add-fact:cha] state]
:: if the glob changed, forget the old and fetch the new
::
=. charges (new-chad:cha %install ~)
[[add-fact:cha fetch-glob:cha] state]
::
++ on-suspend
|= [=desk =arak:hood]
^- (quip card _state)
=* cha ~(. ch desk)
?. (~(has by charges) desk) `state
=/ glob=(unit glob)
=/ =chad
chad:(~(got by charges) desk)
?:(?=(%glob -.chad) `glob.chad ~)
=. charges (new-chad:cha %suspend glob)
:_(state ~[add-fact:cha])
::
++ on-revive
|= [=desk =arak:hood]
^- (quip card _state)
=* cha ~(. ch desk)
?. (~(has by charges) desk) `state
=/ =charge (~(got by charges) desk)
?. ?=(%glob -.href.docket.charge)
=. charges (new-chad:cha %site ~)
:_(state ~[add-fact:cha])
=. charges
%- new-chad:cha
?. ?=([%suspend ~ *] chad.charge)
[%install ~]
[%glob u.glob.chad.charge]
:_(state [add-fact fetch-glob]:cha)
--
==
++ take-charge
|= [=desk =^wire]
^- (quip card _state)
@ -398,6 +307,7 @@
::
++ on-arvo
|= [=wire sign=sign-arvo]
|^
=^ cards state
?+ wire (on-arvo:def wire sign)
[%init ~]
@ -410,8 +320,114 @@
?: accepted.sign `state
~& [dap.bowl %failed-to-bind path.binding.sign]
`state
::
[%tire ~]
?> ?=([%clay %tire *] sign)
?- -.p.sign
%& (on-rock p.p.sign)
%| (on-wave p.p.sign)
==
::
[%warp * ~]
?> ?=(%writ +<.sign)
(on-writ i.t.wire p.sign)
==
[cards this]
::
++ on-rock
|= tyr=rock:tire:clay
^- (quip card _state)
=| fex=(list card)
=/ ark ~(tap by tyr)
|- ^- (quip card _state)
?~ ark [(flop fex) state]
=^ caz state (on-zest [p zest.q]:i.ark)
$(ark t.ark, fex (weld (flop caz) fex))
::
++ on-wave
|= =wave:tire:clay
^- (quip card _state)
?- -.wave
%wait `state
%warp `state
%zest (on-zest +.wave)
==
::
++ on-zest
|= [=desk =zest:clay]
^- (quip card _state)
=* cha ~(. ch desk)
=/ card-1
(~(warp-our pass /warp/[desk]) desk ~ %sing %z da+now.bowl /desk/docket-0)
=^ cards-2 state
?. (~(has by charges) desk)
`state
=/ =charge (~(got by charges) desk)
?- zest
%live
?. ?=(%glob -.href.docket.charge)
=. charges (new-chad:cha %site ~)
:_(state ~[add-fact:cha])
:_(state ~[add-fact:cha])
::
?(%held %dead)
=/ glob=(unit glob)
?:(?=(%glob -.chad.charge) `glob.chad.charge ~)
=. charges (new-chad:cha %suspend glob)
:_(state ~[add-fact:cha])
==
[[card-1 cards-2] state]
::
++ on-writ
|= [=desk =riot:clay]
^- (quip card _state)
=/ card-1
(~(warp-our pass /warp/[desk]) desk ~ %next %z da+now.bowl /desk/docket-0)
=^ cards-2 state
=* cha ~(. ch desk)
=/ tyr
.^(rock:tire:clay %cx /(scot %p our.bowl)//(scot %da now.bowl)/tire)
?. =(%live zest:(~(got by tyr) desk))
`state
?. docket-exists:cha
:: ~? ?& !=(%base desk)
:: !=(%kids desk)
:: ==
:: [dap.bowl %no-docket-file-for desk]
`state
:: always update the docket in state to match clay's
::
=/ =docket docket:cha
=/ pre=(unit charge) (~(get by charges) desk)
=. charges (new-docket:cha docket)
:: if the new chad is a site, we're instantly done
::
?: ?=(%site -.href.docket)
=. charges (new-chad:cha %site ~)
:- ~[add-fact:cha]
state
::
=. by-base (~(put by by-base) base.href.docket desk)
:: if the glob specification is unchanged, keep it
::
?: &(?=(^ pre) =(href.docket.u.pre href.docket) ?=(%glob -.chad.u.pre))
[~[add-fact:cha] state]
:: if the glob spec changed, but we already host it, keep it
:: (this is the "just locally uploaded" case)
::
?: ?& ?=(^ pre)
?=(%glob -.chad.u.pre)
::
.= [(sham glob.chad.u.pre) %ames our.bowl]
glob-reference.href.docket
==
[~[add-fact:cha] state]
:: if the glob changed, forget the old and fetch the new
::
=. charges (new-chad:cha %install ~)
[[add-fact:cha fetch-glob:cha] state]
[[card-1 cards-2] state]
--
::
++ on-fail on-fail:def
++ on-leave on-leave:def
@ -728,4 +744,3 @@
++ docket .^(^docket %cx (scry:io desk docket-loc))
--
--

View File

@ -2,16 +2,16 @@
/+ verb, dbug, default-agent, agentio
|%
+$ card card:agent:gall
+$ state-0 [%0 lagging=_|]
+$ state-1 [%1 lagging=_|]
::
++ lag-interval ~m10
--
%+ verb |
%- agent:dbug
^- agent:gall
=| state-0
=| state-1
=* state -
=<
=<
|_ =bowl:gall
+* this .
def ~(. (default-agent this %|) bowl)
@ -21,12 +21,23 @@
++ on-init
^- (quip card _this)
:_ this
[onboard watch:kiln check:lag ~]:cc
[onboard tire:cy check:lag ~]:cc
::
++ on-load
|= =vase
=+ !<(old=state-0 vase)
`this(state old)
^- (quip card _this)
|^
=+ !<(old=app-states vase)
=^ cards-1 old
?. ?=(%0 -.old) `old
[[tire:cy:cc]~ old(- %1)]
?> ?=(%1 -.old)
=/ cards-tire [tire:cy:cc ~]
[(weld cards-1 cards-tire) this(state old)]
::
+$ app-states $%(state-0 state-1)
+$ state-0 [%0 lagging=_|]
--
::
++ on-save !>(state)
++ on-poke on-poke:def
@ -34,45 +45,57 @@
++ on-watch on-watch:def
++ on-agent
|= [=wire =sign:agent:gall]
|^
^- (quip card _this)
?+ wire (on-agent:def wire sign)
[%kiln %vats ~] take-kiln-vats
[%kiln %vats ~] `this
==
++ take-kiln-vats
?- -.sign
?(%poke-ack %watch-ack) (on-agent:def wire sign)
%kick :_(this (drop safe-watch:kiln:cc))
::
%fact
?. ?=(%kiln-vats-diff-0 p.cage.sign) `this
=+ !<(=diff:hood q.cage.sign)
?+ -.diff `this
::
%commit
?. |(=(desk.diff %base) ~(has-docket de:cc desk.diff)) `this
=/ =action:hark ~(commit de:cc desk.diff)
::
++ on-arvo
|= [=wire sign=sign-arvo]
^- (quip card _this)
|^
?+ wire (on-arvo:def wire sign)
[%clay %tire ~] take-clay-tire
[%clay %warp * ~] (take-clay-warp i.t.t.wire)
[%check-lag ~] take-check-lag
==
::
++ take-check-lag
^- (quip card _this)
?> ?=([%behn %wake *] sign)
=+ .^(lag=? %$ (scry:io %$ /zen/lag))
?: =(lagging lag) :_(this ~[check:lag:cc])
:_ this(lagging lag)
:_ ~[check:lag:cc]
?:(lagging start:lag:cc stop:lag:cc)
::
++ take-clay-tire
^- (quip card _this)
?> ?=(%tire +<.sign)
?- -.p.sign
%& [(turn ~(tap in ~(key by p.p.sign)) warp:cy:cc) this]
%|
?- -.p.p.sign
%zest `this
%warp `this
%wait
=/ =action:hark (~(blocked de:cc desk.p.p.sign) weft.p.p.sign)
:_ this
~[(poke:ha:cc action)]
::
%block
=/ =action:hark (~(blocked de:cc desk.diff) blockers.diff)
:_ this
~[(poke:ha:cc action)]
==
==
::
++ take-clay-warp
|= =desk
^- (quip card _this)
?> ?=(%writ +<.sign)
=/ cards
?. |(=(desk %base) ~(has-docket de:cc desk)) ~
=/ =action:hark ~(commit de:cc desk)
~[(poke:ha:cc action)]
[[(warp:cy:cc desk) cards] this]
--
::
++ on-arvo
|= [=wire sign=sign-arvo]
^- (quip card _this)
?. ?=([%check-lag ~] wire) (on-arvo:def wire sign)
?> ?=([%behn %wake *] sign)
=+ .^(lag=? %$ (scry:io %$ /zen/lag))
?: =(lagging lag) :_(this ~[check:lag:cc])
:_ this(lagging lag)
:_ ~[check:lag:cc]
?:(lagging start:lag:cc stop:lag:cc)
::
++ on-fail on-fail:def
++ on-leave on-leave:def
--
@ -89,7 +112,7 @@
[~[text+'Welcome to urbit'] ~ now.bowl / /]
::
++ lag
|%
|%
++ check (~(wait pass /check-lag) (add now.bowl lag-interval))
++ place [q.byk.bowl /lag]
++ body `body:hark`[~[text/'Runtime lagging'] ~ now.bowl / /]
@ -102,24 +125,23 @@
++ poke
|=(=action:hark (poke-our:pass %hark-store hark-action+!>(action)))
--
++ kiln
::
++ cy
|%
++ path /kiln/vats
++ pass ~(. ^pass path)
++ watch (watch-our:pass %hood path)
++ watching (~(has by wex.bowl) [path our.bowl %hood])
++ safe-watch `(unit card)`?:(watching ~ `watch)
++ tire ~(tire pass /clay/tire)
++ warp
|= =desk
(~(warp-our pass /clay/warp/[desk]) desk ~ %next %z da+now.bowl /)
--
::
++ de
|_ =desk
++ scry-path (scry:io desk /desk/docket-0)
++ scry-path (scry:io desk /desk/docket-0)
++ has-docket .^(? %cu scry-path)
++ docket .^(docket:^docket %cx scry-path)
++ hash .^(@uv %cz (scry:io desk ~))
++ place `place:hark`[q.byk.bowl /desk/[desk]]
++ vat
.^(vat:hood %gx (scry:io %hood /kiln/vat/[desk]/noun))
++ docket .^(docket:^docket %cx scry-path)
++ hash .^(@uv %cz (scry:io desk ~))
++ place `place:hark`[q.byk.bowl /desk/[desk]]
++ version ud:.^(cass:clay %cw (scry:io desk /))
++ body
|= [=path title=cord content=(unit cord)]
^- body:hark
@ -131,7 +153,7 @@
%+ rap 3
?: =(desk %base)
['System software' cord ~]
?: has-docket
?: has-docket
['App: "' title:docket '"' cord ~]
['Desk: ' desk cord ~]
::
@ -142,7 +164,7 @@
::
++ commit
^- action:hark
?:(=(1 ud.cass:vat) created updated)
?:(=(1 version) created updated)
::
++ created
^- action:hark
@ -155,11 +177,11 @@
(body /desk/[desk] (title-prefix (rap 3 ' has been updated to ' get-version ~)) ~)
::
++ blocked
|= blockers=(set ^desk)
|= =weft
^- action:hark
:+ %add-note [/blocked place]
%^ body /blocked (title-prefix ' is blocked from upgrading')
`(rap 3 'Blocking desks: ' (join ', ' ~(tap in blockers)))
`(rap 3 'Blocked waiting for system version: ' (scot %ud num.weft) 'K' ~)
::
++ ver
|= =version:^docket
@ -172,7 +194,7 @@
--
++ note
|%
++ merge
++ merge
|= [=desk hash=@uv]
^- (list body:hark)
:_ ~

View File

@ -28,6 +28,7 @@
%tas s+(scot %tas p.c)
%ud (numb p.c)
==
::
++ foreign-desk
|= [s=^ship =desk]
^- cord

View File

@ -1 +1 @@
[%zuse 418]
[%zuse 417]

53
desk/ted/get-dudes.hoon Normal file
View File

@ -0,0 +1,53 @@
/- spider
/+ *strandio
::
=, strand=strand:spider
::
:: send on /spider/garden/json/get-dudes/json
::
|%
++ buds :: get agents currently running
|= p=desk
=/ m (strand ,(list dude:gall))
^- form:m
?. =(%$ p)
;< q=(list dude:gall) bind:m (suds p)
(pure:m q)
;< q=(list desk) bind:m duds
=| r=(list (list dude:gall))
|- ^- form:m
=* s $
?~ q (pure:m (zing r))
;< t=(list dude:gall) bind:m (suds i.q)
s(q t.q, r [t r])
::
++ suds :: clean %ge scry
|= p=desk
=/ m (strand ,(list dude:gall))
^- form:m
;< q=(set [dude:gall ?]) bind:m
(scry (set ,[dude:gall ?]) /ge/(scot %tas p))
%- pure:m
(murn ~(tap in q) |=([dude:gall ?] ?.(+.+< ~ `-.+<)))
::
++ duds :: get desks
=/ m (strand ,(list desk))
^- form:m
;< p=(set desk) bind:m (scry (set ,desk) /cd/base)
(pure:m ~(tap in p))
--
::
^- thread:spider
|= jon=vase
=/ m (strand ,vase)
^- form:m
;< =bowl:spider bind:m get-bowl
=, bowl
?~ know=!<((unit json) jon)
(pure:m !>(`json`[%s 'invalid-request']))
?. ?=([%s @] u.know)
(pure:m !>(`json`[%s 'invalid-request']))
=, format
;< breh=(list @tas) bind:m (buds (so:dejs u.know))
%- pure:m
!>(`json`(frond:enjs 'buds' a+(turn breh |=(@tas s+[+<]))))

16
ui/package-lock.json generated
View File

@ -20,8 +20,8 @@
"@tlon/sigil-js": "^1.4.4",
"@tloncorp/mock-http-api": "^1.2.0",
"@types/lodash": "^4.14.172",
"@urbit/api": "^2.1.1",
"@urbit/http-api": "^2.2.0",
"@urbit/api": "^2.2.0",
"@urbit/http-api": "^2.3.0",
"big-integer": "^1.6.48",
"browser-cookies": "^1.2.0",
"classnames": "^2.3.1",
@ -1695,9 +1695,9 @@
}
},
"node_modules/@urbit/api": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@urbit/api/-/api-2.1.1.tgz",
"integrity": "sha512-QRlqhtJ73q+pgMdSwuOO62HlxA7/2c5ylCcOUT01LXkJ2LTVCl5u+QnejdDvUmqjOuN2PyZk7df30xJVg6rC2A==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@urbit/api/-/api-2.2.0.tgz",
"integrity": "sha512-W8kP9OT6yOK62n+4yCPO3i9QqU5xriLvZQ9WYW4SAV7ktbSrGuf2kmYbnoqfA/NybIs9Q/MbFkPewrz4XJ96Ag==",
"dependencies": {
"@babel/runtime": "^7.16.0",
"big-integer": "^1.6.48",
@ -8888,9 +8888,9 @@
}
},
"@urbit/api": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@urbit/api/-/api-2.1.1.tgz",
"integrity": "sha512-QRlqhtJ73q+pgMdSwuOO62HlxA7/2c5ylCcOUT01LXkJ2LTVCl5u+QnejdDvUmqjOuN2PyZk7df30xJVg6rC2A==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@urbit/api/-/api-2.2.0.tgz",
"integrity": "sha512-W8kP9OT6yOK62n+4yCPO3i9QqU5xriLvZQ9WYW4SAV7ktbSrGuf2kmYbnoqfA/NybIs9Q/MbFkPewrz4XJ96Ag==",
"requires": {
"@babel/runtime": "^7.16.0",
"big-integer": "^1.6.48",

View File

@ -27,8 +27,8 @@
"@tlon/sigil-js": "^1.4.4",
"@tloncorp/mock-http-api": "^1.2.0",
"@types/lodash": "^4.14.172",
"@urbit/api": "^2.1.1",
"@urbit/http-api": "^2.2.0",
"@urbit/api": "^2.2.0",
"@urbit/http-api": "^2.3.0",
"big-integer": "^1.6.48",
"browser-cookies": "^1.2.0",
"classnames": "^2.3.1",

View File

@ -86,9 +86,8 @@ const AppRoutes = () => {
fetchCharges();
fetchAllies();
const { fetchVats, fetchLag } = useKilnState.getState();
fetchVats();
fetchLag();
const { initializeKiln } = useKilnState.getState();
initializeKiln();
useContactState.getState().initialize(api);
useHarkState.getState().start();

View File

@ -1,4 +1,4 @@
import { chadIsRunning, Treaty, Vat } from '@urbit/api';
import { chadIsRunning, Pike, Treaty } from '@urbit/api';
import clipboardCopy from 'clipboard-copy';
import React, { FC, useCallback, useState } from 'react';
import cn from 'classnames';
@ -6,7 +6,7 @@ import { Button, PillButton } from './Button';
import { Dialog, DialogClose, DialogContent, DialogTrigger } from './Dialog';
import { DocketHeader } from './DocketHeader';
import { Spinner } from './Spinner';
import { VatMeta } from './VatMeta';
import { PikeMeta } from './PikeMeta';
import useDocketState, { ChargeWithDesk, useTreaty } from '../state/docket';
import { getAppHref, getAppName } from '../state/util';
import { addRecentApp } from '../nav/search/Home';
@ -17,7 +17,7 @@ type InstallStatus = 'uninstalled' | 'installing' | 'installed';
type App = ChargeWithDesk | Treaty;
interface AppInfoProps {
docket: App;
vat?: Vat;
pike?: Pike;
className?: string;
}
@ -34,10 +34,9 @@ function getInstallStatus(docket: App): InstallStatus {
return 'uninstalled';
}
function getRemoteDesk(docket: App, vat?: Vat) {
if (vat && vat.arak.rail) {
const { ship, desk } = vat.arak.rail;
return [ship, desk];
function getRemoteDesk(docket: App, pike?: Pike) {
if (pike && pike.sync) {
return [pike.sync.ship, pike.sync.desk];
}
if ('chad' in docket) {
return ['', docket.desk];
@ -46,10 +45,10 @@ function getRemoteDesk(docket: App, vat?: Vat) {
return [ship, desk];
}
export const AppInfo: FC<AppInfoProps> = ({ docket, vat, className }) => {
export const AppInfo: FC<AppInfoProps> = ({ docket, pike, className }) => {
const installStatus = getInstallStatus(docket);
const [ship, desk] = getRemoteDesk(docket, vat);
const publisher = vat?.arak?.rail?.publisher ?? ship;
const [ship, desk] = getRemoteDesk(docket, pike);
const publisher = pike?.sync?.ship ?? ship;
const [copied, setCopied] = useState(false);
const treaty = useTreaty(ship, desk);
@ -136,10 +135,10 @@ export const AppInfo: FC<AppInfoProps> = ({ docket, vat, className }) => {
</div>
</DocketHeader>
<div className="space-y-6">
{vat ? (
{pike ? (
<>
<hr className="-mx-5 sm:-mx-8 border-gray-50" />
<VatMeta vat={vat} />
<PikeMeta pike={pike} />
</>
) : null}
{!treaty ? null : (

View File

@ -0,0 +1,25 @@
import React from 'react';
import { Pike } from '@urbit/api';
import { Attribute } from './Attribute';
export function PikeMeta(props: { pike: Pike }) {
const { pike } = props;
const pluralUpdates = pike.wefts?.length !== 1;
return (
<div className="mt-5 sm:mt-8 space-y-5 sm:space-y-8">
<Attribute title="Desk Hash" attr="hash">
{pike.hash}
</Attribute>
<Attribute title="Installed into" attr="local-desk">
%{pike.sync?.desk}
</Attribute>
{pike.wefts && pike.wefts.length > 0 ? (
<Attribute attr="next" title="Pending Updates">
{pike.wefts.length} update{pluralUpdates ? 's are' : ' is'} pending a System Update
</Attribute>
) : null}
</div>
);
}

View File

@ -3,11 +3,12 @@ import React, { HTMLAttributes } from 'react';
type ShipNameProps = {
name: string;
truncate?: boolean;
} & HTMLAttributes<HTMLSpanElement>;
export const ShipName = ({ name, ...props }: ShipNameProps) => {
export const ShipName = ({ name, truncate = true, ...props }: ShipNameProps) => {
const separator = /([_^-])/;
const citedName = cite(name);
const citedName = truncate ? cite(name) : name;
if (!citedName) {
return null;

View File

@ -0,0 +1,103 @@
import React, { useCallback, useState } from 'react';
import { useAsyncCall } from '../logic/useAsyncCall';
import { Button } from './Button';
import { ShipName } from './ShipName';
import { Spinner } from './Spinner';
interface SourceSetterProps {
appName: string;
srcDesk: string;
srcShip?: string;
title: string;
toggleSrc: (desk: string, ship: string) => Promise<void>;
}
export default function SourceSetter({
appName,
srcDesk,
srcShip,
title,
toggleSrc
}: SourceSetterProps) {
const [newSyncShip, setNewSyncShip] = useState(srcShip ?? '');
const { status: requestStatus, call: handleSubmit } = useAsyncCall(toggleSrc);
const syncDirty = newSyncShip !== srcShip;
const onUnset = useCallback(() => {
if (!srcShip) {
return;
}
if (
// eslint-disable-next-line no-alert, no-restricted-globals
confirm(`Are you sure you want to unsync ${appName}? You will no longer receive updates.`)
) {
toggleSrc(srcDesk, srcShip);
}
}, [srcShip, srcDesk]);
const handleSourceChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
const { target } = e;
const value = target.value.trim();
setNewSyncShip(value.startsWith('~') ? value : `~${value}`);
}, []);
const onSubmit = useCallback(
async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
await handleSubmit(srcDesk, newSyncShip);
},
[srcDesk, newSyncShip]
);
return (
<>
<h2 className="h3 mb-7">{title}</h2>
<div className="space-y-3">
{srcShip ? (
<>
<h3 className="flex items-center h4 mb-2">Automatic Updates</h3>
<p>Automatically download and apply updates to keep {appName} up to date.</p>
<div className="flex-1 flex flex-col justify-center space-y-6">
<p>
OTA Source:{' '}
<ShipName name={srcShip} truncate={false} className="font-semibold font-mono" />
</p>
</div>
<div className="flex space-x-2">
<Button onClick={onUnset} variant="destructive">
Unsync Updates for {appName}...
</Button>
</div>
</>
) : (
<form className="inner-section relative" onSubmit={onSubmit}>
<label htmlFor="ota-source" className="h4 mb-3">
Set Update Source
</label>
<p className="mb-2">Enter a valid urbit name to receive updates for {appName}.</p>
<div className="relative">
<input
id="ota-source"
type="text"
value={newSyncShip}
onChange={handleSourceChange}
className="input font-semibold default-ring"
/>
{syncDirty && (
<Button type="submit" className="absolute top-1 right-1 py-1 px-3 text-sm">
{requestStatus !== 'loading' && 'Save'}
{requestStatus === 'loading' && (
<>
<span className="sr-only">Saving...</span>
<Spinner className="w-5 h-5" />
</>
)}
</Button>
)}
</div>
</form>
)}
</div>
</>
);
}

View File

@ -1,27 +0,0 @@
import React from 'react';
import { Vat } from '@urbit/api';
import { Attribute } from './Attribute';
export function VatMeta(props: { vat: Vat }) {
const { vat } = props;
const { desk, arak, cass, hash } = vat;
const { desk: foreignDesk, ship, next } = arak.rail || {};
const pluralUpdates = next?.length !== 1;
return (
<div className="mt-5 sm:mt-8 space-y-5 sm:space-y-8">
<Attribute title="Desk Hash" attr="hash">
{hash}
</Attribute>
<Attribute title="Installed into" attr="local-desk">
%{desk}
</Attribute>
{next && next.length > 0 ? (
<Attribute attr="next" title="Pending Updates">
{next.length} update{pluralUpdates ? 's are' : ' is'} pending a System Update
</Attribute>
) : null}
</div>
);
}

View File

@ -1,42 +1,37 @@
import { kilnBump, Vat } from '@urbit/api';
import { kilnBump, Pike } from '@urbit/api';
import { partition, pick } from 'lodash';
import { useCallback } from 'react';
import { useHistory } from 'react-router-dom';
import api from '../state/api';
import { useCharges } from '../state/docket';
import useKilnState, { useVat } from '../state/kiln';
import useKilnState, { usePike } from '../state/kiln';
export function vatIsBlocked(newKelvin: number | undefined, vat: Vat) {
if (!newKelvin) {
return false;
}
return !(vat.arak?.rail?.next || []).find(({ weft }) => weft.kelvin === newKelvin);
function pikeIsBlocked(newKelvin: number, pike: Pike) {
return !pike.wefts?.find(({ kelvin }) => kelvin === newKelvin);
}
export function useSystemUpdate() {
const { push } = useHistory();
const base = useVat('base');
const update = base?.arak?.rail?.next?.[0];
const newKelvin = update?.weft?.kelvin;
const base = usePike('base');
const nextUpdate = base?.wefts[0];
const newKelvin = base?.wefts[0]?.kelvin ?? 417;
const charges = useCharges();
const [blocked] = useKilnState((s) => {
const [b, u] = partition(Object.entries(s.vats), ([, vat]) => vatIsBlocked(newKelvin, vat));
const [b, u] = partition(Object.entries(s.pikes), ([, pike]) => pikeIsBlocked(newKelvin, pike));
return [b.map(([d]) => d), u.map(([d]) => d)] as const;
});
const systemBlocked = update && blocked;
const systemBlocked = nextUpdate && blocked;
const blockedCharges = Object.values(pick(charges, blocked));
const blockedCount = blockedCharges.length;
const freezeApps = useCallback(async () => {
api.poke(kilnBump(true));
await api.poke(kilnBump());
push('/leap/upgrading');
}, []);
return {
base,
update,
nextUpdate,
systemBlocked,
blockedCharges,
blockedCount,

View File

@ -1,6 +1,6 @@
import { Handler, SubscriptionHandler, createResponse } from '@tloncorp/mock-http-api';
import mockContacts from './mockContacts';
import { mockAllies, mockCharges, mockTreaties, mockVats } from '../state/mock-data';
import { mockAllies, mockCharges, mockTreaties } from '../state/mock-data';
const settingsSub = {
action: 'subscribe',
@ -45,14 +45,6 @@ const mockHandlers: Handler[] = [
}
})
},
{
action: 'scry',
app: 'hood',
path: '/kiln/vats',
func: () => ({
mockVats
})
},
{
action: 'scry',
app: 'hood',

View File

@ -3,9 +3,9 @@ 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 { Vat } from '@urbit/api';
import { Pike } from '@urbit/api';
import { Adjust } from '../components/icons/Adjust';
import { useVat } from '../state/kiln';
import { usePike } from '../state/kiln';
import { disableDefault, handleDropdownLink } from '../state/util';
import { useMedia } from '../logic/useMedia';
import { Cross } from '../components/icons/Cross';
@ -17,15 +17,15 @@ type SystemMenuProps = HTMLAttributes<HTMLButtonElement> & {
shouldDim: boolean;
};
function getHash(vat: Vat): string {
const parts = vat.hash.split('.');
function getHash(pike: Pike): string {
const parts = pike.hash.split('.');
return parts[parts.length - 1];
}
export const SystemMenu = ({ className, open, subMenuOpen, shouldDim }: SystemMenuProps) => {
const { push } = useHistory();
const [copied, setCopied] = useState(false);
const garden = useVat(window.desk);
const garden = usePike(window.desk);
const hash = garden ? getHash(garden) : null;
const isMobile = useMedia('(max-width: 639px)');
const select = useLeapStore((s) => s.select);

View File

@ -3,7 +3,7 @@ import { useParams } from 'react-router-dom';
import { AppInfo } from '../../components/AppInfo';
import { Spinner } from '../../components/Spinner';
import useDocketState, { useCharge, useTreaty } from '../../state/docket';
import { useVat } from '../../state/kiln';
import { usePike } from '../../state/kiln';
import { getAppName } from '../../state/util';
import { useLeapStore } from '../Nav';
@ -11,7 +11,7 @@ export const TreatyInfo = () => {
const select = useLeapStore((state) => state.select);
const { host, desk } = useParams<{ host: string; desk: string }>();
const treaty = useTreaty(host, desk);
const vat = useVat(desk);
const pike = usePike(desk);
const charge = useCharge(desk);
const name = getAppName(treaty);
@ -34,5 +34,5 @@ export const TreatyInfo = () => {
</div>
);
}
return <AppInfo className="dialog-inner-container" docket={charge || treaty} vat={vat} />;
return <AppInfo className="dialog-inner-container" docket={charge || treaty} pike={pike} />;
};

View File

@ -26,8 +26,8 @@ export const Grid: FunctionComponent = () => {
window.location.reload();
}
const start = performance.now();
await useKilnState.getState().fetchVats();
await useKilnState.getState().fetchVats();
await useKilnState.getState().fetchPikes();
await useKilnState.getState().fetchPikes();
if (performance.now() - start > 5000) {
attempt(count + 1);
} else {

View File

@ -1,15 +1,15 @@
import { Pikes } from '@urbit/api';
import React, { useEffect } from 'react';
import { Switch, Route, Redirect, RouteComponentProps } from 'react-router-dom';
import { Spinner } from '../components/Spinner';
import { useQuery } from '../logic/useQuery';
import { useCharge } from '../state/docket';
import useKilnState, { useKilnLoaded } from '../state/kiln';
import { useKilnLoaded, usePikes } from '../state/kiln';
import { getAppHref } from '../state/util';
function getDeskByForeignRef(ship: string, desk: string): string | undefined {
const { vats } = useKilnState.getState();
const found = Object.entries(vats).find(
([, vat]) => vat.arak.rail?.ship === ship && vat.arak.rail?.desk === desk
function getDeskByForeignRef(pikes: Pikes, ship: string, desk: string): string | undefined {
const found = Object.entries(pikes).find(
([, pike]) => pike.sync?.ship === ship && pike.sync?.desk === desk
);
return found ? found[0] : undefined;
}
@ -22,8 +22,8 @@ type AppLinkProps = RouteComponentProps<{
function AppLink({ match, history, location }: AppLinkProps) {
const { ship, desk, link = '' } = match.params;
const ourDesk = getDeskByForeignRef(ship, desk);
console.log(ourDesk);
const pikes = usePikes();
const ourDesk = getDeskByForeignRef(pikes, ship, desk);
if (ourDesk) {
return <AppLinkRedirect desk={ourDesk} link={link} />;

View File

@ -1,50 +1,27 @@
import React, { useCallback } from 'react';
import React from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { Setting } from '../components/Setting';
import { ShipName } from '../components/ShipName';
import { useCharge } from '../state/docket';
import useKilnState, { useVat } from '../state/kiln';
import useKilnState, { usePike } from '../state/kiln';
import { getAppName } from '../state/util';
import SourceSetter from '../components/SourceSetter';
export const AppPrefs = ({ match }: RouteComponentProps<{ desk: string }>) => {
const { desk } = match.params;
const charge = useCharge(desk);
const vat = useVat(desk);
const tracking = !!vat?.arak.rail;
const otasEnabled = !vat?.arak.rail?.paused;
const otaSource = vat?.arak.rail?.ship;
const toggleOTAs = useKilnState((s) => s.toggleOTAs);
const toggleUpdates = useCallback((on: boolean) => toggleOTAs(desk, on), [desk, toggleOTAs]);
const appName = getAppName(charge);
const pike = usePike(desk);
const srcShip = pike?.sync?.ship;
const { toggleSync } = useKilnState();
return (
<>
<h2 className="h3 mb-7">{getAppName(charge)} Settings</h2>
<div className="space-y-8">
{tracking ? (
<Setting
on={otasEnabled}
toggle={toggleUpdates}
name="Automatic Updates"
>
<p className="mb-1 leading-5">
Automatically download and apply updates to keep{' '}
{getAppName(charge)} up to date.
</p>
{otaSource && (
<p>
OTA Source:{' '}
<ShipName
name={otaSource}
className="font-mono font-semibold"
/>
</p>
)}
</Setting>
) : (
<h4 className="text-gray-500">No settings</h4>
)}
</div>
</>
<div className="inner-section space-y-8 relative">
<SourceSetter
appName={appName}
title={`${appName} Settings`}
toggleSrc={toggleSync}
srcDesk={desk}
srcShip={srcShip}
/>
</div>
);
};

View File

@ -1,4 +1,4 @@
import { Vat } from '@urbit/api';
import { Pike } from '@urbit/api';
import React from 'react';
import { AppList } from '../../components/AppList';
import { Button } from '../../components/Button';
@ -6,24 +6,22 @@ import { Dialog, DialogClose, DialogContent, DialogTrigger } from '../../compone
import { FullTlon16Icon } from '../../components/icons/FullTlon16Icon';
import { useSystemUpdate } from '../../logic/useSystemUpdate';
import { useCharge } from '../../state/docket';
import { useVat } from '../../state/kiln';
import { usePike } from '../../state/kiln';
import { disableDefault, pluralize } from '../../state/util';
import { UpdatePreferences } from './UpdatePreferences';
function getHash(vat: Vat): string {
const parts = vat.hash.split('.');
function getHash(pike: Pike): string {
const parts = pike.hash.split('.');
return parts[parts.length - 1];
}
export const AboutSystem = () => {
const garden = useVat('garden');
const gardenCharge = useCharge('garden');
const { base, update, systemBlocked, blockedCharges, blockedCount, freezeApps } =
const gardenPike = usePike(window.desk);
const { systemBlocked, blockedCharges, blockedCount, freezeApps } =
useSystemUpdate();
const hash = base && getHash(base);
const aeon = base ? base.arak.rail?.aeon : '';
const nextAeon = update?.aeon;
const hash = gardenPike && getHash(gardenPike);
return (
<>
<div className="inner-section space-y-8 relative mb-4">
@ -45,15 +43,6 @@ export const AboutSystem = () => {
<p>
Version {gardenCharge?.version} ({hash})
</p>
{systemBlocked && (
<p>
Aeon {aeon}{' '}
<span className="text-orange-500 mx-4 space-x-2">
<span>&mdash;&gt;</span> <span>/</span> <span>&mdash;&gt;</span>
</span>{' '}
Aeon {nextAeon}
</p>
)}
</div>
{systemBlocked ? (
<>
@ -98,7 +87,7 @@ export const AboutSystem = () => {
)}
</div>
</div>
<UpdatePreferences base={base} />
<UpdatePreferences />
</>
);
};

View File

@ -1,81 +1,25 @@
import { Vat } from '@urbit/api';
import _ from 'lodash';
import React, { ChangeEvent, FormEvent, useCallback, useEffect, useState } from 'react';
import { Button } from '../../components/Button';
import { Setting } from '../../components/Setting';
import { Spinner } from '../../components/Spinner';
import { useAsyncCall } from '../../logic/useAsyncCall';
import useKilnState from '../../state/kiln';
interface UpdatePreferencesProps {
base: Vat | undefined;
}
export const UpdatePreferences = ({ base }: UpdatePreferencesProps) => {
const { changeOTASource, toggleOTAs } = useKilnState((s) =>
_.pick(s, ['toggleOTAs', 'changeOTASource'])
);
const otasEnabled = base && !(base.arak?.rail?.paused ?? true);
const otaSource = base && base.arak.rail?.ship;
const toggleBase = useCallback((on: boolean) => toggleOTAs('base', on), [toggleOTAs]);
const [source, setSource] = useState('');
const sourceDirty = source !== otaSource;
const { status: sourceStatus, call: setOTA } = useAsyncCall(changeOTASource);
useEffect(() => {
if (otaSource) {
setSource(otaSource);
}
}, [otaSource]);
const handleSourceChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
const { target } = e;
const value = target.value.trim();
setSource(value.startsWith('~') ? value : `~${value}`);
}, []);
const onSubmit = useCallback(
(e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
setOTA(source);
},
[source]
);
import React from 'react';
import SourceSetter from '../../components/SourceSetter';
import useKilnState, { usePike } from '../../state/kiln';
export const UpdatePreferences = () => {
const desk = 'base';
const appName = 'your Urbit';
const pike = usePike(desk);
const srcShip = pike?.sync?.ship;
const { toggleInstall } = useKilnState();
return (
<div className="inner-section space-y-8 relative">
<h2 className="h4">Update Preferences</h2>
<form onSubmit={onSubmit}>
<label htmlFor="ota-source" className="block font-semibold mb-1.5">
System Update Provider
</label>
<div className="flex items-center space-x-2">
<input
id="ota-source"
type="text"
value={source}
onChange={handleSourceChange}
className="input bg-gray-50 font-semibold default-ring"
/>
<Button type="submit" disabled={!sourceDirty}>
{sourceStatus !== 'loading' && 'Update'}
{sourceStatus === 'loading' && (
<>
<span className="sr-only">Saving...</span>
<Spinner className="w-5 h-5" />
</>
)}
</Button>
</div>
</form>
<Setting on={!!otasEnabled} toggle={toggleBase} name="Automatically Update My Urbit">
<p className="text-gray-600 leading-5">
Ensure that system updates are downloaded and applied as soon as my update provider has an
update readied
</p>
</Setting>
<SourceSetter
appName={appName}
toggleSrc={toggleInstall}
srcDesk={desk}
srcShip={srcShip}
title="System Updates"
/>
</div>
);
};

View File

@ -1,79 +1,76 @@
import {
getVats,
Vats,
scryLag,
getBlockers,
Vat,
kilnInstall,
kilnPause,
kilnResume
getPikes,
Pikes,
Pike,
kilnUnsync,
kilnSync,
kilnUninstall,
kilnInstall
} from '@urbit/api';
import create from 'zustand';
import produce from 'immer';
import { useCallback } from 'react';
import api from './api';
import { fakeRequest, useMockData } from './util';
import { mockPikes } from './mock-data';
interface KilnState {
vats: Vats;
pikes: Pikes;
loaded: boolean;
fetchVats: () => Promise<void>;
lag: boolean;
fetchLag: () => Promise<void>;
changeOTASource: (ship: string) => Promise<void>;
toggleOTAs: (desk: string, on: boolean) => Promise<void>;
fetchPikes: () => Promise<void>;
toggleInstall: (desk: string, ship: string) => Promise<void>;
toggleSync: (desk: string, ship: string) => Promise<void>;
set: (s: KilnState) => void;
initializeKiln: () => Promise<void>;
}
const useKilnState = create<KilnState>((set, get) => ({
vats: {},
pikes: useMockData ? mockPikes : {},
lag: false,
loaded: false,
fetchVats: async () => {
const vats = await api.scry<Vats>(getVats);
set({ vats, loaded: true });
fetchPikes: async () => {
if (useMockData) {
await fakeRequest({}, 500);
set({ loaded: true });
return;
}
const pikes = await api.scry<Pikes>(getPikes);
set({ pikes, loaded: true });
},
fetchLag: async () => {
const lag = await api.scry<boolean>(scryLag);
set({ lag });
},
changeOTASource: async (ship: string) => {
await api.poke(kilnInstall(ship, 'kids', 'base'));
toggleInstall: async (desk: string, ship: string) => {
const synced = !!get().pikes[desk].sync;
await (useMockData
? fakeRequest('')
: api.poke(synced ? kilnUninstall(desk) : kilnInstall(ship, 'kids', desk)));
await get().fetchPikes();
},
toggleOTAs: async (desk: string, on: boolean) => {
set(
produce((draft: KilnState) => {
const { arak } = draft.vats[desk];
if (!arak.rail) {
return;
}
if (on) {
arak.rail.paused = false;
} else {
arak.rail.paused = true;
}
})
);
await api.poke(on ? kilnResume(desk) : kilnPause(desk));
await get().fetchVats(); // refresh vat state
},
set: produce(set)
toggleSync: async (desk: string, ship: string) => {
const synced = !!get().pikes[desk].sync;
await (useMockData
? fakeRequest('')
: api.poke(synced ? kilnUnsync(ship, desk) : kilnSync(ship, desk)));
await get().fetchPikes();
},
set: produce(set),
initializeKiln: async () => {
await get().fetchLag();
await get().fetchPikes();
}
}));
api.subscribe({
app: 'hood',
path: '/kiln/vats',
event: () => {
useKilnState.getState().fetchVats();
}
});
const selBlockers = (s: KilnState) => getBlockers(s.vats);
export function useBlockers() {
return useKilnState(selBlockers);
const selPikes = (s: KilnState) => s.pikes;
export function usePikes(): Pikes {
return useKilnState(selPikes);
}
export function useVat(desk: string): Vat | undefined {
return useKilnState(useCallback((s) => s.vats[desk], [desk]));
export function usePike(desk: string): Pike | undefined {
return useKilnState(useCallback((s) => s.pikes[desk], [desk]));
}
const selLag = (s: KilnState) => s.lag;

View File

@ -1,6 +1,4 @@
import {
Vat,
Vats,
Allies,
Charges,
DocketHrefGlob,
@ -13,7 +11,8 @@ import {
Contact,
Contacts,
Timebox,
harkBinToId
harkBinToId,
Pikes
} from '@urbit/api';
import _ from 'lodash';
import systemUrl from '../assets/system.png';
@ -341,37 +340,35 @@ export const mockContacts: Contacts = {
}
};
export const mockVat = (desk: string, blockers?: boolean): Vat => ({
cass: {
da: '~2021.9.13..05.41.04..ae65',
ud: 1
export const mockPikes: Pikes = {
kids: {
sync: null,
zest: 'dead',
wefts: [],
hash: '0v19.q7u27.omps3.fbhf4.53rai.co157.pben7.pu94n.63v4p.3kcb7.iafj0'
},
desk,
arak: {
rein: {
sub: [],
add: []
garden: {
sync: {
desk: 'garden',
ship: '~mister-dister-dozzod-dozzod'
},
rail:
desk === 'uniswap'
? null
: {
aeon: 3,
desk,
publisher: '~zod',
next: blockers ? [{ aeon: 3, weft: { name: 'zuse', kelvin: 419 } }] : [],
ship: '~zod',
paused: desk === 'groups'
}
zest: 'live',
wefts: [],
hash: '0v18.hbbs6.onu15.skjkv.qrfgl.vf4oo.0igo5.2q0d3.6r3r8.2dkmo.oa04m'
},
hash: '0vh.lhfn6.julg1.fs52d.g2lqj.q5kp0.2o7j3.2bljl.jdm34.hd46v.9uv5v'
});
const badVats = ['inbox', 'system', 'terminal', 'base'];
export const mockVats = _.reduce(
mockCharges,
(vats, charge, desk) => {
return { ...vats, [desk]: mockVat(desk, !badVats.includes(desk)) };
landscape: {
sync: {
desk: 'landscape',
ship: '~lander-dister-dozzod-dozzod'
},
zest: 'live',
wefts: [],
hash: '0v1t.qln8k.cskmt.cn6lv.gu335.jfba6.kte90.iqqn3.aj67b.t389a.8imuo'
},
{ base: mockVat('base', true) } as Vats
);
base: {
sync: null,
zest: 'live',
wefts: [],
hash: '0v1e.b5auh.6u82i.hqk1r.22kli.4ubef.a1cbo.3g532.6l49k.g0i8e.t6eid'
}
};

View File

@ -2,6 +2,16 @@ import { Docket, DocketHref, Treaty } from '@urbit/api';
import { hsla, parseToHsla } from 'color2k';
import _ from 'lodash';
export const useMockData = import.meta.env.MODE === 'mock';
export async function fakeRequest<T>(data: T, time = 300): Promise<T> {
return new Promise((resolve) => {
setTimeout(() => {
resolve(data);
}, time);
});
}
export function getAppHref(href: DocketHref) {
return 'site' in href ? href.site : `/apps/${href.glob.base}/`;
}

View File

@ -8,7 +8,7 @@ import { getAppHref } from '../state/util';
import { useRecentsStore } from '../nav/search/Home';
import { ChargeWithDesk } from '../state/docket';
import { useTileColor } from './useTileColor';
import { useVat } from '../state/kiln';
import { usePike } from '../state/kiln';
import { Bullet } from '../components/icons/Bullet';
import { dragTypes } from './TileGrid';
@ -21,11 +21,12 @@ type TileProps = {
export const Tile: FunctionComponent<TileProps> = ({ charge, desk, disabled = false }) => {
const addRecentApp = useRecentsStore((state) => state.addRecentApp);
const { title, image, color, chad, href } = charge;
const vat = useVat(desk);
const pike = usePike(desk);
const { lightText, tileColor, menuColor, suspendColor, suspendMenuColor } = useTileColor(color);
const loading = !disabled && 'install' in chad;
const suspended = disabled || 'suspend' in chad;
const hung = 'hung' in chad;
// TODO should held zest be considered inactive? suspended? also, null sync?
const active = !disabled && chadIsRunning(chad);
const link = getAppHref(href);
const backgroundColor = suspended ? suspendColor : active ? tileColor || 'purple' : suspendColor;
@ -56,6 +57,9 @@ export const Tile: FunctionComponent<TileProps> = ({ charge, desk, disabled = fa
>
<div>
<div className="absolute z-10 top-4 left-4 sm:top-6 sm:left-6 flex items-center">
{pike?.zest === 'held' && !disabled && (
<Bullet className="w-4 h-4 text-orange-500 dark:text-black" />
)}
{!active && (
<>
{loading && <Spinner className="h-6 w-6 mr-2" />}
@ -65,9 +69,6 @@ export const Tile: FunctionComponent<TileProps> = ({ charge, desk, disabled = fa
</>
)}
</div>
{vat?.arak.rail?.paused && !disabled && (
<Bullet className="absolute z-10 top-5 left-5 sm:top-7 sm:left-7 w-4 h-4 text-orange-500 dark:text-black" />
)}
<TileMenu
desk={desk}
chad={chad}

View File

@ -3,13 +3,13 @@ import { useHistory, useParams } from 'react-router-dom';
import { Dialog, DialogContent } from '../components/Dialog';
import { AppInfo } from '../components/AppInfo';
import { useCharge } from '../state/docket';
import { useVat } from '../state/kiln';
import { usePike } from '../state/kiln';
export const TileInfo = () => {
const { desk } = useParams<{ desk: string }>();
const { push } = useHistory();
const charge = useCharge(desk);
const vat = useVat(desk);
const pike = usePike(desk);
if (!charge) {
return null;
@ -18,7 +18,7 @@ export const TileInfo = () => {
return (
<Dialog open onOpenChange={(open) => !open && push('/')}>
<DialogContent>
<AppInfo vat={vat} docket={charge} />
<AppInfo pike={pike} docket={charge} />
</DialogContent>
</Dialog>
);