Skip to content

Commit e4e21e4

Browse files
committed
Updating dependencies and using functional components to finish the app.
1 parent ae26155 commit e4e21e4

15 files changed

+30510
-44
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
node_modules
22
.DS_STORE
3+
keys.js

babel.config.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = {
2+
presets: ["@babel/preset-env", "@babel/preset-react"],
3+
};

client/components/App.js

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import React from 'react';
2+
3+
export default (props) => {
4+
return (
5+
<div className="container">
6+
{/* pass any props to the child components */}
7+
{React.Children.map(props.children, child => {
8+
return React.cloneElement(child, { ...props })
9+
})}
10+
</div>
11+
)
12+
}

client/components/LyricCreate.js

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import React, { useState } from 'react';
2+
3+
import { useMutation } from "@apollo/client";
4+
import { withRouter } from 'react-router-dom';
5+
import { ADD_LYRIC_TO_SONG } from '../queries/apolloQueries';
6+
7+
const LyricCreate = ({songId, history}) => {
8+
9+
const [content, setContent] = useState('');
10+
const [ addLyricToSongMutation] = useMutation(ADD_LYRIC_TO_SONG);
11+
12+
const onSubmit = (event) => {
13+
event.preventDefault();
14+
15+
addLyricToSongMutation({
16+
variables: {
17+
songId,
18+
content
19+
}
20+
}).then(()=> setContent(''))
21+
.then(() => history.push('/'));
22+
}
23+
24+
return (
25+
<form onSubmit={onSubmit}>
26+
<label>Add a Lyric</label>
27+
<input
28+
value={content}
29+
onChange={event => setContent(event.target.value)}
30+
/>
31+
</form>
32+
);
33+
};
34+
35+
export default LyricCreate;

client/components/LyricList.js

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import React from 'react';
2+
3+
import { useMutation } from "@apollo/client";
4+
import { LIKE_LYRIC, LIKE_LYRIC_OPTIMISTIC_RESPONSE } from '../queries/apolloQueries';
5+
6+
const LyricList = ({lyrics}) => {
7+
8+
const [ likeLyricMutation ] = useMutation(LIKE_LYRIC);
9+
10+
11+
const onLike = (id, likes) => {
12+
likeLyricMutation({
13+
variables: { id },
14+
optimisticResponse: {
15+
__typename: 'Mutation',
16+
likeLyric: {
17+
id,
18+
__typename: 'LyricType',
19+
likes: likes + 1
20+
}
21+
}
22+
// refetchQueries: [ { query: FETCH_SONGS }]
23+
});
24+
};
25+
26+
return (
27+
<ul className="collection">
28+
{lyrics.map(({ id, content, likes })=> (
29+
<li key={id} className="collection-item">
30+
{content}
31+
<div className="vote-box">
32+
{ likes }
33+
<i className="material-icons"
34+
onClick={() => onLike(id, likes)}
35+
>thumb_up</i>
36+
</div>
37+
</li>
38+
))}
39+
</ul>
40+
);
41+
}
42+
43+
export default LyricList;

client/components/SongCreate.js

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React, { useState } from 'react';
2+
3+
import { useMutation } from "@apollo/client";
4+
import { withRouter, Link } from 'react-router-dom';
5+
import { ADD_SONG, FETCH_SONGS} from '../queries/apolloQueries';
6+
7+
const SongCreate = ({history}) => {
8+
const [songTitle, setSongTitle] = useState('');
9+
const [ addSongMutation] = useMutation(ADD_SONG);
10+
11+
const onSubmit = (e) => {
12+
e.preventDefault();
13+
14+
addSongMutation({
15+
variables: { title: songTitle },
16+
refetchQueries: [ { query: FETCH_SONGS }]
17+
}).then(() => {
18+
history.push('/');
19+
});
20+
}
21+
return (
22+
<div>
23+
<Link to="/">Back</Link>
24+
<h3>Create a New Song</h3>
25+
<form onSubmit={onSubmit}>
26+
<label>Song Title</label>
27+
<input
28+
onChange={e => setSongTitle(e.target.value)}
29+
value={songTitle}
30+
/>
31+
</form>
32+
</div>
33+
);
34+
}
35+
36+
export default withRouter(SongCreate);

client/components/SongDetail.js

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import React from 'react';
2+
import { Link } from 'react-router-dom';
3+
import { withRouter } from 'react-router';
4+
import { useParams } from 'react-router-dom';
5+
6+
import { GET_SONG } from '../queries/apolloQueries';
7+
import { useQuery } from "@apollo/client";
8+
import LyricCreate from './LyricCreate';
9+
import LyricList from './LyricList';
10+
11+
const SongDetail = ({history}) => {
12+
13+
let { id } = useParams();
14+
const { data, loading, error } = useQuery(GET_SONG, {
15+
variables: { id }
16+
});
17+
18+
if (loading) return <p>Loading...</p>;
19+
if (error) return <p>ERROR: {error.message}</p>;
20+
if (!data?.song) return <p>Not found</p>;
21+
22+
const { title, lyrics } = data.song;
23+
24+
console.dir(data.song);
25+
26+
return (<div>
27+
<Link to="/">Back</Link>
28+
<h3>{title}</h3>
29+
<LyricList lyrics={lyrics} />
30+
<LyricCreate songId={id} history={history} />
31+
</div>);
32+
}
33+
34+
export default withRouter(SongDetail);

