Update apollo version, replace local state handling

This commit is contained in:
Ramon Wenger 2022-01-26 15:46:09 +01:00
parent 5b105958e2
commit a85296a628
11 changed files with 162 additions and 135 deletions

View File

@ -34,11 +34,6 @@
"@babel/runtime": "^7.5.4", "@babel/runtime": "^7.5.4",
"@iam4x/cypress-graphql-mock": "0.0.1", "@iam4x/cypress-graphql-mock": "0.0.1",
"@vue/composition-api": "^1.4.2", "@vue/composition-api": "^1.4.2",
"apollo-cache-inmemory": "^1.6.5",
"apollo-client": "^2.6.8",
"apollo-link": "^1.2.13",
"apollo-link-error": "^1.1.12",
"apollo-link-http": "^1.5.16",
"appolo": "^6.0.19", "appolo": "^6.0.19",
"autoprefixer": "^7.1.2", "autoprefixer": "^7.1.2",
"babel-helper-vue-jsx-merge-props": "^2.0.3", "babel-helper-vue-jsx-merge-props": "^2.0.3",

View File

@ -13,7 +13,7 @@
<script> <script>
import {INTERDISCIPLINARY, LANGUAGE_COMMUNICATION, SOCIETY} from '@/consts/instrument.consts'; import {INTERDISCIPLINARY, LANGUAGE_COMMUNICATION, SOCIETY} from '@/consts/instrument.consts';
import INSTRUMENT_FILTER_QUERY from 'gql/local/instrumentFiler.gql'; import INSTRUMENT_FILTER_QUERY from 'gql/local/instrumentFilter.gql';
const ChevronRight = () => import(/* webpackChunkName: "icons" */'@/components/icons/ChevronRight'); const ChevronRight = () => import(/* webpackChunkName: "icons" */'@/components/icons/ChevronRight');
export default { export default {
@ -61,7 +61,6 @@
} }
// eslint-disable-next-line // eslint-disable-next-line
const [_, identifier] = this.instrumentFilter.currentFilter.split(':'); const [_, identifier] = this.instrumentFilter.currentFilter.split(':');
console.log(identifier, this.type);
return this.type === identifier; return this.type === identifier;
}, },
typeClass() { typeClass() {

View File

@ -26,8 +26,7 @@
import FilterEntry from '@/components/instruments/FilterEntry'; import FilterEntry from '@/components/instruments/FilterEntry';
import SET_FILTER_MUTATION from 'gql/local/mutations/setInstrumentFilter.gql'; import SET_FILTER_MUTATION from 'gql/local/mutations/setInstrumentFilter.gql';
import INSTRUMENT_FILTER_QUERY from 'gql/local/instrumentFiler.gql'; import INSTRUMENT_FILTER_QUERY from 'gql/local/instrumentFilter.gql';
const ChevronRight = () => import(/* webpackChunkName: "icons" */'@/components/icons/ChevronRight');
export default { export default {
props: { props: {
@ -46,7 +45,6 @@
}, },
components: { components: {
FilterEntry, FilterEntry,
ChevronRight,
}, },
apollo: { apollo: {

104
client/src/graphql/cache.js Normal file
View File

@ -0,0 +1,104 @@
import {makeVar, defaultDataIdFromObject, InMemoryCache} from '@apollo/client/core';
const showNavigationSidebarVar = makeVar(false);
const showProfileSidebarVar = makeVar(false);
const scrollToElementVar = makeVar('');
const currentFilterVar = makeVar('');
const helloEmailVar = makeVar('');
const typePolicies = {
// https://www.apollographql.com/docs/react/local-state/managing-state-with-field-policies/#example
Query: {
fields: {
sidebar: {
read() {
return {
profile: showProfileSidebarVar(),
navigation: showNavigationSidebarVar()
};
}
} ,
scrollPosition: {
read() {
return {
scrollTo: scrollToElementVar()
};
}
},
instrumentFilter: {
read() {
return {
currentFilter: currentFilterVar()
};
}
},
helloEmail: {
read() {
return {
email: helloEmailVar()
};
}
}
}
},
};
const cache = new InMemoryCache({
// used to 'override' the behavior in resolving different types. We use it for local state management
// https://www.apollographql.com/docs/react/local-state/managing-state-with-field-policies/#example
typePolicies,
// todo: document what this does, or link the doc for it at least
dataIdFromObject: obj => {
switch (obj.__typename) {
case 'InstrumentNode':
case 'ModuleNode':
case 'RoomEntryNode':
return `${obj.__typename}:${obj.slug}`;
default:
return defaultDataIdFromObject(obj);
}
},
// todo: document what this does, or link the doc for it at least
cacheRedirects: {
Query: {
contentBlock: (_, args, {getCacheKey}) => getCacheKey({__typename: 'ContentBlockNode', id: args.id}),
chapter: (_, args, {getCacheKey}) => getCacheKey({__typename: 'ChapterNode', id: args.id}),
assignment: (_, args, {getCacheKey}) => getCacheKey({__typename: 'AssignmentNode', id: args.id}),
objective: (_, args, {getCacheKey}) => getCacheKey({__typename: 'ObjectiveNode', id: args.id}),
objectiveGroup: (_, args, {getCacheKey}) => getCacheKey({__typename: 'ObjectiveGroupNode', id: args.id}),
projectEntry: (_, args, {getCacheKey}) => getCacheKey({__typename: 'ProjectEntryNode', id: args.id}),
project: (_, args, {getCacheKey}) => {
if (args.slug) {
return getCacheKey({__typename: 'ProjectNode', id: args.slug});
} else {
return getCacheKey({__typename: 'ProjectNode', id: args.id});
}
},
},
},
});
// TODO: Monkey-patching in a fix for an open issue suggesting that
// `readQuery` should return null or undefined if the query is not yet in the
// cache: https://github.com/apollographql/apollo-feature-requests/issues/1
// probably not needed any more, as per https://github.com/apollographql/apollo-client/pull/7098
// cache.originalReadQuery = cache.readQuery;
// cache.readQuery = (...args) => {
// try {
// return cache.originalReadQuery(...args);
// } catch (err) {
// console.error(err);
// return undefined;
// }
// };
export {
showProfileSidebarVar,
showNavigationSidebarVar,
scrollToElementVar,
currentFilterVar,
helloEmailVar,
cache
};
export default cache;

View File

@ -1,36 +1,14 @@
import {defaultDataIdFromObject, InMemoryCache} from 'apollo-cache-inmemory'; import {onError} from '@apollo/client/link/error';
import {createHttpLink} from 'apollo-link-http'; import {
import {onError} from 'apollo-link-error'; ApolloClient,
import {ApolloClient} from 'apollo-client'; ApolloLink,
import {ApolloLink, Observable} from 'apollo-link'; createHttpLink,
} from '@apollo/client/core';
import fetch from 'unfetch'; import fetch from 'unfetch';
import {typeDefs} from '@/graphql/typedefs'; import {typeDefs} from '@/graphql/typedefs';
import {resolvers} from '@/graphql/resolvers'; import {resolvers} from '@/graphql/resolvers';
import cache from './cache';
const writeLocalCache = cache => {
// we use the cache as our local state
cache.writeData({
data: {
scrollPosition: {
__typename: 'ScrollPosition',
scrollTo: '',
},
sidebar: {
__typename: 'Sidebar',
profile: false,
navigation: false,
},
helloEmail: {
__typename: 'HelloEmail',
email: '',
},
instrumentFilter: {
__typename: 'InstrumentFilter',
currentFilter: 'abc'
}
},
});
};
export default function (uri, networkErrorCallback) { export default function (uri, networkErrorCallback) {
const httpLink = createHttpLink({ const httpLink = createHttpLink({
@ -44,11 +22,10 @@ export default function (uri, networkErrorCallback) {
}); });
const consoleLink = new ApolloLink((operation, forward) => { const consoleLink = new ApolloLink((operation, forward) => {
// console.log(`starting request for ${operation.operationName}`); console.log('operation', operation);
return forward(operation).map((data) => {
// console.log(`ending request for ${operation.operationName}`);
return forward(operation).map(data => {
console.log('data', data);
return data; return data;
}); });
}); });
@ -66,10 +43,11 @@ export default function (uri, networkErrorCallback) {
return forward(operation); return forward(operation);
}); });
const errorLink = onError(({response, operation, networkError, graphQLErrors}) => { const errorLink = onError(({networkError, graphQLErrors, forward, operation}) => {
// const observable = forward(operation);
if (networkError && networkErrorCallback) { if (networkError && networkErrorCallback) {
networkErrorCallback(networkError.statusCode); networkErrorCallback(networkError.statusCode);
return Observable.of(); // return observable;
} }
if (graphQLErrors) { if (graphQLErrors) {
@ -85,68 +63,23 @@ export default function (uri, networkErrorCallback) {
*/ */
if (networkError && networkError.name === 'ServerParseError') { if (networkError && networkError.name === 'ServerParseError') {
// workaround found here: https://github.com/apollographql/apollo-link/issues/855#issuecomment-485764697 // workaround found here: https://github.com/apollographql/apollo-link/issues/855#issuecomment-485764697
return Observable.of(); // return observable;
} }
}); });
const composedLink = ApolloLink.from([createOmitTypenameLink, consoleLink, errorLink, httpLink]); const composedLink = ApolloLink.from([
consoleLink,
const cache = new InMemoryCache({ createOmitTypenameLink,
dataIdFromObject: obj => { errorLink,
switch (obj.__typename) { httpLink
case 'InstrumentNode': ]);
case 'ModuleNode':
case 'RoomEntryNode':
return `${obj.__typename}:${obj.slug}`;
default:
return defaultDataIdFromObject(obj);
}
},
cacheRedirects: {
Query: {
contentBlock: (_, args, {getCacheKey}) => getCacheKey({__typename: 'ContentBlockNode', id: args.id}),
chapter: (_, args, {getCacheKey}) => getCacheKey({__typename: 'ChapterNode', id: args.id}),
assignment: (_, args, {getCacheKey}) => getCacheKey({__typename: 'AssignmentNode', id: args.id}),
objective: (_, args, {getCacheKey}) => getCacheKey({__typename: 'ObjectiveNode', id: args.id}),
objectiveGroup: (_, args, {getCacheKey}) => getCacheKey({__typename: 'ObjectiveGroupNode', id: args.id}),
projectEntry: (_, args, {getCacheKey}) => getCacheKey({__typename: 'ProjectEntryNode', id: args.id}),
project: (_, args, {getCacheKey}) => {
if (args.slug) {
return getCacheKey({__typename: 'ProjectNode', id: args.slug});
} else {
return getCacheKey({__typename: 'ProjectNode', id: args.id});
}
},
},
},
});
// TODO: Monkey-patching in a fix for an open issue suggesting that
// `readQuery` should return null or undefined if the query is not yet in the
// cache: https://github.com/apollographql/apollo-feature-requests/issues/1
cache.originalReadQuery = cache.readQuery;
cache.readQuery = (...args) => {
try {
return cache.originalReadQuery(...args);
} catch (err) {
console.error(err);
return undefined;
}
};
writeLocalCache(cache);
// Create the apollo client // Create the apollo client
const client = new ApolloClient({ return new ApolloClient({
link: composedLink, link: composedLink,
// link: httpLink,
cache, cache,
connectToDevTools: true, connectToDevTools: true,
typeDefs, typeDefs,
resolvers, resolvers,
}); });
client.onResetStore(() => {
writeLocalCache(cache);
});
return client;
} }

View File

@ -1,39 +1,48 @@
import SCROLL_POSITION from '@/graphql/gql/local/scrollPosition.gql'; import SCROLL_POSITION from '@/graphql/gql/local/scrollPosition.gql';
import HELLO_EMAIL from '@/graphql/gql/local/helloEmail.gql'; import HELLO_EMAIL from '@/graphql/gql/local/helloEmail.gql';
import SIDEBAR from '@/graphql/gql/local/sidebar.gql'; import {
import INSTRUMENT_FILTER from '@/graphql/gql/local/instrumentFiler.gql'; currentFilterVar,
helloEmailVar,
scrollToElementVar,
showNavigationSidebarVar,
showProfileSidebarVar,
} from '@/graphql/cache';
// todo: this probably can all be done with the apollo vars in the calling components, but that might need a rewrite of
// the parts where the mutation is called and then read. But maybe it works by itself already
export const resolvers = { export const resolvers = {
Mutation: { Mutation: {
scrollTo: (_, {scrollTo}, {cache}) => { scrollTo: (_, {scrollTo}) => {
const data = cache.readQuery({query: SCROLL_POSITION}); scrollToElementVar(scrollTo);
data.scrollPosition.scrollTo = scrollTo; return {
cache.writeQuery({query: SCROLL_POSITION, data}); scrollTo: scrollToElementVar()
return data.scrollPosition; };
}, },
helloEmail: (_, {email}, {cache}) => { helloEmail: (_, {email}) => {
const data = cache.readQuery({query: HELLO_EMAIL}); helloEmailVar(email);
data.helloEmail.email = email; return {
cache.writeQuery({query: HELLO_EMAIL, data}); email: helloEmailVar()
return data.helloEmail; };
}, },
setInstrumentFilter: (_, {filter}, {cache}) => { setInstrumentFilter: (_, {filter}) => {
const data = cache.readQuery({query: INSTRUMENT_FILTER}); currentFilterVar(filter);
data.instrumentFilter.currentFilter = filter; return {
cache.writeQuery({query: INSTRUMENT_FILTER, data}); currentFilter: currentFilterVar()
return data.instrumentFilter; };
}, },
toggleSidebar: (_, {sidebar: {profile, navigation}}, {cache}) => { toggleSidebar: (_, {sidebar: {profile, navigation}}) => {
const data = cache.readQuery({query: SIDEBAR});
if (typeof profile !== 'undefined') { if (typeof profile !== 'undefined') {
data.sidebar.profile = profile; showProfileSidebarVar(profile);
} }
if (typeof navigation !== 'undefined') { if (typeof navigation !== 'undefined') {
data.sidebar.navigation = navigation; showNavigationSidebarVar(navigation);
} }
cache.writeQuery({query: SIDEBAR, data}); return {
return data.sidebar; navigation: showNavigationSidebarVar(),
profile: showProfileSidebarVar()
};
}, },
// todo: does this still work?
deleteModuleNodes: (_, _query, {cache}) => { deleteModuleNodes: (_, _query, {cache}) => {
Object.keys(cache.data.data) Object.keys(cache.data.data)
.filter(prop => prop.indexOf('ModuleNode:') === 0) .filter(prop => prop.indexOf('ModuleNode:') === 0)

View File

@ -19,7 +19,7 @@ export default {
apollo: { apollo: {
sidebar: { sidebar: {
query: SIDEBAR query: SIDEBAR,
} }
}, },

View File

@ -56,8 +56,7 @@
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import "@/styles/_variables.scss"; @import "~styles/helpers";
@import "@/styles/_functions.scss";
.instrument { .instrument {
&__title { &__title {

View File

@ -22,8 +22,7 @@
import InstrumentFilter from '@/components/instruments/InstrumentFilter'; import InstrumentFilter from '@/components/instruments/InstrumentFilter';
import InstrumentEntry from '@/components/instruments/InstrumentEntry'; import InstrumentEntry from '@/components/instruments/InstrumentEntry';
import INSTRUMENTS_QUERY from '@/graphql/gql/queries/instrumentsQuery.gql'; import INSTRUMENTS_QUERY from '@/graphql/gql/queries/instrumentsQuery.gql';
import INSTRUMENT_FILTER_QUERY from 'gql/local/instrumentFilter.gql';
import INSTRUMENT_FILTER_QUERY from 'gql/local/instrumentFiler.gql';
export default { export default {
components: { components: {

View File

@ -35,15 +35,6 @@
}; };
}, },
created() {
this.$log.debug('**** module.vue created ****');
this.$log.debug(`||| module id ${this.module.id} |||`);
},
mounted() {
this.$log.debug('**** module.vue mounted ****');
this.$log.debug(`||| module id ${this.module.id} |||`);
},
apollo: { apollo: {
module() { module() {
return { return {