Merge branch 'na-release/next-vere' into jt/minkleton

* na-release/next-vere: (1169 commits)
  jets: fix broken jets so ~marzod can boot.
  jets: hook up preexisting +repn jet.
  jets: add empty hashes
  jets: review comments
  Revert "Revert "u3: clear cached mugs on cell edit""
  jets: +welp and +zing
  jets: fix memory leak in +run:in
  jet: +find
  jets: +all:by and +any:by
  jets: +urn:by
  jet: +rep:by
  jet: +rep:in
  jets: +apt:in
  jets: +run:in
  jets: final cleanup
  jets: reenable +uni:by
  jets: jet +apt:by
  jets: +key:by and +wyt:by
  jets: +run:by
  jet: final cleanup
  ...
This commit is contained in:
Joe Bryan 2020-05-20 14:05:17 -07:00
commit 6d3a5be259
581 changed files with 55770 additions and 11705 deletions

View File

@ -0,0 +1,39 @@
---
name: Kernel or runtime bug report
about: Use this template to file a bug for low-level system components, e.g. Hoon,
Arvo, Zuse, the vanes, Vere, etc.
title: ''
labels: bug
assignees: ''
---
<!-- A good bug report, description of a crash, etc., should ideally be *reproducible*, with clear steps as to how another developer can replicate and examine your problem. That said, this isn't always possible; some bugs depend on having created a complicated or unusual state, or can otherwise simply be difficult to trigger again (say, you encountered it in the last continuity era).
Your issue should thus at a minimum be *informative*. The best advice here is probably "don't write bad issues," where "bad" is a matter of judgment and taste. Issues that the maintainers don't judge to be sufficiently useful or informative may be closed. -->
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behaviour:
1. ...
2. ...
3. ...
**Expected behaviour**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**System (please supply the following information, if relevant):**
- OS: [e.g. macOS, linux64, FreeBSD]
- Vere and Urbit OS versions
- Your ship's `%base` hash (use `.^(@uv %cz /=base=)` to check)
**Additional context**
Add any other context about the problem here.
**Notify maintainers**
If you happen to know who the appropriate maintainers are, consider mentioning them with an @ here. You may want to use `git blame` to see who has last touched any relevant code.

View File

@ -0,0 +1,39 @@
---
name: OS1 Bug report
about: 'Use this template to file a bug for any OS1 app: Chat, Publish, Links, Groups,
Weather or Clock'
title: ''
labels: OS1
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem. If possible, please also screenshot your browser's dev console. Here are [Chrome's docs](https://developers.google.com/web/tools/chrome-devtools/open) for using this feature.
**Desktop (please complete the following information):**
- OS: [e.g. MacOS 10.15.3]
- Browser [e.g. chrome, safari]
- Base hash of your urbit ship. Run ` .^(@uv %cz /=base=)` in Dojo to see this.
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Base hash of your urbit ship. Run ` .^(@uv %cz /=base=)` in Dojo to see this.
**Additional context**
Add any other context about the problem here.

57
.gitignore vendored
View File

@ -1,23 +1,46 @@
/out # nix symlink artifacts
/result #
/result-* result
/work result-*
# common dev piers
#
/zod /zod
/bus /bus
/fakezod* /nec
tags /fakezod
TAGS
# package manager caches
#
.stack-work
node_modules
# build and release artifacts
#
cross/ cross/
release/ release/
.stack-work dist
/out
/work
# landscape dev
#
urbitrc
*-min.js
pkg/interface/link-webext/web-ext-artifacts
# catchall editor and OS stuff
#
.tags
.etags
tags
TAGS
GPATH
GRTAGS
GTAGS
.DS_Store
*.swp
*.swo
\#*\# \#*\#
s/* s/*
**/.DS_Store
**/dist
**/node_modules
**/urbitrc
**/*.swp
**/*.swo
**/*-min.js
.stack-work
pkg/interface/link-webext/web-ext-artifacts

View File

@ -158,9 +158,8 @@ commit that updates the source.
## Releases ## Releases
We typically create releases by cherry picking appropriate commits from We typically create releases by tagging appropriate commits on `master`, so any
`master` and tagging the result, so any given commit in `master` may not given commit in `master` may not actually be present in the latest release.
actually be present in the latest release.
We perform updates by pushing releases over-the-air to `~zod` approximately We perform updates by pushing releases over-the-air to `~zod` approximately
once per week, so any contribution that can be deployed OTA will usually find once per week, so any contribution that can be deployed OTA will usually find

View File

@ -1,13 +1,125 @@
# Maintainers' Guide # Maintainers' Guide
## Branch organization
The essence of this branching scheme is that you create "release branches" of
independently releasable units of work. These can then be released by their
maintainers when ready.
### Master branch
Master is what's released on the network. Deployment instructions are in the
next section, but tagged releases should always come from this branch.
### Feature branches
Anyone can create feature branches. For those with commit access to
urbit/urbit, you're welcome to create them in this repo; otherwise, fork the
repo and create them there.
Usually, new development should start from master, but if your work depends on
work in another feature branch or release branch, start from there.
If, after starting your work, you need changes that are in master, merge it into
your branch. If you need changes that are in a release branch or feature
branch, merge it into your branch, but understand that your work now depends on
that release branch, which means it won't be released until that one is
released.
### Release branches
Release branches are code that is ready to release. All release branch names
should start with `release/`.
All code must be reviewed before being pushed to a release branch. Thus,
feature branches should be PR'd against a release branch, not master.
Create new release branches as needed. You don't need a new one for every PR,
since many changes are relatively small and can be merged together with little
risk. However, once you merge two branches, they're now coupled and will only
be released together -- unless one of the underlying commits is separately put
on a release branch.
Here's a worked example. The rule is to make however many branches are useful,
and no more. This example is not prescriptive, the developers making the
changes may add, remove, or rename branches in this flow at will.
Suppose you (plural, the dev community at large) complete some work in a
userspace app, and you put it in `release/next-userspace`. Separately, you make
a small JS change. If you PR it to `release/next-userspace`, then it will only
be released at the same time as the app changes. Maybe this is fine, or maybe
you want this change to go out quickly, and the change in
`release/next-userspace` is relatively risky, so you don't want to push it out
on Friday afternoon. In this case, put the change in another release branch,
say `release/next-js`. Now either can be released independently.
Suppose you do further work that you want to PR to `release/next-userspace`, but
it depends on your fixes in `release/next-js`. Simply merge `release/next-js`
into either your feature branch or `release/next-userspace` and PR your finished
work to `release/next-userspace`. Now there is a one-way coupling:
`release/next-userspace` contains `release/next-js`, so releasing it will
implicitly release `release/next-js`. However, you can still release
`release/next-js` independently.
This scheme extends to other branches, like `release/next-kernel` or
`release/os1.1` or `release/ford-fusion`. Some branches may be long-lived and
represent simply the "next" release of something, while others will have a
definite lifetime that corresponds to development of a particular feature or
numbered release.
Since they are "done", release branches should be considered "public", in the
sense that others may depend on them at will. Thus, never rebase a release
branch.
When cutting a new release, you can filter branches with `git branch --list
'release/*'` or by typing "release/" in the branch filter on Github. This will
give you the list of branches which have passed review and may be merged to
master and released. When choosing which branches to release, make sure you
understand the risks of releasing them immediately. If merging these produces
nontrivial conflicts, consider asking the developers on those branches to merge
between themselves. In many cases a developer can do this directly, but if it's
sufficiently nontrivial, this may be a reviewed PR of one release branch into
another.
### Non-OTAable release branches
In some cases, work is completed which cannot be OTA'd as written. For example,
the code may lack state adapters, or it may not properly handle outstanding
subscriptions. It could also be code which is planned to be released only upon
a breach (network-wide or rolling).
In this case, the code may be PR'd to a `na-release/` branch. All rules are the
same as for release branches, except that the code does not need to apply
cleanly to an existing ship. If you later write state adapter or otherwise make
it OTAable, then you may PR it to a release branch.
### Other cases
Outside contributors can generally target their PRs against master unless
specifically instructed. Maintainers should retarget those branches as
appropriate.
If a commit is not something that goes into a release (eg changes to README or
CI), it may be committed straight to master.
If a hotfix is urgent, it may be PR'd straight to master. This should only be
done if you reasonably expect that it will be released soon and before anything
else is released.
If a series of commits that you want to release is on a release branch, but you
really don't want to release the whole branch, you must cherry-pick them onto
another release branch. Cherry-picking isn't ideal because those commits will
be duplicated in the history, but it won't have any serious side effects.
## Hotfixes ## Hotfixes
Here lies an informal guide for making hotfix releases and deploying them to Here lies an informal guide for making hotfix releases and deploying them to
the network. the network.
Take [this recent PR][1], as an example. This constituted a great hotfix. Take [this PR][1], as an example. This constituted a great hotfix. It's a
It's a single commit, targeting a problem that existed on the network at the single commit, targeting a problem that existed on the network at the time.
time. Here's it should be released and deployed OTA. Here's it should be released and deployed OTA.
[1]: https://github.com/urbit/urbit/pull/2025 [1]: https://github.com/urbit/urbit/pull/2025
@ -16,14 +128,9 @@ time. Here's it should be released and deployed OTA.
Unless it's very trivial, it should probably have a single "credible looking" Unless it's very trivial, it should probably have a single "credible looking"
review from somebody else on it. review from somebody else on it.
You can just merge the PR in GitHub. As I, `~nidsut-tomdun`, am a l33t You should avoid merging the PR in GitHub directly. Instead, use the
h4x0r, I use a custom merge commit format, gotten by: `sh/merge-with-custom-msg` script -- it will produce a merge commit with
message along the lines of:
```
git merge --no-ff --signoff --log BRANCH
```
with the commit message:
``` ```
Merge branch FOO (#PR_NUM) Merge branch FOO (#PR_NUM)
@ -32,66 +139,58 @@ Merge branch FOO (#PR_NUM)
bar: ... bar: ...
baz: ... baz: ...
Signed-off-by: Jared Tobin <jared@tlon.io> Signed-off-by: SIGNER <signer@example.com>
``` ```
All this extra wankery is hardly required, but IMO it's nice to have the We do this as it's nice to have the commit log information in the merge commit,
commit log information in the merge commit, which GitHub's "Merge PR" button which GitHub's "Merge PR" button doesn't do (at least by default).
doesn't do (at least by default). `sh/merge-with-custom-msg` performs some useful last-minute urbit-specific
checks, as well.
The script at `sh/merge-with-custom-message` can be used to make this simple(r) You might want to alias `sh/merge-with-custom-msg` locally, to make it easier
to do. I use `git mu` as an alias for it, locally. to use. My .git/config contains the following, for example:
### Apply the changes to this era's release branch
This corresponds to the 'vx.y' part of the most recent 'urbit vx.y.z' release.
At the time of writing, we're on v0.10 (and I'll use this branch as a running
example):
If the branch doesn't yet exist, just create it via:
``` ```
git checkout -b v0.10 master [alias]
mu = !sh/merge-with-custom-msg
``` ```
If you can get away with merging master to v0.10 without pulling in any so that I can type e.g. `git mu origin/foo 1337`.
superfluous commits, feel free to do that. Otherwise, you'll want to cherry
pick the commits like so:
``` ### Prepare a release commit
git cherry-pick -x TARGET_COMMITS
```
Use the `-x` flag to `git-cherry-pick`, because this will indicate in the You should create Landscape or alternative pill builds, if or as appropriate
commit message where the things originally came from. (i.e., if anything in Landscape changed -- don't trust any compiled JS/CSS
that's included in the commit), and commit these in a release commit.
Create Landscape or alternative pill builds, if or as appropriate (i.e., if You should always create a solid pill, in particular, as it's convenient for
anything in Landscape changed -- don't trust the compiled JS/CSS that's
included in the commit).
You may also want to create a brass pill, in particular, as it's convenient for
tooling to be able to boot directly from a given release. tooling to be able to boot directly from a given release.
If you're making a Vere release, just play it safe and update all the pills.
### Tag the resulting commit ### Tag the resulting commit
What you should do here depends on the type of release being made. What you should do here depends on the type of release being made.
First, for Arvo releases: First, for Urbit OS releases:
If it's a very trivial hotfix that you know isn't going to break If it's a very trivial hotfix that you know isn't going to break
anything, tag it as `arvo.yyyy.mm.dd`. Use an annotated tag, i.e. anything, tag it as `urbit-os-vx.y.z`. Here 'x' refers to the product version
(e.g. OS1, OS2..), 'y' to the continuity era in that version, and 'z' to an
OTA patch counter. So for a hotfix version, you'll just want to increment 'z'.
Use an annotated tag, i.e.
``` ```
git tag -a arvo.yyyy.mm.dd git tag -a urbit-os-vx.y.z
``` ```
The tag format should look something like this: The tag format should look something like this:
``` ```
arvo.yyyy.mm.dd urbit-os-vx.y.z
This release contains Arvo changes that will be pushed to the live This release will be pushed to the network as an over-the-air update.
network as an over-the-air update.
Release notes: Release notes:
@ -106,8 +205,7 @@ You can get the "contributions" section by the shortlog between the
last release and this release: last release and this release:
``` ```
git log --pretty=short --no-merges \ git log --pretty=short LAST_RELEASE.. | git shortlog
LAST_RELEASE..v0.10 | git shortlog
``` ```
I originally tried to curate this list somewhat, but now just paste it I originally tried to curate this list somewhat, but now just paste it
@ -121,23 +219,28 @@ If the commit descriptions are too poor to easily do this, then again, yell at
your fellow contributors to make them better in the future. your fellow contributors to make them better in the future.
If it's *not* a trivial hotfix, you should probably make any number of release If it's *not* a trivial hotfix, you should probably make any number of release
candidate tags (e.g. `arvo.yyyy.mm.dd.rc-1`, `arvo.yyyy.mm.dd.rc-2`, ..), test candidate tags (e.g. `urbit-os-vx.y.z.rc1`, `urbit-os-vx.y.z.rc2`, ..), test
them, and after you confirm one of them is good, tag the release as them, and after you confirm one of them is good, tag the release as
`arvo.yyyy.mm.dd`. `urbit-os-vx.y.z`.
For Vere releases: For Vere releases:
Tag the release as `vx.y.z`. The tag format should look something Tag the release as `urbit-vx.y.z`. The tag format should look something like
like this: this:
``` ```
urbit vx.y.z urbit-vx.y.z
This release contains Vere changes, so users should update their Note that this Vere release will by default boot fresh ships using an Urbit OS
binaries. va.b.c pill.
This is not a breaching release, so users should not create new Release binaries:
piers.
(linux64)
https://bootstrap.urbit.org/urbit-vx.y.z-linux64.tgz
(macOS)
https://bootstrap.urbit.org/urbit-vx.y.z-darwin.tgz
Release notes: Release notes:
@ -150,29 +253,37 @@ Contributions:
The same schpeel re: release candidates applies here. The same schpeel re: release candidates applies here.
You should probably avoid putting both Arvo and Vere changes into Vere Note that the release notes indicate which version of Urbit OS the Vere release
releases. will use by default when booting fresh ships. Do not include implicit Urbit OS
changes in Vere releases; this used to be done, historically, but shouldn't be
any longer. If there are Urbit OS and Vere changes to be released, make two
separate releases.
### Deploy the update ### Deploy the update
For Arvo updates, this means copying the files into ~zod's %base desk. For (**Note**: the following steps are automated by some other Tlon-internal
consistency, I download the release tarball and then rsync the files in: tooling. Just ask `~nidsut-tomdun` for details.)
For Urbit OS updates, this means copying the files into ~zod's %base desk. The
changes will be synced to /~zod/kids and then propagated through other galaxies
and stars to the rest of the network.
For consistency, I create a release tarball and then rsync the files in.
``` ```
$ wget https://github.com/urbit/urbit/archive/arvo.yyyy.mm.dd.tar.gz $ wget https://github.com/urbit/urbit/archive/urbit-os-vx.y.z.tar.gz
$ tar xzf arvo.yyyy.mm.dd.tar.gz $ tar xzf urbit-os-vx.y.z.tar.gz
$ herb zod -p hood -d "+hood/mount /=base=" $ herb zod -p hood -d "+hood/mount /=base="
$ rsync -zr --delete urbit-arvo.yyyy.mm.dd/pkg/arvo/ zod/base $ rsync -zr --delete urbit-urbit-os-vx.y.z/pkg/arvo/ zod/base
$ herb zod -p hood -d "+hood/commit %base" $ herb zod -p hood -d "+hood/commit %base"
``` ```
For Vere updates, this means shutting down each desired ship, installing the For Vere updates, this means simply shutting down each desired ship, installing
new binary, and restarting the pier with it. the new binary, and restarting the pier with it.
### Announce the update ### Announce the update
Post an announcement to urbit-dev. The tag annotation, basically, is fine here Post an announcement to urbit-dev. The tag annotation, basically, is fine here
-- I usually add the %base hash (for Arvo releases) and the release binary URLs -- I usually add the %base hash (for Urbit OS releases) and the release binary
(for Vere releaes). Check the urbit-dev archives for examples of these URLs (for Vere releases). Check the urbit-dev archives for examples of these
announcements. announcements.

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:362ba607d646cc053ef27c9cab1d7e6cf07856d0949cb5a48e17ef536e857613 oid sha256:5f283336929733f6492f7fe230c949749cea43c8a1fb18959742b2ff88e9d239
size 7227783 size 10418083

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:61a38233c95cd8e0c0790e33c134c1aab607659a16abce22e85f56b6c77b19c5 oid sha256:8d2579ed2b72828ced40789c0eae516a832c66f8f9dcd06af4ba5ec4cb4e2ac6
size 1232440 size 1236461

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:df7e73129cc484fba44301eec4230b9ec3dc533163db36b885074ff8b018b6c8 oid sha256:20219ec89d58a89285733db183b89e5f19e5bb7764bed43218c0c83902dd1e56
size 9650082 size 12878034

View File

@ -1,15 +0,0 @@
source $stdenv/setup
cp -r $src ./src
chmod -R u+w ./src
cd src
bash ./configure
make clean
make all -j8
make test
mkdir -p $out/bin
cp ./build/urbit $out/bin/$exename
cp ./build/urbit-worker $out/bin/$exename-worker

View File

@ -27,9 +27,21 @@ let
inherit name meta; inherit name meta;
exename = name; exename = name;
src = ../../../pkg/urbit; src = ../../../pkg/urbit;
builder = ./builder.sh;
nativeBuildInputs = deps ++ vendor; nativeBuildInputs = deps ++ vendor;
configurePhase = ''
bash ./configure
'';
installPhase = ''
make all -j8
make test
mkdir -p $out/bin
cp ./build/urbit $out/bin/$exename
cp ./build/urbit-worker $out/bin/$exename-worker
'';
# See https://github.com/NixOS/nixpkgs/issues/18995 # See https://github.com/NixOS/nixpkgs/issues/18995
hardeningDisable = if debug then [ "all" ] else []; hardeningDisable = if debug then [ "all" ] else [];

View File

@ -87,8 +87,9 @@
|= [state=app-state our=ship dap=term] |= [state=app-state our=ship dap=term]
^- card:agent:gall ^- card:agent:gall
=/ args=vase !> =/ args=vase !>
:* %watch /[dap] :+ %watch /[dap]
url.state =(%czar (clan:title our)) ~m5 ^- config:eth-watcher
:* url.state =(%czar (clan:title our)) ~m5 ~m30
launch:contracts:azimuth launch:contracts:azimuth
~[azimuth:contracts:azimuth] ~[azimuth:contracts:azimuth]
(topics whos.state) (topics whos.state)

View File

@ -17,8 +17,15 @@
:: ::
|% |%
+$ card card:agent:gall +$ card card:agent:gall
+$ state ::
$: grams=(list mail) :: all messages +$ versioned-state
$% state-1
state-0
==
::
+$ state-1
$: %1
grams=(list mail) :: all messages
known=(set [target serial]) :: known message lookup known=(set [target serial]) :: known message lookup
count=@ud :: (lent grams) count=@ud :: (lent grams)
bound=(map target glyph) :: bound circle glyphs bound=(map target glyph) :: bound circle glyphs
@ -31,11 +38,27 @@
eny=@uvJ :: entropy eny=@uvJ :: entropy
== ==
:: ::
+$ state-0
$: grams=(list [[=ship =path] envelope]) :: all messages
known=(set [[=ship =path] serial]) :: known message lookup
count=@ud :: (lent grams)
bound=(map [=ship =path] glyph) :: bound circle glyphs
binds=(jug glyph [=ship =path]) :: circle glyph lookup
audience=(set [=ship =path]) :: active targets
settings=(set term) :: frontend flags
width=@ud :: display width
timez=(pair ? @ud) :: timezone adjustment
cli=state=sole-share:sole-sur :: console state
eny=@uvJ :: entropy
==
::
+$ mail [source=target envelope] +$ mail [source=target envelope]
+$ target [=ship =path] +$ target [in-group=? =ship =path]
:: ::
+$ glyph char +$ glyph char
++ glyphs "!@#$%^&()-=_+[]\{};'\\:\",.<>?" ++ glyphs "!@#$%^&()-=_+[]\{}'\\:\",.<>?"
::
+$ nu-security ?(%channel %village %village-with-group)
:: ::
+$ command +$ command
$% [%target (set target)] :: set messaging target $% [%target (set target)] :: set messaging target
@ -44,10 +67,10 @@
:: ::
:: ::
:: create chat :: create chat
[%create rw-security path (unit glyph) (unit ?)] [%create nu-security path (unit glyph) (unit ?)]
[%delete path] :: delete chat [%delete path] :: delete chat
[%invite ?(%r %w %rw) path (set ship)] :: allow [%invite [? path] (set ship)] :: allow
[%banish ?(%r %w %rw) path (set ship)] :: disallow [%banish [? path] (set ship)] :: disallow
:: ::
[%join target (unit glyph) (unit ?)] :: join target [%join target (unit glyph) (unit ?)] :: join target
[%leave target] :: nuke target [%leave target] :: nuke target
@ -68,8 +91,8 @@
== :: == ::
:: ::
-- --
=| state =| state-1
=* all-state - =* state -
:: ::
%- agent:dbug %- agent:dbug
%+ verb | %+ verb |
@ -83,26 +106,22 @@
:: ::
++ on-init ++ on-init
^- (quip card _this) ^- (quip card _this)
:- [connect:tc]~ =^ cards state (prep:tc ~)
%_ this [cards this]
audience [[our-self:tc /] ~ ~]
settings (sy %showtime %notify ~)
width 80
==
:: ::
++ on-save !>(all-state) ++ on-save !>(state)
:: ::
++ on-load ++ on-load
|= old-state=vase |= old-state=vase
^- (quip card _this) ^- (quip card _this)
=/ old !<(state old-state) =/ old !<(versioned-state old-state)
=^ cards all-state (prep:tc `old) =^ cards state (prep:tc `old)
[cards this] [cards this]
:: ::
++ on-poke ++ on-poke
|= [=mark =vase] |= [=mark =vase]
^- (quip card _this) ^- (quip card _this)
=^ cards all-state =^ cards state
?+ mark (on-poke:def mark vase) ?+ mark (on-poke:def mark vase)
%noun (poke-noun:tc !<(* vase)) %noun (poke-noun:tc !<(* vase))
%sole-action (poke-sole-action:tc !<(sole-action:sole-sur vase)) %sole-action (poke-sole-action:tc !<(sole-action:sole-sur vase))
@ -112,7 +131,7 @@
++ on-watch ++ on-watch
|= =path |= =path
^- (quip card _this) ^- (quip card _this)
=^ cards all-state (peer:tc path) =^ cards state (peer:tc path)
[cards this] [cards this]
:: ::
++ on-leave on-leave:def ++ on-leave on-leave:def
@ -120,14 +139,22 @@
++ on-agent ++ on-agent
|= [=wire =sign:agent:gall] |= [=wire =sign:agent:gall]
^- (quip card _this) ^- (quip card _this)
=^ cards all-state =^ cards state
?- -.sign ?- -.sign
%poke-ack [- all-state]:(on-agent:def wire sign) %poke-ack [- state]:(on-agent:def wire sign)
%watch-ack [- all-state]:(on-agent:def wire sign) %watch-ack [- state]:(on-agent:def wire sign)
%kick [?:(?=([%chat-store ~] wire) ~[connect:tc] ~) all-state] ::
%kick
:_ state
?+ wire ~
[%chat-store ~] ~[connect:tc]
[%invites ~] ~[connect-invites:tc]
==
::
%fact %fact
?+ p.cage.sign ~|([%chat-cli-bad-sub-mark wire p.cage.sign] !!) ?+ p.cage.sign ~|([%chat-cli-bad-sub-mark wire p.cage.sign] !!)
%chat-update (diff-chat-update:tc wire !<(chat-update q.cage.sign)) %chat-update (diff-chat-update:tc wire !<(chat-update q.cage.sign))
%invite-update (handle-invite-update:tc !<(invite-update q.cage.sign))
== ==
== ==
[cards this] [cards this]
@ -140,58 +167,104 @@
:: +prep: setup & state adapter :: +prep: setup & state adapter
:: ::
++ prep ++ prep
|= old=(unit state) |= old=(unit versioned-state)
^- (quip card state) ^- (quip card _state)
?^ old ?~ old
:_ u.old =^ cards state
?: (~(has by wex.bowl) [/chat-store our-self %chat-store]) %_ catch-up
~ audience [[| our-self /] ~ ~]
~[connect] settings (sy %showtime %notify ~)
=^ cards all-state width 80
%_ catch-up ==
audience [[our-self /] ~ ~] [[connect connect-invites cards] state]
settings (sy %showtime %notify ~) :- %+ weld
width 80 ?: (~(has by wex.bowl) [/invites our-self %invite-store]) ~
~[connect-invites]
?: (~(has by wex.bowl) [/chat-store our-self %chat-store]) ~
~[connect]
::
^- state-1
?- -.u.old
%1
=? width.u.old =(0 width.u.old) 80
u.old(bound (~(gas by *(map target glyph)) ~(tap by bound.u.old)))
::
?(~ ^)
:- %1
%= u.old
grams ~ ::NOTE this only impacts historic message lookup in chat-cli
::
known
^- (set [target serial])
%- ~(run in known.u.old)
|= [t=[ship path] s=serial]
[`target`[| t] s]
::
bound
^- (map target glyph)
%- ~(gas by *(map target glyph))
%+ turn ~(tap by bound.u.old)
|= [t=[ship path] g=glyph]
[`target`[| t] g]
::
binds
^- (jug glyph target)
%- ~(run by binds.u.old)
|= s=(set [ship path])
%- ~(run in s)
|= t=[ship path]
`target`[| t]
::
audience
^- (set target)
%- ~(run in audience.u.old)
|= t=[ship path]
`target`[| t]
== ==
[[connect cards] all-state] ==
:: +catch-up: process all chat-store state :: +catch-up: process all chat-store state
:: ::
++ catch-up ++ catch-up
^- (quip card state) ^- (quip card _state)
=/ =inbox =/ =inbox
.^ inbox (scry-for inbox %chat-store /all)
%gx |- ^- (quip card _state)
(scot %p our.bowl) ?~ inbox [~ state]
%chat-store
(scot %da now.bowl)
/all/noun
==
|- ^- (quip card state)
?~ inbox [~ all-state]
=* path p.n.inbox =* path p.n.inbox
=* mailbox q.n.inbox =* mailbox q.n.inbox
=/ =target (path-to-target path) =/ =target (path-to-target path)
=^ cards-n all-state (read-envelopes target envelopes.mailbox) =^ cards-n state (read-envelopes target (flop envelopes.mailbox))
=^ cards-l all-state $(inbox l.inbox) =^ cards-l state $(inbox l.inbox)
=^ cards-r all-state $(inbox r.inbox) =^ cards-r state $(inbox r.inbox)
[:(weld cards-n cards-l cards-r) all-state] [:(weld cards-n cards-l cards-r) state]
:: +connect: connect to the chat-store :: +connect: connect to the chat-store
:: ::
++ connect ++ connect
^- card ^- card
[%pass /chat-store %agent [our-self %chat-store] %watch /updates] [%pass /chat-store %agent [our-self %chat-store] %watch /updates]
:: ::
++ connect-invites
^- card
[%pass /invites %agent [our.bowl %invite-store] %watch /invitatory/chat]
::
++ our-self (name:title our.bowl) ++ our-self (name:title our.bowl)
:: +target-to-path: prepend ship to the path :: +target-to-path: prepend ship to the path
:: ::
++ target-to-path ++ target-to-path
|= target |= target
%+ weld
?:(in-group ~ /~)
[(scot %p ship) path] [(scot %p ship) path]
:: +path-to-target: deduces a target from a mailbox path :: +path-to-target: deduces a target from a mailbox path
:: ::
++ path-to-target ++ path-to-target
|= =path |= =path
^- target ^- target
=^ in-group path
?. ?=([%'~' *] path)
[& path]
[| t.path]
:- in-group
?. ?=([@ @ *] path) ?. ?=([@ @ *] path)
::TODO can we safely assert the above? ::TODO can we safely assert the above?
~& [%path-without-host path] ~& [%path-without-host path]
@ -203,24 +276,24 @@
:: ::
++ poke-noun ++ poke-noun
|= a=* |= a=*
^- (quip card state) ^- (quip card _state)
?: ?=(%connect a) ?: ?=(%connect a)
[[connect ~] all-state] [[connect ~] state]
?: ?=(%catch-up a) ?: ?=(%catch-up a)
catch-up catch-up
[~ all-state] [~ state]
:: +poke-sole-action: handle cli input :: +poke-sole-action: handle cli input
:: ::
++ poke-sole-action ++ poke-sole-action
::TODO use id.act to support multiple separate sessions ::TODO use id.act to support multiple separate sessions
|= [act=sole-action:sole-sur] |= [act=sole-action:sole-sur]
^- (quip card state) ^- (quip card _state)
(sole:sh-in act) (sole:sh-in act)
:: +peer: accept only cli subscriptions from ourselves :: +peer: accept only cli subscriptions from ourselves
:: ::
++ peer ++ peer
|= =path |= =path
^- (quip card state) ^- (quip card _state)
?. (team:title our-self src.bowl) ?. (team:title our-self src.bowl)
~| [%peer-talk-stranger src.bowl] ~| [%peer-talk-stranger src.bowl]
!! !!
@ -230,40 +303,48 @@
:: display a fresh prompt :: display a fresh prompt
:- [prompt:sh-out ~] :- [prompt:sh-out ~]
:: start with fresh sole state :: start with fresh sole state
all-state(state.cli *sole-share:sole-sur) state(state.cli *sole-share:sole-sur)
:: +handle-invite-update: get new invites
::
++ handle-invite-update
|= upd=invite-update
^- (quip card _state)
?+ -.upd [~ state]
%invite [[(show-invite:sh-out invite.upd) ~] state]
==
:: +diff-chat-update: get new mailboxes & messages :: +diff-chat-update: get new mailboxes & messages
:: ::
++ diff-chat-update ++ diff-chat-update
|= [=wire upd=chat-update] |= [=wire upd=chat-update]
^- (quip card state) ^- (quip card _state)
?+ -.upd [~ all-state] ?+ -.upd [~ state]
%create (notice-create +.upd) %create (notice-create (path-to-target path.upd))
%delete [[(show-delete:sh-out (path-to-target path.upd)) ~] all-state] %delete [[(show-delete:sh-out (path-to-target path.upd)) ~] state]
%message (read-envelope (path-to-target path.upd) envelope.upd) %message (read-envelope (path-to-target path.upd) envelope.upd)
%messages (read-envelopes (path-to-target path.upd) envelopes.upd) %messages (read-envelopes (path-to-target path.upd) (flop envelopes.upd))
== ==
:: ::
++ read-envelopes ++ read-envelopes
|= [=target envs=(list envelope)] |= [=target envs=(list envelope)]
^- (quip card state) ^- (quip card _state)
?~ envs [~ all-state] ?~ envs [~ state]
=^ cards-i all-state (read-envelope target i.envs) =^ cards-i state (read-envelope target i.envs)
=^ cards-t all-state $(envs t.envs) =^ cards-t state $(envs t.envs)
[(weld cards-i cards-t) all-state] [(weld cards-i cards-t) state]
:: ::
++ notice-create ++ notice-create
|= =target |= =target
^- (quip card state) ^- (quip card _state)
=^ cards all-state =^ cards state
?: (~(has by bound) target) ?: (~(has by bound) target)
[~ all-state] [~ state]
(bind-default-glyph target) (bind-default-glyph target)
[[(show-create:sh-out target) cards] all-state] [[(show-create:sh-out target) cards] state]
:: +bind-default-glyph: bind to default, or random available :: +bind-default-glyph: bind to default, or random available
:: ::
++ bind-default-glyph ++ bind-default-glyph
|= =target |= =target
^- (quip card state) ^- (quip card _state)
=; =glyph (bind-glyph glyph target) =; =glyph (bind-glyph glyph target)
|^ =/ g=glyph (choose glyphs) |^ =/ g=glyph (choose glyphs)
?. (~(has by binds) g) g ?. (~(has by binds) g) g
@ -281,7 +362,7 @@
:: ::
++ bind-glyph ++ bind-glyph
|= [=glyph =target] |= [=glyph =target]
^- (quip card state) ^- (quip card _state)
::TODO should send these to settings store eventually ::TODO should send these to settings store eventually
:: if the target was already bound to another glyph, un-bind that :: if the target was already bound to another glyph, un-bind that
:: ::
@ -289,16 +370,16 @@
(~(del ju binds) (~(got by bound) target) target) (~(del ju binds) (~(got by bound) target) target)
=. bound (~(put by bound) target glyph) =. bound (~(put by bound) target glyph)
=. binds (~(put ju binds) glyph target) =. binds (~(put ju binds) glyph target)
[(show-glyph:sh-out glyph `target) all-state] [(show-glyph:sh-out glyph `target) state]
:: +unbind-glyph: remove all binding for glyph :: +unbind-glyph: remove all binding for glyph
:: ::
++ unbind-glyph ++ unbind-glyph
|= [=glyph targ=(unit target)] |= [=glyph targ=(unit target)]
^- (quip card state) ^- (quip card _state)
?^ targ ?^ targ
=. binds (~(del ju binds) glyph u.targ) =. binds (~(del ju binds) glyph u.targ)
=. bound (~(del by bound) u.targ) =. bound (~(del by bound) u.targ)
[(show-glyph:sh-out glyph ~) all-state] [(show-glyph:sh-out glyph ~) state]
=/ ole=(set target) =/ ole=(set target)
(~(get ju binds) glyph) (~(get ju binds) glyph)
=. binds (~(del by binds) glyph) =. binds (~(del by binds) glyph)
@ -308,7 +389,7 @@
=. bound $(ole l.ole) =. bound $(ole l.ole)
=. bound $(ole r.ole) =. bound $(ole r.ole)
(~(del by bound) n.ole) (~(del by bound) n.ole)
[(show-glyph:sh-out glyph ~) all-state] [(show-glyph:sh-out glyph ~) state]
:: +decode-glyph: find the target that matches a glyph, if any :: +decode-glyph: find the target that matches a glyph, if any
:: ::
++ decode-glyph ++ decode-glyph
@ -331,12 +412,12 @@
:: ::
++ read-envelope ++ read-envelope
|= [=target =envelope] |= [=target =envelope]
^- (quip card state) ^- (quip card _state)
?: (~(has in known) [target uid.envelope]) ?: (~(has in known) [target uid.envelope])
::NOTE we no-op only because edits aren't possible ::NOTE we no-op only because edits aren't possible
[~ all-state] [~ state]
:- (show-envelope:sh-out target envelope) :- (show-envelope:sh-out target envelope)
%_ all-state %_ state
known (~(put in known) [target uid.envelope]) known (~(put in known) [target uid.envelope])
grams [[target envelope] grams] grams [[target envelope] grams]
count +(count) count +(count)
@ -351,10 +432,10 @@
:: ::
++ sole ++ sole
|= act=sole-action:sole-sur |= act=sole-action:sole-sur
^- (quip card state) ^- (quip card _state)
?- -.dat.act ?- -.dat.act
%det (edit +.dat.act) %det (edit +.dat.act)
%clr [~ all-state] %clr [~ state]
%ret obey %ret obey
%tab (tab +.dat.act) %tab (tab +.dat.act)
== ==
@ -367,8 +448,8 @@
:: ::
[%create leaf+";create [type] /chat-name (glyph)"] [%create leaf+";create [type] /chat-name (glyph)"]
[%delete leaf+";delete /chat-name"] [%delete leaf+";delete /chat-name"]
[%invite leaf+";invite [rw | r | w] /chat-name ~ships"] [%invite leaf+";invite /chat-name ~ships"]
[%banish leaf+";banish [rw | r | w] /chat-name ~ships"] [%banish leaf+";banish /chat-name ~ships"]
:: ::
[%bind leaf+";bind [glyph] ~ship/chat-name"] [%bind leaf+";bind [glyph] ~ship/chat-name"]
[%unbind leaf+";unbind [glyph]"] [%unbind leaf+";unbind [glyph]"]
@ -383,18 +464,18 @@
== ==
++ tab ++ tab
|= pos=@ud |= pos=@ud
^- (quip card state) ^- (quip card _state)
?: ?| =(~ buf.state.cli) ?: ?| =(~ buf.state.cli)
!=(';' -.buf.state.cli) !=(';' -.buf.state.cli)
== ==
:_ all-state :_ state
[(effect:sh-out [%bel ~]) ~] [(effect:sh-out [%bel ~]) ~]
:: ::
=+ (get-id:auto pos (tufa buf.state.cli)) =+ (get-id:auto pos (tufa buf.state.cli))
=/ needle=term =/ needle=term
(fall id '') (fall id '')
?: &(!=(pos 1) =(0 (met 3 needle))) ?: &(!=(pos 1) =(0 (met 3 needle)))
[~ all-state] :: autocomplete empty command iff user at start of command [~ state] :: autocomplete empty command iff user at start of command
=/ options=(list (option:auto tank)) =/ options=(list (option:auto tank))
(search-prefix:auto needle tab-list) (search-prefix:auto needle tab-list)
=/ advance=term =/ advance=term
@ -407,9 +488,9 @@
=? moves ?=(^ options) =? moves ?=(^ options)
[(tab:sh-out options) moves] [(tab:sh-out options) moves]
=| fxs=(list sole-effect:sole-sur) =| fxs=(list sole-effect:sole-sur)
|- ^- (quip card state) |- ^- (quip card _state)
?~ to-send ?~ to-send
[(flop moves) all-state] [(flop moves) state]
=^ char state.cli =^ char state.cli
(~(transmit sole-lib state.cli) [%ins send-pos `@c`i.to-send]) (~(transmit sole-lib state.cli) [%ins send-pos `@c`i.to-send])
%_ $ %_ $
@ -424,17 +505,17 @@
:: ::
++ edit ++ edit
|= cal=sole-change:sole-sur |= cal=sole-change:sole-sur
^- (quip card state) ^- (quip card _state)
=^ inv state.cli (~(transceive sole-lib state.cli) cal) =^ inv state.cli (~(transceive sole-lib state.cli) cal)
=+ fix=(sanity inv buf.state.cli) =+ fix=(sanity inv buf.state.cli)
?~ lit.fix ?~ lit.fix
[~ all-state] [~ state]
:: just capital correction :: just capital correction
?~ err.fix ?~ err.fix
(slug fix) (slug fix)
:: allow interior edits and deletes :: allow interior edits and deletes
?. &(?=($del -.inv) =(+(p.inv) (lent buf.state.cli))) ?. &(?=($del -.inv) =(+(p.inv) (lent buf.state.cli)))
[~ all-state] [~ state]
(slug fix) (slug fix)
:: +sanity: check input sanity :: +sanity: check input sanity
:: ::
@ -451,13 +532,13 @@
:: ::
++ slug ++ slug
|= [lit=(list sole-edit:sole-sur) err=(unit @u)] |= [lit=(list sole-edit:sole-sur) err=(unit @u)]
^- (quip card state) ^- (quip card _state)
?~ lit [~ all-state] ?~ lit [~ state]
=^ lic state.cli =^ lic state.cli
%- ~(transmit sole-lib state.cli) %- ~(transmit sole-lib state.cli)
^- sole-edit:sole-sur ^- sole-edit:sole-sur
?~(t.lit i.lit [%mor lit]) ?~(t.lit i.lit [%mor lit])
:_ all-state :_ state
:_ ~ :_ ~
%+ effect:sh-out %mor %+ effect:sh-out %mor
:- [%det lic] :- [%det lic]
@ -484,8 +565,8 @@
== ==
== ==
;~((glue ace) (tag %delete) path) ;~((glue ace) (tag %delete) path)
;~((glue ace) (tag %invite) rw path ships) ;~((glue ace) (tag %invite) tarx ships)
;~((glue ace) (tag %banish) rw path ships) ;~((glue ace) (tag %banish) tarx ships)
:: ::
;~ (glue ace) ;~ (glue ace)
(tag %join) (tag %join)
@ -505,6 +586,7 @@
;~((glue ace) (tag %set) flag) ;~((glue ace) (tag %set) flag)
;~((glue ace) (tag %unset) flag) ;~((glue ace) (tag %unset) flag)
;~(plug (cold %width (jest 'set width ')) dem:ag) ;~(plug (cold %width (jest 'set width ')) dem:ag)
::
;~ plug ;~ plug
(cold %timezone (jest 'set timezone ')) (cold %timezone (jest 'set timezone '))
;~ pose ;~ pose
@ -551,10 +633,20 @@
:: ::
++ tag |*(a=@tas (cold a (jest a))) ::TODO into stdlib ++ tag |*(a=@tas (cold a (jest a))) ::TODO into stdlib
++ ship ;~(pfix sig fed:ag) ++ ship ;~(pfix sig fed:ag)
++ path ;~(pfix net (most net urs:ab)) ++ path ;~(pfix net ;~(plug urs:ab (easy ~))) ::NOTE short only, tmp
:: +mang: un/managed indicator prefix
::
++ mang
;~ pose
(cold %| (jest '~/'))
(cold %& (easy ~))
==
:: +tarl: local target, as /path :: +tarl: local target, as /path
:: ::
++ tarl (stag our-self path) ++ tarl (stag our-self path)
:: +tarx: local target, maybe managed
::
++ tarx ;~(plug mang path)
:: +tarp: sponsor target, as ^/path :: +tarp: sponsor target, as ^/path
:: ::
++ tarp ++ tarp
@ -564,9 +656,15 @@
:: ::
++ targ ++ targ
;~ pose ;~ pose
tarl ;~ plug
tarp mang
;~(plug ship path) ::
;~ pose
tarl
tarp
;~(plug ship path)
==
==
(sear decode-glyph glyph) (sear decode-glyph glyph)
== ==
:: +tars: set of comma-separated targs :: +tars: set of comma-separated targs
@ -583,11 +681,7 @@
:: +security: security mode :: +security: security mode
:: ::
++ security ++ security
(perk %channel %village %journal %mailbox ~) (perk %channel %village-with-group %village ~)
:: +rw: read, write, or read-write
::
++ rw
(perk %rw %r %w ~)
:: ::
:: +glyph: shorthand character :: +glyph: shorthand character
:: ::
@ -654,16 +748,16 @@
:: the command (if any) gets echoed to the user. :: the command (if any) gets echoed to the user.
:: ::
++ obey ++ obey
^- (quip card state) ^- (quip card _state)
=+ buf=buf.state.cli =+ buf=buf.state.cli
=+ fix=(sanity [%nop ~] buf) =+ fix=(sanity [%nop ~] buf)
?^ lit.fix ?^ lit.fix
(slug fix) (slug fix)
=+ jub=(rust (tufa buf) read) =+ jub=(rust (tufa buf) read)
?~ jub [[(effect:sh-out %bel ~) ~] all-state] ?~ jub [[(effect:sh-out %bel ~) ~] state]
=^ cal state.cli (~(transmit sole-lib state.cli) [%set ~]) =^ cal state.cli (~(transmit sole-lib state.cli) [%set ~])
=^ cards all-state (work u.jub) =^ cards state (work u.jub)
:_ all-state :_ state
%+ weld %+ weld
^- (list card) ^- (list card)
:: echo commands into scrollback :: echo commands into scrollback
@ -678,7 +772,7 @@
:: ::
++ work ++ work
|= job=command |= job=command
^- (quip card state) ^- (quip card _state)
|^ ?- -.job |^ ?- -.job
%target (set-target +.job) %target (set-target +.job)
%say (say +.job) %say (say +.job)
@ -735,153 +829,145 @@
:^ %invite /chat :^ %invite /chat
(shax (jam [our-self where] who)) (shax (jam [our-self where] who))
^- invite ^- invite
=; desc=cord [our-self %chat-hook where who '']
[our-self %chat-hook where who desc]
%- crip
%+ weld
"You have been invited to chat at "
~(full tr [our-self where])
== ==
:: +set-target: set audience, update prompt :: +set-target: set audience, update prompt
:: ::
++ set-target ++ set-target
|= tars=(set target) |= tars=(set target)
^- (quip card state) ^- (quip card _state)
=. audience tars =. audience tars
[[prompt:sh-out ~] all-state] [[prompt:sh-out ~] state]
:: +create: new local mailbox :: +create: new local mailbox
:: ::
++ create ++ create
|= [security=rw-security =path gyf=(unit char) allow-history=(unit ?)] |= [security=nu-security =path gyf=(unit char) allow-history=(unit ?)]
^- (quip card state) ^- (quip card _state)
::TODO check if already exists =/ with-group=? ?=(%village-with-group security)
=/ =target [our-self path] =/ =target [with-group our-self path]
=/ real-path=^path (target-to-path target)
=/ =rw-security
?- security
%channel %channel
?(%village %village-with-group) %village
==
?^ (scry-for (unit mailbox) %chat-store [%mailbox real-path])
=- [[- ~] state]
%- print:sh-out
"{(spud path)} already exists!"
=. audience [target ~ ~] =. audience [target ~ ~]
=^ moz all-state =^ moz state
?. ?=(^ gyf) [~ all-state] ?. ?=(^ gyf) [~ state]
(bind-glyph u.gyf target) (bind-glyph u.gyf target)
=- [[- moz] all-state] =- [[- moz] state]
%^ act %do-create %chat-view %^ act %do-create %chat-view
:- %chat-view-action :- %chat-view-action
!> !> ^- chat-view-action
:* %create :* %create
path (rsh 3 1 (spat path))
security ''
:: ensure we can read from/write to our own chats real-path :: chat
:: real-path :: group
:: read rw-security
?- security ~
?(%channel %journal) ~
?(%village %mailbox) [our-self ~ ~]
==
:: write
?- security
?(%channel %mailbox) ~
?(%village %journal) [our-self ~ ~]
==
(fall allow-history %.y) (fall allow-history %.y)
== ==
:: +delete: delete local chats :: +delete: delete local chats
:: ::
++ delete ++ delete
|= =path |= =path
^- (quip card state) ^- (quip card _state)
=- [[- ~] all-state] =- [[- ~] state]
%^ act %do-delete %chat-view %^ act %do-delete %chat-view
:- %chat-view-action :- %chat-view-action
!> !> ^- chat-view-action
[%delete (target-to-path our-self path)] [%delete (target-to-path | our-self path)]
:: +change-permission: modify permissions on a local chat :: +change-permission: modify permissions on a local chat
:: ::
++ change-permission ++ change-permission
|= [allow=? rw=?(%r %w %rw) =path ships=(set ship)] |= [allow=? [group=? =path] ships=(set ship)]
^- (quip card state) ^- (quip card _state)
:_ all-state :_ state
=; cards=(list card) =/ real-path=^path
?. allow cards (target-to-path group our-self path)
%+ weld cards =; permit=(unit card)
%+ turn ~(tap in ships) %+ weld (drop permit)
(cury invite-card path) ?. allow ~
%+ murn ^- (list card)
^- (list term) %+ murn ~(tap in ships)
?- rw |= =ship
%r [%read ~] ^- (unit card)
%w [%write ~] :: if they weren't permitted before, some hook will send an invite.
%rw [%read %write ~] :: but if they already were, we want to send an invite ourselves.
== ::
|= =term ?. %^ scry-for ?
^- (unit card) %permission-store
=. path [%permitted (scot %p ship) real-path]
=- (snoc `^path`- term) ~
[%chat (target-to-path our-self path)] `(invite-card real-path ship)
:: whitelist: empty if no matching permission, else true if whitelist :: whitelist: empty if no matching permission, else true if whitelist
:: ::
=/ whitelist=(unit ?) =/ whitelist=(unit ?)
=; perm=(unit permission) =; perm=(unit permission)
?~(perm ~ `?=(%white kind.u.perm)) ?~(perm ~ `?=(%white kind.u.perm))
::TODO +permission-of-target? ::TODO +permission-of-target?
.^ (unit permission) %^ scry-for (unit permission)
%gx %permission-store
(scot %p our-self) [%permission real-path]
%permission-store
(scot %da now.bowl)
%permission
(snoc path %noun)
==
?~ whitelist ?~ whitelist
~& [%weird-no-permission path] ~& [%weird-no-permission real-path]
~ ~
%- some %- some
%^ act %do-permission %group-store %^ act %do-permission %group-store
:- %group-action :- %group-action
!> !> ^- group-action
?: =(u.whitelist allow) ?: =(u.whitelist allow)
[%add ships path] [%add ships real-path]
[%remove ships path] [%remove ships real-path]
:: +join: sync with remote mailbox :: +join: sync with remote mailbox
:: ::
++ join ++ join
|= [=target gyf=(unit char) ask-history=(unit ?)] |= [=target gyf=(unit char) ask-history=(unit ?)]
^- (quip card state) ^- (quip card _state)
=^ moz all-state =^ moz state
?. ?=(^ gyf) [~ all-state] ?. ?=(^ gyf) [~ state]
(bind-glyph u.gyf target) (bind-glyph u.gyf target)
=. audience [target ~ ~] =. audience [target ~ ~]
=; =card =; =card
[[card prompt:sh-out moz] all-state] [[card prompt:sh-out moz] state]
::TODO ideally we'd check permission first. attempting this and failing ::TODO ideally we'd check permission first. attempting this and failing
:: gives ugly %chat-hook-reap :: gives ugly %chat-hook-reap
%^ act %do-join %chat-view %^ act %do-join %chat-view
:- %chat-view-action :- %chat-view-action
!> !> ^- chat-view-action
[%join ship.target path.target (fall ask-history %.y)] [%join ship.target (target-to-path target) (fall ask-history %.y)]
:: +leave: unsync & destroy mailbox :: +leave: unsync & destroy mailbox
:: ::
::TODO allow us to "mute" local chats using this ::TODO allow us to "mute" local chats using this
++ leave ++ leave
|= =target |= =target
=- [[- ~] all-state] =- [[- ~] state]
?: =(our-self ship.target) ?: =(our-self ship.target)
%- print:sh-out %- print:sh-out
"can't ;leave local chats, maybe use ;delete instead" "can't ;leave local chats, maybe use ;delete instead"
%^ act %do-leave %chat-hook %^ act %do-leave %chat-hook
:- %chat-hook-action :- %chat-hook-action
!> !> ^- chat-hook-action
[%remove (target-to-path target)] [%remove (target-to-path target)]
:: +say: send messages :: +say: send messages
:: ::
++ say ++ say
|= =letter |= =letter
^- (quip card state) ^- (quip card _state)
~! bowl ~! bowl
=/ =serial (shaf %msg-uid eny.bowl) =/ =serial (shaf %msg-uid eny.bowl)
:_ all-state(eny (shax eny.bowl)) :_ state(eny (shax eny.bowl))
^- (list card) ^- (list card)
%+ turn ~(tap in audience) %+ turn ~(tap in audience)
|= =target |= =target
%^ act %out-message %chat-hook %^ act %out-message %chat-hook
:- %chat-action :- %chat-action
!> !> ^- chat-action
:+ %message (target-to-path target) :+ %message (target-to-path target)
[serial *@ our-self now.bowl letter] [serial *@ our-self now.bowl letter]
:: +eval: run hoon, send code and result as message :: +eval: run hoon, send code and result as message
@ -895,8 +981,8 @@
:: ::
++ lookup-glyph ++ lookup-glyph
|= qur=(unit $@(glyph target)) |= qur=(unit $@(glyph target))
^- (quip card state) ^- (quip card _state)
=- [[- ~] all-state] =- [[- ~] state]
?^ qur ?^ qur
?^ u.qur ?^ u.qur
=+ gyf=(~(get by bound) u.qur) =+ gyf=(~(get by bound) u.qur)
@ -920,8 +1006,8 @@
:: +show-settings: print enabled flags, timezone and width settings :: +show-settings: print enabled flags, timezone and width settings
:: ::
++ show-settings ++ show-settings
^- (quip card state) ^- (quip card _state)
:_ all-state :_ state
:~ %- print:sh-out :~ %- print:sh-out
%- zing %- zing
^- (list tape) ^- (list tape)
@ -941,24 +1027,24 @@
:: ::
++ set-setting ++ set-setting
|= =term |= =term
^- (quip card state) ^- (quip card _state)
[~ all-state(settings (~(put in settings) term))] [~ state(settings (~(put in settings) term))]
:: +unset-setting: disable settings flag :: +unset-setting: disable settings flag
:: ::
++ unset-setting ++ unset-setting
|= =term |= =term
^- (quip card state) ^- (quip card _state)
[~ all-state(settings (~(del in settings) term))] [~ state(settings (~(del in settings) term))]
:: +set-width: configure cli printing width :: +set-width: configure cli printing width
:: ::
++ set-width ++ set-width
|= w=@ud |= w=@ud
[~ all-state(width w)] [~ state(width w)]
:: +set-timezone: configure timestamp printing adjustment :: +set-timezone: configure timestamp printing adjustment
:: ::
++ set-timezone ++ set-timezone
|= tz=[? @ud] |= tz=[? @ud]
[~ all-state(timez tz)] [~ state(timez tz)]
:: +select: expand message from number reference :: +select: expand message from number reference
:: ::
++ select ++ select
@ -967,7 +1053,7 @@
:: (with leading zeros used for precision) :: (with leading zeros used for precision)
:: ::
|= num=$@(rel=@ud [zeros=@u abs=@ud]) |= num=$@(rel=@ud [zeros=@u abs=@ud])
^- (quip card state) ^- (quip card _state)
|^ ?@ num |^ ?@ num
=+ tum=(scow %s (new:si | +(num))) =+ tum=(scow %s (new:si | +(num)))
?: (gte rel.num count) ?: (gte rel.num count)
@ -985,7 +1071,7 @@
:: ::
++ just-print ++ just-print
|= txt=tape |= txt=tape
[[(print:sh-out txt) ~] all-state] [[(print:sh-out txt) ~] state]
:: +index: get message index from absolute reference :: +index: get message index from absolute reference
:: ::
++ index ++ index
@ -999,10 +1085,10 @@
:: ::
++ activate ++ activate
|= [number=tape index=@ud] |= [number=tape index=@ud]
^- (quip card state) ^- (quip card _state)
=+ gam=(snag index grams) =+ gam=(snag index grams)
=. audience [source.gam ~ ~] =. audience [source.gam ~ ~]
:_ all-state :_ state
^- (list card) ^- (list card)
:~ (print:sh-out ['?' ' ' number]) :~ (print:sh-out ['?' ' ' number])
(effect:sh-out ~(render-activate mr gam)) (effect:sh-out ~(render-activate mr gam))
@ -1012,17 +1098,14 @@
:: +chats: display list of local mailboxes :: +chats: display list of local mailboxes
:: ::
++ chats ++ chats
^- (quip card state) ^- (quip card _state)
:_ all-state :_ state
:_ ~ :_ ~
%- print-more:sh-out %- print-more:sh-out
=/ all =/ all
::TODO refactor %^ scry-for (set path)
::TODO remote scries fail... but moon support? %chat-store
.^ (set path) /keys
%gx
/(scot %p our-self)/chat-store/(scot %da now.bowl)/keys/noun
==
%+ turn ~(tap in all) %+ turn ~(tap in all)
%+ cork path-to-target %+ cork path-to-target
|= target |= target
@ -1030,8 +1113,8 @@
:: +help: print (link to) usage instructions :: +help: print (link to) usage instructions
:: ::
++ help ++ help
^- (quip card state) ^- (quip card _state)
=- [[- ~] all-state] =- [[- ~] state]
(print:sh-out "see https://urbit.org/using/operations/using-your-ship/#messaging") (print:sh-out "see https://urbit.org/using/operations/using-your-ship/#messaging")
-- --
-- --
@ -1148,6 +1231,14 @@
%+ weld "set: {[glyph ~]} " %+ weld "set: {[glyph ~]} "
?~ target "unbound" ?~ target "unbound"
~(phat tr u.target) ~(phat tr u.target)
:: +show-invite: print incoming invite notification
::
++ show-invite
|= invite
^- card
%- note
%+ weld "invited to: "
~(phat tr (path-to-target path))
-- --
:: ::
:: +tr: render targets :: +tr: render targets
@ -1181,17 +1272,21 @@
:: ::
++ full ++ full
^- tape ^- tape
(weld (scow %p ship.one) (spud path.one)) ;: weld
?:(in-group.one "" "~/")
(scow %p ship.one)
(spud path.one)
==
:: +phat: render target with local shorthand :: +phat: render target with local shorthand
:: ::
:: renders as ~ship/path. :: renders as ~ship/path.
:: for local mailboxes, renders just /path. :: for local mailboxes, renders just /path.
:: for sponsor's mailboxes, renders ^/path. :: for sponsor's mailboxes, renders ^/path.
:: ::
::NOTE but, given current implementation, all will be local
::
++ phat ++ phat
^- tape ^- tape
%+ weld
?:(in-group.one "" "~/")
%+ weld %+ weld
?: =(our-self ship.one) ~ ?: =(our-self ship.one) ~
?: =((sein:title our.bowl now.bowl our-self) ship.one) "^" ?: =((sein:title our.bowl now.bowl our-self) ship.one) "^"
@ -1396,4 +1491,16 @@
[(sub wid u.ace) &] [(sub wid u.ace) &]
:- (tufa (scag end `(list @)`txt)) :- (tufa (scag end `(list @)`txt))
$(txt (slag ?:(nex +(end) end) `tape`txt)) $(txt (slag ?:(nex +(end) end) `tape`txt))
::
::NOTE anything that uses this breaks moons support, because moons don't sync
:: full app state rn
++ scry-for
|* [=mold app=term =path]
.^ mold
%gx
(scot %p our.bowl)
app
(scot %da now.bowl)
(snoc `^path`path %noun)
==
-- --

View File

@ -2,18 +2,26 @@
:: mirror chat data from foreign to local based on read permissions :: mirror chat data from foreign to local based on read permissions
:: allow sending chat messages to foreign paths based on write perms :: allow sending chat messages to foreign paths based on write perms
:: ::
/- *permission-store, *chat-hook, *invite-store /- *permission-store, *chat-hook, *invite-store, *metadata-store,
/+ *chat-json, default-agent, verb, dbug *permission-hook, *group-store, *permission-group-hook ::TMP for upgrade
/+ *chat-json, *chat-eval, default-agent, verb, dbug
~% %chat-hook-top ..is ~
|% |%
+$ card card:agent:gall +$ card card:agent:gall
:: ::
+$ versioned-state +$ versioned-state
$% state-zero $% state-0
state-1
== ==
:: ::
+$ state-zero +$ state-1
$: %0 $: %1
synced=(map path ship) loaded-cards=(list card)
state-base
==
+$ state-0 [%0 state-base]
+$ state-base
$: =synced
invite-created=_| invite-created=_|
allow-history=(map path ?) allow-history=(map path ?)
== ==
@ -29,13 +37,14 @@
$% [%chat-update chat-update] $% [%chat-update chat-update]
== ==
-- --
=| state-zero =| state-1
=* state - =* state -
:: ::
%- agent:dbug %- agent:dbug
%+ verb | %+ verb |
^- agent:gall ^- agent:gall
=< =<
~% %chat-hook-agent-core ..poke-json ~
|_ bol=bowl:gall |_ bol=bowl:gall
+* this . +* this .
chat-core +> chat-core +>
@ -51,29 +60,199 @@
== ==
++ on-save !>(state) ++ on-save !>(state)
++ on-load ++ on-load
|= old=vase |= old-vase=vase
`this(state !<(state-zero old)) ^- (quip card _this)
|^
=/ old !<(versioned-state old-vase)
?: ?=(%1 -.old)
:_ this(state old)
%+ murn ~(tap by wex.bol)
|= [[=wire =ship =term] *]
^- (unit card)
?. &(?=([%mailbox *] wire) =(our.bol ship) =(%chat-store term))
~
`[%pass wire %agent [our.bol %chat-store] %leave ~]
:: path structure ugprade logic
::
=/ keys=(set path) (scry:cc (set path) %chat-store /keys)
=/ upgraded-state
%* . *state-1
synced synced
invite-created invite-created
allow-history allow-history
loaded-cards
%- zing
^- (list (list card))
%+ turn ~(tap in keys) generate-cards
==
[loaded-cards.upgraded-state this(state upgraded-state)]
::
++ generate-cards
|= old-chat=path
^- (list card)
=/ host=ship (slav %p (snag 0 old-chat))
=/ new-chat [%'~' old-chat]
=/ newp=permission (unify-permissions old-chat)
=/ old-group=path [%chat old-chat]
%- zing
:~ :~ (delete-group host (snoc old-group %read))
(delete-group host (snoc old-group %write))
==
::
(create-group new-chat who.newp)
(hookup-group new-chat kind.newp)
[(record-group new-chat new-chat)]~
(recreate-chat host old-chat new-chat)
==
::
++ recreate-chat
|= [host=ship chat=path new-chat=path]
^- (list card)
=/ old-mailbox=mailbox
(need (scry:cc (unit mailbox) %chat-store [%mailbox chat]))
=* enves envelopes.old-mailbox
:~ (chat-poke:cc [%delete new-chat])
(chat-poke:cc [%delete chat])
(chat-poke:cc [%create new-chat])
(chat-poke:cc [%messages new-chat enves])
(chat-poke:cc [%read new-chat])
%^ make-poke %chat-hook %chat-hook-action
!> ^- chat-hook-action
?: =(our.bol host) [%add-owned new-chat %.y]
[%add-synced host new-chat %.y]
==
::
++ unify-permissions
|= chat=path
^- permission
=/ read=(unit permission) (get-permission chat %read)
=/ write=(unit permission) (get-permission chat %write)
?. &(?=(^ read) ?=(^ write))
~& [%missing-permission chat read=?=(~ read) write=?=(~ write)]
[%white [(slav %p (snag 0 chat)) ~ ~]]
?+ [kind.u.read kind.u.write] !!
:: village: exclusive to writers
::
[%white %white] [%white who.u.write]
::
:: channel: merge blacklists
::
[%black %black] [%black (~(uni in who.u.read) who.u.write)]
::
:: journal: exclusive to writers
::
[%black %white] [%white who.u.write]
::
:: mailbox: exclusive to readers
::
[%white %black] [%white who.u.read]
==
::
++ get-permission
|= [chat=path what=?(%read %write)]
%^ scry:cc (unit permission)
%permission-store
[%permission %chat (snoc chat what)]
::
++ make-poke
|= [app=term =mark =vase]
^- card
[%pass /on-load/[app]/[mark] %agent [our.bol app] %poke mark vase]
::
++ delete-group
|= [host=ship group=path]
^- card
:: if we host the group, delete it directly
::
?: =(our.bol host)
%^ make-poke %group-store
%group-action
!> ^- group-action
[%unbundle group]
:: else, just delete the sync in the hook
::
%^ make-poke %permission-hook
%permission-hook-action
!> ^- permission-hook-action
[%remove group]
::
++ create-group
|= [group=path who=(set ship)]
^- (list card)
:~ %^ make-poke %group-store
%group-action
!> ^- group-action
[%bundle group]
::
%^ make-poke %group-store
%group-action
!> ^- group-action
[%add who group]
==
::
++ hookup-group
|= [group=path =kind]
^- (list card)
:* %^ make-poke %permission-group-hook
%permission-group-hook-action
!> ^- permission-group-hook-action
[%associate group [group^kind ~ ~]]
::
=/ =ship (slav %p (snag 1 group))
?. =(our.bol ship) ~
:_ ~
%^ make-poke %permission-hook
%permission-hook-action
!> ^- permission-hook-action
[%add-owned group group]
==
::
++ record-group
|= [group=path chat=path]
^- card
=/ =metadata
~| [%weird-chat-path chat]
%* . *metadata
title (snag 2 chat)
date-created now.bol
creator (slav %p (snag 1 chat))
==
%^ make-poke %metadata-store
%metadata-action
!> ^- metadata-action
[%add group [%chat chat] metadata]
--
:: ::
++ on-poke ++ on-poke
~/ %chat-hook-poke
|= [=mark =vase] |= [=mark =vase]
^- (quip card _this) ^- (quip card _this)
=^ cards state =^ cards state
?+ mark (on-poke:def mark vase) ?+ mark (on-poke:def mark vase)
%json (poke-json:cc !<(json vase)) %json (poke-json:cc !<(json vase))
%chat-action (poke-chat-action:cc !<(chat-action vase)) %chat-action (poke-chat-action:cc !<(chat-action vase))
%chat-hook-action (poke-chat-hook-action:cc !<(chat-hook-action vase)) %noun
?: =(%store-load q.vase)
[loaded-cards.state state(loaded-cards ~)]
[~ state]
::
%chat-hook-action
(poke-chat-hook-action:cc !<(chat-hook-action vase))
== ==
[cards this] [cards this]
:: ::
++ on-watch ++ on-watch
~/ %chat-hook-watch
|= =path |= =path
^- (quip card _this) ^- (quip card _this)
?+ path (on-watch:def path) ?+ path (on-watch:def path)
[%backlog *] [(watch-backlog:cc t.path) this] [%backlog *] [(watch-backlog:cc t.path) this]
[%mailbox *] [(watch-mailbox:cc t.path) this] [%mailbox *] [(watch-mailbox:cc t.path) this]
[%synced *] [(watch-synced:cc t.path) this]
== ==
:: ::
++ on-agent ++ on-agent
~/ %chat-hook-agent
|= [=wire =sign:agent:gall] |= [=wire =sign:agent:gall]
^- (quip card _this) ^- (quip card _this)
?+ -.sign (on-agent:def wire sign) ?+ -.sign (on-agent:def wire sign)
@ -113,6 +292,7 @@
-- --
:: ::
:: ::
~% %chat-hook-library ..card ~
|_ bol=bowl:gall |_ bol=bowl:gall
:: ::
++ poke-json ++ poke-json
@ -129,18 +309,19 @@
?: (team:title our.bol src.bol) ?: (team:title our.bol src.bol)
?. (~(has by synced) path.act) ?. (~(has by synced) path.act)
~ ~
=* letter letter.envelope.act
=? letter &(?=(%code -.letter) ?=(~ output.letter))
=/ =hoon (ream expression.letter)
letter(output (eval bol hoon))
=/ ship (~(got by synced) path.act) =/ ship (~(got by synced) path.act)
=/ appl ?:(=(ship our.bol) %chat-store %chat-hook) =/ appl ?:(=(ship our.bol) %chat-store %chat-hook)
[%pass / %agent [ship appl] %poke %chat-action !>(act)]~ [%pass / %agent [ship appl] %poke %chat-action !>(act)]~
:: foreign :: foreign
=/ ship (~(get by synced) path.act) =/ ship (~(get by synced) path.act)
?~ ship ?~ ship ~
~ ?. =(u.ship our.bol) ~
?. =(u.ship our.bol) :: check if write is permitted
~ ?. (is-permitted src.bol path.act) ~
:: scry permissions to check if write is permitted
?. (permitted-scry [(scot %p src.bol) %chat (weld path.act /write)])
~
=: author.envelope.act src.bol =: author.envelope.act src.bol
when.envelope.act now.bol when.envelope.act now.bol
== ==
@ -153,158 +334,154 @@
%add-owned %add-owned
?> (team:title our.bol src.bol) ?> (team:title our.bol src.bol)
=/ chat-path [%mailbox path.act] =/ chat-path [%mailbox path.act]
?: (~(has by synced) path.act) =/ chat-wire [%store path.act]
[~ state] ?: (~(has by synced) path.act) [~ state]
=: synced (~(put by synced) path.act our.bol) =: synced (~(put by synced) path.act our.bol)
allow-history (~(put by allow-history) path.act allow-history.act) allow-history (~(put by allow-history) path.act allow-history.act)
== ==
:_ state :_ state
%+ weld :~ [%pass chat-wire %agent [our.bol %chat-store] %watch chat-path]
[%pass chat-path %agent [our.bol %chat-store] %watch chat-path]~ [%give %fact [/synced]~ %chat-hook-update !>([%initial synced])]
(create-permission [%chat path.act] security.act) ==
:: ::
%add-synced %add-synced
?> (team:title our.bol src.bol) ?> (team:title our.bol src.bol)
?: (~(has by synced) [(scot %p ship.act) path.act]) ?: (~(has by synced) path.act) [~ state]
[~ state] =. synced (~(put by synced) path.act ship.act)
=. synced (~(put by synced) [(scot %p ship.act) path.act] ship.act)
?. ask-history.act ?. ask-history.act
=/ chat-path [%mailbox (scot %p ship.act) path.act] =/ chat-path [%mailbox path.act]
:_ state :_ state
[%pass chat-path %agent [ship.act %chat-hook] %watch chat-path]~ [%pass chat-path %agent [ship.act %chat-hook] %watch chat-path]~
:: TODO: only ask for backlog from previous point =/ mailbox=(unit mailbox) (chat-scry path.act)
=/ chat-history [%backlog (scot %p ship.act) (weld path.act /0)] =/ chat-history=path
:- %backlog
%+ weld path.act
?~(mailbox /0 /(scot %ud (lent envelopes.u.mailbox)))
:_ state :_ state
[%pass chat-history %agent [ship.act %chat-hook] %watch chat-history]~ :~ [%pass chat-history %agent [ship.act %chat-hook] %watch chat-history]
[%give %fact [/synced]~ %chat-hook-update !>([%initial synced])]
==
:: ::
%remove %remove
=/ ship (~(get by synced) path.act) =/ ship=(unit ship)
=/ ship (~(get by synced) path.act)
?^ ship ship
=? path.act ?=([%'~' *] path.act) t.path.act
?~ path.act ~
(slaw %p i.path.act)
?~ ship ?~ ship
~& [dap.bol %unknown-host-cannot-leave path.act]
[~ state] [~ state]
?: &(=(u.ship our.bol) (team:title our.bol src.bol)) ?: &(!=(u.ship src.bol) ?!((team:title our.bol src.bol)))
:: delete one of our.bol own paths
:_ state(synced (~(del by synced) path.act))
%- zing
:~ (pull-wire [%backlog (weld path.act /0)])
(pull-wire [%mailbox path.act])
(delete-permission [%chat path.act])
[%give %kick [%mailbox path.act]~ ~]~
==
?. |(=(u.ship src.bol) (team:title our.bol src.bol))
:: if neither ship = source or source = us, do nothing
[~ state] [~ state]
:: delete a foreign ship's path =. synced (~(del by synced) path.act)
:- (pull-wire [%mailbox path.act]) :_ state
state(synced (~(del by synced) path.act)) :* [%give %kick ~[[%mailbox path.act]] ~]
[%give %fact [/synced]~ %chat-hook-update !>([%initial synced])]
(pull-wire u.ship [%mailbox path.act])
(pull-backlog-subscriptions u.ship path.act)
==
== ==
:: ::
++ watch-synced
|= pax=path
^- (list card)
?> (team:title our.bol src.bol)
[%give %fact ~ %chat-hook-update !>([%initial synced])]~
::
++ watch-mailbox ++ watch-mailbox
|= pax=path |= pax=path
^- (list card) ^- (list card)
?> ?=(^ pax) ?> ?=(^ pax)
?> (~(has by synced) pax) ?> (~(has by synced) pax)
:: scry permissions to check if read is permitted :: check if read is permitted
?> (permitted-scry [(scot %p src.bol) %chat (weld pax /read)]) ?> (is-permitted src.bol pax)
=/ box (chat-scry pax) =/ box (chat-scry pax)
?~ box !! ?~ box !!
[%give %fact ~ %chat-update !>([%create (slav %p i.pax) pax])]~ [%give %fact ~ %chat-update !>([%create pax])]~
:: ::
++ watch-backlog ++ watch-backlog
|= pax=path |= pax=path
^- (list card) ^- (list card)
?> ?=(^ pax) ?> ?=(^ pax)
=/ last (dec (lent pax)) =/ last (dec (lent pax))
=/ backlog-start=(unit @ud) =/ backlog-latest=(unit @ud) (rush (snag last `(list @ta)`pax) dem:ag)
%+ rush
(snag last `(list @ta)`pax)
dem:ag
=/ pas `path`(oust [last 1] `(list @ta)`pax) =/ pas `path`(oust [last 1] `(list @ta)`pax)
?> ?=([* ^] pas) ?> ?=([* ^] pas)
?> (~(has by synced) pas) ?> (~(has by synced) pas)
:: scry permissions to check if read is permitted ?> (is-permitted src.bol pas)
?> (permitted-scry [(scot %p src.bol) %chat (weld pas /read)]) =/ envs envelopes:(need (chat-scry pas))
=/ box (chat-scry pas) =/ length (lent envs)
?~ box !! =/ latest
:- [%give %fact ~ %chat-update !>([%create (slav %p i.pas) pas])] ?~ backlog-latest length
?: (gth u.backlog-latest length) length
(sub length u.backlog-latest)
=. envs (scag latest envs)
=/ =vase !>([%messages pas 0 latest envs])
%- zing %- zing
:~ :~ [%give %fact ~ %chat-update !>([%create pas])]~
?: ?&(?=(^ backlog-start) (~(got by allow-history) pas)) ?. ?&(?=(^ backlog-latest) (~(has by allow-history) pas)) ~
(paginate-messages pas u.box u.backlog-start) [%give %fact ~ %chat-update vase]~
~ [%give %kick [%backlog pax]~ `src.bol]~
[%give %kick [%backlog pax]~ `src.bol]~
== ==
:: ::
++ paginate-messages
|= [=path =mailbox start=@ud]
^- (list card)
=/ cards=(list card) ~
=/ end (lent envelopes.mailbox)
?: |((gte start end) =(end 0))
cards
=. envelopes.mailbox (slag start `(list envelope)`envelopes.mailbox)
|- ^- (list card)
?~ envelopes.mailbox
cards
?: (lte end 5.000)
=. cards
%+ snoc cards
%- messages-fact
[path start (lent envelopes.mailbox) envelopes.mailbox]
$(envelopes.mailbox ~)
=. cards
%+ snoc cards
%- messages-fact
:^ path start
(add start 5.000)
(scag 5.000 `(list envelope)`envelopes.mailbox)
=: start (add start 5.000)
end (sub end 5.000)
==
$(envelopes.mailbox (slag 5.000 `(list envelope)`envelopes.mailbox))
::
++ fact-invite-update ++ fact-invite-update
|= [wir=wire fact=invite-update] |= [wir=wire fact=invite-update]
^- (quip card _state) ^- (quip card _state)
?+ -.fact :_ state
[~ state] ?+ -.fact ~
::
%accepted %accepted
=/ ask-history =/ ask-history ?~((chat-scry path.invite.fact) %.y %.n)
?~ (chat-scry [(scot %p ship.invite.fact) path.invite.fact]) =* shp ship.invite.fact
%.y =* app-path path.invite.fact
%.n ~[(chat-view-poke [%join shp app-path ask-history])]
:_ state ==
[(chat-view-poke [%join ship.invite.fact path.invite.fact ask-history])]~
==
:: ::
++ fact-permission-update ++ fact-permission-update
|= [wir=wire fact=permission-update] |= [wir=wire fact=permission-update]
^- (quip card _state) ^- (quip card _state)
|^
:_ state :_ state
?- -.fact ?+ -.fact ~
%create ~
%delete ~
%add (handle-permissions [%add path.fact who.fact]) %add (handle-permissions [%add path.fact who.fact])
%remove (handle-permissions [%remove path.fact who.fact]) %remove (handle-permissions [%remove path.fact who.fact])
== ==
:: ::
++ handle-permissions ++ handle-permissions
|= [kind=?(%add %remove) pax=path who=(set ship)] |= [kind=?(%add %remove) pax=path who=(set ship)]
^- (list card) ^- (list card)
?> ?=([* *] pax) %- zing
?. =(%chat i.pax) ~ %+ turn
:: check path to see if this is a %read permission (chats-of-group pax)
?. =(%read (snag (dec (lent pax)) `(list @t)`pax)) |= chat=path
~ ^- (list card)
%- zing =/ owner (~(get by synced) chat)
%+ turn ~(tap in who) ?~ owner ~
|= =ship ?. =(u.owner our.bol) ~
?: (permitted-scry [(scot %p ship) pax]) %- zing
~ %+ turn ~(tap in who)
:: if ship is not permitted, kick their subscription |= =ship
=/ mail-path ?: (is-permitted ship chat)
(oust [(dec (lent t.pax)) (lent t.pax)] `(list @t)`t.pax) ?: ?|(=(kind %remove) =(ship our.bol) (is-managed pax)) ~
[%give %kick [%mailbox mail-path]~ `ship]~ :: if ship has just been added to the permitted group,
:: send them an invite
~[(send-invite chat ship)]
:: if ship is not permitted, kick their subscription
[%give %kick [%mailbox chat]~ `ship]~
::
++ send-invite
|= [=path =ship]
^- card
=/ =invite [our.bol %chat-hook path ship '']
=/ act=invite-action [%invite /chat (shaf %msg-uid eny.bol) invite]
[%pass / %agent [our.bol %invite-hook] %poke %invite-action !>(act)]
::
++ is-managed
|= =path
^- ?
?> ?=(^ path)
!=(i.path '~')
--
:: ::
++ fact-chat-update ++ fact-chat-update
|= [wir=wire fact=chat-update] |= [wir=wire fact=chat-update]
@ -316,16 +493,14 @@
++ handle-local ++ handle-local
|= fact=chat-update |= fact=chat-update
^- (quip card _state) ^- (quip card _state)
?- -.fact ?+ -.fact [~ state]
%keys [~ state]
%read [~ state]
%config [~ state]
%create [~ state]
%delete %delete
?. (~(has by synced) path.fact) ?. (~(has by synced) path.fact) [~ state]
[~ state] =. synced (~(del by synced) path.fact)
:_ state(synced (~(del by synced) path.fact)) :_ state
[%pass [%mailbox path.fact] %agent [our.bol %chat-store] %leave ~]~ :~ [%pass [%mailbox path.fact] %agent [our.bol %chat-store] %leave ~]
[%give %fact [/synced]~ %chat-hook-update !>([%initial synced])]
==
:: ::
%message %message
:_ state :_ state
@ -339,28 +514,26 @@
++ handle-foreign ++ handle-foreign
|= fact=chat-update |= fact=chat-update
^- (quip card _state) ^- (quip card _state)
?- -.fact ?+ -.fact [~ state]
%keys [~ state]
%read [~ state]
%config [~ state]
%create %create
:_ state :_ state
?> ?=([* ^] path.fact) ?> ?=([* ^] path.fact)
=/ shp (~(get by synced) path.fact) =/ shp (~(get by synced) path.fact)
?~ shp ~ ?~ shp ~
?. =(src.bol u.shp) ~ ?. =(src.bol u.shp) ~
[(chat-poke [%create ship.fact t.path.fact])]~ [(chat-poke [%create path.fact])]~
:: ::
%delete %delete
?> ?=([* ^] path.fact) ?> ?=([* ^] path.fact)
=/ shp (~(get by synced) path.fact) =/ shp (~(get by synced) path.fact)
?~ shp ?~ shp [~ state]
[~ state] ?. =(u.shp src.bol) [~ state]
?. =(u.shp src.bol) =. synced (~(del by synced) path.fact)
[~ state] :_ state
:_ state(synced (~(del by synced) path.fact))
:- (chat-poke [%delete path.fact]) :- (chat-poke [%delete path.fact])
[%pass [%mailbox path.fact] %agent [src.bol %chat-hook] %leave ~]~ :~ [%pass [%mailbox path.fact] %agent [src.bol %chat-hook] %leave ~]
[%give %fact [/synced]~ %chat-hook-update !>([%initial synced])]
==
:: ::
%message %message
:_ state :_ state
@ -386,43 +559,56 @@
:_ state :_ state
[%pass /permissions %agent [our.bol %permission-store] %watch /updates]~ [%pass /permissions %agent [our.bol %permission-store] %watch /updates]~
:: ::
?: ?=([%mailbox @ *] wir) ?+ wir !!
[%store @ *]
~& store-kick+wir
?. (~(has by synced) t.wir) [~ state]
~& %chat-store-resubscribe
=/ mailbox=(unit mailbox) (chat-scry t.wir)
:_ state
[%pass wir %agent [our.bol %chat-store] %watch [%mailbox t.wir]]~
::
[%mailbox @ *]
~& mailbox-kick+wir ~& mailbox-kick+wir
?. (~(has by synced) t.wir) ?. (~(has by synced) t.wir) [~ state]
:: no-op
[~ state]
~& %chat-hook-resubscribe ~& %chat-hook-resubscribe
=/ =ship (~(got by synced) t.wir) =/ =ship (~(got by synced) t.wir)
=/ mailbox=(unit mailbox) (chat-scry t.wir) =/ mailbox=(unit mailbox) (chat-scry t.wir)
=/ chat-history =/ chat-history
%+ welp backlog+t.wir %+ welp backlog+t.wir
?~ mailbox ?~(mailbox /0 /(scot %ud (lent envelopes.u.mailbox)))
/0
/(scot %ud (lent envelopes.u.mailbox))
:_ state :_ state
[%pass chat-history %agent [ship %chat-hook] %watch chat-history]~ [%pass chat-history %agent [ship %chat-hook] %watch chat-history]~
:: ::
?: ?=([%backlog @ *] wir) [%backlog @ @ *]
=/ pax `path`(oust [(dec (lent t.wir)) 1] `(list @ta)`t.wir) =/ chat=path (oust [(dec (lent t.wir)) 1] `(list @ta)`t.wir)
?. (~(has by synced) pax) [~ state] ?. (~(has by synced) chat) [~ state]
=/ mailbox=(unit mailbox) (chat-scry pax) =/ =ship
=. pax ?~(mailbox wir [%mailbox pax]) ?: =('~' i.t.wir)
(slav %p i.t.t.wir)
(slav %p i.t.wir)
=/ =path ?~((chat-scry chat) wir [%mailbox chat])
:_ state :_ state
[%pass pax %agent [(slav %p i.t.wir) %chat-hook] %watch pax]~ [%pass path %agent [ship %chat-hook] %watch path]~
!! ==
:: ::
++ watch-ack ++ watch-ack
|= [wir=wire saw=(unit tang)] |= [wir=wire saw=(unit tang)]
^- (quip card _state) ^- (quip card _state)
?~ saw ?~ saw [~ state]
[~ state] ?+ wir [~ state]
?> ?=(^ wir) [%store @ *]
:_ state(synced (~(del by synced) t.wir)) (poke-chat-hook-action %remove t.wir)
%. ~ ::
%- slog [%backlog @ @ @ *]
:* leaf+"chat-hook failed subscribe on {(spud t.wir)}" =/ chat=path (oust [(dec (lent t.wir)) 1] `(list @ta)`t.wir)
leaf+"stack trace:" :_ state
u.saw %. ~[(chat-view-poke %delete chat)]
%- slog
:* leaf+"chat-hook failed subscribe on {(spud chat)}"
leaf+"stack trace:"
u.saw
==
== ==
:: ::
++ chat-poke ++ chat-poke
@ -435,57 +621,11 @@
^- card ^- card
[%pass / %agent [our.bol %chat-view] %poke %chat-view-action !>(act)] [%pass / %agent [our.bol %chat-view] %poke %chat-view-action !>(act)]
:: ::
++ permission-poke
|= act=permission-action
^- card
[%pass / %agent [our.bol %permission-store] %poke %permission-action !>(act)]
::
++ invite-poke ++ invite-poke
|= act=invite-action |= act=invite-action
^- card ^- card
[%pass / %agent [our.bol %invite-store] %poke %invite-action !>(act)] [%pass / %agent [our.bol %invite-store] %poke %invite-action !>(act)]
:: ::
++ messages-fact
|= [=path start=@ud end=@ud envelopes=(list envelope)]
^- card
[%give %fact ~ %chat-update !>([%messages path start end envelopes])]
::
++ create-permission
|= [pax=path sec=rw-security]
^- (list card)
=/ read-perm (weld pax /read)
=/ write-perm (weld pax /write)
?- sec
%channel
:~ (permission-poke (sec-to-perm read-perm %black))
(permission-poke (sec-to-perm write-perm %black))
==
::
%village
:~ (permission-poke (sec-to-perm read-perm %white))
(permission-poke (sec-to-perm write-perm %white))
==
::
%journal
:~ (permission-poke (sec-to-perm read-perm %black))
(permission-poke (sec-to-perm write-perm %white))
==
::
%mailbox
:~ (permission-poke (sec-to-perm read-perm %white))
(permission-poke (sec-to-perm write-perm %black))
==
==
::
++ delete-permission
|= pax=path
^- (list card)
=/ read-perm (weld pax /read)
=/ write-perm (weld pax /write)
:~ (permission-poke [%delete read-perm])
(permission-poke [%delete write-perm])
==
::
++ sec-to-perm ++ sec-to-perm
|= [pax=path =kind] |= [pax=path =kind]
^- permission-action ^- permission-action
@ -494,27 +634,104 @@
++ chat-scry ++ chat-scry
|= pax=path |= pax=path
^- (unit mailbox) ^- (unit mailbox)
=. pax ;:(weld /=chat-store/(scot %da now.bol)/mailbox pax /noun) %^ scry (unit mailbox)
.^((unit mailbox) %gx pax) %chat-store
[%mailbox pax]
:: ::
++ invite-scry ++ invite-scry
|= uid=serial |= uid=serial
^- (unit invite) ^- (unit invite)
=/ pax /=invite-store/(scot %da now.bol)/invite/chat/(scot %uv uid)/noun %^ scry (unit invite)
.^((unit invite) %gx pax) %invite-store
/invite/chat/(scot %uv uid)
:: ::
++ permitted-scry ++ chats-of-group
|= pax=path |= =group-path
^- (list path)
:: if metadata-store isn't running yet, we're still in the upgrade ota phase.
:: we can't get chats from the metadata-store, but can make assumptions
:: about group path shape, and the chat that would match it.
::TODO remove me at some point.
::
?. .^(? %gu (scot %p our.bol) %metadata-store (scot %da now.bol) ~) ~
%+ murn
^- (list resource)
=; resources
%~ tap in
%+ ~(gut by resources)
group-path
*(set resource)
.^ (jug path resource)
%gy
(scot %p our.bol)
%metadata-store
(scot %da now.bol)
/group-indices
==
|= resource
^- (unit path)
?. =(%chat app-name) ~
`app-path
::
++ groups-of-chat
|= chat=path
^- (list group-path)
:: if metadata-store isn't running yet, we're still in the upgrade ota phase.
:: we can't get groups from the metadata-store, but can make assumptions
:: about chat path shape, and the chat that would match it.
::TODO remove me at some point.
::
?. .^(? %gu (scot %p our.bol) %metadata-store (scot %da now.bol) ~) ~
=; resources
%~ tap in
%+ ~(gut by resources)
[%chat chat]
*(set group-path)
.^ (jug resource group-path)
%gy
(scot %p our.bol)
%metadata-store
(scot %da now.bol)
/resource-indices
==
::
::NOTE this assumes permission paths match group paths
++ is-permitted
|= [who=ship chat=path]
^- ? ^- ?
.^(? %gx ;:(weld /=permission-store/(scot %da now.bol)/permitted pax /noun)) %+ lien (groups-of-chat chat)
|= =group-path
%^ scry ?
%permission-store
[%permitted (scot %p who) group-path]
::
++ scry
|* [=mold app=term =path]
.^ mold
%gx
(scot %p our.bol)
app
(scot %da now.bol)
(snoc `^path`path %noun)
==
::
++ pull-backlog-subscriptions
|= [target=ship chat=path]
^- (list card)
%+ murn ~(tap by wex.bol)
|= [[=wire =ship =term] [acked=? =path]]
^- (unit card)
?. ?& =(ship target)
?=([%backlog *] wire)
=(`1 (find chat wire))
==
~
`(pull-wire target wire)
:: ::
++ pull-wire ++ pull-wire
|= pax=path |= [=ship =wire]
^- (list card) ^- card
?> ?=(^ pax) ?: =(ship our.bol)
=/ shp (~(get by synced) t.pax) [%pass wire %agent [our.bol %chat-store] %leave ~]
?~ shp ~ [%pass wire %agent [ship %chat-hook] %leave ~]
?: =(u.shp our.bol)
[%pass pax %agent [our.bol %chat-store] %leave ~]~
[%pass pax %agent [u.shp %chat-hook] %leave ~]~
-- --

View File

@ -1,16 +1,18 @@
:: chat-store: data store that holds linear sequences of chat messages :: chat-store: data store that holds linear sequences of chat messages
:: ::
/+ *chat-json, *chat-eval, default-agent, verb, dbug /+ *chat-json, *chat-eval, default-agent, verb, dbug
~% %chat-store-top ..is ~
|% |%
+$ card card:agent:gall +$ card card:agent:gall
+$ versioned-state +$ versioned-state
$% state-zero $% state-zero
state-one
state-two
== ==
:: ::
+$ state-zero +$ state-zero [%0 =inbox]
$: %0 +$ state-one [%1 =inbox]
=inbox +$ state-two [%2 =inbox]
==
:: ::
+$ diff +$ diff
$% [%chat-initial inbox] $% [%chat-initial inbox]
@ -19,13 +21,14 @@
== ==
-- --
:: ::
=| state-zero =| state-two
=* state - =* state -
:: ::
%- agent:dbug %- agent:dbug
%+ verb | %+ verb |
^- agent:gall ^- agent:gall
=< =<
~% %chat-store-agent-core ..peek-x-envelopes ~
|_ =bowl:gall |_ =bowl:gall
+* this . +* this .
chat-core +> chat-core +>
@ -35,10 +38,19 @@
++ on-init on-init:def ++ on-init on-init:def
++ on-save !>(state) ++ on-save !>(state)
++ on-load ++ on-load
|= old=vase |= old-vase=vase
`this(state !<(state-zero old)) =/ old !<(versioned-state old-vase)
?: ?=(%2 -.old)
[~ this(state old)]
=/ reversed-inbox=^inbox
%- ~(run by inbox.old)
|= =mailbox
^- ^mailbox
[config.mailbox (flop envelopes.mailbox)]
[~ this(state [%2 reversed-inbox])]
:: ::
++ on-poke ++ on-poke
~/ %chat-store-poke
|= [=mark =vase] |= [=mark =vase]
^- (quip card _this) ^- (quip card _this)
?> (team:title our.bowl src.bowl) ?> (team:title our.bowl src.bowl)
@ -50,10 +62,11 @@
[cards this] [cards this]
:: ::
++ on-watch ++ on-watch
~/ %chat-store-watch
|= =path |= =path
^- (quip card _this) ^- (quip card _this)
?> (team:title our.bowl src.bowl)
|^ |^
?> (team:title our.bowl src.bowl)
=/ cards=(list card) =/ cards=(list card)
?+ path (on-watch:def path) ?+ path (on-watch:def path)
[%keys ~] (give %chat-update !>([%keys ~(key by inbox)])) [%keys ~] (give %chat-update !>([%keys ~(key by inbox)]))
@ -62,8 +75,7 @@
[%updates ~] ~ [%updates ~] ~
[%mailbox @ *] [%mailbox @ *]
?> (~(has by inbox) t.path) ?> (~(has by inbox) t.path)
=/ =ship (slav %p i.t.path) (give %chat-update !>([%create t.path]))
(give %chat-update !>([%create ship t.t.path]))
== ==
[cards this] [cards this]
:: ::
@ -75,6 +87,7 @@
:: ::
++ on-leave on-leave:def ++ on-leave on-leave:def
++ on-peek ++ on-peek
~/ %chat-store-peek
|= =path |= =path
^- (unit (unit cage)) ^- (unit (unit cage))
?+ path (on-peek:def path) ?+ path (on-peek:def path)
@ -102,6 +115,7 @@
-- --
:: ::
:: ::
~% %chat-store-library ..card ~
|_ bol=bowl:gall |_ bol=bowl:gall
:: ::
++ peek-x-envelopes ++ peek-x-envelopes
@ -153,28 +167,30 @@
?- -.action ?- -.action
%create (handle-create action) %create (handle-create action)
%delete (handle-delete action) %delete (handle-delete action)
%message (handle-message action)
%messages (handle-messages action)
%read (handle-read action) %read (handle-read action)
%messages (handle-messages action)
%message
?. =(our.bol author.envelope.action)
(handle-message action)
=^ message-moves state (handle-message action)
=^ read-moves state (handle-read [%read path.action])
[(weld message-moves read-moves) state]
== ==
:: ::
++ handle-create ++ handle-create
|= act=chat-action |= act=chat-action
^- (quip card _state) ^- (quip card _state)
?> ?=(%create -.act) ?> ?=(%create -.act)
=/ pax [(scot %p ship.act) path.act] ?: (~(has by inbox) path.act) [~ state]
?: (~(has by inbox) pax) :- (send-diff path.act act)
[~ state] state(inbox (~(put by inbox) path.act *mailbox))
:- (send-diff pax act)
state(inbox (~(put by inbox) pax *mailbox))
:: ::
++ handle-delete ++ handle-delete
|= act=chat-action |= act=chat-action
^- (quip card _state) ^- (quip card _state)
?> ?=(%delete -.act) ?> ?=(%delete -.act)
=/ mailbox=(unit mailbox) (~(get by inbox) path.act) =/ mailbox=(unit mailbox) (~(get by inbox) path.act)
?~ mailbox ?~ mailbox [~ state]
[~ state]
:- (send-diff path.act act) :- (send-diff path.act act)
state(inbox (~(del by inbox) path.act)) state(inbox (~(del by inbox) path.act))
:: ::
@ -186,8 +202,8 @@
?~ mailbox ?~ mailbox
[~ state] [~ state]
=. letter.envelope.act (evaluate-letter [author letter]:envelope.act) =. letter.envelope.act (evaluate-letter [author letter]:envelope.act)
=. u.mailbox (append-envelope u.mailbox envelope.act) =^ envelope u.mailbox (prepend-envelope u.mailbox envelope.act)
:- (send-diff path.act act) :- (send-diff path.act act(envelope envelope))
state(inbox (~(put by inbox) path.act u.mailbox)) state(inbox (~(put by inbox) path.act u.mailbox))
:: ::
++ handle-messages ++ handle-messages
@ -197,20 +213,16 @@
=/ mailbox=(unit mailbox) (~(get by inbox) path.act) =/ mailbox=(unit mailbox) (~(get by inbox) path.act)
?~ mailbox ?~ mailbox
[~ state] [~ state]
=. envelopes.act (flop envelopes.act)
=/ evaluated-envelopes=(list envelope) ~ =/ evaluated-envelopes=(list envelope) ~
|- ^- (quip card _state) |- ^- (quip card _state)
?~ envelopes.act ?~ envelopes.act
:_ state(inbox (~(put by inbox) path.act u.mailbox)) :_ state(inbox (~(put by inbox) path.act u.mailbox))
%+ send-diff path.act %+ send-diff path.act
:* %messages [%messages path.act 0 (lent evaluated-envelopes) evaluated-envelopes]
path.act
(sub length.config.u.mailbox (lent evaluated-envelopes))
length.config.u.mailbox
evaluated-envelopes
==
=. letter.i.envelopes.act (evaluate-letter [author letter]:i.envelopes.act) =. letter.i.envelopes.act (evaluate-letter [author letter]:i.envelopes.act)
=. evaluated-envelopes (snoc evaluated-envelopes i.envelopes.act) =^ envelope u.mailbox (prepend-envelope u.mailbox i.envelopes.act)
=. u.mailbox (append-envelope u.mailbox i.envelopes.act) =. evaluated-envelopes [envelope evaluated-envelopes]
$(envelopes.act t.envelopes.act) $(envelopes.act t.envelopes.act)
:: ::
++ handle-read ++ handle-read
@ -236,14 +248,14 @@
letter(output (eval bol hoon)) letter(output (eval bol hoon))
letter letter
:: ::
++ append-envelope ++ prepend-envelope
|= [=mailbox =envelope] |= [=mailbox =envelope]
^- ^mailbox ^+ [envelope mailbox]
=. number.envelope +(length.config.mailbox) =. number.envelope +(length.config.mailbox)
=: length.config.mailbox +(length.config.mailbox) =: length.config.mailbox +(length.config.mailbox)
envelopes.mailbox (snoc envelopes.mailbox envelope) envelopes.mailbox [envelope envelopes.mailbox]
== ==
mailbox [envelope mailbox]
:: ::
++ update-subscribers ++ update-subscribers
|= [pax=path update=chat-update] |= [pax=path update=chat-update]

View File

@ -4,8 +4,11 @@
/- *permission-store, /- *permission-store,
*permission-hook, *permission-hook,
*group-store, *group-store,
*invite-store,
*metadata-store,
*permission-group-hook, *permission-group-hook,
*chat-hook *chat-hook,
*metadata-hook
/+ *server, *chat-json, default-agent, verb, dbug /+ *server, *chat-json, default-agent, verb, dbug
/= index /= index
/^ octs /^ octs
@ -39,6 +42,7 @@
/^ (map knot @) /^ (map knot @)
/: /===/app/chat/img /_ /png/ /: /===/app/chat/img /_ /png/
:: ::
~% %chat-view-top ..is ~
|% |%
+$ card card:agent:gall +$ card card:agent:gall
:: ::
@ -51,10 +55,11 @@
[%permission-group-hook-action permission-group-hook-action] [%permission-group-hook-action permission-group-hook-action]
== ==
-- --
%- agent:dbug
%+ verb | %+ verb |
%- agent:dbug
^- agent:gall ^- agent:gall
=< =<
~% %chat-view-agent-core ..poke-handle-http-request ~
|_ bol=bowl:gall |_ bol=bowl:gall
+* this . +* this .
chat-core +> chat-core +>
@ -63,13 +68,14 @@
:: ::
++ on-init ++ on-init
^- (quip card _this) ^- (quip card _this)
=/ launcha [%launch-action !>([%chat-view /configs '/~chat/js/tile.js'])] =/ launcha [%launch-action !>([%add %chat-view /configs '/~chat/js/tile.js'])]
:_ this :_ this
:~ [%pass /updates %agent [our.bol %chat-store] %watch /updates] :~ [%pass /updates %agent [our.bol %chat-store] %watch /updates]
[%pass / %arvo %e %connect [~ /'~chat'] %chat-view] [%pass / %arvo %e %connect [~ /'~chat'] %chat-view]
[%pass /chat-view %agent [our.bol %launch] %poke launcha] [%pass /chat-view %agent [our.bol %launch] %poke launcha]
== ==
++ on-poke ++ on-poke
~/ %chat-view-poke
|= [=mark =vase] |= [=mark =vase]
^- (quip card _this) ^- (quip card _this)
?> (team:title our.bol src.bol) ?> (team:title our.bol src.bol)
@ -91,6 +97,7 @@
== ==
:: ::
++ on-watch ++ on-watch
~/ %chat-view-watch
|= =path |= =path
^- (quip card _this) ^- (quip card _this)
?> (team:title our.bol src.bol) ?> (team:title our.bol src.bol)
@ -98,7 +105,7 @@
?: ?=([%http-response *] path) ?: ?=([%http-response *] path)
[~ this] [~ this]
?: =(/primary path) ?: =(/primary path)
:: create inbox with 100 messages max per mailbox and send that along :: create inbox with 20 messages max per mailbox and send that along
:: then quit the subscription :: then quit the subscription
:_ this :_ this
[%give %fact ~ %json !>((inbox-to-json truncated-inbox-scry))]~ [%give %fact ~ %json !>((inbox-to-json truncated-inbox-scry))]~
@ -106,24 +113,19 @@
[[%give %fact ~ %json !>(*json)]~ this] [[%give %fact ~ %json !>(*json)]~ this]
(on-watch:def path) (on-watch:def path)
:: ::
++ message-limit 20
::
++ truncated-inbox-scry ++ truncated-inbox-scry
^- inbox ^- inbox
=/ =inbox .^(inbox %gx /=chat-store/(scot %da now.bol)/all/noun) =/ =inbox .^(inbox %gx /=chat-store/(scot %da now.bol)/all/noun)
%- ~(run by inbox) %- ~(run by inbox)
|= =mailbox |= =mailbox
^- ^mailbox ^- ^mailbox
[config.mailbox (truncate-envelopes envelopes.mailbox)] [config.mailbox (scag message-limit envelopes.mailbox)]
::
++ truncate-envelopes
|= envelopes=(list envelope)
^- (list envelope)
=/ length (lent envelopes)
?: (lth length 100)
envelopes
(swag [(sub length 100) 100] envelopes)
-- --
:: ::
++ on-agent ++ on-agent
~/ %chat-view-agent
|= [=wire =sign:agent:gall] |= [=wire =sign:agent:gall]
^- (quip card _this) ^- (quip card _this)
?+ -.sign (on-agent:def wire sign) ?+ -.sign (on-agent:def wire sign)
@ -140,6 +142,7 @@
== ==
:: ::
++ on-arvo ++ on-arvo
~/ %chat-view-arvo
|= [=wire =sign-arvo] |= [=wire =sign-arvo]
^- (quip card _this) ^- (quip card _this)
?. ?=(%bound +<.sign-arvo) ?. ?=(%bound +<.sign-arvo)
@ -154,6 +157,7 @@
-- --
:: ::
:: ::
~% %chat-view-library ..card ~
|_ bol=bowl:gall |_ bol=bowl:gall
:: ::
++ poke-handle-http-request ++ poke-handle-http-request
@ -188,53 +192,290 @@
++ poke-json ++ poke-json
|= jon=json |= jon=json
^- (list card) ^- (list card)
?. =(src.bol our.bol) ?> (team:title our.bol src.bol)
~
(poke-chat-view-action (json-to-view-action jon)) (poke-chat-view-action (json-to-view-action jon))
:: ::
++ poke-chat-view-action ++ poke-chat-view-action
|= act=chat-view-action |= act=chat-view-action
^- (list card) ^- (list card)
?. =(src.bol our.bol) |^
~ ?> (team:title our.bol src.bol)
?- -.act ?- -.act
%create %create
=/ pax [(scot %p our.bol) path.act] ?> ?=(^ app-path.act)
=/ group-read=path [%chat (weld pax /read)] ?> |(=(group-path.act app-path.act) =(~(tap in members.act) ~))
=/ group-write=path [%chat (weld pax /write)] ?^ (chat-scry app-path.act)
~& %chat-already-exists
~
%- zing %- zing
:~ :~ (group-poke [%bundle group-read]) :~ (create-chat app-path.act allow-history.act)
(group-poke [%bundle group-write]) %- create-group
(group-poke [%add read.act group-read]) :* group-path.act
(group-poke [%add write.act group-write]) app-path.act
(chat-poke [%create our.bol path.act]) security.act
(chat-hook-poke [%add-owned pax security.act allow-history.act]) members.act
== title.act
(create-security [%chat pax] security.act) description.act
:~ (permission-hook-poke [%add-owned group-read group-read])
(permission-hook-poke [%add-owned group-write group-read])
== ==
(create-metadata title.act description.act group-path.act app-path.act)
== ==
:: ::
%delete %delete
=/ group-read [%chat (weld path.act /read)] ?> ?=(^ app-path.act)
=/ group-write [%chat (weld path.act /write)] :: always just delete the chat from chat-store
:~ (chat-hook-poke [%remove path.act]) ::
(permission-hook-poke [%remove group-read]) :+ (chat-hook-poke [%remove app-path.act])
(permission-hook-poke [%remove group-write]) (chat-poke [%delete app-path.act])
(group-poke [%unbundle group-read]) :: if we still have metadata for the chat, remove it, and the associated
(group-poke [%unbundle group-write]) :: group if it's unmanaged
(chat-poke [%delete path.act]) ::
:: we aren't guaranteed to have metadata: the chat might have been
:: deleted by the host, which pushes metadata deletion down to us.
::
=/ group-path=(unit path)
(maybe-group-from-chat app-path.act)
?~ group-path ~
=* group u.group-path
%- zing
:~ ?. (is-creator group %chat app-path.act) ~
[(metadata-poke [%remove group [%chat app-path.act]])]~
::
?: (is-managed group) ~
:~ (group-poke [%unbundle group])
(metadata-hook-poke [%remove group])
(metadata-store-poke [%remove group [%chat app-path.act]])
==
== ==
:: ::
%join %join
=/ group-read [%chat (scot %p ship.act) (weld path.act /read)] =/ group-path
=/ group-write [%chat (scot %p ship.act) (weld path.act /write)] ?. (is-managed app-path.act) app-path.act
:~ (chat-hook-poke [%add-synced ship.act path.act ask-history.act]) (group-from-chat app-path.act)
(permission-hook-poke [%add-synced ship.act group-write]) :~ (chat-hook-poke [%add-synced ship.act app-path.act ask-history.act])
(permission-hook-poke [%add-synced ship.act group-read]) (permission-hook-poke [%add-synced ship.act group-path])
(metadata-hook-poke [%add-synced ship.act group-path])
==
::
%groupify
?> ?=([%'~' ^] app-path.act)
:: retrieve old data
::
=/ data=(unit mailbox)
(scry-for (unit mailbox) %chat-store [%mailbox app-path.act])
?~ data
~& [%cannot-groupify-nonexistent app-path.act]
~
=/ permission=(unit permission)
(scry-for (unit permission) %permission-store [%permission app-path.act])
?: |(?=(~ permission) ?=(%black kind.u.permission))
~& [%cannot-groupify-blacklist app-path.act]
~
=/ =metadata
=- (fall - *metadata)
%^ scry-for (unit metadata)
%metadata-store
=/ encoded-path=@ta
(scot %t (spat app-path.act))
/metadata/[encoded-path]/chat/[encoded-path]
:: figure out new data
::
=/ chat-path=^path (slag 1 `path`app-path.act)
:: group-path: the group to associate with the chat
:: members: members of group, if it's new
:: new-members: new members of group, if it already exists
::
=/ [group-path=path members=(set ship) new-members=(set ship)]
?~ existing.act
[chat-path who.u.permission ~]
:+ group-path.u.existing.act
~
?. inclusive.u.existing.act ~
%- ~(dif in who.u.permission)
~| [%groupifying-with-nonexistent-group group-path.u.existing.act]
%- need
(group-scry group-path.u.existing.act)
:: make changes
::
;: weld
:: delete the old chat
::
(poke-chat-view-action %delete app-path.act)
::
:: create the new chat. if needed, creates the new group.
::
%- poke-chat-view-action
:* %create
title.metadata
description.metadata
chat-path
group-path
%village
members
&
==
::
:: if needed, add members to the existing group
::
?~ new-members ~
[(group-poke [%add new-members group-path])]~
::
:: import messages into the new chat
::
[(chat-poke %messages chat-path envelopes.u.data)]~
== ==
== ==
::
++ create-chat
|= [=path history=?]
^- (list card)
:~ (chat-poke [%create path])
(chat-hook-poke [%add-owned path history])
==
::
++ create-group
|= [=path app-path=path sec=rw-security ships=(set ship) title=@t desc=@t]
^- (list card)
?^ (group-scry path) ~
:: do not create a managed group if this is a sig path or a blacklist
::
?: =(sec %channel)
:~ (group-poke [%bundle path])
(create-security path sec)
(permission-hook-poke [%add-owned path path])
==
?: (is-managed path)
~[(contact-view-poke [%create path ships title desc])]
%+ welp
:~ (group-poke [%bundle path])
(group-poke [%add ships path])
(create-security path sec)
(permission-hook-poke [%add-owned path path])
==
%- zing
%+ turn ~(tap in ships)
|= =ship
?: =(ship our.bol) ~
[(send-invite app-path ship)]~
::
++ create-security
|= [pax=path sec=rw-security]
^- card
?+ sec !!
%channel
(perm-group-hook-poke [%associate pax [[pax %black] ~ ~]])
::
%village
(perm-group-hook-poke [%associate pax [[pax %white] ~ ~]])
==
::
++ create-metadata
|= [title=@t description=@t group-path=path app-path=path]
^- (list card)
=/ =metadata
%* . *metadata
title title
description description
date-created now.bol
creator
%+ slav %p
?: (is-managed app-path) (snag 0 app-path)
(snag 1 app-path)
==
:~ (metadata-poke [%add group-path [%chat app-path] metadata])
(metadata-hook-poke [%add-owned group-path])
==
::
++ contact-view-poke
|= act=[%create =path ships=(set ship) title=@t description=@t]
^- card
[%pass / %agent [our.bol %contact-view] %poke %contact-view-action !>(act)]
::
++ metadata-poke
|= act=metadata-action
^- card
[%pass / %agent [our.bol %metadata-hook] %poke %metadata-action !>(act)]
::
++ metadata-store-poke
|= act=metadata-action
^- card
[%pass / %agent [our.bol %metadata-store] %poke %metadata-action !>(act)]
::
++ metadata-hook-poke
|= act=metadata-hook-action
^- card
:* %pass / %agent
[our.bol %metadata-hook]
%poke %metadata-hook-action
!>(act)
==
::
++ send-invite
|= [=path =ship]
^- card
=/ =invite
:* our.bol %chat-hook
path ship ''
==
=/ act=invite-action [%invite /chat (shaf %msg-uid eny.bol) invite]
[%pass / %agent [our.bol %invite-hook] %poke %invite-action !>(act)]
::
++ chat-scry
|= pax=path
^- (unit mailbox)
=. pax ;:(weld /=chat-store/(scot %da now.bol)/mailbox pax /noun)
.^((unit mailbox) %gx pax)
::
++ maybe-group-from-chat
|= app-path=path
^- (unit path)
?. .^(? %gu (scot %p our.bol) %metadata-store (scot %da now.bol) ~)
?: ?=([@ ^] app-path)
~& [%assuming-ported-legacy-chat app-path]
`[%'~' app-path]
~& [%weird-chat app-path]
!!
=/ resource-indices
.^ (jug resource group-path)
%gy
(scot %p our.bol)
%metadata-store
(scot %da now.bol)
/resource-indices
==
=/ groups=(set path)
%+ fall
(~(get by resource-indices) [%chat app-path])
*(set path)
?~ groups ~
`n.groups
::
++ group-from-chat
(cork maybe-group-from-chat need)
::
++ is-managed
|= =path
^- ?
?> ?=(^ path)
!=(i.path '~')
::
++ is-creator
|= [group-path=path app-name=@ta app-path=path]
^- ?
=/ meta=(unit metadata)
.^ (unit metadata)
%gx
(scot %p our.bol)
%metadata-store
(scot %da now.bol)
%metadata
(scot %t (spat group-path))
app-name
(scot %t (spat app-path))
/noun
==
?~ meta !!
=(our.bol creator.u.meta)
--
:: ::
++ diff-chat-update ++ diff-chat-update
|= upd=chat-update |= upd=chat-update
@ -257,6 +498,11 @@
^- card ^- card
[%pass / %agent [our.bol %group-store] %poke %group-action !>(act)] [%pass / %agent [our.bol %group-store] %poke %group-action !>(act)]
:: ::
++ permission-poke
|= act=permission-action
^- card
[%pass / %agent [our.bol %permission-store] %poke %permission-action !>(act)]
::
++ chat-hook-poke ++ chat-hook-poke
|= act=chat-hook-action |= act=chat-hook-action
^- card ^- card
@ -279,37 +525,24 @@
++ envelope-scry ++ envelope-scry
|= pax=path |= pax=path
^- (list envelope) ^- (list envelope)
=. pax ;:(weld /=chat-store/(scot %da now.bol)/envelopes pax /noun) (scry-for (list envelope) %chat-store [%envelopes pax])
.^((list envelope) %gx pax)
:: ::
++ configs-scry ++ configs-scry
^- chat-configs ^- chat-configs
.^(chat-configs %gx /=chat-store/(scot %da now.bol)/configs/noun) (scry-for chat-configs %chat-store /configs)
:: ::
++ create-security ++ group-scry
|= [pax=path sec=rw-security] |= pax=path
^- (list card) ^- (unit group)
=/ read (weld pax /read) (scry-for (unit group) %group-store pax)
=/ write (weld pax /write) ::
?- sec ++ scry-for
%channel |* [=mold app=term =path]
:~ (perm-group-hook-poke [%associate read [[read %black] ~ ~]]) .^ mold
(perm-group-hook-poke [%associate write [[write %black] ~ ~]]) %gx
== (scot %p our.bol)
:: app
%village (scot %da now.bol)
:~ (perm-group-hook-poke [%associate read [[read %white] ~ ~]]) (snoc `^path`path %noun)
(perm-group-hook-poke [%associate write [[write %white] ~ ~]])
==
::
%journal
:~ (perm-group-hook-poke [%associate read [[read %black] ~ ~]])
(perm-group-hook-poke [%associate write [[write %white] ~ ~]])
==
::
%mailbox
:~ (perm-group-hook-poke [%associate read [[read %white] ~ ~]])
(perm-group-hook-poke [%associate write [[write %black] ~ ~]])
==
== ==
-- --

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 866 B

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 861 B

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 611 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 255 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 865 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 854 B

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 951 B

View File

@ -22,9 +22,10 @@
</head> </head>
<body> <body>
<div id="root" /> <div id="root"/>
<script src="/~/channel/channel.js"></script> <script src="/~channel/channel.js"></script>
<script src="/~modulo/session.js"></script> <script src="/~modulo/session.js"></script>
<script src="/~chat/js/index.js"></script> <script src="/~chat/js/index.js"></script>
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.1.12.min.js"></script>
</body> </body>
</html> </html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -417,14 +417,17 @@
:* to :* to
(mul windup-years yer:yo) (mul windup-years yer:yo)
stars stars
(div (mul unlock-years yer:yo) stars)
1 1
(div (mul unlock-years yer:yo) stars)
== ==
:: ::
++ register-conditional ++ register-conditional
|= [to=address [b1=@ud b2=@ud b3=@ud] unlock-years-per-batch=@ud] |= [to=address [b1=@ud b2=@ud b3=@ud] unlock-years-per-batch=@ud]
%- register-conditional:dat %- register-conditional:dat
=- [`address`to b1 b2 b3 `@ud`- 1] :* to
(div (mul unlock-years-per-batch yer:yo) :(add b1 b2 b3)) b1 b2 b3
1
(div (mul unlock-years-per-batch yer:yo) :(add b1 b2 b3))
==
:: ::
-- --

View File

@ -1,4 +1,4 @@
/+ *server, default-agent, verb /+ *server, default-agent, verb, dbug
/= tile-js /= tile-js
/^ octs /^ octs
/; as-octs:mimes:html /; as-octs:mimes:html
@ -8,7 +8,18 @@
== ==
=, format =, format
:: ::
|%
::
+$ card card:agent:gall
+$ versioned-state
$% state-zero
==
+$ state-zero [%0 data=json]
--
%+ verb | %+ verb |
%- agent:dbug
=| state-zero
=* state -
^- agent:gall ^- agent:gall
|_ =bowl:gall |_ =bowl:gall
+* this . +* this .
@ -17,20 +28,28 @@
++ on-init ++ on-init
^- (quip card:agent:gall _this) ^- (quip card:agent:gall _this)
=/ launcha =/ launcha
[%launch-action !>([%clock /tile '/~clock/js/tile.js'])] [%launch-action !>([%add %clock /clocktile '/~clock/js/tile.js'])]
:_ this :_ this
:~ [%pass / %arvo %e %connect [~ /'~clock'] %clock] :~ [%pass / %arvo %e %connect [~ /'~clock'] %clock]
[%pass /clock %agent [our.bowl %launch] %poke launcha] [%pass /clock %agent [our.bowl %launch] %poke launcha]
== ==
:: bootstrapping to get %goad started OTA :: bootstrapping to get %goad started OTA
:: ::
++ on-save !>(%2) ++ on-save !>(%3)
++ on-load ++ on-load
|= old-state=vase |= old-state=vase
=/ old !<(?(~ %1 %2) old-state) ^- (quip card _this)
=/ old !<(?(~ %1 %2 %3) old-state)
=^ cards this =^ cards this
?: ?=(%2 old) ?: ?=(%3 old)
`this `this
?: ?=(%2 old)
:: ensure launch is set up to listen to us correctly
::
=/ launcha
[%launch-action !>([%add %clock /clocktile '/~clock/js/tile.js'])]
:_ this
[%pass /clock %agent [our.bowl %launch] %poke launcha]~
:_ this :_ ~ :_ this :_ ~
[%pass /behn %arvo %b %wait +(now.bowl)] [%pass /behn %arvo %b %wait +(now.bowl)]
:: ::
@ -39,6 +58,9 @@
++ on-poke ++ on-poke
|= [=mark =vase] |= [=mark =vase]
^- (quip card:agent:gall _this) ^- (quip card:agent:gall _this)
|^
?: ?=(%json mark)
(poke-json !<(json vase))
?. ?=(%handle-http-request mark) ?. ?=(%handle-http-request mark)
(on-poke:def mark vase) (on-poke:def mark vase)
=+ !<([eyre-id=@ta =inbound-request:eyre] vase) =+ !<([eyre-id=@ta =inbound-request:eyre] vase)
@ -59,15 +81,23 @@
?: =(name 'tile') ?: =(name 'tile')
(js-response:gen tile-js) (js-response:gen tile-js)
not-found:gen not-found:gen
::
++ poke-json
|= jon=json
^- (quip card:agent:gall _this)
=. data.state jon
:_ this
[%give %fact ~[/clocktile] %json !>(jon)]~
--
:: ::
++ on-watch ++ on-watch
|= =path |= =path
^- (quip card:agent:gall _this) ^- (quip card:agent:gall _this)
?: ?=([%http-response *] path) ?: ?=([%http-response *] path)
`this `this
?. =(/tile path) ?. =(/clocktile path)
(on-watch:def path) (on-watch:def path)
[[%give %fact ~ %json !>(*json)]~ this] [[%give %fact ~ %json !>(data.state)]~ this]
:: ::
++ on-leave on-leave:def ++ on-leave on-leave:def
++ on-peek on-peek:def ++ on-peek on-peek:def

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,483 @@
:: contact-hook:
::
/- *group-store,
*group-hook,
*contact-hook,
*invite-store,
*metadata-hook,
*metadata-store
/+ *contact-json, default-agent, dbug
~% %contact-hook-top ..is ~
|%
+$ card card:agent:gall
::
+$ versioned-state
$% state-zero
state-one
==
::
+$ state-zero [%0 state-base]
+$ state-one [%1 state-base]
+$ state-base
$: =synced
invite-created=_|
==
--
=| state-one
=* state -
%- agent:dbug
^- agent:gall
=<
|_ bol=bowl:gall
+* this .
contact-core +>
cc ~(. contact-core bol)
def ~(. (default-agent this %|) bol)
::
++ on-init
^- (quip card _this)
:_ this(invite-created %.y)
:~ (invite-poke:cc [%create /contacts])
[%pass /inv %agent [our.bol %invite-store] %watch /invitatory/contacts]
[%pass /group %agent [our.bol %group-store] %watch /updates]
==
++ on-save !>(state)
++ on-load
|= old-vase=vase
^- (quip card _this)
=/ old !<(versioned-state old-vase)
?: ?=(%1 -.old)
[~ this(state old)]
=/ upgraded-state
%* . *state-one
synced synced
invite-created invite-created
==
:_ this(state upgraded-state)
[%pass /group %agent [our.bol %group-store] %watch /updates]~
::
++ on-poke
|= [=mark =vase]
^- (quip card _this)
=^ cards state
?+ mark (on-poke:def mark vase)
%json
(poke-json:cc !<(json vase))
::
%contact-action
(poke-contact-action:cc !<(contact-action vase))
::
%contact-hook-action
(poke-hook-action:cc !<(contact-hook-action vase))
==
[cards this]
::
++ on-watch
|= =path
^- (quip card _this)
?+ path (on-watch:def path)
[%contacts *] [(watch-contacts:cc t.path) this]
[%synced *] [(watch-synced:cc t.path) this]
==
::
++ on-agent
|= [=wire =sign:agent:gall]
^- (quip card _this)
?+ -.sign (on-agent:def wire sign)
%kick [(kick:cc wire) this]
%watch-ack
=^ cards state
(watch-ack:cc wire p.sign)
[cards this]
::
%fact
?+ p.cage.sign (on-agent:def wire sign)
%contact-update
=^ cards state
(fact-contact-update:cc wire !<(contact-update q.cage.sign))
[cards this]
::
%group-update
=^ cards state
(fact-group-update:cc wire !<(group-update q.cage.sign))
[cards this]
::
%invite-update
=^ cards state
(fact-invite-update:cc wire !<(invite-update q.cage.sign))
[cards this]
==
==
::
++ on-leave on-leave:def
++ on-peek on-peek:def
++ on-arvo on-arvo:def
++ on-fail on-fail:def
--
::
|_ bol=bowl:gall
::
++ poke-json
|= jon=json
^- (quip card _state)
(poke-contact-action (json-to-action jon))
::
++ poke-contact-action
|= act=contact-action
^- (quip card _state)
:_ state
?+ -.act !!
%edit (handle-contact-action path.act ship.act act)
%add (handle-contact-action path.act ship.act act)
%remove (handle-contact-action path.act ship.act act)
==
::
++ handle-contact-action
|= [=path =ship act=contact-action]
^- (list card)
:: local
?: (team:title our.bol src.bol)
?. (~(has by synced) path) ~
=/ shp ?:(=(path /~/default) our.bol (~(got by synced) path))
=/ appl ?:(=(shp our.bol) %contact-store %contact-hook)
[%pass / %agent [shp appl] %poke %contact-action !>(act)]~
:: foreign
=/ shp (~(got by synced) path)
?. |(=(shp our.bol) =(src.bol ship)) ~
:: scry group to check if ship is a member
=/ =group (need (group-scry path))
?. (~(has in group) shp) ~
[%pass / %agent [our.bol %contact-store] %poke %contact-action !>(act)]~
::
++ poke-hook-action
|= act=contact-hook-action
^- (quip card _state)
?- -.act
%add-owned
?> (team:title our.bol src.bol)
=/ contact-path [%contacts path.act]
?: (~(has by synced) path.act)
[~ state]
=. synced (~(put by synced) path.act our.bol)
:_ state
:~ [%pass contact-path %agent [our.bol %contact-store] %watch contact-path]
[%give %fact [/synced]~ %contact-hook-update !>([%initial synced])]
==
::
%add-synced
?> (team:title our.bol src.bol)
?: (~(has by synced) path.act) [~ state]
=. synced (~(put by synced) path.act ship.act)
=/ contact-path [%contacts path.act]
:_ state
:~ [%pass contact-path %agent [ship.act %contact-hook] %watch contact-path]
[%give %fact [/synced]~ %contact-hook-update !>([%initial synced])]
==
::
%remove
=/ ship (~(get by synced) path.act)
?~ ship [~ state]
?: &(=(u.ship our.bol) (team:title our.bol src.bol))
:: delete one of our.bol own paths
:_ state(synced (~(del by synced) path.act))
%- zing
:~ (pull-wire [%contacts path.act])
[%give %kick ~[[%contacts path.act]] ~]~
[%give %fact [/synced]~ %contact-hook-update !>([%initial synced])]~
==
?. |(=(u.ship src.bol) (team:title our.bol src.bol))
:: if neither ship = source or source = us, do nothing
[~ state]
:: delete a foreign ship's path
=/ cards
(handle-contact-action path.act our.bol [%remove path.act our.bol])
:_ state(synced (~(del by synced) path.act))
%- zing
:~ (pull-wire [%contacts path.act])
[%give %fact [/synced]~ %contact-hook-update !>([%initial synced])]~
cards
==
==
::
++ watch-contacts
|= pax=path
^- (list card)
?> ?=(^ pax)
?> (~(has by synced) pax)
:: scry groups to check if ship is a member
=/ =group (need (group-scry pax))
?> (~(has in group) src.bol)
=/ contacts (need (contacts-scry pax))
[%give %fact ~ %contact-update !>([%contacts pax contacts])]~
::
++ watch-synced
|= pax=path
^- (list card)
?> (team:title our.bol src.bol)
[%give %fact ~ %contact-hook-update !>([%initial synced])]~
::
++ watch-ack
|= [wir=wire saw=(unit tang)]
^- (quip card _state)
?~ saw
[~ state]
?> ?=(^ wir)
[~ state(synced (~(del by synced) t.wir))]
::
++ kick
|= wir=wire
^- (list card)
?+ wir !!
[%inv ~]
[%pass /inv %agent [our.bol %invite-store] %watch /invitatory/contacts]~
::
[%group ~]
[%pass /group %agent [our.bol %group-store] %watch /updates]~
::
[%contacts @ *]
?. (~(has by synced) t.wir) ~
=/ =ship (~(got by synced) t.wir)
?: =(ship our.bol)
[%pass wir %agent [our.bol %contact-store] %watch wir]~
[%pass wir %agent [ship %contact-hook] %watch wir]~
==
::
++ fact-contact-update
|= [wir=wire fact=contact-update]
^- (quip card _state)
|^
?: (team:title our.bol src.bol)
(local fact)
:_ state
(foreign fact)
::
++ give-fact
|= [=path update=contact-update]
^- (list card)
[%give %fact ~[[%contacts path]] %contact-update !>(update)]~
::
++ local
|= fact=contact-update
^- (quip card _state)
?+ -.fact [~ state]
%add
:_ state
(give-fact path.fact [%add path.fact ship.fact contact.fact])
::
%edit
:_ state
(give-fact path.fact [%edit path.fact ship.fact edit-field.fact])
::
%remove
:_ state
~[(group-poke [%remove [ship.fact ~ ~] path.fact])]
::
%delete
=. synced (~(del by synced) path.fact)
:_ state
:~ (group-poke [%unbundle path.fact])
(metadata-hook-poke [%remove path.fact])
(metadata-poke [%remove path.fact [%contacts path.fact]])
==
==
::
++ foreign
|= fact=contact-update
^- (list card)
?+ -.fact ~
%contacts
=/ owner (~(got by synced) path.fact)
?> =(owner src.bol)
=/ have-contacts=(unit contacts)
(contacts-scry path.fact)
?~ have-contacts
:: if we don't have any contacts yet,
:: create the entry, and %add every contact
::
:- (contact-poke [%create path.fact])
%+ turn ~(tap by contacts.fact)
|= [=ship =contact]
(contact-poke [%add path.fact ship contact])
:: if we already have some, decide between %add, %remove and recreate
:: on a per-contact basis
::
%- zing
%+ turn
%~ tap in
%- ~(uni in ~(key by contacts.fact))
~(key by u.have-contacts)
|= =ship
^- (list card)
=/ have=(unit contact) (~(get by u.have-contacts) ship)
=/ want=(unit contact) (~(get by contacts.fact) ship)
?~ have
[(contact-poke %add path.fact ship (need want))]~
?~ want
[(contact-poke %remove path.fact ship)]~
?: =(u.want u.have) ~
::TODO probably want an %all edit-field that resolves to more granular
:: updates within the contact-store?
:~ (contact-poke %remove path.fact ship)
(contact-poke %add path.fact ship u.want)
==
::
%add
=/ owner (~(get by synced) path.fact)
?~ owner ~
?> |(=(u.owner src.bol) =(src.bol ship.fact))
~[(contact-poke [%add path.fact ship.fact contact.fact])]
::
%remove
=/ owner (~(get by synced) path.fact)
?~ owner ~
?> |(=(u.owner src.bol) =(src.bol ship.fact))
%+ welp
:~ (group-poke [%remove [ship.fact ~ ~] path.fact])
(contact-poke [%remove path.fact ship.fact])
==
?. =(ship.fact our.bol) ~
~[(group-poke [%unbundle path.fact])]
::
%edit
=/ owner (~(got by synced) path.fact)
?> |(=(owner src.bol) =(src.bol ship.fact))
~[(contact-poke [%edit path.fact ship.fact edit-field.fact])]
==
--
::
++ fact-group-update
|= [wir=wire fact=group-update]
^- (quip card _state)
|^
?+ -.fact [~ state]
%add (add +.fact)
%remove (remove +.fact)
%unbundle (unbundle +.fact)
==
++ add
|= [ships=(set ship) =path]
^- (quip card _state)
=/ owner (~(get by synced) path)
?~ owner [~ state]
?. =(u.owner our.bol) [~ state]
:_ state
%+ turn ~(tap in (~(del in ships) our.bol))
|= =ship
(send-invite-poke path ship)
::
++ unbundle
|= =path
^- (quip card _state)
?. (~(has by synced) path)
:_ state
[(contact-poke [%delete path])]~
:_ state(synced (~(del by synced) path))
:~ [%pass [%contacts path] %agent [our.bol %contact-store] %leave ~]
[(contact-poke [%delete path])]
==
::
++ remove
|= [members=group =path]
^- (quip card _state)
:: if pax is synced, remove member from contacts and kick their sub
=/ owner=(unit ship) (~(get by synced) path)
?~ owner
:_ state
%+ turn ~(tap in members)
|= =ship
(contact-poke [%remove path ship])
:_ state
%- zing
%+ turn ~(tap in members)
|= =ship
:~ [%give %kick ~[[%contacts path]] `ship]
?: =(ship our.bol)
(contact-poke [%delete path])
(contact-poke [%remove path ship])
==
::
++ send-invite-poke
|= [=path =ship]
^- card
=/ =invite
:* our.bol %contact-hook
path ship ''
==
=/ act=invite-action [%invite /contacts (shaf %msg-uid eny.bol) invite]
[%pass / %agent [our.bol %invite-hook] %poke %invite-action !>(act)]
--
::
++ fact-invite-update
|= [wir=wire fact=invite-update]
^- (quip card _state)
?+ -.fact [~ state]
%accepted
=/ changes
(poke-hook-action [%add-synced ship.invite.fact path.invite.fact])
:-
%+ welp
:~ (group-hook-poke [%add ship.invite.fact path.invite.fact])
(metadata-hook-poke [%add-synced ship.invite.fact path.invite.fact])
==
-.changes
+.changes
==
::
++ group-hook-poke
|= act=group-hook-action
^- card
[%pass / %agent [our.bol %group-hook] %poke %group-hook-action !>(act)]
::
++ invite-poke
|= act=invite-action
^- card
[%pass / %agent [our.bol %invite-store] %poke %invite-action !>(act)]
::
++ contact-poke
|= act=contact-action
^- card
[%pass / %agent [our.bol %contact-store] %poke %contact-action !>(act)]
::
++ group-poke
|= act=group-action
^- card
[%pass / %agent [our.bol %group-store] %poke %group-action !>(act)]
::
++ metadata-poke
|= act=metadata-action
^- card
[%pass / %agent [our.bol %metadata-store] %poke %metadata-action !>(act)]
::
++ metadata-hook-poke
|= act=metadata-hook-action
^- card
[%pass / %agent [our.bol %metadata-hook] %poke %metadata-hook-action !>(act)]
::
++ contacts-scry
|= pax=path
^- (unit contacts)
=. pax ;:(weld /=contact-store/(scot %da now.bol)/contacts pax /noun)
.^((unit contacts) %gx pax)
::
++ invite-scry
|= uid=serial
^- (unit invite)
=/ pax
/=invite-store/(scot %da now.bol)/invite/contacts/(scot %uv uid)/noun
.^((unit invite) %gx pax)
::
++ group-scry
|= pax=path
^- (unit group)
.^((unit group) %gx ;:(weld /=group-store/(scot %da now.bol) pax /noun))
::
++ pull-wire
|= pax=path
^- (list card)
?> ?=(^ pax)
=/ shp (~(get by synced) t.pax)
?~ shp ~
?: =(u.shp our.bol)
[%pass pax %agent [our.bol %contact-store] %leave ~]~
[%pass pax %agent [u.shp %contact-hook] %leave ~]~
--

View File

@ -0,0 +1,214 @@
:: contact-store: data store that holds group-based contact data
::
/+ *contact-json, default-agent, dbug
|%
+$ card card:agent:gall
+$ versioned-state
$% state-zero
state-one
==
::
+$ rolodex-0 (map path contacts-0)
+$ contacts-0 (map ship contact-0)
+$ avatar-0 [content-type=@t octs=[p=@ud q=@t]]
+$ contact-0
$: nickname=@t
email=@t
phone=@t
website=@t
notes=@t
color=@ux
avatar=(unit avatar-0)
==
::
+$ state-zero
$: %0
rolodex=rolodex-0
==
+$ state-one
$: %1
=rolodex
==
--
::
=| state-one
=* state -
%- agent:dbug
^- agent:gall
=<
|_ =bowl:gall
+* this .
contact-core +>
cc ~(. contact-core bowl)
def ~(. (default-agent this %|) bowl)
::
++ on-init on-init:def
++ on-save !>(state)
++ on-load
|= old-vase=vase
=/ old !<(versioned-state old-vase)
?: ?=(%1 -.old)
[~ this(state old)]
=/ new-rolodex=^rolodex
%- ~(run by rolodex.old)
|= cons=contacts-0
^- contacts
%- ~(run by cons)
|= con=contact-0
^- contact
:* nickname.con
email.con
phone.con
website.con
notes.con
color.con
~
==
[~ this(state [%1 new-rolodex])]
::
++ on-poke
|= [=mark =vase]
^- (quip card _this)
?> (team:title our.bowl src.bowl)
=^ cards state
?+ mark (on-poke:def mark vase)
::%json (poke-json:cc !<(json vase))
%contact-action (poke-contact-action:cc !<(contact-action vase))
==
[cards this]
::
++ on-watch
|= =path
^- (quip card _this)
?> (team:title our.bowl src.bowl)
|^
=/ cards=(list card)
?+ path (on-watch:def path)
[%all ~] (give %contact-update !>([%rolodex rolodex]))
[%updates ~] ~
[%contacts @ *]
%+ give %contact-update
!>([%contacts t.path (~(got by rolodex) t.path)])
==
[cards this]
::
++ give
|= =cage
^- (list card)
[%give %fact ~ cage]~
--
::
++ on-leave on-leave:def
++ on-peek
|= =path
^- (unit (unit cage))
?+ path (on-peek:def path)
[%x %all ~] ``noun+!>(rolodex)
[%x %contacts *]
?~ t.t.path
~
``noun+!>((~(get by rolodex) t.t.path))
::
[%x %contact *]
:: /:path/:ship
=/ pax `^path`(flop t.t.path)
?~ pax ~
=/ =ship (slav %p i.pax)
?~ t.pax ~
=> .(pax `(list @ta)`(flop t.pax))
=/ contacts=(unit contacts) (~(get by rolodex) pax)
?~ contacts
~
``noun+!>((~(get by u.contacts) ship))
==
::
++ on-agent on-agent:def
++ on-arvo on-arvo:def
++ on-fail on-fail:def
--
::
::
|_ bol=bowl:gall
::
::++ poke-json
:: |= =json
:: ^- (quip move _this)
:: ?> (team:title our.bol src.bol)
:: (poke-contact-action (json-to-action json))
::
++ poke-contact-action
|= action=contact-action
^- (quip card _state)
?> (team:title our.bol src.bol)
?- -.action
%create (handle-create +.action)
%delete (handle-delete +.action)
%add (handle-add +.action)
%remove (handle-remove +.action)
%edit (handle-edit +.action)
==
::
++ handle-create
|= =path
^- (quip card _state)
?< (~(has by rolodex) path)
:- (send-diff path [%create path])
state(rolodex (~(put by rolodex) path *contacts))
::
++ handle-delete
|= =path
^- (quip card _state)
?. (~(has by rolodex) path) [~ state]
:- (send-diff path [%delete path])
state(rolodex (~(del by rolodex) path))
::
++ handle-add
|= [=path =ship =contact]
^- (quip card _state)
=/ contacts (~(got by rolodex) path)
?< (~(has by contacts) ship)
=. contacts (~(put by contacts) ship contact)
:- (send-diff path [%add path ship contact])
state(rolodex (~(put by rolodex) path contacts))
::
++ handle-remove
|= [=path =ship]
^- (quip card _state)
=/ contacts (~(got by rolodex) path)
?. (~(has by contacts) ship) [~ state]
=. contacts (~(del by contacts) ship)
:- (send-diff path [%remove path ship])
state(rolodex (~(put by rolodex) path contacts))
::
++ handle-edit
|= [=path =ship =edit-field]
^- (quip card _state)
=/ contacts (~(got by rolodex) path)
=/ contact (~(got by contacts) ship)
=. contact (edit-contact contact edit-field)
=. contacts (~(put by contacts) ship contact)
:- (send-diff path [%edit path ship edit-field])
state(rolodex (~(put by rolodex) path contacts))
::
++ edit-contact
|= [con=contact edit=edit-field]
^- contact
?- -.edit
%nickname con(nickname nickname.edit)
%email con(email email.edit)
%phone con(phone phone.edit)
%website con(website website.edit)
%notes con(notes notes.edit)
%color con(color color.edit)
%avatar con(avatar avatar.edit)
==
::
++ send-diff
|= [pax=path upd=contact-update]
^- (list card)
:~ :*
%give %fact
~[/all /updates [%contacts pax]]
%contact-update !>(upd)
== ==
--

View File

@ -0,0 +1,282 @@
:: contact-view: sets up contact JS client and combines commands
:: into semantic actions for the UI
::
/- *group-store,
*group-hook,
*invite-store,
*contact-hook,
*metadata-store,
*metadata-hook,
*permission-group-hook,
*permission-hook
/+ *server, *contact-json, default-agent, dbug
/= index
/^ octs
/; as-octs:mimes:html
/: /===/app/contacts/index
/| /html/
/~ ~
==
/= tile-js
/^ octs
/; as-octs:mimes:html
/: /===/app/contacts/js/tile
/| /js/
/~ ~
==
/= script
/^ octs
/; as-octs:mimes:html
/: /===/app/contacts/js/index
/| /js/
/~ ~
==
/= style
/^ octs
/; as-octs:mimes:html
/: /===/app/contacts/css/index
/| /css/
/~ ~
==
/= contact-png
/^ (map knot @)
/: /===/app/contacts/img /_ /png/
::
|%
+$ card card:agent:gall
--
=* state -
::
%- agent:dbug
^- agent:gall
=<
|_ =bowl:gall
+* this .
contact-core +>
cc ~(. contact-core bowl)
def ~(. (default-agent this %|) bowl)
::
++ on-init
^- (quip card _this)
:_ this
:~ [%pass /updates %agent [our.bowl %contact-store] %watch /updates]
[%pass / %arvo %e %connect [~ /'~groups'] %contact-view]
(contact-poke:cc [%create /~/default])
(group-poke:cc [%bundle /~/default])
(contact-poke:cc [%add /~/default our.bowl *contact])
(group-poke:cc [%add [our.bowl ~ ~] /~/default])
==
::
++ on-save on-save:def
++ on-load on-load:def
++ on-poke
|= [=mark =vase]
^- (quip card _this)
?> (team:title our.bowl src.bowl)
?+ mark (on-poke:def mark vase)
%json [(poke-json:cc !<(json vase)) this]
%contact-view-action
[(poke-contact-view-action:cc !<(contact-view-action vase)) this]
::
%handle-http-request
=+ !<([eyre-id=@ta =inbound-request:eyre] vase)
:_ this
%+ give-simple-payload:app eyre-id
%+ require-authorization:app inbound-request
poke-handle-http-request:cc
==
::
++ on-watch
|= =path
^- (quip card _this)
?> (team:title our.bowl src.bowl)
?: ?=([%http-response *] path) [~ this]
?. =(/primary path) (on-watch:def path)
[[%give %fact ~ %json !>((rolodex-to-json all-scry:cc))]~ this]
::
++ on-agent
|= [=wire =sign:agent:gall]
^- (quip card _this)
?+ -.sign (on-agent:def wire sign)
%kick
[[%pass / %agent [our.bol %contact-store] %watch /updates]~ this]
::
%fact
?+ p.cage.sign (on-agent:def wire sign)
%contact-update
=/ update=json (update-to-json !<(contact-update q.cage.sign))
[[%give %fact ~[/primary] %json !>(update)]~ this]
==
==
::
++ on-arvo
|= [=wire =sign-arvo]
^- (quip card _this)
?. ?=(%bound +<.sign-arvo)
(on-arvo:def wire sign-arvo)
[~ this]
::
++ on-leave on-leave:def
++ on-peek on-peek:def
++ on-fail on-fail:def
--
::
|_ bol=bowl:gall
++ poke-json
|= jon=json
^- (list card)
?> (team:title our.bol src.bol)
(poke-contact-view-action (json-to-view-action jon))
::
++ poke-contact-view-action
|= act=contact-view-action
^- (list card)
?- -.act
%create
?> ?=([@ *] path.act)
%+ weld
:~ (group-poke [%bundle path.act])
(contact-poke [%create path.act])
(contact-hook-poke [%add-owned path.act])
(group-hook-poke [%add our.bol path.act])
(group-poke [%add (~(put in ships.act) our.bol) path.act])
(perm-group-hook-poke [%associate path.act [[path.act %white] ~ ~]])
(permission-hook-poke [%add-owned path.act path.act])
==
(create-metadata path.act title.act description.act)
::
%delete
%+ weld
:~ (contact-hook-poke [%remove path.act])
(group-poke [%unbundle path.act])
(contact-poke [%delete path.act])
==
(delete-metadata path.act)
::
%remove
:~ (group-poke [%remove [ship.act ~ ~] path.act])
(contact-poke [%remove path.act ship.act])
==
::
%share
:: determine whether to send to our contact-hook or foreign
:: send contact-action to contact-hook with %add action
[(share-poke recipient.act [%add path.act ship.act contact.act])]~
==
++ poke-handle-http-request
|= =inbound-request:eyre
^- simple-payload:http
=+ url=(parse-request-line url.request.inbound-request)
=/ name=@t
=+ back-path=(flop site.url)
?~ back-path
''
i.back-path
?+ site.url not-found:gen
[%'~groups' %css %index ~] (css-response:gen style)
[%'~groups' %js %index ~] (js-response:gen script)
[%'~groups' %js %tile ~] (js-response:gen tile-js)
[%'~groups' %img *]
(png-response:gen (as-octs:mimes:html (~(got by contact-png) `@ta`name)))
::
:: avatar images
::
[%'~groups' %avatar @ *]
=/ =path (flop t.t.site.url)
?~ path not-found:gen
=/ contact (contact-scry `^path`(snoc (flop t.path) name))
?~ contact not-found:gen
?~ avatar.u.contact not-found:gen
?- -.u.avatar.u.contact
%url [[307 ['location' url.u.avatar.u.contact]~] ~]
%octt
=/ max-3-days ['cache-control' 'max-age=259200']
=/ content-type ['content-type' content-type.u.avatar.u.contact]
[[200 [content-type max-3-days ~]] `octs.u.avatar.u.contact]
==
::
[%'~groups' *] (html-response:gen index)
==
::
:: +utilities
::
++ contact-poke
|= act=contact-action
^- card
[%pass / %agent [our.bol %contact-store] %poke %contact-action !>(act)]
::
++ contact-hook-poke
|= act=contact-hook-action
^- card
[%pass / %agent [our.bol %contact-hook] %poke %contact-hook-action !>(act)]
::
++ share-poke
|= [=ship act=contact-action]
^- card
[%pass / %agent [ship %contact-hook] %poke %contact-action !>(act)]
::
++ group-poke
|= act=group-action
^- card
[%pass / %agent [our.bol %group-store] %poke %group-action !>(act)]
::
++ group-hook-poke
|= act=group-hook-action
^- card
[%pass / %agent [our.bol %group-hook] %poke %group-hook-action !>(act)]
::
++ metadata-poke
|= act=metadata-action
^- card
[%pass / %agent [our.bol %metadata-store] %poke %metadata-action !>(act)]
::
++ metadata-hook-poke
|= act=metadata-hook-action
^- card
[%pass / %agent [our.bol %metadata-hook] %poke %metadata-hook-action !>(act)]
::
++ perm-group-hook-poke
|= act=permission-group-hook-action
^- card
:* %pass / %agent [our.bol %permission-group-hook]
%poke %permission-group-hook-action !>(act)
==
::
++ permission-hook-poke
|= act=permission-hook-action
^- card
:* %pass / %agent [our.bol %permission-hook]
%poke %permission-hook-action !>(act)
==
::
++ create-metadata
|= [=path title=@t description=@t]
^- (list card)
=/ =metadata
%* . *metadata
title title
description description
date-created now.bol
creator our.bol
==
:~ (metadata-poke [%add path [%contacts path] metadata])
(metadata-hook-poke [%add-owned path])
==
::
++ delete-metadata
|= =path
^- (list card)
:~ (metadata-poke [%remove path [%contacts path]])
(metadata-hook-poke [%remove path])
==
::
++ all-scry
^- rolodex
.^(rolodex %gx /=contact-store/(scot %da now.bol)/all/noun)
::
++ contact-scry
|= pax=path
^- (unit contact)
=. pax ;:(weld /=contact-store/(scot %da now.bol)/contact pax /noun)
.^((unit contact) %gx pax)
--

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 880 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 865 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 951 B

View File

@ -0,0 +1,18 @@
<!doctype html>
<html>
<head>
<title>Groups</title>
<meta charset="utf-8" />
<meta name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"/>
<link rel="stylesheet" href="/~groups/css/index.css" />
<link rel="icon" type="image/png" href="/~launch/img/Favicon.png">
</head>
<body>
<div id="root" />
<script src="/~channel/channel.js"></script>
<script src="/~modulo/session.js"></script>
<script src="/~groups/js/index.js"></script>
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.1.12.min.js"></script>
</body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1115,7 +1115,7 @@
=/ fore-pos-diff (sub fore-pos pos) =/ fore-pos-diff (sub fore-pos pos)
=+ vex=((full parse-command-line:he-parser) [1 1] txt) =+ vex=((full parse-command-line:he-parser) [1 1] txt)
?. ?=([* ~ [* @ %ex *] *] vex) ?. ?=([* ~ [* @ %ex *] *] vex)
res (he-tab-not-hoon pos :(weld buf (tufa buf.say) "\0a"))
=/ typ p:(slop q:he-hoon-head !>(..dawn)) =/ typ p:(slop q:he-hoon-head !>(..dawn))
=/ tl (tab-list-hoon:auto typ p.q.q.p.u.q.vex) =/ tl (tab-list-hoon:auto typ p.q.q.p.u.q.vex)
=/ advance (advance-hoon:auto typ p.q.q.p.u.q.vex) =/ advance (advance-hoon:auto typ p.q.q.p.u.q.vex)
@ -1168,6 +1168,140 @@
*tank *tank
~(duck easy-print type) ~(duck easy-print type)
:: ::
:: Full tab complete for all Dojo sinks and sources is a madmans job.
:: Instead, we try to parse limited but common forms we know we can
:: autocomplete correctly
++ he-tab-not-hoon
|= [pos=@ud txt=tape]
^+ +>
=* res +>
|^
=/ naked-poke=(unit term)
%+ rust txt
(full (ifix [col (just `@`10)] ;~(pose sym (easy %$))))
?^ naked-poke
(complete-naked-poke u.naked-poke)
=/ variable=(unit term)
%+ rust txt
(full (ifix [tis (just `@`10)] ;~(pose sym (easy %$))))
?^ variable
(complete-variable u.variable)
=/ gen-poke-to-app=(unit [term term])
%+ rust txt
;~ sfix
;~ (glue bar)
;~(pose ;~(pfix col sym) (easy %$))
;~(pose sym (easy %$))
==
(just `@`10)
==
?^ gen-poke-to-app
(complete-gen-poke-to-app u.gen-poke-to-app)
=/ naked-gen=(unit term)
%+ rust txt
(full (ifix [lus (just `@`10)] ;~(pose sym (easy %$))))
?~ naked-gen
res
(complete-naked-gen u.naked-gen)
::
++ complete-naked-poke
|= app=term
=/ pax=path
/(scot %p our.hid)/[q.byk.hid]/(scot %da now.hid)/app
%+ complete (cat 3 ':' app)
%+ murn ~(tap by dir:.^(arch %cy pax))
|= [=term ~]
^- (unit [^term tank])
?. =(app (end 3 (met 3 app) term))
~
?~ =<(fil .^(arch %cy (weld pax ~[term %hoon])))
~
`[(cat 3 ':' term) *tank]
::
++ complete-variable
|= variable=term
%+ complete variable
%+ murn ~(tap by var)
|= [name=term =cage]
^- (unit [term tank])
?. =(variable (end 3 (met 3 variable) name))
~
`[name (sell q.cage)]
::
++ complete-gen-poke-to-app
|= [app=term gen=term]
=. app
?:(?=(%$ app) %hood app)
%+ complete
?: =(%hood app)
(cat 3 '|' gen)
:((cury cat 3) ':' app '|' gen)
=/ pfix=path
/(scot %p our.hid)/[q.byk.hid]/(scot %da now.hid)/gen/[app]
::
%^ tab-generators:auto pfix `app
%+ murn
~(tap by dir:.^(arch %cy pfix))
|= [=term ~]
?. =(gen (end 3 (met 3 gen) term))
~
?~ =<(fil .^(arch %cy (weld pfix ~[term %hoon])))
~
(some term)
::
++ complete-naked-gen
|= gen=term
%+ complete (cat 3 '+' gen)
=/ pax=path
/(scot %p our.hid)/[q.byk.hid]/(scot %da now.hid)/gen
%^ tab-generators:auto pax ~
%+ murn
~(tap by dir:.^(arch %cy pax))
|= [=term ~]
?. =(gen (end 3 (met 3 gen) term))
~
?~ =<(fil .^(arch %cy (weld pax ~[term %hoon])))
~
(some term)
::
++ complete
|= [completing=term options=(list [term tank])]
?~ options
res
=/ advance
(longest-match:auto options)
=. pos
(dec (lent txt)) :: lock cursor at end
=/ back-pos
(sub pos (met 3 completing))
=/ to-send
(trip (rsh 3 (sub pos back-pos) advance))
=| fxs=(list sole-effect)
::
:: Cursor is guaranteed to be at end so we don't worry about the
:: backwards case
::
=. res
|- ^+ res
?~ to-send
(he-diff %mor (flop fxs))
=^ lic say (~(transmit sole say) %ins pos `@c`i.to-send)
$(to-send t.to-send, fxs [`sole-effect`det+lic fxs], pos +(pos))
:: If no options, ring the bell
::
?: =(~ options)
(he-diff %bel ~)
:: If only one option, don't print unless the option is already
:: typed in.
::
?: &(?=([* ~] options) !=((met 3 advance) (met 3 completing)))
res
:: Else, print results
::
%+ he-diff %tab
options
--
::
++ he-type :: apply input ++ he-type :: apply input
|= act/sole-action |= act/sole-action
^+ +> ^+ +>

View File

@ -1,21 +1,21 @@
:: eth-watcher: ethereum event log collector :: eth-watcher: ethereum event log collector
:: ::
/- *eth-watcher, spider /- *eth-watcher, spider
/+ default-agent, verb /+ default-agent, verb, dbug
=, ethereum-types =, ethereum-types
=, able:jael =, able:jael
:: ::
=> |% => |%
+$ card card:agent:gall +$ card card:agent:gall
+$ app-state +$ app-state
$: %3 $: %4
dogs=(map path watchdog) dogs=(map path watchdog)
== ==
:: ::
+$ context [=path dog=watchdog] +$ context [=path dog=watchdog]
+$ watchdog +$ watchdog
$: config $: config
running=(unit =tid:spider) running=(unit [since=@da =tid:spider])
=number:block =number:block
=pending-logs =pending-logs
=history =history
@ -57,6 +57,7 @@
:: ::
:: Main :: Main
:: ::
%- agent:dbug
^- agent:gall ^- agent:gall
=| state=app-state =| state=app-state
%+ verb | %+ verb |
@ -97,7 +98,7 @@
:: ::
=? old-state ?=(%2 -.old-state) =? old-state ?=(%2 -.old-state)
%- (slog leaf+"upgrading eth-watcher from %2" ~) %- (slog leaf+"upgrading eth-watcher from %2" ~)
^- app-state ^- app-state-3
%= old-state %= old-state
- %3 - %3
dogs dogs
@ -108,10 +109,52 @@
== ==
== ==
:: ::
[cards-1 this(state ?>(?=(%3 -.old-state) old-state))] =? old-state ?=(%3 -.old-state)
%- (slog leaf+"upgrading eth-watcher from %3" ~)
^- app-state
%= old-state
- %4
dogs
%- ~(run by dogs.old-state)
|= dog=watchdog-3
%= dog
-
=, -.dog
[url eager refresh-rate (mul refresh-rate 6) from contracts topics]
::
running
?~ running.dog ~
`[now.bowl u.running.dog]
==
==
::
[cards-1 this(state ?>(?=(%4 -.old-state) old-state))]
:: ::
+$ app-states +$ app-states
$%(app-state-0 app-state-1 app-state-2 app-state) $%(app-state-0 app-state-1 app-state-2 app-state-3 app-state)
::
+$ app-state-3
$: %3
dogs=(map path watchdog-3)
==
::
+$ watchdog-3
$: config-3
running=(unit =tid:spider)
=number:block
=pending-logs
=history
blocks=(list block)
==
::
+$ config-3
$: url=@ta
eager=?
refresh-rate=@dr
from=number:block
contracts=(list address:ethereum)
=topics
==
:: ::
+$ app-state-2 +$ app-state-2
$: %2 $: %2
@ -174,11 +217,11 @@
?- -.poke ?- -.poke
%watch %watch
:: fully restart the watchdog if it doesn't exist yet, :: fully restart the watchdog if it doesn't exist yet,
:: or if the new config changes more than just the url or refresh rate. :: or if result-altering parts of the config changed.
=/ restart=? =/ restart=?
?| !(~(has by dogs.state) path.poke) ?| !(~(has by dogs.state) path.poke)
?! .= ->+:(~(got by dogs.state) path.poke) ?! .= ->+>+:(~(got by dogs.state) path.poke)
+>.config.poke +>+>.config.poke
== ==
:: ::
=/ already (~(has by dogs.state) path.poke) =/ already (~(has by dogs.state) path.poke)
@ -196,7 +239,7 @@
?=(^ running.u.dog) ?=(^ running.u.dog)
== ==
~ ~
=/ =cage [%spider-stop !>([u.running.u.dog &])] =/ =cage [%spider-stop !>([tid.u.running.u.dog &])]
:_ ~ :_ ~
`card`[%pass [%starting path.poke] %agent [our.bowl %spider] %poke cage] `card`[%pass [%starting path.poke] %agent [our.bowl %spider] %poke cage]
=/ new-dog =/ new-dog
@ -384,25 +427,34 @@
:: ::
%- (slog leaf+"eth-watcher failed; will retry" ~) %- (slog leaf+"eth-watcher failed; will retry" ~)
[[(wait path now.bowl refresh-rate.dog)]~ this] [[(wait path now.bowl refresh-rate.dog)]~ this]
:: start a new thread that checks for updates :: maybe kill a timed-out update thread, maybe start a new one
:: ::
=^ cards-1=(list card) dog =^ stop-cards=(list card) dog
:: if still running, kill it and restart :: if still running beyond timeout time, kill it
:: ::
?~ running.dog ?. ?& ?=(^ running.dog)
::
%+ gth now.bowl
(add since.u.running.dog timeout-time.dog)
==
`dog `dog
:: ::
%- (slog leaf+"eth-watcher still running; will restart" ~) %- (slog leaf+"eth-watcher {(spud path)} timed out; will restart" ~)
=/ =cage [%spider-stop !>([u.running.dog |])] =/ =cage [%spider-stop !>([tid.u.running.dog |])]
:_ dog(running ~) :_ dog(running ~)
:~ (leave-spider path our.bowl) :~ (leave-spider path our.bowl)
[%pass [%starting path] %agent [our.bowl %spider] %poke cage] [%pass [%starting path] %agent [our.bowl %spider] %poke cage]
== ==
:: ::
=^ cards-2=(list card) dog =^ start-cards=(list card) dog
:: if not (or no longer) running, start a new thread
::
?^ running.dog
`dog
::
=/ new-tid=@ta =/ new-tid=@ta
(cat 3 'eth-watcher--' (scot %uv eny.bowl)) (cat 3 'eth-watcher--' (scot %uv eny.bowl))
:_ dog(running `new-tid) :_ dog(running `[now.bowl new-tid])
=/ args =/ args
:^ ~ `new-tid %eth-watcher :^ ~ `new-tid %eth-watcher
!>(`watchpup`[- number pending-logs blocks]:dog) !>(`watchpup`[- number pending-logs blocks]:dog)
@ -410,7 +462,7 @@
(poke-spider path our.bowl %spider-start !>(args)) (poke-spider path our.bowl %spider-start !>(args))
== ==
:: ::
:- [(wait path now.bowl refresh-rate.dog) (weld cards-1 cards-2)] :- [(wait path now.bowl refresh-rate.dog) (weld stop-cards start-cards)]
this(dogs.state (~(put by dogs.state) path dog)) this(dogs.state (~(put by dogs.state) path dog))
== ==
:: ::

View File

@ -51,6 +51,7 @@
:: ::
++ node-url 'http://eth-mainnet.urbit.org:8545' ++ node-url 'http://eth-mainnet.urbit.org:8545'
++ refresh-rate ~h1 ++ refresh-rate ~h1
++ timeout-time ~h2
-- --
:: ::
=| state-0 =| state-0
@ -207,6 +208,7 @@
:* node-url :* node-url
| |
refresh-rate refresh-rate
timeout-time
public:mainnet-contracts public:mainnet-contracts
~[azimuth delegated-sending]:mainnet-contracts ~[azimuth delegated-sending]:mainnet-contracts
~ ~

View File

@ -59,7 +59,7 @@
:_ this :_ ~ :_ this :_ ~
[%pass /dill %arvo %d %flog %crud %goad-fail u.error.sign-arvo] [%pass /dill %arvo %d %flog %crud %goad-fail u.error.sign-arvo]
%- (slog leaf+"goad: recompiling all apps" ~) %- (slog leaf+"goad: recompiling all apps" ~)
[(goad |) this] [(goad &) this]
== ==
:: ::
++ on-fail on-fail:def ++ on-fail on-fail:def

View File

@ -2,6 +2,7 @@
:: ::
/- *group-store, *group-hook /- *group-store, *group-hook
/+ default-agent, verb, dbug /+ default-agent, verb, dbug
~% %group-hook-top ..is ~
|% |%
+$ card card:agent:gall +$ card card:agent:gall
:: ::
@ -33,8 +34,18 @@
++ on-init on-init:def ++ on-init on-init:def
++ on-save !>(state) ++ on-save !>(state)
++ on-load ++ on-load
|= old=vase |= =vase
`this(state !<(state-zero old)) ^- (quip card _this)
=/ old !<(state-zero vase)
:_ this(state old)
%+ murn ~(tap by synced.old)
|= [=path =ship]
^- (unit card)
=/ =wire [(scot %p ship) %group path]
=/ =term ?:(=(our.bowl ship) %group-store %group-hook)
?: (~(has by wex.bowl) [wire ship term]) ~
`[%pass wire %agent [ship term] %watch [%group path]]
::
++ on-leave on-leave:def ++ on-leave on-leave:def
++ on-peek on-peek:def ++ on-peek on-peek:def
++ on-arvo on-arvo:def ++ on-arvo on-arvo:def
@ -74,20 +85,26 @@
?~ p.sign ?~ p.sign
[~ this] [~ this]
%- (slog u.p.sign) %- (slog u.p.sign)
?> ?=([@ @ *] wire) ?> ?=([@ %group ^] wire)
=/ =ship (slav %p i.wire) =/ =ship (slav %p i.wire)
=. synced.state (~(del by synced.state) t.t.wire) =* group t.t.wire
:: only remove from synced if this watch-nack came from the ship we
:: thought we were actively syncing from
::
=? synced.state
=(ship (~(gut by synced.state) group ship))
(~(del by synced.state) group)
[~ this] [~ this]
:: ::
%kick %kick
?> ?=([@ @ *] wire) ?> ?=([@ %group ^] wire)
=/ =ship (slav %p i.wire) =/ =ship (slav %p i.wire)
?. (~(has by synced.state) wire) =* group t.t.wire
?. (~(has by synced.state) group)
[~ this] [~ this]
=/ group-path [%group wire] =* group-path t.wire
=/ group-wire [i.wire group-path]
:_ this :_ this
[%pass group-wire %agent [ship %group-hook] %watch group-path]~ [%pass wire %agent [ship %group-hook] %watch group-path]~
:: ::
%fact %fact
?. ?=(%group-update p.cage.sign) ?. ?=(%group-update p.cage.sign)
@ -150,10 +167,9 @@
%remove [(update-subscribers [%group pax.diff] diff) state] %remove [(update-subscribers [%group pax.diff] diff) state]
:: ::
%unbundle %unbundle
:_ state(synced (~(del by synced.state) pax.diff)) =/ ship (~(get by synced.state) pax.diff)
%+ snoc ?~ ship [~ state]
(update-subscribers [%group pax.diff] diff) (poke-group-hook-action [%remove pax.diff])
[%give %kick [%group pax.diff]~ ~]
== ==
:: ::
++ handle-foreign ++ handle-foreign
@ -162,17 +178,29 @@
?- -.diff ?- -.diff
%keys [~ state] %keys [~ state]
%bundle [~ state] %bundle [~ state]
::
%path %path
:_ state :_ state
?~ pax.diff ~ ?~ pax.diff ~
=/ ship (~(get by synced.state) pax.diff) =/ ship (~(get by synced.state) pax.diff)
?~ ship ~ ?~ ship ~
?. =(src.bol u.ship) ~ ?. =(src.bol u.ship) ~
:~ (group-poke pax.diff [%unbundle pax.diff]) =/ have-group=(unit group)
(group-poke pax.diff [%bundle pax.diff]) (group-scry pax.diff)
(group-poke pax.diff [%add members.diff pax.diff]) ?~ have-group
== :: if we don't have the group yet, create it
::
:~ (group-poke pax.diff [%bundle pax.diff])
(group-poke pax.diff [%add members.diff pax.diff])
==
:: if we already have the group, calculate and apply the diff
::
=/ added=group (~(dif in members.diff) u.have-group)
=/ removed=group (~(dif in u.have-group) members.diff)
%+ weld
?~ added ~
[(group-poke pax.diff [%add added pax.diff])]~
?~ removed ~
[(group-poke pax.diff [%remove removed pax.diff])]~
:: ::
%add %add
:_ state :_ state
@ -183,23 +211,26 @@
[(group-poke pax.diff diff)]~ [(group-poke pax.diff diff)]~
:: ::
%remove %remove
:_ state ?~ pax.diff [~ state]
?~ pax.diff ~
=/ ship (~(get by synced.state) pax.diff) =/ ship (~(get by synced.state) pax.diff)
?~ ship ~ ?~ ship [~ state]
?. =(src.bol u.ship) ~ ?. =(src.bol u.ship) [~ state]
[(group-poke pax.diff diff)]~ ?. (~(has in members.diff) our.bol)
:_ state
[(group-poke pax.diff diff)]~
=/ changes (poke-group-hook-action [%remove pax.diff])
:_ +.changes
%+ welp -.changes
:~ (group-poke pax.diff diff)
(group-poke pax.diff [%unbundle pax.diff])
==
:: ::
%unbundle %unbundle
?~ pax.diff ?~ pax.diff [~ state]
[~ state]
=/ ship (~(get by synced.state) pax.diff) =/ ship (~(get by synced.state) pax.diff)
?~ ship ?~ ship [~ state]
[~ state] ?. =(src.bol u.ship) [~ state]
?. =(src.bol u.ship) (poke-group-hook-action [%remove pax.diff])
[~ state]
:_ state(synced (~(del by synced.state) pax.diff))
[(group-poke pax.diff diff)]~
== ==
:: ::
++ group-poke ++ group-poke
@ -226,5 +257,4 @@
?: =(u.shp our.bol) ?: =(u.shp our.bol)
[%pass wir %agent [our.bol %group-store] %leave ~]~ [%pass wir %agent [our.bol %group-store] %leave ~]~
[%pass wir %agent [u.shp %group-hook] %leave ~]~ [%pass wir %agent [u.shp %group-hook] %leave ~]~
::
-- --

View File

@ -57,6 +57,7 @@
=/ cards=(list card) =/ cards=(list card)
?+ path (on-watch:def path) ?+ path (on-watch:def path)
[%all ~] (give %group-initial !>(groups)) [%all ~] (give %group-initial !>(groups))
[%updates ~] ~
[%keys ~] (give %group-update !>([%keys ~(key by groups)])) [%keys ~] (give %group-update !>([%keys ~(key by groups)]))
[%group *] [%group *]
(give %group-update !>([%path (~(got by groups) t.path) t.path])) (give %group-update !>([%path (~(got by groups) t.path) t.path]))
@ -158,6 +159,7 @@
^- (list card) ^- (list card)
%- zing %- zing
:~ (update-subscribers /all act) :~ (update-subscribers /all act)
(update-subscribers /updates act)
(update-subscribers [%group pax] act) (update-subscribers [%group pax] act)
?. |(=(%bundle -.act) =(%unbundle -.act)) ?. |(=(%bundle -.act) =(%unbundle -.act))
~ ~

View File

@ -43,9 +43,9 @@
!: !:
=> |% :: => |% ::
++ hood-old :: unified old-state ++ hood-old :: unified old-state
{?($1 $2) lac/(map @tas hood-part-old)} :: {?($1 $2 $3 $4) lac/(map @tas hood-part-old)} ::
++ hood-1 :: unified state ++ hood-1 :: unified state
{$2 lac/(map @tas hood-part)} :: {$4 lac/(map @tas hood-part)} ::
++ hood-good :: extract specific ++ hood-good :: extract specific
=+ hed=$:hood-head =+ hed=$:hood-head
|@ ++ $ |@ ++ $
@ -140,16 +140,19 @@
`..on-init `..on-init
:: ::
++ on-save ++ on-save
!>([%2 lac]) !>([%4 lac])
:: ::
++ on-load ++ on-load
|= =old-state=vase |= =old-state=vase
=/ old-state !<(hood-old old-state-vase) =/ old-state !<(hood-old old-state-vase)
=^ cards lac =^ cards lac
=. lac lac.old-state =. lac lac.old-state
?. ?=(%1 -.old-state) ?- -.old-state
`lac %1 ((wrap on-load):from-drum:(help hid) %1)
((wrap on-load):from-drum:(help hid) %1) %2 ((wrap on-load):from-drum:(help hid) %2)
%3 ((wrap on-load):from-drum:(help hid) %3)
%4 `lac
==
[cards ..on-init] [cards ..on-init]
:: ::
++ on-poke ++ on-poke

View File

@ -4,7 +4,7 @@
:: can be poked by the host team to send an invite out to someone. :: can be poked by the host team to send an invite out to someone.
:: can be poked by foreign ships to send an invite to us. :: can be poked by foreign ships to send an invite to us.
:: ::
/+ *invite-json, default-agent, verb /+ *invite-json, default-agent, verb, dbug
:: ::
|% |%
+$ state-0 [%0 ~] +$ state-0 [%0 ~]
@ -16,6 +16,7 @@
=* state - =* state -
:: ::
%+ verb | %+ verb |
%- agent:dbug
^- agent:gall ^- agent:gall
=< =<
|_ =bowl:gall |_ =bowl:gall
@ -49,12 +50,10 @@
%invite-action %invite-action
=/ act=invite-action !<(invite-action vase) =/ act=invite-action !<(invite-action vase)
?. ?=(%invite -.act) ~ ?. ?=(%invite -.act) ~
:: if the sender is us,
::
?: (team:title our.bowl src.bowl) ?: (team:title our.bowl src.bowl)
:: outgoing. we must be inviting another ship. send them the invite. :: outgoing. we must be inviting another ship. send them the invite.
:: ::
?> !(team:title our.bowl recipient.invite.act) ?< (team:title our.bowl recipient.invite.act)
[(invite-hook-poke:do recipient.invite.act act)]~ [(invite-hook-poke:do recipient.invite.act act)]~
:: else incoming. ensure invitatory exists and invite is not a duplicate. :: else incoming. ensure invitatory exists and invite is not a duplicate.
:: ::

View File

@ -1,4 +1,4 @@
/+ *invite-json, default-agent /+ *invite-json, default-agent, dbug
|% |%
+$ card card:agent:gall +$ card card:agent:gall
:: ::
@ -14,6 +14,7 @@
:: ::
=| state-zero =| state-zero
=* state - =* state -
%- agent:dbug
^- agent:gall ^- agent:gall
=< =<
|_ bol=bowl:gall |_ bol=bowl:gall

View File

@ -6,7 +6,7 @@
:: ::
::TODO could maybe use /lib/proxy-hook, be renamed invite-proxy-hook ::TODO could maybe use /lib/proxy-hook, be renamed invite-proxy-hook
:: ::
/+ *invite-json, default-agent /+ *invite-json, default-agent, dbug
:: ::
|% |%
+$ card card:agent:gall +$ card card:agent:gall
@ -19,6 +19,8 @@
^- card ^- card
[%pass /store %agent [our %invite-store] %watch /updates] [%pass /store %agent [our %invite-store] %watch /updates]
-- --
::
%- agent:dbug
^- agent:gall ^- agent:gall
|_ =bowl:gall |_ =bowl:gall
+* this . +* this .

View File

@ -1,8 +1,8 @@
/- launch /- launch
/+ *server, default-agent /+ *server, default-agent, dbug
:: ::
/= index /= index
/^ $-(marl manx) /^ $-([json marl] manx)
/: /===/app/launch/index /!noun/ /: /===/app/launch/index /!noun/
/= script /= script
/^ octs /^ octs
@ -11,6 +11,13 @@
/| /js/ /| /js/
/~ ~ /~ ~
== ==
/= channel-js
/^ octs
/; as-octs:mimes:html
/: /===/app/launch/js/channel
/| /js/
/~ ~
==
/= style /= style
/^ octs /^ octs
/; as-octs:mimes:html /; as-octs:mimes:html
@ -24,53 +31,108 @@
:: ::
|% |%
+$ versioned-state +$ versioned-state
$% state-zero $% [%0 state-zero]
[%1 state-two]
[%2 state-two]
[%3 state-two]
== ==
+$ state-zero +$ state-zero
$: %0 $: tiles=(set tile:launch)
tiles=(set tile:launch)
data=tile-data:launch data=tile-data:launch
path-to-tile=(map path @tas) path-to-tile=(map path @tas)
== ==
+$ state-two
$: tiles=(set tile:launch)
data=tile-data:launch
path-to-tile=(map path @tas)
first-time=?
==
:: ::
+$ card card:agent:gall +$ card card:agent:gall
++ launch-who
|= =desk
[%pass /who %arvo %e %serve [~ /who] desk /gen/who/hoon ~]
-- --
:: ::
=| state-zero =| [%3 state-two]
=* state - =* state -
%- agent:dbug
^- agent:gall ^- agent:gall
|_ bol=bowl:gall |_ bol=bowl:gall
+* this . +* this .
def ~(. (default-agent this %|) bol) def ~(. (default-agent this %|) bol)
++ on-init ++ on-init
^- (quip card _this) ^- (quip card _this)
:_ this :_ this(state *[%3 state-two])
[%pass / %arvo %e %connect [~ /] %launch]~ :~ (launch-who q.byk.bol)
[%pass / %arvo %e %connect [~ /] %launch]
==
:: ::
++ on-save !>(state) ++ on-save !>(state)
:: ::
++ on-load ++ on-load
|= old=vase |= old=vase
`this(state !<(state-zero old)) ^- (quip card _this)
=/ old-state !<(versioned-state old)
=| cards=(list card)
|-
?- -.old-state
%0
$(old-state [%1 tiles data path-to-tile %.n]:old-state)
::
%1
=/ new-state=state-two
=, old-state
:* (~(del in tiles) [%contact-view /primary])
(~(del by data) %contact-view)
(~(del by path-to-tile) /primary)
first-time
==
$(old-state [%2 new-state])
::
%2
$(old-state [%3 +.old-state], cards [(launch-who q.byk.bol) cards])
::
%3
[(flop cards) this(state old-state)]
==
:: ::
++ on-poke ++ on-poke
|= [mar=mark vas=vase] |= [mar=mark vas=vase]
^- (quip card _this) ^- (quip card _this)
?+ mar (on-poke:def mar vas) ?+ mar (on-poke:def mar vas)
%json
?> (team:title our.bol src.bol)
=/ jon !<(json vas)
:- ~
?. =(jon [%s 'disable welcome message'])
this
this(first-time %.n)
:: ::
%launch-action %launch-action
=/ act !<(action:launch vas) =/ act !<(action:launch vas)
=/ beforedata (~(get by data) name.act) ?- -.act
=/ newdata %add
?~ beforedata =/ beforedata (~(get by data) name.act)
(~(put by data) name.act [*json url.act]) =/ newdata
(~(put by data) name.act [jon.u.beforedata url.act]) ?~ beforedata
=/ new-tile `tile:launch`[`@tas`name.act `path`subscribe.act] (~(put by data) name.act [*json url.act])
:- [%pass subscribe.act %agent [our.bol name.act] %watch subscribe.act]~ (~(put by data) name.act [jon.u.beforedata url.act])
%= this =/ new-tile `tile:launch`[`@tas`name.act `path`subscribe.act]
tiles (~(put in tiles) new-tile) :- [%pass subscribe.act %agent [our.bol name.act] %watch subscribe.act]~
data newdata %= this
path-to-tile (~(put by path-to-tile) subscribe.act name.act) tiles (~(put in tiles) new-tile)
data newdata
path-to-tile (~(put by path-to-tile) subscribe.act name.act)
==
::
%remove
:- [%pass subscribe.act %agent [our.bol name.act] %leave ~]~
%= this
tiles (~(del in tiles) [name.act subscribe.act])
data (~(del by data) name.act)
path-to-tile (~(del by path-to-tile) subscribe.act)
==
== ==
:: ::
%handle-http-request %handle-http-request
@ -89,9 +151,10 @@
?+ site.request-line ?+ site.request-line
not-found:gen not-found:gen
:: ::
~ [~ ~]
=/ hym=manx =/ hym=manx
%- index %+ index
[%b first-time]
^- marl ^- marl
%+ turn ~(tap by data) %+ turn ~(tap by data)
|= [key=@tas [jon=json url=@t]] |= [key=@tas [jon=json url=@t]]
@ -119,6 +182,9 @@
"window.urb = new Channel();" "window.urb = new Channel();"
== ==
(js-response:gen session-js) (js-response:gen session-js)
::
[%'~channel' %channel ~]
(js-response:gen channel-js)
== ==
== ==
:: ::

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 B

View File

@ -1,4 +1,4 @@
|= scripts=marl |= [startup=json scripts=marl]
;html ;html
;head ;head
;title: Home ;title: Home
@ -11,9 +11,10 @@
== ==
;body ;body
;div#root; ;div#root;
;script@"/~/channel/channel.js"; ;script@"/~channel/channel.js";
;script@"/~modulo/session.js"; ;script@"/~modulo/session.js";
;* scripts ;* scripts
;script@"/~launch/js/index.js"; ;script@"/~launch/js/index.js";
;script: window.startupMessage = {(en-json:html startup)}
== ==
== ==

View File

@ -0,0 +1,231 @@
class Channel {
constructor() {
this.init();
this.deleteOnUnload();
// a way to handle channel errors
//
//
this.onChannelError = (err) => {
console.error('event source error: ', err);
};
}
init() {
// unique identifier: current time and random number
//
this.uid =
new Date().getTime().toString() +
"-" +
Math.random().toString(16).slice(-6);
this.requestId = 1;
// the currently connected EventSource
//
this.eventSource = null;
// the id of the last EventSource event we received
//
this.lastEventId = 0;
// this last event id acknowledgment sent to the server
//
this.lastAcknowledgedEventId = 0;
// a registry of requestId to successFunc/failureFunc
//
// These functions are registered during a +poke and are executed
// in the onServerEvent()/onServerError() callbacks. Only one of
// the functions will be called, and the outstanding poke will be
// removed after calling the success or failure function.
//
this.outstandingPokes = new Map();
// a registry of requestId to subscription functions.
//
// These functions are registered during a +subscribe and are
// executed in the onServerEvent()/onServerError() callbacks. The
// event function will be called whenever a new piece of data on this
// subscription is available, which may be 0, 1, or many times. The
// disconnect function may be called exactly once.
//
this.outstandingSubscriptions = new Map();
}
setOnChannelError(onError = (err) => {}) {
this.onChannelError = onError;
}
deleteOnUnload() {
window.addEventListener("unload", (event) => {
this.delete();
});
}
// sends a poke to an app on an urbit ship
//
poke(ship, app, mark, json, successFunc, failureFunc) {
let id = this.nextId();
this.outstandingPokes.set(
id,
{
success: successFunc,
fail: failureFunc
}
);
this.sendJSONToChannel({
id,
action: "poke",
ship,
app,
mark,
json
});
}
// subscribes to a path on an specific app and ship.
//
// Returns a subscription id, which is the same as the same internal id
// passed to your Urbit.
subscribe(
ship,
app,
path,
connectionErrFunc = () => {},
eventFunc = () => {},
quitFunc = () => {}) {
let id = this.nextId();
this.outstandingSubscriptions.set(
id,
{
err: connectionErrFunc,
event: eventFunc,
quit: quitFunc
}
);
this.sendJSONToChannel({
id,
action: "subscribe",
ship,
app,
path
});
return id;
}
// quit the channel
//
delete() {
let id = this.nextId();
navigator.sendBeacon(this.channelURL(), JSON.stringify([{
id,
action: "delete"
}]));
}
// unsubscribe to a specific subscription
//
unsubscribe(subscription) {
let id = this.nextId();
this.sendJSONToChannel({
id,
action: "unsubscribe",
subscription
});
}
// sends a JSON command command to the server.
//
sendJSONToChannel(j) {
let req = new XMLHttpRequest();
req.open("PUT", this.channelURL());
req.setRequestHeader("Content-Type", "application/json");
if (this.lastEventId == this.lastAcknowledgedEventId) {
let x = JSON.stringify([j]);
req.send(x);
} else {
// we add an acknowledgment to clear the server side queue
//
// The server side puts messages it sends us in a queue until we
// acknowledge that we received it.
//
let x = JSON.stringify(
[{action: "ack", "event-id": parseInt(this.lastEventId)}, j]
);
req.send(x);
this.lastEventId = this.lastAcknowledgedEventId;
}
this.connectIfDisconnected();
}
// connects to the EventSource if we are not currently connected
//
connectIfDisconnected() {
if (this.eventSource) {
return;
}
this.eventSource = new EventSource(this.channelURL(), {withCredentials:true});
this.eventSource.onmessage = e => {
this.lastEventId = e.lastEventId;
let obj = JSON.parse(e.data);
let pokeFuncs = this.outstandingPokes.get(obj.id);
let subFuncs = this.outstandingSubscriptions.get(obj.id);
if (obj.response == "poke" && !!pokeFuncs) {
let funcs = pokeFuncs;
if (obj.hasOwnProperty("ok")) {
funcs["success"]();
} else if (obj.hasOwnProperty("err")) {
funcs["fail"](obj.err);
} else {
console.error("Invalid poke response: ", obj);
}
this.outstandingPokes.delete(obj.id);
} else if (obj.response == "subscribe" ||
(obj.response == "poke" && !!subFuncs)) {
let funcs = subFuncs;
// on a response to a subscribe, we only notify the caller on err
//
if (obj.hasOwnProperty("err")) {
funcs["err"](obj.err);
this.outstandingSubscriptions.delete(obj.id);
}
} else if (obj.response == "diff") {
let funcs = subFuncs;
funcs["event"](obj.json);
} else if (obj.response == "quit") {
let funcs = subFuncs;
funcs["quit"](obj);
this.outstandingSubscriptions.delete(obj.id);
} else {
console.log("Unrecognized response: ", e);
}
}
this.eventSource.onerror = e => {
this.delete();
this.init();
this.onChannelError(e);
}
}
channelURL() {
return "/~/channel/" + this.uid;
}
nextId() {
return this.requestId++;
}
};

File diff suppressed because one or more lines are too long

View File

@ -1,30 +1,70 @@
:: link-listen-hook: get your friends' bookmarks :: link-listen-hook: get your friends' bookmarks
:: ::
:: on-init, subscribes to all groups on this ship. :: keeps track of a listening=(set app-path). users can manually add to and
:: for every ship in a group, we subscribe to their link's local-pages :: remove from this set.
:: at the group path (through link-proxy-hook),
:: and forwards all entries into our link as submissions.
:: ::
/- *link, group-store :: for all ships in groups associated with those resources, we subscribe to
/+ default-agent, verb :: their link's local-pages and annotations at the resource path (through
:: link-proxy-hook), and forward all entries into our link-store as
:: submissions and comments.
:: ::
:: if a subscription to a target fails, we assume it's because their
:: metadata+groups definition hasn't been updated to include us yet.
:: we retry with exponential backoff, maxing out at one hour timeouts.
:: to expede this process, we prod other potential listeners when we add
:: them to our metadata+groups definition.
::
/- link-listen-hook, *metadata-store, *link, group-store
/+ mdl=metadata, default-agent, verb, dbug
::
~% %link-listen-hook-top ..is ~
|% |%
+$ state-0 +$ versioned-state
$: %0 $% [%0 state-0]
~ [%1 state-1]
::NOTE this means we could get away with just producing cards everywhere, [%2 state-2]
:: never producing new state outside of the agent interface core.
:: we opt to keep ^-(quip card _state) in place for most logic arms
:: because it doesn't cost much, results in unsurprising code, and
:: makes adding any state in the future easier.
== ==
+$ state-2 state-1
+$ state-1
$: listening=(set app-path)
state-0
==
+$ state-0
$: retry-timers=(map target @dr)
:: reasoning: the resources we're subscribed to,
:: and the groups that cause that.
::
:: we don't strictly need to track this in state, but doing so heavily
:: simplifies logic and reduces the amount of big scries we do.
:: this also gives us the option to check & restore subscriptions,
:: should we ever need that.
::
reasoning=(jug [ship app-path] group-path)
==
::
+$ what-target ?(%local-pages %annotations)
+$ target
$: what=what-target
who=ship
where=path
==
++ wire-to-target
|= =wire
^- target
?> ?=([what-target @ ^] wire)
[i.wire (slav %p i.t.wire) t.t.wire]
++ target-to-wire
|= target
^- wire
[what (scot %p who) where]
:: ::
+$ card card:agent:gall +$ card card:agent:gall
-- --
:: ::
=| state-0 =| [%2 state-2]
=* state - =* state -
:: ::
%- agent:dbug
%+ verb | %+ verb |
^- agent:gall ^- agent:gall
=< =<
@ -36,53 +76,244 @@
++ on-init ++ on-init
^- (quip card _this) ^- (quip card _this)
:_ this :_ this
[watch-groups:do]~ ~[watch-metadata:do watch-groups:do]
:: ::
++ on-save !>(state) ++ on-save !>(state)
++ on-load ++ on-load
|= old=vase |= =vase
^- (quip card _this) ^- (quip card _this)
[~ this(state !<(state-0 old))] =/ old=versioned-state
!<(versioned-state vase)
|-
?- -.old
%2 [~ this(state old)]
::
%1
:: the upgrade from 0 left out local-only collections.
:: here, we pull those back in.
::
=. state [%2 +.old]
=. listening.state
(~(run in ~(key by reasoning.old)) tail)
=/ resources=(list [=group-path =app-path])
%~ tap in
%. %link
%~ get ju
.^ (jug app-name [group-path app-path])
%gy
(scot %p our.bowl)
%metadata-store
(scot %da now.bowl)
/app-indices
==
=| cards=(list card)
|-
?~ resources [cards this]
=, i.resources
=/ =group:group-store
=- (fall - *group:group-store)
(scry-for:do (unit group:group-store) %group-store group-path)
:: if we're the only group member, this got incorrectly ignored
:: during 0's upgrade logic. watch it now.
::
?. &(=(1 ~(wyt in group)) (~(has in group) our.bowl))
$(resources t.resources)
=^ more-cards state
(handle-listen-action:do %watch app-path)
$(resources t.resources, cards (weld more-cards cards))
::
%0
=/ listening=(set app-path)
(~(run in ~(key by reasoning.old)) tail)
$(old [%1 listening +.old])
==
:: ::
++ on-agent ++ on-agent
|= [=wire =sign:agent:gall] |= [=wire =sign:agent:gall]
^- (quip card _this) ^- (quip card _this)
?: ?=([%groups ~] wire) =^ cards state
=^ cards state ?+ wire ~|([dap.bowl %weird-agent-wire wire] !!)
[%metadata ~]
(take-metadata-sign:do sign)
::
[%groups ~]
(take-groups-sign:do sign) (take-groups-sign:do sign)
[cards this] ::
?: ?=([%links @ ^] wire) [%links ?(%local-pages %annotations) @ ^]
=^ cards state (take-link-sign:do (wire-to-target t.wire) sign)
(take-links-sign:do (slav %p i.t.wire) t.t.wire sign) ::
[cards this] [%forward ^]
?: ?=([%forward ^] wire)
=^ cards state
(take-forward-sign:do t.wire sign) (take-forward-sign:do t.wire sign)
::
[%prod *]
?> ?=(%poke-ack -.sign)
?~ p.sign [~ state]
%- (slog leaf+"prod failed" u.p.sign)
[~ state]
==
[cards this]
::
++ on-poke
|= [=mark =vase]
?+ mark (on-poke:def mark vase)
%link-listen-poke
=/ =path !<(path vase)
:_ this
%+ weld
(take-retry:do %local-pages src.bowl path)
(take-retry:do %annotations src.bowl path)
::
%link-listen-action
?> (team:title [our src]:bowl)
=^ cards state
~| p.vase
(handle-listen-action:do !<(action:link-listen-hook vase))
[cards this] [cards this]
~| [dap.bowl %weird-wire wire] ==
!!
:: ::
++ on-arvo ++ on-arvo
|= [=wire =sign-arvo] |= [=wire =sign-arvo]
^- (quip card _this) ^- (quip card _this)
?. ?=([%g %done *] sign-arvo) ?+ sign-arvo (on-arvo:def wire sign-arvo)
(on-arvo:def wire sign-arvo) [%g %done *]
?~ error.sign-arvo [~ this] ?~ error.sign-arvo [~ this]
=/ =tank leaf+"{(trip dap.bowl)}'s message went wrong!" =/ =tank leaf+"{(trip dap.bowl)}'s message went wrong!"
%- (slog tank tang.u.error.sign-arvo) %- (slog tank tang.u.error.sign-arvo)
[~ this] [~ this]
::
[%b %wake *]
?> ?=([%retry @ @ ^] wire)
?^ error.sign-arvo
=/ =tank leaf+"wake on {(spud wire)} went wrong!"
%- (slog tank u.error.sign-arvo)
[~ this]
:_ this
(take-retry:do (wire-to-target t.wire))
==
::
++ on-peek
|= =path
^- (unit (unit cage))
?+ path ~
[%x %listening ~] ``noun+!>(listening)
[%x %listening ^] ``noun+!>((~(has in listening) t.t.path))
==
::
++ on-watch
|= =path
^- (quip card _this)
?. ?=([%listening ~] path) (on-watch:def path)
?> (team:title [our src]:bowl)
:_ this
[%give %fact ~ %link-listen-update !>([%listening listening])]~
:: ::
++ on-poke on-poke:def
++ on-peek on-peek:def
++ on-watch on-watch:def
++ on-leave on-leave:def ++ on-leave on-leave:def
++ on-fail on-fail:def ++ on-fail on-fail:def
-- --
:: ::
:: ::
|_ =bowl:gall |_ =bowl:gall
+* md ~(. mdl bowl)
:: ::
:: groups subscription :: user actions & updates
::
++ handle-listen-action
|= =action:link-listen-hook
^- (quip card _state)
::NOTE no-opping where appropriate happens further down the call stack.
:: we *could* no-op here, as %watch when we're already listening should
:: result in no-ops all the way down, but walking through everything
:: makes this a nice "resurrect if broken unexpectedly" option.
::
=* app-path path.action
=^ cards listening
^- (quip card _listening)
=/ had=? (~(has in listening) app-path)
?- -.action
%watch
:_ (~(put in listening) app-path)
?:(had ~ [(send-update action)]~)
::
%leave
:_ (~(del in listening) app-path)
?.(had ~ [(send-update action)]~)
==
=/ groups=(list group-path)
(groups-from-resource:md %link app-path)
|-
?~ groups [cards state]
=^ more-cards state
?- -.action
%watch (listen-to-group app-path i.groups)
%leave (leave-from-group app-path i.groups)
==
$(cards (weld cards more-cards), groups t.groups)
::
++ send-update
|= =update:link-listen-hook
^- card
[%give %fact ~[/listening] %link-listen-update !>(update)]
::
:: metadata subscription
::
++ watch-metadata
^- card
[%pass /metadata %agent [our.bowl %metadata-store] %watch /app-name/link]
::
++ take-metadata-sign
|= =sign:agent:gall
^- (quip card _state)
?- -.sign
%poke-ack ~|([dap.bowl %unexpected-poke-ack /metadata] !!)
%kick [[watch-metadata]~ state]
::
%watch-ack
?~ p.sign [~ state]
=/ =tank
:- %leaf
"{(trip dap.bowl)} failed subscribe to metadata store. very wrong!"
%- (slog tank u.p.sign)
[~ state]
::
%fact
=* mark p.cage.sign
=* vase q.cage.sign
?. ?=(%metadata-update mark)
~| [dap.bowl %unexpected-mark mark]
!!
%- handle-metadata-update
!<(metadata-update vase)
==
::
++ handle-metadata-update
|= upd=metadata-update
^- (quip card _state)
?+ -.upd [~ state]
%add
?> =(%link app-name.resource.upd)
:: auto-listen to collections in unmanaged groups only
::
?. ?=([%'~' ^] group-path.upd) [~ state]
=, resource.upd
=^ update listening
^- (quip card _listening)
?: (~(has in listening) app-path)
[~ listening]
:- [(send-update %watch app-path)]~
(~(put in listening) app-path)
=^ cards state
(listen-to-group app-path group-path.upd)
[(weld update cards) state]
::
%remove
?> =(%link app-name.resource.upd)
=? listening
?=(~ (groups-from-resource:md %link app-path.resource.upd))
(~(del in listening) app-path.resource.upd)
(leave-from-group app-path.resource.upd group-path.upd)
==
::
:: groups subscriptions
:: ::
++ watch-groups ++ watch-groups
^- card ^- card
@ -106,111 +337,268 @@
%fact %fact
=* mark p.cage.sign =* mark p.cage.sign
=* vase q.cage.sign =* vase q.cage.sign
~& [dap.bowl %fact mark]
?+ mark ~|([dap.bowl %unexpected-mark mark] !!) ?+ mark ~|([dap.bowl %unexpected-mark mark] !!)
%group-initial (handle-group-initial !<(groups:group-store vase)) %group-initial [~ state] ::NOTE initial handled using metadata
%group-update (handle-group-update !<(group-update:group-store vase)) %group-update (handle-group-update !<(group-update:group-store vase))
== ==
== ==
:: ::
++ handle-group-initial
|= =groups:group-store
^- (quip card _state)
=| cards=(list card)
=/ groups=(list [=path =group:group-store])
~(tap by groups)
|-
?~ groups [cards state]
=^ caz state
%- handle-group-update
[%add [group path]:i.groups]
$(cards (weld cards caz), groups t.groups)
::
++ handle-group-update ++ handle-group-update
|= upd=group-update:group-store |= upd=group-update:group-store
^- (quip card _state) ^- (quip card _state)
:_ state ?. ?=(?(%path %add %remove) -.upd)
?+ -.upd ~ [~ state]
?(%path %add %remove) =/ socs=(list app-path)
=/ whos=(list ship) ~(tap in members.upd) (app-paths-from-group:md %link pax.upd)
|- ^- (list card) =/ whos=(list ship)
?~ whos ~ ~(tap in members.upd)
:: no need to subscribe to ourselves =| cards=(list card)
:: |-
=* loop-socs $
?~ socs [cards state]
?. (~(has in listening) i.socs)
loop-socs(socs t.socs)
|-
=* loop-whos $
?~ whos loop-socs(socs t.socs)
=^ caz state
?. ?=(%remove -.upd)
(listen-to-peer i.socs pax.upd i.whos)
?: =(our.bowl i.whos) ?: =(our.bowl i.whos)
$(whos t.whos) (handle-listen-action %leave i.socs)
:_ $(whos t.whos) (leave-from-peer i.socs pax.upd i.whos)
%. [i.whos pax.upd] loop-whos(whos t.whos, cards (weld cards caz))
?: ?=(%remove -.upd)
end-link-subscription
start-link-subscription
==
:: ::
:: link subscriptions :: link subscriptions
:: ::
++ listen-to-group
|= [=app-path =group-path]
^- (quip card _state)
=/ peers=(list ship)
~| group-path
%~ tap in
=- (fall - *group:group-store)
%^ scry-for (unit group:group-store)
%group-store
group-path
=| cards=(list card)
|-
?~ peers [cards state]
=^ caz state
(listen-to-peer app-path group-path i.peers)
$(peers t.peers, cards (weld cards caz))
::
++ leave-from-group
|= [=app-path =group-path]
^- (quip card _state)
=/ peers=(list ship)
%~ tap in
=- (fall - *group:group-store)
%^ scry-for (unit group:group-store)
%group-store
group-path
=| cards=(list card)
|-
?~ peers [cards state]
=^ caz state
(leave-from-peer app-path group-path i.peers)
$(peers t.peers, cards (weld cards caz))
::
++ listen-to-peer
|= [=app-path =group-path who=ship]
^- (quip card _state)
?: =(our.bowl who)
[~ state]
:_ =- state(reasoning -)
(~(put ju reasoning) [who app-path] group-path)
:- (prod-other-listener who app-path)
?^ (~(get ju reasoning) [who app-path])
~
(start-link-subscriptions who app-path)
::
++ leave-from-peer
|= [=app-path =group-path who=ship]
^- (quip card _state)
?: =(our.bowl who)
[~ state]
=. reasoning (~(del ju reasoning) [who app-path] group-path)
::NOTE leaving is always safe, so we just do it unconditionally
(end-link-subscriptions who app-path)
::
++ start-link-subscriptions
|= [=ship =app-path]
^- (list card)
:~ (start-link-subscription %local-pages ship app-path)
(start-link-subscription %annotations ship app-path)
==
::
++ start-link-subscription ++ start-link-subscription
|= [who=ship where=path] |= =target
^- card ^- card
:* %pass :* %pass
[%links (scot %p who) where] [%links (target-to-wire target)]
%agent %agent
[who %link-proxy-hook] [who.target %link-proxy-hook]
%watch %watch
[%local-pages where] ?- what.target
%local-pages [what where]:target
%annotations [what %$ where]:target
==
== ==
:: ::
++ end-link-subscription ++ end-link-subscriptions
|= [who=ship where=path]
^- (quip card _state)
=. retry-timers (~(del by retry-timers) [%local-pages who where])
=. retry-timers (~(del by retry-timers) [%annotations who where])
:_ state
|^ ~[(end %local-pages) (end %annotations)]
::
++ end
|= what=what-target
:* %pass
[%links (target-to-wire what who where)]
%agent
[who %link-proxy-hook]
%leave
~
==
--
::
++ prod-other-listener
|= [who=ship where=path] |= [who=ship where=path]
^- card ^- card
:* %pass :* %pass
[%links (scot %p who) where] [%prod (scot %p who) where]
%agent %agent
[who %link-proxy-hook] [who %link-listen-hook]
%leave %poke
~ %link-listen-poke
!>(where)
== ==
:: ::
++ take-links-sign ++ take-link-sign
|= [who=ship where=path =sign:agent:gall] |= [=target =sign:agent:gall]
^- (quip card _state) ^- (quip card _state)
?- -.sign ?- -.sign
%poke-ack ~|([dap.bowl %unexpected-poke-ack /links who where] !!) %poke-ack ~|([dap.bowl %unexpected-poke-ack /links target] !!)
%kick [[(start-link-subscription who where)]~ state] %kick [[(start-link-subscription target)]~ state]
:: ::
%watch-ack %watch-ack
?~ p.sign [~ state] ?~ p.sign
:: our subscription request got rejected for whatever reason, =. retry-timers (~(del by retry-timers) target)
:: (most likely difference in group membership,) [~ state]
:: so we don't try again. :: our subscription request got rejected,
::TODO but now the only way to retry is to remove from group and re-add... :: most likely because our group definition is out of sync with theirs.
:: this is a problem because our and their group may not update :: set timer for retry.
:: simultaneously... ::
[~ state] (start-retry target)
:: ::
%fact %fact
=* mark p.cage.sign =* mark p.cage.sign
=* vase q.cage.sign =* vase q.cage.sign
?+ mark ~|([dap.bowl %unexpected-mark mark] !!) ?+ mark ~|([dap.bowl %unexpected-mark mark] !!)
%link-update (handle-link-update who where !<(update vase)) %link-initial
%- handle-link-initial
[who.target where.target !<(initial vase)]
::
%link-update
%- handle-link-update
[who.target where.target !<(update vase)]
== ==
== ==
:: ::
++ start-retry
|= =target
^- (quip card _state)
=/ timer=@dr
%+ min ~h1
%+ mul 2
(~(gut by retry-timers) target ~s15)
=. retry-timers
(~(put by retry-timers) target timer)
:_ state
:_ ~
:* %pass
[%retry (target-to-wire target)]
[%arvo %b %wait (add now.bowl timer)]
==
::
++ take-retry
|= =target
^- (list card)
:: relevant: whether :who is still associated with resource :where
::
=; relevant=?
?. relevant ~
[(start-link-subscription target)]~
?. (~(has in listening) where.target)
|
?: %- ~(has by wex.bowl)
[[%links (target-to-wire target)] who.target %link-proxy-hook]
|
%+ lien (groups-from-resource:md %link where.target)
|= =group-path
^- ?
=- (~(has in (fall - *group:group-store)) who.target)
%^ scry-for (unit group:group-store)
%group-store
group-path
::
++ do-link-action
|= [=wire =action]
^- card
:* %pass
wire
%agent
[our.bowl %link-store]
%poke
%link-action
!>(action)
==
::
++ handle-link-initial
|= [who=ship where=path =initial]
^- (quip card _state)
?> =(src.bowl who)
?+ -.initial ~|([dap.bowl %unexpected-initial -.initial] !!)
%local-pages
=/ =pages (~(got by pages.initial) where)
(handle-link-update who where [%local-pages where pages])
::
%annotations
=/ urls=(list [=url =notes])
~(tap by (~(got by notes.initial) where))
=| cards=(list card)
|- ^- (quip card _state)
?~ urls [cards state]
=^ caz state
^- (quip card _state)
=, i.urls
(handle-link-update who where [%annotations where url notes])
$(urls t.urls, cards (weld cards caz))
==
::
++ handle-link-update ++ handle-link-update
|= [who=ship where=path =update] |= [who=ship where=path =update]
^- (quip card _state) ^- (quip card _state)
?> ?=(%local-pages -.update)
?> =(src.bowl who) ?> =(src.bowl who)
:_ state :_ state
%+ turn pages.update ?+ -.update ~|([dap.bowl %unexpected-update -.update] !!)
|= =page %local-pages
^- card %+ turn pages.update
:* %pass |= =page
[%forward (scot %p who) where] %+ do-link-action
%agent [%forward %local-page (scot %p who) where]
[our.bowl %link-store] [%hear where who page]
%poke ::
%link-action %annotations
!>([%hear where src.bowl page]) %+ turn notes.update
|= =note
^- card
%+ do-link-action
[%forward %annotation (scot %p who) where]
[%read where url.update who note]
== ==
:: ::
++ take-forward-sign ++ take-forward-sign
@ -228,4 +616,14 @@
== ==
%- (slog tank u.p.sign) %- (slog tank u.p.sign)
[~ state] [~ state]
::
++ scry-for
|* [=mold =app-name =path]
.^ mold
%gx
(scot %p our.bowl)
app-name
(scot %da now.bowl)
(snoc `^path`path %noun)
==
-- --

View File

@ -4,12 +4,11 @@
:: stores if permission conditions are met. :: stores if permission conditions are met.
:: the patterns herein should one day be generalized into a proxy-hook lib. :: the patterns herein should one day be generalized into a proxy-hook lib.
:: ::
:: this adopts a very primitive view of groups-store as containing only :: this uses metadata-store to discover resources and their associated
:: groups of interesting (rather than uninteresting) ships. it sets the :: groups. it sets the permission condition to be that a ship must be in a
:: permission condition to be that ship must be in group matching the path :: group associated with the resource it's subscribing to.
:: it's subscribing to. :: we check this on-watch, but also subscribe to metadata & groups so that
:: we check this on-watch, but also subscribe to groups so that we can kick :: we can kick subscriptions if needed (eg ship removed from group).
:: subscriptions if needed (eg ship removed from group).
:: ::
:: we deduplicate incoming subscriptions on the same path, ensuring we have :: we deduplicate incoming subscriptions on the same path, ensuring we have
:: exactly one local subscription per unique incoming subscription path. :: exactly one local subscription per unique incoming subscription path.
@ -17,8 +16,12 @@
:: whatever's returned by the scry at that path, but perhaps that should :: whatever's returned by the scry at that path, but perhaps that should
:: become part of the stores standard anyway. :: become part of the stores standard anyway.
:: ::
/- *link, group-store :: when adding support for new paths, the only things you'll likely want
/+ default-agent, verb :: to touch are +permitted, +initial-response, & +kick-proxies.
::
/- group-store, *metadata-store
/+ *link, metadata, default-agent, verb, dbug
~% %link-proxy-hook-top ..is ~
|% |%
+$ state-0 +$ state-0
$: %0 $: %0
@ -33,6 +36,7 @@
=| state-0 =| state-0
=* state - =* state -
:: ::
%- agent:dbug
%+ verb | %+ verb |
^- agent:gall ^- agent:gall
=< =<
@ -44,7 +48,7 @@
++ on-init ++ on-init
^- (quip card _this) ^- (quip card _this)
:_ this :_ this
[watch-groups:do]~ ~[watch-groups:do watch-metadata:do]
:: ::
++ on-save !>(state) ++ on-save !>(state)
++ on-load ++ on-load
@ -92,24 +96,92 @@
-- --
:: ::
|_ =bowl:gall |_ =bowl:gall
+* md ~(. metadata bowl)
::
:: permissions
::
++ permitted ++ permitted
|= [who=ship =path] |= [who=ship =path]
^- ? ^- ?
:: we only expose /local-pages, and only to ships in the relevant group :: we only expose /local-pages and /annotations,
:: to ships in the groups associated with the resource.
:: (no url-specific annotations subscriptions, either.)
:: ::
?. ?=([%local-pages ^] path) | =/ target=(unit ^path)
=; group ?: ?=([%local-pages ^] path)
?& ?=(^ group) `t.path
(~(has in u.group) who) ?: ?=([%annotations ~ ^] path)
== `t.t.path
.^ (unit group:group-store) ~
%gx ?~ target |
(scot %p our.bowl) %+ lien (groups-from-resource:md %link u.target)
%group-store |= =group-path
(scot %da now.bowl) ^- ?
(snoc t.path %noun) =- (~(has in (fall - *group:group-store)) who)
%^ scry-for (unit group:group-store)
%group-store
group-path
::
++ kick-revoked-permissions
|= [=path who=(list ship)]
^- (list card)
%+ murn who
|= =ship
^- (unit card)
:: no need to remove to ourselves
::
?: =(our.bowl ship) ~
?: (permitted ship path) ~
`(kick-proxies ship path)
::
:: metadata subscription
::
++ watch-metadata
^- card
[%pass /metadata %agent [our.bowl %metadata-store] %watch /app-name/link]
::
++ take-metadata-sign
|= =sign:agent:gall
^- (quip card _state)
?- -.sign
%poke-ack ~|([dap.bowl %unexpected-poke-ack /metadata] !!)
%kick [[watch-metadata]~ state]
::
%watch-ack
?~ p.sign [~ state]
=/ =tank
:- %leaf
"{(trip dap.bowl)} failed subscribe to metadata store. very wrong!"
%- (slog tank u.p.sign)
[~ state]
::
%fact
=* mark p.cage.sign
=* vase q.cage.sign
?. ?=(%metadata-update mark)
~| [dap.bowl %unexpected-mark mark]
!!
%- handle-metadata-update
!<(metadata-update vase)
== ==
:: ::
++ handle-metadata-update
|= upd=metadata-update
^- (quip card _state)
:_ state
?. ?=(%remove -.upd) ~
?> =(%link app-name.resource.upd)
:: if a group is no longer associated with a resource,
:: we need to re-check permissions for everyone in that group.
::
%+ kick-revoked-permissions
app-path.resource.upd
%~ tap in
=- (fall - *group:group-store)
%^ scry-for (unit group:group-store)
%group-store
group-path.upd
::
:: groups subscription :: groups subscription
::TODO largely copied from link-listen-hook. maybe make a store-listener lib? ::TODO largely copied from link-listen-hook. maybe make a store-listener lib?
:: ::
@ -135,7 +207,6 @@
%fact %fact
=* mark p.cage.sign =* mark p.cage.sign
=* vase q.cage.sign =* vase q.cage.sign
~& [dap.bowl %fact mark]
?+ mark ~|([dap.bowl %unexpected-mark mark] !!) ?+ mark ~|([dap.bowl %unexpected-mark mark] !!)
%group-initial [~ state] %group-initial [~ state]
%group-update (handle-group-update !<(group-update:group-store vase)) %group-update (handle-group-update !<(group-update:group-store vase))
@ -147,33 +218,34 @@
^- (quip card _state) ^- (quip card _state)
:_ state :_ state
?. ?=(%remove -.upd) ~ ?. ?=(%remove -.upd) ~
=/ whos=(list ship) ~(tap in members.upd) :: if someone was removed from a group, find all link resources associated
|- ^- (list card) :: with that group, then kick their subscriptions if they're no longer
?~ whos ~
:: no need to remove to ourselves
:: ::
?: =(our.bowl i.whos) %- zing
$(whos t.whos) %+ turn (app-paths-from-group:md %link pax.upd)
:_ $(whos t.whos) |= =app-path
::NOTE this depends kind of unfortunately on the fact that we only accept ^- (list card)
:: subscriptions to /local-pages/* paths. it'd be more correct if we %+ kick-revoked-permissions
:: "just" looked at all paths in the map, and found the matching ones. app-path
(kick-proxy i.whos [%local-pages pax.upd]) ~(tap in members.upd)
:: ::
:: proxy subscriptions :: proxy subscriptions
:: ::
++ kick-proxy ++ kick-proxies
|= [who=ship =path] |= [who=ship =path]
^- card ^- card
[%give %kick ~[path] `who] =- [%give %kick - `who]
:~ [%local-pages path]
[%annotations %$ path]
==
:: ::
++ handle-proxy-sign ++ handle-proxy-sign
|= [=path =sign:agent:gall] |= [=wire =sign:agent:gall]
^- (quip card _state) ^- (quip card _state)
?- -.sign ?- -.sign
%poke-ack ~|([dap.bowl %unexpected-poke-ack path] !!) %poke-ack ~|([dap.bowl %unexpected-poke-ack wire] !!)
%fact [[%give %fact ~[path] cage.sign]~ state] %fact [[%give %fact ~[wire] cage.sign]~ state]
%kick [[(proxy-pass-link-store path %watch path)]~ state] %kick [[(proxy-pass-link-store wire %watch wire)]~ state]
:: ::
%watch-ack %watch-ack
?~ p.sign [~ state] ?~ p.sign [~ state]
@ -197,9 +269,15 @@
++ initial-response ++ initial-response
|= =path |= =path
^- card ^- card
=/ initial=update =; =initial
[%local-pages path .^(pages %gx path)] [%give %fact ~ %link-initial !>(initial)]
[%give %fact ~ %link-update !>(initial)] ?+ path !!
[%local-pages ^]
[%local-pages (scry-for (map ^path pages) %link-store path)]
::
[%annotations %$ ^]
[%annotations (scry-for (per-path-url notes) %link-store path)]
==
:: ::
++ start-proxy ++ start-proxy
|= [who=ship =path] |= [who=ship =path]
@ -228,4 +306,16 @@
:: else, close the local subscription. :: else, close the local subscription.
:: ::
[(proxy-pass-link-store path %leave ~)]~ [(proxy-pass-link-store path %leave ~)]~
::
:: helpers
::
++ scry-for
|* [=mold =app-name =path]
.^ mold
%gx
(scot %p our.bowl)
app-name
(scot %da now.bowl)
(snoc `^path`path %noun)
==
-- --

View File

@ -1,228 +0,0 @@
:: link-server: accessing link-store via eyre
::
:: only accepts requests authenticated as the host ship.
::
:: GET requests:
:: /~link/local-pages/[some-path].json?p=0
:: our submissions on path, with optional pagination
::
:: POST requests:
:: /~link/add/[some-path]
:: send {title url} json, will save link at path
::
/+ *link, *server, default-agent, verb
::
|%
+$ state-0
$: %0
~
::NOTE this means we could get away with just producing cards everywhere,
:: never producing new state outside of the agent interface core.
:: we opt to keep ^-(quip card _state) in place for most logic arms
:: because it doesn't cost much, results in unsurprising code, and
:: makes adding any state in the future easier.
==
::
+$ card card:agent:gall
--
::
=| state-0
=* state -
::
%+ verb |
^- agent:gall
=<
|_ =bowl:gall
+* this .
do ~(. +> bowl)
def ~(. (default-agent this %|) bowl)
::
++ on-init
^- (quip card _this)
:_ this
[start-serving:do]~
::
++ on-save !>(state)
++ on-load
|= old=vase
^- (quip card _this)
[~ this(state !<(state-0 old))]
::
++ on-watch
|= =path
^- (quip card _this)
?: ?=([%http-response *] path)
[~ this]
(on-watch:def path)
::
++ on-poke
|= [=mark =vase]
^- (quip card _this)
?. ?=(%handle-http-request mark)
(on-poke:def mark vase)
:_ this
=+ !<([eyre-id=@ta =inbound-request:eyre] vase)
(handle-http-request:do eyre-id inbound-request)
::
++ on-arvo
|= [=wire =sign-arvo]
^- (quip card _this)
?. ?=(%bound +<.sign-arvo)
(on-arvo:def wire sign-arvo)
[~ this]
::
++ on-agent
|= [=wire =sign:agent:gall]
^- (quip card _this)
?. ?=(%poke-ack -.sign)
(on-agent:def wire sign)
?~ p.sign [~ this]
=/ =tank
leaf+"{(trip dap.bowl)} failed writing to %link-store"
%- (slog tank u.p.sign)
[~ this]
::
++ on-peek on-peek:def
++ on-leave on-leave:def
++ on-fail on-fail:def
--
::
|_ =bowl:gall
::
++ start-serving
^- card
[%pass / %arvo %e %connect [~ /'~link'] dap.bowl]
::
++ do-action
|= =action
^- card
[%pass / %agent [our.bowl %link-store] %poke %link-action !>(action)]
::
++ do-add
|= [=path title=@t =url]
^- card
(do-action %add path title url)
::
++ handle-http-request
|= [eyre-id=@ta =inbound-request:eyre]
^- (list card)
::NOTE we don't use +require-authorization because it's too restrictive
:: on the flow we want here.
::
?. ?& authenticated.inbound-request
=(src.bowl our.bowl)
==
::TODO `*octs -> ~ everywhere once no-data bug is fixed
(give-simple-payload:app eyre-id [[403 ~] `*octs])
:: request-line: parsed url + params
::
=/ =request-line
%- parse-request-line
url.request.inbound-request
=* req-head header-list.request.inbound-request
=; [cards=(list card) =simple-payload:http]
%+ weld cards
(give-simple-payload:app eyre-id simple-payload)
?+ method.request.inbound-request [~ not-found:gen]
%'OPTIONS'
[~ (include-cors-headers req-head [[200 ~] `*octs])]
::
%'GET'
[~ (handle-get req-head request-line)]
::
%'POST'
(handle-post req-head request-line body.request.inbound-request)
==
::
++ handle-post
|= [request-headers=header-list:http =request-line body=(unit octs)]
^- [(list card) simple-payload:http]
=; [success=? cards=(list card)]
:- cards
%+ include-cors-headers
request-headers
::TODO it would be more correct to wait for the %poke-ack instead of
:: sending this response right away... but link-store pokes can't
:: actually fail right now, so it's fine.
[[?:(success 200 400) ~] `*octs]
?~ body [| ~]
?+ request-line [| ~]
[[~ [%'~link' %add ^]] ~]
^- [? (list card)]
=/ jon=(unit json) (de-json:html q.u.body)
?~ jon [| ~]
=/ page=(unit [title=@t =url])
%. u.jon
(ot title+so url+so ~):dejs-soft:format
?~ page [| ~]
[& [(do-add t.t.site.request-line [title url]:u.page) ~]]
==
::
++ handle-get
|= [request-headers=header-list:http =request-line]
%+ include-cors-headers
request-headers
^- simple-payload:http
:: args: map of params
:: p: pagination index
::
=/ args
%- ~(gas by *(map @t @t))
args.request-line
=/ p=(unit @ud)
%+ biff (~(get by args) 'p')
(curr rush dim:ag)
?+ request-line not-found:gen
::TODO expose submissions, other data
:: local links by recency as json
::
[[[~ %json] [%'~link' %local-pages ^]] *]
%- json-response:gen
%- json-to-octs ::TODO include in +json-response:gen
^- json
:- %a
%+ turn
`pages`(get-pages t.t.site.request-line p)
`$-(page json)`page:en-json
==
::
++ include-cors-headers
|= [request-headers=header-list:http =simple-payload:http]
^+ simple-payload
=* out-heads headers.response-header.simple-payload
=; =header-list:http
|-
?~ header-list simple-payload
=* new-head i.header-list
=. out-heads
(set-header:http key.new-head value.new-head out-heads)
$(header-list t.header-list)
=/ origin=@t
=/ headers=(map @t @t)
(~(gas by *(map @t @t)) request-headers)
(~(gut by headers) 'origin' '*')
:~ 'Access-Control-Allow-Origin'^origin
'Access-Control-Allow-Credentials'^'true'
'Access-Control-Request-Method'^'OPTIONS, GET, POST'
'Access-Control-Allow-Methods'^'OPTIONS, GET, POST'
'Access-Control-Allow-Headers'^'content-type'
==
::
++ page-size 25
++ get-pages
|= [=path p=(unit @ud)]
^- pages
=; =pages
?~ p pages
%+ scag page-size
%+ slag (mul u.p page-size)
pages
.^ pages
%gx
(scot %p our.bowl)
%link-store
(scot %da now.bowl)
%local-pages
(snoc path %noun)
==
--

View File

@ -5,24 +5,71 @@
:: links, arbitrary paths are probably fair game, but could trip up :: links, arbitrary paths are probably fair game, but could trip up
:: primitive ui implementations. :: primitive ui implementations.
:: ::
:: urls in paths are expected to be encoded using +wood, for @ta sanity.
:: generally, use /lib/link's +build-discussion-path.
::
:: see link-listen-hook to see what's synced in, and similarly
:: see link-proxy-hook to see what's exposed.
::
:: scry and subscription paths: :: scry and subscription paths:
:: ::
:: /local-pages/[some-group] all pages we saved by recency :: (map path pages) %local-pages
:: /submissions/[some-group] all submissions by recency :: /local-pages our saved pages
:: /local-pages/some-path our saved pages on path
:: ::
/+ *link, default-agent, verb :: (map path submissions) %submissions
:: /submissions all submissions we've seen
:: /submissions/some-path all submissions we've seen on path
::
:: (map path (map url notes)) %annotations
:: /annotations our comments
:: /annotations/wood-url our comments on url
:: /annotations/wood-url/some-path our comments on url on path
:: /annotations//some-path our comments on path
::
:: (map path (map url comments)) %discussions
:: /discussions all comments
:: /discussions/wood-url all comments on url
:: /discussions/wood-url/some-path all comments on url on path
:: /discussions//some-path all comments on path
::
:: subscription-only paths:
::
:: [path url] %observation
:: /seen updates whenever an item is seen
::
:: scry-only paths:
::
::
:: (map path (set url))
:: /unseen the ones we haven't seen yet
::
:: (set url)
:: /unseen/some-path the ones we haven't seen here yet
::
:: ?
:: /seen/wood-url/some-path have we seen this here
::
/+ *link, default-agent, verb, dbug
:: ::
|% |%
+$ state-0 +$ state-0
$: %0 $: %0
by-group=(map path links) by-group=(map path links)
by-site=(map site (list [path submission])) by-site=(map site (list [path submission]))
discussions=(per-path-url discussion)
== ==
:: ::
+$ links +$ links
$: ::NOTE all lists by recency $: ::NOTE all lists by recency
=submissions =submissions
ours=pages ours=pages
seen=(set url)
==
::
+$ discussion
$: =comments
ours=notes
== ==
:: ::
+$ card card:agent:gall +$ card card:agent:gall
@ -31,6 +78,7 @@
=| state-0 =| state-0
=* state - =* state -
:: ::
%- agent:dbug
%+ verb | %+ verb |
^- agent:gall ^- agent:gall
=< =<
@ -64,12 +112,58 @@
?+ path (on-peek:def path) ?+ path (on-peek:def path)
[%y ?(%local-pages %submissions) ~] [%y ?(%local-pages %submissions) ~]
``noun+!>(~(key by by-group)) ``noun+!>(~(key by by-group))
:: ::
[%x %local-pages ^] [%x %local-pages *]
``noun+!>((get-local-pages:do t.t.path)) ``noun+!>((get-local-pages:do t.t.path))
:: ::
[%x %submissions ^] [%x %submissions *]
``noun+!>((get-submissions:do t.t.path)) ``noun+!>((get-submissions:do t.t.path))
::
[%y ?(%annotations %discussions) *]
=/ [spath=^path surl=url]
(break-discussion-path t.t.path)
=- ``noun+!>(-)
::
?: =(~ surl)
:: no url, provide urls that have comments
::
^- (set url)
?~ spath
:: no path, find urls accross all paths
::
%- ~(rep by discussions)
|= [[* discussions=(map url discussion)] urls=(set url)]
%- ~(uni in urls)
~(key by discussions)
:: specified path, find urls for that specific path
::
%~ key by
(~(gut by discussions) spath *(map url *))
:: specified url and path, nothing to list here
::
?^ spath !!
:: no path, find paths with comments for this url
::
^- (set ^path)
%- ~(rep by discussions)
|= [[=^path urls=(map url discussion)] paths=(set ^path)]
?. (~(has by urls) surl) paths
(~(put in paths) path)
::
[%x %annotations *]
``noun+!>((get-annotations:do t.t.path))
::
[%x %discussions *]
``noun+!>((get-discussions:do t.t.path))
::
[%x %seen @ ^]
``noun+!>((is-seen:do t.t.path))
::
[%x %unseen ~]
``noun+!>(get-all-unseen:do)
::
[%x %unseen ^]
``noun+!>((get-unseen:do t.t.path))
== ==
:: ::
++ on-watch ++ on-watch
@ -78,19 +172,39 @@
?> (team:title [our src]:bowl) ::TODO /lib/store ?> (team:title [our src]:bowl) ::TODO /lib/store
:_ this :_ this
|^ ?+ path (on-watch:def path) |^ ?+ path (on-watch:def path)
[%local-pages ^] [%local-pages *]
%+ give %link-update %+ give %link-initial
[%local-pages t.path (get-local-pages:do t.path)] ^- initial
[%local-pages (get-local-pages:do t.path)]
:: ::
[%submissions ^] [%submissions *]
%+ give %link-update %+ give %link-initial
[%submissions t.path (get-submissions:do t.path)] ^- initial
[%submissions (get-submissions:do t.path)]
::
[%annotations *]
%+ give %link-initial
^- initial
[%annotations (get-annotations:do t.path)]
::
[%discussions *]
%+ give %link-initial
^- initial
[%discussions (get-discussions:do t.path)]
::
[%seen ~]
~
== ==
:: ::
++ give ++ give
|* [=mark =noun] |* [=mark =noun]
^- (list card) ^- (list card)
[%give %fact ~ mark !>(noun)]~ [%give %fact ~ mark !>(noun)]~
::
++ give-single
|* [=mark =noun]
^- card
[%give %fact ~ mark !>(noun)]
-- --
:: ::
++ on-leave on-leave:def ++ on-leave on-leave:def
@ -107,15 +221,19 @@
|= =action |= =action
^- (quip card _state) ^- (quip card _state)
?- -.action ?- -.action
%add (add-page +.action) %save (save-page +.action)
%note (note-note +.action)
%seen (seen-submission +.action)
::
%hear (hear-submission +.action) %hear (hear-submission +.action)
%read (read-comment +.action)
== ==
:: +add-page: save a page ourselves :: +save-page: save a page ourselves
:: ::
++ add-page ++ save-page
|= [=path title=@t =url] |= [=path title=@t =url]
^- (quip card _state) ^- (quip card _state)
?< =(~ path) ?< |(=(~ path) =(~ title) =(~ url))
:: add page to group ours :: add page to group ours
:: ::
=/ =links (~(gut by by-group) path *links) =/ =links (~(gut by by-group) path *links)
@ -124,16 +242,75 @@
=. by-group (~(put by by-group) path links) =. by-group (~(put by by-group) path links)
:: do generic submission logic :: do generic submission logic
:: ::
=^ cards state =^ submission-cards state
(hear-submission path [our.bowl page]) (hear-submission path [our.bowl page])
:: mark page as seen (because we submitted it ourselves)
::
=^ seen-cards state
(seen-submission path `url)
:: send updates to subscribers :: send updates to subscribers
:: ::
:_ state :_ state
:_ cards :_ (weld submission-cards seen-cards)
:+ %give %fact :+ %give %fact
:+ [%local-pages path]~ :+ :~ /local-pages
[%local-pages path]
==
%link-update %link-update
!>([%local-pages path [page]~]) !>([%local-pages path [page]~])
:: +note-note: save a note for a url
::
++ note-note
|= [=path =url udon=@t]
^- (quip card _state)
?< |(=(~ path) =(~ url) =(~ udon))
:: add note to discussion ours
::
=/ urls (~(gut by discussions) path *(map ^url discussion))
=/ =discussion (~(gut by urls) url *discussion)
=/ =note [now.bowl udon]
=. ours.discussion [note ours.discussion]
=. urls (~(put by urls) url discussion)
=. discussions (~(put by discussions) path urls)
:: do generic comment logic
::
=^ cards state
(read-comment path url [our.bowl note])
:: send updates to subscribers
::
:_ state
^- (list card)
:_ cards
:+ %give %fact
:+ :~ /annotations
[%annotations %$ path]
[%annotations (build-discussion-path url)]
[%annotations (build-discussion-path path url)]
==
%link-update
!>([%annotations path url [note]~])
:: +seen-submission: mark url as seen/read
::
:: if no url specified, all under path are marked as read
::
++ seen-submission
|= [=path murl=(unit url)]
^- (quip card _state)
=/ =links (~(gut by by-group) path *links)
:: new: urls we want to, but haven't yet, marked as seen
::
=/ new=(set url)
%. seen.links
%~ dif in
^- (set url)
?^ murl (sy ~[u.murl])
%- ~(gas in *(set url))
%+ turn submissions.links
|=(submission url)
?: =(~ new) [~ state]
=. seen.links (~(uni in seen.links) new)
:_ state(by-group (~(put by by-group) path links))
[%give %fact ~[/seen] %link-update !>([%observation path new])]~
:: +hear-submission: record page someone else saved :: +hear-submission: record page someone else saved
:: ::
++ hear-submission ++ hear-submission
@ -143,7 +320,11 @@
:: add link to group submissions :: add link to group submissions
:: ::
=/ =links (~(gut by by-group) path *links) =/ =links (~(gut by by-group) path *links)
=. submissions.links [submission submissions.links] =^ added submissions.links
?: ?=(^ (find ~[submission] submissions.links))
[| submissions.links]
:- &
(submissions:merge submissions.links ~[submission])
=. by-group (~(put by by-group) path links) =. by-group (~(put by by-group) path links)
:: add submission to global sites :: add submission to global sites
:: ::
@ -152,21 +333,156 @@
:: send updates to subscribers :: send updates to subscribers
:: ::
:_ state :_ state
?. added ~
:_ ~ :_ ~
:+ %give %fact :+ %give %fact
:+ [%submissions path]~ :+ :~ /submissions
[%submissions path]
==
%link-update %link-update
!>([%submissions path [submission]~]) !>([%submissions path [submission]~])
:: +read-comment: record a comment someone else made
::
++ read-comment
|= [=path =url =comment]
^- (quip card _state)
:: add comment to url's discussion
::
=/ urls (~(gut by discussions) path *(map ^url discussion))
=/ =discussion (~(gut by urls) url *discussion)
=^ added comments.discussion
?: ?=(^ (find ~[comment] comments.discussion))
[| comments.discussion]
:- &
(comments:merge comments.discussion ~[comment])
=. urls (~(put by urls) url discussion)
=. discussions (~(put by discussions) path urls)
:: send updates to subscribers
::
:_ state
?. added ~
:_ ~
:+ %give %fact
:+ :~ /discussions
[%discussions '' path]
[%discussions (build-discussion-path url)]
[%discussions (build-discussion-path path url)]
==
%link-update
!>([%discussions path url [comment]~])
:: ::
:: reading :: reading
:: ::
++ get-local-pages ++ get-local-pages
|= =path |= =path
^- pages ^- (map ^path pages)
?~ path
:: all paths
::
%- ~(run by by-group)
|=(links ours)
:: specific path
::
%+ ~(put by *(map ^path pages)) path
ours:(~(gut by by-group) path *links) ours:(~(gut by by-group) path *links)
:: ::
++ get-submissions ++ get-submissions
|= =path |= =path
^- submissions ^- (map ^path submissions)
?~ path
:: all paths
::
%- ~(run by by-group)
|=(links submissions)
:: specific path
::
%+ ~(put by *(map ^path submissions)) path
submissions:(~(gut by by-group) path *links) submissions:(~(gut by by-group) path *links)
::
++ get-all-unseen
^- (jug path url)
%- ~(rut by by-group)
|= [=path *]
(get-unseen path)
::
++ get-unseen
|= =path
^- (set url)
=/ =links
(~(gut by by-group) path *links)
%- ~(gas in *(set url))
%+ murn submissions.links
|= submission
?: (~(has in seen.links) url) ~
(some url)
::
++ is-seen
|= =path
^- ?
=/ [=^path =url]
(break-discussion-path path)
%. url
%~ has in
seen:(~(gut by by-group) path *links)
::
::
++ get-annotations
|= =path
^- (per-path-url notes)
=/ args=[=^path =url]
(break-discussion-path path)
|^ ?~ path
:: all paths
::
(~(run by discussions) get-ours)
:: specific path
::
%+ ~(put by *(per-path-url notes)) path.args
%- get-ours
%+ ~(gut by discussions) path.args
*(map url discussion)
::
++ get-ours
|= m=(map url discussion)
^- (map url notes)
?: =(~ url.args)
:: all urls
::
%- ~(run by m)
|=(discussion ours)
:: specific url
::
%+ ~(put by *(map url notes)) url.args
ours:(~(gut by m) url.args *discussion)
--
::
++ get-discussions
|= =path
^- (per-path-url comments)
=/ args=[=^path =url]
(break-discussion-path path)
|^ ?~ path
:: all paths
::
(~(run by discussions) get-comments)
:: specific path
::
%+ ~(put by *(per-path-url comments)) path.args
%- get-comments
%+ ~(gut by discussions) path.args
*(map url discussion)
::
++ get-comments
|= m=(map url discussion)
^- (map url comments)
?: =(~ url.args)
:: all urls
::
%- ~(run by m)
|=(discussion comments)
:: specific url
::
%+ ~(put by *(map url comments)) url.args
comments:(~(gut by m) url.args *discussion)
--
-- --

634
pkg/arvo/app/link-view.hoon Normal file
View File

@ -0,0 +1,634 @@
:: link-view: frontend endpoints
::
:: endpoints, mapping onto link-store's paths. p is for page as in pagination.
:: only the /0/submissions endpoint provides updates.
:: as with link-store, urls are expected to use +wood encoding.
::
:: /json/0/submissions initial + updates for all
:: /json/[p]/submissions/[collection] page for one collection
:: /json/[p]/discussions/[wood-url]/[collection] page for url in collection
:: /json/[n]/submission/[wood-url]/[collection] nth matching submission
:: /json/seen mark-as-read updates
::
/- *link-view,
*invite-store, group-store,
link-listen-hook,
group-hook, permission-hook, permission-group-hook,
metadata-hook, contact-view
/+ *link, metadata, *server, default-agent, verb, dbug
~% %link-view-top ..is ~
::
|%
+$ state-0
$: %0
~
==
::
+$ card card:agent:gall
--
::
=| state-0
=* state -
::
%+ verb |
%- agent:dbug
^- agent:gall
=<
|_ =bowl:gall
+* this .
do ~(. +> bowl)
def ~(. (default-agent this %|) bowl)
::
++ on-init
^- (quip card _this)
:_ this
:~ [%pass /connect %arvo %e %connect [~ /'~link'] dap.bowl]
[%pass /submissions %agent [our.bowl %link-store] %watch /submissions]
[%pass /discussions %agent [our.bowl %link-store] %watch /discussions]
[%pass /seen %agent [our.bowl %link-store] %watch /seen]
::
=+ [%add dap.bowl /tile '/~link/js/tile.js']
[%pass /launch %agent [our.bowl %launch] %poke %launch-action !>(-)]
::
=+ [%invite-action !>([%create /link])]
[%pass /invitatory/create %agent [our.bowl %invite-store] %poke -]
::
=+ /invitatory/link
[%pass - %agent [our.bowl %invite-store] %watch -]
==
::
++ on-save !>(state)
::
++ on-load
|= old=vase
^- (quip card _this)
[~ this(state !<(state-0 old))]
::
++ on-poke
|= [=mark =vase]
^- (quip card _this)
?> (team:title our.bowl src.bowl)
:_ this
?+ mark (on-poke:def mark vase)
%handle-http-request
=+ !<([eyre-id=@ta =inbound-request:eyre] vase)
%+ give-simple-payload:app eyre-id
%+ require-authorization:app inbound-request
handle-http-request:do
::
%link-action
[(handle-action:do !<(action vase)) ~]
::
%link-view-action
(handle-view-action:do !<(view-action vase))
==
::
++ on-watch
|= =path
^- (quip card _this)
?: ?| ?=([%http-response *] path)
?=([%json %seen ~] path)
==
[~ this]
?: ?=([%tile ~] path)
:_ this
~[give-tile-data:do]
?. ?=([%json @ @ *] path)
(on-watch:def path)
=/ p=@ud (slav %ud i.t.path)
?+ t.t.path (on-watch:def path)
[%submissions ~]
:_ this
(give-initial-submissions:do p ~)
::
[%submissions ^]
:_ this
(give-initial-submissions:do p t.t.t.path)
::
[%submission @ ^]
:_ this
(give-specific-submission:do p (break-discussion-path t.t.t.path))
::
[%discussions @ ^]
:_ this
(give-initial-discussions:do p (break-discussion-path t.t.t.path))
==
::
++ on-agent
|= [=wire =sign:agent:gall]
^- (quip card _this)
?+ -.sign (on-agent:def wire sign)
%kick
:_ this
=/ app=term
?: ?=([%invites *] wire)
%invite-store
%link-store
[%pass wire %agent [our.bowl app] %watch wire]~
::
%fact
=* mark p.cage.sign
=* vase q.cage.sign
?+ mark (on-agent:def wire sign)
%invite-update [(handle-invite-update:do !<(invite-update vase)) this]
%link-initial [~ this]
::
%link-update
:_ this
:- (send-update:do !<(update vase))
?: =(/discussions wire) ~
~[give-tile-data:do]
==
==
::
++ on-arvo
|= [=wire =sign-arvo]
^- (quip card _this)
?. ?=([%e %bound *] sign-arvo)
(on-arvo:def wire sign-arvo)
~? !accepted.sign-arvo
[dap.bowl "bind rejected!" binding.sign-arvo]
[~ this]
::
++ on-peek on-peek:def
++ on-leave on-leave:def
++ on-fail on-fail:def
--
::
~% %link-view-logic ..card ~
|_ =bowl:gall
+* md ~(. metadata bowl)
::
++ page-size 25
++ get-paginated
|* [page=(unit @ud) list=(list)]
^- [total=@ud pages=@ud page=_list]
=/ l=@ud (lent list)
:+ l
%+ add (div l page-size)
(min 1 (mod l page-size))
?~ page list
%+ swag
[(mul u.page page-size) page-size]
list
::
++ page-to-json
=, enjs:format
|* $: page-number=@ud
[total-items=@ud total-pages=@ud page=(list)]
item-to-json=$-(* json)
==
^- json
%- pairs
:~ 'totalItems'^(numb total-items)
'totalPages'^(numb total-pages)
'pageNumber'^(numb page-number)
'page'^a+(turn page item-to-json)
==
::
++ handle-http-request
|= =inbound-request:eyre
^- simple-payload:http
?. =(src.bowl our.bowl)
[[403 ~] ~]
:: request-line: parsed url + params
::
=/ =request-line
%- parse-request-line
url.request.inbound-request
=* req-head header-list.request.inbound-request
?+ method.request.inbound-request not-found:gen
%'GET'
(handle-get req-head request-line)
==
::
++ handle-get
|= [request-headers=header-list:http =request-line]
^- simple-payload:http
:: try to load file from clay
::
?~ ext.request-line
:: for extension-less requests, always just serve the index.html.
:: that way the js can load and figure out how to deal with that route.
::
$(request-line [[`%html ~[%'~link' 'index']] args.request-line])
=/ file=(unit octs)
?. ?=([%'~link' *] site.request-line) ~
(get-file-at /app/link [t.site u.ext]:request-line)
?~ file not-found:gen
?+ u.ext.request-line not-found:gen
%html (html-response:gen u.file)
%js (js-response:gen u.file)
%css (css-response:gen u.file)
%png (png-response:gen u.file)
==
::
++ get-file-at
|= [base=path file=path ext=@ta]
^- (unit octs)
:: only expose html, css and js files for now
::
?. ?=(?(%html %css %js %png) ext)
~
=/ =path
:* (scot %p our.bowl)
q.byk.bowl
(scot %da now.bowl)
(snoc (weld base file) ext)
==
?. .^(? %cu path)
~
%- some
%- as-octs:mimes:html
.^(@ %cx path)
::
++ do-poke
|= [app=term =mark =vase]
^- card
[%pass /create/[app]/[mark] %agent [our.bowl app] %poke mark vase]
::
++ handle-invite-update
|= upd=invite-update
^- (list card)
?. ?=(%accepted -.upd) ~
?. =(/link path.upd) ~
:~ :: sync the group
::
%^ do-poke %group-hook
%group-hook-action
!> ^- group-hook-action:group-hook
[%add ship path]:invite.upd
::
:: sync the metadata
::
%^ do-poke %metadata-hook
%metadata-hook-action
!> ^- metadata-hook-action:metadata-hook
[%add-synced ship path]:invite.upd
==
::
++ handle-action
|= =action
^- card
[%pass /action %agent [our.bowl %link-store] %poke %link-action !>(action)]
::
++ handle-view-action
|= act=view-action
^- (list card)
?- -.act
%create (handle-create +.act)
%delete (handle-delete +.act)
%invite (handle-invite +.act)
==
::
++ handle-create
|= [=path title=@t description=@t members=create-members real-group=?]
^- (list card)
=/ group-path=^path
?- -.members
%group path.members
::
%ships
%+ weld
?:(real-group ~ [~.~]~)
[(scot %p our.bowl) path]
==
=; group-setup=(list card)
%+ weld group-setup
:~ :: add collection to metadata-store
::
%^ do-poke %metadata-hook
%metadata-action
!> ^- metadata-action:md
:^ %add group-path
[%link path]
%* . *metadata:md
title title
description description
date-created now.bowl
creator our.bowl
==
::
:: expose the metadata
::
%^ do-poke %metadata-hook
%metadata-hook-action
!> ^- metadata-hook-action:metadata-hook
[%add-owned group-path]
::
:: watch the collection ourselves
::
%^ do-poke %link-listen-hook
%link-listen-action
!> ^- action:link-listen-hook
[%watch path]
==
?: ?=(%group -.members) ~
:: if the group is "real", make contact-view do the heavy lifting
::
?: real-group
:_ ~
%^ do-poke %contact-view
%contact-view-action
!> ^- contact-view-action:contact-view
[%create group-path ships.members title description]
:: for "unmanaged" groups, do it ourselves
::
:* :: create the new group
::
%^ do-poke %group-store
%group-action
!> ^- group-action:group-store
[%bundle group-path]
::
:: fill the new group
::
%^ do-poke %group-store
%group-action
!> ^- group-action:group-store
[%add (~(put in ships.members) our.bowl) group-path]
::
:: make group available
::
%^ do-poke %group-hook
%group-hook-action
!> ^- group-hook-action:group-hook
[%add our.bowl group-path]
::
:: mirror group into a permission
::
%^ do-poke %permission-group-hook
%permission-group-hook-action
!> ^- permission-group-hook-action:permission-group-hook
[%associate group-path [group-path^%white ~ ~]]
::
:: expose the permission
::
%^ do-poke %permission-hook
%permission-hook-action
!> ^- permission-hook-action:permission-hook
[%add-owned group-path group-path]
::
:: send invites
::
%+ turn ~(tap in ships.members)
|= =ship
^- card
%^ do-poke %invite-hook
%invite-action
!> ^- invite-action
:^ %invite /link
(sham group-path eny.bowl)
:* our.bowl
%group-hook
group-path
ship
title
==
==
::
++ handle-delete
|= =path
^- (list card)
=/ groups=(list ^path)
(groups-from-resource:md [%link path])
%- zing
%+ turn groups
|= =group=^path
%+ snoc
^- (list card)
:: if it's a real group, we can't/shouldn't unsync it. this leaves us with
:: no way to stop propagation of collection deletion.
::
?. ?=([%'~' ^] group-path) ~
:: if it's an unmanaged group, we just stop syncing the group & metadata,
:: and clean up the group (after un-hooking it, to not push deletion).
::
:~ %^ do-poke %group-hook
%group-hook-action
!> ^- group-hook-action:group-hook
[%remove group-path]
::
%^ do-poke %metadata-hook
%metadata-hook-action
!> ^- metadata-hook-action:metadata-hook
[%remove group-path]
::
%^ do-poke %group-store
%group-action
!> ^- group-action:group-store
[%unbundle group-path]
==
:: remove collection from metadata-store
::
%^ do-poke %metadata-store
%metadata-action
!> ^- metadata-action:md
[%remove group-path [%link path]]
::
++ handle-invite
|= [=path ships=(set ship)]
^- (list card)
%- zing
%+ turn (groups-from-resource:md %link path)
|= =group=^path
^- (list card)
:- %^ do-poke %group-store
%group-action
!> ^- group-action:group-store
[%add ships group-path]
:: for managed groups, rely purely on group logic for invites
::
?. ?=([%'~' ^] group-path)
~
:: for unmanaged groups, send invites manually
::
%+ turn ~(tap in ships)
|= =ship
^- card
%^ do-poke %invite-hook
%invite-action
!> ^- invite-action
:^ %invite /link
(sham group-path eny.bowl)
:* our.bowl
%group-hook
group-path
ship
(rsh 3 1 (spat path))
==
:: +give-tile-data: total unread count as json object
::
::NOTE the full recalc of totals here probably isn't the end of the world.
:: but in case it is, well, here it is.
::
++ give-tile-data
^- card
=; =json
[%give %fact ~[/tile] %json !>(json)]
%+ frond:enjs:format 'unseen'
%- numb:enjs:format
%- %~ rep in
(scry-for (jug path url) /unseen)
|= [[=path unseen=(set url)] total=@ud]
%+ add total
~(wyt in unseen)
::
:: +give-initial-submissions: page of submissions on path
::
:: for the / path, give page for every path
::
:: result is in the shape of: {
:: "/some/path": {
:: totalItems: 1,
:: totalPages: 1,
:: pageNumber: 0,
:: page: [
:: { commentCount: 1, ...restOfTheSubmission }
:: ]
:: },
:: "/maybe/more": { etc }
:: }
::
++ give-initial-submissions
~/ %link-view-initial-submissions
|= [p=@ud =requested=path]
^- (list card)
:_ :: only keep the base case alive (for updates), kick all others
::
?: &(=(0 p) ?=(~ requested-path)) ~
[%give %kick ~ ~]~
=; =json
[%give %fact ~ %json !>(json)]
%+ frond:enjs:format 'initial-submissions'
%- pairs:enjs:format
%+ turn
%~ tap by
%+ scry-for (map path submissions)
[%submissions requested-path]
|= [=path =submissions]
^- [@t json]
:- (spat path)
=; =json
:: add unseen count
::
?> ?=(%o -.json)
:- %o
%+ ~(put by p.json) 'unseenCount'
%- numb:enjs:format
%~ wyt in
%+ scry-for (set url)
[%unseen path]
?: &(=(0 p) ?=(~ requested-path))
:: for a broad-scope initial result, only give total counts
::
=, enjs:format
%- pairs
=+ l=(lent submissions)
:~ 'totalItems'^(numb l)
'totalPages'^(numb (div l page-size))
==
%^ page-to-json p
%+ get-paginated `p
submissions
|= =submission
^- json
=/ =json (submission:en-json submission)
?> ?=([%o *] json)
:: add in seen status
::
=. p.json
%+ ~(put by p.json) 'seen'
:- %b
%+ scry-for ?
[%seen (build-discussion-path path url.submission)]
:: add in comment count
::
=; comment-count=@ud
:- %o
%+ ~(put by p.json) 'commentCount'
(numb:enjs:format comment-count)
%- lent
~| [path url.submission]
^- comments
=- (~(got by (~(got by -) path)) url.submission)
%+ scry-for (per-path-url comments)
:- %discussions
(build-discussion-path path url.submission)
::
++ give-specific-submission
|= [n=@ud =path =url]
:_ [%give %kick ~ ~]~
=; =json
[%give %fact ~ %json !>(json)]
%+ frond:enjs:format 'submission'
^- json
=; sub=(unit submission)
?~ sub ~
(submission:en-json u.sub)
=/ =submissions
=- (~(got by -) path)
%+ scry-for (map ^path submissions)
[%submissions path]
|-
?~ submissions ~
=* sub i.submissions
?. =(url.sub url)
$(submissions t.submissions)
?: =(0 n) `sub
$(n (dec n), submissions t.submissions)
::
++ give-initial-discussions
|= [p=@ud =path =url]
^- (list card)
:_ ?: =(0 p) ~
[%give %kick ~ ~]~
=; =json
[%give %fact ~ %json !>(json)]
%+ frond:enjs:format 'initial-discussions'
%^ page-to-json p
%+ get-paginated `p
=- (~(got by (~(got by -) path)) url)
%+ scry-for (per-path-url comments)
[%discussions (build-discussion-path path url)]
comment:en-json
::
++ send-update
|= =update
^- card
?+ -.update ~|([dap.bowl %unexpected-update -.update] !!)
%submissions
%+ give-json
(update:en-json update)
:~ /json/0/submissions
(weld /json/0/submissions path.update)
==
::
%discussions
%+ give-json
(update:en-json update)
:_ ~
%+ weld /json/0/discussions
(build-discussion-path [path url]:update)
::
%observation
%+ give-json
(update:en-json update)
~[/json/seen]
==
::
++ give-json
|= [=json paths=(list path)]
^- card
[%give %fact paths %json !>(json)]
::
++ scry-for
|* [=mold =path]
.^ mold
%gx
(scot %p our.bowl)
%link-store
(scot %da now.bowl)
(snoc `^path`path %noun)
==
--

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 951 B

View File

@ -0,0 +1,20 @@
<!doctype html>
<html>
<head>
<title>Links</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<link rel="stylesheet" href="/~link/css/index.css" />
<link rel="icon" type="image/png" href="/~launch/img/Favicon.png">
</head>
<body class="w-100 h-100">
<div id="root" class="w-100 h-100">
</div>
<script src="/~channel/channel.js"></script>
<script src="/~modulo/session.js"></script>
<script src="/~link/js/index.js"></script>
</body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,248 @@
:: metadata-hook: allow syncing foreign metadata
::
:: watch paths:
:: /group/%group-path all updates related to this group
::
/- *metadata-store, *metadata-hook
/+ default-agent, dbug
~% %metadata-hook-top ..is ~
|%
+$ card card:agent:gall
+$ versioned-state
$% state-zero
==
::
+$ state-zero
$: %0
synced=(map group-path ship)
==
--
=| state-zero
=* state -
%- agent:dbug
^- agent:gall
=<
|_ =bowl:gall
+* this .
hc ~(. +> bowl)
def ~(. (default-agent this %|) bowl)
::
++ on-init
[[%pass /updates %agent [our.bowl %metadata-store] %watch /updates]~ this]
::
++ on-save !>(state)
++ on-load |=(=vase `this(state !<(state-zero vase)))
++ on-leave on-leave:def
++ on-peek on-peek:def
++ on-arvo on-arvo:def
++ on-fail on-fail:def
++ on-poke
|= [=mark =vase]
^- (quip card _this)
?+ mark (on-poke:def mark vase)
%metadata-hook-action
=^ cards state
(poke-hook-action:hc !<(metadata-hook-action vase))
[cards this]
::
%metadata-action
[(poke-action:hc !<(metadata-action vase)) this]
==
::
++ on-watch
|= =path
^- (quip card _this)
?+ path (on-watch:def path)
[%group *] [(watch-group:hc t.path) this]
==
::
++ on-agent
|= [=wire =sign:agent:gall]
^- (quip card _this)
?+ -.sign (on-agent:def wire sign)
%kick =^(cards state (kick:hc wire) [cards this])
%watch-ack =^(cards state (watch-ack:hc wire p.sign) [cards this])
%fact
?+ p.cage.sign (on-agent:def wire sign)
%metadata-update
=^ cards state
(fact-metadata-update:hc wire !<(metadata-update q.cage.sign))
[cards this]
==
==
--
::
|_ =bowl:gall
++ poke-hook-action
|= act=metadata-hook-action
^- (quip card _state)
|^
?- -.act
%add-owned
?> (team:title our.bowl src.bowl)
:- ~
?: (~(has by synced) path.act) state
state(synced (~(put by synced) path.act our.bowl))
::
%add-synced
?> (team:title our.bowl src.bowl)
=/ =path [%group path.act]
?: (~(has by synced) path.act) [~ state]
:_ state(synced (~(put by synced) path.act ship.act))
[%pass path %agent [ship.act %metadata-hook] %watch path]~
::
%remove
=/ ship (~(get by synced) path.act)
?~ ship [~ state]
?: &(!=(u.ship src.bowl) ?!((team:title our.bowl src.bowl)))
[~ state]
:_ state(synced (~(del by synced) path.act))
%- zing
:~ (unsubscribe [%group path.act] u.ship)
[%give %kick ~[[%group path.act]] ~]~
==
==
::
++ unsubscribe
|= [=path =ship]
^- (list card)
?: =(ship our.bowl)
[%pass path %agent [our.bowl %metadata-store] %leave ~]~
[%pass path %agent [ship %metadata-hook] %leave ~]~
--
::
++ poke-action
|= act=metadata-action
^- (list card)
|^
?: (team:title our.bowl src.bowl)
?- -.act
%add (send group-path.act)
%remove (send group-path.act)
==
?> (is-permitted src.bowl group-path.act)
?- -.act
%add (metadata-poke our.bowl %metadata-store)
%remove (metadata-poke our.bowl %metadata-store)
==
::
++ send
|= =group-path
^- (list card)
=/ =ship
%+ slav %p
?: (is-managed group-path) (snag 0 group-path)
(snag 1 group-path)
=/ app ?:(=(ship our.bowl) %metadata-store %metadata-hook)
(metadata-poke ship app)
::
++ metadata-poke
|= [=ship app=@tas]
^- (list card)
[%pass / %agent [ship app] %poke %metadata-action !>(act)]~
::
++ is-managed
|= =path
^- ?
?> ?=(^ path)
!=(i.path '~')
--
::
++ watch-group
|= =path
^- (list card)
|^
?> =(our.bowl (~(got by synced) path))
?> (is-permitted src.bowl path)
%+ turn ~(tap by (metadata-scry path))
|= [[=group-path =resource] =metadata]
^- card
[%give %fact ~ %metadata-update !>([%add group-path resource metadata])]
::
++ metadata-scry
|= pax=^path
^- associations
=. pax ;:(weld /=metadata-store/(scot %da now.bowl)/group pax /noun)
.^(associations %gx pax)
--
::
++ fact-metadata-update
|= [wir=wire fact=metadata-update]
^- (quip card _state)
|^
[?:((team:title our.bowl src.bowl) handle-local handle-foreign) state]
::
++ handle-local
?+ -.fact ~
%add
?. (~(has by synced) group-path.fact) ~
(give group-path.fact fact)
::
%update-metadata
?. (~(has by synced) group-path.fact) ~
(give group-path.fact fact)
::
%remove
?. (~(has by synced) group-path.fact) ~
(give group-path.fact fact)
==
::
++ handle-foreign
?+ -.fact ~
%add
?. =(src.bowl (~(got by synced) group-path.fact)) ~
(poke fact)
::
%update-metadata
?. =(src.bowl (~(got by synced) group-path.fact)) ~
(poke [%add +.fact])
::
%remove
?. =(src.bowl (~(got by synced) group-path.fact)) ~
(poke fact)
==
::
++ give
|= [=path upd=metadata-update]
^- (list card)
[%give %fact ~[[%group path]] %metadata-update !>(upd)]~
::
++ poke
|= act=metadata-action
^- (list card)
[%pass / %agent [our.bowl %metadata-store] %poke %metadata-action !>(act)]~
--
::
++ kick
|= wir=wire
^- (quip card _state)
:_ state
?+ wir !!
[%updates ~]
[%pass /updates %agent [our.bowl %metadata-store] %watch /updates]~
::
[%group @ *]
?. (~(has by synced) t.wir) ~
=/ =ship (~(got by synced) t.wir)
?: =(ship our.bowl)
[%pass wir %agent [our.bowl %metadata-store] %watch wir]~
[%pass wir %agent [ship %metadata-hook] %watch wir]~
==
::
++ watch-ack
|= [wir=wire saw=(unit tang)]
^- (quip card _state)
?> ?=(^ wir)
[~ ?~(saw state state(synced (~(del by synced) t.wir)))]
::
++ is-permitted
|= [=ship pax=path]
^- ?
=. pax
;: weld
/=permission-store/(scot %da now.bowl)/permitted
[(scot %p ship) pax]
/noun
==
.^(? %gx pax)
--

View File

@ -0,0 +1,205 @@
:: metadata-store: data store for application metadata and mappings
:: between groups and resources within applications
::
:: group-paths are expected to be an existing group path
:: resources are expected to correspond to existing app paths
::
:: note: when scrying for metadata, to make the arguments safe in paths,
:: encode group-path and app-path using (scot %t (spat group-path))
::
:: +watch paths:
:: /all assocations + updates
:: /updates just updates
:: /app-name/%app-name specific app's associations + updates
::
:: +peek paths:
:: /associations all associations
:: /group-indices all group indices
:: /app-indices all app indices
:: /resource-indices all resource indices
:: /metadata/%group-path/%app-name/%app-path specific metadatum
:: /app-name/%app-name associations for app
:: /group/%group-path associations for group
::
/+ *metadata-json, default-agent, verb, dbug
|%
+$ card card:agent:gall
::
+$ versioned-state
$% state-zero
==
::
+$ state-zero
$: %0
=associations
group-indices=(jug group-path resource)
app-indices=(jug app-name [group-path app-path])
resource-indices=(jug resource group-path)
==
--
::
=| state-zero
=* state -
%+ verb |
%- agent:dbug
^- agent:gall
=<
|_ =bowl:gall
+* this .
metadata-core +>
mc ~(. metadata-core bowl)
def ~(. (default-agent this %|) bowl)
::
++ on-init on-init:def
++ on-save !>(state)
++ on-load
|= old=vase
`this(state !<(state-zero old))
::
++ on-poke
|= [=mark =vase]
^- (quip card _this)
?> (team:title our.bowl src.bowl)
=^ cards state
?: ?=(%metadata-action mark)
(poke-metadata-action:mc !<(metadata-action vase))
(on-poke:def mark vase)
[cards this]
::
++ on-watch
|= =path
^- (quip card _this)
?> (team:title our.bowl src.bowl)
|^
=/ cards=(list card)
?+ path (on-watch:def path)
[%all ~] (give %metadata-update !>([%associations associations]))
[%updates ~] ~
[%app-name @ ~]
=/ =app-name i.t.path
=/ app-indices (metadata-for-app:mc app-name)
(give %metadata-update !>([%associations app-indices]))
==
[cards this]
::
++ give
|= =cage
^- (list card)
[%give %fact ~ cage]~
--
::
++ on-leave on-leave:def
::
++ on-peek
|= =path
^- (unit (unit cage))
?+ path (on-peek:def path)
[%y %group-indices ~] ``noun+!>(group-indices)
[%y %app-indices ~] ``noun+!>(app-indices)
[%y %resource-indices ~] ``noun+!>(resource-indices)
[%x %associations ~] ``noun+!>(associations)
[%x %app-name @ ~]
=/ =app-name i.t.t.path
``noun+!>((metadata-for-app:mc app-name))
::
[%x %group *]
=/ =group-path t.t.path
``noun+!>((metadata-for-group:mc group-path))
::
[%x %metadata @ @ @ ~]
=/ =group-path (stab (slav %t i.t.t.path))
=/ =resource [`@tas`i.t.t.t.path (stab (slav %t i.t.t.t.t.path))]
``noun+!>((~(get by associations) [group-path resource]))
==
::
++ on-agent on-agent:def
++ on-arvo on-arvo:def
++ on-fail on-fail:def
--
::
|_ =bowl:gall
++ poke-metadata-action
|= act=metadata-action
^- (quip card _state)
?> (team:title our.bowl src.bowl)
?- -.act
%add
(handle-add group-path.act resource.act metadata.act)
::
%remove
(handle-remove group-path.act resource.act)
==
::
++ handle-add
|= [=group-path =resource =metadata]
^- (quip card _state)
:- %+ send-diff app-name.resource
?. (~(has by resource-indices) resource)
[%add group-path resource metadata]
[%update-metadata group-path resource metadata]
%= state
associations
(~(put by associations) [group-path resource] metadata)
::
group-indices
(~(put ju group-indices) group-path resource)
::
app-indices
(~(put ju app-indices) app-name.resource [group-path app-path.resource])
::
resource-indices
(~(put ju resource-indices) resource group-path)
==
::
++ handle-remove
|= [=group-path =resource]
^- (quip card _state)
:- (send-diff app-name.resource [%remove group-path resource])
%= state
associations
(~(del by associations) [group-path resource])
::
group-indices
(~(del ju group-indices) group-path resource)
::
app-indices
(~(del ju app-indices) app-name.resource [group-path app-path.resource])
::
resource-indices
(~(del ju resource-indices) resource group-path)
==
::
++ metadata-for-app
|= =app-name
^- ^associations
%- ~(gas by *^associations)
%+ turn ~(tap in (~(gut by app-indices) app-name ~))
|= [=group-path =app-path]
:- [group-path [app-name app-path]]
(~(got by associations) [group-path [app-name app-path]])
::
++ metadata-for-group
|= =group-path
^- ^associations
%- ~(gas by *^associations)
%+ turn ~(tap in (~(gut by group-indices) group-path ~))
|= =resource
:- [group-path resource]
(~(got by associations) [group-path resource])
::
++ send-diff
|= [=app-name upd=metadata-update]
^- (list card)
|^
%- zing
:~ (update-subscribers /all upd)
(update-subscribers /updates upd)
(update-subscribers [%app-name app-name ~] upd)
==
::
++ update-subscribers
|= [pax=path upd=metadata-update]
^- (list card)
[%give %fact ~[pax] %metadata-update !>(upd)]~
--
--

View File

@ -3,7 +3,7 @@
:: mirror the ships in specified groups to specified permission paths :: mirror the ships in specified groups to specified permission paths
:: ::
/- *group-store, *permission-group-hook /- *group-store, *permission-group-hook
/+ *permission-json, default-agent, verb /+ *permission-json, default-agent, verb, dbug
:: ::
|% |%
+$ state +$ state
@ -25,6 +25,7 @@
=* state - =* state -
:: ::
%+ verb | %+ verb |
%- agent:dbug
^- agent:gall ^- agent:gall
=< =<
|_ =bowl:gall |_ =bowl:gall

View File

@ -7,6 +7,7 @@
/- *permission-hook /- *permission-hook
/+ *permission-json, default-agent, verb, dbug /+ *permission-json, default-agent, verb, dbug
:: ::
~% %permission-hook-top ..is ~
|% |%
+$ state +$ state
$% [%0 state-0] $% [%0 state-0]
@ -195,7 +196,15 @@
%delete %delete
?. (~(has by synced) path.diff) ?. (~(has by synced) path.diff)
[~ state] [~ state]
:_ state(synced (~(del by synced) path.diff)) =/ control=(unit path)
=+ (~(got by synced) path.diff)
?. =(our.bowl ship) ~
`access-control
:_ %_ state
synced (~(del by synced) path.diff)
access-control ?~ control access-control
(~(del ju access-control) u.control path.diff)
==
:_ ~ :_ ~
:* %pass :* %pass
[%permission path.diff] [%permission path.diff]

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 245 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 951 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -10,13 +10,13 @@
=content "width=device-width, initial-scale=1, shrink-to-fit=no"; =content "width=device-width, initial-scale=1, shrink-to-fit=no";
;link(rel "stylesheet", href "/~publish/index.css"); ;link(rel "stylesheet", href "/~publish/index.css");
;link(rel "icon", type "image/png", href "/~launch/img/Favicon.png"); ;link(rel "icon", type "image/png", href "/~launch/img/Favicon.png");
;script@"/~/channel/channel.js"; ;script@"/~channel/channel.js";
;script@"/~modulo/session.js"; ;script@"/~modulo/session.js";
;script: window.injectedState = {(en-json:html inject)} ;script: window.injectedState = {(en-json:html inject)}
== ==
:: ::
;body ;body
;div#root; ;div#root.w-100.h-100;
;script@"/~publish/index.js"; ;script@"/~publish/index.js";
== ==
== ==

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,96 @@
/- *s3
/+ s3-json, default-agent, verb, dbug
~% %s3-top ..is ~
|%
+$ card card:agent:gall
+$ versioned-state
$% state-zero
==
::
+$ state-zero [%0 =credentials =configuration]
--
::
=| state-zero
=* state -
::
%- agent:dbug
%+ verb |
^- agent:gall
~% %s3-agent-core ..card ~
|_ =bowl:gall
+* this .
def ~(. (default-agent this %|) bowl)
::
++ on-init on-init:def
++ on-save !>(state)
++ on-load
|= old-vase=vase
[~ this(state !<(state-zero old-vase))]
::
++ on-poke
~/ %s3-poke
|= [=mark =vase]
^- (quip card _this)
|^
?> (team:title our.bowl src.bowl)
=^ cards state
?+ mark (on-poke:def mark vase)
%s3-action (poke-action !<(action vase))
==
[cards this]
::
++ poke-action
|= act=action
^- (quip card _state)
:- [%give %fact [/all]~ %s3-update !>(act)]~
?- -.act
%set-endpoint
state(endpoint.credentials endpoint.act)
::
%set-access-key-id
state(access-key-id.credentials access-key-id.act)
::
%set-secret-access-key
state(secret-access-key.credentials secret-access-key.act)
::
%set-current-bucket
%_ state
current-bucket.configuration bucket.act
buckets.configuration (~(put in buckets.configuration) bucket.act)
==
::
%add-bucket
state(buckets.configuration (~(put in buckets.configuration) bucket.act))
::
%remove-bucket
state(buckets.configuration (~(del in buckets.configuration) bucket.act))
==
--
::
++ on-watch
~/ %s3-watch
|= =path
^- (quip card _this)
|^
?> (team:title our.bowl src.bowl)
=/ cards=(list card)
?+ path (on-watch:def path)
[%all ~]
:~ (give %s3-update !>([%credentials credentials]))
(give %s3-update !>([%configuration configuration]))
==
==
[cards this]
::
++ give
|= =cage
^- card
[%give %fact ~ cage]
--
::
++ on-leave on-leave:def
++ on-peek on-peek:def
++ on-agent on-agent:def
++ on-arvo on-arvo:def
++ on-fail on-fail:def
--

View File

@ -54,7 +54,7 @@
:_ this :_ this
:~ [%pass /bind/soto %arvo %e %connect [~ /'~dojo'] %soto] :~ [%pass /bind/soto %arvo %e %connect [~ /'~dojo'] %soto]
:* %pass /launch/soto %agent [our.bol %launch] %poke :* %pass /launch/soto %agent [our.bol %launch] %poke
%launch-action !>([%soto /sototile '/~dojo/js/tile.js']) %launch-action !>([%add %soto /sototile '/~dojo/js/tile.js'])
== ==
== ==
++ on-save !>(state) ++ on-save !>(state)

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -6,9 +6,11 @@
<meta name="viewport" <meta name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"/> content="width=device-width, initial-scale=1, shrink-to-fit=no"/>
<link rel="stylesheet" href="/~dojo/css/index.css" /> <link rel="stylesheet" href="/~dojo/css/index.css" />
<link rel="icon" type="image/png" href="/~launch/img/Favicon.png">
</head> </head>
<body class="bg-black"> <body class="w-100 h-100">
<div id="root" /> <div id="root" class="w-100 h-100">
</div>
<script src="/~/channel/channel.js"></script> <script src="/~/channel/channel.js"></script>
<script src="/~modulo/session.js"></script> <script src="/~modulo/session.js"></script>
<script src="/~dojo/js/index.js"></script> <script src="/~dojo/js/index.js"></script>

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More