client/components/SongList.js

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import React from 'react';
2+
import { DELETE_SONG, FETCH_SONGS } from '../queries/apolloQueries';
3+
import { Link } from 'react-router-dom';
4+
import { useQuery, useMutation } from "@apollo/client";
5+
6+
const SongList = () => {
7+
const { data, loading, error } = useQuery(FETCH_SONGS);
8+
const [ deleteSongMutation] = useMutation(DELETE_SONG);
9+
10+
if (loading) return <p>Loading...</p>;
11+
if (error) return <p>ERROR: {error.message}</p>;
12+
if (!data) return <p>Not found</p>;
13+
14+
const deleteSong = (id) => {
15+
deleteSongMutation({
16+
variables: { id },
17+
refetchQueries: [ { query: FETCH_SONGS }]
18+
});
19+
}
20+
21+
return (
22+
<div>
23+
<ul className="collection">
24+
{data && data.songs && data.songs.map(({id, title}) => (
25+
<li key={id} className="collection-item">
26+
<Link to={`/songs/${id}`}>
27+
{title}
28+
</Link>
29+
<i
30+
className="material-icons"
31+
onClick={() => deleteSong(id)}
32+
>delete</i>
33+
</li>
34+
))}
35+
</ul>
36+
<Link
37+
to="/songs/new"
38+
className="btn-floating btn-large red right">
39+
<i className="material-icons">add</i>
40+
</Link>
41+
</div>
42+
)
43+
44+
}
45+
46+
export default SongList;

client/index.js

+38-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,47 @@
1+
import './style/style.css';
12
import React from 'react';
3+
import { HashRouter, Route, Switch } from 'react-router-dom';
24
import ReactDOM from 'react-dom';
5+
import { ApolloClient, ApolloProvider } from "@apollo/client";
6+
import { InMemoryCache } from 'apollo-cache-inmemory';
7+
import SongList from './components/SongList';
8+
import SongCreate from './components/SongCreate';
9+
import { createHttpLink } from "apollo-link-http";
10+
import App from './components/App';
11+
import SongDetail from './components/SongDetail';
12+
13+
const cache = new InMemoryCache();
14+
15+
const httpLink = createHttpLink({
16+
uri: "http://localhost:4000/graphql"
17+
});
18+
19+
const client = new ApolloClient({
20+
link: httpLink,
21+
cache
22+
});
323

424
const Root = () => {
5-
return <div>Lyrical</div>
25+
return (
26+
<ApolloProvider client={client}>
27+
<HashRouter>
28+
<Switch>
29+
<Route exact path="/">
30+
<App children={<SongList />}></App>
31+
</Route>
32+
<Route exact path="/songs/new">
33+
<App children={<SongCreate />}></App>
34+
</Route>
35+
<Route path="/songs/:id">
36+
<App children={<SongDetail />}></App>
37+
</Route>
38+
</Switch>
39+
</HashRouter>
40+
</ApolloProvider>
41+
)
642
};
743

844
ReactDOM.render(
945
<Root />,
1046
document.querySelector('#root')
11-
);
47+
);

client/queries/apolloQueries.js

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { gql } from "@apollo/client";
2+
3+
export const GET_SONG = gql`
4+
query SongQuery($id: ID!) {
5+
song(id: $id) {
6+
id
7+
title
8+
lyrics {
9+
id
10+
content
11+
likes
12+
}
13+
}
14+
}
15+
`;
16+
17+
export const ADD_SONG = gql`
18+
mutation AddSong($title: String) {
19+
addSong(title: $title) {
20+
id
21+
title
22+
}
23+
}
24+
`;
25+
26+
export const FETCH_SONGS = gql`
27+
{
28+
songs {
29+
id
30+
title
31+
}
32+
}
33+
`;
34+
35+
export const DELETE_SONG = gql`
36+
mutation DeleteSong($id: ID) {
37+
deleteSong(id: $id) {
38+
id
39+
}
40+
}
41+
`;
42+
43+
export const ADD_LYRIC_TO_SONG = gql`
44+
mutation AddLyricToSong($content: String, $songId: ID) {
45+
addLyricToSong(content: $content, songId: $songId) {
46+
id
47+
lyrics {
48+
content
49+
}
50+
}
51+
}
52+
`;
53+
54+
export const LIKE_LYRIC = gql`
55+
mutation LikeLyric($id: ID) {
56+
likeLyric(id: $id) {
57+
id
58+
likes
59+
}
60+
}
61+
`;

client/style/style.css

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
.collection-item {
2+
display: flex;
3+
justify-content: space-between;
4+
}
5+
6+
.material-icons {
7+
cursor: pointer;
8+
margin-left: 5px;
9+
}
10+
11+
.vote-box {
12+
display: flex;
13+
align-items: flex-end;
14+
}

0 commit comments

Comments
 (0)