mirror of
https://github.com/pawelmalak/snippet-box.git
synced 2025-12-21 13:23:05 +01:00
Split SnippetsContext into separate files
This commit is contained in:
@@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
33
client/src/store/auth/actions/getUserProfile.ts
Normal file
33
client/src/store/auth/actions/getUserProfile.ts
Normal 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 });
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
export * from './registerUser';
|
export * from './registerUser';
|
||||||
export * from './loginUser';
|
export * from './loginUser';
|
||||||
export * from './logoutUser';
|
export * from './logoutUser';
|
||||||
|
export * from './getUserProfile';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { User, Response, UserWithRole } from '../../../typescript/interfaces';
|
import { User, Response, UserWithRole } from '../../../typescript/interfaces';
|
||||||
import { errorHandler } from '../../../utils';
|
import { authErrorHandler } from '../../../utils';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
@@ -31,9 +31,7 @@ export const loginUser = async (params: Params) => {
|
|||||||
localStorage.setItem('token', resToken);
|
localStorage.setItem('token', resToken);
|
||||||
|
|
||||||
setIsAuthenticated(true);
|
setIsAuthenticated(true);
|
||||||
|
|
||||||
// redirect to snippets? / home?
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
errorHandler(err);
|
authErrorHandler({ err, setIsAuthenticated, setUser });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { User, Response, UserWithRole } from '../../../typescript/interfaces';
|
import { User, Response, UserWithRole } from '../../../typescript/interfaces';
|
||||||
import { errorHandler } from '../../../utils';
|
import { authErrorHandler } from '../../../utils';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
@@ -32,6 +32,6 @@ export const registerUser = async (params: Params) => {
|
|||||||
|
|
||||||
setIsAuthenticated(true);
|
setIsAuthenticated(true);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
errorHandler(err);
|
authErrorHandler({ err, setIsAuthenticated, setUser });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,24 +1,12 @@
|
|||||||
import { createContext, ReactNode, useState } from 'react';
|
import { ReactNode, useState } from 'react';
|
||||||
import axios from 'axios';
|
import { AuthContext } from '..';
|
||||||
|
|
||||||
import { loginUser, logoutUser, registerUser } from './actions';
|
import { getUserProfile, loginUser, logoutUser, registerUser } from './actions';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AuthContext as Context,
|
AuthContext as Context,
|
||||||
Response,
|
|
||||||
User,
|
|
||||||
UserWithRole
|
UserWithRole
|
||||||
} from '../../typescript/interfaces';
|
} from '../../typescript/interfaces';
|
||||||
import { errorHandler } from '../../utils';
|
|
||||||
|
|
||||||
export const AuthContext = createContext<Context>({
|
|
||||||
isAuthenticated: false,
|
|
||||||
user: null,
|
|
||||||
autoLogin: () => {},
|
|
||||||
login: () => {},
|
|
||||||
logout: () => {},
|
|
||||||
register: () => {}
|
|
||||||
});
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
@@ -47,17 +35,7 @@ export const AuthContextProvider = (props: Props): JSX.Element => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getProfile = async (token: string) => {
|
const getProfile = async (token: string) => {
|
||||||
try {
|
await getUserProfile({ token, setIsAuthenticated, setUser });
|
||||||
const res = await axios.get<Response<User>>('/api/auth/me', {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(res.data.data);
|
|
||||||
} catch (err) {
|
|
||||||
errorHandler(err);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const context: Context = {
|
const context: Context = {
|
||||||
|
|||||||
33
client/src/store/contexts.ts
Normal file
33
client/src/store/contexts.ts
Normal 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: () => {}
|
||||||
|
});
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
export * from './SnippetsContext';
|
export * from './snippets';
|
||||||
export * from './auth';
|
export * from './auth';
|
||||||
|
export * from './contexts';
|
||||||
|
|||||||
29
client/src/store/snippets/actions/countTags.ts
Normal file
29
client/src/store/snippets/actions/countTags.ts
Normal 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
34
client/src/store/snippets/actions/createSnippet.ts
Normal file
34
client/src/store/snippets/actions/createSnippet.ts
Normal 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
38
client/src/store/snippets/actions/deleteSnippet.ts
Normal file
38
client/src/store/snippets/actions/deleteSnippet.ts
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
27
client/src/store/snippets/actions/getSnippet.ts
Normal file
27
client/src/store/snippets/actions/getSnippet.ts
Normal 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
26
client/src/store/snippets/actions/getSnippets.ts
Normal file
26
client/src/store/snippets/actions/getSnippets.ts
Normal 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
7
client/src/store/snippets/actions/index.ts
Normal file
7
client/src/store/snippets/actions/index.ts
Normal 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';
|
||||||
31
client/src/store/snippets/actions/searchSnippets.ts
Normal file
31
client/src/store/snippets/actions/searchSnippets.ts
Normal 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
42
client/src/store/snippets/actions/updateSnippet.ts
Normal file
42
client/src/store/snippets/actions/updateSnippet.ts
Normal 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
141
client/src/store/snippets/index.tsx
Normal file
141
client/src/store/snippets/index.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user