1
0
Fork 0
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:
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 = {
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: {

View file

@ -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",

View file

@ -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

View file

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

View file

@ -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) {

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 { 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}

View file

@ -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';

View file

@ -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';

View file

@ -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) {

View file

@ -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) {

View file

@ -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';

View file

@ -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 };

View file

@ -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';

View file

@ -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(

View file

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

View file

@ -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';

View file

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

View file

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

View file

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

View file

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

View file

@ -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}>

View file

@ -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';

View file

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

View file

@ -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';

View file

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

View file

@ -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';

View file

@ -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) {

View file

@ -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;

View file

@ -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 (

View file

@ -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';

View file

@ -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';

View file

@ -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';

View file

@ -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) => {

View file

@ -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',
},

View file

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

View file

@ -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)',
},
};

View file

@ -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';

View file

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

View file

@ -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';

View file

@ -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

View file

@ -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';

View file

@ -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';

View file

@ -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" />;

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 { 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';

View file

@ -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';

View file

@ -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';

View file

@ -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}>

View file

@ -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 {}

View file

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

View file

@ -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';

View file

@ -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) {

View file

@ -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';

View file

@ -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,
};
};

View file

@ -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 };

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

View file

@ -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'),

View file

@ -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 'Юнит деактивирован';
}

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 { 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();

View file

@ -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;
}

View file

@ -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]);
};

View file

@ -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;
};

View file

@ -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]);
};

View file

@ -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;

View file

@ -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],
);
};

View file

@ -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 = () => {

View file

@ -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 };

View file

@ -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]);
};

View file

@ -1,5 +1,3 @@
import { FC } from 'react';
import classNames from 'classnames';
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 { Sticky } from '~/components/containers/Sticky';

View file

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

View file

@ -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>

View file

@ -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,
};

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;

View file

@ -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 {

View file

@ -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

View file

@ -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';

View file

@ -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]);

View file

@ -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"