Merge branch 'release/next-userspace' into mp/line-heights

This commit is contained in:
Matilde Park 2020-07-16 12:14:58 -04:00
commit e39e7c0c0e
41 changed files with 445 additions and 226 deletions

View File

@ -31,7 +31,7 @@ Urbit uses [Nix][nix] to manage builds. On Linux and macOS you can install Nix
via:
```
curl https://nixos.org/nix/install | sh
curl -L https://nixos.org/nix/install | sh
```
The Makefile in the project's root directory contains useful phony targets for

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:581c9b713aa12126f3a618d75bf4254bb431f633fbfc76941663391d3d13cdb0
size 16742515
oid sha256:6614c1334e3e27722a31e0ae41cfda9b5e1915d5fe75daa3a0ec8423a9574bc6
size 16649737

View File

@ -10,9 +10,10 @@ let
tlon = import ../pkgs { inherit pkgs; };
arvo = tlon.arvo;
urbit = tlon.urbit;
herb = tlon.herb;
in
import ./fakeship {
inherit pkgs tlon deps arvo pill ship debug;
inherit pkgs arvo pill ship herb urbit;
}

View File

@ -647,24 +647,24 @@
++ tab-list
^- (list [@t tank])
:~
[%join leaf+";join ~ship/chat-name (glyph)"]
[%leave leaf+";leave ~ship/chat-name"]
[';join' leaf+";join ~ship/chat-name (glyph)"]
[';leave' leaf+";leave ~ship/chat-name"]
::
[%create leaf+";create [type] /chat-name (glyph)"]
[%delete leaf+";delete /chat-name"]
[%invite leaf+";invite /chat-name ~ships"]
[%banish leaf+";banish /chat-name ~ships"]
[';create' leaf+";create [type] /chat-name (glyph)"]
[';delete' leaf+";delete /chat-name"]
[';invite' leaf+";invite /chat-name ~ships"]
[';banish' leaf+";banish /chat-name ~ships"]
::
[%bind leaf+";bind [glyph] ~ship/chat-name"]
[%unbind leaf+";unbind [glyph]"]
[%what leaf+";what (~ship/chat-name) (glyph)"]
[';bind' leaf+";bind [glyph] ~ship/chat-name"]
[';unbind' leaf+";unbind [glyph]"]
[';what' leaf+";what (~ship/chat-name) (glyph)"]
::
[%settings leaf+";settings"]
[%set leaf+";set key (value)"]
[%unset leaf+";unset key"]
[';settings' leaf+";settings"]
[';set' leaf+";set key (value)"]
[';unset' leaf+";unset key"]
::
[%chats leaf+";chats"]
[%help leaf+";help"]
[';chats' leaf+";chats"]
[';help' leaf+";help"]
==
:: +work: run user command
::

View File

@ -43,8 +43,7 @@
(old-to-2 inbox.old)
=? old ?=(%1 -.old)
(old-to-2 inbox.old)
:_ this(state [%2 inbox.old])
[%pass /trim %agent [our.bowl %chat-store] %poke %noun !>([%trim ~])]~
[~ this(state [%2 inbox.old])]
::
++ old-to-2
|= =inbox:store
@ -238,9 +237,6 @@
=. letter.envelope.action (evaluate-letter [author letter]:envelope.action)
=^ envelope u.mailbox (prepend-envelope u.mailbox envelope.action)
:_ state(inbox (~(put by inbox) path.action u.mailbox))
?: =((mod number.envelope 5.000) 0)
:- [%pass /trim %agent [our.bol %chat-store] %poke %noun !>([%trim ~])]
(send-diff path.action action(envelope envelope))
(send-diff path.action action(envelope envelope))
::
++ handle-messages
@ -256,7 +252,6 @@
|- ^- (quip card _state)
?~ envelopes.act
:_ state(inbox (~(put by inbox) path.act u.mailbox))
:- [%pass /trim %agent [our.bol %chat-store] %poke %noun !>([%trim ~])]
%+ send-diff path.act
[%messages path.act 0 (lent evaluated-envelopes) evaluated-envelopes]
=. letter.i.envelopes.act (evaluate-letter [author letter]:i.envelopes.act)

View File

