Split SnippetsContext into separate files

This commit is contained in:
Paweł Malak
2021-10-21 14:02:35 +02:00
parent f55cbc73d8
commit 6fd696b440
17 changed files with 452 additions and 211 deletions

View File

@@ -1,178 +0,0 @@
import { useState, createContext, ReactNode } from 'react';
import { useHistory } from 'react-router-dom';
import axios from 'axios';
import {
SnippetsContext as Context,
Snippet,
Response,
TagCount,
NewSnippet,
SearchQuery
} from '../typescript/interfaces';
export const SnippetsContext = createContext<Context>({
snippets: [],
searchResults: [],
currentSnippet: null,
tagCount: [],
getSnippets: () => {},
getSnippetById: (id: number) => {},
setSnippet: (id: number) => {},
createSnippet: (snippet: NewSnippet) => {},
updateSnippet: (snippet: NewSnippet, id: number, isLocal?: boolean) => {},
deleteSnippet: (id: number) => {},
toggleSnippetPin: (id: number) => {},
countTags: () => {},
searchSnippets: (query: SearchQuery) => {}
});
interface Props {
children: ReactNode;
}
export const SnippetsContextProvider = (props: Props): JSX.Element => {
const [snippets, setSnippets] = useState<Snippet[]>([]);
const [searchResults, setSearchResults] = useState<Snippet[]>([]);
const [currentSnippet, setCurrentSnippet] = useState<Snippet | null>(null);
const [tagCount, setTagCount] = useState<TagCount[]>([]);
const history = useHistory();
const redirectOnError = () => {
history.push('/');
};
const getSnippets = (): void => {
axios
.get<Response<Snippet[]>>('/api/snippets')
.then(res => setSnippets(res.data.data))
.catch(err => redirectOnError());
};
const getSnippetById = (id: number): void => {
axios
.get<Response<Snippet>>(`/api/snippets/${id}`)
.then(res => setCurrentSnippet(res.data.data))
.catch(err => redirectOnError());
};
const setSnippet = (id: number): void => {
if (id < 0) {
setCurrentSnippet(null);
return;
}
getSnippetById(id);
const snippet = snippets.find(s => s.id === id);
if (snippet) {
setCurrentSnippet(snippet);
}
};
const createSnippet = (snippet: NewSnippet): void => {
axios
.post<Response<Snippet>>('/api/snippets', snippet)
.then(res => {
setSnippets([...snippets, res.data.data]);
setCurrentSnippet(res.data.data);
history.push({
pathname: `/snippet/${res.data.data.id}`,
state: { from: '/snippets' }
});
})
.catch(err => redirectOnError());
};
const updateSnippet = (
snippet: NewSnippet,
id: number,
isLocal?: boolean
): void => {
axios
.put<Response<Snippet>>(`/api/snippets/${id}`, snippet)
.then(res => {
const oldSnippetIdx = snippets.findIndex(s => s.id === id);
setSnippets([
...snippets.slice(0, oldSnippetIdx),
res.data.data,
...snippets.slice(oldSnippetIdx + 1)
]);
setCurrentSnippet(res.data.data);
if (!isLocal) {
history.push({
pathname: `/snippet/${res.data.data.id}`,
state: { from: '/snippets' }
});
}
})
.catch(err => redirectOnError());
};
const deleteSnippet = (id: number): void => {
if (window.confirm('Are you sure you want to delete this snippet?')) {
axios
.delete<Response<{}>>(`/api/snippets/${id}`)
.then(res => {
const deletedSnippetIdx = snippets.findIndex(s => s.id === id);
setSnippets([
...snippets.slice(0, deletedSnippetIdx),
...snippets.slice(deletedSnippetIdx + 1)
]);
setSnippet(-1);
history.push('/snippets');
})
.catch(err => redirectOnError());
}
};
const toggleSnippetPin = (id: number): void => {
const snippet = snippets.find(s => s.id === id);
if (snippet) {
updateSnippet({ ...snippet, isPinned: !snippet.isPinned }, id, true);
}
};
const countTags = (): void => {
axios
.get<Response<TagCount[]>>('/api/snippets/statistics/count')
.then(res => setTagCount(res.data.data))
.catch(err => redirectOnError());
};
const searchSnippets = (query: SearchQuery): void => {
axios
.post<Response<Snippet[]>>('/api/snippets/search', query)
.then(res => {
setSearchResults(res.data.data);
console.log(res.data.data);
})
.catch(err => console.log(err));
};
const context: Context = {
snippets,
searchResults,
currentSnippet,
tagCount,
getSnippets,
getSnippetById,
setSnippet,
createSnippet,
updateSnippet,
deleteSnippet,
toggleSnippetPin,
countTags,
searchSnippets
};
return (
<SnippetsContext.Provider value={context}>
{props.children}
</SnippetsContext.Provider>
);
};

