Merge branch 'master' into fix-splitfreq-weights
* master: (39 commits) fix: correct import of merkle in test chore: move snapshot to new dir refactor: move crdt files into separate directory revert: revert pruning of svg imports style: nicer re-exporting syntax refactor: remove unused imports chore: update package scripts to take advantage of yarn v3 build: add missing eslint dependency to loot-core build: add cross-env dependency to desktop-client package.json build: remove deprecated nohoist settings and prevent hoisting of mobile dependencies fix: move downshift patch to monorepo root CI: update CI definition to use yarn v3 fix: wrap glob in quotes so that it's properly passed to npm-run-all chore: update root yarn scripts to use `workspace` command instead of cd chore: fix broken builds chore: update to yarnv3 and fix missing packages preventing install Force react-error-overlay to 6.0.9 to fix error Add docs for building for windows (contributed by @ejmurra) add: tsconfig.json build: replace jwl-dev-utils with react-dev-utils ...
This commit is contained in:
commit
57e0d713da
107 changed files with 25496 additions and 17988 deletions
|
@ -47,7 +47,7 @@ jobs:
|
||||||
keys:
|
keys:
|
||||||
- v3-dependencies-{{ checksum "yarn.lock" }}
|
- v3-dependencies-{{ checksum "yarn.lock" }}
|
||||||
|
|
||||||
- run: yarn install --pure-lockfile
|
- run: yarn install --immutable
|
||||||
|
|
||||||
- save_cache:
|
- save_cache:
|
||||||
<<: *cached_files
|
<<: *cached_files
|
||||||
|
@ -74,7 +74,7 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- run:
|
- run:
|
||||||
command: yarn install --pure-lockfile
|
command: yarn install --immutable
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- run:
|
- run:
|
||||||
|
@ -96,7 +96,7 @@ jobs:
|
||||||
keys:
|
keys:
|
||||||
- v3-dependencies-{{ checksum "yarn.lock" }}
|
- v3-dependencies-{{ checksum "yarn.lock" }}
|
||||||
|
|
||||||
- run: yarn install --pure-lockfile
|
- run: yarn install --immutable
|
||||||
|
|
||||||
- run: sudo npm install -g @sentry/cli --unsafe-perm
|
- run: sudo npm install -g @sentry/cli --unsafe-perm
|
||||||
|
|
||||||
|
|
9
.gitignore
vendored
9
.gitignore
vendored
|
@ -20,3 +20,12 @@ bundle.mobile.js.map
|
||||||
export-2020-01-10.csv
|
export-2020-01-10.csv
|
||||||
|
|
||||||
**/*.log
|
**/*.log
|
||||||
|
|
||||||
|
# Yarn
|
||||||
|
.pnp.*
|
||||||
|
.yarn/*
|
||||||
|
!.yarn/patches
|
||||||
|
!.yarn/plugins
|
||||||
|
!.yarn/releases
|
||||||
|
!.yarn/sdks
|
||||||
|
!.yarn/versions
|
||||||
|
|
28
.yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
vendored
Normal file
28
.yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
vendored
Normal file
File diff suppressed because one or more lines are too long
785
.yarn/releases/yarn-3.2.0.cjs
vendored
Executable file
785
.yarn/releases/yarn-3.2.0.cjs
vendored
Executable file
File diff suppressed because one or more lines are too long
7
.yarnrc.yml
Normal file
7
.yarnrc.yml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
nodeLinker: node-modules
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
|
||||||
|
spec: "@yarnpkg/plugin-workspace-tools"
|
||||||
|
|
||||||
|
yarnPath: .yarn/releases/yarn-3.2.0.cjs
|
21
bin/package
21
bin/package
|
@ -80,27 +80,14 @@ if [[ $CI != true && "$OSTYPE" == "darwin"* ]]; then
|
||||||
yarn lint
|
yarn lint
|
||||||
fi
|
fi
|
||||||
|
|
||||||
./node_modules/.bin/patch-package
|
yarn patch-package
|
||||||
|
|
||||||
(
|
yarn workspace mobile patch-package
|
||||||
cd packages/loot-design;
|
|
||||||
../../node_modules/.bin/patch-package
|
|
||||||
)
|
|
||||||
|
|
||||||
(
|
yarn workspace loot-core build:node
|
||||||
cd packages/mobile;
|
|
||||||
../../node_modules/.bin/patch-package
|
|
||||||
)
|
|
||||||
|
|
||||||
(
|
yarn workspace @actual-app/web build
|
||||||
cd packages/loot-core;
|
|
||||||
NODE_ENV=production yarn build:node
|
|
||||||
)
|
|
||||||
|
|
||||||
(
|
|
||||||
cd packages/desktop-client;
|
|
||||||
yarn build
|
|
||||||
)
|
|
||||||
|
|
||||||
rm -fr packages/desktop-electron/client-build
|
rm -fr packages/desktop-electron/client-build
|
||||||
cp -r packages/desktop-client/build packages/desktop-electron/client-build
|
cp -r packages/desktop-client/build packages/desktop-electron/client-build
|
||||||
|
|
|
@ -56,19 +56,8 @@ if [ $CI != true ]; then
|
||||||
yarn lint
|
yarn lint
|
||||||
fi
|
fi
|
||||||
|
|
||||||
(
|
ACTUAL_RELEASE_TYPE=$RELEASE yarn workspace loot-core build:browser
|
||||||
cd packages/loot-design;
|
|
||||||
../../node_modules/.bin/patch-package
|
|
||||||
)
|
|
||||||
|
|
||||||
(
|
REACT_APP_RELEASE_TYPE=$RELEASE yarn workspace @actual-app/web build:browser
|
||||||
cd packages/loot-core;
|
|
||||||
ACTUAL_RELEASE_TYPE=$RELEASE yarn build:browser
|
|
||||||
)
|
|
||||||
|
|
||||||
(
|
|
||||||
cd packages/desktop-client;
|
|
||||||
REACT_APP_RELEASE_TYPE=$RELEASE yarn build:browser
|
|
||||||
)
|
|
||||||
|
|
||||||
echo "packages/desktop-client/build"
|
echo "packages/desktop-client/build"
|
||||||
|
|
18
docs/Building-for-Windows.md
Normal file
18
docs/Building-for-Windows.md
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# How to build browser for Windows
|
||||||
|
Many of the build scripts are bash scripts and not natively invokable in Windows. To solve this, you can build the project using Git Bash.
|
||||||
|
1. Install [Git & Git Bash for Windows](https://git-scm.com/downloads)
|
||||||
|
2. Install Node v16.x (latest version 17.x does not work due to issue with crypto package)
|
||||||
|
3. Clone this repo
|
||||||
|
4. From the root of this repo, run `sh` to launch a bash shell
|
||||||
|
5. From inside the bash shell, run `yarn install`
|
||||||
|
6. From still inside the shell, run `yarn start:browser`
|
||||||
|
7. Open your browser to `localhost:3001`
|
||||||
|
|
||||||
|
# How to build electron for Windows
|
||||||
|
1. Follow steps 1 - 5 above.
|
||||||
|
2. Run `yarn start`
|
||||||
|
3. If you get an error from electron, run `yarn rebuild-electron` and rerun `yarn start`;
|
||||||
|
|
||||||
|
## rsync: command not found
|
||||||
|
If you run into this error, you will need to install the rsync binary to Git Bash. Follow the [directions here](https://prasaz.medium.com/add-rsync-to-windows-git-bash-f42736bae1b3). When you get to the final step - installing the libxxhash dll - rename the dll from `msys-xxhash-0.8.0.dll` to `msys-xxhash-0.dll`
|
||||||
|
|
51
package.json
51
package.json
|
@ -15,53 +15,30 @@
|
||||||
"workspaces": {
|
"workspaces": {
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
],
|
|
||||||
"nohoist": [
|
|
||||||
"**/better-sqlite3",
|
|
||||||
"**/better-sqlite3/**",
|
|
||||||
"mobile/react-native",
|
|
||||||
"mobile/react-native-*",
|
|
||||||
"mobile/rn-fetch-blob",
|
|
||||||
"mobile/**/event-target-shim",
|
|
||||||
"mobile/@sentry/react-native",
|
|
||||||
"mobile/nodejs-mobile-react-native",
|
|
||||||
"**/mobile/nodejs-mobile-react-native/**",
|
|
||||||
"**/@react-native-community/**",
|
|
||||||
"**/@react-navigation/**",
|
|
||||||
"mobile/react-navigation",
|
|
||||||
"mobile/react-navigation-tabs",
|
|
||||||
"mobile/rn-snoopy",
|
|
||||||
"mobile/rn-snoopy/**",
|
|
||||||
"mobile/detox",
|
|
||||||
"mobile/detox/**",
|
|
||||||
"mobile/jsc-android",
|
|
||||||
"mobile/jsc-android/**",
|
|
||||||
"**/react-native-web",
|
|
||||||
"**/react-native-web/**",
|
|
||||||
"**/@sentry/cli"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "npm-run-all --parallel start:desktop-*",
|
"start": "npm-run-all --parallel 'start:desktop-*'",
|
||||||
"start:desktop-node": "cd packages/loot-core && yarn watch:node",
|
"start:desktop-node": "yarn workspace loot-core watch:node",
|
||||||
"start:desktop-client": "cd packages/desktop-client && yarn watch",
|
"start:desktop-client": "yarn workspace @actual-app/web watch",
|
||||||
"start:desktop-electron": "cd packages/desktop-electron && yarn watch",
|
"start:desktop-electron": "yarn workspace Actual watch",
|
||||||
"start:browser": "npm-run-all --parallel start:browser-*",
|
"start:browser": "npm-run-all --parallel 'start:browser-*'",
|
||||||
"start:browser-backend": "cd packages/loot-core && yarn watch:browser",
|
"start:browser-backend": "yarn workspace loot-core watch:browser",
|
||||||
"start:browser-frontend": "cd packages/desktop-client && yarn start:browser",
|
"start:browser-frontend": "yarn workspace @actual-app/web start:browser",
|
||||||
"test": "./node_modules/.bin/jest --maxWorkers=4",
|
"test": "./node_modules/.bin/jest --maxWorkers=4",
|
||||||
"test:debug": "node ./node_modules/.bin/jest --runInBand --useStderr",
|
"test:debug": "node ./node_modules/.bin/jest --runInBand --useStderr",
|
||||||
"test:debug-brk": "node --inspect-brk ./node_modules/.bin/jest --runInBand",
|
"test:debug-brk": "node --inspect-brk ./node_modules/.bin/jest --runInBand",
|
||||||
"rebuild-electron": "./node_modules/.bin/electron-rebuild -f -m ./packages/loot-core",
|
"rebuild-electron": "./node_modules/.bin/electron-rebuild -f -m ./packages/loot-core",
|
||||||
"rebuild-node": "cd packages/loot-core && npm rebuild",
|
"rebuild-node": "yarn workspace loot-core rebuild",
|
||||||
"lint": "cd packages/loot-core && yarn lint",
|
"lint": "yarn workspace loot-core lint",
|
||||||
"postinstall": "rm -rf ./packages/loot-design/node_modules/react && rm -rf ./packages/mobile/node_modules/react && rm -rf ./node_modules/react-native && patch-package"
|
"postinstall": "rm -rf ./packages/loot-design/node_modules/react && rm -rf ./packages/mobile/node_modules/react && rm -rf ./node_modules/react-native && patch-package"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/plugin-transform-modules-commonjs": "^7.15.0",
|
"@babel/plugin-transform-modules-commonjs": "^7.18.2",
|
||||||
"cross-env": "^5.1.5",
|
"cross-env": "^5.1.5",
|
||||||
"husky": "^3.0.4",
|
"husky": "^3.0.4",
|
||||||
"npm-run-all": "^4.1.3",
|
"npm-run-all": "^4.1.3",
|
||||||
|
"patch-package": "^6.1.2",
|
||||||
"prettier": "^1.18.1",
|
"prettier": "^1.18.1",
|
||||||
"pretty-quick": "^1.11.1",
|
"pretty-quick": "^1.11.1",
|
||||||
"shelljs": "^0.8.2",
|
"shelljs": "^0.8.2",
|
||||||
|
@ -83,6 +60,8 @@
|
||||||
"@babel/preset-env": "^7.15.1",
|
"@babel/preset-env": "^7.15.1",
|
||||||
"@babel/core": "^7.15.1",
|
"@babel/core": "^7.15.1",
|
||||||
"@babel/runtime": "^7.15.1",
|
"@babel/runtime": "^7.15.1",
|
||||||
"@babel/helper-plugin-utils": "^7.14.5"
|
"@babel/helper-plugin-utils": "^7.14.5",
|
||||||
}
|
"react-error-overlay": "6.0.9"
|
||||||
|
},
|
||||||
|
"packageManager": "yarn@3.2.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,26 +7,24 @@ const resolve = require('resolve');
|
||||||
const PnpWebpackPlugin = require('pnp-webpack-plugin');
|
const PnpWebpackPlugin = require('pnp-webpack-plugin');
|
||||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
|
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
|
||||||
const InlineChunkHtmlPlugin = require('jwl-dev-utils/InlineChunkHtmlPlugin');
|
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
|
||||||
const TerserPlugin = require('terser-webpack-plugin');
|
const TerserPlugin = require('terser-webpack-plugin');
|
||||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||||
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
|
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
|
||||||
const safePostCssParser = require('postcss-safe-parser');
|
const safePostCssParser = require('postcss-safe-parser');
|
||||||
const ManifestPlugin = require('webpack-manifest-plugin');
|
const ManifestPlugin = require('webpack-manifest-plugin');
|
||||||
const InterpolateHtmlPlugin = require('jwl-dev-utils/InterpolateHtmlPlugin');
|
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
|
||||||
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
|
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
|
||||||
const WatchMissingNodeModulesPlugin = require('jwl-dev-utils/WatchMissingNodeModulesPlugin');
|
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
|
||||||
const ModuleScopePlugin = require('jwl-dev-utils/ModuleScopePlugin');
|
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
|
||||||
const getCSSModuleLocalIdent = require('jwl-dev-utils/getCSSModuleLocalIdent');
|
|
||||||
const paths = require('./paths');
|
const paths = require('./paths');
|
||||||
const getClientEnvironment = require('./env');
|
const getClientEnvironment = require('./env');
|
||||||
const ModuleNotFoundPlugin = require('jwl-dev-utils/ModuleNotFoundPlugin');
|
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
|
||||||
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin-alt');
|
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin-alt');
|
||||||
const typescriptFormatter = require('jwl-dev-utils/typescriptFormatter');
|
|
||||||
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
|
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
|
||||||
|
|
||||||
const webpackDevClientEntry = require.resolve(
|
const webpackDevClientEntry = require.resolve(
|
||||||
'jwl-dev-utils/webpackHotDevClient'
|
'react-dev-utils/webpackHotDevClient'
|
||||||
);
|
);
|
||||||
|
|
||||||
// Source maps are resource heavy and can cause out of memory issue for large source files.
|
// Source maps are resource heavy and can cause out of memory issue for large source files.
|
||||||
|
@ -290,7 +288,7 @@ module.exports = function(webpackEnv) {
|
||||||
use: [
|
use: [
|
||||||
{
|
{
|
||||||
options: {
|
options: {
|
||||||
formatter: require.resolve('jwl-dev-utils/eslintFormatter'),
|
formatter: require.resolve('react-dev-utils/eslintFormatter'),
|
||||||
eslintPath: require.resolve('eslint')
|
eslintPath: require.resolve('eslint')
|
||||||
},
|
},
|
||||||
loader: require.resolve('eslint-loader')
|
loader: require.resolve('eslint-loader')
|
||||||
|
@ -490,7 +488,7 @@ module.exports = function(webpackEnv) {
|
||||||
entry: webpackDevClientEntry,
|
entry: webpackDevClientEntry,
|
||||||
// The expected exports are slightly different from what the overlay exports,
|
// The expected exports are slightly different from what the overlay exports,
|
||||||
// so an interop is included here to enable feedback on module-level errors.
|
// so an interop is included here to enable feedback on module-level errors.
|
||||||
module: require.resolve('jwl-dev-utils/refreshOverlayInterop'),
|
module: require.resolve('react-dev-utils/refreshOverlayInterop'),
|
||||||
// Since we ship a custom dev client and overlay integration,
|
// Since we ship a custom dev client and overlay integration,
|
||||||
// the bundled socket handling logic can be eliminated.
|
// the bundled socket handling logic can be eliminated.
|
||||||
sockIntegration: false
|
sockIntegration: false
|
||||||
|
@ -500,12 +498,6 @@ module.exports = function(webpackEnv) {
|
||||||
// a plugin that prints an error when you attempt to do this.
|
// a plugin that prints an error when you attempt to do this.
|
||||||
// See https://github.com/facebook/create-react-app/issues/240
|
// See https://github.com/facebook/create-react-app/issues/240
|
||||||
isEnvDevelopment && new CaseSensitivePathsPlugin(),
|
isEnvDevelopment && new CaseSensitivePathsPlugin(),
|
||||||
// If you require a missing module and then `npm install` it, you still have
|
|
||||||
// to restart the development server for Webpack to discover it. This plugin
|
|
||||||
// makes the discovery automatic so you don't have to restart.
|
|
||||||
// See https://github.com/facebook/create-react-app/issues/186
|
|
||||||
isEnvDevelopment &&
|
|
||||||
new WatchMissingNodeModulesPlugin(paths.appNodeModules),
|
|
||||||
isEnvProduction &&
|
isEnvProduction &&
|
||||||
new MiniCssExtractPlugin({
|
new MiniCssExtractPlugin({
|
||||||
// Options similar to the same options in webpackOptions.output
|
// Options similar to the same options in webpackOptions.output
|
||||||
|
@ -545,6 +537,8 @@ module.exports = function(webpackEnv) {
|
||||||
]
|
]
|
||||||
}),
|
}),
|
||||||
// TypeScript type checking
|
// TypeScript type checking
|
||||||
|
// This hasn't been tested since migrating away from `jwl-dev-utils` as we
|
||||||
|
// don't currently use TypeScript and we'll likely have to update the dependency anyway.
|
||||||
useTypeScript &&
|
useTypeScript &&
|
||||||
new ForkTsCheckerWebpackPlugin({
|
new ForkTsCheckerWebpackPlugin({
|
||||||
typescript: resolve.sync('typescript', {
|
typescript: resolve.sync('typescript', {
|
||||||
|
@ -571,7 +565,6 @@ module.exports = function(webpackEnv) {
|
||||||
],
|
],
|
||||||
watch: paths.appSrc,
|
watch: paths.appSrc,
|
||||||
silent: true,
|
silent: true,
|
||||||
formatter: typescriptFormatter
|
|
||||||
})
|
})
|
||||||
].filter(Boolean),
|
].filter(Boolean),
|
||||||
// Some libraries import Node modules but don't use them in the browser.
|
// Some libraries import Node modules but don't use them in the browser.
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const errorOverlayMiddleware = require('jwl-dev-utils/errorOverlayMiddleware');
|
const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware');
|
||||||
const evalSourceMapMiddleware = require('jwl-dev-utils/evalSourceMapMiddleware');
|
const evalSourceMapMiddleware = require('react-dev-utils/evalSourceMapMiddleware');
|
||||||
const noopServiceWorkerMiddleware = require('jwl-dev-utils/noopServiceWorkerMiddleware');
|
const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware');
|
||||||
const ignoredFiles = require('jwl-dev-utils/ignoredFiles');
|
const ignoredFiles = require('react-dev-utils/ignoredFiles');
|
||||||
const paths = require('./paths');
|
const paths = require('./paths');
|
||||||
const getHttpsConfig = require('./getHttpsConfig');
|
const getHttpsConfig = require('./getHttpsConfig');
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
"case-sensitive-paths-webpack-plugin": "2.1.2",
|
"case-sensitive-paths-webpack-plugin": "2.1.2",
|
||||||
"chalk": "2.4.1",
|
"chalk": "2.4.1",
|
||||||
"codemirror": "^5.37.0",
|
"codemirror": "^5.37.0",
|
||||||
|
"cross-env": "^7.0.3",
|
||||||
"css-loader": "1.0.0",
|
"css-loader": "1.0.0",
|
||||||
"date-fns": "2.0.0-alpha.27",
|
"date-fns": "2.0.0-alpha.27",
|
||||||
"debounce": "^1.2.0",
|
"debounce": "^1.2.0",
|
||||||
|
@ -43,7 +44,6 @@
|
||||||
"html-webpack-plugin": "4.0.0-alpha.2",
|
"html-webpack-plugin": "4.0.0-alpha.2",
|
||||||
"http-client": "^4.3.1",
|
"http-client": "^4.3.1",
|
||||||
"identity-obj-proxy": "3.0.0",
|
"identity-obj-proxy": "3.0.0",
|
||||||
"jwl-dev-utils": "^6.1.3",
|
|
||||||
"load-js": "^3.0.3",
|
"load-js": "^3.0.3",
|
||||||
"lodash.memoize": "^4.1.2",
|
"lodash.memoize": "^4.1.2",
|
||||||
"mini-css-extract-plugin": "0.4.3",
|
"mini-css-extract-plugin": "0.4.3",
|
||||||
|
@ -61,6 +61,7 @@
|
||||||
"react-addons-shallow-compare": "^15.6.0",
|
"react-addons-shallow-compare": "^15.6.0",
|
||||||
"react-app-polyfill": "^0.1.3",
|
"react-app-polyfill": "^0.1.3",
|
||||||
"react-autosuggest": "9.3.2",
|
"react-autosuggest": "9.3.2",
|
||||||
|
"react-dev-utils": "^12.0.1",
|
||||||
"react-dnd": "^10.0.2",
|
"react-dnd": "^10.0.2",
|
||||||
"react-dom": "16.13.1",
|
"react-dom": "16.13.1",
|
||||||
"react-modal": "3.4.4",
|
"react-modal": "3.4.4",
|
||||||
|
|
|
@ -22,11 +22,11 @@ const webpack = require('webpack');
|
||||||
const bfj = require('bfj');
|
const bfj = require('bfj');
|
||||||
const configFactory = require('../config/webpack.config');
|
const configFactory = require('../config/webpack.config');
|
||||||
const paths = require('../config/paths');
|
const paths = require('../config/paths');
|
||||||
const checkRequiredFiles = require('jwl-dev-utils/checkRequiredFiles');
|
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
|
||||||
const formatWebpackMessages = require('jwl-dev-utils/formatWebpackMessages');
|
const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
|
||||||
const printHostingInstructions = require('jwl-dev-utils/printHostingInstructions');
|
const printHostingInstructions = require('react-dev-utils/printHostingInstructions');
|
||||||
const FileSizeReporter = require('jwl-dev-utils/FileSizeReporter');
|
const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
|
||||||
const printBuildError = require('jwl-dev-utils/printBuildError');
|
const printBuildError = require('react-dev-utils/printBuildError');
|
||||||
|
|
||||||
const measureFileSizesBeforeBuild =
|
const measureFileSizesBeforeBuild =
|
||||||
FileSizeReporter.measureFileSizesBeforeBuild;
|
FileSizeReporter.measureFileSizesBeforeBuild;
|
||||||
|
@ -53,7 +53,7 @@ const config = configFactory('production');
|
||||||
|
|
||||||
// We require that you explicitly set browsers and do not fall back to
|
// We require that you explicitly set browsers and do not fall back to
|
||||||
// browserslist defaults.
|
// browserslist defaults.
|
||||||
const { checkBrowsers } = require('jwl-dev-utils/browsersHelper');
|
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
|
||||||
checkBrowsers(paths.appPath, isInteractive)
|
checkBrowsers(paths.appPath, isInteractive)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// First, read the current file sizes in build directory.
|
// First, read the current file sizes in build directory.
|
||||||
|
|
|
@ -19,15 +19,15 @@ const fs = require('fs');
|
||||||
const chalk = require('chalk');
|
const chalk = require('chalk');
|
||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
const WebpackDevServer = require('webpack-dev-server');
|
const WebpackDevServer = require('webpack-dev-server');
|
||||||
const clearConsole = require('jwl-dev-utils/clearConsole');
|
const clearConsole = require('react-dev-utils/clearConsole');
|
||||||
const checkRequiredFiles = require('jwl-dev-utils/checkRequiredFiles');
|
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
|
||||||
const {
|
const {
|
||||||
choosePort,
|
choosePort,
|
||||||
createCompiler,
|
createCompiler,
|
||||||
prepareProxy,
|
prepareProxy,
|
||||||
prepareUrls,
|
prepareUrls,
|
||||||
} = require('jwl-dev-utils/WebpackDevServerUtils');
|
} = require('react-dev-utils/WebpackDevServerUtils');
|
||||||
const openBrowser = require('jwl-dev-utils/openBrowser');
|
const openBrowser = require('react-dev-utils/openBrowser');
|
||||||
const paths = require('../config/paths');
|
const paths = require('../config/paths');
|
||||||
const configFactory = require('../config/webpack.config');
|
const configFactory = require('../config/webpack.config');
|
||||||
const createDevServerConfig = require('../config/webpackDevServer.config');
|
const createDevServerConfig = require('../config/webpackDevServer.config');
|
||||||
|
@ -63,7 +63,7 @@ if (process.env.HOST) {
|
||||||
|
|
||||||
// We require that you explictly set browsers and do not fall back to
|
// We require that you explictly set browsers and do not fall back to
|
||||||
// browserslist defaults.
|
// browserslist defaults.
|
||||||
const { checkBrowsers } = require('jwl-dev-utils/browsersHelper');
|
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
|
||||||
checkBrowsers(paths.appPath, isInteractive)
|
checkBrowsers(paths.appPath, isInteractive)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// We attempt to use the default port but if it is busy, we offer the user to
|
// We attempt to use the default port but if it is busy, we offer the user to
|
||||||
|
@ -80,7 +80,7 @@ checkBrowsers(paths.appPath, isInteractive)
|
||||||
const appName = require(paths.appPackageJson).name;
|
const appName = require(paths.appPackageJson).name;
|
||||||
const urls = prepareUrls(protocol, HOST, port);
|
const urls = prepareUrls(protocol, HOST, port);
|
||||||
// Create a webpack compiler that is configured with custom messages.
|
// Create a webpack compiler that is configured with custom messages.
|
||||||
const compiler = createCompiler(webpack, config, appName, urls, useYarn);
|
const compiler = createCompiler({ webpack, config, appName, urls, useYarn });
|
||||||
// Load proxy config
|
// Load proxy config
|
||||||
const proxySetting = require(paths.appPackageJson).proxy;
|
const proxySetting = require(paths.appPackageJson).proxy;
|
||||||
const proxyConfig = prepareProxy(proxySetting, paths.appPublic);
|
const proxyConfig = prepareProxy(proxySetting, paths.appPublic);
|
||||||
|
|
|
@ -24,6 +24,8 @@ import ExpandArrow from 'loot-design/src/svg/ExpandArrow';
|
||||||
import ExclamationSolid from 'loot-design/src/svg/v1/ExclamationSolid';
|
import ExclamationSolid from 'loot-design/src/svg/v1/ExclamationSolid';
|
||||||
import Platform from 'loot-core/src/client/platform';
|
import Platform from 'loot-core/src/client/platform';
|
||||||
|
|
||||||
|
import useServerVersion from '../hooks/useServerVersion';
|
||||||
|
|
||||||
let dateFormats = [
|
let dateFormats = [
|
||||||
{ value: 'MM/dd/yyyy', label: 'MM/DD/YYYY' },
|
{ value: 'MM/dd/yyyy', label: 'MM/DD/YYYY' },
|
||||||
{ value: 'dd/MM/yyyy', label: 'DD/MM/YYYY' },
|
{ value: 'dd/MM/yyyy', label: 'DD/MM/YYYY' },
|
||||||
|
@ -473,6 +475,27 @@ function SettingsLink({ to, name, style, first, last }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Version() {
|
||||||
|
const version = useServerVersion();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
{
|
||||||
|
alignSelf: 'center',
|
||||||
|
color: colors.n7,
|
||||||
|
':hover': { color: colors.n2 },
|
||||||
|
padding: '6px 10px'
|
||||||
|
},
|
||||||
|
styles.staticText,
|
||||||
|
styles.smallText
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{`App: v${window.Actual.ACTUAL_VERSION} | Server: ${version}`}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
class Settings extends React.Component {
|
class Settings extends React.Component {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.unlisten = listen('prefs-updated', () => {
|
this.unlisten = listen('prefs-updated', () => {
|
||||||
|
@ -496,12 +519,21 @@ class Settings extends React.Component {
|
||||||
style={{
|
style={{
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignSelf: 'center',
|
alignSelf: 'center',
|
||||||
margin: '15px 0'
|
margin: '15px 0 5px 0'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SettingsLink to={`${match.path}/file`} name="File" first={true} />
|
<SettingsLink to={`${match.path}/file`} name="File" first={true} />
|
||||||
<SettingsLink to={`${match.path}/global`} name="Global" last={true} />
|
<SettingsLink to={`${match.path}/global`} name="Global" last={true} />
|
||||||
</View>
|
</View>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignSelf: 'center',
|
||||||
|
margin: '0 0 10px 0'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Version />
|
||||||
|
</View>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { createBrowserHistory } from 'history';
|
||||||
import { Switch, Redirect, Router, Route } from 'react-router-dom';
|
import { Switch, Redirect, Router, Route } from 'react-router-dom';
|
||||||
import { send } from 'loot-core/src/platform/client/fetch';
|
import { send } from 'loot-core/src/platform/client/fetch';
|
||||||
import * as actions from 'loot-core/src/client/actions';
|
import * as actions from 'loot-core/src/client/actions';
|
||||||
import { View, ExternalLink, Button } from 'loot-design/src/components/common';
|
import { View, Text } from 'loot-design/src/components/common';
|
||||||
import { colors } from 'loot-design/src/style';
|
import { colors } from 'loot-design/src/style';
|
||||||
import ServerURL from './ServerURL';
|
import ServerURL from './ServerURL';
|
||||||
import LoggedInUser from '../LoggedInUser';
|
import LoggedInUser from '../LoggedInUser';
|
||||||
|
@ -16,10 +16,13 @@ import Bootstrap from './subscribe/Bootstrap';
|
||||||
import Error from './subscribe/Error';
|
import Error from './subscribe/Error';
|
||||||
import ChangePassword from './subscribe/ChangePassword';
|
import ChangePassword from './subscribe/ChangePassword';
|
||||||
import ConfigServer from './ConfigServer';
|
import ConfigServer from './ConfigServer';
|
||||||
|
import useServerVersion from '../../hooks/useServerVersion';
|
||||||
|
|
||||||
function Version() {
|
function Version() {
|
||||||
|
const version = useServerVersion();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ExternalLink
|
<Text
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
|
@ -32,8 +35,8 @@ function Version() {
|
||||||
}}
|
}}
|
||||||
href={'https://actualbudget.com/blog/' + window.Actual.ACTUAL_VERSION}
|
href={'https://actualbudget.com/blog/' + window.Actual.ACTUAL_VERSION}
|
||||||
>
|
>
|
||||||
{window.Actual.ACTUAL_VERSION}
|
{`App: v${window.Actual.ACTUAL_VERSION} | Server: ${version}`}
|
||||||
</ExternalLink>
|
</Text>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
22
packages/desktop-client/src/hooks/useServerVersion.js
Normal file
22
packages/desktop-client/src/hooks/useServerVersion.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { send } from 'loot-core/src/platform/client/fetch';
|
||||||
|
|
||||||
|
function useServerVersion() {
|
||||||
|
let [version, setVersion] = useState('');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
const { error, version } = await send('get-server-version');
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
setVersion('');
|
||||||
|
} else {
|
||||||
|
setVersion(version);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return version ? `v${version}` : 'N/A';
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useServerVersion;
|
|
@ -2,8 +2,8 @@ const d = require('date-fns');
|
||||||
const normalizePathSep = require('slash');
|
const normalizePathSep = require('slash');
|
||||||
const uuid = require('uuid');
|
const uuid = require('uuid');
|
||||||
const AdmZip = require('adm-zip');
|
const AdmZip = require('adm-zip');
|
||||||
const actual = require('@actual-app/api/methods');
|
const actual = require('@actual-app/api');
|
||||||
const { amountToInteger } = require('@actual-app/api/utils');
|
const amountToInteger = actual.utils.amountToInteger;
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
const d = require('date-fns');
|
const d = require('date-fns');
|
||||||
const uuid = require('uuid');
|
const uuid = require('uuid');
|
||||||
const actual = require('@actual-app/api/methods');
|
const actual = require('@actual-app/api');
|
||||||
const { amountToInteger } = require('@actual-app/api/utils');
|
|
||||||
|
|
||||||
function amountFromYnab(amount) {
|
function amountFromYnab(amount) {
|
||||||
// ynabs multiplies amount by 1000 and actual by 100
|
// ynabs multiplies amount by 1000 and actual by 100
|
||||||
|
|
1
packages/loot-core/bin/get-db-schema
Executable file → Normal file
1
packages/loot-core/bin/get-db-schema
Executable file → Normal file
|
@ -3,7 +3,6 @@ process.env.LOOT_DATA_DIR = __dirname + '/../../../../data';
|
||||||
import * as sqlite from '../platform/server/sqlite';
|
import * as sqlite from '../platform/server/sqlite';
|
||||||
import * as db from '../server/db';
|
import * as db from '../server/db';
|
||||||
import { getMessages } from '../server/sync';
|
import { getMessages } from '../server/sync';
|
||||||
import Timestamp from '../server/timestamp';
|
|
||||||
|
|
||||||
let dbPath = process.argv[2];
|
let dbPath = process.argv[2];
|
||||||
let tables = [
|
let tables = [
|
||||||
|
|
|
@ -4,7 +4,6 @@ import os from 'os';
|
||||||
import * as sqlite from '../src/platform/server/sqlite';
|
import * as sqlite from '../src/platform/server/sqlite';
|
||||||
import * as db from '../src/server/db';
|
import * as db from '../src/server/db';
|
||||||
import { batchMessages, setSyncingMode } from '../src/server/sync';
|
import { batchMessages, setSyncingMode } from '../src/server/sync';
|
||||||
import Timestamp from '../src/server/timestamp';
|
|
||||||
import { runQuery } from '../src/server/aql/schema/run-query';
|
import { runQuery } from '../src/server/aql/schema/run-query';
|
||||||
import asyncStorage from '../src/platform/server/asyncStorage';
|
import asyncStorage from '../src/platform/server/asyncStorage';
|
||||||
import { makeChild } from '../src/shared/transactions';
|
import { makeChild } from '../src/shared/transactions';
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
"build:node": "cross-env NODE_ENV=production webpack --config ./webpack/webpack.desktop.config.js",
|
"build:node": "cross-env NODE_ENV=production webpack --config ./webpack/webpack.desktop.config.js",
|
||||||
"watch:node": "cross-env NODE_ENV=development webpack --config ./webpack/webpack.desktop.config.js --watch",
|
"watch:node": "cross-env NODE_ENV=development webpack --config ./webpack/webpack.desktop.config.js --watch",
|
||||||
"build:api": "cross-env NODE_ENV=development webpack --config ./webpack/webpack.api.config.js",
|
"build:api": "cross-env NODE_ENV=development webpack --config ./webpack/webpack.api.config.js",
|
||||||
"build:browser": "NODE_ENV=production ./bin/build-browser",
|
"build:browser": "cross-env NODE_ENV=production ./bin/build-browser",
|
||||||
"watch:browser": "NODE_ENV=development ./bin/build-browser",
|
"watch:browser": "cross-env NODE_ENV=development ./bin/build-browser",
|
||||||
"lint": "eslint src"
|
"lint": "eslint src"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
|
@ -43,9 +43,10 @@
|
||||||
"babel-jest": "25.2.6",
|
"babel-jest": "25.2.6",
|
||||||
"babel-loader": "^8.0.6",
|
"babel-loader": "^8.0.6",
|
||||||
"buffer": "^5.5.0",
|
"buffer": "^5.5.0",
|
||||||
"currency-formatter": "jlongster/currency-formatter",
|
"cross-env": "^7.0.3",
|
||||||
"damerau-levenshtein": "^1.0.4",
|
"damerau-levenshtein": "^1.0.4",
|
||||||
"date-fns": "2.0.0-alpha.27",
|
"date-fns": "2.0.0-alpha.27",
|
||||||
|
"eslint": "5.6.0",
|
||||||
"esm": "^3.0.82",
|
"esm": "^3.0.82",
|
||||||
"fake-indexeddb": "^3.1.3",
|
"fake-indexeddb": "^3.1.3",
|
||||||
"fast-check": "2.13.0",
|
"fast-check": "2.13.0",
|
||||||
|
@ -55,14 +56,14 @@
|
||||||
"lru-cache": "^5.1.1",
|
"lru-cache": "^5.1.1",
|
||||||
"memfs": "3.1.1",
|
"memfs": "3.1.1",
|
||||||
"memoize-one": "^4.0.0",
|
"memoize-one": "^4.0.0",
|
||||||
"mockdate": "^3.0.5",
|
|
||||||
"mock-require": "^3.0.2",
|
"mock-require": "^3.0.2",
|
||||||
|
"mockdate": "^3.0.5",
|
||||||
"murmurhash": "^0.0.2",
|
"murmurhash": "^0.0.2",
|
||||||
"perf-deets": "^1.0.15",
|
"perf-deets": "^1.0.15",
|
||||||
"sanitize-filename": "^1.6.1",
|
"sanitize-filename": "^1.6.1",
|
||||||
"search-query-parser": "^1.3.0",
|
"search-query-parser": "^1.3.0",
|
||||||
"source-map": "^0.7.3",
|
|
||||||
"snapshot-diff": "^0.2.2",
|
"snapshot-diff": "^0.2.2",
|
||||||
|
"source-map": "^0.7.3",
|
||||||
"throttleit": "^1.0.0",
|
"throttleit": "^1.0.0",
|
||||||
"uuid": "3.3.2",
|
"uuid": "3.3.2",
|
||||||
"webpack": "^4.41.2",
|
"webpack": "^4.41.2",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { send } from '../../platform/client/fetch';
|
import { send } from '../../platform/client/fetch';
|
||||||
import constants from '../constants';
|
import constants from '../constants';
|
||||||
import { getPayees, getAccounts, filterTransactions } from './queries';
|
import { getPayees, getAccounts } from './queries';
|
||||||
import { addNotification } from './notifications';
|
import { addNotification } from './notifications';
|
||||||
|
|
||||||
export function setAccountsSyncing(name) {
|
export function setAccountsSyncing(name) {
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
import { send } from '../../platform/client/fetch';
|
import { send } from '../../platform/client/fetch';
|
||||||
import constants from '../constants';
|
import constants from '../constants';
|
||||||
import { loadPrefs } from './prefs';
|
import { loadPrefs } from './prefs';
|
||||||
import { createBudget, loadBudget } from './budgets';
|
|
||||||
import { getCategories, getAccounts, getPayees } from './queries';
|
|
||||||
import { syncAccounts } from './account';
|
import { syncAccounts } from './account';
|
||||||
import { setAppState } from './app';
|
|
||||||
import { pushModal } from './modals';
|
import { pushModal } from './modals';
|
||||||
import { getUploadError } from '../../shared/errors';
|
import { getUploadError } from '../../shared/errors';
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import constants from '../constants';
|
|
||||||
import { send } from '../../platform/client/fetch';
|
import { send } from '../../platform/client/fetch';
|
||||||
import Platform from '../platform';
|
import Platform from '../platform';
|
||||||
import { pushModal } from './modals';
|
import { pushModal } from './modals';
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useMemo, useEffect, useState, useContext } from 'react';
|
import React, { useEffect, useState, useContext } from 'react';
|
||||||
import q, { liveQuery, runQuery } from 'loot-core/src/client/query-helpers';
|
import q, { liveQuery } from 'loot-core/src/client/query-helpers';
|
||||||
import { getAccountsById } from 'loot-core/src/client/reducers/queries';
|
import { getAccountsById } from 'loot-core/src/client/reducers/queries';
|
||||||
|
|
||||||
export function useAccounts() {
|
export function useAccounts() {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useMemo, useEffect, useState, useContext } from 'react';
|
import React, { useEffect, useState, useContext } from 'react';
|
||||||
import q, { liveQuery, runQuery } from 'loot-core/src/client/query-helpers';
|
import q, { liveQuery } from 'loot-core/src/client/query-helpers';
|
||||||
import { getPayeesById } from 'loot-core/src/client/reducers/queries';
|
import { getPayeesById } from 'loot-core/src/client/reducers/queries';
|
||||||
|
|
||||||
export function usePayees() {
|
export function usePayees() {
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
import React, { useMemo, useEffect, useState, useContext } from 'react';
|
import React, { useEffect, useState, useContext } from 'react';
|
||||||
import * as monthUtils from 'loot-core/src/shared/months';
|
|
||||||
import {
|
import {
|
||||||
getStatus,
|
getStatus,
|
||||||
getHasTransactionsQuery
|
getHasTransactionsQuery
|
||||||
} from 'loot-core/src/shared/schedules';
|
} from 'loot-core/src/shared/schedules';
|
||||||
import q, { liveQuery, runQuery } from 'loot-core/src/client/query-helpers';
|
import q, { liveQuery } from 'loot-core/src/client/query-helpers';
|
||||||
import { send } from 'loot-core/src/platform/client/fetch';
|
|
||||||
|
|
||||||
function loadStatuses(schedules, onData) {
|
function loadStatuses(schedules, onData) {
|
||||||
return liveQuery(getHasTransactionsQuery(schedules), onData, {
|
return liveQuery(getHasTransactionsQuery(schedules), onData, {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { listen, send } from '../platform/client/fetch';
|
import { listen, send } from '../platform/client/fetch';
|
||||||
import { once, runAgain } from '../shared/async';
|
import { once } from '../shared/async';
|
||||||
|
|
||||||
import q, { getPrimaryOrderBy } from '../shared/query';
|
import q, { getPrimaryOrderBy } from '../shared/query';
|
||||||
export default q;
|
export default q;
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import dateFns from 'date-fns';
|
|
||||||
const uuid = require('../platform/uuid');
|
const uuid = require('../platform/uuid');
|
||||||
|
|
||||||
export function generateAccount(balance) {
|
export function generateAccount(balance) {
|
||||||
|
|
|
@ -5,7 +5,6 @@ import * as sheet from '../server/sheet';
|
||||||
import * as rules from '../server/accounts/transaction-rules';
|
import * as rules from '../server/accounts/transaction-rules';
|
||||||
import * as tracking from '../server/tracking/events';
|
import * as tracking from '../server/tracking/events';
|
||||||
import { setSyncingMode } from '../server/sync';
|
import { setSyncingMode } from '../server/sync';
|
||||||
import * as monthUtils from '../shared/months';
|
|
||||||
import { updateVersion } from '../server/update';
|
import { updateVersion } from '../server/update';
|
||||||
import { resetTracer, tracer } from '../shared/test-helpers';
|
import { resetTracer, tracer } from '../shared/test-helpers';
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -4,16 +4,7 @@ require('fake-indexeddb/auto');
|
||||||
let FDBFactory = require('fake-indexeddb/lib/FDBFactory');
|
let FDBFactory = require('fake-indexeddb/lib/FDBFactory');
|
||||||
|
|
||||||
let idb = require('../indexeddb');
|
let idb = require('../indexeddb');
|
||||||
let {
|
let { init, readFile, writeFile, exists, pathToId, join } = require('./index');
|
||||||
init,
|
|
||||||
populateFileHeirarchy,
|
|
||||||
readFile,
|
|
||||||
writeFile,
|
|
||||||
exists,
|
|
||||||
pathToId,
|
|
||||||
listDir,
|
|
||||||
join
|
|
||||||
} = require('./index');
|
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
process.env.PUBLIC_URL =
|
process.env.PUBLIC_URL =
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import fs from 'fs';
|
|
||||||
import csvStringify from 'csv-stringify/lib/sync';
|
import csvStringify from 'csv-stringify/lib/sync';
|
||||||
import { runQuery as aqlQuery } from '../aql/schema/run-query';
|
import { runQuery as aqlQuery } from '../aql/schema/run-query';
|
||||||
import { integerToAmount } from '../../shared/util';
|
import { integerToAmount } from '../../shared/util';
|
||||||
|
|
|
@ -7,7 +7,6 @@ import {
|
||||||
isAfter,
|
isAfter,
|
||||||
addDays,
|
addDays,
|
||||||
subDays,
|
subDays,
|
||||||
format as formatDate,
|
|
||||||
parseDate
|
parseDate
|
||||||
} from '../../shared/months';
|
} from '../../shared/months';
|
||||||
import { fastSetMerge } from '../../shared/util';
|
import { fastSetMerge } from '../../shared/util';
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import {
|
import {
|
||||||
parseDateString,
|
parseDateString,
|
||||||
parseRecurDate,
|
|
||||||
rankRules,
|
rankRules,
|
||||||
iterateIds,
|
iterateIds,
|
||||||
Condition,
|
Condition,
|
||||||
|
|
|
@ -1,24 +1,15 @@
|
||||||
import title from './title';
|
import title from './title';
|
||||||
import * as db from '../db';
|
import * as db from '../db';
|
||||||
import {
|
import { hasFieldsChanged, amountToInteger } from '../../shared/util';
|
||||||
mergeObjects,
|
|
||||||
hasFieldsChanged,
|
|
||||||
toRelaxedNumber,
|
|
||||||
amountToInteger,
|
|
||||||
integerToAmount
|
|
||||||
} from '../../shared/util';
|
|
||||||
import {
|
import {
|
||||||
makeChild as makeChildTransaction,
|
makeChild as makeChildTransaction,
|
||||||
recalculateSplit
|
recalculateSplit
|
||||||
} from '../../shared/transactions';
|
} from '../../shared/transactions';
|
||||||
import * as monthUtils from '../../shared/months';
|
import * as monthUtils from '../../shared/months';
|
||||||
import { transactionModel } from '../api-models';
|
|
||||||
import { getServer } from '../server-config';
|
import { getServer } from '../server-config';
|
||||||
import { batchMessages } from '../sync';
|
import { batchMessages } from '../sync';
|
||||||
import { runMutator } from '../mutators';
|
import { runMutator } from '../mutators';
|
||||||
import { getStartingBalancePayee } from './payees';
|
import { getStartingBalancePayee } from './payees';
|
||||||
import * as transfer from './transfer';
|
|
||||||
import { TransactionError } from '../errors';
|
|
||||||
import { runRules } from './transaction-rules';
|
import { runRules } from './transaction-rules';
|
||||||
import { batchUpdateTransactions } from './transactions';
|
import { batchUpdateTransactions } from './transactions';
|
||||||
|
|
||||||
|
|
|
@ -17,13 +17,11 @@ import {
|
||||||
sortNumbers,
|
sortNumbers,
|
||||||
getApproxNumberThreshold
|
getApproxNumberThreshold
|
||||||
} from '../../shared/rules';
|
} from '../../shared/rules';
|
||||||
import q from '../../shared/query';
|
|
||||||
import { requiredFields, toDateRepr } from '../models';
|
import { requiredFields, toDateRepr } from '../models';
|
||||||
import { currentDay } from '../../shared/months';
|
import { currentDay } from '../../shared/months';
|
||||||
import { partitionByField, fastSetMerge } from '../../shared/util';
|
import { partitionByField, fastSetMerge } from '../../shared/util';
|
||||||
import { setSyncingMode, batchMessages } from '../sync';
|
import { setSyncingMode, batchMessages } from '../sync';
|
||||||
import { schemaConfig } from '../aql/schema';
|
import { schemaConfig } from '../aql/schema';
|
||||||
const uuid = require('../../platform/uuid');
|
|
||||||
|
|
||||||
// TODO: Detect if it looks like the user is creating a rename rule
|
// TODO: Detect if it looks like the user is creating a rename rule
|
||||||
// and prompt to create it in the pre phase instead
|
// and prompt to create it in the pre phase instead
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import * as db from '../db';
|
import * as db from '../db';
|
||||||
import { Rule } from './rules';
|
|
||||||
import {
|
import {
|
||||||
getRules,
|
getRules,
|
||||||
loadRules,
|
loadRules,
|
||||||
|
@ -15,7 +14,6 @@ import {
|
||||||
migrateOldRules
|
migrateOldRules
|
||||||
} from './transaction-rules';
|
} from './transaction-rules';
|
||||||
import { loadMappings } from '../db/mappings';
|
import { loadMappings } from '../db/mappings';
|
||||||
import { applyMigration } from '../migrate/migrations';
|
|
||||||
import { runQuery } from '../aql/schema/run-query';
|
import { runQuery } from '../aql/schema/run-query';
|
||||||
import q from '../../shared/query';
|
import q from '../../shared/query';
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import * as sheet from '../sheet';
|
|
||||||
import { batchMessages } from '../sync';
|
import { batchMessages } from '../sync';
|
||||||
import * as db from '../db';
|
import * as db from '../db';
|
||||||
import { incrFetch, whereIn } from '../db/util';
|
import { incrFetch, whereIn } from '../db/util';
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { addTransactions } from './accounts/sync';
|
import { addTransactions } from './accounts/sync';
|
||||||
import {
|
import {
|
||||||
transactionModel,
|
|
||||||
accountModel,
|
accountModel,
|
||||||
categoryModel,
|
categoryModel,
|
||||||
categoryGroupModel,
|
categoryGroupModel,
|
||||||
|
@ -16,17 +15,14 @@ import * as db from './db';
|
||||||
import * as sheet from './sheet';
|
import * as sheet from './sheet';
|
||||||
import * as prefs from './prefs';
|
import * as prefs from './prefs';
|
||||||
import * as monthUtils from '../shared/months';
|
import * as monthUtils from '../shared/months';
|
||||||
import * as update from './update';
|
|
||||||
import * as cloudStorage from './cloud-storage';
|
import * as cloudStorage from './cloud-storage';
|
||||||
import { setSyncingMode, batchMessages } from './sync';
|
import { setSyncingMode, batchMessages } from './sync';
|
||||||
import { groupById, cleanUUID } from '../shared/util';
|
import { getClock } from './crdt';
|
||||||
import { getClock } from './timestamp';
|
|
||||||
import { runMutator } from './mutators';
|
import { runMutator } from './mutators';
|
||||||
import { integerToAmount } from '../shared/util';
|
import { integerToAmount } from '../shared/util';
|
||||||
import { runQuery as aqlQuery } from './aql/schema/run-query';
|
import { runQuery as aqlQuery } from './aql/schema/run-query';
|
||||||
import q from '../shared/query';
|
import q from '../shared/query';
|
||||||
|
|
||||||
const { resolveName } = require('./spreadsheet/util');
|
|
||||||
const connection = require('../platform/server/connection');
|
const connection = require('../platform/server/connection');
|
||||||
|
|
||||||
let IMPORT_MODE = false;
|
let IMPORT_MODE = false;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import * as db from '../../db';
|
import * as db from '../../db';
|
||||||
import { whereIn } from '../../db/util';
|
import { whereIn } from '../../db/util';
|
||||||
import { groupBy } from '../../../shared/util';
|
|
||||||
import { isAggregateQuery } from '../compiler';
|
import { isAggregateQuery } from '../compiler';
|
||||||
import { convertOutputType } from '../schema-helpers';
|
import { convertOutputType } from '../schema-helpers';
|
||||||
import { execQuery } from '../exec';
|
import { execQuery } from '../exec';
|
||||||
|
|
|
@ -1,16 +1,12 @@
|
||||||
import fc from 'fast-check';
|
import fc from 'fast-check';
|
||||||
import * as db from '../../db';
|
import * as db from '../../db';
|
||||||
import query from '../../../shared/query';
|
import query from '../../../shared/query';
|
||||||
import { makeChild } from '../../../shared/transactions';
|
|
||||||
import { batchMessages, setSyncingMode } from '../../sync/index';
|
import { batchMessages, setSyncingMode } from '../../sync/index';
|
||||||
import { setClock } from '../../timestamp';
|
import { setClock } from '../../crdt';
|
||||||
import { groupById } from '../../../shared/util';
|
import { groupById } from '../../../shared/util';
|
||||||
import arbs from '../../../mocks/arbitrary-schema';
|
import arbs from '../../../mocks/arbitrary-schema';
|
||||||
import { isAggregateQuery } from '../compiler';
|
|
||||||
import { runQuery } from './run-query';
|
import { runQuery } from './run-query';
|
||||||
import { toGroup, isHappyPathQuery } from './executors';
|
import { isHappyPathQuery } from './executors';
|
||||||
|
|
||||||
const uuid = require('../../../platform/uuid');
|
|
||||||
|
|
||||||
beforeEach(global.emptyDatabase());
|
beforeEach(global.emptyDatabase());
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import * as monthUtils from '../../shared/months';
|
import * as monthUtils from '../../shared/months';
|
||||||
import * as db from '../db';
|
import * as db from '../db';
|
||||||
import { batchMessages } from '../sync';
|
import { batchMessages } from '../sync';
|
||||||
import { groupBySingle } from '../../shared/util';
|
|
||||||
import * as prefs from '../prefs';
|
import * as prefs from '../prefs';
|
||||||
import * as sheet from '../sheet';
|
import * as sheet from '../sheet';
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import * as sheet from '../sheet';
|
import * as sheet from '../sheet';
|
||||||
import * as monthUtils from '../../shared/months';
|
|
||||||
import { number, sumAmounts } from './util';
|
import { number, sumAmounts } from './util';
|
||||||
const { resolveName } = require('../spreadsheet/util');
|
const { resolveName } = require('../spreadsheet/util');
|
||||||
|
|
||||||
|
|
4
packages/loot-core/src/server/crdt/index.js
Normal file
4
packages/loot-core/src/server/crdt/index.js
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
import * as merkle from "./merkle";
|
||||||
|
|
||||||
|
export { merkle };
|
||||||
|
export { getClock, setClock, makeClock, makeClientId, serializeClock, deserializeClock, Timestamp } from "./timestamp"
|
|
@ -1,5 +1,5 @@
|
||||||
import * as merkle from './merkle';
|
import * as merkle from './merkle';
|
||||||
import Timestamp from './timestamp';
|
import { Timestamp } from './timestamp';
|
||||||
|
|
||||||
function pretty(n) {
|
function pretty(n) {
|
||||||
if (n < 60) {
|
if (n < 60) {
|
|
@ -1,5 +1,5 @@
|
||||||
import murmurhash from 'murmurhash';
|
import murmurhash from 'murmurhash';
|
||||||
const uuid = require('../platform/uuid');
|
const uuid = require('../../platform/uuid');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hybrid Unique Logical Clock (HULC) timestamp generator
|
* Hybrid Unique Logical Clock (HULC) timestamp generator
|
||||||
|
@ -81,7 +81,7 @@ const MAX_NODE_LENGTH = 16;
|
||||||
/**
|
/**
|
||||||
* timestamp instance class
|
* timestamp instance class
|
||||||
*/
|
*/
|
||||||
export default class Timestamp {
|
export class Timestamp {
|
||||||
constructor(millis, counter, node) {
|
constructor(millis, counter, node) {
|
||||||
this._state = {
|
this._state = {
|
||||||
millis: millis,
|
millis: millis,
|
||||||
|
@ -124,7 +124,7 @@ export default class Timestamp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MutableTimestamp extends Timestamp {
|
class MutableTimestamp extends Timestamp {
|
||||||
setMillis(n) {
|
setMillis(n) {
|
||||||
this._state.millis = n;
|
this._state.millis = n;
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import Timestamp, { setClock, makeClock } from './timestamp';
|
import { Timestamp } from './timestamp';
|
||||||
|
|
||||||
describe('Timestamp', function() {
|
describe('Timestamp', function() {
|
||||||
let now = 0;
|
let now = 0;
|
|
@ -11,13 +11,14 @@ import {
|
||||||
payeeRuleModel
|
payeeRuleModel
|
||||||
} from '../models';
|
} from '../models';
|
||||||
import { groupById } from '../../shared/util';
|
import { groupById } from '../../shared/util';
|
||||||
import Timestamp, {
|
import {
|
||||||
makeClock,
|
makeClock,
|
||||||
setClock,
|
setClock,
|
||||||
serializeClock,
|
serializeClock,
|
||||||
deserializeClock,
|
deserializeClock,
|
||||||
makeClientId
|
makeClientId,
|
||||||
} from '../timestamp';
|
Timestamp
|
||||||
|
} from '../crdt';
|
||||||
import {
|
import {
|
||||||
convertForInsert,
|
convertForInsert,
|
||||||
convertForUpdate,
|
convertForUpdate,
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import './polyfills';
|
import './polyfills';
|
||||||
import { differenceInDays } from 'date-fns';
|
|
||||||
import asyncStorage from '../platform/server/asyncStorage';
|
import asyncStorage from '../platform/server/asyncStorage';
|
||||||
import { captureException, captureBreadcrumb } from '../platform/exceptions';
|
import { captureException, captureBreadcrumb } from '../platform/exceptions';
|
||||||
import * as prefs from './prefs';
|
import * as prefs from './prefs';
|
||||||
|
@ -31,8 +30,7 @@ import * as bankSync from './accounts/sync';
|
||||||
import * as link from './accounts/link';
|
import * as link from './accounts/link';
|
||||||
import { uniqueFileName, idFromFileName } from './util/budget-name';
|
import { uniqueFileName, idFromFileName } from './util/budget-name';
|
||||||
import { mutator, runHandler } from './mutators';
|
import { mutator, runHandler } from './mutators';
|
||||||
import * as timestamp from './timestamp';
|
import { getClock, setClock, makeClock, makeClientId, serializeClock, deserializeClock, Timestamp, merkle } from './crdt';
|
||||||
import * as merkle from './merkle';
|
|
||||||
import {
|
import {
|
||||||
initialFullSync,
|
initialFullSync,
|
||||||
fullSync,
|
fullSync,
|
||||||
|
@ -1496,6 +1494,24 @@ handlers['subscribe-sign-out'] = async function() {
|
||||||
return 'ok';
|
return 'ok';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handlers['get-server-version'] = async function() {
|
||||||
|
if (!getServer() || getServer().BASE_SERVER === UNCONFIGURED_SERVER) {
|
||||||
|
return { error: 'no-server' };
|
||||||
|
}
|
||||||
|
|
||||||
|
let version;
|
||||||
|
try {
|
||||||
|
const res = await get(getServer().BASE_SERVER + '/info');
|
||||||
|
|
||||||
|
const info = JSON.parse(res);
|
||||||
|
version = info.build.version;
|
||||||
|
} catch (err) {
|
||||||
|
return { error: 'network-failure' };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { version };
|
||||||
|
};
|
||||||
|
|
||||||
handlers['get-server-url'] = async function() {
|
handlers['get-server-url'] = async function() {
|
||||||
return getServer() && getServer().BASE_SERVER;
|
return getServer() && getServer().BASE_SERVER;
|
||||||
};
|
};
|
||||||
|
@ -1951,10 +1967,10 @@ async function loadBudget(id, appVersion, { showUpdate } = {}) {
|
||||||
//
|
//
|
||||||
// TODO: The client id should be stored elsewhere. It shouldn't
|
// TODO: The client id should be stored elsewhere. It shouldn't
|
||||||
// work this way, but it's fine for now.
|
// work this way, but it's fine for now.
|
||||||
timestamp.getClock().timestamp.setNode(timestamp.makeClientId());
|
getClock().timestamp.setNode(makeClientId());
|
||||||
await db.runQuery(
|
await db.runQuery(
|
||||||
'INSERT OR REPLACE INTO messages_clock (id, clock) VALUES (1, ?)',
|
'INSERT OR REPLACE INTO messages_clock (id, clock) VALUES (1, ?)',
|
||||||
[timestamp.serializeClock(timestamp.getClock())]
|
[serializeClock(getClock())]
|
||||||
);
|
);
|
||||||
|
|
||||||
await prefs.savePrefs({ resetClock: false });
|
await prefs.savePrefs({ resetClock: false });
|
||||||
|
@ -2236,7 +2252,9 @@ export const lib = {
|
||||||
|
|
||||||
// Expose CRDT mechanisms so server can use them
|
// Expose CRDT mechanisms so server can use them
|
||||||
merkle,
|
merkle,
|
||||||
timestamp,
|
timestamp: {
|
||||||
|
getClock, setClock, makeClock, makeClientId, serializeClock, deserializeClock, Timestamp
|
||||||
|
},
|
||||||
SyncProtoBuf: SyncPb
|
SyncProtoBuf: SyncPb
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import * as prefs from './prefs';
|
||||||
import * as db from './db';
|
import * as db from './db';
|
||||||
import * as budget from './budget/base';
|
import * as budget from './budget/base';
|
||||||
import * as monthUtils from '../shared/months';
|
import * as monthUtils from '../shared/months';
|
||||||
import { getClock, deserializeClock } from './timestamp';
|
import { getClock, deserializeClock } from './crdt';
|
||||||
import {
|
import {
|
||||||
runHandler,
|
runHandler,
|
||||||
runMutator,
|
runMutator,
|
||||||
|
|
|
@ -3,8 +3,7 @@ import {
|
||||||
withMigrationsDir,
|
withMigrationsDir,
|
||||||
getAppliedMigrations,
|
getAppliedMigrations,
|
||||||
getMigrationList,
|
getMigrationList,
|
||||||
getPending,
|
getPending
|
||||||
applyMigration
|
|
||||||
} from './migrations';
|
} from './migrations';
|
||||||
import * as db from '../db';
|
import * as db from '../db';
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import Platform from './platform';
|
import Platform from './platform';
|
||||||
const { PostError, HTTPError } = require('./errors');
|
const { PostError } = require('./errors');
|
||||||
const { fetch } = require('../platform/server/fetch');
|
const { fetch } = require('../platform/server/fetch');
|
||||||
|
|
||||||
function throwIfNot200(res, text) {
|
function throwIfNot200(res, text) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { sendMessages } from './sync';
|
import { sendMessages } from './sync';
|
||||||
import Timestamp from './timestamp';
|
import { Timestamp } from './crdt';
|
||||||
const fs = require('../platform/server/fs');
|
const fs = require('../platform/server/fs');
|
||||||
|
|
||||||
let prefs = null;
|
let prefs = null;
|
||||||
|
|
|
@ -5,8 +5,6 @@ import * as db from '../db';
|
||||||
import * as prefs from '../prefs';
|
import * as prefs from '../prefs';
|
||||||
import { toDateRepr } from '../models';
|
import { toDateRepr } from '../models';
|
||||||
import { runQuery as aqlQuery } from '../aql/schema/run-query';
|
import { runQuery as aqlQuery } from '../aql/schema/run-query';
|
||||||
import { compileQuery } from '../aql/compiler';
|
|
||||||
import { schema, schemaConfig } from '../aql/schema';
|
|
||||||
import { dayFromDate, currentDay, parseDate } from '../../shared/months';
|
import { dayFromDate, currentDay, parseDate } from '../../shared/months';
|
||||||
import q from '../../shared/query';
|
import q from '../../shared/query';
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import * as db from '../db';
|
|
||||||
import { runQuery as aqlQuery } from '../aql/schema/run-query';
|
import { runQuery as aqlQuery } from '../aql/schema/run-query';
|
||||||
import q from '../../shared/query';
|
import q from '../../shared/query';
|
||||||
import { loadRules, updateRule } from '../accounts/transaction-rules';
|
import { loadRules, updateRule } from '../accounts/transaction-rules';
|
||||||
|
@ -6,7 +5,6 @@ import { loadMappings } from '../db/mappings';
|
||||||
import {
|
import {
|
||||||
updateConditions,
|
updateConditions,
|
||||||
getNextDate,
|
getNextDate,
|
||||||
listSchedules,
|
|
||||||
createSchedule,
|
createSchedule,
|
||||||
updateSchedule,
|
updateSchedule,
|
||||||
deleteSchedule,
|
deleteSchedule,
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { runQuery as aqlQuery } from '../aql/schema/run-query';
|
||||||
import q from '../../shared/query';
|
import q from '../../shared/query';
|
||||||
import { getApproxNumberThreshold } from '../../shared/rules';
|
import { getApproxNumberThreshold } from '../../shared/rules';
|
||||||
import { recurConfigToRSchedule } from '../../shared/schedules';
|
import { recurConfigToRSchedule } from '../../shared/schedules';
|
||||||
import { dayFromDate, parseDate, subDays } from '../../shared/months';
|
import { dayFromDate, parseDate } from '../../shared/months';
|
||||||
import { conditionsToAQL } from '../accounts/transaction-rules';
|
import { conditionsToAQL } from '../accounts/transaction-rules';
|
||||||
const uuid = require('../../platform/uuid');
|
const uuid = require('../../platform/uuid');
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import * as nodes from './nodes';
|
import * as nodes from './nodes';
|
||||||
const uuid = require('../../../platform/uuid');
|
|
||||||
|
|
||||||
let _uid = 0;
|
let _uid = 0;
|
||||||
function resetUid() {
|
function resetUid() {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { compile, compileBinding } from './compiler';
|
import { compile } from './compiler';
|
||||||
import { MOV, CALL, QUERY, UOP, BOP, REG1, SP, VAR, JUMPF, JUMPT } from './ops';
|
import { MOV, CALL, QUERY, UOP, BOP, JUMPF, JUMPT } from './ops';
|
||||||
|
|
||||||
export default class VM {
|
export default class VM {
|
||||||
constructor(db, scopes) {
|
constructor(db, scopes) {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { sequential, once } from '../../shared/async';
|
import { sequential, once } from '../../shared/async';
|
||||||
import * as perf from '../perf';
|
|
||||||
import * as prefs from '../prefs';
|
import * as prefs from '../prefs';
|
||||||
import app from '../main-app';
|
import app from '../main-app';
|
||||||
import asyncStorage from '../../platform/server/asyncStorage';
|
import asyncStorage from '../../platform/server/asyncStorage';
|
||||||
|
@ -12,12 +11,13 @@ import { triggerBudgetChanges, setType as setBudgetType } from '../budget/base';
|
||||||
import * as undo from '../undo';
|
import * as undo from '../undo';
|
||||||
import { runMutator } from '../mutators';
|
import { runMutator } from '../mutators';
|
||||||
import { setIn, getIn } from '../../shared/util';
|
import { setIn, getIn } from '../../shared/util';
|
||||||
import Timestamp, {
|
import {
|
||||||
serializeClock,
|
serializeClock,
|
||||||
deserializeClock,
|
deserializeClock,
|
||||||
getClock
|
getClock,
|
||||||
} from '../timestamp';
|
Timestamp,
|
||||||
import * as merkle from '../merkle';
|
merkle
|
||||||
|
} from '../crdt';
|
||||||
import * as encoder from './encoder';
|
import * as encoder from './encoder';
|
||||||
import { getServer } from '../server-config';
|
import { getServer } from '../server-config';
|
||||||
import { rebuildMerkleHash } from './repair';
|
import { rebuildMerkleHash } from './repair';
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { addSyncListener, applyMessages } from './index';
|
import { addSyncListener, applyMessages } from './index';
|
||||||
import Timestamp from '../timestamp';
|
import { Timestamp } from '../crdt';
|
||||||
|
|
||||||
function migrateParentIds(oldValues, newValues) {
|
function migrateParentIds(oldValues, newValues) {
|
||||||
newValues.forEach((items, table) => {
|
newValues.forEach((items, table) => {
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import fc from 'fast-check';
|
import fc from 'fast-check';
|
||||||
import * as db from '../db';
|
import * as db from '../db';
|
||||||
import { migrateParentIds, listen, unlisten } from './migrate';
|
import { listen, unlisten } from './migrate';
|
||||||
import { addSyncListener, batchMessages, sendMessages } from './index';
|
import { addSyncListener, sendMessages } from './index';
|
||||||
import { execTracer } from '../../shared/test-helpers';
|
import { execTracer } from '../../shared/test-helpers';
|
||||||
import { schema, schemaConfig } from '../aql/schema';
|
import { schema, schemaConfig } from '../aql/schema';
|
||||||
import arbs from '../../mocks/arbitrary-schema';
|
import arbs from '../../mocks/arbitrary-schema';
|
||||||
import { convertInputType } from '../aql/schema-helpers';
|
import { convertInputType } from '../aql/schema-helpers';
|
||||||
import { groupBy } from '../../shared/util';
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
listen();
|
listen();
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import * as db from '../db';
|
import * as db from '../db';
|
||||||
import Timestamp, { serializeClock, getClock } from '../timestamp';
|
import { serializeClock, getClock, Timestamp, merkle } from '../crdt';
|
||||||
import * as merkle from '../merkle';
|
|
||||||
|
|
||||||
export function rebuildMerkleHash() {
|
export function rebuildMerkleHash() {
|
||||||
let rows = db.runQuery('SELECT timestamp FROM messages_crdt', [], true);
|
let rows = db.runQuery('SELECT timestamp FROM messages_crdt', [], true);
|
||||||
|
|
|
@ -2,13 +2,10 @@ import * as prefs from '../prefs';
|
||||||
import * as db from '../db';
|
import * as db from '../db';
|
||||||
import * as sheet from '../sheet';
|
import * as sheet from '../sheet';
|
||||||
import * as sync from './index';
|
import * as sync from './index';
|
||||||
import Timestamp, { deserializeClock, getClock } from '../timestamp';
|
import { getClock, Timestamp } from '../crdt';
|
||||||
import * as merkle from '../merkle';
|
import { merkle } from '../crdt';
|
||||||
import * as encoder from './encoder';
|
import * as encoder from './encoder';
|
||||||
const Database = require('better-sqlite3');
|
|
||||||
const fs = require('fs');
|
|
||||||
const jsc = require('jsverify');
|
const jsc = require('jsverify');
|
||||||
const uuid = require('uuid');
|
|
||||||
const uuidGenerator = jsc
|
const uuidGenerator = jsc
|
||||||
.integer(97, 122)
|
.integer(97, 122)
|
||||||
.smap(x => String.fromCharCode(x), x => x.charCodeAt(x));
|
.smap(x => String.fromCharCode(x), x => x.charCodeAt(x));
|
||||||
|
|
|
@ -1,14 +1,9 @@
|
||||||
import * as prefs from '../prefs';
|
import * as prefs from '../prefs';
|
||||||
import * as db from '../db';
|
import * as db from '../db';
|
||||||
import * as sheet from '../sheet';
|
import * as sheet from '../sheet';
|
||||||
import Timestamp, { getClock } from '../timestamp';
|
import { getClock, Timestamp } from '../crdt';
|
||||||
import { resolveName } from '../spreadsheet/util';
|
import { resolveName } from '../spreadsheet/util';
|
||||||
import {
|
import { setSyncingMode, sendMessages, applyMessages, fullSync } from './index';
|
||||||
setSyncingMode,
|
|
||||||
sendMessages,
|
|
||||||
applyMessages,
|
|
||||||
fullSync
|
|
||||||
} from './index';
|
|
||||||
import * as encoder from './encoder';
|
import * as encoder from './encoder';
|
||||||
const mockSyncServer = require('../tests/mockSyncServer');
|
const mockSyncServer = require('../tests/mockSyncServer');
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import dateFns from 'date-fns';
|
import dateFns from 'date-fns';
|
||||||
import * as merkle from '../merkle';
|
import { makeClock, Timestamp, merkle } from '../crdt';
|
||||||
import Timestamp, { makeClock } from '../timestamp';
|
|
||||||
const defaultMockData = require('./mockData').basic;
|
const defaultMockData = require('./mockData').basic;
|
||||||
const SyncPb = require('../sync/proto/sync_pb');
|
const SyncPb = require('../sync/proto/sync_pb');
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { batchMessages } from '../sync';
|
|
||||||
import { runMutator } from '../mutators';
|
import { runMutator } from '../mutators';
|
||||||
import { createApp } from '../app';
|
import { createApp } from '../app';
|
||||||
import * as db from '../db';
|
import * as db from '../db';
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
import asyncStorage from '../../platform/server/asyncStorage';
|
|
||||||
import { sha256String } from '../encryption-internals';
|
import { sha256String } from '../encryption-internals';
|
||||||
let uuid = require('../../platform/uuid');
|
|
||||||
|
|
||||||
let currentUniqueId;
|
let currentUniqueId;
|
||||||
let mixpanel;
|
let mixpanel;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { sendMessages } from './sync';
|
import { sendMessages } from './sync';
|
||||||
import { getIn } from '../shared/util';
|
import { getIn } from '../shared/util';
|
||||||
import Timestamp from './timestamp';
|
import { Timestamp } from './crdt';
|
||||||
import { withMutatorContext, getMutatorContext } from './mutators';
|
import { withMutatorContext, getMutatorContext } from './mutators';
|
||||||
const connection = require('../platform/server/connection');
|
const connection = require('../platform/server/connection');
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { last, diffItems, getChangedValues, applyChanges } from './util';
|
import { last, diffItems, applyChanges } from './util';
|
||||||
const uuid = require('../platform/uuid');
|
const uuid = require('../platform/uuid');
|
||||||
|
|
||||||
// The amount might be null when adding a new transaction
|
// The amount might be null when adding a new transaction
|
||||||
|
|
|
@ -5,7 +5,6 @@ import {
|
||||||
addSplitTransaction,
|
addSplitTransaction,
|
||||||
makeChild
|
makeChild
|
||||||
} from './transactions';
|
} from './transactions';
|
||||||
import * as db from '../server/db';
|
|
||||||
const uuid = require('../platform/uuid');
|
const uuid = require('../platform/uuid');
|
||||||
|
|
||||||
// const data = {
|
// const data = {
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
let currencyFormatter = require('currency-formatter');
|
|
||||||
|
|
||||||
export function cleanUUID(uuid) {
|
export function cleanUUID(uuid) {
|
||||||
return uuid.replace(/-/g, '');
|
return uuid.replace(/-/g, '');
|
||||||
}
|
}
|
||||||
|
@ -267,7 +265,7 @@ export function setNumberFormat(format) {
|
||||||
|
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case 'space-comma':
|
case 'space-comma':
|
||||||
locale = 'za-ZA';
|
locale = 'en-ZA';
|
||||||
regex = /[^-0-9,]/g;
|
regex = /[^-0-9,]/g;
|
||||||
separator = ',';
|
separator = ',';
|
||||||
break;
|
break;
|
||||||
|
@ -286,12 +284,10 @@ export function setNumberFormat(format) {
|
||||||
numberFormat = {
|
numberFormat = {
|
||||||
value: format,
|
value: format,
|
||||||
separator,
|
separator,
|
||||||
// This is the keep in line with the Intl API which we might
|
formatter: new Intl.NumberFormat(locale, {
|
||||||
// switch to when it's available on all mobile platforms
|
minimumFractionDigits: 2,
|
||||||
formatter: {
|
maximumFractionDigits: 2
|
||||||
format: number =>
|
}),
|
||||||
currencyFormatter.format(number, { locale, format: '%v' })
|
|
||||||
},
|
|
||||||
regex
|
regex
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -300,7 +296,7 @@ export function getNumberFormat() {
|
||||||
return numberFormat;
|
return numberFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
setNumberFormat('1,000.33');
|
setNumberFormat('comma-dot');
|
||||||
|
|
||||||
export function toRelaxedNumber(value) {
|
export function toRelaxedNumber(value) {
|
||||||
return integerToAmount(currencyToInteger(value) || 0);
|
return integerToAmount(currencyToInteger(value) || 0);
|
||||||
|
@ -319,10 +315,6 @@ export function amountToCurrency(n) {
|
||||||
return numberFormat.formatter.format(n);
|
return numberFormat.formatter.format(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function amountToPrettyCurrency(n, code) {
|
|
||||||
return currencyFormatter.format(n, { code });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function currencyToAmount(str) {
|
export function currencyToAmount(str) {
|
||||||
let amount = parseFloat(
|
let amount = parseFloat(
|
||||||
str.replace(numberFormat.regex, '').replace(numberFormat.separator, '.')
|
str.replace(numberFormat.regex, '').replace(numberFormat.separator, '.')
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { looselyParseAmount } from './util';
|
import { looselyParseAmount, getNumberFormat, setNumberFormat } from './util';
|
||||||
|
|
||||||
describe('utility functions', () => {
|
describe('utility functions', () => {
|
||||||
test('looseParseAmount works with basic numbers', () => {
|
test('looseParseAmount works with basic numbers', () => {
|
||||||
|
@ -28,4 +28,26 @@ describe('utility functions', () => {
|
||||||
// thought through more.
|
// thought through more.
|
||||||
expect(looselyParseAmount('3_45_23.10')).toBe(34523.1);
|
expect(looselyParseAmount('3_45_23.10')).toBe(34523.1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('number formatting works with comma-dot format', () => {
|
||||||
|
setNumberFormat('comma-dot');
|
||||||
|
const formatter = getNumberFormat().formatter;
|
||||||
|
|
||||||
|
expect(formatter.format('1234.56')).toBe('1,234.56');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('number formatting works with dot-comma format', () => {
|
||||||
|
setNumberFormat('dot-comma');
|
||||||
|
const formatter = getNumberFormat().formatter;
|
||||||
|
|
||||||
|
expect(formatter.format('1234.56')).toBe('1.234,56');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('number formatting works with space-comma format', () => {
|
||||||
|
setNumberFormat('space-comma');
|
||||||
|
const formatter = getNumberFormat().formatter;
|
||||||
|
|
||||||
|
// grouping separator space char is a non-breaking space, or UTF-16 \xa0
|
||||||
|
expect(formatter.format('1234.56')).toBe('1\xa0234,56');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
let path = require('path');
|
|
||||||
let webpack = require('webpack');
|
let webpack = require('webpack');
|
||||||
let config = require('./webpack.desktop.config');
|
let config = require('./webpack.desktop.config');
|
||||||
|
|
||||||
|
|
|
@ -58,5 +58,10 @@ module.exports = {
|
||||||
new webpack.IgnorePlugin({
|
new webpack.IgnorePlugin({
|
||||||
resourceRegExp: /worker_threads|original-fs/
|
resourceRegExp: /worker_threads|original-fs/
|
||||||
})
|
})
|
||||||
]
|
],
|
||||||
|
node: {
|
||||||
|
dgram: "empty",
|
||||||
|
net: 'empty',
|
||||||
|
tls: 'empty',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { View, Text, Select } from './common';
|
import { View, Text, Select } from './common';
|
||||||
import Autocomplete, { defaultFilterSuggestion } from './Autocomplete';
|
import Autocomplete, { defaultFilterSuggestion } from './Autocomplete';
|
||||||
import { styles, colors } from '../style';
|
import { colors } from '../style';
|
||||||
import Split from '../svg/split';
|
import Split from '../svg/split';
|
||||||
|
|
||||||
export const NativeCategorySelect = React.forwardRef(
|
export const NativeCategorySelect = React.forwardRef(
|
||||||
|
|
|
@ -7,10 +7,9 @@ import React, {
|
||||||
useMemo
|
useMemo
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import * as d from 'date-fns';
|
import * as d from 'date-fns';
|
||||||
import { css } from 'glamor';
|
|
||||||
import Pikaday from 'pikaday';
|
import Pikaday from 'pikaday';
|
||||||
import 'pikaday/css/pikaday.css';
|
import 'pikaday/css/pikaday.css';
|
||||||
import { styles, colors } from '../style';
|
import { colors } from '../style';
|
||||||
import { View, Input, Tooltip } from './common';
|
import { View, Input, Tooltip } from './common';
|
||||||
import {
|
import {
|
||||||
getDayMonthFormat,
|
getDayMonthFormat,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useState, useMemo, useRef } from 'react';
|
import React, { useState, useMemo, useRef } from 'react';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
import { getActivePayees } from 'loot-core/src/client/reducers/queries';
|
import { getActivePayees } from 'loot-core/src/client/reducers/queries';
|
||||||
import { createPayee } from 'loot-core/src/client/actions/queries';
|
import { createPayee } from 'loot-core/src/client/actions/queries';
|
||||||
import { useCachedPayees } from 'loot-core/src/client/data-hooks/payees';
|
import { useCachedPayees } from 'loot-core/src/client/data-hooks/payees';
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { useEffect, useReducer, useState } from 'react';
|
import React, { useEffect, useReducer, useState } from 'react';
|
||||||
import * as d from 'date-fns';
|
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { parse as parseDate } from 'date-fns';
|
|
||||||
import { sendCatch } from 'loot-core/src/platform/client/fetch';
|
import { sendCatch } from 'loot-core/src/platform/client/fetch';
|
||||||
import * as monthUtils from 'loot-core/src/shared/months';
|
import * as monthUtils from 'loot-core/src/shared/months';
|
||||||
import { getRecurringDescription } from 'loot-core/src/shared/schedules';
|
import { getRecurringDescription } from 'loot-core/src/shared/schedules';
|
||||||
|
@ -16,7 +13,7 @@ import {
|
||||||
Text,
|
Text,
|
||||||
Stack
|
Stack
|
||||||
} from '../components/common';
|
} from '../components/common';
|
||||||
import { colors, styles } from 'loot-design/src/style';
|
import { colors } from 'loot-design/src/style';
|
||||||
import { useTooltip } from 'loot-design/src/components/tooltips';
|
import { useTooltip } from 'loot-design/src/components/tooltips';
|
||||||
import SubtractIcon from 'loot-design/src/svg/Subtract';
|
import SubtractIcon from 'loot-design/src/svg/Subtract';
|
||||||
import AddIcon from 'loot-design/src/svg/Add';
|
import AddIcon from 'loot-design/src/svg/Add';
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useCallback, useState } from 'react';
|
import React from 'react';
|
||||||
import RecurringSchedulePicker from './RecurringSchedulePicker';
|
import RecurringSchedulePicker from './RecurringSchedulePicker';
|
||||||
import { Section } from '../guide/components';
|
import { Section } from '../guide/components';
|
||||||
import { Button, View } from './common';
|
import { Button, View } from './common';
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useContext, useState, useMemo, useEffect } from 'react';
|
import React, { useContext, useState, useMemo } from 'react';
|
||||||
import ElementQuery from '../ElementQuery';
|
import ElementQuery from '../ElementQuery';
|
||||||
import {
|
import {
|
||||||
View,
|
View,
|
||||||
|
|
|
@ -4,7 +4,6 @@ import React, {
|
||||||
useLayoutEffect,
|
useLayoutEffect,
|
||||||
useState,
|
useState,
|
||||||
useCallback,
|
useCallback,
|
||||||
useMemo
|
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { css } from 'glamor';
|
import { css } from 'glamor';
|
||||||
import mergeRefs from 'react-merge-refs';
|
import mergeRefs from 'react-merge-refs';
|
||||||
|
@ -18,7 +17,6 @@ import {
|
||||||
useRouteMatch
|
useRouteMatch
|
||||||
} from 'react-router-dom';
|
} from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
Listbox,
|
|
||||||
ListboxInput,
|
ListboxInput,
|
||||||
ListboxButton,
|
ListboxButton,
|
||||||
ListboxPopover,
|
ListboxPopover,
|
||||||
|
@ -32,12 +30,11 @@ import Loading from '../svg/v1/AnimatedLoading';
|
||||||
import ExpandArrow from 'loot-design/src/svg/ExpandArrow';
|
import ExpandArrow from 'loot-design/src/svg/ExpandArrow';
|
||||||
import View from './View';
|
import View from './View';
|
||||||
import Text from './Text';
|
import Text from './Text';
|
||||||
import Stack from './Stack';
|
|
||||||
import { useProperFocus } from './useProperFocus';
|
import { useProperFocus } from './useProperFocus';
|
||||||
|
|
||||||
export View from './View';
|
export { default as View } from './View';
|
||||||
export Text from './Text';
|
export { default as Text } from './Text';
|
||||||
export Stack from './Stack';
|
export { default as Stack } from './Stack';
|
||||||
|
|
||||||
export const useStableCallback = callback => {
|
export const useStableCallback = callback => {
|
||||||
const callbackRef = useRef();
|
const callbackRef = useRef();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { View, Text, Modal, Button, ButtonWithLoading } from '../common';
|
import { View, Text, Modal, ButtonWithLoading } from '../common';
|
||||||
import { styles, colors } from '../../style';
|
import { colors } from '../../style';
|
||||||
|
|
||||||
export default function DeleteMenu({ modalProps, actions, file }) {
|
export default function DeleteMenu({ modalProps, actions, file }) {
|
||||||
let [loadingState, setLoadingState] = useState(null);
|
let [loadingState, setLoadingState] = useState(null);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { View, Block, Modal, Button, Link } from '../common';
|
import { View, Block, Modal, Button } from '../common';
|
||||||
import { styles, colors } from '../../style';
|
import { styles, colors } from '../../style';
|
||||||
|
|
||||||
function getErrorMessage(error) {
|
function getErrorMessage(error) {
|
||||||
|
|
|
@ -7,7 +7,6 @@ import {
|
||||||
Modal,
|
Modal,
|
||||||
Button,
|
Button,
|
||||||
ButtonWithLoading,
|
ButtonWithLoading,
|
||||||
Link,
|
|
||||||
P
|
P
|
||||||
} from '../common';
|
} from '../common';
|
||||||
import { styles, colors } from '../../style';
|
import { styles, colors } from '../../style';
|
||||||
|
|
|
@ -7,7 +7,6 @@ import {
|
||||||
Modal,
|
Modal,
|
||||||
ButtonWithLoading,
|
ButtonWithLoading,
|
||||||
Button,
|
Button,
|
||||||
Link,
|
|
||||||
P,
|
P,
|
||||||
ExternalLink
|
ExternalLink
|
||||||
} from '../common';
|
} from '../common';
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
export default function InputAccessoryView() {
|
export default function InputAccessoryView() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { MobileScreen } from '../../guide/components';
|
||||||
import { categories, categoryGroups } from './budget.usage';
|
import { categories, categoryGroups } from './budget.usage';
|
||||||
import { BudgetTable, BudgetAccessoryView } from './budget';
|
import { BudgetTable, BudgetAccessoryView } from './budget';
|
||||||
import InputAccessoryView from './InputAccessoryView';
|
import InputAccessoryView from './InputAccessoryView';
|
||||||
import { debugDOM } from 'loot-core/src/mocks/util';
|
|
||||||
import SpreadsheetContext from '../spreadsheet/SpreadsheetContext';
|
import SpreadsheetContext from '../spreadsheet/SpreadsheetContext';
|
||||||
import * as monthUtils from 'loot-core/src/shared/months';
|
import * as monthUtils from 'loot-core/src/shared/months';
|
||||||
import makeSpreadsheet from 'loot-core/src/mocks/spreadsheet';
|
import makeSpreadsheet from 'loot-core/src/mocks/spreadsheet';
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
import { MobileSection } from '../../guide/components';
|
import { MobileSection } from '../../guide/components';
|
||||||
import { BudgetTable, BudgetHeader, BudgetAccessoryView } from './budget';
|
import { BudgetTable, BudgetAccessoryView } from './budget';
|
||||||
import InputAccessoryView from './InputAccessoryView';
|
import InputAccessoryView from './InputAccessoryView';
|
||||||
import { generateCategoryGroups } from 'loot-core/src/mocks';
|
import { generateCategoryGroups } from 'loot-core/src/mocks';
|
||||||
import SpreadsheetContext from '../spreadsheet/SpreadsheetContext';
|
import SpreadsheetContext from '../spreadsheet/SpreadsheetContext';
|
||||||
|
|
|
@ -22,7 +22,7 @@ import {
|
||||||
deleteTransaction,
|
deleteTransaction,
|
||||||
realizeTempTransactions
|
realizeTempTransactions
|
||||||
} from 'loot-core/src/shared/transactions';
|
} from 'loot-core/src/shared/transactions';
|
||||||
import { applyChanges, titleFirst } from 'loot-core/src/shared/util';
|
import { titleFirst } from 'loot-core/src/shared/util';
|
||||||
import {
|
import {
|
||||||
integerToCurrency,
|
integerToCurrency,
|
||||||
integerToAmount,
|
integerToAmount,
|
||||||
|
@ -48,9 +48,6 @@ import {
|
||||||
|
|
||||||
import EditSkull1 from '../../svg/v2/EditSkull1';
|
import EditSkull1 from '../../svg/v2/EditSkull1';
|
||||||
import AlertTriangle from '../../svg/v2/AlertTriangle';
|
import AlertTriangle from '../../svg/v2/AlertTriangle';
|
||||||
import CalendarIcon from '../../svg/v2/Calendar';
|
|
||||||
import ValidationCheck from '../../svg/v2/ValidationCheck';
|
|
||||||
import FavoriteStar from '../../svg/v2/FavoriteStar';
|
|
||||||
import CheckCircle1 from '../../svg/v2/CheckCircle1';
|
import CheckCircle1 from '../../svg/v2/CheckCircle1';
|
||||||
import ArrowsSynchronize from 'loot-design/src/svg/v2/ArrowsSynchronize';
|
import ArrowsSynchronize from 'loot-design/src/svg/v2/ArrowsSynchronize';
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@ import {
|
||||||
P,
|
P,
|
||||||
Select,
|
Select,
|
||||||
FormError,
|
FormError,
|
||||||
AnchorLink
|
|
||||||
} from '../common';
|
} from '../common';
|
||||||
import { integerToCurrency } from 'loot-core/src/shared/util';
|
import { integerToCurrency } from 'loot-core/src/shared/util';
|
||||||
import { colors } from '../../style';
|
import { colors } from '../../style';
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { toRelaxedNumber } from 'loot-core/src/shared/util';
|
import { toRelaxedNumber } from 'loot-core/src/shared/util';
|
||||||
import { determineOffBudget } from 'loot-core/src/shared/accounts';
|
import { determineOffBudget } from 'loot-core/src/shared/accounts';
|
||||||
import { styles, colors } from '../../style';
|
|
||||||
import {
|
import {
|
||||||
View,
|
View,
|
||||||
Modal,
|
Modal,
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { parseISO, format as formatDate, parse as parseDate } from 'date-fns';
|
||||||
import * as actions from 'loot-core/src/client/actions';
|
import * as actions from 'loot-core/src/client/actions';
|
||||||
import { amountToInteger } from 'loot-core/src/shared/util';
|
import { amountToInteger } from 'loot-core/src/shared/util';
|
||||||
import { currentDay, dayFromDate } from 'loot-core/src/shared/months';
|
import { currentDay, dayFromDate } from 'loot-core/src/shared/months';
|
||||||
import { View, Modal, Stack, Button, Input } from '../common';
|
import { View, Modal, Input } from '../common';
|
||||||
import DateSelect from '../DateSelect';
|
import DateSelect from '../DateSelect';
|
||||||
import CategoryAutocomplete from '../CategorySelect';
|
import CategoryAutocomplete from '../CategorySelect';
|
||||||
import AccountAutocomplete from '../AccountAutocomplete';
|
import AccountAutocomplete from '../AccountAutocomplete';
|
||||||
|
|
|
@ -19,7 +19,7 @@ import {
|
||||||
} from '../common';
|
} from '../common';
|
||||||
import { Checkbox } from '../forms';
|
import { Checkbox } from '../forms';
|
||||||
import { TableHeader, TableWithNavigator, Row, Field } from '../table';
|
import { TableHeader, TableWithNavigator, Row, Field } from '../table';
|
||||||
import { SectionLabel, FieldLabel } from '../forms';
|
import { SectionLabel } from '../forms';
|
||||||
import { colors, styles } from '../../style';
|
import { colors, styles } from '../../style';
|
||||||
|
|
||||||
let dateFormats = [
|
let dateFormats = [
|
||||||
|
@ -31,7 +31,7 @@ let dateFormats = [
|
||||||
{ format: 'dd mm yy', label: 'DD MM YY' }
|
{ format: 'dd mm yy', label: 'DD MM YY' }
|
||||||
];
|
];
|
||||||
|
|
||||||
function parseDate(str, order) {
|
export function parseDate(str, order) {
|
||||||
if (typeof str !== 'string') {
|
if (typeof str !== 'string') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -40,37 +40,48 @@ function parseDate(str, order) {
|
||||||
return v && v.length === 1 ? '0' + v : v;
|
return v && v.length === 1 ? '0' + v : v;
|
||||||
}
|
}
|
||||||
|
|
||||||
let parts = str.replace(/ /g, '').split(/[^0-9]/);
|
const dateGroups = (a, b) => str => {
|
||||||
|
const digits = str.replace(/[^\d]/g, '');
|
||||||
|
return [digits.slice(0, a), digits.slice(a, a + b), digits.slice(a + b)];
|
||||||
|
};
|
||||||
|
const yearFirst = dateGroups(4, 2);
|
||||||
|
const twoDig = dateGroups(2, 2);
|
||||||
|
|
||||||
let year, month, day;
|
let parts, year, month, day;
|
||||||
switch (order) {
|
switch (order) {
|
||||||
case 'dd mm yyyy':
|
case 'dd mm yyyy':
|
||||||
|
parts = twoDig(str);
|
||||||
year = parts[2];
|
year = parts[2];
|
||||||
month = parts[1];
|
month = parts[1];
|
||||||
day = parts[0];
|
day = parts[0];
|
||||||
break;
|
break;
|
||||||
case 'dd mm yy':
|
case 'dd mm yy':
|
||||||
|
parts = twoDig(str);
|
||||||
year = `20${parts[2]}`;
|
year = `20${parts[2]}`;
|
||||||
month = parts[1];
|
month = parts[1];
|
||||||
day = parts[0];
|
day = parts[0];
|
||||||
break;
|
break;
|
||||||
case 'yyyy mm dd':
|
case 'yyyy mm dd':
|
||||||
|
parts = yearFirst(str);
|
||||||
year = parts[0];
|
year = parts[0];
|
||||||
month = parts[1];
|
month = parts[1];
|
||||||
day = parts[2];
|
day = parts[2];
|
||||||
break;
|
break;
|
||||||
case 'yy mm dd':
|
case 'yy mm dd':
|
||||||
|
parts = twoDig(str);
|
||||||
year = `20${parts[0]}`;
|
year = `20${parts[0]}`;
|
||||||
month = parts[1];
|
month = parts[1];
|
||||||
day = parts[2];
|
day = parts[2];
|
||||||
break;
|
break;
|
||||||
case 'mm dd yy':
|
case 'mm dd yy':
|
||||||
|
parts = twoDig(str);
|
||||||
year = `20${parts[2]}`;
|
year = `20${parts[2]}`;
|
||||||
month = parts[0];
|
month = parts[0];
|
||||||
day = parts[1];
|
day = parts[1];
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
case 'mm dd yyyy':
|
case 'mm dd yyyy':
|
||||||
|
parts = twoDig(str);
|
||||||
year = parts[2];
|
year = parts[2];
|
||||||
month = parts[0];
|
month = parts[0];
|
||||||
day = parts[1];
|
day = parts[1];
|
||||||
|
@ -336,10 +347,7 @@ function DateFormatSelect({
|
||||||
// try to figure out what delimiter the date is using, and default
|
// try to figure out what delimiter the date is using, and default
|
||||||
// to space if we can't figure it out.
|
// to space if we can't figure it out.
|
||||||
let delimiter = '-';
|
let delimiter = '-';
|
||||||
if (
|
if (transactions.length > 0 && fieldMappings && fieldMappings.date != null) {
|
||||||
transactions.length > 0 &&
|
|
||||||
(fieldMappings && fieldMappings.date != null)
|
|
||||||
) {
|
|
||||||
let date = transactions[0][fieldMappings.date];
|
let date = transactions[0][fieldMappings.date];
|
||||||
let m = date && date.match(/[/.,-/\\]/);
|
let m = date && date.match(/[/.,-/\\]/);
|
||||||
delimiter = m ? m[0] : ' ';
|
delimiter = m ? m[0] : ' ';
|
||||||
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
import { parseDate } from './ImportTransactions';
|
||||||
|
|
||||||
|
describe('Import transactions', function() {
|
||||||
|
describe('date parsing', function() {
|
||||||
|
it('should not parse', function() {
|
||||||
|
const invalidInputs = [
|
||||||
|
{ str: '', order: 'yyyy mm dd' },
|
||||||
|
{ str: null, order: 'yyyy mm dd' },
|
||||||
|
{ str: 42, order: 'yyyy mm dd' },
|
||||||
|
{ str: {}, order: 'yyyy mm dd' },
|
||||||
|
{ str: [], order: 'yyyy mm dd' },
|
||||||
|
{ str: 'invalid', order: 'yyyy mm dd' },
|
||||||
|
{ str: '2020 Dec 24', order: 'yyyy mm dd' },
|
||||||
|
{ str: '12 24 20', order: 'mm dd yyyy' },
|
||||||
|
{ str: '20 12 24', order: 'yyyy mm dd' },
|
||||||
|
{ str: '2020 12 24', order: 'yy mm dd' },
|
||||||
|
{ str: '12 24 2020', order: 'mm dd yy' },
|
||||||
|
{ str: '12 00 2020', order: 'mm dd yyyy' },
|
||||||
|
{ str: '12 32 2020', order: 'mm dd yyyy' },
|
||||||
|
{ str: '13 24 2020', order: 'mm dd yyyy' },
|
||||||
|
{ str: '00 24 2020', order: 'mm dd yyyy' },
|
||||||
|
{ str: '02 30 2020', order: 'mm dd yyyy' },
|
||||||
|
{ str: '04 31 2020', order: 'mm dd yyyy' },
|
||||||
|
{ str: '04 31 2020', order: 'mm dd yyyy' },
|
||||||
|
{ str: '06 31 2020', order: 'mm dd yyyy' },
|
||||||
|
{ str: '09 31 2020', order: 'mm dd yyyy' },
|
||||||
|
{ str: '11 31 2020', order: 'mm dd yyyy' },
|
||||||
|
{ str: '2046 31 2020', order: 'mm dd yyyy' },
|
||||||
|
{ str: '2011 31 2020', order: 'mm dd yy' },
|
||||||
|
{ str: '2020', order: 'mm dd yy' }
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const { str, order } of invalidInputs) {
|
||||||
|
expect(parseDate(str, order)).toBe(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse', function() {
|
||||||
|
const validInputs = [
|
||||||
|
{
|
||||||
|
order: 'yyyy mm dd',
|
||||||
|
cases: [
|
||||||
|
'20201224',
|
||||||
|
'2020 12 24',
|
||||||
|
'2020-12-24',
|
||||||
|
'2020/12/24',
|
||||||
|
' 2020 / 12 / 24',
|
||||||
|
'2020/12/24 ',
|
||||||
|
'2020 12-24 '
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
order: 'yy mm dd',
|
||||||
|
cases: [
|
||||||
|
'201224',
|
||||||
|
'20 12 24',
|
||||||
|
'20-12-24',
|
||||||
|
'20/12/24',
|
||||||
|
'20/12/24',
|
||||||
|
'20/12/24 ',
|
||||||
|
'20 12-24 '
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
order: 'mm dd yyyy',
|
||||||
|
cases: [
|
||||||
|
'12242020',
|
||||||
|
'12 24 2020 ',
|
||||||
|
'12-24-2020',
|
||||||
|
'12/24/2020',
|
||||||
|
' 12/24/2020',
|
||||||
|
'12/24/2020',
|
||||||
|
'12 24-2020'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
order: 'mm dd yy',
|
||||||
|
cases: [
|
||||||
|
'122420',
|
||||||
|
'12 24 20 ',
|
||||||
|
'12-24-20',
|
||||||
|
'12/24/20',
|
||||||
|
' 12/24/20',
|
||||||
|
'12/24/20',
|
||||||
|
'12 24-20'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
order: 'dd mm yyyy',
|
||||||
|
cases: [
|
||||||
|
'24122020',
|
||||||
|
'24 12 2020 ',
|
||||||
|
'24-12-2020',
|
||||||
|
'24/12/2020',
|
||||||
|
' 24/12/2020',
|
||||||
|
'24/12/2020 ',
|
||||||
|
'24-12 2020'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
order: 'dd mm yy',
|
||||||
|
cases: [
|
||||||
|
'241220',
|
||||||
|
'2412 20 ',
|
||||||
|
'24-12-20',
|
||||||
|
'24/12/20',
|
||||||
|
' 24/12/20',
|
||||||
|
'24/12/20 ',
|
||||||
|
'24 12-20 '
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const { order, cases } of validInputs) {
|
||||||
|
for (const str of cases) {
|
||||||
|
const parsed = parseDate(str, order);
|
||||||
|
expect(typeof parsed).toBe('string');
|
||||||
|
expect(parsed).toBe('2020-12-24');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { View, Text, Block, Modal, Button } from '../common';
|
import { View, Text, Block, Modal, Button } from '../common';
|
||||||
import { Row, Cell, DeleteCell } from '../table';
|
import { Row, Cell } from '../table';
|
||||||
import { styles, colors } from '../../style';
|
import { colors } from '../../style';
|
||||||
|
|
||||||
class BackupTable extends React.Component {
|
class BackupTable extends React.Component {
|
||||||
state = { hoveredBackup: null };
|
state = { hoveredBackup: null };
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { styles, colors } from '../../style';
|
import { styles, colors } from '../../style';
|
||||||
import { View, Text, Modal, P, Button } from '../common';
|
import { View, Text, Modal, P, Button } from '../common';
|
||||||
import { amountToPrettyCurrency } from 'loot-core/src/shared/util';
|
|
||||||
import {
|
import {
|
||||||
fromPlaidAccountType,
|
fromPlaidAccountType,
|
||||||
prettyAccountType
|
prettyAccountType
|
||||||
|
|
|
@ -8,7 +8,6 @@ import makeSpreadsheet from 'loot-core/src/mocks/spreadsheet';
|
||||||
import { Sidebar } from './sidebar';
|
import { Sidebar } from './sidebar';
|
||||||
import { Section } from '../guide/components';
|
import { Section } from '../guide/components';
|
||||||
import { generateAccount } from 'loot-core/src/mocks';
|
import { generateAccount } from 'loot-core/src/mocks';
|
||||||
import { colors } from '../style';
|
|
||||||
|
|
||||||
function withState(state, render) {
|
function withState(state, render) {
|
||||||
const Component = lively(render, { getInitialState: () => state });
|
const Component = lively(render, { getInitialState: () => state });
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue