Add react-apollo and make UI work via GraphQL.

This commit is contained in:
James Harton 2018-04-08 14:30:36 +12:00
parent ee94428bec
commit 30e9b1c82b
10 changed files with 295 additions and 7 deletions

View file

@ -22,15 +22,20 @@ import "phoenix_html";
import React from "react"; import React from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import { ApolloProvider } from 'react-apollo';
import Header from "./components/Header"; import Header from "./components/Header";
import Gallery from "./components/Gallery";
import client from "./client";
class App extends React.Component { class App extends React.Component {
render() { render() {
return ( return (
<div class="container"> <ApolloProvider client={client}>
<Header /> <div className="container">
</div> <Header />
<Gallery />
</div>
</ApolloProvider>
); );
} }
} }

12
assets/js/client.js Normal file
View file

@ -0,0 +1,12 @@
import { ApolloClient } from 'apollo-client';
import { HttpLink } from 'apollo-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
const client = new ApolloClient({
link: new HttpLink({
uri: "/api"
}),
cache: new InMemoryCache(),
});
export default client;

View file

@ -0,0 +1,65 @@
import React from "react";
import gql from "graphql-tag";
import { Mutation } from "react-apollo";
import IMPORT_PERSON from "../queries/import_person";
import LIST_PEOPLE from "../queries/list_people";
class AddFace extends React.Component {
// I extracted the cache updating logic from the JSX because there were too
// many nested braces and it was making me confused.
cacheUpdater(cache, { data: { importPerson } }) {
let { people } = cache.readQuery({ query: LIST_PEOPLE });
// If the person already exists in the cache then replace them with the new data.
let existingPersonIndex = people.findIndex(person => person.username === importPerson.username);
if (existingPersonIndex >= 0) {
people.splice(existingPersonIndex, 1, importPerson);
}
// Otherwise add them to the cache.
else {
people = people.concat([importPerson]);
}
cache.writeQuery({
query: LIST_PEOPLE,
data: { people: people }
});
}
render() {
let input;
return (
<Mutation
mutation={IMPORT_PERSON}
update={this.cacheUpdater}>
{(importPerson, { data }) => (
<div className="col-sm-3 mb-3">
<div className="card">
<div className="card-header">
<h5 className="card-title">Add Face</h5>
</div>
<div className="card-body">
<form onSubmit={e => {
e.preventDefault();
importPerson({ variables: { username: input.value } });
input.value = "";
}}>
<div className="form-group">
<label htmlFor="username">Github Username</label>
<input type="text" name="username" className="form-control" ref={node => input = node} />
</div>
<input type="submit" className="btn btn-primary" value="Add" />
</form>
</div>
</div>
</div>
)}
</Mutation>
);
}
}
export default AddFace;

View file

@ -0,0 +1,27 @@
import React from "react";
import gql from "graphql-tag";
import { Query } from "react-apollo";
import Person from "./Person";
import AddFace from "./AddFace";
import LIST_PEOPLE from "../queries/list_people";
class Gallery extends React.Component {
render() {
return (
<div className="row">
<Query query={LIST_PEOPLE}>
{({ loading, error, data }) => {
if (loading) return null;
if (error) return `Error!: ${error}`;
return data.people.map((person, key) => (<Person person={person} key={key} />));
}}
</Query>
<AddFace />
</div>
);
}
}
export default Gallery;

View file

@ -3,8 +3,8 @@ import React from "react";
class Header extends React.Component { class Header extends React.Component {
render() { render() {
return ( return (
<div class="row"> <div className="row">
<div class="col-sm"> <div className="col-sm">
<h1>Face Gallery</h1> <h1>Face Gallery</h1>
</div> </div>
</div> </div>

View file

@ -0,0 +1,22 @@
import React from "react";
class Person extends React.Component {
render() {
const person = this.props.person;
const githubUrl = `https://github.com/${person.username}`;
return (
<div className="col-sm-3 mb-3">
<div className="card">
<img className="card-img-top" src={person.avatarUrl} alt={person.name} />
<div className="card-body">
<a href={githubUrl}><h5 className="card-title">{person.name}</h5></a>
<p className="card-text">{person.location}</p>
</div>
</div>
</div>
);
}
}
export default Person;

View file

@ -0,0 +1,12 @@
import gql from "graphql-tag";
export default gql `
mutation importPerson($username: String!) {
importPerson(username: $username) {
username,
name,
location,
avatarUrl
}
}
`;

View file

@ -0,0 +1,12 @@
import gql from "graphql-tag";
export default gql `
query {
people{
username,
name,
location,
avatarUrl
}
}
`;

View file

@ -6,10 +6,14 @@
"watch": "brunch watch --stdin" "watch": "brunch watch --stdin"
}, },
"dependencies": { "dependencies": {
"apollo-client-preset": "^1.0.8",
"babel-preset-react": "^6.24.1", "babel-preset-react": "^6.24.1",
"graphql": "^0.13.2",
"graphql-tag": "^2.8.0",
"phoenix": "file:../deps/phoenix", "phoenix": "file:../deps/phoenix",
"phoenix_html": "file:../deps/phoenix_html", "phoenix_html": "file:../deps/phoenix_html",
"react": "^16.3.1", "react": "^16.3.1",
"react-apollo": "^2.1.3",
"react-dom": "^16.3.1", "react-dom": "^16.3.1",
"yarn": "^1.5.1" "yarn": "^1.5.1"
}, },

View file

@ -2,6 +2,18 @@
# yarn lockfile v1 # yarn lockfile v1
"@types/async@2.0.47":
version "2.0.47"
resolved "https://registry.yarnpkg.com/@types/async/-/async-2.0.47.tgz#f49ba1dd1f189486beb6e1d070a850f6ab4bd521"
"@types/node@^9.4.6":
version "9.6.2"
resolved "https://registry.yarnpkg.com/@types/node/-/node-9.6.2.tgz#e49ac1adb458835e95ca6487bc20f916b37aff23"
"@types/zen-observable@^0.5.3":
version "0.5.3"
resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.5.3.tgz#91b728599544efbb7386d8b6633693a3c2e7ade5"
abbrev@1: abbrev@1:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
@ -94,6 +106,75 @@ anysort@~1.0:
dependencies: dependencies:
anymatch "~1.3.0" anymatch "~1.3.0"
apollo-cache-inmemory@^1.1.7:
version "1.1.12"
resolved "https://registry.yarnpkg.com/apollo-cache-inmemory/-/apollo-cache-inmemory-1.1.12.tgz#ab489bf046b3e026556ab28bdebb6e010cac9531"
dependencies:
apollo-cache "^1.1.7"
apollo-utilities "^1.0.11"
graphql-anywhere "^4.1.8"
apollo-cache@^1.1.7:
version "1.1.7"
resolved "https://registry.yarnpkg.com/apollo-cache/-/apollo-cache-1.1.7.tgz#5817018a2fbfc05a21ba319bd17a3e7538110cc5"
dependencies:
apollo-utilities "^1.0.11"
apollo-client-preset@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/apollo-client-preset/-/apollo-client-preset-1.0.8.tgz#23bd7176849d0d815f12c648774d009b258a449e"
dependencies:
apollo-cache-inmemory "^1.1.7"
apollo-client "^2.2.2"
apollo-link "^1.0.6"
apollo-link-http "^1.3.1"
graphql-tag "^2.4.2"
apollo-client@^2.2.2:
version "2.2.8"
resolved "https://registry.yarnpkg.com/apollo-client/-/apollo-client-2.2.8.tgz#b604d31ab2d2dd00db3105d8793b93ee02ce567e"
dependencies:
"@types/zen-observable" "^0.5.3"
apollo-cache "^1.1.7"
apollo-link "^1.0.0"
apollo-link-dedup "^1.0.0"
apollo-utilities "^1.0.11"
symbol-observable "^1.0.2"
zen-observable "^0.7.0"
optionalDependencies:
"@types/async" "2.0.47"
apollo-link-dedup@^1.0.0:
version "1.0.8"
resolved "https://registry.yarnpkg.com/apollo-link-dedup/-/apollo-link-dedup-1.0.8.tgz#8c3028cf32557bd040ab6ba8856f38067bdacead"
dependencies:
apollo-link "^1.2.1"
apollo-link-http-common@^0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/apollo-link-http-common/-/apollo-link-http-common-0.2.3.tgz#82ae0d4ff0cdd7c5c8826411d9dd7f7d8049ca46"
dependencies:
apollo-link "^1.2.1"
apollo-link-http@^1.3.1:
version "1.5.3"
resolved "https://registry.yarnpkg.com/apollo-link-http/-/apollo-link-http-1.5.3.tgz#3aa0d3ecfe5666ef0c360f359c425ff6ea1d285b"
dependencies:
apollo-link "^1.2.1"
apollo-link-http-common "^0.2.3"
apollo-link@^1.0.0, apollo-link@^1.0.6, apollo-link@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/apollo-link/-/apollo-link-1.2.1.tgz#c120b16059f9bd93401b9f72b94d2f80f3f305d2"
dependencies:
"@types/node" "^9.4.6"
apollo-utilities "^1.0.0"
zen-observable-ts "^0.8.6"
apollo-utilities@^1.0.0, apollo-utilities@^1.0.11:
version "1.0.11"
resolved "https://registry.yarnpkg.com/apollo-utilities/-/apollo-utilities-1.0.11.tgz#cd36bfa6e5c04eea2caf0c204a0f38a0ad550802"
aproba@^1.0.3: aproba@^1.0.3:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
@ -1945,6 +2026,22 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.3:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
graphql-anywhere@^4.1.8:
version "4.1.8"
resolved "https://registry.yarnpkg.com/graphql-anywhere/-/graphql-anywhere-4.1.8.tgz#23882e6a16ec824febbe5bca40937cdd76c5acdc"
dependencies:
apollo-utilities "^1.0.11"
graphql-tag@^2.4.2, graphql-tag@^2.8.0:
version "2.8.0"
resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.8.0.tgz#52cdea07a842154ec11a2e840c11b977f9b835ce"
graphql@^0.13.2:
version "0.13.2"
resolved "https://registry.yarnpkg.com/graphql/-/graphql-0.13.2.tgz#4c740ae3c222823e7004096f832e7b93b2108270"
dependencies:
iterall "^1.2.1"
growl@~1.8.1: growl@~1.8.1:
version "1.8.1" version "1.8.1"
resolved "https://registry.yarnpkg.com/growl/-/growl-1.8.1.tgz#4b2dec8d907e93db336624dcec0183502f8c9428" resolved "https://registry.yarnpkg.com/growl/-/growl-1.8.1.tgz#4b2dec8d907e93db336624dcec0183502f8c9428"
@ -2021,6 +2118,10 @@ hoek@2.x.x:
version "2.16.3" version "2.16.3"
resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed"
hoist-non-react-statics@^2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.0.tgz#d2ca2dfc19c5a91c5a6615ce8e564ef0347e2a40"
home-or-tmp@^2.0.0: home-or-tmp@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8"
@ -2321,6 +2422,10 @@ isstream@~0.1.2:
version "0.1.2" version "0.1.2"
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
iterall@^1.2.1:
version "1.2.2"
resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.2.2.tgz#92d70deb8028e0c39ff3164fdbf4d8b088130cd7"
js-tokens@^3.0.0, js-tokens@^3.0.2: js-tokens@^3.0.0, js-tokens@^3.0.2:
version "3.0.2" version "3.0.2"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
@ -2432,7 +2537,7 @@ lodash.camelcase@^4.3.0:
version "4.3.0" version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
lodash@^4.17.4, lodash@^4.3.0: lodash@4.17.5, lodash@^4.17.4, lodash@^4.3.0:
version "4.17.5" version "4.17.5"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511"
@ -3036,6 +3141,16 @@ rc@^1.1.7:
minimist "^1.2.0" minimist "^1.2.0"
strip-json-comments "~2.0.1" strip-json-comments "~2.0.1"
react-apollo@^2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/react-apollo/-/react-apollo-2.1.3.tgz#5eb02cdf18cc4bdeb615bda94baedb50354e94e5"
dependencies:
fbjs "^0.8.16"
hoist-non-react-statics "^2.5.0"
invariant "^2.2.2"
lodash "4.17.5"
prop-types "^15.6.0"
react-dom@^16.3.1: react-dom@^16.3.1:
version "16.3.1" version "16.3.1"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.3.1.tgz#6a3c90a4fb62f915bdbcf6204422d93a7d4ca573" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.3.1.tgz#6a3c90a4fb62f915bdbcf6204422d93a7d4ca573"
@ -3515,6 +3630,10 @@ supports-color@^5.3.0:
dependencies: dependencies:
has-flag "^3.0.0" has-flag "^3.0.0"
symbol-observable@^1.0.2:
version "1.2.0"
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
table@4.0.2: table@4.0.2:
version "4.0.2" version "4.0.2"
resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36" resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36"
@ -3779,3 +3898,13 @@ yargs@~3.10.0:
yarn@^1.5.1: yarn@^1.5.1:
version "1.5.1" version "1.5.1"
resolved "https://registry.yarnpkg.com/yarn/-/yarn-1.5.1.tgz#e8680360e832ac89521eb80dad3a7bc27a40bab4" resolved "https://registry.yarnpkg.com/yarn/-/yarn-1.5.1.tgz#e8680360e832ac89521eb80dad3a7bc27a40bab4"
zen-observable-ts@^0.8.6:
version "0.8.8"
resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-0.8.8.tgz#1a586dc204fa5632a88057f879500e0d2ba06869"
dependencies:
zen-observable "^0.7.0"
zen-observable@^0.7.0:
version "0.7.1"
resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.7.1.tgz#f84075c0ee085594d3566e1d6454207f126411b3"