View File

@@ -0,0 +1,33 @@
import { User, UserWithRole, Response } from '../../../typescript/interfaces';
import React from 'react';
import axios from 'axios';
import { authErrorHandler } from '../../../utils';
interface Params {
token: string;
setIsAuthenticated: (v: React.SetStateAction<boolean>) => void;
setUser: (v: React.SetStateAction<UserWithRole | null>) => void;
}
export const getUserProfile = async (params: Params) => {
const { token, setIsAuthenticated, setUser } = params;
try {
const res = await axios.get<Response<User>>('/api/auth/me', {
headers: {
Authorization: `Bearer ${token}`
}
});
const { ...user } = res.data.data;
setUser({
...user,
isAdmin: user.role === 'admin'
});
setIsAuthenticated(true);
} catch (err) {
authErrorHandler({ err, setIsAuthenticated, setUser });
}
};

View File

@@ -1,3 +1,4 @@
export * from './registerUser';
export * from './loginUser';
export * from './logoutUser';
export * from './getUserProfile';

View File

@@ -1,5 +1,5 @@
import { User, Response, UserWithRole } from '../../../typescript/interfaces';
import { errorHandler } from '../../../utils';
import { authErrorHandler } from '../../../utils';
import axios from 'axios';
import React from 'react';
@@ -31,9 +31,7 @@ export const loginUser = async (params: Params) => {
localStorage.setItem('token', resToken);
setIsAuthenticated(true);
// redirect to snippets? / home?
} catch (err) {
errorHandler(err);
authErrorHandler({ err, setIsAuthenticated, setUser });
}
};

View File

@@ -1,5 +1,5 @@
import { User, Response, UserWithRole } from '../../../typescript/interfaces';
import { errorHandler } from '../../../utils';
import { authErrorHandler } from '../../../utils';
import axios from 'axios';
import React from 'react';
@@ -32,6 +32,6 @@ export const registerUser = async (params: Params) => {
setIsAuthenticated(true);
} catch (err) {
errorHandler(err);
authErrorHandler({ err, setIsAuthenticated, setUser });
}
};

View File

