useQuery fix

This commit is contained in:
Riccardo
2021-01-04 18:48:05 +01:00
parent 61acbf02d8
commit 82055b4040
11 changed files with 405 additions and 381 deletions

View File

@@ -1,9 +1,9 @@
import React from 'react'; import React from 'react';
// import CreateAppointment from './CreateAppointment'; // import CreateAppointment from './CreateAppointment';
import Header from './Header'; import Header from './Header';
// import AppointmentList from './AppointmentList';
import ProductList from './ProductList';
import { Switch, Route } from 'react-router-dom'; import { Switch, Route } from 'react-router-dom';
import ProductList from './ProductList';
import AppointmentList from './AppointmentList';
const App = () => { const App = () => {
return ( return (
@@ -11,8 +11,8 @@ const App = () => {
<Header /> <Header />
<div className="ph3 pv1 background-gray"> <div className="ph3 pv1 background-gray">
<Switch> <Switch>
<Route exact path="/" component={ProductList} /> {/* <Route exact path="/" component={ProductList} /> */}
{/* <Route exact path="/" component={AppointmentList} /> */} <Route exact path="/" component={AppointmentList} />
{/* <Route exact path="/create" component={CreateAppointment} /> */} {/* <Route exact path="/create" component={CreateAppointment} /> */}
</Switch> </Switch>
</div> </div>

View File

@@ -1,98 +1,113 @@
import React from 'react'; import React from 'react';
import { useMutation, gql } from '@apollo/client';
import { AUTH_TOKEN, APPOINTMENTS_PER_PAGE } from '../constants';
import { timeDifferenceForDate } from '../utils'
import { FEED_QUERY } from './AppointmentList'
const FOLLOW_MUTATION = gql`
mutation FollowMutation($appointmentId: ID!) {
follow(followId: $followId) {
id
appointment {
id
follows {
id
user {
id
}
}
}
user {
id
}
}
}
`;
const take = APPOINTMENTS_PER_PAGE;
const skip = 0;
const orderBy = { createdAt: 'desc' };
const Appointment = (props) => { const Appointment = (props) => {
const { appointment } = props; const { appointment } = props;
const authToken = localStorage.getItem(AUTH_TOKEN);
const take = APPOINTMENTS_PER_PAGE;
const skip = 0;
const orderBy = { createdAt: 'desc' };
const [follow] = useMutation(FOLLOW_MUTATION, {
variables: {
appointmentId: appointment.id
},
update(cache, { data: { follow } }) {
const { feed } = cache.readQuery({
query: FEED_QUERY
});
const updatedAppointments = feed.follows.map((feedFollow) => {
if (feedFollow.id === appointment.id) {
return {
...feedFollow,
follows: [...feedFollow.follows, follow]
};
}
return feedFollow;
});
cache.writeQuery({
query: FEED_QUERY,
data: {
feed: {
appointments: updatedAppointments
}
}
});
}
});
return ( return (
<div className="flex mt2 items-start"> <div>
<div className="flex items-center"> <div>
<span className="gray">{props.index + 1}.</span> <b>{appointment.title}</b> starts at {appointment.timeStart}, ends at {appointment.timeEnd}. It is described as "{appointment.description}"
{authToken && ( </div>
<div
className="ml1 gray f11"
style={{ cursor: 'pointer' }}
onClick={follow}
>
</div>
)}
</div>
<div className="ml1">
<div>
{appointment.title} ({appointment.description})
</div>
{authToken && (
<div className="f6 lh-copy gray">
{appointment.follows.length} follows | by{' '}
{follow.createdBy ? follow.createdBy.name : 'Unknown'}{' '}
{timeDifferenceForDate(appointment.createdAt)}
</div>
)}
</div>
</div> </div>
); );
}; };
export default Appointment; export default Appointment;
// import React from 'react';
// import { useMutation, gql } from '@apollo/client';
// import { AUTH_TOKEN, APPOINTMENTS_PER_PAGE } from '../constants';
// import { timeDifferenceForDate } from '../utils'
// import { FEED_QUERY } from './AppointmentList'
// const FOLLOW_MUTATION = gql`
// mutation FollowMutation($appointmentId: ID!) {
// follow(followId: $followId) {
// id
// appointment {
// id
// follows {
// id
// user {
// id
// }
// }
// }
// user {
// id
// }
// }
// }
// `;
// const take = APPOINTMENTS_PER_PAGE;
// const skip = 0;
// const orderBy = { createdAt: 'desc' };
// const Appointment = (props) => {
// const { appointment } = props;
// const authToken = localStorage.getItem(AUTH_TOKEN);
// const take = APPOINTMENTS_PER_PAGE;
// const skip = 0;
// const orderBy = { createdAt: 'desc' };
// const [follow] = useMutation(FOLLOW_MUTATION, {
// variables: {
// appointmentId: appointment.id
// },
// update(cache, { data: { follow } }) {
// const { feed } = cache.readQuery({
// query: FEED_QUERY
// });
// const updatedAppointments = feed.follows.map((feedFollow) => {
// if (feedFollow.id === appointment.id) {
// return {
// ...feedFollow,
// follows: [...feedFollow.follows, follow]
// };
// }
// return feedFollow;
// });
// cache.writeQuery({
// query: FEED_QUERY,
// data: {
// feed: {
// appointments: updatedAppointments
// }
// }
// });
// }
// });
// return (
// <div className="flex mt2 items-start">
// <div className="flex items-center">
// <span className="gray">{props.index + 1}.</span>
// {authToken && (
// <div
// className="ml1 gray f11"
// style={{ cursor: 'pointer' }}
// onClick={follow}
// >
// ▲
// </div>
// )}
// </div>
// <div className="ml1">
// <div>
// {appointment.title} ({appointment.description})
// </div>
// {authToken && (
// <div className="f6 lh-copy gray">
// {appointment.follows.length} follows | by{' '}
// {follow.createdBy ? follow.createdBy.name : 'Unknown'}{' '}
// {timeDifferenceForDate(appointment.createdAt)}
// </div>
// )}
// </div>
// </div>
// );
// };
// export default Appointment;

View File

@@ -1,174 +1,217 @@
import React from 'react'; import React from 'react';
import Appointment from './Appointment'; import Appointment from './Appointment';
import { useHistory } from 'react-router';
import { APPOINTMENTS_PER_PAGE } from '../constants';
import { useQuery, gql } from '@apollo/client'; import { useQuery, gql } from '@apollo/client';
import { Link } from 'react-router-dom';
export const FEED_QUERY = gql` const APPOINTMENTS_QUERY = gql`
query AppointmentManyQuery( {
$take: Int allAppointments{
$skip: Int title
$orderBy: AppointmentOrderByInput description
) { timeStart
appointmentMany(take: $take, skip: $skip, orderBy: $orderBy) { timeEnd
id
appointments {
id
createdAt
title
# start
# end
description
# createdBy {
# id
# name
# }
# follows {
# id
# user {
# id
# }
# }
}
count
} }
} }
`; `;
// const NEW_APPOINTMENTS_SUBSCRIPTION = gql` const AppointmentList = () => {
// subscription {
// newAppointment { const { data } = useQuery(APPOINTMENTS_QUERY);
console.log("Data:", data);
if (data !== undefined) {
return (
<div>
{
data.allAppointments.map((appointment) => (
<Appointment key={appointment.id} appointment={appointment} />
))
}
</div>
);
} else {
return (
<div>
Rendering...
</div>
)
}
};
export default AppointmentList;
// import React from 'react';
// import Appointment from './Appointment';
// import { useHistory } from 'react-router';
// import { APPOINTMENTS_PER_PAGE } from '../constants';
// import { useQuery, gql } from '@apollo/client';
// import { Link } from 'react-router-dom';
// export const FEED_QUERY = gql`
// query AppointmentManyQuery(
// $take: Int
// $skip: Int
// $orderBy: AppointmentOrderByInput
// ) {
// appointmentMany(take: $take, skip: $skip, orderBy: $orderBy) {
// id // id
// url // appointments {
// description
// createdAt
// createdBy {
// id // id
// name // createdAt
// } // title
// follows { // # start
// id // # end
// user { // description
// id // # createdBy {
// } // # id
// # name
// # }
// # follows {
// # id
// # user {
// # id
// # }
// # }
// } // }
// count
// } // }
// } // }
// `; // `;
const getQueryVariables = (isNewPage, page) => { // // const NEW_APPOINTMENTS_SUBSCRIPTION = gql`
const skip = isNewPage ? (page - 1) * APPOINTMENTS_PER_PAGE : 0; // // subscription {
const take = isNewPage ? APPOINTMENTS_PER_PAGE : 100; // // newAppointment {
const orderBy = { createdAt: 'desc' }; // // id
// // url
// // description
// // createdAt
// // createdBy {
// // id
// // name
// // }
// // follows {
// // id
// // user {
// // id
// // }
// // }
// // }
// // }
// // `;
return { take, skip, orderBy }; // const getQueryVariables = (isNewPage, page) => {
}; // const skip = isNewPage ? (page - 1) * APPOINTMENTS_PER_PAGE : 0;
// const take = isNewPage ? APPOINTMENTS_PER_PAGE : 100;
// const orderBy = { createdAt: 'desc' };
const AppointmentList = () => { // return { take, skip, orderBy };
const history = useHistory(); // };
const isNewPage = history.location.pathname.includes(
'new'
);
const pageIndexParams = history.location.pathname.split(
'/'
);
const page = parseInt( // const AppointmentList = () => {
pageIndexParams[pageIndexParams.length - 1] // const history = useHistory();
); // const isNewPage = history.location.pathname.includes(
// 'new'
// );
// const pageIndexParams = history.location.pathname.split(
// '/'
// );
const pageIndex = page ? (page - 1) * APPOINTMENTS_PER_PAGE : 0; // const page = parseInt(
// pageIndexParams[pageIndexParams.length - 1]
// );
const { // const pageIndex = page ? (page - 1) * APPOINTMENTS_PER_PAGE : 0;
data,
loading,
error,
subscribeToMore
} = useQuery(FEED_QUERY, {
variables: getQueryVariables(isNewPage, page)
});
// const { data } = useQuery(FEED_QUERY); // const {
// data,
// loading,
// error,
// subscribeToMore
// } = useQuery(FEED_QUERY, {
// variables: getQueryVariables(isNewPage, page)
// });
const getAppointmentsToRender = (isNewPage, data) => { // // const { data } = useQuery(FEED_QUERY);
if (isNewPage) {
return data.feed.appointments;
}
const rankedAppointments = data.feed.appointments.slice();
rankedAppointments.sort(
(l1, l2) => l2.follows.length - l1.follows.length
);
return rankedAppointments;
};
// subscribeToMore({ // const getAppointmentsToRender = (isNewPage, data) => {
// document: NEW_APPOINTMENTS_SUBSCRIPTION, // if (isNewPage) {
// updateQuery: (prev, { subscriptionData }) => { // return data.feed.appointments;
// if (!subscriptionData.data) return prev; // }
// const newAppointment = subscriptionData.data.newAppointment; // const rankedAppointments = data.feed.appointments.slice();
// const exists = prev.feed.appointments.find( // rankedAppointments.sort(
// ({ id }) => id === newAppointment.id // (l1, l2) => l2.follows.length - l1.follows.length
// ); // );
// if (exists) return prev; // return rankedAppointments;
// };
// return Object.assign({}, prev, { // // subscribeToMore({
// feed: { // // document: NEW_APPOINTMENTS_SUBSCRIPTION,
// appointments: [newAppointment, ...prev.feed.appointments], // // updateQuery: (prev, { subscriptionData }) => {
// count: prev.feed.appointments.length + 1, // // if (!subscriptionData.data) return prev;
// __typename: prev.feed.__typename // // const newAppointment = subscriptionData.data.newAppointment;
// } // // const exists = prev.feed.appointments.find(
// }); // // ({ id }) => id === newAppointment.id
// } // // );
// }); // // if (exists) return prev;
return ( // // return Object.assign({}, prev, {
<> // // feed: {
{loading && <p>Loading...</p>} // // appointments: [newAppointment, ...prev.feed.appointments],
{error && <pre>{JSON.stringify(error, null, 2)}</pre>} // // count: prev.feed.appointments.length + 1,
{data && ( // // __typename: prev.feed.__typename
<> // // }
{getAppointmentsToRender(isNewPage, data).map( // // });
(appointment, index) => ( // // }
<Link // // });
key={appointment.id}
link={appointment}
index={index + pageIndex}
/>
)
)}
{isNewPage && (
<div className="flex ml4 mv3 gray">
<div
className="pointer mr2"
onClick={() => {
if (page > 1) {
history.push(`/new/${page - 1}`);
}
}}
>
Previous
</div>
<div
className="pointer"
onClick={() => {
if (
page <=
data.feed.count / APPOINTMENTS_PER_PAGE
) {
const nextPage = page + 1;
history.push(`/new/${nextPage}`);
}
}}
>
Next
</div>
</div>
)}
</>
)}
</>
);
};
export default AppointmentList; // return (
// <>
// {loading && <p>Loading...</p>}
// {error && <pre>{JSON.stringify(error, null, 2)}</pre>}
// {data && (
// <>
// {getAppointmentsToRender(isNewPage, data).map(
// (appointment, index) => (
// <Link
// key={appointment.id}
// link={appointment}
// index={index + pageIndex}
// />
// )
// )}
// {isNewPage && (
// <div className="flex ml4 mv3 gray">
// <div
// className="pointer mr2"
// onClick={() => {
// if (page > 1) {
// history.push(`/new/${page - 1}`);
// }
// }}
// >
// Previous
// </div>
// <div
// className="pointer"
// onClick={() => {
// if (
// page <=
// data.feed.count / APPOINTMENTS_PER_PAGE
// ) {
// const nextPage = page + 1;
// history.push(`/new/${nextPage}`);
// }
// }}
// >
// Next
// </div>
// </div>
// )}
// </>
// )}
// </>
// );
// };
// export default AppointmentList;

View File

@@ -1,14 +1,14 @@
import React from 'react'; import React from 'react';
import { useHistory } from 'react-router'; // import { useHistory } from 'react-router';
import { Link, withRouter } from 'react-router-dom'; // import { Link, withRouter } from 'react-router-dom';
const Header = () => { const Header = () => {
const history = useHistory(); // const history = useHistory();
return ( return (
<div className="flex pa1 justify-between nowrap orange"> <div className="flex pa1 justify-between nowrap orange">
<div className="flex flex-fixed black"> <div className="flex flex-fixed black">
<div className="fw7 mr1">Hacker News</div> <div className="fw7 mr1">Store!</div>
<Link to="/" className="ml1 no-underline black"> {/* <Link to="/" className="ml1 no-underline black">
new new
</Link> </Link>
<div className="ml1">|</div> <div className="ml1">|</div>
@@ -17,7 +17,7 @@ const Header = () => {
className="ml1 no-underline black" className="ml1 no-underline black"
> >
submit submit
</Link> </Link> */}
</div> </div>
</div> </div>
); );

View File

@@ -15,15 +15,26 @@ const ProductList = () => {
const { data } = useQuery(FEED_QUERY); const { data } = useQuery(FEED_QUERY);
console.log("Fah!", data); console.log("Data:", data);
if (data !== undefined) {
return (
<div>
{
data.allProducts.map((product) => (
<Product key={product.id} product={product} />
))
}
</div>
);
} else {
return (
<div>
Rendering...
</div>
)
}
return (
<div>
{data.allProducts.map((product) => (
<Product key={product.id} product={product} />
))}
</div>
);
}; };
export default ProductList; export default ProductList;

View File

@@ -5,7 +5,6 @@ import App from './components/App';
import { BrowserRouter } from 'react-router-dom'; import { BrowserRouter } from 'react-router-dom';
// import * as serviceWorker from './serviceWorker'; // import * as serviceWorker from './serviceWorker';
// 1
import { import {
ApolloProvider, ApolloProvider,
ApolloClient, ApolloClient,
@@ -13,18 +12,15 @@ import {
InMemoryCache InMemoryCache
} from '@apollo/client'; } from '@apollo/client';
// 2
const httpLink = createHttpLink({ const httpLink = createHttpLink({
uri: 'http://localhost:5000/graphql' uri: 'http://localhost:4000/graphql'
}); });
// 3
const client = new ApolloClient({ const client = new ApolloClient({
link: httpLink, link: httpLink,
cache: new InMemoryCache() cache: new InMemoryCache()
}); });
// 4
ReactDOM.render( ReactDOM.render(
<BrowserRouter> <BrowserRouter>
<ApolloProvider client={client}> <ApolloProvider client={client}>
@@ -36,9 +32,6 @@ ReactDOM.render(
// serviceWorker.unregister(); // serviceWorker.unregister();
// import React from 'react';
// import { BrowserRouter } from 'react-router-dom';
// import { setContext } from '@apollo/client/link/context'; // import { setContext } from '@apollo/client/link/context';
// import { AUTH_TOKEN } from './constants'; // import { AUTH_TOKEN } from './constants';
@@ -55,25 +48,6 @@ ReactDOM.render(
// // export default App; // // export default App;
// // import React from 'react';
// import ReactDOM from 'react-dom';
// import './styles/index.css';
// import './styles/tachyons.min.css'
// import App from './components/App';
// // // import * as serviceWorker from './serviceWorker';
// // // 1
// import {
// ApolloProvider,
// ApolloClient,
// createHttpLink,
// InMemoryCache
// } from '@apollo/client';
// // 2
// const httpLink = createHttpLink({
// uri: 'http://localhost:4000'
// });
// // attach the auth_token to all requests to GraphQL server // // attach the auth_token to all requests to GraphQL server
// const authLink = setContext((_, { headers }) => { // const authLink = setContext((_, { headers }) => {
@@ -114,17 +88,6 @@ ReactDOM.render(
// cache: new InMemoryCache() // cache: new InMemoryCache()
// }); // });
// // 4
// ReactDOM.render(
// <BrowserRouter>
// <ApolloProvider client={client}>
// <App />
// </ApolloProvider>
// </BrowserRouter>,
// document.getElementById('root')
// );
// // serviceWorker.unregister();
// // If you want to start measuring performance in your app, pass a function // // If you want to start measuring performance in your app, pass a function
// // to log results (for example: reportWebVitals(console.log)) // // to log results (for example: reportWebVitals(console.log))
// // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals // // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals

View File

@@ -30,33 +30,33 @@ app.use('/graphql', graphqlHTTP({
})); }));
const server = new ApolloServer({ const server = new ApolloServer({
// typeDefs: fs.readFileSync( typeDefs: fs.readFileSync(
// path.join(__dirname, 'schema.graphql'), path.join(__dirname, 'schema.graphql'),
// 'utf8' 'utf8'
// ), ),
schema, // schema,
cors: true, cors: true,
playground: process.env.NODE_ENV === 'development' ? true : false, playground: process.env.NODE_ENV === 'development' ? true : false,
// context: async ({ req }) => { context: async ({ req }) => {
// // if (!db) { // if (!db) {
// // try { // try {
// // if (!dbClient.isConnected()) await dbClient.connect() // if (!dbClient.isConnected()) await dbClient.connect()
// // mongo = dbClient.db('Calendar') // database name // mongo = dbClient.db('Calendar') // database name
// // console.log(db); // console.log(db);
// // } catch (e) { // } catch (e) {
// // console.log('--->error while connecting with graphql context (db)', e) // console.log('--->error while connecting with graphql context (db)', e)
// // } // }
// return { return {
// ...req, ...req,
// mongoose, mongoose,
// pubsub, pubsub,
// userId: // userId:
// req && req.headers.authorization // req && req.headers.authorization
// ? getUserId(req) // ? getUserId(req)
// : null // : null
// } }
// }, },
// subscriptions: { // subscriptions: {
// onConnect: (connectionParams) => { // onConnect: (connectionParams) => {
// if (connectionParams.authToken) { // if (connectionParams.authToken) {

View File

@@ -0,0 +1,25 @@
import mongoose from 'mongoose';
const Schema = mongoose.Schema;
const AppointmentSchema = new Schema({
title: {
type: String,
required: true
},
description: {
type: String,
required: false
},
timeStart: {
type: Date,
required: true
},
timeEnd: {
type: Date,
required: true
},
deleted: {
type: Boolean,
required: false
}
});
export default mongoose.model('appointment', AppointmentSchema);

View File

@@ -1,14 +1,14 @@
// import Appointment from '../../client/src/components/Appointment.js';
import Product from './models/product.js'; import Product from './models/product.js';
import Appointment from './models/appointment.js';
export const resolvers = { export const resolvers = {
Query: { Query: {
async allProducts() { async allProducts() {
const products = await Product.find(); return await Product.find();
console.log("Tah!", products); },
return products; async allAppointments() {
// return { return await Appointment.find();
// products
// };
}, },
}, },
Mutation: { Mutation: {

View File

@@ -7,6 +7,7 @@ type Query {
orderBy: AppointmentOrderByInput orderBy: AppointmentOrderByInput
): Feed! ): Feed!
allProducts: [Product] allProducts: [Product]
allAppointments: [Appointment]
users: [User!]! users: [User!]!
} }
@@ -20,8 +21,8 @@ type Mutation {
createAppointment( createAppointment(
title: String!, title: String!,
description: String!, description: String!,
start: DateTime!, timeStart: DateTime!,
end: DateTime!, timeEnd: DateTime!,
): Appointment! ): Appointment!
createProduct( createProduct(
input: ProductInput input: ProductInput
@@ -58,31 +59,32 @@ type AuthPayload {
} }
type User { type User {
id: ID! _id: ID!
name: String! name: String!
email: String! email: String!
appointments: [Appointment!]! appointments: [Appointment!]!
} }
type Appointment { type Appointment {
id: ID! _id: ID!
title: String! title: String!
description: String! description: String!
start: DateTime! timeStart: DateTime!
end: DateTime! timeEnd: DateTime!
createdBy: User deleted: Boolean
follows: [Follow!]! # createdBy: User
createdAt: DateTime! # follows: [Follow!]!
# createdAt: DateTime!
} }
type Product { type Product {
id: ID! _id: ID!
title: String! title: String!
qty: Int qty: Int
} }
type Follow { type Follow {
id: ID! _id: ID!
appointment: Appointment! appointment: Appointment!
user: User! user: User!
} }

View File

@@ -1,54 +1,19 @@
// import { import fs from 'fs';
// makeExecutableSchema import path from 'path';
// } from 'graphql-tools'; import { makeExecutableSchema } from 'graphql-tools';
// import { import { resolvers } from './resolvers.js';
// resolvers
// } from './resolvers.js';
// import fs from 'fs'; const moduleURL = new URL(import.meta.url);
// import path from 'path'; const __dirname = path.dirname(moduleURL.pathname);
// const moduleURL = new URL(import.meta.url); const typeDefs = fs.readFileSync(
// const __dirname = path.dirname(moduleURL.pathname); path.join(__dirname, 'schema.graphql'),
'utf8'
);
// const typeDefs = fs.readFileSync(
// path.join(__dirname, 'schema.graphql'),
// 'utf8'
// );
// const schema = makeExecutableSchema({
// typeDefs,
// // resolvers
// });
// export default schema;
import {
makeExecutableSchema
} from 'graphql-tools';
import {
resolvers
} from './resolvers.js';
const typeDefs = `
type Product {
_id: ID!
title: String!
qty: Int
}
type Query {
allProducts: [Product]
}
input ProductInput {
title: String!
qty: Int
}
type Mutation {
createProduct(input: ProductInput) : Product
updateProduct(_id: ID!, input: ProductInput): Product
deleteProduct(_id: ID!) : Product
}
`;
const schema = makeExecutableSchema({ const schema = makeExecutableSchema({
typeDefs, typeDefs,
resolvers resolvers
}); });
export default schema;
export default schema;