@ -683,7 +683,8 @@
~|(%one-argument !!)
=/ res (mule |.((slam q.cay (dy-vase p.i.p.cig))))
?: ?=(%| -.res)
(he-diff(poy ~) %tan p.res) :: TODO: or +dy-rash ?
:: TODO: or +dy-rash ?
(he-diff(poy ~) %tan leaf+"dojo: naked generator failure" p.res)
(dy-hand %noun p.res)
:: normal generator
::
@ -697,7 +698,7 @@
::
=/ wat (mule |.(!<(?(%ask %say) (slot 2 q.cay))))
?: ?=(%| -.wat)
(he-diff(poy ~) %tan p.wat)
(he-diff(poy ~) %tan leaf+"dojo: generator neither %ask nor %say" p.wat)
=- =/ res (mule -)
?: ?=(%| -.res)
(he-diff(poy ~) %tan leaf+"dojo: generator failure" p.res)
@ -811,10 +812,10 @@
%do
=/ gat (dy-eval p.bil)
?: ?=(%| -.gat)
(he-diff(poy ~) %tan p.gat)
(he-diff(poy ~) %tan leaf+"dojo: %do create gate failed" p.gat)
=/ res (mule |.((slam q.p.gat (dy-vase p.q.bil))))
?: ?=(%| -.res)
(he-diff(poy ~) %tan p.res)
(he-diff(poy ~) %tan leaf+"dojo: %do execute failed" p.res)
(dy-hand %noun p.res)
::
%tu
@ -848,7 +849,7 @@
|= =hoon
=/ res (dy-eval hoon)
?: ?=(%| -.res)
(he-diff(poy ~) %tan p.res)
(he-diff(poy ~) %tan leaf+"dojo: hoon expression failed" p.res)
(dy-hand p.res)
:: +dy-eval: run hoon source against the dojo subject
::
@ -975,7 +976,7 @@
+>
?~ p.cit
(he-diff %txt ">=")
(he-diff %tan u.p.cit)
(he-diff %tan leaf+"dojo: app poke failed" u.p.cit)
::
++ he-wool
|= [way=wire =sign:agent:gall]
@ -984,13 +985,13 @@
%poke-ack
?~ p.sign
+>.$
=. +>.$ (he-diff(poy ~) %tan u.p.sign)
=. +>.$ (he-diff(poy ~) %tan leaf+"dojo: thread poke failed" u.p.sign)
(he-card %pass /wool %agent [our.hid %spider] %leave ~)
::
%watch-ack
?~ p.sign
+>.$
(he-diff(poy ~) %tan u.p.sign)
(he-diff(poy ~) %tan leaf+"dojo: thread watch failed" u.p.sign)
::
%fact
?+ p.cage.sign ~|([%dojo-thread-bad-mark-result p.cage.sign] !!)

View File

@ -218,11 +218,33 @@
?+ +<.sign (on-arvo:def wire sign)
%bound
?: accepted.sign [~ this]
~& [dap.bowl %failed-to-bind path.binding.sign]
[~ this(serving (~(del by serving) path.binding.sign))]
==
::
++ on-leave on-leave:def
++ on-peek on-peek:def
++ on-peek
|= =path
^- (unit (unit cage))
|^
?+ path (on-peek:def path)
[%x %clay %base %hash ~] ``hash+!>(base-hash)
==
:: stolen from +trouble
:: TODO: move to a lib?
++ base-hash
^- @uv
=+ .^ ota=(unit [=ship =desk =aeon:clay])
%gx /(scot %p our.bowl)/hood/(scot %da now.bowl)/kiln/ota/noun
==
?~ ota
*@uv
=/ parent (scot %p ship.u.ota)
=+ .^(=cass:clay %cs /[parent]/[desk.u.ota]/1/late/foo)
%^ end 3 3
.^(@uv %cz /[parent]/[desk.u.ota]/(scot %ud ud.cass))
--
++ on-agent on-agent:def
++ on-fail on-fail:def
--

View File

@ -39,7 +39,13 @@
[d this]
::
++ on-leave on-leave:def
++ on-peek on-peek:def
++ on-peek
|= =path
^- (unit (unit cage))
?+ path (on-peek:def path)
[* %kiln *] (on-peek:kiln-core path)
==
::
++ on-save !>(state)
++ on-load
|= =old-state=vase

File diff suppressed because one or more lines are too long

View File

@ -42,8 +42,10 @@
?. ?=([%all ~] wire) (on-watch:def wire)
=/ jon
%- pairs:enjs:format
:~ [%weather data]
[%location s+location]
:* ['location' s+location]
::
?. ?=([%o *] data) ~
~(tap by p.data)
==
:_ this
[%give %fact ~ %json !>(jon)]~

View File

@ -43,7 +43,12 @@
==
::
++ base-hash
=/ parent (scot %p (sein:title our now our))
=+ .^(=cass:clay %cs /[parent]/kids/1/late/foo)
.^(@uv %cz /[parent]/kids/(scot %ud ud.cass))
=+ .^ ota=(unit [=ship =desk =aeon:clay])
%gx /(scot %p our)/hood/(scot %da now)/kiln/ota/noun
==
?~ ota
*@uv
=/ parent (scot %p ship.u.ota)
=+ .^(=cass:clay %cs /[parent]/[desk.u.ota]/1/late/foo)
.^(@uv %cz /[parent]/[desk.u.ota]/(scot %ud ud.cass))
--

View File

@ -104,6 +104,13 @@
=. +<+.$.abet old
..abet
::
++ on-peek
|= =path
^- (unit (unit cage))
?. ?=([%x %kiln %ota ~] path)
[~ ~]
``noun+!>(ota)
::
++ poke-commit
|= [mon/kiln-commit auto=?]
=< abet
@ -342,7 +349,7 @@
"OTAs disabled"
"OTAs from {<desk.u.ota>} on {<ship.u.ota>}"
?: =(0 ~(wyt by syn))
[%leaf "no syncs configured"]~
[%leaf "no other syncs configured"]~
%+ turn ~(tap in ~(key by syn))
|=(a/kiln-sync (render "sync configured" [sud her syd]:a))
::

