Skip to content

Commit 89f7ded

Browse files
authored
Merge pull request #21 from howtographql/tutorial-voting-frontend
Tutorial voting frontend
2 parents e2402c6 + 70c1aaa commit 89f7ded

20 files changed

+279
-143
lines changed

.netlify/state.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"siteId": "d0391ba3-241e-40c2-8762-639bed998ab4"
3+
}

packages/gatsby-theme/codegen.yml

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ schema:
33
- http://localhost:8000/___graphql
44
documents:
55
- ./src/*/*.{ts,tsx}
6+
- ./src/components/templates/*.{ts,tsx}
67
- ./src/components/queries/*.{ts,tsx}
78
- ../../../node_modules/gatsby-*/**/*.js
89
generates:

packages/gatsby-theme/gatsby-node.js

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
// const componentWithMDXScope = require('gatsby-mdx/component-with-mdx-scope');
22
const {
33
getTutorialSlug,
4-
getTutorialOverviewSlug,
5-
} = require('./src/utils/getTutorialSlug.js');
4+
getTutorialOverviewSlug
5+
} = require("./src/utils/getTutorialSlug.js");
66

77
exports.createPages = async ({ graphql, actions }) => {
88
const { createPage } = actions;
99
const TutorialLayout = require.resolve(
10-
`./src/components/templates/Tutorial.tsx`,
10+
`./src/components/templates/Tutorial.tsx`
1111
);
1212

1313
const { data } = await graphql(`
@@ -33,7 +33,7 @@ exports.createPages = async ({ graphql, actions }) => {
3333
const tutorialPath = getTutorialSlug(node.fileAbsolutePath);
3434
const overviewPageSlug = getTutorialOverviewSlug(node.fileAbsolutePath);
3535
const overviewTemplate = require.resolve(
36-
'./src/components/templates/TutorialOverview.tsx',
36+
"./src/components/templates/TutorialOverview.tsx"
3737
);
3838
//TODO: find a better way to ID posts & overviews Also a better way to query for them
3939
if (node.frontmatter.tutorialTitle) {
@@ -42,17 +42,17 @@ exports.createPages = async ({ graphql, actions }) => {
4242
component: overviewTemplate,
4343
context: {
4444
id: node.id,
45-
folderRegex: `/(${overviewPageSlug})/`,
46-
},
45+
folderRegex: `/(${overviewPageSlug})/`
46+
}
4747
});
4848
}
4949
createPage({
5050
path: tutorialPath,
5151
component: TutorialLayout, // node.fileAbsolutePath,
5252
context: {
5353
id: node.id,
54-
folderRegex: `/(${overviewPageSlug})/`,
55-
},
54+
folderRegex: `/(${overviewPageSlug})/`
55+
}
5656
});
5757
});
5858
};

packages/gatsby-theme/src/Apollo.ts

+11-11
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,18 @@ import config from '../config';
77
import { getAuthToken } from './utils/auth';
88

99
const authLink = setContext((_, { headers }) => {
10-
// get the authentication token from local storage if it exists
11-
const token = getAuthToken();
12-
// return the headers to the context so httpLink can read them
13-
return {
14-
headers: {
15-
...headers,
16-
authorization: token ? `Bearer ${token}` : "",
17-
}
18-
}
10+
// get the authentication token from local storage if it exists
11+
const token = getAuthToken();
12+
// return the headers to the context so httpLink can read them
13+
return {
14+
headers: {
15+
...headers,
16+
authorization: token ? `Bearer ${token}` : '',
17+
},
18+
};
1919
});
2020
const httpLink = new HttpLink({ uri: config.apiUrl, fetch });
2121
export const client = new ApolloClient({
22-
link: authLink.concat(httpLink),
23-
cache: new InMemoryCache(),
22+
link: authLink.concat(httpLink),
23+
cache: new InMemoryCache(),
2424
});

packages/gatsby-theme/src/components/Account.tsx

+10-17
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,24 @@
11
import React from 'react';
2-
import { Query } from 'react-apollo';
3-
import { CurrentUserQuery } from '../graphqlTypes';
42
import { Text, Image, Flex } from './shared/base';
53
import { Link } from 'gatsby';
6-
import { CenteredLoader } from './Loader';
7-
import CustomButton from './CustomButton';
8-
import { optionalChaining } from '../utils/helpers';
4+
import { GithubButton } from './buttons';
95
import { loginUser } from '../utils/auth';
10-
import { CURRENT_USER } from './queries/userQueries';
6+
import WithCurrentUser from '../utils/auth/WithCurrentUser';
7+
import { CenteredLoader } from '../components/Loader';
118

129
const Account = () => {
1310
return (
14-
<Query<CurrentUserQuery> query={CURRENT_USER}>
15-
{({ data, error, loading }) => {
16-
if (error || loading) {
11+
<WithCurrentUser>
12+
{({ user, loading }) => {
13+
if (loading) {
1714
return <CenteredLoader />;
1815
}
19-
if (optionalChaining(() => data.viewer.user)) {
20-
return <Profile user={data.viewer.user} />;
16+
if (user) {
17+
return <Profile user={user} />;
2118
}
22-
return (
23-
<CustomButton onClick={() => loginUser()} type="github">
24-
Sign up
25-
</CustomButton>
26-
);
19+
return <GithubButton onClick={() => loginUser()}>Sign up</GithubButton>;
2720
}}
28-
</Query>
21+
</WithCurrentUser>
2922
);
3023
};
3124

packages/gatsby-theme/src/components/TutorialListing.tsx

+13-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as React from 'react';
2-
import { Heading, Text, Card } from './shared/base';
2+
import { Heading, Text, Card, Flex, Box } from './shared/base';
33
import { getTutorialOverviewSlug } from '../utils/getTutorialSlug';
4+
import Upvote from './Upvote';
45
import { Link } from 'gatsby';
56

67
type TutorialListingProps = {
@@ -23,10 +24,17 @@ const TutorialListing: React.FunctionComponent<TutorialListingProps> = ({
2324
}) => {
2425
return (
2526
<Card width={[1]} p={4} my={4} borderRadius={8} boxShadow="small">
26-
<Link to={getTutorialOverviewSlug(tutorial.fileAbsolutePath)}>
27-
<Heading>{tutorial.frontmatter.tutorialTitle}</Heading>
28-
</Link>
29-
<Text>{tutorial.frontmatter.description}</Text>
27+
<Flex alignItems="center" justifyContent="center">
28+
<Box width={1 / 12}>
29+
<Upvote />
30+
</Box>
31+
<Box width={11 / 12}>
32+
<Link to={getTutorialOverviewSlug(tutorial.fileAbsolutePath)}>
33+
<Heading>{tutorial.frontmatter.tutorialTitle}</Heading>
34+
</Link>
35+
<Text>{tutorial.frontmatter.description}</Text>
36+
</Box>
37+
</Flex>
3038
</Card>
3139
);
3240
};

packages/gatsby-theme/src/components/TutorialSidebar.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from 'react';
22
import { Card, Image, Heading, Flex } from './shared/base';
33
import { authors } from '../utils/sampleData';
44
import AuthorList from './AuthorList';
5-
import CustomButton from './CustomButton';
5+
import { SpectrumButton } from './buttons';
66

77
type SidebarProps = {
88
tutorialTitle: string;
@@ -26,7 +26,7 @@ export const Sidebar: React.FunctionComponent<SidebarProps> = ({
2626
))}
2727
</ul>
2828
<AuthorList authors={authors} />
29-
<CustomButton type="spectrum"> Get help on Spectrum </CustomButton>
29+
<SpectrumButton> Get help on Spectrum </SpectrumButton>
3030
</div>
3131
);
3232
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import * as React from 'react';
2+
import { Heading, Flex } from './shared/base';
3+
import { VoteButton } from './buttons';
4+
import { loginUser } from '../utils/auth/';
5+
import WithCurrentUser from '../utils/auth/WithCurrentUser';
6+
7+
// Still to-do:
8+
// Query the backend with the ID of the tutorial to see whcih tutorial was upvoted
9+
// Create a way to store which user has upvoted the tutorial so that they can only
10+
// upvote it once
11+
12+
const Upvote = () => {
13+
return (
14+
<WithCurrentUser>
15+
{({ user }) => {
16+
if (user) {
17+
return <UpvoteData event={() => console.log('upvoted!')} />;
18+
}
19+
return <UpvoteData event={() => loginUser()} />;
20+
}}
21+
</WithCurrentUser>
22+
);
23+
};
24+
25+
type UpvoteDataProps = {
26+
event: string;
27+
};
28+
29+
// place holder until we have a backend that stores the number of upvotes
30+
// and can keep track of which tutorials a user upvotes
31+
const UpvoteData: React.FunctionComponent<UpvoteDataProps> = ({ event }) => {
32+
return (
33+
<Flex flexDirection="column" alignItems="center" justifyContent="center">
34+
<VoteButton onClick={event} />
35+
<Heading>{Math.floor(Math.random() * 100)}</Heading>
36+
</Flex>
37+
);
38+
};
39+
export default Upvote;

packages/gatsby-theme/src/components/CustomButton.tsx packages/gatsby-theme/src/components/buttons.tsx

+25-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,27 @@
11
import React from 'react';
2-
import { ButtonProps } from './shared/base.d';
2+
import { ButtonProps } from './shared/base';
33
import { Flex, Image, Button } from './shared/base';
44

5+
export const VoteButton: React.FunctionComponent<ButtonProps> = props => (
6+
<CustomButton {...props} type="vote" />
7+
);
8+
9+
export const GithubButton: React.FunctionComponent<ButtonProps> = props => (
10+
<CustomButton {...props} type="github" />
11+
);
12+
13+
export const TutorialButton: React.FunctionComponent<ButtonProps> = props => (
14+
<CustomButton {...props} type="tutorial" />
15+
);
16+
17+
export const SpectrumButton: React.FunctionComponent<ButtonProps> = props => (
18+
<CustomButton {...props} type="tutorial" />
19+
);
20+
521
export const CustomButton: React.FunctionComponent<
6-
ButtonProps & { type?: 'github' | 'tutorial' | 'spectrum' | 'default' }
22+
ButtonProps & {
23+
type?: 'github' | 'tutorial' | 'spectrum' | 'vote' | 'default';
24+
}
725
> = ({ type = 'default', children, ...buttonProps }) => {
826
const { icon, bg } = customButtonTypes[type];
927

@@ -30,6 +48,7 @@ interface CustomButtonType {
3048
tutorial: ButtonType;
3149
github: ButtonType;
3250
spectrum: ButtonType;
51+
vote: ButtonType;
3352
default: ButtonType;
3453
}
3554

@@ -46,10 +65,12 @@ const customButtonTypes: CustomButtonType = {
4665
icon: 'https://i.ibb.co/gmtgnsP/Spectrum.png',
4766
bg: 'grey',
4867
},
68+
vote: {
69+
icon: 'https://i.ibb.co/b3FGXbD/Vote.png',
70+
bg: 'white',
71+
},
4972
default: {
5073
icon: '',
5174
bg: 'primary',
5275
},
5376
};
54-
55-
export default CustomButton;

packages/gatsby-theme/src/components/layout.tsx

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { RouterProps } from "@reach/router";
2-
import * as React from "react";
3-
import Helmet from "react-helmet";
4-
import { theme, ThemeProvider, styled } from "../styles";
5-
import { useLayoutQuery } from "../hooks/useLayoutQuery";
6-
import Header from "./shared/Header";
1+
import { RouterProps } from '@reach/router';
2+
import * as React from 'react';
3+
import Helmet from 'react-helmet';
4+
import { theme, ThemeProvider, styled } from '../styles';
5+
import { useLayoutQuery } from '../hooks/useLayoutQuery';
6+
import Header from './shared/Header';
77

88
const MainLayout = styled.main`
99
max-width: 90%;
@@ -24,8 +24,8 @@ const Layout: React.FunctionComponent<LayoutProps> = ({ children }) => {
2424
<Helmet
2525
title={title}
2626
meta={[
27-
{ name: "description", content: description },
28-
{ name: "keywords", content: keywords || undefined }
27+
{ name: 'description', content: description },
28+
{ name: 'keywords', content: keywords || undefined },
2929
]}
3030
>
3131
<html lang="en" />

packages/gatsby-theme/src/components/queries/userQueries.ts

-15
This file was deleted.

packages/gatsby-theme/src/components/templates/Tutorial.tsx

+5-4
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ import Layout from '../layout';
44
import { MDXRenderer } from 'gatsby-mdx';
55
import { graphql } from 'gatsby';
66
import { Sidebar, TabletSidebar } from '../TutorialSidebar';
7-
import { TutorialMdxQuery } from 'src/graphqlTypes';
7+
import { TutorialMdxQuery } from '../../graphqlTypes';
88
import { HideOnTablet, ShowOnTablet } from '../../utils/responsive';
99
import { Flex, Box } from '../shared/base';
10+
import { optionalChaining } from '../../utils/helpers';
1011

1112
type TutorialLayoutProps = { data: TutorialMdxQuery } & RouterProps;
1213

@@ -18,10 +19,10 @@ const TutorialLayout: React.FunctionComponent<TutorialLayoutProps> = ({
1819
return null;
1920
}
2021
const { pageTitle } = data!.mdx!.frontmatter!;
21-
const tutorialTitle = data!.tutorialTitle!.frontmatter!.tutorialTitle!;
22-
const chapters = data!.pageTitles!.edges!.map(
22+
const tutorialTitle = optionalChaining(() => data!.tutorialTitle!.frontmatter!.tutorialTitle!);
23+
const chapters = optionalChaining(() => data!.pageTitles!.edges!.map(
2324
a => a.node!.frontmatter!.pageTitle!,
24-
);
25+
)) || [];
2526
const { location } = props;
2627

2728
return (

packages/gatsby-theme/src/components/templates/TutorialOverview.tsx

+5-8
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import AuthorList from '../AuthorList';
66
import TutorialHeader from '../overview/TutorialHeader';
77
import { Heading, Flex, Box } from '../shared/base';
88
import ProgressBar from '../overview/ProgressBar';
9-
import CustomButton from '../CustomButton';
9+
import { GithubButton, SpectrumButton, TutorialButton } from '../buttons';
1010
import { Content } from '../shared/styledHelpers';
1111
import { authors } from '../../utils/sampleData';
1212
import { graphql } from 'gatsby';
@@ -29,13 +29,13 @@ const PageTemplate: React.FunctionComponent<PageTemplateProps> = ({ data }) => {
2929
/>
3030
</Box>
3131
<Box width={1 / 4} m={3}>
32-
<CustomButton type="tutorial">Continue Tutorial</CustomButton>
32+
<TutorialButton>Continue Tutorial</TutorialButton>
3333
<Box m={3}>
3434
<ProgressBar percentage={33} width={100} />
3535
</Box>
3636
<Flex>
37-
<CustomButton type="github">Github</CustomButton>
38-
<CustomButton type="spectrum">Spectrum</CustomButton>
37+
<GithubButton>Github</GithubButton>
38+
<SpectrumButton>Spectrum</SpectrumButton>
3939
</Flex>
4040
<AuthorList authors={authors} />
4141
</Box>
@@ -46,10 +46,7 @@ const PageTemplate: React.FunctionComponent<PageTemplateProps> = ({ data }) => {
4646
let num = index + 1;
4747
return (
4848
<div>
49-
<Chapter
50-
num={num < 10 ? `0${num}` : num}
51-
tutorial = {mdx.node}
52-
/>
49+
<Chapter num={num < 10 ? `0${num}` : num} tutorial={mdx.node} />
5350
</div>
5451
);
5552
})}

0 commit comments

Comments
 (0)