mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-24 12:26:40 +07:00
fix eslint
This commit is contained in:
parent
e4b158dada
commit
a26e4168fd
80 changed files with 199 additions and 205 deletions
|
@ -1,10 +1,15 @@
|
|||
module.exports = {
|
||||
extends: ['plugin:react/recommended', 'plugin:@next/next/recommended'],
|
||||
rules: {
|
||||
'react-hooks/rules-of-hooks': 'error', // Checks rules of Hooks
|
||||
'react-hooks/exhaustive-deps': 'warn', // Checks effect dependencies
|
||||
'react/prop-types': 0,
|
||||
'react/display-name': 0,
|
||||
'react/react-in-jsx-scope': 0,
|
||||
'@next/next/no-img-element': 0,
|
||||
'unused-imports/no-unused-imports': 'warn',
|
||||
// 'no-unused-vars': 'warn',
|
||||
'quotes': [2, 'single', { 'avoidEscape': true }],
|
||||
'import/order': [
|
||||
'error',
|
||||
{
|
||||
|
@ -40,7 +45,7 @@ module.exports = {
|
|||
ecmaVersion: 7,
|
||||
sourceType: 'module',
|
||||
},
|
||||
plugins: ['import', 'react-hooks'],
|
||||
plugins: ['import', 'react-hooks', 'unused-imports'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
settings: {
|
||||
react: {
|
||||
|
|
|
@ -91,7 +91,8 @@
|
|||
"eslint": "^7.32.0",
|
||||
"eslint-plugin-import": "^2.25.4",
|
||||
"eslint-plugin-react": "^7.28.0",
|
||||
"eslint-plugin-react-hooks": "^4.3.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-unused-imports": "^3.0.0",
|
||||
"husky": "^7.0.4",
|
||||
"lint-staged": "^12.1.6",
|
||||
"next-transpile-modules": "^9.0.0",
|
||||
|
|
|
@ -4,9 +4,9 @@ import {
|
|||
ApiCreateNoteRequest,
|
||||
ApiUpdateNoteResponse,
|
||||
ApiUpdateNoteRequest,
|
||||
} from "~/api/notes/types";
|
||||
import { URLS } from "~/constants/urls";
|
||||
import { api, cleanResult } from "~/utils/api";
|
||||
} from '~/api/notes/types';
|
||||
import { URLS } from '~/constants/urls';
|
||||
import { api, cleanResult } from '~/utils/api';
|
||||
|
||||
export const apiListNotes = ({ limit, offset, search }: ApiListNotesRequest) =>
|
||||
api
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Note } from "~/types/notes";
|
||||
import { Note } from '~/types/notes';
|
||||
|
||||
export interface ApiGetNotesRequest {
|
||||
limit: number;
|
||||
|
|
|
@ -86,6 +86,7 @@ const LoginAnimatedScene: FC<LoginSceneProps> = memo(() => {
|
|||
const listener = throttle(100, onMouseMove);
|
||||
document.addEventListener('mousemove', listener);
|
||||
return () => document.removeEventListener('mousemove', listener);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
if (isTablet) {
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import React, { FC, ReactNode, useCallback } from 'react';
|
||||
import { FC, ReactNode } from 'react';
|
||||
|
||||
import { WithDescription } from '~/components/common/WithDescription';
|
||||
import { Icon } from '~/components/input/Icon';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
interface Props {
|
||||
icon: string;
|
||||
title: string;
|
||||
|
@ -22,12 +20,6 @@ const BorisContactItem: FC<Props> = ({
|
|||
prefix,
|
||||
suffix,
|
||||
}) => {
|
||||
const onClick = useCallback(() => {
|
||||
if (!link) return;
|
||||
|
||||
window.open(link);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{prefix}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import React, { FC } from 'react';
|
||||
|
||||
import { BorisContactItem } from '~/components/boris/BorisContactItem';
|
||||
import { Group } from '~/components/containers/Group';
|
||||
import { Padder } from '~/components/containers/Padder';
|
||||
import { Button } from '~/components/input/Button';
|
||||
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
import React, { VFC } from 'react';
|
||||
|
||||
import { parseISO } from 'date-fns';
|
||||
|
||||
import { StatsCountdownCard } from '~/components/charts/StatsCountdownCard';
|
||||
import { StatsGraphCard } from '~/components/charts/StatsGraphCard';
|
||||
import { foundationDate } from '~/constants/boris/constants';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ const BorisStatsBackend: FC<IProps> = ({ isLoading, stats }) => {
|
|||
);
|
||||
const nodesByMonth = useMemo(
|
||||
() => stats.nodes.by_month?.slice(0, -1),
|
||||
[stats.comments.by_month],
|
||||
[stats.nodes.by_month],
|
||||
);
|
||||
|
||||
if (!stats && !isLoading) {
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import React, { useMemo, VFC } from 'react';
|
||||
|
||||
import { lighten } from 'color2k';
|
||||
|
||||
import { makeBezierCurve, PathPoint } from '~/utils/dom/makeBezierCurve';
|
||||
import { SVGProps } from '~/utils/types';
|
||||
|
||||
|
@ -40,7 +38,7 @@ const BasicCurveChart: VFC<BasicCurveChartProps> = ({
|
|||
],
|
||||
[],
|
||||
),
|
||||
[height, width, items, gap],
|
||||
[items, borderGap, height, max, width],
|
||||
);
|
||||
|
||||
if (!points.length) {
|
||||
|
|
|
@ -11,15 +11,13 @@ import React, {
|
|||
|
||||
import classnames from 'classnames';
|
||||
|
||||
import { CommentForm } from '~/components/comment/CommentForm';
|
||||
import { Authorized } from '~/components/containers/Authorized';
|
||||
import { Group } from '~/components/containers/Group';
|
||||
import { AudioPlayer } from '~/components/media/AudioPlayer';
|
||||
import { COMMENT_BLOCK_RENDERERS } from '~/constants/comment';
|
||||
import { UploadType } from '~/constants/uploads';
|
||||
import { imagePresets } from '~/constants/urls';
|
||||
import { IComment, IFile } from '~/types';
|
||||
import { formatCommentText, getPrettyDate, getURL } from '~/utils/dom';
|
||||
import { formatCommentText, getPrettyDate } from '~/utils/dom';
|
||||
import { append, assocPath, path, reduce } from '~/utils/ramda';
|
||||
|
||||
import { CommentEditingForm } from '../CommentEditingForm';
|
||||
|
|
|
@ -10,23 +10,25 @@ interface CommentDistanceProps {
|
|||
secondDate?: Date;
|
||||
}
|
||||
|
||||
const CommentDistance: FC<CommentDistanceProps> = memo(({ firstDate, secondDate }) => {
|
||||
const distance = useMemo(() => {
|
||||
if (!firstDate || !secondDate) {
|
||||
return undefined;
|
||||
const CommentDistance: FC<CommentDistanceProps> = memo(
|
||||
({ firstDate, secondDate }) => {
|
||||
const distance = useMemo(() => {
|
||||
if (!firstDate || !secondDate) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return formatDistance(secondDate, firstDate, {
|
||||
locale: ru,
|
||||
addSuffix: false,
|
||||
});
|
||||
}, [firstDate, secondDate]);
|
||||
|
||||
if (!distance) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return formatDistance(secondDate, firstDate, {
|
||||
locale: ru,
|
||||
addSuffix: false,
|
||||
});
|
||||
}, []);
|
||||
|
||||
if (!distance) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <div className={styles.bar}>прошло {distance}</div>;
|
||||
});
|
||||
return <div className={styles.bar}>прошло {distance}</div>;
|
||||
},
|
||||
);
|
||||
|
||||
export { CommentDistance };
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { FC, useCallback, useMemo, useState } from 'react';
|
||||
import { FC, useCallback, useState } from 'react';
|
||||
|
||||
import { FormikProvider } from 'formik';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
|
@ -9,14 +9,12 @@ import { CommentFormFormatButtons } from '~/components/comment/CommentFormFormat
|
|||
import { LocalCommentFormTextarea } from '~/components/comment/LocalCommentFormTextarea';
|
||||
import { Filler } from '~/components/containers/Filler';
|
||||
import { Button } from '~/components/input/Button';
|
||||
import { UploadDropzone } from '~/components/upload/UploadDropzone';
|
||||
import { ERROR_LITERAL } from '~/constants/errors';
|
||||
import { EMPTY_COMMENT } from '~/constants/node';
|
||||
import { useCommentFormFormik } from '~/hooks/comments/useCommentFormFormik';
|
||||
import { useInputPasteUpload } from '~/hooks/dom/useInputPasteUpload';
|
||||
import { IComment, INode } from '~/types';
|
||||
import { IComment } from '~/types';
|
||||
import {
|
||||
UploaderContextProvider,
|
||||
useUploaderContext,
|
||||
} from '~/utils/context/UploaderContextProvider';
|
||||
|
||||
|
|
|
@ -30,14 +30,14 @@ const CommentFormAttaches: FC = () => {
|
|||
(newFiles: IFile[]) => {
|
||||
setFiles([...filesAudios, ...newFiles.filter((it) => it)]);
|
||||
},
|
||||
[setFiles, filesImages, filesAudios],
|
||||
[setFiles, filesAudios],
|
||||
);
|
||||
|
||||
const onAudioMove = useCallback(
|
||||
(newFiles: IFile[]) => {
|
||||
setFiles([...filesImages, ...newFiles]);
|
||||
},
|
||||
[setFiles, filesImages, filesAudios],
|
||||
[setFiles, filesImages],
|
||||
);
|
||||
|
||||
const onFileDelete = useCallback(
|
||||
|
|
|
@ -115,7 +115,7 @@ const CommentFormFormatButtons: FC<IProps> = ({ element, handler }) => {
|
|||
label="Коммент"
|
||||
className={styles.button}
|
||||
>
|
||||
{`/ /`}
|
||||
{'/ /'}
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
);
|
||||
|
|
|
@ -4,7 +4,6 @@ import classNames from 'classnames';
|
|||
|
||||
import { Square } from '~/components/common/Square';
|
||||
import { imagePresets } from '~/constants/urls';
|
||||
import { useColorGradientFromString } from '~/hooks/color/useColorGradientFromString';
|
||||
import { getURLFromString } from '~/utils/dom';
|
||||
import { DivProps } from '~/utils/types';
|
||||
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import React, {
|
||||
CSSProperties,
|
||||
FC,
|
||||
useCallback,
|
||||
useMemo,
|
||||
useReducer,
|
||||
useState,
|
||||
} from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
|
|
@ -16,7 +16,7 @@ const WithDescription: FC<Props> = ({ icon, title, subtitle, link }) => {
|
|||
if (!link) return;
|
||||
|
||||
window.open(link);
|
||||
}, []);
|
||||
}, [link]);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { FC, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
||||
import React, { FC, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import Masonry from 'react-masonry-css';
|
||||
|
||||
|
@ -34,6 +34,7 @@ const Columns: FC<ColumnsProps> = ({
|
|||
const timeout = setTimeout(() => setColumns([...childs]), 150);
|
||||
|
||||
return () => clearTimeout(timeout);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [ref.current]);
|
||||
|
||||
useScrollEnd(columns, onScrollEnd, { active: hasMore, threshold: 2 });
|
||||
|
|
|
@ -15,7 +15,7 @@ const AudioGrid: FC<IProps> = ({ files, setFiles, locked }) => {
|
|||
(newFiles: IFile[]) => {
|
||||
setFiles(newFiles);
|
||||
},
|
||||
[setFiles, files],
|
||||
[setFiles],
|
||||
);
|
||||
|
||||
const onDrop = useCallback(
|
||||
|
|
|
@ -18,7 +18,7 @@ const ImageGrid: FC<IProps> = ({ files, setFiles, locked }) => {
|
|||
(newFiles: IFile[]) => {
|
||||
setFiles(newFiles.filter((it) => it));
|
||||
},
|
||||
[setFiles, files],
|
||||
[setFiles],
|
||||
);
|
||||
|
||||
const onDrop = useCallback(
|
||||
|
|
|
@ -25,12 +25,16 @@ const CellShade: FC<Props> = ({ color, size = 50, angle = 7, ...rest }) => {
|
|||
|
||||
return `linear-gradient(${angle}deg, ${normalized} ${size}px, ${transparentize(
|
||||
normalized,
|
||||
1
|
||||
1,
|
||||
)} ${size * 5}px)`;
|
||||
}, [color, size]);
|
||||
}, [angle, color, size]);
|
||||
|
||||
return (
|
||||
<div {...rest} className={classNames(rest.className, styles.shade)} style={{ background }} />
|
||||
<div
|
||||
{...rest}
|
||||
className={classNames(rest.className, styles.shade)}
|
||||
style={{ background }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { FC, useCallback } from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
|
||||
import { Filler } from '~/components/containers/Filler';
|
||||
import { Button } from '~/components/input/Button';
|
||||
|
@ -10,7 +10,7 @@ import styles from './styles.module.scss';
|
|||
const FlowLoginStamp = () => {
|
||||
const showModal = useShowModal(Dialog.Login);
|
||||
|
||||
const onClick = useCallback(() => showModal({}), []);
|
||||
const onClick = useCallback(() => showModal({}), [showModal]);
|
||||
|
||||
return (
|
||||
<div className={styles.stamp}>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import React, { FC, useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import Image from 'next/image';
|
||||
import SwiperCore, { Autoplay, EffectFade, Lazy, Navigation } from 'swiper';
|
||||
import { Swiper, SwiperSlide } from 'swiper/react';
|
||||
import SwiperClass from 'swiper/types/swiper-class';
|
||||
|
|
|
@ -90,7 +90,7 @@ const Button: FC<IButtonProps> = memo(
|
|||
}
|
||||
|
||||
return 24;
|
||||
}, []);
|
||||
}, [size]);
|
||||
|
||||
return (
|
||||
<Tippy content={label || ''} disabled={!label}>
|
||||
|
|
|
@ -3,7 +3,6 @@ import React, { FC } from 'react';
|
|||
import Image from 'next/future/image';
|
||||
import SwiperCore, { A11y, Navigation, Pagination } from 'swiper';
|
||||
|
||||
import { ImagePreloader } from '~/components/media/ImagePreloader';
|
||||
import { Placeholder } from '~/components/placeholders/Placeholder';
|
||||
import { INodeComponentProps } from '~/constants/node';
|
||||
import { imagePresets } from '~/constants/urls';
|
||||
|
|
|
@ -61,6 +61,7 @@ const MenuButton: FC<MenuButtonProps> = ({
|
|||
|
||||
useEffect(() => {
|
||||
popper.update?.();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [visible]);
|
||||
|
||||
return (
|
||||
|
|
|
@ -2,7 +2,6 @@ import { PropsWithChildren } from 'react';
|
|||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { Anchor } from '~/components/common/Anchor';
|
||||
import { DivProps, LinkProps } from '~/utils/types';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
|
|
@ -89,6 +89,7 @@ const NodeImageSwiperBlock: FC<IProps> = observer(({ node }) => {
|
|||
useEffect(() => {
|
||||
controlledSwiper?.slideTo(0, 0);
|
||||
return () => controlledSwiper?.slideTo(0, 0);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [images, node?.id]);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -97,6 +98,7 @@ const NodeImageSwiperBlock: FC<IProps> = observer(({ node }) => {
|
|||
} else {
|
||||
controlledSwiper?.keyboard.enable();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isModalActive]);
|
||||
|
||||
if (!images?.length) {
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
import React, { FC, useCallback } from 'react';
|
||||
import React, { FC } from 'react';
|
||||
|
||||
import { Pressable } from '~/components/common/Pressable';
|
||||
import { NodeRelated } from '~/components/node/NodeRelated';
|
||||
import { NodeRelatedPlaceholder } from '~/components/node/NodeRelated/placeholder';
|
||||
import { Dialog } from '~/constants/modal';
|
||||
import { useShowModal } from '~/hooks/modal/useShowModal';
|
||||
import { useTagSidebar } from '~/hooks/sidebar/useTagSidebar';
|
||||
import { INode, ITag } from '~/types';
|
||||
import { INode } from '~/types';
|
||||
import { INodeRelated } from '~/types/node';
|
||||
import { useSidebar } from '~/utils/providers/SidebarProvider';
|
||||
|
||||
interface IProps {
|
||||
isLoading: boolean;
|
||||
|
|
|
@ -25,14 +25,14 @@ const NoteCard: VFC<NoteCardProps> = ({
|
|||
}) => {
|
||||
const [editing, setEditing] = useState(false);
|
||||
|
||||
const toggleEditing = useCallback(() => setEditing(v => !v), []);
|
||||
const toggleEditing = useCallback(() => setEditing((v) => !v), []);
|
||||
const onUpdate = useCallback(
|
||||
(text: string, callback?: () => void) =>
|
||||
update(text, () => {
|
||||
setEditing(false);
|
||||
callback?.();
|
||||
}),
|
||||
[],
|
||||
[update],
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -3,8 +3,6 @@ import React, { FC } from 'react';
|
|||
import { Anchor } from '~/components/common/Anchor';
|
||||
import { InlineUsername } from '~/components/common/InlineUsername';
|
||||
import { Square } from '~/components/common/Square';
|
||||
import { Card } from '~/components/containers/Card';
|
||||
import { FlowRecentItem } from '~/components/flow/FlowRecentItem';
|
||||
import { NotificationItem, NotificationType } from '~/types/notifications';
|
||||
import { formatText, getPrettyDate, getURLFromString } from '~/utils/dom';
|
||||
|
||||
|
|
|
@ -2,9 +2,7 @@ import React, { ChangeEvent, FC, useCallback } from 'react';
|
|||
|
||||
import { Avatar } from '~/components/common/Avatar';
|
||||
import { Button } from '~/components/input/Button';
|
||||
import { imagePresets } from '~/constants/urls';
|
||||
import { IFile } from '~/types';
|
||||
import { getURL } from '~/utils/dom';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ import { useState, VFC } from 'react';
|
|||
|
||||
import { Group } from '~/components/containers/Group';
|
||||
import { Button } from '~/components/input/Button';
|
||||
import { Icon } from '~/components/input/Icon';
|
||||
import { HorizontalMenu } from '~/components/menu/HorizontalMenu';
|
||||
import { useStackContext } from '~/components/sidebar/SidebarStack';
|
||||
import { SidebarStackCard } from '~/components/sidebar/SidebarStackCard';
|
||||
|
|
|
@ -38,7 +38,7 @@ const SidebarCards: FC = ({ children }) => {
|
|||
return [];
|
||||
}
|
||||
|
||||
return Array.isArray(children) ? children.filter(it => it) : [children];
|
||||
return Array.isArray(children) ? children.filter((it) => it) : [children];
|
||||
}, [children]);
|
||||
|
||||
if (isNil(activeTab) || !nonEmptyChildren[activeTab]) {
|
||||
|
@ -48,7 +48,7 @@ const SidebarCards: FC = ({ children }) => {
|
|||
return <div className={styles.card}>{nonEmptyChildren[activeTab]}</div>;
|
||||
};
|
||||
|
||||
const SidebarStack = function({
|
||||
const SidebarStack = function ({
|
||||
children,
|
||||
tab,
|
||||
onTabChange,
|
||||
|
@ -58,7 +58,7 @@ const SidebarStack = function({
|
|||
const closeAllTabs = useCallback(() => {
|
||||
setActiveTab(undefined);
|
||||
onTabChange?.(undefined);
|
||||
}, []);
|
||||
}, [onTabChange]);
|
||||
|
||||
const onChangeTab = useCallback(
|
||||
(index: number) => {
|
||||
|
|
|
@ -22,8 +22,8 @@ export const API = {
|
|||
|
||||
GET_SOCIALS: '/oauth',
|
||||
DROP_SOCIAL: (provider, id) => `/oauth/${provider}/${id}`,
|
||||
ATTACH_SOCIAL: `/oauth`,
|
||||
LOGIN_WITH_SOCIAL: `/oauth`,
|
||||
ATTACH_SOCIAL: '/oauth',
|
||||
LOGIN_WITH_SOCIAL: '/oauth',
|
||||
ATTACH_TELEGRAM: '/oauth/telegram/attach',
|
||||
},
|
||||
NODES: {
|
||||
|
@ -55,11 +55,11 @@ export const API = {
|
|||
GITHUB_ISSUES: 'https://api.github.com/repos/muerwre/vault-frontend/issues',
|
||||
},
|
||||
TAG: {
|
||||
NODES: `/tags/nodes`,
|
||||
AUTOCOMPLETE: `/tags/autocomplete`,
|
||||
NODES: '/tags/nodes',
|
||||
AUTOCOMPLETE: '/tags/autocomplete',
|
||||
},
|
||||
LAB: {
|
||||
NODES: `/nodes/lab`,
|
||||
NODES: '/nodes/lab',
|
||||
STATS: '/nodes/lab/stats',
|
||||
UPDATES: '/nodes/lab/updates',
|
||||
},
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { SettingsSidebar } from '~/containers/sidebars/ProfileSidebar';
|
||||
|
||||
export enum SidebarName {
|
||||
Settings = 'settings',
|
||||
|
|
|
@ -17,7 +17,7 @@ export const themeColors: Record<Theme, ThemeColors> = {
|
|||
'linear-gradient(165deg, #ff7549 -50%, #ff3344 150%)',
|
||||
'linear-gradient(170deg, #582cd0, #592071)',
|
||||
],
|
||||
background: `url('/images/noise_top.png') 0% 0% #23201f`,
|
||||
background: 'url(\'/images/noise_top.png\') 0% 0% #23201f',
|
||||
},
|
||||
[Theme.Horizon]: {
|
||||
name: 'Веспера',
|
||||
|
@ -26,6 +26,6 @@ export const themeColors: Record<Theme, ThemeColors> = {
|
|||
'linear-gradient(165deg, #fab795 -50%, #fab795 150%)',
|
||||
'linear-gradient(170deg, #25b0bc, #7693d6)',
|
||||
],
|
||||
background: `url("/images/horizon_bg.svg") 50% 50% / cover rgb(28, 30, 38)`,
|
||||
background: 'url("/images/horizon_bg.svg") 50% 50% / cover rgb(28, 30, 38)',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -4,7 +4,6 @@ import { BorisContacts } from '~/components/boris/BorisContacts';
|
|||
import { BorisStats } from '~/components/boris/BorisStats';
|
||||
import { Group } from '~/components/containers/Group';
|
||||
import { SuperPowersToggle } from '~/containers/auth/SuperPowersToggle';
|
||||
import { useAuth } from '~/hooks/auth/useAuth';
|
||||
import { useTelegramAccount } from '~/hooks/auth/useTelegramAccount';
|
||||
import { BorisUsageStats } from '~/types/boris';
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ const TelegramAttachDialog: FC<TelegramAttachDialogProps> = ({
|
|||
|
||||
const onAttach = useCallback(
|
||||
(data: TelegramUser) => attach(data, onRequestClose),
|
||||
[onRequestClose],
|
||||
[attach, onRequestClose],
|
||||
);
|
||||
|
||||
const buttons = useMemo(
|
||||
|
|
|
@ -2,7 +2,6 @@ import { FC, memo } from 'react';
|
|||
|
||||
import { Hoverable } from '~/components/common/Hoverable';
|
||||
import { Columns } from '~/components/containers/Columns';
|
||||
import { InfiniteScroll } from '~/components/containers/InfiniteScroll';
|
||||
import { LabNoResults } from '~/components/lab/LabNoResults';
|
||||
import { LabNode } from '~/components/lab/LabNode';
|
||||
import { useLabContext } from '~/utils/context/LabContextProvider';
|
||||
|
|
|
@ -43,7 +43,7 @@ const Header: FC<HeaderProps> = observer(() => {
|
|||
borisCommentedAt &&
|
||||
((fetched && !user.last_seen_boris) ||
|
||||
isBefore(new Date(user.last_seen_boris), new Date(borisCommentedAt))),
|
||||
[borisCommentedAt, isUser, user.last_seen_boris, fetched],
|
||||
[isUser, indicatorEnabled, borisCommentedAt, fetched, user.last_seen_boris],
|
||||
);
|
||||
|
||||
// Needed for SSR
|
||||
|
|
|
@ -2,7 +2,6 @@ import { FC, useMemo } from 'react';
|
|||
|
||||
import { SubTitle } from '~/components/common/SubTitle';
|
||||
import { Card } from '~/components/containers/Card';
|
||||
import { Padder } from '~/components/containers/Padder';
|
||||
import { Backlink } from '~/components/node/Backlink';
|
||||
import { NodeBackLink } from '~/types';
|
||||
import { has } from '~/utils/ramda';
|
||||
|
|
|
@ -5,7 +5,6 @@ import { observer } from 'mobx-react-lite';
|
|||
import { CommentForm } from '~/components/comment/CommentForm';
|
||||
import { CommentWrapper } from '~/components/containers/CommentWrapper';
|
||||
import { UploadDropzone } from '~/components/upload/UploadDropzone';
|
||||
import { EMPTY_USER } from '~/constants/auth';
|
||||
import { Dialog } from '~/constants/modal';
|
||||
import { UploadSubject, UploadTarget } from '~/constants/uploads';
|
||||
import { useAuth } from '~/hooks/auth/useAuth';
|
||||
|
|
|
@ -23,23 +23,27 @@ const NotificationList: FC<NotificationListProps> = () => {
|
|||
|
||||
useEffect(() => {
|
||||
return () => markAsRead();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const renderItem = useCallback((item: NotificationItem) => {
|
||||
const isNew =
|
||||
!lastSeen ||
|
||||
!item.created_at ||
|
||||
isAfter(parseISO(item.created_at), lastSeen);
|
||||
switch (item.type) {
|
||||
case NotificationType.Comment:
|
||||
case NotificationType.Boris:
|
||||
return <NotificationComment item={item} isNew={isNew} />;
|
||||
case NotificationType.Node:
|
||||
return <NotificationNode item={item} isNew={isNew} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
const renderItem = useCallback(
|
||||
(item: NotificationItem) => {
|
||||
const isNew =
|
||||
!lastSeen ||
|
||||
!item.created_at ||
|
||||
isAfter(parseISO(item.created_at), lastSeen);
|
||||
switch (item.type) {
|
||||
case NotificationType.Comment:
|
||||
case NotificationType.Boris:
|
||||
return <NotificationComment item={item} isNew={isNew} />;
|
||||
case NotificationType.Node:
|
||||
return <NotificationNode item={item} isNew={isNew} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
},
|
||||
[lastSeen],
|
||||
);
|
||||
|
||||
if (isLoading) {
|
||||
return <LoaderScreen align="top" />;
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
import React, { FC, Fragment, useCallback, useMemo } from 'react';
|
||||
import React, { FC, Fragment } from 'react';
|
||||
|
||||
import { Superpower } from '~/components/boris/Superpower';
|
||||
import { Group } from '~/components/containers/Group';
|
||||
import { Button } from '~/components/input/Button';
|
||||
import { Icon } from '~/components/input/Icon';
|
||||
import { Placeholder } from '~/components/placeholders/Placeholder';
|
||||
import { SOCIAL_ICONS } from '~/constants/auth/socials';
|
||||
import { Dialog } from '~/constants/modal';
|
||||
import { useOAuth } from '~/hooks/auth/useOAuth';
|
||||
import { useModal } from '~/hooks/modal/useModal';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import React, { FC } from 'react';
|
||||
|
||||
import { Avatar } from '~/components/common/Avatar';
|
||||
import { Markdown } from '~/components/containers/Markdown';
|
||||
import { Placeholder } from '~/components/placeholders/Placeholder';
|
||||
import { imagePresets } from '~/constants/urls';
|
||||
import { IUser } from '~/types/auth';
|
||||
import { formatText } from '~/utils/dom';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
|
|
|
@ -2,12 +2,10 @@ import React, { FC } from 'react';
|
|||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { Avatar } from '~/components/common/Avatar';
|
||||
import { Filler } from '~/components/containers/Filler';
|
||||
import { Group } from '~/components/containers/Group';
|
||||
import { useUserActiveStatus } from '~/hooks/auth/useUserActiveStatus';
|
||||
import { IUser } from '~/types/auth';
|
||||
import { path } from '~/utils/ramda';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ import React, { useCallback, VFC } from 'react';
|
|||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { Superpower } from '~/components/boris/Superpower';
|
||||
import { Filler } from '~/components/containers/Filler';
|
||||
import { Group } from '~/components/containers/Group';
|
||||
import { Zone } from '~/components/containers/Zone';
|
||||
|
@ -32,7 +31,7 @@ const ProfileSidebarMenu: VFC<ProfileSidebarMenuProps> = ({ onClose }) => {
|
|||
const onLogout = useCallback(() => {
|
||||
logout();
|
||||
onClose();
|
||||
}, [onClose]);
|
||||
}, [logout, onClose]);
|
||||
|
||||
return (
|
||||
<div className={styles.wrap}>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import React, { FC } from 'react';
|
||||
|
||||
import { Group } from '~/components/containers/Group';
|
||||
import { Zone } from '~/components/containers/Zone';
|
||||
import { SuperPowersToggle } from '~/containers/auth/SuperPowersToggle';
|
||||
|
||||
interface ProfileTogglesProps {}
|
||||
|
|
|
@ -20,7 +20,7 @@ const List = () => {
|
|||
async (id: number) => {
|
||||
confirm('Удалить? Это удалит заметку навсегда', () => remove(id));
|
||||
},
|
||||
[remove],
|
||||
[confirm, remove],
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -6,11 +6,9 @@ import { Group } from '~/components/containers/Group';
|
|||
import { Zone } from '~/components/containers/Zone';
|
||||
import { InputText } from '~/components/input/InputText';
|
||||
import { Textarea } from '~/components/input/Textarea';
|
||||
import { ERROR_LITERAL } from '~/constants/errors';
|
||||
import { ProfileAccounts } from '~/containers/profile/ProfileAccounts';
|
||||
import { useWindowSize } from '~/hooks/dom/useWindowSize';
|
||||
import { useSettings } from '~/utils/providers/SettingsProvider';
|
||||
import { has } from '~/utils/ramda';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ import { SidebarWrapper } from '~/components/sidebar/SidebarWrapper';
|
|||
import { SidebarName } from '~/constants/sidebar';
|
||||
import { ProfileSidebarMenu } from '~/containers/profile/ProfileSidebarMenu';
|
||||
import { useAuth } from '~/hooks/auth/useAuth';
|
||||
import { useUser } from '~/hooks/auth/useUser';
|
||||
import type { SidebarComponentProps } from '~/types/sidebar';
|
||||
import { isNil } from '~/utils/ramda';
|
||||
|
||||
|
@ -41,13 +40,14 @@ const SettingsSidebar: VFC<SettingsSidebarProps> = ({
|
|||
page: !isNil(val) ? tabs[val] : undefined,
|
||||
});
|
||||
},
|
||||
[open, onRequestClose],
|
||||
[openSidebar],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isUser) {
|
||||
onRequestClose();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isUser]);
|
||||
|
||||
if (!isUser) {
|
||||
|
|
|
@ -2,7 +2,6 @@ import { useMemo, VFC } from 'react';
|
|||
|
||||
import { InfiniteScroll } from '~/components/containers/InfiniteScroll';
|
||||
import { Icon } from '~/components/input/Icon';
|
||||
import { LoaderCircle } from '~/components/input/LoaderCircle';
|
||||
import { SidebarStack } from '~/components/sidebar/SidebarStack';
|
||||
import { SidebarStackCard } from '~/components/sidebar/SidebarStackCard';
|
||||
import { SidebarWrapper } from '~/components/sidebar/SidebarWrapper';
|
||||
|
|
|
@ -95,7 +95,6 @@ export const useOAuth = () => {
|
|||
);
|
||||
|
||||
const accounts = useMemo(() => data || [], [data]);
|
||||
const refresh = useCallback(() => mutate(), []);
|
||||
|
||||
const hasTelegram = useMemo(
|
||||
() => accounts.some((acc) => acc.provider === 'telegram'),
|
||||
|
@ -104,7 +103,7 @@ export const useOAuth = () => {
|
|||
|
||||
const showTelegramModal = useCallback(
|
||||
() => showModal(Dialog.TelegramAttach, {}),
|
||||
[],
|
||||
[showModal],
|
||||
);
|
||||
|
||||
return {
|
||||
|
@ -117,6 +116,6 @@ export const useOAuth = () => {
|
|||
dropAccount,
|
||||
accounts,
|
||||
isLoading: !data && isLoading,
|
||||
refresh,
|
||||
refresh: mutate,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -5,7 +5,6 @@ import { ApiUpdateUserRequest } from '~/api/auth/types';
|
|||
import { UploadSubject, UploadTarget } from '~/constants/uploads';
|
||||
import { useUser } from '~/hooks/auth/useUser';
|
||||
import { useUploader } from '~/hooks/data/useUploader';
|
||||
import { IFile } from '~/types';
|
||||
import { showErrorToast } from '~/utils/errors/showToast';
|
||||
|
||||
export const usePatchUser = () => {
|
||||
|
@ -39,7 +38,7 @@ export const usePatchUser = () => {
|
|||
showErrorToast(error);
|
||||
}
|
||||
},
|
||||
[uploadFile, save],
|
||||
[uploadFile, update],
|
||||
);
|
||||
|
||||
return { updatePhoto, save };
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { useMemo } from "react";
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { useAuth } from "~/hooks/auth/useAuth";
|
||||
import { useAuth } from '~/hooks/auth/useAuth';
|
||||
|
||||
export const useSuperPowers = () => {
|
||||
const { isTester, setIsTester } = useAuth();
|
||||
|
|
|
@ -30,10 +30,13 @@ export const useTelegramAccount = () => {
|
|||
setLoading(false);
|
||||
}
|
||||
},
|
||||
[],
|
||||
[refresh, refreshNotificationSettings],
|
||||
);
|
||||
|
||||
const connect = useCallback(() => showModal(Dialog.TelegramAttach, {}), []);
|
||||
const connect = useCallback(
|
||||
() => showModal(Dialog.TelegramAttach, {}),
|
||||
[showModal],
|
||||
);
|
||||
|
||||
const connected = useMemo(
|
||||
() => accounts.some((it) => it.provider === 'telegram'),
|
||||
|
|
|
@ -5,12 +5,12 @@ import { IUser } from '~/types/auth';
|
|||
export const useUserDescription = (user?: Partial<IUser>) => {
|
||||
const randomPhrase = useRandomPhrase('USER_DESCRIPTION');
|
||||
|
||||
const isActive = useUserActiveStatus(user?.last_seen);
|
||||
|
||||
if (!user) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const isActive = useUserActiveStatus(user.last_seen);
|
||||
|
||||
if (!isActive) {
|
||||
return 'Юнит деактивирован';
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import { useCallback, useEffect } from "react";
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import isBefore from "date-fns/isBefore";
|
||||
import isBefore from 'date-fns/isBefore';
|
||||
|
||||
import { useRandomPhrase } from "~/constants/phrases";
|
||||
import { useLastSeenBoris } from "~/hooks/auth/useLastSeenBoris";
|
||||
import { useBorisStats } from "~/hooks/boris/useBorisStats";
|
||||
import { IComment } from "~/types";
|
||||
import { useRandomPhrase } from '~/constants/phrases';
|
||||
import { useLastSeenBoris } from '~/hooks/auth/useLastSeenBoris';
|
||||
import { useBorisStats } from '~/hooks/boris/useBorisStats';
|
||||
import { IComment } from '~/types';
|
||||
|
||||
export const useBoris = (comments: IComment[]) => {
|
||||
const title = useRandomPhrase("BORIS_TITLE");
|
||||
const title = useRandomPhrase('BORIS_TITLE');
|
||||
|
||||
const { lastSeen, setLastSeen } = useLastSeenBoris();
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { useCallback } from "react";
|
||||
import { useCallback } from 'react';
|
||||
|
||||
export const useConfirmation = () =>
|
||||
useCallback((prompt = "", onApprove: () => {}, onReject?: () => {}) => {
|
||||
if (!window.confirm(prompt || "Уверен?")) {
|
||||
useCallback((prompt = '', onApprove: () => {}, onReject?: () => {}) => {
|
||||
if (!window.confirm(prompt || 'Уверен?')) {
|
||||
onReject?.();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -29,9 +29,11 @@ export const useGlobalLoader = () => {
|
|||
}
|
||||
|
||||
hideLoader();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [flow.isRefreshed, getInitialNodes]);
|
||||
|
||||
useEffect(() => {
|
||||
void getInitialNodes();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isUser]);
|
||||
};
|
||||
|
|
|
@ -30,7 +30,7 @@ export const useSSRLoadingIndicator = (delay = 0) => {
|
|||
Router.events.on('routeChangeStart', show);
|
||||
Router.events.on('routeChangeComplete', hide);
|
||||
Router.events.on('routeChangeError', hide);
|
||||
}, []);
|
||||
}, [delay]);
|
||||
|
||||
return shown;
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
|
||||
import { throttle } from 'throttle-debounce';
|
||||
|
||||
|
@ -44,5 +44,5 @@ export const useScrollEnd = (
|
|||
window.addEventListener('scroll', eventHandler);
|
||||
|
||||
return () => window.removeEventListener('scroll', eventHandler);
|
||||
}, [item, active, innerHeight, debouncedCallback]);
|
||||
}, [item, active, innerHeight, debouncedCallback, threshold]);
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
export const useWindowSize = () => {
|
||||
const [size, setSize] = useState({
|
||||
|
@ -22,9 +22,10 @@ export const useWindowSize = () => {
|
|||
useEffect(() => {
|
||||
onResize();
|
||||
|
||||
window.addEventListener("resize", onResize);
|
||||
window.addEventListener('resize', onResize);
|
||||
|
||||
return () => window.removeEventListener("resize", onResize);
|
||||
return () => window.removeEventListener('resize', onResize);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return size;
|
||||
|
|
|
@ -1,42 +1,40 @@
|
|||
import { useCallback, useMemo } from "react";
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
import useSWRInfinite, { SWRInfiniteKeyLoader } from "swr/infinite";
|
||||
import useSWRInfinite, { SWRInfiniteKeyLoader } from 'swr/infinite';
|
||||
|
||||
import {
|
||||
apiCreateNote,
|
||||
apiDeleteNote,
|
||||
apiListNotes,
|
||||
apiUpdateNote,
|
||||
} from "~/api/notes";
|
||||
import { ApiGetNotesRequest } from "~/api/notes/types";
|
||||
import { useAuth } from "~/hooks/auth/useAuth";
|
||||
import { GetLabNodesRequest, ILabNode } from "~/types/lab";
|
||||
import { Note } from "~/types/notes";
|
||||
import { flatten, uniqBy } from "~/utils/ramda";
|
||||
} from '~/api/notes';
|
||||
import { ApiGetNotesRequest } from '~/api/notes/types';
|
||||
import { useAuth } from '~/hooks/auth/useAuth';
|
||||
import { GetLabNodesRequest, ILabNode } from '~/types/lab';
|
||||
import { Note } from '~/types/notes';
|
||||
import { flatten, uniqBy } from '~/utils/ramda';
|
||||
|
||||
const DEFAULT_COUNT = 20;
|
||||
|
||||
const getKey: (isUser: boolean, search: string) => SWRInfiniteKeyLoader = (
|
||||
isUser,
|
||||
search,
|
||||
) => (index, prev: ILabNode[]) => {
|
||||
if (!isUser) return null;
|
||||
if (index > 0 && (!prev?.length || prev.length < 20)) return null;
|
||||
const getKey: (isUser: boolean, search: string) => SWRInfiniteKeyLoader =
|
||||
(isUser, search) => (index, prev: ILabNode[]) => {
|
||||
if (!isUser) return null;
|
||||
if (index > 0 && (!prev?.length || prev.length < 20)) return null;
|
||||
|
||||
const props: GetLabNodesRequest = {
|
||||
limit: DEFAULT_COUNT,
|
||||
offset: index * DEFAULT_COUNT,
|
||||
search: search || "",
|
||||
const props: GetLabNodesRequest = {
|
||||
limit: DEFAULT_COUNT,
|
||||
offset: index * DEFAULT_COUNT,
|
||||
search: search || '',
|
||||
};
|
||||
|
||||
return JSON.stringify(props);
|
||||
};
|
||||
|
||||
return JSON.stringify(props);
|
||||
};
|
||||
|
||||
const parseKey = (key: string): ApiGetNotesRequest => {
|
||||
try {
|
||||
return JSON.parse(key);
|
||||
} catch (error) {
|
||||
return { limit: DEFAULT_COUNT, offset: 0, search: "" };
|
||||
return { limit: DEFAULT_COUNT, offset: 0, search: '' };
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -74,7 +72,7 @@ export const useNotes = (search: string) => {
|
|||
async (id: number, onSuccess?: () => void) => {
|
||||
await apiDeleteNote(id);
|
||||
await mutate(
|
||||
data?.map(page => page.filter(it => it.id !== id)),
|
||||
data?.map((page) => page.filter((it) => it.id !== id)),
|
||||
{ revalidate: false },
|
||||
);
|
||||
onSuccess?.();
|
||||
|
@ -86,7 +84,7 @@ export const useNotes = (search: string) => {
|
|||
async (id: number, text: string, onSuccess?: () => void) => {
|
||||
const result = await apiUpdateNote({ id, text });
|
||||
await mutate(
|
||||
data?.map(page => page.map(it => (it.id === id ? result : it))),
|
||||
data?.map((page) => page.map((it) => (it.id === id ? result : it))),
|
||||
{ revalidate: false },
|
||||
);
|
||||
onSuccess?.();
|
||||
|
@ -94,7 +92,7 @@ export const useNotes = (search: string) => {
|
|||
[mutate, data],
|
||||
);
|
||||
|
||||
const notes = useMemo(() => uniqBy(n => n.id, flatten(data || [])), [data]);
|
||||
const notes = useMemo(() => uniqBy((n) => n.id, flatten(data || [])), [data]);
|
||||
const hasMore = (data?.[size - 1]?.length || 0) >= 1;
|
||||
const loadMore = useCallback(() => setSize(size + 1), [setSize, size]);
|
||||
|
||||
|
@ -108,6 +106,6 @@ export const useNotes = (search: string) => {
|
|||
remove,
|
||||
update,
|
||||
}),
|
||||
[notes, hasMore, loadMore, data, isValidating, create, remove],
|
||||
[notes, hasMore, loadMore, data, isValidating, create, remove, update],
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { useCallback } from 'react';
|
||||
|
||||
import { SidebarName } from '~/constants/sidebar';
|
||||
import { ITag } from '~/types';
|
||||
import { useSidebar } from '~/utils/providers/SidebarProvider';
|
||||
|
||||
export const useTagSidebar = () => {
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import { DragStartEvent, MouseSensor, TouchSensor, useSensor, useSensors } from '@dnd-kit/core';
|
||||
import {
|
||||
DragStartEvent,
|
||||
MouseSensor,
|
||||
TouchSensor,
|
||||
useSensor,
|
||||
useSensors,
|
||||
} from '@dnd-kit/core';
|
||||
import { DragEndEvent } from '@dnd-kit/core/dist/types';
|
||||
|
||||
import { moveArrItem } from '~/utils/fn';
|
||||
|
@ -8,11 +14,11 @@ import { moveArrItem } from '~/utils/fn';
|
|||
export const useSortableActions = <T>(
|
||||
items: T[],
|
||||
getID: (item: T) => string | number,
|
||||
onSortEnd: (items: T[]) => void
|
||||
onSortEnd: (items: T[]) => void,
|
||||
) => {
|
||||
const [draggingItem, setDraggingItem] = useState<T | null>(null);
|
||||
|
||||
const ids = useMemo(() => items.map(getID), [items]);
|
||||
const ids = useMemo(() => items.map(getID), [getID, items]);
|
||||
|
||||
const onDragEnd = useCallback(
|
||||
({ active, over }: DragEndEvent) => {
|
||||
|
@ -22,12 +28,12 @@ export const useSortableActions = <T>(
|
|||
return;
|
||||
}
|
||||
|
||||
const oldIndex = items.findIndex(it => getID(it) === active.id);
|
||||
const newIndex = items.findIndex(it => getID(it) === over.id);
|
||||
const oldIndex = items.findIndex((it) => getID(it) === active.id);
|
||||
const newIndex = items.findIndex((it) => getID(it) === over.id);
|
||||
|
||||
onSortEnd(moveArrItem(oldIndex, newIndex, items));
|
||||
},
|
||||
[items]
|
||||
[getID, items, onSortEnd],
|
||||
);
|
||||
|
||||
const onDragStart = useCallback(
|
||||
|
@ -36,11 +42,11 @@ export const useSortableActions = <T>(
|
|||
return;
|
||||
}
|
||||
|
||||
const activeItem = items.find(it => getID(it) === active.id);
|
||||
const activeItem = items.find((it) => getID(it) === active.id);
|
||||
|
||||
setDraggingItem(activeItem ?? null);
|
||||
},
|
||||
[items]
|
||||
[getID, items],
|
||||
);
|
||||
|
||||
const sensors = useSensors(
|
||||
|
@ -50,7 +56,7 @@ export const useSortableActions = <T>(
|
|||
tolerance: 5,
|
||||
},
|
||||
}),
|
||||
useSensor(MouseSensor)
|
||||
useSensor(MouseSensor),
|
||||
);
|
||||
|
||||
return { sensors, onDragEnd, onDragStart, draggingItem, ids };
|
||||
|
|
|
@ -16,5 +16,6 @@ export const useFormAutoSubmit = <T>(
|
|||
const timeout = setTimeout(onSubmit, delay);
|
||||
|
||||
return () => clearTimeout(timeout);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [values]);
|
||||
};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import { FC } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { FlowGrid } from '~/components/flow/FlowGrid';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { FC, useMemo } from 'react';
|
||||
import React, { FC } from 'react';
|
||||
|
||||
import { Group } from '~/components/containers/Group';
|
||||
import { Sticky } from '~/components/containers/Sticky';
|
||||
|
|
|
@ -31,7 +31,10 @@ const NodeLayout = observer(() => {
|
|||
|
||||
useNodeCoverImage(node);
|
||||
|
||||
const onUnauthorizedLike = useCallback(() => showRegisterDialog({}), []);
|
||||
const onUnauthorizedLike = useCallback(
|
||||
() => showRegisterDialog({}),
|
||||
[showRegisterDialog],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={styles.wrap}>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React, { FC } from 'react';
|
||||
|
||||
import { Anchor } from '~/components/common/Anchor';
|
||||
import { Button } from '~/components/input/Button';
|
||||
import { Role } from '~/constants/auth';
|
||||
import { Dialog } from '~/constants/modal';
|
||||
|
@ -26,7 +27,7 @@ const RoomLayout: FC<RoomLayoutProps> = () => {
|
|||
|
||||
<p>
|
||||
Пока ещё концепт, над которым я размышляю, ты видишь его, потому
|
||||
что включил суперсилы в <a href="/boris">Борисе</a>.
|
||||
что включил суперсилы в <Anchor href="/boris">Борисе</Anchor>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
|
|
@ -91,7 +91,7 @@ export const getStaticProps = async (
|
|||
revalidate: 7 * 86400, // every week
|
||||
};
|
||||
} catch (error) {
|
||||
console.warn(`[NEXT] can't generate node: `, error);
|
||||
console.warn('[NEXT] can\'t generate node: ', error);
|
||||
return {
|
||||
notFound: true,
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { FunctionComponent } from "react";
|
||||
import { FunctionComponent } from 'react';
|
||||
|
||||
import type { SidebarComponents } from "~/constants/sidebar/components";
|
||||
import type { SidebarComponents } from '~/constants/sidebar/components';
|
||||
|
||||
export type SidebarComponent = keyof SidebarComponents;
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ import {
|
|||
COMMENT_BLOCK_TYPES,
|
||||
ICommentBlock,
|
||||
} from '~/constants/comment';
|
||||
import { imagePresets } from '~/constants/urls';
|
||||
import { IFile, ValueOf } from '~/types';
|
||||
import { CONFIG } from '~/utils/config';
|
||||
import {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { marked } from 'marked';
|
||||
|
||||
import { EventMessageType } from '~/constants/events';
|
||||
import { stripHTMLTags } from '~/utils/stripHTMLTags';
|
||||
|
||||
/**
|
||||
|
@ -22,7 +21,7 @@ export const formatTextSanitizeTags = (text: string): string =>
|
|||
* Returns clickable usernames
|
||||
*/
|
||||
export const formatTextClickableUsernames = (text: string): string =>
|
||||
text.replace(/~([\wа-яА-Я-]+)/giu, `<span class="username">~$1</span>`);
|
||||
text.replace(/~([\wа-яА-Я-]+)/giu, '<span class="username">~$1</span>');
|
||||
|
||||
/**
|
||||
* Makes gray comments
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { createContext, FC, useContext } from 'react';
|
||||
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { boolean } from 'yup';
|
||||
|
||||
import { EMPTY_USER } from '~/constants/auth';
|
||||
import { useAuth } from '~/hooks/auth/useAuth';
|
||||
|
|
|
@ -44,10 +44,13 @@ const ThemeProvider: FC<ProvidersProps> = ({ children }) => {
|
|||
|
||||
useEffect(() => {
|
||||
const stored = localStorage.getItem('vault__theme');
|
||||
|
||||
if (!stored || !keys(themeClass).includes(stored as Theme)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setTheme(stored as Theme);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const value = useMemo(() => ({ theme, setTheme }), [theme, setTheme]);
|
||||
|
|
20
yarn.lock
20
yarn.lock
|
@ -1136,10 +1136,10 @@ eslint-plugin-import@^2.25.4:
|
|||
resolve "^1.20.0"
|
||||
tsconfig-paths "^3.12.0"
|
||||
|
||||
eslint-plugin-react-hooks@^4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz#318dbf312e06fab1c835a4abef00121751ac1172"
|
||||
integrity sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA==
|
||||
eslint-plugin-react-hooks@^4.6.0:
|
||||
version "4.6.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3"
|
||||
integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==
|
||||
|
||||
eslint-plugin-react@^7.28.0:
|
||||
version "7.28.0"
|
||||
|
@ -1161,6 +1161,18 @@ eslint-plugin-react@^7.28.0:
|
|||
semver "^6.3.0"
|
||||
string.prototype.matchall "^4.0.6"
|
||||
|
||||
eslint-plugin-unused-imports@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-3.0.0.tgz#d25175b0072ff16a91892c3aa72a09ca3a9e69e7"
|
||||
integrity sha512-sduiswLJfZHeeBJ+MQaG+xYzSWdRXoSw61DpU13mzWumCkR0ufD0HmO4kdNokjrkluMHpj/7PJeN35pgbhW3kw==
|
||||
dependencies:
|
||||
eslint-rule-composer "^0.3.0"
|
||||
|
||||
eslint-rule-composer@^0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz#79320c927b0c5c0d3d3d2b76c8b4a488f25bbaf9"
|
||||
integrity sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==
|
||||
|
||||
eslint-scope@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue