Skip to content

Commit 8c603dc

Browse files
authored
Merge pull request #9 from pawelmalak/clipboard-fix
Clipboard fix & pin icon
2 parents 7df5c8d + 390705c commit 8c603dc

File tree

9 files changed

+74
-46
lines changed

9 files changed

+74
-46
lines changed

.prettierignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
*.css
1+
*.css
2+
CHANGELOG.md

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
### v1.1 (2021-09-24)
2+
- Added pin icon directly to snippet card ([#4](https://github.com/pawelmalak/snippet-box/issues/4))
3+
- Fixed issue with copying snippets ([#6](https://github.com/pawelmalak/snippet-box/issues/6))
4+
5+
### v1.0 (2021-09-23)
6+
Initial release

client/package-lock.json

+5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"@types/react-dom": "^17.0.9",
1616
"@types/react-router-dom": "^5.3.0",
1717
"axios": "^0.21.4",
18+
"clipboard-copy": "^4.0.1",
1819
"dayjs": "^1.10.7",
1920
"react": "^17.0.2",
2021
"react-dom": "^17.0.2",

client/src/components/Snippets/SnippetCard.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import { Snippet } from '../../typescript/interfaces';
44
import { dateParser, badgeColor } from '../../utils';
55
import { Badge, Button, Card } from '../UI';
66
import { SnippetsContext } from '../../store';
7-
import Icon from '@mdi/react';
8-
import { mdiPin } from '@mdi/js';
7+
import copy from 'clipboard-copy';
8+
import { SnippetPin } from './SnippetPin';
99

1010
interface Props {
1111
snippet: Snippet;
@@ -17,15 +17,15 @@ export const SnippetCard = (props: Props): JSX.Element => {
1717
const { setSnippet } = useContext(SnippetsContext);
1818

1919
const copyHandler = () => {
20-
navigator.clipboard.writeText(code);
20+
copy(code);
2121
};
2222

2323
return (
2424
<Card classes='h-100' bodyClasses='d-flex flex-column'>
2525
{/* TITLE */}
2626
<h5 className='card-title d-flex align-items-center justify-content-between'>
2727
{title}
28-
{isPinned ? <Icon path={mdiPin} size={0.8} color='#212529' /> : ''}
28+
<SnippetPin id={id} isPinned={isPinned} />
2929
</h5>
3030

3131
<h6 className='card-subtitle mb-2 text-muted'>

client/src/components/Snippets/SnippetDetails.tsx

+17-33
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { useContext } from 'react';
2-
import { Link } from 'react-router-dom';
2+
import { useHistory } from 'react-router-dom';
33
import { SnippetsContext } from '../../store';
44
import { Snippet } from '../../typescript/interfaces';
55
import { dateParser } from '../../utils';
66
import { Button, Card } from '../UI';
7-
import Icon from '@mdi/react';
8-
import { mdiPin } from '@mdi/js';
7+
import copy from 'clipboard-copy';
8+
import { SnippetPin } from './SnippetPin';
99

1010
interface Props {
1111
snippet: Snippet;
@@ -23,21 +23,22 @@ export const SnippetDetails = (props: Props): JSX.Element => {
2323
isPinned
2424
} = props.snippet;
2525

26-
const { deleteSnippet, toggleSnippetPin, setSnippet } =
27-
useContext(SnippetsContext);
26+
const history = useHistory();
27+
28+
const { deleteSnippet, setSnippet } = useContext(SnippetsContext);
2829

2930
const creationDate = dateParser(createdAt);
3031
const updateDate = dateParser(updatedAt);
3132

3233
const copyHandler = () => {
33-
navigator.clipboard.writeText(code);
34+
copy(code);
3435
};
3536

3637
return (
3738
<Card>
3839
<h5 className='card-title d-flex align-items-center justify-content-between'>
3940
{title}
40-
{isPinned ? <Icon path={mdiPin} size={0.8} color='#212529' /> : ''}
41+
<SnippetPin id={id} isPinned={isPinned} />
4142
</h5>
4243
<p>{description}</p>
4344

@@ -58,33 +59,22 @@ export const SnippetDetails = (props: Props): JSX.Element => {
5859
<span>Last updated</span>
5960
<span>{updateDate.relative}</span>
6061
</div>
61-
6262
<hr />
6363

6464
{/* ACTIONS */}
65-
<div className='d-flex justify-content-between'>
66-
<Link
67-
to={{
68-
pathname: `/editor/${id}`,
69-
state: { from: window.location.pathname }
70-
}}
71-
>
72-
<Button
73-
text='Edit'
74-
color='dark'
75-
small
76-
outline
77-
classes='me-3'
78-
handler={() => setSnippet(id)}
79-
/>
80-
</Link>
65+
<div className='d-grid g-2' style={{ rowGap: '10px' }}>
8166
<Button
82-
text={`${isPinned ? 'Unpin snippet' : 'Pin snippet'}`}
67+
text='Edit'
8368
color='dark'
8469
small
8570
outline
86-
handler={() => toggleSnippetPin(id)}
87-
classes='me-3'
71+
handler={() => {
72+
setSnippet(id);
73+
history.push({
74+
pathname: `/editor/${id}`,
75+
state: { from: window.location.pathname }
76+
});
77+
}}
8878
/>
8979
<Button
9080
text='Delete'
@@ -93,12 +83,6 @@ export const SnippetDetails = (props: Props): JSX.Element => {
9383
outline
9484
handler={() => deleteSnippet(id)}
9585
/>
96-
</div>
97-
98-
<hr />
99-
100-
{/* COPY */}
101-
<div className='d-grid'>
10286
<Button text='Copy code' color='dark' small handler={copyHandler} />
10387
</div>
10488
</Card>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { useContext } from 'react';
2+
import { SnippetsContext } from '../../store';
3+
import Icon from '@mdi/react';
4+
import { mdiPin, mdiPinOutline } from '@mdi/js';
5+
6+
interface Props {
7+
id: number;
8+
isPinned: boolean;
9+
}
10+
11+
export const SnippetPin = (props: Props): JSX.Element => {
12+
const { toggleSnippetPin } = useContext(SnippetsContext);
13+
const { id, isPinned } = props;
14+
15+
return (
16+
<div onClick={() => toggleSnippetPin(id)} className='cursor-pointer'>
17+
{isPinned ? (
18+
<Icon path={mdiPin} size={0.8} color='#212529' />
19+
) : (
20+
<Icon path={mdiPinOutline} size={0.8} color='#9a9a9a' />
21+
)}
22+
</div>
23+
);
24+
};

client/src/store/SnippetsContext.tsx

+14-7
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export const SnippetsContext = createContext<Context>({
1717
getSnippetById: (id: number) => {},
1818
setSnippet: (id: number) => {},
1919
createSnippet: (snippet: NewSnippet) => {},
20-
updateSnippet: (snippet: NewSnippet, id: number) => {},
20+
updateSnippet: (snippet: NewSnippet, id: number, isLocal?: boolean) => {},
2121
deleteSnippet: (id: number) => {},
2222
toggleSnippetPin: (id: number) => {},
2323
countSnippets: () => {}
@@ -81,7 +81,11 @@ export const SnippetsContextProvider = (props: Props): JSX.Element => {
8181
.catch(err => redirectOnError());
8282
};
8383

84-
const updateSnippet = (snippet: NewSnippet, id: number): void => {
84+
const updateSnippet = (
85+
snippet: NewSnippet,
86+
id: number,
87+
isLocal?: boolean
88+
): void => {
8589
axios
8690
.put<Response<Snippet>>(`/api/snippets/${id}`, snippet)
8791
.then(res => {
@@ -92,10 +96,13 @@ export const SnippetsContextProvider = (props: Props): JSX.Element => {
9296
...snippets.slice(oldSnippetIdx + 1)
9397
]);
9498
setCurrentSnippet(res.data.data);
95-
history.push({
96-
pathname: `/snippet/${res.data.data.id}`,
97-
state: { from: '/snippets' }
98-
});
99+
100+
if (!isLocal) {
101+
history.push({
102+
pathname: `/snippet/${res.data.data.id}`,
103+
state: { from: '/snippets' }
104+
});
105+
}
99106
})
100107
.catch(err => redirectOnError());
101108
};
@@ -121,7 +128,7 @@ export const SnippetsContextProvider = (props: Props): JSX.Element => {
121128
const snippet = snippets.find(s => s.id === id);
122129

123130
if (snippet) {
124-
updateSnippet({ ...snippet, isPinned: !snippet.isPinned }, id);
131+
updateSnippet({ ...snippet, isPinned: !snippet.isPinned }, id, true);
125132
}
126133
};
127134

client/src/typescript/interfaces/Context.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export interface Context {
88
getSnippetById: (id: number) => void;
99
setSnippet: (id: number) => void;
1010
createSnippet: (snippet: NewSnippet) => void;
11-
updateSnippet: (snippet: NewSnippet, id: number) => void;
11+
updateSnippet: (snippet: NewSnippet, id: number, isLocal?: boolean) => void;
1212
deleteSnippet: (id: number) => void;
1313
toggleSnippetPin: (id: number) => void;
1414
countSnippets: () => void;

0 commit comments

Comments
 (0)