@@ -1,24 +1,12 @@
import { createContext, ReactNode, useState } from 'react';
import axios from 'axios';
import { ReactNode, useState } from 'react';
import { AuthContext } from '..';
import { loginUser, logoutUser, registerUser } from './actions';
import { getUserProfile, loginUser, logoutUser, registerUser } from './actions';
import {
AuthContext as Context,
Response,
User,
UserWithRole
} from '../../typescript/interfaces';
import { errorHandler } from '../../utils';
export const AuthContext = createContext<Context>({
isAuthenticated: false,
user: null,
autoLogin: () => {},
login: () => {},
logout: () => {},
register: () => {}
});
interface Props {
children: ReactNode;
@@ -47,17 +35,7 @@ export const AuthContextProvider = (props: Props): JSX.Element => {
};
const getProfile = async (token: string) => {
try {
const res = await axios.get<Response<User>>('/api/auth/me', {
headers: {
Authorization: `Bearer ${token}`
}
});
console.log(res.data.data);
} catch (err) {
errorHandler(err);
}
await getUserProfile({ token, setIsAuthenticated, setUser });
};
const context: Context = {

View File

@@ -0,0 +1,33 @@
import { createContext } from 'react';
import {
SnippetsContext as SnippetsContextInterface,
AuthContext as AuthContextInterface,
NewSnippet,
SearchQuery
} from '../typescript/interfaces';
export const SnippetsContext = createContext<SnippetsContextInterface>({
snippets: [],
searchResults: [],
currentSnippet: null,
tagCount: [],
getSnippets: () => {},
getSnippetById: (id: number) => {},
setSnippet: (id: number) => {},
createSnippet: (snippet: NewSnippet) => {},
updateSnippet: (snippet: NewSnippet, id: number, isLocal?: boolean) => {},
deleteSnippet: (id: number) => {},
toggleSnippetPin: (id: number) => {},
countTags: () => {},
searchSnippets: (query: SearchQuery) => {}
});
export const AuthContext = createContext<AuthContextInterface>({
isAuthenticated: false,
user: null,
autoLogin: () => {},
login: () => {},
logout: () => {},
register: () => {}
});

View File

@@ -1,2 +1,3 @@
export * from './SnippetsContext';
export * from './snippets';
export * from './auth';
export * from './contexts';

View File

@@ -0,0 +1,29 @@
import { SetStateAction } from 'react';
import { errorHandler } from '../../../utils';
import { Response, TagCount } from '../../../typescript/interfaces';
import axios from 'axios';
interface Params {
setTagCount: (v: SetStateAction<TagCount[]>) => void;
}
export const countTagsAction = async (params: Params) => {
const { setTagCount } = params;
const token = `Bearer ${localStorage.token}`;
try {
const res = await axios.get<Response<TagCount[]>>(
'/api/snippets/statistics/count',
{
headers: {
Authorization: token
}
}
);
setTagCount(res.data.data);
} catch (err) {
errorHandler(err);
}
};

View File

@@ -0,0 +1,34 @@
import { SetStateAction } from 'react';
import { errorHandler } from '../../../utils';
import { NewSnippet, Response, Snippet } from '../../../typescript/interfaces';
import axios from 'axios';
interface Params {
snippet: NewSnippet;
snippets: Snippet[];
setSnippets: (v: SetStateAction<Snippet[]>) => void;
setCurrentSnippet: (v: SetStateAction<Snippet | null>) => void;
}
export const createSnippetAction = async (
params: Params
): Promise<number | undefined> => {
const { snippet, snippets, setSnippets, setCurrentSnippet } = params;
const token = `Bearer ${localStorage.token}`;
try {
const res = await axios.post<Response<Snippet>>('/api/snippets', snippet, {
headers: {
Authorization: token
}
});
setSnippets([...snippets, res.data.data]);
setCurrentSnippet(res.data.data);
return res.data.data.id;
} catch (err) {
errorHandler(err);
}
};

View File

@@ -0,0 +1,38 @@
import { SetStateAction } from 'react';
import { errorHandler } from '../../../utils';
import { Response, Snippet } from '../../../typescript/interfaces';
import axios from 'axios';
interface Params {
id: number;
snippets: Snippet[];
setSnippets: (v: SetStateAction<Snippet[]>) => void;
setSnippet: (id: number) => void;
}
export const deleteSnippetAction = async (params: Params) => {
const { id, snippets, setSnippets, setSnippet } = params;
const token = `Bearer ${localStorage.token}`;
if (window.confirm('Are you sure you want to delete this snippet?')) {
try {
await axios.delete<Response<{}>>(`/api/snippets/${id}`, {
headers: {
Authorization: token
}
});
const deletedSnippetIdx = snippets.findIndex(s => s.id === id);
setSnippets([
...snippets.slice(0, deletedSnippetIdx),
...snippets.slice(deletedSnippetIdx + 1)
]);
setSnippet(-1);
} catch (err) {
errorHandler(err);
}
}
};

View File

@@ -0,0 +1,27 @@
import { SetStateAction } from 'react';
import { errorHandler } from '../../../utils';
import { Response, Snippet } from '../../../typescript/interfaces';
import axios from 'axios';
interface Params {
id: number;
setCurrentSnippet: (v: SetStateAction<Snippet | null>) => void;
}
export const getSnippetByIdAction = async (params: Params) => {
const { id, setCurrentSnippet } = params;
const token = `Bearer ${localStorage.token}`;
try {
const res = await axios.get<Response<Snippet>>(`/api/snippets/${id}`, {
headers: {
Authorization: token
}
});
setCurrentSnippet(res.data.data);
} catch (err) {
errorHandler(err);
}
};

View File

@@ -0,0 +1,26 @@
import { SetStateAction } from 'react';
import { errorHandler } from '../../../utils';
import { Response, Snippet } from '../../../typescript/interfaces';
import axios from 'axios';
interface Params {
setSnippets: (v: SetStateAction<Snippet[]>) => void;
}
export const getSnippetsAction = async (params: Params) => {
const { setSnippets } = params;
const token = `Bearer ${localStorage.token}`;
try {
const res = await axios.get<Response<Snippet[]>>('/api/snippets', {
headers: {
Authorization: token
}
});
setSnippets(res.data.data);
} catch (err) {
errorHandler(err);
}
};

View File

@@ -0,0 +1,7 @@
export * from './getSnippet';
export * from './createSnippet';
export * from './countTags';
export * from './searchSnippets';
export * from './updateSnippet';
export * from './getSnippets';
export * from './deleteSnippet';

View File

@@ -0,0 +1,31 @@
import { SetStateAction } from 'react';
import { errorHandler } from '../../../utils';
import { SearchQuery, Response, Snippet } from '../../../typescript/interfaces';
import axios from 'axios';
interface Params {
query: SearchQuery;
setSearchResults: (v: SetStateAction<Snippet[]>) => void;
}
export const searchSnippetsAction = async (params: Params) => {
const { query, setSearchResults } = params;
const token = `Bearer ${localStorage.token}`;
try {
const res = await axios.post<Response<Snippet[]>>(
'/api/snippets/search',
query,
{
headers: {
Authorization: `Bearer ${localStorage.token}`
}
}
);
setSearchResults(res.data.data);
} catch (err) {
errorHandler(err);
}
};

View File

@@ -0,0 +1,42 @@
import { SetStateAction } from 'react';
import { errorHandler } from '../../../utils';
import { Response, Snippet, NewSnippet } from '../../../typescript/interfaces';
import axios from 'axios';
interface Params {
id: number;
snippet: NewSnippet;
snippets: Snippet[];
setSnippets: (v: SetStateAction<Snippet[]>) => void;
setCurrentSnippet: (v: SetStateAction<Snippet | null>) => void;
}
export const updateSnippetAction = async (params: Params) => {
const { id, snippet, snippets, setSnippets, setCurrentSnippet } = params;
const token = `Bearer ${localStorage.token}`;
try {
const res = await axios.put<Response<Snippet>>(
`/api/snippets/${id}`,
snippet,
{
headers: {
Authorization: token
}
}
);
const oldSnippetIdx = snippets.findIndex(s => s.id === id);
setSnippets([
...snippets.slice(0, oldSnippetIdx),
res.data.data,
...snippets.slice(oldSnippetIdx + 1)
]);
setCurrentSnippet(res.data.data);
} catch (err) {
errorHandler(err);
}
};

View File

@@ -0,0 +1,141 @@
import { useState, ReactNode } from 'react';
import { useHistory } from 'react-router-dom';
import { SnippetsContext } from '..';
import {
countTagsAction,
createSnippetAction,
deleteSnippetAction,
getSnippetByIdAction,
getSnippetsAction,
searchSnippetsAction,
updateSnippetAction
} from './actions';
import {
SnippetsContext as Context,
Snippet,
TagCount,
NewSnippet,
SearchQuery
} from '../../typescript/interfaces';
interface Props {
children: ReactNode;
}
export const SnippetsContextProvider = (props: Props): JSX.Element => {
const [snippets, setSnippets] = useState<Snippet[]>([]);
const [searchResults, setSearchResults] = useState<Snippet[]>([]);
const [currentSnippet, setCurrentSnippet] = useState<Snippet | null>(null);
const [tagCount, setTagCount] = useState<TagCount[]>([]);
const history = useHistory();
const getSnippets = async () => {
await getSnippetsAction({ setSnippets });
};
const getSnippetById = async (id: number) => {
await getSnippetByIdAction({ id, setCurrentSnippet });
};
const setSnippet = async (id: number) => {
if (id < 0) {
setCurrentSnippet(null);
return;
}
await getSnippetById(id);
const snippet = snippets.find(s => s.id === id);
if (snippet) {
setCurrentSnippet(snippet);
}
};
const createSnippet = async (snippet: NewSnippet) => {
const id = await createSnippetAction({
snippet,
snippets,
setCurrentSnippet,
setSnippets
});
if (id) {
history.push({
pathname: `/snippet/${id}`,
state: { from: '/snippets' }
});
}
};
const updateSnippet = async (
snippet: NewSnippet,
id: number,
isLocal?: boolean
) => {
await updateSnippetAction({
id,
snippet,
snippets,
setSnippets,
setCurrentSnippet
});
if (!isLocal) {
history.push({
pathname: `/snippet/${id}`,
state: { from: '/snippets' }
});
}
};
const deleteSnippet = async (id: number) => {
await deleteSnippetAction({ id, snippets, setSnippets, setSnippet });
history.push('/snippets');
};
const countTags = async () => {
await countTagsAction({ setTagCount });
};
const searchSnippets = async (query: SearchQuery) => {
await searchSnippetsAction({ query, setSearchResults });
};
const toggleSnippetPin = async (id: number) => {
const snippet = snippets.find(s => s.id === id);
if (snippet) {
await updateSnippet(
{ ...snippet, isPinned: !snippet.isPinned },
id,
true
);
}
};
const context: Context = {
snippets,
searchResults,
currentSnippet,
tagCount,
getSnippets,
getSnippetById,
setSnippet,
createSnippet,
updateSnippet,
deleteSnippet,
toggleSnippetPin,
countTags,
searchSnippets
};
return (
<SnippetsContext.Provider value={context}>
{props.children}
</SnippetsContext.Provider>
);
};