diff --git a/.prettierignore b/.prettierignore index 2318862..12df85d 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1 +1,2 @@ -*.css \ No newline at end of file +*.css +CHANGELOG.md \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a172248 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,6 @@ +### v1.1 (2021-09-24) +- Added pin icon directly to snippet card ([#4](https://github.com/pawelmalak/snippet-box/issues/4)) +- Fixed issue with copying snippets ([#6](https://github.com/pawelmalak/snippet-box/issues/6)) + +### v1.0 (2021-09-23) +Initial release \ No newline at end of file diff --git a/client/package-lock.json b/client/package-lock.json index 256e2ac..31db5ed 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -4277,6 +4277,11 @@ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" }, + "clipboard-copy": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clipboard-copy/-/clipboard-copy-4.0.1.tgz", + "integrity": "sha512-wOlqdqziE/NNTUJsfSgXmBMIrYmfd5V0HCGsR8uAKHcg+h9NENWINcfRjtWGU77wDHC8B8ijV4hMTGYbrKovng==" + }, "cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", diff --git a/client/package.json b/client/package.json index 2b0b096..6f158dc 100644 --- a/client/package.json +++ b/client/package.json @@ -15,6 +15,7 @@ "@types/react-dom": "^17.0.9", "@types/react-router-dom": "^5.3.0", "axios": "^0.21.4", + "clipboard-copy": "^4.0.1", "dayjs": "^1.10.7", "react": "^17.0.2", "react-dom": "^17.0.2", diff --git a/client/src/components/Snippets/SnippetCard.tsx b/client/src/components/Snippets/SnippetCard.tsx index 3e8c873..d880a30 100644 --- a/client/src/components/Snippets/SnippetCard.tsx +++ b/client/src/components/Snippets/SnippetCard.tsx @@ -4,8 +4,8 @@ import { Snippet } from '../../typescript/interfaces'; import { dateParser, badgeColor } from '../../utils'; import { Badge, Button, Card } from '../UI'; import { SnippetsContext } from '../../store'; -import Icon from '@mdi/react'; -import { mdiPin } from '@mdi/js'; +import copy from 'clipboard-copy'; +import { SnippetPin } from './SnippetPin'; interface Props { snippet: Snippet; @@ -17,7 +17,7 @@ export const SnippetCard = (props: Props): JSX.Element => { const { setSnippet } = useContext(SnippetsContext); const copyHandler = () => { - navigator.clipboard.writeText(code); + copy(code); }; return ( @@ -25,7 +25,7 @@ export const SnippetCard = (props: Props): JSX.Element => { {/* TITLE */}
{title} - {isPinned ? : ''} +
diff --git a/client/src/components/Snippets/SnippetDetails.tsx b/client/src/components/Snippets/SnippetDetails.tsx index 9729189..9de585b 100644 --- a/client/src/components/Snippets/SnippetDetails.tsx +++ b/client/src/components/Snippets/SnippetDetails.tsx @@ -1,11 +1,11 @@ import { useContext } from 'react'; -import { Link } from 'react-router-dom'; +import { useHistory } from 'react-router-dom'; import { SnippetsContext } from '../../store'; import { Snippet } from '../../typescript/interfaces'; import { dateParser } from '../../utils'; import { Button, Card } from '../UI'; -import Icon from '@mdi/react'; -import { mdiPin } from '@mdi/js'; +import copy from 'clipboard-copy'; +import { SnippetPin } from './SnippetPin'; interface Props { snippet: Snippet; @@ -23,21 +23,22 @@ export const SnippetDetails = (props: Props): JSX.Element => { isPinned } = props.snippet; - const { deleteSnippet, toggleSnippetPin, setSnippet } = - useContext(SnippetsContext); + const history = useHistory(); + + const { deleteSnippet, setSnippet } = useContext(SnippetsContext); const creationDate = dateParser(createdAt); const updateDate = dateParser(updatedAt); const copyHandler = () => { - navigator.clipboard.writeText(code); + copy(code); }; return (
{title} - {isPinned ? : ''} +

{description}

@@ -58,33 +59,22 @@ export const SnippetDetails = (props: Props): JSX.Element => { Last updated {updateDate.relative} -
{/* ACTIONS */} -
- -
- -
- - {/* COPY */} -
diff --git a/client/src/components/Snippets/SnippetPin.tsx b/client/src/components/Snippets/SnippetPin.tsx new file mode 100644 index 0000000..c61aa20 --- /dev/null +++ b/client/src/components/Snippets/SnippetPin.tsx @@ -0,0 +1,24 @@ +import { useContext } from 'react'; +import { SnippetsContext } from '../../store'; +import Icon from '@mdi/react'; +import { mdiPin, mdiPinOutline } from '@mdi/js'; + +interface Props { + id: number; + isPinned: boolean; +} + +export const SnippetPin = (props: Props): JSX.Element => { + const { toggleSnippetPin } = useContext(SnippetsContext); + const { id, isPinned } = props; + + return ( +
toggleSnippetPin(id)} className='cursor-pointer'> + {isPinned ? ( + + ) : ( + + )} +
+ ); +}; diff --git a/client/src/store/SnippetsContext.tsx b/client/src/store/SnippetsContext.tsx index 962c8ed..f73d7ee 100644 --- a/client/src/store/SnippetsContext.tsx +++ b/client/src/store/SnippetsContext.tsx @@ -17,7 +17,7 @@ export const SnippetsContext = createContext({ getSnippetById: (id: number) => {}, setSnippet: (id: number) => {}, createSnippet: (snippet: NewSnippet) => {}, - updateSnippet: (snippet: NewSnippet, id: number) => {}, + updateSnippet: (snippet: NewSnippet, id: number, isLocal?: boolean) => {}, deleteSnippet: (id: number) => {}, toggleSnippetPin: (id: number) => {}, countSnippets: () => {} @@ -81,7 +81,11 @@ export const SnippetsContextProvider = (props: Props): JSX.Element => { .catch(err => redirectOnError()); }; - const updateSnippet = (snippet: NewSnippet, id: number): void => { + const updateSnippet = ( + snippet: NewSnippet, + id: number, + isLocal?: boolean + ): void => { axios .put>(`/api/snippets/${id}`, snippet) .then(res => { @@ -92,10 +96,13 @@ export const SnippetsContextProvider = (props: Props): JSX.Element => { ...snippets.slice(oldSnippetIdx + 1) ]); setCurrentSnippet(res.data.data); - history.push({ - pathname: `/snippet/${res.data.data.id}`, - state: { from: '/snippets' } - }); + + if (!isLocal) { + history.push({ + pathname: `/snippet/${res.data.data.id}`, + state: { from: '/snippets' } + }); + } }) .catch(err => redirectOnError()); }; @@ -121,7 +128,7 @@ export const SnippetsContextProvider = (props: Props): JSX.Element => { const snippet = snippets.find(s => s.id === id); if (snippet) { - updateSnippet({ ...snippet, isPinned: !snippet.isPinned }, id); + updateSnippet({ ...snippet, isPinned: !snippet.isPinned }, id, true); } }; diff --git a/client/src/typescript/interfaces/Context.ts b/client/src/typescript/interfaces/Context.ts index 258d4e0..215674e 100644 --- a/client/src/typescript/interfaces/Context.ts +++ b/client/src/typescript/interfaces/Context.ts @@ -8,7 +8,7 @@ export interface Context { getSnippetById: (id: number) => void; setSnippet: (id: number) => void; createSnippet: (snippet: NewSnippet) => void; - updateSnippet: (snippet: NewSnippet, id: number) => void; + updateSnippet: (snippet: NewSnippet, id: number, isLocal?: boolean) => void; deleteSnippet: (id: number) => void; toggleSnippetPin: (id: number) => void; countSnippets: () => void;