Merge remote-tracking branch 'origin/dist' into m/dist-ames-glob-fixes
@ -512,10 +512,10 @@
|
|||||||
++ pause
|
++ pause
|
||||||
|= lac=desk
|
|= lac=desk
|
||||||
^+ vats
|
^+ vats
|
||||||
|
=. vats (abed lac)
|
||||||
?. is-tracking
|
?. is-tracking
|
||||||
~> %slog.0^leaf/"kiln: {<lac>} already paused, ignoring"
|
~> %slog.0^leaf/"kiln: {<lac>} already paused, ignoring"
|
||||||
vats
|
vats
|
||||||
=. vats (abed lac)
|
|
||||||
~> %slog.0^leaf/"kiln: {<lac>} pausing updates"
|
~> %slog.0^leaf/"kiln: {<lac>} pausing updates"
|
||||||
=/ rel ral
|
=/ rel ral
|
||||||
=. rail.rak `rel(paused &, aeon 0)
|
=. rail.rak `rel(paused &, aeon 0)
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
?- -.diff
|
?- -.diff
|
||||||
%block (block +.diff)
|
%block (block +.diff)
|
||||||
?(%merge-sunk %merge-fail) (desk-arak-err +.diff)
|
?(%merge-sunk %merge-fail) (desk-arak-err +.diff)
|
||||||
?(%reset %merge %suspend %revive) (desk-arak +.diff)
|
?(%reset %commit %suspend %revive) (desk-arak +.diff)
|
||||||
==
|
==
|
||||||
::
|
::
|
||||||
++ block
|
++ block
|
||||||
|
13
pkg/base-dev/mar/ship.hoon
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
|_ s=ship
|
||||||
|
++ grad %noun
|
||||||
|
++ grow
|
||||||
|
|%
|
||||||
|
++ noun s
|
||||||
|
++ json s+(scot %p s)
|
||||||
|
--
|
||||||
|
++ grab
|
||||||
|
|%
|
||||||
|
++ noun ship
|
||||||
|
++ json (su:dejs:format ;~(pfix sig fed:ag))
|
||||||
|
--
|
||||||
|
--
|
@ -67,7 +67,11 @@
|
|||||||
++ report-vat
|
++ report-vat
|
||||||
|= [our=ship now=@da vat]
|
|= [our=ship now=@da vat]
|
||||||
^- tank
|
^- tank
|
||||||
=+ .^(=weft %cx /(scot %p our)/[desk]/(scot %da now)/sys/kelvin)
|
=/ kel-path
|
||||||
|
/(scot %p our)/[desk]/(scot %da now)/sys/kelvin
|
||||||
|
?. .^(? %cu kel-path)
|
||||||
|
leaf+"bad desk: {<desk>}"
|
||||||
|
=+ .^(=weft %cx kel-path)
|
||||||
:+ %rose ["" "{<desk>}" "::"]
|
:+ %rose ["" "{<desk>}" "::"]
|
||||||
^- tang
|
^- tang
|
||||||
=/ meb (mergebase-hashes our desk now arak)
|
=/ meb (mergebase-hashes our desk now arak)
|
||||||
@ -81,7 +85,7 @@
|
|||||||
:~ leaf/"/sys/kelvin: {<[lal num]:weft>}"
|
:~ leaf/"/sys/kelvin: {<[lal num]:weft>}"
|
||||||
leaf/"base hash: {?.(=(1 (lent meb)) <meb> <(head meb)>)}"
|
leaf/"base hash: {?.(=(1 (lent meb)) <meb> <(head meb)>)}"
|
||||||
leaf/"%cz hash: {<hash>}"
|
leaf/"%cz hash: {<hash>}"
|
||||||
leaf/"updates: {sat}"
|
leaf/"updates: {poz}"
|
||||||
leaf/"source ship: {?~(rail.arak <~> <ship.u.rail.arak>)}"
|
leaf/"source ship: {?~(rail.arak <~> <ship.u.rail.arak>)}"
|
||||||
leaf/"source desk: {?~(rail.arak <~> <desk.u.rail.arak>)}"
|
leaf/"source desk: {?~(rail.arak <~> <desk.u.rail.arak>)}"
|
||||||
leaf/"source aeon: {?~(rail.arak <~> <aeon.u.rail.arak>)}"
|
leaf/"source aeon: {?~(rail.arak <~> <aeon.u.rail.arak>)}"
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
title+'Bitcoin'
|
title+'Bitcoin'
|
||||||
info+'BTC wallet for Urbit. Testing'
|
info+'BTC wallet for Urbit. Testing'
|
||||||
color+0xf9.8e40
|
color+0xf9.8e40
|
||||||
glob-http+'https://bootstrap.urbit.org/glob-0v4.ghaim.of1as.9ucee.uj93f.a9nbs.glob'
|
glob-http+['https://bootstrap.urbit.org/glob-0v4.ghaim.of1as.9ucee.uj93f.a9nbs.glob' 0v4.ghaim.of1as.9ucee.uj93f.a9nbs]
|
||||||
image+'https://urbit.ewr1.vultrobjects.com/hastuc-dibtux/2021.8.24..02.57.38-bitcoin.svg'
|
image+'https://urbit.ewr1.vultrobjects.com/hastuc-dibtux/2021.8.24..02.57.38-bitcoin.svg'
|
||||||
base+'bitcoin'
|
base+'bitcoin'
|
||||||
version+[0 0 1]
|
version+[0 0 1]
|
||||||
|
@ -572,15 +572,17 @@
|
|||||||
?: =(suffix /desk/js)
|
?: =(suffix /desk/js)
|
||||||
%- inline-js-response
|
%- inline-js-response
|
||||||
(rap 3 'window.desk = "' u.des '";' ~)
|
(rap 3 'window.desk = "' u.des '";' ~)
|
||||||
|
=/ requested
|
||||||
|
?: (~(has by glob) suffix) suffix
|
||||||
|
/index/html
|
||||||
=/ data=mime
|
=/ data=mime
|
||||||
(~(gut by glob) suffix (~(got by glob) /index/html))
|
(~(got by glob) requested)
|
||||||
=/ mime-type=@t (rsh 3 (crip <p.data>))
|
=/ mime-type=@t (rsh 3 (crip <p.data>))
|
||||||
=; headers
|
=; headers
|
||||||
[[200 headers] `q.data]
|
[[200 headers] `q.data]
|
||||||
:~ content-type+mime-type
|
:- content-type+mime-type
|
||||||
max-1-wk:gen
|
?: =(/index/html requested) ~
|
||||||
'service-worker-allowed'^'/'
|
~[max-1-wk:gen]
|
||||||
==
|
|
||||||
--
|
--
|
||||||
::
|
::
|
||||||
++ get-light-charge
|
++ get-light-charge
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
/- docket, *treaty
|
/- docket, *treaty
|
||||||
/+ default-agent, agentio, verb, dbug
|
/+ default-agent, agentio, verb, dbug
|
||||||
|%
|
|%
|
||||||
|
:: TODO: update before livenet deploy
|
||||||
|
++ default-ally ~zod
|
||||||
|
::
|
||||||
+$ card card:agent:gall
|
+$ card card:agent:gall
|
||||||
+$ state-0
|
+$ state-0
|
||||||
$: treaties=(map [=ship =desk] treaty)
|
$: treaties=(map [=ship =desk] treaty)
|
||||||
@ -23,9 +26,8 @@
|
|||||||
pass pass:io
|
pass pass:io
|
||||||
cc ~(. +> bowl)
|
cc ~(. +> bowl)
|
||||||
++ on-init
|
++ on-init
|
||||||
=/ sponsor=ship (sein:title [our now our]:bowl)
|
?: =(our.bowl default-ally) `this
|
||||||
?: =(our.bowl sponsor) `this
|
(on-poke %ally-update-0 !>([%add default-ally]))
|
||||||
(on-poke %ally-update-0 !>([%add sponsor]))
|
|
||||||
++ on-save !>(state)
|
++ on-save !>(state)
|
||||||
++ on-load
|
++ on-load
|
||||||
|= =vase
|
|= =vase
|
||||||
@ -120,8 +122,9 @@
|
|||||||
|= =path
|
|= =path
|
||||||
^- (unit (unit cage))
|
^- (unit (unit cage))
|
||||||
?+ path (on-peek:def path)
|
?+ path (on-peek:def path)
|
||||||
[%x %alliance ~] ``(alliance-update:cg:ca %ini entente)
|
[%x %alliance ~] ``(alliance-update:cg:ca %ini entente)
|
||||||
[%x %allies ~] ``(ally-update:cg:ca %ini allies)
|
[%x %default-ally ~] ``ship+!>(default-ally)
|
||||||
|
[%x %allies ~] ``(ally-update:cg:ca %ini allies)
|
||||||
::
|
::
|
||||||
[%x %treaties @ ~]
|
[%x %treaties @ ~]
|
||||||
=/ =ship (slav %p i.t.t.path)
|
=/ =ship (slav %p i.t.t.path)
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
:~ title+'Garden'
|
:~ title+'Garden'
|
||||||
info+'An app launcher for Urbit.'
|
info+'An app launcher for Urbit. '
|
||||||
color+0xee.5432
|
color+0xee.5432
|
||||||
glob-http+['https://bootstrap.urbit.org/glob-0v2.dne76.9hibl.1o442.h6l11.cn7os.glob' 0v2.dne76.9hibl.1o442.h6l11.cn7os]
|
glob-http+['https://bootstrap.urbit.org/glob-0v2.dne76.9hibl.1o442.h6l11.cn7os.glob' 0v2.dne76.9hibl.1o442.h6l11.cn7os]
|
||||||
|
glob-http+['https://bootstrap.urbit.org/glob-0v7.1jqf3.5gao8.67i4k.64c49.i6kr8.glob' 0v7.1jqf3.5gao8.67i4k.64c49.i6kr8]
|
||||||
::glob-ames+~zod^0v0
|
::glob-ames+~zod^0v0
|
||||||
base+'grid'
|
base+'grid'
|
||||||
version+[0 0 1]
|
version+[0 0 1]
|
||||||
|
1
pkg/garden/mar/ship.hoon
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../../base-dev/mar/ship.hoon
|
@ -2,14 +2,17 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" href="/src/assets/favicon.png" />
|
|
||||||
<link rel="icon" href="/src/assets/favicon.svg" sizes="any" type="image/svg+xml" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Landscape • Home</title>
|
<title>Landscape • Home</title>
|
||||||
|
<meta name="theme-color" content="#ffffff" />
|
||||||
|
<link rel="icon" href="/src/assets/favicon.svg" sizes="any" type="image/svg+xml" />
|
||||||
|
<link rel="mask-icon" href="/src/assets/safari-pinned-tab.svg" color="#000000" />
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/src/assets/apple-touch-icon.png" />
|
||||||
|
<link rel="manifest" href="/src/assets/manifest.json" />
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
<link
|
<link
|
||||||
href="https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@400;600&display=swap"
|
href="https://fonts.googleapis.com/css2?family=Inter&family=Source+Code+Pro:wght@400;600&display=swap"
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
/>
|
/>
|
||||||
</head>
|
</head>
|
||||||
|
14091
pkg/grid/package-lock.json
generated
@ -49,7 +49,8 @@ const AppRoutes = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
window.name = 'grid';
|
window.name = 'grid';
|
||||||
|
|
||||||
const { fetchAllies, fetchCharges } = useDocketState.getState();
|
const { fetchDefaultAlly, fetchAllies, fetchCharges } = useDocketState.getState();
|
||||||
|
fetchDefaultAlly();
|
||||||
fetchCharges();
|
fetchCharges();
|
||||||
fetchAllies();
|
fetchAllies();
|
||||||
const { fetchVats, fetchLag } = useKilnState.getState();
|
const { fetchVats, fetchLag } = useKilnState.getState();
|
||||||
|
BIN
pkg/grid/src/assets/android-chrome-512x512.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
pkg/grid/src/assets/apple-touch-icon.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
pkg/grid/src/assets/favicon.ico
Normal file
After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 4.0 KiB |
@ -1,11 +1,12 @@
|
|||||||
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="513" height="513" viewBox="0 0 513 513" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<style>
|
<style>
|
||||||
|
.icon-color { fill: #000000; }
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
rect { fill: #ffffff; }
|
.icon-color { fill: #ffffff; }
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<rect x="51" y="51" width="173" height="173" rx="24" fill="#444444"/>
|
<path d="M247.634 122.758C247.634 147.519 238.15 162.538 219.182 171.468C198.239 181.21 184.804 197.853 178.876 220.584C172.159 245.751 153.981 260.364 129.481 259.552C109.723 259.146 94.7075 249.404 86.014 231.138C79.2963 216.931 70.6028 204.753 56.7723 197.041C50.4497 193.388 43.732 190.952 36.6192 188.923C16.4661 183.24 1.84525 166.597 0.264615 145.896C-1.71118 121.135 7.37747 103.274 27.5305 93.1266C49.6594 82.5728 63.49 65.1184 69.4173 41.1695C75.3447 16.8145 95.8929 2.60752 122.369 4.6371C137.385 5.85484 148.844 13.5672 157.538 25.7447C163.465 34.2689 168.602 43.199 174.529 51.3173C181.642 61.4652 191.126 67.9598 202.586 72.019C208.118 74.0486 214.045 76.0781 219.182 78.9195C238.15 88.6615 247.634 104.492 247.634 122.758Z" class="icon-color" />
|
||||||
<rect x="51" y="288" width="173" height="173" rx="24" fill="#444444"/>
|
<path d="M512.01 355.757C511.615 372.806 503.712 387.824 486.325 396.349C463.406 407.714 449.18 425.981 442.067 450.335C435.745 471.037 419.543 484.026 398.6 485.65C377.657 487.274 359.084 476.72 350.391 457.236C340.512 434.505 323.915 420.704 300.601 414.209C258.714 403.249 254.762 350.48 279.262 328.561C283.609 324.908 288.351 321.254 293.488 318.819C314.036 308.671 327.076 292.029 333.399 270.109C338.931 250.219 350.786 236.824 370.939 232.765C393.858 228.3 415.197 235.2 426.261 259.555C436.535 282.287 453.132 295.682 476.446 302.582C500.551 309.889 512.01 326.937 512.01 355.757Z" class="icon-color" />
|
||||||
<rect x="288" y="51" width="173" height="173" rx="24" fill="#444444"/>
|
<path d="M463.786 51.3199C462.6 58.2204 461.415 65.121 459.439 71.6156C453.117 93.1291 454.697 113.425 466.157 132.909C476.431 150.363 476.036 168.629 465.762 185.678C455.883 202.32 440.472 210.033 421.899 210.033C417.157 210.033 412.415 208.815 408.069 207.191C384.359 199.073 361.44 200.697 339.706 214.498C310.069 233.17 271.739 206.379 268.973 178.371C268.182 169.441 268.182 160.511 271.344 152.393C280.037 130.067 276.481 109.366 266.997 89.07C262.65 80.1398 259.094 70.8038 259.094 60.6559C259.489 27.3709 290.311 3.42188 321.529 11.9461C331.408 14.7875 341.287 18.4407 351.956 18.4407C364.206 18.4407 374.875 14.3816 385.94 9.10469C393.052 5.45146 400.956 2.20414 408.859 0.580478C434.544 -3.88459 461.02 18.0348 462.996 44.8252C462.996 47.2607 463.391 49.2903 463.391 51.7258C463.391 51.3199 463.786 51.3199 463.786 51.3199Z" class="icon-color" />
|
||||||
<rect x="288" y="288" width="173" height="173" rx="24" fill="#444444"/>
|
<path d="M235.757 342.773C234.571 349.268 233.781 356.168 231.805 362.663C225.483 384.176 227.063 404.878 238.523 424.362C257.095 456.429 237.337 498.239 197.822 501.486C192.684 501.892 187.152 500.674 182.015 499.05C157.515 490.526 134.201 491.744 112.072 505.951C82.8304 524.217 44.8952 497.833 41.7339 470.636C40.9436 461.706 40.5485 453.182 43.7097 444.658C52.4032 421.521 48.8468 399.601 38.1775 378.088C33.0405 367.94 30.2743 356.98 31.4598 345.209C33.8308 322.883 53.9839 302.587 76.1127 300.964C84.4111 300.558 92.3142 301.37 99.8222 304.211C120.766 312.329 140.524 309.488 159.886 298.528C174.507 290.004 190.314 286.757 206.12 294.469C224.692 304.617 234.966 320.448 235.757 342.773Z" class="icon-color" />
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 474 B After Width: | Height: | Size: 3.1 KiB |
14
pkg/grid/src/assets/manifest.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "urbit",
|
||||||
|
"short_name": "urbit",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/apps/grid/android-chrome-512x512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"theme_color": "#ffffff",
|
||||||
|
"background_color": "#ffffff",
|
||||||
|
"display": "standalone"
|
||||||
|
}
|
54
pkg/grid/src/assets/safari-pinned-tab.svg
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||||
|
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||||
|
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="700.000000pt" height="700.000000pt" viewBox="0 0 700.000000 700.000000"
|
||||||
|
preserveAspectRatio="xMidYMid meet">
|
||||||
|
<metadata>
|
||||||
|
Created by potrace 1.14, written by Peter Selinger 2001-2017
|
||||||
|
</metadata>
|
||||||
|
<g transform="translate(0.000000,700.000000) scale(0.100000,-0.100000)"
|
||||||
|
fill="#000000" stroke="none">
|
||||||
|
<path d="M5565 6989 c-16 -5 -39 -11 -50 -13 -54 -12 -116 -37 -230 -91 -219
|
||||||
|
-105 -335 -135 -495 -131 -63 2 -126 7 -140 10 -14 4 -36 9 -50 12 -14 3 -79
|
||||||
|
21 -145 41 -205 61 -361 54 -525 -23 -239 -113 -392 -356 -392 -625 0 -110 25
|
||||||
|
-201 107 -389 85 -197 116 -320 121 -480 3 -137 -12 -231 -60 -370 -15 -41
|
||||||
|
-30 -97 -33 -125 -8 -58 -8 -190 0 -255 27 -220 225 -440 475 -527 67 -24 92
|
||||||
|
-27 197 -27 109 0 127 3 189 28 37 16 100 47 140 70 174 103 316 141 521 140
|
||||||
|
146 0 208 -10 362 -58 171 -54 291 -54 449 -1 294 99 498 442 448 755 -4 25
|
||||||
|
-9 50 -10 55 -2 6 -7 24 -10 40 -4 17 -36 86 -71 153 -60 117 -113 253 -128
|
||||||
|
332 -4 19 -8 91 -10 160 -4 126 8 227 40 336 24 82 65 283 60 297 -2 6 -6 43
|
||||||
|
-10 81 -8 97 -22 150 -62 231 -44 90 -71 126 -148 200 -123 117 -255 175 -415
|
||||||
|
180 -52 2 -108 -1 -125 -6z"/>
|
||||||
|
<path d="M1445 6923 c-38 -8 -106 -31 -150 -52 -187 -90 -284 -214 -354 -455
|
||||||
|
-59 -201 -123 -321 -243 -451 -81 -88 -154 -143 -295 -222 -64 -35 -135 -79
|
||||||
|
-157 -96 -100 -76 -187 -210 -223 -347 -21 -80 -24 -308 -4 -390 22 -91 84
|
||||||
|
-209 146 -277 102 -112 188 -163 385 -228 307 -100 451 -229 620 -550 61 -115
|
||||||
|
94 -161 164 -228 52 -50 156 -117 181 -117 8 0 16 -4 19 -9 16 -26 274 -53
|
||||||
|
356 -37 14 3 37 7 53 10 71 12 165 55 237 109 122 90 201 216 265 420 51 165
|
||||||
|
96 258 172 359 86 114 212 216 358 290 147 75 226 139 292 238 36 53 80 162
|
||||||
|
89 216 3 22 8 42 10 46 3 4 7 53 9 109 10 221 -44 374 -180 512 -67 68 -167
|
||||||
|
136 -253 173 -31 14 -66 29 -77 33 -11 5 -47 19 -80 31 -164 59 -294 148 -377
|
||||||
|
257 -25 32 -97 137 -160 233 -131 199 -185 263 -272 324 -147 103 -335 138
|
||||||
|
-531 99z"/>
|
||||||
|
<path d="M5070 3824 c-177 -38 -296 -111 -399 -245 -36 -46 -88 -160 -122
|
||||||
|
-264 -103 -321 -254 -508 -534 -660 -184 -100 -292 -208 -352 -350 -113 -268
|
||||||
|
-56 -593 138 -785 81 -80 154 -122 300 -171 152 -51 233 -88 322 -147 139 -93
|
||||||
|
274 -254 346 -415 83 -185 208 -309 376 -372 84 -32 139 -40 260 -39 145 1
|
||||||
|
248 34 370 117 121 82 205 205 266 388 113 340 278 537 601 714 145 80 234
|
||||||
|
172 290 300 86 195 64 503 -49 694 -50 85 -141 172 -224 216 -37 19 -120 53
|
||||||
|
-184 76 -197 67 -303 129 -425 246 -92 87 -158 179 -230 323 -102 202 -207
|
||||||
|
301 -380 360 -82 28 -272 36 -370 14z"/>
|
||||||
|
<path d="M2395 3017 c-76 -22 -110 -37 -270 -119 -72 -38 -195 -82 -252 -93
|
||||||
|
-127 -22 -184 -24 -293 -10 -86 11 -114 17 -179 40 -132 47 -193 58 -317 57
|
||||||
|
-103 0 -176 -18 -268 -64 -142 -72 -258 -190 -325 -331 -95 -200 -84 -408 34
|
||||||
|
-666 95 -209 136 -366 136 -528 1 -142 -12 -213 -67 -380 -22 -66 -27 -97 -27
|
||||||
|
-198 -1 -66 2 -142 6 -169 29 -183 195 -384 394 -476 186 -87 369 -86 533 2
|
||||||
|
41 23 102 55 135 73 73 39 196 80 290 95 69 12 250 15 283 6 9 -3 38 -7 64
|
||||||
|
-11 26 -3 87 -17 135 -31 48 -14 113 -32 143 -41 211 -56 483 46 643 242 48
|
||||||
|
58 120 194 131 245 3 14 8 36 12 50 13 50 13 225 0 285 -19 81 -35 123 -94
|
||||||
|
240 -96 188 -122 287 -127 468 -2 115 7 188 40 317 52 202 68 337 50 423 -3
|
||||||
|
15 -7 39 -10 54 -8 38 -40 125 -58 159 -8 16 -24 45 -33 64 -28 53 -150 172
|
||||||
|
-229 222 -106 68 -172 89 -295 94 -88 3 -118 0 -185 -19z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.3 KiB |
@ -68,6 +68,8 @@ export const AppInfo: FC<AppInfoProps> = ({ docket, vat, className }) => {
|
|||||||
}, 1250);
|
}, 1250);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const installing = installStatus === 'installing';
|
||||||
|
|
||||||
if (!docket) {
|
if (!docket) {
|
||||||
// TODO: maybe replace spinner with skeletons
|
// TODO: maybe replace spinner with skeletons
|
||||||
return (
|
return (
|
||||||
@ -87,15 +89,15 @@ export const AppInfo: FC<AppInfoProps> = ({ docket, vat, className }) => {
|
|||||||
as="a"
|
as="a"
|
||||||
href={getAppHref(docket.href)}
|
href={getAppHref(docket.href)}
|
||||||
target={docket.desk || '_blank'}
|
target={docket.desk || '_blank'}
|
||||||
onClick={() => addRecentApp(docket)}
|
onClick={() => addRecentApp(docket.desk)}
|
||||||
>
|
>
|
||||||
Open App
|
Open App
|
||||||
</PillButton>
|
</PillButton>
|
||||||
)}
|
)}
|
||||||
{installStatus !== 'installed' && (
|
{installStatus !== 'installed' && (
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<DialogTrigger as={PillButton} variant="alt-primary">
|
<DialogTrigger as={PillButton} disabled={installing} variant="alt-primary">
|
||||||
{installStatus === 'installing' ? (
|
{installing ? (
|
||||||
<>
|
<>
|
||||||
<Spinner />
|
<Spinner />
|
||||||
<span className="sr-only">Installing...</span>
|
<span className="sr-only">Installing...</span>
|
||||||
|
@ -52,7 +52,7 @@ export const AppList = <T extends DocketWithDesk>({
|
|||||||
size={size}
|
size={size}
|
||||||
selected={selected(app)}
|
selected={selected(app)}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
addRecentApp(app);
|
addRecentApp(app.desk);
|
||||||
onClick?.(e, app);
|
onClick?.(e, app);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -4,6 +4,7 @@ import { sigil, reactRenderer } from '@tlon/sigil-js';
|
|||||||
import { deSig, Contact } from '@urbit/api';
|
import { deSig, Contact } from '@urbit/api';
|
||||||
import { darken, lighten, parseToHsla } from 'color2k';
|
import { darken, lighten, parseToHsla } from 'color2k';
|
||||||
import { useCurrentTheme } from '../state/local';
|
import { useCurrentTheme } from '../state/local';
|
||||||
|
import { normalizeUrbitColor } from '../state/util';
|
||||||
|
|
||||||
export type AvatarSizes = 'xs' | 'small' | 'default';
|
export type AvatarSizes = 'xs' | 'small' | 'default';
|
||||||
|
|
||||||
@ -66,7 +67,7 @@ export const Avatar = ({ size, className, ...ship }: AvatarProps) => {
|
|||||||
const currentTheme = useCurrentTheme();
|
const currentTheme = useCurrentTheme();
|
||||||
const { shipName, color, avatar } = { ...emptyContact, ...ship };
|
const { shipName, color, avatar } = { ...emptyContact, ...ship };
|
||||||
const { classes, size: sigilSize } = sizeMap[size];
|
const { classes, size: sigilSize } = sizeMap[size];
|
||||||
const adjustedColor = themeAdjustColor(color, currentTheme);
|
const adjustedColor = themeAdjustColor(normalizeUrbitColor(color), currentTheme);
|
||||||
const foregroundColor = foregroundFromBackground(adjustedColor);
|
const foregroundColor = foregroundFromBackground(adjustedColor);
|
||||||
const sigilElement = useMemo(() => {
|
const sigilElement = useMemo(() => {
|
||||||
if (shipName.match(/[_^]/)) {
|
if (shipName.match(/[_^]/)) {
|
||||||
|
@ -8,10 +8,18 @@ import { Toggle } from './Toggle';
|
|||||||
type SettingsProps = {
|
type SettingsProps = {
|
||||||
name: string;
|
name: string;
|
||||||
on: boolean;
|
on: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
toggle: (open: boolean) => Promise<void>;
|
toggle: (open: boolean) => Promise<void>;
|
||||||
} & HTMLAttributes<HTMLDivElement>;
|
} & HTMLAttributes<HTMLDivElement>;
|
||||||
|
|
||||||
export const Setting: FC<SettingsProps> = ({ name, on, toggle, className, children }) => {
|
export const Setting: FC<SettingsProps> = ({
|
||||||
|
name,
|
||||||
|
on,
|
||||||
|
disabled = false,
|
||||||
|
toggle,
|
||||||
|
className,
|
||||||
|
children
|
||||||
|
}) => {
|
||||||
const { status, call } = useAsyncCall(toggle);
|
const { status, call } = useAsyncCall(toggle);
|
||||||
const id = slugify(name);
|
const id = slugify(name);
|
||||||
|
|
||||||
@ -26,6 +34,8 @@ export const Setting: FC<SettingsProps> = ({ name, on, toggle, className, childr
|
|||||||
pressed={on}
|
pressed={on}
|
||||||
onPressedChange={call}
|
onPressedChange={call}
|
||||||
className="flex-none self-start text-blue-400"
|
className="flex-none self-start text-blue-400"
|
||||||
|
disabled={disabled}
|
||||||
|
loading={status === 'loading'}
|
||||||
/>
|
/>
|
||||||
<div className="flex-1 flex flex-col justify-center space-y-6">{children}</div>
|
<div className="flex-1 flex flex-col justify-center space-y-6">{children}</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,13 +6,17 @@ import type * as Polymorphic from '@radix-ui/react-polymorphic';
|
|||||||
type ToggleComponent = Polymorphic.ForwardRefComponent<
|
type ToggleComponent = Polymorphic.ForwardRefComponent<
|
||||||
Polymorphic.IntrinsicElement<typeof RadixToggle.Root>,
|
Polymorphic.IntrinsicElement<typeof RadixToggle.Root>,
|
||||||
Polymorphic.OwnProps<typeof RadixToggle.Root> & {
|
Polymorphic.OwnProps<typeof RadixToggle.Root> & {
|
||||||
|
loading?: boolean;
|
||||||
toggleClass?: string;
|
toggleClass?: string;
|
||||||
knobClass?: string;
|
knobClass?: string;
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export const Toggle = React.forwardRef(
|
export const Toggle = React.forwardRef(
|
||||||
({ defaultPressed, pressed, onPressedChange, disabled, className, toggleClass }, ref) => {
|
(
|
||||||
|
{ defaultPressed, pressed, onPressedChange, disabled, className, toggleClass, loading = false },
|
||||||
|
ref
|
||||||
|
) => {
|
||||||
const [on, setOn] = useState(defaultPressed);
|
const [on, setOn] = useState(defaultPressed);
|
||||||
const isControlled = !!onPressedChange;
|
const isControlled = !!onPressedChange;
|
||||||
const proxyPressed = isControlled ? pressed : on;
|
const proxyPressed = isControlled ? pressed : on;
|
||||||
@ -24,7 +28,7 @@ export const Toggle = React.forwardRef(
|
|||||||
className={classNames('default-ring rounded-full', className)}
|
className={classNames('default-ring rounded-full', className)}
|
||||||
pressed={proxyPressed}
|
pressed={proxyPressed}
|
||||||
onPressedChange={proxyOnPressedChange}
|
onPressedChange={proxyOnPressedChange}
|
||||||
disabled={disabled}
|
disabled={disabled || loading}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
|
@ -25,8 +25,8 @@ function SystemPreferencesSection({
|
|||||||
<Link
|
<Link
|
||||||
to={url}
|
to={url}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'flex items-center px-2 py-2 hover:text-black hover:bg-gray-100 rounded-xl',
|
'flex items-center px-2 py-2 hover:text-black hover:bg-gray-50 rounded-xl',
|
||||||
active && 'text-black bg-gray-100'
|
active && 'text-black bg-gray-50'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
@ -63,7 +63,7 @@ export const SystemPreferences = (props: RouteComponentProps<{ submenu: string }
|
|||||||
<div className="flex h-full overflow-y-auto">
|
<div className="flex h-full overflow-y-auto">
|
||||||
<aside className="flex-none self-start min-w-60 py-8 font-semibold border-r-2 border-gray-50">
|
<aside className="flex-none self-start min-w-60 py-8 font-semibold border-r-2 border-gray-50">
|
||||||
<nav className="px-6">
|
<nav className="px-6">
|
||||||
<ul>
|
<ul className="space-y-1">
|
||||||
<SystemPreferencesSection
|
<SystemPreferencesSection
|
||||||
url={subUrl('notifications')}
|
url={subUrl('notifications')}
|
||||||
active={matchSub('notifications')}
|
active={matchSub('notifications')}
|
||||||
@ -86,7 +86,7 @@ export const SystemPreferences = (props: RouteComponentProps<{ submenu: string }
|
|||||||
</nav>
|
</nav>
|
||||||
<hr className="my-4 border-t-2 border-gray-50" />
|
<hr className="my-4 border-t-2 border-gray-50" />
|
||||||
<nav className="px-6">
|
<nav className="px-6">
|
||||||
<ul>
|
<ul className="space-y-1">
|
||||||
{Object.values(charges).map((charge) => (
|
{Object.values(charges).map((charge) => (
|
||||||
<SystemPreferencesSection
|
<SystemPreferencesSection
|
||||||
key={charge.desk}
|
key={charge.desk}
|
||||||
|
@ -8,6 +8,7 @@ import { ShipName } from '../../components/ShipName';
|
|||||||
import { DeskLink } from '../../components/DeskLink';
|
import { DeskLink } from '../../components/DeskLink';
|
||||||
import { useHarkStore } from '../../state/hark';
|
import { useHarkStore } from '../../state/hark';
|
||||||
import { DocketImage } from '../../components/DocketImage';
|
import { DocketImage } from '../../components/DocketImage';
|
||||||
|
import { Button } from '../../components/Button';
|
||||||
|
|
||||||
interface BasicNotificationProps {
|
interface BasicNotificationProps {
|
||||||
notification: Notification;
|
notification: Notification;
|
||||||
@ -45,14 +46,20 @@ export const BasicNotification = ({ notification, lid }: BasicNotificationProps)
|
|||||||
useHarkStore.getState().archiveNote(notification.bin, lid);
|
useHarkStore.getState().archiveNote(notification.bin, lid);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const archiveNoFollow = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
archive();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DeskLink
|
<DeskLink
|
||||||
onClick={archive}
|
onClick={archive}
|
||||||
to={`?grid-note=${encodeURIComponent(first.link)}`}
|
to={`?grid-note=${encodeURIComponent(first.link)}`}
|
||||||
desk={desk}
|
desk={desk}
|
||||||
className={cn(
|
className={cn(
|
||||||
'text-black rounded-xl',
|
'text-black rounded-xl group',
|
||||||
'unseen' in lid ? 'bg-blue-100' : 'bg-gray-100',
|
'unseen' in lid ? 'bg-blue-100' : 'bg-gray-50',
|
||||||
large ? 'note-grid-no-content' : 'note-grid-content'
|
large ? 'note-grid-no-content' : 'note-grid-content'
|
||||||
)}
|
)}
|
||||||
aria-labelledby={id}
|
aria-labelledby={id}
|
||||||
@ -64,6 +71,9 @@ export const BasicNotification = ({ notification, lid }: BasicNotificationProps)
|
|||||||
<h2 id={`${id}-title`} className="note-grid-head font-semibold text-gray-600">
|
<h2 id={`${id}-title`} className="note-grid-head font-semibold text-gray-600">
|
||||||
<NotificationText contents={first.title} />
|
<NotificationText contents={first.title} />
|
||||||
</h2>
|
</h2>
|
||||||
|
<div className="note-grid-actions hidden justify-center self-center group-hover:flex">
|
||||||
|
<Button onClick={archiveNoFollow}>Archive</Button>
|
||||||
|
</div>
|
||||||
</header>
|
</header>
|
||||||
{contents.length > 0 ? (
|
{contents.length > 0 ? (
|
||||||
<div className="note-grid-body space-y-2">
|
<div className="note-grid-body space-y-2">
|
||||||
|
@ -14,8 +14,8 @@ const cards: OnboardingCardProps[] = [
|
|||||||
href: '/leap/search/direct/apps/~zod/webterm'
|
href: '/leap/search/direct/apps/~zod/webterm'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Groups',
|
title: 'Landscape',
|
||||||
body: 'Install Groups, a suite of social software to communicate with other urbit users',
|
body: 'Install Landscape, a suite of social software to communicate with other urbit users',
|
||||||
button: 'Install',
|
button: 'Install',
|
||||||
color: '#D1DDD3',
|
color: '#D1DDD3',
|
||||||
href: '/leap/search/direct/apps/~zod/landscape'
|
href: '/leap/search/direct/apps/~zod/landscape'
|
||||||
@ -43,6 +43,16 @@ const cards: OnboardingCardProps[] = [
|
|||||||
// }
|
// }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if ('registerProtocolHandler' in window.navigator) {
|
||||||
|
cards.push({
|
||||||
|
title: 'Open Urbit-Native Links',
|
||||||
|
body: 'Enable your Urbit to open links you find in the wild',
|
||||||
|
button: 'Enable Link Handler',
|
||||||
|
color: '#82A6CA',
|
||||||
|
href: '/apps/grid/leap/system-preferences/interface'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
interface OnboardingCardProps {
|
interface OnboardingCardProps {
|
||||||
title: string;
|
title: string;
|
||||||
button: string;
|
button: string;
|
||||||
|
@ -4,6 +4,8 @@ import { useProtocolHandling, setLocalState } from '../../state/local';
|
|||||||
|
|
||||||
export function InterfacePrefs() {
|
export function InterfacePrefs() {
|
||||||
const protocolHandling = useProtocolHandling();
|
const protocolHandling = useProtocolHandling();
|
||||||
|
const secure = window.location.protocol === 'https:';
|
||||||
|
const linkHandlingAllowed = secure && !('registerProtocolHandler' in window.navigator);
|
||||||
const toggleProtoHandling = async () => {
|
const toggleProtoHandling = async () => {
|
||||||
if (!protocolHandling && window?.navigator?.registerProtocolHandler) {
|
if (!protocolHandling && window?.navigator?.registerProtocolHandler) {
|
||||||
try {
|
try {
|
||||||
@ -33,8 +35,20 @@ export function InterfacePrefs() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h2 className="h3 mb-7">Interface Settings</h2>
|
<h2 className="h3 mb-7">Interface Settings</h2>
|
||||||
<Setting on={protocolHandling} toggle={toggleProtoHandling} name="Handle Urbit links">
|
<Setting
|
||||||
<p>Automatically open urbit links with this urbit</p>
|
on={protocolHandling}
|
||||||
|
toggle={toggleProtoHandling}
|
||||||
|
name="Handle Urbit links"
|
||||||
|
disabled={!linkHandlingAllowed}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
Automatically open urbit links with this urbit
|
||||||
|
{!linkHandlingAllowed && (
|
||||||
|
<>
|
||||||
|
, <strong className="text-orange-500">requires HTTPS</strong>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
</Setting>
|
</Setting>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -9,7 +9,7 @@ const selDnd = (s: SettingsState) => s.display.doNotDisturb;
|
|||||||
async function toggleDnd() {
|
async function toggleDnd() {
|
||||||
const state = useSettingsState.getState();
|
const state = useSettingsState.getState();
|
||||||
const curr = selDnd(state);
|
const curr = selDnd(state);
|
||||||
if(curr) {
|
if (curr) {
|
||||||
Notification.requestPermission();
|
Notification.requestPermission();
|
||||||
}
|
}
|
||||||
await state.putEntry('display', 'doNotDisturb', !curr);
|
await state.putEntry('display', 'doNotDisturb', !curr);
|
||||||
@ -24,12 +24,18 @@ async function toggleMentions() {
|
|||||||
export const NotificationPrefs = () => {
|
export const NotificationPrefs = () => {
|
||||||
const doNotDisturb = useSettingsState(selDnd);
|
const doNotDisturb = useSettingsState(selDnd);
|
||||||
const mentions = useHarkStore(selMentions);
|
const mentions = useHarkStore(selMentions);
|
||||||
|
const secure = window.location.protocol === 'https:';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h2 className="h3 mb-7">Notifications</h2>
|
<h2 className="h3 mb-7">Notifications</h2>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<Setting on={doNotDisturb} toggle={toggleDnd} name="Do Not Disturb">
|
<Setting
|
||||||
|
on={doNotDisturb}
|
||||||
|
toggle={toggleDnd}
|
||||||
|
name="Do Not Disturb"
|
||||||
|
disabled={doNotDisturb && !secure}
|
||||||
|
>
|
||||||
<p>
|
<p>
|
||||||
Block visual desktop notifications whenever Urbit software produces an in-Landscape
|
Block visual desktop notifications whenever Urbit software produces an in-Landscape
|
||||||
notification badge.
|
notification badge.
|
||||||
@ -37,6 +43,11 @@ export const NotificationPrefs = () => {
|
|||||||
<p>
|
<p>
|
||||||
Turning this "off" will prompt your browser to ask if you'd like to
|
Turning this "off" will prompt your browser to ask if you'd like to
|
||||||
enable notifications
|
enable notifications
|
||||||
|
{!secure && (
|
||||||
|
<>
|
||||||
|
, <strong className="text-orange-500">requires HTTPS</strong>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
</Setting>
|
</Setting>
|
||||||
<Setting on={mentions} toggle={toggleMentions} name="Mentions">
|
<Setting on={mentions} toggle={toggleMentions} name="Mentions">
|
||||||
|
@ -12,7 +12,7 @@ export const SystemUpdatePrefs = () => {
|
|||||||
_.pick(s, ['toggleOTAs', 'changeOTASource'])
|
_.pick(s, ['toggleOTAs', 'changeOTASource'])
|
||||||
);
|
);
|
||||||
const base = useVat('base');
|
const base = useVat('base');
|
||||||
const otasEnabled = base && !(base.arak?.rail ?? true);
|
const otasEnabled = base && !(base.arak?.rail?.paused ?? true);
|
||||||
const otaSource = base && base.arak.rail!.ship!;
|
const otaSource = base && base.arak.rail!.ship!;
|
||||||
|
|
||||||
const toggleBase = useCallback((on: boolean) => toggleOTAs('base', on), [toggleOTAs]);
|
const toggleBase = useCallback((on: boolean) => toggleOTAs('base', on), [toggleOTAs]);
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import produce from 'immer';
|
import produce from 'immer';
|
||||||
import create from 'zustand';
|
import create from 'zustand';
|
||||||
|
import _ from 'lodash';
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { persist } from 'zustand/middleware';
|
import { persist } from 'zustand/middleware';
|
||||||
import { take } from 'lodash';
|
|
||||||
import { MatchItem, useLeapStore } from '../Nav';
|
import { MatchItem, useLeapStore } from '../Nav';
|
||||||
import { providerMatch } from './Providers';
|
import { providerMatch } from './Providers';
|
||||||
import { AppList } from '../../components/AppList';
|
import { AppList } from '../../components/AppList';
|
||||||
@ -10,15 +10,16 @@ import { ProviderList } from '../../components/ProviderList';
|
|||||||
import { AppLink } from '../../components/AppLink';
|
import { AppLink } from '../../components/AppLink';
|
||||||
import { ShipName } from '../../components/ShipName';
|
import { ShipName } from '../../components/ShipName';
|
||||||
import { ProviderLink } from '../../components/ProviderLink';
|
import { ProviderLink } from '../../components/ProviderLink';
|
||||||
import { DocketWithDesk, useCharges } from '../../state/docket';
|
import useDocketState, { ChargesWithDesks, useCharges } from '../../state/docket';
|
||||||
import { getAppHref } from '../../state/util';
|
import { getAppHref } from '../../state/util';
|
||||||
import useContactState from '../../state/contact';
|
import useContactState from '../../state/contact';
|
||||||
|
|
||||||
export interface RecentsStore {
|
export interface RecentsStore {
|
||||||
recentApps: DocketWithDesk[];
|
recentApps: string[];
|
||||||
recentDevs: string[];
|
recentDevs: string[];
|
||||||
addRecentApp: (app: DocketWithDesk) => void;
|
addRecentApp: (desk: string) => void;
|
||||||
addRecentDev: (ship: string) => void;
|
addRecentDev: (ship: string) => void;
|
||||||
|
removeRecentApp: (desk: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useRecentsStore = create<RecentsStore>(
|
export const useRecentsStore = create<RecentsStore>(
|
||||||
@ -26,15 +27,15 @@ export const useRecentsStore = create<RecentsStore>(
|
|||||||
(set) => ({
|
(set) => ({
|
||||||
recentApps: [],
|
recentApps: [],
|
||||||
recentDevs: [],
|
recentDevs: [],
|
||||||
addRecentApp: (app) => {
|
addRecentApp: (desk: string) => {
|
||||||
set(
|
set(
|
||||||
produce((draft: RecentsStore) => {
|
produce((draft: RecentsStore) => {
|
||||||
const hasApp = draft.recentApps.find((a) => a.desk === app.desk);
|
const hasApp = draft.recentApps.find((testDesk) => testDesk === desk);
|
||||||
if (!hasApp) {
|
if (!hasApp) {
|
||||||
draft.recentApps.unshift(app);
|
draft.recentApps.unshift(desk);
|
||||||
}
|
}
|
||||||
|
|
||||||
draft.recentApps = take(draft.recentApps, 3);
|
draft.recentApps = _.take(draft.recentApps, 3);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -46,7 +47,14 @@ export const useRecentsStore = create<RecentsStore>(
|
|||||||
draft.recentDevs.unshift(dev);
|
draft.recentDevs.unshift(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
draft.recentDevs = take(draft.recentDevs, 3);
|
draft.recentDevs = _.take(draft.recentDevs, 3);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
removeRecentApp: (desk: string) => {
|
||||||
|
set(
|
||||||
|
produce((draft: RecentsStore) => {
|
||||||
|
_.remove(draft.recentApps, (test) => test === desk);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -65,21 +73,28 @@ export function addRecentDev(dev: string) {
|
|||||||
return useRecentsStore.getState().addRecentDev(dev);
|
return useRecentsStore.getState().addRecentDev(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addRecentApp(app: DocketWithDesk) {
|
export function addRecentApp(app: string) {
|
||||||
return useRecentsStore.getState().addRecentApp(app);
|
return useRecentsStore.getState().addRecentApp(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getApps(desks: string[], charges: ChargesWithDesks) {
|
||||||
|
return desks.filter((desk) => desk in charges).map((desk) => charges[desk]);
|
||||||
|
}
|
||||||
|
|
||||||
export const Home = () => {
|
export const Home = () => {
|
||||||
const selectedMatch = useLeapStore((state) => state.selectedMatch);
|
const selectedMatch = useLeapStore((state) => state.selectedMatch);
|
||||||
const { recentApps, recentDevs } = useRecentsStore();
|
const { recentApps, recentDevs } = useRecentsStore();
|
||||||
const charges = useCharges();
|
const charges = useCharges();
|
||||||
const groups = charges?.groups;
|
const groups = charges?.groups;
|
||||||
const contacts = useContactState((s) => s.contacts);
|
const contacts = useContactState((s) => s.contacts);
|
||||||
const zod = { shipName: '~zod', ...contacts['~zod'] };
|
const defaultAlly = useDocketState((s) =>
|
||||||
|
s.defaultAlly ? { shipName: s.defaultAlly, ...contacts[s.defaultAlly] } : null
|
||||||
|
);
|
||||||
const providerList = recentDevs.map((d) => ({ shipName: d, ...contacts[d] }));
|
const providerList = recentDevs.map((d) => ({ shipName: d, ...contacts[d] }));
|
||||||
|
const apps = getApps(recentApps, charges);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const apps = recentApps.map((app) => ({
|
const appMatches = apps.map((app) => ({
|
||||||
url: getAppHref(app.href),
|
url: getAppHref(app.href),
|
||||||
openInNewTab: true,
|
openInNewTab: true,
|
||||||
value: app.desk,
|
value: app.desk,
|
||||||
@ -88,7 +103,7 @@ export const Home = () => {
|
|||||||
const devs = recentDevs.map(providerMatch);
|
const devs = recentDevs.map(providerMatch);
|
||||||
|
|
||||||
useLeapStore.setState({
|
useLeapStore.setState({
|
||||||
matches: ([] as MatchItem[]).concat(apps, devs)
|
matches: ([] as MatchItem[]).concat(appMatches, devs)
|
||||||
});
|
});
|
||||||
}, [recentApps, recentDevs]);
|
}, [recentApps, recentDevs]);
|
||||||
|
|
||||||
@ -97,26 +112,15 @@ export const Home = () => {
|
|||||||
<h2 id="recent-apps" className="mb-4 h4 text-gray-500">
|
<h2 id="recent-apps" className="mb-4 h4 text-gray-500">
|
||||||
Recent Apps
|
Recent Apps
|
||||||
</h2>
|
</h2>
|
||||||
{recentApps.length === 0 && (
|
{apps.length === 0 && (
|
||||||
<div className="min-h-[150px] p-6 rounded-xl bg-gray-50">
|
<div className="min-h-[150px] p-6 rounded-xl bg-gray-50">
|
||||||
<p className="mb-4">Apps you use will be listed here, in the order you used them.</p>
|
<p className="mb-4">Apps you use will be listed here, in the order you used them.</p>
|
||||||
<p className="mb-6">You can click/tap/keyboard on a listed app to open it.</p>
|
<p className="mb-6">You can click/tap/keyboard on a listed app to open it.</p>
|
||||||
{groups && (
|
{groups && <AppLink app={groups} size="small" onClick={() => addRecentApp('groups')} />}
|
||||||
<AppLink
|
|
||||||
app={groups}
|
|
||||||
size="small"
|
|
||||||
onClick={() => addRecentApp({ ...groups, desk: 'groups' })}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{recentApps.length > 0 && (
|
{apps.length > 0 && (
|
||||||
<AppList
|
<AppList apps={apps} labelledBy="recent-apps" matchAgainst={selectedMatch} size="small" />
|
||||||
apps={recentApps}
|
|
||||||
labelledBy="recent-apps"
|
|
||||||
matchAgainst={selectedMatch}
|
|
||||||
size="small"
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
<hr className="-mx-4 my-6 md:-mx-8 md:my-9 border-t-2 border-gray-50" />
|
<hr className="-mx-4 my-6 md:-mx-8 md:my-9 border-t-2 border-gray-50" />
|
||||||
<h2 id="recent-devs" className="mb-4 h4 text-gray-500">
|
<h2 id="recent-devs" className="mb-4 h4 text-gray-500">
|
||||||
@ -125,15 +129,15 @@ export const Home = () => {
|
|||||||
{recentDevs.length === 0 && (
|
{recentDevs.length === 0 && (
|
||||||
<div className="min-h-[150px] p-6 rounded-xl bg-gray-50">
|
<div className="min-h-[150px] p-6 rounded-xl bg-gray-50">
|
||||||
<p className="mb-4">Urbit app developers you search for will be listed here.</p>
|
<p className="mb-4">Urbit app developers you search for will be listed here.</p>
|
||||||
{zod && (
|
{defaultAlly && (
|
||||||
<>
|
<>
|
||||||
<p className="mb-6">
|
<p className="mb-6">
|
||||||
Try out app discovery by visiting <ShipName name="~zod" /> below.
|
Try out app discovery by visiting <ShipName name={defaultAlly.shipName} /> below.
|
||||||
</p>
|
</p>
|
||||||
<ProviderLink
|
<ProviderLink
|
||||||
provider={zod}
|
provider={defaultAlly}
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() => addRecentDev(zod.shipName)}
|
onClick={() => addRecentDev(defaultAlly.shipName)}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -9,6 +9,7 @@ import {
|
|||||||
scryAllies,
|
scryAllies,
|
||||||
scryAllyTreaties,
|
scryAllyTreaties,
|
||||||
scryCharges,
|
scryCharges,
|
||||||
|
scryDefaultAlly,
|
||||||
Treaty,
|
Treaty,
|
||||||
Docket,
|
Docket,
|
||||||
Treaties,
|
Treaties,
|
||||||
@ -22,7 +23,7 @@ import {
|
|||||||
} from '@urbit/api';
|
} from '@urbit/api';
|
||||||
import api from './api';
|
import api from './api';
|
||||||
import { mockAllies, mockCharges, mockTreaties } from './mock-data';
|
import { mockAllies, mockCharges, mockTreaties } from './mock-data';
|
||||||
import { fakeRequest, useMockData } from './util';
|
import { fakeRequest, normalizeUrbitColor, useMockData } from './util';
|
||||||
|
|
||||||
export interface ChargeWithDesk extends Charge {
|
export interface ChargeWithDesk extends Charge {
|
||||||
desk: string;
|
desk: string;
|
||||||
@ -40,7 +41,9 @@ interface DocketState {
|
|||||||
charges: ChargesWithDesks;
|
charges: ChargesWithDesks;
|
||||||
treaties: Treaties;
|
treaties: Treaties;
|
||||||
allies: Allies;
|
allies: Allies;
|
||||||
|
defaultAlly: string | null;
|
||||||
fetchCharges: () => Promise<void>;
|
fetchCharges: () => Promise<void>;
|
||||||
|
fetchDefaultAlly: () => Promise<void>;
|
||||||
requestTreaty: (ship: string, desk: string) => Promise<Treaty>;
|
requestTreaty: (ship: string, desk: string) => Promise<Treaty>;
|
||||||
fetchAllies: () => Promise<Allies>;
|
fetchAllies: () => Promise<Allies>;
|
||||||
fetchAllyTreaties: (ally: string) => Promise<Treaties>;
|
fetchAllyTreaties: (ally: string) => Promise<Treaties>;
|
||||||
@ -50,6 +53,11 @@ interface DocketState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const useDocketState = create<DocketState>((set, get) => ({
|
const useDocketState = create<DocketState>((set, get) => ({
|
||||||
|
defaultAlly: useMockData ? '~zod' : null,
|
||||||
|
fetchDefaultAlly: async () => {
|
||||||
|
const defaultAlly = await api.scry<string>(scryDefaultAlly);
|
||||||
|
set({ defaultAlly });
|
||||||
|
},
|
||||||
fetchCharges: async () => {
|
fetchCharges: async () => {
|
||||||
const charg = useMockData
|
const charg = useMockData
|
||||||
? await fakeRequest(mockCharges)
|
? await fakeRequest(mockCharges)
|
||||||
@ -100,8 +108,8 @@ const useDocketState = create<DocketState>((set, get) => ({
|
|||||||
if (!treaty) {
|
if (!treaty) {
|
||||||
throw new Error('Bad install');
|
throw new Error('Bad install');
|
||||||
}
|
}
|
||||||
|
set((state) => addCharge(state, desk, { ...treaty, chad: { install: null } }));
|
||||||
if (useMockData) {
|
if (useMockData) {
|
||||||
set((state) => addCharge(state, desk, { ...treaty, chad: { install: null } }));
|
|
||||||
await new Promise<void>((res) => setTimeout(() => res(), 10000));
|
await new Promise<void>((res) => setTimeout(() => res(), 10000));
|
||||||
set((state) => addCharge(state, desk, { ...treaty, chad: { glob: null } }));
|
set((state) => addCharge(state, desk, { ...treaty, chad: { glob: null } }));
|
||||||
}
|
}
|
||||||
@ -147,14 +155,10 @@ const useDocketState = create<DocketState>((set, get) => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
function normalizeDocket<T extends Docket>(docket: T, desk: string): T {
|
function normalizeDocket<T extends Docket>(docket: T, desk: string): T {
|
||||||
const color = docket?.color?.startsWith('#')
|
|
||||||
? docket.color
|
|
||||||
: `#${docket.color.slice(2).replace('.', '')}`.toUpperCase();
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...docket,
|
...docket,
|
||||||
desk,
|
desk,
|
||||||
color
|
color: normalizeUrbitColor(docket.color)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,9 +84,19 @@ export const useHarkStore = createState<HarkState>(
|
|||||||
await api.poke(archiveAll);
|
await api.poke(archiveAll);
|
||||||
},
|
},
|
||||||
archiveNote: async (bin, lid) => {
|
archiveNote: async (bin, lid) => {
|
||||||
|
if (useMockData) {
|
||||||
|
get().set((draft) => {
|
||||||
|
const seen = 'seen' in lid ? 'seen' : 'unseen';
|
||||||
|
const binId = harkBinToId(bin);
|
||||||
|
delete draft[seen][binId];
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
await api.poke(archive(bin, lid));
|
await api.poke(archive(bin, lid));
|
||||||
},
|
},
|
||||||
opened: async () => {
|
opened: async () => {
|
||||||
|
reduceHark({ opened: null });
|
||||||
|
|
||||||
await api.poke(opened);
|
await api.poke(opened);
|
||||||
},
|
},
|
||||||
getMore: async () => {
|
getMore: async () => {
|
||||||
|
@ -51,31 +51,22 @@ const useKilnState = create<KilnState>((set, get) => ({
|
|||||||
await api.poke(kilnInstall(ship, '%kids', 'base'));
|
await api.poke(kilnInstall(ship, '%kids', 'base'));
|
||||||
},
|
},
|
||||||
toggleOTAs: async (desk: string, on: boolean) => {
|
toggleOTAs: async (desk: string, on: boolean) => {
|
||||||
if (useMockData) {
|
set(
|
||||||
await fakeRequest('');
|
produce((draft: KilnState) => {
|
||||||
set(
|
const { arak } = draft.vats[desk];
|
||||||
produce((draft: KilnState) => {
|
if (!arak.rail) {
|
||||||
const { arak } = draft.vats[desk];
|
return;
|
||||||
if (!arak.rail) {
|
}
|
||||||
return;
|
if (on) {
|
||||||
}
|
arak.rail.paused = false;
|
||||||
|
} else {
|
||||||
|
arak.rail.paused = true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
if (on) {
|
await (useMockData ? fakeRequest('') : api.poke(on ? kilnResume(desk) : kilnPause(desk)));
|
||||||
arak.rail.paused = false;
|
await get().fetchVats(); // refresh vat state
|
||||||
} else {
|
|
||||||
arak.rail.paused = true;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await api.poke(on ? kilnResume(desk) : kilnPause(desk));
|
|
||||||
|
|
||||||
if (!on) {
|
|
||||||
get().fetchVats(); // refresh vat state
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
set: produce(set)
|
set: produce(set)
|
||||||
}));
|
}));
|
||||||
|
@ -329,7 +329,6 @@ export const mockVat = (desk: string, blockers?: boolean): Vat => ({
|
|||||||
ud: 1
|
ud: 1
|
||||||
},
|
},
|
||||||
desk,
|
desk,
|
||||||
paused: false,
|
|
||||||
arak: {
|
arak: {
|
||||||
rein: {
|
rein: {
|
||||||
sub: [],
|
sub: [],
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { DocketHref } from '@urbit/api/docket';
|
import { DocketHref } from '@urbit/api/docket';
|
||||||
import { hsla, parseToHsla } from 'color2k';
|
import { hsla, parseToHsla } from 'color2k';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
export const useMockData = import.meta.env.MODE === 'mock';
|
export const useMockData = import.meta.env.MODE === 'mock';
|
||||||
|
|
||||||
@ -35,6 +36,16 @@ export function deSig(ship: string): string {
|
|||||||
return ship.replace('~', '');
|
return ship.replace('~', '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function normalizeUrbitColor(color: string): string {
|
||||||
|
if (color.startsWith('#')) {
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
const colorString = color.slice(2).replace('.', '').toUpperCase();
|
||||||
|
const lengthAdjustedColor = _.padEnd(colorString, 6, _.last(colorString));
|
||||||
|
return `#${lengthAdjustedColor}`;
|
||||||
|
}
|
||||||
|
|
||||||
export function getDarkColor(color: string): string {
|
export function getDarkColor(color: string): string {
|
||||||
const hslaColor = parseToHsla(color);
|
const hslaColor = parseToHsla(color);
|
||||||
return hsla(hslaColor[0], hslaColor[1], 1 - hslaColor[2], 1);
|
return hsla(hslaColor[0], hslaColor[1], 1 - hslaColor[2], 1);
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
.note-grid-content {
|
.note-grid-content {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1.5rem 1fr;
|
grid-template-columns: 1.5rem 1fr 6rem;
|
||||||
grid-template-rows: 1.5rem 1.5rem;
|
grid-template-rows: 1.5rem 1.5rem;
|
||||||
grid-gap: 0.5rem;
|
grid-gap: 0.5rem;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
'icon title'
|
'icon title actions '
|
||||||
'arrow head'
|
'arrow head actions'
|
||||||
'. body';
|
'. body actions';
|
||||||
}
|
}
|
||||||
|
|
||||||
.note-grid-no-content {
|
.note-grid-no-content {
|
||||||
@ -15,12 +15,12 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
grid-template-rows: 1.75rem 1.75rem;
|
grid-template-rows: 1.75rem 1.75rem;
|
||||||
grid-template-columns: 3.5rem 1fr;
|
grid-template-columns: 3.5rem 1fr 6rem;
|
||||||
grid-column-gap: 0.75rem;
|
grid-column-gap: 0.75rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
'icon title'
|
'icon title actions'
|
||||||
'icon head';
|
'icon head actions';
|
||||||
}
|
}
|
||||||
.note-grid-title {
|
.note-grid-title {
|
||||||
grid-area: title;
|
grid-area: title;
|
||||||
@ -42,4 +42,6 @@
|
|||||||
grid-area: head;
|
grid-area: head;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.note-grid-actions {
|
||||||
|
grid-area: actions;
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ import React, { useCallback } from 'react';
|
|||||||
import { useHistory, useParams } from 'react-router-dom';
|
import { useHistory, useParams } from 'react-router-dom';
|
||||||
import { Button } from '../components/Button';
|
import { Button } from '../components/Button';
|
||||||
import { Dialog, DialogClose, DialogContent } from '../components/Dialog';
|
import { Dialog, DialogClose, DialogContent } from '../components/Dialog';
|
||||||
|
import { useRecentsStore } from '../nav/search/Home';
|
||||||
import useDocketState, { useCharges } from '../state/docket';
|
import useDocketState, { useCharges } from '../state/docket';
|
||||||
|
|
||||||
export const RemoveApp = () => {
|
export const RemoveApp = () => {
|
||||||
@ -14,6 +15,7 @@ export const RemoveApp = () => {
|
|||||||
// TODO: add optimistic updates
|
// TODO: add optimistic updates
|
||||||
const handleRemoveApp = useCallback(() => {
|
const handleRemoveApp = useCallback(() => {
|
||||||
uninstallDocket(desk);
|
uninstallDocket(desk);
|
||||||
|
useRecentsStore.getState().removeRecentApp(desk);
|
||||||
}, [desk]);
|
}, [desk]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -2,6 +2,7 @@ import React, { useCallback } from 'react';
|
|||||||
import { Redirect, useHistory, useParams } from 'react-router-dom';
|
import { Redirect, useHistory, useParams } from 'react-router-dom';
|
||||||
import { Button } from '../components/Button';
|
import { Button } from '../components/Button';
|
||||||
import { Dialog, DialogClose, DialogContent } from '../components/Dialog';
|
import { Dialog, DialogClose, DialogContent } from '../components/Dialog';
|
||||||
|
import { useRecentsStore } from '../nav/search/Home';
|
||||||
import useDocketState, { useCharges } from '../state/docket';
|
import useDocketState, { useCharges } from '../state/docket';
|
||||||
|
|
||||||
export const SuspendApp = () => {
|
export const SuspendApp = () => {
|
||||||
@ -13,6 +14,7 @@ export const SuspendApp = () => {
|
|||||||
// TODO: add optimistic updates
|
// TODO: add optimistic updates
|
||||||
const handleSuspendApp = useCallback(() => {
|
const handleSuspendApp = useCallback(() => {
|
||||||
useDocketState.getState().toggleDocket(desk);
|
useDocketState.getState().toggleDocket(desk);
|
||||||
|
useRecentsStore.getState().removeRecentApp(desk);
|
||||||
}, [desk]);
|
}, [desk]);
|
||||||
|
|
||||||
if ('suspend' in charge.chad) {
|
if ('suspend' in charge.chad) {
|
||||||
|
@ -33,8 +33,8 @@ export const Tile: FunctionComponent<TileProps> = ({ charge, desk }) => {
|
|||||||
!active && 'cursor-default'
|
!active && 'cursor-default'
|
||||||
)}
|
)}
|
||||||
style={{ backgroundColor }}
|
style={{ backgroundColor }}
|
||||||
onClick={() => addRecentApp(charge)}
|
onClick={() => addRecentApp(desk)}
|
||||||
onAuxClick={() => addRecentApp(charge)}
|
onAuxClick={() => addRecentApp(desk)}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
|
@ -13,20 +13,27 @@ function getMenuColor(color: string, darkBg: boolean): string {
|
|||||||
return bgAdjustedColor(satAdjustedColor, darkBg);
|
return bgAdjustedColor(satAdjustedColor, darkBg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// makes tiles look broken because they blend into BG
|
||||||
|
function disallowWhiteTiles(color: string): string {
|
||||||
|
const hslaColor = parseToHsla(color);
|
||||||
|
return hslaColor[2] >= 0.95 ? darken(color, hslaColor[2] - 0.95) : color;
|
||||||
|
}
|
||||||
|
|
||||||
export const useTileColor = (color: string) => {
|
export const useTileColor = (color: string) => {
|
||||||
const theme = useCurrentTheme();
|
const theme = useCurrentTheme();
|
||||||
const darkTheme = theme === 'dark';
|
const darkTheme = theme === 'dark';
|
||||||
const tileColor = darkTheme ? getDarkColor(color) : color;
|
const allowedColor = disallowWhiteTiles(color);
|
||||||
|
const tileColor = darkTheme ? getDarkColor(allowedColor) : allowedColor;
|
||||||
const darkBg = !readableColorIsBlack(tileColor);
|
const darkBg = !readableColorIsBlack(tileColor);
|
||||||
const lightText = darkBg !== darkTheme; // if not same, light text
|
const lightText = darkBg !== darkTheme; // if not same, light text
|
||||||
const suspendColor = darkTheme ? 'rgb(26,26,26)' : 'rgb(220,220,220)';
|
const suspendColor = darkTheme ? 'rgb(26,26,26)' : 'rgb(220,220,220)';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
theme,
|
theme,
|
||||||
tileColor: theme === 'dark' ? getDarkColor(color) : color,
|
tileColor,
|
||||||
menuColor: getMenuColor(tileColor, darkBg),
|
menuColor: getMenuColor(tileColor, darkBg),
|
||||||
suspendColor,
|
suspendColor,
|
||||||
suspendMenuColor: bgAdjustedColor(suspendColor, darkBg),
|
suspendMenuColor: bgAdjustedColor(suspendColor, darkTheme),
|
||||||
lightText
|
lightText
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -200,7 +200,8 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
variants: {
|
variants: {
|
||||||
extend: {
|
extend: {
|
||||||
opacity: ['hover-none']
|
opacity: ['hover-none'],
|
||||||
|
display: ['group-hover']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
@ -26,6 +26,9 @@ if(urbitrc.URL) {
|
|||||||
devServer = {
|
devServer = {
|
||||||
...devServer,
|
...devServer,
|
||||||
index: 'index.html',
|
index: 'index.html',
|
||||||
|
// headers: {
|
||||||
|
// 'Service-Worker-Allowed': '/'
|
||||||
|
// },
|
||||||
proxy: [
|
proxy: [
|
||||||
{
|
{
|
||||||
context: (path) => {
|
context: (path) => {
|
||||||
|
@ -13,6 +13,7 @@ import _ from 'lodash';
|
|||||||
import f from 'lodash/fp';
|
import f from 'lodash/fp';
|
||||||
import { pluralize } from './util';
|
import { pluralize } from './util';
|
||||||
import useMetadataState from '../state/metadata';
|
import useMetadataState from '../state/metadata';
|
||||||
|
import { emptyHarkStats } from '../state/hark';
|
||||||
|
|
||||||
export function getLastSeen(
|
export function getLastSeen(
|
||||||
unreads: Unreads,
|
unreads: Unreads,
|
||||||
@ -28,13 +29,16 @@ export function getLastSeen(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getHarkStats(unreads: Unreads, path: string) {
|
||||||
|
return unreads?.[path] ?? emptyHarkStats();
|
||||||
|
}
|
||||||
|
|
||||||
export function getUnreadCount(
|
export function getUnreadCount(
|
||||||
unreads: Unreads,
|
unreads: Unreads,
|
||||||
path: string,
|
path: string
|
||||||
index: string
|
|
||||||
): number {
|
): number {
|
||||||
const graphUnreads = unreads.graph?.[path]?.[index]?.unreads ?? 0;
|
const { count, each } = getHarkStats(unreads, path);
|
||||||
return typeof graphUnreads === 'number' ? graphUnreads : graphUnreads.size;
|
return count + each.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNotificationCount(unreads: Unreads, path: string): number {
|
export function getNotificationCount(unreads: Unreads, path: string): number {
|
||||||
|
@ -88,7 +88,6 @@ const otherIndex = function(config) {
|
|||||||
const other = [];
|
const other = [];
|
||||||
const idx = {
|
const idx = {
|
||||||
mychannel: result('My Channels', '/~landscape/home', 'home', null),
|
mychannel: result('My Channels', '/~landscape/home', 'home', null),
|
||||||
updates: result('Notifications', '/~notifications', 'inbox', null),
|
|
||||||
profile: result('Profile', `/~profile/~${window.ship}`, 'profile', null),
|
profile: result('Profile', `/~profile/~${window.ship}`, 'profile', null),
|
||||||
messages: result('Messages', '/~landscape/messages', 'messages', null),
|
messages: result('Messages', '/~landscape/messages', 'messages', null),
|
||||||
logout: result('Log Out', '/~/logout', 'logout', null)
|
logout: result('Log Out', '/~/logout', 'logout', null)
|
||||||
|
@ -121,7 +121,7 @@ const useHarkState = createState<HarkState>(
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
const emptyStats = () => ({
|
export const emptyHarkStats = () => ({
|
||||||
last: 0,
|
last: 0,
|
||||||
count: 0,
|
count: 0,
|
||||||
each: []
|
each: []
|
||||||
@ -132,7 +132,7 @@ export function useHarkDm(ship: string) {
|
|||||||
useCallback(
|
useCallback(
|
||||||
(s) => {
|
(s) => {
|
||||||
const key = `/graph/~${window.ship}/dm-inbox/${patp2dec(ship)}`;
|
const key = `/graph/~${window.ship}/dm-inbox/${patp2dec(ship)}`;
|
||||||
return s.unreads[key] || emptyStats();
|
return s.unreads[key] || emptyHarkStats();
|
||||||
},
|
},
|
||||||
[ship]
|
[ship]
|
||||||
)
|
)
|
||||||
@ -141,15 +141,19 @@ export function useHarkDm(ship: string) {
|
|||||||
|
|
||||||
export function useHarkStat(path: string) {
|
export function useHarkStat(path: string) {
|
||||||
return useHarkState(
|
return useHarkState(
|
||||||
useCallback(s => s.unreads[path] || emptyStats(), [path])
|
useCallback(s => s.unreads[path] || emptyHarkStats(), [path])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function selHarkGraph(graph: string) {
|
||||||
|
const [,, ship, name] = graph.split('/');
|
||||||
|
const path = `/graph/${ship}/${name}`;
|
||||||
|
return (s: HarkState) => (s.unreads[path] || emptyHarkStats());
|
||||||
|
}
|
||||||
|
|
||||||
export function useHarkGraph(graph: string) {
|
export function useHarkGraph(graph: string) {
|
||||||
const [, ship, name] = useMemo(() => graph.split('/'), [graph]);
|
const sel = useMemo(() => selHarkGraph(graph), [graph]);
|
||||||
return useHarkState(
|
return useHarkState(sel);
|
||||||
useCallback(s => s.unreads[`/graph/${ship}/${name}`], [ship, name])
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useHarkGraphIndex(graph: string, index: string) {
|
export function useHarkGraphIndex(graph: string, index: string) {
|
||||||
|
@ -4,7 +4,6 @@ import {
|
|||||||
createSubscription,
|
createSubscription,
|
||||||
reduceStateN
|
reduceStateN
|
||||||
} from './base';
|
} from './base';
|
||||||
import airlock from '~/logic/api';
|
|
||||||
import { reduce } from '../reducers/launch-update';
|
import { reduce } from '../reducers/launch-update';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
@ -16,10 +15,6 @@ export interface LaunchState {
|
|||||||
};
|
};
|
||||||
weather: WeatherState | null | Record<string, never> | boolean;
|
weather: WeatherState | null | Record<string, never> | boolean;
|
||||||
userLocation: string | null;
|
userLocation: string | null;
|
||||||
baseHash: string | null;
|
|
||||||
runtimeLag: boolean;
|
|
||||||
getRuntimeLag: () => Promise<void>;
|
|
||||||
getBaseHash: () => Promise<void>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-ignore investigate zustand types
|
// @ts-ignore investigate zustand types
|
||||||
@ -30,23 +25,7 @@ const useLaunchState = createState<LaunchState>(
|
|||||||
tileOrdering: [],
|
tileOrdering: [],
|
||||||
tiles: {},
|
tiles: {},
|
||||||
weather: null,
|
weather: null,
|
||||||
userLocation: null,
|
userLocation: null
|
||||||
baseHash: null,
|
|
||||||
runtimeLag: false,
|
|
||||||
getBaseHash: async () => {
|
|
||||||
const baseHash = await airlock.scry({
|
|
||||||
app: 'file-server',
|
|
||||||
path: '/clay/base/hash'
|
|
||||||
});
|
|
||||||
set({ baseHash });
|
|
||||||
},
|
|
||||||
getRuntimeLag: async () => {
|
|
||||||
const runtimeLag = await airlock.scry({
|
|
||||||
app: 'launch',
|
|
||||||
path: '/runtime-lag'
|
|
||||||
});
|
|
||||||
set({ runtimeLag });
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
['weather'],
|
['weather'],
|
||||||
[
|
[
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
if ('serviceWorker' in navigator && process.env.NODE_ENV !== 'development') {
|
if ('serviceWorker' in navigator && process.env.NODE_ENV !== 'development') {
|
||||||
window.addEventListener('load', () => {
|
window.addEventListener('load', () => {
|
||||||
navigator.serviceWorker.register('/~landscape/js/bundle/serviceworker.js', {
|
navigator.serviceWorker.register('/apps/landscape/serviceworker.js', {
|
||||||
scope: '/'
|
scope: '/apps/landscape'
|
||||||
}).then((reg) => {
|
}).then((reg) => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -28,7 +28,6 @@ import './css/indigo-static.css';
|
|||||||
import { Content } from './landscape/components/Content';
|
import { Content } from './landscape/components/Content';
|
||||||
import './landscape/css/custom.css';
|
import './landscape/css/custom.css';
|
||||||
import { bootstrapApi } from '~/logic/api/bootstrap';
|
import { bootstrapApi } from '~/logic/api/bootstrap';
|
||||||
import useLaunchState from '../logic/state/launch';
|
|
||||||
|
|
||||||
const Root = withState(styled.div`
|
const Root = withState(styled.div`
|
||||||
font-family: ${p => p.theme.fonts.sans};
|
font-family: ${p => p.theme.fonts.sans};
|
||||||
@ -104,8 +103,6 @@ class App extends React.Component {
|
|||||||
this.updateMedium(this.mediumWatcher);
|
this.updateMedium(this.mediumWatcher);
|
||||||
this.updateLarge(this.largeWatcher);
|
this.updateLarge(this.largeWatcher);
|
||||||
}, 500);
|
}, 500);
|
||||||
this.props.getBaseHash();
|
|
||||||
this.props.getRuntimeLag(); // TODO consider polling periodically
|
|
||||||
this.props.getAll();
|
this.props.getAll();
|
||||||
gcpManager.start();
|
gcpManager.start();
|
||||||
Mousetrap.bindGlobal(['command+/', 'ctrl+/'], (e) => {
|
Mousetrap.bindGlobal(['command+/', 'ctrl+/'], (e) => {
|
||||||
@ -211,14 +208,12 @@ const selContacts = s => s.contacts[`~${window.ship}`];
|
|||||||
const selLocal = s => [s.set, s.omniboxShown, s.toggleOmnibox, s.dark];
|
const selLocal = s => [s.set, s.omniboxShown, s.toggleOmnibox, s.dark];
|
||||||
const selSettings = s => [s.display, s.getAll];
|
const selSettings = s => [s.display, s.getAll];
|
||||||
const selGraph = s => s.getShallowChildren;
|
const selGraph = s => s.getShallowChildren;
|
||||||
const selLaunch = s => [s.getRuntimeLag, s.getBaseHash];
|
|
||||||
|
|
||||||
const WithApp = React.forwardRef((props, ref) => {
|
const WithApp = React.forwardRef((props, ref) => {
|
||||||
const ourContact = useContactState(selContacts);
|
const ourContact = useContactState(selContacts);
|
||||||
const [display, getAll] = useSettingsState(selSettings, shallow);
|
const [display, getAll] = useSettingsState(selSettings, shallow);
|
||||||
const [setLocal, omniboxShown, toggleOmnibox, dark] = useLocalState(selLocal);
|
const [setLocal, omniboxShown, toggleOmnibox, dark] = useLocalState(selLocal);
|
||||||
const getShallowChildren = useGraphState(selGraph);
|
const getShallowChildren = useGraphState(selGraph);
|
||||||
const [getRuntimeLag, getBaseHash] = useLaunchState(selLaunch, shallow);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WarmApp
|
<WarmApp
|
||||||
@ -229,8 +224,6 @@ const WithApp = React.forwardRef((props, ref) => {
|
|||||||
set={setLocal}
|
set={setLocal}
|
||||||
dark={dark}
|
dark={dark}
|
||||||
getShallowChildren={getShallowChildren}
|
getShallowChildren={getShallowChildren}
|
||||||
getRuntimeLag={getRuntimeLag}
|
|
||||||
getBaseHash={getBaseHash}
|
|
||||||
toggleOmnibox={toggleOmnibox}
|
toggleOmnibox={toggleOmnibox}
|
||||||
omniboxShown={omniboxShown}
|
omniboxShown={omniboxShown}
|
||||||
/>
|
/>
|
||||||
|
@ -3,23 +3,20 @@ import { Box, Button, Col, Icon, Row, Text } from '@tlon/indigo-react';
|
|||||||
import f from 'lodash/fp';
|
import f from 'lodash/fp';
|
||||||
import React, { ReactElement, useEffect, useMemo, useState } from 'react';
|
import React, { ReactElement, useEffect, useMemo, useState } from 'react';
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
import { Route, useHistory } from 'react-router-dom';
|
import { Route } from 'react-router-dom';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import {
|
import {
|
||||||
hasTutorialGroup,
|
hasTutorialGroup,
|
||||||
|
TUTORIAL_BOOK,
|
||||||
TUTORIAL_BOOK,
|
TUTORIAL_CHAT,
|
||||||
TUTORIAL_CHAT, TUTORIAL_GROUP,
|
TUTORIAL_GROUP,
|
||||||
TUTORIAL_HOST,
|
TUTORIAL_HOST,
|
||||||
|
TUTORIAL_LINKS
|
||||||
TUTORIAL_LINKS
|
|
||||||
} from '~/logic/lib/tutorialModal';
|
} from '~/logic/lib/tutorialModal';
|
||||||
import { useModal } from '~/logic/lib/useModal';
|
import { useModal } from '~/logic/lib/useModal';
|
||||||
import { useQuery } from '~/logic/lib/useQuery';
|
import { useQuery } from '~/logic/lib/useQuery';
|
||||||
import { useWaitForProps } from '~/logic/lib/useWaitForProps';
|
import { useWaitForProps } from '~/logic/lib/useWaitForProps';
|
||||||
import { writeText } from '~/logic/lib/util';
|
|
||||||
import useHarkState from '~/logic/state/hark';
|
import useHarkState from '~/logic/state/hark';
|
||||||
import useLaunchState from '~/logic/state/launch';
|
|
||||||
import useLocalState from '~/logic/state/local';
|
import useLocalState from '~/logic/state/local';
|
||||||
import useMetadataState from '~/logic/state/metadata';
|
import useMetadataState from '~/logic/state/metadata';
|
||||||
import useSettingsState, { selectCalmState } from '~/logic/state/settings';
|
import useSettingsState, { selectCalmState } from '~/logic/state/settings';
|
||||||
@ -53,8 +50,6 @@ interface LaunchAppProps {
|
|||||||
|
|
||||||
export const LaunchApp = (props: LaunchAppProps): ReactElement | null => {
|
export const LaunchApp = (props: LaunchAppProps): ReactElement | null => {
|
||||||
const { connection } = props;
|
const { connection } = props;
|
||||||
const { baseHash, runtimeLag } = useLaunchState(state => state);
|
|
||||||
const [hashText, setHashText] = useState(baseHash);
|
|
||||||
const [exitingTut, setExitingTut] = useState(false);
|
const [exitingTut, setExitingTut] = useState(false);
|
||||||
const seen = useSettingsState(s => s?.tutorial?.seen) ?? true;
|
const seen = useSettingsState(s => s?.tutorial?.seen) ?? true;
|
||||||
const associations = useMetadataState(s => s.associations);
|
const associations = useMetadataState(s => s.associations);
|
||||||
@ -67,35 +62,6 @@ export const LaunchApp = (props: LaunchAppProps): ReactElement | null => {
|
|||||||
!hideGroups ? { hideGroups } = calmState : null;
|
!hideGroups ? { hideGroups } = calmState : null;
|
||||||
|
|
||||||
const waiter = useWaitForProps({ ...props, associations });
|
const waiter = useWaitForProps({ ...props, associations });
|
||||||
const hashBox = (
|
|
||||||
<Box
|
|
||||||
position="sticky"
|
|
||||||
left={3}
|
|
||||||
bottom={3}
|
|
||||||
mt={3}
|
|
||||||
backgroundColor="white"
|
|
||||||
borderRadius={2}
|
|
||||||
width="fit-content"
|
|
||||||
fontSize={0}
|
|
||||||
cursor="pointer"
|
|
||||||
onClick={() => {
|
|
||||||
writeText(baseHash);
|
|
||||||
setHashText('copied');
|
|
||||||
setTimeout(() => {
|
|
||||||
setHashText(baseHash);
|
|
||||||
}, 2000);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
height="100%"
|
|
||||||
backgroundColor={runtimeLag ? 'yellow' : 'washedGray'}
|
|
||||||
p={2}
|
|
||||||
width="fit-content"
|
|
||||||
>
|
|
||||||
<Text mono bold>{hashText || baseHash}</Text>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
|
|
||||||
const { query } = useQuery();
|
const { query } = useQuery();
|
||||||
|
|
||||||
@ -247,7 +213,6 @@ export const LaunchApp = (props: LaunchAppProps): ReactElement | null => {
|
|||||||
(<Groups />)
|
(<Groups />)
|
||||||
}
|
}
|
||||||
</Box>
|
</Box>
|
||||||
{hashBox}
|
|
||||||
</ScrollbarLessBox>
|
</ScrollbarLessBox>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -3,18 +3,16 @@ import { Association, Associations, Unreads } from '@urbit/api';
|
|||||||
import f from 'lodash/fp';
|
import f from 'lodash/fp';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import React, { useRef } from 'react';
|
import React, { useRef } from 'react';
|
||||||
import { getNotificationCount, getUnreadCount } from '~/logic/lib/hark';
|
import { getNotificationCount } from '~/logic/lib/hark';
|
||||||
import { TUTORIAL_GROUP, TUTORIAL_GROUP_RESOURCE, TUTORIAL_HOST } from '~/logic/lib/tutorialModal';
|
import { TUTORIAL_GROUP, TUTORIAL_GROUP_RESOURCE, TUTORIAL_HOST } from '~/logic/lib/tutorialModal';
|
||||||
import { alphabeticalOrder } from '~/logic/lib/util';
|
import { alphabeticalOrder } from '~/logic/lib/util';
|
||||||
import useGroupState from '~/logic/state/group';
|
import useGroupState from '~/logic/state/group';
|
||||||
import useHarkState from '~/logic/state/hark';
|
import useHarkState, { selHarkGraph } from '~/logic/state/hark';
|
||||||
import useMetadataState from '~/logic/state/metadata';
|
import useMetadataState from '~/logic/state/metadata';
|
||||||
import useSettingsState, { selectCalmState, SettingsState } from '~/logic/state/settings';
|
import useSettingsState, { selectCalmState, SettingsState } from '~/logic/state/settings';
|
||||||
import { useTutorialModal } from '~/views/components/useTutorialModal';
|
import { useTutorialModal } from '~/views/components/useTutorialModal';
|
||||||
import Tile from '../components/tiles/tile';
|
import Tile from '../components/tiles/tile';
|
||||||
|
|
||||||
interface GroupsProps {}
|
|
||||||
|
|
||||||
const sortGroupsAlph = (a: Association, b: Association) =>
|
const sortGroupsAlph = (a: Association, b: Association) =>
|
||||||
a.group === TUTORIAL_GROUP_RESOURCE
|
a.group === TUTORIAL_GROUP_RESOURCE
|
||||||
? -1
|
? -1
|
||||||
@ -22,13 +20,22 @@ const sortGroupsAlph = (a: Association, b: Association) =>
|
|||||||
? 1
|
? 1
|
||||||
: alphabeticalOrder(a.metadata.title, b.metadata.title);
|
: alphabeticalOrder(a.metadata.title, b.metadata.title);
|
||||||
|
|
||||||
const getGraphUnreads = (associations: Associations, unreads: Unreads) => (path: string) =>
|
const getGraphUnreads = (associations: Associations) => {
|
||||||
f.flow(
|
const state = useHarkState.getState();
|
||||||
f.pickBy((a: Association) => a.group === path),
|
const selUnread = (graph: string) => {
|
||||||
f.map('resource'),
|
const { count, each } = selHarkGraph(graph)(state);
|
||||||
f.map(rid => getUnreadCount(unreads, rid, '/')),
|
const result = count + each.length;
|
||||||
f.reduce(f.add, 0)
|
console.log(graph, result);
|
||||||
)(associations.graph);
|
return result;
|
||||||
|
};
|
||||||
|
return (path: string) =>
|
||||||
|
f.flow(
|
||||||
|
f.pickBy((a: Association) => a.group === path),
|
||||||
|
f.map('resource'),
|
||||||
|
f.map(selUnread),
|
||||||
|
f.reduce(f.add, 0)
|
||||||
|
)(associations.graph);
|
||||||
|
};
|
||||||
|
|
||||||
const getGraphNotifications = (associations: Associations, unreads: Unreads) => (path: string) =>
|
const getGraphNotifications = (associations: Associations, unreads: Unreads) => (path: string) =>
|
||||||
f.flow(
|
f.flow(
|
||||||
@ -38,8 +45,7 @@ const getGraphNotifications = (associations: Associations, unreads: Unreads) =>
|
|||||||
f.reduce(f.add, 0)
|
f.reduce(f.add, 0)
|
||||||
)(associations.graph);
|
)(associations.graph);
|
||||||
|
|
||||||
export default function Groups(props: GroupsProps & Parameters<typeof Box>[0]) {
|
export default function Groups(props: Parameters<typeof Box>[0]) {
|
||||||
const { inbox, ...boxProps } = props;
|
|
||||||
const unreads = useHarkState(state => state.unreads);
|
const unreads = useHarkState(state => state.unreads);
|
||||||
const groupState = useGroupState(state => state.groups);
|
const groupState = useGroupState(state => state.groups);
|
||||||
const associations = useMetadataState(state => state.associations);
|
const associations = useMetadataState(state => state.associations);
|
||||||
@ -47,7 +53,7 @@ export default function Groups(props: GroupsProps & Parameters<typeof Box>[0]) {
|
|||||||
const groups = Object.values(associations?.groups || {})
|
const groups = Object.values(associations?.groups || {})
|
||||||
.filter(e => e?.group in groupState)
|
.filter(e => e?.group in groupState)
|
||||||
.sort(sortGroupsAlph);
|
.sort(sortGroupsAlph);
|
||||||
const graphUnreads = getGraphUnreads(associations || {} as Associations, unreads);
|
const graphUnreads = getGraphUnreads(associations || {} as Associations);
|
||||||
const graphNotifications = getGraphNotifications(associations || {} as Associations, unreads);
|
const graphNotifications = getGraphNotifications(associations || {} as Associations, unreads);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
import React, { ReactElement } from 'react';
|
import React, { ReactElement } from 'react';
|
||||||
import useLaunchState from '~/logic/state/launch';
|
import useLaunchState from '~/logic/state/launch';
|
||||||
import { WeatherState } from '~/types';
|
import { WeatherState } from '~/types';
|
||||||
import BasicTile from './tiles/basic';
|
|
||||||
import ClockTile from './tiles/clock';
|
import ClockTile from './tiles/clock';
|
||||||
import CustomTile from './tiles/custom';
|
|
||||||
import WeatherTile from './tiles/weather';
|
import WeatherTile from './tiles/weather';
|
||||||
|
|
||||||
const Tiles = (): ReactElement => {
|
const Tiles = (): ReactElement => {
|
||||||
@ -16,16 +14,7 @@ const Tiles = (): ReactElement => {
|
|||||||
return tile.isShown;
|
return tile.isShown;
|
||||||
}).map((key) => {
|
}).map((key) => {
|
||||||
const tile = tileState[key];
|
const tile = tileState[key];
|
||||||
if ('basic' in tile.type) {
|
if ('custom' in tile.type) {
|
||||||
const basic = tile.type.basic;
|
|
||||||
return (
|
|
||||||
<BasicTile
|
|
||||||
key={key}
|
|
||||||
title={basic.title}
|
|
||||||
linkedUrl={basic.linkedUrl}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
} else if ('custom' in tile.type) {
|
|
||||||
if (key === 'weather') {
|
if (key === 'weather') {
|
||||||
return (
|
return (
|
||||||
<WeatherTile key={key} />
|
<WeatherTile key={key} />
|
||||||
@ -35,14 +24,6 @@ const Tiles = (): ReactElement => {
|
|||||||
return (
|
return (
|
||||||
<ClockTile key={key} location={location} />
|
<ClockTile key={key} location={location} />
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<CustomTile
|
|
||||||
key={key}
|
|
||||||
tileImage={tile.type.custom.image}
|
|
||||||
linkedUrl={tile.type.custom.linkedUrl}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -9,7 +9,7 @@ import useLocalState from '~/logic/state/local';
|
|||||||
import BigIntOrderedMap from '@urbit/api/lib/BigIntOrderedMap';
|
import BigIntOrderedMap from '@urbit/api/lib/BigIntOrderedMap';
|
||||||
import bigInt from 'big-integer';
|
import bigInt from 'big-integer';
|
||||||
import airlock from '~/logic/api';
|
import airlock from '~/logic/api';
|
||||||
import useHarkState from '~/logic/state/hark';
|
import useHarkState, { selHarkGraph } from '~/logic/state/hark';
|
||||||
import { BlockScroller } from '~/views/components/BlockScroller';
|
import { BlockScroller } from '~/views/components/BlockScroller';
|
||||||
|
|
||||||
export interface LinkBlocksProps {
|
export interface LinkBlocksProps {
|
||||||
@ -46,11 +46,13 @@ export function LinkBlocks(props: LinkBlocksProps) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unreads =
|
const unreads = selHarkGraph(association.resource)(useHarkState.getState());
|
||||||
useHarkState.getState().unreads.graph?.[association.resource]?.['/']
|
const [,,ship,name] = association.resource.split('/');
|
||||||
?.unreads || new Set<string>();
|
unreads.each.forEach((u) => {
|
||||||
Array.from(unreads as Set<string>).forEach((u) => {
|
airlock.poke(markEachAsRead({
|
||||||
airlock.poke(markEachAsRead(association.resource, '/', u));
|
desk: (window as any).desk,
|
||||||
|
path: `/graph/${ship}/${name}`
|
||||||
|
}, u));
|
||||||
});
|
});
|
||||||
}, [association.resource]);
|
}, [association.resource]);
|
||||||
|
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
import { Box, Center, Col, LoadingSpinner, Text, Icon } from '@tlon/indigo-react';
|
import { Col } from '@tlon/indigo-react';
|
||||||
import {
|
import {
|
||||||
IndexedNotification,
|
IndexedNotification,
|
||||||
|
JoinRequests,
|
||||||
JoinRequests, Notifications,
|
Notifications,
|
||||||
|
|
||||||
seen,
|
seen,
|
||||||
|
|
||||||
Timebox,
|
Timebox,
|
||||||
unixToDa
|
unixToDa
|
||||||
} from '@urbit/api';
|
} from '@urbit/api';
|
||||||
@ -13,10 +11,8 @@ import { BigInteger } from 'big-integer';
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import f from 'lodash/fp';
|
import f from 'lodash/fp';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import React, { useEffect, useRef } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { getNotificationKey } from '~/logic/lib/hark';
|
import { getNotificationKey } from '~/logic/lib/hark';
|
||||||
import { useLazyScroll } from '~/logic/lib/useLazyScroll';
|
|
||||||
import useLaunchState from '~/logic/state/launch';
|
|
||||||
import { daToUnix } from '~/logic/lib/util';
|
import { daToUnix } from '~/logic/lib/util';
|
||||||
import useHarkState from '~/logic/state/hark';
|
import useHarkState from '~/logic/state/hark';
|
||||||
import { Invites } from './invites';
|
import { Invites } from './invites';
|
||||||
@ -59,8 +55,6 @@ export default function Inbox(props: {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const runtimeLag = useLaunchState(state => state.runtimeLag);
|
|
||||||
|
|
||||||
const ready = useHarkState(
|
const ready = useHarkState(
|
||||||
s => Object.keys(s.unreads.graph).length > 0
|
s => Object.keys(s.unreads.graph).length > 0
|
||||||
);
|
);
|
||||||
@ -99,14 +93,6 @@ export default function Inbox(props: {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Col p={1} position="relative" height="100%" overflowY="auto" overflowX="hidden">
|
<Col p={1} position="relative" height="100%" overflowY="auto" overflowX="hidden">
|
||||||
{runtimeLag && (
|
|
||||||
<Box bg="yellow" borderRadius={2} p={2} m={2}>
|
|
||||||
<Icon verticalAlign="middle" mr={2} icon="Tutorial" />
|
|
||||||
<Text verticalAlign="middle">
|
|
||||||
Update your binary to continue receiving updates.
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
<Invites pendingJoin={props.pendingJoin} />
|
<Invites pendingJoin={props.pendingJoin} />
|
||||||
</Col>
|
</Col>
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { BaseAnchor, Box, Button, Center, Col, H3, Icon, Image, Row, Text } from '@tlon/indigo-react';
|
import { BaseAnchor, Box, BoxProps, Button, Center, Col, H3, Icon, Image, Row, Text } from '@tlon/indigo-react';
|
||||||
import { Association, GraphNode, resourceFromPath, GraphConfig } from '@urbit/api';
|
import { Association, GraphNode, resourceFromPath, GraphConfig, Treaty } from '@urbit/api';
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { Link, useLocation } from 'react-router-dom';
|
import { Link, useLocation } from 'react-router-dom';
|
||||||
@ -196,8 +196,38 @@ const ClampedText = styled(Text)`
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
type AppTileProps = Treaty & BoxProps;
|
||||||
|
|
||||||
|
export function AppTile({ color, image, ...props }: AppTileProps) {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
position="relative"
|
||||||
|
flex="none"
|
||||||
|
height={['48px', '132px']}
|
||||||
|
width={['48px', '132px']}
|
||||||
|
marginRight={3}
|
||||||
|
borderRadius={3}
|
||||||
|
bg={color || 'gray'}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{image && (
|
||||||
|
<Image
|
||||||
|
src={image}
|
||||||
|
position="absolute"
|
||||||
|
top="0"
|
||||||
|
left="0"
|
||||||
|
width="100%"
|
||||||
|
height="100%"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function AppPermalink({ link, ship, desk }: Omit<IAppPermalink, 'type'>) {
|
function AppPermalink({ link, ship, desk }: Omit<IAppPermalink, 'type'>) {
|
||||||
const treaty = useTreaty(ship, desk);
|
const treaty = useTreaty(ship, desk);
|
||||||
|
const hasProtocolHandling = Boolean(window?.navigator?.registerProtocolHandler);
|
||||||
|
const href = hasProtocolHandling ? link : `/apps/grid/perma?ext=${link}`;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!treaty) {
|
if (!treaty) {
|
||||||
@ -208,36 +238,23 @@ function AppPermalink({ link, ship, desk }: Omit<IAppPermalink, 'type'>) {
|
|||||||
return (
|
return (
|
||||||
<Row
|
<Row
|
||||||
display="inline-flex"
|
display="inline-flex"
|
||||||
width="500px"
|
width="100%"
|
||||||
|
maxWidth="500px"
|
||||||
padding={3}
|
padding={3}
|
||||||
bg="washedGray"
|
bg="washedGray"
|
||||||
borderRadius={3}
|
borderRadius={3}
|
||||||
>
|
>
|
||||||
<Box
|
{treaty && <AppTile display={['none', 'block']} {...treaty} />}
|
||||||
position="relative"
|
|
||||||
flex="none"
|
|
||||||
height="132px"
|
|
||||||
width="132px"
|
|
||||||
marginRight={3}
|
|
||||||
borderRadius={3}
|
|
||||||
bg={treaty?.color || 'gray'}
|
|
||||||
>
|
|
||||||
{treaty?.image && (
|
|
||||||
<Image
|
|
||||||
src={treaty.image}
|
|
||||||
position="absolute"
|
|
||||||
top="0"
|
|
||||||
left="0"
|
|
||||||
width="100%"
|
|
||||||
height="100%"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
<Col>
|
<Col>
|
||||||
<H3 color="black">{treaty?.title}</H3>
|
<Row flexDirection={['row', 'column']} alignItems={['center', 'start']} marginBottom={2}>
|
||||||
{treaty?.ship && <Author ship={treaty?.ship} showImage dontShowTime={true} marginBottom={2} />}
|
{treaty && <AppTile display={['block', 'none']} {...treaty} />}
|
||||||
|
<Col>
|
||||||
|
<H3 color="black">{treaty?.title}</H3>
|
||||||
|
{treaty?.ship && <Author ship={treaty?.ship} showImage dontShowTime={true} />}
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
<ClampedText marginBottom={2} color="gray">{treaty?.info}</ClampedText>
|
<ClampedText marginBottom={2} color="gray">{treaty?.info}</ClampedText>
|
||||||
<Button as="a" href={link} primary alignSelf="start" display="inline-flex" marginTop="auto">Open App</Button>
|
<Button as="a" href={href} primary alignSelf="start" display="inline-flex" marginTop="auto">Open App</Button>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
|
@ -19,7 +19,6 @@ import GroupSearch from '~/views/components/GroupSearch';
|
|||||||
import { ImageInput } from '~/views/components/ImageInput';
|
import { ImageInput } from '~/views/components/ImageInput';
|
||||||
import {
|
import {
|
||||||
ProfileControls, ProfileHeader,
|
ProfileControls, ProfileHeader,
|
||||||
|
|
||||||
ProfileImages, ProfileStatus
|
ProfileImages, ProfileStatus
|
||||||
} from './Profile';
|
} from './Profile';
|
||||||
import airlock from '~/logic/api';
|
import airlock from '~/logic/api';
|
||||||
@ -96,7 +95,7 @@ export function EditProfile(props: any): ReactElement {
|
|||||||
const newValue = key !== 'color' ? values[key] : uxToHex(values[key]);
|
const newValue = key !== 'color' ? values[key] : uxToHex(values[key]);
|
||||||
if (newValue !== contact[key]) {
|
if (newValue !== contact[key]) {
|
||||||
if (key === 'isPublic') {
|
if (key === 'isPublic') {
|
||||||
airlock.poke(setPublic(true));
|
airlock.poke(setPublic(newValue));
|
||||||
return;
|
return;
|
||||||
} else if (key === 'groups') {
|
} else if (key === 'groups') {
|
||||||
const toRemove: string[] = _.difference(
|
const toRemove: string[] = _.difference(
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React, {
|
import React, {
|
||||||
MouseEvent,
|
MouseEvent,
|
||||||
useCallback,
|
useCallback,
|
||||||
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
useState
|
useState
|
||||||
} from 'react';
|
} from 'react';
|
||||||
@ -14,7 +15,8 @@ import {
|
|||||||
allSystemStyle,
|
allSystemStyle,
|
||||||
Icon,
|
Icon,
|
||||||
Row,
|
Row,
|
||||||
Col
|
Col,
|
||||||
|
Text
|
||||||
} from '@tlon/indigo-react';
|
} from '@tlon/indigo-react';
|
||||||
|
|
||||||
import { TruncatedText } from '~/views/components/TruncatedText';
|
import { TruncatedText } from '~/views/components/TruncatedText';
|
||||||
@ -23,11 +25,13 @@ import { IconRef, PropFunc } from '~/types';
|
|||||||
import { system } from 'styled-system';
|
import { system } from 'styled-system';
|
||||||
import { Association, GraphConfig, ReferenceContent } from '@urbit/api';
|
import { Association, GraphConfig, ReferenceContent } from '@urbit/api';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { referenceToPermalink } from '~/logic/lib/permalinks';
|
import { AppPermalink, referenceToPermalink } from '~/logic/lib/permalinks';
|
||||||
import useMetadataState from '~/logic/state/metadata';
|
import useMetadataState from '~/logic/state/metadata';
|
||||||
import { RemoteContentWrapper } from './wrapper';
|
import { RemoteContentWrapper } from './wrapper';
|
||||||
import { useEmbed } from '~/logic/state/embed';
|
import { useEmbed } from '~/logic/state/embed';
|
||||||
import { IS_SAFARI } from '~/logic/lib/platform';
|
import { IS_SAFARI } from '~/logic/lib/platform';
|
||||||
|
import useDocketState, { useTreaty } from '~/logic/state/docket';
|
||||||
|
import { AppTile } from '~/views/apps/permalinks/embed';
|
||||||
|
|
||||||
interface RemoteContentEmbedProps {
|
interface RemoteContentEmbedProps {
|
||||||
url: string;
|
url: string;
|
||||||
@ -217,11 +221,41 @@ export function RemoteContentPermalinkEmbed(props: {
|
|||||||
return <RemoteContentPermalinkEmbedGraph {...permalink} />;
|
return <RemoteContentPermalinkEmbedGraph {...permalink} />;
|
||||||
} else if (permalink.type === 'group') {
|
} else if (permalink.type === 'group') {
|
||||||
return <RemoteContentPermalinkEmbedGroup {...permalink} />;
|
return <RemoteContentPermalinkEmbedGroup {...permalink} />;
|
||||||
|
} else if (permalink.type === 'app') {
|
||||||
|
return <RemoteContentPermalinkEmbedApp {...permalink} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function RemoteContentPermalinkEmbedApp({ link, ship, desk }: Omit<AppPermalink, 'type'>) {
|
||||||
|
const treaty = useTreaty(ship, desk);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!treaty) {
|
||||||
|
useDocketState.getState().requestTreaty(ship, desk);
|
||||||
|
}
|
||||||
|
}, [treaty, ship, desk]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Col
|
||||||
|
width="100%"
|
||||||
|
height="100%"
|
||||||
|
padding={3}
|
||||||
|
borderRadius={3}
|
||||||
|
justifyContent="space-around"
|
||||||
|
alignItems="center"
|
||||||
|
>
|
||||||
|
{treaty && (
|
||||||
|
<>
|
||||||
|
<AppTile color="washedGray" marginRight={0} {...treaty} />
|
||||||
|
<Row><Text fontSize="1" color="gray">App: {treaty.title}</Text></Row>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Col>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function RemoteContentPermalinkEmbedGroup(props: {
|
function RemoteContentPermalinkEmbedGroup(props: {
|
||||||
group: string;
|
group: string;
|
||||||
link: string;
|
link: string;
|
||||||
|
@ -14,6 +14,7 @@ import { PostRepliesRoutes } from './Post/PostReplies';
|
|||||||
import PostTimeline from './Post/PostTimeline';
|
import PostTimeline from './Post/PostTimeline';
|
||||||
import airlock from '~/logic/api';
|
import airlock from '~/logic/api';
|
||||||
import { PostThreadRoutes } from './Post/PostThread';
|
import { PostThreadRoutes } from './Post/PostThread';
|
||||||
|
import { toHarkPlace } from '~/logic/lib/util';
|
||||||
|
|
||||||
function GroupFeed(props) {
|
function GroupFeed(props) {
|
||||||
const {
|
const {
|
||||||
@ -52,7 +53,8 @@ function GroupFeed(props) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
getNewest(graphResource.ship, graphResource.name, 100);
|
getNewest(graphResource.ship, graphResource.name, 100);
|
||||||
airlock.poke(markCountAsRead(graphPath));
|
|
||||||
|
airlock.poke(markCountAsRead(toHarkPlace(graphPath)));
|
||||||
}, [graphPath]);
|
}, [graphPath]);
|
||||||
|
|
||||||
if (!graphPath) {
|
if (!graphPath) {
|
||||||
|
@ -14,6 +14,7 @@ import PostFlatTimeline from './Post/PostFlatTimeline';
|
|||||||
import airlock from '~/logic/api';
|
import airlock from '~/logic/api';
|
||||||
import { markCountAsRead } from '@urbit/api';
|
import { markCountAsRead } from '@urbit/api';
|
||||||
import { PostRepliesRoutes } from './Post/PostReplies';
|
import { PostRepliesRoutes } from './Post/PostReplies';
|
||||||
|
import { toHarkPlace } from '~/logic/lib/util';
|
||||||
|
|
||||||
function GroupFlatFeed(props) {
|
function GroupFlatFeed(props) {
|
||||||
const {
|
const {
|
||||||
@ -43,7 +44,7 @@ function GroupFlatFeed(props) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
getDeepOlderThan(graphRid.ship, graphRid.name, 100);
|
getDeepOlderThan(graphRid.ship, graphRid.name, 100);
|
||||||
airlock.poke(markCountAsRead(graphPath));
|
airlock.poke(markCountAsRead(toHarkPlace(graphPath)));
|
||||||
}, [graphPath]);
|
}, [graphPath]);
|
||||||
|
|
||||||
if (!graphPath) {
|
if (!graphPath) {
|
||||||
|
@ -104,8 +104,7 @@ export function JoinGroup(props: JoinGroupProps): ReactElement {
|
|||||||
history.push(`/~landscape${group}`);
|
history.push(`/~landscape${group}`);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// drop them into inbox to show join request still pending
|
console.error(e);
|
||||||
history.push('/~notifications');
|
|
||||||
}
|
}
|
||||||
}, [waiter, history, associations, groups]);
|
}, [waiter, history, associations, groups]);
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React, { ReactElement, useCallback } from 'react';
|
import React, { ReactElement, useCallback } from 'react';
|
||||||
import { Associations, Graph } from '@urbit/api';
|
import { Associations, Graph } from '@urbit/api';
|
||||||
import { patp, patp2dec } from 'urbit-ob';
|
import { patp, patp2dec } from 'urbit-ob';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
import { SidebarAssociationItem, SidebarDmItem } from './SidebarItem';
|
import { SidebarAssociationItem, SidebarDmItem } from './SidebarItem';
|
||||||
import useGraphState, { useInbox } from '~/logic/state/graph';
|
import useGraphState, { useInbox } from '~/logic/state/graph';
|
||||||
@ -86,7 +87,7 @@ function getItems(associations: Associations, workspace: Workspace, inbox: Graph
|
|||||||
? []
|
? []
|
||||||
: Array.from(pending).map(s => `~${s}`);
|
: Array.from(pending).map(s => `~${s}`);
|
||||||
|
|
||||||
return [...filtered, ...direct, ...pend];
|
return [...filtered, ..._.union(direct, pend)];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SidebarList(props: {
|
export function SidebarList(props: {
|
||||||
|
@ -193,7 +193,8 @@
|
|||||||
++ jn-start
|
++ jn-start
|
||||||
|= [rid=resource =^ship]
|
|= [rid=resource =^ship]
|
||||||
^+ jn-core
|
^+ jn-core
|
||||||
?< (~(has by joining) rid)
|
?> ?= $@(~ [~ %done])
|
||||||
|
(bind (~(get by joining) rid) |=(request:view progress))
|
||||||
=. joining
|
=. joining
|
||||||
(~(put by joining) rid [%.n now.bowl ship %start])
|
(~(put by joining) rid [%.n now.bowl ship %start])
|
||||||
=. jn-core
|
=. jn-core
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
:~ title+'Landscape'
|
:~ title+'Landscape'
|
||||||
info+'A suite of applications to communicate on Urbit'
|
info+'A suite of applications to communicate on Urbit'
|
||||||
color+0xee.5432
|
color+0xee.5432
|
||||||
glob-http+'https://bootstrap.urbit.org/glob-0v3.55j15.2edo4.e891p.cgslf.iv6oo.glob'
|
glob-http+['https://bootstrap.urbit.org/glob-0v1.vu4e7.efplp.stcdg.7hhds.0tp5v.glob' 0v1.vu4e7.efplp.stcdg.7hhds.0tp5v]
|
||||||
base+'landscape'
|
base+'landscape'
|
||||||
version+[1 3 5]
|
version+[1 3 5]
|
||||||
website+'https://tlon.io'
|
website+'https://tlon.io'
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
import { Path, Patp, Poke, resourceAsPath, Scry } from "../lib";
|
import { Patp, Poke, Scry } from '../lib';
|
||||||
import {
|
import {
|
||||||
Contact,
|
Contact,
|
||||||
ContactUpdateAdd,
|
ContactUpdateAdd,
|
||||||
@ -10,36 +10,36 @@ import {
|
|||||||
ContactUpdate,
|
ContactUpdate,
|
||||||
ContactUpdateAllowShips,
|
ContactUpdateAllowShips,
|
||||||
ContactUpdateAllowGroup,
|
ContactUpdateAllowGroup,
|
||||||
ContactUpdateSetPublic,
|
ContactUpdateSetPublic
|
||||||
} from "./types";
|
} from './types';
|
||||||
|
|
||||||
export const CONTACT_UPDATE_VERSION: number = 0;
|
export const CONTACT_UPDATE_VERSION = 0;
|
||||||
|
|
||||||
const storeAction = <T extends ContactUpdate>(data: T, version: number = CONTACT_UPDATE_VERSION): Poke<T> => ({
|
const storeAction = <T extends ContactUpdate>(data: T, version: number = CONTACT_UPDATE_VERSION): Poke<T> => ({
|
||||||
app: "contact-store",
|
app: 'contact-store',
|
||||||
mark: `contact-update-${version}`,
|
mark: `contact-update-${version}`,
|
||||||
json: data,
|
json: data
|
||||||
});
|
});
|
||||||
|
|
||||||
export { storeAction as contactStoreAction };
|
export { storeAction as contactStoreAction };
|
||||||
|
|
||||||
export const addContact = (ship: Patp, contact: Contact): Poke<ContactUpdateAdd> => {
|
export const addContact = (ship: Patp, contact: Contact): Poke<ContactUpdateAdd> => {
|
||||||
contact["last-updated"] = Date.now();
|
contact['last-updated'] = Date.now();
|
||||||
|
|
||||||
return storeAction({
|
return storeAction({
|
||||||
add: { ship, contact },
|
add: { ship, contact }
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const removeContact = (ship: Patp): Poke<ContactUpdateRemove> =>
|
export const removeContact = (ship: Patp): Poke<ContactUpdateRemove> =>
|
||||||
storeAction({
|
storeAction({
|
||||||
remove: { ship },
|
remove: { ship }
|
||||||
});
|
});
|
||||||
|
|
||||||
export const share = (recipient: Patp, version: number = CONTACT_UPDATE_VERSION): Poke<ContactShare> => ({
|
export const share = (recipient: Patp, version: number = CONTACT_UPDATE_VERSION): Poke<ContactShare> => ({
|
||||||
app: "contact-push-hook",
|
app: 'contact-push-hook',
|
||||||
mark: "contact-share",
|
mark: 'contact-share',
|
||||||
json: { share: recipient },
|
json: { share: recipient }
|
||||||
});
|
});
|
||||||
|
|
||||||
export const editContact = (
|
export const editContact = (
|
||||||
@ -49,9 +49,9 @@ export const editContact = (
|
|||||||
storeAction({
|
storeAction({
|
||||||
edit: {
|
edit: {
|
||||||
ship,
|
ship,
|
||||||
"edit-field": editField,
|
'edit-field': editField,
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now()
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export const allowShips = (
|
export const allowShips = (
|
||||||
@ -67,7 +67,7 @@ export const allowGroup = (
|
|||||||
name: string
|
name: string
|
||||||
): Poke<ContactUpdateAllowGroup> => storeAction({
|
): Poke<ContactUpdateAllowGroup> => storeAction({
|
||||||
allow: {
|
allow: {
|
||||||
group: resourceAsPath({ ship, name })
|
group: { ship, name }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ export const setPublic = (
|
|||||||
return storeAction({
|
return storeAction({
|
||||||
'set-public': setPublic
|
'set-public': setPublic
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
export const retrieve = (
|
export const retrieve = (
|
||||||
ship: string
|
ship: string
|
||||||
@ -93,7 +93,7 @@ export const retrieve = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
export const fetchIsAllowed = (
|
export const fetchIsAllowed = (
|
||||||
entity: string,
|
entity: string,
|
||||||
@ -105,5 +105,5 @@ export const fetchIsAllowed = (
|
|||||||
return {
|
return {
|
||||||
app: 'contact-store',
|
app: 'contact-store',
|
||||||
path: `/is-allowed/${entity}/${name}/${ship}/${isPersonal}`
|
path: `/is-allowed/${entity}/${name}/${ship}/${isPersonal}`
|
||||||
}
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Path, Patp } from "../lib";
|
import { Path, Patp } from '../lib';
|
||||||
import { Resource } from "../groups";
|
import { Resource } from '../groups';
|
||||||
|
|
||||||
export type ContactUpdate =
|
export type ContactUpdate =
|
||||||
| ContactUpdateAdd
|
| ContactUpdateAdd
|
||||||
@ -26,7 +26,7 @@ export interface ContactUpdateRemove {
|
|||||||
export interface ContactUpdateEdit {
|
export interface ContactUpdateEdit {
|
||||||
edit: {
|
edit: {
|
||||||
ship: Patp;
|
ship: Patp;
|
||||||
"edit-field": ContactEditField;
|
'edit-field': ContactEditField;
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -39,7 +39,7 @@ export interface ContactUpdateAllowShips {
|
|||||||
|
|
||||||
export interface ContactUpdateAllowGroup {
|
export interface ContactUpdateAllowGroup {
|
||||||
allow: {
|
allow: {
|
||||||
group: Path;
|
group: Resource;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ export interface Contact {
|
|||||||
|
|
||||||
type ContactKeys = keyof Contact;
|
type ContactKeys = keyof Contact;
|
||||||
|
|
||||||
export type ContactEditFieldPrim = Exclude<ContactKeys, "groups" | "last-updated">;
|
export type ContactEditFieldPrim = Exclude<ContactKeys, 'groups' | 'last-updated'>;
|
||||||
|
|
||||||
export type ContactEditField = Partial<Pick<Contact, ContactEditFieldPrim>> & {
|
export type ContactEditField = Partial<Pick<Contact, ContactEditFieldPrim>> & {
|
||||||
'add-group'?: Resource;
|
'add-group'?: Resource;
|
||||||
|
@ -20,6 +20,11 @@ export const scryTreaties: Scry = {
|
|||||||
path: '/treaties'
|
path: '/treaties'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const scryDefaultAlly: Scry = {
|
||||||
|
app: 'treaty',
|
||||||
|
path: '/default-ally'
|
||||||
|
};
|
||||||
|
|
||||||
export const scryAllies: Scry = {
|
export const scryAllies: Scry = {
|
||||||
app: 'treaty',
|
app: 'treaty',
|
||||||
path: '/allies'
|
path: '/allies'
|
||||||
|
@ -143,10 +143,6 @@ export interface Vat {
|
|||||||
* .^(@uv %cz /=desk=)
|
* .^(@uv %cz /=desk=)
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
/**
|
|
||||||
* True if desk is no longer syncing from upstream
|
|
||||||
*/
|
|
||||||
paused: boolean;
|
|
||||||
hash: string;
|
hash: string;
|
||||||
/**
|
/**
|
||||||
* Current revision
|
* Current revision
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
:~ title+'Web Terminal'
|
:~ title+'Web Terminal'
|
||||||
info+'A web interface for dill, through herm.'
|
info+'A web interface for dill, through herm.'
|
||||||
color+0xff.ffff
|
color+0x2e.4347
|
||||||
glob-http+'https://bootstrap.urbit.org/glob-0v4.8ui32.ui10d.t0v4d.n9g1s.1ftua.glob'
|
glob-http+['https://bootstrap.urbit.org/glob-0v4.8ui32.ui10d.t0v4d.n9g1s.1ftua.glob' 0v4.8ui32.ui10d.t0v4d.n9g1s.1ftua]
|
||||||
base+'webterm'
|
base+'webterm'
|
||||||
version+[0 0 1]
|
version+[0 0 1]
|
||||||
website+'https://tlon.io'
|
website+'https://tlon.io'
|
||||||
|