mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-24 20:36:40 +07:00
add eslint-plugin-prettier
This commit is contained in:
parent
0e4d2bf44d
commit
ba0604ab9d
69 changed files with 419 additions and 249 deletions
20
.eslintrc.js
20
.eslintrc.js
|
@ -1,6 +1,7 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
extends: ['plugin:react/recommended', 'plugin:@next/next/recommended'],
|
extends: ['plugin:react/recommended', 'plugin:@next/next/recommended'],
|
||||||
rules: {
|
rules: {
|
||||||
|
'prettier/prettier': 'error',
|
||||||
'react-hooks/rules-of-hooks': 'error', // Checks rules of Hooks
|
'react-hooks/rules-of-hooks': 'error', // Checks rules of Hooks
|
||||||
'react-hooks/exhaustive-deps': 'warn', // Checks effect dependencies
|
'react-hooks/exhaustive-deps': 'warn', // Checks effect dependencies
|
||||||
'react/prop-types': 0,
|
'react/prop-types': 0,
|
||||||
|
@ -9,13 +10,21 @@ module.exports = {
|
||||||
'@next/next/no-img-element': 0,
|
'@next/next/no-img-element': 0,
|
||||||
'unused-imports/no-unused-imports': 'warn',
|
'unused-imports/no-unused-imports': 'warn',
|
||||||
// 'no-unused-vars': 'warn',
|
// 'no-unused-vars': 'warn',
|
||||||
'quotes': [2, 'single', { 'avoidEscape': true }],
|
quotes: [2, 'single', { avoidEscape: true }],
|
||||||
'import/order': [
|
'import/order': [
|
||||||
'error',
|
'error',
|
||||||
{
|
{
|
||||||
alphabetize: { order: 'asc' },
|
alphabetize: { order: 'asc' },
|
||||||
'newlines-between': 'always',
|
'newlines-between': 'always',
|
||||||
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'unknown'],
|
groups: [
|
||||||
|
'builtin',
|
||||||
|
'external',
|
||||||
|
'internal',
|
||||||
|
'parent',
|
||||||
|
'sibling',
|
||||||
|
'index',
|
||||||
|
'unknown',
|
||||||
|
],
|
||||||
pathGroups: [
|
pathGroups: [
|
||||||
{
|
{
|
||||||
pattern: 'react',
|
pattern: 'react',
|
||||||
|
@ -34,18 +43,17 @@ module.exports = {
|
||||||
paths: [
|
paths: [
|
||||||
{
|
{
|
||||||
name: 'ramda',
|
name: 'ramda',
|
||||||
message:
|
message: "import from '~/utils/ramda' instead",
|
||||||
'import from \'~/utils/ramda\' instead',
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
ecmaVersion: 7,
|
ecmaVersion: 7,
|
||||||
sourceType: 'module',
|
sourceType: 'module',
|
||||||
},
|
},
|
||||||
plugins: ['import', 'react-hooks', 'unused-imports'],
|
plugins: ['import', 'react-hooks', 'unused-imports', 'prettier'],
|
||||||
parser: '@typescript-eslint/parser',
|
parser: '@typescript-eslint/parser',
|
||||||
settings: {
|
settings: {
|
||||||
react: {
|
react: {
|
||||||
|
|
|
@ -92,13 +92,14 @@
|
||||||
"@typescript-eslint/parser": "^5.10.1",
|
"@typescript-eslint/parser": "^5.10.1",
|
||||||
"eslint": "^7.32.0",
|
"eslint": "^7.32.0",
|
||||||
"eslint-plugin-import": "^2.25.4",
|
"eslint-plugin-import": "^2.25.4",
|
||||||
|
"eslint-plugin-prettier": "^5.2.3",
|
||||||
"eslint-plugin-react": "^7.28.0",
|
"eslint-plugin-react": "^7.28.0",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
"eslint-plugin-unused-imports": "^3.0.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",
|
||||||
"prettier": "^2.7.1"
|
"prettier": "^3.0.0"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"./**/*.{js,jsx,ts,tsx}": [
|
"./**/*.{js,jsx,ts,tsx}": [
|
||||||
|
|
|
@ -14,7 +14,7 @@ interface Props extends DivProps {
|
||||||
username?: string;
|
username?: string;
|
||||||
size?: number;
|
size?: number;
|
||||||
hasUpdates?: boolean;
|
hasUpdates?: boolean;
|
||||||
preset?: typeof imagePresets[keyof typeof imagePresets];
|
preset?: (typeof imagePresets)[keyof typeof imagePresets];
|
||||||
}
|
}
|
||||||
|
|
||||||
const Avatar = forwardRef<HTMLDivElement, Props>(
|
const Avatar = forwardRef<HTMLDivElement, Props>(
|
||||||
|
|
|
@ -20,7 +20,7 @@ export const COMMENT_BLOCK_DETECTORS = [
|
||||||
];
|
];
|
||||||
|
|
||||||
export type ICommentBlock = {
|
export type ICommentBlock = {
|
||||||
type: typeof COMMENT_BLOCK_TYPES[keyof typeof COMMENT_BLOCK_TYPES];
|
type: (typeof COMMENT_BLOCK_TYPES)[keyof typeof COMMENT_BLOCK_TYPES];
|
||||||
content: string;
|
content: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
export enum SidebarName {
|
export enum SidebarName {
|
||||||
Settings = 'settings',
|
Settings = 'settings',
|
||||||
Tag = 'tag',
|
Tag = 'tag',
|
||||||
|
|
|
@ -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: 'Веспера',
|
||||||
|
|
|
@ -37,7 +37,7 @@ export const imagePresets = {
|
||||||
flow_horizontal: 'flow_horizontal',
|
flow_horizontal: 'flow_horizontal',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export type ImagePreset = typeof imagePresets[keyof typeof imagePresets];
|
export type ImagePreset = (typeof imagePresets)[keyof typeof imagePresets];
|
||||||
|
|
||||||
export const imageSrcSets: Partial<Record<ImagePreset, number>> = {
|
export const imageSrcSets: Partial<Record<ImagePreset, number>> = {
|
||||||
[imagePresets[1600]]: 1600,
|
[imagePresets[1600]]: 1600,
|
||||||
|
@ -49,7 +49,7 @@ export const imageSrcSets: Partial<Record<ImagePreset, number>> = {
|
||||||
|
|
||||||
export const flowDisplayToPreset: Record<
|
export const flowDisplayToPreset: Record<
|
||||||
FlowDisplayVariant,
|
FlowDisplayVariant,
|
||||||
typeof imagePresets[keyof typeof imagePresets]
|
(typeof imagePresets)[keyof typeof imagePresets]
|
||||||
> = {
|
> = {
|
||||||
single: 'flow_square',
|
single: 'flow_square',
|
||||||
quadro: 'flow_square',
|
quadro: 'flow_square',
|
||||||
|
|
|
@ -3,10 +3,12 @@ import dynamic from 'next/dynamic';
|
||||||
import type { BorisSuperpowersProps } from './index';
|
import type { BorisSuperpowersProps } from './index';
|
||||||
|
|
||||||
export const BorisSuperPowersSSR = dynamic<BorisSuperpowersProps>(
|
export const BorisSuperPowersSSR = dynamic<BorisSuperpowersProps>(
|
||||||
() => import('~/containers/boris/BorisSuperpowers/index')
|
() =>
|
||||||
.then(it => it.BorisSuperpowers),
|
import('~/containers/boris/BorisSuperpowers/index').then(
|
||||||
|
(it) => it.BorisSuperpowers,
|
||||||
|
),
|
||||||
{
|
{
|
||||||
ssr: false,
|
ssr: false,
|
||||||
loading: () => <div />,
|
loading: () => <div />,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { DialogComponentProps } from '~/types/modal';
|
||||||
import { values } from '~/utils/ramda';
|
import { values } from '~/utils/ramda';
|
||||||
|
|
||||||
export interface EditorCreateDialogProps extends DialogComponentProps {
|
export interface EditorCreateDialogProps extends DialogComponentProps {
|
||||||
type: typeof NODE_TYPES[keyof typeof NODE_TYPES];
|
type: (typeof NODE_TYPES)[keyof typeof NODE_TYPES];
|
||||||
isInLab: boolean;
|
isInLab: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { TextEditor } from '../components/TextEditor';
|
||||||
import { VideoEditor } from '../components/VideoEditor';
|
import { VideoEditor } from '../components/VideoEditor';
|
||||||
|
|
||||||
export const NODE_EDITORS: Record<
|
export const NODE_EDITORS: Record<
|
||||||
typeof NODE_TYPES[keyof typeof NODE_TYPES],
|
(typeof NODE_TYPES)[keyof typeof NODE_TYPES],
|
||||||
FC<NodeEditorProps>
|
FC<NodeEditorProps>
|
||||||
> = {
|
> = {
|
||||||
[NODE_TYPES.IMAGE]: ImageEditor,
|
[NODE_TYPES.IMAGE]: ImageEditor,
|
||||||
|
@ -22,7 +22,7 @@ export const NODE_EDITORS: Record<
|
||||||
};
|
};
|
||||||
|
|
||||||
export const NODE_EDITOR_DATA: Record<
|
export const NODE_EDITOR_DATA: Record<
|
||||||
typeof NODE_TYPES[keyof typeof NODE_TYPES],
|
(typeof NODE_TYPES)[keyof typeof NODE_TYPES],
|
||||||
Partial<INode>
|
Partial<INode>
|
||||||
> = {
|
> = {
|
||||||
[NODE_TYPES.TEXT]: {
|
[NODE_TYPES.TEXT]: {
|
||||||
|
|
|
@ -4,9 +4,12 @@ import type { HeaderProps } from '~/containers/main/Header/index';
|
||||||
|
|
||||||
import styles from './styles.module.scss';
|
import styles from './styles.module.scss';
|
||||||
|
|
||||||
export const HeaderSSR = dynamic<HeaderProps>(() => import('./index').then(it => it.Header), {
|
export const HeaderSSR = dynamic<HeaderProps>(
|
||||||
|
() => import('./index').then((it) => it.Header),
|
||||||
|
{
|
||||||
ssr: false,
|
ssr: false,
|
||||||
loading: () => <div className={styles.wrap} />,
|
loading: () => <div className={styles.wrap} />,
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export const HeaderSSRPlaceholder = () => <div className={styles.wrap} />;
|
export const HeaderSSRPlaceholder = () => <div className={styles.wrap} />;
|
||||||
|
|
|
@ -3,5 +3,6 @@ import dynamic from 'next/dynamic';
|
||||||
import type { SubmitBarProps } from './index';
|
import type { SubmitBarProps } from './index';
|
||||||
|
|
||||||
export const SubmitBarSSR = dynamic<SubmitBarProps>(
|
export const SubmitBarSSR = dynamic<SubmitBarProps>(
|
||||||
() => import('./index').then(it => it.SubmitBar),
|
() => import('./index').then((it) => it.SubmitBar),
|
||||||
{ ssr: false });
|
{ ssr: false },
|
||||||
|
);
|
||||||
|
|
|
@ -14,7 +14,7 @@ import type { SidebarComponentProps } from '~/types/sidebar';
|
||||||
import { isNil } from '~/utils/ramda';
|
import { isNil } from '~/utils/ramda';
|
||||||
|
|
||||||
const tabs = ['profile', 'notifications', 'bookmarks'] as const;
|
const tabs = ['profile', 'notifications', 'bookmarks'] as const;
|
||||||
type TabName = typeof tabs[number];
|
type TabName = (typeof tabs)[number];
|
||||||
|
|
||||||
interface SettingsSidebarProps
|
interface SettingsSidebarProps
|
||||||
extends SidebarComponentProps<SidebarName.Settings> {
|
extends SidebarComponentProps<SidebarName.Settings> {
|
||||||
|
|
|
@ -10,7 +10,7 @@ export const useLastSeenBoris = () => {
|
||||||
async (date: string) => {
|
async (date: string) => {
|
||||||
await update({ last_seen_boris: date }, false);
|
await update({ last_seen_boris: date }, false);
|
||||||
},
|
},
|
||||||
[update]
|
[update],
|
||||||
);
|
);
|
||||||
|
|
||||||
return { setLastSeen, lastSeen };
|
return { setLastSeen, lastSeen };
|
||||||
|
|
|
@ -20,7 +20,7 @@ export const useLoginLogoutRestore = () => {
|
||||||
showToastInfo(getRandomPhrase('WELCOME'));
|
showToastInfo(getRandomPhrase('WELCOME'));
|
||||||
return result.user;
|
return result.user;
|
||||||
},
|
},
|
||||||
[auth]
|
[auth],
|
||||||
);
|
);
|
||||||
|
|
||||||
return { logout, login };
|
return { logout, login };
|
||||||
|
|
|
@ -5,8 +5,9 @@ import { API } from '~/constants/api';
|
||||||
import { getErrorMessage } from '~/utils/errors/getErrorMessage';
|
import { getErrorMessage } from '~/utils/errors/getErrorMessage';
|
||||||
|
|
||||||
export const useRestoreCode = (code: string) => {
|
export const useRestoreCode = (code: string) => {
|
||||||
const { data, isValidating, error } = useSWR(API.USER.REQUEST_CODE(code), () =>
|
const { data, isValidating, error } = useSWR(
|
||||||
apiCheckRestoreCode({ code })
|
API.USER.REQUEST_CODE(code),
|
||||||
|
() => apiCheckRestoreCode({ code }),
|
||||||
);
|
);
|
||||||
|
|
||||||
const codeUser = data?.user;
|
const codeUser = data?.user;
|
||||||
|
|
|
@ -18,7 +18,7 @@ const validationSchema = object({
|
||||||
.test(
|
.test(
|
||||||
'sameAsPassword',
|
'sameAsPassword',
|
||||||
'Должен совпадать с паролем',
|
'Должен совпадать с паролем',
|
||||||
(val, ctx) => val === ctx.parent.newPassword
|
(val, ctx) => val === ctx.parent.newPassword,
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -26,15 +26,21 @@ export type RestorePasswordData = Asserts<typeof validationSchema>;
|
||||||
|
|
||||||
export const useRestorePasswordForm = (
|
export const useRestorePasswordForm = (
|
||||||
code: string,
|
code: string,
|
||||||
fetcher: (props: { code: string; password: string }) => Promise<{ token: string; user: IUser }>,
|
fetcher: (props: {
|
||||||
onSuccess: () => void
|
code: string;
|
||||||
|
password: string;
|
||||||
|
}) => Promise<{ token: string; user: IUser }>,
|
||||||
|
onSuccess: () => void,
|
||||||
) => {
|
) => {
|
||||||
const auth = useAuthStore();
|
const auth = useAuthStore();
|
||||||
|
|
||||||
const onSubmit = useCallback<FormikConfig<RestorePasswordData>['onSubmit']>(
|
const onSubmit = useCallback<FormikConfig<RestorePasswordData>['onSubmit']>(
|
||||||
async (values, { setErrors }) => {
|
async (values, { setErrors }) => {
|
||||||
try {
|
try {
|
||||||
const { token, user } = await fetcher({ password: values.newPassword, code });
|
const { token, user } = await fetcher({
|
||||||
|
password: values.newPassword,
|
||||||
|
code,
|
||||||
|
});
|
||||||
auth.setUser(user);
|
auth.setUser(user);
|
||||||
auth.setToken(token);
|
auth.setToken(token);
|
||||||
onSuccess();
|
onSuccess();
|
||||||
|
@ -47,7 +53,7 @@ export const useRestorePasswordForm = (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[onSuccess, fetcher, code, auth]
|
[onSuccess, fetcher, code, auth],
|
||||||
);
|
);
|
||||||
|
|
||||||
return useFormik<RestorePasswordData>({
|
return useFormik<RestorePasswordData>({
|
||||||
|
|
|
@ -15,7 +15,7 @@ type RestoreRequestData = Asserts<typeof validationSchema>;
|
||||||
|
|
||||||
export const useRestoreRequestForm = (
|
export const useRestoreRequestForm = (
|
||||||
fetcher: (field: string) => Promise<unknown>,
|
fetcher: (field: string) => Promise<unknown>,
|
||||||
onSuccess: () => void
|
onSuccess: () => void,
|
||||||
) => {
|
) => {
|
||||||
const onSubmit = useCallback<FormikConfig<RestoreRequestData>['onSubmit']>(
|
const onSubmit = useCallback<FormikConfig<RestoreRequestData>['onSubmit']>(
|
||||||
async (values, { setErrors }) => {
|
async (values, { setErrors }) => {
|
||||||
|
@ -31,7 +31,7 @@ export const useRestoreRequestForm = (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[fetcher, onSuccess]
|
[fetcher, onSuccess],
|
||||||
);
|
);
|
||||||
|
|
||||||
return useFormik({
|
return useFormik({
|
||||||
|
|
|
@ -13,6 +13,6 @@ export const useSessionCookie = () => {
|
||||||
autorun(() => {
|
autorun(() => {
|
||||||
setCookie('session', auth.token, 30);
|
setCookie('session', auth.token, 30);
|
||||||
}),
|
}),
|
||||||
[auth]
|
[auth],
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,9 +9,7 @@ import { showErrorToast } from '~/utils/errors/showToast';
|
||||||
|
|
||||||
const validationSchema = object({
|
const validationSchema = object({
|
||||||
username: string().required(ERRORS.REQUIRED),
|
username: string().required(ERRORS.REQUIRED),
|
||||||
password: string()
|
password: string().required(ERRORS.REQUIRED).min(6, ERRORS.PASSWORD_IS_SHORT),
|
||||||
.required(ERRORS.REQUIRED)
|
|
||||||
.min(6, ERRORS.PASSWORD_IS_SHORT),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
type SocialRegisterData = Asserts<typeof validationSchema>;
|
type SocialRegisterData = Asserts<typeof validationSchema>;
|
||||||
|
@ -23,7 +21,7 @@ export const useSocialRegisterForm = (
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
}) => Promise<{ token: string }>,
|
}) => Promise<{ token: string }>,
|
||||||
onSuccess: (token: string) => void
|
onSuccess: (token: string) => void,
|
||||||
) => {
|
) => {
|
||||||
const onSubmit = useCallback<FormikConfig<SocialRegisterData>['onSubmit']>(
|
const onSubmit = useCallback<FormikConfig<SocialRegisterData>['onSubmit']>(
|
||||||
async (values, { setErrors }) => {
|
async (values, { setErrors }) => {
|
||||||
|
@ -43,7 +41,7 @@ export const useSocialRegisterForm = (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[token, onSuccess, fetcher]
|
[token, onSuccess, fetcher],
|
||||||
);
|
);
|
||||||
|
|
||||||
return useFormik<SocialRegisterData>({
|
return useFormik<SocialRegisterData>({
|
||||||
|
|
|
@ -7,7 +7,10 @@ const today = new Date();
|
||||||
export const useUserActiveStatus = (lastSeen?: string) => {
|
export const useUserActiveStatus = (lastSeen?: string) => {
|
||||||
try {
|
try {
|
||||||
const lastSeenDate = lastSeen ? parseISO(lastSeen) : undefined;
|
const lastSeenDate = lastSeen ? parseISO(lastSeen) : undefined;
|
||||||
return lastSeenDate && differenceInDays(today, lastSeenDate) < INACTIVE_ACCOUNT_DAYS;
|
return (
|
||||||
|
lastSeenDate &&
|
||||||
|
differenceInDays(today, lastSeenDate) < INACTIVE_ACCOUNT_DAYS
|
||||||
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,14 @@ import { initialBackendStats } from '~/constants/boris/constants';
|
||||||
import { BorisUsageStats } from '~/types/boris';
|
import { BorisUsageStats } from '~/types/boris';
|
||||||
|
|
||||||
export const useBorisStats = () => {
|
export const useBorisStats = () => {
|
||||||
const { data: backend = initialBackendStats, isValidating: isValidatingBackend } = useSWR(
|
const {
|
||||||
API.BORIS.GET_BACKEND_STATS,
|
data: backend = initialBackendStats,
|
||||||
() => getBorisBackendStats()
|
isValidating: isValidatingBackend,
|
||||||
);
|
} = useSWR(API.BORIS.GET_BACKEND_STATS, () => getBorisBackendStats());
|
||||||
|
|
||||||
const { data: issues = [] } = useSWR(API.BORIS.GITHUB_ISSUES, () => getGithubIssues());
|
const { data: issues = [] } = useSWR(API.BORIS.GITHUB_ISSUES, () =>
|
||||||
|
getGithubIssues(),
|
||||||
|
);
|
||||||
|
|
||||||
const stats: BorisUsageStats = {
|
const stats: BorisUsageStats = {
|
||||||
backend,
|
backend,
|
||||||
|
|
|
@ -3,9 +3,16 @@ import { useMemo } from 'react';
|
||||||
import { normalizeBrightColor } from '~/utils/color';
|
import { normalizeBrightColor } from '~/utils/color';
|
||||||
import { stringToColour } from '~/utils/dom';
|
import { stringToColour } from '~/utils/dom';
|
||||||
|
|
||||||
export const useColorFromString = (val?: string, saturation = 3, lightness = 3) => {
|
export const useColorFromString = (
|
||||||
|
val?: string,
|
||||||
|
saturation = 3,
|
||||||
|
lightness = 3,
|
||||||
|
) => {
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() => (val && normalizeBrightColor(stringToColour(val), saturation, lightness)) || '',
|
() =>
|
||||||
[lightness, saturation, val]
|
(val &&
|
||||||
|
normalizeBrightColor(stringToColour(val), saturation, lightness)) ||
|
||||||
|
'',
|
||||||
|
[lightness, saturation, val],
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,7 +6,7 @@ export const useColorGradientFromString = (
|
||||||
val?: string,
|
val?: string,
|
||||||
saturation = 3,
|
saturation = 3,
|
||||||
lightness = 3,
|
lightness = 3,
|
||||||
angle = 155
|
angle = 155,
|
||||||
) =>
|
) =>
|
||||||
useMemo(() => {
|
useMemo(() => {
|
||||||
if (!val) {
|
if (!val) {
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
export const usePersistedState = (key: string, initial: string): [string, (val: string) => any] => {
|
export const usePersistedState = (
|
||||||
|
key: string,
|
||||||
|
initial: string,
|
||||||
|
): [string, (val: string) => any] => {
|
||||||
const stored = useMemo(() => {
|
const stored = useMemo(() => {
|
||||||
try {
|
try {
|
||||||
return localStorage.getItem(`vault_${key}`) || initial;
|
return localStorage.getItem(`vault_${key}`) || initial;
|
||||||
|
|
|
@ -4,15 +4,18 @@ export const useFocusEvent = (initialState = false, delay = 0) => {
|
||||||
const [focused, setFocused] = useState(initialState);
|
const [focused, setFocused] = useState(initialState);
|
||||||
|
|
||||||
const onFocus = useCallback(
|
const onFocus = useCallback(
|
||||||
event => {
|
(event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
setFocused(true);
|
setFocused(true);
|
||||||
},
|
},
|
||||||
[setFocused]
|
[setFocused],
|
||||||
|
);
|
||||||
|
const onBlur = useCallback(
|
||||||
|
() => setTimeout(() => setFocused(false), delay),
|
||||||
|
[delay],
|
||||||
);
|
);
|
||||||
const onBlur = useCallback(() => setTimeout(() => setFocused(false), delay), [delay]);
|
|
||||||
|
|
||||||
return { focused, onBlur, onFocus };
|
return { focused, onBlur, onFocus };
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,12 +7,13 @@ export const useFormatWrapper = (onChange: (val: string) => void) => {
|
||||||
target: HTMLTextAreaElement,
|
target: HTMLTextAreaElement,
|
||||||
|
|
||||||
prefix = '',
|
prefix = '',
|
||||||
suffix = ''
|
suffix = '',
|
||||||
) => event => {
|
) =>
|
||||||
|
(event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
wrapTextInsideInput(target, prefix, suffix, onChange);
|
wrapTextInsideInput(target, prefix, suffix, onChange);
|
||||||
},
|
},
|
||||||
[onChange]
|
[onChange],
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -21,7 +22,7 @@ export const wrapTextInsideInput = (
|
||||||
target: HTMLTextAreaElement,
|
target: HTMLTextAreaElement,
|
||||||
prefix: string,
|
prefix: string,
|
||||||
suffix: string,
|
suffix: string,
|
||||||
onChange: (val: string) => void
|
onChange: (val: string) => void,
|
||||||
) => {
|
) => {
|
||||||
if (!target) return;
|
if (!target) return;
|
||||||
|
|
||||||
|
@ -34,7 +35,7 @@ export const wrapTextInsideInput = (
|
||||||
onChange(
|
onChange(
|
||||||
target.value.substring(0, start) +
|
target.value.substring(0, start) +
|
||||||
replacement +
|
replacement +
|
||||||
target.value.substring(end, target.value.length)
|
target.value.substring(end, target.value.length),
|
||||||
);
|
);
|
||||||
|
|
||||||
target.focus();
|
target.focus();
|
||||||
|
|
|
@ -2,7 +2,8 @@ import { useCallback, useEffect } from 'react';
|
||||||
|
|
||||||
export const useInfiniteLoader = (loader: () => void, isLoading?: boolean) => {
|
export const useInfiniteLoader = (loader: () => void, isLoading?: boolean) => {
|
||||||
const onLoadMore = useCallback(() => {
|
const onLoadMore = useCallback(() => {
|
||||||
const pos = window.scrollY + window.innerHeight - document.body.scrollHeight;
|
const pos =
|
||||||
|
window.scrollY + window.innerHeight - document.body.scrollHeight;
|
||||||
|
|
||||||
if (isLoading || pos < -600) return;
|
if (isLoading || pos < -600) return;
|
||||||
|
|
||||||
|
|
|
@ -5,13 +5,13 @@ import { getImageFromPaste } from '~/utils/uploader';
|
||||||
// useInputPasteUpload attaches event listener to input, that calls onUpload if user pasted any image
|
// useInputPasteUpload attaches event listener to input, that calls onUpload if user pasted any image
|
||||||
export const useInputPasteUpload = (onUpload: (files: File[]) => void) => {
|
export const useInputPasteUpload = (onUpload: (files: File[]) => void) => {
|
||||||
return useCallback(
|
return useCallback(
|
||||||
async event => {
|
async (event) => {
|
||||||
const image = await getImageFromPaste(event);
|
const image = await getImageFromPaste(event);
|
||||||
|
|
||||||
if (!image) return;
|
if (!image) return;
|
||||||
|
|
||||||
onUpload([image]);
|
onUpload([image]);
|
||||||
},
|
},
|
||||||
[onUpload]
|
[onUpload],
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,7 +17,11 @@ const sameWidth = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const usePopperModifiers = (offsetX = 0, offsetY = 10, justify?: boolean): Modifier<any>[] =>
|
export const usePopperModifiers = (
|
||||||
|
offsetX = 0,
|
||||||
|
offsetY = 10,
|
||||||
|
justify?: boolean,
|
||||||
|
): Modifier<any>[] =>
|
||||||
useMemo(
|
useMemo(
|
||||||
() =>
|
() =>
|
||||||
[
|
[
|
||||||
|
@ -35,5 +39,5 @@ export const usePopperModifiers = (offsetX = 0, offsetY = 10, justify?: boolean)
|
||||||
},
|
},
|
||||||
...(justify ? [sameWidth] : []),
|
...(justify ? [sameWidth] : []),
|
||||||
] as Modifier<any>[],
|
] as Modifier<any>[],
|
||||||
[offsetX, offsetY, justify]
|
[offsetX, offsetY, justify],
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,7 +11,7 @@ const getHeight = () => {
|
||||||
body.offsetHeight,
|
body.offsetHeight,
|
||||||
html.clientHeight,
|
html.clientHeight,
|
||||||
html.scrollHeight,
|
html.scrollHeight,
|
||||||
html.offsetHeight
|
html.offsetHeight,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export const useScrollHeight = () => getHeight();
|
export const useScrollHeight = () => getHeight();
|
||||||
|
|
|
@ -18,6 +18,6 @@ export const useScrollToTop = (deps?: any[]) => {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
deps && Array.isArray(deps) ? deps : []
|
deps && Array.isArray(deps) ? deps : [],
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
export const useScrollTop = () => {
|
export const useScrollTop = () => {
|
||||||
const [top, setTop] = useState(typeof window !== 'undefined' ? window.scrollY : 0);
|
const [top, setTop] = useState(
|
||||||
|
typeof window !== 'undefined' ? window.scrollY : 0,
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTop(window.scrollY);
|
setTop(window.scrollY);
|
||||||
|
|
|
@ -6,11 +6,12 @@ export const useFlowCellControls = (
|
||||||
id: INode['id'],
|
id: INode['id'],
|
||||||
description: string | undefined,
|
description: string | undefined,
|
||||||
flow: FlowDisplay,
|
flow: FlowDisplay,
|
||||||
onChangeCellView: (id: INode['id'], flow: FlowDisplay) => void
|
onChangeCellView: (id: INode['id'], flow: FlowDisplay) => void,
|
||||||
) => {
|
) => {
|
||||||
const onChange = useCallback(
|
const onChange = useCallback(
|
||||||
(value: Partial<FlowDisplay>) => onChangeCellView(id, { ...flow, ...value }),
|
(value: Partial<FlowDisplay>) =>
|
||||||
[flow, id, onChangeCellView]
|
onChangeCellView(id, { ...flow, ...value }),
|
||||||
|
[flow, id, onChangeCellView],
|
||||||
);
|
);
|
||||||
|
|
||||||
const hasDescription = !!description && description.length > 32;
|
const hasDescription = !!description && description.length > 32;
|
||||||
|
|
|
@ -17,6 +17,6 @@ export const useFlowSetCellView = () => {
|
||||||
showErrorToast(error);
|
showErrorToast(error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[updateNode]
|
[updateNode],
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,15 +21,19 @@ export const useGetLabStats = () => {
|
||||||
heroes: lab.heroes,
|
heroes: lab.heroes,
|
||||||
tags: lab.tags,
|
tags: lab.tags,
|
||||||
},
|
},
|
||||||
onSuccess: data => {
|
onSuccess: (data) => {
|
||||||
lab.setHeroes(data.heroes);
|
lab.setHeroes(data.heroes);
|
||||||
lab.setTags(data.tags);
|
lab.setTags(data.tags);
|
||||||
},
|
},
|
||||||
refreshInterval,
|
refreshInterval,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const { data: updatesData, isValidating: isValidatingUpdates, mutate: mutateUpdates } = useSWR(
|
const {
|
||||||
|
data: updatesData,
|
||||||
|
isValidating: isValidatingUpdates,
|
||||||
|
mutate: mutateUpdates,
|
||||||
|
} = useSWR(
|
||||||
isUser ? API.LAB.UPDATES : null,
|
isUser ? API.LAB.UPDATES : null,
|
||||||
async () => {
|
async () => {
|
||||||
const result = await getLabUpdates();
|
const result = await getLabUpdates();
|
||||||
|
@ -37,26 +41,27 @@ export const useGetLabStats = () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fallbackData: lab.updates,
|
fallbackData: lab.updates,
|
||||||
onSuccess: data => {
|
onSuccess: (data) => {
|
||||||
lab.setUpdates(data);
|
lab.setUpdates(data);
|
||||||
},
|
},
|
||||||
refreshInterval,
|
refreshInterval,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const heroes = useMemo(() => stats?.heroes || [], [stats]);
|
const heroes = useMemo(() => stats?.heroes || [], [stats]);
|
||||||
const tags = useMemo(() => stats?.tags || [], [stats]);
|
const tags = useMemo(() => stats?.tags || [], [stats]);
|
||||||
const updates = useMemo(() => updatesData || [], [updatesData]);
|
const updates = useMemo(() => updatesData || [], [updatesData]);
|
||||||
|
|
||||||
const isLoading = (!stats || !updates) && (isValidatingStats || isValidatingUpdates);
|
const isLoading =
|
||||||
|
(!stats || !updates) && (isValidatingStats || isValidatingUpdates);
|
||||||
const seenNode = useCallback(
|
const seenNode = useCallback(
|
||||||
async (nodeId: number) => {
|
async (nodeId: number) => {
|
||||||
await mutateUpdates(
|
await mutateUpdates(
|
||||||
updates.filter(it => it.id !== nodeId),
|
updates.filter((it) => it.id !== nodeId),
|
||||||
false
|
false,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[mutateUpdates, updates]
|
[mutateUpdates, updates],
|
||||||
);
|
);
|
||||||
|
|
||||||
return { heroes, tags, updates, isLoading, seenNode };
|
return { heroes, tags, updates, isLoading, seenNode };
|
||||||
|
|
|
@ -11,7 +11,7 @@ const getKey = (username: string): string | null => {
|
||||||
};
|
};
|
||||||
export const useMessages = (username: string) => {
|
export const useMessages = (username: string) => {
|
||||||
const { data, isValidating } = useSWR(getKey(username), async () =>
|
const { data, isValidating } = useSWR(getKey(username), async () =>
|
||||||
apiGetUserMessages({ username })
|
apiGetUserMessages({ username }),
|
||||||
);
|
);
|
||||||
|
|
||||||
const messages: IMessage[] = useMemo(() => data?.messages || [], [data]);
|
const messages: IMessage[] = useMemo(() => data?.messages || [], [data]);
|
||||||
|
|
|
@ -5,7 +5,9 @@ import { useModalStore } from '~/store/modal/useModalStore';
|
||||||
import { DialogComponentProps } from '~/types/modal';
|
import { DialogComponentProps } from '~/types/modal';
|
||||||
|
|
||||||
export type DialogContentProps = {
|
export type DialogContentProps = {
|
||||||
[K in keyof typeof DIALOG_CONTENT]: typeof DIALOG_CONTENT[K] extends (props: infer U) => any
|
[K in keyof typeof DIALOG_CONTENT]: (typeof DIALOG_CONTENT)[K] extends (
|
||||||
|
props: infer U,
|
||||||
|
) => any
|
||||||
? U extends DialogComponentProps
|
? U extends DialogComponentProps
|
||||||
? keyof Omit<U, 'onRequestClose' | 'children'> extends never
|
? keyof Omit<U, 'onRequestClose' | 'children'> extends never
|
||||||
? {}
|
? {}
|
||||||
|
@ -21,7 +23,7 @@ export const useModal = () => {
|
||||||
<T extends Dialog>(dialog: T, props: DialogContentProps[T]) => {
|
<T extends Dialog>(dialog: T, props: DialogContentProps[T]) => {
|
||||||
setCurrent(dialog, props);
|
setCurrent(dialog, props);
|
||||||
},
|
},
|
||||||
[setCurrent]
|
[setCurrent],
|
||||||
);
|
);
|
||||||
|
|
||||||
return { showModal, hideModal: hide, current, isOpened: !!current };
|
return { showModal, hideModal: hide, current, isOpened: !!current };
|
||||||
|
|
|
@ -10,6 +10,6 @@ export const useShowModal = <T extends Dialog>(dialog: T) => {
|
||||||
(props: DialogContentProps[T]) => {
|
(props: DialogContentProps[T]) => {
|
||||||
modal.showModal(dialog, props);
|
modal.showModal(dialog, props);
|
||||||
},
|
},
|
||||||
[dialog, modal]
|
[dialog, modal],
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,6 +11,6 @@ export const useImageModal = () => {
|
||||||
(images: IFile[], index: number) => {
|
(images: IFile[], index: number) => {
|
||||||
showModal({ items: images, index });
|
showModal({ items: images, index });
|
||||||
},
|
},
|
||||||
[showModal]
|
[showModal],
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,7 +17,7 @@ export const useNavigation = () => {
|
||||||
craHistory.push(url);
|
craHistory.push(url);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[craHistory, nextRouter]
|
[craHistory, nextRouter],
|
||||||
);
|
);
|
||||||
|
|
||||||
return { push };
|
return { push };
|
||||||
|
|
|
@ -16,9 +16,13 @@ export const useCreateNode = () => {
|
||||||
if (node.is_promoted) {
|
if (node.is_promoted) {
|
||||||
flow.setNodes([result.node, ...flow.nodes]);
|
flow.setNodes([result.node, ...flow.nodes]);
|
||||||
} else {
|
} else {
|
||||||
await lab.unshift({ node: result.node, comment_count: 0, last_seen: node.created_at });
|
await lab.unshift({
|
||||||
|
node: result.node,
|
||||||
|
comment_count: 0,
|
||||||
|
last_seen: node.created_at,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[flow, lab]
|
[flow, lab],
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,13 +6,13 @@ import { groupCommentsByUser } from '~/utils/fn';
|
||||||
export const useGrouppedComments = (
|
export const useGrouppedComments = (
|
||||||
comments: IComment[],
|
comments: IComment[],
|
||||||
order: 'ASC' | 'DESC',
|
order: 'ASC' | 'DESC',
|
||||||
lastSeen?: string
|
lastSeen?: string,
|
||||||
) =>
|
) =>
|
||||||
useMemo(
|
useMemo(
|
||||||
() =>
|
() =>
|
||||||
(order === 'DESC' ? [...comments].reverse() : comments).reduce(
|
(order === 'DESC' ? [...comments].reverse() : comments).reduce(
|
||||||
groupCommentsByUser(lastSeen),
|
groupCommentsByUser(lastSeen),
|
||||||
[]
|
[],
|
||||||
),
|
),
|
||||||
[comments, lastSeen, order]
|
[comments, lastSeen, order],
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,7 +6,10 @@ import { useModal } from '~/hooks/modal/useModal';
|
||||||
import { INode } from '~/types';
|
import { INode } from '~/types';
|
||||||
import { showErrorToast } from '~/utils/errors/showToast';
|
import { showErrorToast } from '~/utils/errors/showToast';
|
||||||
|
|
||||||
export const useNodeActions = (node: INode, update: (node: Partial<INode>) => Promise<unknown>) => {
|
export const useNodeActions = (
|
||||||
|
node: INode,
|
||||||
|
update: (node: Partial<INode>) => Promise<unknown>,
|
||||||
|
) => {
|
||||||
const { showModal } = useModal();
|
const { showModal } = useModal();
|
||||||
|
|
||||||
const onLike = useCallback(async () => {
|
const onLike = useCallback(async () => {
|
||||||
|
@ -35,17 +38,20 @@ export const useNodeActions = (node: INode, update: (node: Partial<INode>) => Pr
|
||||||
|
|
||||||
const onLock = useCallback(async () => {
|
const onLock = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const result = await apiLockNode({ id: node.id, is_locked: !node.deleted_at });
|
const result = await apiLockNode({
|
||||||
|
id: node.id,
|
||||||
|
is_locked: !node.deleted_at,
|
||||||
|
});
|
||||||
await update({ deleted_at: result.deleted_at });
|
await update({ deleted_at: result.deleted_at });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showErrorToast(error);
|
showErrorToast(error);
|
||||||
}
|
}
|
||||||
}, [node.deleted_at, node.id, update]);
|
}, [node.deleted_at, node.id, update]);
|
||||||
|
|
||||||
const onEdit = useCallback(() => showModal(Dialog.EditNode, { nodeId: node.id! }), [
|
const onEdit = useCallback(
|
||||||
node,
|
() => showModal(Dialog.EditNode, { nodeId: node.id! }),
|
||||||
showModal,
|
[node, showModal],
|
||||||
]);
|
);
|
||||||
|
|
||||||
return { onLike, onStar, onLock, onEdit };
|
return { onLike, onStar, onLock, onEdit };
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,7 +4,8 @@ import { UploadType } from '~/constants/uploads';
|
||||||
import { INode } from '~/types';
|
import { INode } from '~/types';
|
||||||
|
|
||||||
export const useNodeAudios = (node: INode) => {
|
export const useNodeAudios = (node: INode) => {
|
||||||
return useMemo(() => node.files.filter(file => file && file.type === UploadType.Audio), [
|
return useMemo(
|
||||||
node.files,
|
() => node.files.filter((file) => file && file.type === UploadType.Audio),
|
||||||
]);
|
[node.files],
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
import { useCallback, useRef } from 'react';
|
import { useCallback, useRef } from 'react';
|
||||||
|
|
||||||
import { FormikConfig, FormikHelpers, useFormik, useFormikContext } from 'formik';
|
import {
|
||||||
|
FormikConfig,
|
||||||
|
FormikHelpers,
|
||||||
|
useFormik,
|
||||||
|
useFormikContext,
|
||||||
|
} from 'formik';
|
||||||
import { object } from 'yup';
|
import { object } from 'yup';
|
||||||
|
|
||||||
import { INode } from '~/types';
|
import { INode } from '~/types';
|
||||||
|
@ -10,9 +15,9 @@ import { showErrorToast } from '~/utils/errors/showToast';
|
||||||
|
|
||||||
const validationSchema = object().shape({});
|
const validationSchema = object().shape({});
|
||||||
|
|
||||||
const afterSubmit = ({ resetForm, setSubmitting, setErrors }: FormikHelpers<INode>) => (
|
const afterSubmit =
|
||||||
error?: unknown
|
({ resetForm, setSubmitting, setErrors }: FormikHelpers<INode>) =>
|
||||||
) => {
|
(error?: unknown) => {
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
|
@ -34,7 +39,7 @@ export const useNodeFormFormik = (
|
||||||
values: INode,
|
values: INode,
|
||||||
uploader: Uploader,
|
uploader: Uploader,
|
||||||
stopEditing: () => void,
|
stopEditing: () => void,
|
||||||
sendSaveRequest: (node: INode) => Promise<unknown>
|
sendSaveRequest: (node: INode) => Promise<unknown>,
|
||||||
) => {
|
) => {
|
||||||
const { current: initialValues } = useRef(values);
|
const { current: initialValues } = useRef(values);
|
||||||
|
|
||||||
|
@ -53,7 +58,7 @@ export const useNodeFormFormik = (
|
||||||
afterSubmit(helpers)(error);
|
afterSubmit(helpers)(error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[sendSaveRequest, uploader.files]
|
[sendSaveRequest, uploader.files],
|
||||||
);
|
);
|
||||||
|
|
||||||
return useFormik<INode>({
|
return useFormik<INode>({
|
||||||
|
|
|
@ -4,7 +4,8 @@ import { UploadType } from '~/constants/uploads';
|
||||||
import { INode } from '~/types';
|
import { INode } from '~/types';
|
||||||
|
|
||||||
export const useNodeImages = (node: INode) => {
|
export const useNodeImages = (node: INode) => {
|
||||||
return useMemo(() => node.files.filter(file => file && file.type === UploadType.Image), [
|
return useMemo(
|
||||||
node.files,
|
() => node.files.filter((file) => file && file.type === UploadType.Image),
|
||||||
]);
|
[node.files],
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,6 +27,6 @@ export const useUpdateNode = (id: number) => {
|
||||||
await lab.updateNode(result.node.id!, result.node);
|
await lab.updateNode(result.node.id!, result.node);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[update, flow, lab]
|
[update, flow, lab],
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,7 +20,7 @@ export const useGetProfile = (username?: string) => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
refreshInterval: 60000,
|
refreshInterval: 60000,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const profile = data || EMPTY_USER;
|
const profile = data || EMPTY_USER;
|
||||||
|
@ -29,7 +29,7 @@ export const useGetProfile = (username?: string) => {
|
||||||
async (user: Partial<IUser>) => {
|
async (user: Partial<IUser>) => {
|
||||||
await mutate({ ...profile, ...user });
|
await mutate({ ...profile, ...user });
|
||||||
},
|
},
|
||||||
[mutate, profile]
|
[mutate, profile],
|
||||||
);
|
);
|
||||||
|
|
||||||
return { profile, isLoading: !data && isValidating, update };
|
return { profile, isLoading: !data && isValidating, update };
|
||||||
|
|
|
@ -9,10 +9,8 @@ import { flatten } from '~/utils/ramda';
|
||||||
|
|
||||||
const RESULTS_COUNT = 20;
|
const RESULTS_COUNT = 20;
|
||||||
|
|
||||||
const getKey: (text: string) => SWRInfiniteKeyLoader = text => (
|
const getKey: (text: string) => SWRInfiniteKeyLoader =
|
||||||
pageIndex,
|
(text) => (pageIndex, previousPageData: INode[]) => {
|
||||||
previousPageData: INode[]
|
|
||||||
) => {
|
|
||||||
if ((pageIndex > 0 && !previousPageData?.length) || !text) return null;
|
if ((pageIndex > 0 && !previousPageData?.length) || !text) return null;
|
||||||
|
|
||||||
const props: GetSearchResultsRequest = {
|
const props: GetSearchResultsRequest = {
|
||||||
|
@ -40,7 +38,7 @@ export const useSearch = () => {
|
||||||
const result = await getSearchResults(props);
|
const result = await getSearchResults(props);
|
||||||
|
|
||||||
return result.nodes;
|
return result.nodes;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const loadMore = useCallback(() => setSize(size + 1), [setSize, size]);
|
const loadMore = useCallback(() => setSize(size + 1), [setSize, size]);
|
||||||
|
|
|
@ -26,5 +26,5 @@ export const useTagAutocomplete = (
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return useMemo(() => (search ? data ?? [] : []), [data, search]);
|
return useMemo(() => (search ? (data ?? []) : []), [data, search]);
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,10 +9,8 @@ import { flatten, isNil } from '~/utils/ramda';
|
||||||
|
|
||||||
const PAGE_SIZE = 10;
|
const PAGE_SIZE = 10;
|
||||||
|
|
||||||
const getKey: (tag: string) => SWRInfiniteKeyLoader = tag => (
|
const getKey: (tag: string) => SWRInfiniteKeyLoader =
|
||||||
pageIndex,
|
(tag) => (pageIndex, previousPageData: INode[]) => {
|
||||||
previousPageData: INode[]
|
|
||||||
) => {
|
|
||||||
if (pageIndex > 0 && !previousPageData?.length) return null;
|
if (pageIndex > 0 && !previousPageData?.length) return null;
|
||||||
return `${API.TAG.NODES}?tag=${tag}&page=${pageIndex}`;
|
return `${API.TAG.NODES}?tag=${tag}&page=${pageIndex}`;
|
||||||
};
|
};
|
||||||
|
@ -39,7 +37,7 @@ export const useTagNodes = (tag: string) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
return result.nodes;
|
return result.nodes;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const nodes = useMemo(() => flatten(data || []), [data]);
|
const nodes = useMemo(() => flatten(data || []), [data]);
|
||||||
|
@ -47,5 +45,12 @@ export const useTagNodes = (tag: string) => {
|
||||||
|
|
||||||
const loadMore = useCallback(() => setSize(size + 1), [setSize, size]);
|
const loadMore = useCallback(() => setSize(size + 1), [setSize, size]);
|
||||||
|
|
||||||
return { nodes, hasMore, loadMore, isLoading: !data && isValidating, mutate, data };
|
return {
|
||||||
|
nodes,
|
||||||
|
hasMore,
|
||||||
|
loadMore,
|
||||||
|
isLoading: !data && isValidating,
|
||||||
|
mutate,
|
||||||
|
data,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,7 +9,7 @@ export const useUpdates = () => {
|
||||||
const { data } = useSWR(
|
const { data } = useSWR(
|
||||||
isUser ? API.USER.GET_UPDATES : null,
|
isUser ? API.USER.GET_UPDATES : null,
|
||||||
() => apiAuthGetUpdates({ exclude_dialogs: 0, last: '' }),
|
() => apiAuthGetUpdates({ exclude_dialogs: 0, last: '' }),
|
||||||
{ refreshInterval: 5 * 60 * 1000 }
|
{ refreshInterval: 5 * 60 * 1000 },
|
||||||
);
|
);
|
||||||
|
|
||||||
const borisCommentedAt = data?.boris?.commented_at || '';
|
const borisCommentedAt = data?.boris?.commented_at || '';
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const reportWebVitals = onPerfEntry => {
|
const reportWebVitals = (onPerfEntry) => {
|
||||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||||
getCLS(onPerfEntry);
|
getCLS(onPerfEntry);
|
||||||
|
|
|
@ -24,11 +24,13 @@ export class FlowStore {
|
||||||
|
|
||||||
/** removes node from updated after user seen it */
|
/** removes node from updated after user seen it */
|
||||||
seenNode = (nodeId: number) => {
|
seenNode = (nodeId: number) => {
|
||||||
this.setUpdated(this.updated.filter(node => node.id !== nodeId));
|
this.setUpdated(this.updated.filter((node) => node.id !== nodeId));
|
||||||
};
|
};
|
||||||
|
|
||||||
/** replaces node with value */
|
/** replaces node with value */
|
||||||
updateNode = (id: number, node: Partial<IFlowNode>) => {
|
updateNode = (id: number, node: Partial<IFlowNode>) => {
|
||||||
this.setNodes(this.nodes.map(it => (it.id === id ? { ...it, ...node } : it)));
|
this.setNodes(
|
||||||
|
this.nodes.map((it) => (it.id === id ? { ...it, ...node } : it)),
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,9 @@ export class MetadataStore {
|
||||||
pending: string[] = [];
|
pending: string[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected apiMetadataLoader: (ids: string[]) => Promise<Record<string, EmbedMetadata>>
|
protected apiMetadataLoader: (
|
||||||
|
ids: string[],
|
||||||
|
) => Promise<Record<string, EmbedMetadata>>,
|
||||||
) {
|
) {
|
||||||
makeAutoObservable(this);
|
makeAutoObservable(this);
|
||||||
}
|
}
|
||||||
|
@ -59,7 +61,7 @@ export class MetadataStore {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await this.apiMetadataLoader(items);
|
const result = await this.apiMetadataLoader(items);
|
||||||
const fetchedIDs = values(result).map(it => it.address);
|
const fetchedIDs = values(result).map((it) => it.address);
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.pushMetadataItems(result);
|
this.pushMetadataItems(result);
|
||||||
|
@ -72,7 +74,11 @@ export class MetadataStore {
|
||||||
|
|
||||||
/** adds items to queue */
|
/** adds items to queue */
|
||||||
enqueue = (id: string) => {
|
enqueue = (id: string) => {
|
||||||
if (this.queue.includes(id) || keys(this.metadata).includes(id) || this.pending.includes(id)) {
|
if (
|
||||||
|
this.queue.includes(id) ||
|
||||||
|
keys(this.metadata).includes(id) ||
|
||||||
|
this.pending.includes(id)
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -153,13 +153,13 @@ export const NOTIFICATION_TYPES = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type IMessageNotification = {
|
export type IMessageNotification = {
|
||||||
type: typeof NOTIFICATION_TYPES['message'];
|
type: (typeof NOTIFICATION_TYPES)['message'];
|
||||||
content: Partial<IMessage>;
|
content: Partial<IMessage>;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ICommentNotification = {
|
export type ICommentNotification = {
|
||||||
type: typeof NOTIFICATION_TYPES['comment'];
|
type: (typeof NOTIFICATION_TYPES)['comment'];
|
||||||
content: Partial<IComment>;
|
content: Partial<IComment>;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,9 +5,8 @@ import type { SidebarComponents } from '~/constants/sidebar/components';
|
||||||
export type SidebarComponent = keyof SidebarComponents;
|
export type SidebarComponent = keyof SidebarComponents;
|
||||||
|
|
||||||
// TODO: use it to store props for sidebar
|
// TODO: use it to store props for sidebar
|
||||||
export type SidebarProps<
|
export type SidebarProps<T extends SidebarComponent> =
|
||||||
T extends SidebarComponent
|
SidebarComponents[T] extends FunctionComponent<infer U>
|
||||||
> = SidebarComponents[T] extends FunctionComponent<infer U>
|
|
||||||
? U extends object
|
? U extends object
|
||||||
? U extends SidebarComponentProps<T>
|
? U extends SidebarComponentProps<T>
|
||||||
? Omit<U, keyof SidebarComponentProps<T>>
|
? Omit<U, keyof SidebarComponentProps<T>>
|
||||||
|
|
|
@ -1,9 +1,19 @@
|
||||||
import { adjustHue, darken, desaturate, parseToHsla, transparentize } from 'color2k';
|
import {
|
||||||
|
adjustHue,
|
||||||
|
darken,
|
||||||
|
desaturate,
|
||||||
|
parseToHsla,
|
||||||
|
transparentize,
|
||||||
|
} from 'color2k';
|
||||||
|
|
||||||
import { DEFAULT_DOMINANT_COLOR } from '~/constants/node';
|
import { DEFAULT_DOMINANT_COLOR } from '~/constants/node';
|
||||||
import { stringToColour } from '~/utils/dom';
|
import { stringToColour } from '~/utils/dom';
|
||||||
|
|
||||||
export const normalizeBrightColor = (color?: string, saturationExp = 3, lightnessExp = 3) => {
|
export const normalizeBrightColor = (
|
||||||
|
color?: string,
|
||||||
|
saturationExp = 3,
|
||||||
|
lightnessExp = 3,
|
||||||
|
) => {
|
||||||
if (!color) {
|
if (!color) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
@ -12,12 +22,23 @@ export const normalizeBrightColor = (color?: string, saturationExp = 3, lightnes
|
||||||
const saturation = hsla[1];
|
const saturation = hsla[1];
|
||||||
const lightness = hsla[2];
|
const lightness = hsla[2];
|
||||||
|
|
||||||
const desaturated = saturationExp > 1 ? desaturate(color, saturation ** saturationExp) : color;
|
const desaturated =
|
||||||
return lightnessExp > 1 ? darken(desaturated, lightness ** lightnessExp) : desaturated;
|
saturationExp > 1 ? desaturate(color, saturation ** saturationExp) : color;
|
||||||
|
return lightnessExp > 1
|
||||||
|
? darken(desaturated, lightness ** lightnessExp)
|
||||||
|
: desaturated;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const generateColorTriplet = (val: string, saturation: number, lightness: number) => {
|
export const generateColorTriplet = (
|
||||||
const color = normalizeBrightColor(stringToColour(val), saturation, lightness);
|
val: string,
|
||||||
|
saturation: number,
|
||||||
|
lightness: number,
|
||||||
|
) => {
|
||||||
|
const color = normalizeBrightColor(
|
||||||
|
stringToColour(val),
|
||||||
|
saturation,
|
||||||
|
lightness,
|
||||||
|
);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
color,
|
color,
|
||||||
|
@ -31,9 +52,13 @@ export const generateGradientFromColor = (
|
||||||
saturation = 3,
|
saturation = 3,
|
||||||
lightness = 3,
|
lightness = 3,
|
||||||
angle = 155,
|
angle = 155,
|
||||||
opacity = 1
|
opacity = 1,
|
||||||
) => {
|
) => {
|
||||||
const [first, second, third] = generateColorTriplet(val, saturation, lightness).map(it => {
|
const [first, second, third] = generateColorTriplet(
|
||||||
|
val,
|
||||||
|
saturation,
|
||||||
|
lightness,
|
||||||
|
).map((it) => {
|
||||||
if (opacity > 1 || opacity < 0) {
|
if (opacity > 1 || opacity < 0) {
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,5 +6,6 @@ export const CONFIG = {
|
||||||
// image storage endpoint (sames as backend, but with /static usualy)
|
// image storage endpoint (sames as backend, but with /static usualy)
|
||||||
remoteCurrent: process.env.NEXT_PUBLIC_REMOTE_CURRENT || '',
|
remoteCurrent: process.env.NEXT_PUBLIC_REMOTE_CURRENT || '',
|
||||||
// transitional prop, marks migration to nextjs
|
// transitional prop, marks migration to nextjs
|
||||||
isNextEnvironment: !!process.env.NEXT_PUBLIC_REMOTE_CURRENT || typeof window === 'undefined',
|
isNextEnvironment:
|
||||||
|
!!process.env.NEXT_PUBLIC_REMOTE_CURRENT || typeof window === 'undefined',
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,11 +3,19 @@ import { differenceInDays, isAfter, isValid, parseISO } from 'date-fns';
|
||||||
import { IComment, ICommentGroup } from '~/types';
|
import { IComment, ICommentGroup } from '~/types';
|
||||||
import { curry, insert, nth, path, remove } from '~/utils/ramda';
|
import { curry, insert, nth, path, remove } from '~/utils/ramda';
|
||||||
|
|
||||||
export const moveArrItem = curry((at, to, list) => insert(to, nth(at, list), remove(at, 1, list)));
|
export const moveArrItem = curry((at, to, list) =>
|
||||||
|
insert(to, nth(at, list), remove(at, 1, list)),
|
||||||
|
);
|
||||||
export const objFromArray = (array: any[], key: string) =>
|
export const objFromArray = (array: any[], key: string) =>
|
||||||
array.reduce((obj, el) => (key && el[key] ? { ...obj, [el[key]]: el } : obj), {});
|
array.reduce(
|
||||||
|
(obj, el) => (key && el[key] ? { ...obj, [el[key]]: el } : obj),
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
|
||||||
const compareCommentDates = (commentDateValue?: string, lastSeenDateValue?: string) => {
|
const compareCommentDates = (
|
||||||
|
commentDateValue?: string,
|
||||||
|
lastSeenDateValue?: string,
|
||||||
|
) => {
|
||||||
if (!commentDateValue || !lastSeenDateValue) {
|
if (!commentDateValue || !lastSeenDateValue) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -37,11 +45,13 @@ const getCommentDistance = (firstDate?: string, secondDate?: string) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const groupCommentsByUser = (lastSeen?: string) => (
|
export const groupCommentsByUser =
|
||||||
grouppedComments: ICommentGroup[],
|
(lastSeen?: string) =>
|
||||||
comment: IComment
|
(grouppedComments: ICommentGroup[], comment: IComment): ICommentGroup[] => {
|
||||||
): ICommentGroup[] => {
|
const last: ICommentGroup | undefined = path(
|
||||||
const last: ICommentGroup | undefined = path([grouppedComments.length - 1], grouppedComments);
|
[grouppedComments.length - 1],
|
||||||
|
grouppedComments,
|
||||||
|
);
|
||||||
|
|
||||||
if (!comment.user) {
|
if (!comment.user) {
|
||||||
return grouppedComments;
|
return grouppedComments;
|
||||||
|
@ -69,12 +79,14 @@ export const groupCommentsByUser = (lastSeen?: string) => (
|
||||||
...last.distancesInDays,
|
...last.distancesInDays,
|
||||||
getCommentDistance(
|
getCommentDistance(
|
||||||
comment?.created_at,
|
comment?.created_at,
|
||||||
last.comments[last.comments.length - 1]?.created_at
|
last.comments[last.comments.length - 1]?.created_at,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
comments: [...last.comments, comment],
|
comments: [...last.comments, comment],
|
||||||
ids: [...last.ids, comment.id],
|
ids: [...last.ids, comment.id],
|
||||||
hasNew: last.hasNew || compareCommentDates(comment.created_at, lastSeen),
|
hasNew:
|
||||||
|
last.hasNew ||
|
||||||
|
compareCommentDates(comment.created_at, lastSeen),
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
];
|
];
|
||||||
|
|
|
@ -18,11 +18,16 @@ const ProfileContext = createContext<ProfileContextValue>({
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const ProfileProvider: FC<ProfileProviderProps> = ({ children, username }) => {
|
export const ProfileProvider: FC<ProfileProviderProps> = ({
|
||||||
|
children,
|
||||||
|
username,
|
||||||
|
}) => {
|
||||||
const { profile, isLoading } = useGetProfile(username);
|
const { profile, isLoading } = useGetProfile(username);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ProfileContext.Provider value={{ profile, isLoading }}>{children}</ProfileContext.Provider>
|
<ProfileContext.Provider value={{ profile, isLoading }}>
|
||||||
|
{children}
|
||||||
|
</ProfileContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,12 @@ import { flatten, isEmpty } from '~/utils/ramda';
|
||||||
|
|
||||||
export const splitTextByYoutube = (strings: string[]): string[] =>
|
export const splitTextByYoutube = (strings: string[]): string[] =>
|
||||||
flatten(
|
flatten(
|
||||||
strings.map(str =>
|
strings.map((str) =>
|
||||||
str.split(/(https?:\/\/(?:www\.)?(?:youtube\.com|youtu\.be)\/(?:watch)?(?:\?v=)?[\w\-&=]+)/)
|
str.split(
|
||||||
)
|
/(https?:\/\/(?:www\.)?(?:youtube\.com|youtu\.be)\/(?:watch)?(?:\?v=)?[\w\-&=]+)/,
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
export const splitTextOmitEmpty = (strings: string[]): string[] =>
|
export const splitTextOmitEmpty = (strings: string[]): string[] =>
|
||||||
strings.map(el => el.trim()).filter(el => !isEmpty(el));
|
strings.map((el) => el.trim()).filter((el) => !isEmpty(el));
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/** just combines title elements to form title of the page */
|
/** just combines title elements to form title of the page */
|
||||||
export const getPageTitle = (...props: string[]): string => {
|
export const getPageTitle = (...props: string[]): string => {
|
||||||
return ['Убежище', ...props].filter(it => it.trim()).join(' • ');
|
return ['Убежище', ...props].filter((it) => it.trim()).join(' • ');
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,11 +3,13 @@ import { ITag } from '~/types';
|
||||||
export const separateTags = (tags: Partial<ITag>[]): Partial<ITag>[][] =>
|
export const separateTags = (tags: Partial<ITag>[]): Partial<ITag>[][] =>
|
||||||
(tags || []).reduce(
|
(tags || []).reduce(
|
||||||
(obj, tag) =>
|
(obj, tag) =>
|
||||||
tag?.title?.substr(0, 1) === '/' ? [[...obj[0], tag], obj[1]] : [obj[0], [...obj[1], tag]],
|
tag?.title?.substr(0, 1) === '/'
|
||||||
[[], []] as Partial<ITag>[][]
|
? [[...obj[0], tag], obj[1]]
|
||||||
|
: [obj[0], [...obj[1], tag]],
|
||||||
|
[[], []] as Partial<ITag>[][],
|
||||||
);
|
);
|
||||||
|
|
||||||
export const separateTagOptions = (options: string[]): string[][] =>
|
export const separateTagOptions = (options: string[]): string[][] =>
|
||||||
separateTags(options.map((title): Partial<ITag> => ({ title }))).map(item =>
|
separateTags(options.map((title): Partial<ITag> => ({ title }))).map((item) =>
|
||||||
item.filter(tag => tag.title).map(({ title }) => title!)
|
item.filter((tag) => tag.title).map(({ title }) => title!),
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { ERROR_LITERAL, ERRORS } from '~/constants/errors';
|
import { ERROR_LITERAL, ERRORS } from '~/constants/errors';
|
||||||
import { ValueOf } from '~/types';
|
import { ValueOf } from '~/types';
|
||||||
|
|
||||||
export const t = (string: ValueOf<typeof ERRORS>): ValueOf<typeof ERROR_LITERAL> =>
|
export const t = (
|
||||||
ERROR_LITERAL[string] || string;
|
string: ValueOf<typeof ERRORS>,
|
||||||
|
): ValueOf<typeof ERROR_LITERAL> => ERROR_LITERAL[string] || string;
|
||||||
|
|
|
@ -2,10 +2,10 @@ import { FILE_MIMES, UploadType } from '~/constants/uploads';
|
||||||
import { isMimeOfImage } from '~/utils/validators';
|
import { isMimeOfImage } from '~/utils/validators';
|
||||||
|
|
||||||
/** if file is image, returns data-uri of thumbnail */
|
/** if file is image, returns data-uri of thumbnail */
|
||||||
export const uploadGetThumb = async file => {
|
export const uploadGetThumb = async (file) => {
|
||||||
if (!file.type || !isMimeOfImage(file.type)) return '';
|
if (!file.type || !isMimeOfImage(file.type)) return '';
|
||||||
|
|
||||||
return new Promise<string>(resolve => {
|
return new Promise<string>((resolve) => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onloadend = () => resolve(reader.result?.toString() || '');
|
reader.onloadend = () => resolve(reader.result?.toString() || '');
|
||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(file);
|
||||||
|
@ -15,14 +15,17 @@ export const uploadGetThumb = async file => {
|
||||||
/** returns UploadType by file */
|
/** returns UploadType by file */
|
||||||
export const getFileType = (file: File): UploadType | undefined =>
|
export const getFileType = (file: File): UploadType | undefined =>
|
||||||
((file.type &&
|
((file.type &&
|
||||||
Object.keys(FILE_MIMES).find(mime => FILE_MIMES[mime].includes(file.type))) as UploadType) ||
|
Object.keys(FILE_MIMES).find((mime) =>
|
||||||
undefined;
|
FILE_MIMES[mime].includes(file.type),
|
||||||
|
)) as UploadType) || undefined;
|
||||||
|
|
||||||
/** getImageFromPaste returns any images from paste event */
|
/** getImageFromPaste returns any images from paste event */
|
||||||
export const getImageFromPaste = (event: ClipboardEvent): Promise<File | undefined> => {
|
export const getImageFromPaste = (
|
||||||
|
event: ClipboardEvent,
|
||||||
|
): Promise<File | undefined> => {
|
||||||
const items = event.clipboardData?.items;
|
const items = event.clipboardData?.items;
|
||||||
|
|
||||||
return new Promise(resolve => {
|
return new Promise((resolve) => {
|
||||||
for (let index in items) {
|
for (let index in items) {
|
||||||
const item = items[index];
|
const item = items[index];
|
||||||
|
|
||||||
|
@ -40,7 +43,7 @@ export const getImageFromPaste = (event: ClipboardEvent): Promise<File | undefin
|
||||||
new File([e.target?.result], 'paste.png', {
|
new File([e.target?.result], 'paste.png', {
|
||||||
type,
|
type,
|
||||||
lastModified: new Date().getTime(),
|
lastModified: new Date().getTime(),
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
import { IMAGE_MIME_TYPES } from '~/constants/uploads';
|
import { IMAGE_MIME_TYPES } from '~/constants/uploads';
|
||||||
|
|
||||||
export const isMimeOfImage = (mime): boolean => !!mime && IMAGE_MIME_TYPES.indexOf(mime) >= 0;
|
export const isMimeOfImage = (mime): boolean =>
|
||||||
|
!!mime && IMAGE_MIME_TYPES.indexOf(mime) >= 0;
|
||||||
|
|
46
yarn.lock
46
yarn.lock
|
@ -221,6 +221,11 @@
|
||||||
"@nodelib/fs.scandir" "2.1.5"
|
"@nodelib/fs.scandir" "2.1.5"
|
||||||
fastq "^1.6.0"
|
fastq "^1.6.0"
|
||||||
|
|
||||||
|
"@pkgr/core@^0.1.0":
|
||||||
|
version "0.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31"
|
||||||
|
integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==
|
||||||
|
|
||||||
"@polka/url@^1.0.0-next.20":
|
"@polka/url@^1.0.0-next.20":
|
||||||
version "1.0.0-next.21"
|
version "1.0.0-next.21"
|
||||||
resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1"
|
resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1"
|
||||||
|
@ -1206,6 +1211,14 @@ 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-prettier@^5.2.3:
|
||||||
|
version "5.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.3.tgz#c4af01691a6fa9905207f0fbba0d7bea0902cce5"
|
||||||
|
integrity sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw==
|
||||||
|
dependencies:
|
||||||
|
prettier-linter-helpers "^1.0.0"
|
||||||
|
synckit "^0.9.1"
|
||||||
|
|
||||||
eslint-plugin-react-hooks@^4.6.0:
|
eslint-plugin-react-hooks@^4.6.0:
|
||||||
version "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"
|
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3"
|
||||||
|
@ -1394,6 +1407,11 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
|
||||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
||||||
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
|
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
|
||||||
|
|
||||||
|
fast-diff@^1.1.2:
|
||||||
|
version "1.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0"
|
||||||
|
integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==
|
||||||
|
|
||||||
fast-fifo@^1.1.0, fast-fifo@^1.2.0:
|
fast-fifo@^1.1.0, fast-fifo@^1.2.0:
|
||||||
version "1.3.2"
|
version "1.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c"
|
resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c"
|
||||||
|
@ -2452,10 +2470,17 @@ prelude-ls@^1.2.1:
|
||||||
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
|
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
|
||||||
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
|
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
|
||||||
|
|
||||||
prettier@^2.7.1:
|
prettier-linter-helpers@^1.0.0:
|
||||||
version "2.7.1"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64"
|
resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b"
|
||||||
integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==
|
integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==
|
||||||
|
dependencies:
|
||||||
|
fast-diff "^1.1.2"
|
||||||
|
|
||||||
|
prettier@^3.0.0:
|
||||||
|
version "3.4.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.4.2.tgz#a5ce1fb522a588bf2b78ca44c6e6fe5aa5a2b13f"
|
||||||
|
integrity sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==
|
||||||
|
|
||||||
pretty-format@^26.6.2:
|
pretty-format@^26.6.2:
|
||||||
version "26.6.2"
|
version "26.6.2"
|
||||||
|
@ -3087,6 +3112,14 @@ swr@^1.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/swr/-/swr-1.2.0.tgz#8649f6e9131ce94bbcf7ffd65c21334da3d1ec20"
|
resolved "https://registry.yarnpkg.com/swr/-/swr-1.2.0.tgz#8649f6e9131ce94bbcf7ffd65c21334da3d1ec20"
|
||||||
integrity sha512-C3IXeKOREn0jQ1ewXRENE7ED7jjGbFTakwB64eLACkCqkF/A0N2ckvpCTftcaSYi5yV36PzoehgVCOVRmtECcA==
|
integrity sha512-C3IXeKOREn0jQ1ewXRENE7ED7jjGbFTakwB64eLACkCqkF/A0N2ckvpCTftcaSYi5yV36PzoehgVCOVRmtECcA==
|
||||||
|
|
||||||
|
synckit@^0.9.1:
|
||||||
|
version "0.9.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.9.2.tgz#a3a935eca7922d48b9e7d6c61822ee6c3ae4ec62"
|
||||||
|
integrity sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==
|
||||||
|
dependencies:
|
||||||
|
"@pkgr/core" "^0.1.0"
|
||||||
|
tslib "^2.6.2"
|
||||||
|
|
||||||
table@^6.0.9:
|
table@^6.0.9:
|
||||||
version "6.8.0"
|
version "6.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/table/-/table-6.8.0.tgz#87e28f14fa4321c3377ba286f07b79b281a3b3ca"
|
resolved "https://registry.yarnpkg.com/table/-/table-6.8.0.tgz#87e28f14fa4321c3377ba286f07b79b281a3b3ca"
|
||||||
|
@ -3221,6 +3254,11 @@ tslib@^2.0.3, tslib@^2.1.0:
|
||||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
|
||||||
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
|
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
|
||||||
|
|
||||||
|
tslib@^2.6.2:
|
||||||
|
version "2.8.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
|
||||||
|
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
|
||||||
|
|
||||||
tsutils@^3.21.0:
|
tsutils@^3.21.0:
|
||||||
version "3.21.0"
|
version "3.21.0"
|
||||||
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"
|
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue