Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: Adds collections & uploads Apollo to version 3 #455

Merged
merged 18 commits into from
Feb 4, 2021
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@ import App from "./src"
import { AppRegistry } from "react-native"
import * as Sentry from "@sentry/react-native"
import "react-native-gesture-handler"
// import { enableScreens } from "react-native-screens"

// This enables the native stack navigator using (react-native-screens)
// https://reactnavigation.org/blog/2020/02/06/react-navigation-5.0/#native-stack-navigator
// Leaving it commented out for now because there's glitch when navigating to the Product view
// enableScreens()
// "core-js/features/promise" is a polyfill Apollo refetch in RN, see: https://github.com/apollographql/apollo-client/issues/6381
import "core-js/features/promise"

AppRegistry.registerComponent("seasons", () => App)
9 changes: 3 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"build:fastlane": "fastlane ios beta"
},
"dependencies": {
"@apollo/react-hooks": "^3.1.1",
"@apollo/client": "^3.3.6",
"@expo/react-native-action-sheet": "^3.8.0",
"@material-ui/core": "^4.11.1",
"@react-native-community/async-storage": "~1.12.0",
Expand All @@ -41,24 +41,21 @@
"@sentry/react-native": "^1.1.0",
"@types/jest": "^24.0.18",
"@types/luxon": "^1.24.3",
"apollo-boost": "^0.4.4",
"apollo-cache": "^1.3.2",
"apollo-cache-persist": "^0.1.1",
"apollo-client": "2.6.4",
"apollo-link": "^1.2.12",
"apollo-link-context": "^1.0.19",
"apollo-link-error": "^1.1.12",
"apollo-link-error": "^1.1.13",
"apollo-upload-client": "^14.1.1",
"apollo-utilities": "^1.3.2",
"aws-sdk": "^2.639.0",
"core-js": "^3.8.3",
"expo": "^39.0.0",
"graphql": "^14.5.8",
"lodash": "^4.17.15",
"luxon": "^1.21.1",
"node-fetch": "^2.6.0",
"querystring": "^0.2.0",
"react": "16.13.1",
"react-apollo": "^3.1.1",
"react-dom": "16.13.1",
"react-native": "0.63.3",
"react-native-animatable": "^1.3.3",
Expand Down
78 changes: 42 additions & 36 deletions src/Apollo/index.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,51 @@
import { InMemoryCache, IntrospectionFragmentMatcher } from "apollo-cache-inmemory"
import { persistCache } from "apollo-cache-persist"
import { ApolloClient } from "apollo-client"
import { ApolloLink, Observable } from "apollo-link"
import { ApolloLink, Observable, ApolloClient, InMemoryCache, HttpLink } from "@apollo/client"
import { setContext } from "apollo-link-context"
import { onError } from "apollo-link-error"
import { createUploadLink } from "apollo-upload-client"
import { getAccessTokenFromSession, getNewToken } from "App/utils/auth"
import { config, Env } from "App/utils/config"
import { Platform } from "react-native"
import unfetch from "unfetch"

import AsyncStorage from "@react-native-community/async-storage"
import * as Sentry from "@sentry/react-native"

import introspectionQueryResultData from "../fragmentTypes.json"
import { resolvers, typeDefs } from "./resolvers"
import { isEmpty } from "lodash"

export const setupApolloClient = async () => {
const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData,
const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
fitPicsCount: {
read(newCount) {
return newCount
},
},
fitPics: {
merge(existing = [], incoming = [], { args: { skipFitPics = 0 } }) {
const merged = existing ? existing.slice(0) : []
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kierangillen Do we actually need to do this for every paginated query? seems a bit odd they would add that much complexity

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this is the new method for handling merging arrays now that updateQuery is deprecated: https://www.apollographql.com/docs/react/caching/cache-field-behavior/#merging-arrays

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for (let i = 0; i < incoming.length; ++i) {
merged[skipFitPics + i] = incoming[i]
}
existing = merged
return existing
},
},
productsConnection: {
merge(existing = {}, incoming = {}, { args: { skip = 0 } }) {
let mergedEdges = !isEmpty(existing) ? existing?.edges?.slice(0) : []
if (incoming?.edges?.length > 0) {
for (let i = 0; i < incoming?.edges?.length; ++i) {
mergedEdges[skip + i] = incoming?.edges?.[i]
}
}
return { ...existing, ...incoming, edges: mergedEdges }
},
},
},
},
},
})

const cache = new InMemoryCache({ fragmentMatcher })

const link = createUploadLink({
uri: config.get(Env.MONSOON_ENDPOINT) || "http://localhost:4000/",
// FIXME: unfetch here is being used for this fix https://github.com/jhen0409/react-native-debugger/issues/432
fetch: unfetch,
})
const httpLink = new HttpLink({
uri: process.env.MONSOON_ENDPOINT || "http://localhost:4000/", // Server URL (must be absolute)
}) as any
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should figure out the proper types for this


