Merge pull request #5211 from urbit/lf/garden-settings

settings-store: move to garden, namespace
This commit is contained in:
Liam Fitzgerald 2021-09-10 10:16:27 +10:00 committed by GitHub
commit 03a7bdda09
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 386 additions and 147 deletions

View File

@ -0,0 +1,44 @@
/+ *mip
|%
::
++ settings-0
=< settings
|%
+$ settings (map key bucket)
+$ bucket (map key val)
+$ val
$% [%s p=@t]
[%b p=?]
[%n p=@]
==
--
::
++ settings-1
=< settings
|%
+$ settings (map key bucket)
--
+$ bucket (map key val)
+$ key term
+$ val
$~ [%n 0]
$% [%s p=@t]
[%b p=?]
[%n p=@]
[%a p=(list val)]
==
::
+$ settings (mip desk key bucket)
+$ event
$% [%put-bucket =desk =key =bucket]
[%del-bucket =desk =key]
[%put-entry =desk buc=key =key =val]
[%del-entry =desk buc=key =key]
==
+$ data
$% [%all =settings]
[%bucket =bucket]
[%desk desk=(map key bucket)]
[%entry =val]
==
--

View File

@ -5,11 +5,13 @@
+$ versioned-state
$% state-0
state-1
state-2
==
+$ state-0 [%0 settings=settings-0]
+$ state-1 [%1 =settings]
+$ state-1 [%1 settings=settings-1]
+$ state-2 [%2 =settings]
--
=| state-1
=| state-2
=* state -
::
%- agent:dbug
@ -25,9 +27,8 @@
++ on-init
^- (quip card _this)
=^ cards state
(put-entry:do %tutorial %seen b+|)
(put-entry:do q.byk.bol %tutorial %seen b+|)
[cards this]
::
++ on-save !>(state)
::
@ -38,7 +39,8 @@
|-
?- -.old
%0 $(old [%1 +.old])
%1 [~ this(state old)]
%1 $(old [%2 (~(put by *^settings) q.byk.bol settings.old)])
%2 `this(state old)
==
::
++ on-poke
@ -50,10 +52,10 @@
=/ evt=event !<(event vas)
=^ cards state
?- -.evt
%put-bucket (put-bucket:do key.evt bucket.evt)
%del-bucket (del-bucket:do key.evt)
%put-entry (put-entry:do buc.evt key.evt val.evt)
%del-entry (del-entry:do buc.evt key.evt)
%put-bucket (put-bucket:do [desk key bucket]:evt)
%del-bucket (del-bucket:do [desk key]:evt)
%put-entry (put-entry:do [desk buc key val]:evt)
%del-entry (del-entry:do [desk buc key]:evt)
==
[cards this]
::
@ -65,15 +67,22 @@
[%all ~]
[~ this]
::
[%bucket @ ~]
=* bucket-key i.t.pax
?> (~(has by settings) bucket-key)
[%desk @ ~]
=* desk i.t.pax
?> (~(has by settings) desk)
[~ this]
::
[%entry @ @ ~]
=* bucket-key i.t.pax
=* entry-key i.t.t.pax
=/ bucket (~(got by settings) bucket-key)
[%bucket @ @ ~]
=* desk i.t.pax
=* bucket-key i.t.t.pax
?> (~(has bi settings) desk bucket-key)
[~ this]
::
[%entry @ @ @ ~]
=* desk i.t.pax
=* bucket-key i.t.t.pax
=* entry-key i.t.t.t.pax
=/ bucket (~(got bi settings) desk bucket-key)
?> (~(has by bucket) entry-key)
[~ this]
==
@ -85,29 +94,38 @@
[%x %all ~]
``settings-data+!>(`data`all+settings)
::
[%x %bucket @ ~]
=* buc i.t.t.pax
=/ bucket=(unit bucket) (~(get by settings) buc)
[%x %desk @ ~]
=* desk i.t.t.pax
?~ desk-settings=(~(get by settings) desk) [~ ~]
``settings-data+!>(desk+u.desk-settings)
::
[%x %bucket @ @ ~]
=* desk i.t.t.pax
=* buc i.t.t.t.pax
=/ bucket=(unit bucket) (~(get bi settings) desk buc)
?~ bucket [~ ~]
``settings-data+!>(`data`bucket+u.bucket)
::
[%x %entry @ @ ~]
=* buc i.t.t.pax
=* key i.t.t.t.pax
=/ =bucket (fall (~(get by settings) buc) ~)
[%x %entry @ @ @ ~]
=* desk i.t.t.pax
=* buc i.t.t.t.pax
=* key i.t.t.t.t.pax
=/ =bucket (~(gut bi settings) desk buc *bucket)
=/ entry=(unit val) (~(get by bucket) key)
?~ entry [~ ~]
``settings-data+!>(`data`entry+u.entry)
::
[%x %has-bucket @ ~]
=* buc i.t.t.pax
=/ has-bucket=? (~(has by settings) buc)
[%x %has-bucket @ @ ~]
=/ desk i.t.t.pax
=/ buc i.t.t.t.pax
=/ has-bucket=? (~(has bi settings) desk buc)
``noun+!>(`?`has-bucket)
::
[%x %has-entry @ @ ~]
=* buc i.t.t.pax
=* key i.t.t.t.pax
=/ =bucket (fall (~(get by settings) buc) ~)
[%x %has-entry @ @ @ ~]
=* desk i.t.t.pax
=* buc i.t.t.t.pax
=* key i.t.t.t.t.pax
=/ =bucket (~(gut bi settings) desk buc *bucket)
=/ has-entry=? (~(has by bucket) key)
``noun+!>(`?`has-entry)
==
@ -124,60 +142,63 @@
:: already exists
::
++ put-bucket
|= [=key =bucket]
|= [=desk =key =bucket]
^- (quip card _state)
=/ pas=(list path)
:~ /all
/bucket/[key]
/desk/[desk]
/bucket/[desk]/[key]
==
:- [(give-event pas %put-bucket key bucket)]~
state(settings (~(put by settings) key bucket))
:- [(give-event pas %put-bucket desk key bucket)]~
state(settings (~(put bi settings) desk key bucket))
::
:: +del-bucket: delete a bucket from the top level settings map
::
++ del-bucket
|= =key
|= [=desk =key]
^- (quip card _state)
=/ pas=(list path)
:~ /all
/desk/[desk]
/bucket/[key]
==
:- [(give-event pas %del-bucket key)]~
state(settings (~(del by settings) key))
:- [(give-event pas %del-bucket desk key)]~
state(settings (~(del bi settings) desk key))
::
:: +put-entry: put an entry in a bucket, overwriting if it already exists
:: if bucket does not yet exist, create it
::
++ put-entry
|= [buc=key =key =val]
|= [=desk buc=key =key =val]
^- (quip card _state)
=/ pas=(list path)
:~ /all
/bucket/[buc]
/entry/[buc]/[key]
/desk/[desk]
/bucket/[desk]/[buc]
/entry/[desk]/[buc]/[key]
==
=/ =bucket (fall (~(get by settings) buc) ~)
=. bucket (~(put by bucket) key val)
:- [(give-event pas %put-entry buc key val)]~
state(settings (~(put by settings) buc bucket))
=/ =bucket (~(put by (~(gut bi settings) desk buc *bucket)) key val)
:- [(give-event pas %put-entry desk buc key val)]~
state(settings (~(put bi settings) desk key bucket))
::
:: +del-entry: delete an entry from a bucket, fail quietly if bucket does not
:: exist
::
++ del-entry
|= [buc=key =key]
|= [=desk buc=key =key]
^- (quip card _state)
=/ pas=(list path)
:~ /all
/bucket/[buc]
/entry/[buc]/[key]
/desk/[desk]
/bucket/[desk]/[buc]
/entry/[desk]/[buc]/[key]
==
=/ bucket=(unit bucket) (~(get by settings) buc)
=/ bucket=(unit bucket) (~(get bi settings) desk buc)
?~ bucket
[~ state]
=. u.bucket (~(del by u.bucket) key)
:- [(give-event pas %del-entry buc key)]~
state(settings (~(put by settings) buc u.bucket))
:- [(give-event pas %del-entry desk buc key)]~
state(settings (~(put bi settings) desk buc u.bucket))
::
++ give-event
|= [pas=(list path) evt=event]

View File

@ -1,6 +1,7 @@
:~ :- %apes
:~ %docket
%treaty
%settings-store
==
:- %fish ~
==

View File

@ -1,8 +1,8 @@
:~ title+'Garden'
info+'An app launcher for Urbit.'
color+0xee.5432
::glob-http+'https://bootstrap.urbit.org/glob-0v6.t43bu.cpl0b.bsisc.sqr4d.dckpn.glob'
glob-ames+~zod
glob-http+'https://bootstrap.urbit.org/glob-0v6.t43bu.cpl0b.bsisc.sqr4d.dckpn.glob'
::glob-ames+~zod
base+'grid'
version+[0 0 1]
website+'https://tlon.io'

55
pkg/garden/lib/mip.hoon Normal file
View File

@ -0,0 +1,55 @@
|%
++ mip :: map of maps
|$ [kex key value]
(map kex (map key value))
::
++ bi :: mip engine
=| a=(map * (map))
|@
++ del
|* [b=* c=*]
=+ d=(~(gut by a) b ~)
=+ e=(~(del by d) c)
?~ e
(~(del by a) b)
(~(put by a) b e)
::
++ get
|* [b=* c=*]
=> .(b `_?>(?=(^ a) p.n.a)`b, c `_?>(?=(^ a) ?>(?=(^ q.n.a) p.n.q.n.a))`c)
^- (unit _?>(?=(^ a) ?>(?=(^ q.n.a) q.n.q.n.a)))
(~(get by (~(gut by a) b ~)) c)
::
++ got
|* [b=* c=*]
(need (get b c))
::
++ gut
|* [b=* c=* d=*]
(~(gut by (~(gut by a) b ~)) c d)
::
++ has
|* [b=* c=*]
!=(~ (get b c))
::
++ key
|* b=*
~(key by (~(gut by a) b ~))
::
++ put
|* [b=* c=* d=*]
%+ ~(put by a) b
%. [c d]
%~ put by
(~(gut by a) b ~)
::
++ tap
::NOTE naive turn-based implementation find-errors ):
=< $
=+ b=`_?>(?=(^ a) *(list [x=_p.n.a _?>(?=(^ q.n.a) [y=p v=q]:n.q.n.a)]))`~
|. ^+ b
?~ a
b
$(a r.a, b (welp (turn ~(tap by q.n.a) (lead p.n.a)) $(a l.a)))
--
--

View File

@ -11,11 +11,16 @@
%all (settings +.dat)
%bucket (bucket +.dat)
%entry (value +.dat)
%desk (desk-settings +.dat)
==
::
++ settings
|= s=^settings
^- json
[%o (~(run by s) desk-settings)]
::
++ desk-settings
|= s=(map key ^bucket)
[%o (~(run by s) bucket)]
::
++ event
@ -30,35 +35,39 @@
==
::
++ put-bucket
|= [k=key b=^bucket]
|= [d=desk k=key b=^bucket]
^- json
%- pairs
:~ bucket-key+s+k
bucket+(bucket b)
desk+s+d
==
::
++ del-bucket
|= k=key
|= [d=desk k=key]
^- json
%- pairs
:~ bucket-key+s+k
desk+s+d
==
::
++ put-entry
|= [b=key k=key v=val]
|= [d=desk b=key k=key v=val]
^- json
%- pairs
:~ bucket-key+s+b
entry-key+s+k
value+(value v)
desk+s+d
==
::
++ del-entry
|= [buc=key =key]
|= [d=desk buc=key =key]
^- json
%- pairs
:~ bucket-key+s+buc
entry-key+s+key
desk+s+d
==
::
++ value
@ -93,25 +102,29 @@
::
++ put-bucket
%- ot
:~ bucket-key+so
:~ desk+so
bucket-key+so
bucket+bucket
==
::
++ del-bucket
%- ot
:~ bucket-key+so
:~ desk+so
bucket-key+so
==
::
++ put-entry
%- ot
:~ bucket-key+so
:~ desk+so
bucket-key+so
entry-key+so
value+value
==
::
++ del-entry
%- ot
:~ bucket-key+so
:~ desk+so
bucket-key+so
entry-key+so
==
::

View File

@ -0,0 +1 @@
../../garden-dev/sur/settings.hoon

View File

@ -1,15 +1,23 @@
import React from 'react';
import { Setting } from '../../components/Setting';
import { useSettingsState, SettingsState } from '../../state/settings';
import { usePreferencesStore } from './usePreferencesStore';
const selDnd = (s: SettingsState) => s.display.doNotDisturb;
async function toggleDnd() {
const state = useSettingsState.getState();
await state.putEntry('display', 'doNotDisturb', !selDnd(state));
}
export const NotificationPrefs = () => {
const { doNotDisturb, mentions, toggleDoNotDisturb, toggleMentions } = usePreferencesStore();
const { mentions, toggleMentions } = usePreferencesStore();
const doNotDisturb = useSettingsState(selDnd);
return (
<>
<h2 className="h3 mb-7">Notifications</h2>
<div className="space-y-3">
<Setting on={doNotDisturb} toggle={toggleDoNotDisturb} name="Do Not Disturb">
<Setting on={doNotDisturb} toggle={toggleDnd} name="Do Not Disturb">
<p>
Block visual desktop notifications whenever Urbit software produces an in-Landscape
notification badge.

View File

@ -7,6 +7,7 @@ import { persist } from 'zustand/middleware';
import Urbit, { SubscriptionRequestInterface } from '@urbit/http-api';
import { Poke } from '@urbit/api';
import api from './api';
import { useMockData } from './util';
setAutoFreeze(false);
enablePatches();
@ -181,7 +182,7 @@ export async function pokeOptimisticallyN<A, S extends Record<string, unknown>>(
let num: string | undefined;
try {
num = optReduceState(state, poke.json, reduce);
await api.poke(poke);
await (useMockData ? new Promise((res) => setTimeout(res, 500)) : api.poke(poke));
state.getState().removePatch(num);
} catch (e) {
console.error(e);

View File

@ -0,0 +1,98 @@
/* eslint-disable no-param-reassign */
import {
SettingsUpdate,
Value,
putEntry as doPutEntry,
getDeskSettings,
DeskData
} from '@urbit/api/settings';
import _ from 'lodash';
import {
BaseState,
createState,
createSubscription,
pokeOptimisticallyN,
reduceStateN
} from './base';
import api from './api';
interface BaseSettingsState {
display: {
theme: 'light' | 'dark' | 'automatic';
doNotDisturb: boolean;
};
putEntry: (bucket: string, key: string, value: Value) => Promise<void>;
[ref: string]: unknown;
}
export type SettingsState = BaseSettingsState & BaseState<BaseSettingsState>;
function putBucket(json: SettingsUpdate, state: SettingsState): SettingsState {
const data = _.get(json, 'put-bucket', false);
if (data) {
state[data['bucket-key']] = data.bucket;
}
return state;
}
function delBucket(json: SettingsUpdate, state: SettingsState): SettingsState {
const data = _.get(json, 'del-bucket', false);
if (data) {
delete state[data['bucket-key']];
}
return state;
}
function putEntry(json: SettingsUpdate, state: any): SettingsState {
const data: Record<string, string> = _.get(json, 'put-entry', false);
if (data) {
if (!state[data['bucket-key']]) {
state[data['bucket-key']] = {};
}
state[data['bucket-key']][data['entry-key']] = data.value;
}
return state;
}
function delEntry(json: SettingsUpdate, state: any): SettingsState {
const data = _.get(json, 'del-entry', false);
if (data) {
delete state[data['bucket-key']][data['entry-key']];
}
return state;
}
export const reduceUpdate = [putBucket, delBucket, putEntry, delEntry];
export const useSettingsState = createState<BaseSettingsState>(
'Settings',
(set, get) => ({
display: {
theme: 'automatic',
doNotDisturb: true
},
loaded: false,
putEntry: async (bucket, key, val) => {
const poke = doPutEntry(window.desk, bucket, key, val);
await pokeOptimisticallyN(useSettingsState, poke, reduceUpdate);
},
fetchAll: async () => {
const result = (await api.scry<DeskData>(getDeskSettings(window.desk))).desk;
const newState = {
loaded: true,
..._.mergeWith(get(), result, (obj, src) => (_.isArray(src) ? src : undefined))
};
set(newState);
}
}),
[],
[
(set, get) =>
createSubscription('settings-store', `/desk/${window.desk}`, (e) => {
const data = _.get(e, 'settings-event', false);
if (data) {
reduceStateN(get(), data, reduceUpdate);
}
})
]
);

View File

@ -40,8 +40,8 @@ function delEntry(json: SettingsUpdate, state: any): SettingsState {
return state;
}
function getAll(json: any, state: SettingsState): SettingsState {
const data = _.get(json, 'all');
function getDesk(json: any, state: SettingsState): SettingsState {
const data = _.get(json, 'desk');
if(data) {
_.mergeWith(state, data, (obj, src) => _.isArray(src) ? src : undefined);
}
@ -75,7 +75,7 @@ export const reduceUpdate = [
];
export const reduceScry = [
getAll,
getDesk,
getBucket,
getEntry
];

View File

@ -16,7 +16,7 @@ import {
import { useCallback } from 'react';
import { reduceUpdate } from '../reducers/settings-update';
import airlock from '~/logic/api';
import { getAll, Value } from '@urbit/api';
import { getDeskSettings, Value } from '@urbit/api';
import { putEntry } from '@urbit/api/settings';
export interface ShortcutMapping {
@ -45,7 +45,7 @@ export interface SettingsState {
keyboard: ShortcutMapping;
remoteContentPolicy: RemoteContentPolicy;
getAll: () => Promise<void>;
putEntry: (bucket: string, key: string, value: Value) => void;
putEntry: (bucket: string, key: string, value: Value) => Promise<void>;
leap: {
categories: LeapCategories[];
};
@ -101,20 +101,22 @@ const useSettingsState = createState<SettingsState>(
readGroup: 'shift+Escape'
},
getAll: async () => {
const { all } = await airlock.scry(getAll);
const { desk } = await airlock.scry(getDeskSettings((window as any).desk));
get().set((s) => {
Object.assign(s, all);
for(const bucket in desk) {
s[bucket] = { ...(s[bucket] || {}), ...desk[bucket] };
}
});
},
putEntry: (bucket: string, entry: string, value: Value) => {
const poke = putEntry(bucket, entry, value);
putEntry: async (bucket: string, entry: string, value: Value) => {
const poke = putEntry((window as any).desk, bucket, entry, value);
pokeOptimisticallyN(useSettingsState, poke, reduceUpdate);
}
}),
[],
[
(set, get) =>
createSubscription('settings-store', '/all', (e) => {
createSubscription('settings-store', `/desk/${(window as any).desk}`, (e) => {
const data = _.get(e, 'settings-event', false);
if (data) {
reduceStateN(get(), data, reduceUpdate);

View File

@ -32,7 +32,6 @@ import Tiles from './components/tiles';
import Tile from './components/tiles/tile';
import './css/custom.css';
import { join } from '@urbit/api/groups';
import { putEntry } from '@urbit/api/settings';
import { joinGraph } from '@urbit/api/graph';
import airlock from '~/logic/api';
@ -103,15 +102,17 @@ export const LaunchApp = (props: LaunchAppProps): ReactElement | null => {
maxWidth: '350px',
modal: function modal(dismiss) {
const onDismiss = (e) => {
const { putEntry } = useSettingsState.getState();
e.stopPropagation();
airlock.poke(putEntry('tutorial', 'seen', true));
putEntry('tutorial', 'seen', true);
dismiss();
};
const onContinue = async (e) => {
const { putEntry } = useSettingsState.getState();
e.stopPropagation();
if (!hasTutorialGroup({ associations })) {
await airlock.poke(join(TUTORIAL_HOST, TUTORIAL_GROUP));
await airlock.poke(putEntry('tutorial', 'joined', Date.now()));
await putEntry('tutorial', 'joined', Date.now());
await waiter(hasTutorialGroup);
await Promise.all(
[TUTORIAL_BOOK, TUTORIAL_CHAT, TUTORIAL_LINKS].map(graph => airlock.thread(joinGraph(TUTORIAL_HOST, graph))));

View File

@ -4,7 +4,6 @@ import {
ManagedCheckboxField, Text
} from '@tlon/indigo-react';
import { Form, useFormikContext } from 'formik';
import { putEntry } from '@urbit/api/settings';
import _ from 'lodash';
import React from 'react';
import useSettingsState, { selectSettingsState } from '~/logic/state/settings';
@ -15,7 +14,6 @@ import {
import { FormikOnBlur } from '~/views/components/FormikOnBlur';
import { ShuffleFields } from '~/views/components/ShuffleFields';
import { BackButton } from './BackButton';
import airlock from '~/logic/api';
const labels: Record<LeapCategories, string> = {
mychannel: 'My Channels',
@ -60,11 +58,12 @@ export function LeapSettings() {
};
const onSubmit = async (values: FormSchema) => {
const { putEntry } = useSettingsState.getState();
const result = values.categories.reduce(
(acc, { display, category }) => (display ? [...acc, category] : acc),
[] as LeapCategories[]
);
await airlock.poke(putEntry('leap', 'categories', result));
await putEntry('leap', 'categories', result);
};
return (

View File

@ -3,7 +3,6 @@ import _ from 'lodash';
import { Box, Col, Text } from '@tlon/indigo-react';
import { Formik, Form, useField } from 'formik';
import { putEntry } from '@urbit/api/settings';
import { getChord } from '~/logic/lib/util';
import useSettingsState, {
@ -12,7 +11,6 @@ import useSettingsState, {
} from '~/logic/state/settings';
import { AsyncButton } from '~/views/components/AsyncButton';
import { BackButton } from './BackButton';
import airlock from '~/logic/api';
const settingsSel = selectSettingsState(['keyboard']);
@ -69,9 +67,10 @@ export default function ShortcutSettings() {
initialValues={keyboard}
onSubmit={async (values: ShortcutMapping, actions) => {
const promises = _.map(values, (value, key) => {
const { putEntry } = useSettingsState.getState();
return keyboard[key] !== value
? airlock.poke(putEntry('keyboard', key, value))
: Promise.resolve(0);
? putEntry('keyboard', key, value)
: Promise.resolve();
});
await Promise.all(promises);
actions.setStatus({ success: null });

View File

@ -4,7 +4,7 @@ import {
ManagedTextInputField as Input, Row,
Text
} from '@tlon/indigo-react';
import { join, MetadataUpdatePreview, putEntry } from '@urbit/api';
import { join, MetadataUpdatePreview } from '@urbit/api';
import { Form, Formik, FormikHelpers, useFormikContext } from 'formik';
import _ from 'lodash';
import React, { ReactElement, useCallback, useEffect, useState } from 'react';
@ -22,6 +22,7 @@ import { FormError } from '~/views/components/FormError';
import { StatelessAsyncButton } from '~/views/components/StatelessAsyncButton';
import { GroupSummary } from './GroupSummary';
import airlock from '~/logic/api';
import useSettingsState from '~/logic/state/settings';
const formSchema = Yup.object({
group: Yup.string()
@ -73,8 +74,9 @@ export function JoinGroup(props: JoinGroupProps): ReactElement {
const onConfirm = useCallback(async (group: string) => {
const [,,ship,name] = group.split('/');
const { putEntry } = useSettingsState.getState();
if(group === TUTORIAL_GROUP_RESOURCE) {
await airlock.poke(putEntry('tutorial', 'joined', Date.now()));
await putEntry('tutorial', 'joined', Date.now());
}
if (group in groups) {
return history.push(`/~landscape${group}`);

View File

@ -1,5 +1,5 @@
import { Box, Button, Col, Icon, Row, Text } from '@tlon/indigo-react';
import { leaveGroup, putEntry } from '@urbit/api';
import { leaveGroup } from '@urbit/api';
import _ from 'lodash';
import React, { useCallback, useEffect, useState } from 'react';
import { useHistory } from 'react-router-dom';
@ -16,6 +16,7 @@ import { Portal } from '~/views/components/Portal';
import { StatelessAsyncButton } from '~/views/components/StatelessAsyncButton';
import { Triangle } from '~/views/components/Triangle';
import airlock from '~/logic/api';
import useSettingsState from '~/logic/state/settings';
const localSelector = selectLocalState([
'tutorialProgress',
@ -94,7 +95,8 @@ export function TutorialModal() {
const dismiss = useCallback(async () => {
setPaused(false);
hideTutorial();
await airlock.poke(putEntry('tutorial', 'seen', true));
const { putEntry } = useSettingsState.getState();
await putEntry('tutorial', 'seen', true);
}, [hideTutorial]);
const bailExit = useCallback(() => {

View File

@ -35,7 +35,6 @@
%observe-hook
%s3-store
%sane
%settings-store
%weather
==
:- %fish

View File

@ -1,7 +1,7 @@
:~ title+'Landscape'
info+'A suite of applications to communicate on Urbit'
color+0xee.5432
glob+'https://bootstrap.urbit.org/glob-0v4.0k6hb.4s38v.su79d.10vd5.7c8lu.glob'
glob-http+'https://bootstrap.urbit.org/glob-0v4.0k6hb.4s38v.su79d.10vd5.7c8lu.glob'
base+'landscape'
version+[0 0 1]
website+'https://tlon.io'

View File

@ -1,31 +0,0 @@
|%
+$ settings-0 (map key bucket-0)
+$ bucket-0 (map key val-0)
+$ val-0
$% [%s p=@t]
[%b p=?]
[%n p=@]
==
::
+$ settings (map key bucket)
+$ bucket (map key val)
+$ key term
+$ val
$~ [%n 0]
$% [%s p=@t]
[%b p=?]
[%n p=@]
[%a p=(list val)]
==
+$ event
$% [%put-bucket =key =bucket]
[%del-bucket =key]
[%put-entry buc=key =key =val]
[%del-entry buc=key =key]
==
+$ data
$% [%all =settings]
[%bucket =bucket]
[%entry =val]
==
--

View File

@ -0,0 +1 @@
../../garden-dev/sur/settings.hoon

View File

@ -44,15 +44,16 @@
++ read-setting
|= key=term
=/ m (strand @t) ^- form:m
;< =bowl:spider bind:m get-bowl:strandio
;< has=? bind:m
%+ scry:strandio ?
/gx/settings-store/has-entry/gcp-store/[key]/noun
/gx/settings-store/has-entry/[q.byk.bowl]/gcp-store/[key]/noun
?. has
(strand-fail:strandio (rap 3 %gcp-missing- key ~) ~)
;< =data:settings bind:m
%+ scry:strandio
data:settings
/gx/settings-store/entry/gcp-store/[key]/settings-data
/gx/settings-store/entry/[q.byk.bowl]/gcp-store/[key]/settings-data
?> ?=([%entry %s @] data)
(pure:m p.val.data)
::

View File

@ -40,9 +40,10 @@ b+has
|= key=@tas
=/ m (strand ?)
^- form:m
;< =bowl:spider bind:m get-bowl:strandio
;< has=? bind:m
%+ scry:strandio ?
/gx/settings-store/has-entry/gcp-store/[key]/noun
/gx/settings-store/has-entry/[q.byk.bowl]/gcp-store/[key]/noun
(pure:m has)
::
--

View File

@ -1,4 +1,4 @@
import { Poke, Scry } from "../lib";
import { Poke, Scry } from '../lib';
import { PutBucket, Key, Bucket, DelBucket, Value, PutEntry, DelEntry, SettingsUpdate } from './types';
export const action = <T extends SettingsUpdate>(data: T): Poke<T> => ({
@ -8,29 +8,35 @@ export const action = <T extends SettingsUpdate>(data: T): Poke<T> => ({
});
export const putBucket = (
desk: string,
key: Key,
bucket: Bucket
): Poke<PutBucket> => action({
'put-bucket': {
desk,
'bucket-key': key,
'bucket': bucket
}
});
export const delBucket = (
desk: string,
key: Key
): Poke<DelBucket> => action({
'del-bucket': {
desk,
'bucket-key': key
}
});
export const putEntry = (
desk: string,
bucket: Key,
key: Key,
value: Value
): Poke<PutEntry> => action({
'put-entry': {
desk,
'bucket-key': bucket,
'entry-key': key,
value: value
@ -38,10 +44,12 @@ export const putEntry = (
});
export const delEntry = (
desk: string,
bucket: Key,
key: Key
): Poke<DelEntry> => action({
'del-entry': {
desk,
'bucket-key': bucket,
'entry-key': key
}
@ -50,17 +58,21 @@ export const delEntry = (
export const getAll: Scry = {
app: 'settings-store',
path: '/all'
}
};
export const getBucket = (bucket: string) => ({
export const getBucket = (desk: string, bucket: string) => ({
app: 'settings-store',
path: `/bucket/${bucket}`
});
export const getEntry = (bucket: string, entry: string) => ({
export const getEntry = (desk: string, bucket: string, entry: string) => ({
app: 'settings-store',
path: `/entry/${bucket}/${entry}`
path: `/entry/${desk}/${bucket}/${entry}`
});
export const getDeskSettings = (desk: string) => ({
app: 'settings-store',
path: `/desk/${desk}`
});
export * from './types';

View File

@ -1,46 +1,54 @@
export type Key = string;
export type Value = string | string[] | boolean | number;
export type Bucket = Map<string, Value>;
export type Settings = Map<string, Bucket>;
export type Bucket = { [key: string]: Value; };
export type DeskSettings = { [bucket: string]: Bucket; };
export type Settings = { [desk: string]: Settings; }
export interface PutBucket {
"put-bucket": {
"bucket-key": Key;
"bucket": Bucket;
'put-bucket': {
desk: string;
'bucket-key': Key;
'bucket': Bucket;
};
}
export interface DelBucket {
"del-bucket": {
"bucket-key": Key;
'del-bucket': {
desk: string;
'bucket-key': Key;
};
}
export interface PutEntry {
"put-entry": {
"bucket-key": Key;
"entry-key": Key;
"value"?: Value;
'put-entry': {
'bucket-key': Key;
'entry-key': Key;
'value'?: Value;
};
}
export interface DelEntry {
"del-entry": {
"bucket-key": Key;
"entry-key": Key;
'del-entry': {
desk: string;
'bucket-key': Key;
'entry-key': Key;
};
}
export interface AllData {
"all": Settings;
'all': Settings;
}
export interface DeskData {
desk: DeskSettings;
}
export interface BucketData {
"bucket": Bucket;
'bucket': Bucket;
}
export interface EntryData {
"entry": Value;
'entry': Value;
}
export type SettingsUpdate =
@ -52,4 +60,5 @@ export type SettingsUpdate =
export type SettingsData =
| AllData
| BucketData
| EntryData;
| EntryData
| DeskData;