mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 12:56:41 +07:00
removed redux completely
This commit is contained in:
parent
26e6d8d41b
commit
a4bb07e9cf
323 changed files with 2464 additions and 3348 deletions
|
@ -1,18 +1,34 @@
|
|||
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
|
||||
import { push } from 'connected-react-router';
|
||||
import axios, { AxiosError, AxiosResponse } from 'axios';
|
||||
import { API } from '~/constants/api';
|
||||
import { store } from '~/redux/store';
|
||||
import { IApiErrorResult, IResultWithStatus } from '~/redux/types';
|
||||
|
||||
export const authMiddleware = r => {
|
||||
store.dispatch(push('/login'));
|
||||
return r;
|
||||
};
|
||||
import { getMOBXStore } from '~/store';
|
||||
import { assocPath } from 'ramda';
|
||||
|
||||
export const api = axios.create({
|
||||
baseURL: API.BASE,
|
||||
});
|
||||
|
||||
// Pass token to axios
|
||||
api.interceptors.request.use(options => {
|
||||
const token = getMOBXStore().auth.token;
|
||||
|
||||
if (!token) {
|
||||
return options;
|
||||
}
|
||||
|
||||
return assocPath(['headers', 'authorization'], `Bearer ${token}`, options);
|
||||
});
|
||||
|
||||
// Logout on 401
|
||||
api.interceptors.response.use(undefined, (error: AxiosError<{ error: string }>) => {
|
||||
if (error.response?.status === HTTP_RESPONSES.UNAUTHORIZED) {
|
||||
getMOBXStore().auth.logout();
|
||||
}
|
||||
|
||||
error.message = error?.response?.data?.error || error?.response?.statusText || error.message;
|
||||
|
||||
throw error;
|
||||
});
|
||||
|
||||
export const HTTP_RESPONSES = {
|
||||
SUCCESS: 200,
|
||||
CREATED: 201,
|
||||
|
@ -23,32 +39,4 @@ export const HTTP_RESPONSES = {
|
|||
TOO_MANY_REQUESTS: 429,
|
||||
};
|
||||
|
||||
export const resultMiddleware = <T extends any>({ status, data }: { status; data: T }) => ({
|
||||
status,
|
||||
data,
|
||||
});
|
||||
|
||||
export const errorMiddleware = <T extends any = any>(debug): IResultWithStatus<T> =>
|
||||
debug && debug.response
|
||||
? {
|
||||
status: debug.response.status,
|
||||
data:
|
||||
(debug.response.data as T & IApiErrorResult) || (debug.response as T & IApiErrorResult),
|
||||
error: debug?.response?.data?.error || debug?.response?.error || 'Неизвестная ошибка',
|
||||
}
|
||||
: {
|
||||
status: HTTP_RESPONSES.CONNECTION_REFUSED,
|
||||
data: {} as T & IApiErrorResult & any,
|
||||
debug,
|
||||
error: 'Ошибка сети',
|
||||
};
|
||||
|
||||
export const configWithToken = (
|
||||
access: string,
|
||||
config: AxiosRequestConfig = {}
|
||||
): AxiosRequestConfig => ({
|
||||
...config,
|
||||
headers: { ...(config.headers || {}), Authorization: `Bearer ${access}` },
|
||||
});
|
||||
|
||||
export const cleanResult = <T extends any>(response: AxiosResponse<T>): T => response?.data;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { IComment, IFile } from '~/redux/types';
|
||||
import { IComment, IFile } from '~/types';
|
||||
import React, { createContext, FC, useContext } from 'react';
|
||||
|
||||
export interface CommentProviderProps {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { createContext, FC, useContext } from 'react';
|
||||
import { FlowDisplay, IFlowNode, INode } from '~/redux/types';
|
||||
import { FlowDisplay, IFlowNode, INode } from '~/types';
|
||||
|
||||
export interface FlowContextProps {
|
||||
updates: IFlowNode[];
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { ILabNode } from '~/types/lab';
|
||||
import React, { createContext, FC, useContext } from 'react';
|
||||
import { IFlowNode, ITag } from '~/redux/types';
|
||||
import { IFlowNode, ITag } from '~/types';
|
||||
|
||||
export interface LabContextProps {
|
||||
isLoading: boolean;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { INode } from '~/redux/types';
|
||||
import { INode } from '~/types';
|
||||
import { EMPTY_NODE } from '~/constants/node';
|
||||
import React, { createContext, FC, useContext } from 'react';
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { INodeRelated } from "~/types/node";
|
||||
import React, { createContext, FC, useContext } from "react";
|
||||
import { INodeRelated } from '~/types/node';
|
||||
import React, { createContext, FC, useContext } from 'react';
|
||||
|
||||
interface NodeRelatedProviderProps {
|
||||
related: INodeRelated;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { createContext, FC, useContext } from 'react';
|
||||
import { ITag } from '~/redux/types';
|
||||
import { ITag } from '~/types';
|
||||
|
||||
export interface TagContextProps {
|
||||
tags: ITag[];
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, { createContext, FC, useContext } from 'react';
|
||||
import { useUploader } from '~/hooks/data/useUploader';
|
||||
import { IFile } from '~/redux/types';
|
||||
import { IFile } from '~/types';
|
||||
import { EMPTY_FILE } from '~/constants/uploads';
|
||||
|
||||
export type Uploader = ReturnType<typeof useUploader>;
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import React, { createContext, FC, useContext } from 'react';
|
||||
import { IUser } from '~/redux/auth/types';
|
||||
import { EMPTY_USER } from '~/redux/auth/constants';
|
||||
import { useUser } from '~/hooks/user/userUser';
|
||||
import { IUser } from '~/types/auth';
|
||||
import { EMPTY_USER } from '~/constants/auth';
|
||||
import { useUser } from '~/hooks/auth/useUser';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
|
||||
const UserContext = createContext<IUser>(EMPTY_USER);
|
||||
|
||||
export const UserContextProvider: FC = ({ children }) => {
|
||||
const user = useUser();
|
||||
export const UserContextProvider: FC = observer(({ children }) => {
|
||||
const { user } = useUser();
|
||||
|
||||
return <UserContext.Provider value={user}>{children}</UserContext.Provider>;
|
||||
};
|
||||
});
|
||||
|
||||
export const useUserContext = () => useContext(UserContext);
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import { IFile, ValueOf } from '~/redux/types';
|
||||
import { IFile, ValueOf } from '~/types';
|
||||
import formatDistanceToNow from 'date-fns/formatDistanceToNow';
|
||||
import isAfter from 'date-fns/isAfter';
|
||||
import differenceInMonths from 'date-fns/differenceInMonths';
|
||||
import differenceInMinutes from 'date-fns/differenceInMinutes';
|
||||
import ru from 'date-fns/locale/ru';
|
||||
import Axios from 'axios';
|
||||
import { PRESETS } from '~/constants/urls';
|
||||
import { COMMENT_BLOCK_DETECTORS, COMMENT_BLOCK_TYPES, ICommentBlock } from '~/constants/comment';
|
||||
import format from 'date-fns/format';
|
||||
|
@ -21,18 +20,6 @@ import {
|
|||
} from '~/utils/formatText';
|
||||
import { splitTextByYoutube, splitTextOmitEmpty } from '~/utils/splitText';
|
||||
|
||||
export const getStyle = (oElm: any, strCssRule: string) => {
|
||||
if (document.defaultView && document.defaultView.getComputedStyle) {
|
||||
return document.defaultView.getComputedStyle(oElm, '').getPropertyValue(strCssRule);
|
||||
}
|
||||
|
||||
if (oElm.currentStyle) {
|
||||
return oElm.currentStyle[strCssRule.replace(/-(\w)/g, (strMatch, p1) => p1.toUpperCase())];
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
|
||||
const angleInRadians = ((angleInDegrees - 90) * Math.PI) / 180.0;
|
||||
|
||||
|
@ -126,8 +113,6 @@ export const splitCommentByBlocks = (text: string): ICommentBlock[] =>
|
|||
export const formatCommentText = (author?: string, text?: string): ICommentBlock[] =>
|
||||
author && text ? splitCommentByBlocks(text) : [];
|
||||
|
||||
export const formatCellText = (text: string): string => formatTextParagraphs(text);
|
||||
|
||||
export const getPrettyDate = (date?: string): string => {
|
||||
if (!date) {
|
||||
return '';
|
||||
|
@ -147,30 +132,16 @@ export const getPrettyDate = (date?: string): string => {
|
|||
});
|
||||
};
|
||||
|
||||
export const getYoutubeTitle = async (id: string) => {
|
||||
Axios.get(`http://youtube.com/get_video_info?video_id=${id}`).then(console.log);
|
||||
};
|
||||
|
||||
export const getYoutubeThumb = (url: string) => {
|
||||
const match =
|
||||
url &&
|
||||
url.match(
|
||||
/http(?:s?):\/\/(?:www\.)?youtu(?:be\.com\/watch\?v=|\.be\/)([\w\-\_]*)(&(amp;)?[\w\?=]*)?/
|
||||
/http(?:s?):\/\/(?:www\.)?youtu(?:be\.com\/watch\?v=|\.be\/)([\w\-_]*)(&(amp;)?[\w?=]*)?/
|
||||
);
|
||||
|
||||
return match && match[1] ? `https://i.ytimg.com/vi/${match[1]}/hqdefault.jpg` : null;
|
||||
};
|
||||
|
||||
export function plural(n: number, one: string, two: string, five: string) {
|
||||
if (n % 10 === 1 && n % 100 !== 11) {
|
||||
return `${n} ${one}`;
|
||||
} else if (n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20)) {
|
||||
return `${n} ${two}`;
|
||||
} else {
|
||||
return `${n} ${five}`;
|
||||
}
|
||||
}
|
||||
|
||||
export const stringToColour = (str: string) => {
|
||||
let hash = 0;
|
||||
|
||||
|
@ -189,26 +160,26 @@ export const stringToColour = (str: string) => {
|
|||
};
|
||||
|
||||
export const darken = (col: string, amt: number) => {
|
||||
var usePound = false;
|
||||
let usePound = false;
|
||||
|
||||
if (col[0] == '#') {
|
||||
if (col[0] === '#') {
|
||||
col = col.slice(1);
|
||||
usePound = true;
|
||||
}
|
||||
|
||||
var num = parseInt(col, 16);
|
||||
const num = parseInt(col, 16);
|
||||
|
||||
var r = (num >> 16) + amt;
|
||||
let r = (num >> 16) + amt;
|
||||
|
||||
if (r > 255) r = 255;
|
||||
else if (r < 0) r = 0;
|
||||
|
||||
var b = ((num >> 8) & 0x00ff) + amt;
|
||||
let b = ((num >> 8) & 0x00ff) + amt;
|
||||
|
||||
if (b > 255) b = 255;
|
||||
else if (b < 0) b = 0;
|
||||
|
||||
var g = (num & 0x0000ff) + amt;
|
||||
let g = (num & 0x0000ff) + amt;
|
||||
|
||||
if (g > 255) g = 255;
|
||||
else if (g < 0) g = 0;
|
||||
|
@ -217,9 +188,9 @@ export const darken = (col: string, amt: number) => {
|
|||
};
|
||||
|
||||
export const sizeOf = (bytes: number): string => {
|
||||
if (bytes == 0) {
|
||||
if (bytes === 0) {
|
||||
return '0.00 B';
|
||||
}
|
||||
var e = Math.floor(Math.log(bytes) / Math.log(1024));
|
||||
let e = Math.floor(Math.log(bytes) / Math.log(1024));
|
||||
return (bytes / Math.pow(1024, e)).toFixed(2) + ' ' + ' KMGTP'.charAt(e) + 'B';
|
||||
};
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
import { has, path } from 'ramda';
|
||||
import { ERROR_LITERAL, ERRORS } from '~/constants/errors';
|
||||
|
||||
export const getErrorMessage = (error: unknown) => {
|
||||
export const getErrorMessage = (error: unknown): string | undefined => {
|
||||
if (error === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (typeof error === 'string' && has(error, ERROR_LITERAL)) {
|
||||
return ERROR_LITERAL[error];
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { curry, insert, nth, path, remove } from 'ramda';
|
||||
import { IComment, ICommentGroup } from '~/redux/types';
|
||||
import { IComment, ICommentGroup } from '~/types';
|
||||
import { isAfter, isValid, parseISO } from 'date-fns';
|
||||
|
||||
export const moveArrItem = curry((at, to, list) => insert(to, nth(at, list), remove(at, 1, list)));
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import marked from "marked";
|
||||
import { stripHTMLTags } from "~/utils/stripHTMLTags";
|
||||
import marked from 'marked';
|
||||
import { stripHTMLTags } from '~/utils/stripHTMLTags';
|
||||
import { EventMessageType } from '~/constants/events';
|
||||
|
||||
/**
|
||||
* Cleans youtube urls
|
||||
*/
|
||||
export const formatTextSanitizeYoutube = (text: string): string =>
|
||||
text.replace(
|
||||
/(https?:\/\/(www\.)?(youtube\.com|youtu\.be)\/(watch)?(\?v=)?[\w\-\&\=]+)/gim,
|
||||
/(https?:\/\/(www\.)?(youtube\.com|youtu\.be)\/(watch)?(\?v=)?[\w\-&=]+)/gim,
|
||||
'\n$1\n'
|
||||
);
|
||||
|
||||
|
@ -21,7 +22,7 @@ export const formatTextSanitizeTags = (text: string): string => stripHTMLTags(te
|
|||
export const formatTextClickableUsernames = (text: string): string =>
|
||||
text.replace(
|
||||
/~([\wа-яА-Я-]+)/giu,
|
||||
'<span class="username" onClick="window.postMessage({ type: \'username\', username: \'$1\'});">~$1</span>'
|
||||
`<span class="username" onClick="window.postMessage({ type: '${EventMessageType.OpenProfile}', username: '$1'});">~$1</span>`
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -49,16 +50,7 @@ export const formatTextTodos = (text: string): string =>
|
|||
* Formats !!exclamation messages with green color
|
||||
*/
|
||||
export const formatExclamations = (text: string): string =>
|
||||
text.replace(/(\!\![\s\S]*?(\!\!|\n|$))/gim, '<span class="green">$1$2</span>');
|
||||
|
||||
/**
|
||||
* Formats links
|
||||
*/
|
||||
export const formatLinks = (text: string): string =>
|
||||
text.replace(
|
||||
/(\b(https?|ftp|file):\/\/([-A-Z0-9+&@#%?=~_|!:,.;]*)([-A-Z0-9+&@#%?\/=~_|!:,.;]*)[-A-Z0-9+&@#\/%=~_|])/gi,
|
||||
'<a href="$1" target="blank" rel="nofollow">$1</a>'
|
||||
);
|
||||
text.replace(/(!![\s\S]*?(!!|\n|$))/gim, '<span class="green">$1$2</span>');
|
||||
|
||||
/**
|
||||
* Replaces -- with dash
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
import { USER_ROLES } from '~/redux/auth/constants';
|
||||
import { ICommentGroup, INode } from '~/redux/types';
|
||||
import { IUser } from '~/redux/auth/types';
|
||||
import { Role } from '~/constants/auth';
|
||||
import { ICommentGroup, INode } from '~/types';
|
||||
import { IUser } from '~/types/auth';
|
||||
import { path } from 'ramda';
|
||||
import { NODE_TYPES } from '~/constants/node';
|
||||
|
||||
export const canEditNode = (node?: Partial<INode>, user?: Partial<IUser>): boolean =>
|
||||
path(['role'], user) === USER_ROLES.ADMIN || path(['user', 'id'], node) === path(['id'], user);
|
||||
path(['role'], user) === Role.Admin || path(['user', 'id'], node) === path(['id'], user);
|
||||
|
||||
export const canEditComment = (comment?: Partial<ICommentGroup>, user?: Partial<IUser>): boolean =>
|
||||
path(['role'], user) === USER_ROLES.ADMIN || path(['user', 'id'], comment) === path(['id'], user);
|
||||
path(['role'], user) === Role.Admin || path(['user', 'id'], comment) === path(['id'], user);
|
||||
|
||||
export const canLikeNode = (node?: Partial<INode>, user?: Partial<IUser>): boolean =>
|
||||
path(['role'], user) !== USER_ROLES.GUEST;
|
||||
path(['role'], user) !== Role.Guest;
|
||||
|
||||
export const canStarNode = (node?: Partial<INode>, user?: Partial<IUser>): boolean =>
|
||||
path(['type'], node) === NODE_TYPES.IMAGE &&
|
||||
path(['is_promoted'], node) === false &&
|
||||
path(['role'], user) === USER_ROLES.ADMIN;
|
||||
path(['role'], user) === Role.Admin;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import React, { createContext, FC, useCallback, useContext, useEffect, useState } from "react";
|
||||
import { IFile } from "~/redux/types";
|
||||
import { getURL } from "~/utils/dom";
|
||||
import { path } from "ramda";
|
||||
import { PlayerState } from "~/constants/player";
|
||||
import { PlayerProgress } from "~/types/player";
|
||||
import React, { createContext, FC, useCallback, useContext, useEffect, useState } from 'react';
|
||||
import { IFile } from '~/types';
|
||||
import { getURL } from '~/utils/dom';
|
||||
import { path } from 'ramda';
|
||||
import { PlayerState } from '~/constants/player';
|
||||
import { PlayerProgress } from '~/types/player';
|
||||
|
||||
interface AudioPlayerProps {
|
||||
file?: IFile;
|
||||
|
|
29
src/utils/providers/AuthProvider.tsx
Normal file
29
src/utils/providers/AuthProvider.tsx
Normal file
|
@ -0,0 +1,29 @@
|
|||
import { createContext, FC, useContext } from 'react';
|
||||
import { useRestorePasswordRedirect } from '~/hooks/auth/useRestorePasswordRedirect';
|
||||
import { useMessageEventReactions } from '~/hooks/auth/useMessageEventReactions';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useAuth } from '~/hooks/auth/useAuth';
|
||||
import { EMPTY_USER } from '~/constants/auth';
|
||||
|
||||
interface AuthProviderContextType extends ReturnType<typeof useAuth> {}
|
||||
|
||||
const AuthContext = createContext<AuthProviderContextType>({
|
||||
user: EMPTY_USER,
|
||||
isUser: false,
|
||||
isTester: false,
|
||||
setIsTester: isTester => isTester,
|
||||
logout: () => {},
|
||||
login: async () => EMPTY_USER,
|
||||
setToken: () => {},
|
||||
});
|
||||
|
||||
export const AuthProvider: FC = observer(({ children }) => {
|
||||
const value = useAuth();
|
||||
|
||||
useMessageEventReactions();
|
||||
useRestorePasswordRedirect();
|
||||
|
||||
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
||||
});
|
||||
|
||||
export const useAuthProvider = () => useContext(AuthContext);
|
|
@ -1,6 +1,6 @@
|
|||
import React, { FC } from "react";
|
||||
import { LabContextProvider } from "~/utils/context/LabContextProvider";
|
||||
import { useLab } from "~/hooks/lab/useLab";
|
||||
import React, { FC } from 'react';
|
||||
import { LabContextProvider } from '~/utils/context/LabContextProvider';
|
||||
import { useLab } from '~/hooks/lab/useLab';
|
||||
|
||||
interface LabProviderProps {}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import React, { createContext, FC, useContext, useEffect } from "react";
|
||||
import { MetadataStore } from "~/store/metadata/MetadataStore";
|
||||
import { observer, useLocalObservable } from "mobx-react-lite";
|
||||
import { apiGetEmbedYoutube } from "~/api/metadata";
|
||||
import { EmbedMetadata } from "~/types/metadata";
|
||||
import React, { createContext, FC, useContext, useEffect } from 'react';
|
||||
import { MetadataStore } from '~/store/metadata/MetadataStore';
|
||||
import { observer, useLocalObservable } from 'mobx-react-lite';
|
||||
import { apiGetEmbedYoutube } from '~/api/metadata';
|
||||
import { EmbedMetadata } from '~/types/metadata';
|
||||
|
||||
interface MetadataContextProps {
|
||||
metadata: Record<string, EmbedMetadata>;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import React, { FC, useEffect } from "react";
|
||||
import { INode, ITag } from "~/redux/types";
|
||||
import { NodeRelatedContextProvider } from "~/utils/context/NodeRelatedContextProvider";
|
||||
import { INodeRelated } from "~/types/node";
|
||||
import { useGetNodeRelated } from "~/hooks/node/useGetNodeRelated";
|
||||
import React, { FC, useEffect } from 'react';
|
||||
import { INode, ITag } from '~/types';
|
||||
import { NodeRelatedContextProvider } from '~/utils/context/NodeRelatedContextProvider';
|
||||
import { INodeRelated } from '~/types/node';
|
||||
import { useGetNodeRelated } from '~/hooks/node/useGetNodeRelated';
|
||||
|
||||
interface NodeRelatedProviderProps {
|
||||
id: INode['id'];
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
import { createContext, FC, useCallback, useContext } from 'react';
|
||||
import { ApiUpdateUserRequest, IUser } from '~/redux/auth/types';
|
||||
import { createContext, FC, useContext } from 'react';
|
||||
import { IUser } from '~/types/auth';
|
||||
import { useGetProfile } from '~/hooks/profile/useGetProfile';
|
||||
import { EMPTY_USER } from '~/redux/auth/constants';
|
||||
import { usePatchProfile } from '~/hooks/profile/usePatchProfile';
|
||||
import { useUser } from '~/hooks/user/userUser';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { authSetUser } from '~/redux/auth/actions';
|
||||
import { EMPTY_USER } from '~/constants/auth';
|
||||
|
||||
interface ProfileProviderProps {
|
||||
username: string;
|
||||
|
@ -14,41 +10,18 @@ interface ProfileProviderProps {
|
|||
interface ProfileContextValue {
|
||||
profile: IUser;
|
||||
isLoading: boolean;
|
||||
updatePhoto: (file: File) => Promise<unknown>;
|
||||
updateProfile: (user: Partial<ApiUpdateUserRequest['user']>) => Promise<IUser>;
|
||||
}
|
||||
|
||||
const ProfileContext = createContext<ProfileContextValue>({
|
||||
profile: EMPTY_USER,
|
||||
isLoading: false,
|
||||
updatePhoto: async () => {},
|
||||
updateProfile: async () => EMPTY_USER,
|
||||
});
|
||||
|
||||
export const ProfileProvider: FC<ProfileProviderProps> = ({ children, username }) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const user = useUser();
|
||||
const { profile, isLoading, update: updateProfileData } = useGetProfile(username);
|
||||
|
||||
const update = useCallback(
|
||||
async (data: Partial<IUser>) => {
|
||||
if (profile.id === user.id) {
|
||||
await updateProfileData(data);
|
||||
}
|
||||
|
||||
// TODO: user updateUser from useGetUser or something
|
||||
dispatch(authSetUser(data));
|
||||
},
|
||||
[updateProfileData, dispatch, profile, user]
|
||||
);
|
||||
|
||||
const { updatePhoto, updateProfile } = usePatchProfile(update);
|
||||
const { profile, isLoading } = useGetProfile(username);
|
||||
|
||||
return (
|
||||
<ProfileContext.Provider value={{ profile, isLoading, updatePhoto, updateProfile }}>
|
||||
{children}
|
||||
</ProfileContext.Provider>
|
||||
<ProfileContext.Provider value={{ profile, isLoading }}>{children}</ProfileContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { createContext, FC, useContext } from 'react';
|
||||
import { INode } from '~/redux/types';
|
||||
import { INode } from '~/types';
|
||||
import { useSearch } from '~/hooks/search/useSearch';
|
||||
|
||||
export interface SearchContextProps {
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
// create-index.tsx
|
||||
import { Action } from "redux";
|
||||
|
||||
type Handlers<State, Types extends string, Actions extends Action<Types>> = {
|
||||
readonly [Type in Types]: (state: State, action: Actions) => State;
|
||||
};
|
||||
|
||||
export const createReducer = (initialState, handlers) => (state = initialState, action) =>
|
||||
handlers.hasOwnProperty(action.type) ? handlers[action.type](state, action) : state;
|
|
@ -3,7 +3,7 @@ import { flatten, isEmpty } from 'ramda';
|
|||
export const splitTextByYoutube = (strings: string[]): string[] =>
|
||||
flatten(
|
||||
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\-&=]+)/)
|
||||
)
|
||||
);
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { ITag } from '~/redux/types';
|
||||
import { ITag } from '~/types';
|
||||
|
||||
export const separateTags = (tags: Partial<ITag>[]): Partial<ITag>[][] =>
|
||||
(tags || []).reduce(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { ERROR_LITERAL, ERRORS } from '~/constants/errors';
|
||||
import { ValueOf } from '~/redux/types';
|
||||
import { ValueOf } from '~/types';
|
||||
|
||||
export const t = (string: ValueOf<typeof ERRORS>): ValueOf<typeof ERROR_LITERAL> =>
|
||||
ERROR_LITERAL[string] || string;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { VALIDATORS } from "~/utils/validators";
|
||||
import { FILE_MIMES, UploadType } from "~/constants/uploads";
|
||||
import { isMimeOfImage } from '~/utils/validators';
|
||||
import { FILE_MIMES, UploadType } from '~/constants/uploads';
|
||||
|
||||
/** if file is image, returns data-uri of thumbnail */
|
||||
export const uploadGetThumb = async file => {
|
||||
if (!file.type || !VALIDATORS.IS_IMAGE_MIME(file.type)) return '';
|
||||
if (!file.type || !isMimeOfImage(file.type)) return '';
|
||||
|
||||
return new Promise<string>(resolve => {
|
||||
const reader = new FileReader();
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import { EventMessageType } from '~/constants/events';
|
||||
|
||||
export const openUserProfile = (username?: string) => {
|
||||
if (!username) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.postMessage({ type: 'username', username }, '*');
|
||||
window.postMessage({ type: EventMessageType.OpenProfile, username }, '*');
|
||||
};
|
||||
|
|
|
@ -1,36 +1,3 @@
|
|||
import isValid from 'date-fns/isValid';
|
||||
import { IMAGE_MIME_TYPES } from '~/constants/uploads';
|
||||
|
||||
const isValidEmail = (email: string): boolean =>
|
||||
!!email &&
|
||||
!!String(email) &&
|
||||
!!String(email).match(
|
||||
/^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/
|
||||
);
|
||||
|
||||
const isLikeEmail = (email: string): boolean =>
|
||||
!!email && !!String(email) && !!String(email).match(/^([^\@]+)@([^\@]+)\.([^\@]+)$$/);
|
||||
|
||||
const isNonEmpty = (value: string): boolean => !!value && value.trim().length > 0;
|
||||
const isLikePhone = isNonEmpty;
|
||||
|
||||
const isAtLeast = (length: number, value: string): boolean =>
|
||||
!!value && value.trim().length >= length;
|
||||
|
||||
const isMimeOfImage = (mime): boolean => !!mime && IMAGE_MIME_TYPES.indexOf(mime) >= 0;
|
||||
|
||||
const isDate = (val: string): boolean => !!val && isValid(new Date(val));
|
||||
const isStringDate = (val: string): boolean => !!val && !!val.match(/^[\d]{2,4}\-[\d]{2}-[\d]{2}/);
|
||||
|
||||
export const VALIDATORS = {
|
||||
EMAIL: isValidEmail,
|
||||
LIKE_PHONE: isLikePhone,
|
||||
LIKE_EMAIL: isLikeEmail,
|
||||
NON_EMPTY: isNonEmpty,
|
||||
AT_LEAST: length => isAtLeast.bind(null, length),
|
||||
IS_IMAGE_MIME: isMimeOfImage,
|
||||
IS_DATE: isDate,
|
||||
IS_STRINGY_DATE: isStringDate,
|
||||
EVOLVE: (validator: (val: any) => boolean, error: string) => (val: any) =>
|
||||
!val || !validator(val) ? error : null,
|
||||
};
|
||||
export const isMimeOfImage = (mime): boolean => !!mime && IMAGE_MIME_TYPES.indexOf(mime) >= 0;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue