Merge pull request #67 from midsum-salrux/ns/lure-settings

landscape: add lure integration
This commit is contained in:
Hunter Miller 2023-03-28 17:24:06 -05:00 committed by GitHub
commit 6c9300bb90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 644 additions and 2 deletions

164
desk/app/bait.hoon Normal file
View File

@ -0,0 +1,164 @@
/- reel
/+ default-agent, verb, dbug, server, *reel
|%
+$ card card:agent:gall
+$ versioned-state
$% state-0
state-1
==
::
+$ state-0
$: %0
todd=(map [inviter=ship token=cord] description=cord)
==
+$ state-1
$: %1
token-metadata=(map [inviter=ship token=cord] metadata:reel)
==
--
::
|%
++ landing-page
|= =metadata:reel
^- manx
=/ description
?. =(tag.metadata 'groups-0') ""
(trip (~(got by fields.metadata) 'description'))
;html
;head
;title:"Lure"
==
;body
;p: {description}
Enter your @p:
;form(method "post")
;input(type "text", name "ship", id "ship", placeholder "~sampel");
;button(type "submit"):"Request invite"
==
;script: ship = document.cookie.split("; ").find((row) => row.startsWith("ship="))?.split("=")[1]; document.getElementById("ship").value=(ship || "~sampel-palnet")
==
==
::
++ sent-page
|= invitee=ship
^- manx
;html
;head
;title:"Lure"
==
;body
Your invite has been sent! Go to your ship to accept it.
;script: document.cookie="ship={(trip (scot %p invitee))}"
==
==
--
::
=| state-1
=* state -
::
%- agent:dbug
%+ verb |
|_ =bowl:gall
+* this .
def ~(. (default-agent this %|) bowl)
::
++ on-init
^- (quip card _this)
[[%pass /eyre/connect %arvo %e %connect [~ /lure] dap.bowl]~ this]
::
++ on-save !>(state)
++ on-load
|= old-state=vase
^- (quip card _this)
=/ old !<(versioned-state old-state)
?- -.old
%1
`this(state old)
%0
`this(state *state-1)
==
::
++ on-poke
|= [=mark =vase]
^- (quip card _this)
?+ mark (on-poke:def mark vase)
%handle-http-request
=+ !<([id=@ta inbound-request:eyre] vase)
|^
:_ this
=/ full-line=request-line:server (parse-request-line:server url.request)
=/ line
?: ?=([%lure @ @ *] site.full-line)
t.site.full-line
?: ?=([@ @ *] site.full-line)
site.full-line
!!
?+ method.request (give not-found:gen:server)
%'GET'
?: ?=([%bait %who ~] line)
(give (json-response:gen:server s+(scot %p our.bowl)))
=/ inviter (slav %p i.line)
=/ token i.t.line
=/ =metadata:reel (fall (~(get by token-metadata) [inviter token]) *metadata:reel)
?: ?=([@ @ %metadata ~] line)
(give (json-response:gen:server (enjs-metadata metadata)))
(give (manx-response:gen:server (landing-page metadata)))
%'POST'
=/ inviter (slav %p i.line)
=/ token i.t.line
?~ body.request
(give not-found:gen:server)
?. =('ship=%7E' (end [3 8] q.u.body.request))
(give not-found:gen:server)
=/ joiner (slav %p (cat 3 '~' (rsh [3 8] q.u.body.request)))
:* :* %pass /bite %agent [inviter %reel]
%poke %reel-bite !>([%bite-1 token joiner inviter])
==
:* %pass /bite %agent [our.bowl %reel]
%poke %reel-bite !>([%bite-1 token joiner inviter])
==
(give (manx-response:gen:server (sent-page joiner)))
==
==
::
++ give
|= =simple-payload:http
(give-simple-payload:app:server id simple-payload)
--
%bait-describe
=+ !<([token=cord =metadata:reel] vase)
`this(token-metadata (~(put by token-metadata) [src.bowl token] metadata))
::
%bait-undescribe
=+ !<(token=cord vase)
`this(token-metadata (~(del by token-metadata) [src.bowl token]))
%bind-slash
:_ this
~[[%pass /eyre/connect %arvo %e %connect [~ /] dap.bowl]]
%unbind-slash
:_ this
~[[%pass /eyre/connect %arvo %e %connect [~ /] %docket]]
==
::
++ on-agent on-agent:def
++ on-watch
|= =path
^- (quip card _this)
?> =(our.bowl src.bowl)
?+ path (on-watch:def path)
[%http-response *] `this
==
++ on-leave on-leave:def
++ on-peek on-peek:def
++ on-arvo
|= [=wire =sign-arvo]
^- (quip card _this)
?+ sign-arvo (on-arvo:def wire sign-arvo)
[%eyre %bound *]
~? !accepted.sign-arvo
[dap.bowl 'eyre bind rejected!' binding.sign-arvo]
[~ this]
==
::
++ on-fail on-fail:def
--

154
desk/app/reel.hoon Normal file
View File

@ -0,0 +1,154 @@
/- reel
/+ default-agent, verb, dbug, *reel
|%
+$ card card:agent:gall
+$ versioned-state
$% state-0
state-1
state-2
==
::
:: vic: URL of bait service
:: civ: @p of bait service
:: our-metadata: map from tokens to their metadata
:: outstanding-pokes: ships we have poked and await a response from
::
+$ state-0
$: %0
vic=@t
civ=ship
descriptions=(map cord cord)
==
+$ state-1
$: %1
vic=@t
civ=ship
our-metadata=(map cord metadata:reel)
==
+$ state-2
$: %2
vic=@t
civ=ship
our-metadata=(map cord metadata:reel)
outstanding-pokes=(set (pair ship cord))
==
++ url-for-token
|= [vic=cord our=ship token=cord]
(crip "{(trip vic)}{(trip (scot %p our))}/{(trip token)}")
--
=| state-2
=* state -
::
%- agent:dbug
%+ verb |
|_ =bowl:gall
+* this .
def ~(. (default-agent this %|) bowl)
::
++ on-init
^- (quip card _this)
`this(vic 'https://tlon.network/lure/', civ ~loshut-lonreg)
::
++ on-save !>(state)
++ on-load
|= old-state=vase
^- (quip card _this)
=/ old !<(versioned-state old-state)
?- -.old
%2
`this(state old)
%1
`this(state [%2 'https://tlon.network/lure/' ~loshut-lonreg ~ ~])
%0
`this(state [%2 'https://tlon.network/lure/' ~loshut-lonreg ~ ~])
==
::
++ on-poke
|= [=mark =vase]
^- (quip card _this)
?+ mark (on-poke:def mark vase)
%reel-command
?> =(our.bowl src.bowl)
=+ !<(=command:reel vase)
?- -.command
%set-service
:_ this(vic vic.command)
~[[%pass /set-ship %arvo %k %fard q.byk.bowl %reel-set-ship %noun !>(vic)]]
%set-ship
`this(civ civ.command)
==
::
%reel-bite
=+ !<(=bite:reel vase)
[[%give %fact ~[/bites] mark !>(bite)]~ this]
::
%reel-describe
=+ !<([token=cord =metadata:reel] vase)
:_ this(our-metadata (~(put by our-metadata) token metadata))
~[[%pass /describe %agent [civ %bait] %poke %bait-describe !>([token metadata])]]
%reel-undescribe
=+ !<(token=cord vase)
:_ this(our-metadata (~(del by our-metadata) token))
~[[%pass /undescribe %agent [civ %bait] %poke %bait-undescribe !>(token)]]
%reel-want-token-link
=+ !<(token=cord vase)
:_ this
=/ result=(unit [cord cord])
?. (~(has by our-metadata) token) ~
`[token (url-for-token vic our.bowl token)]
~[[%pass [%token-link-want token ~] %agent [src.bowl %reel] %poke %reel-give-token-link !>(result)]]
%reel-give-token-link
=+ !<(result=(unit [cord cord]) vase)
?~ result `this
=/ [token=cord url=cord] u.result
:_ this
~[[%give %fact ~[[%token-link (scot %p src.bowl) token ~]] %json !>(s+url)]]
==
::
++ on-agent
|= [=wire =sign:agent:gall]
^- (quip card _this)
?: ?=([%token-link @ @ ~] wire)
?+ -.sign (on-agent:def wire sign)
%poke-ack
`this(outstanding-pokes (~(del in outstanding-pokes) [src.bowl i.t.t.wire]))
==
(on-agent:def wire sign)
::
++ on-watch
|= =path
^- (quip card _this)
?> =(our.bowl src.bowl)
?+ path (on-watch:def path)
[%bites ~] `this
[%token-link @ @ ~]
=/ target (slav %p i.t.path)
=/ group i.t.t.path
?~ (~(has in outstanding-pokes) [target group]) `this
:_ this(outstanding-pokes (~(put in outstanding-pokes) [target group]))
~[[%pass path %agent [target %reel] %poke %reel-want-token-link !>(group)]]
==
::
++ on-leave on-leave:def
++ on-peek
|= =path
^- (unit (unit cage))
?+ path [~ ~]
[%x %service ~] ``noun+!>(vic)
[%x %bait ~] ``reel-bait+!>([vic civ])
::
[%x %metadata @ ~]
=/ =metadata:reel (fall (~(get by our-metadata) i.t.t.path) *metadata:reel)
``reel-metadata+!>(metadata)
==
::
++ on-arvo
|= [=wire sign=sign-arvo]
^- (quip card:agent:gall _this)
?> ?=([%set-ship ~] wire)
?> ?=([%khan %arow *] sign)
?: ?=(%.n -.p.sign)
((slog 'reel: fetch bait ship failed' p.p.sign) `this)
`this
++ on-fail on-fail:def
--

View File

@ -3,4 +3,6 @@
%hark-store
%hark-system-hook
%settings-store
%reel
%bait
==

20
desk/lib/reel.hoon Normal file
View File

@ -0,0 +1,20 @@
/- reel
|%
++ enjs-metadata
|= =metadata:reel
^- json
=/ fields
%+ turn ~(tap by fields.metadata)
|= [key=cord value=cord]
^- [cord json]
[key s+value]
%- pairs:enjs:format
:~ ['tag' s+tag.metadata]
['fields' (pairs:enjs:format fields)]
==
++ dejs-metadata
%- ot:dejs:format
:~ tag+so:dejs:format
fields+(om so):dejs:format
==
--

View File

@ -0,0 +1,12 @@
/- reel
|_ [token=cord =metadata:reel]
++ grad %noun
++ grab
|%
++ noun (pair cord metadata:reel)
--
++ grow
|%
++ noun [token metadata]
--
--

View File

@ -0,0 +1,11 @@
|_ token=cord
++ grad %noun
++ grab
|%
++ noun cord
--
++ grow
|%
++ noun token
--
--

18
desk/mar/reel/bait.hoon Normal file
View File

@ -0,0 +1,18 @@
/- reel
|_ [vic=cord civ=ship]
++ grad %noun
++ grab
|%
++ noun (pair cord ship)
++ json
%- ot:dejs:format
:~ url+so:dejs:format
ship+(cu:dejs:format |=(=cord (slav %p cord)) so:dejs:format)
==
--
++ grow
|%
++ noun [vic civ]
++ json (pairs:enjs:format ~[['url' s+vic] ['ship' s+(scot %p civ)]])
--
--

12
desk/mar/reel/bite.hoon Normal file
View File

@ -0,0 +1,12 @@
/- reel
|_ =bite:reel
++ grad %noun
++ grab
|%
++ noun bite:reel
--
++ grow
|%
++ noun bite
--
--

View File

@ -0,0 +1,19 @@
/- reel
|_ =command:reel
++ grad %noun
++ grab
|%
++ noun command:reel
++ json
|= j=^json
:- %set-service
%. j
%- ot:dejs:format
:~ url+so:dejs:format
==
--
++ grow
|%
++ noun command
--
--

View File

@ -0,0 +1,14 @@
/- reel
/+ *reel
|_ [token=cord =metadata:reel]
++ grad %noun
++ grab
|%
++ noun (pair cord cord)
++ json (ot:dejs:format ~[token+so:dejs:format metadata+dejs-metadata])
--
++ grow
|%
++ noun [token metadata]
--
--

View File

@ -0,0 +1,13 @@
|_ description=cord
++ grad %noun
++ grab
|%
++ noun cord
++ json so:dejs:format
--
++ grow
|%
++ noun description
++ json [%s description]
--
--

View File

@ -0,0 +1,11 @@
|_ token-url=(unit [token=cord url=cord])
++ grad %noun
++ grab
|%
++ noun (unit (pair cord cord))
--
++ grow
|%
++ noun token-url
--
--

View File

@ -0,0 +1,15 @@
/- reel
/+ *reel
|_ =metadata:reel
++ grad %noun
++ grab
|%
++ noun metadata
++ json dejs-metadata
--
++ grow
|%
++ noun metadata
++ json (enjs-metadata metadata)
--
--

View File

@ -0,0 +1,14 @@
/- reel
/+ *reel
|_ token=cord
++ grad %noun
++ grab
|%
++ noun (pair cord cord)
++ json (ot:dejs:format ~[token+so:dejs:format])
--
++ grow
|%
++ noun token
--
--

View File

@ -0,0 +1,11 @@
|_ token=cord
++ grad %noun
++ grab
|%
++ noun cord
--
++ grow
|%
++ noun token
--
--

13
desk/sur/reel.hoon Normal file
View File

@ -0,0 +1,13 @@
|%
+$ command
$% [%set-service vic=@t]
[%set-ship civ=@p]
==
::
+$ bite
$% [%bite-0 token=@ta ship=@p]
[%bite-1 token=@ta joiner=@p inviter=@p]
==
::
+$ metadata [tag=term fields=(map cord cord)]
--

14
desk/ted/set-ship.hoon Normal file
View File

@ -0,0 +1,14 @@
/- spider
/+ *strandio
=, strand=strand:spider
=, strand-fail=strand-fail:libstrand:spider
^- thread:spider
|= arg=vase
=/ m (strand ,vase)
^- form:m
=+ !<(vic=cord arg)
;< our=@p bind:m get-our
;< =json bind:m (fetch-json "{(trip vic)}bait/who")
=/ =ship (slav %p (so:dejs:format json))
;< ~ bind:m (poke [our %reel] reel-command+!>([%set-ship ship]))
(pure:m !>(~))

View File

@ -0,0 +1,11 @@
import React from 'react';
import { IconProps } from './icon';
export default function InvitesIcon({ className }: IconProps) {
// Placeholder, please make a real icon
return (
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className={className}>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 5.23077C9 7.60984 7.65685 8.46154 6 8.46154C4.34315 8.46154 3 7.60984 3 5.23077C3 2.8517 4.34315 2 6 2C7.65685 2 9 2.8517 9 5.23077ZM6 8.46154C2.68629 8.46154 0 9.92159 0 14H12C12 9.92159 9.31371 8.46154 6 8.46154ZM14 4C14 3.44772 13.5523 3 13 3C12.4477 3 12 3.44772 12 4V5H11C10.4477 5 10 5.44772 10 6C10 6.55228 10.4477 7 11 7H12V8C12 8.55228 12.4477 9 13 9C13.5523 9 14 8.55228 14 8V7H15C15.5523 7 16 6.55228 16 6C16 5.44772 15.5523 5 15 5H14V4Z" fill="#666666"/>
</svg>
);
}

View File

@ -0,0 +1,51 @@
import React from 'react';
import { useForm } from 'react-hook-form';
import useInviteState from '../state/invites';
import { Button } from '../components/Button';
import { Spinner } from '../components/Spinner';
import cn from 'classnames';
export const InvitePrefs = () => {
const {baitURL, setBaitURL, loaded, save} = useInviteState();
const {
register,
handleSubmit,
reset,
formState: { isSubmitting, isDirty, isValid, isSubmitSuccessful },
} = useForm<{url: string}>({
mode: 'onChange',
});
return (
<div className="inner-section space-y-8">
<h2 className="h4">Invite Links</h2>
<form onSubmit={handleSubmit(save)}>
<div className="mb-8 flex flex-col space-y-2">
<label className="font-semibold" htmlFor="endpoint">
Invite Server URL
</label>
<input
disabled={!loaded}
id="url"
type="text"
defaultValue={baitURL}
{...register('url', { pattern: /^http.*\/$/ })}
className="input default-ring bg-gray-50"
/>
</div>
<Button
type="submit"
disabled={!isDirty || !isValid}
className={cn(
!isDirty || !isValid || isSubmitSuccessful
? 'cursor-not-allowed bg-gray-200 text-gray-100'
: ''
)}
>
{isSubmitting ? <Spinner /> : 'Save'}
{isSubmitSuccessful && ' Successful'}
</Button>
</form>
</div>
);
};

View File

@ -16,6 +16,7 @@ import { AppearancePrefs } from './AppearancePrefs';
import { useCharges } from '../state/docket';
import { AppPrefs } from './AppPrefs';
import { StoragePrefs } from './StoragePrefs';
import { InvitePrefs } from './InvitePrefs';
import { DocketImage } from '../components/DocketImage';
import { ErrorAlert } from '../components/ErrorAlert';
import { useMedia } from '../logic/useMedia';
@ -31,21 +32,25 @@ import PencilIcon from '../components/icons/PencilIcon';
import ForwardSlashIcon from '../components/icons/ForwardSlashIcon';
import SlidersIcon from '../components/icons/SlidersIcon';
import Sig16Icon from '../components/icons/Sig16Icon';
import InvitesIcom from '../components/icons/InvitesIcon';
import { useSystemUpdate } from '../logic/useSystemUpdate';
import { Bullet } from '../components/icons/Bullet';
import SearchSystemPreferences from './SearchSystemPrefences';
import { ShortcutPrefs } from './ShortcutPrefs';
import { AttentionAndPrivacy } from './AttentionAndPrivacy';
import { useReelInstalled } from "../state/invites"
interface SystemPreferencesSectionProps {
url: string;
active: boolean;
visible?: boolean;
}
function SystemPreferencesSection({
url,
active,
children,
visible=true,
}: PropsWithChildren<SystemPreferencesSectionProps>) {
return (
<li>
@ -53,7 +58,8 @@ function SystemPreferencesSection({
to={url}
className={classNames(
'flex items-center rounded-lg px-2 py-2 hover:bg-gray-50 hover:text-black',
active && 'bg-gray-50 text-black'
active && 'bg-gray-50 text-black',
!visible && 'hidden'
)}
>
{children}
@ -75,6 +81,7 @@ export const SystemPreferences = (
.filter((charge) => charge.desk !== 'landscape');
const isMobile = useMedia('(max-width: 639px)');
const settingsPath = isMobile ? `${match.url}/:submenu` : '/';
const reelInstalled = useReelInstalled();
const matchSub = useCallback(
(target: string, desk?: string) => {
@ -187,6 +194,14 @@ export const SystemPreferences = (
<SlidersIcon className="mr-3 h-6 w-6 rounded-md text-gray-600" />
Remote Storage
</SystemPreferencesSection>
<SystemPreferencesSection
url={subUrl('invites')}
active={matchSub('invites')}
visible={reelInstalled}
>
<InvitesIcom className="mr-3 h-6 w-6 rounded-md text-gray-600" />
Invite Links
</SystemPreferencesSection>
</ul>
</nav>
<nav className="flex flex-col px-2 sm:px-6">
@ -205,7 +220,7 @@ export const SystemPreferences = (
<DocketImage size="small" className="mr-3" {...charge} />
{getAppName(charge)}
</SystemPreferencesSection>
))}
))}
</ul>
</nav>
</aside>
@ -237,6 +252,7 @@ export const SystemPreferences = (
/>
<Route path={[`${match.url}/storage`]} component={StoragePrefs} />
<Route path={`${match.url}/security`} component={SecurityPrefs} />
<Route path={[`${match.url}/invites`]} component={InvitePrefs} />
<Route
path={[`${match.url}/system-updates`, match.url]}
component={AboutSystem}

47
ui/src/state/invites.ts Normal file
View File

@ -0,0 +1,47 @@
import api from '../state/api';
import { useState, useEffect } from 'react';
export function useReelInstalled() {
const [reelInstalled, setReelInstalled] = useState(false);
useEffect(() => {
api.scry<{url: string}>({
app: 'reel',
path: '/bait'
}).then(({url}) => {
setReelInstalled(true);
});
}, []);
return reelInstalled
}
export default function useInviteState() {
const [baitURL, setBaitURL] = useState('');
const [loaded, setLoaded] = useState(false)
useEffect(() => {
api.scry<{url: string}>({
app: 'reel',
path: '/bait'
}).then(({url}) => {
setBaitURL(url);
setLoaded(true);
});
}, []);
return {
baitURL: baitURL,
setBaitURL: setBaitURL,
loaded: loaded,
save: async (data: {url: string}) => {
await api.poke({
app: 'reel',
mark: 'reel-command',
json: {
url: data.url,
}
})
}
}
}