1
0
Fork 0
mirror of https://github.com/muerwre/vault-frontend.git synced 2025-04-24 20:36:40 +07:00

fix eslint

This commit is contained in:
Fedor Katurov 2023-10-30 21:49:10 +06:00
parent e4b158dada
commit a26e4168fd
80 changed files with 199 additions and 205 deletions

View file

@ -1,10 +1,15 @@
module.exports = { module.exports = {
extends: ['plugin:react/recommended', 'plugin:@next/next/recommended'], extends: ['plugin:react/recommended', 'plugin:@next/next/recommended'],
rules: { rules: {
'react-hooks/rules-of-hooks': 'error', // Checks rules of Hooks
'react-hooks/exhaustive-deps': 'warn', // Checks effect dependencies
'react/prop-types': 0, 'react/prop-types': 0,
'react/display-name': 0, 'react/display-name': 0,
'react/react-in-jsx-scope': 0, 'react/react-in-jsx-scope': 0,
'@next/next/no-img-element': 0, '@next/next/no-img-element': 0,
'unused-imports/no-unused-imports': 'warn',
// 'no-unused-vars': 'warn',
'quotes': [2, 'single', { 'avoidEscape': true }],
'import/order': [ 'import/order': [
'error', 'error',
{ {
@ -40,7 +45,7 @@ module.exports = {
ecmaVersion: 7, ecmaVersion: 7,
sourceType: 'module', sourceType: 'module',
}, },
plugins: ['import', 'react-hooks'], plugins: ['import', 'react-hooks', 'unused-imports'],
parser: '@typescript-eslint/parser', parser: '@typescript-eslint/parser',
settings: { settings: {
react: { react: {

View file

@ -91,7 +91,8 @@
"eslint": "^7.32.0", "eslint": "^7.32.0",
"eslint-plugin-import": "^2.25.4", "eslint-plugin-import": "^2.25.4",
"eslint-plugin-react": "^7.28.0", "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", "husky": "^7.0.4",
"lint-staged": "^12.1.6", "lint-staged": "^12.1.6",
"next-transpile-modules": "^9.0.0", "next-transpile-modules": "^9.0.0",

View file

@ -4,9 +4,9 @@ import {
ApiCreateNoteRequest, ApiCreateNoteRequest,
ApiUpdateNoteResponse, ApiUpdateNoteResponse,
ApiUpdateNoteRequest, ApiUpdateNoteRequest,
} from "~/api/notes/types"; } from '~/api/notes/types';
import { URLS } from "~/constants/urls"; import { URLS } from '~/constants/urls';
import { api, cleanResult } from "~/utils/api"; import { api, cleanResult } from '~/utils/api';
export const apiListNotes = ({ limit, offset, search }: ApiListNotesRequest) => export const apiListNotes = ({ limit, offset, search }: ApiListNotesRequest) =>
api api

View file

@ -1,4 +1,4 @@
import { Note } from "~/types/notes"; import { Note } from '~/types/notes';
export interface ApiGetNotesRequest { export interface ApiGetNotesRequest {
limit: number; limit: number;

View file

@ -86,6 +86,7 @@ const LoginAnimatedScene: FC<LoginSceneProps> = memo(() => {
const listener = throttle(100, onMouseMove); const listener = throttle(100, onMouseMove);
document.addEventListener('mousemove', listener); document.addEventListener('mousemove', listener);
return () => document.removeEventListener('mousemove', listener); return () => document.removeEventListener('mousemove', listener);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
if (isTablet) { if (isTablet) {

View file

@ -1,10 +1,8 @@
import React, { FC, ReactNode, useCallback } from 'react'; import { FC, ReactNode } from 'react';
import { WithDescription } from '~/components/common/WithDescription'; import { WithDescription } from '~/components/common/WithDescription';
import { Icon } from '~/components/input/Icon'; import { Icon } from '~/components/input/Icon';
import styles from './styles.module.scss';
interface Props { interface Props {
icon: string; icon: string;
title: string; title: string;
@ -22,12 +20,6 @@ const BorisContactItem: FC<Props> = ({
prefix, prefix,
suffix, suffix,
}) => { }) => {
const onClick = useCallback(() => {
if (!link) return;
window.open(link);
}, []);
return ( return (
<div> <div>
{prefix} {prefix}

View file

@ -1,7 +1,6 @@
import React, { FC } from 'react'; import React, { FC } from 'react';
import { BorisContactItem } from '~/components/boris/BorisContactItem'; import { BorisContactItem } from '~/components/boris/BorisContactItem';
import { Group } from '~/components/containers/Group';
import { Padder } from '~/components/containers/Padder'; import { Padder } from '~/components/containers/Padder';
import { Button } from '~/components/input/Button'; import { Button } from '~/components/input/Button';

View file

@ -1,10 +1,7 @@
import React, { VFC } from 'react'; import React, { VFC } from 'react';
import { parseISO } from 'date-fns';
import { StatsCountdownCard } from '~/components/charts/StatsCountdownCard';
import { StatsGraphCard } from '~/components/charts/StatsGraphCard'; import { StatsGraphCard } from '~/components/charts/StatsGraphCard';
import { foundationDate } from '~/constants/boris/constants';
import styles from './styles.module.scss'; import styles from './styles.module.scss';

View file

@ -20,7 +20,7 @@ const BorisStatsBackend: FC<IProps> = ({ isLoading, stats }) => {
); );
const nodesByMonth = useMemo( const nodesByMonth = useMemo(
() => stats.nodes.by_month?.slice(0, -1), () => stats.nodes.by_month?.slice(0, -1),
[stats.comments.by_month], [stats.nodes.by_month],
); );
if (!stats && !isLoading) { if (!stats && !isLoading) {

View file

@ -1,7 +1,5 @@
import React, { useMemo, VFC } from 'react'; import React, { useMemo, VFC } from 'react';
import { lighten } from 'color2k';
import { makeBezierCurve, PathPoint } from '~/utils/dom/makeBezierCurve'; import { makeBezierCurve, PathPoint } from '~/utils/dom/makeBezierCurve';
import { SVGProps } from '~/utils/types'; 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) { if (!points.length) {

View file

@ -11,15 +11,13 @@ import React, {
import classnames from 'classnames'; import classnames from 'classnames';
import { CommentForm } from '~/components/comment/CommentForm';
import { Authorized } from '~/components/containers/Authorized'; import { Authorized } from '~/components/containers/Authorized';
import { Group } from '~/components/containers/Group'; import { Group } from '~/components/containers/Group';
import { AudioPlayer } from '~/components/media/AudioPlayer'; import { AudioPlayer } from '~/components/media/AudioPlayer';
import { COMMENT_BLOCK_RENDERERS } from '~/constants/comment'; import { COMMENT_BLOCK_RENDERERS } from '~/constants/comment';
import { UploadType } from '~/constants/uploads'; import { UploadType } from '~/constants/uploads';
import { imagePresets } from '~/constants/urls';
import { IComment, IFile } from '~/types'; 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 { append, assocPath, path, reduce } from '~/utils/ramda';
import { CommentEditingForm } from '../CommentEditingForm'; import { CommentEditingForm } from '../CommentEditingForm';

View file

@ -10,7 +10,8 @@ interface CommentDistanceProps {
secondDate?: Date; secondDate?: Date;
} }
const CommentDistance: FC<CommentDistanceProps> = memo(({ firstDate, secondDate }) => { const CommentDistance: FC<CommentDistanceProps> = memo(
({ firstDate, secondDate }) => {
const distance = useMemo(() => { const distance = useMemo(() => {
if (!firstDate || !secondDate) { if (!firstDate || !secondDate) {
return undefined; return undefined;
@ -20,13 +21,14 @@ const CommentDistance: FC<CommentDistanceProps> = memo(({ firstDate, secondDate
locale: ru, locale: ru,
addSuffix: false, addSuffix: false,
}); });
}, []); }, [firstDate, secondDate]);
if (!distance) { if (!distance) {
return null; return null;
} }
return <div className={styles.bar}>прошло {distance}</div>; return <div className={styles.bar}>прошло {distance}</div>;
}); },
);
export { CommentDistance }; export { CommentDistance };

View file

@ -1,4 +1,4 @@
import { FC, useCallback, useMemo, useState } from 'react'; import { FC, useCallback, useState } from 'react';
import { FormikProvider } from 'formik'; import { FormikProvider } from 'formik';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
@ -9,14 +9,12 @@ import { CommentFormFormatButtons } from '~/components/comment/CommentFormFormat
import { LocalCommentFormTextarea } from '~/components/comment/LocalCommentFormTextarea'; import { LocalCommentFormTextarea } from '~/components/comment/LocalCommentFormTextarea';
import { Filler } from '~/components/containers/Filler'; import { Filler } from '~/components/containers/Filler';
import { Button } from '~/components/input/Button'; import { Button } from '~/components/input/Button';
import { UploadDropzone } from '~/components/upload/UploadDropzone';
import { ERROR_LITERAL } from '~/constants/errors'; import { ERROR_LITERAL } from '~/constants/errors';
import { EMPTY_COMMENT } from '~/constants/node'; import { EMPTY_COMMENT } from '~/constants/node';
import { useCommentFormFormik } from '~/hooks/comments/useCommentFormFormik'; import { useCommentFormFormik } from '~/hooks/comments/useCommentFormFormik';
import { useInputPasteUpload } from '~/hooks/dom/useInputPasteUpload'; import { useInputPasteUpload } from '~/hooks/dom/useInputPasteUpload';
import { IComment, INode } from '~/types'; import { IComment } from '~/types';
import { import {
UploaderContextProvider,
useUploaderContext, useUploaderContext,
} from '~/utils/context/UploaderContextProvider'; } from '~/utils/context/UploaderContextProvider';

View file

@ -30,14 +30,14 @@ const CommentFormAttaches: FC = () => {
(newFiles: IFile[]) => { (newFiles: IFile[]) => {
setFiles([...filesAudios, ...newFiles.filter((it) => it)]); setFiles([...filesAudios, ...newFiles.filter((it) => it)]);
}, },
[setFiles, filesImages, filesAudios], [setFiles, filesAudios],
); );
const onAudioMove = useCallback( const onAudioMove = useCallback(
(newFiles: IFile[]) => { (newFiles: IFile[]) => {
setFiles([...filesImages, ...newFiles]); setFiles([...filesImages, ...newFiles]);
}, },
[setFiles, filesImages, filesAudios], [setFiles, filesImages],
); );
const onFileDelete = useCallback( const onFileDelete = useCallback(

View file

@ -115,7 +115,7 @@ const CommentFormFormatButtons: FC<IProps> = ({ element, handler }) => {
label="Коммент" label="Коммент"
className={styles.button} className={styles.button}
> >
{`/ /`} {'/ /'}
</Button> </Button>
</ButtonGroup> </ButtonGroup>
); );

View file

@ -4,7 +4,6 @@ import classNames from 'classnames';
import { Square } from '~/components/common/Square'; import { Square } from '~/components/common/Square';
import { imagePresets } from '~/constants/urls'; import { imagePresets } from '~/constants/urls';
import { useColorGradientFromString } from '~/hooks/color/useColorGradientFromString';
import { getURLFromString } from '~/utils/dom'; import { getURLFromString } from '~/utils/dom';
import { DivProps } from '~/utils/types'; import { DivProps } from '~/utils/types';

View file

@ -1,10 +1,8 @@
import React, { import React, {
CSSProperties, CSSProperties,
FC, FC,
useCallback,
useMemo, useMemo,
useReducer, useReducer,
useState,
} from 'react'; } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';

View file

@ -16,7 +16,7 @@ const WithDescription: FC<Props> = ({ icon, title, subtitle, link }) => {
if (!link) return; if (!link) return;
window.open(link); window.open(link);
}, []); }, [link]);
return ( return (
<div <div

View file

@ -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'; import Masonry from 'react-masonry-css';
@ -34,6 +34,7 @@ const Columns: FC<ColumnsProps> = ({
const timeout = setTimeout(() => setColumns([...childs]), 150); const timeout = setTimeout(() => setColumns([...childs]), 150);
return () => clearTimeout(timeout); return () => clearTimeout(timeout);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ref.current]); }, [ref.current]);
useScrollEnd(columns, onScrollEnd, { active: hasMore, threshold: 2 }); useScrollEnd(columns, onScrollEnd, { active: hasMore, threshold: 2 });

View file

@ -15,7 +15,7 @@ const AudioGrid: FC<IProps> = ({ files, setFiles, locked }) => {
(newFiles: IFile[]) => { (newFiles: IFile[]) => {
setFiles(newFiles); setFiles(newFiles);
}, },
[setFiles, files], [setFiles],
); );
const onDrop = useCallback( const onDrop = useCallback(

View file

@ -18,7 +18,7 @@ const ImageGrid: FC<IProps> = ({ files, setFiles, locked }) => {
(newFiles: IFile[]) => { (newFiles: IFile[]) => {
setFiles(newFiles.filter((it) => it)); setFiles(newFiles.filter((it) => it));
}, },
[setFiles, files], [setFiles],
); );
const onDrop = useCallback( const onDrop = useCallback(

View file

@ -25,12 +25,16 @@ const CellShade: FC<Props> = ({ color, size = 50, angle = 7, ...rest }) => {
return `linear-gradient(${angle}deg, ${normalized} ${size}px, ${transparentize( return `linear-gradient(${angle}deg, ${normalized} ${size}px, ${transparentize(
normalized, normalized,
1 1,
)} ${size * 5}px)`; )} ${size * 5}px)`;
}, [color, size]); }, [angle, color, size]);
return ( return (
<div {...rest} className={classNames(rest.className, styles.shade)} style={{ background }} /> <div
{...rest}
className={classNames(rest.className, styles.shade)}
style={{ background }}
/>
); );
}; };

View file

@ -1,4 +1,4 @@
import React, { FC, useCallback } from 'react'; import React, { useCallback } from 'react';
import { Filler } from '~/components/containers/Filler'; import { Filler } from '~/components/containers/Filler';
import { Button } from '~/components/input/Button'; import { Button } from '~/components/input/Button';
@ -10,7 +10,7 @@ import styles from './styles.module.scss';
const FlowLoginStamp = () => { const FlowLoginStamp = () => {
const showModal = useShowModal(Dialog.Login); const showModal = useShowModal(Dialog.Login);
const onClick = useCallback(() => showModal({}), []); const onClick = useCallback(() => showModal({}), [showModal]);
return ( return (
<div className={styles.stamp}> <div className={styles.stamp}>

View file

@ -1,7 +1,6 @@
import React, { FC, useCallback, useMemo, useState } from 'react'; import React, { FC, useCallback, useMemo, useState } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import Image from 'next/image';
import SwiperCore, { Autoplay, EffectFade, Lazy, Navigation } from 'swiper'; import SwiperCore, { Autoplay, EffectFade, Lazy, Navigation } from 'swiper';
import { Swiper, SwiperSlide } from 'swiper/react'; import { Swiper, SwiperSlide } from 'swiper/react';
import SwiperClass from 'swiper/types/swiper-class'; import SwiperClass from 'swiper/types/swiper-class';

View file

@ -90,7 +90,7 @@ const Button: FC<IButtonProps> = memo(
} }
return 24; return 24;
}, []); }, [size]);
return ( return (
<Tippy content={label || ''} disabled={!label}> <Tippy content={label || ''} disabled={!label}>

View file

@ -3,7 +3,6 @@ import React, { FC } from 'react';
import Image from 'next/future/image'; import Image from 'next/future/image';
import SwiperCore, { A11y, Navigation, Pagination } from 'swiper'; import SwiperCore, { A11y, Navigation, Pagination } from 'swiper';
import { ImagePreloader } from '~/components/media/ImagePreloader';
import { Placeholder } from '~/components/placeholders/Placeholder'; import { Placeholder } from '~/components/placeholders/Placeholder';
import { INodeComponentProps } from '~/constants/node'; import { INodeComponentProps } from '~/constants/node';
import { imagePresets } from '~/constants/urls'; import { imagePresets } from '~/constants/urls';

View file

@ -61,6 +61,7 @@ const MenuButton: FC<MenuButtonProps> = ({
useEffect(() => { useEffect(() => {
popper.update?.(); popper.update?.();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [visible]); }, [visible]);
return ( return (

View file

@ -2,7 +2,6 @@ import { PropsWithChildren } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { Anchor } from '~/components/common/Anchor';
import { DivProps, LinkProps } from '~/utils/types'; import { DivProps, LinkProps } from '~/utils/types';
import styles from './styles.module.scss'; import styles from './styles.module.scss';

View file

@ -89,6 +89,7 @@ const NodeImageSwiperBlock: FC<IProps> = observer(({ node }) => {
useEffect(() => { useEffect(() => {
controlledSwiper?.slideTo(0, 0); controlledSwiper?.slideTo(0, 0);
return () => controlledSwiper?.slideTo(0, 0); return () => controlledSwiper?.slideTo(0, 0);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [images, node?.id]); }, [images, node?.id]);
useEffect(() => { useEffect(() => {
@ -97,6 +98,7 @@ const NodeImageSwiperBlock: FC<IProps> = observer(({ node }) => {
} else { } else {
controlledSwiper?.keyboard.enable(); controlledSwiper?.keyboard.enable();
} }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isModalActive]); }, [isModalActive]);
if (!images?.length) { if (!images?.length) {

View file

@ -1,14 +1,11 @@
import React, { FC, useCallback } from 'react'; import React, { FC } from 'react';
import { Pressable } from '~/components/common/Pressable'; import { Pressable } from '~/components/common/Pressable';
import { NodeRelated } from '~/components/node/NodeRelated'; import { NodeRelated } from '~/components/node/NodeRelated';
import { NodeRelatedPlaceholder } from '~/components/node/NodeRelated/placeholder'; 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 { useTagSidebar } from '~/hooks/sidebar/useTagSidebar';
import { INode, ITag } from '~/types'; import { INode } from '~/types';
import { INodeRelated } from '~/types/node'; import { INodeRelated } from '~/types/node';
import { useSidebar } from '~/utils/providers/SidebarProvider';
interface IProps { interface IProps {
isLoading: boolean; isLoading: boolean;

View file

@ -25,14 +25,14 @@ const NoteCard: VFC<NoteCardProps> = ({
}) => { }) => {
const [editing, setEditing] = useState(false); const [editing, setEditing] = useState(false);
const toggleEditing = useCallback(() => setEditing(v => !v), []); const toggleEditing = useCallback(() => setEditing((v) => !v), []);
const onUpdate = useCallback( const onUpdate = useCallback(
(text: string, callback?: () => void) => (text: string, callback?: () => void) =>
update(text, () => { update(text, () => {
setEditing(false); setEditing(false);
callback?.(); callback?.();
}), }),
[], [update],
); );
return ( return (

View file

@ -3,8 +3,6 @@ import React, { FC } from 'react';
import { Anchor } from '~/components/common/Anchor'; import { Anchor } from '~/components/common/Anchor';
import { InlineUsername } from '~/components/common/InlineUsername'; import { InlineUsername } from '~/components/common/InlineUsername';
import { Square } from '~/components/common/Square'; 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 { NotificationItem, NotificationType } from '~/types/notifications';
import { formatText, getPrettyDate, getURLFromString } from '~/utils/dom'; import { formatText, getPrettyDate, getURLFromString } from '~/utils/dom';

View file

@ -2,9 +2,7 @@ import React, { ChangeEvent, FC, useCallback } from 'react';
import { Avatar } from '~/components/common/Avatar'; import { Avatar } from '~/components/common/Avatar';
import { Button } from '~/components/input/Button'; import { Button } from '~/components/input/Button';
import { imagePresets } from '~/constants/urls';
import { IFile } from '~/types'; import { IFile } from '~/types';
import { getURL } from '~/utils/dom';
import styles from './styles.module.scss'; import styles from './styles.module.scss';

View file

@ -2,7 +2,6 @@ import { useState, VFC } from 'react';
import { Group } from '~/components/containers/Group'; import { Group } from '~/components/containers/Group';
import { Button } from '~/components/input/Button'; import { Button } from '~/components/input/Button';
import { Icon } from '~/components/input/Icon';
import { HorizontalMenu } from '~/components/menu/HorizontalMenu'; import { HorizontalMenu } from '~/components/menu/HorizontalMenu';
import { useStackContext } from '~/components/sidebar/SidebarStack'; import { useStackContext } from '~/components/sidebar/SidebarStack';
import { SidebarStackCard } from '~/components/sidebar/SidebarStackCard'; import { SidebarStackCard } from '~/components/sidebar/SidebarStackCard';

View file

@ -38,7 +38,7 @@ const SidebarCards: FC = ({ children }) => {
return []; return [];
} }
return Array.isArray(children) ? children.filter(it => it) : [children]; return Array.isArray(children) ? children.filter((it) => it) : [children];
}, [children]); }, [children]);
if (isNil(activeTab) || !nonEmptyChildren[activeTab]) { if (isNil(activeTab) || !nonEmptyChildren[activeTab]) {
@ -58,7 +58,7 @@ const SidebarStack = function({
const closeAllTabs = useCallback(() => { const closeAllTabs = useCallback(() => {
setActiveTab(undefined); setActiveTab(undefined);
onTabChange?.(undefined); onTabChange?.(undefined);
}, []); }, [onTabChange]);
const onChangeTab = useCallback( const onChangeTab = useCallback(
(index: number) => { (index: number) => {

View file

@ -22,8 +22,8 @@ export const API = {
GET_SOCIALS: '/oauth', GET_SOCIALS: '/oauth',
DROP_SOCIAL: (provider, id) => `/oauth/${provider}/${id}`, DROP_SOCIAL: (provider, id) => `/oauth/${provider}/${id}`,
ATTACH_SOCIAL: `/oauth`, ATTACH_SOCIAL: '/oauth',
LOGIN_WITH_SOCIAL: `/oauth`, LOGIN_WITH_SOCIAL: '/oauth',
ATTACH_TELEGRAM: '/oauth/telegram/attach', ATTACH_TELEGRAM: '/oauth/telegram/attach',
}, },
NODES: { NODES: {
@ -55,11 +55,11 @@ export const API = {
GITHUB_ISSUES: 'https://api.github.com/repos/muerwre/vault-frontend/issues', GITHUB_ISSUES: 'https://api.github.com/repos/muerwre/vault-frontend/issues',
}, },
TAG: { TAG: {
NODES: `/tags/nodes`, NODES: '/tags/nodes',
AUTOCOMPLETE: `/tags/autocomplete`, AUTOCOMPLETE: '/tags/autocomplete',
}, },
LAB: { LAB: {
NODES: `/nodes/lab`, NODES: '/nodes/lab',
STATS: '/nodes/lab/stats', STATS: '/nodes/lab/stats',
UPDATES: '/nodes/lab/updates', UPDATES: '/nodes/lab/updates',
}, },

View file

@ -1,4 +1,3 @@
import { SettingsSidebar } from '~/containers/sidebars/ProfileSidebar';
export enum SidebarName { export enum SidebarName {
Settings = 'settings', Settings = 'settings',

View file

@ -17,7 +17,7 @@ export const themeColors: Record<Theme, ThemeColors> = {
'linear-gradient(165deg, #ff7549 -50%, #ff3344 150%)', 'linear-gradient(165deg, #ff7549 -50%, #ff3344 150%)',
'linear-gradient(170deg, #582cd0, #592071)', '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]: { [Theme.Horizon]: {
name: 'Веспера', name: 'Веспера',
@ -26,6 +26,6 @@ export const themeColors: Record<Theme, ThemeColors> = {
'linear-gradient(165deg, #fab795 -50%, #fab795 150%)', 'linear-gradient(165deg, #fab795 -50%, #fab795 150%)',
'linear-gradient(170deg, #25b0bc, #7693d6)', '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)',
}, },
}; };

View file

@ -4,7 +4,6 @@ import { BorisContacts } from '~/components/boris/BorisContacts';
import { BorisStats } from '~/components/boris/BorisStats'; import { BorisStats } from '~/components/boris/BorisStats';
import { Group } from '~/components/containers/Group'; import { Group } from '~/components/containers/Group';
import { SuperPowersToggle } from '~/containers/auth/SuperPowersToggle'; import { SuperPowersToggle } from '~/containers/auth/SuperPowersToggle';
import { useAuth } from '~/hooks/auth/useAuth';
import { useTelegramAccount } from '~/hooks/auth/useTelegramAccount'; import { useTelegramAccount } from '~/hooks/auth/useTelegramAccount';
import { BorisUsageStats } from '~/types/boris'; import { BorisUsageStats } from '~/types/boris';

View file

@ -21,7 +21,7 @@ const TelegramAttachDialog: FC<TelegramAttachDialogProps> = ({
const onAttach = useCallback( const onAttach = useCallback(
(data: TelegramUser) => attach(data, onRequestClose), (data: TelegramUser) => attach(data, onRequestClose),
[onRequestClose], [attach, onRequestClose],
); );
const buttons = useMemo( const buttons = useMemo(

View file

@ -2,7 +2,6 @@ import { FC, memo } from 'react';
import { Hoverable } from '~/components/common/Hoverable'; import { Hoverable } from '~/components/common/Hoverable';
import { Columns } from '~/components/containers/Columns'; import { Columns } from '~/components/containers/Columns';
import { InfiniteScroll } from '~/components/containers/InfiniteScroll';
import { LabNoResults } from '~/components/lab/LabNoResults'; import { LabNoResults } from '~/components/lab/LabNoResults';
import { LabNode } from '~/components/lab/LabNode'; import { LabNode } from '~/components/lab/LabNode';
import { useLabContext } from '~/utils/context/LabContextProvider'; import { useLabContext } from '~/utils/context/LabContextProvider';

View file

@ -43,7 +43,7 @@ const Header: FC<HeaderProps> = observer(() => {
borisCommentedAt && borisCommentedAt &&
((fetched && !user.last_seen_boris) || ((fetched && !user.last_seen_boris) ||
isBefore(new Date(user.last_seen_boris), new Date(borisCommentedAt))), 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 // Needed for SSR

View file

@ -2,7 +2,6 @@ import { FC, useMemo } from 'react';
import { SubTitle } from '~/components/common/SubTitle'; import { SubTitle } from '~/components/common/SubTitle';
import { Card } from '~/components/containers/Card'; import { Card } from '~/components/containers/Card';
import { Padder } from '~/components/containers/Padder';
import { Backlink } from '~/components/node/Backlink'; import { Backlink } from '~/components/node/Backlink';
import { NodeBackLink } from '~/types'; import { NodeBackLink } from '~/types';
import { has } from '~/utils/ramda'; import { has } from '~/utils/ramda';

View file

@ -5,7 +5,6 @@ import { observer } from 'mobx-react-lite';
import { CommentForm } from '~/components/comment/CommentForm'; import { CommentForm } from '~/components/comment/CommentForm';
import { CommentWrapper } from '~/components/containers/CommentWrapper'; import { CommentWrapper } from '~/components/containers/CommentWrapper';
import { UploadDropzone } from '~/components/upload/UploadDropzone'; import { UploadDropzone } from '~/components/upload/UploadDropzone';
import { EMPTY_USER } from '~/constants/auth';
import { Dialog } from '~/constants/modal'; import { Dialog } from '~/constants/modal';
import { UploadSubject, UploadTarget } from '~/constants/uploads'; import { UploadSubject, UploadTarget } from '~/constants/uploads';
import { useAuth } from '~/hooks/auth/useAuth'; import { useAuth } from '~/hooks/auth/useAuth';

View file

@ -23,9 +23,11 @@ const NotificationList: FC<NotificationListProps> = () => {
useEffect(() => { useEffect(() => {
return () => markAsRead(); return () => markAsRead();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
const renderItem = useCallback((item: NotificationItem) => { const renderItem = useCallback(
(item: NotificationItem) => {
const isNew = const isNew =
!lastSeen || !lastSeen ||
!item.created_at || !item.created_at ||
@ -39,7 +41,9 @@ const NotificationList: FC<NotificationListProps> = () => {
default: default:
return null; return null;
} }
}, []); },
[lastSeen],
);
if (isLoading) { if (isLoading) {
return <LoaderScreen align="top" />; return <LoaderScreen align="top" />;

View file

@ -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 { Group } from '~/components/containers/Group';
import { Button } from '~/components/input/Button'; import { Button } from '~/components/input/Button';
import { Icon } from '~/components/input/Icon'; import { Icon } from '~/components/input/Icon';
import { Placeholder } from '~/components/placeholders/Placeholder'; import { Placeholder } from '~/components/placeholders/Placeholder';
import { SOCIAL_ICONS } from '~/constants/auth/socials'; import { SOCIAL_ICONS } from '~/constants/auth/socials';
import { Dialog } from '~/constants/modal';
import { useOAuth } from '~/hooks/auth/useOAuth'; import { useOAuth } from '~/hooks/auth/useOAuth';
import { useModal } from '~/hooks/modal/useModal';
import styles from './styles.module.scss'; import styles from './styles.module.scss';

View file

@ -1,11 +1,9 @@
import React, { FC } from 'react'; import React, { FC } from 'react';
import { Avatar } from '~/components/common/Avatar'; import { Avatar } from '~/components/common/Avatar';
import { Markdown } from '~/components/containers/Markdown';
import { Placeholder } from '~/components/placeholders/Placeholder'; import { Placeholder } from '~/components/placeholders/Placeholder';
import { imagePresets } from '~/constants/urls'; import { imagePresets } from '~/constants/urls';
import { IUser } from '~/types/auth'; import { IUser } from '~/types/auth';
import { formatText } from '~/utils/dom';
import styles from './styles.module.scss'; import styles from './styles.module.scss';

View file

@ -2,12 +2,10 @@ import React, { FC } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { Avatar } from '~/components/common/Avatar';
import { Filler } from '~/components/containers/Filler'; import { Filler } from '~/components/containers/Filler';
import { Group } from '~/components/containers/Group'; import { Group } from '~/components/containers/Group';
import { useUserActiveStatus } from '~/hooks/auth/useUserActiveStatus'; import { useUserActiveStatus } from '~/hooks/auth/useUserActiveStatus';
import { IUser } from '~/types/auth'; import { IUser } from '~/types/auth';
import { path } from '~/utils/ramda';
import styles from './styles.module.scss'; import styles from './styles.module.scss';

View file

@ -2,7 +2,6 @@ import React, { useCallback, VFC } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { Superpower } from '~/components/boris/Superpower';
import { Filler } from '~/components/containers/Filler'; import { Filler } from '~/components/containers/Filler';
import { Group } from '~/components/containers/Group'; import { Group } from '~/components/containers/Group';
import { Zone } from '~/components/containers/Zone'; import { Zone } from '~/components/containers/Zone';
@ -32,7 +31,7 @@ const ProfileSidebarMenu: VFC<ProfileSidebarMenuProps> = ({ onClose }) => {
const onLogout = useCallback(() => { const onLogout = useCallback(() => {
logout(); logout();
onClose(); onClose();
}, [onClose]); }, [logout, onClose]);
return ( return (
<div className={styles.wrap}> <div className={styles.wrap}>

View file

@ -1,7 +1,6 @@
import React, { FC } from 'react'; import React, { FC } from 'react';
import { Group } from '~/components/containers/Group'; import { Group } from '~/components/containers/Group';
import { Zone } from '~/components/containers/Zone';
import { SuperPowersToggle } from '~/containers/auth/SuperPowersToggle'; import { SuperPowersToggle } from '~/containers/auth/SuperPowersToggle';
interface ProfileTogglesProps {} interface ProfileTogglesProps {}

View file

@ -20,7 +20,7 @@ const List = () => {
async (id: number) => { async (id: number) => {
confirm('Удалить? Это удалит заметку навсегда', () => remove(id)); confirm('Удалить? Это удалит заметку навсегда', () => remove(id));
}, },
[remove], [confirm, remove],
); );
return ( return (

View file

@ -6,11 +6,9 @@ import { Group } from '~/components/containers/Group';
import { Zone } from '~/components/containers/Zone'; import { Zone } from '~/components/containers/Zone';
import { InputText } from '~/components/input/InputText'; import { InputText } from '~/components/input/InputText';
import { Textarea } from '~/components/input/Textarea'; import { Textarea } from '~/components/input/Textarea';
import { ERROR_LITERAL } from '~/constants/errors';
import { ProfileAccounts } from '~/containers/profile/ProfileAccounts'; import { ProfileAccounts } from '~/containers/profile/ProfileAccounts';
import { useWindowSize } from '~/hooks/dom/useWindowSize'; import { useWindowSize } from '~/hooks/dom/useWindowSize';
import { useSettings } from '~/utils/providers/SettingsProvider'; import { useSettings } from '~/utils/providers/SettingsProvider';
import { has } from '~/utils/ramda';
import styles from './styles.module.scss'; import styles from './styles.module.scss';

View file

@ -10,7 +10,6 @@ import { SidebarWrapper } from '~/components/sidebar/SidebarWrapper';
import { SidebarName } from '~/constants/sidebar'; import { SidebarName } from '~/constants/sidebar';
import { ProfileSidebarMenu } from '~/containers/profile/ProfileSidebarMenu'; import { ProfileSidebarMenu } from '~/containers/profile/ProfileSidebarMenu';
import { useAuth } from '~/hooks/auth/useAuth'; import { useAuth } from '~/hooks/auth/useAuth';
import { useUser } from '~/hooks/auth/useUser';
import type { SidebarComponentProps } from '~/types/sidebar'; import type { SidebarComponentProps } from '~/types/sidebar';
import { isNil } from '~/utils/ramda'; import { isNil } from '~/utils/ramda';
@ -41,13 +40,14 @@ const SettingsSidebar: VFC<SettingsSidebarProps> = ({
page: !isNil(val) ? tabs[val] : undefined, page: !isNil(val) ? tabs[val] : undefined,
}); });
}, },
[open, onRequestClose], [openSidebar],
); );
useEffect(() => { useEffect(() => {
if (!isUser) { if (!isUser) {
onRequestClose(); onRequestClose();
} }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isUser]); }, [isUser]);
if (!isUser) { if (!isUser) {

View file

@ -2,7 +2,6 @@ import { useMemo, VFC } from 'react';
import { InfiniteScroll } from '~/components/containers/InfiniteScroll'; import { InfiniteScroll } from '~/components/containers/InfiniteScroll';
import { Icon } from '~/components/input/Icon'; import { Icon } from '~/components/input/Icon';
import { LoaderCircle } from '~/components/input/LoaderCircle';
import { SidebarStack } from '~/components/sidebar/SidebarStack'; import { SidebarStack } from '~/components/sidebar/SidebarStack';
import { SidebarStackCard } from '~/components/sidebar/SidebarStackCard'; import { SidebarStackCard } from '~/components/sidebar/SidebarStackCard';
import { SidebarWrapper } from '~/components/sidebar/SidebarWrapper'; import { SidebarWrapper } from '~/components/sidebar/SidebarWrapper';

View file

@ -95,7 +95,6 @@ export const useOAuth = () => {
); );
const accounts = useMemo(() => data || [], [data]); const accounts = useMemo(() => data || [], [data]);
const refresh = useCallback(() => mutate(), []);
const hasTelegram = useMemo( const hasTelegram = useMemo(
() => accounts.some((acc) => acc.provider === 'telegram'), () => accounts.some((acc) => acc.provider === 'telegram'),
@ -104,7 +103,7 @@ export const useOAuth = () => {
const showTelegramModal = useCallback( const showTelegramModal = useCallback(
() => showModal(Dialog.TelegramAttach, {}), () => showModal(Dialog.TelegramAttach, {}),
[], [showModal],
); );
return { return {
@ -117,6 +116,6 @@ export const useOAuth = () => {
dropAccount, dropAccount,
accounts, accounts,
isLoading: !data && isLoading, isLoading: !data && isLoading,
refresh, refresh: mutate,
}; };
}; };

View file

@ -5,7 +5,6 @@ import { ApiUpdateUserRequest } from '~/api/auth/types';
import { UploadSubject, UploadTarget } from '~/constants/uploads'; import { UploadSubject, UploadTarget } from '~/constants/uploads';
import { useUser } from '~/hooks/auth/useUser'; import { useUser } from '~/hooks/auth/useUser';
import { useUploader } from '~/hooks/data/useUploader'; import { useUploader } from '~/hooks/data/useUploader';
import { IFile } from '~/types';
import { showErrorToast } from '~/utils/errors/showToast'; import { showErrorToast } from '~/utils/errors/showToast';
export const usePatchUser = () => { export const usePatchUser = () => {
@ -39,7 +38,7 @@ export const usePatchUser = () => {
showErrorToast(error); showErrorToast(error);
} }
}, },
[uploadFile, save], [uploadFile, update],
); );
return { updatePhoto, save }; return { updatePhoto, save };

View file

@ -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 = () => { export const useSuperPowers = () => {
const { isTester, setIsTester } = useAuth(); const { isTester, setIsTester } = useAuth();

View file

@ -30,10 +30,13 @@ export const useTelegramAccount = () => {
setLoading(false); setLoading(false);
} }
}, },
[], [refresh, refreshNotificationSettings],
); );
const connect = useCallback(() => showModal(Dialog.TelegramAttach, {}), []); const connect = useCallback(
() => showModal(Dialog.TelegramAttach, {}),
[showModal],
);
const connected = useMemo( const connected = useMemo(
() => accounts.some((it) => it.provider === 'telegram'), () => accounts.some((it) => it.provider === 'telegram'),

View file

@ -5,12 +5,12 @@ import { IUser } from '~/types/auth';
export const useUserDescription = (user?: Partial<IUser>) => { export const useUserDescription = (user?: Partial<IUser>) => {
const randomPhrase = useRandomPhrase('USER_DESCRIPTION'); const randomPhrase = useRandomPhrase('USER_DESCRIPTION');
const isActive = useUserActiveStatus(user?.last_seen);
if (!user) { if (!user) {
return ''; return '';
} }
const isActive = useUserActiveStatus(user.last_seen);
if (!isActive) { if (!isActive) {
return 'Юнит деактивирован'; return 'Юнит деактивирован';
} }

View file

@ -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 { useRandomPhrase } from '~/constants/phrases';
import { useLastSeenBoris } from "~/hooks/auth/useLastSeenBoris"; import { useLastSeenBoris } from '~/hooks/auth/useLastSeenBoris';
import { useBorisStats } from "~/hooks/boris/useBorisStats"; import { useBorisStats } from '~/hooks/boris/useBorisStats';
import { IComment } from "~/types"; import { IComment } from '~/types';
export const useBoris = (comments: IComment[]) => { export const useBoris = (comments: IComment[]) => {
const title = useRandomPhrase("BORIS_TITLE"); const title = useRandomPhrase('BORIS_TITLE');
const { lastSeen, setLastSeen } = useLastSeenBoris(); const { lastSeen, setLastSeen } = useLastSeenBoris();

View file

@ -1,8 +1,8 @@
import { useCallback } from "react"; import { useCallback } from 'react';
export const useConfirmation = () => export const useConfirmation = () =>
useCallback((prompt = "", onApprove: () => {}, onReject?: () => {}) => { useCallback((prompt = '', onApprove: () => {}, onReject?: () => {}) => {
if (!window.confirm(prompt || "Уверен?")) { if (!window.confirm(prompt || 'Уверен?')) {
onReject?.(); onReject?.();
return; return;
} }

View file

@ -29,9 +29,11 @@ export const useGlobalLoader = () => {
} }
hideLoader(); hideLoader();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [flow.isRefreshed, getInitialNodes]); }, [flow.isRefreshed, getInitialNodes]);
useEffect(() => { useEffect(() => {
void getInitialNodes(); void getInitialNodes();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isUser]); }, [isUser]);
}; };

View file

@ -30,7 +30,7 @@ export const useSSRLoadingIndicator = (delay = 0) => {
Router.events.on('routeChangeStart', show); Router.events.on('routeChangeStart', show);
Router.events.on('routeChangeComplete', hide); Router.events.on('routeChangeComplete', hide);
Router.events.on('routeChangeError', hide); Router.events.on('routeChangeError', hide);
}, []); }, [delay]);
return shown; return shown;
}; };

View file

@ -1,4 +1,4 @@
import { useCallback, useEffect, useMemo } from 'react'; import { useEffect, useMemo } from 'react';
import { throttle } from 'throttle-debounce'; import { throttle } from 'throttle-debounce';
@ -44,5 +44,5 @@ export const useScrollEnd = (
window.addEventListener('scroll', eventHandler); window.addEventListener('scroll', eventHandler);
return () => window.removeEventListener('scroll', eventHandler); return () => window.removeEventListener('scroll', eventHandler);
}, [item, active, innerHeight, debouncedCallback]); }, [item, active, innerHeight, debouncedCallback, threshold]);
}; };

View file

@ -1,4 +1,4 @@
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from 'react';
export const useWindowSize = () => { export const useWindowSize = () => {
const [size, setSize] = useState({ const [size, setSize] = useState({
@ -22,9 +22,10 @@ export const useWindowSize = () => {
useEffect(() => { useEffect(() => {
onResize(); 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; return size;

View file

@ -1,32 +1,30 @@
import { useCallback, useMemo } from "react"; import { useCallback, useMemo } from 'react';
import useSWRInfinite, { SWRInfiniteKeyLoader } from "swr/infinite"; import useSWRInfinite, { SWRInfiniteKeyLoader } from 'swr/infinite';
import { import {
apiCreateNote, apiCreateNote,
apiDeleteNote, apiDeleteNote,
apiListNotes, apiListNotes,
apiUpdateNote, apiUpdateNote,
} from "~/api/notes"; } from '~/api/notes';
import { ApiGetNotesRequest } from "~/api/notes/types"; import { ApiGetNotesRequest } from '~/api/notes/types';
import { useAuth } from "~/hooks/auth/useAuth"; import { useAuth } from '~/hooks/auth/useAuth';
import { GetLabNodesRequest, ILabNode } from "~/types/lab"; import { GetLabNodesRequest, ILabNode } from '~/types/lab';
import { Note } from "~/types/notes"; import { Note } from '~/types/notes';
import { flatten, uniqBy } from "~/utils/ramda"; import { flatten, uniqBy } from '~/utils/ramda';
const DEFAULT_COUNT = 20; const DEFAULT_COUNT = 20;
const getKey: (isUser: boolean, search: string) => SWRInfiniteKeyLoader = ( const getKey: (isUser: boolean, search: string) => SWRInfiniteKeyLoader =
isUser, (isUser, search) => (index, prev: ILabNode[]) => {
search,
) => (index, prev: ILabNode[]) => {
if (!isUser) return null; if (!isUser) return null;
if (index > 0 && (!prev?.length || prev.length < 20)) return null; if (index > 0 && (!prev?.length || prev.length < 20)) return null;
const props: GetLabNodesRequest = { const props: GetLabNodesRequest = {
limit: DEFAULT_COUNT, limit: DEFAULT_COUNT,
offset: index * DEFAULT_COUNT, offset: index * DEFAULT_COUNT,
search: search || "", search: search || '',
}; };
return JSON.stringify(props); return JSON.stringify(props);
@ -36,7 +34,7 @@ const parseKey = (key: string): ApiGetNotesRequest => {
try { try {
return JSON.parse(key); return JSON.parse(key);
} catch (error) { } 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) => { async (id: number, onSuccess?: () => void) => {
await apiDeleteNote(id); await apiDeleteNote(id);
await mutate( await mutate(
data?.map(page => page.filter(it => it.id !== id)), data?.map((page) => page.filter((it) => it.id !== id)),
{ revalidate: false }, { revalidate: false },
); );
onSuccess?.(); onSuccess?.();
@ -86,7 +84,7 @@ export const useNotes = (search: string) => {
async (id: number, text: string, onSuccess?: () => void) => { async (id: number, text: string, onSuccess?: () => void) => {
const result = await apiUpdateNote({ id, text }); const result = await apiUpdateNote({ id, text });
await mutate( 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 }, { revalidate: false },
); );
onSuccess?.(); onSuccess?.();
@ -94,7 +92,7 @@ export const useNotes = (search: string) => {
[mutate, data], [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 hasMore = (data?.[size - 1]?.length || 0) >= 1;
const loadMore = useCallback(() => setSize(size + 1), [setSize, size]); const loadMore = useCallback(() => setSize(size + 1), [setSize, size]);
@ -108,6 +106,6 @@ export const useNotes = (search: string) => {
remove, remove,
update, update,
}), }),
[notes, hasMore, loadMore, data, isValidating, create, remove], [notes, hasMore, loadMore, data, isValidating, create, remove, update],
); );
}; };

View file

@ -1,7 +1,6 @@
import { useCallback } from 'react'; import { useCallback } from 'react';
import { SidebarName } from '~/constants/sidebar'; import { SidebarName } from '~/constants/sidebar';
import { ITag } from '~/types';
import { useSidebar } from '~/utils/providers/SidebarProvider'; import { useSidebar } from '~/utils/providers/SidebarProvider';
export const useTagSidebar = () => { export const useTagSidebar = () => {

View file

@ -1,6 +1,12 @@
import { useCallback, useMemo, useState } from 'react'; 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 { DragEndEvent } from '@dnd-kit/core/dist/types';
import { moveArrItem } from '~/utils/fn'; import { moveArrItem } from '~/utils/fn';
@ -8,11 +14,11 @@ import { moveArrItem } from '~/utils/fn';
export const useSortableActions = <T>( export const useSortableActions = <T>(
items: T[], items: T[],
getID: (item: T) => string | number, getID: (item: T) => string | number,
onSortEnd: (items: T[]) => void onSortEnd: (items: T[]) => void,
) => { ) => {
const [draggingItem, setDraggingItem] = useState<T | null>(null); 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( const onDragEnd = useCallback(
({ active, over }: DragEndEvent) => { ({ active, over }: DragEndEvent) => {
@ -22,12 +28,12 @@ export const useSortableActions = <T>(
return; return;
} }
const oldIndex = items.findIndex(it => getID(it) === active.id); const oldIndex = items.findIndex((it) => getID(it) === active.id);
const newIndex = items.findIndex(it => getID(it) === over.id); const newIndex = items.findIndex((it) => getID(it) === over.id);
onSortEnd(moveArrItem(oldIndex, newIndex, items)); onSortEnd(moveArrItem(oldIndex, newIndex, items));
}, },
[items] [getID, items, onSortEnd],
); );
const onDragStart = useCallback( const onDragStart = useCallback(
@ -36,11 +42,11 @@ export const useSortableActions = <T>(
return; return;
} }
const activeItem = items.find(it => getID(it) === active.id); const activeItem = items.find((it) => getID(it) === active.id);
setDraggingItem(activeItem ?? null); setDraggingItem(activeItem ?? null);
}, },
[items] [getID, items],
); );
const sensors = useSensors( const sensors = useSensors(
@ -50,7 +56,7 @@ export const useSortableActions = <T>(
tolerance: 5, tolerance: 5,
}, },
}), }),
useSensor(MouseSensor) useSensor(MouseSensor),
); );
return { sensors, onDragEnd, onDragStart, draggingItem, ids }; return { sensors, onDragEnd, onDragStart, draggingItem, ids };

View file

@ -16,5 +16,6 @@ export const useFormAutoSubmit = <T>(
const timeout = setTimeout(onSubmit, delay); const timeout = setTimeout(onSubmit, delay);
return () => clearTimeout(timeout); return () => clearTimeout(timeout);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [values]); }, [values]);
}; };

View file

@ -1,5 +1,3 @@
import { FC } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { FlowGrid } from '~/components/flow/FlowGrid'; import { FlowGrid } from '~/components/flow/FlowGrid';

View file

@ -1,4 +1,4 @@
import React, { FC, useMemo } from 'react'; import React, { FC } from 'react';
import { Group } from '~/components/containers/Group'; import { Group } from '~/components/containers/Group';
import { Sticky } from '~/components/containers/Sticky'; import { Sticky } from '~/components/containers/Sticky';

View file

@ -31,7 +31,10 @@ const NodeLayout = observer(() => {
useNodeCoverImage(node); useNodeCoverImage(node);
const onUnauthorizedLike = useCallback(() => showRegisterDialog({}), []); const onUnauthorizedLike = useCallback(
() => showRegisterDialog({}),
[showRegisterDialog],
);
return ( return (
<div className={styles.wrap}> <div className={styles.wrap}>

View file

@ -1,5 +1,6 @@
import React, { FC } from 'react'; import React, { FC } from 'react';
import { Anchor } from '~/components/common/Anchor';
import { Button } from '~/components/input/Button'; import { Button } from '~/components/input/Button';
import { Role } from '~/constants/auth'; import { Role } from '~/constants/auth';
import { Dialog } from '~/constants/modal'; import { Dialog } from '~/constants/modal';
@ -26,7 +27,7 @@ const RoomLayout: FC<RoomLayoutProps> = () => {
<p> <p>
Пока ещё концепт, над которым я размышляю, ты видишь его, потому Пока ещё концепт, над которым я размышляю, ты видишь его, потому
что включил суперсилы в <a href="/boris">Борисе</a>. что включил суперсилы в <Anchor href="/boris">Борисе</Anchor>.
</p> </p>
<p> <p>

View file

@ -91,7 +91,7 @@ export const getStaticProps = async (
revalidate: 7 * 86400, // every week revalidate: 7 * 86400, // every week
}; };
} catch (error) { } catch (error) {
console.warn(`[NEXT] can't generate node: `, error); console.warn('[NEXT] can\'t generate node: ', error);
return { return {
notFound: true, notFound: true,
}; };

View file

@ -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; export type SidebarComponent = keyof SidebarComponents;

View file

@ -10,7 +10,6 @@ import {
COMMENT_BLOCK_TYPES, COMMENT_BLOCK_TYPES,
ICommentBlock, ICommentBlock,
} from '~/constants/comment'; } from '~/constants/comment';
import { imagePresets } from '~/constants/urls';
import { IFile, ValueOf } from '~/types'; import { IFile, ValueOf } from '~/types';
import { CONFIG } from '~/utils/config'; import { CONFIG } from '~/utils/config';
import { import {

View file

@ -1,6 +1,5 @@
import { marked } from 'marked'; import { marked } from 'marked';
import { EventMessageType } from '~/constants/events';
import { stripHTMLTags } from '~/utils/stripHTMLTags'; import { stripHTMLTags } from '~/utils/stripHTMLTags';
/** /**
@ -22,7 +21,7 @@ export const formatTextSanitizeTags = (text: string): string =>
* Returns clickable usernames * Returns clickable usernames
*/ */
export const formatTextClickableUsernames = (text: string): string => 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 * Makes gray comments

View file

@ -1,7 +1,6 @@
import { createContext, FC, useContext } from 'react'; import { createContext, FC, useContext } from 'react';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import { boolean } from 'yup';
import { EMPTY_USER } from '~/constants/auth'; import { EMPTY_USER } from '~/constants/auth';
import { useAuth } from '~/hooks/auth/useAuth'; import { useAuth } from '~/hooks/auth/useAuth';

View file

@ -44,10 +44,13 @@ const ThemeProvider: FC<ProvidersProps> = ({ children }) => {
useEffect(() => { useEffect(() => {
const stored = localStorage.getItem('vault__theme'); const stored = localStorage.getItem('vault__theme');
if (!stored || !keys(themeClass).includes(stored as Theme)) { if (!stored || !keys(themeClass).includes(stored as Theme)) {
return; return;
} }
setTheme(stored as Theme); setTheme(stored as Theme);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
const value = useMemo(() => ({ theme, setTheme }), [theme, setTheme]); const value = useMemo(() => ({ theme, setTheme }), [theme, setTheme]);

View file

@ -1136,10 +1136,10 @@ eslint-plugin-import@^2.25.4:
resolve "^1.20.0" resolve "^1.20.0"
tsconfig-paths "^3.12.0" tsconfig-paths "^3.12.0"
eslint-plugin-react-hooks@^4.3.0: eslint-plugin-react-hooks@^4.6.0:
version "4.3.0" version "4.6.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz#318dbf312e06fab1c835a4abef00121751ac1172" resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3"
integrity sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA== integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==
eslint-plugin-react@^7.28.0: eslint-plugin-react@^7.28.0:
version "7.28.0" version "7.28.0"
@ -1161,6 +1161,18 @@ eslint-plugin-react@^7.28.0:
semver "^6.3.0" semver "^6.3.0"
string.prototype.matchall "^4.0.6" 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: eslint-scope@^5.1.1:
version "5.1.1" version "5.1.1"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"