mirror of
https://github.com/dnote/dnote
synced 2026-03-14 14:35:50 +01:00
parent
628d29c8d1
commit
3883a076ef
113 changed files with 6030 additions and 4531 deletions
74
.eslintrc
Normal file
74
.eslintrc
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
{ "extends": ["eslint-config-airbnb", "prettier"],
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true,
|
||||
"jest": true
|
||||
},
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"rules": {
|
||||
"camelcase": 0,
|
||||
"strict": 0,
|
||||
"react/no-multi-comp": 0,
|
||||
"import/default": 0,
|
||||
"import/no-duplicates": 0,
|
||||
"import/named": 0,
|
||||
"import/namespace": 0,
|
||||
"import/no-unresolved": 0,
|
||||
"import/no-named-as-default": 2,
|
||||
"import/prefer-default-export": 0,
|
||||
"comma-dangle": 0, // not sure why airbnb turned this on. gross!
|
||||
"indent": [2, 2, {"SwitchCase": 1}],
|
||||
"no-console": 0,
|
||||
"no-alert": 0,
|
||||
"no-shadow": 2,
|
||||
"arrow-body-style": 0,
|
||||
"react/prop-types": 0,
|
||||
"react/jsx-filename-extension": 0,
|
||||
"react/prefer-stateless-function": 0,
|
||||
"jsx-a11y/anchor-is-valid": 0,
|
||||
"jsx-a11y/tabindex-no-positive": 0,
|
||||
"no-mixed-operators": 0,
|
||||
"no-plusplus": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"prettier/prettier": "error",
|
||||
"jsx-a11y/no-autofocus": 0,
|
||||
"jsx-a11y/label-has-for": 0,
|
||||
"prefer-destructuring": 0,
|
||||
"react-hooks/rules-of-hooks": "error",
|
||||
"react-hooks/exhaustive-deps": "warn",
|
||||
"react/jsx-wrap-multilines": ["error", {"declaration": false, "assignment": false}],
|
||||
"react/jsx-one-expression-per-line": 0,
|
||||
"@typescript-eslint/no-unused-vars": 1,
|
||||
"import/no-extraneous-dependencies": ["error", {"devDependencies": ["**/*_test.ts"]}],
|
||||
"lines-between-class-members": 0,
|
||||
"react/jsx-fragments": 0,
|
||||
"jsx-a11y/label-has-associated-control": 0,
|
||||
"no-empty": 0
|
||||
},
|
||||
"plugins": [
|
||||
"react", "react-hooks", "import", "prettier", "@typescript-eslint"
|
||||
],
|
||||
"globals": {
|
||||
// web
|
||||
"__DEVELOPMENT__": true,
|
||||
"__PRODUCTION__": true,
|
||||
"__DISABLE_SSR__": true,
|
||||
"__DEVTOOLS__": true,
|
||||
"__DOMAIN__": true,
|
||||
"__BASE_URL__": true,
|
||||
"__BASE_NAME__": true,
|
||||
"__STRIPE_PUBLIC_KEY__": true,
|
||||
"__ROOT_URL__": true,
|
||||
"__CDN_URL__": true,
|
||||
"socket": true,
|
||||
"webpackIsomorphicTools": true,
|
||||
"StripeCheckout": true,
|
||||
|
||||
// browser
|
||||
"browser": true,
|
||||
"chrome": true,
|
||||
__WEB_URL__: true,
|
||||
__API_ENDPOINT__: true,
|
||||
__VERSION__: true
|
||||
}
|
||||
}
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -2,3 +2,4 @@
|
|||
/build
|
||||
.vagrant
|
||||
*.log
|
||||
node_modules
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ install:
|
|||
- make install
|
||||
|
||||
script:
|
||||
- make lint
|
||||
- make test-cli
|
||||
- make test-api
|
||||
- make test-web
|
||||
|
|
|
|||
8
Makefile
8
Makefile
|
|
@ -29,16 +29,24 @@ endif
|
|||
@echo "==> installing js dependencies"
|
||||
|
||||
ifeq ($(CI), true)
|
||||
@(cd ${currentDir} && npm install --unsafe-perm=true)
|
||||
@(cd ${currentDir}/web && npm install --unsafe-perm=true)
|
||||
@(cd ${currentDir}/browser && npm install --unsafe-perm=true)
|
||||
@(cd ${currentDir}/jslib && npm install --unsafe-perm=true)
|
||||
else
|
||||
@(cd ${currentDir} && npm install)
|
||||
@(cd ${currentDir}/web && npm install)
|
||||
@(cd ${currentDir}/browser && npm install)
|
||||
@(cd ${currentDir}/jslib && npm install)
|
||||
endif
|
||||
.PHONY: install-js
|
||||
|
||||
lint:
|
||||
@(cd ${currentDir}/web && npm run lint)
|
||||
@(cd ${currentDir}/jslib && npm run lint)
|
||||
@(cd ${currentDir}/browser && npm run lint)
|
||||
.PHONY: lint
|
||||
|
||||
## test
|
||||
test: test-cli test-api test-web test-jslib
|
||||
.PHONY: test
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
],
|
||||
}
|
||||
1923
browser/package-lock.json
generated
1923
browser/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"name": "dnote-extension",
|
||||
"repository": "https://github.com/dnote/dnote",
|
||||
"description": "Dnote browser extension for Chrome and Firefox",
|
||||
"scripts": {
|
||||
"clean": "TARGET=firefox gulp clean && TARGET=chrome gulp clean",
|
||||
|
|
@ -8,7 +9,8 @@
|
|||
"package:chrome": "TARGET=chrome 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:firefox": "TARGET=firefox NODE_ENV=development concurrently \"webpack --watch\" \"gulp watch\" "
|
||||
"watch:firefox": "TARGET=firefox NODE_ENV=development concurrently \"webpack --watch\" \"gulp watch\" ",
|
||||
"lint": "../node_modules/.bin/eslint ./src --ext .ts,.tsx,.js"
|
||||
},
|
||||
"author": "Monomax Software Pty Ltd",
|
||||
"license": "GPL-3.0-or-later",
|
||||
|
|
@ -26,17 +28,12 @@
|
|||
"redux-thunk": "^2.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.7.4",
|
||||
"@babel/preset-env": "^7.7.4",
|
||||
"@types/react": "^16.9.11",
|
||||
"@types/react-dom": "^16.9.3",
|
||||
"@typescript-eslint/eslint-plugin": "^2.6.0",
|
||||
"@typescript-eslint/parser": "^2.6.0",
|
||||
"concurrently": "^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-if": "^3.0.0",
|
||||
"gulp-imagemin": "^6.1.1",
|
||||
|
|
|
|||
4
browser/src/browser.d.ts
vendored
4
browser/src/browser.d.ts
vendored
|
|
@ -17,5 +17,5 @@
|
|||
*/
|
||||
|
||||
// browser.d.ts
|
||||
declare var browser: any;
|
||||
declare var chrome: any;
|
||||
declare let browser: any;
|
||||
declare let chrome: any;
|
||||
|
|
|
|||
6
browser/src/global.d.ts
vendored
6
browser/src/global.d.ts
vendored
|
|
@ -19,6 +19,6 @@
|
|||
// global.d.ts
|
||||
|
||||
// defined by webpack-define-plugin
|
||||
declare var __API_ENDPOINT__: string;
|
||||
declare var __WEB_URL__: string;
|
||||
declare var __VERSION__: string;
|
||||
declare let __API_ENDPOINT__: string;
|
||||
declare let __WEB_URL__: string;
|
||||
declare let __VERSION__: string;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
*/
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import classnames from 'classnames';
|
||||
|
||||
import initServices from '../utils/services';
|
||||
import { logout } from '../store/auth/actions';
|
||||
|
|
@ -69,13 +68,11 @@ const App: React.FunctionComponent<Props> = () => {
|
|||
const [errMsg, setErrMsg] = useState('');
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const { path, auth, settings } = useSelector(state => {
|
||||
return {
|
||||
path: state.location.path,
|
||||
auth: state.auth,
|
||||
settings: state.settings
|
||||
};
|
||||
});
|
||||
const { path, auth, settings } = useSelector(state => ({
|
||||
path: state.location.path,
|
||||
auth: state.auth,
|
||||
settings: state.settings
|
||||
}));
|
||||
|
||||
useCheckSessionValid(auth);
|
||||
|
||||
|
|
|
|||
|
|
@ -16,23 +16,19 @@
|
|||
* 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 cloneDeep from 'lodash/cloneDeep';
|
||||
import { useSelector, useDispatch } from '../store/hooks';
|
||||
import { updateBook, resetBook } from '../store/composer/actions';
|
||||
|
||||
import BookIcon from './BookIcon';
|
||||
|
||||
interface Props {
|
||||
selectorRef: React.Dispatch<any>;
|
||||
onAfterChange: () => void;
|
||||
}
|
||||
|
||||
function useCurrentOptions(options) {
|
||||
const currentValue = useSelector(state => {
|
||||
return state.composer.bookUUID;
|
||||
});
|
||||
const currentValue = useSelector(state => state.composer.bookUUID);
|
||||
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
const option = options[i];
|
||||
|
|
@ -46,19 +42,15 @@ function useCurrentOptions(options) {
|
|||
}
|
||||
|
||||
function useOptions() {
|
||||
const { books, composer } = useSelector(state => {
|
||||
return {
|
||||
books: state.books,
|
||||
composer: state.composer
|
||||
};
|
||||
});
|
||||
const { books, composer } = useSelector(state => ({
|
||||
books: state.books,
|
||||
composer: state.composer
|
||||
}));
|
||||
|
||||
const opts = books.items.map(book => {
|
||||
return {
|
||||
label: book.label,
|
||||
value: book.uuid
|
||||
};
|
||||
});
|
||||
const opts = books.items.map(book => ({
|
||||
label: book.label,
|
||||
value: book.uuid
|
||||
}));
|
||||
|
||||
if (composer.bookLabel !== '' && composer.bookUUID === '') {
|
||||
opts.push({
|
||||
|
|
@ -77,12 +69,9 @@ const BookSelector: React.FunctionComponent<Props> = ({
|
|||
onAfterChange
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const { books, composer } = useSelector(state => {
|
||||
return {
|
||||
books: state.books,
|
||||
composer: state.composer
|
||||
};
|
||||
});
|
||||
const { books } = useSelector(state => ({
|
||||
books: state.books
|
||||
}));
|
||||
const options = useOptions();
|
||||
const currentOption = useCurrentOptions(options);
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
* 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 { 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,
|
||||
// and to guard against possible breaking changes, if the path does not exist, it noops.
|
||||
function focusBookSelectorInput(bookSelectorRef) {
|
||||
bookSelectorRef.select &&
|
||||
return (
|
||||
bookSelectorRef.select &&
|
||||
bookSelectorRef.select.select &&
|
||||
bookSelectorRef.select.select.inputRef &&
|
||||
bookSelectorRef.select.select.inputRef.focus();
|
||||
bookSelectorRef.select.select.inputRef.focus()
|
||||
);
|
||||
}
|
||||
|
||||
function useFetchData() {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const { books } = useSelector(state => {
|
||||
return {
|
||||
books: state.books
|
||||
};
|
||||
});
|
||||
const { books } = useSelector(state => ({
|
||||
books: state.books
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
if (!books.isFetched) {
|
||||
|
|
@ -57,12 +57,10 @@ function useFetchData() {
|
|||
}
|
||||
|
||||
function useInitFocus(contentRef, bookSelectorRef) {
|
||||
const { composer, books } = useSelector(state => {
|
||||
return {
|
||||
composer: state.composer,
|
||||
books: state.books
|
||||
};
|
||||
});
|
||||
const { composer, books } = useSelector(state => ({
|
||||
composer: state.composer,
|
||||
books: state.books
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
if (!books.isFetched) {
|
||||
|
|
@ -76,7 +74,9 @@ function useInitFocus(contentRef, bookSelectorRef) {
|
|||
contentRef.focus();
|
||||
}
|
||||
}
|
||||
}, [contentRef, bookSelectorRef, books.isFetched]);
|
||||
|
||||
return () => null;
|
||||
}, [contentRef, bookSelectorRef, books.isFetched, composer.bookLabel]);
|
||||
}
|
||||
|
||||
const Composer: React.FunctionComponent<Props> = () => {
|
||||
|
|
@ -88,27 +88,43 @@ const Composer: React.FunctionComponent<Props> = () => {
|
|||
const [contentRef, setContentEl] = useState(null);
|
||||
const [bookSelectorRef, setBookSelectorEl] = useState(null);
|
||||
|
||||
const { composer, settings, auth } = useSelector(state => {
|
||||
return {
|
||||
composer: state.composer,
|
||||
settings: state.settings,
|
||||
auth: state.auth
|
||||
};
|
||||
});
|
||||
const { composer, settings, auth } = useSelector(state => ({
|
||||
composer: state.composer,
|
||||
settings: state.settings,
|
||||
auth: state.auth
|
||||
}));
|
||||
|
||||
const handleSubmit = async e => {
|
||||
e.preventDefault();
|
||||
const handleSubmit = useCallback(
|
||||
async e => {
|
||||
e.preventDefault();
|
||||
|
||||
const services = initServices(settings.apiUrl);
|
||||
const services = initServices(settings.apiUrl);
|
||||
|
||||
setSubmitting(true);
|
||||
setSubmitting(true);
|
||||
|
||||
try {
|
||||
let bookUUID;
|
||||
if (composer.bookUUID === '') {
|
||||
const resp = await services.books.create(
|
||||
try {
|
||||
let bookUUID;
|
||||
if (composer.bookUUID === '') {
|
||||
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: {
|
||||
|
|
@ -117,56 +133,48 @@ const Composer: React.FunctionComponent<Props> = () => {
|
|||
}
|
||||
);
|
||||
|
||||
bookUUID = resp.book.uuid;
|
||||
} else {
|
||||
bookUUID = composer.bookUUID;
|
||||
// clear the composer state
|
||||
setErrMsg('');
|
||||
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(
|
||||
{
|
||||
book_uuid: bookUUID,
|
||||
content: composer.content
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
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);
|
||||
}
|
||||
};
|
||||
},
|
||||
[
|
||||
settings.apiUrl,
|
||||
composer.bookUUID,
|
||||
composer.content,
|
||||
composer.bookLabel,
|
||||
auth.sessionKey,
|
||||
dispatch
|
||||
]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const handleSubmitShortcut = e => {
|
||||
// Shift + Enter
|
||||
if (e.shiftKey && e.keyCode === KEYCODE_ENTER) {
|
||||
handleSubmit(e);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handleSubmitShortcut);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleSubmitShortcut);
|
||||
};
|
||||
}, [composer]);
|
||||
}, [composer, handleSubmit]);
|
||||
|
||||
let submitBtnText: string;
|
||||
if (submitting) {
|
||||
|
|
|
|||
|
|
@ -17,15 +17,9 @@
|
|||
*/
|
||||
|
||||
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 { updateSettings } from '../store/settings/actions';
|
||||
import { useDispatch } from '../store/hooks';
|
||||
import services from '../utils/services';
|
||||
import Flash from '../components/Flash';
|
||||
|
||||
interface Props {}
|
||||
|
|
@ -45,8 +39,8 @@ const Home: React.FunctionComponent<Props> = () => {
|
|||
|
||||
try {
|
||||
await dispatch(login({ email, password }));
|
||||
} catch (e) {
|
||||
console.log('error while logging in', e);
|
||||
} catch (err) {
|
||||
console.log('error while logging in', err);
|
||||
|
||||
setErrMsg(e.message);
|
||||
setLoggingIn(false);
|
||||
|
|
@ -97,10 +91,11 @@ const Home: React.FunctionComponent<Props> = () => {
|
|||
</form>
|
||||
|
||||
<div className="actions">
|
||||
Don't have an account?{' '}
|
||||
Don't have an account?{' '}
|
||||
<a
|
||||
href="https://app.getdnote.com/join"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="signup"
|
||||
>
|
||||
Sign Up
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@
|
|||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable react/jsx-props-no-spreading */
|
||||
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
|
||||
import { useDispatch } from '../store/hooks';
|
||||
import { navigate } from '../store/location/actions';
|
||||
|
|
|
|||
|
|
@ -53,6 +53,11 @@ export default ({ toggleMenu, loggedIn, onLogout }) => (
|
|||
)}
|
||||
</ul>
|
||||
|
||||
<div className="menu-overlay" onClick={toggleMenu} />
|
||||
<div
|
||||
className="menu-overlay"
|
||||
onClick={toggleMenu}
|
||||
onKeyDown={() => {}}
|
||||
role="none"
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -17,23 +17,18 @@
|
|||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { findDOMNode } from 'react-dom';
|
||||
|
||||
import Link from './Link';
|
||||
import Flash from './Flash';
|
||||
import config from '../utils/config';
|
||||
import { updateSettings, resetSettings } from '../store/settings/actions';
|
||||
import { useDispatch, useSelector, useStore } from '../store/hooks';
|
||||
import services from '../utils/services';
|
||||
|
||||
interface Props {}
|
||||
|
||||
// isValidURL checks if the given string is a valid URL
|
||||
function isValidURL(url: string): boolean {
|
||||
var a = document.createElement('a');
|
||||
const a = document.createElement('a');
|
||||
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
|
||||
|
|
@ -49,11 +44,9 @@ function validateFormState({ apiUrl, webUrl }) {
|
|||
}
|
||||
|
||||
const Settings: React.FunctionComponent<Props> = () => {
|
||||
const { settings } = useSelector(state => {
|
||||
return {
|
||||
settings: state.settings
|
||||
};
|
||||
});
|
||||
const { settings } = useSelector(state => ({
|
||||
settings: state.settings
|
||||
}));
|
||||
const store = useStore();
|
||||
|
||||
const [apiUrl, setAPIUrl] = useState(settings.apiUrl);
|
||||
|
|
@ -66,10 +59,10 @@ const Settings: React.FunctionComponent<Props> = () => {
|
|||
dispatch(resetSettings());
|
||||
setSuccessMsg('Restored the default settings');
|
||||
|
||||
const { settings } = store.getState();
|
||||
const { settings: settingsState } = store.getState();
|
||||
|
||||
setAPIUrl(settings.apiUrl);
|
||||
setWebUrl(settings.webUrl);
|
||||
setAPIUrl(settingsState.apiUrl);
|
||||
setWebUrl(settingsState.webUrl);
|
||||
}
|
||||
|
||||
function handleSubmit(e) {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
* 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 {
|
||||
KEYCODE_ENTER,
|
||||
|
|
@ -25,7 +25,6 @@ import {
|
|||
} from 'jslib/helpers/keyboard';
|
||||
import Flash from './Flash';
|
||||
import ext from '../utils/ext';
|
||||
import config from '../utils/config';
|
||||
import BookIcon from './BookIcon';
|
||||
import { navigate } from '../store/location/actions';
|
||||
import { useSelector, useDispatch } from '../store/hooks';
|
||||
|
|
@ -34,43 +33,41 @@ const Success: React.FunctionComponent = () => {
|
|||
const [errorMsg, setErrorMsg] = useState('');
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const { location, settings } = useSelector(state => {
|
||||
return {
|
||||
location: state.location,
|
||||
settings: state.settings
|
||||
};
|
||||
});
|
||||
const { location, settings } = useSelector(state => ({
|
||||
location: state.location,
|
||||
settings: state.settings
|
||||
}));
|
||||
|
||||
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(() => {
|
||||
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);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleKeydown);
|
||||
};
|
||||
}, []);
|
||||
}, [dispatch, noteUUID, settings.webUrl]);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
|
|
@ -80,7 +77,10 @@ const Success: React.FunctionComponent = () => {
|
|||
<div>
|
||||
<BookIcon width={20} height={20} className="book-icon" />
|
||||
|
||||
<h1 className="heading">Saved to {bookName}</h1>
|
||||
<h1 className="heading">
|
||||
Saved to
|
||||
{bookName}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<ul className="key-list">
|
||||
|
|
@ -93,7 +93,8 @@ const Success: React.FunctionComponent = () => {
|
|||
<div className="key-desc">Open in browser</div>
|
||||
</li>
|
||||
<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>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -19,8 +19,6 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
import { createStore, applyMiddleware, compose } from 'redux';
|
||||
import thunkMiddleware from 'redux-thunk';
|
||||
|
||||
import { debounce } from 'jslib/helpers/perf';
|
||||
import configureStore from './store';
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
* 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 initServices from '../../utils/services';
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
*/
|
||||
|
||||
import { LOGIN, LOGOUT, AuthState, AuthActionType } from './types';
|
||||
import config from '../../utils/config';
|
||||
|
||||
const initialState: AuthState = {
|
||||
sessionKey: '',
|
||||
|
|
@ -34,8 +33,8 @@ export default function(
|
|||
|
||||
return {
|
||||
...state,
|
||||
sessionKey: sessionKey,
|
||||
sessionKeyExpiry: sessionKeyExpiry
|
||||
sessionKey,
|
||||
sessionKeyExpiry
|
||||
};
|
||||
}
|
||||
case LOGOUT:
|
||||
|
|
|
|||
|
|
@ -43,14 +43,14 @@ function initState(s: AppState | undefined): AppState {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
const { settings } = s;
|
||||
const { settings: settingsState } = s;
|
||||
|
||||
return {
|
||||
...s,
|
||||
settings: {
|
||||
...settings,
|
||||
apiUrl: settings.apiUrl || config.defaultApiEndpoint,
|
||||
webUrl: settings.webUrl || config.defaultWebUrl
|
||||
...settingsState,
|
||||
apiUrl: settingsState.apiUrl || config.defaultApiEndpoint,
|
||||
webUrl: settingsState.webUrl || config.defaultWebUrl
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,6 @@
|
|||
*/
|
||||
|
||||
import { UPDATE, RESET, UpdateAction, ResetAction } from './types';
|
||||
import { ThunkAction } from '../types';
|
||||
import initServices from '../../utils/services';
|
||||
|
||||
export function updateSettings(settings): UpdateAction {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
// module ext provides a cross-browser interface to access extension APIs
|
||||
// by using WebExtensions API if available, and using Chrome as a fallback.
|
||||
let ext: any = {};
|
||||
const ext: any = {};
|
||||
|
||||
const apis = ['tabs', 'storage', 'runtime'];
|
||||
|
||||
|
|
@ -40,9 +40,9 @@ for (let i = 0; i < apis.length; i++) {
|
|||
const fn = ext[api].create;
|
||||
|
||||
// Promisify chrome.tabs.create
|
||||
ext[api].create = function(obj) {
|
||||
ext[api].create = obj => {
|
||||
return new Promise(resolve => {
|
||||
fn(obj, function(tab) {
|
||||
fn(obj, tab => {
|
||||
resolve(tab);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ function checkStatus(response) {
|
|||
if (response.status >= 200 && response.status < 300) {
|
||||
return response;
|
||||
}
|
||||
return response.text().then((body) => {
|
||||
return response.text().then(body => {
|
||||
const error = new Error(body);
|
||||
error.response = response;
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ export function post(url, data, options = {}) {
|
|||
return request(url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
...options,
|
||||
...options
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -61,6 +61,6 @@ export function get(url, options = {}) {
|
|||
|
||||
return request(endpoint, {
|
||||
method: 'GET',
|
||||
...options,
|
||||
...options
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,13 +17,11 @@
|
|||
*/
|
||||
|
||||
import init from 'jslib/services';
|
||||
import config from './config';
|
||||
|
||||
const initServices = (baseUrl: string) => {
|
||||
return init({
|
||||
baseUrl: baseUrl,
|
||||
const initServices = (baseUrl: string) =>
|
||||
init({
|
||||
baseUrl,
|
||||
pathPrefix: ''
|
||||
});
|
||||
};
|
||||
|
||||
export default initServices;
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"sourceMap": true,
|
||||
"esModuleInterop": true,
|
||||
"noImplicitAny": false,
|
||||
"module": "es6",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"target": "es6",
|
||||
"jsx": "react",
|
||||
"esModuleInterop": true,
|
||||
"allowJs": true,
|
||||
"target": "es5",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"jslib/*": [
|
||||
|
|
|
|||
47
jslib/package-lock.json
generated
47
jslib/package-lock.json
generated
|
|
@ -472,12 +472,6 @@
|
|||
"jest-diff": "^24.3.0"
|
||||
}
|
||||
},
|
||||
"@types/mocha": {
|
||||
"version": "5.2.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz",
|
||||
"integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/stack-utils": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz",
|
||||
|
|
@ -1598,8 +1592,7 @@
|
|||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
|
|
@ -1620,14 +1613,12 @@
|
|||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
|
|
@ -1642,20 +1633,17 @@
|
|||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
|
|
@ -1772,8 +1760,7 @@
|
|||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
|
|
@ -1785,7 +1772,6 @@
|
|||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
|
|
@ -1800,7 +1786,6 @@
|
|||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
|
|
@ -1808,14 +1793,12 @@
|
|||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.3.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
|
|
@ -1834,7 +1817,6 @@
|
|||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
|
|
@ -1915,8 +1897,7 @@
|
|||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
|
|
@ -1928,7 +1909,6 @@
|
|||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
|
|
@ -2014,8 +1994,7 @@
|
|||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
|
|
@ -2051,7 +2030,6 @@
|
|||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
|
|
@ -2071,7 +2049,6 @@
|
|||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
|
|
@ -2115,14 +2092,12 @@
|
|||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.3",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"name": "jslib",
|
||||
"repository": "https://github.com/dnote/dnote",
|
||||
"version": "0.1.0",
|
||||
"description": "An internal JavaScript SDK for Dnote",
|
||||
"types": "dist/types/index.d.ts",
|
||||
|
|
@ -8,7 +9,8 @@
|
|||
"build": "tsc",
|
||||
"build:watch": "tsc --watch",
|
||||
"test": "jest --coverage",
|
||||
"test:watch": "jest --watch"
|
||||
"test:watch": "jest --watch",
|
||||
"lint": "../node_modules/.bin/eslint ./src --ext .ts,.tsx,.js"
|
||||
},
|
||||
"author": "Monomax Software Pty Ltd",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
|
@ -20,7 +22,6 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^24.0.23",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"jest": "^24.9.0",
|
||||
"prettier": "^1.19.1",
|
||||
"ts-jest": "^24.1.0",
|
||||
|
|
|
|||
|
|
@ -33,5 +33,5 @@ export {
|
|||
QueriesHelpers,
|
||||
SearchHelpers,
|
||||
SelectHelpers,
|
||||
UrlHelpers,
|
||||
}
|
||||
UrlHelpers
|
||||
};
|
||||
|
|
|
|||
|
|
@ -16,10 +16,6 @@
|
|||
* 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';
|
||||
|
||||
export interface Queries {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,4 @@
|
|||
import * as Helpers from './helpers';
|
||||
import * as Operations from './helpers';
|
||||
|
||||
export {
|
||||
Helpers,
|
||||
Operations
|
||||
};
|
||||
export { Helpers, Operations };
|
||||
|
|
|
|||
1975
package-lock.json
generated
Normal file
1975
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
22
package.json
Normal file
22
package.json
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"name": "dnote",
|
||||
"repository": "https://github.com/dnote/dnote",
|
||||
"version": "0.1.0",
|
||||
"description": "Dnote monorepo",
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^2.8.0",
|
||||
"@typescript-eslint/parser": "^2.8.0",
|
||||
"babel-eslint": "^10.0.3",
|
||||
"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",
|
||||
"prettier": "^1.19.1",
|
||||
"typescript": "^3.7.2"
|
||||
}
|
||||
}
|
||||
|
|
@ -21,7 +21,7 @@ appPath="$basePath/web"
|
|||
|
||||
ROOT_URL=$ROOT_URL \
|
||||
VERSION="$VERSION" \
|
||||
"$appPath"/node_modules/.bin/webpack-dev-server\
|
||||
"$appPath"/node_modules/.bin/webpack-dev-server \
|
||||
--env.isTest="$IS_TEST" \
|
||||
--host "$WEBPACK_HOST" \
|
||||
--config "$appPath"/webpack/dev.config.js
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"env": {
|
||||
"browser": true,
|
||||
"node": true,
|
||||
"mocha": true
|
||||
"jest": true
|
||||
},
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"rules": {
|
||||
|
|
@ -38,9 +38,10 @@
|
|||
"react/jsx-wrap-multilines": ["error", {"declaration": false, "assignment": false}],
|
||||
"react/jsx-one-expression-per-line": 0,
|
||||
"@typescript-eslint/no-unused-vars": 1,
|
||||
"import/no-extraneous-dependencies": ["error", {"devDependencies": ["**/*_test.ts"]}],
|
||||
"import/no-extraneous-dependencies": ["error", {"devDependencies": ["**/*_test.ts", "**/webpack/**/*"]}],
|
||||
"lines-between-class-members": 0,
|
||||
"react/jsx-fragments": 0
|
||||
"react/jsx-fragments": 0,
|
||||
"jsx-a11y/label-has-associated-control": 0
|
||||
},
|
||||
"plugins": [
|
||||
"react", "react-hooks", "import", "prettier", "@typescript-eslint"
|
||||
|
|
@ -62,6 +63,7 @@
|
|||
"__STRIPE_PUBLIC_KEY__": true,
|
||||
"__ROOT_URL__": true,
|
||||
"__CDN_URL__": true,
|
||||
"__VERSION__": true,
|
||||
"socket": true,
|
||||
"webpackIsomorphicTools": true,
|
||||
"StripeCheckout": true
|
||||
|
|
|
|||
1211
web/package-lock.json
generated
1211
web/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -7,7 +7,7 @@
|
|||
"scripts": {
|
||||
"test": "jest --coverage",
|
||||
"test:watch": "jest --watch",
|
||||
"lint": "eslint ./src/**/*.ts ./src/**/*.tsx"
|
||||
"lint": "../node_modules/.bin/eslint ./src --ext .ts,.tsx,.js"
|
||||
},
|
||||
"author": "Monomax Software Pty Ltd",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
|
@ -22,21 +22,10 @@
|
|||
"@types/jest": "^24.0.23",
|
||||
"@types/react": "^16.9.11",
|
||||
"@types/react-dom": "^16.9.4",
|
||||
"@typescript-eslint/eslint-plugin": "^2.8.0",
|
||||
"@typescript-eslint/parser": "^2.8.0",
|
||||
"autoprefixer": "^9.7.2",
|
||||
"babel-eslint": "^10.0.3",
|
||||
"babel-loader": "^8.0.6",
|
||||
"css-loader": "^3.2.0",
|
||||
"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",
|
||||
"file-loader": "^4.3.0",
|
||||
"jest": "^24.9.0",
|
||||
|
|
|
|||
|
|
@ -16,42 +16,41 @@
|
|||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useState, useEffect, Fragment } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import { hot } from 'react-hot-loader/root';
|
||||
import { Switch, Route } from 'react-router';
|
||||
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
||||
import React, { Fragment, useEffect, useState } from 'react';
|
||||
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 {
|
||||
homePathDef,
|
||||
notePathDef,
|
||||
noHeaderPaths,
|
||||
subscriptionPaths,
|
||||
noFooterPaths,
|
||||
checkCurrentPath,
|
||||
checkCurrentPathIn,
|
||||
checkCurrentPath
|
||||
homePathDef,
|
||||
noFooterPaths,
|
||||
noHeaderPaths,
|
||||
notePathDef,
|
||||
subscriptionPaths
|
||||
} from 'web/libs/paths';
|
||||
import { getFiltersFromSearchStr } from 'jslib/helpers/filters';
|
||||
import Splash from '../Splash';
|
||||
import render from '../../routes';
|
||||
import { useDispatch, useSelector } from '../../store';
|
||||
import { getCurrentUser } from '../../store/auth';
|
||||
import { getBooks } from '../../store/books';
|
||||
import { updatePage, updateQuery } from '../../store/filters';
|
||||
import { setPrevLocation } from '../../store/route';
|
||||
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 styles from './App.scss';
|
||||
import { updateQuery, updatePage } from '../../store/filters';
|
||||
|
||||
import SystemMessage from '../Common/SystemMessage';
|
||||
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 styles from './App.scss';
|
||||
import HeaderData from './HeaderData';
|
||||
|
||||
interface Props extends RouteComponentProps<any> {}
|
||||
|
||||
|
|
@ -198,7 +197,7 @@ const App: React.FunctionComponent<Props> = ({ location }) => {
|
|||
<Route path={noFooterPaths} exact component={null} />
|
||||
<Route
|
||||
path="/"
|
||||
render={({ location }) => {
|
||||
render={() => {
|
||||
if (noFooter) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,11 +16,9 @@
|
|||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useState, useRef } from 'react';
|
||||
import classnames from 'classnames';
|
||||
|
||||
import React, { useRef, useState } from 'react';
|
||||
import ItemActions from '../../Common/ItemActions';
|
||||
import DotsIcon from '../../Icons/Dots';
|
||||
import ItemActionsStyles from '../../Common/ItemActions/ItemActions.scss';
|
||||
import styles from './Actions.scss';
|
||||
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ import classnames from 'classnames';
|
|||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { getHomePath } from 'web/libs/paths';
|
||||
import Actions from './Actions';
|
||||
import { BookData } from 'jslib/operations/types';
|
||||
import Actions from './Actions';
|
||||
|
||||
import styles from './BookItem.scss';
|
||||
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@
|
|||
import React, { Fragment } from 'react';
|
||||
import classnames from 'classnames';
|
||||
|
||||
import { BookData } from 'jslib/operations/types';
|
||||
import BookItem from './BookItem';
|
||||
import BookHolder from './BookHolder';
|
||||
import { BookData } from 'jslib/operations/types';
|
||||
import styles from './BookList.scss';
|
||||
|
||||
function Placeholder() {
|
||||
|
|
|
|||
|
|
@ -34,10 +34,8 @@ import CreateBookModal from './CreateBookModal';
|
|||
import BookList from './BookList';
|
||||
import EmptyList from './EmptyList';
|
||||
import SearchInput from '../Common/SearchInput';
|
||||
import Button from '../Common/Button';
|
||||
import DeleteBookModal from './DeleteBookModal';
|
||||
import { usePrevious } from '../../libs/hooks';
|
||||
import BookPlusIcon from '../Icons/BookPlus';
|
||||
import CreateBookButton from './CreateBookButton';
|
||||
import styles from './Content.scss';
|
||||
|
||||
|
|
@ -91,14 +89,17 @@ function useFocusInputOnReset(
|
|||
inputRef.current.focus();
|
||||
}
|
||||
}
|
||||
}, [searchValue, inputRef]);
|
||||
}, [searchValue, inputRef, prevSearchValue]);
|
||||
}
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
setSuccessMessage: (string) => void;
|
||||
}
|
||||
|
||||
const Content: React.FunctionComponent<Props> = ({ history, setSuccessMessage }) => {
|
||||
const Content: React.FunctionComponent<Props> = ({
|
||||
history,
|
||||
setSuccessMessage
|
||||
}) => {
|
||||
const { books } = useSelector(state => {
|
||||
return {
|
||||
books: state.books
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@
|
|||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { Fragment, useState, useEffect, useRef } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import React from 'react';
|
||||
import BookPlusIcon from '../Icons/BookPlus';
|
||||
import styles from './CreateBookButton.scss';
|
||||
|
||||
|
|
|
|||
|
|
@ -20,12 +20,12 @@ import React, { useState } from 'react';
|
|||
import classnames from 'classnames';
|
||||
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
||||
|
||||
import { checkDuplicate, validateBookName } from 'jslib/helpers/books';
|
||||
import Modal, { Header, Body } from '../Common/Modal';
|
||||
import { createBook } from '../../store/books';
|
||||
import { useSelector, useDispatch } from '../../store';
|
||||
import Button from '../Common/Button';
|
||||
import Flash from '../Common/Flash';
|
||||
import { checkDuplicate, validateBookName } from 'jslib/helpers/books';
|
||||
|
||||
import styles from './CreateBookModal.scss';
|
||||
|
||||
|
|
|
|||
|
|
@ -20,17 +20,17 @@ import React from 'react';
|
|||
import { Switch, Route } from 'react-router';
|
||||
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 {
|
||||
ClassicMigrationSteps,
|
||||
getClassicMigrationPath,
|
||||
getHomePath,
|
||||
homePathDef
|
||||
} 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 {}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@
|
|||
* 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 classnames from 'classnames';
|
||||
|
||||
|
|
|
|||
|
|
@ -16,14 +16,13 @@
|
|||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import classnames from 'classnames';
|
||||
|
||||
import { KEYCODE_ENTER } from 'jslib/helpers/keyboard';
|
||||
import React, { useState } from 'react';
|
||||
import { useDispatch } from '../../../store';
|
||||
import { flushContent } from '../../../store/editor';
|
||||
import { AppState, useDispatch } from '../../../store';
|
||||
import styles from './Textarea.scss';
|
||||
import editorStyles from './Editor.scss';
|
||||
import styles from './Textarea.scss';
|
||||
|
||||
interface Props {
|
||||
sessionKey: string;
|
||||
|
|
|
|||
|
|
@ -16,9 +16,8 @@
|
|||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useState, useRef } from 'react';
|
||||
import classnames from 'classnames';
|
||||
|
||||
import React from 'react';
|
||||
import Menu, { MenuOption } from '../../Common/Menu';
|
||||
import DotsIcon from '../../Icons/Dots';
|
||||
import styles from './ItemActions.scss';
|
||||
|
|
@ -59,8 +58,8 @@ const ItemActions: React.FunctionComponent<Props> = ({
|
|||
menuId={id}
|
||||
triggerId={triggerId}
|
||||
triggerContent={<DotsIcon width={12} height={12} />}
|
||||
triggerClassName={styles['trigger']}
|
||||
contentClassName={styles['content']}
|
||||
triggerClassName={styles.trigger}
|
||||
contentClassName={styles.content}
|
||||
alignment="right"
|
||||
direction="bottom"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,11 @@ interface Props {
|
|||
onDismiss: () => void;
|
||||
}
|
||||
|
||||
const Header: React.FunctionComponent<Props> = ({ labelId, heading, onDismiss }) => {
|
||||
const Header: React.FunctionComponent<Props> = ({
|
||||
labelId,
|
||||
heading,
|
||||
onDismiss
|
||||
}) => {
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<strong id={labelId}>{heading}</strong>
|
||||
|
|
|
|||
|
|
@ -16,16 +16,14 @@
|
|||
* 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 { booksToOptions, filterOptions, Option } from 'jslib/helpers/select';
|
||||
import { KEYCODE_BACKSPACE } from 'jslib/helpers/keyboard';
|
||||
import { useSearchMenuKeydown, useScrollToFocused } from 'web/libs/hooks/dom';
|
||||
import { useSelector } from '../../store';
|
||||
import { filterOptions, Option } from 'jslib/helpers/select';
|
||||
import { useScrollToFocused, useSearchMenuKeydown } from 'web/libs/hooks/dom';
|
||||
import PopoverContent from '../Common/Popover/PopoverContent';
|
||||
import CloseIcon from '../Icons/Close';
|
||||
import { usePrevious } from 'web/libs/hooks';
|
||||
import styles from './MultiSelect.scss';
|
||||
|
||||
function getTextInputWidth(term: string, active: boolean) {
|
||||
|
|
@ -140,6 +138,8 @@ const MultiSelect: React.FunctionComponent<Props> = ({
|
|||
'form-select-disabled': disabled
|
||||
})}
|
||||
ref={wrapperRef}
|
||||
tabIndex={-1}
|
||||
role="listbox"
|
||||
onClick={() => {
|
||||
if (inputRef.current) {
|
||||
inputRef.current.focus();
|
||||
|
|
@ -147,6 +147,7 @@ const MultiSelect: React.FunctionComponent<Props> = ({
|
|||
|
||||
// setIsOpen(!isOpen);
|
||||
}}
|
||||
onKeyDown={() => {}}
|
||||
>
|
||||
<ul className={styles['current-options']}>
|
||||
<span
|
||||
|
|
@ -185,9 +186,11 @@ const MultiSelect: React.FunctionComponent<Props> = ({
|
|||
type="text"
|
||||
id={textInputId}
|
||||
ref={el => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
inputRef.current = el;
|
||||
|
||||
if (inputInnerRef) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
inputInnerRef.current = el;
|
||||
}
|
||||
}}
|
||||
|
|
@ -237,7 +240,7 @@ const MultiSelect: React.FunctionComponent<Props> = ({
|
|||
closeOnEscapeKeydown
|
||||
>
|
||||
<ul
|
||||
className={classnames(styles['suggestion'], 'list-unstyled')}
|
||||
className={classnames(styles.suggestion, 'list-unstyled')}
|
||||
ref={listRef}
|
||||
>
|
||||
{filteredOptions.map((o, idx) => {
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ import React, { Fragment } from 'react';
|
|||
import classnames from 'classnames';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import LockIcon from '../Icons/Lock';
|
||||
import { getSubscriptionPath } from 'web/libs/paths';
|
||||
import LockIcon from '../Icons/Lock';
|
||||
import { useSelector } from '../../store';
|
||||
|
||||
import styles from './PayWall.scss';
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@
|
|||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable jsx-a11y/control-has-associated-label */
|
||||
|
||||
import React from 'react';
|
||||
import classnames from 'classnames';
|
||||
|
||||
|
|
|
|||
|
|
@ -20,14 +20,14 @@ import React, { useState, useRef, useEffect } from 'react';
|
|||
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
||||
import classnames from 'classnames';
|
||||
|
||||
import Result from './Result';
|
||||
import { makeOptionId, getOptIdxByValue } from '../../../helpers/accessibility';
|
||||
import { Option, filterOptions } from 'jslib/helpers/select';
|
||||
import {
|
||||
useScrollToFocused,
|
||||
useScrollToSelected,
|
||||
useSearchMenuKeydown
|
||||
} from 'web/libs/hooks/dom';
|
||||
import Result from './Result';
|
||||
import { makeOptionId, getOptIdxByValue } from '../../../helpers/accessibility';
|
||||
import styles from './SearchableMenu.scss';
|
||||
|
||||
function useFocusedIdx(options: Option[], currentValue) {
|
||||
|
|
|
|||
|
|
@ -196,8 +196,5 @@ const mapDispatchToProps = {
|
|||
};
|
||||
|
||||
export default withRouter(
|
||||
connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(SettingsSidebar)
|
||||
connect(mapStateToProps, mapDispatchToProps)(SettingsSidebar)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -17,16 +17,11 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
msToHTMLTimeDuration,
|
||||
getMonthName,
|
||||
getUTCOffset
|
||||
} from '../../helpers/time';
|
||||
import { msToHTMLTimeDuration } from '../../helpers/time';
|
||||
import formatTime from '../../helpers/time/format';
|
||||
import Tooltip from './Tooltip';
|
||||
import { Alignment, Direction } from '../Common/Popover/types';
|
||||
import styles from './Time.scss';
|
||||
import Tooltip from './Tooltip';
|
||||
|
||||
interface ContentProps {
|
||||
text: string;
|
||||
|
|
|
|||
|
|
@ -16,9 +16,8 @@
|
|||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import classnames from 'classnames';
|
||||
|
||||
import React from 'react';
|
||||
import styles from './Toggle.scss';
|
||||
|
||||
interface Props {
|
||||
|
|
@ -56,7 +55,7 @@ const Toggle: React.FunctionComponent<Props> = ({
|
|||
/>
|
||||
|
||||
<div className={classnames(styles.toggle, {})}>
|
||||
<div className={styles.indicator}></div>
|
||||
<div className={styles.indicator} />
|
||||
</div>
|
||||
|
||||
{label}
|
||||
|
|
|
|||
|
|
@ -16,17 +16,9 @@
|
|||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { Fragment, useState, useRef } from 'react';
|
||||
import classnames from 'classnames';
|
||||
|
||||
import Overlay from './Overlay';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { Alignment, Direction } from '../Popover/types';
|
||||
import { isMobileWidth } from 'web/libs/dom';
|
||||
import {
|
||||
KEYCODE_ESC,
|
||||
KEYCODE_ENTER,
|
||||
KEYCODE_SPACE
|
||||
} from 'jslib/helpers/keyboard';
|
||||
import Overlay from './Overlay';
|
||||
|
||||
interface Props {
|
||||
id: string;
|
||||
|
|
@ -51,7 +43,6 @@ const Tooltip: React.FunctionComponent<Props> = ({
|
|||
}) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const triggerRef = useRef(null);
|
||||
const touchingRef = useRef(false);
|
||||
|
||||
function show() {
|
||||
setIsOpen(true);
|
||||
|
|
|
|||
|
|
@ -16,20 +16,17 @@
|
|||
* 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 { 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 React, { useRef, useState } from 'react';
|
||||
import { Prompt, RouteComponentProps, withRouter } from 'react-router-dom';
|
||||
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 { resetEditor, EditorSession } from '../../store/editor';
|
||||
import { createBook } from '../../store/books';
|
||||
import { EditorSession, resetEditor } from '../../store/editor';
|
||||
import { setMessage } from '../../store/ui';
|
||||
import Editor from '../Common/Editor';
|
||||
import styles from '../New/New.scss';
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
|
|
|
|||
|
|
@ -16,19 +16,17 @@
|
|||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import { Prompt, RouteComponentProps } from 'react-router-dom';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
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 operations from 'web/libs/operations';
|
||||
import Flash from '../Common/Flash';
|
||||
import { useDispatch, useSelector } from '../../store';
|
||||
import { createSession } from '../../store/editor';
|
||||
import Content from './Content';
|
||||
import Flash from '../Common/Flash';
|
||||
import styles from '../New/New.scss';
|
||||
import Content from './Content';
|
||||
|
||||
interface Match {
|
||||
noteUUID: string;
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@
|
|||
import React from 'react';
|
||||
import classnames from 'classnames';
|
||||
|
||||
import Item from './Item';
|
||||
import { getNewPath, getBooksPath, getRepetitionsPath } from 'web/libs/paths';
|
||||
import { Filters, toSearchObj } from 'jslib/helpers/filters';
|
||||
import Item from './Item';
|
||||
import styles from './Nav.scss';
|
||||
|
||||
interface Props {
|
||||
|
|
|
|||
|
|
@ -19,9 +19,8 @@
|
|||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import LogoWithText from '../../Icons/LogoWithText';
|
||||
import Logo from '../../Icons/Logo';
|
||||
import { getHomePath } from 'web/libs/paths';
|
||||
import LogoWithText from '../../Icons/LogoWithText';
|
||||
import styles from './Guest.scss';
|
||||
|
||||
const UserNoteHeader: React.FunctionComponent = () => {
|
||||
|
|
|
|||
|
|
@ -20,16 +20,12 @@ import React, { useState, useRef, useEffect } from 'react';
|
|||
import classnames from 'classnames';
|
||||
|
||||
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 PopoverContent from '../../../Common/Popover/PopoverContent';
|
||||
import { usePrevious } from 'web/libs/hooks';
|
||||
import styles from './AdvancedPanel.scss';
|
||||
|
||||
import {
|
||||
useScrollToFocused,
|
||||
useSearchMenuKeydown
|
||||
} from 'web/libs/hooks/dom';
|
||||
|
||||
interface Props {
|
||||
value: string;
|
||||
setValue: (string) => void;
|
||||
|
|
@ -117,7 +113,11 @@ function useSetSuggestionVisibility(
|
|||
}, [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 [focusedIdx, setFocusedIdx] = useState(0);
|
||||
const [focusedOptEl, setFocusedOptEl] = useState(null);
|
||||
|
|
|
|||
|
|
@ -27,7 +27,11 @@ interface Props {
|
|||
disabled: boolean;
|
||||
}
|
||||
|
||||
const WordsSearch: React.FunctionComponent<Props> = ({ words, setWords, disabled }) => {
|
||||
const WordsSearch: React.FunctionComponent<Props> = ({
|
||||
words,
|
||||
setWords,
|
||||
disabled
|
||||
}) => {
|
||||
return (
|
||||
<section className={styles.section}>
|
||||
<label htmlFor="has-words" className={styles.label}>
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ import React from 'react';
|
|||
import classnames from 'classnames';
|
||||
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useFilters, useSelector } from '../../../store';
|
||||
import { getHomePath } from 'web/libs/paths';
|
||||
import { useFilters, useSelector } from '../../../store';
|
||||
import CaretIcon from '../../Icons/Caret';
|
||||
import styles from './Paginator.scss';
|
||||
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ const NoteItem: React.FunctionComponent<Props> = ({ note, filters }) => {
|
|||
<Time
|
||||
id={`${note.uuid}-ts`}
|
||||
text={timeAgo(nanosecToMillisec(note.added_on))}
|
||||
mobileText={timeAgo(nanosecToMillisec(note.added_on), true)}
|
||||
mobileText={timeAgo(nanosecToMillisec(note.added_on))}
|
||||
ms={nanosecToMillisec(note.added_on)}
|
||||
wrapperClassName={styles.ts}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -37,7 +37,11 @@ interface Props {
|
|||
filters: Filters;
|
||||
}
|
||||
|
||||
const NoteGroup: React.FunctionComponent<Props> = ({ group, isFirst, filters }) => {
|
||||
const NoteGroup: React.FunctionComponent<Props> = ({
|
||||
group,
|
||||
isFirst,
|
||||
filters
|
||||
}) => {
|
||||
const { year, month } = group;
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -17,10 +17,9 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { IconProps } from './types';
|
||||
|
||||
const Icon = ({ fill, width, height, className }: IconProps) => {
|
||||
const Icon = ({ fill, width, height }: IconProps) => {
|
||||
const h = `${height}px`;
|
||||
const w = `${width}px`;
|
||||
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { getPasswordResetRequestPath } from 'web/libs/paths';
|
||||
import styles from '../Common/Auth.scss';
|
||||
import Button from '../Common/Button';
|
||||
import { getPasswordResetRequestPath } from 'web/libs/paths';
|
||||
|
||||
interface Props {
|
||||
email: string;
|
||||
|
|
|
|||
|
|
@ -16,23 +16,20 @@
|
|||
* 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 React, { Fragment, useEffect, useRef, useState } from 'react';
|
||||
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 { getEditorSessionkey } from 'web/libs/editor';
|
||||
import { useFocus } from 'web/libs/hooks/dom';
|
||||
import operations from 'web/libs/operations';
|
||||
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 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 styles from './New.scss';
|
||||
|
||||
|
|
@ -54,10 +51,14 @@ function useInitFocus({ bookLabel, content, textareaRef, setTriggerFocus }) {
|
|||
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 [errMessage, setErrMessage] = useState('');
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
|
|
|
|||
|
|
@ -16,28 +16,16 @@
|
|||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useState, useRef, useEffect, Fragment } from 'react';
|
||||
import { RouteComponentProps } 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 React, { Fragment, useEffect } from 'react';
|
||||
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
||||
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 { resetEditor, createSession } from '../../store/editor';
|
||||
import { createBook } from '../../store/books';
|
||||
import { setMessage } from '../../store/ui';
|
||||
import { createSession } from '../../store/editor';
|
||||
import Content from './Content';
|
||||
import styles from './New.scss';
|
||||
|
||||
interface Props extends RouteComponentProps {}
|
||||
|
||||
const New: React.FunctionComponent<Props> = ({ history }) => {
|
||||
const New: React.FunctionComponent<Props> = () => {
|
||||
const sessionKey = getEditorSessionkey(null);
|
||||
const { editor } = useSelector(state => {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import { tokenize, TokenKind } from 'web/libs/fts/lexer';
|
|||
import BookIcon from '../Icons/Book';
|
||||
import GlobeIcon from '../Icons/Globe';
|
||||
import { parseMarkdown } from '../../helpers/markdown';
|
||||
import { nanosecToMillisec, getMonthName } from '../../helpers/time';
|
||||
import { nanosecToMillisec } from '../../helpers/time';
|
||||
import formatTime from '../../helpers/time/format';
|
||||
import { useSelector } from '../../store';
|
||||
import Time from '../Common/Time';
|
||||
|
|
|
|||
|
|
@ -17,10 +17,7 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import classnames from 'classnames';
|
||||
|
||||
import Button from '../../Common/Button';
|
||||
import styles from './ShareModal.scss';
|
||||
|
||||
interface Props {
|
||||
kind: string;
|
||||
|
|
|
|||
|
|
@ -16,20 +16,18 @@
|
|||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
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 React, { useState } from 'react';
|
||||
import { copyToClipboard, selectTextInputValue } from 'web/libs/dom';
|
||||
import operations from 'web/libs/operations';
|
||||
import Modal, { Header, Body } from '../../Common/Modal';
|
||||
import Flash from '../../Common/Flash';
|
||||
import { setMessage } from '../../../store/ui';
|
||||
import { getNotePath } from 'web/libs/paths';
|
||||
import { useDispatch } from '../../../store';
|
||||
import Button from '../../Common/Button';
|
||||
import Toggle from '../../Common/Toggle';
|
||||
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 styles from './ShareModal.scss';
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
||||
|
||||
import { notePathDef } from 'web/libs/paths';
|
||||
import { parseSearchString } from 'jslib/helpers/url';
|
||||
import Content from './Content';
|
||||
import Flash from '../Common/Flash';
|
||||
|
|
|
|||
|
|
@ -36,7 +36,10 @@ interface 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 [submitting, setSubmitting] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ const Content: React.FunctionComponent<Props> = ({
|
|||
|
||||
return (
|
||||
<div>
|
||||
<p>Toggle the repetition for "{data.title}"</p>
|
||||
<p>Toggle the repetition for "{data.title}"</p>
|
||||
|
||||
<form id="T-pref-repetition-form" onSubmit={handleSubmit}>
|
||||
<div>
|
||||
|
|
@ -72,8 +72,7 @@ const Content: React.FunctionComponent<Props> = ({
|
|||
name="repetition"
|
||||
value="off"
|
||||
checked={!isEnabled}
|
||||
onChange={e => {
|
||||
const val = e.target.value;
|
||||
onChange={() => {
|
||||
setIsEnabled(false);
|
||||
}}
|
||||
/>
|
||||
|
|
@ -89,8 +88,7 @@ const Content: React.FunctionComponent<Props> = ({
|
|||
name="repetition"
|
||||
value="on"
|
||||
checked={isEnabled}
|
||||
onChange={e => {
|
||||
const val = e.target.value;
|
||||
onChange={() => {
|
||||
setIsEnabled(true);
|
||||
}}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -16,18 +16,15 @@
|
|||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
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 { 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 { 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 styles from './EmailPreferenceRepetition.scss';
|
||||
|
||||
|
|
@ -36,7 +33,10 @@ interface 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 [isFetching, setIsFetching] = useState(false);
|
||||
const [successMsg, setSuccessMsg] = useState('');
|
||||
|
|
@ -67,7 +67,7 @@ const EmailPreferenceRepetition: React.FunctionComponent<Props> = ({ location, m
|
|||
|
||||
setIsFetching(false);
|
||||
});
|
||||
}, [data, setData, setFailureMsg, setIsFetching]);
|
||||
}, [data, repetitionUUID, setData, setFailureMsg, setIsFetching, token]);
|
||||
|
||||
const isFetched = data !== null;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import React, { Fragment, useState, useEffect } from 'react';
|
||||
|
||||
import { getRepetitionRules } from '../../store/repetitionRules';
|
||||
import { useDispatch, useSelector } from '../../store';
|
||||
import classnames from 'classnames';
|
||||
import {
|
||||
getNewRepetitionPath,
|
||||
|
|
@ -10,6 +8,8 @@ import {
|
|||
repetitionsPathDef
|
||||
} from 'web/libs/paths';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useDispatch, useSelector } from '../../store';
|
||||
import { getRepetitionRules } from '../../store/repetitionRules';
|
||||
import RepetitionList from './RepetitionList';
|
||||
import DeleteRepetitionRuleModal from './DeleteRepetitionRuleModal';
|
||||
import Flash from '../Common/Flash';
|
||||
|
|
|
|||
|
|
@ -16,17 +16,15 @@
|
|||
* 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 Modal, { Header, Body } from '../Common/Modal';
|
||||
import Flash from '../Common/Flash';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
||||
import services from 'web/libs/services';
|
||||
import { useDispatch, useSelector } from '../../store';
|
||||
import { removeRepetitionRule } from '../../store/repetitionRules';
|
||||
import { useSelector, useDispatch } from '../../store';
|
||||
import Button from '../Common/Button';
|
||||
import Flash from '../Common/Flash';
|
||||
import Modal, { Body, Header } from '../Common/Modal';
|
||||
import styles from './DeleteRepetitionRuleModal.scss';
|
||||
|
||||
function getRepetitionRuleByUUID(
|
||||
|
|
@ -73,7 +71,6 @@ const DeleteRepetitionModal: React.FunctionComponent<Props> = ({
|
|||
);
|
||||
|
||||
const labelId = 'delete-rule-modal-label';
|
||||
const nameInputId = 'delete-rule-modal-name-input';
|
||||
const descId = 'delete-rule-modal-desc';
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -16,16 +16,15 @@
|
|||
* 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 { RepetitionRuleData } from 'jslib/operations/types';
|
||||
import React from 'react';
|
||||
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
||||
import { getRepetitionsPath, repetitionsPathDef } from 'web/libs/paths';
|
||||
import Form, { FormState, serializeFormState } from '../Form';
|
||||
import services from 'web/libs/services';
|
||||
import { useDispatch } from '../../../store';
|
||||
import { setMessage } from '../../../store/ui';
|
||||
import Form, { FormState, serializeFormState } from '../Form';
|
||||
|
||||
interface Props extends RouteComponentProps {
|
||||
setErrMsg: (string) => void;
|
||||
|
|
|
|||
|
|
@ -16,20 +16,17 @@
|
|||
* 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 { getRepetitionsPath, repetitionsPathDef } from 'web/libs/paths';
|
||||
import { BookDomain, RepetitionRuleData } from 'jslib/operations/types';
|
||||
import { RepetitionRuleData } from 'jslib/operations/types';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
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 { createRepetitionRule } from '../../../store/repetitionRules';
|
||||
import { useDispatch } from '../../../store';
|
||||
import Flash from '../../Common/Flash';
|
||||
import { setMessage } from '../../../store/ui';
|
||||
import Content from './Content';
|
||||
import repetitionStyles from '../Repetition.scss';
|
||||
import Content from './Content';
|
||||
|
||||
interface Match {
|
||||
repetitionUUID: string;
|
||||
|
|
@ -37,7 +34,7 @@ interface Match {
|
|||
|
||||
interface Props extends RouteComponentProps<Match> {}
|
||||
|
||||
const EditRepetition: React.FunctionComponent<Props> = ({ history, match }) => {
|
||||
const EditRepetition: React.FunctionComponent<Props> = ({ match }) => {
|
||||
const dispatch = useDispatch();
|
||||
const [errMsg, setErrMsg] = useState('');
|
||||
const [data, setData] = useState<RepetitionRuleData | null>(null);
|
||||
|
|
|
|||
|
|
@ -16,21 +16,20 @@
|
|||
* 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 { 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 { CreateParams } from 'jslib/services/repetitionRules';
|
||||
import Modal, { Header, Body } from '../../Common/Modal';
|
||||
import { useSelector } from '../../../store';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { getRepetitionsPath } from 'web/libs/paths';
|
||||
import { daysToMs } from '../../../helpers/time';
|
||||
import { useSelector } from '../../../store';
|
||||
import Button from '../../Common/Button';
|
||||
import modalStyles from '../../Common/Modal/Modal.scss';
|
||||
import MultiSelect from '../../Common/MultiSelect';
|
||||
import styles from './Form.scss';
|
||||
import modalStyles from '../../Common/Modal/Modal.scss';
|
||||
|
||||
export interface FormState {
|
||||
title: string;
|
||||
|
|
@ -72,6 +71,8 @@ interface Props {
|
|||
cancelPath?: string;
|
||||
initialState?: FormState;
|
||||
isEditing?: boolean;
|
||||
// TODO: implement inProgress
|
||||
inProgress?: boolean;
|
||||
}
|
||||
|
||||
enum Action {
|
||||
|
|
@ -162,9 +163,9 @@ const Form: React.FunctionComponent<Props> = ({
|
|||
setErrMsg,
|
||||
cancelPath = getRepetitionsPath(),
|
||||
initialState = formInitialState,
|
||||
isEditing = false
|
||||
isEditing = false,
|
||||
inProgress = false
|
||||
}) => {
|
||||
const [inProgress, setInProgress] = useState(false);
|
||||
const bookSelectorInputRef = useRef(null);
|
||||
const [formState, formDispatch] = useReducer(formReducer, initialState);
|
||||
const { books } = useSelector(state => {
|
||||
|
|
@ -200,10 +201,8 @@ const Form: React.FunctionComponent<Props> = ({
|
|||
if (bookSelectorInputRef.current) {
|
||||
bookSelectorInputRef.current.blur();
|
||||
}
|
||||
} else {
|
||||
if (bookSelectorInputRef.current) {
|
||||
bookSelectorInputRef.current.focus();
|
||||
}
|
||||
} else if (bookSelectorInputRef.current) {
|
||||
bookSelectorInputRef.current.focus();
|
||||
}
|
||||
}, [formState.bookDomain, isEditing]);
|
||||
|
||||
|
|
@ -356,7 +355,7 @@ const Form: React.FunctionComponent<Props> = ({
|
|||
|
||||
formDispatch({
|
||||
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) => {
|
||||
return (
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
<option key={i} value={i}>
|
||||
{i}
|
||||
</option>
|
||||
|
|
@ -421,6 +421,7 @@ const Form: React.FunctionComponent<Props> = ({
|
|||
>
|
||||
{[...Array(60)].map((_, i) => {
|
||||
return (
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
<option key={i} value={i}>
|
||||
{i}
|
||||
</option>
|
||||
|
|
@ -454,7 +455,7 @@ const Form: React.FunctionComponent<Props> = ({
|
|||
if (value === '') {
|
||||
data = '';
|
||||
} else {
|
||||
data = Number.parseInt(value);
|
||||
data = Number.parseInt(value, 10);
|
||||
}
|
||||
|
||||
formDispatch({
|
||||
|
|
@ -502,7 +503,6 @@ const Form: React.FunctionComponent<Props> = ({
|
|||
const ok = window.confirm('Are you sure?');
|
||||
if (!ok) {
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
}}
|
||||
className="button button-second button-normal"
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import { Link, withRouter, RouteComponentProps } from 'react-router-dom';
|
|||
import classnames from 'classnames';
|
||||
|
||||
import { getRepetitionsPath, repetitionsPathDef } from 'web/libs/paths';
|
||||
import { BookDomain } from 'jslib/operations/types';
|
||||
import {
|
||||
getRepetitionRules,
|
||||
createRepetitionRule
|
||||
|
|
|
|||
|
|
@ -16,14 +16,12 @@
|
|||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useState, useRef } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import React, { useRef, useState } from 'react';
|
||||
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 ItemActions from '../../Common/ItemActions';
|
||||
import ItemActionsStyles from '../../Common/ItemActions/ItemActions.scss';
|
||||
|
||||
interface Props {
|
||||
isActive: boolean;
|
||||
|
|
|
|||
|
|
@ -16,20 +16,18 @@
|
|||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import classnames from 'classnames';
|
||||
|
||||
import { RepetitionRuleData } from 'jslib/operations/types';
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
msToDuration,
|
||||
msToHTMLTimeDuration,
|
||||
timeAgo,
|
||||
relativeTimeDiff
|
||||
relativeTimeDiff,
|
||||
timeAgo
|
||||
} from 'web/helpers/time';
|
||||
import formatTime from 'web/helpers/time/format';
|
||||
import Time from '../../Common/Time';
|
||||
import Actions from './Actions';
|
||||
import BookMeta from './BookMeta';
|
||||
import Time from '../../Common/Time';
|
||||
import styles from './RepetitionItem.scss';
|
||||
|
||||
interface Props {
|
||||
|
|
|
|||
|
|
@ -16,14 +16,12 @@
|
|||
* 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 classnames from 'classnames';
|
||||
|
||||
import config from '../../../libs/config';
|
||||
import { useSelector } from '../../../store';
|
||||
import SettingRow from '../SettingRow';
|
||||
import styles from '../Settings.scss';
|
||||
import { useSelector } from '../../../store';
|
||||
import config from '../../../libs/config';
|
||||
|
||||
interface Props {}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,17 +16,15 @@
|
|||
* 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 services from 'web/libs/services';
|
||||
import { getCurrentUser } from '../../../store/auth';
|
||||
import { useDispatch } from '../../../store';
|
||||
import { getCurrentUser } from '../../../store/auth';
|
||||
import Button from '../../Common/Button';
|
||||
import Modal, { Header, Body } from '../../Common/Modal';
|
||||
import Flash from '../../Common/Flash';
|
||||
import Modal, { Body, Header } from '../../Common/Modal';
|
||||
import modalStyles from '../../Common/Modal/Modal.scss';
|
||||
import settingsStyles from '../Settings.scss';
|
||||
|
||||
interface Props {
|
||||
currentEmail: string;
|
||||
|
|
@ -34,7 +32,11 @@ interface Props {
|
|||
onDismiss: () => void;
|
||||
}
|
||||
|
||||
const EmailModal: React.FunctionComponent<Props> = ({ currentEmail, isOpen, onDismiss }) => {
|
||||
const EmailModal: React.FunctionComponent<Props> = ({
|
||||
currentEmail,
|
||||
isOpen,
|
||||
onDismiss
|
||||
}) => {
|
||||
const [passwordVal, setPasswordVal] = useState('');
|
||||
const [emailVal, setEmailVal] = useState('');
|
||||
const [inProgress, setInProgress] = useState(false);
|
||||
|
|
@ -183,7 +185,4 @@ const mapDispatchToProps = {
|
|||
doGetCurrentUser: getCurrentUser
|
||||
};
|
||||
|
||||
export default connect(
|
||||
null,
|
||||
mapDispatchToProps
|
||||
)(EmailModal);
|
||||
export default connect(null, mapDispatchToProps)(EmailModal);
|
||||
|
|
|
|||
|
|
@ -16,13 +16,12 @@
|
|||
* 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 Button from '../../Common/Button';
|
||||
import Modal, { Header, Body } from '../../Common/Modal';
|
||||
import Flash from '../../Common/Flash';
|
||||
import settingsStyles from '../Settings.scss';
|
||||
import Modal, { Body, Header } from '../../Common/Modal';
|
||||
import modalStyles from '../../Common/Modal/Modal.scss';
|
||||
|
||||
interface Props {
|
||||
|
|
@ -32,7 +31,10 @@ interface Props {
|
|||
|
||||
const labelId = 'password-modal';
|
||||
|
||||
const PasswordModal: React.FunctionComponent<Props> = ({ isOpen, onDismiss }) => {
|
||||
const PasswordModal: React.FunctionComponent<Props> = ({
|
||||
isOpen,
|
||||
onDismiss
|
||||
}) => {
|
||||
const [oldPassword, setOldPassword] = useState('');
|
||||
const [newPassword, setNewPassword] = useState('');
|
||||
const [newPasswordConfirmation, setNewPasswordConfirmation] = useState('');
|
||||
|
|
|
|||
|
|
@ -15,4 +15,3 @@
|
|||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
* 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 SettingRow from '../../SettingRow';
|
||||
|
|
|
|||
|
|
@ -17,13 +17,9 @@
|
|||
*/
|
||||
|
||||
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 PaymentMethodRow from './PaymentMethodRow';
|
||||
import Placeholder from './Placeholder';
|
||||
import styles from './Placeholder.scss';
|
||||
|
||||
interface Props {
|
||||
source: SourceData;
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
* 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 SettingRow from '../../SettingRow';
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@ import { Link } from 'react-router-dom';
|
|||
import classnames from 'classnames';
|
||||
|
||||
import { getPlanLabel } from 'web/libs/subscription';
|
||||
import LogoIcon from '../../../Icons/Logo';
|
||||
import { SECOND } from 'web/helpers/time';
|
||||
import formatDate from 'web/helpers/time/format';
|
||||
import LogoIcon from '../../../Icons/Logo';
|
||||
import styles from './PlanRow.scss';
|
||||
import settingRowStyles from '../../SettingRow.scss';
|
||||
|
||||
|
|
|
|||
|
|
@ -17,15 +17,12 @@
|
|||
*/
|
||||
|
||||
import React, { Fragment } from 'react';
|
||||
import classnames from 'classnames';
|
||||
|
||||
import PlanRow from './PlanRow';
|
||||
import CancelRow from './CancelRow';
|
||||
import ReactivateRow from './ReactivateRow';
|
||||
import settingsStyles from '../../Settings.scss';
|
||||
import { SubscriptionData } from '../../../../store/auth/type';
|
||||
import Placeholder from './Placeholder';
|
||||
import styles from './Placeholder.scss';
|
||||
|
||||
interface Props {
|
||||
subscription: SubscriptionData;
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@
|
|||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import Helmet from 'react-helmet';
|
||||
import classnames from 'classnames';
|
||||
|
||||
import { useScript } from 'web/libs/hooks';
|
||||
import { useSelector, useDispatch } from '../../../store';
|
||||
|
|
@ -31,7 +30,6 @@ import {
|
|||
getSource,
|
||||
clearSource
|
||||
} from '../../../store/auth';
|
||||
import SettingRow from '../SettingRow';
|
||||
import PlanSection from './PlanSection';
|
||||
import PaymentSection from './PaymentSection';
|
||||
import styles from '../Settings.scss';
|
||||
|
|
@ -64,7 +62,6 @@ const Billing: React.FunctionComponent = () => {
|
|||
});
|
||||
|
||||
const subscription = subscriptionData.data;
|
||||
const source = sourceData.data;
|
||||
|
||||
const key = `${__STRIPE_PUBLIC_KEY__}`;
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,11 @@ interface Props extends RouteComponentProps {
|
|||
history: History;
|
||||
}
|
||||
|
||||
const Form: React.FunctionComponent<Props> = ({ stripe, stripeLoadError, history }) => {
|
||||
const Form: React.FunctionComponent<Props> = ({
|
||||
stripe,
|
||||
stripeLoadError,
|
||||
history
|
||||
}) => {
|
||||
const [nameOnCard, setNameOnCard] = useState('');
|
||||
const cardElementRef = useRef(null);
|
||||
const [cardElementLoaded, setCardElementLoaded] = useState(false);
|
||||
|
|
|
|||
|
|
@ -16,20 +16,11 @@
|
|||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {
|
||||
getMonthName,
|
||||
getUTCOffset,
|
||||
pad,
|
||||
nanosecToMillisec,
|
||||
DAY,
|
||||
timeAgo,
|
||||
getDayName
|
||||
} from './index';
|
||||
import { addOrdinalSuffix } from '../..//libs/string';
|
||||
import { getMonthName, getUTCOffset, pad, getDayName } from './index';
|
||||
import { addOrdinalSuffix } from '../../libs/string';
|
||||
|
||||
// format verbs
|
||||
const YYYY = '%YYYY';
|
||||
const YYY = '%YYY';
|
||||
const YY = '%YY';
|
||||
const MMMM = '%MMMM';
|
||||
const MMM = '%MMM';
|
||||
|
|
@ -49,10 +40,10 @@ const dddd = '%dddd';
|
|||
|
||||
// getPeriod returns the period for the time for the given date
|
||||
function getPeriod(date: Date) {
|
||||
const h = date.getHours();
|
||||
const hour = date.getHours();
|
||||
|
||||
let ret;
|
||||
if (h > 12) {
|
||||
if (hour > 12) {
|
||||
ret = 'PM';
|
||||
} else {
|
||||
ret = 'AM';
|
||||
|
|
@ -110,14 +101,14 @@ export default function formatTime(date: Date, format: string): string {
|
|||
}
|
||||
|
||||
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) {
|
||||
let hour = date.getHours();
|
||||
if (hour > 12) {
|
||||
hour = hour - 12;
|
||||
hour -= 12;
|
||||
}
|
||||
|
||||
ret = ret.replace(new RegExp(h, 'g'), hour.toString());
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ const fullDayNames = [
|
|||
'Saturday'
|
||||
];
|
||||
|
||||
/******* durations in milliseconds */
|
||||
/** ***** durations in milliseconds */
|
||||
export const SECOND = 1000;
|
||||
export const MINUTE = 60 * SECOND;
|
||||
export const HOUR = 60 * MINUTE;
|
||||
|
|
@ -92,6 +92,16 @@ export function getDayName(date: Date, short: boolean = false) {
|
|||
return fullDayNames[day];
|
||||
}
|
||||
|
||||
// monthNumToFullName returns a full month name based on the number denoting the month,
|
||||
// ranging from 1 to 12 corresponding to each month of a year.
|
||||
export function monthNumToFullName(num: number): string {
|
||||
if (num > 12 || num < 1) {
|
||||
throw new Error(`invalid month number ${num}`);
|
||||
}
|
||||
|
||||
return fullMonthNames[num - 1];
|
||||
}
|
||||
|
||||
// getMonthName returns the shortened month name of the given date
|
||||
export function getMonthName(date: Date, short: boolean = false) {
|
||||
const month = date.getMonth();
|
||||
|
|
@ -103,16 +113,6 @@ export function getMonthName(date: Date, short: boolean = false) {
|
|||
return monthNumToFullName(month + 1);
|
||||
}
|
||||
|
||||
// monthNumToFullName returns a full month name based on the number denoting the month,
|
||||
// ranging from 1 to 12 corresponding to each month of a year.
|
||||
export function monthNumToFullName(num: number): string {
|
||||
if (num > 12 || num < 1) {
|
||||
throw new Error(`invalid month number ${num}`);
|
||||
}
|
||||
|
||||
return fullMonthNames[num - 1];
|
||||
}
|
||||
|
||||
export function pad(value: number): string {
|
||||
return value < 10 ? `0${value}` : `${value}`;
|
||||
}
|
||||
|
|
@ -191,7 +191,7 @@ export function msToHTMLTimeDuration(ms: number): string {
|
|||
|
||||
// msToDuration translates the given time in seconds into a human-readable duration
|
||||
export function msToDuration(ms: number): string {
|
||||
const { weeks, days, hours, minutes, seconds } = parseMs(ms);
|
||||
const { weeks, days, hours, minutes } = parseMs(ms);
|
||||
|
||||
let ret = '';
|
||||
|
||||
|
|
@ -233,7 +233,6 @@ export function relativeTimeDiff(t1: number, t2: number): TimeDiff {
|
|||
tense = 'future';
|
||||
}
|
||||
|
||||
let text = '';
|
||||
let interval = Math.floor(ts / (52 * WEEK));
|
||||
if (interval > 1) {
|
||||
return {
|
||||
|
|
@ -287,15 +286,7 @@ export function relativeTimeDiff(t1: number, t2: number): TimeDiff {
|
|||
};
|
||||
}
|
||||
|
||||
export function timeAgo(ms: number, simple: boolean = false): string {
|
||||
function getStr(interval: number, noun: string, isPast: boolean): string {
|
||||
let measure = `${interval} ${pluralize(noun, interval)}`;
|
||||
|
||||
if (isPast) {
|
||||
return `${measure} ago`;
|
||||
}
|
||||
}
|
||||
|
||||
export function timeAgo(ms: number): string {
|
||||
const now = new Date().getTime();
|
||||
const diff = relativeTimeDiff(now, ms);
|
||||
|
||||
|
|
|
|||
|
|
@ -20,8 +20,6 @@ import React from 'react';
|
|||
import { Redirect, RouteComponentProps } from 'react-router-dom';
|
||||
|
||||
import { getReferrer } from 'jslib//helpers/url';
|
||||
import { RemoteData } from '../store';
|
||||
import { UserData } from '../store/auth';
|
||||
import { useSelector } from '../store';
|
||||
|
||||
function renderFallback(referrer?: string) {
|
||||
|
|
@ -56,6 +54,7 @@ export default function(Component: React.ComponentType): React.ComponentType {
|
|||
return renderFallback(referrer);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
return <Component {...props} />;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -18,12 +18,9 @@
|
|||
|
||||
import React from 'react';
|
||||
import { withRouter, RouteComponentProps } from 'react-router-dom';
|
||||
import { connect } from 'react-redux';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
|
||||
import { getPathFromLocation } from 'jslib//helpers/url';
|
||||
import { AppState, RemoteData } from '../store';
|
||||
import { UserData } from '../store/auth';
|
||||
import { useSelector } from '../store';
|
||||
|
||||
// userOnly returns a HOC that redirects to Login page if user is not logged in
|
||||
|
|
@ -51,6 +48,7 @@ export default function(
|
|||
return <Redirect to={dest} />;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
return <Component {...props} />;
|
||||
};
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue