mirror of
https://github.com/jhillyerd/inbucket.git
synced 2025-12-17 09:37:02 +00:00
Merge branch 'release/3.0.0-rc1'
This commit is contained in:
44
.github/workflows/release.yml
vendored
Normal file
44
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
name: Build and Release
|
||||
on:
|
||||
push:
|
||||
branches: [ "master", "develop" ]
|
||||
tags: [ "v*" ]
|
||||
pull_request:
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.15
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '10.x'
|
||||
- name: Setup Elm
|
||||
uses: jorelali/setup-elm@v2
|
||||
with:
|
||||
elm-version: 0.19.1
|
||||
- name: Build frontend
|
||||
run: |
|
||||
npm ci
|
||||
npm run build
|
||||
working-directory: ./ui
|
||||
- name: Test build release
|
||||
uses: goreleaser/goreleaser-action@v2
|
||||
if: "!startsWith(github.ref, 'refs/tags/v')"
|
||||
with:
|
||||
version: latest
|
||||
args: release --snapshot
|
||||
- name: Build and publish release
|
||||
uses: goreleaser/goreleaser-action@v2
|
||||
if: "startsWith(github.ref, 'refs/tags/v')"
|
||||
with:
|
||||
version: latest
|
||||
args: release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
16
.travis.yml
16
.travis.yml
@@ -25,22 +25,6 @@ jobs:
|
||||
script:
|
||||
- "elm-format --validate ."
|
||||
- "npm run build"
|
||||
- stage: deploy
|
||||
go: "1.15.x"
|
||||
before_install:
|
||||
- "nvm install 10.19.0"
|
||||
install:
|
||||
- "cd ui"
|
||||
- "npm ci"
|
||||
- "npm run build"
|
||||
- "cd .."
|
||||
script: "curl -sL https://git.io/goreleaser | bash"
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- rpm
|
||||
|
||||
stages:
|
||||
- test
|
||||
- name: deploy
|
||||
if: tag IS present
|
||||
|
||||
12
CHANGELOG.md
12
CHANGELOG.md
@@ -4,6 +4,17 @@ Change Log
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [v3.0.0-rc1]
|
||||
|
||||
### Added
|
||||
- Refresh button to reload mailbox contents
|
||||
- Improved keyboard (tab) focus highlights
|
||||
|
||||
### Changed
|
||||
- The UI now includes the Open Sans webfont instead of relying on browser/OS
|
||||
fonts
|
||||
|
||||
|
||||
## [v3.0.0-beta3]
|
||||
|
||||
### Added
|
||||
@@ -228,6 +239,7 @@ No change from beta1.
|
||||
specific message.
|
||||
|
||||
[Unreleased]: https://github.com/inbucket/inbucket/compare/master...develop
|
||||
[v3.0.0-rc1]: https://github.com/inbucket/inbucket/compare/v3.0.0-beta3...v3.0.0-rc1
|
||||
[v3.0.0-beta3]: https://github.com/inbucket/inbucket/compare/v3.0.0-beta2...v3.0.0-beta3
|
||||
[v3.0.0-beta2]: https://github.com/inbucket/inbucket/compare/v3.0.0-beta1...v3.0.0-beta2
|
||||
[v3.0.0-beta1]: https://github.com/inbucket/inbucket/compare/v2.1.0...v3.0.0-beta1
|
||||
|
||||
62
README.md
62
README.md
@@ -1,11 +1,12 @@
|
||||
Inbucket
|
||||
=============================================================================
|
||||
[][Build Status]
|
||||
[][Docker Image]
|
||||
|
||||
Inbucket is an email testing service; it will accept messages for any email
|
||||
address and make them available via web, REST and POP3. Once compiled,
|
||||
Inbucket does not have any external dependencies (HTTP, SMTP, POP3 and storage
|
||||
are all built in).
|
||||
address and make them available via web, REST and POP3 interfaces. Once
|
||||
compiled, Inbucket does not have any external dependencies - HTTP, SMTP, POP3
|
||||
and storage are all built in.
|
||||
|
||||
A Go client for the REST API is available in
|
||||
`github.com/inbucket/inbucket/pkg/rest/client` - [Go API docs]
|
||||
@@ -14,6 +15,7 @@ Read more at the [Inbucket Website]
|
||||
|
||||

