mirror of
https://github.com/mirego/accent.git
synced 2024-08-16 06:20:31 +03:00
Convert codebase to typescript and latest Ember (#157)
* Update eslint setup (#134) * Convert services (#136) * Convert login and logged-in routes (#137) * Convert project routes and controllers (#138) * Convert activities and comments routes (#140) * Convert project edit sub-routes and controllers (#139) * Convert files routes and controllers (#141) * Convert revision routes and controllers (#142) * Convert phoenix service (#143) * Convert translation routes and controllers (#144) * Convert versions routes and controllers (#145) * Convert JIPT routes and controllers (#146) * Convert Sass variables to CSS modules @value (#147) * Convert CSS variables and fix projects page components * Fix a bunch of components * Fix another bunch of components * Update styles and missing global for powerselect * Fix typings * Fix typescript warnings * Add typings for phoenix and file-saver vendor * Fix github ci Co-authored-by: Charles Demers <cdemers@mirego.com> Co-authored-by: Charles Demers <charles.demers6@gmail.com>
This commit is contained in:
parent
cc48848377
commit
9567db9b4d
27
.eslintrc
27
.eslintrc
@ -1,27 +0,0 @@
|
||||
{
|
||||
"globals": {
|
||||
"JsDiff": true
|
||||
},
|
||||
"plugins": [
|
||||
"ember",
|
||||
"mirego"
|
||||
],
|
||||
"extends": [
|
||||
"plugin:mirego/recommended",
|
||||
"prettier"
|
||||
],
|
||||
"env": {
|
||||
"es6": true
|
||||
},
|
||||
"rules": {
|
||||
"no-irregular-whitespace": 0,
|
||||
"ember/jquery-ember-run": 2,
|
||||
"ember/use-brace-expansion": 2,
|
||||
"ember/no-side-effects": 2,
|
||||
"ember/order-in-routes": 2,
|
||||
"ember/order-in-models": 2,
|
||||
"ember/order-in-controllers": 2,
|
||||
"ember/no-empty-attrs": 2,
|
||||
"ember/closure-actions": 2
|
||||
}
|
||||
}
|
231
.eslintrc.js
Normal file
231
.eslintrc.js
Normal file
@ -0,0 +1,231 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
overrides: [
|
||||
{
|
||||
files: ['webapp/app/**/*', 'webapp/tests/**/*', 'webapp/types/**/*'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: './webapp/tsconfig.json'
|
||||
},
|
||||
plugins: ['@typescript-eslint', 'ember', 'mirego'],
|
||||
extends: [
|
||||
'plugin:mirego/recommended',
|
||||
'prettier',
|
||||
'prettier/@typescript-eslint',
|
||||
'typestrict'
|
||||
],
|
||||
env: {
|
||||
es6: true,
|
||||
browser: true
|
||||
},
|
||||
globals: {
|
||||
JsDiff: true
|
||||
},
|
||||
rules: {
|
||||
'complexity': 0,
|
||||
'no-irregular-whitespace': 0,
|
||||
'ember/closure-actions': 2,
|
||||
'ember/named-functions-in-promises': 0,
|
||||
'ember/new-module-imports': 2,
|
||||
'ember/no-global-jquery': 2,
|
||||
'ember/no-on-calls-in-components': 2,
|
||||
'ember/no-duplicate-dependent-keys': 2,
|
||||
'ember/no-side-effects': 2,
|
||||
'ember/require-super-in-init': 2,
|
||||
'ember/avoid-leaking-state-in-ember-objects': 2,
|
||||
'ember/use-brace-expansion': 2,
|
||||
'ember/jquery-ember-run': 2,
|
||||
'ember/order-in-routes': 2,
|
||||
'ember/order-in-controllers': 2,
|
||||
'ember/no-empty-attrs': 2,
|
||||
'@typescript-eslint/adjacent-overload-signatures': 2,
|
||||
'@typescript-eslint/array-type': [2, {default: 'array-simple'}],
|
||||
'@typescript-eslint/await-thenable': 2,
|
||||
'@typescript-eslint/ban-ts-ignore': 2,
|
||||
'@typescript-eslint/consistent-type-assertions': [
|
||||
2,
|
||||
{assertionStyle: 'as'}
|
||||
],
|
||||
'@typescript-eslint/consistent-type-definitions': [2, 'interface'],
|
||||
'@typescript-eslint/member-delimiter-style': [
|
||||
2,
|
||||
{
|
||||
multiline: {
|
||||
delimiter: 'semi',
|
||||
requireLast: true
|
||||
},
|
||||
singleline: {
|
||||
delimiter: 'semi',
|
||||
requireLast: false
|
||||
}
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/member-ordering': 2,
|
||||
'@typescript-eslint/no-empty-interface': 2,
|
||||
'@typescript-eslint/no-floating-promises': 2,
|
||||
'@typescript-eslint/no-misused-new': 2,
|
||||
'@typescript-eslint/no-misused-promises': 2,
|
||||
'@typescript-eslint/no-non-null-assertion': 2,
|
||||
'@typescript-eslint/no-parameter-properties': 2,
|
||||
'@typescript-eslint/no-require-imports': 2,
|
||||
'@typescript-eslint/no-unnecessary-type-assertion': 2,
|
||||
'@typescript-eslint/promise-function-async': 2,
|
||||
'@typescript-eslint/require-await': 2,
|
||||
'@typescript-eslint/type-annotation-spacing': 2,
|
||||
'@typescript-eslint/unified-signatures': 2,
|
||||
'no-unused-vars': 0,
|
||||
'no-invalid-this': 0,
|
||||
'@typescript-eslint/no-unused-vars': 2,
|
||||
}
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'webapp/.ember-cli.js',
|
||||
'webapp/.eslintrc.js',
|
||||
'webapp/.template-lintrc.js',
|
||||
'webapp/ember-cli-build.js',
|
||||
'webapp/testem.js',
|
||||
'webapp/config/**/*.js',
|
||||
'webapp/lib/*/index.js',
|
||||
'webapp/scripts/**/*.js',
|
||||
'webapp/node-server/**/*.js'
|
||||
],
|
||||
parserOptions: {
|
||||
sourceType: 'script',
|
||||
ecmaVersion: 2015
|
||||
},
|
||||
env: {
|
||||
browser: false,
|
||||
node: true
|
||||
},
|
||||
plugins: ['node'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-require-imports': 0,
|
||||
|
||||
// this can be removed once the following is fixed
|
||||
// https://github.com/mysticatea/eslint-plugin-node/issues/77
|
||||
'node/no-unpublished-require': 'off'
|
||||
},
|
||||
extends: ['plugin:node/recommended']
|
||||
},
|
||||
{
|
||||
files: ['cli/**/*'],
|
||||
env: {
|
||||
es6: true
|
||||
},
|
||||
globals: {
|
||||
process: true
|
||||
},
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: './cli/tsconfig.json'
|
||||
},
|
||||
plugins: ['@typescript-eslint', 'mirego'],
|
||||
extends: [
|
||||
'plugin:mirego/recommended',
|
||||
'prettier',
|
||||
'prettier/@typescript-eslint'
|
||||
],
|
||||
rules: {
|
||||
'complexity': 0,
|
||||
'no-irregular-whitespace': 0,
|
||||
'no-unused-vars': 0,
|
||||
'no-console': 0,
|
||||
'max-nested-callbacks': [2, {max: 3}],
|
||||
'@typescript-eslint/adjacent-overload-signatures': 2,
|
||||
'@typescript-eslint/array-type': [2, {default: 'array-simple'}],
|
||||
'@typescript-eslint/await-thenable': 2,
|
||||
'@typescript-eslint/ban-ts-ignore': 2,
|
||||
'@typescript-eslint/consistent-type-assertions': [
|
||||
2,
|
||||
{assertionStyle: 'as'}
|
||||
],
|
||||
'@typescript-eslint/consistent-type-definitions': [2, 'interface'],
|
||||
'@typescript-eslint/member-delimiter-style': [
|
||||
2,
|
||||
{
|
||||
multiline: {
|
||||
delimiter: 'semi',
|
||||
requireLast: true
|
||||
},
|
||||
singleline: {
|
||||
delimiter: 'semi',
|
||||
requireLast: false
|
||||
}
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/member-ordering': 2,
|
||||
'@typescript-eslint/no-empty-interface': 2,
|
||||
'@typescript-eslint/no-floating-promises': 2,
|
||||
'@typescript-eslint/no-misused-new': 2,
|
||||
'@typescript-eslint/no-misused-promises': 2,
|
||||
'@typescript-eslint/no-non-null-assertion': 0,
|
||||
'@typescript-eslint/no-parameter-properties': 2,
|
||||
'@typescript-eslint/no-require-imports': 2,
|
||||
'@typescript-eslint/no-unnecessary-type-assertion': 2,
|
||||
'@typescript-eslint/promise-function-async': 2,
|
||||
'@typescript-eslint/require-await': 2,
|
||||
'@typescript-eslint/type-annotation-spacing': 2,
|
||||
'@typescript-eslint/unified-signatures': 2,
|
||||
'@typescript-eslint/no-unused-vars': 2
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['jipt/**/*'],
|
||||
env: {
|
||||
es6: true
|
||||
},
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: './jipt/tsconfig.json'
|
||||
},
|
||||
plugins: ['@typescript-eslint', 'mirego'],
|
||||
extends: [
|
||||
'plugin:mirego/recommended',
|
||||
'prettier',
|
||||
'prettier/@typescript-eslint'
|
||||
],
|
||||
rules: {
|
||||
'complexity': 0,
|
||||
'no-irregular-whitespace': 0,
|
||||
'no-unused-vars': 0,
|
||||
'@typescript-eslint/adjacent-overload-signatures': 2,
|
||||
'@typescript-eslint/array-type': [2, {default: 'array-simple'}],
|
||||
'@typescript-eslint/await-thenable': 2,
|
||||
'@typescript-eslint/ban-ts-ignore': 2,
|
||||
'@typescript-eslint/consistent-type-assertions': [
|
||||
2,
|
||||
{assertionStyle: 'as'}
|
||||
],
|
||||
'@typescript-eslint/consistent-type-definitions': [2, 'interface'],
|
||||
'@typescript-eslint/member-delimiter-style': [
|
||||
2,
|
||||
{
|
||||
multiline: {
|
||||
delimiter: 'semi',
|
||||
requireLast: true
|
||||
},
|
||||
singleline: {
|
||||
delimiter: 'semi',
|
||||
requireLast: false
|
||||
}
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/member-ordering': 2,
|
||||
'@typescript-eslint/no-empty-interface': 2,
|
||||
'@typescript-eslint/no-floating-promises': 2,
|
||||
'@typescript-eslint/no-misused-new': 2,
|
||||
'@typescript-eslint/no-misused-promises': 2,
|
||||
'@typescript-eslint/no-non-null-assertion': 2,
|
||||
'@typescript-eslint/no-parameter-properties': 2,
|
||||
'@typescript-eslint/no-require-imports': 2,
|
||||
'@typescript-eslint/no-unnecessary-type-assertion': 2,
|
||||
'@typescript-eslint/promise-function-async': 2,
|
||||
'@typescript-eslint/require-await': 2,
|
||||
'@typescript-eslint/type-annotation-spacing': 2,
|
||||
'@typescript-eslint/unified-signatures': 2,
|
||||
'@typescript-eslint/no-unused-vars': 2
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
39
.github/workflows/ci.yml
vendored
39
.github/workflows/ci.yml
vendored
@ -1,38 +1,48 @@
|
||||
name: CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
env:
|
||||
MIX_ENV: test
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
services:
|
||||
db:
|
||||
image: postgres:10
|
||||
ports: ['5432:5432']
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
env:
|
||||
POSTGRES_DB: accent_test
|
||||
POSTGRES_PASSWORD: password
|
||||
ports: ["5432:5432"]
|
||||
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
|
||||
|
||||
env:
|
||||
MIX_ENV: test
|
||||
DATABASE_URL: postgres://postgres:password@localhost/accent_test
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/setup-elixir@v1.0.0
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-elixir@v1
|
||||
with:
|
||||
otp-version: 22.x
|
||||
elixir-version: 1.9.x
|
||||
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 10.14.x
|
||||
|
||||
- name: Install System Dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y gcc libyaml-dev python-yaml
|
||||
|
||||
- name: Install Elixir Dependencies
|
||||
run: |
|
||||
mix local.rebar --force
|
||||
mix local.hex --force
|
||||
mix deps.get
|
||||
mix deps.compile
|
||||
|
||||
- name: Install NodeJS Dependencies
|
||||
run: |
|
||||
npm config set spin false
|
||||
@ -40,15 +50,18 @@ jobs:
|
||||
npm i --prefix webapp --no-audit --no-color
|
||||
npm i --prefix cli --no-audit --no-color
|
||||
npm i --prefix jipt --no-audit --no-color
|
||||
|
||||
- name: Build webapp production
|
||||
run: npm run build-production-inline --prefix webapp
|
||||
|
||||
- name: Run Tests
|
||||
run: |
|
||||
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/accent_test mix ecto.setup
|
||||
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/accent_test ./priv/scripts/ci-check.sh
|
||||
mix ecto.setup
|
||||
./priv/scripts/ci-check.sh
|
||||
|
||||
- name: Build CLI
|
||||
run: npm --prefix cli run build
|
||||
- name: Build JIPT
|
||||
run: npm --prefix jipt run build-production-inline
|
||||
- name: Coverage report
|
||||
run: DATABASE_URL=postgresql://postgres:postgres@localhost:5432/accent_test mix coveralls.post --token ${{ secrets.COVERALLS_REPO_TOKEN }} --name 'github-actions' --branch ${{ github.ref }} --committer ${{ github.actor }} --sha ${{ github.sha }}
|
||||
run: mix coveralls.post --token ${{ secrets.COVERALLS_REPO_TOKEN }} --name 'github-actions' --branch ${{ github.ref }} --committer ${{ github.actor }} --sha ${{ github.sha }}
|
||||
|
18
Makefile
18
Makefile
@ -71,7 +71,7 @@ compose-build: ## Build the Docker image from the docker-compose.yml file
|
||||
# ----------
|
||||
|
||||
.PHONY: lint
|
||||
lint: lint-compile lint-format lint-credo lint-eslint lint-prettier lint-tslint lint-template-hbs ## Run lint tools on the code
|
||||
lint: lint-compile lint-format lint-credo lint-eslint lint-prettier lint-template-hbs ## Run lint tools on the code
|
||||
|
||||
.PHONY: lint-compile
|
||||
lint-compile:
|
||||
@ -87,19 +87,19 @@ lint-credo:
|
||||
|
||||
.PHONY: lint-eslint
|
||||
lint-eslint:
|
||||
./node_modules/.bin/eslint webapp/app/. webapp/tests/. cli/. jipt/.
|
||||
|
||||
.PHONY: lint-tslint
|
||||
lint-tslint:
|
||||
./node_modules/.bin/tslint -c tslint.json '{cli,jipt}/src/**/*.{js,ts,json}'
|
||||
npx eslint --ext .js,.ts ./webapp/app ./cli ./jipt
|
||||
|
||||
.PHONY: lint-prettier
|
||||
lint-prettier:
|
||||
./node_modules/.bin/prettier --check './{webapp,jipt,cli}/!(node_modules)/**/*.{js,ts,json,svg,scss,md}' '*.md'
|
||||
npx prettier --check './{webapp,jipt,cli}/!(node_modules)/**/*.{js,ts,json,svg,scss,md}' '*.md'
|
||||
|
||||
.PHONY: lint-template-hbs
|
||||
lint-template-hbs:
|
||||
./node_modules/.bin/ember-template-lint './webapp/app/**/*.hbs' --config-path './webapp/.template-lintrc'
|
||||
npx ember-template-lint './webapp/app/**/*.hbs' --config-path './webapp/.template-lintrc'
|
||||
|
||||
.PHONY: type-check
|
||||
type-check: ## Type-check typescript files
|
||||
cd webapp && npx tsc
|
||||
|
||||
.PHONY: test
|
||||
test: ## Run the test suite
|
||||
@ -118,7 +118,7 @@ format-elixir:
|
||||
|
||||
.PHONY: format-prettier
|
||||
format-prettier:
|
||||
./node_modules/.bin/prettier --write --single-quote --no-bracket-spacing '{webapp,jipt,cli}/*.{js,json}' 'webapp/{app,config}/**/*.{js,ts,json,scss}' 'jipt/src/**/*.{js,ts,json,gql}' 'cli/{examples,src}/**/*.{js,ts,json,gql}' 'README.md'
|
||||
npx prettier --write --single-quote --no-bracket-spacing './{webapp,jipt,cli}/!(node_modules)/**/*.{js,ts,json,svg,scss,md}' '*.md'
|
||||
|
||||
# Development targets
|
||||
# -------------------
|
||||
|
318
cli/package-lock.json
generated
318
cli/package-lock.json
generated
@ -4,29 +4,6 @@
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@fimbul/bifrost": {
|
||||
"version": "0.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@fimbul/bifrost/-/bifrost-0.17.0.tgz",
|
||||
"integrity": "sha512-gVTkJAOef5HtN6LPmrtt5fAUmBywwlgmObsU3FBhPoNeXPLaIl2zywXkJEtvvVLQnaFmtff3x+wIj5lHRCDE3Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@fimbul/ymir": "^0.17.0",
|
||||
"get-caller-file": "^2.0.0",
|
||||
"tslib": "^1.8.1",
|
||||
"tsutils": "^3.5.0"
|
||||
}
|
||||
},
|
||||
"@fimbul/ymir": {
|
||||
"version": "0.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@fimbul/ymir/-/ymir-0.17.0.tgz",
|
||||
"integrity": "sha512-xMXM9KTXRLHLVS6dnX1JhHNEkmWHcAVCQ/4+DA1KKwC/AFnGHzu/7QfQttEPgw3xplT+ILf9e3i64jrFwB3JtA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"inversify": "^5.0.0",
|
||||
"reflect-metadata": "^0.1.12",
|
||||
"tslib": "^1.8.1"
|
||||
}
|
||||
},
|
||||
"@mrmlnc/readdir-enhanced": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz",
|
||||
@ -565,16 +542,6 @@
|
||||
"fancy-test": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"@oclif/tslint": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@oclif/tslint/-/tslint-3.1.1.tgz",
|
||||
"integrity": "sha512-B1ZWbgzwxDhNZLzVnn+JjyFf9u+J9wNwsz/ZX9YvA9edRYcdiJz9JikCttGPi35V0NU0TUV4UqTqo/q/wQ06jQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslint-eslint-rules": "^5.4.0",
|
||||
"tslint-xo": "^0.9.0"
|
||||
}
|
||||
},
|
||||
"@types/chai": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.2.tgz",
|
||||
@ -673,15 +640,6 @@
|
||||
"resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz",
|
||||
"integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk="
|
||||
},
|
||||
"argparse": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"sprintf-js": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"arr-diff": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
|
||||
@ -750,65 +708,6 @@
|
||||
"integrity": "sha1-GcenYEc3dEaPILLS0DNyrX1Mv10=",
|
||||
"dev": true
|
||||
},
|
||||
"babel-code-frame": {
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
|
||||
"integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^1.1.3",
|
||||
"esutils": "^2.0.2",
|
||||
"js-tokens": "^3.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
|
||||
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
|
||||
"dev": true
|
||||
},
|
||||
"chalk": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
|
||||
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^2.2.1",
|
||||
"escape-string-regexp": "^1.0.2",
|
||||
"has-ansi": "^2.0.0",
|
||||
"strip-ansi": "^3.0.0",
|
||||
"supports-color": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"esutils": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
|
||||
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
|
||||
"dev": true
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
|
||||
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
@ -1364,16 +1263,6 @@
|
||||
"path-type": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"doctrine": {
|
||||
"version": "0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-0.7.2.tgz",
|
||||
"integrity": "sha1-fLhgNZujvpDgQLJrcpzkv6ZUxSM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"esutils": "^1.1.6",
|
||||
"isarray": "0.0.1"
|
||||
}
|
||||
},
|
||||
"end-of-stream": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
|
||||
@ -1402,12 +1291,6 @@
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz",
|
||||
"integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw=="
|
||||
},
|
||||
"esutils": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/esutils/-/esutils-1.1.6.tgz",
|
||||
"integrity": "sha1-wBzKqa5LiXxtDD4hCuUvPHqEQ3U=",
|
||||
"dev": true
|
||||
},
|
||||
"execa": {
|
||||
"version": "0.10.0",
|
||||
"resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz",
|
||||
@ -1693,12 +1576,6 @@
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
||||
},
|
||||
"get-caller-file": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.1.tgz",
|
||||
"integrity": "sha512-SpOZHfz845AH0wJYVuZk2jWDqFmu7Xubsx+ldIpwzy5pDUpu7OJHK7QYNSA2NPlDSKQwM1GFaAkciOWjjW92Sg==",
|
||||
"dev": true
|
||||
},
|
||||
"get-func-name": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
|
||||
@ -1783,23 +1660,6 @@
|
||||
"integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==",
|
||||
"dev": true
|
||||
},
|
||||
"has-ansi": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
|
||||
"integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
@ -1898,12 +1758,6 @@
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||
},
|
||||
"inversify": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/inversify/-/inversify-5.0.1.tgz",
|
||||
"integrity": "sha512-Ieh06s48WnEYGcqHepdsJUIJUXpwH5o5vodAX+DK2JA/gjy4EbEcQZxw+uFfzysmKjiLXGYwNG3qDZsKVMcINQ==",
|
||||
"dev": true
|
||||
},
|
||||
"is-accessor-descriptor": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
|
||||
@ -2055,12 +1909,6 @@
|
||||
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
|
||||
"integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0="
|
||||
},
|
||||
"isarray": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
||||
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
|
||||
"dev": true
|
||||
},
|
||||
"isexe": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
@ -2072,22 +1920,6 @@
|
||||
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
|
||||
"dev": true
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
|
||||
"integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
|
||||
"dev": true
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "3.12.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz",
|
||||
"integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"json-parse-better-errors": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
|
||||
@ -2553,12 +2385,6 @@
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
|
||||
"integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
|
||||
},
|
||||
"path-parse": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
||||
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
|
||||
"dev": true
|
||||
},
|
||||
"path-type": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
|
||||
@ -2676,12 +2502,6 @@
|
||||
"esprima": "~4.0.0"
|
||||
}
|
||||
},
|
||||
"reflect-metadata": {
|
||||
"version": "0.1.13",
|
||||
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
|
||||
"integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==",
|
||||
"dev": true
|
||||
},
|
||||
"regex-not": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
|
||||
@ -2704,15 +2524,6 @@
|
||||
"integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
|
||||
"dev": true
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz",
|
||||
"integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"path-parse": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"resolve-url": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
|
||||
@ -3030,12 +2841,6 @@
|
||||
"extend-shallow": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"sprintf-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
|
||||
"dev": true
|
||||
},
|
||||
"static-extend": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
|
||||
@ -3303,129 +3108,6 @@
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
|
||||
"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ=="
|
||||
},
|
||||
"tslint": {
|
||||
"version": "5.11.0",
|
||||
"resolved": "https://registry.npmjs.org/tslint/-/tslint-5.11.0.tgz",
|
||||
"integrity": "sha1-mPMMAurjzecAYgHkwzywi0hYHu0=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"babel-code-frame": "^6.22.0",
|
||||
"builtin-modules": "^1.1.1",
|
||||
"chalk": "^2.3.0",
|
||||
"commander": "^2.12.1",
|
||||
"diff": "^3.2.0",
|
||||
"glob": "^7.1.1",
|
||||
"js-yaml": "^3.7.0",
|
||||
"minimatch": "^3.0.4",
|
||||
"resolve": "^1.3.2",
|
||||
"semver": "^5.3.0",
|
||||
"tslib": "^1.8.0",
|
||||
"tsutils": "^2.27.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"commander": {
|
||||
"version": "2.19.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz",
|
||||
"integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==",
|
||||
"dev": true
|
||||
},
|
||||
"tsutils": {
|
||||
"version": "2.29.0",
|
||||
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz",
|
||||
"integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "^1.8.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tslint-config-prettier": {
|
||||
"version": "1.17.0",
|
||||
"resolved": "https://registry.npmjs.org/tslint-config-prettier/-/tslint-config-prettier-1.17.0.tgz",
|
||||
"integrity": "sha512-NKWNkThwqE4Snn4Cm6SZB7lV5RMDDFsBwz6fWUkTxOKGjMx8ycOHnjIbhn7dZd5XmssW3CwqUjlANR6EhP9YQw=="
|
||||
},
|
||||
"tslint-consistent-codestyle": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/tslint-consistent-codestyle/-/tslint-consistent-codestyle-1.15.0.tgz",
|
||||
"integrity": "sha512-6BNDBbZh2K0ibRXe70Mkl9gfVttxQ3t3hqV1BRDfpIcjrUoOgD946iH4SrXp+IggDgeMs3dJORjD5tqL5j4jXg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@fimbul/bifrost": "^0.17.0",
|
||||
"tslib": "^1.7.1",
|
||||
"tsutils": "^2.29.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"tsutils": {
|
||||
"version": "2.29.0",
|
||||
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz",
|
||||
"integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "^1.8.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tslint-eslint-rules": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/tslint-eslint-rules/-/tslint-eslint-rules-5.4.0.tgz",
|
||||
"integrity": "sha512-WlSXE+J2vY/VPgIcqQuijMQiel+UtmXS+4nvK4ZzlDiqBfXse8FAvkNnTcYhnQyOTW5KFM+uRRGXxYhFpuBc6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"doctrine": "0.7.2",
|
||||
"tslib": "1.9.0",
|
||||
"tsutils": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.0.tgz",
|
||||
"integrity": "sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"tslint-microsoft-contrib": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/tslint-microsoft-contrib/-/tslint-microsoft-contrib-5.2.1.tgz",
|
||||
"integrity": "sha512-PDYjvpo0gN9IfMULwKk0KpVOPMhU6cNoT9VwCOLeDl/QS8v8W2yspRpFFuUS7/c5EIH/n8ApMi8TxJAz1tfFUA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tsutils": "^2.27.2 <2.29.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"tsutils": {
|
||||
"version": "2.28.0",
|
||||
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.28.0.tgz",
|
||||
"integrity": "sha512-bh5nAtW0tuhvOJnx1GLRn5ScraRLICGyJV5wJhtRWOLsxW70Kk5tZtpK3O/hW6LDnqKS9mlUMPZj9fEMJ0gxqA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "^1.8.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tslint-xo": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/tslint-xo/-/tslint-xo-0.9.0.tgz",
|
||||
"integrity": "sha512-Zk5jBdQVUaHEmR9TUoh1TJOjjCr7/nRplA+jDZBvucyBMx65pt0unTr6H/0HvrtSlucFvOMYsyBZE1W8b4AOig==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslint-consistent-codestyle": "^1.11.0",
|
||||
"tslint-eslint-rules": "^5.3.1",
|
||||
"tslint-microsoft-contrib": "^5.0.2"
|
||||
}
|
||||
},
|
||||
"tsutils": {
|
||||
"version": "3.7.0",
|
||||
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.7.0.tgz",
|
||||
"integrity": "sha512-n+e+3q7Jx2kfZw7tjfI9axEIWBY0sFMOlC+1K70X0SeXpO/UYSB+PN+E9tIJNqViB7oiXQdqD7dNchnvoneZew==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "^1.8.1"
|
||||
}
|
||||
},
|
||||
"tunnel-agent": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||
|
@ -23,13 +23,11 @@
|
||||
"form-data": "2.3.3",
|
||||
"glob": "7.1.3",
|
||||
"node-fetch": "2.3.0",
|
||||
"tslib": "1.9.3",
|
||||
"tslint-config-prettier": "1.17.0"
|
||||
"tslib": "1.9.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@oclif/dev-cli": "1.21.0",
|
||||
"@oclif/test": "1.0.1",
|
||||
"@oclif/tslint": "3.1.1",
|
||||
"@types/chai": "4.1.2",
|
||||
"@types/mkdirp": "0.5.2",
|
||||
"@types/mocha": "5.0.0",
|
||||
@ -38,7 +36,6 @@
|
||||
"globby": "8.0.1",
|
||||
"mocha": "5.0.5",
|
||||
"ts-node": "7.0.1",
|
||||
"tslint": "5.11.0",
|
||||
"typescript": "3.2.2"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -11,7 +11,7 @@ import ProjectFetcher from './services/project-fetcher';
|
||||
// Types
|
||||
import {Project} from './types/project';
|
||||
|
||||
const sleep = (ms: number) =>
|
||||
const sleep = async (ms: number) =>
|
||||
new Promise((resolve: () => void) => setTimeout(resolve, ms));
|
||||
|
||||
export default abstract class extends Command {
|
||||
|
@ -24,8 +24,8 @@ export default class Export extends Command {
|
||||
'order-by': flags.string({
|
||||
default: 'index',
|
||||
description: 'Will be used in the export call as the order of the keys',
|
||||
options: ['index', 'key-asc']
|
||||
})
|
||||
options: ['index', 'key-asc'],
|
||||
}),
|
||||
};
|
||||
|
||||
async run() {
|
||||
@ -43,9 +43,9 @@ export default class Export extends Command {
|
||||
const targets = new DocumentPathsFetcher().fetch(this.project!, document);
|
||||
|
||||
await Promise.all(
|
||||
targets.map(({path, language, documentPath}) => {
|
||||
targets.map(async ({path, language, documentPath}) => {
|
||||
const localFile = document.fetchLocalFile(documentPath, path);
|
||||
if (!localFile) return new Promise(resolve => resolve());
|
||||
if (!localFile) return new Promise((resolve) => resolve());
|
||||
formatter.log(localFile);
|
||||
|
||||
return document.export(localFile, language, documentPath, flags);
|
||||
|
@ -22,8 +22,8 @@ export default class Jipt extends Command {
|
||||
{
|
||||
description: 'The pseudo language for in-place-translation-editing',
|
||||
name: 'pseudoLanguageName',
|
||||
required: true
|
||||
}
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
static flags = {};
|
||||
|
||||
@ -45,7 +45,7 @@ export default class Jipt extends Command {
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
targets.map(({path, documentPath}) => {
|
||||
targets.map(async ({path, documentPath}) => {
|
||||
formatter.log(path);
|
||||
|
||||
return document.exportJipt(path, documentPath);
|
||||
|
@ -9,9 +9,11 @@ export default class Stats extends Command {
|
||||
|
||||
static examples = [`$ accent stats`];
|
||||
|
||||
/* eslint-disable @typescript-eslint/require-await */
|
||||
async run() {
|
||||
const formatter = new Formatter(this.project!);
|
||||
|
||||
formatter.log();
|
||||
}
|
||||
/* eslint-enable @typescript-eslint/require-await */
|
||||
}
|
||||
|
@ -32,28 +32,29 @@ export default class Sync extends Command {
|
||||
static flags = {
|
||||
'add-translations': flags.boolean({
|
||||
description:
|
||||
'Add translations in Accent to help translators if you already have translated strings'
|
||||
'Add translations in Accent to help translators if you already have translated strings',
|
||||
}),
|
||||
'dry-run': flags.boolean({
|
||||
default: false,
|
||||
description: 'Do not write the file from the export _after_ the operation'
|
||||
description:
|
||||
'Do not write the file from the export _after_ the operation',
|
||||
}),
|
||||
'merge-type': flags.string({
|
||||
default: 'smart',
|
||||
description:
|
||||
'Will be used in the add translations call as the "merge_type" param',
|
||||
options: ['smart', 'passive', 'force']
|
||||
options: ['smart', 'passive', 'force'],
|
||||
}),
|
||||
'order-by': flags.string({
|
||||
default: 'index',
|
||||
description: 'Will be used in the export call as the order of the keys',
|
||||
options: ['index', 'key-asc']
|
||||
options: ['index', 'key-asc'],
|
||||
}),
|
||||
'sync-type': flags.string({
|
||||
default: 'smart',
|
||||
description: 'Will be used in the sync call as the "sync_type" param',
|
||||
options: ['smart', 'passive']
|
||||
})
|
||||
options: ['smart', 'passive'],
|
||||
}),
|
||||
};
|
||||
|
||||
async run() {
|
||||
@ -98,9 +99,9 @@ export default class Sync extends Command {
|
||||
const targets = new DocumentPathsFetcher().fetch(this.project!, document);
|
||||
|
||||
await Promise.all(
|
||||
targets.map(({path, language, documentPath}) => {
|
||||
targets.map(async ({path, language, documentPath}) => {
|
||||
const localFile = document.fetchLocalFile(documentPath, path);
|
||||
if (!localFile) return new Promise(resolve => resolve());
|
||||
if (!localFile) return new Promise((resolve) => resolve());
|
||||
|
||||
formatter.log(localFile);
|
||||
|
||||
@ -116,7 +117,7 @@ export default class Sync extends Command {
|
||||
const {flags} = this.parse(Sync);
|
||||
const formatter = new CommitOperationFormatter();
|
||||
|
||||
return document.paths.map(async path => {
|
||||
return document.paths.map(async (path) => {
|
||||
const operations = await document.sync(this.project!, path, flags);
|
||||
const documentPath = document.parseDocumentName(path, document.config);
|
||||
|
||||
|
@ -35,7 +35,7 @@ export default class ConfigFetcher {
|
||||
|
||||
files(): Document[] {
|
||||
return this.config.files.map(
|
||||
documentConfig => new Document(documentConfig, this.config)
|
||||
(documentConfig) => new Document(documentConfig, this.config)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ export default class DocumentJiptPathsFetcher {
|
||||
): DocumentPath[] {
|
||||
return project.documents.entries
|
||||
.map(({path}) => path)
|
||||
.map(path => {
|
||||
.map((path) => {
|
||||
const parsedTarget = document.target
|
||||
.replace('%slug%', pseudoLanguageName)
|
||||
.replace('%original_file_name%', path);
|
||||
@ -19,7 +19,7 @@ export default class DocumentJiptPathsFetcher {
|
||||
return {
|
||||
documentPath: path,
|
||||
language: pseudoLanguageName,
|
||||
path: parsedTarget
|
||||
path: parsedTarget,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ export default class DocumentPathsFetcher {
|
||||
const documentPaths = project.documents.entries.map(({path}) => path);
|
||||
|
||||
return languageSlugs.reduce((memo: DocumentPath[], slug) => {
|
||||
documentPaths.forEach(path => {
|
||||
documentPaths.forEach((path) => {
|
||||
const parsedTarget = document.target
|
||||
.replace('%slug%', slug)
|
||||
.replace('%original_file_name%', path)
|
||||
|
@ -17,9 +17,11 @@ import {Project} from '../types/project';
|
||||
|
||||
const enum OperationName {
|
||||
Sync = 'sync',
|
||||
AddTranslation = 'addTranslations'
|
||||
AddTranslation = 'addTranslations',
|
||||
}
|
||||
|
||||
const ERROR_THRESHOLD_STATUS_CODE = 400;
|
||||
|
||||
export default class Document {
|
||||
paths: string[];
|
||||
readonly apiKey: string;
|
||||
@ -40,7 +42,7 @@ export default class Document {
|
||||
}
|
||||
|
||||
async sync(project: Project, file: string, options: any) {
|
||||
const masterLanguage = fetchFromRevision(project!.masterRevision);
|
||||
const masterLanguage = fetchFromRevision(project.masterRevision);
|
||||
const formData = new FormData();
|
||||
formData.append('file', fs.createReadStream(file));
|
||||
formData.append('document_path', this.parseDocumentName(file, this.config));
|
||||
@ -56,7 +58,7 @@ export default class Document {
|
||||
const response = await fetch(url, {
|
||||
body: formData,
|
||||
headers: this.authorizationHeader(),
|
||||
method: 'POST'
|
||||
method: 'POST',
|
||||
});
|
||||
|
||||
return this.handleResponse(response, options, OperationName.Sync);
|
||||
@ -84,7 +86,7 @@ export default class Document {
|
||||
const response = await fetch(url, {
|
||||
body: formData,
|
||||
headers: this.authorizationHeader(),
|
||||
method: 'POST'
|
||||
method: 'POST',
|
||||
});
|
||||
|
||||
return this.handleResponse(response, options, OperationName.AddTranslation);
|
||||
@ -110,12 +112,12 @@ export default class Document {
|
||||
['document_path', documentPath],
|
||||
['document_format', this.config.format],
|
||||
['order_by', options['order-by']],
|
||||
['language', language]
|
||||
['language', language],
|
||||
];
|
||||
|
||||
const url = `${this.apiUrl}/export?${this.encodeQuery(query)}`;
|
||||
const response = await fetch(url, {
|
||||
headers: this.authorizationHeader()
|
||||
headers: this.authorizationHeader(),
|
||||
});
|
||||
|
||||
return this.writeResponseToFile(response, file);
|
||||
@ -124,12 +126,12 @@ export default class Document {
|
||||
async exportJipt(file: string, documentPath: string) {
|
||||
const query = [
|
||||
['document_path', documentPath],
|
||||
['document_format', this.config.format]
|
||||
['document_format', this.config.format],
|
||||
];
|
||||
|
||||
const url = `${this.apiUrl}/jipt-export?${this.encodeQuery(query)}`;
|
||||
const response = await fetch(url, {
|
||||
headers: this.authorizationHeader()
|
||||
headers: this.authorizationHeader(),
|
||||
});
|
||||
|
||||
return this.writeResponseToFile(response, file);
|
||||
@ -182,7 +184,7 @@ export default class Document {
|
||||
return config;
|
||||
}
|
||||
|
||||
private writeResponseToFile(response: Response, file: string) {
|
||||
private async writeResponseToFile(response: Response, file: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
mkdirp.sync(path.dirname(file));
|
||||
|
||||
@ -199,7 +201,7 @@ export default class Document {
|
||||
operationName: OperationName
|
||||
): Promise<OperationResponse> {
|
||||
if (!options['dry-run']) {
|
||||
if (response.status >= 400) {
|
||||
if (response.status >= ERROR_THRESHOLD_STATUS_CODE) {
|
||||
return {[operationName]: {success: false}, peek: false};
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@ export default class HookRunnerFomatter {
|
||||
log(name: string, commands: string[]) {
|
||||
const operation = capitalizeFirstLetter(decamelize(name, ' '));
|
||||
console.log(chalk.yellow('➤ '), chalk.bold(chalk.yellow(`${operation}:`)));
|
||||
commands.forEach(command => {
|
||||
commands.forEach((command) => {
|
||||
console.log(' ', chalk.yellow(command));
|
||||
});
|
||||
console.log('');
|
||||
|
@ -11,7 +11,7 @@ export default class ProjectAddTranslationsFormatter {
|
||||
log(project: Project) {
|
||||
const languages = project.revisions
|
||||
.filter(
|
||||
revision =>
|
||||
(revision) =>
|
||||
fetchFromRevision(revision) !==
|
||||
fetchFromRevision(project.masterRevision)
|
||||
)
|
||||
|
@ -7,7 +7,7 @@ import {Document, Project, Revision} from '../../types/project';
|
||||
// Services
|
||||
import {
|
||||
fetchFromRevision,
|
||||
fetchNameFromRevision
|
||||
fetchNameFromRevision,
|
||||
} from '../revision-slug-fetcher';
|
||||
|
||||
export default class ProjectStatsFormatter {
|
||||
|
@ -17,6 +17,7 @@ export default class HookRunner {
|
||||
this.hooks = document.config.hooks;
|
||||
}
|
||||
|
||||
/* eslint-disable @typescript-eslint/require-await */
|
||||
async run(name: Hooks) {
|
||||
if (!this.hooks) return null;
|
||||
const hooks = this.hooks[name];
|
||||
@ -29,4 +30,5 @@ export default class HookRunner {
|
||||
|
||||
return this.document.refreshPaths();
|
||||
}
|
||||
/* eslint-disable @typescript-eslint/require-await */
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ export default class ProjectFetcher {
|
||||
return data.data && data.data.viewer.project;
|
||||
}
|
||||
|
||||
private graphql(config: Config) {
|
||||
private async graphql(config: Config) {
|
||||
const query = `query ProjectDetails($project_id: ID!) {
|
||||
viewer {
|
||||
project(id: $project_id) {
|
||||
@ -68,9 +68,9 @@ export default class ProjectFetcher {
|
||||
body: JSON.stringify({query}),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
authorization: `Bearer ${config.apiKey}`
|
||||
authorization: `Bearer ${config.apiKey}`,
|
||||
},
|
||||
method: 'POST'
|
||||
method: 'POST',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -4,13 +4,13 @@ export enum Hooks {
|
||||
beforeExport = 'beforeExport',
|
||||
afterExport = 'afterExport',
|
||||
beforeSync = 'beforeSync',
|
||||
afterSync = 'afterSync'
|
||||
afterSync = 'afterSync',
|
||||
}
|
||||
|
||||
export enum NamePattern {
|
||||
file = 'file',
|
||||
fileWithSlugSuffix = 'fileWithSlugSuffix',
|
||||
parentDirectory = 'parentDirectory'
|
||||
parentDirectory = 'parentDirectory',
|
||||
}
|
||||
|
||||
export interface HookConfig {
|
||||
|
@ -1,4 +1,4 @@
|
||||
export interface OperationResponse {
|
||||
peek: any;
|
||||
[x: string]: any;
|
||||
peek: any;
|
||||
}
|
||||
|
@ -1,3 +1,3 @@
|
||||
import Accent from './src/accent';
|
||||
|
||||
window['accent'].q.forEach(([fun, args]) => Accent[fun](args));
|
||||
window['accent'].q.forEach(([fun, args]) => Accent[fun](args)); // eslint-disable-line dot-notation
|
||||
|
2810
jipt/package-lock.json
generated
2810
jipt/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -14,7 +14,7 @@ export interface Config {
|
||||
const state = new State({
|
||||
nodes: new WeakMap(),
|
||||
projectTranslations: {},
|
||||
refs: new Map()
|
||||
refs: new Map(),
|
||||
});
|
||||
|
||||
const liveNode = new LiveNode(state);
|
||||
@ -39,7 +39,7 @@ export const Accent = {
|
||||
|
||||
const mutation = new Mutation(liveNode);
|
||||
mutation.bindEvents();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default Accent;
|
||||
|
@ -10,7 +10,7 @@ const enum ACTIONS {
|
||||
redirectIfEmbedded = 'redirectIfEmbedded',
|
||||
login = 'login',
|
||||
loggedIn = 'loggedIn',
|
||||
changeText = 'changeText'
|
||||
changeText = 'changeText',
|
||||
}
|
||||
|
||||
interface Props {
|
||||
|
@ -24,7 +24,7 @@ export default class LiveNode {
|
||||
}
|
||||
|
||||
matchAttributes(node: Element) {
|
||||
Array.from(node.attributes).forEach(attribute => {
|
||||
Array.from(node.attributes).forEach((attribute) => {
|
||||
const translation = this.findTranslationByValue(attribute.value);
|
||||
if (!translation || !translation.text) return;
|
||||
|
||||
@ -34,7 +34,7 @@ export default class LiveNode {
|
||||
attribute.value = newAttribute;
|
||||
|
||||
this.state.addReference(node, translation, {
|
||||
attributeName: attribute.name
|
||||
attributeName: attribute.name,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -12,6 +12,12 @@ interface Translation {
|
||||
window nodes on mutation and messages FROM the Accent client.
|
||||
*/
|
||||
export default class Mutation {
|
||||
private readonly liveNode: LiveNode;
|
||||
|
||||
constructor(liveNode: LiveNode) {
|
||||
this.liveNode = liveNode;
|
||||
}
|
||||
|
||||
static nodeChange(node: Element, meta: any, text: string) {
|
||||
this.textNodeChange(node, meta, text);
|
||||
this.attributeNodeChange(node, meta, text);
|
||||
@ -55,12 +61,6 @@ export default class Mutation {
|
||||
}, NODE_UPDATE_STYLE_TIMEOUT);
|
||||
}
|
||||
|
||||
private readonly liveNode: LiveNode;
|
||||
|
||||
constructor(liveNode: LiveNode) {
|
||||
this.liveNode = liveNode;
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
const onMutation = (instance: MutationRecord[]) => {
|
||||
return instance.forEach(this.handleNodeMutation.bind(this));
|
||||
@ -71,7 +71,7 @@ export default class Mutation {
|
||||
characterData: true,
|
||||
characterDataOldValue: true,
|
||||
childList: true,
|
||||
subtree: true
|
||||
subtree: true,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,8 @@ interface Props {
|
||||
state: State;
|
||||
}
|
||||
|
||||
const CENTER_OFFSET = 6;
|
||||
|
||||
/*
|
||||
The Pin component serves as the entrypoint for the user. The element it creates
|
||||
is responsible to sending messages to the Accent UI.
|
||||
@ -34,7 +36,7 @@ export default class Pin {
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
this.element.addEventListener('click', event => {
|
||||
this.element.addEventListener('click', (event) => {
|
||||
const target = event.target as HTMLElement;
|
||||
|
||||
if (target.dataset.id) {
|
||||
@ -47,7 +49,7 @@ export default class Pin {
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('mouseover', event => {
|
||||
document.addEventListener('mouseover', (event) => {
|
||||
const node = event.target as HTMLElement;
|
||||
|
||||
if (this.liveNode.isLive(node)) this.showFor(node);
|
||||
@ -61,7 +63,9 @@ export default class Pin {
|
||||
);
|
||||
styles.set(
|
||||
this.element,
|
||||
`top: ${top + height - 6}px; left: ${left - 6}px; ${styles.pin}`
|
||||
`top: ${top + height - CENTER_OFFSET}px; left: ${
|
||||
left - CENTER_OFFSET
|
||||
}px; ${styles.pin}`
|
||||
);
|
||||
|
||||
const ids = keys
|
||||
|
@ -1,5 +1,5 @@
|
||||
const BASE = 36;
|
||||
const LENGTH = 8;
|
||||
|
||||
/* Random class that should not conflict with the parent window styles */
|
||||
export default () =>
|
||||
`acc_${Math.random()
|
||||
.toString(36)
|
||||
.substring(8)}`;
|
||||
export default () => `acc_${Math.random().toString(BASE).substring(LENGTH)}`;
|
||||
|
@ -52,5 +52,5 @@ export default {
|
||||
set,
|
||||
translationNode,
|
||||
translationNodeConflicted,
|
||||
translationNodeUpdated
|
||||
translationNodeUpdated,
|
||||
};
|
||||
|
@ -144,9 +144,7 @@ export default class UI {
|
||||
element.innerHTML = `
|
||||
<div class="${DISABLE_CLASS}" style="${styles.frameDisableButton}">×</div>
|
||||
<div class="${EXPAND_CLASS}" style="${styles.frameExpandButton}"></div>
|
||||
<div class="${COLLAPSE_CLASS}" style="${
|
||||
styles.frameCollapseButton
|
||||
}">×</div>
|
||||
<div class="${COLLAPSE_CLASS}" style="${styles.frameCollapseButton}">×</div>
|
||||
`;
|
||||
|
||||
return element;
|
||||
|
@ -8,6 +8,10 @@ defmodule Accent.Project do
|
||||
field(:last_synced_at, :utc_datetime)
|
||||
field(:locked_file_operations, :boolean, default: false)
|
||||
|
||||
field(:translations_count, :integer, virtual: true, default: :not_loaded)
|
||||
field(:reviewed_count, :integer, virtual: true, default: :not_loaded)
|
||||
field(:conflicts_count, :integer, virtual: true, default: :not_loaded)
|
||||
|
||||
has_many(:integrations, Accent.Integration)
|
||||
has_many(:revisions, Accent.Revision)
|
||||
has_many(:target_revisions, Accent.Revision, where: [master: false])
|
||||
|
@ -1,4 +1,6 @@
|
||||
defmodule Accent.Scopes.Project do
|
||||
import Ecto.Query
|
||||
|
||||
@doc """
|
||||
## Examples
|
||||
|
||||
@ -15,4 +17,35 @@ defmodule Accent.Scopes.Project do
|
||||
def from_search(query, term) do
|
||||
Accent.Scopes.Search.from_search(query, term, :name)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Fill `translations_count`, `conflicts_count` and `reviewed_count` for projects.
|
||||
"""
|
||||
@spec with_stats(Ecto.Queryable.t()) :: Ecto.Queryable.t()
|
||||
def with_stats(query) do
|
||||
translations =
|
||||
from(
|
||||
t in Accent.Translation,
|
||||
inner_join: revisions in assoc(t, :revision),
|
||||
select: %{field_id: revisions.project_id, count: count(t)},
|
||||
where: [removed: false, locked: false],
|
||||
where: is_nil(t.version_id),
|
||||
group_by: revisions.project_id
|
||||
)
|
||||
|
||||
reviewed = from(translations, where: [conflicted: false])
|
||||
|
||||
from(
|
||||
projects in query,
|
||||
left_join: translations in subquery(translations),
|
||||
on: translations.field_id == projects.id,
|
||||
left_join: reviewed in subquery(reviewed),
|
||||
on: reviewed.field_id == projects.id,
|
||||
select_merge: %{
|
||||
translations_count: coalesce(translations.count, 0),
|
||||
reviewed_count: coalesce(reviewed.count, 0),
|
||||
conflicts_count: coalesce(translations.count, 0) - coalesce(reviewed.count, 0)
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -62,7 +62,7 @@ defmodule Accent.Scopes.Revision do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Fill `translations_count`, `conflicts_count` and `reviewed_count` for documents.
|
||||
Fill `translations_count`, `conflicts_count` and `reviewed_count` for revisions.
|
||||
"""
|
||||
@spec with_stats(Ecto.Queryable.t()) :: Ecto.Queryable.t()
|
||||
def with_stats(query) do
|
||||
|
@ -3,12 +3,29 @@ defmodule Accent.Scopes.TranslationsCount do
|
||||
|
||||
def with_stats(query, column, options \\ []) do
|
||||
exclude_empty_translations = Keyword.get(options, :exclude_empty_translations, false)
|
||||
translations = countable_translations_query(column)
|
||||
|
||||
query
|
||||
|> count_translations(translations, exclude_empty_translations)
|
||||
|> count_reviewed(translations)
|
||||
|> merge_select()
|
||||
translations =
|
||||
from(
|
||||
t in Accent.Translation,
|
||||
select: %{field_id: field(t, ^column), count: count(t)},
|
||||
where: [removed: false, locked: false],
|
||||
where: is_nil(t.version_id),
|
||||
group_by: field(t, ^column)
|
||||
)
|
||||
|
||||
query =
|
||||
query
|
||||
|> count_translations(translations, exclude_empty_translations)
|
||||
|> count_reviewed(translations)
|
||||
|
||||
from(
|
||||
[translations: t, reviewed: r] in query,
|
||||
select_merge: %{
|
||||
translations_count: coalesce(t.count, 0),
|
||||
reviewed_count: coalesce(r.count, 0),
|
||||
conflicts_count: coalesce(t.count, 0) - coalesce(r.count, 0)
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
defp count_translations(query, translations, _exclude_empty_translations = true) do
|
||||
@ -23,25 +40,4 @@ defmodule Accent.Scopes.TranslationsCount do
|
||||
reviewed = from(translations, where: [conflicted: false])
|
||||
from(q in query, left_join: translations in subquery(reviewed), as: :reviewed, on: translations.field_id == q.id)
|
||||
end
|
||||
|
||||
defp merge_select(query) do
|
||||
from(
|
||||
[translations: t, reviewed: r] in query,
|
||||
select_merge: %{
|
||||
translations_count: coalesce(t.count, 0),
|
||||
reviewed_count: coalesce(r.count, 0),
|
||||
conflicts_count: coalesce(t.count, 0) - coalesce(r.count, 0)
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
defp countable_translations_query(column) do
|
||||
from(
|
||||
t in Accent.Translation,
|
||||
select: %{field_id: field(t, ^column), count: count(t)},
|
||||
where: [removed: false, locked: false],
|
||||
where: is_nil(t.version_id),
|
||||
group_by: field(t, ^column)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -78,6 +78,7 @@ defmodule Accent.GraphQL.Resolvers.Project do
|
||||
|> Query.where([_, c], c.user_id == ^viewer.id)
|
||||
|> Query.order_by([p, _], asc: p.name)
|
||||
|> ProjectScope.from_search(args[:query])
|
||||
|> ProjectScope.with_stats()
|
||||
|> Repo.paginate(page: args[:page])
|
||||
|> Paginated.format()
|
||||
|> (&{:ok, &1}).()
|
||||
@ -86,6 +87,7 @@ defmodule Accent.GraphQL.Resolvers.Project do
|
||||
@spec show_viewer(any(), %{id: String.t()}, GraphQLContext.t()) :: {:ok, Project.t() | nil}
|
||||
def show_viewer(_, %{id: id}, _) do
|
||||
Project
|
||||
|> ProjectScope.with_stats()
|
||||
|> Repo.get(id)
|
||||
|> (&{:ok, &1}).()
|
||||
end
|
||||
|
@ -16,6 +16,11 @@ defmodule Accent.GraphQL.Types.Project do
|
||||
field(:main_color, :string)
|
||||
field(:last_synced_at, :datetime)
|
||||
field(:logo, :string)
|
||||
|
||||
field(:translations_count, non_null(:integer))
|
||||
field(:conflicts_count, non_null(:integer))
|
||||
field(:reviewed_count, non_null(:integer))
|
||||
|
||||
field(:last_activity, :activity, resolve: &Accent.GraphQL.Resolvers.Project.last_activity/3)
|
||||
field(:is_file_operations_locked, non_null(:boolean), resolve: field_alias(:locked_file_operations))
|
||||
|
||||
|
139
mix.lock
139
mix.lock
@ -1,74 +1,73 @@
|
||||
%{
|
||||
"absinthe": {:hex, :absinthe, "1.4.16", "0933e4d9f12652b12115d5709c0293a1bf78a22578032e9ad0dad4efee6b9eb1", [:mix], [{:dataloader, "~> 1.0.0", [hex: :dataloader, repo: "hexpm", optional: true]}, {:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"absinthe_error_payload": {:hex, :absinthe_error_payload, "1.1.1", "48c89baaff23c8fdf93313a3e2637648456a60f010e5cde34b1ed3309feceabc", [:make, :mix], [{:absinthe, "~> 1.3", [hex: :absinthe, repo: "hexpm", optional: false]}, {:ecto, "~> 3.1", [hex: :ecto, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"absinthe_plug": {:hex, :absinthe_plug, "1.4.7", "939b6b9e1c7abc6b399a5b49faa690a1fbb55b195c670aa35783b14b08ccec7a", [:mix], [{:absinthe, "~> 1.4.11", [hex: :absinthe, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.2 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"bamboo": {:hex, :bamboo, "1.4.0", "7b9201c49a843e4802061cf45692405b2c00efcf1cebf8b7b64f015ead072392", [:mix], [{:hackney, ">= 1.13.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"bamboo_smtp": {:hex, :bamboo_smtp, "2.1.0", "4be58f3c51d9f7875dc169ae58a1d2f08e5b718bf3895f70d130548c0598f422", [:mix], [{:bamboo, "~> 1.2", [hex: :bamboo, repo: "hexpm", optional: false]}, {:gen_smtp, "~> 0.15.0", [hex: :gen_smtp, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"},
|
||||
"canada": {:hex, :canada, "1.0.2", "040e4c47609b0a67d5773ac1fbe5e99f840cef173d69b739beda7c98453e0770", [:mix], [], "hexpm"},
|
||||
"canary": {:hex, :canary, "1.1.1", "4138d5e05db8497c477e4af73902eb9ae06e49dceaa13c2dd9f0b55525ded48b", [:mix], [{:canada, "~> 1.0.1", [hex: :canada, repo: "hexpm", optional: false]}, {:ecto, ">= 1.1.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm"},
|
||||
"corsica": {:hex, :corsica, "1.1.3", "5f1de40bc9285753aa03afbdd10c364dac79b2ddbf2ba9c5c9c47b397ec06f40", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"cowboy": {:hex, :cowboy, "2.7.0", "91ed100138a764355f43316b1d23d7ff6bdb0de4ea618cb5d8677c93a7a2f115", [:rebar3], [{:cowlib, "~> 2.8.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"cowlib": {:hex, :cowlib, "2.8.0", "fd0ff1787db84ac415b8211573e9a30a3ebe71b5cbff7f720089972b2319c8a4", [:rebar3], [], "hexpm"},
|
||||
"credo": {:hex, :credo, "1.3.1", "082e8d9268a489becf8e7aa75671a7b9088b1277cd6c1b13f40a55554b3f5126", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"credo_envvar": {:hex, :credo_envvar, "0.1.4", "40817c10334e400f031012c0510bfa0d8725c19d867e4ae39cf14f2cbebc3b20", [:mix], [{:credo, "~> 1.0", [hex: :credo, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"csv": {:hex, :csv, "2.3.1", "9ce11eff5a74a07baf3787b2b19dd798724d29a9c3a492a41df39f6af686da0e", [:mix], [{:parallel_stream, "~> 1.0.4", [hex: :parallel_stream, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"dataloader": {:hex, :dataloader, "1.0.7", "58351b335673cf40601429bfed6c11fece6ce7ad169b2ac0f0fe83e716587391", [:mix], [{:ecto, ">= 0.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"db_connection": {:hex, :db_connection, "2.2.1", "caee17725495f5129cb7faebde001dc4406796f12a62b8949f4ac69315080566", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm"},
|
||||
"dialyxir": {:hex, :dialyxir, "1.0.0", "6a1fa629f7881a9f5aaf3a78f094b2a51a0357c843871b8bc98824e7342d00a5", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"ecto": {:hex, :ecto, "3.4.0", "a7a83ab8359bf816ce729e5e65981ce25b9fc5adfc89c2ea3980f4fed0bfd7c1", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"ecto_sql": {:hex, :ecto_sql, "3.4.1", "3c9136ba138f9b74d31286c73c61232a92bd19385f7c5607bdeb3a4587ef91f5", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.0", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm"},
|
||||
"erlsom": {:hex, :erlsom, "1.5.0", "c5a5cdd0ee0e8dca62bcc4b13ff08da24fdefc16ccd8b25282a2fda2ba1be24a", [:rebar3], [], "hexpm"},
|
||||
"ex_brace_expansion": {:hex, :ex_brace_expansion, "0.0.2", "7574fd9497f3f045346dfd9517f10f237f4d39137bf42142b0fbdcd4bacbc6ed", [:mix], [], "hexpm"},
|
||||
"ex_minimatch": {:hex, :ex_minimatch, "0.0.1", "4b41726183c104ac227c5996f083ec370f97bd38c2232d74a847888c1bb715bc", [:mix], [{:ex_brace_expansion, "~> 0.0.1", [hex: :ex_brace_expansion, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"excoveralls": {:hex, :excoveralls, "0.12.3", "2142be7cb978a3ae78385487edda6d1aff0e482ffc6123877bb7270a8ffbcfe0", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"absinthe": {:hex, :absinthe, "1.4.16", "0933e4d9f12652b12115d5709c0293a1bf78a22578032e9ad0dad4efee6b9eb1", [:mix], [{:dataloader, "~> 1.0.0", [hex: :dataloader, repo: "hexpm", optional: true]}, {:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "076b8bd9552f4966ba1242f412f6c439b731169a36a0ddaaffcd3893828f5bf6"},
|
||||
"absinthe_error_payload": {:hex, :absinthe_error_payload, "1.0.1", "0696314517f2d42a4e942cdeaf0c42a05db4135c6e9257607b4b13ebc83f894f", [:make, :mix], [{:absinthe, "~> 1.3", [hex: :absinthe, repo: "hexpm", optional: false]}, {:ecto, "~> 3.1", [hex: :ecto, repo: "hexpm", optional: false]}], "hexpm", "03b300138a2fd4b0c012401ce4e1d038f89d22f3aa49f8066c959208902469b7"},
|
||||
"absinthe_plug": {:hex, :absinthe_plug, "1.4.7", "939b6b9e1c7abc6b399a5b49faa690a1fbb55b195c670aa35783b14b08ccec7a", [:mix], [{:absinthe, "~> 1.4.11", [hex: :absinthe, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.2 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "c6ecb0e56a963287ac252d0563e5b33b84b300ce8203d3d1410dddb5dc6d08e9"},
|
||||
"bamboo": {:hex, :bamboo, "1.3.0", "9ab7c054f1c3435464efcba939396c29c5e1b28f73c34e1f169e0881297a3141", [:mix], [{:hackney, ">= 1.13.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "e1197188512d4a4458eaaad9a1659ce9eeb54a1b41574a9cd7507217b33e0f3e"},
|
||||
"bamboo_smtp": {:hex, :bamboo_smtp, "2.1.0", "4be58f3c51d9f7875dc169ae58a1d2f08e5b718bf3895f70d130548c0598f422", [:mix], [{:bamboo, "~> 1.2", [hex: :bamboo, repo: "hexpm", optional: false]}, {:gen_smtp, "~> 0.15.0", [hex: :gen_smtp, repo: "hexpm", optional: false]}], "hexpm", "0aad00ef93d0e0c83a0e1ca6998fea070c8a720a990fbda13ce834136215ee49"},
|
||||
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"},
|
||||
"canada": {:hex, :canada, "1.0.2", "040e4c47609b0a67d5773ac1fbe5e99f840cef173d69b739beda7c98453e0770", [:mix], [], "hexpm", "4269f74153fe89583fe50bd4d5de57bfe01f31258a6b676d296f3681f1483c68"},
|
||||
"canary": {:hex, :canary, "1.1.1", "4138d5e05db8497c477e4af73902eb9ae06e49dceaa13c2dd9f0b55525ded48b", [:mix], [{:canada, "~> 1.0.1", [hex: :canada, repo: "hexpm", optional: false]}, {:ecto, ">= 1.1.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "f348d9848693c830a65b707bba9e4dfdd6434e8c356a8d4477e4535afb0d653b"},
|
||||
"certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "805abd97539caf89ec6d4732c91e62ba9da0cda51ac462380bbd28ee697a8c42"},
|
||||
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm", "4a0850c9be22a43af9920a71ab17c051f5f7d45c209e40269a1938832510e4d9"},
|
||||
"corsica": {:hex, :corsica, "1.1.2", "5ad8b9dcbeeda4762d78a57c0c8c2f88e1eef8741508517c98cb79e0db1f107d", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "b7ae4485dc782266cab8d72fc7bb528f2393a4accf3a944834f86978c85fd8fa"},
|
||||
"cowboy": {:hex, :cowboy, "2.7.0", "91ed100138a764355f43316b1d23d7ff6bdb0de4ea618cb5d8677c93a7a2f115", [:rebar3], [{:cowlib, "~> 2.8.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "04fd8c6a39edc6aaa9c26123009200fc61f92a3a94f3178c527b70b767c6e605"},
|
||||
"cowlib": {:hex, :cowlib, "2.8.0", "fd0ff1787db84ac415b8211573e9a30a3ebe71b5cbff7f720089972b2319c8a4", [:rebar3], [], "hexpm", "79f954a7021b302186a950a32869dbc185523d99d3e44ce430cd1f3289f41ed4"},
|
||||
"credo": {:hex, :credo, "1.1.5", "caec7a3cadd2e58609d7ee25b3931b129e739e070539ad1a0cd7efeeb47014f4", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "d0bbd3222607ccaaac5c0340f7f525c627ae4d7aee6c8c8c108922620c5b6446"},
|
||||
"credo_envvar": {:hex, :credo_envvar, "0.1.4", "40817c10334e400f031012c0510bfa0d8725c19d867e4ae39cf14f2cbebc3b20", [:mix], [{:credo, "~> 1.0", [hex: :credo, repo: "hexpm", optional: false]}], "hexpm", "5055cdb4bcbaf7d423bc2bb3ac62b4e2d825e2b1e816884c468dee59d0363009"},
|
||||
"csv": {:hex, :csv, "2.3.1", "9ce11eff5a74a07baf3787b2b19dd798724d29a9c3a492a41df39f6af686da0e", [:mix], [{:parallel_stream, "~> 1.0.4", [hex: :parallel_stream, repo: "hexpm", optional: false]}], "hexpm", "86626e1c89a4ad9a96d0d9c638f9e88c2346b89b4ba1611988594ebe72b5d5ee"},
|
||||
"dataloader": {:hex, :dataloader, "1.0.6", "fb724d6d3fb6acb87d27e3b32dea3a307936ad2d245faf9cf5221d1323d6a4ba", [:mix], [{:ecto, ">= 0.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm", "d16b82bc004038243236c73b76bef39be634828a4f4f4f9d637fb498ff886a10"},
|
||||
"db_connection": {:hex, :db_connection, "2.1.1", "a51e8a2ee54ef2ae6ec41a668c85787ed40cb8944928c191280fe34c15b76ae5", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm", "5a0e8c1c722dbcd31c0cbd1906b1d1074c863d335c295e4b994849b65a1fbe47"},
|
||||
"decimal": {:hex, :decimal, "1.8.0", "ca462e0d885f09a1c5a342dbd7c1dcf27ea63548c65a65e67334f4b61803822e", [:mix], [], "hexpm", "52694ef56e60108e5012f8af9673874c66ed58ac1c4fae9b5b7ded31786663f5"},
|
||||
"dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [:mix], [], "hexpm", "6c32a70ed5d452c6650916555b1f96c79af5fc4bf286997f8b15f213de786f73"},
|
||||
"ecto": {:hex, :ecto, "3.2.5", "76c864b77948a479e18e69cc1d0f0f4ee7cced1148ffe6a093ff91eba644f0b5", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "01251d9b28081b7e0af02a1875f9b809b057f064754ca3b274949d5216ea6f5f"},
|
||||
"ecto_sql": {:hex, :ecto_sql, "3.2.2", "d10845bc147b9f61ef485cbf0973c0a337237199bd9bd30dd9542db00aadc26b", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.2.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.2.0 or ~> 0.3.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4a68f58cd12f3df73ea37c253f9b957c81313e532e60191890f71066840c714e"},
|
||||
"erlsom": {:hex, :erlsom, "1.5.0", "c5a5cdd0ee0e8dca62bcc4b13ff08da24fdefc16ccd8b25282a2fda2ba1be24a", [:rebar3], [], "hexpm", "55a9dbf9cfa77fcfc108bd8e2c4f9f784dea228a8f4b06ea10b684944946955a"},
|
||||
"ex_brace_expansion": {:hex, :ex_brace_expansion, "0.0.2", "7574fd9497f3f045346dfd9517f10f237f4d39137bf42142b0fbdcd4bacbc6ed", [:mix], [], "hexpm", "d7470a00cffe4425f89e83d7288c24b641c3f6cbde136a08089e7420467cd237"},
|
||||
"ex_minimatch": {:hex, :ex_minimatch, "0.0.1", "4b41726183c104ac227c5996f083ec370f97bd38c2232d74a847888c1bb715bc", [:mix], [{:ex_brace_expansion, "~> 0.0.1", [hex: :ex_brace_expansion, repo: "hexpm", optional: false]}], "hexpm", "3255bb8496635d3ef5d86ec6829958a3573ff730ca01534b0fead9c2e3af7de4"},
|
||||
"excoveralls": {:hex, :excoveralls, "0.12.1", "a553c59f6850d0aff3770e4729515762ba7c8e41eedde03208182a8dc9d0ce07", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "5c1f717066a299b1b732249e736c5da96bb4120d1e55dc2e6f442d251e18a812"},
|
||||
"fast_yaml": {:git, "https://github.com/processone/fast_yaml.git", "e789f68895f71b7ad31057177810ca0161bf790e", [ref: "e789f68895f71b7ad31057177810ca0161bf790e"]},
|
||||
"file_system": {:hex, :file_system, "0.2.8", "f632bd287927a1eed2b718f22af727c5aeaccc9a98d8c2bd7bff709e851dc986", [:mix], [], "hexpm"},
|
||||
"gen_smtp": {:hex, :gen_smtp, "0.15.0", "9f51960c17769b26833b50df0b96123605a8024738b62db747fece14eb2fbfcc", [:rebar3], [], "hexpm"},
|
||||
"gen_stage": {:hex, :gen_stage, "0.14.3", "d0c66f1c87faa301c1a85a809a3ee9097a4264b2edf7644bf5c123237ef732bf", [:mix], [], "hexpm"},
|
||||
"gettext": {:hex, :gettext, "0.17.4", "f13088e1ec10ce01665cf25f5ff779e7df3f2dc71b37084976cf89d1aa124d5c", [:mix], [], "hexpm"},
|
||||
"hackney": {:hex, :hackney, "1.15.2", "07e33c794f8f8964ee86cebec1a8ed88db5070e52e904b8f12209773c1036085", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.5", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"httpoison": {:hex, :httpoison, "1.6.2", "ace7c8d3a361cebccbed19c283c349b3d26991eff73a1eaaa8abae2e3c8089b6", [:mix], [{:hackney, "~> 1.15 and >= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"jason": {:hex, :jason, "1.2.0", "10043418c42d2493d0ee212d3fddd25d7ffe484380afad769a0a38795938e448", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"jsone": {:hex, :jsone, "1.5.2", "87adea283c9cf24767b4deed44602989a5331156df5d60a2660e9c9114d54046", [:rebar3], [], "hexpm"},
|
||||
"meck": {:hex, :meck, "0.8.13", "ffedb39f99b0b99703b8601c6f17c7f76313ee12de6b646e671e3188401f7866", [:rebar3], [], "hexpm"},
|
||||
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"},
|
||||
"mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm"},
|
||||
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm"},
|
||||
"mochiweb": {:hex, :mochiweb, "2.20.1", "e4dbd0ed716f076366ecf62ada5755a844e1d95c781e8c77df1d4114be868cdf", [:rebar3], [], "hexpm"},
|
||||
"mock": {:hex, :mock, "0.3.4", "c5862eb3b8c64237f45f586cf00c9d892ba07bb48305a43319d428ce3c2897dd", [:mix], [{:meck, "~> 0.8.13", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"mox": {:hex, :mox, "0.5.2", "55a0a5ba9ccc671518d068c8dddd20eeb436909ea79d1799e2209df7eaa98b6c", [:mix], [], "hexpm"},
|
||||
"oauth2": {:hex, :oauth2, "2.0.0", "338382079fe16c514420fa218b0903f8ad2d4bfc0ad0c9f988867dfa246731b0", [:mix], [{:hackney, "~> 1.13", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"p1_utils": {:hex, :p1_utils, "1.0.15", "731f76ae1f31f4554afb2ae629cb5589d53bd13efc72b11f5a7c3b1242f91046", [:rebar3], [], "hexpm"},
|
||||
"parallel_stream": {:hex, :parallel_stream, "1.0.6", "b967be2b23f0f6787fab7ed681b4c45a215a81481fb62b01a5b750fa8f30f76c", [:mix], [], "hexpm"},
|
||||
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"},
|
||||
"phoenix": {:hex, :phoenix, "1.4.16", "2cbbe0c81e6601567c44cc380c33aa42a1372ac1426e3de3d93ac448a7ec4308", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"phoenix_ecto": {:hex, :phoenix_ecto, "4.1.0", "a044d0756d0464c5a541b4a0bf4bcaf89bffcaf92468862408290682c73ae50d", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"phoenix_html": {:hex, :phoenix_html, "2.14.1", "7dabafadedb552db142aacbd1f11de1c0bbaa247f90c449ca549d5e30bbc66b4", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.2.1", "274a4b07c4adbdd7785d45a8b0bb57634d0b4f45b18d2c508b26c0344bd59b8f", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.2", "496c303bdf1b2e98a9d26e89af5bba3ab487ba3a3735f74bf1f4064d2a845a3e", [:mix], [], "hexpm"},
|
||||
"php_assoc_map": {:hex, :php_assoc_map, "0.5.2", "8b55283c2ffa762f8703cb30ef40085bf5c06ee08d5a82e38405fa4b949b2a6b", [:mix], [], "hexpm"},
|
||||
"plug": {:hex, :plug, "1.10.0", "6508295cbeb4c654860845fb95260737e4a8838d34d115ad76cd487584e2fc4d", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"plug_assign": {:hex, :plug_assign, "1.0.2", "da31479ac1e048af7c0630fe4b2a6d993b12cdd148405639fc1ad20c38eaf31b", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"plug_cowboy": {:hex, :plug_cowboy, "2.1.2", "8b0addb5908c5238fac38e442e81b6fcd32788eaa03246b4d55d147c47c5805e", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"plug_crypto": {:hex, :plug_crypto, "1.1.2", "bdd187572cc26dbd95b87136290425f2b580a116d3fb1f564216918c9730d227", [:mix], [], "hexpm"},
|
||||
"postgrex": {:hex, :postgrex, "0.15.3", "5806baa8a19a68c4d07c7a624ccdb9b57e89cbc573f1b98099e3741214746ae4", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"},
|
||||
"scrivener": {:hex, :scrivener, "2.7.0", "fa94cdea21fad0649921d8066b1833d18d296217bfdf4a5389a2f45ee857b773", [:mix], [], "hexpm"},
|
||||
"scrivener_ecto": {:hex, :scrivener_ecto, "2.3.0", "057f9dd3c77315f0a470263c3565353860d0294404aed611b3524c6df9044189", [:mix], [{:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:scrivener, "~> 2.4", [hex: :scrivener, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"sentry": {:hex, :sentry, "7.2.4", "b5bc90b594d40c2e653581e797a5fd2fdf994f2568f6bd66b7fa4971598be8d5", [:mix], [{:hackney, "~> 1.8 or 1.6.5", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.3", [hex: :phoenix, repo: "hexpm", optional: true]}, {:plug, "~> 1.6", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.5", "6eaf7ad16cb568bb01753dbbd7a95ff8b91c7979482b95f38443fe2c8852a79b", [:make, :mix, :rebar3], [], "hexpm"},
|
||||
"telemetry": {:hex, :telemetry, "0.4.1", "ae2718484892448a24470e6aa341bc847c3277bfb8d4e9289f7474d752c09c7f", [:rebar3], [], "hexpm"},
|
||||
"ueberauth": {:hex, :ueberauth, "0.6.3", "d42ace28b870e8072cf30e32e385579c57b9cc96ec74fa1f30f30da9c14f3cc0", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"ueberauth_discord": {:hex, :ueberauth_discord, "0.4.0", "737951cbdeaadc882de33b4058e92afaa3d024c7d1858c3ee0c1826c52401aed", [:mix], [{:oauth2, "~> 0.8", [hex: :oauth2, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.4", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"ueberauth_github": {:hex, :ueberauth_github, "0.8.0", "2216c8cdacee0de6245b422fb397921b64a29416526985304e345dab6a799d17", [:mix], [{:oauth2, "~> 1.0 or ~> 2.0", [hex: :oauth2, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.6.0", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"ueberauth_google": {:hex, :ueberauth_google, "0.9.0", "e098e1d6df647696b858b0289eae7e4dc8c662abee9e309d64bc115192c51bf5", [:mix], [{:oauth2, "~> 1.0 or ~> 2.0", [hex: :oauth2, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.6.0", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"file_system": {:hex, :file_system, "0.2.7", "e6f7f155970975789f26e77b8b8d8ab084c59844d8ecfaf58cbda31c494d14aa", [:mix], [], "hexpm", "b4cfa2d69c7f0b18fd06db222b2398abeef743a72504e6bd7df9c52f171b047f"},
|
||||
"gen_smtp": {:hex, :gen_smtp, "0.15.0", "9f51960c17769b26833b50df0b96123605a8024738b62db747fece14eb2fbfcc", [:rebar3], [], "hexpm", "29bd14a88030980849c7ed2447b8db6d6c9278a28b11a44cafe41b791205440f"},
|
||||
"gen_stage": {:hex, :gen_stage, "0.14.3", "d0c66f1c87faa301c1a85a809a3ee9097a4264b2edf7644bf5c123237ef732bf", [:mix], [], "hexpm", "8453e2289d94c3199396eb517d65d6715ef26bcae0ee83eb5ff7a84445458d76"},
|
||||
"gettext": {:hex, :gettext, "0.17.1", "8baab33482df4907b3eae22f719da492cee3981a26e649b9c2be1c0192616962", [:mix], [], "hexpm", "f7d97341e536f95b96eef2988d6d4230f7262cf239cda0e2e63123ee0b717222"},
|
||||
"hackney": {:hex, :hackney, "1.15.2", "07e33c794f8f8964ee86cebec1a8ed88db5070e52e904b8f12209773c1036085", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.5", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "e0100f8ef7d1124222c11ad362c857d3df7cb5f4204054f9f0f4a728666591fc"},
|
||||
"httpoison": {:hex, :httpoison, "1.6.2", "ace7c8d3a361cebccbed19c283c349b3d26991eff73a1eaaa8abae2e3c8089b6", [:mix], [{:hackney, "~> 1.15 and >= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "aa2c74bd271af34239a3948779612f87df2422c2fdcfdbcec28d9c105f0773fe"},
|
||||
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"},
|
||||
"jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fdf843bca858203ae1de16da2ee206f53416bbda5dc8c9e78f43243de4bc3afe"},
|
||||
"jsone": {:hex, :jsone, "1.5.2", "87adea283c9cf24767b4deed44602989a5331156df5d60a2660e9c9114d54046", [:rebar3], [], "hexpm", "170c171ce7f6dd70c858065154a3305b8564833c6dcca17e10b676ca31ea976f"},
|
||||
"meck": {:hex, :meck, "0.8.13", "ffedb39f99b0b99703b8601c6f17c7f76313ee12de6b646e671e3188401f7866", [:rebar3], [], "hexpm", "d34f013c156db51ad57cc556891b9720e6a1c1df5fe2e15af999c84d6cebeb1a"},
|
||||
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
|
||||
"mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm", "6cbe761d6a0ca5a31a0931bf4c63204bceb64538e664a8ecf784a9a6f3b875f1"},
|
||||
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
|
||||
"mochiweb": {:hex, :mochiweb, "2.18.0", "eb55f1db3e6e960fac4e6db4e2db9ec3602cc9f30b86cd1481d56545c3145d2e", [:rebar3], [], "hexpm", "b93e2b1e564bdbadfecc297277f9e6d0902da645b417d6c9210f6038ac63489a"},
|
||||
"mock": {:hex, :mock, "0.3.3", "42a433794b1291a9cf1525c6d26b38e039e0d3a360732b5e467bfc77ef26c914", [:mix], [{:meck, "~> 0.8.13", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "a280d1f7b6f4bbcbd9282616e57502721781c66ee5b540720efabeaf627cc7eb"},
|
||||
"mox": {:hex, :mox, "0.5.1", "f86bb36026aac1e6f924a4b6d024b05e9adbed5c63e8daa069bd66fb3292165b", [:mix], [], "hexpm", "052346cf322311c49a0f22789f3698eea030eec09b8c47367f0686ef2634ae14"},
|
||||
"oauth2": {:hex, :oauth2, "2.0.0", "338382079fe16c514420fa218b0903f8ad2d4bfc0ad0c9f988867dfa246731b0", [:mix], [{:hackney, "~> 1.13", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "881b8364ac7385f9fddc7949379cbe3f7081da37233a1aa7aab844670a91e7e7"},
|
||||
"p1_utils": {:hex, :p1_utils, "1.0.15", "731f76ae1f31f4554afb2ae629cb5589d53bd13efc72b11f5a7c3b1242f91046", [:rebar3], [], "hexpm", "1d308c3f37d7f770fb39abe3b86701b82d54414bc2499d9499edde3cb50bcf19"},
|
||||
"parallel_stream": {:hex, :parallel_stream, "1.0.6", "b967be2b23f0f6787fab7ed681b4c45a215a81481fb62b01a5b750fa8f30f76c", [:mix], [], "hexpm", "639b2e8749e11b87b9eb42f2ad325d161c170b39b288ac8d04c4f31f8f0823eb"},
|
||||
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"},
|
||||
"phoenix": {:hex, :phoenix, "1.4.11", "d112c862f6959f98e6e915c3b76c7a87ca3efd075850c8daa7c3c7a609014b0d", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ef19d737ca23b66f7333eaa873cbfc5e6fa6427ef5a0ffd358de1ba8e1a4b2f4"},
|
||||
"phoenix_ecto": {:hex, :phoenix_ecto, "4.1.0", "a044d0756d0464c5a541b4a0bf4bcaf89bffcaf92468862408290682c73ae50d", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "c5e666a341ff104d0399d8f0e4ff094559b2fde13a5985d4cb5023b2c2ac558b"},
|
||||
"phoenix_html": {:hex, :phoenix_html, "2.13.3", "850e292ff6e204257f5f9c4c54a8cb1f6fbc16ed53d360c2b780a3d0ba333867", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "8b01b3d6d39731ab18aa548d928b5796166d2500755f553725cfe967bafba7d9"},
|
||||
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.2.1", "274a4b07c4adbdd7785d45a8b0bb57634d0b4f45b18d2c508b26c0344bd59b8f", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "41b4103a2fa282cfd747d377233baf213c648fdcc7928f432937676532490eee"},
|
||||
"phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.2", "496c303bdf1b2e98a9d26e89af5bba3ab487ba3a3735f74bf1f4064d2a845a3e", [:mix], [], "hexpm", "1f13f9f0f3e769a667a6b6828d29dec37497a082d195cc52dbef401a9b69bf38"},
|
||||
"php_assoc_map": {:hex, :php_assoc_map, "0.5.2", "8b55283c2ffa762f8703cb30ef40085bf5c06ee08d5a82e38405fa4b949b2a6b", [:mix], [], "hexpm", "c95f27f74075cdd5908e4217db96887709334a9fe1da30fc98706c225f3ceafd"},
|
||||
"plug": {:hex, :plug, "1.8.3", "12d5f9796dc72e8ac9614e94bda5e51c4c028d0d428e9297650d09e15a684478", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "164baaeb382d19beee0ec484492aa82a9c8685770aee33b24ec727a0971b34d0"},
|
||||
"plug_assign": {:hex, :plug_assign, "1.0.2", "da31479ac1e048af7c0630fe4b2a6d993b12cdd148405639fc1ad20c38eaf31b", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "341944464b45d44964b2946a0dad8226788481a49c1a95f60e236536b40979ce"},
|
||||
"plug_cowboy": {:hex, :plug_cowboy, "2.1.0", "b75768153c3a8a9e8039d4b25bb9b14efbc58e9c4a6e6a270abff1cd30cbe320", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "6cd8ddd1bd1fbfa54d3fc61d4719c2057dae67615395d58d40437a919a46f132"},
|
||||
"plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm", "73c1682f0e414cfb5d9b95c8e8cd6ffcfdae699e3b05e1db744e58b7be857759"},
|
||||
"postgrex": {:hex, :postgrex, "0.15.1", "23ce3417de70f4c0e9e7419ad85bdabcc6860a6925fe2c6f3b1b5b1e8e47bf2f", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "12cd418e207b8ed787dfe0f520fccd6c001f58d9108233feae7df36462593d1f"},
|
||||
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"},
|
||||
"scrivener": {:hex, :scrivener, "2.7.0", "fa94cdea21fad0649921d8066b1833d18d296217bfdf4a5389a2f45ee857b773", [:mix], [], "hexpm", "30da36a427f2519cf75993271fb7c5aad1759682a70f90d880a85c3d743d2c57"},
|
||||
"scrivener_ecto": {:hex, :scrivener_ecto, "2.2.0", "53d5f1ba28f35f17891cf526ee102f8f225b7024d1cdaf8984875467158c9c5e", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:scrivener, "~> 2.4", [hex: :scrivener, repo: "hexpm", optional: false]}], "hexpm", "3eadfc0a762db4ba8acceee3450404f6ce5e710e52ccf04aae69fca5afe0cd2f"},
|
||||
"sentry": {:hex, :sentry, "7.2.0", "37a367ae58b112cc548e17aa8640e5e329eb1d19b71bc368fcb7ccad919d5dac", [:mix], [{:hackney, "~> 1.8 or 1.6.5", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.3", [hex: :phoenix, repo: "hexpm", optional: true]}, {:plug, "~> 1.6", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm", "0bea1c16bc9d26173847e43535750d0da90a718b27b2781d648980c53ed20baf"},
|
||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.5", "6eaf7ad16cb568bb01753dbbd7a95ff8b91c7979482b95f38443fe2c8852a79b", [:make, :mix, :rebar3], [], "hexpm", "13104d7897e38ed7f044c4de953a6c28597d1c952075eb2e328bc6d6f2bfc496"},
|
||||
"telemetry": {:hex, :telemetry, "0.4.1", "ae2718484892448a24470e6aa341bc847c3277bfb8d4e9289f7474d752c09c7f", [:rebar3], [], "hexpm", "4738382e36a0a9a2b6e25d67c960e40e1a2c95560b9f936d8e29de8cd858480f"},
|
||||
"ueberauth": {:hex, :ueberauth, "0.6.2", "25a31111249d60bad8b65438b2306a4dc91f3208faa62f5a8c33e8713989b2e8", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "db9fbfb5ac707bc4f85a297758406340bf0358b4af737a88113c1a9eee120ac7"},
|
||||
"ueberauth_discord": {:hex, :ueberauth_discord, "0.4.0", "737951cbdeaadc882de33b4058e92afaa3d024c7d1858c3ee0c1826c52401aed", [:mix], [{:oauth2, "~> 0.8", [hex: :oauth2, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.4", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "ff2e030ec61551e372cda422cbf694201e88a55d488ded57c66cbbf2d1fe258f"},
|
||||
"ueberauth_github": {:hex, :ueberauth_github, "0.8.0", "2216c8cdacee0de6245b422fb397921b64a29416526985304e345dab6a799d17", [:mix], [{:oauth2, "~> 1.0 or ~> 2.0", [hex: :oauth2, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.6.0", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "b65ccc001a7b0719ba069452f3333d68891f4613ae787a340cce31e2a43307a3"},
|
||||
"ueberauth_google": {:hex, :ueberauth_google, "0.9.0", "e098e1d6df647696b858b0289eae7e4dc8c662abee9e309d64bc115192c51bf5", [:mix], [{:oauth2, "~> 1.0 or ~> 2.0", [hex: :oauth2, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.6.0", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "5453ba074df7ee14fb5b121bb04a64cda5266cd23b28af8a2fdf02dd40959ab4"},
|
||||
"ueberauth_slack": {:git, "https://github.com/ueberauth/ueberauth_slack.git", "525594c870f959aba67acc759d5c1a588ee75e9e", [ref: "525594c870f959ab"]},
|
||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"},
|
||||
"xml_builder": {:hex, :xml_builder, "2.1.2", "90cb9ad382958934c78c6ddfbe6d385a8ce147d84b61cbfa83ec93a169d0feab", [:mix], [], "hexpm"},
|
||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"},
|
||||
"xml_builder": {:hex, :xml_builder, "2.1.2", "90cb9ad382958934c78c6ddfbe6d385a8ce147d84b61cbfa83ec93a169d0feab", [:mix], [], "hexpm", "b89046041da2fbc1d51d31493ba31b9d5fc6223c93384bf513a1a9e1df9ec081"},
|
||||
}
|
||||
|
1288
package-lock.json
generated
1288
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
20
package.json
20
package.json
@ -8,15 +8,19 @@
|
||||
"node": ">= 8.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "2.25.0",
|
||||
"@typescript-eslint/parser": "2.16.0",
|
||||
"babel-eslint": "8.2.3",
|
||||
"ember-template-lint": "1.8.2",
|
||||
"eslint": "4.19.0",
|
||||
"eslint-config-prettier": "2.9.0",
|
||||
"eslint-plugin-ember": "5.1.0",
|
||||
"eslint": "6.8.0",
|
||||
"eslint-config-prettier": "6.9.0",
|
||||
"eslint-config-typestrict": "1.0.0",
|
||||
"eslint-plugin-ember": "7.7.2",
|
||||
"eslint-plugin-mirego": "0.0.1",
|
||||
"prettier": "1.16.4",
|
||||
"tslint": "5.12.1",
|
||||
"tslint-config-prettier": "1.17.0",
|
||||
"typescript": "3.2.2"
|
||||
}
|
||||
"eslint-plugin-node": "11.0.0",
|
||||
"eslint-plugin-sonarjs": "0.5.0",
|
||||
"prettier": "2.0.2",
|
||||
"typescript": "3.8.3"
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
|
@ -43,8 +43,11 @@ run make lint-prettier
|
||||
header "Eslint code lint…"
|
||||
run make lint-eslint
|
||||
|
||||
header "Tslint code lint…"
|
||||
run make lint-tslint
|
||||
header "Handlebar template code lint…"
|
||||
run make lint-template-hbs
|
||||
|
||||
header "Type check Typescript files…"
|
||||
run make type-check
|
||||
|
||||
if [ ${error_status} -ne 0 ]; then
|
||||
echo "\n\n${YELLOW}▶▶ One of the checks ${RED_BOLD}failed${YELLOW}. Please fix it before committing.${NO_COLOR}"
|
||||
|
@ -7,6 +7,7 @@ defmodule AccentTest.GraphQL.Requests.Projects do
|
||||
Project,
|
||||
Repo,
|
||||
Revision,
|
||||
Translation,
|
||||
User
|
||||
}
|
||||
|
||||
@ -18,12 +19,16 @@ defmodule AccentTest.GraphQL.Requests.Projects do
|
||||
project = %Project{main_color: "#f00", name: "My project", last_synced_at: DateTime.from_naive!(~N[2017-01-01T00:00:00], "Etc/UTC")} |> Repo.insert!()
|
||||
|
||||
%Collaborator{project_id: project.id, user_id: user.id, role: "admin"} |> Repo.insert!()
|
||||
%Revision{language_id: french_language.id, project_id: project.id, master: true} |> Repo.insert!()
|
||||
revision = %Revision{language_id: french_language.id, project_id: project.id, master: true} |> Repo.insert!()
|
||||
|
||||
{:ok, [user: user, project: project, language: french_language]}
|
||||
{:ok, [user: user, project: project, language: french_language, revision: revision]}
|
||||
end
|
||||
|
||||
test "list projects", %{user: user, project: project} do
|
||||
test "list projects", %{user: user, project: project, revision: revision} do
|
||||
%Translation{revision_id: revision.id, key: "A", conflicted: true} |> Repo.insert!()
|
||||
%Translation{revision_id: revision.id, key: "B", conflicted: true} |> Repo.insert!()
|
||||
%Translation{revision_id: revision.id, key: "C", conflicted: false} |> Repo.insert!()
|
||||
|
||||
{:ok, data} =
|
||||
"""
|
||||
query {
|
||||
@ -33,6 +38,9 @@ defmodule AccentTest.GraphQL.Requests.Projects do
|
||||
id
|
||||
name
|
||||
lastSyncedAt
|
||||
translationsCount
|
||||
conflictsCount
|
||||
reviewedCount
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -43,5 +51,8 @@ defmodule AccentTest.GraphQL.Requests.Projects do
|
||||
assert get_in(data, [:data, "viewer", "projects", "entries", Access.at(0), "id"]) === project.id
|
||||
assert get_in(data, [:data, "viewer", "projects", "entries", Access.at(0), "name"]) === project.name
|
||||
assert get_in(data, [:data, "viewer", "projects", "entries", Access.at(0), "lastSyncedAt"]) === "2017-01-01T00:00:00Z"
|
||||
assert get_in(data, [:data, "viewer", "projects", "entries", Access.at(0), "translationsCount"]) === 3
|
||||
assert get_in(data, [:data, "viewer", "projects", "entries", Access.at(0), "reviewedCount"]) === 1
|
||||
assert get_in(data, [:data, "viewer", "projects", "entries", Access.at(0), "conflictsCount"]) === 2
|
||||
end
|
||||
end
|
||||
|
77
tslint.json
77
tslint.json
@ -1,77 +0,0 @@
|
||||
{
|
||||
"extends": ["tslint:latest", "tslint-config-prettier"],
|
||||
"rules": {
|
||||
"adjacent-overload-signatures": true,
|
||||
"arrow-return-shorthand": true,
|
||||
"ban-comma-operator": true,
|
||||
"class-name": true,
|
||||
"comment-format": [true, "check-space"],
|
||||
"curly": [true, "ignore-same-line"],
|
||||
"encoding": true,
|
||||
"interface-name": false,
|
||||
"max-classes-per-file": false,
|
||||
"member-access": [true, "no-public"],
|
||||
"newline-before-return": true,
|
||||
"no-arg": true,
|
||||
"no-conditional-assignment": true,
|
||||
"no-console": false,
|
||||
"no-consecutive-blank-lines": true,
|
||||
"no-debugger": true,
|
||||
"no-duplicate-imports": true,
|
||||
"no-duplicate-super": true,
|
||||
"no-duplicate-switch-case": true,
|
||||
"no-duplicate-variable": [true, "check-parameters"],
|
||||
"no-empty-interface": false,
|
||||
"no-empty": false,
|
||||
"no-eval": true,
|
||||
"no-implicit-dependencies": false,
|
||||
"no-non-null-assertion": false,
|
||||
"no-parameter-reassignment": true,
|
||||
"no-return-await": true,
|
||||
"no-shadowed-variable": false,
|
||||
"no-string-literal": true,
|
||||
"no-string-throw": true,
|
||||
"no-submodule-imports": false,
|
||||
"no-switch-case-fall-through": true,
|
||||
"no-this-assignment": [true, {"allow-destructuring": true}],
|
||||
"no-unsafe-finally": true,
|
||||
"no-unused-expression": [true, "allow-fast-null-checks"],
|
||||
"no-var-keyword": true,
|
||||
"number-literal-format": true,
|
||||
"object-literal-key-quotes": [true, "as-needed"],
|
||||
"one-variable-per-declaration": [true, "ignore-for-loop"],
|
||||
"prefer-conditional-expression": true,
|
||||
"prefer-const": true,
|
||||
"prefer-for-of": true,
|
||||
"prefer-object-spread": true,
|
||||
"prefer-template": true,
|
||||
"radix": true,
|
||||
"triple-equals": [true, "allow-undefined-check", "allow-null-check"],
|
||||
"typedef-whitespace": [
|
||||
true,
|
||||
{
|
||||
"call-signature": "nospace",
|
||||
"index-signature": "nospace",
|
||||
"parameter": "nospace",
|
||||
"property-declaration": "nospace",
|
||||
"variable-declaration": "nospace"
|
||||
},
|
||||
{
|
||||
"call-signature": "onespace",
|
||||
"index-signature": "onespace",
|
||||
"parameter": "onespace",
|
||||
"property-declaration": "onespace",
|
||||
"variable-declaration": "onespace"
|
||||
}
|
||||
],
|
||||
"unified-signatures": true,
|
||||
"use-isnan": true,
|
||||
"variable-name": [
|
||||
true,
|
||||
"ban-keywords",
|
||||
"check-format",
|
||||
"allow-leading-underscore",
|
||||
"allow-pascal-case"
|
||||
]
|
||||
}
|
||||
}
|
@ -11,6 +11,9 @@ module.exports = {
|
||||
'link-rel-noopener': true,
|
||||
'no-abstract-roles': true,
|
||||
'no-bare-strings': true,
|
||||
'no-curly-component-invocation': {
|
||||
allow: ['inline-svg', 't', 'string-diff', 'time-ago-in-words'],
|
||||
},
|
||||
'no-debugger': true,
|
||||
'no-element-event-actions': true,
|
||||
'no-duplicate-attributes': true,
|
||||
@ -26,10 +29,11 @@ module.exports = {
|
||||
'no-triple-curlies': false,
|
||||
'no-unused-block-params': true,
|
||||
quotes: 'double',
|
||||
'require-valid-alt-text': false,
|
||||
'self-closing-void-elements': true,
|
||||
'simple-unless': false,
|
||||
'style-concatenation': true,
|
||||
'table-groups': true,
|
||||
'template-length': [true, {min: 1, max: 200}]
|
||||
}
|
||||
'template-length': [true, {min: 1, max: 200}],
|
||||
},
|
||||
};
|
||||
|
@ -4,11 +4,12 @@ import loadInitializers from 'ember-load-initializers';
|
||||
import config from './config/environment';
|
||||
|
||||
const {modulePrefix, podModulePrefix} = config;
|
||||
const App = Application.extend({
|
||||
modulePrefix,
|
||||
podModulePrefix,
|
||||
Resolver
|
||||
});
|
||||
|
||||
class App extends Application {
|
||||
modulePrefix = modulePrefix;
|
||||
podModulePrefix = podModulePrefix;
|
||||
Resolver = Resolver;
|
||||
}
|
||||
|
||||
loadInitializers(App, modulePrefix);
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
export default (count, total) => {
|
||||
export default (count: number, total: number) => {
|
||||
const percentage = (count / total) * 100;
|
||||
|
||||
if (percentage) {
|
@ -1,9 +0,0 @@
|
||||
import {computed} from '@ember/object';
|
||||
|
||||
export default (errorsKey, property) => {
|
||||
return computed(errorsKey, function() {
|
||||
const errors = this.get(errorsKey);
|
||||
|
||||
return errors && errors.find(({field}) => field === property);
|
||||
});
|
||||
};
|
6
webapp/app/computed-macros/field-error.ts
Normal file
6
webapp/app/computed-macros/field-error.ts
Normal file
@ -0,0 +1,6 @@
|
||||
interface Error {
|
||||
field: String;
|
||||
}
|
||||
|
||||
export default (errors: [Error], property: String): Error | undefined =>
|
||||
errors && errors.find(({field}) => field === property);
|
@ -1,17 +0,0 @@
|
||||
import EmberObject, {computed} from '@ember/object';
|
||||
|
||||
export default property => {
|
||||
return computed(property, function() {
|
||||
const key = this.get(property);
|
||||
|
||||
if (!key) return EmberObject.create({value: '', prefix: ''});
|
||||
|
||||
const splittedKey = key.split('|');
|
||||
const isSplitted = !!splittedKey[1];
|
||||
|
||||
return EmberObject.create({
|
||||
value: isSplitted ? splittedKey[1] : key,
|
||||
prefix: isSplitted ? splittedKey[0] : ''
|
||||
});
|
||||
});
|
||||
};
|
16
webapp/app/computed-macros/parsed-key.ts
Normal file
16
webapp/app/computed-macros/parsed-key.ts
Normal file
@ -0,0 +1,16 @@
|
||||
interface ParsedKey {
|
||||
value: String;
|
||||
prefix: String;
|
||||
}
|
||||
|
||||
export default (key: String): ParsedKey => {
|
||||
if (!key) return {value: '', prefix: ''};
|
||||
|
||||
const splittedKey = key.split('|');
|
||||
const isSplitted = !!splittedKey[1];
|
||||
|
||||
return {
|
||||
value: isSplitted ? splittedKey[1] : key,
|
||||
prefix: isSplitted ? splittedKey[0] : '',
|
||||
};
|
||||
};
|
70
webapp/app/config/environment.d.ts
vendored
Normal file
70
webapp/app/config/environment.d.ts
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Type declarations for
|
||||
* import config from './config/environment'
|
||||
*
|
||||
* For now these need to be managed by the developer
|
||||
* since different ember addons can materialize new entries.
|
||||
*/
|
||||
declare const config: {
|
||||
environment: any;
|
||||
modulePrefix: string;
|
||||
podModulePrefix: string;
|
||||
locationType: string;
|
||||
rootURL: string;
|
||||
|
||||
EmberENV: {
|
||||
EXTEND_PROTOTYPES: boolean;
|
||||
LOG_VERSION: boolean;
|
||||
};
|
||||
|
||||
APP: {
|
||||
LOCAL_STORAGE: {
|
||||
SESSION_NAMESPACE: string;
|
||||
};
|
||||
};
|
||||
|
||||
API: {
|
||||
WS_HOST: string;
|
||||
HOST: string;
|
||||
AUTHENTICATION_PATH: string;
|
||||
HOOKS_PATH: string;
|
||||
PROJECT_PATH: string;
|
||||
SYNC_PEEK_PROJECT_PATH: string;
|
||||
SYNC_PROJECT_PATH: string;
|
||||
MERGE_PEEK_PROJECT_PATH: string;
|
||||
MERGE_REVISION_PATH: string;
|
||||
EXPORT_DOCUMENT: string;
|
||||
JIPT_EXPORT_DOCUMENT: string;
|
||||
PERCENTAGE_REVIEWED_BADGE_SVG_PROJECT_PATH: string;
|
||||
JIPT_SCRIPT_PATH: string;
|
||||
};
|
||||
|
||||
SENTRY: {
|
||||
DSN: string;
|
||||
};
|
||||
|
||||
contentSecurityPolicy: {
|
||||
'default-src': string | string[];
|
||||
'script-src': string | string[];
|
||||
'font-src': string | string[];
|
||||
'connect-src': string | string[];
|
||||
'img-src': string | string[];
|
||||
'style-src': string | string[];
|
||||
'media-src': string | string[];
|
||||
'frame-src': string | string[];
|
||||
};
|
||||
|
||||
flashMessageDefaults: {
|
||||
timeout: number;
|
||||
destroyOnClick: boolean;
|
||||
extendedTimeout: number;
|
||||
priority: number;
|
||||
sticky: boolean;
|
||||
showProgress: boolean;
|
||||
type: string;
|
||||
types: string[];
|
||||
injectionFactories: [];
|
||||
};
|
||||
};
|
||||
|
||||
export default config;
|
@ -3,22 +3,22 @@ export default {
|
||||
hhmmss: {
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
second: 'numeric'
|
||||
}
|
||||
second: 'numeric',
|
||||
},
|
||||
},
|
||||
date: {
|
||||
hhmmss: {
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
second: 'numeric'
|
||||
}
|
||||
second: 'numeric',
|
||||
},
|
||||
},
|
||||
number: {
|
||||
USD: {
|
||||
style: 'currency',
|
||||
currency: 'USD',
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
}
|
||||
}
|
||||
maximumFractionDigits: 2,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -1,31 +0,0 @@
|
||||
import Ember from 'ember';
|
||||
import {helper} from '@ember/component/helper';
|
||||
import {htmlSafe} from '@ember/string';
|
||||
import Diff from 'diff';
|
||||
|
||||
const {
|
||||
Handlebars: {
|
||||
Utils: {escapeExpression}
|
||||
}
|
||||
} = Ember;
|
||||
|
||||
const REMOVED_TAG_TEMPLATE = value => `<span class="removed">${value}</span>`;
|
||||
const ADDED_TAG_TEMPLATE = value => `<span class="added">${value}</span>`;
|
||||
|
||||
const stringDiff = ([text1, text2]) => {
|
||||
const diff = Diff.diffWords(text2 || '', text1 || '');
|
||||
|
||||
return htmlSafe(
|
||||
diff
|
||||
.map(part => {
|
||||
const value = escapeExpression(part.value);
|
||||
if (part.removed) return REMOVED_TAG_TEMPLATE(value);
|
||||
if (part.added) return ADDED_TAG_TEMPLATE(value);
|
||||
|
||||
return value;
|
||||
})
|
||||
.join('')
|
||||
);
|
||||
};
|
||||
|
||||
export default helper(stringDiff);
|
64
webapp/app/helpers/string-diff.ts
Normal file
64
webapp/app/helpers/string-diff.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import {helper} from '@ember/component/helper';
|
||||
import {htmlSafe} from '@ember/string';
|
||||
import Diff from 'diff';
|
||||
|
||||
const badChars = /[&<>"'`=]/g;
|
||||
const possible = /[&<>"'`=]/;
|
||||
const escape = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": ''',
|
||||
'`': '`',
|
||||
'=': '=',
|
||||
};
|
||||
|
||||
const escapeChar = (chr: keyof typeof escape) => {
|
||||
return escape[chr];
|
||||
};
|
||||
|
||||
const escapeExpression = (string: any): string => {
|
||||
if (typeof string !== 'string') {
|
||||
// don't escape SafeStrings, since they're already safe
|
||||
if (string && string.toHTML) {
|
||||
return string.toHTML();
|
||||
} else if (string === null || string === undefined) {
|
||||
return '';
|
||||
} else if (!string) {
|
||||
return String(string);
|
||||
}
|
||||
|
||||
// Force a string conversion as this will be done by the append regardless and
|
||||
// the regex test will do this transparently behind the scenes, causing issues if
|
||||
// an object's to string has escaped characters in it.
|
||||
string = String(string);
|
||||
}
|
||||
|
||||
if (!possible.test(string)) return string;
|
||||
|
||||
return string.replace(badChars, escapeChar);
|
||||
};
|
||||
|
||||
const REMOVED_TAG_TEMPLATE = (value: string) =>
|
||||
`<span class="removed">${value}</span>`;
|
||||
const ADDED_TAG_TEMPLATE = (value: string) =>
|
||||
`<span class="added">${value}</span>`;
|
||||
|
||||
const stringDiff = ([text1, text2]: [string, string]) => {
|
||||
const diff = Diff.diffWords(text2 || '', text1 || '');
|
||||
|
||||
return htmlSafe(
|
||||
diff
|
||||
.map((part: {value: string; removed: string; added: string}) => {
|
||||
const value = escapeExpression(part.value);
|
||||
if (part.removed) return REMOVED_TAG_TEMPLATE(value);
|
||||
if (part.added) return ADDED_TAG_TEMPLATE(value);
|
||||
|
||||
return value;
|
||||
})
|
||||
.join('')
|
||||
);
|
||||
};
|
||||
|
||||
export default helper(stringDiff);
|
@ -4,10 +4,10 @@ import formatDistanceToNow from 'date-fns/formatDistanceToNow';
|
||||
|
||||
const OPTIONS = {
|
||||
addSuffix: true,
|
||||
includeSeconds: false
|
||||
includeSeconds: false,
|
||||
};
|
||||
|
||||
const timeAgoInWords = ([date]) => {
|
||||
const timeAgoInWords = ([date]: [string]) => {
|
||||
if (isBlank(date)) return '';
|
||||
|
||||
return formatDistanceToNow(new Date(date), OPTIONS);
|
@ -1,7 +1,7 @@
|
||||
import Raven from 'raven-js';
|
||||
import config from 'accent-webapp/config/environment';
|
||||
|
||||
export const initialize = application => {
|
||||
export const initialize = (application) => {
|
||||
if (config.SENTRY.DSN) {
|
||||
Raven.config(config.SENTRY.DSN).install();
|
||||
|
||||
@ -15,5 +15,5 @@ export const initialize = application => {
|
||||
|
||||
export default {
|
||||
name: 'raven-setup',
|
||||
initialize
|
||||
initialize,
|
||||
};
|
||||
|
@ -177,8 +177,8 @@
|
||||
}
|
||||
},
|
||||
"date_tag": {
|
||||
"formatted_date_time_format": "yyyy-MM-ddTHH:mm:ss",
|
||||
"humanized_date_title_format": "MMMM Do yyyy, HH:mm:ss"
|
||||
"formatted_date_time_format": "yyyy-MM-dd'T'HH:mm:ss",
|
||||
"humanized_date_title_format": "EEEE, MMMM do yyyy, H:mm a"
|
||||
},
|
||||
"versions_list": {
|
||||
"export": "Export",
|
||||
@ -199,7 +199,7 @@
|
||||
"save_button": "Save",
|
||||
"cancel_button": "Cancel",
|
||||
"item": {
|
||||
"deleting_label": "Deleting",
|
||||
"deleting_label": "Deleting {path}.{extension}…",
|
||||
"path_label": "Path",
|
||||
"path_help": "Used to scope your strings. The path will be used to export all your languages (en/<em>path</em>.format, fr/<em>path</em>.format)"
|
||||
}
|
||||
@ -621,8 +621,8 @@
|
||||
"translations_link_title": "All strings"
|
||||
},
|
||||
"time_ago_in_words_tag": {
|
||||
"formatted_date_time_format": "yyyy-MM-ddTHH:mm:ss",
|
||||
"humanized_date_title_format": "dddd, MMMM Do yyyy, H:mm a"
|
||||
"formatted_date_time_format": "yyyy-MM-dd'T'HH:mm:ss",
|
||||
"humanized_date_title_format": "EEEE, MMMM do yyyy, H:mm a"
|
||||
},
|
||||
"translation_activities_list_item": {
|
||||
"action_text": {
|
||||
|
@ -1,78 +0,0 @@
|
||||
import {inject as service} from '@ember/service';
|
||||
import Mixin from '@ember/object/mixin';
|
||||
import EmberObject, {setProperties} from '@ember/object';
|
||||
|
||||
const PROPS_FN = data => data;
|
||||
|
||||
export default Mixin.create({
|
||||
apollo: service('apollo'),
|
||||
|
||||
graphql(query, {options, props}) {
|
||||
props = props || PROPS_FN;
|
||||
const graphqlObject = () => this.modelFor(this.routeName);
|
||||
|
||||
this._createQuery(query, options);
|
||||
this._createSubscription(props, graphqlObject);
|
||||
|
||||
return this._currentResult(props);
|
||||
},
|
||||
|
||||
deactivate() {
|
||||
this._super(...arguments);
|
||||
|
||||
this._clearSubscription();
|
||||
},
|
||||
|
||||
_currentResult(props) {
|
||||
const queryObservable = this.queryObservable;
|
||||
const result = queryObservable.currentResult();
|
||||
const mappedResult = this._mapResult(result, props);
|
||||
|
||||
return EmberObject.create(mappedResult);
|
||||
},
|
||||
|
||||
_createQuery(query, options = {}) {
|
||||
this._clearSubscription();
|
||||
|
||||
const queryObservable = this.apollo.client.watchQuery({
|
||||
query,
|
||||
...options
|
||||
});
|
||||
setProperties(this, {queryObservable});
|
||||
},
|
||||
|
||||
_createSubscription(props, graphqlObject) {
|
||||
const next = result => {
|
||||
const o = graphqlObject();
|
||||
if (!o) return;
|
||||
|
||||
const mappedResult = this._mapResult(result, props);
|
||||
setProperties(o, mappedResult);
|
||||
};
|
||||
|
||||
const querySubscription = this.queryObservable.subscribe({next});
|
||||
setProperties(this, {querySubscription});
|
||||
},
|
||||
|
||||
_clearSubscription() {
|
||||
const subscription = this.querySubscription;
|
||||
if (subscription) subscription.unsubscribe();
|
||||
},
|
||||
|
||||
_mapResult(result, props) {
|
||||
if (result.data && Object.keys(result.data).length) {
|
||||
const data = props(result.data);
|
||||
|
||||
return {
|
||||
...data,
|
||||
loading: result.loading,
|
||||
refetch: this.queryObservable.refetch,
|
||||
fetchMore: this.queryObservable.fetchMore,
|
||||
startPolling: this.queryObservable.startPolling,
|
||||
stopPolling: this.queryObservable.stopPolling
|
||||
};
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
});
|
@ -1,12 +0,0 @@
|
||||
import {inject as service} from '@ember/service';
|
||||
import Mixin from '@ember/object/mixin';
|
||||
|
||||
export default Mixin.create({
|
||||
session: service(),
|
||||
|
||||
redirect() {
|
||||
if (!this.session.isAuthenticated) {
|
||||
this.transitionTo('login');
|
||||
}
|
||||
}
|
||||
});
|
@ -1,9 +0,0 @@
|
||||
import Mixin from '@ember/object/mixin';
|
||||
|
||||
export default Mixin.create({
|
||||
activate() {
|
||||
this._super();
|
||||
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
});
|
@ -1,32 +0,0 @@
|
||||
import {inject as service} from '@ember/service';
|
||||
import Route from '@ember/routing/route';
|
||||
import raven from 'raven-js';
|
||||
import config from 'accent-webapp/config/environment';
|
||||
|
||||
export default Route.extend({
|
||||
session: service('session'),
|
||||
intl: service('intl'),
|
||||
|
||||
beforeModel() {
|
||||
this._super(...arguments);
|
||||
this.intl.setLocale(['en-us']);
|
||||
|
||||
raven.config(config.SENTRY.DSN).install();
|
||||
|
||||
this._tryLoginAfterRedirect();
|
||||
},
|
||||
|
||||
_tryLoginAfterRedirect() {
|
||||
const match = window.location.search
|
||||
.substring(window.location.search.indexOf('?') + 1)
|
||||
.split('&')
|
||||
.find(segment => segment.split('=')[0] === 'token');
|
||||
const token = match && match.split('=')[1];
|
||||
|
||||
if (!token) return;
|
||||
|
||||
this.session.login({token}).then(data => {
|
||||
if (data && data.user) this.transitionTo('logged-in.projects');
|
||||
});
|
||||
}
|
||||
});
|
43
webapp/app/pods/application/route.ts
Normal file
43
webapp/app/pods/application/route.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import {inject as service} from '@ember/service';
|
||||
import Route from '@ember/routing/route';
|
||||
import raven from 'raven-js';
|
||||
import config from 'accent-webapp/config/environment';
|
||||
import Session from 'accent-webapp/services/session';
|
||||
import IntlService from 'ember-intl/services/intl';
|
||||
import RouterService from '@ember/routing/router-service';
|
||||
|
||||
export default class ApplicationRoute extends Route {
|
||||
@service('session')
|
||||
session: Session;
|
||||
|
||||
@service('intl')
|
||||
intl: IntlService;
|
||||
|
||||
@service('router')
|
||||
router: RouterService;
|
||||
|
||||
async beforeModel() {
|
||||
this.intl.setLocale('en-us');
|
||||
|
||||
raven.config(config.SENTRY.DSN).install();
|
||||
|
||||
await this.tryLoginAfterRedirect();
|
||||
}
|
||||
|
||||
private async tryLoginAfterRedirect() {
|
||||
const match = window.location.search
|
||||
.substring(window.location.search.indexOf('?') + 1)
|
||||
.split('&')
|
||||
.find((segment) => segment.split('=')[0] === 'token');
|
||||
|
||||
const token = match && match.split('=')[1];
|
||||
|
||||
if (!token) return;
|
||||
|
||||
const data = await this.session.login({token});
|
||||
|
||||
if (data && data.user) {
|
||||
this.router.transitionTo('logged-in.projects');
|
||||
}
|
||||
}
|
||||
}
|
13
webapp/app/pods/components/acc-avatar-img/component.ts
Normal file
13
webapp/app/pods/components/acc-avatar-img/component.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import Component from '@glimmer/component';
|
||||
|
||||
const FALLBACK_SOURCE =
|
||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABGdBTUEAALGPC/xhBQAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAMKADAAQAAAABAAAAMAAAAAD4/042AAACj0lEQVRoBdVaPUsDQRCNCuIX+C0ELGy0sBD9AxIjiI2VoFhZ5e+IlVbWgo2gIFhE0EKshZRiIXZWJoqCGt87cmcSzO1uZpLsDTx2Lzc7895OdrN3mkr9WRe6OeAOKAJlz0BO5EaO5FpjaVzlAd9IN+JDruQcGNUkiXwoipyDSrAk4YdJa3M9IH8ATANJtCmWgItjKInswblEAfzaJNa6E8u8QrwVAm4RexdYAkaBEWAR4Ge8p25aO88LmGUs2K3Ah75aeVUCPYHQnAX50GUWnWdAQ4RKkM2QmUO77YuAGwfS9a5cE6IqaCzi03pWDtfnDr7/umoI4AmxWVPZlUQlBPPhZtljHE+UovwaFegTCOgXjA2GagiQnKMGfRAwISAxKRirVoFlAQnJWDUBqwIBWcHYaKhoF0CUN6A3imbfGYDrJyDKr7GISWTdnnfkuYFeM8KjAGFHNAMIwvEXYTCHVutFgqyEFQHfaOcdyC9UxmlMnooAEjl2EHDiowBWYcZCBH3oqzH7ZY1FHHJmrK3wIqbls4NaXrVAFcJ8y2EyGx9TjJr7KqVExEfA5mgwDr8HQCuvOFARZPYBErO1MTjuAa+ASAjLyQAuRv974Bq4Ai6BD6AZ41F8DcgCGYDbq/NXzHYGSHQH4Oy1yhibOZjLlpfR8QvB+Aah3caczG0SYnQ4bDfzqnzMHSvAZhs9qwrY7q5V7liFYCx54pIK5rYcy8+mAu9SFoLxfNaINZttlCI5C50w5ua5qaHZVKDh4Dbc+DHlsKmAKUZH7/teAePkUEDJ6OWvQ4kCCv7yMzIrUMCR0c1fh4A7F3IeiP3B8PA+OUcn13TCRJA8OdcY1fD/JvgHCz6k+FYRciI3coxm/hfolUaVtS+2pQAAAABJRU5ErkJggg==';
|
||||
|
||||
export default class AccAvatarImg extends Component {
|
||||
fallbackImage(event: ErrorEvent) {
|
||||
const target = event.target as HTMLImageElement;
|
||||
|
||||
target.src = FALLBACK_SOURCE;
|
||||
target.style.opacity = '0.15';
|
||||
}
|
||||
}
|
2
webapp/app/pods/components/acc-avatar-img/template.hbs
Normal file
2
webapp/app/pods/components/acc-avatar-img/template.hbs
Normal file
@ -0,0 +1,2 @@
|
||||
{{!-- template-lint-disable no-invalid-interactive --}}
|
||||
<img alt="" {{on "error" this.fallbackImage }} ...attributes>
|
@ -1,5 +0,0 @@
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
classNameBindings: ['link', 'primary', 'danger']
|
||||
});
|
9
webapp/app/pods/components/acc-badge/component.ts
Normal file
9
webapp/app/pods/components/acc-badge/component.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import Component from '@glimmer/component';
|
||||
|
||||
interface Args {
|
||||
link?: boolean;
|
||||
primary?: boolean;
|
||||
danger?: boolean;
|
||||
}
|
||||
|
||||
export default class Badge extends Component<Args> {}
|
@ -1,54 +1,57 @@
|
||||
& {
|
||||
transition: $transition-speed $transition-easing;
|
||||
@value transition-speed, transition-easing from 'accent-webapp/styles/variables/transitions';
|
||||
@value color-grey, color-green, color-error from 'accent-webapp/styles/variables/colors';
|
||||
|
||||
.badge {
|
||||
transition: transition-speed transition-easing;
|
||||
transition-property: background;
|
||||
display: inline-block;
|
||||
padding: 1px 6px 1px 5px;
|
||||
background: lighten($color-grey, 28%);
|
||||
background: lighten(color-grey, 28%);
|
||||
border-radius: 3px;
|
||||
color: darken($color-grey, 10%);
|
||||
color: darken(color-grey, 10%);
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&.primary {
|
||||
background: $color-green;
|
||||
.primary {
|
||||
background: color-green;
|
||||
background: var(--color-primary);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&.danger {
|
||||
background: $color-error;
|
||||
.danger {
|
||||
background: color-error;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&.link {
|
||||
.link {
|
||||
padding: 0;
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
background: lighten($color-grey, 26%);
|
||||
background: lighten(color-grey, 26%);
|
||||
}
|
||||
}
|
||||
|
||||
&.link.primary {
|
||||
.link.primary {
|
||||
padding: 0;
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
background: darken($color-green, 3%);
|
||||
background: darken(color-green, 3%);
|
||||
background: var(--color-primary-darken-10);
|
||||
}
|
||||
}
|
||||
|
||||
&.link a {
|
||||
.link a {
|
||||
display: inline-block;
|
||||
padding: 1px 6px 1px 5px;
|
||||
color: lighten(#000, 50%);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&.link.primary a {
|
||||
.link.primary a {
|
||||
color: #fff;
|
||||
}
|
||||
|
3
webapp/app/pods/components/acc-badge/template.hbs
Normal file
3
webapp/app/pods/components/acc-badge/template.hbs
Normal file
@ -0,0 +1,3 @@
|
||||
<div local-class="badge {{if @link "link"}} {{if @primary "primary"}} {{if @danger "danger"}}">
|
||||
{{yield}}
|
||||
</div>
|
@ -1,17 +0,0 @@
|
||||
import Component from '@ember/component';
|
||||
import EmojiButton from '@joeattardi/emoji-button';
|
||||
|
||||
export default Component.extend({
|
||||
didInsertElement() {
|
||||
const button = this.element;
|
||||
const picker = new EmojiButton({
|
||||
showPreview: false
|
||||
});
|
||||
|
||||
picker.on('emoji', this.onPicked);
|
||||
|
||||
button.addEventListener('click', () => {
|
||||
picker.pickerVisible ? picker.hidePicker() : picker.showPicker(button);
|
||||
});
|
||||
}
|
||||
});
|
37
webapp/app/pods/components/acc-emoji-picker/component.ts
Normal file
37
webapp/app/pods/components/acc-emoji-picker/component.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import Component from '@glimmer/component';
|
||||
import {action} from '@ember/object';
|
||||
import EmojiButton from '@joeattardi/emoji-button';
|
||||
|
||||
interface Args {
|
||||
onPicked: () => string;
|
||||
}
|
||||
|
||||
export default class EmojiPicker extends Component<Args> {
|
||||
picker: EmojiButton;
|
||||
element: HTMLDivElement;
|
||||
|
||||
clickCallback = this.onClick.bind(this);
|
||||
|
||||
@action
|
||||
initializePicker(element: HTMLDivElement) {
|
||||
this.element = element;
|
||||
|
||||
this.picker = new EmojiButton({
|
||||
showPreview: false,
|
||||
});
|
||||
|
||||
this.picker.on('emoji', this.args.onPicked);
|
||||
}
|
||||
|
||||
@action
|
||||
destroyPicker() {
|
||||
this.picker.off('emoji', this.args.onPicked);
|
||||
}
|
||||
|
||||
@action
|
||||
onClick() {
|
||||
this.picker.pickerVisible
|
||||
? this.picker.hidePicker()
|
||||
: this.picker.showPicker(this.element);
|
||||
}
|
||||
}
|
@ -1 +1,11 @@
|
||||
{{yield}}
|
||||
<button
|
||||
{{did-insert this.initializePicker}}
|
||||
{{will-destroy this.destroyPicker}}
|
||||
{{on "click" (fn this.onClick)}}
|
||||
...attributes
|
||||
>
|
||||
<div>
|
||||
{{yield}}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
|
@ -1,31 +0,0 @@
|
||||
import {computed} from '@ember/object';
|
||||
import {readOnly} from '@ember/object/computed';
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
classNameBindings: ['isExiting', 'type'],
|
||||
|
||||
isExiting: readOnly('flash.exiting'),
|
||||
type: readOnly('flash.type'),
|
||||
|
||||
iconPath: computed('type', function() {
|
||||
switch (this.type) {
|
||||
case 'success':
|
||||
return 'assets/check.svg';
|
||||
case 'error':
|
||||
return 'assets/x.svg';
|
||||
case 'socket':
|
||||
return 'assets/activity.svg';
|
||||
default:
|
||||
null;
|
||||
}
|
||||
}),
|
||||
|
||||
actions: {
|
||||
close() {
|
||||
const flash = this.flash;
|
||||
|
||||
if (flash) flash.destroyMessage();
|
||||
}
|
||||
}
|
||||
});
|
39
webapp/app/pods/components/acc-flash-message/component.ts
Normal file
39
webapp/app/pods/components/acc-flash-message/component.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import Component from '@glimmer/component';
|
||||
import {action} from '@ember/object';
|
||||
import {readOnly} from '@ember/object/computed';
|
||||
|
||||
interface Args {
|
||||
flash: {
|
||||
exiting: boolean;
|
||||
type: string;
|
||||
destroyMessage: () => void;
|
||||
};
|
||||
}
|
||||
|
||||
export default class FlashMessage extends Component<Args> {
|
||||
@readOnly('args.flash.exiting')
|
||||
isExiting: boolean;
|
||||
|
||||
@readOnly('args.flash.type')
|
||||
type: 'info' | 'success' | 'error' | 'socket';
|
||||
|
||||
get iconPath() {
|
||||
switch (this.type) {
|
||||
case 'success':
|
||||
return 'assets/check.svg';
|
||||
case 'error':
|
||||
return 'assets/x.svg';
|
||||
case 'socket':
|
||||
return 'assets/activity.svg';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
close() {
|
||||
const flash = this.args.flash;
|
||||
|
||||
if (flash) flash.destroyMessage();
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
& {
|
||||
@value color-green, color-socket, color-error from 'accent-webapp/styles/variables/colors';
|
||||
|
||||
.flash {
|
||||
position: relative;
|
||||
margin-bottom: 10px;
|
||||
width: 400px;
|
||||
@ -8,35 +10,37 @@
|
||||
text-shadow: 0 1px 1px rgba(#000, 0.1);
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
animation: 0.3s flash-message-in ease;
|
||||
animation: 0.3s ease;
|
||||
animation-name: flash-message-in;
|
||||
pointer-events: all;
|
||||
|
||||
&.is-exiting {
|
||||
animation: 0.3s flash-message-out forwards ease-in-out;
|
||||
animation: 0.3s forwards ease-in-out;
|
||||
animation-name: flash-message-out;
|
||||
}
|
||||
}
|
||||
|
||||
&.success {
|
||||
background: $color-green;
|
||||
.flash.success {
|
||||
background: color-green;
|
||||
|
||||
.icon {
|
||||
stroke: lighten($color-green, 35%);
|
||||
stroke: lighten(color-green, 35%);
|
||||
}
|
||||
}
|
||||
|
||||
&.socket {
|
||||
background: $color-socket;
|
||||
.flash.socket {
|
||||
background: color-socket;
|
||||
|
||||
.icon {
|
||||
stroke: lighten($color-socket, 35%);
|
||||
stroke: lighten(color-socket, 35%);
|
||||
}
|
||||
}
|
||||
|
||||
&.error {
|
||||
background: $color-error;
|
||||
.flash.error {
|
||||
background: color-error;
|
||||
|
||||
.icon {
|
||||
stroke: lighten($color-error, 35%);
|
||||
stroke: lighten(color-error, 35%);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,14 +1,16 @@
|
||||
<button class="deleteButton" {{action "close"}}>
|
||||
{{inline-svg "assets/x.svg" class="deleteButton-icon"}}
|
||||
</button>
|
||||
<div local-class="flash {{if this.isExiting "is-exiting"}} {{this.type}}">
|
||||
<button local-class="deleteButton" {{on "click" (fn this.close)}}>
|
||||
{{inline-svg "assets/x.svg" local-class="deleteButton-icon"}}
|
||||
</button>
|
||||
|
||||
<div class="content">
|
||||
{{#if iconPath}}
|
||||
<div class="icon">
|
||||
{{inline-svg iconPath class="icon"}}
|
||||
</div>
|
||||
{{/if}}
|
||||
<p class="text">
|
||||
{{flash.message}}
|
||||
</p>
|
||||
<div local-class="content">
|
||||
{{#if this.iconPath}}
|
||||
<div local-class="icon">
|
||||
{{inline-svg this.iconPath}}
|
||||
</div>
|
||||
{{/if}}
|
||||
<p local-class="text">
|
||||
{{@flash.message}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,9 +0,0 @@
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
actions: {
|
||||
close() {
|
||||
this.onClose();
|
||||
}
|
||||
}
|
||||
});
|
8
webapp/app/pods/components/acc-modal/component.ts
Normal file
8
webapp/app/pods/components/acc-modal/component.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import Component from '@glimmer/component';
|
||||
|
||||
interface Args {
|
||||
small: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export default class Modal extends Component<Args> {}
|
@ -1,7 +1,7 @@
|
||||
<EmberWormhole @to="modals">
|
||||
<div class="acc-modal__wrapper">
|
||||
<div role="button" class="acc-modal__overlay" {{action "close"}}></div>
|
||||
<div role="dialog" class="acc-modal__container {{if small "acc-modal__container--small"}}">
|
||||
<div role="button" class="acc-modal__overlay" {{on "click" (fn @onClose)}}></div>
|
||||
<div role="dialog" class="acc-modal__container {{if @small "acc-modal__container--small"}}">
|
||||
{{yield}}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,3 +0,0 @@
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend();
|
15
webapp/app/pods/components/acc-select/component.ts
Normal file
15
webapp/app/pods/components/acc-select/component.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import Component from '@glimmer/component';
|
||||
|
||||
interface Args {
|
||||
searchEnabled: boolean;
|
||||
selected: any;
|
||||
options: any[];
|
||||
onchange: (value: any) => void;
|
||||
placeholder: string;
|
||||
search: (term: string) => Promise<any>;
|
||||
searchPlaceholder: string;
|
||||
matchTriggerWidth: boolean;
|
||||
renderInPlace: boolean;
|
||||
}
|
||||
|
||||
export default class Select extends Component<Args> {}
|
@ -1,12 +1,13 @@
|
||||
<PowerSelect
|
||||
@options={{options}}
|
||||
@matchTriggerWidth={{matchTriggerWidth}}
|
||||
@searchEnabled={{searchEnabled}}
|
||||
@selected={{selected}}
|
||||
@search={{search}}
|
||||
@placeholder={{placeholder}}
|
||||
@searchPlaceholder={{searchPlaceholder}}
|
||||
@onChange={{onchange}} as |option|
|
||||
@options={{@options}}
|
||||
@matchTriggerWidth={{@matchTriggerWidth}}
|
||||
@searchEnabled={{@searchEnabled}}
|
||||
@selected={{@selected}}
|
||||
@search={{@search}}
|
||||
@placeholder={{@placeholder}}
|
||||
@searchPlaceholder={{@searchPlaceholder}}
|
||||
@renderInPlace={{@renderInPlace}}
|
||||
@onChange={{@onchange}} as |option|
|
||||
>
|
||||
{{option.label}}
|
||||
</PowerSelect>
|
||||
|
@ -1,178 +0,0 @@
|
||||
import {computed} from '@ember/object';
|
||||
import {inject as service} from '@ember/service';
|
||||
import {readOnly, reads, equal} from '@ember/object/computed';
|
||||
import Component from '@ember/component';
|
||||
import {underscore, dasherize} from '@ember/string';
|
||||
|
||||
import parsedKeyProperty from 'accent-webapp/computed-macros/parsed-key';
|
||||
|
||||
/* eslint camelcase:0 */
|
||||
const ACTIONS_ICON_PATHS = {
|
||||
version_new: 'assets/tag.svg',
|
||||
add_to_version: 'assets/tag.svg',
|
||||
create_version: 'assets/tag.svg',
|
||||
sync: 'assets/sync.svg',
|
||||
merge: 'assets/merge.svg',
|
||||
rollback: 'assets/revert.svg',
|
||||
update: 'assets/pencil.svg',
|
||||
correct_conflict: 'assets/check.svg',
|
||||
correct_all: 'assets/check.svg',
|
||||
uncorrect_all: 'assets/revert.svg',
|
||||
uncorrect_conflict: 'assets/revert.svg',
|
||||
conflict_on_slave: 'assets/x.svg',
|
||||
conflict_on_corrected: 'assets/x.svg',
|
||||
conflict_on_proposed: 'assets/x.svg',
|
||||
remove: 'assets/x.svg',
|
||||
new_comment: 'assets/bubble.svg',
|
||||
new_slave: 'assets/language.svg',
|
||||
document_delete: 'assets/file.svg'
|
||||
};
|
||||
|
||||
// Attributes:
|
||||
// project: Object <project>
|
||||
// permissions: Ember Object containing <permission>
|
||||
// showTranslationLink: Boolean
|
||||
// activity: Object <project-activity>
|
||||
// componentTranslationPrefix: String
|
||||
export default Component.extend({
|
||||
intl: service('intl'),
|
||||
|
||||
classNameBindings: ['compact', 'activityItemClassName', 'rollbacked'],
|
||||
|
||||
action: readOnly('activity.action'),
|
||||
rollbacked: reads('activity.isRollbacked'),
|
||||
rollbackedOperationHasEmptyText: equal(
|
||||
'activity.rollbackedOperation.valueType',
|
||||
'EMPTY'
|
||||
),
|
||||
hasEmptyText: equal('activity.valueType', 'EMPTY'),
|
||||
|
||||
translationKey: parsedKeyProperty('activity.translation.key'),
|
||||
|
||||
activityItemClassName: computed('activity.action', function() {
|
||||
return dasherize(this.activity.action);
|
||||
}),
|
||||
|
||||
actionText: computed('action', function() {
|
||||
return this._getActionText(this.action);
|
||||
}),
|
||||
|
||||
rollbackedOperationActionText: computed(
|
||||
'activity.rollbackedOperation.action',
|
||||
function() {
|
||||
return this._getActionText(this.activity.rollbackedOperation.action);
|
||||
}
|
||||
),
|
||||
|
||||
showFromOperationTranslationLink: computed(
|
||||
'showTranslationLink',
|
||||
'activity.rollbackedOperation.translation.id',
|
||||
function() {
|
||||
return (
|
||||
this.showTranslationLink &&
|
||||
this.activity.rollbackedOperation &&
|
||||
this.activity.rollbackedOperation.translation &&
|
||||
this.activity.rollbackedOperation.translation.id
|
||||
);
|
||||
}
|
||||
),
|
||||
|
||||
showStats: computed('activity.stats.[]', function() {
|
||||
return this.activity.stats;
|
||||
}),
|
||||
|
||||
localizedStats: computed('activity.stats.[]', function() {
|
||||
return this.activity.stats.map(stat => {
|
||||
const text = this.intl.t(
|
||||
`components.${this.componentTranslationPrefix}.stats_text.${underscore(
|
||||
stat.action
|
||||
)}`
|
||||
);
|
||||
const count = stat.count;
|
||||
|
||||
return {text, count};
|
||||
});
|
||||
}),
|
||||
|
||||
statsLabel: computed('componentTranslationPrefix', function() {
|
||||
return this.intl.t(
|
||||
`components.${this.componentTranslationPrefix}.stats_label_text`
|
||||
);
|
||||
}),
|
||||
|
||||
showDocumentInfo: computed('action', 'activity.document.path', function() {
|
||||
const action = this.action;
|
||||
const actionsWithDocument = ['sync', 'document_delete', 'merge'];
|
||||
|
||||
return (
|
||||
actionsWithDocument.includes(action) && readOnly('activity.document.path')
|
||||
);
|
||||
}),
|
||||
|
||||
showVersionInfo: readOnly('activity.version.id'),
|
||||
|
||||
revisionName: computed('activity.revision.{name,language.name}', function() {
|
||||
return this.activity.revision.name || this.activity.revision.language.name;
|
||||
}),
|
||||
|
||||
showRevisionInfo: computed(
|
||||
'action',
|
||||
'activity.revision.language.id',
|
||||
function() {
|
||||
if (!this.activity.revision) return false;
|
||||
|
||||
const actionsWithRevision = [
|
||||
'new',
|
||||
'remove',
|
||||
'renew',
|
||||
'new_slave',
|
||||
'merge',
|
||||
'uncorrect_all',
|
||||
'correct_all',
|
||||
'batch_correct_conflict',
|
||||
'batch_update',
|
||||
'conflict_on_slave'
|
||||
];
|
||||
|
||||
return (
|
||||
actionsWithRevision.includes(this.action) &&
|
||||
this.activity.revision.language.id
|
||||
);
|
||||
}
|
||||
),
|
||||
|
||||
showFromOperationDocumentInfo: computed(
|
||||
'activity.rollbackedOperation.{action,document.path}',
|
||||
function() {
|
||||
const action = this.activity.rollbackedOperation.action;
|
||||
const actionsWithDocument = ['sync', 'document_delete', 'merge'];
|
||||
|
||||
return (
|
||||
actionsWithDocument.includes(action) &&
|
||||
readOnly('activity.rollbackedOperation.document.path')
|
||||
);
|
||||
}
|
||||
),
|
||||
|
||||
isShowingTranslationLink: computed(
|
||||
'showTranslationLink',
|
||||
'activity.{action,translation}',
|
||||
function() {
|
||||
return (
|
||||
this.showTranslationLink &&
|
||||
this.activity.translation &&
|
||||
this.activity.action !== 'rollback'
|
||||
);
|
||||
}
|
||||
),
|
||||
|
||||
iconPath: computed('action', function() {
|
||||
return ACTIONS_ICON_PATHS[this.action] || 'assets/add.svg';
|
||||
}),
|
||||
|
||||
_getActionText(action) {
|
||||
return this.intl.t(
|
||||
`components.${this.componentTranslationPrefix}.action_text.${action}`
|
||||
);
|
||||
}
|
||||
});
|
176
webapp/app/pods/components/activity-item/component.ts
Normal file
176
webapp/app/pods/components/activity-item/component.ts
Normal file
@ -0,0 +1,176 @@
|
||||
import {inject as service} from '@ember/service';
|
||||
import {readOnly, equal} from '@ember/object/computed';
|
||||
import Component from '@glimmer/component';
|
||||
import {underscore, dasherize} from '@ember/string';
|
||||
|
||||
import parsedKeyProperty from 'accent-webapp/computed-macros/parsed-key';
|
||||
import IntlService from 'ember-intl/services/intl';
|
||||
|
||||
/* eslint camelcase:0 */
|
||||
const ACTIONS_ICON_PATHS = {
|
||||
version_new: 'assets/tag.svg',
|
||||
add_to_version: 'assets/tag.svg',
|
||||
create_version: 'assets/tag.svg',
|
||||
sync: 'assets/sync.svg',
|
||||
merge: 'assets/merge.svg',
|
||||
rollback: 'assets/revert.svg',
|
||||
update: 'assets/pencil.svg',
|
||||
correct_conflict: 'assets/check.svg',
|
||||
correct_all: 'assets/check.svg',
|
||||
uncorrect_all: 'assets/revert.svg',
|
||||
uncorrect_conflict: 'assets/revert.svg',
|
||||
conflict_on_slave: 'assets/x.svg',
|
||||
conflict_on_corrected: 'assets/x.svg',
|
||||
conflict_on_proposed: 'assets/x.svg',
|
||||
remove: 'assets/x.svg',
|
||||
new_comment: 'assets/bubble.svg',
|
||||
new_slave: 'assets/language.svg',
|
||||
document_delete: 'assets/file.svg',
|
||||
};
|
||||
|
||||
interface Args {
|
||||
compact: boolean;
|
||||
permissions: Record<string, true>;
|
||||
showTranslationLink: boolean;
|
||||
componentTranslationPrefix: string;
|
||||
activity: any;
|
||||
project: any;
|
||||
}
|
||||
|
||||
export default class ActivityItem extends Component<Args> {
|
||||
@service('intl')
|
||||
intl: IntlService;
|
||||
|
||||
@readOnly('args.activity.action')
|
||||
action: keyof typeof ACTIONS_ICON_PATHS;
|
||||
|
||||
@readOnly('args.activity.isRollbacked')
|
||||
rollbacked: boolean;
|
||||
|
||||
@equal('args.activity.rollbackedOperation.valueType', 'EMPTY')
|
||||
rollbackedOperationHasEmptyText: boolean;
|
||||
|
||||
@equal('args.activity.fromOperation.text', 'EMPTY')
|
||||
fromOperationHasEmptyText: boolean;
|
||||
|
||||
@equal('args.activity.valueType', 'EMPTY')
|
||||
hasEmptyText: boolean;
|
||||
|
||||
@readOnly('args.activity.version.id')
|
||||
showVersionInfo: boolean;
|
||||
|
||||
translationKey = parsedKeyProperty(this.args.activity.translation?.key);
|
||||
|
||||
get activityItemClassName() {
|
||||
return dasherize(this.args.activity.action);
|
||||
}
|
||||
|
||||
get actionText() {
|
||||
return this.getActionText(this.action);
|
||||
}
|
||||
|
||||
get rollbackedOperationActionText() {
|
||||
return this.getActionText(this.args.activity.rollbackedOperation.action);
|
||||
}
|
||||
|
||||
get showFromOperationTranslationLink() {
|
||||
return (
|
||||
this.args.showTranslationLink &&
|
||||
this.args.activity.rollbackedOperation &&
|
||||
this.args.activity.rollbackedOperation.translation &&
|
||||
this.args.activity.rollbackedOperation.translation.id
|
||||
);
|
||||
}
|
||||
|
||||
get showStats() {
|
||||
return this.args.activity.stats;
|
||||
}
|
||||
|
||||
get localizedStats() {
|
||||
return this.args.activity.stats.map((stat: any) => {
|
||||
const text = this.intl.t(
|
||||
`components.${
|
||||
this.args.componentTranslationPrefix
|
||||
}.stats_text.${underscore(stat.action)}`
|
||||
);
|
||||
|
||||
const count = stat.count;
|
||||
|
||||
return {text, count};
|
||||
});
|
||||
}
|
||||
|
||||
get statsLabel() {
|
||||
return this.intl.t(
|
||||
`components.${this.args.componentTranslationPrefix}.stats_label_text`
|
||||
);
|
||||
}
|
||||
|
||||
get showDocumentInfo() {
|
||||
const action = this.action;
|
||||
const actionsWithDocument = ['sync', 'document_delete', 'merge'];
|
||||
|
||||
return (
|
||||
actionsWithDocument.includes(action) &&
|
||||
this.args.activity.document &&
|
||||
this.args.activity.document.path
|
||||
);
|
||||
}
|
||||
|
||||
get revisionName() {
|
||||
return (
|
||||
this.args.activity.revision.name ||
|
||||
this.args.activity.revision.language.name
|
||||
);
|
||||
}
|
||||
|
||||
get showRevisionInfo() {
|
||||
if (!this.args.activity.revision) return false;
|
||||
|
||||
const actionsWithRevision = [
|
||||
'new',
|
||||
'remove',
|
||||
'renew',
|
||||
'new_slave',
|
||||
'merge',
|
||||
'uncorrect_all',
|
||||
'correct_all',
|
||||
'batch_correct_conflict',
|
||||
'batch_update',
|
||||
'conflict_on_slave',
|
||||
];
|
||||
|
||||
return (
|
||||
actionsWithRevision.includes(this.action) &&
|
||||
this.args.activity.revision.language.id
|
||||
);
|
||||
}
|
||||
|
||||
get showFromOperationDocumentInfo() {
|
||||
const action = this.args.activity.rollbackedOperation.action;
|
||||
const actionsWithDocument = ['sync', 'document_delete', 'merge'];
|
||||
|
||||
return (
|
||||
actionsWithDocument.includes(action) &&
|
||||
this.args.activity.rollbackedOperation.document.path
|
||||
);
|
||||
}
|
||||
|
||||
get isShowingTranslationLink() {
|
||||
return (
|
||||
this.args.showTranslationLink &&
|
||||
this.args.activity.translation &&
|
||||
this.args.activity.action !== 'rollback'
|
||||
);
|
||||
}
|
||||
|
||||
get iconPath() {
|
||||
return ACTIONS_ICON_PATHS[this.action] || 'assets/add.svg';
|
||||
}
|
||||
|
||||
private getActionText(action: keyof typeof ACTIONS_ICON_PATHS) {
|
||||
return this.intl.t(
|
||||
`components.${this.args.componentTranslationPrefix}.action_text.${action}`
|
||||
);
|
||||
}
|
||||
}
|
@ -1,4 +1,8 @@
|
||||
& {
|
||||
@value transition-speed, transition-easing from 'accent-webapp/styles/variables/transitions';
|
||||
@value color-border, color-light-background, color-green, color-grey, color-error, color-success, color-warning, color-black from 'accent-webapp/styles/variables/colors';
|
||||
@value font-monospace from 'accent-webapp/styles/variables/fonts';
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
position: relative;
|
||||
margin: 25px 0;
|
||||
@ -10,17 +14,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.rollback,
|
||||
&.merge,
|
||||
&.new-slave,
|
||||
&.uncorrect-all,
|
||||
&.correct-all,
|
||||
&.sync {
|
||||
left: 9px;
|
||||
.item.rollback,
|
||||
.item.merge,
|
||||
.item.new-slave,
|
||||
.item.uncorrect-all,
|
||||
.item.correct-all,
|
||||
.item.sync {
|
||||
left: 8px;
|
||||
width: calc(100% + -9px);
|
||||
padding: 5px;
|
||||
background: #fff;
|
||||
border: 1px solid rgba($color-border, 0.5);
|
||||
border: 1px solid rgba(color-border, 0.5);
|
||||
|
||||
.item-iconContainer {
|
||||
top: 3px;
|
||||
@ -29,29 +33,35 @@
|
||||
|
||||
.item-content {
|
||||
width: 100%;
|
||||
padding: 5px 5px 0;
|
||||
padding: 5px 5px 0 2px;
|
||||
margin-left: -21px;
|
||||
background: #fff;
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.sync {
|
||||
.item.sync {
|
||||
position: relative;
|
||||
padding: 6px 10px 5px;
|
||||
background: $color-light-background;
|
||||
border-color: transparent;
|
||||
border-left-color: $color-green;
|
||||
border-left-color: var(--color-primary);
|
||||
|
||||
&.compact {
|
||||
.item-content {
|
||||
padding-left: 0;
|
||||
}
|
||||
&:before {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
content: '';
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--color-primary);
|
||||
box-shadow: 0 1px 10px var(--color-primary);
|
||||
opacity: 0.16;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.item-iconContainer {
|
||||
left: -21px;
|
||||
background: $color-green;
|
||||
background: color-green;
|
||||
background: var(--color-primary);
|
||||
border-color: transparent;
|
||||
box-shadow: none;
|
||||
@ -63,63 +73,52 @@
|
||||
|
||||
.item-stats {
|
||||
padding: 0;
|
||||
background: lighten($color-green, 46%);
|
||||
background: var(--color-primary-lighten-99);
|
||||
color: darken($color-green, 25%);
|
||||
background: transparent;
|
||||
color: darken(color-green, 25%);
|
||||
color: var(--color-primary-darken-50);
|
||||
}
|
||||
|
||||
.item-date {
|
||||
color: $color-grey;
|
||||
color: rgba(#000, 0.3);
|
||||
}
|
||||
|
||||
.item-details-link {
|
||||
color: $color-green;
|
||||
color: color-green;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.item-content {
|
||||
background: var(--color-primary-lighten-95);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.item-documentPath {
|
||||
color: darken($color-green, 25%);
|
||||
color: darken(color-green, 25%);
|
||||
color: var(--color-primary-darken-30);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.item-user,
|
||||
.item-header-content {
|
||||
color: darken($color-green, 25%);
|
||||
color: darken(color-green, 25%);
|
||||
color: var(--color-primary-darken-30);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.item-user.item-user--bot {
|
||||
padding-left: 23px;
|
||||
}
|
||||
|
||||
.item-user.item-user--pictureUrl {
|
||||
padding-left: 28px;
|
||||
}
|
||||
|
||||
.item-user-icon {
|
||||
stroke: darken($color-green, 25%);
|
||||
stroke: darken(color-green, 25%);
|
||||
stroke: var(--color-primary);
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
}
|
||||
|
||||
.item-user-picture {
|
||||
top: -1px;
|
||||
width: 21px;
|
||||
height: 21px;
|
||||
color: rgba(color-black, 0.9);
|
||||
}
|
||||
|
||||
&.compact {
|
||||
.item-content {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.item-header {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.item-iconContainer-icon {
|
||||
stroke: $color-green;
|
||||
stroke: color-green;
|
||||
stroke: var(--color-primary);
|
||||
}
|
||||
|
||||
@ -139,9 +138,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.rollback {
|
||||
background: $color-light-background;
|
||||
border: 1px solid rgba($color-border, 0.3);
|
||||
.item.rollback {
|
||||
background: color-light-background;
|
||||
border: 1px solid rgba(color-border, 0.3);
|
||||
|
||||
.item-header {
|
||||
margin-bottom: 4px;
|
||||
@ -158,14 +157,14 @@
|
||||
.item-content {
|
||||
padding: 5px 0 0;
|
||||
margin: 0;
|
||||
background: $color-light-background;
|
||||
background: color-light-background;
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.uncorrect-all {
|
||||
.item.uncorrect-all {
|
||||
.item-iconContainer {
|
||||
background: $color-error;
|
||||
background: color-error;
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
@ -175,20 +174,20 @@
|
||||
|
||||
&.compact {
|
||||
.item-iconContainer-icon {
|
||||
stroke: $color-error;
|
||||
stroke: color-error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.correct-all {
|
||||
.item.correct-all {
|
||||
&.compact {
|
||||
.item-iconContainer-icon {
|
||||
stroke: $color-success;
|
||||
stroke: color-success;
|
||||
}
|
||||
}
|
||||
|
||||
.item-iconContainer {
|
||||
background: $color-success;
|
||||
background: color-success;
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
@ -197,11 +196,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.document-delete {
|
||||
border-color: lighten($color-error, 35%);
|
||||
.item.document-delete {
|
||||
border-color: lighten(color-error, 35%);
|
||||
|
||||
.item-iconContainer {
|
||||
background: $color-error;
|
||||
background: color-error;
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
@ -211,57 +210,57 @@
|
||||
|
||||
&.compact {
|
||||
.item-iconContainer-icon {
|
||||
stroke: $color-error;
|
||||
stroke: color-error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.correct-conflict {
|
||||
.item.correct-conflict {
|
||||
.item-iconContainer {
|
||||
border-color: rgba($color-success, 0.4);
|
||||
border-color: rgba(color-success, 0.4);
|
||||
}
|
||||
|
||||
.item-iconContainer-icon {
|
||||
stroke: $color-success;
|
||||
stroke: color-success;
|
||||
}
|
||||
}
|
||||
|
||||
&.uncorrect-conflict {
|
||||
.item.uncorrect-conflict {
|
||||
.item-iconContainer {
|
||||
border-color: rgba($color-error, 0.2);
|
||||
border-color: rgba(color-error, 0.2);
|
||||
}
|
||||
|
||||
.item-iconContainer-icon {
|
||||
stroke: $color-error;
|
||||
stroke: color-error;
|
||||
}
|
||||
}
|
||||
|
||||
&.conflict-on-corrected {
|
||||
.item.conflict-on-corrected {
|
||||
.item-iconContainer {
|
||||
border-color: lighten($color-error, 35%);
|
||||
border-color: lighten(color-error, 35%);
|
||||
}
|
||||
|
||||
.item-iconContainer-icon {
|
||||
stroke: $color-error;
|
||||
stroke: color-error;
|
||||
}
|
||||
}
|
||||
|
||||
&.conflict-on-proposed {
|
||||
.item.conflict-on-proposed {
|
||||
.item-iconContainer {
|
||||
border-color: lighten($color-warning, 25%);
|
||||
border-color: lighten(color-warning, 25%);
|
||||
}
|
||||
|
||||
.item-iconContainer-icon {
|
||||
stroke: $color-warning;
|
||||
stroke: color-warning;
|
||||
}
|
||||
}
|
||||
|
||||
&.rollbacked {
|
||||
.item.rollbacked {
|
||||
.item-stats,
|
||||
.item-translationText,
|
||||
.item-actions,
|
||||
.item-header {
|
||||
opacity: 0.4;
|
||||
opacity: 0.6;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
@ -272,13 +271,13 @@
|
||||
|
||||
.item-iconContainer {
|
||||
background: #fff;
|
||||
border-color: rgba($color-grey, 0.4);
|
||||
border-color: rgba(color-grey, 0.4);
|
||||
}
|
||||
|
||||
.item-iconContainer-icon {
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
stroke: rgba($color-grey, 0.5);
|
||||
stroke: rgba(color-grey, 0.5);
|
||||
}
|
||||
|
||||
&.rollback,
|
||||
@ -286,7 +285,7 @@
|
||||
&.new-slave,
|
||||
&.uncorrect-all,
|
||||
&.correct-all {
|
||||
border-color: $color-border;
|
||||
border-color: color-border;
|
||||
|
||||
.item-iconContainer {
|
||||
left: -19px;
|
||||
@ -295,7 +294,11 @@
|
||||
}
|
||||
|
||||
&.sync {
|
||||
border-color: rgba($color-green, 0.1);
|
||||
border-color: rgba(color-green, 0.1);
|
||||
|
||||
&:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.item-iconContainer {
|
||||
left: -19px;
|
||||
@ -304,7 +307,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.rollback.compact {
|
||||
.item.rollback.compact {
|
||||
.item-iconContainer {
|
||||
top: -2px;
|
||||
left: -17px;
|
||||
@ -316,8 +319,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.rollbacked.compact,
|
||||
&.compact {
|
||||
.item.rollbacked.compact,
|
||||
.item.compact {
|
||||
width: 100%;
|
||||
margin: 12px 0 2px;
|
||||
border-color: transparent;
|
||||
@ -395,6 +398,10 @@
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
.item-details-link {
|
||||
color: rgba(color-black, 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
.item-wrapper {
|
||||
@ -406,7 +413,7 @@
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
font-size: 12px;
|
||||
font-family: $font-monospace;
|
||||
font-family: font-monospace;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
@ -421,7 +428,7 @@
|
||||
height: 21px;
|
||||
margin-right: 10px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid $color-grey;
|
||||
border: 1px solid color-grey;
|
||||
box-shadow: 0 0 0 5px #fff;
|
||||
background: #fff;
|
||||
}
|
||||
@ -430,7 +437,7 @@
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
flex: 0 0 11px;
|
||||
stroke: $color-grey;
|
||||
stroke: color-grey;
|
||||
}
|
||||
|
||||
.item-content {
|
||||
@ -438,7 +445,7 @@
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
&.compact {
|
||||
.item.compact {
|
||||
.item-header {
|
||||
flex-direction: column;
|
||||
}
|
||||
@ -498,7 +505,7 @@
|
||||
margin-left: 2px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
color: #565656;
|
||||
color: rgba(color-black, 0.6);
|
||||
}
|
||||
|
||||
.item-translationFromOperationText,
|
||||
@ -506,7 +513,7 @@
|
||||
.item-translationText {
|
||||
margin: 5px 0 10px;
|
||||
padding: 8px;
|
||||
background: $color-light-background;
|
||||
background: color-light-background;
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
}
|
||||
@ -536,7 +543,7 @@
|
||||
|
||||
.item-documentPath {
|
||||
display: inline-block;
|
||||
color: $color-black;
|
||||
color: color-black;
|
||||
color: var(--color-black);
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
@ -555,12 +562,12 @@
|
||||
text-decoration: none;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
transition: $transition-speed $transition-easing;
|
||||
transition: transition-speed transition-easing;
|
||||
transition-property: color;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: $color-green;
|
||||
color: color-green;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
@ -579,7 +586,7 @@
|
||||
.item-revisionLink {
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
color: $color-green;
|
||||
color: color-green;
|
||||
color: var(--color-primary);
|
||||
|
||||
&:hover,
|
||||
@ -596,7 +603,7 @@
|
||||
}
|
||||
|
||||
.item-date {
|
||||
color: $color-grey;
|
||||
color: rgba(#000, 0.3);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
@ -606,18 +613,18 @@
|
||||
}
|
||||
|
||||
.item-rollback {
|
||||
transition: $transition-speed $transition-easing;
|
||||
transition: transition-speed transition-easing;
|
||||
transition-property: opacity, color;
|
||||
opacity: 0;
|
||||
background: none;
|
||||
padding: 0;
|
||||
margin: 0 0 0 5px;
|
||||
color: $color-grey;
|
||||
color: color-grey;
|
||||
font-size: 11px;
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
color: darken($color-grey, 25%);
|
||||
color: darken(color-grey, 25%);
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
@ -649,21 +656,21 @@
|
||||
margin-left: 5px;
|
||||
border-left: 1px solid #ddd;
|
||||
text-decoration: none;
|
||||
color: $color-grey;
|
||||
color: color-grey;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
color: $color-green;
|
||||
color: color-green;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.item-content-rollbacked {
|
||||
margin-bottom: 6px;
|
||||
color: $color-error;
|
||||
color: color-error;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
@ -1,219 +1,219 @@
|
||||
<li class="item-wrapper">
|
||||
<span class="item-iconContainer">
|
||||
{{inline-svg iconPath class="item-iconContainer-icon"}}
|
||||
</span>
|
||||
<div class="item-content">
|
||||
{{#if activity.isRollbacked}}
|
||||
<div class="item-content-rollbacked">
|
||||
{{t "components.activity_item.rollbacked"}}
|
||||
<TimeAgoInWordsTag
|
||||
@date={{activity.updatedAt}}
|
||||
class="item-rollbacked-date"
|
||||
/>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="item-header">
|
||||
<div class="item-header-content">
|
||||
{{#if activity.user.isBot}}
|
||||
<span class="item-user item-user--bot">
|
||||
{{inline-svg "assets/bot.svg" class="item-user-icon"}}
|
||||
{{activity.user.fullname}}
|
||||
<div local-class="item {{this.activityItemClassName}} {{if @compact "compact"}} {{if this.rollbacked "rollbacked"}}">
|
||||
<li local-class="item-wrapper">
|
||||
<span local-class="item-iconContainer">
|
||||
{{inline-svg this.iconPath local-class="item-iconContainer-icon"}}
|
||||
</span>
|
||||
<div local-class="item-content">
|
||||
{{#if @activity.isRollbacked}}
|
||||
<div local-class="item-content-rollbacked">
|
||||
{{t "components.activity_item.rollbacked"}}
|
||||
<span local-class="item-rollbacked-date">
|
||||
<TimeAgoInWordsTag @date={{@activity.updatedAt}} />
|
||||
</span>
|
||||
{{else}}
|
||||
<span
|
||||
class="item-user
|
||||
{{if activity.user.pictureUrl "item-user--pictureUrl"}}"
|
||||
>
|
||||
{{#if activity.user.pictureUrl}}
|
||||
<img
|
||||
class="item-user-picture"
|
||||
src="{{activity.user.pictureUrl}}"
|
||||
>
|
||||
{{/if}}
|
||||
{{activity.user.fullname}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{actionText}}
|
||||
|
||||
{{#if showDocumentInfo}}
|
||||
<LinkTo
|
||||
@route="logged-in.project.files.export"
|
||||
@models={{array project.id activity.document.id}}
|
||||
class="item-documentPath"
|
||||
>
|
||||
{{activity.document.path}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
|
||||
{{#if showVersionInfo}}
|
||||
<span class="item-version-tag">
|
||||
{{activity.version.tag}}
|
||||
</span>
|
||||
{{/if}}
|
||||
|
||||
{{#if showRevisionInfo}}
|
||||
<LinkTo
|
||||
@route="logged-in.project.revision.translations"
|
||||
@models={{array project.id activity.revision.id}}
|
||||
class="item-revisionLink"
|
||||
>
|
||||
{{revisionName}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
|
||||
{{#if isShowingTranslationLink}}
|
||||
{{#if activity.translation.isRemoved}}
|
||||
<LinkTo
|
||||
@route="logged-in.project.translation"
|
||||
@models={{array project.id activity.translation.id}}
|
||||
class="item-translationLink
|
||||
item-translationLink--removed"
|
||||
>
|
||||
<small class="item-translationLink-prefix">
|
||||
{{#if translationKey.prefix}}
|
||||
{{translationKey.prefix}}
|
||||
{{else}}
|
||||
{{translation.document.path}}
|
||||
{{/if}}
|
||||
</small>
|
||||
{{translationKey.value}}
|
||||
</LinkTo>
|
||||
<div local-class="item-header">
|
||||
<div local-class="item-header-content">
|
||||
{{#if @activity.user.isBot}}
|
||||
<span local-class="item-user item-user--bot">
|
||||
{{inline-svg "assets/bot.svg" local-class="item-user-icon"}}
|
||||
{{@activity.user.fullname}}
|
||||
</span>
|
||||
{{else}}
|
||||
<LinkTo
|
||||
@route="logged-in.project.translation"
|
||||
@models={{array project.id activity.translation.id}}
|
||||
class="item-translationLink"
|
||||
<span
|
||||
local-class="item-user
|
||||
{{if @activity.user.pictureUrl "item-user--pictureUrl"}}"
|
||||
>
|
||||
<small class="item-translationLink-prefix">
|
||||
{{#if translationKey.prefix}}
|
||||
{{translationKey.prefix}}
|
||||
{{else}}
|
||||
{{translation.document.path}}
|
||||
{{/if}}
|
||||
</small>
|
||||
{{translationKey.value}}
|
||||
{{#if @activity.user.pictureUrl}}
|
||||
<AccAvatarImg
|
||||
local-class="item-user-picture"
|
||||
src="{{@activity.user.pictureUrl}}"
|
||||
/>
|
||||
{{/if}}
|
||||
{{@activity.user.fullname}}
|
||||
</span>
|
||||
{{/if}}
|
||||
|
||||
{{this.actionText}}
|
||||
|
||||
{{#if this.showDocumentInfo}}
|
||||
<LinkTo
|
||||
@route="logged-in.project.files.export"
|
||||
@models={{array @project.id @activity.document.id}}
|
||||
local-class="item-documentPath"
|
||||
>
|
||||
{{@activity.document.path}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div class="item-actions">
|
||||
<TimeAgoInWordsTag @date={{activity.insertedAt}} class="item-date" />
|
||||
<LinkTo
|
||||
@route="logged-in.project.activity"
|
||||
@models={{array project.id activity.id}}
|
||||
class="item-details-link"
|
||||
>
|
||||
{{t "components.activity_item.details"}}
|
||||
</LinkTo>
|
||||
</div>
|
||||
</div>
|
||||
{{#if this.showVersionInfo}}
|
||||
<span local-class="item-version-tag">
|
||||
{{@activity.version.tag}}
|
||||
</span>
|
||||
{{/if}}
|
||||
|
||||
{{#if activity.rollbackedOperation}}
|
||||
<div class="item-rollback-content">
|
||||
<div>
|
||||
<span class="item-rollback-user">
|
||||
{{activity.rollbackedOperation.user.fullname}}
|
||||
</span>
|
||||
{{rollbackedOperationActionText}}
|
||||
{{#if this.showRevisionInfo}}
|
||||
<LinkTo
|
||||
@route="logged-in.project.revision.translations"
|
||||
@models={{array @project.id @activity.revision.id}}
|
||||
local-class="item-revisionLink"
|
||||
>
|
||||
{{this.revisionName}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
|
||||
{{#if showFromOperationTranslationLink}}
|
||||
{{#if activity.rollbackedOperation.translation.isRemoved}}
|
||||
{{#if this.isShowingTranslationLink}}
|
||||
{{#if @activity.translation.isRemoved}}
|
||||
<LinkTo
|
||||
@route="logged-in.project.translation"
|
||||
@models={{array
|
||||
project.id
|
||||
activity.rollbackedOperation.translation.id
|
||||
}}
|
||||
class="item-translationLink--removed"
|
||||
@models={{array @project.id @activity.translation.id}}
|
||||
local-class="item-translationLink
|
||||
item-translationLink--removed"
|
||||
>
|
||||
{{activity.rollbackedOperation.translation.key}}
|
||||
<small local-class="item-translationLink-prefix">
|
||||
{{#if this.translationKey.prefix}}
|
||||
{{this.translationKey.prefix}}
|
||||
{{else}}
|
||||
{{@activity.document.path}}
|
||||
{{/if}}
|
||||
</small>
|
||||
{{this.translationKey.value}}
|
||||
</LinkTo>
|
||||
{{else}}
|
||||
<LinkTo
|
||||
@route="logged-in.project.translation"
|
||||
@models={{array
|
||||
project.id
|
||||
activity.rollbackedOperation.translation.id
|
||||
}}
|
||||
class="item-translationLink"
|
||||
@models={{array @project.id @activity.translation.id}}
|
||||
local-class="item-translationLink"
|
||||
>
|
||||
{{activity.rollbackedOperation.translation.key}}
|
||||
<small local-class="item-translationLink-prefix">
|
||||
{{#if this.translationKey.prefix}}
|
||||
{{this.translationKey.prefix}}
|
||||
{{else}}
|
||||
{{@activity.document.path}}
|
||||
{{/if}}
|
||||
</small>
|
||||
{{this.translationKey.value}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{#if activity.fromOperation.text}}
|
||||
<div class="item-translationFromOperationText">
|
||||
{{activity.fromOperation.text}}
|
||||
</div>
|
||||
{{else if fromOperationHasEmptyText}}
|
||||
<div class="item-translationFromOperationText">
|
||||
<span class="item-translationText-emptyText">
|
||||
{{t "components.activity_item.empty_text"}}
|
||||
</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if showFromOperationDocumentInfo}}
|
||||
<LinkTo
|
||||
@route="logged-in.project.files.export"
|
||||
@models={{array
|
||||
project.id
|
||||
activity.rollbackedOperation.document.id
|
||||
}}
|
||||
class="item-documentPath"
|
||||
>
|
||||
{{activity.rollbackedOperation.document.path}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#unless compact}}
|
||||
<TimeAgoInWordsTag
|
||||
@date={{activity.rollbackedOperation.insertedAt}}
|
||||
class="item-date"
|
||||
/>
|
||||
<div local-class="item-actions">
|
||||
<span local-class="item-date"><TimeAgoInWordsTag @date={{@activity.insertedAt}} /></span>
|
||||
<LinkTo
|
||||
@route="logged-in.project.activity"
|
||||
@models={{array project.id activity.rollbackedOperation.id}}
|
||||
class="item-details-link"
|
||||
@models={{array @project.id @activity.id}}
|
||||
local-class="item-details-link"
|
||||
>
|
||||
{{t "components.activity_item.details"}}
|
||||
</LinkTo>
|
||||
{{/unless}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if activity.text}}
|
||||
<div class="item-translationText">
|
||||
<div class="item-translationText-text">{{activity.text}}</div>
|
||||
</div>
|
||||
{{else if hasEmptyText}}
|
||||
<div class="item-translationText">
|
||||
<span class="item-translationText-emptyText">
|
||||
{{t "components.activity_item.empty_text"}}
|
||||
</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if @activity.rollbackedOperation}}
|
||||
<div local-class="item-rollback-content">
|
||||
<div>
|
||||
<span local-class="item-rollback-user">
|
||||
{{@activity.rollbackedOperation.user.fullname}}
|
||||
</span>
|
||||
{{this.rollbackedOperationActionText}}
|
||||
|
||||
{{#if showStats}}
|
||||
<ul class="item-stats">
|
||||
<span class="item-stats-label">
|
||||
{{statsLabel}}
|
||||
</span>
|
||||
{{#each localizedStats as |stat|}}
|
||||
<li>
|
||||
{{stat.text}}
|
||||
:
|
||||
<b>
|
||||
{{stat.count}}
|
||||
</b>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/if}}
|
||||
</div>
|
||||
</li>
|
||||
{{#if this.showFromOperationTranslationLink}}
|
||||
{{#if @activity.rollbackedOperation.translation.isRemoved}}
|
||||
<LinkTo
|
||||
@route="logged-in.project.translation"
|
||||
@models={{array
|
||||
@project.id
|
||||
@activity.rollbackedOperation.translation.id
|
||||
}}
|
||||
local-class="item-translationLink--removed"
|
||||
>
|
||||
{{@activity.rollbackedOperation.translation.key}}
|
||||
</LinkTo>
|
||||
{{else}}
|
||||
<LinkTo
|
||||
@route="logged-in.project.translation"
|
||||
@models={{array
|
||||
@project.id
|
||||
@activity.rollbackedOperation.translation.id
|
||||
}}
|
||||
local-class="item-translationLink"
|
||||
>
|
||||
{{@activity.rollbackedOperation.translation.key}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{#if @activity.fromOperation.text}}
|
||||
<div local-class="item-translationFromOperationText">
|
||||
{{@activity.fromOperation.text}}
|
||||
</div>
|
||||
{{else if this.fromOperationHasEmptyText}}
|
||||
<div local-class="item-translationFromOperationText">
|
||||
<span local-class="item-translationText-emptyText">
|
||||
{{t "components.activity_item.empty_text"}}
|
||||
</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.showFromOperationDocumentInfo}}
|
||||
<LinkTo
|
||||
@route="logged-in.project.files.export"
|
||||
@models={{array
|
||||
@project.id
|
||||
@activity.rollbackedOperation.document.id
|
||||
}}
|
||||
local-class="item-documentPath"
|
||||
>
|
||||
{{@activity.rollbackedOperation.document.path}}
|
||||
</LinkTo>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#unless @compact}}
|
||||
<span local-class="item-date">
|
||||
<TimeAgoInWordsTag @date={{@activity.rollbackedOperation.insertedAt}} />
|
||||
</span>
|
||||
<LinkTo
|
||||
@route="logged-in.project.activity"
|
||||
@models={{array @project.id @activity.rollbackedOperation.id}}
|
||||
local-class="item-details-link"
|
||||
>
|
||||
{{t "components.activity_item.details"}}
|
||||
</LinkTo>
|
||||
{{/unless}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if @activity.text}}
|
||||
<div local-class="item-translationText">
|
||||
<div local-class="item-translationText-text">{{@activity.text}}</div>
|
||||
</div>
|
||||
{{else if this.hasEmptyText}}
|
||||
<div local-class="item-translationText">
|
||||
<span local-class="item-translationText-emptyText">
|
||||
{{t "components.activity_item.empty_text"}}
|
||||
</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.showStats}}
|
||||
<ul local-class="item-stats">
|
||||
<span local-class="item-stats-label">
|
||||
{{this.statsLabel}}
|
||||
</span>
|
||||
{{#each this.localizedStats as |stat|}}
|
||||
<li>
|
||||
{{stat.text}}
|
||||
:
|
||||
<b>
|
||||
{{stat.count}}
|
||||
</b>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/if}}
|
||||
</div>
|
||||
</li>
|
||||
</div>
|
||||
|
@ -1,5 +0,0 @@
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
tagName: 'footer'
|
||||
});
|
@ -0,0 +1,3 @@
|
||||
import Component from '@glimmer/component';
|
||||
|
||||
export default class ApplicationFooter extends Component {}
|
@ -1,8 +1,10 @@
|
||||
& {
|
||||
@value color-light-background, color-grey from 'accent-webapp/styles/variables/colors';
|
||||
|
||||
.footer {
|
||||
padding: 10px;
|
||||
margin-top: 40px;
|
||||
background: $color-light-background;
|
||||
color: $color-grey;
|
||||
background: color-light-background;
|
||||
color: color-grey;
|
||||
font-size: 12px;
|
||||
text-align: right;
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
<div class="inner">
|
||||
<div>
|
||||
{{t "components.application_footer.text"}}
|
||||
<a class="external-link" target="_blank" rel="noopener" href="http://mirego.com">
|
||||
{{t "general.company_name"}}
|
||||
</a>
|
||||
<footer local-class="footer">
|
||||
<div local-class="inner">
|
||||
<div>
|
||||
{{t "components.application_footer.text"}}
|
||||
<a local-class="external-link" target="_blank" rel="noopener" href="https://mirego.com">
|
||||
{{t "general.company_name"}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
@ -1,18 +0,0 @@
|
||||
import {reads} from '@ember/object/computed';
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
classNameBindings: [':button', 'loading:button--loading'],
|
||||
tagName: 'button',
|
||||
attributeBindings: ['disabled', 'type'],
|
||||
|
||||
disabled: reads('loading'),
|
||||
loading: false,
|
||||
|
||||
click() {
|
||||
if (this.disabled) return;
|
||||
const click = this.onClick;
|
||||
|
||||
if (typeof click === 'function') click();
|
||||
}
|
||||
});
|
21
webapp/app/pods/components/async-button/component.ts
Normal file
21
webapp/app/pods/components/async-button/component.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import Component from '@glimmer/component';
|
||||
import {action} from '@ember/object';
|
||||
|
||||
interface Args {
|
||||
onClick: () => void;
|
||||
loading?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export default class AsyncButton extends Component<Args> {
|
||||
get disabled() {
|
||||
return this.args.disabled || this.args.loading;
|
||||
}
|
||||
|
||||
@action
|
||||
onClick() {
|
||||
if (this.args.disabled) return;
|
||||
|
||||
if (typeof this.args.onClick === 'function') this.args.onClick();
|
||||
}
|
||||
}
|
@ -1,5 +1,8 @@
|
||||
& {
|
||||
padding: 0;
|
||||
@value transition-speed, transition-easing from 'accent-webapp/styles/variables/transitions';
|
||||
@value color-black from 'accent-webapp/styles/variables/colors';
|
||||
|
||||
.button {
|
||||
padding: 0 !important;
|
||||
|
||||
&.button--loading {
|
||||
cursor: default;
|
||||
@ -14,13 +17,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.button--filled {
|
||||
&:global(.button--filled) {
|
||||
.loading {
|
||||
fill: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
&.button--small {
|
||||
&:global(.button--small) {
|
||||
&.button--loading {
|
||||
.loading {
|
||||
width: 14px;
|
||||
@ -40,7 +43,7 @@
|
||||
}
|
||||
|
||||
.label {
|
||||
transition: $transition-speed $transition-easing;
|
||||
transition: transition-speed transition-easing;
|
||||
transition-property: transform;
|
||||
transform: translate3d(0, 0, 0);
|
||||
display: flex;
|
||||
@ -49,12 +52,12 @@
|
||||
}
|
||||
|
||||
.loading {
|
||||
transition: $transition-speed $transition-easing;
|
||||
transition: transition-speed transition-easing;
|
||||
transition-property: left;
|
||||
will-change: left;
|
||||
width: 15px;
|
||||
position: absolute;
|
||||
top: calc(50% - 8px);
|
||||
left: 100%;
|
||||
fill: $color-black;
|
||||
fill: color-black;
|
||||
}
|
||||
|
@ -1,5 +1,13 @@
|
||||
<div class="content">
|
||||
<span class="label">{{yield}}</span>
|
||||
<button
|
||||
local-class="button {{if @loading "button--loading"}}"
|
||||
class="button"
|
||||
disabled={{this.disabled}}
|
||||
...attributes
|
||||
{{on "click" (fn this.onClick)}}
|
||||
>
|
||||
<div local-class="content">
|
||||
<span local-class="label" class="label">{{yield}}</span>
|
||||
|
||||
{{inline-svg "/assets/loading.svg" class="loading"}}
|
||||
</div>
|
||||
{{inline-svg "/assets/loading.svg" local-class="loading"}}
|
||||
</div>
|
||||
</button>
|
||||
|
@ -1,267 +0,0 @@
|
||||
import {computed} from '@ember/object';
|
||||
import {inject as service} from '@ember/service';
|
||||
import {equal} from '@ember/object/computed';
|
||||
import Component from '@ember/component';
|
||||
|
||||
const DEFAULT_PROPERTIES = {
|
||||
isFileReading: false,
|
||||
isFileRead: false,
|
||||
isPeeking: false,
|
||||
isPeekingDone: false,
|
||||
isPeekingError: false,
|
||||
isCommiting: false,
|
||||
isCommitingDone: false,
|
||||
isCommitingError: false,
|
||||
|
||||
file: null,
|
||||
fileSource: null,
|
||||
documentPath: null,
|
||||
documentFormat: 'json'
|
||||
};
|
||||
|
||||
// Attributes
|
||||
// permissions: Ember Object containing <permission>
|
||||
// revisions: Array of <revision>
|
||||
// documents: Array of <document>
|
||||
// commitButtonText: String
|
||||
// onFileCancel: Function
|
||||
// onPeek: Function
|
||||
// onCommit: Function
|
||||
export default Component.extend({
|
||||
intl: service('intl'),
|
||||
globalState: service('global-state'),
|
||||
|
||||
init(...args) {
|
||||
this._super(...args);
|
||||
|
||||
this._initProperties();
|
||||
},
|
||||
|
||||
mergeTypes: ['smart', 'passive', 'force'],
|
||||
syncTypes: ['smart', 'passive'],
|
||||
|
||||
syncType: computed('mappedSyncTypes.[]', function() {
|
||||
return this.mappedSyncTypes[0];
|
||||
}),
|
||||
mergeType: computed('mappedMergeTypes.[]', function() {
|
||||
return this.mappedMergeTypes[0];
|
||||
}),
|
||||
|
||||
revisionValue: computed('revision', 'mappedRevisions.[]', function() {
|
||||
return (
|
||||
this.mappedRevisions.find(({value}) => value === this.revision) ||
|
||||
this.mappedRevisions[0]
|
||||
);
|
||||
}),
|
||||
|
||||
mappedMergeTypes: computed('mergeTypes.[]', function() {
|
||||
return this.mergeTypes.map(name => ({
|
||||
label: name,
|
||||
value: name
|
||||
}));
|
||||
}),
|
||||
|
||||
mappedSyncTypes: computed('syncTypes.[]', function() {
|
||||
return this.syncTypes.map(name => ({
|
||||
label: name,
|
||||
value: name
|
||||
}));
|
||||
}),
|
||||
|
||||
mappedRevisions: computed('revisions.[]', function() {
|
||||
return this.revisions.map(({id, language}) => ({
|
||||
label: language.name,
|
||||
value: id
|
||||
}));
|
||||
}),
|
||||
|
||||
revision: computed('revisions.[]', function() {
|
||||
return this.revisions.find(revision => revision.isMaster);
|
||||
}),
|
||||
|
||||
isMerge: equal('commitAction', 'merge'),
|
||||
isSync: equal('commitAction', 'sync'),
|
||||
|
||||
documentFormatValue: computed(
|
||||
'documentFormat',
|
||||
'documentFormatOptions',
|
||||
function() {
|
||||
return this.documentFormatOptions.find(
|
||||
({value}) => value === this.documentFormat
|
||||
);
|
||||
}
|
||||
),
|
||||
|
||||
documentFormatOptions: computed('globalState.documentFormats', function() {
|
||||
if (!this.globalState.documentFormats) return [];
|
||||
|
||||
return this.globalState.documentFormats.map(({slug, name}) => ({
|
||||
value: slug,
|
||||
label: name
|
||||
}));
|
||||
}),
|
||||
|
||||
existingDocumentPath: computed(
|
||||
'documentPath',
|
||||
'documents.[].path',
|
||||
function() {
|
||||
if (!this.documentPath) return false;
|
||||
if (!this.documents) return false;
|
||||
|
||||
const path = this.documentPath.replace(/\..+/, '');
|
||||
|
||||
return this.documents.find(document => document.path === path);
|
||||
}
|
||||
),
|
||||
|
||||
actions: {
|
||||
onSelectMergeType(mergeType) {
|
||||
this.set('mergeType', mergeType);
|
||||
},
|
||||
|
||||
onSelectSyncType(syncType) {
|
||||
this.set('syncType', syncType);
|
||||
},
|
||||
|
||||
onSelectRevision(revision) {
|
||||
this.set(
|
||||
'revision',
|
||||
this.revisions.find(({id}) => id === revision.value)
|
||||
);
|
||||
this.set('revisionValue', revision);
|
||||
},
|
||||
|
||||
commit() {
|
||||
this._onCommiting();
|
||||
|
||||
this.onCommit(
|
||||
this.getProperties(
|
||||
'fileSource',
|
||||
'documentPath',
|
||||
'documentFormat',
|
||||
'revision',
|
||||
'mergeType',
|
||||
'syncType'
|
||||
)
|
||||
)
|
||||
.then(this._onCommitingDone.bind(this))
|
||||
.catch(this._onCommitingError.bind(this));
|
||||
},
|
||||
|
||||
peek() {
|
||||
this._onPeeking();
|
||||
|
||||
this.onPeek(
|
||||
this.getProperties(
|
||||
'fileSource',
|
||||
'documentPath',
|
||||
'documentFormat',
|
||||
'revision',
|
||||
'mergeType',
|
||||
'syncType'
|
||||
)
|
||||
)
|
||||
.then(this._onPeekingDone.bind(this))
|
||||
.catch(this._onPeekingError.bind(this));
|
||||
},
|
||||
|
||||
fileChange(files) {
|
||||
const fileSource = files[0];
|
||||
const filename = fileSource.name.split('.');
|
||||
const fileExtension = filename.pop();
|
||||
|
||||
const documentPath = filename.join('.');
|
||||
const documentFormat = this._formatFromExtension(fileExtension);
|
||||
const isFileReading = true;
|
||||
const isFileRead = false;
|
||||
const reader = new FileReader();
|
||||
|
||||
this.setProperties({
|
||||
fileSource,
|
||||
documentPath,
|
||||
isFileReading,
|
||||
isFileRead,
|
||||
documentFormat
|
||||
});
|
||||
|
||||
reader.onload = this._fileRead.bind(this);
|
||||
reader.readAsText(files[0]);
|
||||
},
|
||||
|
||||
fileCancel() {
|
||||
this.onFileCancel();
|
||||
|
||||
this._initProperties();
|
||||
}
|
||||
},
|
||||
|
||||
_formatFromExtension(fileExtension) {
|
||||
if (!this.globalState.documentFormats) return null;
|
||||
|
||||
const documentFormatItem = this.globalState.documentFormats.find(
|
||||
({extension}) => extension === fileExtension
|
||||
);
|
||||
|
||||
return documentFormatItem
|
||||
? documentFormatItem.slug
|
||||
: this.globalState.documentFormats[0].slug;
|
||||
},
|
||||
|
||||
/**
|
||||
* Called after a file is read.
|
||||
*
|
||||
* @private
|
||||
* @method
|
||||
* @param {ProgressEvent} result Native progress event containing the file raw content and infos
|
||||
*/
|
||||
_fileRead(file) {
|
||||
const isFileReading = false;
|
||||
const isFileRead = true;
|
||||
|
||||
this.setProperties({
|
||||
isFileReading,
|
||||
isFileRead,
|
||||
file
|
||||
});
|
||||
|
||||
this.send('peek');
|
||||
},
|
||||
|
||||
_onCommiting() {
|
||||
this.setProperties({
|
||||
isCommiting: true,
|
||||
isCommitingDone: false,
|
||||
isCommitingError: false,
|
||||
isPeekingError: false
|
||||
});
|
||||
},
|
||||
|
||||
_onCommitingDone() {
|
||||
this.setProperties({isCommiting: false, isCommitingDone: true});
|
||||
},
|
||||
|
||||
_onCommitingError() {
|
||||
this.setProperties({isCommiting: false, isCommitingError: true});
|
||||
},
|
||||
|
||||
_onPeeking() {
|
||||
this.setProperties({
|
||||
isPeeking: true,
|
||||
isPeekingDone: false,
|
||||
isPeekingError: false,
|
||||
isCommitingError: false
|
||||
});
|
||||
},
|
||||
|
||||
_onPeekingDone() {
|
||||
this.setProperties({isPeeking: false, isPeekingDone: true});
|
||||
},
|
||||
|
||||
_onPeekingError() {
|
||||
this.setProperties({isPeeking: false, isPeekingError: true});
|
||||
},
|
||||
|
||||
_initProperties() {
|
||||
this.setProperties(DEFAULT_PROPERTIES);
|
||||
}
|
||||
});
|
328
webapp/app/pods/components/commit-file/component.ts
Normal file
328
webapp/app/pods/components/commit-file/component.ts
Normal file
@ -0,0 +1,328 @@
|
||||
import {action} from '@ember/object';
|
||||
import {inject as service} from '@ember/service';
|
||||
import {equal} from '@ember/object/computed';
|
||||
import Component from '@glimmer/component';
|
||||
import IntlService from 'ember-intl/services/intl';
|
||||
import GlobalState from 'accent-webapp/services/global-state';
|
||||
import {tracked} from '@glimmer/tracking';
|
||||
|
||||
const DEFAULT_PROPERTIES = {
|
||||
isFileReading: false,
|
||||
isFileRead: false,
|
||||
isPeeking: false,
|
||||
isPeekingDone: false,
|
||||
isPeekingError: false,
|
||||
isCommiting: false,
|
||||
isCommitingDone: false,
|
||||
isCommitingError: false,
|
||||
|
||||
file: null,
|
||||
fileSource: null,
|
||||
documentPath: null,
|
||||
documentFormat: 'json',
|
||||
};
|
||||
|
||||
interface Args {
|
||||
permissions: Record<string, true>;
|
||||
revisions: any;
|
||||
documents: any;
|
||||
canCommit: boolean;
|
||||
commitAction: 'merge' | 'sync';
|
||||
peekAction: 'peek_merge' | 'peek_sync';
|
||||
commitButtonText: string;
|
||||
onFileCancel: () => void;
|
||||
onPeek: (options: {
|
||||
fileSource: any;
|
||||
documentPath: string | null;
|
||||
documentFormat: any;
|
||||
revision: any;
|
||||
mergeType: string;
|
||||
syncType: string;
|
||||
}) => Promise<void>;
|
||||
onCommit: (options: {
|
||||
fileSource: any;
|
||||
documentPath: string | null;
|
||||
documentFormat: any;
|
||||
revision: any;
|
||||
mergeType: string;
|
||||
syncType: string;
|
||||
}) => Promise<void>;
|
||||
}
|
||||
|
||||
export default class CommitFile extends Component<Args> {
|
||||
@service('intl')
|
||||
intl: IntlService;
|
||||
|
||||
@service('global-state')
|
||||
globalState: GlobalState;
|
||||
|
||||
@equal('args.commitAction', 'merge')
|
||||
isMerge: boolean;
|
||||
|
||||
@equal('args.commitAction', 'sync')
|
||||
isSync: boolean;
|
||||
|
||||
mergeTypes = ['smart', 'passive', 'force'];
|
||||
syncTypes = ['smart', 'passive'];
|
||||
|
||||
@tracked
|
||||
isFileReading = DEFAULT_PROPERTIES.isFileReading;
|
||||
|
||||
@tracked
|
||||
isFileRead = DEFAULT_PROPERTIES.isFileRead;
|
||||
|
||||
@tracked
|
||||
isPeeking = DEFAULT_PROPERTIES.isPeeking;
|
||||
|
||||
@tracked
|
||||
isPeekingDone = DEFAULT_PROPERTIES.isPeekingDone;
|
||||
|
||||
@tracked
|
||||
isPeekingError = DEFAULT_PROPERTIES.isPeekingError;
|
||||
|
||||
@tracked
|
||||
isCommiting = DEFAULT_PROPERTIES.isCommiting;
|
||||
|
||||
@tracked
|
||||
isCommitingDone = DEFAULT_PROPERTIES.isCommitingDone;
|
||||
|
||||
@tracked
|
||||
isCommitingError = DEFAULT_PROPERTIES.isCommitingError;
|
||||
|
||||
@tracked
|
||||
file: ProgressEvent<FileReader> | null = DEFAULT_PROPERTIES.file;
|
||||
|
||||
@tracked
|
||||
fileSource: File | null = DEFAULT_PROPERTIES.fileSource;
|
||||
|
||||
@tracked
|
||||
documentPath: string | null = DEFAULT_PROPERTIES.documentPath;
|
||||
|
||||
@tracked
|
||||
documentFormat: string | null = DEFAULT_PROPERTIES.documentFormat;
|
||||
|
||||
@tracked
|
||||
mergeType = this.mappedMergeTypes[0];
|
||||
|
||||
@tracked
|
||||
syncType = this.mappedSyncTypes[0];
|
||||
|
||||
@tracked
|
||||
revisionValue =
|
||||
this.mappedRevisions.find(({value}) => value === this.revision) ||
|
||||
this.mappedRevisions[0];
|
||||
|
||||
@tracked
|
||||
revision = this.args.revisions.find((revision: any) => revision.isMaster);
|
||||
|
||||
get mappedMergeTypes() {
|
||||
return this.mergeTypes.map((name) => ({
|
||||
label: name,
|
||||
value: name,
|
||||
}));
|
||||
}
|
||||
|
||||
get mappedSyncTypes() {
|
||||
return this.syncTypes.map((name) => ({
|
||||
label: name,
|
||||
value: name,
|
||||
}));
|
||||
}
|
||||
|
||||
get mappedRevisions(): Array<{label: string; value: string}> {
|
||||
return this.args.revisions.map(
|
||||
({id, language}: {id: string; language: {name: string}}) => ({
|
||||
label: language.name,
|
||||
value: id,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
get documentFormatValue() {
|
||||
return this.documentFormatOptions.find(({value}) => {
|
||||
return value === this.documentFormat;
|
||||
});
|
||||
}
|
||||
|
||||
get documentFormatOptions(): Array<{value: string; label: string}> {
|
||||
if (!this.globalState.documentFormats) return [];
|
||||
|
||||
return this.globalState.documentFormats.map(({slug, name}) => ({
|
||||
value: slug,
|
||||
label: name,
|
||||
}));
|
||||
}
|
||||
|
||||
get existingDocumentPath() {
|
||||
if (!this.documentPath) return false;
|
||||
if (!this.args.documents) return false;
|
||||
|
||||
const path = this.documentPath.replace(/\..+/, '');
|
||||
|
||||
return this.args.documents.find((document: any) => document.path === path);
|
||||
}
|
||||
|
||||
@action
|
||||
onSelectMergeType(mergeType: {label: string; value: string}) {
|
||||
this.mergeType = mergeType;
|
||||
}
|
||||
|
||||
@action
|
||||
onSelectSyncType(syncType: {label: string; value: string}) {
|
||||
this.syncType = syncType;
|
||||
}
|
||||
|
||||
@action
|
||||
onSelectRevision(revision: {label: string; value: string}) {
|
||||
this.revision = this.args.revisions.find(
|
||||
({id}: {id: string}) => id === revision.value
|
||||
);
|
||||
|
||||
this.revisionValue = revision;
|
||||
}
|
||||
|
||||
@action
|
||||
onSelectDocumentFormat(documentFormat: {label: string; value: string}) {
|
||||
this.documentFormat = documentFormat.value;
|
||||
}
|
||||
|
||||
@action
|
||||
async commit() {
|
||||
this.onCommiting();
|
||||
|
||||
try {
|
||||
await this.args.onCommit({
|
||||
fileSource: this.fileSource,
|
||||
documentPath: this.documentPath,
|
||||
documentFormat: this.documentFormat,
|
||||
revision: this.revision,
|
||||
mergeType: this.mergeType.value,
|
||||
syncType: this.syncType.value,
|
||||
});
|
||||
|
||||
this.onCommitingDone();
|
||||
} catch (error) {
|
||||
this.onCommitingError();
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
async peek() {
|
||||
this.onPeeking();
|
||||
|
||||
try {
|
||||
await this.args.onPeek({
|
||||
fileSource: this.fileSource,
|
||||
documentPath: this.documentPath,
|
||||
documentFormat: this.documentFormat,
|
||||
revision: this.revision,
|
||||
mergeType: this.mergeType.value,
|
||||
syncType: this.syncType.value,
|
||||
});
|
||||
|
||||
this.onPeekingDone();
|
||||
} catch (error) {
|
||||
this.onPeekingError();
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
fileChange(files: File[]) {
|
||||
const fileSource = files[0];
|
||||
const filename = fileSource.name.split('.');
|
||||
const fileExtension = filename.pop();
|
||||
|
||||
const documentPath = filename.join('.');
|
||||
const documentFormat = this.formatFromExtension(fileExtension);
|
||||
const isFileReading = true;
|
||||
const isFileRead = false;
|
||||
const reader = new FileReader();
|
||||
|
||||
this.fileSource = fileSource;
|
||||
this.documentPath = documentPath;
|
||||
this.isFileReading = isFileReading;
|
||||
this.isFileRead = isFileRead;
|
||||
this.documentFormat = documentFormat;
|
||||
|
||||
reader.onload = this.fileRead.bind(this);
|
||||
reader.readAsText(files[0]);
|
||||
}
|
||||
|
||||
@action
|
||||
fileCancel() {
|
||||
this.args.onFileCancel();
|
||||
|
||||
this.initProperties();
|
||||
}
|
||||
|
||||
private formatFromExtension(fileExtension?: string) {
|
||||
if (!this.globalState.documentFormats) return null;
|
||||
|
||||
const documentFormatItem = this.globalState.documentFormats.find(
|
||||
({extension}) => {
|
||||
return extension === fileExtension;
|
||||
}
|
||||
);
|
||||
|
||||
return documentFormatItem
|
||||
? documentFormatItem.slug
|
||||
: this.globalState.documentFormats[0].slug;
|
||||
}
|
||||
|
||||
private async fileRead(event: ProgressEvent<FileReader>) {
|
||||
this.isFileReading = false;
|
||||
this.isFileRead = true;
|
||||
this.file = event;
|
||||
|
||||
await this.peek();
|
||||
}
|
||||
|
||||
private onCommiting() {
|
||||
this.isCommiting = true;
|
||||
this.isCommitingDone = false;
|
||||
this.isCommitingError = false;
|
||||
this.isPeekingError = false;
|
||||
}
|
||||
|
||||
private onCommitingDone() {
|
||||
this.isCommiting = false;
|
||||
this.isCommitingDone = true;
|
||||
}
|
||||
|
||||
private onCommitingError() {
|
||||
this.isCommiting = false;
|
||||
this.isCommitingError = true;
|
||||
}
|
||||
|
||||
private onPeeking() {
|
||||
this.isPeeking = true;
|
||||
this.isPeekingDone = false;
|
||||
this.isPeekingError = false;
|
||||
this.isCommitingError = false;
|
||||
}
|
||||
|
||||
private onPeekingDone() {
|
||||
this.isPeeking = false;
|
||||
this.isPeekingDone = true;
|
||||
}
|
||||
|
||||
private onPeekingError() {
|
||||
this.isPeeking = false;
|
||||
this.isPeekingError = true;
|
||||
}
|
||||
|
||||
private initProperties() {
|
||||
this.isFileReading = DEFAULT_PROPERTIES.isFileReading;
|
||||
this.isFileRead = DEFAULT_PROPERTIES.isFileRead;
|
||||
this.isPeeking = DEFAULT_PROPERTIES.isPeeking;
|
||||
this.isPeekingDone = DEFAULT_PROPERTIES.isPeekingDone;
|
||||
this.isPeekingError = DEFAULT_PROPERTIES.isPeekingError;
|
||||
this.isCommiting = DEFAULT_PROPERTIES.isCommiting;
|
||||
this.isCommitingDone = DEFAULT_PROPERTIES.isCommitingDone;
|
||||
this.isCommitingError = DEFAULT_PROPERTIES.isCommitingError;
|
||||
this.file = DEFAULT_PROPERTIES.file;
|
||||
this.fileSource = DEFAULT_PROPERTIES.fileSource;
|
||||
this.documentPath = DEFAULT_PROPERTIES.documentPath;
|
||||
this.documentFormat = DEFAULT_PROPERTIES.documentFormat;
|
||||
}
|
||||
}
|
@ -1,15 +1,32 @@
|
||||
.separator {
|
||||
display: block;
|
||||
margin: 10px 0;
|
||||
background: #eee;
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
@value color-grey, color-green, color-black, color-error from 'accent-webapp/styles/variables/colors';
|
||||
@value font-monospace from 'accent-webapp/styles/variables/fonts';
|
||||
|
||||
.commit-file {
|
||||
:global(.textInput) {
|
||||
@extend %textInput;
|
||||
margin-bottom: 8px;
|
||||
padding: 5px;
|
||||
outline: 0;
|
||||
border: 1px solid lighten(color-grey, 20%);
|
||||
box-shadow: inset 0 2px 6px rgba(color-black, 0.05);
|
||||
background: #fff;
|
||||
font-family: font-monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
:global(.ember-power-select-trigger) {
|
||||
padding: 6px 10px;
|
||||
border: 1px solid rgba(color-black, 0.1);
|
||||
color: rgba(color-black, 0.8);
|
||||
color: var(--color-black-opacity-70);
|
||||
background: #fafafa;
|
||||
}
|
||||
}
|
||||
|
||||
.textHelper {
|
||||
margin-bottom: 3px;
|
||||
width: 80%;
|
||||
color: $color-grey;
|
||||
color: color-grey;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
@ -18,30 +35,18 @@
|
||||
margin-bottom: 7px;
|
||||
padding: 2px 4px 3px;
|
||||
border-radius: 4px;
|
||||
background: lighten($color-green, 48%);
|
||||
background: lighten(color-green, 48%);
|
||||
background: var(--color-primary-opacity-10);
|
||||
color: $color-green;
|
||||
color: color-green;
|
||||
color: var(--color-primary);
|
||||
font-size: 11px;
|
||||
|
||||
&.documentHelper--new {
|
||||
background: lighten($color-green, 48%);
|
||||
color: $color-green;
|
||||
background: lighten(color-green, 48%);
|
||||
color: color-green;
|
||||
}
|
||||
}
|
||||
|
||||
.textInput {
|
||||
@extend %textInput;
|
||||
margin-bottom: 8px;
|
||||
padding: 5px;
|
||||
outline: 0;
|
||||
border: 1px solid lighten($color-grey, 20%);
|
||||
box-shadow: inset 0 2px 6px rgba($color-black, 0.05);
|
||||
background: #fff;
|
||||
font-family: $font-monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.options {
|
||||
display: flex;
|
||||
padding: 0;
|
||||
@ -84,7 +89,7 @@
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
margin-right: 10px;
|
||||
stroke: $color-black;
|
||||
stroke: color-black;
|
||||
stroke: var(--color-black);
|
||||
}
|
||||
|
||||
@ -92,7 +97,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0 0 6px;
|
||||
color: $color-black;
|
||||
color: color-black;
|
||||
color: var(--color-black);
|
||||
}
|
||||
|
||||
@ -101,16 +106,16 @@
|
||||
margin: 0 0 10px;
|
||||
font-size: 14px;
|
||||
font-weight: 300;
|
||||
color: rgba($color-black, 0.7);
|
||||
color: rgba(color-black, 0.7);
|
||||
color: var(--color-black-opacity-70);
|
||||
}
|
||||
|
||||
.fileButton {
|
||||
margin-top: 7px;
|
||||
padding: 9px 30px;
|
||||
margin-top: 7px !important;
|
||||
padding: 9px 30px !important;
|
||||
|
||||
.button-icon {
|
||||
margin-right: 10px;
|
||||
margin-right: 10px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@ -134,7 +139,7 @@
|
||||
margin-bottom: 5px;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
color: $color-black;
|
||||
color: color-black;
|
||||
color: var(--color-black);
|
||||
}
|
||||
|
||||
@ -142,13 +147,13 @@
|
||||
.instructions-text {
|
||||
margin-bottom: 18px;
|
||||
font-size: 13px;
|
||||
color: rgba($color-black, 0.5);
|
||||
color: rgba(color-black, 0.5);
|
||||
color: var(--color-black);
|
||||
|
||||
em {
|
||||
font-style: normal;
|
||||
font-weight: bold;
|
||||
color: rgba($color-green, 0.8);
|
||||
color: rgba(color-green, 0.8);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
@ -164,12 +169,12 @@
|
||||
}
|
||||
|
||||
.peekButton {
|
||||
margin-top: 7px;
|
||||
margin-top: 7px !important;
|
||||
}
|
||||
|
||||
.errorMessage {
|
||||
margin: 10px 0;
|
||||
color: $color-error;
|
||||
color: color-error;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
}
|
||||
@ -178,14 +183,6 @@
|
||||
@extend %textInput;
|
||||
padding: 6px 10px;
|
||||
font-size: 12px;
|
||||
font-family: $font-monospace;
|
||||
font-family: font-monospace;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.ember-power-select-trigger {
|
||||
padding: 6px 10px;
|
||||
border: 1px solid rgba($color-black, 0.1);
|
||||
color: rgba($color-black, 0.8);
|
||||
color: var(--color-black-opacity-70);
|
||||
background: #fafafa;
|
||||
}
|
||||
|
@ -1,185 +1,185 @@
|
||||
<div>
|
||||
{{#if isPeekingError}}
|
||||
<div class="errorMessage">
|
||||
<div local-class="commit-file">
|
||||
{{#if this.isPeekingError}}
|
||||
<div local-class="errorMessage">
|
||||
{{t "components.commit_file.peek_error"}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if isCommitingError}}
|
||||
<div class="errorMessage">
|
||||
{{#if this.isCommitingError}}
|
||||
<div local-class="errorMessage">
|
||||
{{t "components.commit_file.commit_error"}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if isMerge}}
|
||||
{{#if file}}
|
||||
<div class="options">
|
||||
<div class="option option--borderless">
|
||||
<p class="textHelper">
|
||||
{{#if this.isMerge}}
|
||||
{{#if this.file}}
|
||||
<div local-class="options">
|
||||
<div local-class="option option--borderless">
|
||||
<p local-class="textHelper">
|
||||
{{t "components.commit_file.language"}}
|
||||
:
|
||||
</p>
|
||||
<AccSelect
|
||||
@searchEnabled={{false}}
|
||||
@selected={{revisionValue}}
|
||||
@options={{mappedRevisions}}
|
||||
@onchange={{action "onSelectRevision"}}
|
||||
@selected={{this.revisionValue}}
|
||||
@options={{this.mappedRevisions}}
|
||||
@onchange={{fn this.onSelectRevision}}
|
||||
/>
|
||||
</div>
|
||||
<div class="option option--borderless">
|
||||
<p class="textHelper">
|
||||
<div local-class="option option--borderless">
|
||||
<p local-class="textHelper">
|
||||
{{t "components.commit_file.commit_type"}}
|
||||
:
|
||||
</p>
|
||||
<AccSelect
|
||||
@searchEnabled={{false}}
|
||||
@selected={{mergeType}}
|
||||
@options={{mappedMergeTypes}}
|
||||
@onchange={{action "onSelectMergeType"}}
|
||||
@selected={{this.mergeType}}
|
||||
@options={{this.mappedMergeTypes}}
|
||||
@onchange={{fn this.onSelectMergeType}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{#if isSync}}
|
||||
{{#if file}}
|
||||
<div class="options">
|
||||
<div class="option option--borderless">
|
||||
<p class="textHelper">
|
||||
{{#if this.isSync}}
|
||||
{{#if this.file}}
|
||||
<div local-class="options">
|
||||
<div local-class="option option--borderless">
|
||||
<p local-class="textHelper">
|
||||
{{t "components.commit_file.commit_type"}}
|
||||
:
|
||||
</p>
|
||||
<AccSelect
|
||||
@searchEnabled={{false}}
|
||||
@selected={{syncType}}
|
||||
@options={{mappedSyncTypes}}
|
||||
@onchange={{action "onSelectSyncType"}}
|
||||
@selected={{this.syncType}}
|
||||
@options={{this.mappedSyncTypes}}
|
||||
@onchange={{fn this.onSelectSyncType}}
|
||||
/>
|
||||
</div>
|
||||
<div class="option option--borderless"></div>
|
||||
<div local-class="option option--borderless"></div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{#if file}}
|
||||
<div>
|
||||
{{#unless document}}
|
||||
<div class="option">
|
||||
<p class="textHelper">
|
||||
{{#unless this.documents}}
|
||||
<div local-class="option">
|
||||
<p local-class="textHelper">
|
||||
{{t "components.commit_file.file_source"}}
|
||||
</p>
|
||||
<p>
|
||||
{{#if existingDocumentPath}}
|
||||
<span class="documentHelper">
|
||||
{{#if this.existingDocumentPath}}
|
||||
<span local-class="documentHelper">
|
||||
{{t "components.commit_file.existing_document_warning"}}
|
||||
</span>
|
||||
{{else}}
|
||||
<span class="documentHelper documentHelper--new">
|
||||
<span local-class="documentHelper documentHelper--new">
|
||||
{{t "components.commit_file.new_document_warning"}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</p>
|
||||
<Input @value={{documentPath}} class="fileSourceName" />
|
||||
<Input @value={{this.documentPath}} local-class="fileSourceName" />
|
||||
</div>
|
||||
{{/unless}}
|
||||
|
||||
<div class="option">
|
||||
<p class="textHelper">
|
||||
<div local-class="option">
|
||||
<p local-class="textHelper">
|
||||
{{t "components.commit_file.document_format"}}
|
||||
</p>
|
||||
<AccSelect
|
||||
@searchEnabled={{false}}
|
||||
@selected={{documentFormatValue}}
|
||||
@options={{documentFormatOptions}}
|
||||
@onchange={{action (mut documentFormat) value="value"}}
|
||||
@selected={{this.documentFormatValue}}
|
||||
@options={{this.documentFormatOptions}}
|
||||
@onchange={{fn this.onSelectDocumentFormat}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{{#if (get permissions peekAction)}}
|
||||
<div class="option">
|
||||
<p class="textHelper">
|
||||
{{#if (get @permissions @peekAction)}}
|
||||
<div local-class="option">
|
||||
<p local-class="textHelper">
|
||||
{{t "components.commit_file.peek_help"}}
|
||||
</p>
|
||||
<AsyncButton
|
||||
@onClick={{action "peek"}}
|
||||
@loading={{isPeeking}}
|
||||
class="button
|
||||
button--filled button--blue peekButton"
|
||||
@onClick={{fn this.peek}}
|
||||
@loading={{this.isPeeking}}
|
||||
class="button button--filled button--blue"
|
||||
local-class="peekButton"
|
||||
>
|
||||
{{t "components.commit_file.peek_button"}}
|
||||
</AsyncButton>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if (get permissions commitAction)}}
|
||||
<div class="actions">
|
||||
{{#if (get @permissions @commitAction)}}
|
||||
<div local-class="actions">
|
||||
<AsyncButton
|
||||
@onClick={{action "fileCancel"}}
|
||||
@onClick={{fn this.fileCancel}}
|
||||
class="button button--filled button--white"
|
||||
>
|
||||
{{t "components.commit_file.cancel_button"}}
|
||||
</AsyncButton>
|
||||
|
||||
{{#if canCommit}}
|
||||
{{#if @canCommit}}
|
||||
<AsyncButton
|
||||
@onClick={{action "commit"}}
|
||||
@loading={{isCommiting}}
|
||||
@onClick={{fn this.commit}}
|
||||
@loading={{this.isCommiting}}
|
||||
class="button button--filled"
|
||||
>
|
||||
{{commitButtonText}}
|
||||
{{@commitButtonText}}
|
||||
</AsyncButton>
|
||||
{{else}}
|
||||
<AsyncButton class="button button--filled button--disabled">
|
||||
{{commitButtonText}}
|
||||
{{@commitButtonText}}
|
||||
</AsyncButton>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="emptyFile">
|
||||
<div class="emptyFile-upload">
|
||||
<div local-class="emptyFile">
|
||||
<div local-class="emptyFile-upload">
|
||||
<FileInput
|
||||
@name="file-input"
|
||||
@id="file-input"
|
||||
@onChange={{action "fileChange"}}
|
||||
class="fileInput"
|
||||
name="file-input"
|
||||
id="file-input"
|
||||
@onChange={{this.fileChange}}
|
||||
local-class="fileInput"
|
||||
/>
|
||||
|
||||
<strong class="fileInputTitle">
|
||||
{{inline-svg "/assets/folder.svg" class="fileInputIcon"}}
|
||||
<strong local-class="fileInputTitle">
|
||||
{{inline-svg "/assets/folder.svg" local-class="fileInputIcon"}}
|
||||
{{t "components.commit_file.upload_title"}}
|
||||
</strong>
|
||||
|
||||
<p class="fileInputHelper">
|
||||
<p local-class="fileInputHelper">
|
||||
{{t "components.commit_file.upload_help"}}
|
||||
</p>
|
||||
|
||||
<label for="file-input" class="button button--filled fileButton">
|
||||
{{inline-svg "/assets/import.svg" class="button-icon"}}
|
||||
<label for="file-input" class="button button--filled" local-class="fileButton">
|
||||
{{inline-svg "/assets/import.svg" class="button-icon" local-class="button-icon"}}
|
||||
{{t "components.commit_file.file_input_button"}}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<a class="instructions-title" rel="noopener noreferrer" href="https://www.accent.reviews/guides/glossary.html#sync" target="_blank">
|
||||
<a local-class="instructions-title" rel="noopener noreferrer" href="https://www.accent.reviews/guides/glossary.html#sync" target="_blank">
|
||||
{{t "components.commit_file.instructions.sync.title"}}
|
||||
</a>
|
||||
<p class="instructions-text">
|
||||
<p local-class="instructions-text">
|
||||
{{{t "components.commit_file.instructions.sync.text"}}}
|
||||
</p>
|
||||
|
||||
<a class="instructions-title" rel="noopener noreferrer" href="https://www.accent.reviews/guides/glossary.html#add-translations" target="_blank">
|
||||
<a local-class="instructions-title" rel="noopener noreferrer" href="https://www.accent.reviews/guides/glossary.html#add-translations" target="_blank">
|
||||
{{t "components.commit_file.instructions.merge.title"}}
|
||||
</a>
|
||||
<p class="instructions-text">
|
||||
<p local-class="instructions-text">
|
||||
{{{t "components.commit_file.instructions.merge.text"}}}
|
||||
</p>
|
||||
|
||||
<h3 class="instructions-title">
|
||||
<h3 local-class="instructions-title">
|
||||
{{t "components.commit_file.instructions.mistakes.title"}}
|
||||
</h3>
|
||||
<ul class="instructions-list">
|
||||
<li class="instructions-list-item">{{t "components.commit_file.instructions.mistakes.item_1"}}</li>
|
||||
<li class="instructions-list-item">{{t "components.commit_file.instructions.mistakes.item_2"}}</li>
|
||||
<li class="instructions-list-item">{{t "components.commit_file.instructions.mistakes.item_3"}}</li>
|
||||
<ul local-class="instructions-list">
|
||||
<li local-class="instructions-list-item">{{t "components.commit_file.instructions.mistakes.item_1"}}</li>
|
||||
<li local-class="instructions-list-item">{{t "components.commit_file.instructions.mistakes.item_2"}}</li>
|
||||
<li local-class="instructions-list-item">{{t "components.commit_file.instructions.mistakes.item_3"}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,65 +0,0 @@
|
||||
import {computed} from '@ember/object';
|
||||
import {empty, reads} from '@ember/object/computed';
|
||||
import Component from '@ember/component';
|
||||
|
||||
import parsedKeyProperty from 'accent-webapp/computed-macros/parsed-key';
|
||||
|
||||
// Attributes:
|
||||
// project: Object <project>
|
||||
// permissions: Ember Object containing <permission>
|
||||
// conflict: Object <conflict>
|
||||
// revision: Object <revision> (optional)
|
||||
// onCorrect: Function
|
||||
export default Component.extend({
|
||||
classNameBindings: ['active', 'resolved', 'error:errored', 'fullscreen'],
|
||||
|
||||
emptyPreviousText: empty('conflict.conflictedText'),
|
||||
textInput: reads('conflict.correctedText'),
|
||||
samePreviousText: computed(
|
||||
'conflict.{conflictedText,correctedText}',
|
||||
function() {
|
||||
return this.conflict.conflictedText === this.conflict.correctedText;
|
||||
}
|
||||
),
|
||||
|
||||
loading: false,
|
||||
error: false,
|
||||
resolved: false,
|
||||
active: false,
|
||||
|
||||
conflictKey: parsedKeyProperty('conflict.key'),
|
||||
|
||||
actions: {
|
||||
correct() {
|
||||
this._onLoading();
|
||||
|
||||
this.onCorrect(this.conflict, this.textInput)
|
||||
.then(this._onCorrectSuccess.bind(this))
|
||||
.catch(this._onError.bind(this));
|
||||
},
|
||||
|
||||
inputBlur() {
|
||||
this.set('active', false);
|
||||
},
|
||||
|
||||
inputFocus() {
|
||||
this.set('active', true);
|
||||
}
|
||||
},
|
||||
|
||||
_onLoading() {
|
||||
this.setProperties({error: false, loading: true});
|
||||
},
|
||||
|
||||
_onError() {
|
||||
this.setProperties({error: true, loading: false});
|
||||
},
|
||||
|
||||
_onCorrectSuccess() {
|
||||
this.setProperties({resolved: true, loading: false});
|
||||
},
|
||||
|
||||
_onUncorrectSuccess() {
|
||||
this.setProperties({resolved: false, loading: false});
|
||||
}
|
||||
});
|
80
webapp/app/pods/components/conflict-item/component.ts
Normal file
80
webapp/app/pods/components/conflict-item/component.ts
Normal file
@ -0,0 +1,80 @@
|
||||
import {action} from '@ember/object';
|
||||
import {empty} from '@ember/object/computed';
|
||||
import Component from '@glimmer/component';
|
||||
|
||||
import parsedKeyProperty from 'accent-webapp/computed-macros/parsed-key';
|
||||
import {tracked} from '@glimmer/tracking';
|
||||
|
||||
interface Args {
|
||||
fullscreen: boolean;
|
||||
revision: any;
|
||||
permissions: Record<string, true>;
|
||||
project: any;
|
||||
conflict: any;
|
||||
onCorrect: (conflict: any, textInput: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export default class ConflictItem extends Component<Args> {
|
||||
@empty('args.conflict.conflictedText')
|
||||
emptyPreviousText: boolean;
|
||||
|
||||
@tracked
|
||||
textInput = this.args.conflict.correctedText;
|
||||
|
||||
@tracked
|
||||
loading = false;
|
||||
|
||||
@tracked
|
||||
error = false;
|
||||
|
||||
@tracked
|
||||
resolved = false;
|
||||
|
||||
@tracked
|
||||
active = false;
|
||||
|
||||
conflictKey = parsedKeyProperty(this.args.conflict.key);
|
||||
|
||||
get samePreviousText() {
|
||||
return (
|
||||
this.args.conflict.conflictedText === this.args.conflict.correctedText
|
||||
);
|
||||
}
|
||||
|
||||
@action
|
||||
async correct() {
|
||||
this.onLoading();
|
||||
|
||||
try {
|
||||
await this.args.onCorrect(this.args.conflict, this.textInput);
|
||||
this.onCorrectSuccess();
|
||||
} catch (error) {
|
||||
this.onError();
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
inputBlur() {
|
||||
this.active = false;
|
||||
}
|
||||
|
||||
@action
|
||||
inputFocus() {
|
||||
this.active = true;
|
||||
}
|
||||
|
||||
private onLoading() {
|
||||
this.error = false;
|
||||
this.loading = true;
|
||||
}
|
||||
|
||||
private onError() {
|
||||
this.error = true;
|
||||
this.loading = false;
|
||||
}
|
||||
|
||||
private onCorrectSuccess() {
|
||||
this.resolved = true;
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user