Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
133 changes: 89 additions & 44 deletions with-apollo/App.js
Original file line number Diff line number Diff line change
@@ -1,46 +1,50 @@
import { ApolloProvider, gql, useQuery } from "@apollo/client";
import { gql } from "@apollo/client";
import { ApolloProvider, useQuery } from "@apollo/client/react";
import { Picker } from "@react-native-picker/picker";
import { useState } from "react";
import { useState, useEffect } from "react";
import { ActivityIndicator, StyleSheet, Text, View } from "react-native";

import { apolloClient } from "./apollo";

// Imperial I-class Star Destroyer
const defaultStarshipId = "c3RhcnNoaXBzOjM=";
// Imperial I-class Star Destroyer (numeric ID string, not Relay)
const defaultStarshipId = "3";

const LIST_STARSHIPTS = gql`
// List all starships (your wrapper returns results array)
const LIST_STARSHIPS = gql`
query listStarships {
allStarships {
starships {
id
name
edges {
node {
id
name
}
}
}
}
`;

// Fetch single starship by numeric id (snake_case + films = [String])
const GET_STARSHIP = gql`
query getStarship($id: ID!) {
starship(id: $id) {
starshipById(id: $id) {
id
name
model
starshipClass
manufacturers
length
starship_class
crew
costInCredits
length
cost_in_credits
consumables
filmConnection {
films {
id
title
}
manufacturers
film_names {
title
episode_id
release_date
}
}
}
`;


function RootComponent() {
const [starshipId, setStarshipId] = useState(defaultStarshipId);
const { data, error, loading } = useQuery(GET_STARSHIP, {
Expand All @@ -51,6 +55,8 @@ function RootComponent() {
console.log("Error fetching starship", error);
}

const starship = data?.starshipById;

return (
<View style={styles.container}>
<View style={styles.section}>
Expand All @@ -59,24 +65,33 @@ function RootComponent() {
onStarshipChange={setStarshipId}
/>
</View>
{loading ? (
<ActivityIndicator color="#333" />
) : (
<StarshipDetails starship={data.starship} />
)}
<View style={styles.detailsContainer}>
{loading ? (
<ActivityIndicator color="#333" />
) : starship ? (
<StarshipDetails starship={starship} />
) : (
<Text style={{ color: "red" }}>No starship found</Text>
)}
</View>
</View>
);
}

function StarshipPicker(props) {
const { data, error, loading } = useQuery(LIST_STARSHIPTS);
const { data, error, loading } = useQuery(LIST_STARSHIPS);

if (error) {
console.log("Error listing starships", error);
return <Text style={{ color: "red" }}>Failed to load starships</Text>;
}
if (loading) return null;

const { starships } = data.allStarships;
if (loading) {
return null;
}

// Flatten edges → nodes
const starships = data.allStarships.edges.map(edge => edge.node);

return (
<Picker
Expand All @@ -94,7 +109,11 @@ function StarshipPicker(props) {
);
}


function StarshipDetails({ starship }) {
const [films, setFilms] = useState([]);
const [loadingFilms, setLoadingFilms] = useState(false);

return (
<>
<View style={styles.section}>
Expand All @@ -104,29 +123,48 @@ function StarshipDetails({ starship }) {

<View style={styles.section}>
<Text style={styles.label}>Operational abilities</Text>
<Text>- {starship.crew} crew members</Text>
<Text>- {starship.consumables} without restocking</Text>
<Text>- {starship.crew ?? "N/A"} crew members</Text>
<Text>- {starship.consumables ?? "N/A"} without restocking</Text>
</View>

<View>
<View style={styles.section}>
<Text style={styles.label}>Ship attributes</Text>
<Text>- {starship.length}m long</Text>
<Text>- {starship.costInCredits} credits</Text>
<Text>- {starship.length ?? "N/A"}m long</Text>
<Text>- {starship.cost_in_credits ?? "N/A"} credits</Text>
</View>

<View style={styles.section}>
<Text style={styles.label}>Manufacturers</Text>
{starship.manufacturers.map((manufacturer) => (
<Text key={manufacturer}>- {manufacturer}</Text>
))}
</View>
{starship.manufacturers?.length > 0 && (
<View style={styles.section}>
<Text style={styles.label}>Manufacturers</Text>
{starship.manufacturers.map((m, idx) => (
<Text key={idx}>- {m}</Text>
))}
</View>
)}

<View style={styles.section}>
<Text style={styles.label}>Appeared in</Text>
{starship.filmConnection.films.map((film) => (
<Text key={film.id}>- {film.title}</Text>
))}
</View>
{starship.film_names?.length > 0 && (
<View style={styles.section}>
<Text style={styles.label}>Appeared in</Text>
{starship.film_names.map((film, idx) => (
<Text key={idx}>
- {film.title} (Episode {film.episode_id}, {film.release_date})
</Text>
))}
</View>
)}

{loadingFilms ? (
<ActivityIndicator color="#333" />
) : films.length > 0 ? (
<View style={styles.section}>
<Text style={styles.label}>Appeared in</Text>
{films.map((film, idx) => (
<Text key={idx}>
- {film.title} (Episode {film.episode_id})
</Text>
))}
</View>
) : null}
</>
);
}
Expand All @@ -136,11 +174,18 @@ const styles = StyleSheet.create({
flex: 1,
justifyContent: "center",
alignItems: "center",
},
detailsContainer: {
flexGrow: 1,
justifyContent: "flex-start", // keeps it pinned to the top
minHeight: 200, // avoids collapse
paddingTop: 20,
},
container: {
flex: 1,
justifyContent: "center",
paddingHorizontal: 50,
paddingTop: 100,
},
label: {
marginBottom: 2,
Expand Down
2 changes: 1 addition & 1 deletion with-apollo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@

- The Apollo configuration lies in the `apollo.js` file.
- The file also contains an option (with commented code) to pass an authorization token to the API.
- [Apollo Client Docs](https://www.apollographql.com/docs/react/v3.0-beta/)
- [Apollo Client Docs](https://www.apollographql.com/docs/react/get-started/)
20 changes: 12 additions & 8 deletions with-apollo/apollo.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client';
// import { setContext } from '@apollo/link-context';
// import { ApolloProvider, ApolloLink } from '@apollo/client';


// see: https://github.com/graphql/swapi-graphql
const GRAPHQL_API_URL = 'https://swapi-graphql.netlify.app/.netlify/functions/index';
const GRAPHQL_API_URL = 'https://swapi-gql-wrapper.vercel.app/api/graphql';

/*
uncomment the code below in case you are using a GraphQL API that requires some form of
Expand All @@ -11,12 +12,15 @@ you provide while making the request.


const TOKEN = '';
const asyncAuthLink = setContext(async () => {
return {

const authLink = new ApolloLink((operation, forward) => {
operation.setContext(({ headers = {} }) => ({
headers: {
Authorization: TOKEN,
...headers,
Authorization: TOKEN ? `Bearer ${TOKEN}` : '',
},
};
}));
return forward(operation);
});

*/
Expand All @@ -28,5 +32,5 @@ const httpLink = new HttpLink({
export const apolloClient = new ApolloClient({
cache: new InMemoryCache(),
link: httpLink,
// link: asyncAuthLink.concat(httpLink),
});
// link: authLink.concat(httpLink),
});
17 changes: 8 additions & 9 deletions with-apollo/package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
{
"dependencies": {
"@apollo/client": "^3.4.16",
"@apollo/link-context": "^2.0.0-beta.3",
"@react-native-picker/picker": "2.11.1",
"expo": "^54.0.1",
"graphql": "^15.0.0",
"react": "19.1.0",
"react-dom": "19.1.0",
"react-native": "0.81.4",
"react-native-web": "^0.21.0"
"@apollo/client": "^4.0.4",
"@react-native-picker/picker": "latest",
"expo": "^54.0.1",
"graphql": "^16.11.0",
"react": "19.1.0",
"react-dom": "19.1.0",
"react-native": "0.81.4",
"react-native-web": "latest"
}
}