const authLink = setContext(async (_, { headers: oldHeaders }) => {
const headers = { ...oldHeaders, application: "harvest", platform: Platform.OS }
Expand All @@ -49,6 +67,7 @@ export const setupApolloClient = async () => {
// return the headers to the context so createUploadLink can read them
})

// @ts-ignore
const errorLink = onError(({ graphQLErrors, networkError, operation, forward, response }) => {
if (graphQLErrors) {
console.log("graphQLErrors", graphQLErrors)
Expand Down Expand Up @@ -78,7 +97,7 @@ export const setupApolloClient = async () => {
if (networkError) {
console.log("networkError", JSON.stringify(networkError))
// User access token has expired
if (networkError.statusCode === 401) {
if ((networkError as any).statusCode === 401) {
// We assume we have both tokens needed to run the async request
// Let's refresh token through async request
return new Observable((observer) => {
Expand Down Expand Up @@ -112,25 +131,12 @@ export const setupApolloClient = async () => {
}
})

const accessToken = await getAccessTokenFromSession()
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're already storing the useSession in the context provider, no need for it in the cache layer

cache.writeData({
data: {
isLoggedIn: !!accessToken,
localBagItems: [],
},
})

await persistCache({
cache,
storage: AsyncStorage,
})

return new ApolloClient({
// Provide required constructor fields
cache,
link: ApolloLink.from([authLink, errorLink, link]),
link: ApolloLink.from([authLink, errorLink, httpLink]) as any,
typeDefs,
resolvers,
cache,
// Provide some optional constructor fields
name: "react-web-client",
version: "1.3",
Expand Down
3 changes: 1 addition & 2 deletions src/Apollo/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export const typeDefs = gql`
variantID: ID!
}
extend type Query {
isLoggedIn: Boolean!
localBagItems: [LocalProduct!]!
isInBag(productID: ID!): Boolean!
}
Expand All @@ -28,7 +27,7 @@ interface AppResolvers extends Resolvers {
// We will update this with our app's resolvers later
}

export const resolvers = {
export const resolvers: any = {
localBagItems: (_, __, { cache }) => {
const queryResult = cache.readQuery({
query: GET_LOCAL_BAG,
Expand Down
19 changes: 16 additions & 3 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@ import DeviceInfo from "react-native-device-info"
import { SafeAreaProvider } from "react-native-safe-area-context"
import { enableScreens } from "react-native-screens"
import stripe from "tipsi-stripe"

import { ApolloProvider } from "@apollo/react-hooks"
import { ApolloProvider, gql } from "@apollo/client"
import AsyncStorage from "@react-native-community/async-storage"
import * as Sentry from "@sentry/react-native"

import { setupApolloClient } from "./Apollo"
import { NetworkProvider } from "./NetworkProvider"
import { config, Env } from "./utils/config"
Expand All @@ -22,6 +20,21 @@ export const App = () => {
async function loadClient() {
await config.start()
const client = await setupApolloClient()

client.writeQuery({
query: gql`
query InitializeLocalCache {
localBagItems {
productID
variantID
}
}
`,
data: {
localBagItems: [],
},
})

setApolloClient(client)

if (!__DEV__) {
Expand Down
2 changes: 1 addition & 1 deletion src/Components/Pause/PauseButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React, { useState } from "react"
import { Linking } from "react-native"
import { ButtonVariant } from "../Button"
import { usePopUpContext } from "App/Navigation/ErrorPopUp/PopUpContext"
import { useMutation } from "react-apollo"
import { useMutation } from "@apollo/client"
import gql from "graphql-tag"
import { DateTime } from "luxon"
import { GET_MEMBERSHIP_INFO } from "App/Scenes/Account/MembershipInfo/MembershipInfo"
Expand Down
14 changes: 11 additions & 3 deletions src/Navigation/AuthProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import analytics from "@segment/analytics-react-native"

import AuthContext from "./AuthContext"
import { ModalAndMainScreens } from "./Stacks"
import { gql } from "@apollo/client"

// For docs on auth see: https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html

Expand Down Expand Up @@ -103,13 +104,20 @@ export const AuthProvider = React.forwardRef<AuthProviderRef, AuthProviderProps>
await AsyncStorage.removeItem("beamsData")
RNPusherPushNotifications.clearAllState()
apolloClient.resetStore()
analytics.reset()
apolloClient.writeData({
apolloClient.writeQuery({
query: gql`
query ResetLocalCache {
localBagItems {
productID
variantID
}
}
`,
data: {
isLoggedIn: false,
localBagItems: [],
},
})
analytics.reset()
dispatch({ type: "SIGN_OUT" })
},
resetStore: () => {
Expand Down
2 changes: 1 addition & 1 deletion src/Navigation/ErrorPopUp/PopUpContext.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PopUpData } from "@seasons/eclipse"
import React, { useContext } from "react"
import { PopUpData } from "./PopUpProvider"

export const usePopUpContext = () => useContext(PopUpContext)

Expand Down
12 changes: 1 addition & 11 deletions src/Navigation/ErrorPopUp/PopUpProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,7 @@
import { PopUpData } from "@seasons/eclipse"
import React, { useReducer } from "react"
import PopUpContext from "./PopUpContext"

export interface PopUpData {
title?: string
icon?: JSX.Element
note?: string
buttonText?: string
onClose: any
theme?: "light" | "dark"
secondaryButtonText?: string
secondaryButtonOnPress?: () => void
}

enum PopUpAction {
Show = "SHOW",
Hide = "HIDE",
Expand Down
6 changes: 6 additions & 0 deletions src/Navigation/Stacks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { SurpriseMe } from "App/Scenes/Bag/SurpriseMe"
import { Brand } from "App/Scenes/Brand"
import { Brands } from "App/Scenes/Brands"
import { Browse, Filters } from "App/Scenes/Browse"
import { CollectionScene } from "App/Scenes/Collection"
import { CreateAccount } from "App/Scenes/CreateAccount"
import { ApplyPromoCode } from "App/Scenes/CreateAccount/Admitted/ApplyPromoCode/ApplyPromoCode"
import { DebugMenu } from "App/Scenes/DebugMenu"
Expand Down Expand Up @@ -148,6 +149,11 @@ const HomeStackScreen = () => {
<HomeStack.Screen name={Schema.PageNames.Home} component={Home} />
<HomeStack.Screen name={Schema.PageNames.Product} component={Product} initialParams={{ id: "" }} />
<HomeStack.Screen name={Schema.PageNames.Brand} component={Brand} initialParams={{ id: "" }} />
<HomeStack.Screen
name={Schema.PageNames.Collection}
component={CollectionScene}
initialParams={{ collectionSlug: "" }}
/>
<HomeStack.Screen name={Schema.PageNames.Tag} component={Tag} />
<HomeStack.Screen name={Schema.PageNames.Brands} component={Brands} />
<HomeStack.Screen name={Schema.PageNames.Webview} component={Webview} />
Expand Down
1 change: 1 addition & 0 deletions src/Navigation/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export enum PageNames {
Brand = "Brand",
Brands = "Brands",
FitPicDetail = "FitPicDetail",
Collection = "Collection",
FitPicConfirmation = "FitPicConfirmation",

// Browse
Expand Down
3 changes: 2 additions & 1 deletion src/Notifications/NotificationsProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Platform } from "react-native"
import gql from "graphql-tag"
import NotificationsContext from "./NotificationsContext"
import RNPusherPushNotifications from "react-native-pusher-push-notifications"
import { useQuery } from "react-apollo"
import { useQuery } from "@apollo/client"
import { useNavigation } from "@react-navigation/native"
import { getUserSession } from "App/utils/auth"

Expand All @@ -16,6 +16,7 @@ export const seasonsNotifInterest = "seasons-general-notifications"
export const GET_BEAMS_DATA = gql`
query BeamsData {
me {
id
user {
email
beamsToken
Expand Down
5 changes: 3 additions & 2 deletions src/Scenes/Account/Account.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
import gql from "graphql-tag"
import { DateTime } from "luxon"
import { default as React, useEffect } from "react"
import { useQuery } from "react-apollo"
import { useQuery } from "@apollo/client"
import { Linking, Platform, ScrollView, StatusBar } from "react-native"
import * as Animatable from "react-native-animatable"
import Share from "react-native-share"
Expand All @@ -32,6 +32,7 @@ import { WaitlistedCTA, AuthorizedCTA } from "@seasons/eclipse"
export const GET_USER = gql`
query GetUser {
me {
id
customer {
id
status
Expand Down Expand Up @@ -84,7 +85,7 @@ export const GET_USER = gql`

export const Account = screenTrack()(({ navigation }) => {
const { authState, signOut } = useAuthContext()
const { data, refetch } = useQuery(GET_USER)
const { previousData, data = previousData, refetch } = useQuery(GET_USER)

const tracking = useTracking()

Expand Down
2 changes: 1 addition & 1 deletion src/Scenes/Account/Components/NotificationToggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Text, Linking, AppState } from "react-native"
import { checkNotifications } from "react-native-permissions"
import { useNotificationsContext } from "App/Notifications/NotificationsContext"
import { useTracking, Schema } from "App/utils/track"
import { useMutation } from "react-apollo"
import { useMutation } from "@apollo/client"
import { usePopUpContext } from "App/Navigation/ErrorPopUp/PopUpContext"
import * as Sentry from "@sentry/react-native"
import { GetUser_me_customer_user_pushNotification } from "App/generated/getUser"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { FlatList, Keyboard, KeyboardAvoidingView } from "react-native"
import { useSafeAreaInsets } from "react-native-safe-area-context"
import { StatePickerPopUp } from "./StatePickerPopup"
import gql from "graphql-tag"
import { useMutation } from "react-apollo"
import { useMutation } from "@apollo/client"
import { usePopUpContext } from "App/Navigation/ErrorPopUp/PopUpContext"
import { FadeBottom2 } from "Assets/svgs/FadeBottom2"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { useSafeAreaInsets } from "react-native-safe-area-context"
import { areIndicesEqual, Index, Item, Section, sections } from "./Sections"

import gql from "graphql-tag"
import { useMutation } from "react-apollo"
import { useMutation } from "@apollo/client"
import { usePopUpContext } from "App/Navigation/ErrorPopUp/PopUpContext"

const UPDATE_STYLE_PREFERENCES = gql`
Expand Down
Loading