mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-11-30 10:46:11 +03:00
Merge branch 'release/next-js' into lf/infinite-link-scroll
This commit is contained in:
commit
3418b0f6e6
2
.github/actions/glob/Dockerfile
vendored
2
.github/actions/glob/Dockerfile
vendored
@ -1,4 +1,4 @@
|
||||
FROM jaredtobin/janeway:v0.13.1
|
||||
FROM jaredtobin/janeway:v0.13.3
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
EXPOSE 22/tcp
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
|
8
.github/actions/glob/entrypoint.sh
vendored
8
.github/actions/glob/entrypoint.sh
vendored
@ -10,13 +10,7 @@ chmod 600 service-account
|
||||
chmod 600 id_ssh
|
||||
chmod 600 id_ssh.pub
|
||||
|
||||
LANDSCAPE_STREAM="development"
|
||||
export LANDSCAPE_STREAM
|
||||
|
||||
LANDSCAPE_SHORTHASH="${GITHUB_SHA:0:7}"
|
||||
export LANDSCAPE_SHORTHASH
|
||||
|
||||
janeway release glob --no-pill \
|
||||
janeway release glob --dev --no-pill \
|
||||
--credentials service-account \
|
||||
--ssh-key id_ssh \
|
||||
--do-it-live \
|
||||
|
19
.github/workflows/build.yml
vendored
19
.github/workflows/build.yml
vendored
@ -47,10 +47,22 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
# We only want the extra nix config on linux, where it is necessary
|
||||
# for the docker build. We don't want in on Mac, where it isn't but
|
||||
# it breaks the nix install. The two `if` clauses should be mutually
|
||||
# exclusive
|
||||
- uses: cachix/install-nix-action@v12
|
||||
with:
|
||||
extra_nix_config: |
|
||||
system-features = nixos-test benchmark big-parallel kvm
|
||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
- uses: cachix/install-nix-action@v12
|
||||
if: ${{ matrix.os != 'ubuntu-latest' }}
|
||||
|
||||
- uses: cachix/cachix-action@v8
|
||||
with:
|
||||
name: ares
|
||||
name: ${{ secrets.CACHIX_NAME }}
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
|
||||
- run: nix-build -A urbit --arg enableStatic true
|
||||
@ -58,6 +70,9 @@ jobs:
|
||||
- if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
run: nix-build -A urbit-tests
|
||||
|
||||
- if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
run: nix-build -A docker-image
|
||||
|
||||
haskell:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@ -73,7 +88,7 @@ jobs:
|
||||
- uses: cachix/install-nix-action@v12
|
||||
- uses: cachix/cachix-action@v8
|
||||
with:
|
||||
name: ares
|
||||
name: ${{ secrets.CACHIX_NAME }}
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
|
||||
- run: nix-build -A hs.urbit-king.components.exes.urbit-king --arg enableStatic true
|
||||
|
2
.github/workflows/merge.yml
vendored
2
.github/workflows/merge.yml
vendored
@ -13,5 +13,5 @@ jobs:
|
||||
with:
|
||||
type: now
|
||||
target_branch: release/next-js
|
||||
github_token: ${{ github.token }}
|
||||
github_token: ${{ secrets.JANEWAY_BOT_TOKEN }}
|
||||
|
||||
|
51
.github/workflows/release-docker.yml
vendored
Normal file
51
.github/workflows/release-docker.yml
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
name: release-docker
|
||||
|
||||
on:
|
||||
release: null
|
||||
push:
|
||||
tags: ['urbit-v*']
|
||||
|
||||
jobs:
|
||||
upload:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- { os: ubuntu-latest, system: x86_64-linux }
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: cachix/install-nix-action@v12
|
||||
with:
|
||||
extra_nix_config: |
|
||||
system-features = nixos-test benchmark big-parallel kvm
|
||||
- uses: cachix/cachix-action@v8
|
||||
with:
|
||||
name: ${{ secrets.CACHIX_NAME }}
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
- uses: docker/docker-login-action@v1.8.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- uses: christian-korneck/update-container-description-action@v1
|
||||
env:
|
||||
DOCKER_USER: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
DOCKER_PASS: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
with:
|
||||
destination_container_repo: ${{ secrets.DOCKERHUB_USERNAME }}/urbit
|
||||
provider: dockerhub
|
||||
short_description: 'Urbit: a clean-slate OS and network for the 21st century'
|
||||
readme_file: 'pkg/docker-image/README.md'
|
||||
|
||||
- run: |
|
||||
version="$(cat ./pkg/urbit/version)"
|
||||
image="$(nix-build -A docker-image)"
|
||||
imageName="$(nix-instantiate --eval -A docker-image.imageName | cut -d'"' -f2)"
|
||||
imageTag="$(nix-instantiate --eval -A docker-image.imageTag | cut -d'"' -f2)"
|
||||
# Load the image from the nix-built tarball
|
||||
docker load -i $image
|
||||
docker tag "$imageName:$imageTag" ${{secrets.DOCKERHUB_USERNAME }}/urbit:v$version
|
||||
docker tag "$imageName:$imageTag" ${{secrets.DOCKERHUB_USERNAME }}/urbit:latest
|
||||
docker push ${{secrets.DOCKERHUB_USERNAME }}/urbit:v$version
|
||||
docker push ${{secrets.DOCKERHUB_USERNAME }}/urbit:latest
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@ -20,7 +20,7 @@ jobs:
|
||||
- uses: cachix/install-nix-action@v12
|
||||
- uses: cachix/cachix-action@v8
|
||||
with:
|
||||
name: ares
|
||||
name: ${{ secrets.CACHIX_NAME }}
|
||||
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
|
||||
- uses: google-github-actions/setup-gcloud@v0.2.0
|
||||
|
@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c512d0c3da3ce7f0ac25babcbbceba04a9e62975e9d92aec56ab00e1c2fc6224
|
||||
size 8618963
|
||||
oid sha256:288b3ab68e2e3946dbf0e0de05c2907c351ec97fcc08134e558569eab4121c94
|
||||
size 8809816
|
||||
|
@ -115,6 +115,8 @@ let
|
||||
|
||||
urbit = callPackage ./nix/pkgs/urbit { inherit enableStatic; };
|
||||
|
||||
docker-image = callPackage ./nix/pkgs/docker-image { };
|
||||
|
||||
hs = callPackage ./nix/pkgs/hs {
|
||||
inherit enableStatic;
|
||||
inherit (pkgsCross) haskell-nix;
|
||||
@ -158,6 +160,8 @@ let
|
||||
};
|
||||
};
|
||||
|
||||
inherit (pkgsNative) skopeo;
|
||||
|
||||
# A convenience function for constructing a shell.nix for any of the
|
||||
# pkgsLocal derivations by automatically propagating any dependencies
|
||||
# to the nix-shell.
|
||||
|
68
nix/pkgs/docker-image/default.nix
Normal file
68
nix/pkgs/docker-image/default.nix
Normal file
@ -0,0 +1,68 @@
|
||||
{ urbit, libcap, coreutils, bashInteractive, dockerTools, writeScriptBin, amesPort ? 34343 }:
|
||||
let
|
||||
startUrbit = writeScriptBin "start-urbit" ''
|
||||
#!${bashInteractive}/bin/bash
|
||||
|
||||
set -eu
|
||||
|
||||
# If the container is not started with the `-i` flag
|
||||
# then STDIN will be closed and we need to start
|
||||
# Urbit/vere with the `-t` flag.
|
||||
ttyflag=""
|
||||
if [ ! -t 0 ]; then
|
||||
echo "Running with no STDIN"
|
||||
ttyflag="-t"
|
||||
fi
|
||||
|
||||
# Check if there is a keyfile, if so boot a ship with its name, and then remove the key
|
||||
if [ -e *.key ]; then
|
||||
# Get the name of the key
|
||||
keynames="*.key"
|
||||
keys=( $keynames )
|
||||
keyname=''${keys[0]}
|
||||
mv $keyname /tmp
|
||||
|
||||
# Boot urbit with the key, exit when done booting
|
||||
urbit $ttyflag -w $(basename $keyname .key) -k /tmp/$keyname -c $(basename $keyname .key) -p ${toString amesPort} -x
|
||||
|
||||
# Remove the keyfile for security
|
||||
rm /tmp/$keyname
|
||||
rm *.key || true
|
||||
elif [ -e *.comet ]; then
|
||||
cometnames="*.comet"
|
||||
comets=( $cometnames )
|
||||
cometname=''${comets[0]}
|
||||
rm *.comet
|
||||
|
||||
urbit $ttyflag -c $(basename $cometname .comet) -p ${toString amesPort} -x
|
||||
fi
|
||||
|
||||
# Find the first directory and start urbit with the ship therein
|
||||
dirnames="*/"
|
||||
dirs=( $dirnames )
|
||||
dirname=''${dirnames[0]}
|
||||
|
||||
urbit $ttyflag -p ${toString amesPort} $dirname
|
||||
'';
|
||||
|
||||
|
||||
in dockerTools.buildImage {
|
||||
name = "urbit";
|
||||
tag = "v${urbit.version}";
|
||||
contents = [ bashInteractive urbit startUrbit coreutils ];
|
||||
runAsRoot = ''
|
||||
#!${bashInteractive}
|
||||
mkdir -p /urbit
|
||||
mkdir -p /tmp
|
||||
${libcap}/bin/setcap 'cap_net_bind_service=+ep' /bin/urbit
|
||||
'';
|
||||
config = {
|
||||
Cmd = [ "/bin/start-urbit" ];
|
||||
Env = [ "PATH=/bin" ];
|
||||
WorkingDir = "/urbit";
|
||||
Volumes = {
|
||||
"/urbit" = {};
|
||||
};
|
||||
Expose = [ "80/tcp" "${toString amesPort}/udp" ];
|
||||
};
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
/- glob
|
||||
/+ default-agent, verb, dbug
|
||||
|%
|
||||
++ hash 0v5.sekq0.8skge.ekt62.i73ig.g5sep
|
||||
++ hash 0v1.4u9gp.rs1fi.ki7ok.ib4cp.mgdvs
|
||||
+$ state-0 [%0 hash=@uv glob=(unit (each glob:glob tid=@ta))]
|
||||
+$ all-states
|
||||
$% state-0
|
||||
|
@ -92,27 +92,6 @@
|
||||
%run-updates (is-allowed resource.q.update bowl %.y)
|
||||
==
|
||||
::
|
||||
++ resource-for-update
|
||||
|= =vase
|
||||
^- (unit resource:res)
|
||||
=/ =update:store !<(update:store vase)
|
||||
?- -.q.update
|
||||
%add-graph `resource.q.update
|
||||
%remove-graph `resource.q.update
|
||||
%add-nodes `resource.q.update
|
||||
%remove-nodes `resource.q.update
|
||||
%add-signatures `resource.uid.q.update
|
||||
%remove-signatures `resource.uid.q.update
|
||||
%archive-graph `resource.q.update
|
||||
%unarchive-graph ~
|
||||
%add-tag ~
|
||||
%remove-tag ~
|
||||
%keys ~
|
||||
%tags ~
|
||||
%tag-queries ~
|
||||
%run-updates `resource.q.update
|
||||
==
|
||||
::
|
||||
++ initial-watch
|
||||
|= [=path =resource:res]
|
||||
^- vase
|
||||
|
@ -230,7 +230,7 @@
|
||||
?> ?=(%0 -.update)
|
||||
=? p.update =(p.update *time) now.bowl
|
||||
?- -.q.update
|
||||
%add-graph (add-graph +.q.update)
|
||||
%add-graph (add-graph p.update +.q.update)
|
||||
%remove-graph (remove-graph +.q.update)
|
||||
%add-nodes (add-nodes p.update +.q.update)
|
||||
%remove-nodes (remove-nodes p.update +.q.update)
|
||||
@ -247,7 +247,8 @@
|
||||
==
|
||||
::
|
||||
++ add-graph
|
||||
|= $: =resource:store
|
||||
|= $: =time
|
||||
=resource:store
|
||||
=graph:store
|
||||
mark=(unit mark:store)
|
||||
overwrite=?
|
||||
@ -258,9 +259,13 @@
|
||||
!(~(has by graphs) resource)
|
||||
== ==
|
||||
?> (validate-graph graph mark)
|
||||
=/ =logged-update:store
|
||||
[%0 time %add-graph resource graph mark overwrite]
|
||||
=/ =update-log:store
|
||||
(gas:orm-log ~ [time logged-update] ~)
|
||||
:_ %_ state
|
||||
graphs (~(put by graphs) resource [graph mark])
|
||||
update-logs (~(put by update-logs) resource (gas:orm-log ~ ~))
|
||||
update-logs (~(put by update-logs) resource update-log)
|
||||
archive (~(del by archive) resource)
|
||||
::
|
||||
validators
|
||||
@ -643,6 +648,7 @@
|
||||
%- graph-update
|
||||
^- update:store
|
||||
?- -.q.update
|
||||
%add-graph update(resource.q resource)
|
||||
%add-nodes update(resource.q resource)
|
||||
%remove-nodes update(resource.q resource)
|
||||
%add-signatures update(resource.uid.q resource)
|
||||
|
@ -81,15 +81,6 @@
|
||||
==
|
||||
--
|
||||
::
|
||||
++ resource-for-update
|
||||
|= =vase
|
||||
^- (unit resource)
|
||||
=/ =update:store
|
||||
!<(update:store vase)
|
||||
?: ?=(%initial -.update)
|
||||
~
|
||||
`resource.update
|
||||
::
|
||||
++ take-update
|
||||
|= =vase
|
||||
^- [(list card) agent]
|
||||
|
@ -9,11 +9,17 @@
|
||||
+$ card card:agent:gall
|
||||
+$ versioned-state
|
||||
$% state-0
|
||||
state-1
|
||||
==
|
||||
::
|
||||
+$ state-0
|
||||
$: %0
|
||||
watching=(set [resource index:post])
|
||||
[%0 base-state-0]
|
||||
::
|
||||
+$ state-1
|
||||
[%1 base-state-0]
|
||||
::
|
||||
+$ base-state-0
|
||||
$: watching=(set [resource index:post])
|
||||
mentions=_&
|
||||
watch-on-self=_&
|
||||
==
|
||||
@ -36,7 +42,7 @@
|
||||
::
|
||||
--
|
||||
::
|
||||
=| state-0
|
||||
=| state-1
|
||||
=* state -
|
||||
::
|
||||
=<
|
||||
@ -57,13 +63,25 @@
|
||||
::
|
||||
++ on-save !>(state)
|
||||
++ on-load
|
||||
|= old=vase
|
||||
|= =vase
|
||||
^- (quip card _this)
|
||||
:_ this(state !<(state-0 old))
|
||||
=+ !<(old=versioned-state vase)
|
||||
=| cards=(list card)
|
||||
|-
|
||||
?: ?=(%0 -.old)
|
||||
%_ $
|
||||
-.old %1
|
||||
::
|
||||
cards
|
||||
:_ cards
|
||||
[%pass / %agent [our dap]:bowl %poke noun+!>(%rewatch-dms)]
|
||||
==
|
||||
:_ this(state old)
|
||||
=. cards (flop cards)
|
||||
%+ welp
|
||||
?: (~(has by wex.bowl) [/graph our.bowl %graph-store])
|
||||
~
|
||||
~[watch-graph:ha]
|
||||
cards
|
||||
[watch-graph:ha cards]
|
||||
%+ turn
|
||||
^- (list mark)
|
||||
:~ %graph-validator-chat
|
||||
@ -103,9 +121,23 @@
|
||||
?+ mark (on-poke:def mark vase)
|
||||
%hark-graph-hook-action
|
||||
(hark-graph-hook-action !<(action:hook vase))
|
||||
%noun
|
||||
(poke-noun !<(* vase))
|
||||
==
|
||||
[cards this]
|
||||
::
|
||||
++ poke-noun
|
||||
|= non=*
|
||||
?> ?=(%rewatch-dms non)
|
||||
=/ graphs=(list resource)
|
||||
~(tap in get-keys:gra)
|
||||
:- ~
|
||||
%_ state
|
||||
watching
|
||||
%- ~(gas in watching)
|
||||
(murn graphs |=(rid=resource ?:((should-watch:ha rid) `[rid ~] ~)))
|
||||
==
|
||||
::
|
||||
++ hark-graph-hook-action
|
||||
|= =action:hook
|
||||
^- (quip card _state)
|
||||
@ -223,23 +255,14 @@
|
||||
++ add-graph
|
||||
|= rid=resource
|
||||
^- (quip card _state)
|
||||
=/ group-rid=(unit resource)
|
||||
(group-from-app-resource:met %graph rid)
|
||||
?~ group-rid
|
||||
~& no-group+rid
|
||||
`state
|
||||
=/ is-hidden=?
|
||||
!(is-managed:grp u.group-rid)
|
||||
=/ should-watch
|
||||
|(is-hidden &(watch-on-self =(our.bowl entity.rid)))
|
||||
?. should-watch
|
||||
`state
|
||||
=/ graph=graph:graph-store :: graph in subscription is bunted
|
||||
(get-graph-mop:gra rid)
|
||||
=/ node=(unit node:graph-store)
|
||||
(bind (peek:orm:graph-store graph) |=([@ =node:graph-store] node))
|
||||
=^ cards state
|
||||
(check-nodes (drop node) rid)
|
||||
?. (should-watch:ha rid)
|
||||
[cards state]
|
||||
:_ state(watching (~(put in watching) [rid ~]))
|
||||
(weld cards (give:ha ~[/updates] %listen [rid ~]))
|
||||
::
|
||||
@ -277,6 +300,9 @@
|
||||
--
|
||||
::
|
||||
|_ =bowl:gall
|
||||
+* met ~(. metadata bowl)
|
||||
grp ~(. grouplib bowl)
|
||||
gra ~(. graph bowl)
|
||||
::
|
||||
++ get-conversion
|
||||
|= rid=resource
|
||||
@ -314,6 +340,16 @@
|
||||
%.y
|
||||
$(contents t.contents)
|
||||
::
|
||||
++ should-watch
|
||||
|= rid=resource
|
||||
^- ?
|
||||
=/ group-rid=(unit resource)
|
||||
(group-from-app-resource:met %graph rid)
|
||||
?~ group-rid %.n
|
||||
?| !(is-managed:grp u.group-rid)
|
||||
&(watch-on-self =(our.bowl entity.rid))
|
||||
==
|
||||
::
|
||||
++ handle-update
|
||||
|_ $: rid=resource :: input
|
||||
updates=(list node:graph-store)
|
||||
|
@ -49,7 +49,7 @@
|
||||
:: $cache: useful to have precalculated, but can be derived from state
|
||||
:: albeit expensively
|
||||
+$ cache
|
||||
$: by-index=(jug stats-index:store @da)
|
||||
$: by-index=(jug stats-index:store [time=@da =index:store])
|
||||
~
|
||||
==
|
||||
::
|
||||
@ -228,6 +228,7 @@
|
||||
[%count count]
|
||||
(~(gut by last-seen) stats-index *time)
|
||||
==
|
||||
::
|
||||
++ give-each-unreads
|
||||
^- (list [stats-index:store stats:store])
|
||||
%+ turn
|
||||
@ -477,19 +478,16 @@
|
||||
::
|
||||
++ read-index-each
|
||||
|= [=stats-index:store ref=index:graph-store]
|
||||
%+ read-index stats-index
|
||||
%- read-indices
|
||||
%+ skim
|
||||
~(tap ^in (~(get ju by-index) stats-index))
|
||||
|= time=@da
|
||||
|= [time=@da =index:store]
|
||||
=/ =timebox:store
|
||||
(gut-orm notifications time)
|
||||
%+ roll
|
||||
~(tap ^in timebox)
|
||||
|= [[=index:store not=notification:store] out=?]
|
||||
?: out out
|
||||
?. (stats-index-is-index:store stats-index index) out
|
||||
?. ?=(%graph -.index) out
|
||||
?. ?=(%graph -.contents.not) out
|
||||
=/ not=notification:store
|
||||
(~(got by timebox) index)
|
||||
?. ?=(%graph -.index) %.n
|
||||
?. ?=(%graph -.contents.not) %.n
|
||||
(lien list.contents.not |=(p=post:post =(index.p ref)))
|
||||
::
|
||||
++ read-each
|
||||
@ -517,35 +515,18 @@
|
||||
++ read-count
|
||||
|= =stats-index:store
|
||||
=. unreads-count (~(put by unreads-count) stats-index 0)
|
||||
=/ times=(list @da)
|
||||
=/ times=(list [@da index:store])
|
||||
~(tap ^in (~(get ju by-index) stats-index))
|
||||
(give:(read-index stats-index times) %read-count stats-index)
|
||||
(give:(read-indices times) %read-count stats-index)
|
||||
::
|
||||
++ read-index
|
||||
|= [=stats-index:store times=(list @da)]
|
||||
++ read-indices
|
||||
|= times=(list [time=@da =index:store])
|
||||
|-
|
||||
?~ times poke-core
|
||||
=/ core
|
||||
(read-stats-index i.times stats-index)
|
||||
(read-note i.times)
|
||||
$(poke-core core, times t.times)
|
||||
::
|
||||
++ read-stats-index
|
||||
|= [time=@da =stats-index:store]
|
||||
=/ keys
|
||||
~(tap ^in ~(key by (gut-orm notifications time)))
|
||||
|- ^+ poke-core
|
||||
?~ keys
|
||||
poke-core
|
||||
?. (stats-index-is-index:store stats-index i.keys)
|
||||
$(keys t.keys)
|
||||
=/ =notification:store
|
||||
(~(got by (gut-orm notifications time)) i.keys)
|
||||
?: read.notification
|
||||
$(keys t.keys)
|
||||
=/ core
|
||||
(read-note time i.keys)
|
||||
$(poke-core core, keys t.keys)
|
||||
::
|
||||
++ seen-index
|
||||
|= [time=@da =stats-index:store]
|
||||
=/ new-time=@da
|
||||
@ -570,7 +551,7 @@
|
||||
=. last-seen
|
||||
((dif-map-by-key ,@da) last-seen indices)
|
||||
=. by-index
|
||||
((dif-map-by-key ,(set @da)) by-index indices)
|
||||
((dif-map-by-key ,(set [@da =index:store])) by-index indices)
|
||||
poke-core
|
||||
::
|
||||
++ get-stats-indices
|
||||
@ -603,10 +584,10 @@
|
||||
~(tap ^in set)
|
||||
|-
|
||||
?~ indices poke-core
|
||||
=/ times=(list @da)
|
||||
=/ times=(list [time=@da =index:store])
|
||||
~(tap ^in (~(get ju by-index) i.indices))
|
||||
=. poke-core
|
||||
(read-index i.indices times)
|
||||
(read-indices times)
|
||||
$(indices t.indices)
|
||||
--
|
||||
::
|
||||
@ -694,7 +675,7 @@
|
||||
%_ +.state
|
||||
::
|
||||
by-index
|
||||
%. [(to-stats-index:store index) time]
|
||||
%. [(to-stats-index:store index) time index]
|
||||
?: read
|
||||
~(del ju by-index)
|
||||
~(put ju by-index)
|
||||
|
@ -24,6 +24,6 @@
|
||||
<div id="portal-root"></div>
|
||||
<script src="/~landscape/js/channel.js"></script>
|
||||
<script src="/~landscape/js/session.js"></script>
|
||||
<script src="/~landscape/js/bundle/index.6056641fbc32a19ee0fd.js"></script>
|
||||
<script src="/~landscape/js/bundle/index.86c6e416c338a305e1e9.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -63,7 +63,7 @@ class Channel {
|
||||
}
|
||||
|
||||
resetDebounceTimer() {
|
||||
if(this.debounceTimer) {
|
||||
if (this.debounceTimer) {
|
||||
clearTimeout(this.debounceTimer);
|
||||
this.debounceTimer = null;
|
||||
}
|
||||
@ -182,19 +182,19 @@ class Channel {
|
||||
// sends a JSON command command to the server.
|
||||
//
|
||||
sendJSONToChannel(j) {
|
||||
if(!j && this.outstandingJSON.length === 0) {
|
||||
return;
|
||||
}
|
||||
let req = new XMLHttpRequest();
|
||||
req.open("PUT", this.channelURL());
|
||||
req.setRequestHeader("Content-Type", "application/json");
|
||||
|
||||
if (this.lastEventId == this.lastAcknowledgedEventId) {
|
||||
if(j) {
|
||||
if (j) {
|
||||
this.outstandingJSON.push(j);
|
||||
}
|
||||
let x = JSON.stringify(this.outstandingJSON);
|
||||
req.send(x);
|
||||
|
||||
if (this.outstandingJSON.length > 0) {
|
||||
let x = JSON.stringify(this.outstandingJSON);
|
||||
req.send(x);
|
||||
}
|
||||
} else {
|
||||
// we add an acknowledgment to clear the server side queue
|
||||
//
|
||||
@ -203,15 +203,15 @@ class Channel {
|
||||
//
|
||||
let payload = [
|
||||
...this.outstandingJSON,
|
||||
{action: "ack", "event-id": parseInt(this.lastEventId)}
|
||||
{action: "ack", "event-id": this.lastEventId}
|
||||
];
|
||||
if(j) {
|
||||
if (j) {
|
||||
payload.push(j)
|
||||
}
|
||||
let x = JSON.stringify(payload);
|
||||
req.send(x);
|
||||
|
||||
this.lastEventId = this.lastAcknowledgedEventId;
|
||||
this.lastAcknowledgedEventId = this.lastEventId;
|
||||
}
|
||||
this.outstandingJSON = [];
|
||||
|
||||
@ -227,7 +227,7 @@ class Channel {
|
||||
|
||||
this.eventSource = new EventSource(this.channelURL(), {withCredentials:true});
|
||||
this.eventSource.onmessage = e => {
|
||||
this.lastEventId = e.lastEventId;
|
||||
this.lastEventId = parseInt(e.lastEventId, 10);
|
||||
|
||||
let obj = JSON.parse(e.data);
|
||||
let pokeFuncs = this.outstandingPokes.get(obj.id);
|
||||
|
@ -285,7 +285,7 @@
|
||||
(on-agent:og wire sign)
|
||||
[cards this]
|
||||
:_ this
|
||||
~[(update-store:hc q.cage.sign)]
|
||||
~[(update-store:hc rid q.cage.sign)]
|
||||
==
|
||||
++ on-leave
|
||||
|= =path
|
||||
@ -424,15 +424,24 @@
|
||||
/helper/pull-hook
|
||||
wire
|
||||
::
|
||||
++ get-conversion
|
||||
.^ tube:clay
|
||||
%cc (scot %p our.bowl) %home (scot %da now.bowl)
|
||||
/[update-mark.config]/resource
|
||||
==
|
||||
::
|
||||
++ give-update
|
||||
^- card
|
||||
[%give %fact ~[/tracking] %pull-hook-update !>(tracking)]
|
||||
::
|
||||
++ update-store
|
||||
|= =vase
|
||||
|= [wire-rid=resource =vase]
|
||||
^- card
|
||||
=/ =wire
|
||||
(make-wire /store)
|
||||
=+ !<(rid=resource (get-conversion vase))
|
||||
?> =(src.bowl (~(got by tracking) rid))
|
||||
?> =(wire-rid rid)
|
||||
[%pass wire %agent [our.bowl store-name.config] %poke update-mark.config vase]
|
||||
--
|
||||
--
|
||||
|
@ -67,16 +67,6 @@
|
||||
|* =config
|
||||
$_ ^|
|
||||
|_ bowl:gall
|
||||
::
|
||||
:: +resource-for-update: get affected resource from an update
|
||||
::
|
||||
:: Given a vase of the update, the mark of which is
|
||||
:: update-mark.config, produce the affected resource, if any.
|
||||
::
|
||||
++ resource-for-update
|
||||
|~ vase
|
||||
*(unit resource)
|
||||
::
|
||||
:: +take-update: handle update from store
|
||||
::
|
||||
:: Given an update from the store, do other things after proxying
|
||||
@ -175,9 +165,11 @@
|
||||
|^
|
||||
?- -.old
|
||||
%1
|
||||
=. cards
|
||||
:_(cards (build-mark:hc %sing))
|
||||
=^ og-cards push-hook
|
||||
(on-load:og inner-state.old)
|
||||
[(weld cards og-cards) this(state old)]
|
||||
[(weld (flop cards) og-cards) this(state old)]
|
||||
::
|
||||
%0
|
||||
%_ $
|
||||
@ -274,11 +266,18 @@
|
||||
=^ cards push-hook
|
||||
(on-leave:og path)
|
||||
[cards this]
|
||||
::
|
||||
++ on-arvo
|
||||
|= [=wire =sign-arvo]
|
||||
=^ cards push-hook
|
||||
(on-arvo:og wire sign-arvo)
|
||||
[cards this]
|
||||
?. ?=([%helper %push-hook @ *] wire)
|
||||
=^ cards push-hook
|
||||
(on-arvo:og wire sign-arvo)
|
||||
[cards this]
|
||||
?. ?=(%resource-conversion i.t.t.wire)
|
||||
(on-arvo:def wire sign-arvo)
|
||||
:_ this
|
||||
~[(build-mark:hc %next)]
|
||||
::
|
||||
++ on-fail
|
||||
|= [=term =tang]
|
||||
=^ cards push-hook
|
||||
@ -368,7 +367,7 @@
|
||||
|= =vase
|
||||
^- (list card:agent:gall)
|
||||
=/ rid=(unit resource)
|
||||
(resource-for-update:og vase)
|
||||
(resource-for-update vase)
|
||||
?~ rid ~
|
||||
=/ prefix=path
|
||||
resource+(en-path:resource u.rid)
|
||||
@ -385,7 +384,7 @@
|
||||
|= =vase
|
||||
^- (list card:agent:gall)
|
||||
=/ rid=(unit resource)
|
||||
(resource-for-update:og vase)
|
||||
(resource-for-update vase)
|
||||
?~ rid ~
|
||||
=/ =path
|
||||
resource+(en-path:resource u.rid)
|
||||
@ -394,5 +393,30 @@
|
||||
=/ dap=term
|
||||
?:(=(our.bowl entity.u.rid) store-name.config dap.bowl)
|
||||
[%pass wire %agent [entity.u.rid dap] %poke update-mark.config vase]~
|
||||
::
|
||||
++ get-conversion
|
||||
.^ tube:clay
|
||||
%cc (scot %p our.bowl) %home (scot %da now.bowl)
|
||||
/[update-mark.config]/resource
|
||||
==
|
||||
::
|
||||
++ resource-for-update
|
||||
|= update=vase
|
||||
=/ =tube:clay
|
||||
get-conversion
|
||||
%+ bind
|
||||
(mole |.((tube update)))
|
||||
|=(=vase !<(resource vase))
|
||||
::
|
||||
++ build-mark
|
||||
|= rav=?(%sing %next)
|
||||
^- card
|
||||
=/ =wire
|
||||
(make-wire /resource-conversion)
|
||||
=/ =mood:clay
|
||||
[%c da+now.bowl /[update-mark.config]/resource]
|
||||
=/ =rave:clay
|
||||
?:(?=(%next rav) [rav mood] [rav mood])
|
||||
[%pass wire %arvo %c %warp our.bowl [%home `rave]]
|
||||
--
|
||||
--
|
||||
|
@ -7,6 +7,13 @@
|
||||
|%
|
||||
++ noun upd
|
||||
++ json (update:enjs upd)
|
||||
++ resource
|
||||
?+ -.q.upd !!
|
||||
?(%run-updates %add-nodes %remove-nodes %add-graph) resource.q.upd
|
||||
?(%remove-graph %archive-graph %unarchive-graph) resource.q.upd
|
||||
?(%add-tag %remove-tag) resource.q.upd
|
||||
?(%add-signatures %remove-signatures) resource.uid.q.upd
|
||||
==
|
||||
++ mime [/application/x-urb-graph-update (as-octs (jam upd))]
|
||||
--
|
||||
::
|
||||
|
@ -4,9 +4,13 @@
|
||||
++ grow
|
||||
|%
|
||||
++ noun upd
|
||||
++ resource
|
||||
?< ?=(%initial -.upd)
|
||||
resource.upd
|
||||
::
|
||||
++ json
|
||||
%+ frond:enjs:format 'groupUpdate'
|
||||
(update:enjs upd)
|
||||
%+ frond:enjs:format 'groupUpdate'
|
||||
(update:enjs upd)
|
||||
--
|
||||
++ grab
|
||||
|%
|
||||
|
14
pkg/arvo/mar/resource.hoon
Normal file
14
pkg/arvo/mar/resource.hoon
Normal file
@ -0,0 +1,14 @@
|
||||
/+ resource
|
||||
|_ rid=resource
|
||||
++ grad %noun
|
||||
++ grow
|
||||
|%
|
||||
++ noun rid
|
||||
++ json (enjs:resource rid)
|
||||
--
|
||||
++ grab
|
||||
|%
|
||||
++ noun resource
|
||||
++ json dejs:resource
|
||||
--
|
||||
--
|
@ -34,7 +34,8 @@
|
||||
==
|
||||
::
|
||||
+$ logged-update-0
|
||||
$% [%add-nodes =resource nodes=(map index node)]
|
||||
$% [%add-graph =resource =graph mark=(unit mark) overwrite=?]
|
||||
[%add-nodes =resource nodes=(map index node)]
|
||||
[%remove-nodes =resource indices=(set index)]
|
||||
[%add-signatures =uid =signatures]
|
||||
[%remove-signatures =uid =signatures]
|
||||
@ -42,7 +43,6 @@
|
||||
::
|
||||
+$ update-0
|
||||
$% logged-update-0
|
||||
[%add-graph =resource =graph mark=(unit mark) overwrite=?]
|
||||
[%remove-graph =resource]
|
||||
::
|
||||
[%add-tag =term =resource]
|
||||
|
37
pkg/docker-image/README.md
Normal file
37
pkg/docker-image/README.md
Normal file
@ -0,0 +1,37 @@
|
||||
# Official Urbit Docker Image
|
||||
|
||||
This is the official Docker image for [Urbit](https://urbit.org).
|
||||
|
||||
Urbit is a clean-slate OS and network for the 21st century.
|
||||
|
||||
## Using
|
||||
|
||||
To use this image, you should mount a volume with a keyfile, comet file, or existing pier at `/urbit`, and map ports
|
||||
as described below.
|
||||
|
||||
### Volume Mount
|
||||
This image expects a volume mounted at `/urbit`. This volume should initially contain one of
|
||||
|
||||
- A keyfile `<shipname>.key` for a galaxy, star, planet, or moon. See the setup instructions for Urbit for information on [obtaining a keyfile](https://urbit.org/using/install/).
|
||||
* e.g. `sampel-palnet.key` for the planet `sampel-palnet`.
|
||||
- An empty file with the extension `.comet`. This will cause Urbit to boot a [comet](https://urbit.org/docs/glossary/comet/) in a pier named for the `.comet` file (less the extension).
|
||||
* e.g. starting with an empty file `my-urbit-bot.comet` will result in Urbit booting a comet into the pier
|
||||
`my-urbit-bot` under your volume.
|
||||
- An existing pier as a directory `<shipname>`. You can migrate an existing ship to a new docker container in this way by placing its pier under the volume.
|
||||
* e.g. if your ship is `sampel-palnet` then you likely have a directory `sampel-palnet` whose path you pass to `./urbit` when starting. [Move your pier](https://urbit.org/using/operations/using-your-ship/#moving-your-pier) directory to the volume and then start the container.
|
||||
|
||||
The first two options result in Urbit attempting to boot either the ship named by the name of the keyfile, or a comet. In both cases, after that boot is successful, the `.key` or `.comet` file will be removed from the volume and the pier will take its place.
|
||||
|
||||
In consequence, it is safe to remove the container and start a new container which mounts the same volume, e.g. to upgrade the version of the urbit binary by running a later container version. It is also possible to stop the container and then move the pier away e.g. to a location where you will run it directly with the Urbit binary.
|
||||
|
||||
### Ports
|
||||
The image includes `EXPOSE` directives for TCP port 80 and UDP port 34343. Port `80` is used for Urbit's HTTP interface for both [Landscape](https://urbit.org/docs/glossary/landscape/) and for [API calls](https://urbit.org/using/integrating-api/) to the ship. Port `34343` is used by [Ames](https://urbit.org/docs/glossary/ames/) for ship-to-ship communication.
|
||||
|
||||
You can either pass the `-P` flag to docker to map ports directly to the corresponding ports on the host, or map them individually with `-p` flags. For local testing the latter is often convenient, for instance to remap port 80 to an unprivileged port.
|
||||
|
||||
## Extending
|
||||
|
||||
You likely do not want to extend this image. External applications which interact with Urbit do so primarily via an HTTP API, which should be exposed as described above. For containerized applications using Urbit, it is more appropriate to use a container orchestration service such as Docker Compose or Kubernetes to run Urbit alongside other containers which will interface with its API.
|
||||
|
||||
## Development
|
||||
The docker image is built by a Nix derivation in the [`nix/pkgs/docker-image/default.nix`](https://github.com/urbit/urbit/tree/master/nix/pkgs/docker-image/default.nix) file under the Urbit git repository.
|
@ -96,7 +96,7 @@ module.exports = {
|
||||
]
|
||||
}
|
||||
},
|
||||
exclude: /node_modules/
|
||||
exclude: /node_modules\/(?!(@tlon\/indigo-dark|@tlon\/indigo-light)\/).*/
|
||||
},
|
||||
{
|
||||
test: /\.css$/i,
|
||||
|
@ -26,7 +26,7 @@ module.exports = {
|
||||
]
|
||||
}
|
||||
},
|
||||
exclude: /node_modules/
|
||||
exclude: /node_modules\/(?!(@tlon\/indigo-dark|@tlon\/indigo-light)\/).*/
|
||||
},
|
||||
{
|
||||
test: /\.css$/i,
|
||||
|
122
pkg/interface/package-lock.json
generated
122
pkg/interface/package-lock.json
generated
@ -1687,15 +1687,20 @@
|
||||
"@styled-system/css": "^5.1.5"
|
||||
}
|
||||
},
|
||||
"@tlon/indigo-dark": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@tlon/indigo-dark/-/indigo-dark-1.0.6.tgz",
|
||||
"integrity": "sha512-/c+3/aC+gSnLHiLwTdje7pYS84ZAR3zyMJhp2mT9BIPtk7ek/EGsrrugZjVJxeKXqy+mQpFD5TXktgAEh0Ko1A=="
|
||||
},
|
||||
"@tlon/indigo-light": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@tlon/indigo-light/-/indigo-light-1.0.3.tgz",
|
||||
"integrity": "sha512-3OPSdf9cejP/TSzWXuBaYbzLtAfBzQnc75SlPLkoPfwpxnv1Bvy9hiWngLY0WnKRR6lMOldnkYQCCuNWeDibYQ=="
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@tlon/indigo-light/-/indigo-light-1.0.6.tgz",
|
||||
"integrity": "sha512-kBzJueOoGDVF2knGt+Kf5ylvil6+V1qn8/RqAj1S6wUTnfUfAMRzDp4LQI2MxLI8Is0OG3XCErVSOUImU6R3lg=="
|
||||
},
|
||||
"@tlon/indigo-react": {
|
||||
"version": "1.2.15",
|
||||
"resolved": "https://registry.npmjs.org/@tlon/indigo-react/-/indigo-react-1.2.15.tgz",
|
||||
"integrity": "sha512-h9umWEzYZwyb53ujWoCQCJQwY9RUuoDaf6189+0LH3C7y9fybJe6vzbW6g2cUVH8dXA2EZkedS5nriYR0IpQbw==",
|
||||
"version": "1.2.16",
|
||||
"resolved": "https://registry.npmjs.org/@tlon/indigo-react/-/indigo-react-1.2.16.tgz",
|
||||
"integrity": "sha512-9bQ43cXiJGOsrihwy8+MBfG4WroKucZJOm4whfSjsNFCHorjS+5Y/6nWl2hEwHo068XONFmD7xlDE1QBMTk+pA==",
|
||||
"requires": {
|
||||
"@reach/menu-button": "^0.10.5",
|
||||
"react": "^16.13.1",
|
||||
@ -1703,16 +1708,16 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
|
||||
"integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz",
|
||||
"integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@tlon/sigil-js": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@tlon/sigil-js/-/sigil-js-1.4.2.tgz",
|
||||
"integrity": "sha512-meb0q0kf4S34oTKDulRMfVU6Wq/9lSOALeQil4EWttL72Lae9Fznsm+ix3tgT69g1xUpjeZIB+vqGOtAFhZX3g==",
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@tlon/sigil-js/-/sigil-js-1.4.3.tgz",
|
||||
"integrity": "sha512-IaJUvAgXRmPFj5JA/MDfd+b+RFDhGdiMLfzJZKuFIQyl3Dl/3cC9HdDLCYSoK4GBTu3gZqoqi6wxZl5Xia/cSw==",
|
||||
"requires": {
|
||||
"invariant": "^2.2.4",
|
||||
"svgson": "^4.0.0",
|
||||
@ -8132,6 +8137,11 @@
|
||||
"integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=",
|
||||
"dev": true
|
||||
},
|
||||
"remark-breaks": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/remark-breaks/-/remark-breaks-2.0.1.tgz",
|
||||
"integrity": "sha512-CZKI8xdPUnvMqPxYEIBBUg8C0B0kyn14lkW0abzhfh/P71YRIxCC3wvBh6AejQL602OxF6kNRl1x4HAZA07JyQ=="
|
||||
},
|
||||
"remark-disable-tokenizers": {
|
||||
"version": "1.0.24",
|
||||
"resolved": "https://registry.npmjs.org/remark-disable-tokenizers/-/remark-disable-tokenizers-1.0.24.tgz",
|
||||
@ -10128,8 +10138,7 @@
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
@ -10150,14 +10159,12 @@
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@ -10172,20 +10179,17 @@
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
@ -10302,8 +10306,7 @@
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
@ -10315,7 +10318,6 @@
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
@ -10330,7 +10332,6 @@
|
||||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
@ -10338,14 +10339,12 @@
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.9.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
@ -10364,7 +10363,6 @@
|
||||
"version": "0.5.3",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.5"
|
||||
}
|
||||
@ -10426,8 +10424,7 @@
|
||||
"npm-normalize-package-bin": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"npm-packlist": {
|
||||
"version": "1.4.8",
|
||||
@ -10455,8 +10452,7 @@
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
@ -10468,7 +10464,6 @@
|
||||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
@ -10546,8 +10541,7 @@
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
@ -10583,7 +10577,6 @@
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
@ -10603,7 +10596,6 @@
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
@ -10647,14 +10639,12 @@
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -11135,8 +11125,7 @@
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
@ -11157,14 +11146,12 @@
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@ -11179,20 +11166,17 @@
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
@ -11309,8 +11293,7 @@
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
@ -11322,7 +11305,6 @@
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
@ -11337,7 +11319,6 @@
|
||||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
@ -11345,14 +11326,12 @@
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.9.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
@ -11371,7 +11350,6 @@
|
||||
"version": "0.5.3",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.5"
|
||||
}
|
||||
@ -11433,8 +11411,7 @@
|
||||
"npm-normalize-package-bin": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"npm-packlist": {
|
||||
"version": "1.4.8",
|
||||
@ -11462,8 +11439,7 @@
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
@ -11475,7 +11451,6 @@
|
||||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
@ -11553,8 +11528,7 @@
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
@ -11590,7 +11564,6 @@
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
@ -11610,7 +11583,6 @@
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
@ -11654,14 +11626,12 @@
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -8,9 +8,10 @@
|
||||
"@reach/disclosure": "^0.10.5",
|
||||
"@reach/menu-button": "^0.10.5",
|
||||
"@reach/tabs": "^0.10.5",
|
||||
"@tlon/indigo-light": "^1.0.3",
|
||||
"@tlon/indigo-react": "1.2.15",
|
||||
"@tlon/sigil-js": "^1.4.2",
|
||||
"@tlon/indigo-dark": "^1.0.6",
|
||||
"@tlon/indigo-light": "^1.0.6",
|
||||
"@tlon/indigo-react": "1.2.16",
|
||||
"@tlon/sigil-js": "^1.4.3",
|
||||
"aws-sdk": "^2.726.0",
|
||||
"big-integer": "^1.6.48",
|
||||
"classnames": "^2.2.6",
|
||||
@ -36,6 +37,7 @@
|
||||
"react-router-dom": "^5.0.0",
|
||||
"react-virtuoso": "^0.20.0",
|
||||
"react-visibility-sensor": "^5.1.1",
|
||||
"remark-breaks": "^2.0.1",
|
||||
"remark-disable-tokenizers": "^1.0.24",
|
||||
"style-loader": "^1.2.1",
|
||||
"styled-components": "^5.1.0",
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import _ from "lodash";
|
||||
import f, { memoize } from "lodash/fp";
|
||||
import bigInt, { BigInteger } from "big-integer";
|
||||
@ -13,7 +13,7 @@ export const MOMENT_CALENDAR_DATE = {
|
||||
nextWeek: "dddd",
|
||||
lastDay: "[Yesterday]",
|
||||
lastWeek: "[Last] dddd",
|
||||
sameElse: "DD/MM/YYYY",
|
||||
sameElse: "~YYYY.M.D",
|
||||
};
|
||||
|
||||
export function appIsGraph(app: string) {
|
||||
@ -361,4 +361,13 @@ export function pluralize(text: string, isPlural = false, vowel = false) {
|
||||
export function useShowNickname(contact: Contact | null, hide?: boolean): boolean {
|
||||
const hideNicknames = typeof hide !== 'undefined' ? hide : useLocalState(state => state.hideNicknames);
|
||||
return !!(contact && contact.nickname && !hideNicknames);
|
||||
}
|
||||
|
||||
export function useHovering() {
|
||||
const [hovering, setHovering] = useState(false);
|
||||
const bind = {
|
||||
onMouseEnter: () => setHovering(true),
|
||||
onMouseLeave: () => setHovering(false)
|
||||
};
|
||||
return { hovering, bind };
|
||||
}
|
@ -386,5 +386,7 @@ function archive(json: any, state: HarkState) {
|
||||
notifIdxEqual(index, idxNotif.index)
|
||||
);
|
||||
state.notifications.set(time, unarchived);
|
||||
const newlyRead = archived.filter(x => !x.notification.read).length;
|
||||
updateNotificationStats(state, index, 'notifications', (x) => x - newlyRead);
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ const useLocalState = create<LocalState>(persist((set, get) => ({
|
||||
suspendedFocus: undefined,
|
||||
toggleOmnibox: () => set(produce(state => {
|
||||
state.omniboxShown = !state.omniboxShown;
|
||||
if (state.suspendedFocus) {
|
||||
if (typeof state.suspendedFocus?.focus === 'function') {
|
||||
state.suspendedFocus.focus();
|
||||
state.suspendedFocus = undefined;
|
||||
} else {
|
||||
|
@ -11,8 +11,8 @@ import 'mousetrap-global-bind';
|
||||
|
||||
import './css/indigo-static.css';
|
||||
import './css/fonts.css';
|
||||
import light from './themes/light';
|
||||
import dark from './themes/old-dark';
|
||||
import light from '@tlon/indigo-light';
|
||||
import dark from '@tlon/indigo-dark';
|
||||
|
||||
import { Text, Anchor, Row } from '@tlon/indigo-react';
|
||||
|
||||
@ -40,7 +40,7 @@ const Root = styled.div`
|
||||
background-size: cover;
|
||||
` : p.background?.type === 'color' ? `
|
||||
background-color: ${p.background.color};
|
||||
` : ''
|
||||
` : `background-color: ${p.theme.colors.white};`
|
||||
}
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
|
@ -231,8 +231,8 @@ export const MessageWithSigil = (props) => {
|
||||
}}
|
||||
title={`~${msg.author}`}
|
||||
>{name}</Text>
|
||||
<Text flexShrink='0' gray mono className="v-mid">{timestamp}</Text>
|
||||
<Text flexShrink={0} gray mono ml={2} className="v-mid child dn-s">{datestamp}</Text>
|
||||
<Text flexShrink='0' fontSize='0' gray mono className="v-mid">{timestamp}</Text>
|
||||
<Text flexShrink={0} gray mono ml={2} className="v-mid child dn-s">{datestamp}</Text>
|
||||
</Box>
|
||||
<ContentBox flexShrink={0} fontSize={fontSize ? fontSize : '14px'}>
|
||||
{msg.contents.map(c =>
|
||||
@ -259,7 +259,7 @@ const ContentBox = styled(Box)`
|
||||
|
||||
export const MessageWithoutSigil = ({ timestamp, contacts, msg, measure, group }) => (
|
||||
<>
|
||||
<Text flexShrink={0} mono gray display='inline-block' pt='2px' lineHeight='tall' className="child">{timestamp}</Text>
|
||||
<Text flexShrink={0} mono gray display='inline-block' pt='2px' lineHeight='tall' className="child" fontSize='0'>{timestamp}</Text>
|
||||
<ContentBox flexShrink={0} fontSize='14px' className="clamp-message" style={{ flexGrow: 1 }}>
|
||||
{msg.contents.map((c, i) => (
|
||||
<MessageContent
|
||||
|
@ -193,6 +193,8 @@ export default class ChatEditor extends Component {
|
||||
alignItems='center'
|
||||
flexGrow='1'
|
||||
height='100%'
|
||||
paddingTop={MOBILE_BROWSER_REGEX.test(navigator.userAgent) ? '16px' : '0'}
|
||||
paddingBottom={MOBILE_BROWSER_REGEX.test(navigator.userAgent) ? '16px' : '0'}
|
||||
maxHeight='224px'
|
||||
width='calc(100% - 88px)'
|
||||
className={inCodeMode ? 'chat code' : 'chat'}
|
||||
@ -201,7 +203,7 @@ export default class ChatEditor extends Component {
|
||||
{MOBILE_BROWSER_REGEX.test(navigator.userAgent)
|
||||
? <MobileBox
|
||||
data-value={this.state.message}
|
||||
fontSize="14px"
|
||||
fontSize="1"
|
||||
lineHeight="tall"
|
||||
onClick={event => {
|
||||
if (this.editor) {
|
||||
@ -211,7 +213,7 @@ export default class ChatEditor extends Component {
|
||||
>
|
||||
<BaseTextArea
|
||||
fontFamily={inCodeMode ? 'Source Code Pro' : 'Inter'}
|
||||
fontSize="14px"
|
||||
fontSize="1"
|
||||
lineHeight="tall"
|
||||
rows="1"
|
||||
style={{ width: '100%', background: 'transparent', color: 'currentColor' }}
|
||||
|
@ -12,6 +12,7 @@ export default class CodeContent extends Component {
|
||||
(
|
||||
<Text
|
||||
display='block'
|
||||
fontSize='0'
|
||||
mono
|
||||
p='1'
|
||||
my='0'
|
||||
@ -37,6 +38,7 @@ export default class CodeContent extends Component {
|
||||
overflow='auto'
|
||||
maxHeight='10em'
|
||||
maxWidth='100%'
|
||||
fontSize='0'
|
||||
style={{ whiteSpace: 'pre' }}
|
||||
>
|
||||
{content.code.expression}
|
||||
|
@ -2,6 +2,7 @@ import React, { Component } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import RemarkDisableTokenizers from 'remark-disable-tokenizers';
|
||||
import RemarkBreaks from 'remark-breaks';
|
||||
import urbitOb from 'urbit-ob';
|
||||
import { Text } from '@tlon/indigo-react';
|
||||
|
||||
@ -26,10 +27,10 @@ const DISABLED_INLINE_TOKENS = [
|
||||
|
||||
const renderers = {
|
||||
inlineCode: ({language, value}) => {
|
||||
return <Text mono p='1' backgroundColor='washedGray' style={{ whiteSpace: 'preWrap'}}>{value}</Text>
|
||||
return <Text mono p='1' backgroundColor='washedGray' fontSize='0' style={{ whiteSpace: 'preWrap'}}>{value}</Text>
|
||||
},
|
||||
paragraph: ({ children }) => {
|
||||
return (<Text fontSize="14px">{children}</Text>);
|
||||
return (<Text fontSize="1">{children}</Text>);
|
||||
},
|
||||
code: ({language, value}) => {
|
||||
return <Text
|
||||
@ -38,6 +39,7 @@ const renderers = {
|
||||
display='block'
|
||||
borderRadius='1'
|
||||
mono
|
||||
fontSize='0'
|
||||
backgroundColor='washedGray'
|
||||
overflowX='auto'
|
||||
style={{ whiteSpace: 'pre'}}>
|
||||
@ -66,6 +68,7 @@ const MessageMarkdown = React.memo(props => (
|
||||
return true;
|
||||
}}
|
||||
plugins={[[
|
||||
RemarkBreaks,
|
||||
RemarkDisableTokenizers,
|
||||
{ block: DISABLED_BLOCK_TOKENS, inline: DISABLED_INLINE_TOKENS }
|
||||
]]} />
|
||||
|
@ -277,9 +277,6 @@ pre.CodeMirror-placeholder.CodeMirror-line-like { color: var(--gray); }
|
||||
/* dark */
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: #333;
|
||||
}
|
||||
.bg-black-d {
|
||||
background-color: black;
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ export default function LaunchApp(props) {
|
||||
color="black"
|
||||
icon="Mail"
|
||||
/>
|
||||
<Text ml="1" mt='1px' color="black">DMs + Drafts</Text>
|
||||
<Text ml="2" mt='1px' color="black">DMs + Drafts</Text>
|
||||
</Row>
|
||||
</Box>
|
||||
</Tile>
|
||||
@ -102,7 +102,7 @@ export default function LaunchApp(props) {
|
||||
icon="CreateGroup"
|
||||
bg="green"
|
||||
color="#fff"
|
||||
text="Create a Group"
|
||||
text="Create Group"
|
||||
>
|
||||
<NewGroup {...props} />
|
||||
</ModalButton>
|
||||
|
@ -20,8 +20,8 @@ export default class BasicTile extends React.PureComponent {
|
||||
size='12px'
|
||||
display='inline-block'
|
||||
verticalAlign='top'
|
||||
pt='5px'
|
||||
pr='2px'
|
||||
mt='5px'
|
||||
mr='2'
|
||||
/>
|
||||
: null
|
||||
}{props.title}
|
||||
|
@ -171,7 +171,7 @@ export default class WeatherTile extends React.Component {
|
||||
onClick={() => this.setState({ manualEntry: !this.state.manualEntry })}
|
||||
>
|
||||
<Box>
|
||||
<Icon icon='Weather' display='inline-block' verticalAlign='top' pt='3px' pr='2px' />
|
||||
<Icon icon='Weather' display='inline-block' verticalAlign='top' mt='3px' mr='2' />
|
||||
<Text>Weather</Text>
|
||||
</Box>
|
||||
<Text style={{ cursor: 'pointer' }}>
|
||||
@ -217,15 +217,14 @@ export default class WeatherTile extends React.Component {
|
||||
title={`${locationName} Weather`}
|
||||
>
|
||||
<Text>
|
||||
<Icon icon='Weather' display='inline' style={{ position: 'relative', top: '.3em' }} />
|
||||
Weather
|
||||
<Icon icon='Weather' display='inline' mr='2' style={{ position: 'relative', top: '.3em' }} />
|
||||
<Text
|
||||
cursor='pointer'
|
||||
onClick={() =>
|
||||
this.setState({ manualEntry: !this.state.manualEntry })
|
||||
}
|
||||
>
|
||||
->
|
||||
Weather ->
|
||||
</Text>
|
||||
</Text>
|
||||
|
||||
@ -268,7 +267,7 @@ export default class WeatherTile extends React.Component {
|
||||
flexDirection="column"
|
||||
justifyContent="flex-start"
|
||||
>
|
||||
<Text><Icon icon='Weather' color='black' display='inline' style={{ position: 'relative', top: '.3em' }} /> Weather</Text>
|
||||
<Text><Icon icon='Weather' color='black' display='inline' mr='2' style={{ position: 'relative', top: '.3em' }} /> Weather</Text>
|
||||
<Text width='100%' display='flex' flexDirection='column' mt={1}>
|
||||
Loading, please check again later...
|
||||
</Text>
|
||||
|
@ -40,12 +40,12 @@ button {
|
||||
/* stolen from indigo-react reset.css
|
||||
* TODO: remove and add reset.css properly
|
||||
*/
|
||||
|
||||
|
||||
@keyframes loadingSpinnerRotation {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
@ -53,9 +53,6 @@ button {
|
||||
|
||||
/* dark */
|
||||
@media all and (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: #333;
|
||||
}
|
||||
.bg-gray0-d {
|
||||
background-color: #333;
|
||||
}
|
||||
|
@ -130,9 +130,9 @@ export const LinkItem = (props: LinkItemProps) => {
|
||||
</Anchor>
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
|
||||
<Row minWidth='0' flexShrink={0} width="100%" justifyContent="space-between" py={3} bg="white">
|
||||
|
||||
|
||||
<Author
|
||||
showImage
|
||||
contacts={contacts}
|
||||
@ -150,9 +150,9 @@ export const LinkItem = (props: LinkItemProps) => {
|
||||
</Box>
|
||||
</Link>
|
||||
</Box>
|
||||
|
||||
|
||||
<Dropdown
|
||||
width="200px"
|
||||
dropWidth="200px"
|
||||
alignX="right"
|
||||
alignY="top"
|
||||
options={
|
||||
@ -170,7 +170,7 @@ export const LinkItem = (props: LinkItemProps) => {
|
||||
>
|
||||
<Icon ml="2" display="block" icon="Ellipsis" color="gray" />
|
||||
</Dropdown>
|
||||
|
||||
|
||||
</Row>
|
||||
</Box>);
|
||||
};
|
||||
|
@ -132,7 +132,6 @@ const LinkSubmit = (props: LinkSubmitProps) => {
|
||||
position="absolute"
|
||||
px={2}
|
||||
pt={2}
|
||||
fontSize={0}
|
||||
style={{ pointerEvents: 'none' }}
|
||||
>{canUpload
|
||||
? <>
|
||||
@ -180,7 +179,6 @@ const LinkSubmit = (props: LinkSubmitProps) => {
|
||||
type="url"
|
||||
pl={2}
|
||||
width="100%"
|
||||
fontSize={0}
|
||||
py={2}
|
||||
color="black"
|
||||
backgroundColor="transparent"
|
||||
@ -198,8 +196,8 @@ const LinkSubmit = (props: LinkSubmitProps) => {
|
||||
pl={2}
|
||||
backgroundColor="transparent"
|
||||
width="100%"
|
||||
fontSize={0}
|
||||
color="black"
|
||||
fontSize={1}
|
||||
style={{
|
||||
resize: 'none',
|
||||
height: 40
|
||||
|
@ -1,105 +0,0 @@
|
||||
import React, { useCallback } from "react";
|
||||
import _ from "lodash";
|
||||
import { Link } from "react-router-dom";
|
||||
import GlobalApi from "~/logic/api/global";
|
||||
import {
|
||||
Rolodex,
|
||||
Associations,
|
||||
ChatNotifIndex,
|
||||
ChatNotificationContents,
|
||||
Groups,
|
||||
} from "~/types";
|
||||
import { BigInteger } from "big-integer";
|
||||
import { Box, Col } from "@tlon/indigo-react";
|
||||
import { Header } from "./header";
|
||||
import { pluralize } from "~/logic/lib/util";
|
||||
import ChatMessage from "../chat/components/ChatMessage";
|
||||
|
||||
function describeNotification(mention: boolean, lent: number) {
|
||||
const msg = pluralize("message", lent !== 1);
|
||||
if (mention) {
|
||||
return `mentioned you in ${msg} in`;
|
||||
}
|
||||
return `sent ${msg} in`;
|
||||
}
|
||||
|
||||
export function ChatNotification(props: {
|
||||
index: ChatNotifIndex;
|
||||
contents: ChatNotificationContents;
|
||||
archived: boolean;
|
||||
read: boolean;
|
||||
time: number;
|
||||
timebox: BigInteger;
|
||||
associations: Associations;
|
||||
contacts: Rolodex;
|
||||
groups: Groups;
|
||||
api: GlobalApi;
|
||||
}) {
|
||||
const { index, contents, read, time, api, timebox } = props;
|
||||
const authors = _.map(contents, "author");
|
||||
|
||||
const { chat, mention } = index;
|
||||
const association = props?.associations?.chat?.[chat];
|
||||
const groupPath = association?.["group-path"];
|
||||
const appPath = index?.chat;
|
||||
|
||||
const group = props?.groups?.[groupPath];
|
||||
|
||||
const desc = describeNotification(mention, contents.length);
|
||||
const groupContacts = props.contacts[groupPath] || {};
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
if (props.archived) {
|
||||
return;
|
||||
}
|
||||
|
||||
const func = read ? "unread" : "read";
|
||||
return api.hark[func](timebox, { chat: index });
|
||||
}, [api, timebox, index, read]);
|
||||
|
||||
return (
|
||||
<Col onClick={onClick} flexGrow="1" p="2">
|
||||
<Header
|
||||
chat
|
||||
associations={props.associations}
|
||||
read={read}
|
||||
archived={props.archived}
|
||||
time={time}
|
||||
authors={authors}
|
||||
moduleIcon="Chat"
|
||||
channel={chat}
|
||||
contacts={props.contacts}
|
||||
group={groupPath}
|
||||
description={desc}
|
||||
/>
|
||||
<Col pb="3" pl="5">
|
||||
{_.map(_.take(contents, 5), (content, idx) => {
|
||||
let workspace = groupPath;
|
||||
if (workspace === undefined || group?.hidden) {
|
||||
workspace = '/home';
|
||||
}
|
||||
const to = `/~landscape${workspace}/resource/chat${appPath}?msg=${content.number}`;
|
||||
return (
|
||||
<Link key={idx} to={to}>
|
||||
<ChatMessage
|
||||
measure={() => {}}
|
||||
msg={content}
|
||||
isLastRead={false}
|
||||
group={group}
|
||||
contacts={groupContacts}
|
||||
fontSize='0'
|
||||
pt='2'
|
||||
/>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
{contents.length > 5 && (
|
||||
<Box ml="4" mt="3" mb="2" color="gray" fontSize="14px">
|
||||
and {contents.length - 5} other message
|
||||
{contents.length > 6 ? "s" : ""}
|
||||
</Box>
|
||||
)}
|
||||
</Col>
|
||||
</Col>
|
||||
);
|
||||
}
|
@ -90,6 +90,8 @@ const GraphNodeContent = ({ group, post, contacts, mod, description, index, remo
|
||||
content={contents}
|
||||
group={group}
|
||||
contacts={contacts}
|
||||
fontSize='14px'
|
||||
lineHeight="tall"
|
||||
/>
|
||||
} else if (idx[1] === "1") {
|
||||
const [{ text: header }, { text: body }] = contents;
|
||||
@ -164,13 +166,14 @@ const GraphNode = ({
|
||||
group,
|
||||
read,
|
||||
onRead,
|
||||
showContact = false,
|
||||
remoteContentPolicy
|
||||
}) => {
|
||||
const { contents } = post;
|
||||
author = deSig(author);
|
||||
const history = useHistory();
|
||||
|
||||
const img = (
|
||||
const img = showContact ? (
|
||||
<Sigil
|
||||
ship={`~${author}`}
|
||||
size={16}
|
||||
@ -178,7 +181,7 @@ const GraphNode = ({
|
||||
color={`#000000`}
|
||||
classes="mix-blend-diff"
|
||||
/>
|
||||
);
|
||||
) : <Box style={{ width: '16px' }}></Box>;
|
||||
|
||||
const groupContacts = contacts[groupPath] ?? {};
|
||||
|
||||
@ -192,10 +195,10 @@ const GraphNode = ({
|
||||
}, [read, onRead]);
|
||||
|
||||
return (
|
||||
<Row onClick={onClick} gapX="2" pt="2">
|
||||
<Row onClick={onClick} gapX="2" pt={showContact ? 2 : 0}>
|
||||
<Col>{img}</Col>
|
||||
<Col flexGrow={1} alignItems="flex-start">
|
||||
<Row
|
||||
{showContact && <Row
|
||||
mb="2"
|
||||
height="16px"
|
||||
alignItems="center"
|
||||
@ -208,8 +211,8 @@ const GraphNode = ({
|
||||
<Text ml="2" gray>
|
||||
{moment(time).format("HH:mm")}
|
||||
</Text>
|
||||
</Row>
|
||||
<Row width="100%" p="1">
|
||||
</Row>}
|
||||
<Row width="100%" p="1" flexDirection="column">
|
||||
<GraphNodeContent
|
||||
contacts={groupContacts}
|
||||
post={post}
|
||||
@ -244,16 +247,15 @@ export function GraphNotification(props: {
|
||||
const desc = describeNotification(index.description, contents.length !== 1);
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
if (props.archived) {
|
||||
if (props.archived || read) {
|
||||
return;
|
||||
}
|
||||
|
||||
const func = read ? "unread" : "read";
|
||||
return api.hark[func](timebox, { graph: index });
|
||||
return api.hark["read"](timebox, { graph: index });
|
||||
}, [api, timebox, index, read]);
|
||||
|
||||
return (
|
||||
<Col flexGrow={1} width="100%" p="2">
|
||||
<>
|
||||
<Header
|
||||
onClick={onClick}
|
||||
archived={props.archived}
|
||||
@ -267,7 +269,7 @@ return (
|
||||
description={desc}
|
||||
associations={props.associations}
|
||||
/>
|
||||
<Col flexGrow={1} width="100%" pl="5">
|
||||
<Box flexGrow={1} width="100%" pl={5} gridArea="main">
|
||||
{_.map(contents, (content, idx) => (
|
||||
<GraphNode
|
||||
post={content}
|
||||
@ -282,9 +284,10 @@ return (
|
||||
groupPath={group}
|
||||
read={read}
|
||||
onRead={onClick}
|
||||
showContact={idx === 0}
|
||||
/>
|
||||
))}
|
||||
</Col>
|
||||
</Col>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from "react";
|
||||
import { Text as NormalText, Row, Icon, Rule } from "@tlon/indigo-react";
|
||||
import { Text as NormalText, Row, Icon, Rule, Box } from "@tlon/indigo-react";
|
||||
import f from "lodash/fp";
|
||||
import _ from "lodash";
|
||||
import moment from "moment";
|
||||
@ -71,12 +71,13 @@ export function Header(props: {
|
||||
channel;
|
||||
|
||||
return (
|
||||
<Row onClick={props.onClick} p="2" flexWrap="wrap" gapX="1" alignItems="center">
|
||||
<Row onClick={props.onClick} p="2" flexWrap="wrap" alignItems="center" gridArea="header">
|
||||
{!props.archived && (
|
||||
<Icon
|
||||
display="block"
|
||||
mr="1"
|
||||
icon={read ? "Circle" : "Bullet"}
|
||||
opacity={read ? 0 : 1}
|
||||
mr={2}
|
||||
icon="Bullet"
|
||||
color="blue"
|
||||
/>
|
||||
)}
|
||||
@ -84,13 +85,13 @@ export function Header(props: {
|
||||
{authorDesc}
|
||||
</Text>
|
||||
<Text mr="1">{description}</Text>
|
||||
{!!moduleIcon && <Icon icon={moduleIcon as any} />}
|
||||
{!!channel && <Text fontWeight="500">{channelTitle}</Text>}
|
||||
<Rule vertical height="12px" />
|
||||
{!!moduleIcon && <Icon icon={moduleIcon as any} mr={1} />}
|
||||
{!!channel && <Text fontWeight="500" mr={1}>{channelTitle}</Text>}
|
||||
<Rule vertical height="12px" mr={1} />
|
||||
{groupTitle &&
|
||||
<>
|
||||
<Text fontWeight="500">{groupTitle}</Text>
|
||||
<Rule vertical height="12px"/>
|
||||
<Text fontWeight="500" mr={1}>{groupTitle}</Text>
|
||||
<Rule vertical height="12px" mr={1} />
|
||||
</>
|
||||
}
|
||||
<Text fontWeight="regular" color="lightGray">
|
||||
|
@ -61,20 +61,36 @@ export default function Inbox(props: {
|
||||
};
|
||||
}, []);
|
||||
|
||||
const [newNotifications, ...notifications] =
|
||||
const notifications =
|
||||
Array.from(props.showArchive ? props.archive : props.notifications) || [];
|
||||
|
||||
const calendar = {
|
||||
...MOMENT_CALENDAR_DATE, sameDay: function (now) {
|
||||
if (this.subtract(6, 'hours').isBefore(now)) {
|
||||
return "[Earlier Today]";
|
||||
} else {
|
||||
return MOMENT_CALENDAR_DATE.sameDay;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const notificationsByDay = f.flow(
|
||||
let notificationsByDay = f.flow(
|
||||
f.map<DatedTimebox>(([date, nots]) => [
|
||||
date,
|
||||
nots.filter(filterNotification(associations, props.filter)),
|
||||
]),
|
||||
f.groupBy<DatedTimebox>(([date]) =>
|
||||
moment(daToUnix(date)).format("DDMMYYYY")
|
||||
),
|
||||
f.values,
|
||||
f.reverse
|
||||
f.groupBy<DatedTimebox>(([date]) => {
|
||||
date = moment(daToUnix(date));
|
||||
if (moment().subtract(6, 'hours').isBefore(date)) {
|
||||
return 'latest';
|
||||
} else {
|
||||
return date.format("YYYYMMDD");
|
||||
}
|
||||
}),
|
||||
)(notifications);
|
||||
notificationsByDay = new Map(Object.keys(notificationsByDay).sort().reverse().map(timebox => {
|
||||
return [timebox, notificationsByDay[timebox]];
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
api.hark.getMore(props.showArchive);
|
||||
@ -133,38 +149,24 @@ export default function Inbox(props: {
|
||||
<Col zIndex={4} gapY={2} bg="white" top="0px" position="sticky">
|
||||
{inviteItems(invites, api)}
|
||||
</Col>
|
||||
{newNotifications && (
|
||||
<DaySection
|
||||
latest
|
||||
timeboxes={[newNotifications]}
|
||||
contacts={props.contacts}
|
||||
archive={!!props.showArchive}
|
||||
associations={props.associations}
|
||||
groups={props.groups}
|
||||
graphConfig={props.notificationsGraphConfig}
|
||||
groupConfig={props.notificationsGroupConfig}
|
||||
chatConfig={props.notificationsChatConfig}
|
||||
api={api}
|
||||
/>
|
||||
)}
|
||||
{_.map(
|
||||
notificationsByDay,
|
||||
(timeboxes, idx) =>
|
||||
timeboxes.length > 0 && (
|
||||
<DaySection
|
||||
key={idx}
|
||||
timeboxes={timeboxes}
|
||||
contacts={props.contacts}
|
||||
archive={!!props.showArchive}
|
||||
associations={props.associations}
|
||||
api={api}
|
||||
groups={props.groups}
|
||||
graphConfig={props.notificationsGraphConfig}
|
||||
groupConfig={props.notificationsGroupConfig}
|
||||
chatConfig={props.notificationsChatConfig}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
{[...notificationsByDay.keys()].map((day, index) => {
|
||||
const timeboxes = notificationsByDay.get(day);
|
||||
return timeboxes.length > 0 && (
|
||||
<DaySection
|
||||
key={day}
|
||||
label={day === 'latest' ? 'Today' : moment(day).calendar(null, calendar)}
|
||||
timeboxes={timeboxes}
|
||||
contacts={props.contacts}
|
||||
archive={!!props.showArchive}
|
||||
associations={props.associations}
|
||||
api={api}
|
||||
groups={props.groups}
|
||||
graphConfig={props.notificationsGraphConfig}
|
||||
groupConfig={props.notificationsGroupConfig}
|
||||
chatConfig={props.notificationsChatConfig}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Col>
|
||||
);
|
||||
}
|
||||
@ -181,21 +183,18 @@ function sortIndexedNotification(
|
||||
}
|
||||
|
||||
function DaySection({
|
||||
label,
|
||||
contacts,
|
||||
groups,
|
||||
archive,
|
||||
timeboxes,
|
||||
latest = false,
|
||||
associations,
|
||||
api,
|
||||
groupConfig,
|
||||
graphConfig,
|
||||
chatConfig,
|
||||
remoteContentPolicy
|
||||
}) {
|
||||
const calendar = latest
|
||||
? MOMENT_CALENDAR_DATE
|
||||
: { ...MOMENT_CALENDAR_DATE, sameDay: "[Earlier Today]" };
|
||||
|
||||
const lent = timeboxes.map(([,nots]) => nots.length).reduce(f.add, 0);
|
||||
if (lent === 0 || timeboxes.length === 0) {
|
||||
return null;
|
||||
@ -206,7 +205,7 @@ function DaySection({
|
||||
<Box position="sticky" zIndex="3" top="-1px" bg="white">
|
||||
<Box p="2" bg="scales.black05">
|
||||
<Text>
|
||||
{moment(daToUnix(timeboxes[0][0])).calendar(null, calendar)}
|
||||
{label}
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { ReactNode, useCallback, useMemo } from "react";
|
||||
import { Row, Box, Col, Text, Anchor, Icon, Action } from "@tlon/indigo-react";
|
||||
import React, { ReactNode, useCallback, useMemo, useState } from "react";
|
||||
import { Row, Box } from "@tlon/indigo-react";
|
||||
import _ from "lodash";
|
||||
import {
|
||||
GraphNotificationContents,
|
||||
@ -7,7 +7,6 @@ import {
|
||||
GroupNotificationContents,
|
||||
NotificationGraphConfig,
|
||||
GroupNotificationsConfig,
|
||||
NotifIndex,
|
||||
Groups,
|
||||
Associations,
|
||||
Contacts,
|
||||
@ -17,8 +16,8 @@ import { getParentIndex } from "~/logic/lib/notification";
|
||||
import { StatelessAsyncAction } from "~/views/components/StatelessAsyncAction";
|
||||
import { GroupNotification } from "./group";
|
||||
import { GraphNotification } from "./graph";
|
||||
import { ChatNotification } from "./chat";
|
||||
import { BigInteger } from "big-integer";
|
||||
import { useHovering } from "~/logic/lib/util";
|
||||
|
||||
interface NotificationProps {
|
||||
notification: IndexedNotification;
|
||||
@ -55,9 +54,6 @@ function getMuted(
|
||||
if ("group" in index) {
|
||||
return _.findIndex(groups || [], (g) => g === index.group.group) === -1;
|
||||
}
|
||||
if ("chat" in index) {
|
||||
return _.findIndex(chat || [], (c) => c === index.chat.chat) === -1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -89,11 +85,21 @@ function NotificationWrapper(props: {
|
||||
return api.hark[func](notif);
|
||||
}, [notif, api, isMuted]);
|
||||
|
||||
const { hovering, bind } = useHovering();
|
||||
|
||||
const changeMuteDesc = isMuted ? "Unmute" : "Mute";
|
||||
return (
|
||||
<Row width="100%" flexShrink={0} alignItems="top" justifyContent="space-between">
|
||||
<Box
|
||||
width="100%"
|
||||
display="grid"
|
||||
gridTemplateColumns="1fr 200px"
|
||||
gridTemplateRows="auto"
|
||||
gridTemplateAreas="'header actions' 'main main'"
|
||||
pb={2}
|
||||
{...bind}
|
||||
>
|
||||
{children}
|
||||
<Row gapX="2" p="2" pt='3' alignItems="top">
|
||||
<Row gapX="2" p="2" pt='3' gridArea="actions" justifyContent="flex-end" opacity={[1, hovering ? 1 : 0]}>
|
||||
<StatelessAsyncAction name={changeMuteDesc} onClick={onChangeMute} backgroundColor="transparent">
|
||||
{changeMuteDesc}
|
||||
</StatelessAsyncAction>
|
||||
@ -103,7 +109,7 @@ function NotificationWrapper(props: {
|
||||
</StatelessAsyncAction>
|
||||
)}
|
||||
</Row>
|
||||
</Row>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@ -166,26 +172,6 @@ export function Notification(props: NotificationProps) {
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
if ("chat" in notification.index) {
|
||||
const index = notification.index.chat;
|
||||
const c: ChatNotificationContents = (contents as any).chat;
|
||||
return (
|
||||
<Wrapper>
|
||||
<ChatNotification
|
||||
api={props.api}
|
||||
index={index}
|
||||
contents={c}
|
||||
contacts={props.contacts}
|
||||
read={read}
|
||||
archived={archived}
|
||||
groups={props.groups}
|
||||
timebox={props.time}
|
||||
time={time}
|
||||
associations={associations}
|
||||
/>
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ const SidebarItem = ({ children, view, current }) => {
|
||||
backgroundColor={selected ? "washedGray" : "white"}
|
||||
>
|
||||
<Icon mr={2} display="inline-block" icon={icon(view)} color='black' />
|
||||
<Text color='black' fontSize={0}>
|
||||
<Text color='black'>
|
||||
{children}
|
||||
</Text>
|
||||
</Row>
|
||||
@ -76,7 +76,7 @@ export default function ProfileScreen(props: any) {
|
||||
height="100%"
|
||||
width="100%"
|
||||
display="grid"
|
||||
gridTemplateColumns={["100%", "200px 1fr"]}
|
||||
gridTemplateColumns={["100%", "250px 1fr"]}
|
||||
gridTemplateRows={["48px 1fr", "1fr"]}
|
||||
borderRadius={1}
|
||||
bg="white"
|
||||
@ -95,8 +95,8 @@ export default function ProfileScreen(props: any) {
|
||||
bg={sigilColor}
|
||||
borderRadius={8}
|
||||
my={4}
|
||||
height={128}
|
||||
width={128}
|
||||
height={160}
|
||||
width={160}
|
||||
display="flex"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
|
@ -10,6 +10,7 @@ import CodeMirror from "codemirror";
|
||||
|
||||
import "codemirror/mode/markdown/markdown";
|
||||
import "codemirror/addon/display/placeholder";
|
||||
import "codemirror/addon/edit/continuelist";
|
||||
|
||||
import "codemirror/lib/codemirror.css";
|
||||
import { Box } from "@tlon/indigo-react";
|
||||
@ -54,6 +55,7 @@ export function MarkdownEditor(
|
||||
scrollbarStyle: "native",
|
||||
// cursorHeight: 0.85,
|
||||
placeholder: placeholder || "",
|
||||
extraKeys: { 'Enter': 'newlineAndIndentContinueMarkdownList' }
|
||||
};
|
||||
|
||||
const editor: React.RefObject<any> = useRef();
|
||||
|
@ -67,7 +67,7 @@ export function Note(props: NoteProps & RouteComponentProps) {
|
||||
color="red"
|
||||
ml={2}
|
||||
onClick={deletePost}
|
||||
css={{ cursor: "pointer" }}
|
||||
style={{ cursor: "pointer" }}
|
||||
>
|
||||
Delete
|
||||
</Text>
|
||||
|
@ -61,9 +61,9 @@ export function NotePreview(props: NotePreviewProps) {
|
||||
overflow='hidden'
|
||||
p='2'
|
||||
>
|
||||
<WrappedBox mb={2}><Text bold fontSize='0'>{title}</Text></WrappedBox>
|
||||
<WrappedBox mb={2}><Text bold>{title}</Text></WrappedBox>
|
||||
<WrappedBox>
|
||||
<Text fontSize='14px'>
|
||||
<Text fontSize='14px' lineHeight='tall'>
|
||||
<ReactMarkdown
|
||||
unwrapDisallowed
|
||||
allowedTypes={['text', 'root', 'break', 'paragraph', 'image']}
|
||||
|
@ -1,10 +1,9 @@
|
||||
import React from "react";
|
||||
import { Link, RouteComponentProps } from "react-router-dom";
|
||||
import { RouteComponentProps } from "react-router-dom";
|
||||
import { NotebookPosts } from "./NotebookPosts";
|
||||
import { Box, Button, Text, Row, Col } from "@tlon/indigo-react";
|
||||
import { Col } from "@tlon/indigo-react";
|
||||
import GlobalApi from "~/logic/api/global";
|
||||
import { Contacts, Rolodex, Groups, Associations, Graph, Association, Unreads } from "~/types";
|
||||
import { useShowNickname } from "~/logic/lib/util";
|
||||
|
||||
interface NotebookProps {
|
||||
api: GlobalApi;
|
||||
@ -30,44 +29,14 @@ export function Notebook(props: NotebookProps & RouteComponentProps) {
|
||||
association,
|
||||
graph
|
||||
} = props;
|
||||
const { metadata } = association;
|
||||
|
||||
const group = groups[association?.['group-path']];
|
||||
if (!group) {
|
||||
return null; // Waiting on groups to populate
|
||||
}
|
||||
|
||||
const relativePath = (p: string) => props.baseUrl + p;
|
||||
|
||||
const contact = notebookContacts?.[ship];
|
||||
const isOwn = `~${window.ship}` === ship;
|
||||
let isWriter = true;
|
||||
|
||||
if (group.tags?.publish?.[`writers-${book}`]) {
|
||||
isWriter = isOwn || group.tags?.publish?.[`writers-${book}`]?.has(window.ship);
|
||||
}
|
||||
|
||||
const showNickname = useShowNickname(contact);
|
||||
|
||||
return (
|
||||
<Col gapY="4" pt={4} mx="auto" px={3} maxWidth="768px">
|
||||
<Row justifyContent="space-between">
|
||||
<Box>
|
||||
<Text display='block'>{metadata?.title}</Text>
|
||||
<Text color="lightGray">by </Text>
|
||||
<Text fontFamily={showNickname ? 'sans' : 'mono'}>
|
||||
{showNickname ? contact?.nickname : ship}
|
||||
</Text>
|
||||
</Box>
|
||||
{isWriter && (
|
||||
<Link to={relativePath('/new')}>
|
||||
<Button primary style={{ cursor: 'pointer' }}>
|
||||
New Post
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
</Row>
|
||||
<Box borderBottom="1" borderBottomColor="washedGray" />
|
||||
<NotebookPosts
|
||||
graph={graph}
|
||||
host={ship}
|
||||
|
@ -206,9 +206,6 @@
|
||||
}
|
||||
|
||||
@media all and (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: #333;
|
||||
}
|
||||
.bg-black-d {
|
||||
background-color: black;
|
||||
}
|
||||
|
@ -52,13 +52,14 @@ export default function Author(props: AuthorProps) {
|
||||
<Box
|
||||
ml={showImage ? 2 : 0}
|
||||
color="black"
|
||||
fontSize='1'
|
||||
lineHeight='tall'
|
||||
fontFamily={showNickname ? "sans" : "mono"}
|
||||
fontWeight={showNickname ? '500' : '400'}
|
||||
>
|
||||
{name}
|
||||
</Box>
|
||||
<Box ml={2} color={props.unread ? "blue" : "gray"}>
|
||||
<Box fontSize='1' ml={2} color={props.unread ? "blue" : "gray"}>
|
||||
{dateFmt}
|
||||
</Box>
|
||||
{props.children}
|
||||
|
@ -21,6 +21,7 @@ interface DropdownProps {
|
||||
alignY: AlignY | AlignY[];
|
||||
alignX: AlignX | AlignX[];
|
||||
width?: string;
|
||||
dropWidth?: string;
|
||||
}
|
||||
|
||||
const ClickBox = styled(Box)`
|
||||
@ -111,14 +112,14 @@ export function Dropdown(props: DropdownProps) {
|
||||
});
|
||||
|
||||
return (
|
||||
<Box flexShrink={1} position={open ? "relative" : "static"} minWidth='0'>
|
||||
<Box flexShrink={1} position={open ? "relative" : "static"} minWidth='0' width={props?.width ? props.width : 'auto'}>
|
||||
<ClickBox width='100%' ref={anchorRef} onClick={onOpen}>
|
||||
{children}
|
||||
</ClickBox>
|
||||
{open && (
|
||||
<Portal>
|
||||
<DropdownOptions
|
||||
width={props.width || "max-content"}
|
||||
width={props?.dropWidth || "max-content"}
|
||||
{...coords}
|
||||
ref={dropdownRef}
|
||||
>
|
||||
|
@ -19,10 +19,10 @@ interface MentionTextProps {
|
||||
group: Group;
|
||||
}
|
||||
export function MentionText(props: MentionTextProps) {
|
||||
const { content, contacts, contact, group } = props;
|
||||
const { content, contacts, contact, group, ...rest } = props;
|
||||
|
||||
return (
|
||||
<RichText contacts={contacts} contact={contact} group={group}>
|
||||
<RichText contacts={contacts} contact={contact} group={group} {...rest}>
|
||||
{content.reduce((accum, c) => {
|
||||
if ("text" in c) {
|
||||
return accum + c.text;
|
||||
@ -44,7 +44,7 @@ export function Mention(props: {
|
||||
const { contacts, ship } = props;
|
||||
let { contact } = props;
|
||||
|
||||
contact = (contact?.nickname) ? contact : contacts?.[ship];
|
||||
contact = (contact?.color) ? contact : contacts?.[ship];
|
||||
|
||||
const showNickname = useShowNickname(contact);
|
||||
|
||||
|
@ -129,7 +129,7 @@ class ProfileOverlay extends PureComponent<ProfileOverlayProps, {}> {
|
||||
)}
|
||||
<Text mono gray>{cite(`~${ship}`)}</Text>
|
||||
{!isOwn && (
|
||||
<Button mt={2} width="100%" style={{ cursor: 'pointer' }} onClick={() => history.push(`/~landscape/dm/${ship}`)}>
|
||||
<Button mt={2} fontSize='0' width="100%" style={{ cursor: 'pointer' }} onClick={() => history.push(`/~landscape/dm/${ship}`)}>
|
||||
Send Message
|
||||
</Button>
|
||||
)}
|
||||
|
@ -27,19 +27,17 @@ const RichText = React.memo(({ disableRemoteContent, ...props }) => (
|
||||
{...props}
|
||||
renderers={{
|
||||
link: (linkProps) => {
|
||||
if (disableRemoteContent) {
|
||||
linkProps.remoteContentPolicy = {
|
||||
imageShown: false,
|
||||
audioShown: false,
|
||||
videoShown: false,
|
||||
oembedShown: false
|
||||
};
|
||||
}
|
||||
const remoteContentPolicy = disableRemoteContent ? {
|
||||
imageShown: false,
|
||||
audioShown: false,
|
||||
videoShown: false,
|
||||
oembedShown: false
|
||||
} : null;
|
||||
if (hasProvider(linkProps.href)) {
|
||||
return <RemoteContent className="mw-100" url={linkProps.href} />;
|
||||
}
|
||||
|
||||
return <BaseAnchor target='_blank' rel='noreferrer noopener' borderBottom='1px solid' {...linkProps}>{linkProps.children}</BaseAnchor>;
|
||||
return <BaseAnchor target='_blank' rel='noreferrer noopener' borderBottom='1px solid' remoteContentPolicy={remoteContentPolicy} {...linkProps}>{linkProps.children}</BaseAnchor>;
|
||||
},
|
||||
linkReference: (linkProps) => {
|
||||
const linkText = String(linkProps.children[0].props.children);
|
||||
|
@ -5,6 +5,7 @@ import ReconnectButton from './ReconnectButton';
|
||||
import { StatusBarItem } from './StatusBarItem';
|
||||
import { Sigil } from '~/logic/lib/sigil';
|
||||
import useLocalState from '~/logic/state/local';
|
||||
import { cite } from '~/logic/lib/util';
|
||||
|
||||
const StatusBar = (props) => {
|
||||
const invites = [].concat(...Object.values(props.invites).map(obj => Object.values(obj)));
|
||||
@ -21,7 +22,7 @@ const StatusBar = (props) => {
|
||||
pb='3'
|
||||
>
|
||||
<Row collapse>
|
||||
<Button borderColor='washedGray' mr='2' px='2' onClick={() => props.history.push('/')} {...props}>
|
||||
<Button width="32px" borderColor='washedGray' mr='2' px='2' onClick={() => props.history.push('/')} {...props}>
|
||||
<Icon icon='Spaces' color='black'/>
|
||||
</Button>
|
||||
|
||||
@ -59,9 +60,8 @@ const StatusBar = (props) => {
|
||||
>
|
||||
<Text color='#000000'>Submit <Text color='#000000' display={['none', 'inline']}>an</Text> issue</Text>
|
||||
</StatusBarItem>
|
||||
<StatusBarItem px={'2'} flexShrink='0' onClick={() => props.history.push('/~profile')}>
|
||||
<StatusBarItem width={['32px', 'auto']} px={'2'} flexShrink='0' onClick={() => props.history.push('/~profile')}>
|
||||
<Sigil ship={props.ship} size={16} color='black' classes='mix-blend-diff' icon />
|
||||
<Text ml={2} display={["none", "inline"]} fontFamily="mono">~{props.ship}</Text>
|
||||
</StatusBarItem>
|
||||
</Row>
|
||||
</Box>
|
||||
|
@ -22,7 +22,7 @@ export class OmniboxInput extends Component {
|
||||
border='1px solid transparent'
|
||||
borderRadius='2'
|
||||
maxWidth='calc(600px - 1.15rem)'
|
||||
fontSize='0'
|
||||
fontSize='1'
|
||||
style={{ boxSizing: 'border-box' }}
|
||||
placeholder='Search...'
|
||||
onKeyDown={props.control}
|
||||
|
@ -48,7 +48,7 @@ export function ChannelMenu(props: ChannelMenuProps) {
|
||||
|
||||
const isOurs = ship.slice(1) === window.ship;
|
||||
|
||||
const isMuted =
|
||||
const isMuted =
|
||||
props.graphNotificationConfig.watching.findIndex(
|
||||
(a) => a.graph === appPath && a.index === "/"
|
||||
) === -1;
|
||||
@ -102,7 +102,7 @@ export function ChannelMenu(props: ChannelMenuProps) {
|
||||
</ChannelMenuItem>
|
||||
<ChannelMenuItem bottom icon="Gear" color="black">
|
||||
<Link to={`${baseUrl}/settings`}>
|
||||
<Box fontSize={0} p="2">
|
||||
<Box fontSize={1} p="2">
|
||||
Channel Settings
|
||||
</Box>
|
||||
</Link>
|
||||
@ -119,9 +119,9 @@ export function ChannelMenu(props: ChannelMenuProps) {
|
||||
}
|
||||
alignX="right"
|
||||
alignY="top"
|
||||
width="250px"
|
||||
dropWidth="250px"
|
||||
>
|
||||
<Icon display="block" icon="Menu" color="gray" />
|
||||
<Icon display="block" icon="Menu" color="gray" pr='2' />
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
|
@ -1,21 +1,18 @@
|
||||
import React from "react";
|
||||
import React from 'react';
|
||||
import {
|
||||
Center,
|
||||
Box,
|
||||
Col,
|
||||
Row,
|
||||
Text,
|
||||
IconButton,
|
||||
Button,
|
||||
Icon,
|
||||
} from "@tlon/indigo-react";
|
||||
import { uxToHex } from "~/logic/lib/util";
|
||||
import { Link } from "react-router-dom";
|
||||
Icon
|
||||
} from '@tlon/indigo-react';
|
||||
import { uxToHex } from '~/logic/lib/util';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { Association, Associations } from "~/types/metadata-update";
|
||||
import { Dropdown } from "~/views/components/Dropdown";
|
||||
import { Workspace } from "~/types";
|
||||
import { getTitleFromWorkspace } from "~/logic/lib/workspace";
|
||||
import { Associations } from '~/types/metadata-update';
|
||||
import { Dropdown } from '~/views/components/Dropdown';
|
||||
import { Workspace } from '~/types';
|
||||
import { getTitleFromWorkspace } from '~/logic/lib/workspace';
|
||||
|
||||
const GroupSwitcherItem = ({ to, children, bottom = false, ...rest }) => (
|
||||
<Link to={to}>
|
||||
@ -47,7 +44,7 @@ function RecentGroups(props: { recent: string[]; associations: Associations }) {
|
||||
return (e in associations?.contacts);
|
||||
}).slice(1, 5).map((g) => {
|
||||
const assoc = associations.contacts[g];
|
||||
const color = uxToHex(assoc?.metadata?.color || "0x0");
|
||||
const color = uxToHex(assoc?.metadata?.color || '0x0');
|
||||
return (
|
||||
<Link key={g} style={{ minWidth: 0 }} to={`/~landscape${g}`}>
|
||||
<Row px={1} pb={2} alignItems="center">
|
||||
@ -60,7 +57,7 @@ function RecentGroups(props: { recent: string[]; associations: Associations }) {
|
||||
bg={`#${color}`}
|
||||
mr={2}
|
||||
display="block"
|
||||
flexShrink='0'
|
||||
flexShrink={0}
|
||||
/>
|
||||
<Text verticalAlign='top' maxWidth='100%' overflow='hidden' display='inline-block' style={{ textOverflow: 'ellipsis', whiteSpace: 'pre' }}>{assoc?.metadata?.title}</Text>
|
||||
</Row>
|
||||
@ -76,22 +73,20 @@ export function GroupSwitcher(props: {
|
||||
workspace: Workspace;
|
||||
baseUrl: string;
|
||||
recentGroups: string[];
|
||||
isAdmin: any;
|
||||
}) {
|
||||
const { associations, workspace, isAdmin } = props;
|
||||
const title = getTitleFromWorkspace(associations, workspace);
|
||||
const navTo = (to: string) => `${props.baseUrl}${to}`;
|
||||
return (
|
||||
<Box zIndex="2" position="sticky" top="0px" p={2}>
|
||||
<Box height='48px' backgroundColor="white" zIndex="2" position="sticky" top="0px" py={3} pl='3' borderBottom='1px solid' borderColor='washedGray'>
|
||||
<Col
|
||||
justifyContent="center"
|
||||
bg="white"
|
||||
borderRadius={1}
|
||||
border={1}
|
||||
borderColor="washedGray"
|
||||
>
|
||||
<Row alignItems="center" justifyContent="space-between">
|
||||
<Row justifyContent="space-between">
|
||||
<Dropdown
|
||||
width="231px"
|
||||
width="100%"
|
||||
dropWidth="231px"
|
||||
alignY="top"
|
||||
options={
|
||||
<Col
|
||||
@ -134,9 +129,9 @@ export function GroupSwitcher(props: {
|
||||
<Icon mr="2" color="gray" icon="Plus" />
|
||||
<Text> Join Group</Text>
|
||||
</GroupSwitcherItem>
|
||||
{workspace.type === "group" && (
|
||||
{workspace.type === 'group' && (
|
||||
<>
|
||||
<GroupSwitcherItem to={navTo("/popover/participants")}>
|
||||
<GroupSwitcherItem to={navTo('/popover/participants')}>
|
||||
<Icon
|
||||
mr={2}
|
||||
color="gray"
|
||||
@ -144,7 +139,7 @@ export function GroupSwitcher(props: {
|
||||
/>
|
||||
<Text> Participants</Text>
|
||||
</GroupSwitcherItem>
|
||||
<GroupSwitcherItem to={navTo("/popover/settings")}>
|
||||
<GroupSwitcherItem to={navTo('/popover/settings')}>
|
||||
<Icon
|
||||
mr={2}
|
||||
color="gray"
|
||||
@ -152,7 +147,7 @@ export function GroupSwitcher(props: {
|
||||
/>
|
||||
<Text> Group Settings</Text>
|
||||
</GroupSwitcherItem>
|
||||
{isAdmin && (<GroupSwitcherItem bottom to={navTo("/invites")}>
|
||||
{isAdmin && (<GroupSwitcherItem bottom to={navTo('/invites')}>
|
||||
<Icon
|
||||
mr={2}
|
||||
color="blue"
|
||||
@ -165,25 +160,25 @@ export function GroupSwitcher(props: {
|
||||
</Col>
|
||||
}
|
||||
>
|
||||
<Row p={2} alignItems="center" width='100%' minWidth='0'>
|
||||
<Row alignItems="center" mr={1} flex='1' width='100%' minWidth='0'>
|
||||
<Text overflow='hidden' display='inline-block' flexShrink='1' style={{ textOverflow: 'ellipsis', whiteSpace: 'pre'}}>{title}</Text>
|
||||
<Icon size='12px' ml='1' mt="0px" display="inline-block" icon="ChevronSouth" />
|
||||
<Row width='100%' minWidth='0' flexShrink={0}>
|
||||
<Row justifyContent="space-between" mr={1} flexShrink={0} width='100%' minWidth='0'>
|
||||
<Text lineHeight="1.1" fontSize='2' fontWeight="700" overflow='hidden' display='inline-block' flexShrink='1' style={{ textOverflow: 'ellipsis', whiteSpace: 'pre' }}>{title}</Text>
|
||||
</Row>
|
||||
</Row>
|
||||
</Dropdown>
|
||||
<Row pr={1} justifyContent="flex-end" alignItems="center">
|
||||
{(workspace.type === "group") && (
|
||||
<Row pr='3' verticalAlign="middle">
|
||||
{(workspace.type === 'group') && (
|
||||
<>
|
||||
{isAdmin && (<Link to={navTo("/invites")}>
|
||||
{isAdmin && (<Link to={navTo('/invites')}>
|
||||
<Icon
|
||||
display="block"
|
||||
display="inline-block"
|
||||
color='blue'
|
||||
icon="Users"
|
||||
ml='12px'
|
||||
/>
|
||||
</Link>)}
|
||||
<Link to={navTo("/popover/settings")}>
|
||||
<Icon color='gray' display="block" m={1} icon="Gear" />
|
||||
<Link to={navTo('/popover/settings')}>
|
||||
<Icon color='gray' display="inline-block" ml={'12px'} icon="Gear" />
|
||||
</Link>
|
||||
</>
|
||||
)}
|
||||
|
@ -85,7 +85,7 @@ export function NewChannel(props: NewChannelProps & RouteComponentProps) {
|
||||
moduleType
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (!group) {
|
||||
await waiter(p => Boolean(p?.groups?.[`/ship/~${window.ship}/${resId}`]));
|
||||
}
|
||||
@ -99,11 +99,11 @@ export function NewChannel(props: NewChannelProps & RouteComponentProps) {
|
||||
actions.setStatus({ error: 'Channel creation failed' });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<Col overflowY="auto" p={3}>
|
||||
<Box pb='3' display={['block', 'none']} onClick={() => history.push(props.baseUrl)}>
|
||||
{'<- Back'}
|
||||
<Text fontSize='0' bold>{'<- Back'}</Text>
|
||||
</Box>
|
||||
<Box fontWeight="bold" mb={4} color="black">
|
||||
New Channel
|
||||
|
@ -80,7 +80,7 @@ export function PopoverRoutes(
|
||||
<Box
|
||||
display="grid"
|
||||
gridTemplateRows={["32px 1fr", "100%"]}
|
||||
gridTemplateColumns={["100%", "150px 1fr"]}
|
||||
gridTemplateColumns={["100%", "250px 1fr"]}
|
||||
height="100%"
|
||||
width="100%"
|
||||
>
|
||||
|
@ -52,6 +52,7 @@ export function Resource(props: ResourceProps) {
|
||||
return (
|
||||
<ResourceSkeleton
|
||||
baseUrl={props.baseUrl}
|
||||
groupTags={props.groups?.[selectedGroup]?.tags}
|
||||
{...skelProps}
|
||||
>
|
||||
<ChannelSettings
|
||||
@ -72,6 +73,7 @@ export function Resource(props: ResourceProps) {
|
||||
notificationsGraphConfig={props.notificationsGraphConfig}
|
||||
notificationsChatConfig={props.notificationsChatConfig}
|
||||
baseUrl={props.baseUrl}
|
||||
groupTags={props.groups?.[selectedGroup]?.tags}
|
||||
{...skelProps}
|
||||
atRoot
|
||||
>
|
||||
|
@ -16,7 +16,7 @@ import { ChannelMenu } from "./ChannelMenu";
|
||||
import { NotificationGraphConfig } from "~/types";
|
||||
|
||||
const TruncatedBox = styled(Box)`
|
||||
white-space: nowrap;
|
||||
white-space: pre;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
`;
|
||||
@ -29,19 +29,33 @@ type ResourceSkeletonProps = {
|
||||
children: ReactNode;
|
||||
atRoot?: boolean;
|
||||
title?: string;
|
||||
groupTags?: any;
|
||||
};
|
||||
|
||||
export function ResourceSkeleton(props: ResourceSkeletonProps) {
|
||||
const { association, api, baseUrl, children, atRoot } = props;
|
||||
const { association, api, baseUrl, children, atRoot, groupTags } = props;
|
||||
const app = association?.metadata?.module || association["app-name"];
|
||||
const appPath = association["app-path"];
|
||||
const workspace =
|
||||
baseUrl === "/~landscape/home" ? "/home" : association["group-path"];
|
||||
const title = props.title || association?.metadata?.title;
|
||||
|
||||
const [, , ship, resource] = appPath.split("/");
|
||||
|
||||
const resourcePath = (p: string) => baseUrl + `/resource/${app}/ship/${ship}/${resource}` + p;
|
||||
|
||||
const isOwn = `~${window.ship}` === ship;
|
||||
let isWriter = (app === 'publish') ? true : false;
|
||||
|
||||
if (groupTags?.publish?.[`writers-${resource}`]) {
|
||||
isWriter = isOwn || groupTags?.publish?.[`writers-${resource}`]?.has(window.ship);
|
||||
}
|
||||
|
||||
return (
|
||||
<Col width="100%" height="100%" overflowY="hidden">
|
||||
<Box
|
||||
flexShrink="0"
|
||||
height='48px'
|
||||
py="2"
|
||||
px="2"
|
||||
display="flex"
|
||||
@ -54,6 +68,7 @@ export function ResourceSkeleton(props: ResourceSkeletonProps) {
|
||||
borderRight={1}
|
||||
borderRightColor="gray"
|
||||
pr={3}
|
||||
fontSize='1'
|
||||
mr={3}
|
||||
my="1"
|
||||
display={["block", "none"]}
|
||||
@ -70,15 +85,15 @@ export function ResourceSkeleton(props: ResourceSkeletonProps) {
|
||||
|
||||
{atRoot && (
|
||||
<>
|
||||
<Box pr={1} mr={2}>
|
||||
<Text display="inline-block" verticalAlign="middle">
|
||||
<Box px={1} mr={2}>
|
||||
<Text fontSize='2' fontWeight='700' display="inline-block" verticalAlign="middle" textOverflow="ellipsis" overflow="hidden" whiteSpace="pre">
|
||||
{title}
|
||||
</Text>
|
||||
</Box>
|
||||
<TruncatedBox
|
||||
display={["none", "block"]}
|
||||
maxWidth="60%"
|
||||
verticalAlign="middle"
|
||||
maxWidth='60%'
|
||||
flexShrink={1}
|
||||
title={association?.metadata?.description}
|
||||
color="gray"
|
||||
@ -93,6 +108,11 @@ export function ResourceSkeleton(props: ResourceSkeletonProps) {
|
||||
</RichText>
|
||||
</TruncatedBox>
|
||||
<Box flexGrow={1} />
|
||||
{isWriter && (
|
||||
<Link to={resourcePath('/new')} style={{ flexShrink: '0' }}>
|
||||
<Text bold pr='3' color='blue'>+ New Post</Text>
|
||||
</Link>
|
||||
)}
|
||||
<ChannelMenu
|
||||
graphNotificationConfig={props.notificationsGraphConfig}
|
||||
chatNotificationConfig={props.notificationsChatConfig}
|
||||
|
@ -1,26 +1,24 @@
|
||||
import React, { ReactNode } from "react";
|
||||
import React, { ReactNode } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import {
|
||||
Box,
|
||||
Col,
|
||||
} from "@tlon/indigo-react";
|
||||
import { Link } from "react-router-dom";
|
||||
Col
|
||||
} from '@tlon/indigo-react';
|
||||
|
||||
import GlobalApi from "~/logic/api/global";
|
||||
import { GroupSwitcher } from "../GroupSwitcher";
|
||||
import GlobalApi from '~/logic/api/global';
|
||||
import { GroupSwitcher } from '../GroupSwitcher';
|
||||
import {
|
||||
Associations,
|
||||
Workspace,
|
||||
Groups,
|
||||
Invites,
|
||||
Rolodex,
|
||||
} from "~/types";
|
||||
import { SidebarListHeader } from "./SidebarListHeader";
|
||||
import { useLocalStorageState } from "~/logic/lib/useLocalStorageState";
|
||||
import { getGroupFromWorkspace } from "~/logic/lib/workspace";
|
||||
Rolodex
|
||||
} from '~/types';
|
||||
import { SidebarListHeader } from './SidebarListHeader';
|
||||
import { useLocalStorageState } from '~/logic/lib/useLocalStorageState';
|
||||
import { getGroupFromWorkspace } from '~/logic/lib/workspace';
|
||||
import { SidebarAppConfigs } from './types';
|
||||
import { SidebarList } from "./SidebarList";
|
||||
import { roleForShip } from "~/logic/lib/group";
|
||||
import { SidebarList } from './SidebarList';
|
||||
import { roleForShip } from '~/logic/lib/group';
|
||||
|
||||
const ScrollbarLessCol = styled(Col)`
|
||||
scrollbar-width: none !important;
|
||||
@ -30,7 +28,6 @@ const ScrollbarLessCol = styled(Col)`
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
interface SidebarProps {
|
||||
contacts: Rolodex;
|
||||
children: ReactNode;
|
||||
@ -48,38 +45,24 @@ interface SidebarProps {
|
||||
workspace: Workspace;
|
||||
}
|
||||
|
||||
// Magic spacer that because firefox doesn't correctly calculate
|
||||
// position: sticky on a flex child
|
||||
// remove when https://bugzilla.mozilla.org/show_bug.cgi?id=1488080
|
||||
// is fixed
|
||||
const SidebarStickySpacer = styled(Box)`
|
||||
height: 0px;
|
||||
flex-grow: 1;
|
||||
@-moz-document url-prefix() {
|
||||
& {
|
||||
height: ${p => p.theme.space[6] }px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export function Sidebar(props: SidebarProps) {
|
||||
const { invites, api, associations, selected, apps, workspace } = props;
|
||||
const { associations, selected, workspace } = props;
|
||||
const groupPath = getGroupFromWorkspace(workspace);
|
||||
const display = props.mobileHide ? ["none", "flex"] : "flex";
|
||||
const display = props.mobileHide ? ['none', 'flex'] : 'flex';
|
||||
if (!associations) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const [config, setConfig] = useLocalStorageState<SidebarListConfig>(
|
||||
`group-config:${groupPath || "home"}`,
|
||||
`group-config:${groupPath || 'home'}`,
|
||||
{
|
||||
sortBy: "lastUpdated",
|
||||
hideUnjoined: false,
|
||||
sortBy: 'lastUpdated',
|
||||
hideUnjoined: false
|
||||
}
|
||||
);
|
||||
|
||||
const role = props.groups?.[groupPath] ? roleForShip(props.groups[groupPath], window.ship) : undefined;
|
||||
const isAdmin = (role === "admin") || (workspace?.type === 'home');
|
||||
const isAdmin = (role === 'admin') || (workspace?.type === 'home');
|
||||
|
||||
return (
|
||||
<ScrollbarLessCol
|
||||
@ -107,8 +90,9 @@ export function Sidebar(props: SidebarProps) {
|
||||
groups={props.groups}
|
||||
initialValues={config}
|
||||
handleSubmit={setConfig}
|
||||
selected={selected || ""}
|
||||
workspace={workspace} />
|
||||
selected={selected || ''}
|
||||
workspace={workspace}
|
||||
/>
|
||||
<SidebarList
|
||||
config={config}
|
||||
associations={associations}
|
||||
@ -118,31 +102,6 @@ export function Sidebar(props: SidebarProps) {
|
||||
apps={props.apps}
|
||||
baseUrl={props.baseUrl}
|
||||
/>
|
||||
<SidebarStickySpacer flexShrink={0} />
|
||||
<Box
|
||||
flexShrink="0"
|
||||
display={isAdmin ? "flex" : "none"}
|
||||
justifyContent="center"
|
||||
position="sticky"
|
||||
bottom={"8px"}
|
||||
width="100%"
|
||||
height="fit-content"
|
||||
py="2"
|
||||
>
|
||||
<Link
|
||||
to={!!groupPath ? `/~landscape${groupPath}/new` : `/~landscape/home/new`}
|
||||
>
|
||||
<Box
|
||||
bg="white"
|
||||
p={2}
|
||||
borderRadius={1}
|
||||
border={1}
|
||||
borderColor="lightGray"
|
||||
>
|
||||
+ New Channel
|
||||
</Box>
|
||||
</Link>
|
||||
</Box>
|
||||
</ScrollbarLessCol>
|
||||
);
|
||||
}
|
||||
|
@ -93,8 +93,8 @@ export function SidebarItem(props: {
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
py={1}
|
||||
pl={2}
|
||||
pr={2}
|
||||
pl={3}
|
||||
pr={3}
|
||||
selected={selected}
|
||||
>
|
||||
<Row width='100%' alignItems="center" flex='1 auto' minWidth='0'>
|
||||
@ -105,7 +105,7 @@ export function SidebarItem(props: {
|
||||
/>
|
||||
<Box width='100%' flexShrink={2} ml={2} display='flex' overflow='hidden'>
|
||||
<Text
|
||||
lineHeight="short"
|
||||
lineHeight="tall"
|
||||
display='inline-block'
|
||||
flex='1'
|
||||
overflow='hidden'
|
||||
|
@ -14,7 +14,8 @@ import { Dropdown } from "~/views/components/Dropdown";
|
||||
import { FormikHelpers } from "formik";
|
||||
import { SidebarListConfig, Workspace } from "./types";
|
||||
import { Link, useHistory } from 'react-router-dom';
|
||||
import {ShipSearch} from "~/views/components/ShipSearch";
|
||||
import { getGroupFromWorkspace } from "~/logic/lib/workspace";
|
||||
import { roleForShip } from "~/logic/lib/group";
|
||||
import {Groups, Rolodex} from "~/types";
|
||||
|
||||
export function SidebarListHeader(props: {
|
||||
@ -36,42 +37,51 @@ export function SidebarListHeader(props: {
|
||||
[props.handleSubmit]
|
||||
);
|
||||
|
||||
const groupPath = getGroupFromWorkspace(props.workspace);
|
||||
const role = props.groups?.[groupPath] ? roleForShip(props.groups[groupPath], window.ship) : undefined;
|
||||
const isAdmin = (role === "admin") || (props.workspace?.type === 'home');
|
||||
|
||||
return (
|
||||
<Row
|
||||
flexShrink="0"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
py={2}
|
||||
pr={2}
|
||||
pl={2}
|
||||
px={3}
|
||||
height='48px'
|
||||
>
|
||||
<Box flexShrink='0'>
|
||||
<Text bold>
|
||||
<Text>
|
||||
{props.initialValues.hideUnjoined ? "Joined Channels" : "All Channels"}
|
||||
</Text>
|
||||
</Box>
|
||||
<Box
|
||||
width='100%'
|
||||
textAlign='right'
|
||||
mr='2'
|
||||
display={(props.workspace?.type === 'home') ? 'inline-block' : 'none'}
|
||||
display='flex'
|
||||
>
|
||||
<Link to={`${props.baseUrl}/invites`}>
|
||||
<Link
|
||||
style={{
|
||||
display: isAdmin ? "inline-block" : "none" }}
|
||||
to={
|
||||
!!groupPath ? `/~landscape${groupPath}/new` : `/~landscape/home/new`}>
|
||||
<Icon icon="Plus" color="gray" pr='2'/>
|
||||
</Link>
|
||||
<Link to={`${props.baseUrl}/invites`}
|
||||
style={{ display: (props.workspace?.type === 'home') ? 'inline-block' : 'none'}}>
|
||||
<Text
|
||||
display='inline-block'
|
||||
verticalAlign='middle'
|
||||
py='1px'
|
||||
px='3px'
|
||||
mr='2'
|
||||
backgroundColor='washedBlue'
|
||||
color='blue'
|
||||
borderRadius='1'>
|
||||
+ DM
|
||||
</Text>
|
||||
</Link>
|
||||
</Box>
|
||||
<Dropdown
|
||||
flexShrink='0'
|
||||
width="200px"
|
||||
width="auto"
|
||||
alignY="top"
|
||||
alignX={["right", "left"]}
|
||||
options={
|
||||
@ -102,6 +112,7 @@ export function SidebarListHeader(props: {
|
||||
>
|
||||
<Icon color="gray" icon="Adjust" />
|
||||
</Dropdown>
|
||||
</Box>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
@ -1,171 +0,0 @@
|
||||
import baseStyled, { ThemedStyledInterface } from "styled-components";
|
||||
|
||||
const base = {
|
||||
white: "rgba(255,255,255,1)",
|
||||
black: "rgba(0,0,0,1)",
|
||||
red: "rgba(255,65,54,1)",
|
||||
yellow: "rgba(255,199,0,1)",
|
||||
green: "rgba(0,159,101,1)",
|
||||
blue: "rgba(0,142,255,1)",
|
||||
};
|
||||
|
||||
const scales = {
|
||||
white10: "rgba(255,255,255,0.1)",
|
||||
white20: "rgba(255,255,255,0.2)",
|
||||
white30: "rgba(255,255,255,0.3)",
|
||||
white40: "rgba(255,255,255,0.4)",
|
||||
white50: "rgba(255,255,255,0.5)",
|
||||
white60: "rgba(255,255,255,0.6)",
|
||||
white70: "rgba(255,255,255,0.7)",
|
||||
white80: "rgba(255,255,255,0.8)",
|
||||
white90: "rgba(255,255,255,0.9)",
|
||||
white100: "rgba(255,255,255,1)",
|
||||
black05: "rgba(0,0,0,0.05)",
|
||||
black10: "rgba(0,0,0,0.1)",
|
||||
black20: "rgba(0,0,0,0.2)",
|
||||
black30: "rgba(0,0,0,0.3)",
|
||||
black40: "rgba(0,0,0,0.4)",
|
||||
black50: "rgba(0,0,0,0.5)",
|
||||
black60: "rgba(0,0,0,0.6)",
|
||||
black70: "rgba(0,0,0,0.7)",
|
||||
black80: "rgba(0,0,0,0.8)",
|
||||
black90: "rgba(0,0,0,0.9)",
|
||||
black100: "rgba(0,0,0,1)",
|
||||
red10: "rgba(255,65,54,0.1)",
|
||||
red20: "rgba(255,65,54,0.2)",
|
||||
red30: "rgba(255,65,54,0.3)",
|
||||
red40: "rgba(255,65,54,0.4)",
|
||||
red50: "rgba(255,65,54,0.5)",
|
||||
red60: "rgba(255,65,54,0.6)",
|
||||
red70: "rgba(255,65,54,0.7)",
|
||||
red80: "rgba(255,65,54,0.8)",
|
||||
red90: "rgba(255,65,54,0.9)",
|
||||
red100: "rgba(255,65,54,1)",
|
||||
yellow10: "rgba(255,199,0,0.1)",
|
||||
yellow20: "rgba(255,199,0,0.2)",
|
||||
yellow30: "rgba(255,199,0,0.3)",
|
||||
yellow40: "rgba(255,199,0,0.4)",
|
||||
yellow50: "rgba(255,199,0,0.5)",
|
||||
yellow60: "rgba(255,199,0,0.6)",
|
||||
yellow70: "rgba(255,199,0,0.7)",
|
||||
yellow80: "rgba(255,199,0,0.8)",
|
||||
yellow90: "rgba(255,199,0,0.9)",
|
||||
yellow100: "rgba(255,199,0,1)",
|
||||
green10: "rgba(0,159,101,0.1)",
|
||||
green20: "rgba(0,159,101,0.2)",
|
||||
green30: "rgba(0,159,101,0.3)",
|
||||
green40: "rgba(0,159,101,0.4)",
|
||||
green50: "rgba(0,159,101,0.5)",
|
||||
green60: "rgba(0,159,101,0.6)",
|
||||
green70: "rgba(0,159,101,0.7)",
|
||||
green80: "rgba(0,159,101,0.8)",
|
||||
green90: "rgba(0,159,101,0.9)",
|
||||
green100: "rgba(0,159,101,1)",
|
||||
blue10: "rgba(0,142,255,0.1)",
|
||||
blue20: "rgba(0,142,255,0.2)",
|
||||
blue30: "rgba(0,142,255,0.3)",
|
||||
blue40: "rgba(0,142,255,0.4)",
|
||||
blue50: "rgba(0,142,255,0.5)",
|
||||
blue60: "rgba(0,142,255,0.6)",
|
||||
blue70: "rgba(0,142,255,0.7)",
|
||||
blue80: "rgba(0,142,255,0.8)",
|
||||
blue90: "rgba(0,142,255,0.9)",
|
||||
blue100: "rgba(0,142,255,1)",
|
||||
};
|
||||
|
||||
const theme = {
|
||||
colors: {
|
||||
white: base.white,
|
||||
black: base.black,
|
||||
|
||||
darkGray: scales.black80,
|
||||
gray: scales.black60,
|
||||
lightGray: scales.black30,
|
||||
washedGray: scales.black10,
|
||||
|
||||
red: base.red,
|
||||
lightRed: scales.red30,
|
||||
washedRed: scales.red10,
|
||||
|
||||
yellow: base.yellow,
|
||||
lightYellow: scales.yellow30,
|
||||
washedYellow: scales.yellow10,
|
||||
|
||||
green: base.green,
|
||||
lightGreen: scales.green30,
|
||||
washedGreen: scales.green10,
|
||||
|
||||
blue: base.blue,
|
||||
lightBlue: scales.blue30,
|
||||
washedBlue: scales.blue10,
|
||||
|
||||
none: "rgba(0,0,0,0)",
|
||||
scales: scales,
|
||||
},
|
||||
fonts: {
|
||||
sans: `"Inter", "Inter UI", -apple-system, BlinkMacSystemFont, 'San Francisco', 'Helvetica Neue', Arial, sans-serif`,
|
||||
mono: `"Source Code Pro", "Roboto mono", "Courier New", monospace`,
|
||||
},
|
||||
// font-size
|
||||
fontSizes: [
|
||||
12, // 0
|
||||
16, // 1
|
||||
24, // 2
|
||||
32, // 3
|
||||
48, // 4
|
||||
64, // 5
|
||||
],
|
||||
// font-weight
|
||||
fontWeights: {
|
||||
thin: 300,
|
||||
regular: 400,
|
||||
bold: 600,
|
||||
},
|
||||
// line-height
|
||||
lineHeights: {
|
||||
min: 1.2,
|
||||
short: 1.333333,
|
||||
regular: 1.5,
|
||||
tall: 1.666666,
|
||||
},
|
||||
// border, border-top, border-right, border-bottom, border-left
|
||||
borders: ["none", "1px solid"],
|
||||
// margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, grid-gap, grid-column-gap, grid-row-gap
|
||||
space: [
|
||||
0, // 0
|
||||
4, // 1
|
||||
8, // 2
|
||||
16, // 3
|
||||
24, // 4
|
||||
32, // 5
|
||||
48, // 6
|
||||
64, // 7
|
||||
96, // 8
|
||||
],
|
||||
// border-radius
|
||||
radii: [
|
||||
0, // 0
|
||||
2, // 1
|
||||
4, // 2
|
||||
8, // 3
|
||||
16, // 4
|
||||
],
|
||||
// width, height, min-width, max-width, min-height, max-height
|
||||
sizes: [
|
||||
0, // 0
|
||||
4, // 1
|
||||
8, // 2
|
||||
16, // 3
|
||||
24, // 4
|
||||
32, // 5
|
||||
48, // 6
|
||||
64, // 7
|
||||
96, // 8
|
||||
],
|
||||
// z-index
|
||||
zIndices: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
|
||||
breakpoints: ["550px", "750px", "960px"],
|
||||
};
|
||||
export type Theme = typeof theme;
|
||||
export const styled = baseStyled as ThemedStyledInterface<Theme>;
|
||||
export default theme;
|
@ -1,186 +0,0 @@
|
||||
import baseStyled, { ThemedStyledInterface } from "styled-components";
|
||||
|
||||
const base = {
|
||||
white: "rgba(255,255,255,1)",
|
||||
black: "rgba(0,0,0,1)",
|
||||
red: "rgba(255,65,54,1)",
|
||||
yellow: "rgba(255,199,0,1)",
|
||||
green: "rgba(0,159,101,1)",
|
||||
blue: "rgba(0,142,255,1)",
|
||||
};
|
||||
|
||||
const scales = {
|
||||
white05: "rgba(255,255,255,0.05)",
|
||||
white10: "rgba(255,255,255,0.1)",
|
||||
white20: "rgba(255,255,255,0.2)",
|
||||
white30: "rgba(255,255,255,0.3)",
|
||||
white40: "rgba(255,255,255,0.4)",
|
||||
white50: "rgba(255,255,255,0.5)",
|
||||
white60: "rgba(255,255,255,0.6)",
|
||||
white70: "rgba(255,255,255,0.7)",
|
||||
white80: "rgba(255,255,255,0.8)",
|
||||
white90: "rgba(255,255,255,0.9)",
|
||||
white100: "rgba(255,255,255,1)",
|
||||
black05: "rgba(0,0,0,0.05)",
|
||||
black10: "rgba(0,0,0,0.1)",
|
||||
black20: "rgba(0,0,0,0.2)",
|
||||
black30: "rgba(0,0,0,0.3)",
|
||||
black40: "rgba(0,0,0,0.4)",
|
||||
black50: "rgba(0,0,0,0.5)",
|
||||
black60: "rgba(0,0,0,0.6)",
|
||||
black70: "rgba(0,0,0,0.7)",
|
||||
black80: "rgba(0,0,0,0.8)",
|
||||
black90: "rgba(0,0,0,0.9)",
|
||||
black100: "rgba(0,0,0,1)",
|
||||
red05: "rgba(255,65,54,0.05)",
|
||||
red10: "rgba(255,65,54,0.1)",
|
||||
red20: "rgba(255,65,54,0.2)",
|
||||
red30: "rgba(255,65,54,0.3)",
|
||||
red40: "rgba(255,65,54,0.4)",
|
||||
red50: "rgba(255,65,54,0.5)",
|
||||
red60: "rgba(255,65,54,0.6)",
|
||||
red70: "rgba(255,65,54,0.7)",
|
||||
red80: "rgba(255,65,54,0.8)",
|
||||
red90: "rgba(255,65,54,0.9)",
|
||||
red100: "rgba(255,65,54,1)",
|
||||
yellow05: "rgba(255,199,0,0.05)",
|
||||
yellow10: "rgba(255,199,0,0.1)",
|
||||
yellow20: "rgba(255,199,0,0.2)",
|
||||
yellow30: "rgba(255,199,0,0.3)",
|
||||
yellow40: "rgba(255,199,0,0.4)",
|
||||
yellow50: "rgba(255,199,0,0.5)",
|
||||
yellow60: "rgba(255,199,0,0.6)",
|
||||
yellow70: "rgba(255,199,0,0.7)",
|
||||
yellow80: "rgba(255,199,0,0.8)",
|
||||
yellow90: "rgba(255,199,0,0.9)",
|
||||
yellow100: "rgba(255,199,0,1)",
|
||||
green05: "rgba(0,159,101,0.05)",
|
||||
green10: "rgba(0,159,101,0.1)",
|
||||
green20: "rgba(0,159,101,0.2)",
|
||||
green30: "rgba(0,159,101,0.3)",
|
||||
green40: "rgba(0,159,101,0.4)",
|
||||
green50: "rgba(0,159,101,0.5)",
|
||||
green60: "rgba(0,159,101,0.6)",
|
||||
green70: "rgba(0,159,101,0.7)",
|
||||
green80: "rgba(0,159,101,0.8)",
|
||||
green90: "rgba(0,159,101,0.9)",
|
||||
green100: "rgba(0,159,101,1)",
|
||||
blue05: "rgba(0,142,255,0.05)",
|
||||
blue10: "rgba(0,142,255,0.1)",
|
||||
blue20: "rgba(0,142,255,0.2)",
|
||||
blue30: "rgba(0,142,255,0.3)",
|
||||
blue40: "rgba(0,142,255,0.4)",
|
||||
blue50: "rgba(0,142,255,0.5)",
|
||||
blue60: "rgba(0,142,255,0.6)",
|
||||
blue70: "rgba(0,142,255,0.7)",
|
||||
blue80: "rgba(0,142,255,0.8)",
|
||||
blue90: "rgba(0,142,255,0.9)",
|
||||
blue100: "rgba(0,142,255,1)",
|
||||
};
|
||||
|
||||
const util = {
|
||||
cyan: "#00FFFF",
|
||||
magenta: "#FF00FF",
|
||||
yellow: "#FFFF00",
|
||||
black: "#000000",
|
||||
gray0: "#333333"
|
||||
};
|
||||
|
||||
const theme = {
|
||||
colors: {
|
||||
white: util.gray0,
|
||||
black: base.white,
|
||||
|
||||
darkGray: scales.white80,
|
||||
gray: scales.white60,
|
||||
lightGray: scales.white30,
|
||||
washedGray: scales.white05,
|
||||
|
||||
red: base.red,
|
||||
lightRed: scales.red30,
|
||||
washedRed: scales.red05,
|
||||
|
||||
yellow: base.yellow,
|
||||
lightYellow: scales.yellow30,
|
||||
washedYellow: scales.yellow10,
|
||||
|
||||
green: base.green,
|
||||
lightGreen: scales.green30,
|
||||
washedGreen: scales.green10,
|
||||
|
||||
blue: base.blue,
|
||||
lightBlue: scales.blue30,
|
||||
washedBlue: scales.blue10,
|
||||
|
||||
none: "rgba(0,0,0,0)",
|
||||
|
||||
scales: scales,
|
||||
util: util,
|
||||
},
|
||||
fonts: {
|
||||
sans: `"Inter", "Inter UI", -apple-system, BlinkMacSystemFont, 'San Francisco', 'Helvetica Neue', Arial, sans-serif`,
|
||||
mono: `"Source Code Pro", "Roboto mono", "Courier New", monospace`,
|
||||
},
|
||||
// font-size
|
||||
fontSizes: [
|
||||
12, // 0
|
||||
16, // 1
|
||||
24, // 2
|
||||
32, // 3
|
||||
48, // 4
|
||||
64, // 5
|
||||
],
|
||||
// font-weight
|
||||
fontWeights: {
|
||||
thin: 300,
|
||||
regular: 400,
|
||||
semibold: 500,
|
||||
bold: 600,
|
||||
},
|
||||
// line-height
|
||||
lineHeights: {
|
||||
min: 1.2,
|
||||
short: 1.333333,
|
||||
regular: 1.5,
|
||||
tall: 1.666666,
|
||||
},
|
||||
// border, border-top, border-right, border-bottom, border-left
|
||||
borders: ["none", "1px solid"],
|
||||
// margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, grid-gap, grid-column-gap, grid-row-gap
|
||||
space: [
|
||||
0, // 0
|
||||
4, // 1
|
||||
8, // 2
|
||||
16, // 3
|
||||
24, // 4
|
||||
32, // 5
|
||||
48, // 6
|
||||
64, // 7
|
||||
96, // 8
|
||||
],
|
||||
// border-radius
|
||||
radii: [
|
||||
0, // 0
|
||||
2, // 1
|
||||
4, // 2
|
||||
8, // 3
|
||||
],
|
||||
// width, height, min-width, max-width, min-height, max-height
|
||||
sizes: [
|
||||
0, // 0
|
||||
4, // 1
|
||||
8, // 2
|
||||
16, // 3
|
||||
24, // 4
|
||||
32, // 5
|
||||
48, // 6
|
||||
64, // 7
|
||||
96, // 8
|
||||
],
|
||||
// z-index
|
||||
zIndices: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
|
||||
breakpoints: ["550px", "750px", "960px"],
|
||||
};
|
||||
export type Theme = typeof theme;
|
||||
export const styled = baseStyled as ThemedStyledInterface<Theme>;
|
||||
export default theme;
|
Loading…
Reference in New Issue
Block a user