14
pkg/arvo/mar/hash.hoon Normal file
View File

@ -0,0 +1,14 @@
|_ hash=@uv
::
++ grad %noun
++ grow
|%
++ noun hash
++ json
s+(rsh 3 2 (scot %uv hash))
--
++ grab
|%
++ noun @uv
--
--

View File

@ -2366,7 +2366,8 @@
|= [pax=path *]
=((~(got by new.dal) pax) (~(got by new.dob) pax))
?: &(?=(%mate germ) ?=(^ con))
[%| %mate-conflict ~]
=+ (turn ~(tap by `(map path *)`con) |=([path *] >[+<-]<))
[%| %mate-conflict -]
=/ old=(map path lobe) :: oldies but goodies
%+ roll ~(tap by (~(uni by old.dal) old.dob))
=< .(old q.bas)

View File

@ -807,21 +807,33 @@
=/ cag=cage (need (need ski))
?> =(%vase p.cag)
=/ gat=vase !<(vase q.cag)
=/ res=(each vase tang)
%- mule |.
=/ res=toon
%- mock :_ (sloy scry)
:_ [%9 2 %0 1] |.
%+ slam
%+ slam gat
!>([now=now eny=eny bek=bek])
!>([[now=now eny=eny bek=bek] ~ ~])
!>([authenticated request])
?: ?=(%| -.res)
?: ?=(%2 -.res)
=+ connection=(~(got by connections.state) duct)
%^ return-static-data-on-duct 500 'text/html'
%: internal-server-error
authenticated.inbound-request.connection
url.request.inbound-request.connection
leaf+"generator crashed"
p.res
==
=/ result !<(simple-payload:http p.res)
?: ?=(%1 -.res)
=+ connection=(~(got by connections.state) duct)
%^ return-static-data-on-duct 500 'text/html'
%: internal-server-error
authenticated.inbound-request.connection
url.request.inbound-request.connection
leaf+"scry blocked on"
>p.res<
~
==
=/ result ;;(simple-payload:http +.p.res)
:: ensure we have a valid content-length header
::
:: We pass on the response and the headers the generator produces, but

View File

@ -869,9 +869,17 @@
?=(%rift -.a-diff)
(gth to.a-diff rift.point)
==
%+ public-keys-give
(subscribers-on-ship who)
[%breach who]
=. ..feel
%+ public-keys-give
(subscribers-on-ship who)
[%breach who]
=/ sor (~(get by sources-reverse) %& who)
?~ sor
..feel
:: delay resubscribing because Ames is going to clear any
:: messages we send now.
::
(emit hen %pass /breach/(scot %ud u.sor) %b %wait now)
::
=. point
?- -.a-diff

View File

@ -5,8 +5,9 @@
|= arg=vase
=/ m (strand ,vase)
^- form:m
=+ !<([a=mark b=mark ~] arg)
;< =bowl:spider bind:m get-bowl:strandio
=/ bek=beak [our q.byk da+now]:bowl
;< =tube:clay bind:m (build-cast:strandio bek a b)
=+ !<([pax=path ~] arg)
?~ bem=(de-beam:format pax)
(strand-fail:strand %path-not-beam >pax< ~)
=/ =mars:clay [i.t i]:?>(?=([@ @ ~] s.u.bem) s.u.bem)
;< =tube:clay bind:m (build-cast:strandio -.u.bem mars)
(pure:m !>(tube))

View File

@ -1,11 +1,11 @@
/- spider
/+ strandio
/+ strand, strandio
=, strand=strand:spider
^- thread:spider
|= arg=vase
=/ m (strand ,vase)
^- form:m
=+ !<([pax=path ~] arg)
;< =bowl:spider bind:m get-bowl:strandio
=/ bek=beak [our q.byk da+now]:bowl
(build-file:strandio bek (flop pax))
?^ bem=(de-beam:format pax)
(build-file:strandio u.bem)
(strand-fail:strand %path-not-beam >pax< ~)

View File

@ -5,8 +5,9 @@
|= arg=vase
=/ m (strand ,vase)
^- form:m
=+ !<([mak=mark ~] arg)
;< =bowl:spider bind:m get-bowl:strandio
=/ bek=beak [our q.byk da+now]:bowl
;< =dais:clay bind:m (build-mark:strandio bek mak)
=+ !<([pax=path ~] arg)
?~ bem=(de-beam:format pax)
(strand-fail:strand %path-not-beam >pax< ~)
=/ =mark (head s.u.bem)
;< =dais:clay bind:m (build-mark:strandio -.u.bem mark)
(pure:m !>(dais))

View File

@ -10,19 +10,98 @@ applications. Landscape applications will usually make good use of Gall, but
it's not strictly required if a Landscape application is not interacting with
ships directly.
Create a development ship, then once your ship is running, mount to Unix with
`|mount %`. This will create a folder named 'home' in your pier in Unix. The
'home' desk contains the working state of your ship -- like a Git repository,
when you want to make a change to it, `|commit %home`.
## Contributing to Landscape applications
To begin developing on Landscape, find the `urbitrc-sample` file found
at `urbit/pkg/interface/config/urbitrc-sample`. Copy it as `urbitrc`.
Open it using your preferred code editor and you should see the following:
```
module.exports = {
URBIT_PIERS: [
"/Users/user/ships/zod/home",
],
herb: false,
URL: 'http://localhost:80'
};
```
This file is the configuration file for your front-end development environment.
Let's walk through it.
The first line, listing piers, specifies which piers to copy the JS files into.
By default, the development environment won't copy files into any pier, even if
you've set the pier in `urbitrc`.
If you want to copy the JS files into your ship, as it would run in a regular
user environment, uncomment these lines in
`pkg/interface/config/webpack.dev.js`:
```javascript
// uncomment to copy into all piers
//
// return Promise.all(this.piers.map(pier => {
// const dst = path.resolve(pier, 'app/landscape/js/index.js');
// copyFile(src, dst).then(() => {
// if(!this.herb) {
// return;
// }
// pier = pier.split('/');
// const desk = pier.pop();
// return exec(`herb -p hood -d '+hood/commit %${desk}' ${pier.join('/')}`);
// });
// }));
```
And then set your pier in `urbitrc` (ensure it ends in `/home`). The `herb`
option in your `urbitrc` will automatically commit the changes to your ship if
you have herb installed (see `pkg/herb`).
For most developers, if you are making changes to Landscape without any back-end
changes on the Urbit ship itself, and you have an Urbit ship running already,
you don't have to boot a development ship. You can simply set up the dev server
for the development environment and point it at your running ship.
To do this, set the `URL` property in your urbitrc and replace it with the URL
of the urbit that you are testing on. For example, a development ship by default
lives at `localhost:80` so our `urbitrc` would have:
```javascript
module.exports = {
URL: 'http://localhost:80'
}
```
Then get everything installed:
```
## go to urbit's interface directory and install the required tooling
cd urbit/pkg/interface
npm install
## Start your development server
npm run start
```
You can then access a hot reloaded version of the interface at
`http://localhost:9000`.
If you set the URL to your running ship, like
`http://sampel-palnet.arvo.network`, then you can use your actual ship while
running local-only development changes.
As previously stated, if your changes require back-end development (front-end
and Gall changes, for example), or you just want an empty development
environment, you'll want to create a development ship.
### Creating a development ship
[nix](https://github.com/NixOS/nix) and `git-lfs` should be installed at this
point, and have been used to `make build` the project.
Designing interfaces within urbit/urbit additionally requires that the
First follow the
[instructions](https://urbit.org/using/develop/#creating-a-development-ship) for
fake `~zod` initialization have been followed.
fake `~zod` initialization.
Once your fake ship is running and you see
```
@ -32,43 +111,27 @@ in your console, be sure to 'mount' your ship's working state (what we call
'desks') to your local machine via the `|mount %` command. This will ensure that
code you modify locally can be committed to your ship and initialized.
To begin developing Urbit's frontend, you'll need to sync your currently-running
fake ship with the urbit/urbit repo's code. Find the `urbitrc-sample` file found
at `urbit/pkg/interface/config/urbitrc-sample`. Open it using your preferred
code editor and you should see the following:
```
module.exports = {
URBIT_PIERS: [
"/Users/user/ships/zod/home",
]
};
```
Edit the path between quotes `/Users/user/ships/zod/home` with wherever your
fake ship is located on your machine. This zod location path *must* end in
`../home` to correctly intitalize any code you write. Save the file as `urbitrc`
inside that same folder. Any code edited within the `urbit/urbit` will now be
able to be synced to your running ship, and previewed in the browser.
To set up urbit's Javascript environment, you'll need node (ideally installed
via [nvm](https://github.com/nvm-sh/nvm)) and webpack, which will be installed via
node.
via [nvm](https://github.com/nvm-sh/nvm)) and webpack, which will be installed
via node.
Perform the following steps to get the above set up for urbit's apps:
If you want to copy the code into your ship, perform the following steps:
```
## go to urbit's interface directory and install the required tooling
cd urbit/pkg/interface
npm install
## Start watching the entire directory for changes
## Build the JS code
npm run build:dev
```
Any changes made to any files within the `/pkg/interface` directory will now trigger
a gulp rebuild when saved. To sync these changes to your running ship, enter
dojo and input the following:
If you want to run the JavaScript code in a dev server, you can simply set the
URL in your `urbitrc` to `localhost:80` and `npm run start` instead.
If you set your pier in `urbitrc`, and uncommented the code in the webpack
config, then once the build process is running, commit on your ship to copy the
changed JS code in:
```
|commit %home
@ -78,19 +141,21 @@ Your urbit should take a moment to process the changes, and will emit a `>=`.
Refreshing your browser will display the newly-rendered interface.
Once you are done editing code, and wish to commit changes to git, stop your
`build:dev` process. Do not commit compiled code, but submit the source code
process. Do not commit compiled code, but submit the source code
for review.
Please also ensure your pull request fits our standards for [Git
hygiene][contributing].
[contributing]: /CONTRIBUTING.md#git-practice [arvo]: /pkg/arvo
[contributing]: /CONTRIBUTING.md#git-practice
[arvo]: /pkg/arvo
[interface]:/pkg/interface
## Linting
The Urbit interface uses Eslint to lint the JavaScript code. To install the
linter and for usage through the command, do the following:
```bash
$ cd ./pkg/interface
$ npm install
@ -98,6 +163,7 @@ $ npm run lint
```
To use the linter, run npm scripts
```bash
$ npm run lint # lints all files in `interface`
$ npm run lint-file ./src/apps/chat/**/*.js # lints all .js files in `interface/chat`
@ -119,7 +185,8 @@ documentation for its everyday use -- just create a repo [using its
template][template], install and then start it, and you'll soon be up and
running.
[cla]: https://github.com/urbit/create-landscape-app [template]:
https://github.com/urbit/create-landscape-app/generate [gall]:
https://urbit.org/docs/learn/arvo/gall/ [chat]: /pkg/arvo/app/chat-view.hoon
[cla]: https://github.com/urbit/create-landscape-app
[template]: https://github.com/urbit/create-landscape-app/generate
[gall]:https://urbit.org/docs/learn/arvo/gall/
[chat]: /pkg/arvo/app/chat-view.hoon
[publish]: /pkg/arvo/app/publish.hoon

View File

@ -2,5 +2,6 @@ module.exports = {
URBIT_PIERS: [
"/Users/user/ships/zod/home",
],
herb: false
herb: false,
URL: 'http://localhost:80'
};

View File

@ -22,22 +22,49 @@ class UrbitShipPlugin {
'UrbitShipPlugin',
async (compilation) => {
const src = path.resolve(compiler.options.output.path, 'index.js');
return Promise.all(this.piers.map(pier => {
const dst = path.resolve(pier, 'app/landscape/js/index.js');
copyFile(src, dst).then(() => {
if(!this.herb) {
return;
}
pier = pier.split('/');
const desk = pier.pop();
return exec(`herb -p hood -d '+hood/commit %${desk}' ${pier.join('/')}`);
});
}));
// uncomment to copy into all piers
//
// return Promise.all(this.piers.map(pier => {
// const dst = path.resolve(pier, 'app/landscape/js/index.js');
// copyFile(src, dst).then(() => {
// if(!this.herb) {
// return;
// }
// pier = pier.split('/');
// const desk = pier.pop();
// return exec(`herb -p hood -d '+hood/commit %${desk}' ${pier.join('/')}`);
// });
// }));
}
)
);
}
}
let devServer = {
contentBase: path.join(__dirname, '../dist'),
hot: true,
port: 9000,
historyApiFallback: true
};
if(urbitrc.URL) {
devServer = {
...devServer,
index: '',
proxy: {
'/~landscape/js/index.js': {
target: 'http://localhost:9000',
pathRewrite: (req, path) => '/index.js'
},
'**': {
target: urbitrc.URL,
// ensure proxy doesn't timeout channels
proxyTimeout: 0
}
}
};
}
module.exports = {
mode: 'development',
entry: {
@ -54,7 +81,8 @@ module.exports = {
plugins: [
'@babel/plugin-proposal-object-rest-spread',
'@babel/plugin-proposal-optional-chaining',
'@babel/plugin-proposal-class-properties'
'@babel/plugin-proposal-class-properties',
'react-hot-loader/babel'
]
}
},
@ -77,12 +105,7 @@ module.exports = {
extensions: ['.js', '.ts', '.tsx']
},
devtool: 'inline-source-map',
// devServer: {
// contentBase: path.join(__dirname, './'),
// hot: true,
// port: 9000,
// historyApiFallback: true
// },
devServer: devServer,
plugins: [
new UrbitShipPlugin(urbitrc)
// new CleanWebpackPlugin(),

View File

@ -11,7 +11,7 @@ module.exports = {
module: {
rules: [
{
test: /\.js?$/,
test: /\.(j|t)sx?$/,
use: {
loader: 'babel-loader',
options: {
@ -39,7 +39,7 @@ module.exports = {
]
},
resolve: {
extensions: ['.js']
extensions: ['.js', '.ts', '.tsx']
},
devtool: 'inline-source-map',
// devServer: {
@ -58,7 +58,7 @@ module.exports = {
output: {
filename: 'index.js',
chunkFilename: 'index.js',
path: path.resolve(urbitrc.URBIT_PIERS[0] + '/app/landscape/', 'js'),
path: path.resolve(__dirname, '../../arvo/app/landscape/js'),
publicPath: '/'
},
optimization: {

View File

@ -3510,6 +3510,12 @@
"entities": "^2.0.0"
}
},
"dom-walk": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz",
"integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==",
"dev": true
},
"domain-browser": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
@ -4761,6 +4767,16 @@
"is-glob": "^4.0.1"
}
},
"global": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz",
"integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==",
"dev": true,
"requires": {
"min-document": "^2.19.0",
"process": "^0.11.10"
}
},
"global-modules": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz",
@ -6113,6 +6129,15 @@
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
"dev": true
},
"min-document": {
"version": "2.19.0",
"resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz",
"integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=",
"dev": true,
"requires": {
"dom-walk": "^0.1.0"
}
},
"mini-create-react-context": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.3.2.tgz",
@ -7327,11 +7352,41 @@
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",
"integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw=="
},
"react-hot-loader": {
"version": "4.12.21",
"resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.12.21.tgz",
"integrity": "sha512-Ynxa6ROfWUeKWsTHxsrL2KMzujxJVPjs385lmB2t5cHUxdoRPGind9F00tOkdc1l5WBleOF4XEAMILY1KPIIDA==",
"dev": true,
"requires": {
"fast-levenshtein": "^2.0.6",
"global": "^4.3.0",
"hoist-non-react-statics": "^3.3.0",
"loader-utils": "^1.1.0",
"prop-types": "^15.6.1",
"react-lifecycles-compat": "^3.0.4",
"shallowequal": "^1.1.0",
"source-map": "^0.7.3"
},
"dependencies": {
"source-map": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
"dev": true
}
}
},
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"react-lifecycles-compat": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==",
"dev": true
},
"react-markdown": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-4.3.1.tgz",
@ -9452,8 +9507,7 @@
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"aproba": {
"version": "1.2.0",
@ -9474,14 +9528,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"
@ -9496,20 +9548,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",
@ -9626,8 +9675,7 @@
"inherits": {
"version": "2.0.4",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"ini": {
"version": "1.3.5",
@ -9639,7 +9687,6 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@ -9654,7 +9701,6 @@
"version": "3.0.4",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@ -9662,14 +9708,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"
@ -9688,7 +9732,6 @@
"version": "0.5.3",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"minimist": "^1.2.5"
}
@ -9750,8 +9793,7 @@
"npm-normalize-package-bin": {
"version": "1.0.1",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"npm-packlist": {
"version": "1.4.8",
@ -9779,8 +9821,7 @@
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"object-assign": {
"version": "4.1.1",
@ -9792,7 +9833,6 @@
"version": "1.4.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@ -9870,8 +9910,7 @@
"safe-buffer": {
"version": "5.1.2",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"safer-buffer": {
"version": "2.1.2",
@ -9907,7 +9946,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",
@ -9927,7 +9965,6 @@
"version": "3.0.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@ -9971,14 +10008,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
}
}
},
@ -10459,8 +10494,7 @@
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"aproba": {
"version": "1.2.0",
@ -10481,14 +10515,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"
@ -10503,20 +10535,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",
@ -10633,8 +10662,7 @@
"inherits": {
"version": "2.0.4",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"ini": {
"version": "1.3.5",
@ -10646,7 +10674,6 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@ -10661,7 +10688,6 @@
"version": "3.0.4",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@ -10669,14 +10695,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"
@ -10695,7 +10719,6 @@
"version": "0.5.3",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"minimist": "^1.2.5"
}
@ -10757,8 +10780,7 @@
"npm-normalize-package-bin": {
"version": "1.0.1",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"npm-packlist": {
"version": "1.4.8",
@ -10786,8 +10808,7 @@
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"object-assign": {
"version": "4.1.1",
@ -10799,7 +10820,6 @@
"version": "1.4.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@ -10877,8 +10897,7 @@
"safe-buffer": {
"version": "5.1.2",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"safer-buffer": {
"version": "2.1.2",
@ -10914,7 +10933,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",
@ -10934,7 +10952,6 @@
"version": "3.0.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@ -10978,14 +10995,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
}
}
},

View File

@ -46,6 +46,7 @@
"eslint-plugin-react": "^7.19.0",
"file-loader": "^6.0.0",
"html-webpack-plugin": "^4.2.0",
"react-hot-loader": "^4.12.21",
"sass": "^1.26.5",
"sass-loader": "^8.0.2",
"webpack": "^4.43.0",
@ -59,7 +60,7 @@
"tsc:watch": "tsc --watch",
"build:dev": "cross-env NODE_ENV=development webpack --config config/webpack.dev.js",
"build:prod": "cross-env NODE_ENV=production webpack --config config/webpack.prod.js",
"start": "webpack-dev-server",
"start": "webpack-dev-server --config config/webpack.dev.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",

View File

@ -1,3 +1,5 @@
import { hot } from 'react-hot-loader/root';
import 'react-hot-loader';
import * as React from 'react';
import { BrowserRouter as Router, Route, withRouter, Switch } from 'react-router-dom';
import styled, { ThemeProvider, createGlobalStyle } from 'styled-components';
@ -44,7 +46,7 @@ const Content = styled.div`
const StatusBarWithRouter = withRouter(StatusBar);
export default class App extends React.Component {
class App extends React.Component {
constructor(props) {
super(props);
this.ship = window.ship;
@ -60,6 +62,7 @@ export default class App extends React.Component {
componentDidMount() {
this.subscription.start();
this.api.local.getBaseHash();
}
render() {
@ -151,3 +154,5 @@ export default class App extends React.Component {
}
}
export default process.env.NODE_ENV === 'production' ? App : hot(App);

View File

@ -53,4 +53,8 @@ export default class BaseApi<S extends object = {}> {
);
});
}
scry<T>(app: string, path: Path): Promise<T> {
return fetch(`/~/scry/${app}${path}.json`).then(r => r.json() as Promise<T>);
}
}

View File

@ -2,9 +2,13 @@ import BaseApi from "./base";
import { StoreState } from "../store/type";
import { SelectedGroup } from "../types/local-update";
export default class LocalApi extends BaseApi<StoreState> {
getBaseHash() {
this.scry<string>('file-server', '/clay/base/hash').then(baseHash => {
this.store.handleEvent({ data: { local: { baseHash } } });
});
}
setSelected(selected: SelectedGroup[]) {
this.store.handleEvent({
data: {

View File

@ -73,6 +73,7 @@ export default class ChatApp extends React.Component<ChatAppProps, {}> {
unreads[stat] = Boolean(unread);
if (
unread &&
stat in associations.chat &&
(selectedGroups.length === 0 ||
selectedGroups
.map((e) => {
@ -87,7 +88,7 @@ export default class ChatApp extends React.Component<ChatAppProps, {}> {
if (totalUnreads !== this.totalUnreads) {
document.title =
totalUnreads > 0 ? `OS1 - Chat (${totalUnreads})` : 'OS1 - Chat';
totalUnreads > 0 ? `(${totalUnreads}) OS1 - Chat` : 'OS1 - Chat';
this.totalUnreads = totalUnreads;
}

View File

@ -1,4 +1,4 @@
import React, { Component } from "react";
import React, { Component, Fragment } from "react";
import _ from "lodash";
import moment from "moment";
@ -359,6 +359,10 @@ export class ChatScreen extends Component<ChatScreenProps, ChatScreenState> {
props.pendingMessages.get(props.station) || []
).map((value) => ({ ...value, pending: true }));
if(unread !== 0) {
unread += pendingMessages.length;
}
messages = pendingMessages.concat(messages);
const messageElements = messages.map((msg, i) => {
@ -389,10 +393,9 @@ export class ChatScreen extends Component<ChatScreenProps, ChatScreenState> {
);
if (unread > 0 && i === unread - 1) {
return (
<>
<Fragment key={msg.uid}>
{messageElem}
<div
key={"unreads" + msg.uid}
ref={this.setUnreadMarker}
className="mv2 green2 flex items-center f9"
>
@ -409,19 +412,18 @@ export class ChatScreen extends Component<ChatScreenProps, ChatScreenState> {
className="b--green2 ma0 bt-0"
/>
</div>
</>
</Fragment>
);
} else if (dayBreak) {
return (
<>
<Fragment key={msg.uid}>
{messageElem}
<div
key={"daybreak" + msg.uid}
className="pv3 gray2 b--gray2 flex items-center justify-center f9 "
>
<p>{moment(_.get(messages[i], when)).calendar()}</p>
</div>
</>
</Fragment>
);
} else {
return messageElem;

View File

@ -72,7 +72,7 @@ export class Message extends Component {
);
} else if ('url' in letter) {
let imgMatch =
/(jpg|img|png|gif|tiff|jpeg|JPG|IMG|PNG|TIFF|GIF|webp|WEBP|webm|WEBM|svg|SVG)$/
/(jpg|img|png|gif|tiff|jpeg|JPG|IMG|PNG|TIFF|GIF|webp|WEBP|svg|SVG)$/
.exec(letter.url);
const youTubeRegex = new RegExp(String(/(?:https?:\/\/(?:[a-z]+.)?)/.source) // protocol
+ /(?:youtu\.?be(?:\.com)?\/)(?:embed\/)?/.source // short and long-links
@ -88,7 +88,8 @@ export class Message extends Component {
src={letter.url}
style={{
height: 'min(250px, 20vh)',
maxWidth: '80vw'
maxWidth: 'calc(100% - 36px - 1.5rem)',
objectFit: 'contain'
}}
></img>
);

View File

@ -19,31 +19,26 @@ export class OverlaySigil extends Component {
this.profileShow = this.profileShow.bind(this);
this.profileHide = this.profileHide.bind(this);
this.updateContainerInterval = null;
}
profileShow() {
this.updateContainerOffset();
this.setState({ profileClicked: true });
this.updateContainerInterval = setInterval(
this.updateContainerOffset.bind(this),
1000
);
}
componentDidMount() {
this.updateContainerOffset();
}
componentWillUnmount() {
if (this.updateContainerInterval) {
profileHide() {
this.setState({ profileClicked: false });
if(this.updateContainerInterval) {
clearInterval(this.updateContainerInterval);
this.updateContainerInterval = null;
}
}
profileShow() {
this.setState({ profileClicked: true });
}
profileHide() {
this.setState({ profileClicked: false });
}
updateContainerOffset() {
if (this.containerRef && this.containerRef.current) {
const parent = this.containerRef.current.offsetParent;

View File

@ -27,7 +27,7 @@ export class Welcome extends Component {
return ((!wasWelcomed && this.state.show) && (contacts.length !== 0)) ? (
<div className="ma4 pa2 bg-welcome-green bg-gray1-d white-d">
<p className="f8 lh-copy">Each Group is a list of other Urbit IDs that share some set of modules: chats, links and notebooks.</p>
<p className="f8 lh-copy">Each Group is a list of other Urbit IDs that share some set of modules: chats, links, and notebooks.</p>
<p className="f8 pt2 dib pointer bb"
onClick={(() => this.disableWelcome())}
>

View File

@ -24,6 +24,7 @@ export default class LaunchApp extends React.Component {
const { props } = this;
return (
<div className="h-100 flex flex-column h-100">
<div className='v-mid ph2 dtc-m dtc-l dtc-xl flex justify-between flex-wrap' style={{ maxWidth: '40rem' }}>
<Welcome firstTime={props.launch.firstTime} api={props.api} />
<Tiles
@ -34,6 +35,8 @@ export default class LaunchApp extends React.Component {
weather={props.weather}
/>
</div>
<div className="gray2-d gray2 ml4 mb4 f8"> {props.baseHash} </div>
</div>
);
}
}

View File

@ -54,21 +54,29 @@ export class LinksApp extends Component {
const seen = props.linksSeen ? props.linksSeen : {};
const selectedGroups = props.selectedGroups ? props.selectedGroups : [];
const selGroupPaths = selectedGroups.map(g => g[0]);
const totalUnseen = _.reduce(
seen,
(acc, links) => acc + _.reduce(links, (total, hasSeen) => total + (hasSeen ? 0 : 1), 0),
links,
(acc, collection, path) => {
if(selGroupPaths.length > 0
&& !selGroupPaths.includes(associations.link?.[path]?.['group-path'])) {
return acc;
}
return acc + collection.unseenCount;
},
0
);
if(totalUnseen !== this.totalUnseen) {
document.title = totalUnseen !== 0 ? `OS1 - Links (${totalUnseen})` : 'OS1 - Links';
document.title = totalUnseen !== 0 ? `(${totalUnseen}) OS1 - Links` : 'OS1 - Links';
this.totalUnseen = totalUnseen;
}
const invites = props.invites ?
props.invites : {};
const selectedGroups = props.selectedGroups ? props.selectedGroups : [];
const listening = props.linkListening;

View File

@ -65,7 +65,7 @@ export default class PublishApp extends React.Component {
.value();
if (this.unreadTotal !== unreadTotal) {
document.title = unreadTotal > 0 ? `OS1 - Publish (${unreadTotal})` : 'OS1 - Publish';
document.title = unreadTotal > 0 ? `(${unreadTotal}) OS1 - Publish` : 'OS1 - Publish';
this.unreadTotal = unreadTotal;
}

View File

@ -97,7 +97,7 @@ export default class GroupFilter extends Component {
selected: selected,
results: []
}, (() => {
this.props.api.setSelected(this.state.selected);
this.props.api.local.setSelected(this.state.selected);
localStorage.setItem('urbit-selectedGroups', JSON.stringify(this.state.selected));
}));
}
@ -108,7 +108,7 @@ export default class GroupFilter extends Component {
return e !== group;
});
this.setState({ selected: selected }, (() => {
this.props.api.setSelected(this.state.selected);
this.props.api.local.setSelected(this.state.selected);
localStorage.setItem('urbit-selectedGroups', JSON.stringify(this.state.selected));
}));
}

View File

@ -3,7 +3,7 @@ import { StoreState } from '../store/type';
import { Cage } from '../types/cage';
import { LocalUpdate } from '../types/local-update';
type LocalState = Pick<StoreState, 'sidebarShown' | 'selectedGroups'>;
type LocalState = Pick<StoreState, 'sidebarShown' | 'selectedGroups' | 'baseHash'>;
export default class LocalReducer<S extends LocalState> {
reduce(json: Cage, state: S) {
@ -11,8 +11,14 @@ export default class LocalReducer<S extends LocalState> {
if (data) {
this.sidebarToggle(data, state);
this.setSelected(data, state);
this.baseHash(data, state);
}
}
baseHash(obj: LocalUpdate, state: S) {
if ('baseHash' in obj) {
state.baseHash = obj.baseHash;
}
}
sidebarToggle(obj: LocalUpdate, state: S) {
if ('sidebarToggle' in obj) {

View File

@ -38,6 +38,7 @@ export default class GlobalStore extends BaseStore<StoreState> {
pendingMessages: new Map(),
chatInitialized: false,
sidebarShown: true,
baseHash: null,
invites: {},
associations: {
chat: {},

View File

@ -16,6 +16,7 @@ export interface StoreState {
// local state
sidebarShown: boolean;
selectedGroups: SelectedGroup[];
baseHash: string | null;
// invite state
invites: Invites;
// metadata state

View File

@ -2,7 +2,8 @@ import { Path } from './noun';
export type LocalUpdate =
LocalUpdateSidebarToggle
| LocalUpdateSelectedGroups;
| LocalUpdateSelectedGroups
| LocalUpdateBaseHash;
interface LocalUpdateSidebarToggle {
sidebarToggle: boolean;
@ -12,4 +13,8 @@ interface LocalUpdateSelectedGroups {
selected: SelectedGroup[];
}
interface LocalUpdateBaseHash {
baseHash: string;
}
export type SelectedGroup = [Path, string];