Compare commits

...

6 commits

Author SHA1 Message Date
Sung Won Cho
d9c041d545 Bump 2019-11-23 15:23:58 +08:00
Sung Won Cho
7f1a17de05 Extract common dev dep 2019-11-23 15:20:39 +08:00
Sung Won Cho
140c4df958 Fix 2019-11-23 15:16:48 +08:00
Sung Won Cho
427948dcc3 Remove package-lock.json 2019-11-23 15:02:58 +08:00
Sung Won Cho
f3211625ab Use yarn workspace 2019-11-23 14:57:29 +08:00
Sung Won Cho
2e855ad8cc Implement lint in all js projects 2019-11-23 14:57:29 +08:00
117 changed files with 12281 additions and 32567 deletions

View file

@ -2,7 +2,7 @@
"env": { "env": {
"browser": true, "browser": true,
"node": true, "node": true,
"mocha": true "jest": true
}, },
"parser": "@typescript-eslint/parser", "parser": "@typescript-eslint/parser",
"rules": { "rules": {
@ -20,6 +20,7 @@
"indent": [2, 2, {"SwitchCase": 1}], "indent": [2, 2, {"SwitchCase": 1}],
"no-console": 0, "no-console": 0,
"no-alert": 0, "no-alert": 0,
"no-shadow": 2,
"arrow-body-style": 0, "arrow-body-style": 0,
"react/prop-types": 0, "react/prop-types": 0,
"react/jsx-filename-extension": 0, "react/jsx-filename-extension": 0,
@ -40,18 +41,15 @@
"@typescript-eslint/no-unused-vars": 1, "@typescript-eslint/no-unused-vars": 1,
"import/no-extraneous-dependencies": ["error", {"devDependencies": ["**/*_test.ts"]}], "import/no-extraneous-dependencies": ["error", {"devDependencies": ["**/*_test.ts"]}],
"lines-between-class-members": 0, "lines-between-class-members": 0,
"react/jsx-fragments": 0 "react/jsx-fragments": 0,
"jsx-a11y/label-has-associated-control": 0,
"no-empty": 0
}, },
"plugins": [ "plugins": [
"react", "react-hooks", "import", "prettier", "@typescript-eslint" "react", "react-hooks", "import", "prettier", "@typescript-eslint"
], ],
"settings": {
"import/parser": "babel-eslint",
"import/resolve": {
"moduleDirectory": ["node_modules", "src"]
}
},
"globals": { "globals": {
// web
"__DEVELOPMENT__": true, "__DEVELOPMENT__": true,
"__PRODUCTION__": true, "__PRODUCTION__": true,
"__DISABLE_SSR__": true, "__DISABLE_SSR__": true,
@ -64,6 +62,13 @@
"__CDN_URL__": true, "__CDN_URL__": true,
"socket": true, "socket": true,
"webpackIsomorphicTools": true, "webpackIsomorphicTools": true,
"StripeCheckout": true "StripeCheckout": true,
// browser
"browser": true,
"chrome": true,
__WEB_URL__: true,
__API_ENDPOINT__: true,
__VERSION__: true
} }
} }

1
.gitignore vendored
View file

@ -1,4 +1,5 @@
/vendor /vendor
/build /build
/node_modules
.vagrant .vagrant
*.log *.log

View file

@ -6,6 +6,7 @@ go:
env: env:
- NODE_VERSION=10.15.0 - NODE_VERSION=10.15.0
YARN_VERSION=1.19.1-1
before_install: before_install:
- sudo apt-get update - sudo apt-get update
@ -14,16 +15,25 @@ before_install:
- sudo cp /etc/postgresql/{9.6,11}/main/pg_hba.conf - sudo cp /etc/postgresql/{9.6,11}/main/pg_hba.conf
- sudo service postgresql restart 11 - sudo service postgresql restart 11
before_script: # install yarn
- sudo apt-key adv --fetch-keys http://dl.yarnpkg.com/debian/pubkey.gpg
- echo "deb http://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
- sudo apt-get update -qq
- sudo apt-get install -y -qq yarn="$YARN_VERSION"
- nvm install "$NODE_VERSION" - nvm install "$NODE_VERSION"
- nvm use "$NODE_VERSION" - nvm use "$NODE_VERSION"
- node --version - node --version
- psql -c "CREATE DATABASE dnote_test;" -U postgres - psql -c "CREATE DATABASE dnote_test;" -U postgres
cache:
yarn: true
install: install:
- make install - make install
script: script:
- make lint
- make test-cli - make test-cli
- make test-api - make test-api
- make test-web - make test-web

View file

@ -1,5 +1,5 @@
PACKR2 := $(shell command -v packr2 2> /dev/null) PACKR2 := $(shell command -v packr2 2> /dev/null)
NPM := $(shell command -v npm 2> /dev/null) YARN := $(shell command -v yarn 2> /dev/null)
HUB := $(shell command -v hub 2> /dev/null) HUB := $(shell command -v hub 2> /dev/null)
currentDir = $(shell pwd) currentDir = $(shell pwd)
@ -22,23 +22,25 @@ endif
.PHONY: install-go .PHONY: install-go
install-js: install-js:
ifndef NPM ifndef YARN
$(error npm is not installed) $(error yarn is not installed)
endif endif
@echo "==> installing js dependencies" @echo "==> installing js dependencies"
ifeq ($(CI), true) ifeq ($(CI), true)
@(cd ${currentDir}/web && npm install --unsafe-perm=true) @(cd ${currentDir} && yarn --unsafe-perm=true)
@(cd ${currentDir}/browser && npm install --unsafe-perm=true)
@(cd ${currentDir}/jslib && npm install --unsafe-perm=true)
else else
@(cd ${currentDir}/web && npm install) @(cd ${currentDir} && yarn)
@(cd ${currentDir}/browser && npm install)
@(cd ${currentDir}/jslib && npm install)
endif endif
.PHONY: install-js .PHONY: install-js
lint:
@(cd ${currentDir}/web && yarn lint)
@(cd ${currentDir}/jslib && yarn lint)
@(cd ${currentDir}/browser && yarn lint)
.PHONY: lint
## test ## test
test: test-cli test-api test-web test-jslib test: test-cli test-api test-web test-jslib
.PHONY: test .PHONY: test
@ -57,9 +59,9 @@ test-web:
@echo "==> running web test" @echo "==> running web test"
ifeq ($(WATCH), true) ifeq ($(WATCH), true)
@(cd ${currentDir}/web && npm run test:watch) @(cd ${currentDir}/web && yarn test:watch)
else else
@(cd ${currentDir}/web && npm run test) @(cd ${currentDir}/web && yarn test)
endif endif
.PHONY: test-web .PHONY: test-web
@ -67,9 +69,9 @@ test-jslib:
@echo "==> running jslib test" @echo "==> running jslib test"
ifeq ($(WATCH), true) ifeq ($(WATCH), true)
@(cd ${currentDir}/jslib && npm run test:watch) @(cd ${currentDir}/jslib && yarn test:watch)
else else
@(cd ${currentDir}/jslib && npm run test) @(cd ${currentDir}/jslib && yarn test)
endif endif
.PHONY: test-jslib .PHONY: test-jslib

View file

@ -1,16 +0,0 @@
{ "extends": ["eslint-config-airbnb"],
"env": {
"browser": true,
"node": true,
"mocha": true
},
"parser": "@typescript-eslint/parser",
"rules": {
"@typescript-eslint/no-unused-vars": 1,
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
},
"plugins": [
"react-hooks", "@typescript-eslint"
],
}

View file

@ -4,7 +4,7 @@ Use the following commands to set up, build, and release.
## Set up ## Set up
* `npm install` to install dependencies. * Run `npm install-js` from the monorepo root.
## Developing locally ## Developing locally

View file

@ -8,7 +8,7 @@ All releases are tagged and pushed to [the GitHub repository](https://github.com
To reproduce the obfuscated code for Firefox, please follow the steps below. To reproduce the obfuscated code for Firefox, please follow the steps below.
1. Run `npm install` to install dependencies 1. From the monorepo project root, run `make install-js` to install dependencies
2. Run `./scripts/build_prod.sh` to build for Firefox and Chrome. 2. Run `./scripts/build_prod.sh` to build for Firefox and Chrome.
The obfuscated code will be under `/dist/firefox` and `/dist/chrome`. The obfuscated code will be under `/dist/firefox` and `/dist/chrome`.

9695
browser/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -8,7 +8,8 @@
"package:chrome": "TARGET=chrome NODE_ENV=production gulp package", "package:chrome": "TARGET=chrome NODE_ENV=production gulp package",
"package:firefox": "TARGET=firefox NODE_ENV=production gulp package", "package:firefox": "TARGET=firefox NODE_ENV=production gulp package",
"watch:chrome": "TARGET=chrome NODE_ENV=development concurrently \"webpack --watch\" \"gulp watch\" ", "watch:chrome": "TARGET=chrome NODE_ENV=development concurrently \"webpack --watch\" \"gulp watch\" ",
"watch:firefox": "TARGET=firefox NODE_ENV=development concurrently \"webpack --watch\" \"gulp watch\" " "watch:firefox": "TARGET=firefox NODE_ENV=development concurrently \"webpack --watch\" \"gulp watch\" ",
"lint": "eslint ./src --ext .ts,.tsx,.js"
}, },
"author": "Monomax Software Pty Ltd", "author": "Monomax Software Pty Ltd",
"license": "GPL-3.0-or-later", "license": "GPL-3.0-or-later",
@ -26,24 +27,17 @@
"redux-thunk": "^2.2.0" "redux-thunk": "^2.2.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/preset-env": "^7.7.4",
"@types/react": "^16.9.11", "@types/react": "^16.9.11",
"@types/react-dom": "^16.9.3", "@types/react-dom": "^16.9.3",
"@typescript-eslint/eslint-plugin": "^2.6.0",
"@typescript-eslint/parser": "^2.6.0",
"concurrently": "^5.0.0", "concurrently": "^5.0.0",
"del": "^5.0.0", "del": "^5.0.0",
"eslint-config-airbnb": "^18.0.1",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-react": "^7.16.0",
"eslint-plugin-react-hooks": "^2.2.0",
"gulp": "^4.0.0", "gulp": "^4.0.0",
"gulp-if": "^3.0.0", "gulp-if": "^3.0.0",
"gulp-imagemin": "^6.1.1", "gulp-imagemin": "^6.1.1",
"gulp-livereload": "^4.0.2", "gulp-livereload": "^4.0.2",
"gulp-replace": "^1.0.0", "gulp-replace": "^1.0.0",
"gulp-zip": "^5.0.1", "gulp-zip": "^5.0.1",
"prettier": "^1.18.2",
"ts-loader": "^6.2.1", "ts-loader": "^6.2.1",
"typescript": "^3.6.4", "typescript": "^3.6.4",
"webpack": "^4.41.2", "webpack": "^4.41.2",

View file

@ -4,11 +4,11 @@
set -eux set -eux
# clean # clean
npm run clean yarn clean
# chrome # chrome
npm run build:chrome yarn build:chrome
npm run package:chrome yarn package:chrome
# firefox # firefox
npm run build:firefox yarn build:firefox
npm run package:firefox yarn package:firefox

View file

@ -17,5 +17,5 @@
*/ */
// browser.d.ts // browser.d.ts
declare var browser: any; declare let browser: any;
declare var chrome: any; declare let chrome: any;

View file

@ -19,6 +19,6 @@
// global.d.ts // global.d.ts
// defined by webpack-define-plugin // defined by webpack-define-plugin
declare var __API_ENDPOINT__: string; declare let __API_ENDPOINT__: string;
declare var __WEB_URL__: string; declare let __WEB_URL__: string;
declare var __VERSION__: string; declare let __VERSION__: string;

View file

@ -17,7 +17,6 @@
*/ */
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import classnames from 'classnames';
import initServices from '../utils/services'; import initServices from '../utils/services';
import { logout } from '../store/auth/actions'; import { logout } from '../store/auth/actions';
@ -69,13 +68,11 @@ const App: React.FunctionComponent<Props> = () => {
const [errMsg, setErrMsg] = useState(''); const [errMsg, setErrMsg] = useState('');
const dispatch = useDispatch(); const dispatch = useDispatch();
const { path, auth, settings } = useSelector(state => { const { path, auth, settings } = useSelector(state => ({
return { path: state.location.path,
path: state.location.path, auth: state.auth,
auth: state.auth, settings: state.settings
settings: state.settings }));
};
});
useCheckSessionValid(auth); useCheckSessionValid(auth);

View file

@ -16,23 +16,19 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import React, { useEffect } from 'react'; import React from 'react';
import CreatableSelect from 'react-select/creatable'; import CreatableSelect from 'react-select/creatable';
import cloneDeep from 'lodash/cloneDeep'; import cloneDeep from 'lodash/cloneDeep';
import { useSelector, useDispatch } from '../store/hooks'; import { useSelector, useDispatch } from '../store/hooks';
import { updateBook, resetBook } from '../store/composer/actions'; import { updateBook, resetBook } from '../store/composer/actions';
import BookIcon from './BookIcon';
interface Props { interface Props {
selectorRef: React.Dispatch<any>; selectorRef: React.Dispatch<any>;
onAfterChange: () => void; onAfterChange: () => void;
} }
function useCurrentOptions(options) { function useCurrentOptions(options) {
const currentValue = useSelector(state => { const currentValue = useSelector(state => state.composer.bookUUID);
return state.composer.bookUUID;
});
for (let i = 0; i < options.length; i++) { for (let i = 0; i < options.length; i++) {
const option = options[i]; const option = options[i];
@ -46,19 +42,15 @@ function useCurrentOptions(options) {
} }
function useOptions() { function useOptions() {
const { books, composer } = useSelector(state => { const { books, composer } = useSelector(state => ({
return { books: state.books,
books: state.books, composer: state.composer
composer: state.composer }));
};
});
const opts = books.items.map(book => { const opts = books.items.map(book => ({
return { label: book.label,
label: book.label, value: book.uuid
value: book.uuid }));
};
});
if (composer.bookLabel !== '' && composer.bookUUID === '') { if (composer.bookLabel !== '' && composer.bookUUID === '') {
opts.push({ opts.push({
@ -77,12 +69,9 @@ const BookSelector: React.FunctionComponent<Props> = ({
onAfterChange onAfterChange
}) => { }) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const { books, composer } = useSelector(state => { const { books } = useSelector(state => ({
return { books: state.books
books: state.books, }));
composer: state.composer
};
});
const options = useOptions(); const options = useOptions();
const currentOption = useCurrentOptions(options); const currentOption = useCurrentOptions(options);

View file

@ -16,7 +16,7 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect, useCallback } from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import { KEYCODE_ENTER } from 'jslib/helpers/keyboard'; import { KEYCODE_ENTER } from 'jslib/helpers/keyboard';
@ -34,20 +34,20 @@ interface Props {}
// It needs to traverse the tree returned by the ref API of the 'react-select' library, // It needs to traverse the tree returned by the ref API of the 'react-select' library,
// and to guard against possible breaking changes, if the path does not exist, it noops. // and to guard against possible breaking changes, if the path does not exist, it noops.
function focusBookSelectorInput(bookSelectorRef) { function focusBookSelectorInput(bookSelectorRef) {
bookSelectorRef.select && return (
bookSelectorRef.select &&
bookSelectorRef.select.select && bookSelectorRef.select.select &&
bookSelectorRef.select.select.inputRef && bookSelectorRef.select.select.inputRef &&
bookSelectorRef.select.select.inputRef.focus(); bookSelectorRef.select.select.inputRef.focus()
);
} }
function useFetchData() { function useFetchData() {
const dispatch = useDispatch(); const dispatch = useDispatch();
const { books } = useSelector(state => { const { books } = useSelector(state => ({
return { books: state.books
books: state.books }));
};
});
useEffect(() => { useEffect(() => {
if (!books.isFetched) { if (!books.isFetched) {
@ -57,12 +57,10 @@ function useFetchData() {
} }
function useInitFocus(contentRef, bookSelectorRef) { function useInitFocus(contentRef, bookSelectorRef) {
const { composer, books } = useSelector(state => { const { composer, books } = useSelector(state => ({
return { composer: state.composer,
composer: state.composer, books: state.books
books: state.books }));
};
});
useEffect(() => { useEffect(() => {
if (!books.isFetched) { if (!books.isFetched) {
@ -76,7 +74,9 @@ function useInitFocus(contentRef, bookSelectorRef) {
contentRef.focus(); contentRef.focus();
} }
} }
}, [contentRef, bookSelectorRef, books.isFetched]);
return () => null;
}, [contentRef, bookSelectorRef, books.isFetched, composer.bookLabel]);
} }
const Composer: React.FunctionComponent<Props> = () => { const Composer: React.FunctionComponent<Props> = () => {
@ -88,27 +88,43 @@ const Composer: React.FunctionComponent<Props> = () => {
const [contentRef, setContentEl] = useState(null); const [contentRef, setContentEl] = useState(null);
const [bookSelectorRef, setBookSelectorEl] = useState(null); const [bookSelectorRef, setBookSelectorEl] = useState(null);
const { composer, settings, auth } = useSelector(state => { const { composer, settings, auth } = useSelector(state => ({
return { composer: state.composer,
composer: state.composer, settings: state.settings,
settings: state.settings, auth: state.auth
auth: state.auth }));
};
});
const handleSubmit = async e => { const handleSubmit = useCallback(
e.preventDefault(); async e => {
e.preventDefault();
const services = initServices(settings.apiUrl); const services = initServices(settings.apiUrl);
setSubmitting(true); setSubmitting(true);
try { try {
let bookUUID; let bookUUID;
if (composer.bookUUID === '') { if (composer.bookUUID === '') {
const resp = await services.books.create( const resp = await services.books.create(
{
name: composer.bookLabel
},
{
headers: {
Authorization: `Bearer ${auth.sessionKey}`
}
}
);
bookUUID = resp.book.uuid;
} else {
bookUUID = composer.bookUUID;
}
const resp = await services.notes.create(
{ {
name: composer.bookLabel book_uuid: bookUUID,
content: composer.content
}, },
{ {
headers: { headers: {
@ -117,56 +133,48 @@ const Composer: React.FunctionComponent<Props> = () => {
} }
); );
bookUUID = resp.book.uuid; // clear the composer state
} else { setErrMsg('');
bookUUID = composer.bookUUID; setSubmitting(false);
dispatch(resetComposer());
// navigate
dispatch(
navigate('/success', {
bookName: composer.bookLabel,
noteUUID: resp.result.uuid
})
);
} catch (err) {
setErrMsg(err.message);
setSubmitting(false);
} }
},
const resp = await services.notes.create( [
{ settings.apiUrl,
book_uuid: bookUUID, composer.bookUUID,
content: composer.content composer.content,
}, composer.bookLabel,
{ auth.sessionKey,
headers: { dispatch
Authorization: `Bearer ${auth.sessionKey}` ]
} );
}
);
// clear the composer state
setErrMsg('');
setSubmitting(false);
dispatch(resetComposer());
// navigate
dispatch(
navigate('/success', {
bookName: composer.bookLabel,
noteUUID: resp.result.uuid
})
);
} catch (e) {
setErrMsg(e.message);
setSubmitting(false);
}
};
const handleSubmitShortcut = e => {
// Shift + Enter
if (e.shiftKey && e.keyCode === KEYCODE_ENTER) {
handleSubmit(e);
}
};
useEffect(() => { useEffect(() => {
const handleSubmitShortcut = e => {
// Shift + Enter
if (e.shiftKey && e.keyCode === KEYCODE_ENTER) {
handleSubmit(e);
}
};
window.addEventListener('keydown', handleSubmitShortcut); window.addEventListener('keydown', handleSubmitShortcut);
return () => { return () => {
window.removeEventListener('keydown', handleSubmitShortcut); window.removeEventListener('keydown', handleSubmitShortcut);
}; };
}, [composer]); }, [composer, handleSubmit]);
let submitBtnText: string; let submitBtnText: string;
if (submitting) { if (submitting) {

View file

@ -17,15 +17,9 @@
*/ */
import React, { useState } from 'react'; import React, { useState } from 'react';
import { connect } from 'react-redux';
import { findDOMNode } from 'react-dom';
import Link from './Link';
import config from '../utils/config';
import { login } from '../store/auth/actions'; import { login } from '../store/auth/actions';
import { updateSettings } from '../store/settings/actions';
import { useDispatch } from '../store/hooks'; import { useDispatch } from '../store/hooks';
import services from '../utils/services';
import Flash from '../components/Flash'; import Flash from '../components/Flash';
interface Props {} interface Props {}
@ -45,8 +39,8 @@ const Home: React.FunctionComponent<Props> = () => {
try { try {
await dispatch(login({ email, password })); await dispatch(login({ email, password }));
} catch (e) { } catch (err) {
console.log('error while logging in', e); console.log('error while logging in', err);
setErrMsg(e.message); setErrMsg(e.message);
setLoggingIn(false); setLoggingIn(false);
@ -97,10 +91,11 @@ const Home: React.FunctionComponent<Props> = () => {
</form> </form>
<div className="actions"> <div className="actions">
Don't have an account?{' '} Don&#39;t have an account?{' '}
<a <a
href="https://app.getdnote.com/join" href="https://app.getdnote.com/join"
target="_blank" target="_blank"
rel="noopener noreferrer"
className="signup" className="signup"
> >
Sign Up Sign Up

View file

@ -16,9 +16,9 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
/* eslint-disable react/jsx-props-no-spreading */
import React from 'react'; import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { useDispatch } from '../store/hooks'; import { useDispatch } from '../store/hooks';
import { navigate } from '../store/location/actions'; import { navigate } from '../store/location/actions';

View file

@ -53,6 +53,11 @@ export default ({ toggleMenu, loggedIn, onLogout }) => (
)} )}
</ul> </ul>
<div className="menu-overlay" onClick={toggleMenu} /> <div
className="menu-overlay"
onClick={toggleMenu}
onKeyDown={() => {}}
role="none"
/>
</Fragment> </Fragment>
); );

View file

@ -17,23 +17,18 @@
*/ */
import React, { useState } from 'react'; import React, { useState } from 'react';
import { connect } from 'react-redux';
import { findDOMNode } from 'react-dom';
import Link from './Link';
import Flash from './Flash'; import Flash from './Flash';
import config from '../utils/config';
import { updateSettings, resetSettings } from '../store/settings/actions'; import { updateSettings, resetSettings } from '../store/settings/actions';
import { useDispatch, useSelector, useStore } from '../store/hooks'; import { useDispatch, useSelector, useStore } from '../store/hooks';
import services from '../utils/services';
interface Props {} interface Props {}
// isValidURL checks if the given string is a valid URL // isValidURL checks if the given string is a valid URL
function isValidURL(url: string): boolean { function isValidURL(url: string): boolean {
var a = document.createElement('a'); const a = document.createElement('a');
a.href = url; a.href = url;
return a.host && a.host != window.location.host; return a.host && a.host !== window.location.host;
} }
// validateFormState validates the given form state. If any input is // validateFormState validates the given form state. If any input is
@ -49,11 +44,9 @@ function validateFormState({ apiUrl, webUrl }) {
} }
const Settings: React.FunctionComponent<Props> = () => { const Settings: React.FunctionComponent<Props> = () => {
const { settings } = useSelector(state => { const { settings } = useSelector(state => ({
return { settings: state.settings
settings: state.settings }));
};
});
const store = useStore(); const store = useStore();
const [apiUrl, setAPIUrl] = useState(settings.apiUrl); const [apiUrl, setAPIUrl] = useState(settings.apiUrl);
@ -66,10 +59,10 @@ const Settings: React.FunctionComponent<Props> = () => {
dispatch(resetSettings()); dispatch(resetSettings());
setSuccessMsg('Restored the default settings'); setSuccessMsg('Restored the default settings');
const { settings } = store.getState(); const { settings: settingsState } = store.getState();
setAPIUrl(settings.apiUrl); setAPIUrl(settingsState.apiUrl);
setWebUrl(settings.webUrl); setWebUrl(settingsState.webUrl);
} }
function handleSubmit(e) { function handleSubmit(e) {

View file

@ -16,7 +16,7 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import React, { Fragment, useEffect, useState } from 'react'; import React, { useEffect, useState, Fragment } from 'react';
import { import {
KEYCODE_ENTER, KEYCODE_ENTER,
@ -25,7 +25,6 @@ import {
} from 'jslib/helpers/keyboard'; } from 'jslib/helpers/keyboard';
import Flash from './Flash'; import Flash from './Flash';
import ext from '../utils/ext'; import ext from '../utils/ext';
import config from '../utils/config';
import BookIcon from './BookIcon'; import BookIcon from './BookIcon';
import { navigate } from '../store/location/actions'; import { navigate } from '../store/location/actions';
import { useSelector, useDispatch } from '../store/hooks'; import { useSelector, useDispatch } from '../store/hooks';
@ -34,43 +33,41 @@ const Success: React.FunctionComponent = () => {
const [errorMsg, setErrorMsg] = useState(''); const [errorMsg, setErrorMsg] = useState('');
const dispatch = useDispatch(); const dispatch = useDispatch();
const { location, settings } = useSelector(state => { const { location, settings } = useSelector(state => ({
return { location: state.location,
location: state.location, settings: state.settings
settings: state.settings }));
};
});
const { bookName, noteUUID } = location.state; const { bookName, noteUUID } = location.state;
const handleKeydown = e => {
e.preventDefault();
if (e.keyCode === KEYCODE_ENTER) {
dispatch(navigate('/'));
} else if (e.keyCode === KEYCODE_ESC) {
window.close();
} else if (e.keyCode === KEYCODE_LOWERCASE_B) {
const url = `${settings.webUrl}/notes/${noteUUID}`;
ext.tabs
.create({ url })
.then(() => {
window.close();
})
.catch(err => {
setErrorMsg(err.message);
});
}
};
useEffect(() => { useEffect(() => {
const handleKeydown = e => {
e.preventDefault();
if (e.keyCode === KEYCODE_ENTER) {
dispatch(navigate('/'));
} else if (e.keyCode === KEYCODE_ESC) {
window.close();
} else if (e.keyCode === KEYCODE_LOWERCASE_B) {
const url = `${settings.webUrl}/notes/${noteUUID}`;
ext.tabs
.create({ url })
.then(() => {
window.close();
})
.catch(err => {
setErrorMsg(err.message);
});
}
};
window.addEventListener('keydown', handleKeydown); window.addEventListener('keydown', handleKeydown);
return () => { return () => {
window.removeEventListener('keydown', handleKeydown); window.removeEventListener('keydown', handleKeydown);
}; };
}, []); }, [dispatch, noteUUID, settings.webUrl]);
return ( return (
<Fragment> <Fragment>
@ -80,7 +77,10 @@ const Success: React.FunctionComponent = () => {
<div> <div>
<BookIcon width={20} height={20} className="book-icon" /> <BookIcon width={20} height={20} className="book-icon" />
<h1 className="heading">Saved to {bookName}</h1> <h1 className="heading">
Saved to
{bookName}
</h1>
</div> </div>
<ul className="key-list"> <ul className="key-list">
@ -93,7 +93,8 @@ const Success: React.FunctionComponent = () => {
<div className="key-desc">Open in browser</div> <div className="key-desc">Open in browser</div>
</li> </li>
<li className="key-item"> <li className="key-item">
<kbd className="key">ESC</kbd> <div className="key-desc">Close</div> <kbd className="key">ESC</kbd>
<div className="key-desc">Close</div>
</li> </li>
</ul> </ul>
</div> </div>

View file

@ -19,8 +19,6 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import { createStore, applyMiddleware, compose } from 'redux';
import thunkMiddleware from 'redux-thunk';
import { debounce } from 'jslib/helpers/perf'; import { debounce } from 'jslib/helpers/perf';
import configureStore from './store'; import configureStore from './store';

View file

@ -16,7 +16,7 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { LOGIN, LOGOUT, LogoutAction, LoginAction } from './types'; import { LOGIN, LOGOUT, LogoutAction } from './types';
import { ThunkAction } from '../types'; import { ThunkAction } from '../types';
import initServices from '../../utils/services'; import initServices from '../../utils/services';

View file

@ -17,7 +17,6 @@
*/ */
import { LOGIN, LOGOUT, AuthState, AuthActionType } from './types'; import { LOGIN, LOGOUT, AuthState, AuthActionType } from './types';
import config from '../../utils/config';
const initialState: AuthState = { const initialState: AuthState = {
sessionKey: '', sessionKey: '',
@ -34,8 +33,8 @@ export default function(
return { return {
...state, ...state,
sessionKey: sessionKey, sessionKey,
sessionKeyExpiry: sessionKeyExpiry sessionKeyExpiry
}; };
} }
case LOGOUT: case LOGOUT:

View file

@ -43,14 +43,14 @@ function initState(s: AppState | undefined): AppState {
return undefined; return undefined;
} }
const { settings } = s; const { settings: settingsState } = s;
return { return {
...s, ...s,
settings: { settings: {
...settings, ...settingsState,
apiUrl: settings.apiUrl || config.defaultApiEndpoint, apiUrl: settingsState.apiUrl || config.defaultApiEndpoint,
webUrl: settings.webUrl || config.defaultWebUrl webUrl: settingsState.webUrl || config.defaultWebUrl
} }
}; };
} }

View file

@ -17,8 +17,6 @@
*/ */
import { UPDATE, RESET, UpdateAction, ResetAction } from './types'; import { UPDATE, RESET, UpdateAction, ResetAction } from './types';
import { ThunkAction } from '../types';
import initServices from '../../utils/services';
export function updateSettings(settings): UpdateAction { export function updateSettings(settings): UpdateAction {
return { return {

View file

@ -18,7 +18,7 @@
// module ext provides a cross-browser interface to access extension APIs // module ext provides a cross-browser interface to access extension APIs
// by using WebExtensions API if available, and using Chrome as a fallback. // by using WebExtensions API if available, and using Chrome as a fallback.
let ext: any = {}; const ext: any = {};
const apis = ['tabs', 'storage', 'runtime']; const apis = ['tabs', 'storage', 'runtime'];
@ -40,9 +40,9 @@ for (let i = 0; i < apis.length; i++) {
const fn = ext[api].create; const fn = ext[api].create;
// Promisify chrome.tabs.create // Promisify chrome.tabs.create
ext[api].create = function(obj) { ext[api].create = obj => {
return new Promise(resolve => { return new Promise(resolve => {
fn(obj, function(tab) { fn(obj, tab => {
resolve(tab); resolve(tab);
}); });
}); });

View file

@ -22,7 +22,7 @@ function checkStatus(response) {
if (response.status >= 200 && response.status < 300) { if (response.status >= 200 && response.status < 300) {
return response; return response;
} }
return response.text().then((body) => { return response.text().then(body => {
const error = new Error(body); const error = new Error(body);
error.response = response; error.response = response;
@ -48,7 +48,7 @@ export function post(url, data, options = {}) {
return request(url, { return request(url, {
method: 'POST', method: 'POST',
body: JSON.stringify(data), body: JSON.stringify(data),
...options, ...options
}); });
} }
@ -61,6 +61,6 @@ export function get(url, options = {}) {
return request(endpoint, { return request(endpoint, {
method: 'GET', method: 'GET',
...options, ...options
}); });
} }

View file

@ -17,13 +17,11 @@
*/ */
import init from 'jslib/services'; import init from 'jslib/services';
import config from './config';
const initServices = (baseUrl: string) => { const initServices = (baseUrl: string) =>
return init({ init({
baseUrl: baseUrl, baseUrl,
pathPrefix: '' pathPrefix: ''
}); });
};
export default initServices; export default initServices;

View file

@ -1,13 +1,13 @@
{ {
"compilerOptions": { "compilerOptions": {
"sourceMap": true, "sourceMap": true,
"esModuleInterop": true,
"noImplicitAny": false, "noImplicitAny": false,
"module": "es6", "module": "commonjs",
"moduleResolution": "node", "moduleResolution": "node",
"target": "es6",
"jsx": "react", "jsx": "react",
"esModuleInterop": true,
"allowJs": true, "allowJs": true,
"target": "es5",
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"jslib/*": [ "jslib/*": [

9
go.mod
View file

@ -7,6 +7,8 @@ require (
github.com/aymerick/douceur v0.2.0 github.com/aymerick/douceur v0.2.0
github.com/dnote/actions v0.2.0 github.com/dnote/actions v0.2.0
github.com/dnote/color v1.7.0 github.com/dnote/color v1.7.0
github.com/gobuffalo/envy v1.8.1 // indirect
github.com/gobuffalo/logger v1.0.2 // indirect
github.com/gobuffalo/packr v1.30.1 // indirect github.com/gobuffalo/packr v1.30.1 // indirect
github.com/gobuffalo/packr/v2 v2.7.1 github.com/gobuffalo/packr/v2 v2.7.1
github.com/google/go-cmp v0.3.1 github.com/google/go-cmp v0.3.1
@ -26,14 +28,17 @@ require (
github.com/pkg/errors v0.8.1 github.com/pkg/errors v0.8.1
github.com/radovskyb/watcher v1.0.7 github.com/radovskyb/watcher v1.0.7
github.com/robfig/cron v1.2.0 github.com/robfig/cron v1.2.0
github.com/rogpeppe/go-internal v1.5.0 // indirect
github.com/rubenv/sql-migrate v0.0.0-20190618074426-f4d34eae5a5c github.com/rubenv/sql-migrate v0.0.0-20190618074426-f4d34eae5a5c
github.com/sergi/go-diff v1.0.0 github.com/sergi/go-diff v1.0.0
github.com/spf13/cobra v0.0.5 github.com/spf13/cobra v0.0.5
github.com/spf13/pflag v1.0.5 // indirect
github.com/stripe/stripe-go v61.7.1+incompatible github.com/stripe/stripe-go v61.7.1+incompatible
github.com/ziutek/mymysql v1.5.4 // indirect github.com/ziutek/mymysql v1.5.4 // indirect
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4 golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 // indirect golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e // indirect
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4
golang.org/x/tools v0.0.0-20191122232904-2a6ccf25d769 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/gomail.v2 v2.0.0-20150902115704-41f357289737 gopkg.in/gomail.v2 v2.0.0-20150902115704-41f357289737
gopkg.in/gorp.v1 v1.7.2 // indirect gopkg.in/gorp.v1 v1.7.2 // indirect

16
go.sum
View file

@ -44,9 +44,13 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
github.com/gobuffalo/envy v1.7.1 h1:OQl5ys5MBea7OGCdvPbBJWRgnhC/fGona6QKfvFeau8= github.com/gobuffalo/envy v1.7.1 h1:OQl5ys5MBea7OGCdvPbBJWRgnhC/fGona6QKfvFeau8=
github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w= github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w=
github.com/gobuffalo/envy v1.8.1 h1:RUr68liRvs0TS1D5qdW3mQv2SjAsu1QWMCx1tG4kDjs=
github.com/gobuffalo/envy v1.8.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w=
github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
github.com/gobuffalo/logger v1.0.1 h1:ZEgyRGgAm4ZAhAO45YXMs5Fp+bzGLESFewzAVBMKuTg= github.com/gobuffalo/logger v1.0.1 h1:ZEgyRGgAm4ZAhAO45YXMs5Fp+bzGLESFewzAVBMKuTg=
github.com/gobuffalo/logger v1.0.1/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= github.com/gobuffalo/logger v1.0.1/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
github.com/gobuffalo/logger v1.0.2 h1:C07Fb1g3P0CVQxTlqFSKyM2T/VIwHTjIVFzkxdYAI4Y=
github.com/gobuffalo/logger v1.0.2/go.mod h1:3Fdhr3hXXXumcpR83wrlHIMbCn/AGNhDYjrDyYDbixc=
github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4= github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4=
github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q= github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg= github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=
@ -151,6 +155,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.4.0 h1:LUa41nrWTQNGhzdsZ5lTnkwbNjj6rXTdazA1cSdjkOY= github.com/rogpeppe/go-internal v1.4.0 h1:LUa41nrWTQNGhzdsZ5lTnkwbNjj6rXTdazA1cSdjkOY=
github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.5.0 h1:Usqs0/lDK/NqTkvrmKSwA/3XkZAs7ZAW/eLeQ2MVBTw=
github.com/rogpeppe/go-internal v1.5.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rubenv/sql-migrate v0.0.0-20190618074426-f4d34eae5a5c h1:LCELEbde3/GT141OpHRs+jJZrI1tI3ayVd4VqW7Ui2U= github.com/rubenv/sql-migrate v0.0.0-20190618074426-f4d34eae5a5c h1:LCELEbde3/GT141OpHRs+jJZrI1tI3ayVd4VqW7Ui2U=
github.com/rubenv/sql-migrate v0.0.0-20190618074426-f4d34eae5a5c/go.mod h1:WS0rl9eEliYI8DPnr3TOwz4439pay+qNgzJoVya/DmY= github.com/rubenv/sql-migrate v0.0.0-20190618074426-f4d34eae5a5c/go.mod h1:WS0rl9eEliYI8DPnr3TOwz4439pay+qNgzJoVya/DmY=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
@ -166,6 +172,8 @@ github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tL
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -186,6 +194,9 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4 h1:ydJNl0ENAG67pFbB+9tfhiL2pYqLhfoaZFw/cjLhY4A= golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4 h1:ydJNl0ENAG67pFbB+9tfhiL2pYqLhfoaZFw/cjLhY4A=
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191119213627-4f8c1d86b1ba/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c h1:/nJuwDLoL/zrqY6gf57vxC+Pi+pZ8bfhpPkicO5H7W4=
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@ -224,6 +235,8 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e h1:N7DeIrjYszNmSW409R3frPPwglRwMkXSBzwVbkOjLLA=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -235,7 +248,10 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3 h1:2AmBLzhAfXj+2HCW09VCkJtHIYgHTIPcTeYqgP7Bwt0=
golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191122232904-2a6ccf25d769 h1:nIPDpirk90v9eLG0L8usrehSoJ1rWd6wX7BdjAKhZ4I=
golang.org/x/tools v0.0.0-20191122232904-2a6ccf25d769/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=

4947
jslib/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -8,7 +8,8 @@
"build": "tsc", "build": "tsc",
"build:watch": "tsc --watch", "build:watch": "tsc --watch",
"test": "jest --coverage", "test": "jest --coverage",
"test:watch": "jest --watch" "test:watch": "jest --watch",
"lint": "eslint ./src --ext .ts,.tsx,.js"
}, },
"author": "Monomax Software Pty Ltd", "author": "Monomax Software Pty Ltd",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
@ -20,9 +21,7 @@
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^24.0.23", "@types/jest": "^24.0.23",
"@types/mocha": "^5.2.7",
"jest": "^24.9.0", "jest": "^24.9.0",
"prettier": "^1.19.1",
"ts-jest": "^24.1.0", "ts-jest": "^24.1.0",
"typescript": "^3.7.2" "typescript": "^3.7.2"
} }

View file

@ -33,5 +33,5 @@ export {
QueriesHelpers, QueriesHelpers,
SearchHelpers, SearchHelpers,
SelectHelpers, SelectHelpers,
UrlHelpers, UrlHelpers
} };

View file

@ -16,10 +16,6 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Location } from 'history';
import { parseSearchString } from './url';
import { removeKey } from './obj';
import * as searchLib from './search'; import * as searchLib from './search';
export interface Queries { export interface Queries {

View file

@ -19,7 +19,4 @@
import * as Helpers from './helpers'; import * as Helpers from './helpers';
import * as Operations from './helpers'; import * as Operations from './helpers';
export { export { Helpers, Operations };
Helpers,
Operations
};

22
package.json Normal file
View file

@ -0,0 +1,22 @@
{
"private": true,
"workspaces": [
"jslib",
"web",
"browser"
],
"devDependencies": {
"prettier": "^1.19.1",
"eslint": "^6.7.0",
"eslint-config-airbnb": "^18.0.1",
"eslint-config-prettier": "^6.7.0",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-prettier": "^3.1.1",
"eslint-plugin-react": "^7.16.0",
"eslint-plugin-react-hooks": "^2.3.0",
"@typescript-eslint/eslint-plugin": "^2.8.0",
"@typescript-eslint/parser": "^2.8.0",
"babel-eslint": "^10.0.3"
}
}

View file

@ -56,9 +56,9 @@ agpl="/* Copyright (C) 2019 Monomax Software Pty Ltd
*/" */"
dir=$(dirname "${BASH_SOURCE[0]}") dir=$(dirname "${BASH_SOURCE[0]}")
pkgPath="$dir/pkg" pkgPath="$dir/../pkg"
serverPath="$dir/pkg/server" serverPath="$dir/../pkg/server"
browserPath="$dir/browser" browserPath="$dir/../browser"
gplFiles=$(find "$pkgPath" "$browserPath" -type f \( -name "*.go" -o -name "*.js" -o -name "*.ts" -o -name "*.tsx" -o -name "*.scss" -o -name "*.css" \) ! -path "**/vendor/*" ! -path "**/node_modules/*" ! -path "$serverPath/*") gplFiles=$(find "$pkgPath" "$browserPath" -type f \( -name "*.go" -o -name "*.js" -o -name "*.ts" -o -name "*.tsx" -o -name "*.scss" -o -name "*.css" \) ! -path "**/vendor/*" ! -path "**/node_modules/*" ! -path "$serverPath/*")

View file

@ -1,6 +1,8 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -eux set -eux
YARN_VERSION=1.19.1-1
sudo apt-get update sudo apt-get update
sudo apt-get install -y htop git wget build-essential inotify-tools sudo apt-get install -y htop git wget build-essential inotify-tools
@ -9,3 +11,8 @@ wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-ke
echo 'deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main' | sudo tee /etc/apt/sources.list.d/google-chrome.list echo 'deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main' | sudo tee /etc/apt/sources.list.d/google-chrome.list
sudo apt-get -y update sudo apt-get -y update
sudo apt-get install -y google-chrome-stable sudo apt-get install -y google-chrome-stable
# Install yarn
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt update && sudo apt install -y yarn="$YARN_VERSION"

17313
web/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -7,7 +7,7 @@
"scripts": { "scripts": {
"test": "jest --coverage", "test": "jest --coverage",
"test:watch": "jest --watch", "test:watch": "jest --watch",
"lint": "eslint ./src/**/*.ts ./src/**/*.tsx" "lint": "eslint ./src --ext .ts,.tsx,.js"
}, },
"author": "Monomax Software Pty Ltd", "author": "Monomax Software Pty Ltd",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
@ -22,28 +22,16 @@
"@types/jest": "^24.0.23", "@types/jest": "^24.0.23",
"@types/react": "^16.9.11", "@types/react": "^16.9.11",
"@types/react-dom": "^16.9.4", "@types/react-dom": "^16.9.4",
"@typescript-eslint/eslint-plugin": "^2.8.0",
"@typescript-eslint/parser": "^2.8.0",
"autoprefixer": "^9.7.2", "autoprefixer": "^9.7.2",
"babel-eslint": "^10.0.3",
"babel-loader": "^8.0.6", "babel-loader": "^8.0.6",
"css-loader": "^3.2.0", "css-loader": "^3.2.0",
"cssnano": "^4.1.10", "cssnano": "^4.1.10",
"eslint": "^6.6.0",
"eslint-config-airbnb": "^18.0.1",
"eslint-config-prettier": "^6.7.0",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-prettier": "^3.1.1",
"eslint-plugin-react": "^7.16.0",
"eslint-plugin-react-hooks": "^2.3.0",
"fibers": "^4.0.2", "fibers": "^4.0.2",
"file-loader": "^4.3.0", "file-loader": "^4.3.0",
"jest": "^24.9.0", "jest": "^24.9.0",
"mini-css-extract-plugin": "^0.8.0", "mini-css-extract-plugin": "^0.8.0",
"node-sass": "^4.13.0", "node-sass": "^4.13.0",
"postcss-loader": "^3.0.0", "postcss-loader": "^3.0.0",
"prettier": "^1.19.1",
"sass-loader": "^8.0.0", "sass-loader": "^8.0.0",
"source-map-loader": "^0.2.4", "source-map-loader": "^0.2.4",
"source-map-support": "^0.5.16", "source-map-support": "^0.5.16",

View file

@ -16,42 +16,41 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import React, { useState, useEffect, Fragment } from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import { hot } from 'react-hot-loader/root'; import React, { Fragment, useEffect, useState } from 'react';
import { Switch, Route } from 'react-router';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import { Location } from 'history'; import { Location } from 'history';
import { getFiltersFromSearchStr } from 'jslib/helpers/filters';
import { hot } from 'react-hot-loader/root';
import { Route, Switch } from 'react-router';
import { RouteComponentProps, withRouter } from 'react-router-dom';
import { usePrevious } from 'web/libs/hooks'; import { usePrevious } from 'web/libs/hooks';
import { import {
homePathDef, checkCurrentPath,
notePathDef,
noHeaderPaths,
subscriptionPaths,
noFooterPaths,
checkCurrentPathIn, checkCurrentPathIn,
checkCurrentPath homePathDef,
noFooterPaths,
noHeaderPaths,
notePathDef,
subscriptionPaths
} from 'web/libs/paths'; } from 'web/libs/paths';
import { getFiltersFromSearchStr } from 'jslib/helpers/filters'; import render from '../../routes';
import Splash from '../Splash'; import { useDispatch, useSelector } from '../../store';
import { getCurrentUser } from '../../store/auth'; import { getCurrentUser } from '../../store/auth';
import { getBooks } from '../../store/books'; import { getBooks } from '../../store/books';
import { updatePage, updateQuery } from '../../store/filters';
import { setPrevLocation } from '../../store/route'; import { setPrevLocation } from '../../store/route';
import { unsetMessage } from '../../store/ui'; import { unsetMessage } from '../../store/ui';
import { useDispatch, useSelector } from '../../store';
import HeaderData from './HeaderData';
import render from '../../routes';
import NoteHeader from '../Header/Note';
import NormalHeader from '../Header/Normal';
import SubscriptionHeader from '../Header/SubscriptionHeader';
import TabBar from '../TabBar';
import SystemMessage from '../Common/SystemMessage';
import MobileMenu from '../Common/MobileMenu'; import MobileMenu from '../Common/MobileMenu';
import styles from './App.scss'; import SystemMessage from '../Common/SystemMessage';
import { updateQuery, updatePage } from '../../store/filters'; import NormalHeader from '../Header/Normal';
import NoteHeader from '../Header/Note';
import SubscriptionHeader from '../Header/SubscriptionHeader';
import Splash from '../Splash';
import TabBar from '../TabBar';
import './App.global.scss'; import './App.global.scss';
import styles from './App.scss';
import HeaderData from './HeaderData';
interface Props extends RouteComponentProps<any> {} interface Props extends RouteComponentProps<any> {}
@ -198,7 +197,7 @@ const App: React.FunctionComponent<Props> = ({ location }) => {
<Route path={noFooterPaths} exact component={null} /> <Route path={noFooterPaths} exact component={null} />
<Route <Route
path="/" path="/"
render={({ location }) => { render={() => {
if (noFooter) { if (noFooter) {
return null; return null;
} }

View file

@ -16,11 +16,9 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import React, { useState, useRef } from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import React, { useRef, useState } from 'react';
import ItemActions from '../../Common/ItemActions'; import ItemActions from '../../Common/ItemActions';
import DotsIcon from '../../Icons/Dots';
import ItemActionsStyles from '../../Common/ItemActions/ItemActions.scss'; import ItemActionsStyles from '../../Common/ItemActions/ItemActions.scss';
import styles from './Actions.scss'; import styles from './Actions.scss';

View file

@ -21,8 +21,8 @@ import classnames from 'classnames';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { getHomePath } from 'web/libs/paths'; import { getHomePath } from 'web/libs/paths';
import Actions from './Actions';
import { BookData } from 'jslib/operations/types'; import { BookData } from 'jslib/operations/types';
import Actions from './Actions';
import styles from './BookItem.scss'; import styles from './BookItem.scss';

View file

@ -19,9 +19,9 @@
import React, { Fragment } from 'react'; import React, { Fragment } from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import { BookData } from 'jslib/operations/types';
import BookItem from './BookItem'; import BookItem from './BookItem';
import BookHolder from './BookHolder'; import BookHolder from './BookHolder';
import { BookData } from 'jslib/operations/types';
import styles from './BookList.scss'; import styles from './BookList.scss';
function Placeholder() { function Placeholder() {

View file

@ -34,10 +34,8 @@ import CreateBookModal from './CreateBookModal';
import BookList from './BookList'; import BookList from './BookList';
import EmptyList from './EmptyList'; import EmptyList from './EmptyList';
import SearchInput from '../Common/SearchInput'; import SearchInput from '../Common/SearchInput';
import Button from '../Common/Button';
import DeleteBookModal from './DeleteBookModal'; import DeleteBookModal from './DeleteBookModal';
import { usePrevious } from '../../libs/hooks'; import { usePrevious } from '../../libs/hooks';
import BookPlusIcon from '../Icons/BookPlus';
import CreateBookButton from './CreateBookButton'; import CreateBookButton from './CreateBookButton';
import styles from './Content.scss'; import styles from './Content.scss';
@ -91,14 +89,17 @@ function useFocusInputOnReset(
inputRef.current.focus(); inputRef.current.focus();
} }
} }
}, [searchValue, inputRef]); }, [searchValue, inputRef, prevSearchValue]);
} }
interface Props extends RouteComponentProps { interface Props extends RouteComponentProps {
setSuccessMessage: (string) => void; setSuccessMessage: (string) => void;
} }
const Content: React.FunctionComponent<Props> = ({ history, setSuccessMessage }) => { const Content: React.FunctionComponent<Props> = ({
history,
setSuccessMessage
}) => {
const { books } = useSelector(state => { const { books } = useSelector(state => {
return { return {
books: state.books books: state.books

View file

@ -16,8 +16,8 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import React, { Fragment, useState, useEffect, useRef } from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import React from 'react';
import BookPlusIcon from '../Icons/BookPlus'; import BookPlusIcon from '../Icons/BookPlus';
import styles from './CreateBookButton.scss'; import styles from './CreateBookButton.scss';

View file

@ -20,12 +20,12 @@ import React, { useState } from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import { withRouter, RouteComponentProps } from 'react-router-dom'; import { withRouter, RouteComponentProps } from 'react-router-dom';
import { checkDuplicate, validateBookName } from 'jslib/helpers/books';
import Modal, { Header, Body } from '../Common/Modal'; import Modal, { Header, Body } from '../Common/Modal';
import { createBook } from '../../store/books'; import { createBook } from '../../store/books';
import { useSelector, useDispatch } from '../../store'; import { useSelector, useDispatch } from '../../store';
import Button from '../Common/Button'; import Button from '../Common/Button';
import Flash from '../Common/Flash'; import Flash from '../Common/Flash';
import { checkDuplicate, validateBookName } from 'jslib/helpers/books';
import styles from './CreateBookModal.scss'; import styles from './CreateBookModal.scss';

View file

@ -20,17 +20,17 @@ import React from 'react';
import { Switch, Route } from 'react-router'; import { Switch, Route } from 'react-router';
import { Redirect } from 'react-router-dom'; import { Redirect } from 'react-router-dom';
import { useDispatch, useSelector } from '../../store';
import ClassicLogin from './Login';
import { setMessage } from '../../store/ui';
import ClassicSetPassword from './SetPassword';
import ClassicDecrypt from './Decrypt';
import { import {
ClassicMigrationSteps, ClassicMigrationSteps,
getClassicMigrationPath, getClassicMigrationPath,
getHomePath, getHomePath,
homePathDef homePathDef
} from 'web/libs/paths'; } from 'web/libs/paths';
import { useDispatch, useSelector } from '../../store';
import ClassicLogin from './Login';
import { setMessage } from '../../store/ui';
import ClassicSetPassword from './SetPassword';
import ClassicDecrypt from './Decrypt';
interface Props {} interface Props {}

View file

@ -16,6 +16,9 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
// TODO: refactor to enforce the following rule
/* eslint-disable react/jsx-props-no-spreading */
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import classnames from 'classnames'; import classnames from 'classnames';

View file

@ -16,14 +16,13 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import React, { useState } from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import { KEYCODE_ENTER } from 'jslib/helpers/keyboard'; import { KEYCODE_ENTER } from 'jslib/helpers/keyboard';
import React, { useState } from 'react';
import { useDispatch } from '../../../store';
import { flushContent } from '../../../store/editor'; import { flushContent } from '../../../store/editor';
import { AppState, useDispatch } from '../../../store';
import styles from './Textarea.scss';
import editorStyles from './Editor.scss'; import editorStyles from './Editor.scss';
import styles from './Textarea.scss';
interface Props { interface Props {
sessionKey: string; sessionKey: string;

View file

@ -16,9 +16,8 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import React, { useState, useRef } from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import React from 'react';
import Menu, { MenuOption } from '../../Common/Menu'; import Menu, { MenuOption } from '../../Common/Menu';
import DotsIcon from '../../Icons/Dots'; import DotsIcon from '../../Icons/Dots';
import styles from './ItemActions.scss'; import styles from './ItemActions.scss';
@ -59,8 +58,8 @@ const ItemActions: React.FunctionComponent<Props> = ({
menuId={id} menuId={id}
triggerId={triggerId} triggerId={triggerId}
triggerContent={<DotsIcon width={12} height={12} />} triggerContent={<DotsIcon width={12} height={12} />}
triggerClassName={styles['trigger']} triggerClassName={styles.trigger}
contentClassName={styles['content']} contentClassName={styles.content}
alignment="right" alignment="right"
direction="bottom" direction="bottom"
/> />

View file

@ -28,7 +28,11 @@ interface Props {
onDismiss: () => void; onDismiss: () => void;
} }
const Header: React.FunctionComponent<Props> = ({ labelId, heading, onDismiss }) => { const Header: React.FunctionComponent<Props> = ({
labelId,
heading,
onDismiss
}) => {
return ( return (
<div className={styles.wrapper}> <div className={styles.wrapper}>
<strong id={labelId}>{heading}</strong> <strong id={labelId}>{heading}</strong>

View file

@ -16,16 +16,14 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import React, { useState, useRef, useEffect } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import { booksToOptions, filterOptions, Option } from 'jslib/helpers/select';
import { KEYCODE_BACKSPACE } from 'jslib/helpers/keyboard'; import { KEYCODE_BACKSPACE } from 'jslib/helpers/keyboard';
import { useSearchMenuKeydown, useScrollToFocused } from 'web/libs/hooks/dom'; import { filterOptions, Option } from 'jslib/helpers/select';
import { useSelector } from '../../store'; import { useScrollToFocused, useSearchMenuKeydown } from 'web/libs/hooks/dom';
import PopoverContent from '../Common/Popover/PopoverContent'; import PopoverContent from '../Common/Popover/PopoverContent';
import CloseIcon from '../Icons/Close'; import CloseIcon from '../Icons/Close';
import { usePrevious } from 'web/libs/hooks';
import styles from './MultiSelect.scss'; import styles from './MultiSelect.scss';
function getTextInputWidth(term: string, active: boolean) { function getTextInputWidth(term: string, active: boolean) {
@ -140,6 +138,8 @@ const MultiSelect: React.FunctionComponent<Props> = ({
'form-select-disabled': disabled 'form-select-disabled': disabled
})} })}
ref={wrapperRef} ref={wrapperRef}
tabIndex={-1}
role="listbox"
onClick={() => { onClick={() => {
if (inputRef.current) { if (inputRef.current) {
inputRef.current.focus(); inputRef.current.focus();
@ -147,6 +147,7 @@ const MultiSelect: React.FunctionComponent<Props> = ({
// setIsOpen(!isOpen); // setIsOpen(!isOpen);
}} }}
onKeyDown={() => {}}
> >
<ul className={styles['current-options']}> <ul className={styles['current-options']}>
<span <span
@ -185,9 +186,11 @@ const MultiSelect: React.FunctionComponent<Props> = ({
type="text" type="text"
id={textInputId} id={textInputId}
ref={el => { ref={el => {
// eslint-disable-next-line no-param-reassign
inputRef.current = el; inputRef.current = el;
if (inputInnerRef) { if (inputInnerRef) {
// eslint-disable-next-line no-param-reassign
inputInnerRef.current = el; inputInnerRef.current = el;
} }
}} }}
@ -237,7 +240,7 @@ const MultiSelect: React.FunctionComponent<Props> = ({
closeOnEscapeKeydown closeOnEscapeKeydown
> >
<ul <ul
className={classnames(styles['suggestion'], 'list-unstyled')} className={classnames(styles.suggestion, 'list-unstyled')}
ref={listRef} ref={listRef}
> >
{filteredOptions.map((o, idx) => { {filteredOptions.map((o, idx) => {

View file

@ -20,8 +20,8 @@ import React, { Fragment } from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import LockIcon from '../Icons/Lock';
import { getSubscriptionPath } from 'web/libs/paths'; import { getSubscriptionPath } from 'web/libs/paths';
import LockIcon from '../Icons/Lock';
import { useSelector } from '../../store'; import { useSelector } from '../../store';
import styles from './PayWall.scss'; import styles from './PayWall.scss';

View file

@ -16,6 +16,8 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
/* eslint-disable jsx-a11y/control-has-associated-label */
import React from 'react'; import React from 'react';
import classnames from 'classnames'; import classnames from 'classnames';

View file

@ -20,14 +20,14 @@ import React, { useState, useRef, useEffect } from 'react';
import { withRouter, RouteComponentProps } from 'react-router-dom'; import { withRouter, RouteComponentProps } from 'react-router-dom';
import classnames from 'classnames'; import classnames from 'classnames';
import Result from './Result';
import { makeOptionId, getOptIdxByValue } from '../../../helpers/accessibility';
import { Option, filterOptions } from 'jslib/helpers/select'; import { Option, filterOptions } from 'jslib/helpers/select';
import { import {
useScrollToFocused, useScrollToFocused,
useScrollToSelected, useScrollToSelected,
useSearchMenuKeydown useSearchMenuKeydown
} from 'web/libs/hooks/dom'; } from 'web/libs/hooks/dom';
import Result from './Result';
import { makeOptionId, getOptIdxByValue } from '../../../helpers/accessibility';
import styles from './SearchableMenu.scss'; import styles from './SearchableMenu.scss';
function useFocusedIdx(options: Option[], currentValue) { function useFocusedIdx(options: Option[], currentValue) {

View file

@ -196,8 +196,5 @@ const mapDispatchToProps = {
}; };
export default withRouter( export default withRouter(
connect( connect(mapStateToProps, mapDispatchToProps)(SettingsSidebar)
mapStateToProps,
mapDispatchToProps
)(SettingsSidebar)
); );

View file

@ -17,16 +17,11 @@
*/ */
import React from 'react'; import React from 'react';
import { msToHTMLTimeDuration } from '../../helpers/time';
import {
msToHTMLTimeDuration,
getMonthName,
getUTCOffset
} from '../../helpers/time';
import formatTime from '../../helpers/time/format'; import formatTime from '../../helpers/time/format';
import Tooltip from './Tooltip';
import { Alignment, Direction } from '../Common/Popover/types'; import { Alignment, Direction } from '../Common/Popover/types';
import styles from './Time.scss'; import styles from './Time.scss';
import Tooltip from './Tooltip';
interface ContentProps { interface ContentProps {
text: string; text: string;

View file

@ -16,9 +16,8 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import React, { useState } from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import React from 'react';
import styles from './Toggle.scss'; import styles from './Toggle.scss';
interface Props { interface Props {
@ -56,7 +55,7 @@ const Toggle: React.FunctionComponent<Props> = ({
/> />
<div className={classnames(styles.toggle, {})}> <div className={classnames(styles.toggle, {})}>
<div className={styles.indicator}></div> <div className={styles.indicator} />
</div> </div>
{label} {label}

View file

@ -16,17 +16,9 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import React, { Fragment, useState, useRef } from 'react'; import React, { useRef, useState } from 'react';
import classnames from 'classnames';
import Overlay from './Overlay';
import { Alignment, Direction } from '../Popover/types'; import { Alignment, Direction } from '../Popover/types';
import { isMobileWidth } from 'web/libs/dom'; import Overlay from './Overlay';
import {
KEYCODE_ESC,
KEYCODE_ENTER,
KEYCODE_SPACE
} from 'jslib/helpers/keyboard';
interface Props { interface Props {
id: string; id: string;
@ -51,7 +43,6 @@ const Tooltip: React.FunctionComponent<Props> = ({
}) => { }) => {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const triggerRef = useRef(null); const triggerRef = useRef(null);
const touchingRef = useRef(false);
function show() { function show() {
setIsOpen(true); setIsOpen(true);

View file

@ -16,20 +16,17 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import React, { useState, useRef } from 'react';
import { Prompt, RouteComponentProps } from 'react-router-dom';
import classnames from 'classnames'; import classnames from 'classnames';
import { withRouter } from 'react-router-dom'; import React, { useRef, useState } from 'react';
import { Prompt, RouteComponentProps, withRouter } from 'react-router-dom';
import operations from 'web/libs/operations';
import { getEditorSessionkey } from 'web/libs/editor';
import { getNotePath, notePathDef } from 'web/libs/paths';
import { useFocusTextarea } from 'web/libs/hooks/editor'; import { useFocusTextarea } from 'web/libs/hooks/editor';
import Editor from '../Common/Editor'; import operations from 'web/libs/operations';
import { getNotePath, notePathDef } from 'web/libs/paths';
import { useDispatch, useSelector } from '../../store'; import { useDispatch, useSelector } from '../../store';
import { resetEditor, EditorSession } from '../../store/editor';
import { createBook } from '../../store/books'; import { createBook } from '../../store/books';
import { EditorSession, resetEditor } from '../../store/editor';
import { setMessage } from '../../store/ui'; import { setMessage } from '../../store/ui';
import Editor from '../Common/Editor';
import styles from '../New/New.scss'; import styles from '../New/New.scss';
interface Props extends RouteComponentProps { interface Props extends RouteComponentProps {

View file

@ -16,19 +16,17 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import React, { useEffect, useState } from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import { Prompt, RouteComponentProps } from 'react-router-dom'; import React, { useEffect, useState } from 'react';
import Helmet from 'react-helmet'; import Helmet from 'react-helmet';
import { withRouter } from 'react-router-dom'; import { RouteComponentProps, withRouter } from 'react-router-dom';
import { getEditorSessionkey } from 'web/libs/editor'; import { getEditorSessionkey } from 'web/libs/editor';
import operations from 'web/libs/operations'; import operations from 'web/libs/operations';
import Flash from '../Common/Flash';
import { useDispatch, useSelector } from '../../store'; import { useDispatch, useSelector } from '../../store';
import { createSession } from '../../store/editor'; import { createSession } from '../../store/editor';
import Content from './Content'; import Flash from '../Common/Flash';
import styles from '../New/New.scss'; import styles from '../New/New.scss';
import Content from './Content';
interface Match { interface Match {
noteUUID: string; noteUUID: string;

View file

@ -19,9 +19,9 @@
import React from 'react'; import React from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import Item from './Item';
import { getNewPath, getBooksPath, getRepetitionsPath } from 'web/libs/paths'; import { getNewPath, getBooksPath, getRepetitionsPath } from 'web/libs/paths';
import { Filters, toSearchObj } from 'jslib/helpers/filters'; import { Filters, toSearchObj } from 'jslib/helpers/filters';
import Item from './Item';
import styles from './Nav.scss'; import styles from './Nav.scss';
interface Props { interface Props {

View file

@ -19,9 +19,8 @@
import React from 'react'; import React from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import LogoWithText from '../../Icons/LogoWithText';
import Logo from '../../Icons/Logo';
import { getHomePath } from 'web/libs/paths'; import { getHomePath } from 'web/libs/paths';
import LogoWithText from '../../Icons/LogoWithText';
import styles from './Guest.scss'; import styles from './Guest.scss';
const UserNoteHeader: React.FunctionComponent = () => { const UserNoteHeader: React.FunctionComponent = () => {

View file

@ -20,16 +20,12 @@ import React, { useState, useRef, useEffect } from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import { booksToOptions, filterOptions, Option } from 'jslib/helpers/select'; import { booksToOptions, filterOptions, Option } from 'jslib/helpers/select';
import { usePrevious } from 'web/libs/hooks';
import { useScrollToFocused, useSearchMenuKeydown } from 'web/libs/hooks/dom';
import { useSelector } from '../../../../store'; import { useSelector } from '../../../../store';
import PopoverContent from '../../../Common/Popover/PopoverContent'; import PopoverContent from '../../../Common/Popover/PopoverContent';
import { usePrevious } from 'web/libs/hooks';
import styles from './AdvancedPanel.scss'; import styles from './AdvancedPanel.scss';
import {
useScrollToFocused,
useSearchMenuKeydown
} from 'web/libs/hooks/dom';
interface Props { interface Props {
value: string; value: string;
setValue: (string) => void; setValue: (string) => void;
@ -117,7 +113,11 @@ function useSetSuggestionVisibility(
}, [setIsOpen, triggerRef, inputValue, prevInputValue]); }, [setIsOpen, triggerRef, inputValue, prevInputValue]);
} }
const BookSearch: React.FunctionComponent<Props> = ({ value, setValue, disabled }) => { const BookSearch: React.FunctionComponent<Props> = ({
value,
setValue,
disabled
}) => {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [focusedIdx, setFocusedIdx] = useState(0); const [focusedIdx, setFocusedIdx] = useState(0);
const [focusedOptEl, setFocusedOptEl] = useState(null); const [focusedOptEl, setFocusedOptEl] = useState(null);

View file

@ -27,7 +27,11 @@ interface Props {
disabled: boolean; disabled: boolean;
} }
const WordsSearch: React.FunctionComponent<Props> = ({ words, setWords, disabled }) => { const WordsSearch: React.FunctionComponent<Props> = ({
words,
setWords,
disabled
}) => {
return ( return (
<section className={styles.section}> <section className={styles.section}>
<label htmlFor="has-words" className={styles.label}> <label htmlFor="has-words" className={styles.label}>

View file

@ -20,8 +20,8 @@ import React from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { useFilters, useSelector } from '../../../store';
import { getHomePath } from 'web/libs/paths'; import { getHomePath } from 'web/libs/paths';
import { useFilters, useSelector } from '../../../store';
import CaretIcon from '../../Icons/Caret'; import CaretIcon from '../../Icons/Caret';
import styles from './Paginator.scss'; import styles from './Paginator.scss';

View file

@ -37,7 +37,11 @@ interface Props {
filters: Filters; filters: Filters;
} }
const NoteGroup: React.FunctionComponent<Props> = ({ group, isFirst, filters }) => { const NoteGroup: React.FunctionComponent<Props> = ({
group,
isFirst,
filters
}) => {
const { year, month } = group; const { year, month } = group;
return ( return (

View file

@ -17,10 +17,9 @@
*/ */
import React from 'react'; import React from 'react';
import { IconProps } from './types'; import { IconProps } from './types';
const Icon = ({ fill, width, height, className }: IconProps) => { const Icon = ({ fill, width, height }: IconProps) => {
const h = `${height}px`; const h = `${height}px`;
const w = `${width}px`; const w = `${width}px`;

View file

@ -19,9 +19,9 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { getPasswordResetRequestPath } from 'web/libs/paths';
import styles from '../Common/Auth.scss'; import styles from '../Common/Auth.scss';
import Button from '../Common/Button'; import Button from '../Common/Button';
import { getPasswordResetRequestPath } from 'web/libs/paths';
interface Props { interface Props {
email: string; email: string;

View file

@ -16,23 +16,20 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import React, { useState, useRef, useEffect, Fragment } from 'react';
import { Prompt, RouteComponentProps } from 'react-router-dom';
import classnames from 'classnames'; import classnames from 'classnames';
import React, { Fragment, useEffect, useRef, useState } from 'react';
import Helmet from 'react-helmet'; import Helmet from 'react-helmet';
import { withRouter } from 'react-router-dom'; import { Prompt, RouteComponentProps, withRouter } from 'react-router-dom';
import { focusTextarea } from 'web/libs/dom'; import { focusTextarea } from 'web/libs/dom';
import { getEditorSessionkey } from 'web/libs/editor'; import { useFocus } from 'web/libs/hooks/dom';
import operations from 'web/libs/operations'; import operations from 'web/libs/operations';
import { getNotePath, notePathDef } from 'web/libs/paths'; import { getNotePath, notePathDef } from 'web/libs/paths';
import { useFocus } from 'web/libs/hooks/dom'; import { useDispatch } from '../../store';
import { createBook } from '../../store/books';
import { EditorSession, resetEditor } from '../../store/editor';
import { setMessage } from '../../store/ui';
import Editor from '../Common/Editor'; import Editor from '../Common/Editor';
import Flash from '../Common/Flash'; import Flash from '../Common/Flash';
import { useDispatch, useSelector } from '../../store';
import { resetEditor, createSession, EditorSession } from '../../store/editor';
import { createBook } from '../../store/books';
import { setMessage } from '../../store/ui';
import PayWall from '../Common/PayWall'; import PayWall from '../Common/PayWall';
import styles from './New.scss'; import styles from './New.scss';
@ -54,10 +51,14 @@ function useInitFocus({ bookLabel, content, textareaRef, setTriggerFocus }) {
focusTextarea(textareaEl); focusTextarea(textareaEl);
} }
} }
}, [setTriggerFocus, bookLabel, textareaRef]); }, [setTriggerFocus, bookLabel, textareaRef, content]);
} }
const New: React.FunctionComponent<Props> = ({ editor, persisted, history }) => { const New: React.FunctionComponent<Props> = ({
editor,
persisted,
history
}) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const [errMessage, setErrMessage] = useState(''); const [errMessage, setErrMessage] = useState('');
const [submitting, setSubmitting] = useState(false); const [submitting, setSubmitting] = useState(false);

View file

@ -16,28 +16,16 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import React, { useState, useRef, useEffect, Fragment } from 'react'; import React, { Fragment, useEffect } from 'react';
import { RouteComponentProps } from 'react-router-dom'; import { RouteComponentProps, withRouter } from 'react-router-dom';
import classnames from 'classnames';
import Helmet from 'react-helmet';
import { withRouter } from 'react-router-dom';
import { focusTextarea } from 'web/libs/dom';
import { getEditorSessionkey } from 'web/libs/editor'; import { getEditorSessionkey } from 'web/libs/editor';
import operations from 'web/libs/operations';
import { getNotePath, notePathDef } from 'web/libs/paths';
import Editor from '../Common/Editor';
import Flash from '../Common/Flash';
import { useDispatch, useSelector } from '../../store'; import { useDispatch, useSelector } from '../../store';
import { resetEditor, createSession } from '../../store/editor'; import { createSession } from '../../store/editor';
import { createBook } from '../../store/books';
import { setMessage } from '../../store/ui';
import Content from './Content'; import Content from './Content';
import styles from './New.scss';
interface Props extends RouteComponentProps {} interface Props extends RouteComponentProps {}
const New: React.FunctionComponent<Props> = ({ history }) => { const New: React.FunctionComponent<Props> = () => {
const sessionKey = getEditorSessionkey(null); const sessionKey = getEditorSessionkey(null);
const { editor } = useSelector(state => { const { editor } = useSelector(state => {
return { return {

View file

@ -27,7 +27,7 @@ import { tokenize, TokenKind } from 'web/libs/fts/lexer';
import BookIcon from '../Icons/Book'; import BookIcon from '../Icons/Book';
import GlobeIcon from '../Icons/Globe'; import GlobeIcon from '../Icons/Globe';
import { parseMarkdown } from '../../helpers/markdown'; import { parseMarkdown } from '../../helpers/markdown';
import { nanosecToMillisec, getMonthName } from '../../helpers/time'; import { nanosecToMillisec } from '../../helpers/time';
import formatTime from '../../helpers/time/format'; import formatTime from '../../helpers/time/format';
import { useSelector } from '../../store'; import { useSelector } from '../../store';
import Time from '../Common/Time'; import Time from '../Common/Time';

View file

@ -17,10 +17,7 @@
*/ */
import React from 'react'; import React from 'react';
import classnames from 'classnames';
import Button from '../../Common/Button'; import Button from '../../Common/Button';
import styles from './ShareModal.scss';
interface Props { interface Props {
kind: string; kind: string;

View file

@ -16,20 +16,18 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import React, { useState } from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import { homePathDef, getHomePath, getNotePath } from 'web/libs/paths';
import { copyToClipboard, selectTextInputValue } from 'web/libs/dom';
import { NoteData } from 'jslib/operations/types'; import { NoteData } from 'jslib/operations/types';
import React, { useState } from 'react';
import { copyToClipboard, selectTextInputValue } from 'web/libs/dom';
import operations from 'web/libs/operations'; import operations from 'web/libs/operations';
import Modal, { Header, Body } from '../../Common/Modal'; import { getNotePath } from 'web/libs/paths';
import Flash from '../../Common/Flash';
import { setMessage } from '../../../store/ui';
import { useDispatch } from '../../../store'; import { useDispatch } from '../../../store';
import Button from '../../Common/Button';
import Toggle from '../../Common/Toggle';
import { receiveNote } from '../../../store/note'; import { receiveNote } from '../../../store/note';
import Button from '../../Common/Button';
import Flash from '../../Common/Flash';
import Modal, { Body, Header } from '../../Common/Modal';
import Toggle from '../../Common/Toggle';
import CopyButton from './CopyButton'; import CopyButton from './CopyButton';
import styles from './ShareModal.scss'; import styles from './ShareModal.scss';

View file

@ -19,7 +19,6 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { withRouter, RouteComponentProps } from 'react-router-dom'; import { withRouter, RouteComponentProps } from 'react-router-dom';
import { notePathDef } from 'web/libs/paths';
import { parseSearchString } from 'jslib/helpers/url'; import { parseSearchString } from 'jslib/helpers/url';
import Content from './Content'; import Content from './Content';
import Flash from '../Common/Flash'; import Flash from '../Common/Flash';

View file

@ -36,7 +36,10 @@ interface Match {
interface Props extends RouteComponentProps<Match> {} interface Props extends RouteComponentProps<Match> {}
const PasswordResetConfirm: React.FunctionComponent<Props> = ({ match, history }) => { const PasswordResetConfirm: React.FunctionComponent<Props> = ({
match,
history
}) => {
const [errorMsg, setErrorMsg] = useState(''); const [errorMsg, setErrorMsg] = useState('');
const [submitting, setSubmitting] = useState(false); const [submitting, setSubmitting] = useState(false);
const dispatch = useDispatch(); const dispatch = useDispatch();

View file

@ -60,7 +60,7 @@ const Content: React.FunctionComponent<Props> = ({
return ( return (
<div> <div>
<p>Toggle the repetition for "{data.title}"</p> <p>Toggle the repetition for &#34;{data.title}&#34;</p>
<form id="T-pref-repetition-form" onSubmit={handleSubmit}> <form id="T-pref-repetition-form" onSubmit={handleSubmit}>
<div> <div>
@ -72,8 +72,7 @@ const Content: React.FunctionComponent<Props> = ({
name="repetition" name="repetition"
value="off" value="off"
checked={!isEnabled} checked={!isEnabled}
onChange={e => { onChange={() => {
const val = e.target.value;
setIsEnabled(false); setIsEnabled(false);
}} }}
/> />
@ -89,8 +88,7 @@ const Content: React.FunctionComponent<Props> = ({
name="repetition" name="repetition"
value="on" value="on"
checked={isEnabled} checked={isEnabled}
onChange={e => { onChange={() => {
const val = e.target.value;
setIsEnabled(true); setIsEnabled(true);
}} }}
/> />

View file

@ -16,18 +16,15 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import React, { useState, useEffect } from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import { Link, withRouter, RouteComponentProps } from 'react-router-dom';
import Helmet from 'react-helmet';
import services from 'web/libs/services';
import Logo from '../../Icons/Logo';
import Flash from '../../Common/Flash';
import { parseSearchString } from 'jslib/helpers/url'; import { parseSearchString } from 'jslib/helpers/url';
import { getEmailPreference } from '../../../store/auth'; import React, { useEffect, useState } from 'react';
import Helmet from 'react-helmet';
import { Link, RouteComponentProps, withRouter } from 'react-router-dom';
import { getLoginPath } from 'web/libs/paths'; import { getLoginPath } from 'web/libs/paths';
import { useSelector, useDispatch } from '../../../store'; import services from 'web/libs/services';
import Flash from '../../Common/Flash';
import Logo from '../../Icons/Logo';
import Content from './Content'; import Content from './Content';
import styles from './EmailPreferenceRepetition.scss'; import styles from './EmailPreferenceRepetition.scss';
@ -36,7 +33,10 @@ interface Match {
} }
interface Props extends RouteComponentProps<Match> {} interface Props extends RouteComponentProps<Match> {}
const EmailPreferenceRepetition: React.FunctionComponent<Props> = ({ location, match }) => { const EmailPreferenceRepetition: React.FunctionComponent<Props> = ({
location,
match
}) => {
const [data, setData] = useState(null); const [data, setData] = useState(null);
const [isFetching, setIsFetching] = useState(false); const [isFetching, setIsFetching] = useState(false);
const [successMsg, setSuccessMsg] = useState(''); const [successMsg, setSuccessMsg] = useState('');
@ -67,7 +67,7 @@ const EmailPreferenceRepetition: React.FunctionComponent<Props> = ({ location, m
setIsFetching(false); setIsFetching(false);
}); });
}, [data, setData, setFailureMsg, setIsFetching]); }, [data, repetitionUUID, setData, setFailureMsg, setIsFetching, token]);
const isFetched = data !== null; const isFetched = data !== null;

View file

@ -1,7 +1,5 @@
import React, { Fragment, useState, useEffect } from 'react'; import React, { Fragment, useState, useEffect } from 'react';
import { getRepetitionRules } from '../../store/repetitionRules';
import { useDispatch, useSelector } from '../../store';
import classnames from 'classnames'; import classnames from 'classnames';
import { import {
getNewRepetitionPath, getNewRepetitionPath,
@ -10,6 +8,8 @@ import {
repetitionsPathDef repetitionsPathDef
} from 'web/libs/paths'; } from 'web/libs/paths';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { useDispatch, useSelector } from '../../store';
import { getRepetitionRules } from '../../store/repetitionRules';
import RepetitionList from './RepetitionList'; import RepetitionList from './RepetitionList';
import DeleteRepetitionRuleModal from './DeleteRepetitionRuleModal'; import DeleteRepetitionRuleModal from './DeleteRepetitionRuleModal';
import Flash from '../Common/Flash'; import Flash from '../Common/Flash';

View file

@ -16,17 +16,15 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import React, { useState, useEffect } from 'react';
import classnames from 'classnames';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import services from 'web/libs/services';
import { RepetitionRuleData } from 'jslib/operations/types'; import { RepetitionRuleData } from 'jslib/operations/types';
import Modal, { Header, Body } from '../Common/Modal'; import React, { useEffect, useState } from 'react';
import Flash from '../Common/Flash'; import { RouteComponentProps, withRouter } from 'react-router-dom';
import services from 'web/libs/services';
import { useDispatch, useSelector } from '../../store';
import { removeRepetitionRule } from '../../store/repetitionRules'; import { removeRepetitionRule } from '../../store/repetitionRules';
import { useSelector, useDispatch } from '../../store';
import Button from '../Common/Button'; import Button from '../Common/Button';
import Flash from '../Common/Flash';
import Modal, { Body, Header } from '../Common/Modal';
import styles from './DeleteRepetitionRuleModal.scss'; import styles from './DeleteRepetitionRuleModal.scss';
function getRepetitionRuleByUUID( function getRepetitionRuleByUUID(
@ -73,7 +71,6 @@ const DeleteRepetitionModal: React.FunctionComponent<Props> = ({
); );
const labelId = 'delete-rule-modal-label'; const labelId = 'delete-rule-modal-label';
const nameInputId = 'delete-rule-modal-name-input';
const descId = 'delete-rule-modal-desc'; const descId = 'delete-rule-modal-desc';
useEffect(() => { useEffect(() => {

View file

@ -16,16 +16,15 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import React from 'react';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import services from 'web/libs/services';
import { BookDomain, RepetitionRuleData } from 'jslib/operations/types';
import { booksToOptions } from 'jslib/helpers/select'; import { booksToOptions } from 'jslib/helpers/select';
import { RepetitionRuleData } from 'jslib/operations/types';
import React from 'react';
import { RouteComponentProps, withRouter } from 'react-router-dom';
import { getRepetitionsPath, repetitionsPathDef } from 'web/libs/paths'; import { getRepetitionsPath, repetitionsPathDef } from 'web/libs/paths';
import Form, { FormState, serializeFormState } from '../Form'; import services from 'web/libs/services';
import { useDispatch } from '../../../store'; import { useDispatch } from '../../../store';
import { setMessage } from '../../../store/ui'; import { setMessage } from '../../../store/ui';
import Form, { FormState, serializeFormState } from '../Form';
interface Props extends RouteComponentProps { interface Props extends RouteComponentProps {
setErrMsg: (string) => void; setErrMsg: (string) => void;

View file

@ -16,20 +16,17 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import React, { useState, useEffect } from 'react';
import Helmet from 'react-helmet';
import { Link, withRouter, RouteComponentProps } from 'react-router-dom';
import classnames from 'classnames'; import classnames from 'classnames';
import { RepetitionRuleData } from 'jslib/operations/types';
import { getRepetitionsPath, repetitionsPathDef } from 'web/libs/paths'; import React, { useEffect, useState } from 'react';
import { BookDomain, RepetitionRuleData } from 'jslib/operations/types'; import Helmet from 'react-helmet';
import { Link, RouteComponentProps } from 'react-router-dom';
import { getRepetitionsPath } from 'web/libs/paths';
import services from 'web/libs/services'; import services from 'web/libs/services';
import { createRepetitionRule } from '../../../store/repetitionRules';
import { useDispatch } from '../../../store'; import { useDispatch } from '../../../store';
import Flash from '../../Common/Flash'; import Flash from '../../Common/Flash';
import { setMessage } from '../../../store/ui';
import Content from './Content';
import repetitionStyles from '../Repetition.scss'; import repetitionStyles from '../Repetition.scss';
import Content from './Content';
interface Match { interface Match {
repetitionUUID: string; repetitionUUID: string;
@ -37,7 +34,7 @@ interface Match {
interface Props extends RouteComponentProps<Match> {} interface Props extends RouteComponentProps<Match> {}
const EditRepetition: React.FunctionComponent<Props> = ({ history, match }) => { const EditRepetition: React.FunctionComponent<Props> = ({ match }) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const [errMsg, setErrMsg] = useState(''); const [errMsg, setErrMsg] = useState('');
const [data, setData] = useState<RepetitionRuleData | null>(null); const [data, setData] = useState<RepetitionRuleData | null>(null);

View file

@ -16,21 +16,20 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import React, { useState, useReducer, useRef, useEffect } from 'react'; import React, { useEffect, useReducer, useRef } from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import { Link } from 'react-router-dom'; import { booksToOptions, Option } from 'jslib/helpers/select';
import { getRepetitionsPath } from 'web/libs/paths';
import { Option, booksToOptions } from 'jslib/helpers/select';
import { BookDomain } from 'jslib/operations/types'; import { BookDomain } from 'jslib/operations/types';
import { CreateParams } from 'jslib/services/repetitionRules'; import { CreateParams } from 'jslib/services/repetitionRules';
import Modal, { Header, Body } from '../../Common/Modal'; import { Link } from 'react-router-dom';
import { useSelector } from '../../../store'; import { getRepetitionsPath } from 'web/libs/paths';
import { daysToMs } from '../../../helpers/time'; import { daysToMs } from '../../../helpers/time';
import { useSelector } from '../../../store';
import Button from '../../Common/Button'; import Button from '../../Common/Button';
import modalStyles from '../../Common/Modal/Modal.scss';
import MultiSelect from '../../Common/MultiSelect'; import MultiSelect from '../../Common/MultiSelect';
import styles from './Form.scss'; import styles from './Form.scss';
import modalStyles from '../../Common/Modal/Modal.scss';
export interface FormState { export interface FormState {
title: string; title: string;
@ -72,6 +71,8 @@ interface Props {
cancelPath?: string; cancelPath?: string;
initialState?: FormState; initialState?: FormState;
isEditing?: boolean; isEditing?: boolean;
// TODO: implement inProgress
inProgress?: boolean;
} }
enum Action { enum Action {
@ -162,9 +163,9 @@ const Form: React.FunctionComponent<Props> = ({
setErrMsg, setErrMsg,
cancelPath = getRepetitionsPath(), cancelPath = getRepetitionsPath(),
initialState = formInitialState, initialState = formInitialState,
isEditing = false isEditing = false,
inProgress = false
}) => { }) => {
const [inProgress, setInProgress] = useState(false);
const bookSelectorInputRef = useRef(null); const bookSelectorInputRef = useRef(null);
const [formState, formDispatch] = useReducer(formReducer, initialState); const [formState, formDispatch] = useReducer(formReducer, initialState);
const { books } = useSelector(state => { const { books } = useSelector(state => {
@ -200,10 +201,8 @@ const Form: React.FunctionComponent<Props> = ({
if (bookSelectorInputRef.current) { if (bookSelectorInputRef.current) {
bookSelectorInputRef.current.blur(); bookSelectorInputRef.current.blur();
} }
} else { } else if (bookSelectorInputRef.current) {
if (bookSelectorInputRef.current) { bookSelectorInputRef.current.focus();
bookSelectorInputRef.current.focus();
}
} }
}, [formState.bookDomain, isEditing]); }, [formState.bookDomain, isEditing]);
@ -356,7 +355,7 @@ const Form: React.FunctionComponent<Props> = ({
formDispatch({ formDispatch({
type: Action.setFrequency, type: Action.setFrequency,
data: Number.parseInt(value) data: Number.parseInt(value, 10)
}); });
}} }}
> >
@ -393,6 +392,7 @@ const Form: React.FunctionComponent<Props> = ({
> >
{[...Array(24)].map((_, i) => { {[...Array(24)].map((_, i) => {
return ( return (
// eslint-disable-next-line react/no-array-index-key
<option key={i} value={i}> <option key={i} value={i}>
{i} {i}
</option> </option>
@ -421,6 +421,7 @@ const Form: React.FunctionComponent<Props> = ({
> >
{[...Array(60)].map((_, i) => { {[...Array(60)].map((_, i) => {
return ( return (
// eslint-disable-next-line react/no-array-index-key
<option key={i} value={i}> <option key={i} value={i}>
{i} {i}
</option> </option>
@ -454,7 +455,7 @@ const Form: React.FunctionComponent<Props> = ({
if (value === '') { if (value === '') {
data = ''; data = '';
} else { } else {
data = Number.parseInt(value); data = Number.parseInt(value, 10);
} }
formDispatch({ formDispatch({
@ -502,7 +503,6 @@ const Form: React.FunctionComponent<Props> = ({
const ok = window.confirm('Are you sure?'); const ok = window.confirm('Are you sure?');
if (!ok) { if (!ok) {
e.preventDefault(); e.preventDefault();
return;
} }
}} }}
className="button button-second button-normal" className="button button-second button-normal"

View file

@ -22,7 +22,6 @@ import { Link, withRouter, RouteComponentProps } from 'react-router-dom';
import classnames from 'classnames'; import classnames from 'classnames';
import { getRepetitionsPath, repetitionsPathDef } from 'web/libs/paths'; import { getRepetitionsPath, repetitionsPathDef } from 'web/libs/paths';
import { BookDomain } from 'jslib/operations/types';
import { import {
getRepetitionRules, getRepetitionRules,
createRepetitionRule createRepetitionRule

View file

@ -16,14 +16,12 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import React, { useState, useRef } from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import React, { useRef, useState } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import ItemActions from '../../Common/ItemActions';
import DotsIcon from '../../Icons/Dots';
import ItemActionsStyles from '../../Common/ItemActions/ItemActions.scss';
import { getEditRepetitionPath } from '../../../libs/paths'; import { getEditRepetitionPath } from '../../../libs/paths';
import ItemActions from '../../Common/ItemActions';
import ItemActionsStyles from '../../Common/ItemActions/ItemActions.scss';
interface Props { interface Props {
isActive: boolean; isActive: boolean;

View file

@ -16,20 +16,18 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import React, { useState } from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import { RepetitionRuleData } from 'jslib/operations/types'; import { RepetitionRuleData } from 'jslib/operations/types';
import React, { useState } from 'react';
import { import {
msToDuration, msToDuration,
msToHTMLTimeDuration, msToHTMLTimeDuration,
timeAgo, relativeTimeDiff,
relativeTimeDiff timeAgo
} from 'web/helpers/time'; } from 'web/helpers/time';
import formatTime from 'web/helpers/time/format'; import Time from '../../Common/Time';
import Actions from './Actions'; import Actions from './Actions';
import BookMeta from './BookMeta'; import BookMeta from './BookMeta';
import Time from '../../Common/Time';
import styles from './RepetitionItem.scss'; import styles from './RepetitionItem.scss';
interface Props { interface Props {

View file

@ -16,14 +16,12 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import React, { useState } from 'react'; import React from 'react';
import Helmet from 'react-helmet'; import Helmet from 'react-helmet';
import classnames from 'classnames'; import config from '../../../libs/config';
import { useSelector } from '../../../store';
import SettingRow from '../SettingRow'; import SettingRow from '../SettingRow';
import styles from '../Settings.scss'; import styles from '../Settings.scss';
import { useSelector } from '../../../store';
import config from '../../../libs/config';
interface Props {} interface Props {}

View file

@ -16,17 +16,15 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import React, { useState, useEffect } from 'react'; import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import services from 'web/libs/services'; import services from 'web/libs/services';
import { getCurrentUser } from '../../../store/auth';
import { useDispatch } from '../../../store'; import { useDispatch } from '../../../store';
import { getCurrentUser } from '../../../store/auth';
import Button from '../../Common/Button'; import Button from '../../Common/Button';
import Modal, { Header, Body } from '../../Common/Modal';
import Flash from '../../Common/Flash'; import Flash from '../../Common/Flash';
import Modal, { Body, Header } from '../../Common/Modal';
import modalStyles from '../../Common/Modal/Modal.scss'; import modalStyles from '../../Common/Modal/Modal.scss';
import settingsStyles from '../Settings.scss';
interface Props { interface Props {
currentEmail: string; currentEmail: string;
@ -34,7 +32,11 @@ interface Props {
onDismiss: () => void; onDismiss: () => void;
} }
const EmailModal: React.FunctionComponent<Props> = ({ currentEmail, isOpen, onDismiss }) => { const EmailModal: React.FunctionComponent<Props> = ({
currentEmail,
isOpen,
onDismiss
}) => {
const [passwordVal, setPasswordVal] = useState(''); const [passwordVal, setPasswordVal] = useState('');
const [emailVal, setEmailVal] = useState(''); const [emailVal, setEmailVal] = useState('');
const [inProgress, setInProgress] = useState(false); const [inProgress, setInProgress] = useState(false);
@ -183,7 +185,4 @@ const mapDispatchToProps = {
doGetCurrentUser: getCurrentUser doGetCurrentUser: getCurrentUser
}; };
export default connect( export default connect(null, mapDispatchToProps)(EmailModal);
null,
mapDispatchToProps
)(EmailModal);

View file

@ -16,13 +16,12 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import React, { useState, useEffect } from 'react'; import React, { useEffect, useState } from 'react';
import services from 'web/libs/services'; import services from 'web/libs/services';
import Button from '../../Common/Button'; import Button from '../../Common/Button';
import Modal, { Header, Body } from '../../Common/Modal';
import Flash from '../../Common/Flash'; import Flash from '../../Common/Flash';
import settingsStyles from '../Settings.scss'; import Modal, { Body, Header } from '../../Common/Modal';
import modalStyles from '../../Common/Modal/Modal.scss'; import modalStyles from '../../Common/Modal/Modal.scss';
interface Props { interface Props {
@ -32,7 +31,10 @@ interface Props {
const labelId = 'password-modal'; const labelId = 'password-modal';
const PasswordModal: React.FunctionComponent<Props> = ({ isOpen, onDismiss }) => { const PasswordModal: React.FunctionComponent<Props> = ({
isOpen,
onDismiss
}) => {
const [oldPassword, setOldPassword] = useState(''); const [oldPassword, setOldPassword] = useState('');
const [newPassword, setNewPassword] = useState(''); const [newPassword, setNewPassword] = useState('');
const [newPasswordConfirmation, setNewPasswordConfirmation] = useState(''); const [newPasswordConfirmation, setNewPasswordConfirmation] = useState('');

View file

@ -15,4 +15,3 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */

View file

@ -16,7 +16,7 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import React, { useState, useEffect } from 'react'; import React from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import SettingRow from '../../SettingRow'; import SettingRow from '../../SettingRow';

View file

@ -17,13 +17,9 @@
*/ */
import React, { Fragment } from 'react'; import React, { Fragment } from 'react';
import classnames from 'classnames';
import PaymentMethodRow from './PaymentMethodRow';
import settingsStyles from '../../Settings.scss';
import { SourceData } from '../../../../store/auth/type'; import { SourceData } from '../../../../store/auth/type';
import PaymentMethodRow from './PaymentMethodRow';
import Placeholder from './Placeholder'; import Placeholder from './Placeholder';
import styles from './Placeholder.scss';
interface Props { interface Props {
source: SourceData; source: SourceData;

View file

@ -16,7 +16,7 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import React, { useState, useEffect } from 'react'; import React from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import SettingRow from '../../SettingRow'; import SettingRow from '../../SettingRow';

View file

@ -21,9 +21,9 @@ import { Link } from 'react-router-dom';
import classnames from 'classnames'; import classnames from 'classnames';
import { getPlanLabel } from 'web/libs/subscription'; import { getPlanLabel } from 'web/libs/subscription';
import LogoIcon from '../../../Icons/Logo';
import { SECOND } from 'web/helpers/time'; import { SECOND } from 'web/helpers/time';
import formatDate from 'web/helpers/time/format'; import formatDate from 'web/helpers/time/format';
import LogoIcon from '../../../Icons/Logo';
import styles from './PlanRow.scss'; import styles from './PlanRow.scss';
import settingRowStyles from '../../SettingRow.scss'; import settingRowStyles from '../../SettingRow.scss';

View file

@ -17,15 +17,12 @@
*/ */
import React, { Fragment } from 'react'; import React, { Fragment } from 'react';
import classnames from 'classnames';
import PlanRow from './PlanRow'; import PlanRow from './PlanRow';
import CancelRow from './CancelRow'; import CancelRow from './CancelRow';
import ReactivateRow from './ReactivateRow'; import ReactivateRow from './ReactivateRow';
import settingsStyles from '../../Settings.scss';
import { SubscriptionData } from '../../../../store/auth/type'; import { SubscriptionData } from '../../../../store/auth/type';
import Placeholder from './Placeholder'; import Placeholder from './Placeholder';
import styles from './Placeholder.scss';
interface Props { interface Props {
subscription: SubscriptionData; subscription: SubscriptionData;

View file

@ -18,7 +18,6 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import Helmet from 'react-helmet'; import Helmet from 'react-helmet';
import classnames from 'classnames';
import { useScript } from 'web/libs/hooks'; import { useScript } from 'web/libs/hooks';
import { useSelector, useDispatch } from '../../../store'; import { useSelector, useDispatch } from '../../../store';
@ -31,7 +30,6 @@ import {
getSource, getSource,
clearSource clearSource
} from '../../../store/auth'; } from '../../../store/auth';
import SettingRow from '../SettingRow';
import PlanSection from './PlanSection'; import PlanSection from './PlanSection';
import PaymentSection from './PaymentSection'; import PaymentSection from './PaymentSection';
import styles from '../Settings.scss'; import styles from '../Settings.scss';
@ -64,7 +62,6 @@ const Billing: React.FunctionComponent = () => {
}); });
const subscription = subscriptionData.data; const subscription = subscriptionData.data;
const source = sourceData.data;
const key = `${__STRIPE_PUBLIC_KEY__}`; const key = `${__STRIPE_PUBLIC_KEY__}`;

View file

@ -41,7 +41,11 @@ interface Props extends RouteComponentProps {
history: History; history: History;
} }
const Form: React.FunctionComponent<Props> = ({ stripe, stripeLoadError, history }) => { const Form: React.FunctionComponent<Props> = ({
stripe,
stripeLoadError,
history
}) => {
const [nameOnCard, setNameOnCard] = useState(''); const [nameOnCard, setNameOnCard] = useState('');
const cardElementRef = useRef(null); const cardElementRef = useRef(null);
const [cardElementLoaded, setCardElementLoaded] = useState(false); const [cardElementLoaded, setCardElementLoaded] = useState(false);

View file

@ -16,20 +16,11 @@
* along with Dnote. If not, see <https://www.gnu.org/licenses/>. * along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { import { getMonthName, getUTCOffset, pad, getDayName } from './index';
getMonthName, import { addOrdinalSuffix } from '../../libs/string';
getUTCOffset,
pad,
nanosecToMillisec,
DAY,
timeAgo,
getDayName
} from './index';
import { addOrdinalSuffix } from '../..//libs/string';
// format verbs // format verbs
const YYYY = '%YYYY'; const YYYY = '%YYYY';
const YYY = '%YYY';
const YY = '%YY'; const YY = '%YY';
const MMMM = '%MMMM'; const MMMM = '%MMMM';
const MMM = '%MMM'; const MMM = '%MMM';
@ -49,10 +40,10 @@ const dddd = '%dddd';
// getPeriod returns the period for the time for the given date // getPeriod returns the period for the time for the given date
function getPeriod(date: Date) { function getPeriod(date: Date) {
const h = date.getHours(); const hour = date.getHours();
let ret; let ret;
if (h > 12) { if (hour > 12) {
ret = 'PM'; ret = 'PM';
} else { } else {
ret = 'AM'; ret = 'AM';
@ -110,14 +101,14 @@ export default function formatTime(date: Date, format: string): string {
} }
if (ret.indexOf(hh) > -1) { if (ret.indexOf(hh) > -1) {
const h = date.getHours(); const hour = date.getHours();
ret = ret.replace(new RegExp(hh, 'g'), pad(h)); ret = ret.replace(new RegExp(hh, 'g'), pad(hour));
} }
if (ret.indexOf(h) > -1) { if (ret.indexOf(h) > -1) {
let hour = date.getHours(); let hour = date.getHours();
if (hour > 12) { if (hour > 12) {
hour = hour - 12; hour -= 12;
} }
ret = ret.replace(new RegExp(h, 'g'), hour.toString()); ret = ret.replace(new RegExp(h, 'g'), hour.toString());

Some files were not shown because too many files have changed in this diff Show more