|
||||
|
||||
|
||||
## Development Status
|
||||
|
||||
Inbucket is currently production quality: it is being used for real work.
|
||||
@@ -29,15 +31,6 @@ tracks our `master` branch (releases), `latest` tracks our unstable
|
||||
`development` branch.
|
||||
|
||||
|
||||
## Homebrew Tap
|
||||
|
||||
(currently broken, being tracked in [issue
|
||||
#68](https://github.com/inbucket/inbucket/issues/68))
|
||||
|
||||
Inbucket has an OS X [Homebrew] tap available as [jhillyerd/inbucket][Homebrew Tap],
|
||||
see the `README.md` there for installation instructions.
|
||||
|
||||
|
||||
## Building from Source
|
||||
|
||||
You will need functioning [Go] and [Node.js] installations for this to work.
|
||||
@@ -45,17 +38,20 @@ You will need functioning [Go] and [Node.js] installations for this to work.
|
||||
```sh
|
||||
git clone https://github.com/inbucket/inbucket.git
|
||||
cd inbucket/ui
|
||||
npm i
|
||||
npm ci
|
||||
npm run build
|
||||
cd ..
|
||||
go build ./cmd/inbucket
|
||||
```
|
||||
|
||||
_Note:_ You may also use the included Makefile to build and test the Go binaries.
|
||||
For more information on building and development flows, check out the
|
||||
[Development Quickstart] page of our wiki.
|
||||
|
||||
### Configure and Launch
|
||||
|
||||
Inbucket reads its configuration from environment variables, but comes with
|
||||
built in sane defaults. It should work on most Unix and OS X machines as is.
|
||||
Launch the daemon:
|
||||
reasonable defaults built-in. It should work on most Unix and OS X machines as
|
||||
is. Launch the daemon:
|
||||
|
||||
```sh
|
||||
./inbucket
|
||||
@@ -65,27 +61,29 @@ By default the SMTP server will be listening on localhost port 2500 and
|
||||
the web interface will be available at [localhost:9000](http://localhost:9000/).
|
||||
|
||||
See doc/[config.md] for more information on configuring Inbucket, but you will
|
||||
likely find the [Configurator] tool easier to use.
|
||||
likely find the [Configurator] tool the easiest way to generate a configuration.
|
||||
|
||||
|
||||
## About
|
||||
|
||||
Inbucket is written in [Go]
|
||||
Inbucket is written in [Go] and [Elm].
|
||||
|
||||
Inbucket is open source software released under the MIT License. The latest
|
||||
version can be found at https://github.com/inbucket/inbucket
|
||||
|
||||
[Build Status]: https://travis-ci.org/inbucket/inbucket
|
||||
[Change Log]: https://github.com/inbucket/inbucket/blob/master/CHANGELOG.md
|
||||
[config.md]: https://github.com/inbucket/inbucket/blob/master/doc/config.md
|
||||
[Configurator]: https://www.inbucket.org/configurator/
|
||||
[CONTRIBUTING.md]: https://github.com/inbucket/inbucket/blob/develop/CONTRIBUTING.md
|
||||
[Docker Image]: https://www.inbucket.org/binaries/docker.html
|
||||
[From Source]: https://www.inbucket.org/installation/from-source.html
|
||||
[Go]: https://golang.org/
|
||||
[Go API docs]: https://godoc.org/github.com/inbucket/inbucket/pkg/rest/client
|
||||
[Homebrew]: http://brew.sh/
|
||||
[Homebrew Tap]: https://github.com/inbucket/homebrew-inbucket
|
||||
[Inbucket Website]: https://www.inbucket.org/
|
||||
[Issues List]: https://github.com/inbucket/inbucket/issues?state=open
|
||||
[Node.js]: https://nodejs.org/en/
|
||||
[Build Status]: https://travis-ci.org/inbucket/inbucket
|
||||
[Change Log]: https://github.com/inbucket/inbucket/blob/master/CHANGELOG.md
|
||||
[config.md]: https://github.com/inbucket/inbucket/blob/master/doc/config.md
|
||||
[Configurator]: https://www.inbucket.org/configurator/
|
||||
[CONTRIBUTING.md]: https://github.com/inbucket/inbucket/blob/develop/CONTRIBUTING.md
|
||||
[Development Quickstart]: https://github.com/inbucket/inbucket/wiki/Development-Quickstart
|
||||
[Docker Image]: https://www.inbucket.org/binaries/docker.html
|
||||
[Elm]: https://elm-lang.org/
|
||||
[From Source]: https://www.inbucket.org/installation/from-source.html
|
||||
[Go]: https://golang.org/
|
||||
[Go API docs]: https://pkg.go.dev/github.com/inbucket/inbucket/pkg/rest/client
|
||||
[Homebrew]: http://brew.sh/
|
||||
[Homebrew Tap]: https://github.com/inbucket/homebrew-inbucket
|
||||
[Inbucket Website]: https://www.inbucket.org/
|
||||
[Issues List]: https://github.com/inbucket/inbucket/issues?state=open
|
||||
[Node.js]: https://nodejs.org/en/
|
||||
|
||||
4
go.mod
4
go.mod
@@ -15,9 +15,9 @@ require (
|
||||
github.com/microcosm-cc/bluemonday v1.0.4
|
||||
github.com/olekukonko/tablewriter v0.0.4 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/rs/zerolog v1.19.0
|
||||
github.com/rs/zerolog v1.20.0
|
||||
github.com/stretchr/testify v1.6.1
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202
|
||||
golang.org/x/net v0.0.0-20200923182212-328152dc79b1
|
||||
golang.org/x/text v0.3.3 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect
|
||||
)
|
||||
|
||||
8
go.sum
8
go.sum
@@ -50,8 +50,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.19.0 h1:hYz4ZVdUgjXTBUmrkrw55j1nHx68LfOKIQk5IYtyScg=
|
||||
github.com/rs/zerolog v1.19.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
|
||||
github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs=
|
||||
github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
|
||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI=
|
||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
|
||||
@@ -66,8 +66,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200923182212-328152dc79b1 h1:Iu68XRPd67wN4aRGGWwwq6bZo/25jR6uu52l/j2KkUE=
|
||||
golang.org/x/net v0.0.0-20200923182212-328152dc79b1/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
||||
481
ui/package-lock.json
generated
481
ui/package-lock.json
generated
@@ -25,19 +25,19 @@
|
||||
}
|
||||
},
|
||||
"@babel/core": {
|
||||
"version": "7.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.4.tgz",
|
||||
"integrity": "sha512-5deljj5HlqRXN+5oJTY7Zs37iH3z3b++KjiKtIsJy1NrjOOVSEaJHEetLBhyu0aQOSNNZ/0IuEAan9GzRuDXHg==",
|
||||
"version": "7.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.6.tgz",
|
||||
"integrity": "sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.10.4",
|
||||
"@babel/generator": "^7.11.4",
|
||||
"@babel/generator": "^7.11.6",
|
||||
"@babel/helper-module-transforms": "^7.11.0",
|
||||
"@babel/helpers": "^7.10.4",
|
||||
"@babel/parser": "^7.11.4",
|
||||
"@babel/parser": "^7.11.5",
|
||||
"@babel/template": "^7.10.4",
|
||||
"@babel/traverse": "^7.11.0",
|
||||
"@babel/types": "^7.11.0",
|
||||
"@babel/traverse": "^7.11.5",
|
||||
"@babel/types": "^7.11.5",
|
||||
"convert-source-map": "^1.7.0",
|
||||
"debug": "^4.1.0",
|
||||
"gensync": "^1.0.0-beta.1",
|
||||
@@ -46,23 +46,15 @@
|
||||
"resolve": "^1.3.2",
|
||||
"semver": "^5.4.1",
|
||||
"source-map": "^0.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"lodash": {
|
||||
"version": "4.17.20",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/generator": {
|
||||
"version": "7.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.4.tgz",
|
||||
"integrity": "sha512-Rn26vueFx0eOoz7iifCN2UHT6rGtnkSGWSoDRIy8jZN3B91PzeSULbswfLoOWuTuAcNwpG/mxy+uCTDnZ9Mp1g==",
|
||||
"version": "7.11.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.6.tgz",
|
||||
"integrity": "sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.11.0",
|
||||
"@babel/types": "^7.11.5",
|
||||
"jsesc": "^2.5.1",
|
||||
"source-map": "^0.5.0"
|
||||
}
|
||||
@@ -133,14 +125,6 @@
|
||||
"@babel/helper-function-name": "^7.10.4",
|
||||
"@babel/types": "^7.10.5",
|
||||
"lodash": "^4.17.19"
|
||||
},
|
||||
"dependencies": {
|
||||
"lodash": {
|
||||
"version": "4.17.20",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/helper-explode-assignable-expression": {
|
||||
@@ -212,14 +196,6 @@
|
||||
"@babel/template": "^7.10.4",
|
||||
"@babel/types": "^7.11.0",
|
||||
"lodash": "^4.17.19"
|
||||
},
|
||||
"dependencies": {
|
||||
"lodash": {
|
||||
"version": "4.17.20",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/helper-optimise-call-expression": {
|
||||
@@ -244,14 +220,6 @@
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash": "^4.17.19"
|
||||
},
|
||||
"dependencies": {
|
||||
"lodash": {
|
||||
"version": "4.17.20",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/helper-remap-async-to-generator": {
|
||||
@@ -347,9 +315,9 @@
|
||||
}
|
||||
},
|
||||
"@babel/parser": {
|
||||
"version": "7.11.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.4.tgz",
|
||||
"integrity": "sha512-MggwidiH+E9j5Sh8pbrX5sJvMcsqS5o+7iB42M9/k0CD63MjYbdP4nhSh7uB5wnv2/RVzTZFTxzF/kIa5mrCqA==",
|
||||
"version": "7.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz",
|
||||
"integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/plugin-proposal-async-generator-functions": {
|
||||
@@ -909,9 +877,9 @@
|
||||
}
|
||||
},
|
||||
"@babel/preset-env": {
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.11.0.tgz",
|
||||
"integrity": "sha512-2u1/k7rG/gTh02dylX2kL3S0IJNF+J6bfDSp4DI2Ma8QN6Y9x9pmAax59fsCk6QUQG0yqH47yJWA+u1I1LccAg==",
|
||||
"version": "7.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.11.5.tgz",
|
||||
"integrity": "sha512-kXqmW1jVcnB2cdueV+fyBM8estd5mlNfaQi6lwLgRwCby4edpavgbFhiBNjmWA3JpB/yZGSISa7Srf+TwxDQoA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/compat-data": "^7.11.0",
|
||||
@@ -976,7 +944,7 @@
|
||||
"@babel/plugin-transform-unicode-escapes": "^7.10.4",
|
||||
"@babel/plugin-transform-unicode-regex": "^7.10.4",
|
||||
"@babel/preset-modules": "^0.1.3",
|
||||
"@babel/types": "^7.11.0",
|
||||
"@babel/types": "^7.11.5",
|
||||
"browserslist": "^4.12.0",
|
||||
"core-js-compat": "^3.6.2",
|
||||
"invariant": "^2.2.2",
|
||||
@@ -1018,47 +986,31 @@
|
||||
}
|
||||
},
|
||||
"@babel/traverse": {
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz",
|
||||
"integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==",
|
||||
"version": "7.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.5.tgz",
|
||||
"integrity": "sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.10.4",
|
||||
"@babel/generator": "^7.11.0",
|
||||
"@babel/generator": "^7.11.5",
|
||||
"@babel/helper-function-name": "^7.10.4",
|
||||
"@babel/helper-split-export-declaration": "^7.11.0",
|
||||
"@babel/parser": "^7.11.0",
|
||||
"@babel/types": "^7.11.0",
|
||||
"@babel/parser": "^7.11.5",
|
||||
"@babel/types": "^7.11.5",
|
||||
"debug": "^4.1.0",
|
||||
"globals": "^11.1.0",
|
||||
"lodash": "^4.17.19"
|
||||
},
|
||||
"dependencies": {
|
||||
"lodash": {
|
||||
"version": "4.17.20",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/types": {
|
||||
"version": "7.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
|
||||
"integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
|
||||
"version": "7.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz",
|
||||
"integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-validator-identifier": "^7.10.4",
|
||||
"lodash": "^4.17.19",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"lodash": {
|
||||
"version": "4.17.20",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@fortawesome/fontawesome-free": {
|
||||
@@ -1084,9 +1036,9 @@
|
||||
}
|
||||
},
|
||||
"@types/html-minifier-terser": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.0.tgz",
|
||||
"integrity": "sha512-iYCgjm1dGPRuo12+BStjd1HiVQqhlRhWDOQigNxn023HcjnhsiFz9pc6CzJj4HwDCSQca9bxTL4PxJDbkdm3PA==",
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz",
|
||||
"integrity": "sha512-giAlZwstKbmvMk1OO7WXSj4OZ0keXAcl2TQq4LWHiiPH2ByaH7WeUzng+Qej8UPxxv+8lRTuouo0iaNDBuzIBA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/json-schema": {
|
||||
@@ -1137,9 +1089,9 @@
|
||||
}
|
||||
},
|
||||
"@types/webpack": {
|
||||
"version": "4.41.21",
|
||||
"resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.21.tgz",
|
||||
"integrity": "sha512-2j9WVnNrr/8PLAB5csW44xzQSJwS26aOnICsP3pSGCEdsu6KYtfQ6QJsVUKHWRnm1bL7HziJsfh5fHqth87yKA==",
|
||||
"version": "4.41.22",
|
||||
"resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.22.tgz",
|
||||
"integrity": "sha512-JQDJK6pj8OMV9gWOnN1dcLCyU9Hzs6lux0wBO4lr1+gyEhIBR9U3FMrz12t2GPkg110XAxEAw2WHF6g7nZIbRQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/anymatch": "*",
|
||||
@@ -1762,6 +1714,16 @@
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"bindings": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
|
||||
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"file-uri-to-path": "1.0.0"
|
||||
}
|
||||
},
|
||||
"bluebird": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
|
||||
@@ -1987,15 +1949,15 @@
|
||||
}
|
||||
},
|
||||
"browserslist": {
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.0.tgz",
|
||||
"integrity": "sha512-pUsXKAF2lVwhmtpeA3LJrZ76jXuusrNyhduuQs7CDFf9foT4Y38aQOserd2lMe5DSSrjf3fx34oHwryuvxAUgQ==",
|
||||
"version": "4.14.4",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.4.tgz",
|
||||
"integrity": "sha512-7FOuawafVdEwa5Jv4nzeik/PepAjVte6HmVGHsjt2bC237jeL9QlcTBDF3PnHEvcC6uHwLGYPwZHNZMB7wWAnw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"caniuse-lite": "^1.0.30001111",
|
||||
"electron-to-chromium": "^1.3.523",
|
||||
"escalade": "^3.0.2",
|
||||
"node-releases": "^1.1.60"
|
||||
"caniuse-lite": "^1.0.30001135",
|
||||
"electron-to-chromium": "^1.3.570",
|
||||
"escalade": "^3.1.0",
|
||||
"node-releases": "^1.1.61"
|
||||
}
|
||||
},
|
||||
"buffer": {
|
||||
@@ -2075,12 +2037,6 @@
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"y18n": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
|
||||
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2111,10 +2067,16 @@
|
||||
"tslib": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"camelcase": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.0.0.tgz",
|
||||
"integrity": "sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w==",
|
||||
"dev": true
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001120",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001120.tgz",
|
||||
"integrity": "sha512-JBP68okZs1X8D7MQTY602jxMYBmXEKOFkzTBaNSkubooMPFOAv2TXWaKle7qgHpjLDhUzA/TMT0qsNleVyXGUQ==",
|
||||
"version": "1.0.30001135",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001135.tgz",
|
||||
"integrity": "sha512-ziNcheTGTHlu9g34EVoHQdIu5g4foc8EsxMGC7Xkokmvw0dqNtX8BS8RgCgFBaAiSp2IdjvBxNdh0ssib28eVQ==",
|
||||
"dev": true
|
||||
},
|
||||
"chalk": {
|
||||
@@ -2527,9 +2489,9 @@
|
||||
}
|
||||
},
|
||||
"css-loader": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-4.2.2.tgz",
|
||||
"integrity": "sha512-omVGsTkZPVwVRpckeUnLshPp12KsmMSLqYxs12+RzM9jRR5Y+Idn/tBffjXRvOE+qW7if24cuceFJqYR5FmGBg==",
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-4.3.0.tgz",
|
||||
"integrity": "sha512-rdezjCjScIrsL8BSYszgT4s476IcNKt6yX69t0pHjJVnPUTDpn4WfIpDQTN3wCJvUvfsz/mFjuGOekf3PY3NUg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"camelcase": "^6.0.0",
|
||||
@@ -2542,14 +2504,14 @@
|
||||
"postcss-modules-scope": "^2.2.0",
|
||||
"postcss-modules-values": "^3.0.0",
|
||||
"postcss-value-parser": "^4.1.0",
|
||||
"schema-utils": "^2.7.0",
|
||||
"schema-utils": "^2.7.1",
|
||||
"semver": "^7.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "6.12.4",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz",
|
||||
"integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==",
|
||||
"version": "6.12.5",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz",
|
||||
"integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
@@ -2564,50 +2526,21 @@
|
||||
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
|
||||
"dev": true
|
||||
},
|
||||
"big.js": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
|
||||
"integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
|
||||
"dev": true
|
||||
},
|
||||
"camelcase": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.0.0.tgz",
|
||||
"integrity": "sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w==",
|
||||
"dev": true
|
||||
},
|
||||
"emojis-list": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
|
||||
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
|
||||
"dev": true
|
||||
},
|
||||
"fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"dev": true
|
||||
},
|
||||
"loader-utils": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
|
||||
"integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"big.js": "^5.2.2",
|
||||
"emojis-list": "^3.0.0",
|
||||
"json5": "^2.1.2"
|
||||
}
|
||||
},
|
||||
"schema-utils": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
|
||||
"integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",
|
||||
"integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/json-schema": "^7.0.4",
|
||||
"ajv": "^6.12.2",
|
||||
"ajv-keywords": "^3.4.1"
|
||||
"@types/json-schema": "^7.0.5",
|
||||
"ajv": "^6.12.4",
|
||||
"ajv-keywords": "^3.5.2"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
@@ -2864,9 +2797,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"domelementtype": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz",
|
||||
"integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.2.tgz",
|
||||
"integrity": "sha512-wFwTwCVebUrMgGeAwRL/NhZtHAUyT9n9yg4IMDwf10+6iCMxSkVq9MGCVEH+QZWo1nNidy8kNvwmv4zWHDTqvA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@@ -2931,9 +2864,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.555",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.555.tgz",
|
||||
"integrity": "sha512-/55x3nF2feXFZ5tdGUOr00TxnUjUgdxhrn+eCJ1FAcoAt+cKQTjQkUC5XF4frMWE1R5sjHk+JueuBalimfe5Pg==",
|
||||
"version": "1.3.571",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.571.tgz",
|
||||
"integrity": "sha512-UYEQ2Gtc50kqmyOmOVtj6Oqi38lm5yRJY3pLuWt6UIot0No1L09uu6Ja6/1XKwmz/p0eJFZTUZi+khd1PV1hHA==",
|
||||
"dev": true
|
||||
},
|
||||
"elliptic": {
|
||||
@@ -3019,9 +2952,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"emojis-list": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
|
||||
"integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
|
||||
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
|
||||
"dev": true
|
||||
},
|
||||
"encodeurl": {
|
||||
@@ -3131,9 +3064,9 @@
|
||||
}
|
||||
},
|
||||
"escalade": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.0.2.tgz",
|
||||
"integrity": "sha512-gPYAU37hYCUhW5euPeR+Y74F7BL+IBsV93j5cvGriSaD1aG6MGsqsV1yamRdrWrb2j3aiZvb0X+UBOWpx3JWtQ==",
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.0.tgz",
|
||||
"integrity": "sha512-mAk+hPSO8fLDkhV7V0dXazH5pDc6MrjBTPyD3VeKzxnVFjH1MIxbCdqGZB9O8+EwWakZs3ZCbDS4IpRt79V1ig==",
|
||||
"dev": true
|
||||
},
|
||||
"escape-html": {
|
||||
@@ -3159,12 +3092,20 @@
|
||||
}
|
||||
},
|
||||
"esrecurse": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
|
||||
"integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
|
||||
"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"estraverse": "^4.1.0"
|
||||
"estraverse": "^5.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"estraverse": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
|
||||
"integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"estraverse": {
|
||||
@@ -3174,9 +3115,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"esutils": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
|
||||
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
|
||||
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
|
||||
"dev": true
|
||||
},
|
||||
"etag": {
|
||||
@@ -3465,19 +3406,19 @@
|
||||
"dev": true
|
||||
},
|
||||
"file-loader": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.0.0.tgz",
|
||||
"integrity": "sha512-/aMOAYEFXDdjG0wytpTL5YQLfZnnTmLNjn+AIrJ/6HVnTfDqLsVKUUwkDf4I4kgex36BvjuXEn/TX9B/1ESyqQ==",
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.1.0.tgz",
|
||||
"integrity": "sha512-26qPdHyTsArQ6gU4P1HJbAbnFTyT2r0pG7czh1GFAd9TZbj0n94wWbupgixZH/ET/meqi2/5+F7DhW4OAXD+Lg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loader-utils": "^2.0.0",
|
||||
"schema-utils": "^2.6.5"
|
||||
"schema-utils": "^2.7.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "6.12.4",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz",
|
||||
"integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==",
|
||||
"version": "6.12.5",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz",
|
||||
"integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
@@ -3492,48 +3433,32 @@
|
||||
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
|
||||
"dev": true
|
||||
},
|
||||
"big.js": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
|
||||
"integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
|
||||
"dev": true
|
||||
},
|
||||
"emojis-list": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
|
||||
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
|
||||
"dev": true
|
||||
},
|
||||
"fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"dev": true
|
||||
},
|
||||
"loader-utils": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
|
||||
"integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"big.js": "^5.2.2",
|
||||
"emojis-list": "^3.0.0",
|
||||
"json5": "^2.1.2"
|
||||
}
|
||||
},
|
||||
"schema-utils": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
|
||||
"integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",
|
||||
"integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/json-schema": "^7.0.4",
|
||||
"ajv": "^6.12.2",
|
||||
"ajv-keywords": "^3.4.1"
|
||||
"@types/json-schema": "^7.0.5",
|
||||
"ajv": "^6.12.4",
|
||||
"ajv-keywords": "^3.5.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"file-uri-to-path": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
||||
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"fill-range": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
|
||||
@@ -4007,9 +3932,9 @@
|
||||
}
|
||||
},
|
||||
"html-webpack-plugin": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.4.1.tgz",
|
||||
"integrity": "sha512-nEtdEIsIGXdXGG7MjTTZlmhqhpHU9pJFc1OYxcP36c5/ZKP6b0BJMww2QTvJGQYA9aMxUnjDujpZdYcVOXiBCQ==",
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.5.0.tgz",
|
||||
"integrity": "sha512-MouoXEYSjTzCrjIxWwg8gxL5fE2X2WZJLmBYXlaJhQUH5K/b5OrqmV7T4dB7iu0xkmJ6JlUuV6fFVtnqbPopZw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/html-minifier-terser": "^5.0.0",
|
||||
@@ -4023,6 +3948,26 @@
|
||||
"util.promisify": "1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"json5": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
||||
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"loader-utils": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
|
||||
"integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"big.js": "^5.2.2",
|
||||
"emojis-list": "^3.0.0",
|
||||
"json5": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"tapable": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
|
||||
@@ -4284,9 +4229,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"is-callable": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz",
|
||||
"integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==",
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz",
|
||||
"integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==",
|
||||
"dev": true
|
||||
},
|
||||
"is-data-descriptor": {
|
||||
@@ -4535,39 +4480,14 @@
|
||||
"dev": true
|
||||
},
|
||||
"loader-utils": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz",
|
||||
"integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
|
||||
"integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"big.js": "^5.2.2",
|
||||
"emojis-list": "^2.0.0",
|
||||
"json5": "^1.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"big.js": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
|
||||
"integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
|
||||
"dev": true
|
||||
},
|
||||
"json5": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
||||
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
||||
"emojis-list": "^3.0.0",
|
||||
"json5": "^2.1.2"
|
||||
}
|
||||
},
|
||||
"locate-path": {
|
||||
@@ -4862,6 +4782,13 @@
|
||||
"integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=",
|
||||
"dev": true
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.14.1",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz",
|
||||
"integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"nanomatch": {
|
||||
"version": "1.2.13",
|
||||
"resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
|
||||
@@ -4975,9 +4902,9 @@
|
||||
}
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "1.1.60",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.60.tgz",
|
||||
"integrity": "sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA==",
|
||||
"version": "1.1.61",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.61.tgz",
|
||||
"integrity": "sha512-DD5vebQLg8jLCOzwupn954fbIiZht05DAZs0k2u8NStSe6h9XdsuIQL8hSRKYiU8WUQRznmSDrKGbv3ObOmC7g==",
|
||||
"dev": true
|
||||
},
|
||||
"normalize-path": {
|
||||
@@ -5192,6 +5119,11 @@
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"opensans-npm-webfont": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/opensans-npm-webfont/-/opensans-npm-webfont-1.0.0.tgz",
|
||||
"integrity": "sha512-2ehgrX+NpoxLOil30tYGr0cDsDXSJn9gon6PfM1Ki0CxZF6ui9Mi6Dm5DGglKyK2QiX0gMb6Ch7VmRHwfc4M6Q=="
|
||||
},
|
||||
"opn": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz",
|
||||
@@ -5465,9 +5397,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"postcss": {
|
||||
"version": "7.0.32",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz",
|
||||
"integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==",
|
||||
"version": "7.0.34",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.34.tgz",
|
||||
"integrity": "sha512-H/7V2VeNScX9KE83GDrDZNiGT1m2H+UTnlinIzhjlLX9hfMUn1mHNnGeX81a1c8JSBdBvqk7c2ZOG6ZPn5itGw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^2.4.2",
|
||||
@@ -5534,14 +5466,15 @@
|
||||
}
|
||||
},
|
||||
"postcss-selector-parser": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz",
|
||||
"integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==",
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.3.tgz",
|
||||
"integrity": "sha512-0ClFaY4X1ra21LRqbW6y3rUbWcxnSVkDFG57R7Nxus9J9myPFlv+jYDMohzpkBx0RrjjiqjtycpchQ+PLGmZ9w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cssesc": "^3.0.0",
|
||||
"indexes-of": "^1.0.1",
|
||||
"uniq": "^1.0.1"
|
||||
"uniq": "^1.0.1",
|
||||
"util-deprecate": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"postcss-value-parser": {
|
||||
@@ -5750,9 +5683,9 @@
|
||||
}
|
||||
},
|
||||
"regenerate": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz",
|
||||
"integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz",
|
||||
"integrity": "sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A==",
|
||||
"dev": true
|
||||
},
|
||||
"regenerate-unicode-properties": {
|
||||
@@ -5859,9 +5792,9 @@
|
||||
}
|
||||
},
|
||||
"regexpu-core": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz",
|
||||
"integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==",
|
||||
"version": "4.7.1",
|
||||
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz",
|
||||
"integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"regenerate": "^1.4.0",
|
||||
@@ -5945,9 +5878,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.12.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz",
|
||||
"integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==",
|
||||
"version": "1.17.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
|
||||
"integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"path-parse": "^1.0.6"
|
||||
@@ -7342,7 +7275,11 @@
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
|
||||
"integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"bindings": "^1.5.0",
|
||||
"nan": "^2.12.1"
|
||||
}
|
||||
},
|
||||
"glob-parent": {
|
||||
"version": "3.1.0",
|
||||
@@ -7401,9 +7338,9 @@
|
||||
}
|
||||
},
|
||||
"webpack": {
|
||||
"version": "4.44.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.44.1.tgz",
|
||||
"integrity": "sha512-4UOGAohv/VGUNQJstzEywwNxqX417FnjZgZJpJQegddzPmTvph37eBIRbRTfdySXzVtJXLJfbMN3mMYhM6GdmQ==",
|
||||
"version": "4.44.2",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.44.2.tgz",
|
||||
"integrity": "sha512-6KJVGlCxYdISyurpQ0IPTklv+DULv05rs2hseIXer6D7KrUicRDLFb4IUM1S6LUAKypPM/nSiVSuv8jHu1m3/Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@webassemblyjs/ast": "1.9.0",
|
||||
@@ -7432,9 +7369,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "6.12.4",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz",
|
||||
"integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==",
|
||||
"version": "6.12.5",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz",
|
||||
"integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
@@ -7455,6 +7392,26 @@
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"dev": true
|
||||
},
|
||||
"json5": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
||||
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"loader-utils": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
|
||||
"integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"big.js": "^5.2.2",
|
||||
"emojis-list": "^3.0.0",
|
||||
"json5": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"tapable": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
|
||||
@@ -7785,7 +7742,11 @@
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
|
||||
"integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"bindings": "^1.5.0",
|
||||
"nan": "^2.12.1"
|
||||
}
|
||||
},
|
||||
"get-caller-file": {
|
||||
"version": "2.0.5",
|
||||
@@ -8029,6 +7990,12 @@
|
||||
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
|
||||
"dev": true
|
||||
},
|
||||
"y18n": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
|
||||
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||
|
||||
@@ -4,26 +4,29 @@
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"clean": "rm -rf dist elm-stuff",
|
||||
"build": "webpack --mode production",
|
||||
"watch": "webpack --mode development --watch",
|
||||
"dev": "webpack-dev-server --mode development --host 0.0.0.0 --port 3000 --hot",
|
||||
"dev": "webpack-dev-server --mode development --port 3000 --hot --watch",
|
||||
"errors": "webpack --mode development --display-error-details"
|
||||
},
|
||||
"dependencies": {},
|
||||
"dependencies": {
|
||||
"opensans-npm-webfont": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.11.4",
|
||||
"@babel/preset-env": "^7.11.0",
|
||||
"@babel/core": "^7.11.6",
|
||||
"@babel/preset-env": "^7.11.5",
|
||||
"@fortawesome/fontawesome-free": "^5.14.0",
|
||||
"@webcomponents/webcomponentsjs": "^2.4.4",
|
||||
"babel-loader": "^8.1.0",
|
||||
"css-loader": "^4.2.2",
|
||||
"css-loader": "^4.3.0",
|
||||
"elm-hot-webpack-loader": "^1.1.7",
|
||||
"elm-webpack-loader": "^7.0.1",
|
||||
"file-loader": "^6.0.0",
|
||||
"html-webpack-plugin": "^4.4.1",
|
||||
"file-loader": "^6.1.0",
|
||||
"html-webpack-plugin": "^4.5.0",
|
||||
"node-elm-compiler": "^5.0.5",
|
||||
"style-loader": "^1.2.1",
|
||||
"webpack": "^4.44.1",
|
||||
"webpack": "^4.44.2",
|
||||
"webpack-cli": "^3.3.12",
|
||||
"webpack-dev-server": "^3.11.0"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
module Api exposing
|
||||
( deleteMessage
|
||||
( DataResult
|
||||
, HttpResult
|
||||
, deleteMessage
|
||||
, getGreeting
|
||||
, getHeaderList
|
||||
, getMessage
|
||||
|
||||
370
ui/src/Effect.elm
Normal file
370
ui/src/Effect.elm
Normal file
@@ -0,0 +1,370 @@
|
||||
module Effect exposing
|
||||
( Effect
|
||||
, addRecent
|
||||
, append
|
||||
, batch
|
||||
, clearFlash
|
||||
, deleteMessage
|
||||
, disableRouting
|
||||
, enableRouting
|
||||
, focusModal
|
||||
, focusModalResult
|
||||
, getGreeting
|
||||
, getHeaderList
|
||||
, getMessage
|
||||
, getServerConfig
|
||||
, getServerMetrics
|
||||
, map
|
||||
, markMessageSeen
|
||||
, navigateRoute
|
||||
, none
|
||||
, perform
|
||||
, posixTime
|
||||
, purgeMailbox
|
||||
, schedule
|
||||
, showFlash
|
||||
, updateRoute
|
||||
)
|
||||
|
||||
import Api exposing (DataResult, HttpResult)
|
||||
import Browser.Navigation as Nav
|
||||
import Data.Message exposing (Message)
|
||||
import Data.MessageHeader exposing (MessageHeader)
|
||||
import Data.Metrics exposing (Metrics)
|
||||
import Data.ServerConfig exposing (ServerConfig)
|
||||
import Data.Session as Session exposing (Session)
|
||||
import Modal
|
||||
import Route exposing (Route)
|
||||
import Task
|
||||
import Time
|
||||
import Timer exposing (Timer)
|
||||
|
||||
|
||||
type Effect msg
|
||||
= None
|
||||
| ApiEffect (ApiEffect msg)
|
||||
| Batch (List (Effect msg))
|
||||
| ModalFocus (Modal.Msg -> msg)
|
||||
| PosixTime (Time.Posix -> msg)
|
||||
| RouteNavigate Bool Route
|
||||
| RouteUpdate Route
|
||||
| ScheduleTimer (Timer -> msg) Timer Float
|
||||
| SessionEffect SessionEffect
|
||||
|
||||
|
||||
type ApiEffect msg
|
||||
= DeleteMessage (HttpResult msg) String String
|
||||
| GetGreeting (DataResult msg String)
|
||||
| GetServerConfig (DataResult msg ServerConfig)
|
||||
| GetServerMetrics (DataResult msg Metrics)
|
||||
| GetHeaderList (DataResult msg (List MessageHeader)) String
|
||||
| GetMessage (DataResult msg Message) String String
|
||||
| MarkMessageSeen (HttpResult msg) String String
|
||||
| PurgeMailbox (HttpResult msg) String
|
||||
|
||||
|
||||
type SessionEffect
|
||||
= FlashClear
|
||||
| FlashShow Session.Flash
|
||||
| ModalFocusResult Modal.Msg
|
||||
| RecentAdd String
|
||||
| RoutingDisable
|
||||
| RoutingEnable
|
||||
|
||||
|
||||
{-| Appends a new effect to a model/effect tuple.
|
||||
-}
|
||||
append : Effect msg -> ( a, Effect msg ) -> ( a, Effect msg )
|
||||
append e ( model, effect ) =
|
||||
( model, batch [ effect, e ] )
|
||||
|
||||
|
||||
{-| Packs a List of Effects into a single Effect
|
||||
-}
|
||||
batch : List (Effect msg) -> Effect msg
|
||||
batch effects =
|
||||
Batch effects
|
||||
|
||||
|
||||
{-| Transform message types produced by an effect.
|
||||
-}
|
||||
map : (a -> b) -> Effect a -> Effect b
|
||||
map f effect =
|
||||
case effect of
|
||||
None ->
|
||||
None
|
||||
|
||||
Batch effects ->
|
||||
Batch <| List.map (map f) effects
|
||||
|
||||
ModalFocus toMsg ->
|
||||
ModalFocus <| toMsg >> f
|
||||
|
||||
PosixTime toMsg ->
|
||||
PosixTime <| toMsg >> f
|
||||
|
||||
ScheduleTimer toMsg timer millis ->
|
||||
ScheduleTimer (toMsg >> f) timer millis
|
||||
|
||||
RouteNavigate pushHistory route ->
|
||||
RouteNavigate pushHistory route
|
||||
|
||||
RouteUpdate route ->
|
||||
RouteUpdate route
|
||||
|
||||
ApiEffect apiEffect ->
|
||||
ApiEffect <| mapApi f apiEffect
|
||||
|
||||
SessionEffect sessionEffect ->
|
||||
SessionEffect sessionEffect
|
||||
|
||||
|
||||
mapApi : (a -> b) -> ApiEffect a -> ApiEffect b
|
||||
mapApi f effect =
|
||||
case effect of
|
||||
DeleteMessage result mailbox id ->
|
||||
DeleteMessage (result >> f) mailbox id
|
||||
|
||||
GetGreeting result ->
|
||||
GetGreeting (result >> f)
|
||||
|
||||
GetServerConfig result ->
|
||||
GetServerConfig (result >> f)
|
||||
|
||||
GetServerMetrics result ->
|
||||
GetServerMetrics (result >> f)
|
||||
|
||||
GetHeaderList result mailbox ->
|
||||
GetHeaderList (result >> f) mailbox
|
||||
|
||||
GetMessage result mailbox id ->
|
||||
GetMessage (result >> f) mailbox id
|
||||
|
||||
MarkMessageSeen result mailbox id ->
|
||||
MarkMessageSeen (result >> f) mailbox id
|
||||
|
||||
PurgeMailbox result mailbox ->
|
||||
PurgeMailbox (result >> f) mailbox
|
||||
|
||||
|
||||
{-| Applies an effect by updating the session and/or producing a Cmd.
|
||||
-}
|
||||
perform : ( Session, Effect msg ) -> ( Session, Cmd msg )
|
||||
perform ( session, effect ) =
|
||||
case effect of
|
||||
None ->
|
||||
( session, Cmd.none )
|
||||
|
||||
Batch effects ->
|
||||
List.foldl batchPerform ( session, [] ) effects
|
||||
|> Tuple.mapSecond Cmd.batch
|
||||
|
||||
ModalFocus toMsg ->
|
||||
( session, Modal.resetFocusCmd toMsg )
|
||||
|
||||
PosixTime toMsg ->
|
||||
( session, Task.perform toMsg Time.now )
|
||||
|
||||
ScheduleTimer toMsg timer millis ->
|
||||
( session, Timer.schedule toMsg timer millis )
|
||||
|
||||
RouteNavigate pushHistory route ->
|
||||
let
|
||||
url =
|
||||
session.router.toPath route
|
||||
in
|
||||
( Session.enableRouting session
|
||||
, if pushHistory then
|
||||
Nav.pushUrl session.key url
|
||||
|
||||
else
|
||||
Nav.replaceUrl session.key url
|
||||
)
|
||||
|
||||
RouteUpdate route ->
|
||||
( Session.disableRouting session
|
||||
, session.router.toPath route
|
||||
|> Nav.replaceUrl session.key
|
||||
)
|
||||
|
||||
ApiEffect apiEffect ->
|
||||
performApi ( session, apiEffect )
|
||||
|
||||
SessionEffect sessionEffect ->
|
||||
performSession ( session, sessionEffect )
|
||||
|
||||
|
||||
performApi : ( Session, ApiEffect msg ) -> ( Session, Cmd msg )
|
||||
performApi ( session, effect ) =
|
||||
case effect of
|
||||
DeleteMessage toMsg mailbox id ->
|
||||
( session, Api.deleteMessage session toMsg mailbox id )
|
||||
|
||||
GetGreeting toMsg ->
|
||||
( session, Api.getGreeting session toMsg )
|
||||
|
||||
GetServerConfig toMsg ->
|
||||
( session, Api.getServerConfig session toMsg )
|
||||
|
||||
GetServerMetrics toMsg ->
|
||||
( session, Api.getServerMetrics session toMsg )
|
||||
|
||||
GetHeaderList toMsg mailbox ->
|
||||
( session, Api.getHeaderList session toMsg mailbox )
|
||||
|
||||
GetMessage toMsg mailbox id ->
|
||||
( session, Api.getMessage session toMsg mailbox id )
|
||||
|
||||
MarkMessageSeen toMsg mailbox id ->
|
||||
( session, Api.markMessageSeen session toMsg mailbox id )
|
||||
|
||||
PurgeMailbox toMsg mailbox ->
|
||||
( session, Api.purgeMailbox session toMsg mailbox )
|
||||
|
||||
|
||||
performSession : ( Session, SessionEffect ) -> ( Session, Cmd msg )
|
||||
performSession ( session, effect ) =
|
||||
case effect of
|
||||
RecentAdd mailbox ->
|
||||
( Session.addRecent mailbox session, Cmd.none )
|
||||
|
||||
FlashClear ->
|
||||
( Session.clearFlash session, Cmd.none )
|
||||
|
||||
FlashShow flash ->
|
||||
( Session.showFlash flash session, Cmd.none )
|
||||
|
||||
ModalFocusResult result ->
|
||||
( Modal.updateSession result session, Cmd.none )
|
||||
|
||||
RoutingDisable ->
|
||||
( Session.disableRouting session, Cmd.none )
|
||||
|
||||
RoutingEnable ->
|
||||
( Session.enableRouting session, Cmd.none )
|
||||
|
||||
|
||||
|
||||
-- EFFECT CONSTRUCTORS
|
||||
|
||||
|
||||
none : Effect msg
|
||||
none =
|
||||
None
|
||||
|
||||
|
||||
{-| Adds specified mailbox to the recently viewed list
|
||||
-}
|
||||
addRecent : String -> Effect msg
|
||||
addRecent mailbox =
|
||||
SessionEffect (RecentAdd mailbox)
|
||||
|
||||
|
||||
disableRouting : Effect msg
|
||||
disableRouting =
|
||||
SessionEffect RoutingDisable
|
||||
|
||||
|
||||
enableRouting : Effect msg
|
||||
enableRouting =
|
||||
SessionEffect RoutingEnable
|
||||
|
||||
|
||||
clearFlash : Effect msg
|
||||
clearFlash =
|
||||
SessionEffect FlashClear
|
||||
|
||||
|
||||
showFlash : Session.Flash -> Effect msg
|
||||
showFlash flash =
|
||||
SessionEffect (FlashShow flash)
|
||||
|
||||
|
||||
{-| Locks focus to the `modal-dialog` dom ID.
|
||||
-}
|
||||
focusModal : (Modal.Msg -> msg) -> Effect msg
|
||||
focusModal toMsg =
|
||||
ModalFocus toMsg
|
||||
|
||||
|
||||
focusModalResult : Modal.Msg -> Effect msg
|
||||
focusModalResult msg =
|
||||
SessionEffect (ModalFocusResult msg)
|
||||
|
||||
|
||||
deleteMessage : HttpResult msg -> String -> String -> Effect msg
|
||||
deleteMessage toMsg mailboxName id =
|
||||
ApiEffect (DeleteMessage toMsg mailboxName id)
|
||||
|
||||
|
||||
getGreeting : DataResult msg String -> Effect msg
|
||||
getGreeting toMsg =
|
||||
ApiEffect (GetGreeting toMsg)
|
||||
|
||||
|
||||
getHeaderList : DataResult msg (List MessageHeader) -> String -> Effect msg
|
||||
getHeaderList toMsg mailboxName =
|
||||
ApiEffect (GetHeaderList toMsg mailboxName)
|
||||
|
||||
|
||||
getServerConfig : DataResult msg ServerConfig -> Effect msg
|
||||
getServerConfig toMsg =
|
||||
ApiEffect (GetServerConfig toMsg)
|
||||
|
||||
|
||||
getServerMetrics : DataResult msg Metrics -> Effect msg
|
||||
getServerMetrics toMsg =
|
||||
ApiEffect (GetServerMetrics toMsg)
|
||||
|
||||
|
||||
getMessage : DataResult msg Message -> String -> String -> Effect msg
|
||||
getMessage toMsg mailboxName id =
|
||||
ApiEffect (GetMessage toMsg mailboxName id)
|
||||
|
||||
|
||||
markMessageSeen : HttpResult msg -> String -> String -> Effect msg
|
||||
markMessageSeen toMsg mailboxName id =
|
||||
ApiEffect (MarkMessageSeen toMsg mailboxName id)
|
||||
|
||||
|
||||
posixTime : (Time.Posix -> msg) -> Effect msg
|
||||
posixTime toMsg =
|
||||
PosixTime toMsg
|
||||
|
||||
|
||||
purgeMailbox : HttpResult msg -> String -> Effect msg
|
||||
purgeMailbox toMsg mailboxName =
|
||||
ApiEffect (PurgeMailbox toMsg mailboxName)
|
||||
|
||||
|
||||
{-| Schedules a Timer to fire after the specified delay.
|
||||
-}
|
||||
schedule : (Timer -> msg) -> Timer -> Float -> Effect msg
|
||||
schedule toMsg timer millis =
|
||||
ScheduleTimer toMsg timer millis
|
||||
|
||||
|
||||
{-| Updates the browsers displayed URL to the specified route, and triggers the route to be
|
||||
handled by the frontend.
|
||||
-}
|
||||
navigateRoute : Bool -> Route -> Effect msg
|
||||
navigateRoute pushHistory route =
|
||||
RouteNavigate pushHistory route
|
||||
|
||||
|
||||
{-| Updates the browsers displayed URL to the specified route. Does not trigger our own route
|
||||
handling.
|
||||
-}
|
||||
updateRoute : Route -> Effect msg
|
||||
updateRoute route =
|
||||
RouteUpdate route
|
||||
|
||||
|
||||
|
||||
-- UTILITY
|
||||
|
||||
|
||||
batchPerform : Effect msg -> ( Session, List (Cmd msg) ) -> ( Session, List (Cmd msg) )
|
||||
batchPerform effect ( session, cmds ) =
|
||||
perform ( session, effect )
|
||||
|> Tuple.mapSecond (\cmd -> cmd :: cmds)
|
||||
@@ -1,7 +1,7 @@
|
||||
module Layout exposing (Model, Msg, Page(..), frame, init, reset, update)
|
||||
|
||||
import Browser.Navigation as Nav
|
||||
import Data.Session as Session exposing (Session)
|
||||
import Effect exposing (Effect)
|
||||
import Html
|
||||
exposing
|
||||
( Attribute
|
||||
@@ -39,7 +39,7 @@ import Html.Attributes
|
||||
)
|
||||
import Html.Events as Events
|
||||
import Modal
|
||||
import Route exposing (Route)
|
||||
import Route
|
||||
import Timer exposing (Timer)
|
||||
|
||||
|
||||
@@ -96,46 +96,31 @@ type Msg
|
||||
| RecentMenuToggled
|
||||
|
||||
|
||||
update : Msg -> Model msg -> Session -> ( Model msg, Session, Cmd msg )
|
||||
update msg model session =
|
||||
update : Msg -> Model msg -> ( Model msg, Effect msg )
|
||||
update msg model =
|
||||
case msg of
|
||||
ClearFlash ->
|
||||
( model
|
||||
, Session.clearFlash session
|
||||
, Cmd.none
|
||||
)
|
||||
( model, Effect.clearFlash )
|
||||
|
||||
MainMenuToggled ->
|
||||
( { model | mainMenuVisible = not model.mainMenuVisible }
|
||||
, session
|
||||
, Cmd.none
|
||||
)
|
||||
( { model | mainMenuVisible = not model.mainMenuVisible }, Effect.none )
|
||||
|
||||
ModalFocused message ->
|
||||
( model
|
||||
, Modal.updateSession message session
|
||||
, Cmd.none
|
||||
)
|
||||
( model, Effect.focusModalResult message )
|
||||
|
||||
ModalUnfocused ->
|
||||
( model, session, Modal.resetFocusCmd (ModalFocused >> model.mapMsg) )
|
||||
( model, Effect.focusModal (ModalFocused >> model.mapMsg) )
|
||||
|
||||
OnMailboxNameInput name ->
|
||||
( { model | mailboxName = name }
|
||||
, session
|
||||
, Cmd.none
|
||||
)
|
||||
( { model | mailboxName = name }, Effect.none )
|
||||
|
||||
OpenMailbox ->
|
||||
if model.mailboxName == "" then
|
||||
( model, session, Cmd.none )
|
||||
( model, Effect.none )
|
||||
|
||||
else
|
||||
( model
|
||||
, session
|
||||
, Route.Mailbox model.mailboxName
|
||||
|> session.router.toPath
|
||||
|> Nav.pushUrl session.key
|
||||
, Effect.navigateRoute True (Route.Mailbox model.mailboxName)
|
||||
)
|
||||
|
||||
RecentMenuMouseOver ->
|
||||
@@ -143,20 +128,19 @@ update msg model session =
|
||||
| recentMenuVisible = True
|
||||
, recentMenuTimer = Timer.cancel model.recentMenuTimer
|
||||
}
|
||||
, session
|
||||
, Cmd.none
|
||||
, Effect.none
|
||||
)
|
||||
|
||||
RecentMenuMouseOut ->
|
||||
let
|
||||
-- Keep the recent menu open for a moment even if the mouse leaves it.
|
||||
newTimer =
|
||||
Timer.replace model.recentMenuTimer
|
||||
in
|
||||
( { model
|
||||
| recentMenuTimer = newTimer
|
||||
}
|
||||
, session
|
||||
, Timer.schedule (RecentMenuTimeout >> model.mapMsg) newTimer 400
|
||||
, Effect.schedule (RecentMenuTimeout >> model.mapMsg) newTimer 400
|
||||
)
|
||||
|
||||
RecentMenuTimeout timer ->
|
||||
@@ -165,18 +149,16 @@ update msg model session =
|
||||
| recentMenuVisible = False
|
||||
, recentMenuTimer = Timer.cancel timer
|
||||
}
|
||||
, session
|
||||
, Cmd.none
|
||||
, Effect.none
|
||||
)
|
||||
|
||||
else
|
||||
-- Timer was no longer valid.
|
||||
( model, session, Cmd.none )
|
||||
( model, Effect.none )
|
||||
|
||||
RecentMenuToggled ->
|
||||
( { model | recentMenuVisible = not model.recentMenuVisible }
|
||||
, session
|
||||
, Cmd.none
|
||||
, Effect.none
|
||||
)
|
||||
|
||||
|
||||
|
||||
108
ui/src/Main.elm
108
ui/src/Main.elm
@@ -4,6 +4,7 @@ import Browser exposing (Document, UrlRequest)
|
||||
import Browser.Navigation as Nav
|
||||
import Data.AppConfig as AppConfig exposing (AppConfig)
|
||||
import Data.Session as Session exposing (Session)
|
||||
import Effect exposing (Effect)
|
||||
import Html exposing (Html)
|
||||
import Json.Decode as D exposing (Value)
|
||||
import Layout
|
||||
@@ -58,6 +59,8 @@ init configValue location key =
|
||||
Session.initError key location (D.errorToString error)
|
||||
|
||||
( subModel, _ ) =
|
||||
-- Home.init effect is discarded because this subModel will be immediately replaced
|
||||
-- when we change routes to the specified location.
|
||||
Home.init session
|
||||
|
||||
initModel =
|
||||
@@ -67,11 +70,9 @@ init configValue location key =
|
||||
|
||||
route =
|
||||
session.router.fromUrl location
|
||||
|
||||
( model, cmd ) =
|
||||
changeRouteTo route initModel
|
||||
in
|
||||
( model, Cmd.batch [ cmd, Task.perform TimeZoneLoaded Time.here ] )
|
||||
changeRouteTo route initModel
|
||||
|> Tuple.mapSecond (\cmd -> Cmd.batch [ cmd, Task.perform TimeZoneLoaded Time.here ])
|
||||
|
||||
|
||||
type Msg
|
||||
@@ -198,20 +199,18 @@ updateMain msg model session =
|
||||
|
||||
LayoutMsg subMsg ->
|
||||
let
|
||||
( layout, newSession, cmd ) =
|
||||
Layout.update subMsg model.layout session
|
||||
( layout, effect ) =
|
||||
Layout.update subMsg model.layout
|
||||
in
|
||||
( updateSession { model | layout = layout } newSession
|
||||
, cmd
|
||||
)
|
||||
( { model | layout = layout }, effect ) |> performEffects
|
||||
|
||||
_ ->
|
||||
updatePage msg model
|
||||
updatePage msg model |> performEffects
|
||||
|
||||
|
||||
{-| Delegate incoming messages to their respective sub-pages.
|
||||
-}
|
||||
updatePage : Msg -> Model -> ( Model, Cmd Msg )
|
||||
updatePage : Msg -> Model -> ( Model, Effect Msg )
|
||||
updatePage msg model =
|
||||
case ( msg, model.page ) of
|
||||
( HomeMsg subMsg, Home subModel ) ->
|
||||
@@ -232,61 +231,70 @@ updatePage msg model =
|
||||
|
||||
( _, _ ) ->
|
||||
-- Disregard messages destined for the wrong page.
|
||||
( model, Cmd.none )
|
||||
( model, Effect.none )
|
||||
|
||||
|
||||
changeRouteTo : Route -> Model -> ( Model, Cmd Msg )
|
||||
changeRouteTo route model =
|
||||
let
|
||||
session =
|
||||
getSession model |> Session.clearFlash
|
||||
Session.clearFlash (getSession model)
|
||||
|
||||
newModel =
|
||||
{ model | layout = Layout.reset model.layout }
|
||||
in
|
||||
case route of
|
||||
Route.Home ->
|
||||
Home.init session
|
||||
|> updateWith Home HomeMsg newModel
|
||||
performEffects <|
|
||||
case route of
|
||||
Route.Home ->
|
||||
Home.init session
|
||||
|> updateWith Home HomeMsg newModel
|
||||
|
||||
Route.Mailbox name ->
|
||||
Mailbox.init session name Nothing
|
||||
|> updateWith Mailbox MailboxMsg newModel
|
||||
Route.Mailbox name ->
|
||||
Mailbox.init session name Nothing
|
||||
|> updateWith Mailbox MailboxMsg newModel
|
||||
|
||||
Route.Message mailbox id ->
|
||||
Mailbox.init session mailbox (Just id)
|
||||
|> updateWith Mailbox MailboxMsg newModel
|
||||
Route.Message mailbox id ->
|
||||
Mailbox.init session mailbox (Just id)
|
||||
|> updateWith Mailbox MailboxMsg newModel
|
||||
|
||||
Route.Monitor ->
|
||||
if session.config.monitorVisible then
|
||||
Monitor.init session
|
||||
|> updateWith Monitor MonitorMsg newModel
|
||||
Route.Monitor ->
|
||||
if session.config.monitorVisible then
|
||||
Monitor.init session
|
||||
|> updateWith Monitor MonitorMsg newModel
|
||||
|
||||
else
|
||||
else
|
||||
let
|
||||
flash =
|
||||
{ title = "Disabled route requested"
|
||||
, table = [ ( "Error", "Monitor disabled by configuration." ) ]
|
||||
}
|
||||
in
|
||||
( applyToModelSession (Session.showFlash flash) newModel
|
||||
, Effect.none
|
||||
)
|
||||
|
||||
Route.Status ->
|
||||
Status.init session
|
||||
|> updateWith Status StatusMsg newModel
|
||||
|
||||
Route.Unknown path ->
|
||||
-- Unknown routes display Home with an error flash.
|
||||
let
|
||||
flash =
|
||||
{ title = "Disabled route requested"
|
||||
, table = [ ( "Error", "Monitor disabled by configuration." ) ]
|
||||
{ title = "Unknown route requested"
|
||||
, table = [ ( "Path", path ) ]
|
||||
}
|
||||
in
|
||||
( applyToModelSession (Session.showFlash flash) newModel
|
||||
, Cmd.none
|
||||
)
|
||||
Home.init (Session.showFlash flash session)
|
||||
|> updateWith Home HomeMsg newModel
|
||||
|
||||
Route.Status ->
|
||||
Status.init session
|
||||
|> updateWith Status StatusMsg newModel
|
||||
|
||||
Route.Unknown path ->
|
||||
-- Unknown routes display Home with an error flash.
|
||||
let
|
||||
flash =
|
||||
{ title = "Unknown route requested"
|
||||
, table = [ ( "Path", path ) ]
|
||||
}
|
||||
in
|
||||
Home.init (Session.showFlash flash session)
|
||||
|> updateWith Home HomeMsg newModel
|
||||
{-| Perform effects by updating model and/or producing Cmds to be executed.
|
||||
-}
|
||||
performEffects : ( Model, Effect Msg ) -> ( Model, Cmd Msg )
|
||||
performEffects ( model, effect ) =
|
||||
Effect.perform ( getSession model, effect )
|
||||
|> Tuple.mapFirst (\newSession -> updateSession model newSession)
|
||||
|
||||
|
||||
getSession : Model -> Session
|
||||
@@ -332,11 +340,11 @@ updateWith :
|
||||
(subModel -> PageModel)
|
||||
-> (subMsg -> Msg)
|
||||
-> Model
|
||||
-> ( subModel, Cmd subMsg )
|
||||
-> ( Model, Cmd Msg )
|
||||
updateWith toPage toMsg model ( subModel, subCmd ) =
|
||||
-> ( subModel, Effect subMsg )
|
||||
-> ( Model, Effect Msg )
|
||||
updateWith toPage toMsg model ( subModel, subEffect ) =
|
||||
( { model | page = toPage subModel }
|
||||
, Cmd.map toMsg subCmd
|
||||
, Effect.map toMsg subEffect
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module Page.Home exposing (Model, Msg, init, update, view)
|
||||
|
||||
import Api
|
||||
import Data.Session as Session exposing (Session)
|
||||
import Data.Session exposing (Session)
|
||||
import Effect exposing (Effect)
|
||||
import Html exposing (Html)
|
||||
import Html.Attributes exposing (class, property)
|
||||
import HttpUtil
|
||||
@@ -18,9 +18,9 @@ type alias Model =
|
||||
}
|
||||
|
||||
|
||||
init : Session -> ( Model, Cmd Msg )
|
||||
init : Session -> ( Model, Effect Msg )
|
||||
init session =
|
||||
( Model session "", Api.getGreeting session GreetingLoaded )
|
||||
( Model session "", Effect.getGreeting GreetingLoaded )
|
||||
|
||||
|
||||
|
||||
@@ -31,16 +31,14 @@ type Msg
|
||||
= GreetingLoaded (Result HttpUtil.Error String)
|
||||
|
||||
|
||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||
update : Msg -> Model -> ( Model, Effect Msg )
|
||||
update msg model =
|
||||
case msg of
|
||||
GreetingLoaded (Ok greeting) ->
|
||||
( { model | greeting = greeting }, Cmd.none )
|
||||
( { model | greeting = greeting }, Effect.none )
|
||||
|
||||
GreetingLoaded (Err err) ->
|
||||
( { model | session = Session.showFlash (HttpUtil.errorFlash err) model.session }
|
||||
, Cmd.none
|
||||
)
|
||||
( model, Effect.showFlash (HttpUtil.errorFlash err) )
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
module Page.Mailbox exposing (Model, Msg, init, load, subscriptions, update, view)
|
||||
module Page.Mailbox exposing (Model, Msg, init, subscriptions, update, view)
|
||||
|
||||
import Api
|
||||
import Browser.Navigation as Nav
|
||||
import Data.Message as Message exposing (Message)
|
||||
import Data.MessageHeader exposing (MessageHeader)
|
||||
import Data.Session as Session exposing (Session)
|
||||
import Data.Session exposing (Session)
|
||||
import DateFormat as DF
|
||||
import DateFormat.Relative as Relative
|
||||
import Effect exposing (Effect)
|
||||
import Html
|
||||
exposing
|
||||
( Attribute
|
||||
@@ -54,7 +54,6 @@ import Json.Decode as D
|
||||
import Json.Encode as E
|
||||
import Modal
|
||||
import Route
|
||||
import Task
|
||||
import Time exposing (Posix)
|
||||
import Timer exposing (Timer)
|
||||
|
||||
@@ -95,6 +94,7 @@ type alias Model =
|
||||
{ session : Session
|
||||
, mailboxName : String
|
||||
, state : State
|
||||
, socketConnected : Bool
|
||||
, bodyMode : Body
|
||||
, searchInput : String
|
||||
, promptPurge : Bool
|
||||
@@ -103,27 +103,27 @@ type alias Model =
|
||||
}
|
||||
|
||||
|
||||
init : Session -> String -> Maybe MessageID -> ( Model, Cmd Msg )
|
||||
type alias ServeUrl =
|
||||
List String -> String
|
||||
|
||||
|
||||
init : Session -> String -> Maybe MessageID -> ( Model, Effect Msg )
|
||||
init session mailboxName selection =
|
||||
( { session = session
|
||||
, mailboxName = mailboxName
|
||||
, state = LoadingList selection
|
||||
, socketConnected = False
|
||||
, bodyMode = SafeHtmlBody
|
||||
, searchInput = ""
|
||||
, promptPurge = False
|
||||
, markSeenTimer = Timer.empty
|
||||
, now = Time.millisToPosix 0
|
||||
}
|
||||
, load session mailboxName
|
||||
)
|
||||
|
||||
|
||||
load : Session -> String -> Cmd Msg
|
||||
load session mailboxName =
|
||||
Cmd.batch
|
||||
[ Task.perform Tick Time.now
|
||||
, Api.getHeaderList session ListLoaded mailboxName
|
||||
, Effect.batch
|
||||
[ Effect.posixTime Tick
|
||||
, Effect.getHeaderList ListLoaded mailboxName
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -142,6 +142,7 @@ subscriptions _ =
|
||||
type Msg
|
||||
= ListLoaded (Result HttpUtil.Error (List MessageHeader))
|
||||
| ClickMessage MessageID
|
||||
| ClickRefresh
|
||||
| ListKeyPress String Int
|
||||
| CloseMessage
|
||||
| MessageLoaded (Result HttpUtil.Error Message)
|
||||
@@ -159,38 +160,49 @@ type Msg
|
||||
| ModalFocused Modal.Msg
|
||||
|
||||
|
||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||
update : Msg -> Model -> ( Model, Effect Msg )
|
||||
update msg model =
|
||||
case msg of
|
||||
ClickMessage id ->
|
||||
( updateSelected { model | session = Session.disableRouting model.session } id
|
||||
, Cmd.batch
|
||||
( updateSelected model id
|
||||
, Effect.batch
|
||||
[ -- Update browser location.
|
||||
Route.Message model.mailboxName id
|
||||
|> model.session.router.toPath
|
||||
|> Nav.replaceUrl model.session.key
|
||||
, Api.getMessage model.session MessageLoaded model.mailboxName id
|
||||
Effect.updateRoute (Route.Message model.mailboxName id)
|
||||
, Effect.getMessage MessageLoaded model.mailboxName id
|
||||
]
|
||||
)
|
||||
|
||||
ClickRefresh ->
|
||||
let
|
||||
selection =
|
||||
case model.state of
|
||||
ShowingList _ (ShowingMessage message) ->
|
||||
Just message.id
|
||||
|
||||
_ ->
|
||||
Nothing
|
||||
in
|
||||
-- Reset to loading state, preserving the current message selection.
|
||||
( { model | state = LoadingList selection }
|
||||
, Effect.getHeaderList ListLoaded model.mailboxName
|
||||
)
|
||||
|
||||
CloseMessage ->
|
||||
case model.state of
|
||||
ShowingList list _ ->
|
||||
( { model | state = ShowingList list NoMessage }, Cmd.none )
|
||||
( { model | state = ShowingList list NoMessage }, Effect.none )
|
||||
|
||||
_ ->
|
||||
( model, Cmd.none )
|
||||
( model, Effect.none )
|
||||
|
||||
DeleteMessage message ->
|
||||
updateDeleteMessage model message
|
||||
|
||||
DeletedMessage (Ok _) ->
|
||||
( model, Cmd.none )
|
||||
( model, Effect.none )
|
||||
|
||||
DeletedMessage (Err err) ->
|
||||
( { model | session = Session.showFlash (HttpUtil.errorFlash err) model.session }
|
||||
, Cmd.none
|
||||
)
|
||||
( model, Effect.showFlash (HttpUtil.errorFlash err) )
|
||||
|
||||
ListKeyPress id keyCode ->
|
||||
case keyCode of
|
||||
@@ -198,79 +210,49 @@ update msg model =
|
||||
updateOpenMessage model id
|
||||
|
||||
_ ->
|
||||
( model, Cmd.none )
|
||||
( model, Effect.none )
|
||||
|
||||
ListLoaded (Ok headers) ->
|
||||
case model.state of
|
||||
LoadingList selection ->
|
||||
let
|
||||
newModel =
|
||||
{ model
|
||||
| state = ShowingList (MessageList headers Nothing "") NoMessage
|
||||
}
|
||||
in
|
||||
case selection of
|
||||
Just id ->
|
||||
updateOpenMessage newModel id
|
||||
|
||||
Nothing ->
|
||||
( { newModel
|
||||
| session = Session.addRecent model.mailboxName model.session
|
||||
}
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
_ ->
|
||||
( model, Cmd.none )
|
||||
updateListLoaded model headers
|
||||
|
||||
ListLoaded (Err err) ->
|
||||
( { model | session = Session.showFlash (HttpUtil.errorFlash err) model.session }
|
||||
, Cmd.none
|
||||
)
|
||||
( model, Effect.showFlash (HttpUtil.errorFlash err) )
|
||||
|
||||
MarkSeenLoaded (Ok _) ->
|
||||
( model, Cmd.none )
|
||||
( model, Effect.none )
|
||||
|
||||
MarkSeenLoaded (Err err) ->
|
||||
( { model | session = Session.showFlash (HttpUtil.errorFlash err) model.session }
|
||||
, Cmd.none
|
||||
)
|
||||
( model, Effect.showFlash (HttpUtil.errorFlash err) )
|
||||
|
||||
MessageLoaded (Ok message) ->
|
||||
updateMessageResult model message
|
||||
|
||||
MessageLoaded (Err err) ->
|
||||
( { model | session = Session.showFlash (HttpUtil.errorFlash err) model.session }
|
||||
, Cmd.none
|
||||
)
|
||||
( model, Effect.showFlash (HttpUtil.errorFlash err) )
|
||||
|
||||
MessageBody bodyMode ->
|
||||
( { model | bodyMode = bodyMode }, Cmd.none )
|
||||
( { model | bodyMode = bodyMode }, Effect.none )
|
||||
|
||||
ModalFocused message ->
|
||||
( { model | session = Modal.updateSession message model.session }
|
||||
, Cmd.none
|
||||
)
|
||||
( model, Effect.focusModalResult message )
|
||||
|
||||
OnSearchInput searchInput ->
|
||||
updateSearchInput model searchInput
|
||||
|
||||
PurgeMailboxPrompt ->
|
||||
( { model | promptPurge = True }, Modal.resetFocusCmd ModalFocused )
|
||||
( { model | promptPurge = True }, Effect.focusModal ModalFocused )
|
||||
|
||||
PurgeMailboxCanceled ->
|
||||
( { model | promptPurge = False }, Cmd.none )
|
||||
( { model | promptPurge = False }, Effect.none )
|
||||
|
||||
PurgeMailboxConfirmed ->
|
||||
updateTriggerPurge model
|
||||
|
||||
PurgedMailbox (Ok _) ->
|
||||
( model, Cmd.none )
|
||||
( model, Effect.none )
|
||||
|
||||
PurgedMailbox (Err err) ->
|
||||
( { model | session = Session.showFlash (HttpUtil.errorFlash err) model.session }
|
||||
, Cmd.none
|
||||
)
|
||||
( model, Effect.showFlash (HttpUtil.errorFlash err) )
|
||||
|
||||
MarkSeenTriggered timer ->
|
||||
if timer == model.markSeenTimer then
|
||||
@@ -278,15 +260,42 @@ update msg model =
|
||||
updateMarkMessageSeen model
|
||||
|
||||
else
|
||||
( model, Cmd.none )
|
||||
( model, Effect.none )
|
||||
|
||||
Tick now ->
|
||||
( { model | now = now }, Cmd.none )
|
||||
( { model | now = now }, Effect.none )
|
||||
|
||||
|
||||
updateListLoaded : Model -> List MessageHeader -> ( Model, Effect Msg )
|
||||
updateListLoaded model headers =
|
||||
case model.state of
|
||||
LoadingList selection ->
|
||||
let
|
||||
newModel =
|
||||
{ model
|
||||
| state = ShowingList (MessageList headers Nothing "") NoMessage
|
||||
}
|
||||
in
|
||||
Effect.append (Effect.addRecent newModel.mailboxName) <|
|
||||
case selection of
|
||||
Just id ->
|
||||
-- Don't try to load selected message if not present in headers.
|
||||
if List.any (\header -> Just header.id == selection) headers then
|
||||
updateOpenMessage newModel id
|
||||
|
||||
else
|
||||
( newModel, Effect.updateRoute (Route.Mailbox model.mailboxName) )
|
||||
|
||||
Nothing ->
|
||||
( newModel, Effect.none )
|
||||
|
||||
_ ->
|
||||
( model, Effect.none )
|
||||
|
||||
|
||||
{-| Replace the currently displayed message.
|
||||
-}
|
||||
updateMessageResult : Model -> Message -> ( Model, Cmd Msg )
|
||||
updateMessageResult : Model -> Message -> ( Model, Effect Msg )
|
||||
updateMessageResult model message =
|
||||
let
|
||||
bodyMode =
|
||||
@@ -298,7 +307,7 @@ updateMessageResult model message =
|
||||
in
|
||||
case model.state of
|
||||
LoadingList _ ->
|
||||
( model, Cmd.none )
|
||||
( model, Effect.none )
|
||||
|
||||
ShowingList list _ ->
|
||||
let
|
||||
@@ -314,38 +323,26 @@ updateMessageResult model message =
|
||||
, markSeenTimer = newTimer
|
||||
}
|
||||
-- Set 1500ms delay before reporting message as seen to backend.
|
||||
, Timer.schedule MarkSeenTriggered newTimer 1500
|
||||
, Effect.schedule MarkSeenTriggered newTimer 1500
|
||||
)
|
||||
|
||||
|
||||
{-| Updates model and triggers commands to purge this mailbox.
|
||||
-}
|
||||
updateTriggerPurge : Model -> ( Model, Cmd Msg )
|
||||
updateTriggerPurge : Model -> ( Model, Effect Msg )
|
||||
updateTriggerPurge model =
|
||||
let
|
||||
cmd =
|
||||
Cmd.batch
|
||||
[ Route.Mailbox model.mailboxName
|
||||
|> model.session.router.toPath
|
||||
|> Nav.replaceUrl model.session.key
|
||||
, Api.purgeMailbox model.session PurgedMailbox model.mailboxName
|
||||
]
|
||||
in
|
||||
case model.state of
|
||||
ShowingList _ _ ->
|
||||
( { model
|
||||
| promptPurge = False
|
||||
, session = Session.disableRouting model.session
|
||||
, state = ShowingList (MessageList [] Nothing "") NoMessage
|
||||
}
|
||||
, cmd
|
||||
)
|
||||
|
||||
_ ->
|
||||
( model, cmd )
|
||||
( { model
|
||||
| promptPurge = False
|
||||
, state = ShowingList (MessageList [] Nothing "") NoMessage
|
||||
}
|
||||
, Effect.batch
|
||||
[ Effect.updateRoute (Route.Mailbox model.mailboxName)
|
||||
, Effect.purgeMailbox PurgedMailbox model.mailboxName
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
updateSearchInput : Model -> String -> ( Model, Cmd Msg )
|
||||
updateSearchInput : Model -> String -> ( Model, Effect Msg )
|
||||
updateSearchInput model searchInput =
|
||||
let
|
||||
searchFilter =
|
||||
@@ -357,14 +354,14 @@ updateSearchInput model searchInput =
|
||||
in
|
||||
case model.state of
|
||||
LoadingList _ ->
|
||||
( model, Cmd.none )
|
||||
( model, Effect.none )
|
||||
|
||||
ShowingList list messageState ->
|
||||
( { model
|
||||
| searchInput = searchInput
|
||||
, state = ShowingList { list | searchFilter = searchFilter } messageState
|
||||
}
|
||||
, Cmd.none
|
||||
, Effect.none
|
||||
)
|
||||
|
||||
|
||||
@@ -396,7 +393,7 @@ updateSelected model id =
|
||||
{ model | state = ShowingList newList (Transitioning visible) }
|
||||
|
||||
|
||||
updateDeleteMessage : Model -> Message -> ( Model, Cmd Msg )
|
||||
updateDeleteMessage : Model -> Message -> ( Model, Effect Msg )
|
||||
updateDeleteMessage model message =
|
||||
let
|
||||
filter f messageList =
|
||||
@@ -404,26 +401,20 @@ updateDeleteMessage model message =
|
||||
in
|
||||
case model.state of
|
||||
ShowingList list _ ->
|
||||
( { model
|
||||
| session = Session.disableRouting model.session
|
||||
, state =
|
||||
ShowingList (filter (\x -> x.id /= message.id) list) NoMessage
|
||||
}
|
||||
, Cmd.batch
|
||||
[ Api.deleteMessage model.session DeletedMessage message.mailbox message.id
|
||||
, Route.Mailbox model.mailboxName
|
||||
|> model.session.router.toPath
|
||||
|> Nav.replaceUrl model.session.key
|
||||
( { model | state = ShowingList (filter (\x -> x.id /= message.id) list) NoMessage }
|
||||
, Effect.batch
|
||||
[ Effect.deleteMessage DeletedMessage message.mailbox message.id
|
||||
, Effect.updateRoute (Route.Mailbox model.mailboxName)
|
||||
]
|
||||
)
|
||||
|
||||
_ ->
|
||||
( model, Cmd.none )
|
||||
( model, Effect.none )
|
||||
|
||||
|
||||
{-| Updates both the active message, and the message list to mark the currently viewed message as seen.
|
||||
-}
|
||||
updateMarkMessageSeen : Model -> ( Model, Cmd Msg )
|
||||
updateMarkMessageSeen : Model -> ( Model, Effect Msg )
|
||||
updateMarkMessageSeen model =
|
||||
case model.state of
|
||||
ShowingList messages (ShowingMessage visibleMessage) ->
|
||||
@@ -442,21 +433,17 @@ updateMarkMessageSeen model =
|
||||
| state =
|
||||
ShowingList newMessages (ShowingMessage { visibleMessage | seen = True })
|
||||
}
|
||||
, Api.markMessageSeen model.session MarkSeenLoaded visibleMessage.mailbox visibleMessage.id
|
||||
, Effect.markMessageSeen MarkSeenLoaded visibleMessage.mailbox visibleMessage.id
|
||||
)
|
||||
|
||||
_ ->
|
||||
( model, Cmd.none )
|
||||
( model, Effect.none )
|
||||
|
||||
|
||||
updateOpenMessage : Model -> String -> ( Model, Cmd Msg )
|
||||
updateOpenMessage : Model -> String -> ( Model, Effect Msg )
|
||||
updateOpenMessage model id =
|
||||
let
|
||||
newModel =
|
||||
{ model | session = Session.addRecent model.mailboxName model.session }
|
||||
in
|
||||
( updateSelected newModel id
|
||||
, Api.getMessage model.session MessageLoaded model.mailboxName id
|
||||
( updateSelected model id
|
||||
, Effect.getMessage MessageLoaded model.mailboxName id
|
||||
)
|
||||
|
||||
|
||||
@@ -467,6 +454,10 @@ updateOpenMessage model id =
|
||||
view : Model -> { title : String, modal : Maybe (Html Msg), content : List (Html Msg) }
|
||||
view model =
|
||||
let
|
||||
serveUrl : ServeUrl
|
||||
serveUrl =
|
||||
Api.serveUrl model.session
|
||||
|
||||
mode =
|
||||
case model.state of
|
||||
ShowingList _ (ShowingMessage _) ->
|
||||
@@ -479,26 +470,7 @@ view model =
|
||||
, modal = viewModal model.promptPurge
|
||||
, content =
|
||||
[ div [ class ("mailbox " ++ mode) ]
|
||||
[ aside [ class "message-list-controls" ]
|
||||
[ input
|
||||
[ type_ "text"
|
||||
, placeholder "search"
|
||||
, Events.onInput OnSearchInput
|
||||
, value model.searchInput
|
||||
]
|
||||
[]
|
||||
, button
|
||||
[ Events.onClick (OnSearchInput "")
|
||||
, disabled (model.searchInput == "")
|
||||
, alt "Clear Search"
|
||||
]
|
||||
[ i [ class "fas fa-times" ] [] ]
|
||||
, button
|
||||
[ Events.onClick PurgeMailboxPrompt
|
||||
, alt "Purge Mailbox"
|
||||
]
|
||||
[ i [ class "fas fa-trash" ] [] ]
|
||||
]
|
||||
[ viewMessageListControls model
|
||||
, viewMessageList model
|
||||
, main_
|
||||
[ class "message" ]
|
||||
@@ -510,10 +482,10 @@ view model =
|
||||
)
|
||||
|
||||
ShowingList _ (ShowingMessage message) ->
|
||||
viewMessage model.session model.session.zone message model.bodyMode
|
||||
viewMessage serveUrl model.session.zone message model.bodyMode
|
||||
|
||||
ShowingList _ (Transitioning message) ->
|
||||
viewMessage model.session model.session.zone message model.bodyMode
|
||||
viewMessage serveUrl model.session.zone message model.bodyMode
|
||||
|
||||
_ ->
|
||||
text ""
|
||||
@@ -539,6 +511,53 @@ viewModal promptPurge =
|
||||
Nothing
|
||||
|
||||
|
||||
viewMessageListControls : Model -> Html Msg
|
||||
viewMessageListControls model =
|
||||
let
|
||||
clearButton =
|
||||
Just <|
|
||||
button
|
||||
[ Events.onClick (OnSearchInput "")
|
||||
, disabled (model.searchInput == "")
|
||||
, alt "Clear Search"
|
||||
]
|
||||
[ i [ class "fas fa-times" ] [] ]
|
||||
|
||||
purgeButton =
|
||||
Just <|
|
||||
button
|
||||
[ Events.onClick PurgeMailboxPrompt
|
||||
, alt "Purge Mailbox"
|
||||
]
|
||||
[ i [ class "fas fa-trash" ] [] ]
|
||||
|
||||
refreshButton =
|
||||
if model.socketConnected then
|
||||
Nothing
|
||||
|
||||
else
|
||||
Just <|
|
||||
button
|
||||
[ Events.onClick ClickRefresh
|
||||
, alt "Refresh Mailbox"
|
||||
]
|
||||
[ i [ class "fas fa-sync" ] [] ]
|
||||
|
||||
searchInput =
|
||||
Just <|
|
||||
input
|
||||
[ type_ "text"
|
||||
, placeholder "search"
|
||||
, Events.onInput OnSearchInput
|
||||
, value model.searchInput
|
||||
]
|
||||
[]
|
||||
in
|
||||
[ searchInput, clearButton, refreshButton, purgeButton ]
|
||||
|> List.filterMap identity
|
||||
|> aside [ class "message-list-controls" ]
|
||||
|
||||
|
||||
viewMessageList : Model -> Html Msg
|
||||
viewMessageList model =
|
||||
aside [ class "message-list" ] <|
|
||||
@@ -571,14 +590,14 @@ messageChip model selected message =
|
||||
]
|
||||
|
||||
|
||||
viewMessage : Session -> Time.Zone -> Message -> Body -> Html Msg
|
||||
viewMessage session zone message bodyMode =
|
||||
viewMessage : ServeUrl -> Time.Zone -> Message -> Body -> Html Msg
|
||||
viewMessage serveUrl zone message bodyMode =
|
||||
let
|
||||
htmlUrl =
|
||||
Api.serveUrl session [ "mailbox", message.mailbox, message.id, "html" ]
|
||||
serveUrl [ "mailbox", message.mailbox, message.id, "html" ]
|
||||
|
||||
sourceUrl =
|
||||
Api.serveUrl session [ "mailbox", message.mailbox, message.id, "source" ]
|
||||
serveUrl [ "mailbox", message.mailbox, message.id, "source" ]
|
||||
|
||||
htmlButton =
|
||||
if message.html == "" then
|
||||
@@ -609,7 +628,7 @@ viewMessage session zone message bodyMode =
|
||||
]
|
||||
, messageErrors message
|
||||
, messageBody message bodyMode
|
||||
, attachments session message
|
||||
, attachments serveUrl message
|
||||
]
|
||||
|
||||
|
||||
@@ -672,20 +691,22 @@ messageBody message bodyMode =
|
||||
]
|
||||
|
||||
|
||||
attachments : Session -> Message -> Html Msg
|
||||
attachments session message =
|
||||
attachments : ServeUrl -> Message -> Html Msg
|
||||
attachments serveUrl message =
|
||||
if List.isEmpty message.attachments then
|
||||
div [] []
|
||||
text ""
|
||||
|
||||
else
|
||||
table [ class "attachments well" ] (List.map (attachmentRow session message) message.attachments)
|
||||
message.attachments
|
||||
|> List.map (attachmentRow serveUrl message)
|
||||
|> table [ class "attachments well" ]
|
||||
|
||||
|
||||
attachmentRow : Session -> Message -> Message.Attachment -> Html Msg
|
||||
attachmentRow session message attach =
|
||||
attachmentRow : ServeUrl -> Message -> Message.Attachment -> Html Msg
|
||||
attachmentRow serveUrl message attach =
|
||||
let
|
||||
url =
|
||||
Api.serveUrl session
|
||||
serveUrl
|
||||
[ "mailbox"
|
||||
, message.mailbox
|
||||
, message.id
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
module Page.Monitor exposing (Model, Msg, init, update, view)
|
||||
|
||||
import Api
|
||||
import Browser.Navigation as Nav
|
||||
import Data.MessageHeader as MessageHeader exposing (MessageHeader)
|
||||
import Data.Session as Session exposing (Session)
|
||||
import Data.Session exposing (Session)
|
||||
import DateFormat as DF
|
||||
import Effect exposing (Effect)
|
||||
import Html
|
||||
exposing
|
||||
( Attribute
|
||||
@@ -41,9 +41,9 @@ type alias Model =
|
||||
}
|
||||
|
||||
|
||||
init : Session -> ( Model, Cmd Msg )
|
||||
init : Session -> ( Model, Effect Msg )
|
||||
init session =
|
||||
( Model session False [], Cmd.none )
|
||||
( Model session False [], Effect.none )
|
||||
|
||||
|
||||
|
||||
@@ -58,20 +58,20 @@ type Msg
|
||||
| MessageKeyPress MessageHeader Int
|
||||
|
||||
|
||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||
update : Msg -> Model -> ( Model, Effect Msg )
|
||||
update msg model =
|
||||
case msg of
|
||||
Connected True ->
|
||||
( { model | connected = True, messages = [] }, Cmd.none )
|
||||
( { model | connected = True, messages = [] }, Effect.none )
|
||||
|
||||
Connected False ->
|
||||
( { model | connected = False }, Cmd.none )
|
||||
( { model | connected = False }, Effect.none )
|
||||
|
||||
MessageReceived value ->
|
||||
case D.decodeValue (MessageHeader.decoder |> D.at [ "detail" ]) value of
|
||||
Ok header ->
|
||||
( { model | messages = header :: List.take 500 model.messages }
|
||||
, Cmd.none
|
||||
, Effect.none
|
||||
)
|
||||
|
||||
Err err ->
|
||||
@@ -81,12 +81,10 @@ update msg model =
|
||||
, table = [ ( "Error", D.errorToString err ) ]
|
||||
}
|
||||
in
|
||||
( { model | session = Session.showFlash flash model.session }
|
||||
, Cmd.none
|
||||
)
|
||||
( model, Effect.showFlash flash )
|
||||
|
||||
Clear ->
|
||||
( { model | messages = [] }, Cmd.none )
|
||||
( { model | messages = [] }, Effect.none )
|
||||
|
||||
OpenMessage header ->
|
||||
openMessage header model
|
||||
@@ -97,15 +95,13 @@ update msg model =
|
||||
openMessage header model
|
||||
|
||||
_ ->
|
||||
( model, Cmd.none )
|
||||
( model, Effect.none )
|
||||
|
||||
|
||||
openMessage : MessageHeader -> Model -> ( Model, Cmd Msg )
|
||||
openMessage : MessageHeader -> Model -> ( Model, Effect Msg )
|
||||
openMessage header model =
|
||||
( model
|
||||
, Route.Message header.mailbox header.id
|
||||
|> model.session.router.toPath
|
||||
|> Nav.replaceUrl model.session.key
|
||||
, Effect.navigateRoute True (Route.Message header.mailbox header.id)
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
module Page.Status exposing (Model, Msg, init, subscriptions, update, view)
|
||||
|
||||
import Api
|
||||
import Data.Metrics exposing (Metrics)
|
||||
import Data.ServerConfig exposing (ServerConfig)
|
||||
import Data.Session as Session exposing (Session)
|
||||
import Data.Session exposing (Session)
|
||||
import DateFormat.Relative as Relative
|
||||
import Effect exposing (Effect)
|
||||
import Filesize
|
||||
import Html
|
||||
exposing
|
||||
@@ -19,7 +19,6 @@ import Html.Attributes exposing (class)
|
||||
import HttpUtil
|
||||
import Sparkline as Spark
|
||||
import Svg.Attributes as SvgAttrib
|
||||
import Task
|
||||
import Time exposing (Posix)
|
||||
|
||||
|
||||
@@ -60,7 +59,7 @@ type alias Metric =
|
||||
}
|
||||
|
||||
|
||||
init : Session -> ( Model, Cmd Msg )
|
||||
init : Session -> ( Model, Effect Msg )
|
||||
init session =
|
||||
( { session = session
|
||||
, now = Time.millisToPosix 0
|
||||
@@ -82,9 +81,9 @@ init session =
|
||||
, retainedCount = Metric "Stored Messages" 0 fmtInt graphZero initDataSet 60
|
||||
, retainedSize = Metric "Store Size" 0 Filesize.format graphZero initDataSet 60
|
||||
}
|
||||
, Cmd.batch
|
||||
[ Task.perform Tick Time.now
|
||||
, Api.getServerConfig session ServerConfigLoaded
|
||||
, Effect.batch
|
||||
[ Effect.posixTime Tick
|
||||
, Effect.getServerConfig ServerConfigLoaded
|
||||
]
|
||||
)
|
||||
|
||||
@@ -114,27 +113,25 @@ type Msg
|
||||
| Tick Posix
|
||||
|
||||
|
||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||
update : Msg -> Model -> ( Model, Effect Msg )
|
||||
update msg model =
|
||||
case msg of
|
||||
MetricsReceived (Ok metrics) ->
|
||||
( updateMetrics metrics model, Cmd.none )
|
||||
( updateMetrics metrics model, Effect.none )
|
||||
|
||||
MetricsReceived (Err err) ->
|
||||
( { model | session = Session.showFlash (HttpUtil.errorFlash err) model.session }
|
||||
, Cmd.none
|
||||
)
|
||||
( model, Effect.showFlash (HttpUtil.errorFlash err) )
|
||||
|
||||
ServerConfigLoaded (Ok config) ->
|
||||
( { model | config = Just config }, Cmd.none )
|
||||
( { model | config = Just config }, Effect.none )
|
||||
|
||||
ServerConfigLoaded (Err err) ->
|
||||
( { model | session = Session.showFlash (HttpUtil.errorFlash err) model.session }
|
||||
, Cmd.none
|
||||
)
|
||||
( model, Effect.showFlash (HttpUtil.errorFlash err) )
|
||||
|
||||
Tick time ->
|
||||
( { model | now = time }, Api.getServerMetrics model.session MetricsReceived )
|
||||
( { model | now = time }
|
||||
, Effect.getServerMetrics MetricsReceived
|
||||
)
|
||||
|
||||
|
||||
{-| Update all metrics in Model; increment xCounter.
|
||||
@@ -416,7 +413,7 @@ viewMetric metric =
|
||||
, div [ class "value" ] [ text (metric.formatter metric.value) ]
|
||||
, div [ class "graph" ]
|
||||
[ metric.graph metric.history
|
||||
, text ("(" ++ String.fromInt metric.minutes ++ "min)")
|
||||
, text (" (" ++ String.fromInt metric.minutes ++ "min)")
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
@@ -49,6 +49,8 @@ cancel previous =
|
||||
previous
|
||||
|
||||
|
||||
{-| Increments the timer identity, preventing integer overflow.
|
||||
-}
|
||||
next : Int -> Int
|
||||
next index =
|
||||
if index > 2 ^ 30 then
|
||||
|
||||
@@ -3,6 +3,7 @@ import './mailbox.css'
|
||||
import './navbar.css'
|
||||
import '@fortawesome/fontawesome-free/css/all.css'
|
||||
import '@webcomponents/webcomponentsjs/webcomponents-bundle'
|
||||
import 'opensans-npm-webfont'
|
||||
import { Elm } from './Main.elm'
|
||||
import './monitorMessages'
|
||||
import './renderedHtml'
|
||||
|
||||
@@ -98,9 +98,14 @@
|
||||
border-width: 1px;
|
||||
border-style: none solid solid solid;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
padding: 5px 8px;
|
||||
}
|
||||
|
||||
.message-list-entry:focus {
|
||||
background-color: var(--focused-bg-color) !important;
|
||||
}
|
||||
|
||||
.message-list-entry.selected {
|
||||
background-color: var(--selected-color);
|
||||
}
|
||||
@@ -109,6 +114,10 @@
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
.message-list-entry:focus .subject {
|
||||
color: var(--focused-color);
|
||||
}
|
||||
|
||||
.message-list-entry .subject {
|
||||
color: var(--high-color);
|
||||
}
|
||||
@@ -117,6 +126,12 @@
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.message-list-entry:focus .from,
|
||||
.message-list-entry:focus .date {
|
||||
color: var(--focused-color);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.message-list-entry .from,
|
||||
.message-list-entry .date {
|
||||
color: var(--low-color);
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
--border-color: #ddd;
|
||||
--placeholder-color: #9f9f9f;
|
||||
--selected-color: #eee;
|
||||
--focused-color: #fff;
|
||||
--focused-bg-color: #337ab7;
|
||||
}
|
||||
|
||||
html, body, div, span, applet, object, iframe,
|
||||
@@ -46,8 +48,9 @@ body {
|
||||
}
|
||||
|
||||
body, button, input, table {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-family: "Open Sans", Helvetica, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 1.43;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
@@ -73,7 +76,7 @@ a.button {
|
||||
font-size: 11px;
|
||||
font-style: normal;
|
||||
margin: 4px;
|
||||
padding: 3px 8px;
|
||||
padding: 3px 8px 4px;
|
||||
text-decoration: none;
|
||||
text-shadow: 0 -1px 0 rgba(0,0,0,0.2);
|
||||
}
|
||||
@@ -245,7 +248,7 @@ h3 {
|
||||
font-weight: 400;
|
||||
height: 30px;
|
||||
margin: 0;
|
||||
padding: 5px 10px;
|
||||
padding: 5px 10px 6px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
text-shadow: 0 -1px 0 rgba(0,0,0,0.2);
|
||||
@@ -351,3 +354,9 @@ h3 {
|
||||
background-color: var(--selected-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.monitor tr:focus {
|
||||
color: var(--focused-color);
|
||||
background-color: var(--focused-bg-color);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
.navbar-brand {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.navbar-toggle {
|
||||
|
||||
@@ -35,12 +35,10 @@ module.exports = (env, argv) => {
|
||||
],
|
||||
},
|
||||
{
|
||||
include: [/\/src/, /\/node_modules\/@fortawesome\/fontawesome-free\/css/],
|
||||
test: /\.css$/,
|
||||
loader: ['style-loader', 'css-loader'],
|
||||
},
|
||||
{
|
||||
include: [/\/node_modules\/@fortawesome\/fontawesome-free\/webfonts/],
|
||||
test: /\.(eot|svg|ttf|woff|woff2)$/,
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
|
||||
Reference in New Issue
Block a user