mirror of
https://github.com/dkhamsing/open-source-ios-apps.git
synced 2024-11-22 03:24:39 +03:00
Add Gatsby to build site (#887) [ci skip]
This commit is contained in:
parent
42ec2aa9c2
commit
43958025e7
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/.gtm/
|
10
gatsby/.editorconfig
Normal file
10
gatsby/.editorconfig
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# editorconfig.org
|
||||||
|
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
5
gatsby/.eslintignore
Normal file
5
gatsby/.eslintignore
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
.cache
|
||||||
|
package.json
|
||||||
|
package-lock.json
|
||||||
|
public
|
||||||
|
node_modules
|
40
gatsby/.eslintrc
Normal file
40
gatsby/.eslintrc
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 2020,
|
||||||
|
"sourceType": "module",
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"jsx": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:react/recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"prettier/@typescript-eslint",
|
||||||
|
"prettier"
|
||||||
|
],
|
||||||
|
"plugins": [
|
||||||
|
"@typescript-eslint",
|
||||||
|
"react",
|
||||||
|
"react-hooks",
|
||||||
|
"prettier"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"es6": true,
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"react": {
|
||||||
|
"version": "detect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"prettier/prettier": "error",
|
||||||
|
"react/prop-types": 0,
|
||||||
|
"import/prefer-default-export": "off",
|
||||||
|
"@typescript-eslint/explicit-function-return-type": "off",
|
||||||
|
"@typescript-eslint/explicit-module-boundary-types": "off"
|
||||||
|
}
|
||||||
|
}
|
69
gatsby/.gitignore
vendored
Normal file
69
gatsby/.gitignore
vendored
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Typescript v1 declaration files
|
||||||
|
typings/
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
.env*
|
||||||
|
|
||||||
|
# gatsby files
|
||||||
|
.cache/
|
||||||
|
public
|
||||||
|
|
||||||
|
# Mac files
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Yarn
|
||||||
|
yarn-error.log
|
||||||
|
.pnp/
|
||||||
|
.pnp.js
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
5
gatsby/.prettierignore
Normal file
5
gatsby/.prettierignore
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
.cache
|
||||||
|
package.json
|
||||||
|
package-lock.json
|
||||||
|
public
|
||||||
|
node_modules
|
8
gatsby/.prettierrc
Normal file
8
gatsby/.prettierrc
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"arrowParens": "avoid",
|
||||||
|
"semi": false,
|
||||||
|
"printWidth": 80,
|
||||||
|
"singleQuote": true,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"trailingComma": "all"
|
||||||
|
}
|
53
gatsby/.vscode/settings.json
vendored
Normal file
53
gatsby/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
{
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"files.encoding": "utf8",
|
||||||
|
"files.trimTrailingWhitespace": true,
|
||||||
|
"files.insertFinalNewline": true,
|
||||||
|
"search.exclude": {
|
||||||
|
"public/**": true,
|
||||||
|
"node_modules/**": true
|
||||||
|
},
|
||||||
|
"eslint.validate": [
|
||||||
|
"javascript",
|
||||||
|
"javascriptreact",
|
||||||
|
"typescript",
|
||||||
|
"typescriptreact"
|
||||||
|
],
|
||||||
|
"files.exclude": {
|
||||||
|
"**/logs": true,
|
||||||
|
"**/*.log": true,
|
||||||
|
"**/npm-debug.log*": true,
|
||||||
|
"**/yarn-debug.log*": true,
|
||||||
|
"**/yarn-error.log*": true,
|
||||||
|
"**/pids": true,
|
||||||
|
"**/*.pid": true,
|
||||||
|
"**/*.seed": true,
|
||||||
|
"**/*.pid.lock": true,
|
||||||
|
"**/lib-cov": true,
|
||||||
|
"**/coverage": true,
|
||||||
|
"**/.nyc_output": true,
|
||||||
|
"**/.grunt": true,
|
||||||
|
"**/bower_components": true,
|
||||||
|
"**/.lock-wscript": true,
|
||||||
|
"build/Release": true,
|
||||||
|
"**/node_modules/": true,
|
||||||
|
"**/jspm_packages/": true,
|
||||||
|
"**/typings/": true,
|
||||||
|
"**/.npm": true,
|
||||||
|
"**/.eslintcache": true,
|
||||||
|
"**/.node_repl_history": true,
|
||||||
|
"**/*.tgz": true,
|
||||||
|
"**/.env*": true,
|
||||||
|
"**/.cache/": true,
|
||||||
|
"**/public": true,
|
||||||
|
"**/.DS_Store": true,
|
||||||
|
"**/yarn-error.log": true,
|
||||||
|
"**/.pnp/": true,
|
||||||
|
"**/.pnp.js": true,
|
||||||
|
"**/.yarn-integrity": true
|
||||||
|
},
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll.eslint": true,
|
||||||
|
"source.fixAll": true
|
||||||
|
}
|
||||||
|
}
|
22
gatsby/LICENSE
Normal file
22
gatsby/LICENSE
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2020 junscuzzy
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
117
gatsby/README.md
Normal file
117
gatsby/README.md
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
# Gatsby's Typescript + Material-ui starter
|
||||||
|
|
||||||
|
[![Netlify Status](https://api.netlify.com/api/v1/badges/1b625068-4ac6-42d5-87fb-902d9077bbef/deploy-status)](https://app.netlify.com/sites/gatsby-material-typescript-starter/deploys)
|
||||||
|
|
||||||
|
Kick off your project with this [Material-ui](https://material-ui.com/) boilerplate. This starter ships with the main Gatsby configuration files you might need to get up and running blazing fast with the blazing fast app generator for React.
|
||||||
|
It includes support for Typescript in front-side and node-side and uses Eslint & Prettier.
|
||||||
|
|
||||||
|
This starter don't have any source or style supports, it's your choice.
|
||||||
|
|
||||||
|
_Have another more specific idea? You may want to check out our vibrant collection of [official and community-created starters](https://www.gatsbyjs.org/docs/gatsby-starters/)._
|
||||||
|
|
||||||
|
## 🚀 Quick start
|
||||||
|
|
||||||
|
1. **Create a Gatsby site.**
|
||||||
|
|
||||||
|
Use the Gatsby CLI to create a new site, specifying the default starter.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# create a new Gatsby site using the starter
|
||||||
|
gatsby new gatsby-material-typescript-starter https://github.com/Junscuzzy/gatsby-material-typescript-starter
|
||||||
|
```
|
||||||
|
|
||||||
|
1. **Start developing.**
|
||||||
|
|
||||||
|
Navigate into your new site’s directory and start it up.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cd gatsby-material-typescript-starter/
|
||||||
|
yarn develop
|
||||||
|
```
|
||||||
|
|
||||||
|
1. **Open the source code and start editing!**
|
||||||
|
|
||||||
|
Your site is now running at `http://localhost:8000`!
|
||||||
|
|
||||||
|
_Note: You'll also see a second link: _`http://localhost:8000/___graphql`_. This is a tool you can use to experiment with querying your data. Learn more about using this tool in the [Gatsby tutorial](https://www.gatsbyjs.org/tutorial/part-five/#introducing-graphiql)._
|
||||||
|
|
||||||
|
Open the `gatsby-material-typescript-starter` directory in your code editor of choice and edit `src/pages/index.tsx`. Save your changes and the browser will update in real time!
|
||||||
|
|
||||||
|
1. **Bonus**: Check all linters using
|
||||||
|
|
||||||
|
```shell
|
||||||
|
yarn lint
|
||||||
|
```
|
||||||
|
|
||||||
|
Will execute Prettier, Eslint and Typescript checking
|
||||||
|
|
||||||
|
All the commands are in your `package.json > scripts`.
|
||||||
|
|
||||||
|
## 🧐 What's inside?
|
||||||
|
|
||||||
|
A quick look at the top-level files and directories you'll see in a Gatsby project.
|
||||||
|
|
||||||
|
.
|
||||||
|
├── .vscode/
|
||||||
|
├── node_modules/
|
||||||
|
├── src/
|
||||||
|
├── static/
|
||||||
|
├── .editorconfig
|
||||||
|
├── .eslintrc
|
||||||
|
├── .gitignore
|
||||||
|
├── .prettierrc
|
||||||
|
├── gatsby-browser.js
|
||||||
|
├── gatsby-config.js
|
||||||
|
├── gatsby-node.js
|
||||||
|
├── gatsby-ssr.js
|
||||||
|
├── LICENSE
|
||||||
|
├── package.json
|
||||||
|
├── README.md
|
||||||
|
├── tsconfig.json
|
||||||
|
└── yarn.lock
|
||||||
|
|
||||||
|
1. **`/.vscode`**: VSCode projects settings.
|
||||||
|
|
||||||
|
1. **`/node_modules`**: This directory contains all of the modules of code that your project depends on (npm packages) are automatically installed.
|
||||||
|
|
||||||
|
1. **`/src`**: This directory will contain all of the code related to what you will see on the front-end of your site (what you see in the browser) such as your site header or a page template. `src` is a convention for “source code”.
|
||||||
|
|
||||||
|
1. **`/static`**: Static files like `robots.txt` or `favicon.ico`.
|
||||||
|
|
||||||
|
1. **`.editorconfig`**: EditorConfig helps maintain consistent coding styles for multiple developers working on the same project across various editors and IDEs.
|
||||||
|
|
||||||
|
1. **`.eslintrc`**: This is a configuration file for [Eslint](https://eslint.org/). Find and fix problems in your JavaScript code
|
||||||
|
|
||||||
|
1. **`.gitignore`**: This file tells git which files it should not track / not maintain a version history for.
|
||||||
|
|
||||||
|
1. **`.prettierrc`**: This is a configuration file for [Prettier](https://prettier.io/). Prettier is a tool to help keep the formatting of your code consistent.
|
||||||
|
|
||||||
|
1. **`gatsby-browser.js`**: This file is where Gatsby expects to find any usage of the [Gatsby browser APIs](https://www.gatsbyjs.org/docs/browser-apis/) (if any). These allow customization/extension of default Gatsby settings affecting the browser.
|
||||||
|
|
||||||
|
1. **`gatsby-config.js`**: This is the main configuration file for a Gatsby site. This is where you can specify information about your site (metadata) like the site title and description, which Gatsby plugins you’d like to include, etc. (Check out the [config docs](https://www.gatsbyjs.org/docs/gatsby-config/) for more detail).
|
||||||
|
|
||||||
|
1. **`gatsby-node.js`**: This file is where Gatsby expects to find any usage of the [Gatsby Node APIs](https://www.gatsbyjs.org/docs/node-apis/) (if any). These allow customization/extension of default Gatsby settings affecting pieces of the site build process.
|
||||||
|
|
||||||
|
1. **`gatsby-ssr.js`**: This file is where Gatsby expects to find any usage of the [Gatsby server-side rendering APIs](https://www.gatsbyjs.org/docs/ssr-apis/) (if any). These allow customization of default Gatsby settings affecting server-side rendering.
|
||||||
|
|
||||||
|
1. **`LICENSE`**: Gatsby is licensed under the MIT license.
|
||||||
|
|
||||||
|
1. **`package.json`**: A manifest file for Node.js projects, which includes things like metadata (the project’s name, author, etc). This manifest is how npm knows which packages to install for your project.
|
||||||
|
|
||||||
|
1. **`README.md`**: A text file containing useful reference information about your project.
|
||||||
|
|
||||||
|
1. **`tsconfig.json`**: This is a configuration file for [Typescript](https://www.typescriptlang.org/). TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.
|
||||||
|
|
||||||
|
1. **`yarn.lock`** (See `package.json` below, first). This is an automatically generated file based on the exact versions of your npm dependencies that were installed for your project. **(You won’t change this file directly).**
|
||||||
|
|
||||||
|
## 🎓 Learning Gatsby
|
||||||
|
|
||||||
|
Looking for more guidance? Full documentation for Gatsby lives [on the website](https://www.gatsbyjs.org/). Here are some places to start:
|
||||||
|
|
||||||
|
- **For most developers, we recommend starting with our [in-depth tutorial for creating a site with Gatsby](https://www.gatsbyjs.org/tutorial/).** It starts with zero assumptions about your level of ability and walks through every step of the process.
|
||||||
|
|
||||||
|
- **To dive straight into code samples, head [to our documentation](https://www.gatsbyjs.org/docs/).** In particular, check out the _Guides_, _API Reference_, and _Advanced Tutorials_ sections in the sidebar.
|
||||||
|
|
||||||
|
## 💫 Deploy
|
||||||
|
|
||||||
|
As a static generated website, you can deploy it on [Netlify](https://www.netlify.com), [Github Page](https://pages.github.com/) or [ZEIT Now](https://zeit.co/)
|
BIN
gatsby/content/images/gatsby.jpeg
Normal file
BIN
gatsby/content/images/gatsby.jpeg
Normal file
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
8
gatsby/gatsby-browser.js
Normal file
8
gatsby/gatsby-browser.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* Implement Gatsby's Browser APIs in this file.
|
||||||
|
*
|
||||||
|
* See: https://www.gatsbyjs.org/docs/browser-apis/
|
||||||
|
*/
|
||||||
|
|
||||||
|
export { default as wrapRootElement } from './src/libs/wrapRootElement'
|
||||||
|
export { default as wrapPageElement } from './src/libs/wrapPageElement'
|
56
gatsby/gatsby-config.js
Normal file
56
gatsby/gatsby-config.js
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
|
const join = require('path').join
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
pathPrefix: '/open-source-ios/',
|
||||||
|
siteMetadata: {
|
||||||
|
title: `Open Source iOS Apps`,
|
||||||
|
description: `A collaborative list of open-source iOS, watchOS and tvOS apps.`,
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
`gatsby-plugin-typescript`,
|
||||||
|
`gatsby-plugin-react-helmet`,
|
||||||
|
{
|
||||||
|
resolve: 'gatsby-plugin-material-ui',
|
||||||
|
// If you want to use styled components you should change the injection order.
|
||||||
|
options: {
|
||||||
|
// stylesProvider: {
|
||||||
|
// injectFirst: true,
|
||||||
|
// },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
resolve: `gatsby-plugin-manifest`,
|
||||||
|
options: {
|
||||||
|
name: `open-source-ios-apps`,
|
||||||
|
short_name: `os-ios-apps`,
|
||||||
|
start_url: `/`,
|
||||||
|
background_color: `#663399`,
|
||||||
|
theme_color: `#3E3F3A`,
|
||||||
|
display: `minimal-ui`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
`gatsby-transformer-json`,
|
||||||
|
{
|
||||||
|
resolve: `gatsby-source-filesystem`,
|
||||||
|
options: {
|
||||||
|
name: `apps`,
|
||||||
|
path: join(__dirname, `../`),
|
||||||
|
// This aims to ignore everything EXCEPT the `contents.json` file at the
|
||||||
|
// root of the repo
|
||||||
|
ignore: [
|
||||||
|
`**/.*`,
|
||||||
|
`**/.*/**`,
|
||||||
|
`**/gatsby/**`,
|
||||||
|
`**/*.md`,
|
||||||
|
`**/*.toml`,
|
||||||
|
`**/LICENSE`,
|
||||||
|
`**/Dangerfile`,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// this (optional) plugin enables Progressive Web App + Offline functionality
|
||||||
|
// To learn more, visit: https://gatsby.dev/offline
|
||||||
|
// `gatsby-plugin-offline`,
|
||||||
|
],
|
||||||
|
}
|
142
gatsby/gatsby-node.js
Normal file
142
gatsby/gatsby-node.js
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
|
const path = require('path')
|
||||||
|
const crypto = require('crypto')
|
||||||
|
const Bluebird = require('bluebird')
|
||||||
|
|
||||||
|
// Set this to true to enable more logging in this file
|
||||||
|
const DEBUG = false
|
||||||
|
const isDev = process.env.NODE_ENV === 'development'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement Gatsby's Node APIs in this file.
|
||||||
|
*
|
||||||
|
* See: https://www.gatsbyjs.org/docs/node-apis/
|
||||||
|
*/
|
||||||
|
|
||||||
|
// You can delete this file if you're not using it
|
||||||
|
|
||||||
|
exports.onCreateNode = async ({ node, actions }) => {
|
||||||
|
const { createNode } = actions
|
||||||
|
if (node.internal.type === 'OpenSourceIosAppsJson') {
|
||||||
|
const { categories, projects } = node
|
||||||
|
|
||||||
|
categories.forEach(category => {
|
||||||
|
if (typeof category.id !== 'string' || category.id.length < 1) {
|
||||||
|
console.error('Invalid category #veJYyW', category)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count how many projects are in each category. We can't easily do this
|
||||||
|
// in Gatsby's GraphQL API.
|
||||||
|
const projectCount = projects.filter(project =>
|
||||||
|
project['category-ids'].includes(category.id),
|
||||||
|
).length
|
||||||
|
|
||||||
|
createNode({
|
||||||
|
...category,
|
||||||
|
slug: category.id,
|
||||||
|
parentSlug: category.parent,
|
||||||
|
projectCount,
|
||||||
|
id: `Category__${category.id}`,
|
||||||
|
parent: node.id,
|
||||||
|
internal: {
|
||||||
|
type: `AppCategory`,
|
||||||
|
contentDigest: crypto
|
||||||
|
.createHash(`md5`)
|
||||||
|
.update(JSON.stringify(category))
|
||||||
|
.digest(`hex`),
|
||||||
|
content: JSON.stringify(category),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
await Bluebird.each(projects, async project => {
|
||||||
|
const contentDigest = crypto
|
||||||
|
.createHash(`md5`)
|
||||||
|
.update(JSON.stringify(project))
|
||||||
|
.digest(`hex`)
|
||||||
|
const id = `Project__${contentDigest}`
|
||||||
|
|
||||||
|
const projectNode = {
|
||||||
|
...project,
|
||||||
|
parent: node.id,
|
||||||
|
id,
|
||||||
|
internal: {
|
||||||
|
type: `AppProject`,
|
||||||
|
contentDigest,
|
||||||
|
content: JSON.stringify(project),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
await createNode(projectNode)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.createPages = async ({ actions, graphql }) => {
|
||||||
|
const { createPage } = actions
|
||||||
|
|
||||||
|
const categoryTemplate = path.resolve('src/templates/category.tsx')
|
||||||
|
const tagTemplate = path.resolve('src/templates/tag.tsx')
|
||||||
|
|
||||||
|
const categoryResult = await graphql(`
|
||||||
|
query Categories {
|
||||||
|
allAppCategory {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
|
const categories = categoryResult.data.allAppCategory.edges
|
||||||
|
|
||||||
|
categories.forEach(({ node: category }) => {
|
||||||
|
createPage({
|
||||||
|
path: `/category/${category.slug}/`,
|
||||||
|
component: categoryTemplate,
|
||||||
|
context: {
|
||||||
|
id: category.id,
|
||||||
|
slug: category.slug,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const projectResult = await graphql(`
|
||||||
|
query Projects {
|
||||||
|
allAppProject {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
tags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
|
const projects = projectResult.data.allAppProject.edges
|
||||||
|
const tags = new Set()
|
||||||
|
|
||||||
|
projects.forEach(({ node: project }) => {
|
||||||
|
if (project.tags && project.tags.length > 0) {
|
||||||
|
project.tags.forEach(tag => {
|
||||||
|
tags.add(tag)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
tags.forEach(tag => {
|
||||||
|
createPage({
|
||||||
|
path: `/tag/${tag}/`,
|
||||||
|
component: tagTemplate,
|
||||||
|
context: {
|
||||||
|
tag,
|
||||||
|
slug: tag,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
8
gatsby/gatsby-ssr.js
Normal file
8
gatsby/gatsby-ssr.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* Implement Gatsby's SSR (Server Side Rendering) APIs in this file.
|
||||||
|
*
|
||||||
|
* See: https://www.gatsbyjs.org/docs/ssr-apis/
|
||||||
|
*/
|
||||||
|
|
||||||
|
export { default as wrapRootElement } from './src/libs/wrapRootElement'
|
||||||
|
export { default as wrapPageElement } from './src/libs/wrapPageElement'
|
9
gatsby/netlify.toml
Normal file
9
gatsby/netlify.toml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[[plugins]]
|
||||||
|
package = "netlify-plugin-gatsby-cache"
|
||||||
|
|
||||||
|
[build.environment]
|
||||||
|
SHARP_IGNORE_GLOBAL_LIBVIPS="true"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
publish = "public/"
|
||||||
|
command = "yarn run build"
|
76
gatsby/package.json
Normal file
76
gatsby/package.json
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
{
|
||||||
|
"name": "gatsby-material-typescript-starter",
|
||||||
|
"private": true,
|
||||||
|
"description": "A simple starter using Typescript & Material-ui",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"author": "Julien CARON <juliencaron@protonmail.com>",
|
||||||
|
"keywords": [
|
||||||
|
"gatsby",
|
||||||
|
"typescript",
|
||||||
|
"eslint",
|
||||||
|
"prettier",
|
||||||
|
"material-ui"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"scripts": {
|
||||||
|
"build": "NODE_ENV=production gatsby build",
|
||||||
|
"develop": "gatsby develop",
|
||||||
|
"deploy-gh": "yarn build --prefix-paths && yarn gh-pages -d public",
|
||||||
|
"start": "yarn develop",
|
||||||
|
"serve": "gatsby serve",
|
||||||
|
"clean": "gatsby clean",
|
||||||
|
"lint": "run-p lint:**",
|
||||||
|
"lint:prettier": "prettier --write \"**/*.{js,jsx,json,md}\"",
|
||||||
|
"lint:eslint": "eslint 'src/**/*.{ts,tsx}'",
|
||||||
|
"lint:typescript": "tsc",
|
||||||
|
"test": "echo \"Write tests! -> https://gatsby.dev/unit-testing\" && exit 1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@material-ui/core": "^4.11.0",
|
||||||
|
"@material-ui/icons": "^4.9.1",
|
||||||
|
"@material-ui/styles": "^4.10.0",
|
||||||
|
"@reduxjs/toolkit": "^1.4.0",
|
||||||
|
"deepmerge": "^4.2.2",
|
||||||
|
"gatsby": "^2.24.56",
|
||||||
|
"gatsby-plugin-manifest": "^2.4.28",
|
||||||
|
"gatsby-plugin-material-ui": "^2.1.10",
|
||||||
|
"gatsby-plugin-offline": "^3.2.27",
|
||||||
|
"gatsby-plugin-react-helmet": "^3.3.10",
|
||||||
|
"gatsby-plugin-typescript": "^2.4.19",
|
||||||
|
"gatsby-source-filesystem": "^2.3.29",
|
||||||
|
"gatsby-transformer-json": "^2.4.11",
|
||||||
|
"react": "^16.13.1",
|
||||||
|
"react-dom": "^16.13.1",
|
||||||
|
"react-helmet": "^6.1.0",
|
||||||
|
"react-image-lightbox": "^5.1.1",
|
||||||
|
"react-redux": "^7.2.1",
|
||||||
|
"redux": "^4.0.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^14.6.4",
|
||||||
|
"@types/react": "^16.9.49",
|
||||||
|
"@types/react-dom": "^16.9.8",
|
||||||
|
"@types/react-helmet": "^6.1.0",
|
||||||
|
"@types/react-redux": "^7.1.9",
|
||||||
|
"@types/redux": "^3.6.0",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^4.1.0",
|
||||||
|
"@typescript-eslint/parser": "^4.1.0",
|
||||||
|
"bluebird": "^3.7.2",
|
||||||
|
"eslint": "^7.8.1",
|
||||||
|
"eslint-config-prettier": "^6.11.0",
|
||||||
|
"eslint-plugin-prettier": "^3.1.4",
|
||||||
|
"eslint-plugin-react": "^7.20.6",
|
||||||
|
"eslint-plugin-react-hooks": "^4.1.0",
|
||||||
|
"gh-pages": "^3.1.0",
|
||||||
|
"npm-run-all": "^4.1.5",
|
||||||
|
"prettier": "^2.1.1",
|
||||||
|
"typescript": "^4.0.2"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/Junscuzzy/gatsby-material-typescript-starter"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/Junscuzzy/gatsby-material-typescript-starter/issues"
|
||||||
|
}
|
||||||
|
}
|
209
gatsby/src/components/ProjectCard.tsx
Normal file
209
gatsby/src/components/ProjectCard.tsx
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
createStyles,
|
||||||
|
Divider,
|
||||||
|
GridList,
|
||||||
|
GridListTile,
|
||||||
|
makeStyles,
|
||||||
|
Theme,
|
||||||
|
Typography,
|
||||||
|
} from '@material-ui/core'
|
||||||
|
import Star from '@material-ui/icons/Star'
|
||||||
|
import { graphql, Link } from 'gatsby'
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
import Lightbox from 'react-image-lightbox'
|
||||||
|
import 'react-image-lightbox/style.css'
|
||||||
|
import { Project } from '../types'
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
description: {
|
||||||
|
marginTop: theme.spacing(1),
|
||||||
|
marginBottom: theme.spacing(1),
|
||||||
|
fontSize: '1.2em',
|
||||||
|
},
|
||||||
|
tag: {
|
||||||
|
marginRight: theme.spacing(1),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
const urlToReaddable = (url: string) => {
|
||||||
|
if (url.indexOf('https://github.com/') === 0) {
|
||||||
|
return `@${url.substr('https://github.com/'.length)}`
|
||||||
|
} else if (url.indexOf('http://github.com/') === 0) {
|
||||||
|
return `@${url.substr('http://github.com/'.length)}`
|
||||||
|
} else return url
|
||||||
|
}
|
||||||
|
|
||||||
|
const starCountToIconCount = (githubStars: number): number => {
|
||||||
|
if (githubStars > 2000) {
|
||||||
|
return 5
|
||||||
|
} else if (githubStars > 1000) {
|
||||||
|
return 4
|
||||||
|
} else if (githubStars > 500) {
|
||||||
|
return 3
|
||||||
|
} else if (githubStars > 200) {
|
||||||
|
return 2
|
||||||
|
} else if (githubStars > 100) {
|
||||||
|
return 1
|
||||||
|
} else return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
const RepeatingStars = ({ count }: { count: number }) => {
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
{Array.from({ length: count }).map((_, i) => (
|
||||||
|
<Star key={i} />
|
||||||
|
))}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ProjectCard = ({ project }: { project: Project }) => {
|
||||||
|
const classes = useStyles()
|
||||||
|
const [isLightboxOpen, setIsLightboxOpen] = useState(false)
|
||||||
|
const [lightboxSlide, setLightboxSlide] = useState(0)
|
||||||
|
|
||||||
|
const showStarCount = starCountToIconCount(project.stars)
|
||||||
|
const screenshotSources = project.screenshots || []
|
||||||
|
const tags = project.tags || []
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardContent>
|
||||||
|
<Typography variant="h4" component="h2">
|
||||||
|
{project.title}
|
||||||
|
</Typography>
|
||||||
|
{showStarCount > 0 ? <RepeatingStars count={showStarCount} /> : null}
|
||||||
|
<Typography className={classes.description}>
|
||||||
|
{project.description}
|
||||||
|
</Typography>
|
||||||
|
<Divider />
|
||||||
|
<Typography>Lang: {project.lang || 'en'}</Typography>
|
||||||
|
<Typography>Added: {project.date_added}</Typography>
|
||||||
|
<Typography>GitHub Stars: {project.stars}</Typography>
|
||||||
|
<Typography>
|
||||||
|
Code:{' '}
|
||||||
|
{project.source ? (
|
||||||
|
<a href={project.source} target="_blank" rel="noreferrer">
|
||||||
|
{urlToReaddable(project.source)}
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
'n/a'
|
||||||
|
)}
|
||||||
|
</Typography>
|
||||||
|
<Typography>
|
||||||
|
License:{' '}
|
||||||
|
{project.license ? (
|
||||||
|
<a
|
||||||
|
href={`https://choosealicense.com/licenses/${project.license}/`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
{project.license}
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
'n/a'
|
||||||
|
)}
|
||||||
|
</Typography>
|
||||||
|
<Typography>
|
||||||
|
iTunes:{' '}
|
||||||
|
{project.itunes ? (
|
||||||
|
<a href={project.itunes} target="_blank" rel="noreferrer">
|
||||||
|
{project.itunes}
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
'n/a'
|
||||||
|
)}
|
||||||
|
</Typography>
|
||||||
|
<Typography>
|
||||||
|
Homepage:{' '}
|
||||||
|
{project.homepage ? (
|
||||||
|
<a href={project.homepage} target="_blank" rel="noreferrer">
|
||||||
|
{project.homepage}
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
'n/a'
|
||||||
|
)}
|
||||||
|
</Typography>
|
||||||
|
<Typography>
|
||||||
|
Tags:{' '}
|
||||||
|
{tags.map(tag => {
|
||||||
|
return (
|
||||||
|
<Link key={tag} to={`/tag/${tag}/`} className={classes.tag}>
|
||||||
|
#{tag}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</Typography>
|
||||||
|
{screenshotSources.length === 0 ? null : (
|
||||||
|
<>
|
||||||
|
<GridList cellHeight={160} cols={4}>
|
||||||
|
{screenshotSources.map((url, i) => {
|
||||||
|
return (
|
||||||
|
<GridListTile key={i} cols={1}>
|
||||||
|
<a
|
||||||
|
href={url}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
onClick={event => {
|
||||||
|
event.preventDefault()
|
||||||
|
setLightboxSlide(i)
|
||||||
|
setIsLightboxOpen(true)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img src={url} width="120" height="160" />
|
||||||
|
</a>
|
||||||
|
</GridListTile>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</GridList>
|
||||||
|
{!isLightboxOpen ? null : (
|
||||||
|
<Lightbox
|
||||||
|
mainSrc={screenshotSources[lightboxSlide]}
|
||||||
|
nextSrc={screenshotSources[lightboxSlide + 1]}
|
||||||
|
prevSrc={screenshotSources[lightboxSlide - 1]}
|
||||||
|
onCloseRequest={() => setIsLightboxOpen(false)}
|
||||||
|
onMoveNextRequest={() => {
|
||||||
|
setLightboxSlide(
|
||||||
|
(lightboxSlide + screenshotSources.length + 1) %
|
||||||
|
screenshotSources.length,
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
onMovePrevRequest={() => {
|
||||||
|
setLightboxSlide(
|
||||||
|
(lightboxSlide + screenshotSources.length - 1) %
|
||||||
|
screenshotSources.length,
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ProjectCard
|
||||||
|
|
||||||
|
export const projectCardFragment = graphql`
|
||||||
|
fragment ProjectCardFields on AppProject {
|
||||||
|
category_ids
|
||||||
|
date_added
|
||||||
|
description
|
||||||
|
homepage
|
||||||
|
id
|
||||||
|
itunes
|
||||||
|
lang
|
||||||
|
license
|
||||||
|
screenshots
|
||||||
|
source
|
||||||
|
stars
|
||||||
|
suggested_by
|
||||||
|
tags
|
||||||
|
title
|
||||||
|
}
|
||||||
|
`
|
40
gatsby/src/components/hero.tsx
Normal file
40
gatsby/src/components/hero.tsx
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import React, { FC } from 'react'
|
||||||
|
import { makeStyles } from '@material-ui/styles'
|
||||||
|
import { Typography, Container, Theme } from '@material-ui/core'
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) => ({
|
||||||
|
heroContent: {
|
||||||
|
backgroundColor: theme.palette.background.paper,
|
||||||
|
padding: theme.spacing(8, 0, 6),
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
export interface HeroProps {
|
||||||
|
title: string
|
||||||
|
description?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const Hero: FC<HeroProps> = ({ title, description = '', children }) => {
|
||||||
|
const classes = useStyles()
|
||||||
|
return (
|
||||||
|
<div className={classes.heroContent}>
|
||||||
|
<Container maxWidth="md">
|
||||||
|
<Typography
|
||||||
|
component="h1"
|
||||||
|
variant="h2"
|
||||||
|
align="center"
|
||||||
|
color="textPrimary"
|
||||||
|
gutterBottom
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="h5" align="center" color="textSecondary" paragraph>
|
||||||
|
{description}
|
||||||
|
</Typography>
|
||||||
|
{children}
|
||||||
|
</Container>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Hero
|
93
gatsby/src/components/seo.tsx
Normal file
93
gatsby/src/components/seo.tsx
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
/**
|
||||||
|
* SEO component that queries for data with
|
||||||
|
* Gatsby's useStaticQuery React hook
|
||||||
|
*
|
||||||
|
* See: https://www.gatsbyjs.org/docs/use-static-query/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { FC } from 'react'
|
||||||
|
import { Helmet } from 'react-helmet'
|
||||||
|
import { useStaticQuery, graphql } from 'gatsby'
|
||||||
|
|
||||||
|
interface MetaProperty {
|
||||||
|
property: string
|
||||||
|
content: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MetaName {
|
||||||
|
name: string
|
||||||
|
content: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Meta = MetaName | MetaProperty
|
||||||
|
|
||||||
|
export interface SEOProps {
|
||||||
|
title: string
|
||||||
|
description?: string
|
||||||
|
lang?: string
|
||||||
|
meta?: Meta[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const SEO: FC<SEOProps> = ({
|
||||||
|
description = '',
|
||||||
|
lang = 'en',
|
||||||
|
meta = [],
|
||||||
|
title,
|
||||||
|
}) => {
|
||||||
|
const { site } = useStaticQuery(
|
||||||
|
graphql`
|
||||||
|
query {
|
||||||
|
site {
|
||||||
|
siteMetadata {
|
||||||
|
title
|
||||||
|
description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
)
|
||||||
|
|
||||||
|
const metaDescription = description || site.siteMetadata.description
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Helmet
|
||||||
|
htmlAttributes={{
|
||||||
|
lang,
|
||||||
|
}}
|
||||||
|
title={title}
|
||||||
|
titleTemplate={`%s | ${site.siteMetadata.title}`}
|
||||||
|
meta={[
|
||||||
|
{
|
||||||
|
name: `description`,
|
||||||
|
content: metaDescription,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
property: `og:title`,
|
||||||
|
content: title,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
property: `og:description`,
|
||||||
|
content: metaDescription,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
property: `og:type`,
|
||||||
|
content: `website`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: `twitter:card`,
|
||||||
|
content: `summary`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: `twitter:title`,
|
||||||
|
content: title,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: `twitter:description`,
|
||||||
|
content: metaDescription,
|
||||||
|
},
|
||||||
|
].concat(meta)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SEO
|
21
gatsby/src/hooks/useSiteMetadata.ts
Normal file
21
gatsby/src/hooks/useSiteMetadata.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { graphql, useStaticQuery } from 'gatsby'
|
||||||
|
|
||||||
|
export interface SiteMetadata {
|
||||||
|
title: string
|
||||||
|
description: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default (): SiteMetadata => {
|
||||||
|
const data = useStaticQuery(graphql`
|
||||||
|
{
|
||||||
|
site {
|
||||||
|
siteMetadata {
|
||||||
|
title
|
||||||
|
description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
|
return data.site.siteMetadata
|
||||||
|
}
|
BIN
gatsby/src/images/gatsby-icon.png
Normal file
BIN
gatsby/src/images/gatsby-icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
34
gatsby/src/layout/footer.tsx
Normal file
34
gatsby/src/layout/footer.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import React, { FC } from 'react'
|
||||||
|
|
||||||
|
import { makeStyles } from '@material-ui/styles'
|
||||||
|
import { Container, Typography, Theme, Link } from '@material-ui/core'
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) => ({
|
||||||
|
footer: {
|
||||||
|
padding: theme.spacing(3, 2),
|
||||||
|
marginTop: theme.spacing(4),
|
||||||
|
backgroundColor: theme.palette.background.paper,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
const Footer: FC = () => {
|
||||||
|
const classes = useStyles()
|
||||||
|
return (
|
||||||
|
<footer className={classes.footer}>
|
||||||
|
<Container maxWidth="md">
|
||||||
|
<Typography variant="body1" color="textSecondary">
|
||||||
|
Built with the data from{' '}
|
||||||
|
<Link
|
||||||
|
href="https://github.com/dkhamsing/open-source-ios-apps"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
@dkhamsing/open-source-ios-apps
|
||||||
|
</Link>
|
||||||
|
</Typography>
|
||||||
|
</Container>
|
||||||
|
</footer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Footer
|
50
gatsby/src/layout/header.tsx
Normal file
50
gatsby/src/layout/header.tsx
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { AppBar, Button, Link, Toolbar, Typography } from '@material-ui/core'
|
||||||
|
import { makeStyles } from '@material-ui/styles'
|
||||||
|
import { Link as GatsbyLink } from 'gatsby'
|
||||||
|
import React, { FC } from 'react'
|
||||||
|
|
||||||
|
const useStyles = makeStyles({
|
||||||
|
toolbar: {},
|
||||||
|
title: {
|
||||||
|
flexGrow: 1,
|
||||||
|
},
|
||||||
|
link: {
|
||||||
|
textDecoration: 'none',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export interface HeaderProps {
|
||||||
|
siteTitle?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const Header: FC<HeaderProps> = ({ siteTitle = '' }) => {
|
||||||
|
const classes = useStyles()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AppBar component="header" position="static">
|
||||||
|
<Toolbar className={classes.toolbar}>
|
||||||
|
<Typography variant="h6" className={classes.title}>
|
||||||
|
<Link
|
||||||
|
to="/"
|
||||||
|
component={GatsbyLink}
|
||||||
|
color="inherit"
|
||||||
|
className={classes.link}
|
||||||
|
>
|
||||||
|
{siteTitle}
|
||||||
|
</Link>
|
||||||
|
</Typography>
|
||||||
|
<Button
|
||||||
|
color="inherit"
|
||||||
|
component="a"
|
||||||
|
href="https://github.com/dkhamsing/open-source-ios-apps"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
GitHub
|
||||||
|
</Button>
|
||||||
|
</Toolbar>
|
||||||
|
</AppBar>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Header
|
46
gatsby/src/layout/index.tsx
Normal file
46
gatsby/src/layout/index.tsx
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import React, { FC } from 'react'
|
||||||
|
import { makeStyles, ThemeProvider } from '@material-ui/styles'
|
||||||
|
import { Theme } from '@material-ui/core'
|
||||||
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
|
import Header from './header'
|
||||||
|
import Footer from './footer'
|
||||||
|
import themes from '../theme'
|
||||||
|
import useSiteMetadata from '../hooks/useSiteMetadata'
|
||||||
|
import { RootState } from '../redux/store'
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) => ({
|
||||||
|
root: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
minHeight: '100vh',
|
||||||
|
backgroundColor: theme.palette.background.default,
|
||||||
|
},
|
||||||
|
main: {
|
||||||
|
backgroundColor: theme.palette.background.default,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
const LayoutComponent: FC = ({ children }) => {
|
||||||
|
const classes = useStyles()
|
||||||
|
const { title } = useSiteMetadata()
|
||||||
|
return (
|
||||||
|
<div className={classes.root}>
|
||||||
|
<Header siteTitle={title} />
|
||||||
|
<main className={classes.main}>{children}</main>
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const Layout: FC = ({ children }) => {
|
||||||
|
const { theme } = useSelector((state: RootState) => state.app)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeProvider theme={themes[theme]}>
|
||||||
|
<LayoutComponent>{children}</LayoutComponent>
|
||||||
|
</ThemeProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Layout
|
17
gatsby/src/libs/wrapPageElement.tsx
Normal file
17
gatsby/src/libs/wrapPageElement.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import React, { ReactNode } from 'react'
|
||||||
|
import { ThemeProvider } from '@material-ui/styles'
|
||||||
|
import { CssBaseline } from '@material-ui/core'
|
||||||
|
|
||||||
|
import themes from '../theme'
|
||||||
|
import Layout from '../layout'
|
||||||
|
|
||||||
|
const wrapPageElement = ({ element }: { element: ReactNode }) => {
|
||||||
|
return (
|
||||||
|
<ThemeProvider theme={themes['light']}>
|
||||||
|
<CssBaseline />
|
||||||
|
<Layout>{element}</Layout>
|
||||||
|
</ThemeProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default wrapPageElement
|
10
gatsby/src/libs/wrapRootElement.tsx
Normal file
10
gatsby/src/libs/wrapRootElement.tsx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import React, { ReactNode } from 'react'
|
||||||
|
import { Provider } from 'react-redux'
|
||||||
|
|
||||||
|
import store from '../redux/store'
|
||||||
|
|
||||||
|
const wrapRootElement = ({ element }: { element: ReactNode }) => {
|
||||||
|
return <Provider store={store}>{element}</Provider>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default wrapRootElement
|
30
gatsby/src/pages/404.tsx
Normal file
30
gatsby/src/pages/404.tsx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import React, { FC } from 'react'
|
||||||
|
import { makeStyles } from '@material-ui/styles'
|
||||||
|
import { Theme } from '@material-ui/core'
|
||||||
|
|
||||||
|
import SEO from '../components/seo'
|
||||||
|
import { Typography, Container } from '@material-ui/core'
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) => ({
|
||||||
|
root: {
|
||||||
|
marginTop: theme.spacing(8),
|
||||||
|
marginBottom: theme.spacing(2),
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
const NotFoundPage: FC = () => {
|
||||||
|
const classes = useStyles()
|
||||||
|
return (
|
||||||
|
<Container maxWidth="md" className={classes.root}>
|
||||||
|
<SEO title="404: Not found" />
|
||||||
|
<Typography variant="h2" gutterBottom component="h1">
|
||||||
|
NOT FOUND
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body1">
|
||||||
|
You just hit a route that doesn't exist... the sadness.
|
||||||
|
</Typography>
|
||||||
|
</Container>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NotFoundPage
|
158
gatsby/src/pages/index.tsx
Normal file
158
gatsby/src/pages/index.tsx
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
Grid,
|
||||||
|
List,
|
||||||
|
ListItem,
|
||||||
|
ListItemText,
|
||||||
|
Theme,
|
||||||
|
Typography,
|
||||||
|
} from '@material-ui/core'
|
||||||
|
import { makeStyles } from '@material-ui/styles'
|
||||||
|
import { graphql, Link } from 'gatsby'
|
||||||
|
import React, { FC } from 'react'
|
||||||
|
import Hero from '../components/hero'
|
||||||
|
import SEO from '../components/seo'
|
||||||
|
import { Category } from '../types'
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) => ({
|
||||||
|
wrapper: {
|
||||||
|
paddingLeft: theme.spacing(2),
|
||||||
|
paddingRight: theme.spacing(2),
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
marginTop: theme.spacing(2),
|
||||||
|
marginBottom: theme.spacing(2),
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
const CategoryItem = ({
|
||||||
|
category,
|
||||||
|
categories,
|
||||||
|
}: {
|
||||||
|
category: Category
|
||||||
|
categories: Category[]
|
||||||
|
}) => {
|
||||||
|
const classes = useStyles()
|
||||||
|
|
||||||
|
const childCategories = categories.filter(
|
||||||
|
cat => cat.parentSlug === category.slug,
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid item xs={12} sm={6} md={4}>
|
||||||
|
<Card>
|
||||||
|
<CardContent>
|
||||||
|
<Typography variant="h3">{category.title}</Typography>
|
||||||
|
<Typography>{category.description}</Typography>
|
||||||
|
<Typography>{category.projectCount} projects</Typography>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
component={Link}
|
||||||
|
fullWidth
|
||||||
|
to={`/category/${category.slug}/`}
|
||||||
|
className={classes.button}
|
||||||
|
>
|
||||||
|
Browse {category.title}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{childCategories.length === 0 ? null : (
|
||||||
|
<>
|
||||||
|
<Typography variant="h5" component="h4">
|
||||||
|
Child categories:
|
||||||
|
</Typography>
|
||||||
|
<List>
|
||||||
|
{childCategories.map(childCategory => {
|
||||||
|
return (
|
||||||
|
<ListItem
|
||||||
|
button
|
||||||
|
key={childCategory.id}
|
||||||
|
component={Link}
|
||||||
|
to={`/category/${childCategory.slug}/`}
|
||||||
|
>
|
||||||
|
<ListItemText
|
||||||
|
primary={`${childCategory.title} (${childCategory.projectCount} projects)`}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</List>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
type IndexPageProps = {
|
||||||
|
data: {
|
||||||
|
allAppCategory: {
|
||||||
|
edges: {
|
||||||
|
node: Category
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
allAppProject: {
|
||||||
|
totalCount: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const IndexPage: FC<IndexPageProps> = props => {
|
||||||
|
const classes = useStyles()
|
||||||
|
|
||||||
|
const { edges: categoryEdges } = props.data.allAppCategory
|
||||||
|
const projectCount = props.data.allAppProject.totalCount
|
||||||
|
|
||||||
|
const categories = categoryEdges.map(e => e.node)
|
||||||
|
|
||||||
|
const topLevelCategories = categories.filter(category => {
|
||||||
|
return category.parentSlug === null
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SEO title="Home" />
|
||||||
|
<Hero
|
||||||
|
title="Open Source iOS Apps"
|
||||||
|
description={`A community curated set of ${projectCount} open source iOS apps.`}
|
||||||
|
/>
|
||||||
|
<div className={classes.wrapper}>
|
||||||
|
<Grid container spacing={2}>
|
||||||
|
{topLevelCategories.map(cat => {
|
||||||
|
return (
|
||||||
|
<CategoryItem
|
||||||
|
key={cat.id}
|
||||||
|
category={cat}
|
||||||
|
categories={categories}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</Grid>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const pageQuery = graphql`
|
||||||
|
query IndexPageQuery {
|
||||||
|
allAppCategory {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
slug
|
||||||
|
parentSlug
|
||||||
|
projectCount
|
||||||
|
description
|
||||||
|
title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
allAppProject {
|
||||||
|
totalCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export default IndexPage
|
24
gatsby/src/redux/appModule.ts
Normal file
24
gatsby/src/redux/appModule.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { createSlice } from '@reduxjs/toolkit'
|
||||||
|
|
||||||
|
export interface AppState {
|
||||||
|
theme: 'light' | 'dark'
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: AppState = {
|
||||||
|
theme: 'light',
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = createSlice({
|
||||||
|
name: 'app',
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
toggleTheme(state) {
|
||||||
|
const newTheme = state.theme === 'light' ? 'dark' : 'light'
|
||||||
|
state.theme = newTheme
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export const { toggleTheme } = app.actions
|
||||||
|
|
||||||
|
export default app.reducer
|
23
gatsby/src/redux/persistStore.ts
Normal file
23
gatsby/src/redux/persistStore.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { RootState } from './store'
|
||||||
|
|
||||||
|
export const loadState: () => RootState = () => {
|
||||||
|
try {
|
||||||
|
const serializedState = localStorage.getItem('state')
|
||||||
|
if (serializedState === null || serializedState === 'undefined') {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
return JSON.parse(serializedState)
|
||||||
|
} catch (err) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const saveState = (state: Partial<RootState>) => {
|
||||||
|
try {
|
||||||
|
const serializedState = JSON.stringify(state)
|
||||||
|
localStorage.setItem('state', serializedState)
|
||||||
|
} catch (err) {
|
||||||
|
// Ignore write errors
|
||||||
|
}
|
||||||
|
}
|
26
gatsby/src/redux/store.ts
Normal file
26
gatsby/src/redux/store.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { configureStore } from '@reduxjs/toolkit'
|
||||||
|
import { combineReducers } from 'redux'
|
||||||
|
|
||||||
|
import app from '../redux/appModule'
|
||||||
|
import { loadState, saveState } from './persistStore'
|
||||||
|
|
||||||
|
const preloadedState = loadState()
|
||||||
|
|
||||||
|
const rootReducer = combineReducers({
|
||||||
|
app,
|
||||||
|
})
|
||||||
|
|
||||||
|
const store = configureStore({
|
||||||
|
reducer: rootReducer,
|
||||||
|
preloadedState,
|
||||||
|
})
|
||||||
|
|
||||||
|
store.subscribe(() => {
|
||||||
|
const { app } = store.getState()
|
||||||
|
saveState({ app })
|
||||||
|
})
|
||||||
|
|
||||||
|
export type RootState = ReturnType<typeof rootReducer>
|
||||||
|
export type RootDispatch = typeof store.dispatch
|
||||||
|
|
||||||
|
export default store
|
93
gatsby/src/templates/category.tsx
Normal file
93
gatsby/src/templates/category.tsx
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import {
|
||||||
|
Button,
|
||||||
|
createStyles,
|
||||||
|
Grid,
|
||||||
|
makeStyles,
|
||||||
|
Theme,
|
||||||
|
Typography,
|
||||||
|
} from '@material-ui/core'
|
||||||
|
import { graphql, Link } from 'gatsby'
|
||||||
|
import React from 'react'
|
||||||
|
import Hero from '../components/hero'
|
||||||
|
import ProjectCard from '../components/ProjectCard'
|
||||||
|
import SEO from '../components/seo'
|
||||||
|
import { Category, Project } from '../types'
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
wrapper: {
|
||||||
|
paddingLeft: theme.spacing(2),
|
||||||
|
paddingRight: theme.spacing(2),
|
||||||
|
},
|
||||||
|
backButton: {
|
||||||
|
textAlign: 'center',
|
||||||
|
margin: theme.spacing(4),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
data: {
|
||||||
|
appCategory: Category
|
||||||
|
allAppProject: {
|
||||||
|
edges: {
|
||||||
|
node: Project
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const CategoryTemplate: React.FC<Props> = props => {
|
||||||
|
const classes = useStyles()
|
||||||
|
|
||||||
|
const category = props.data.appCategory
|
||||||
|
const projectEdges = props.data.allAppProject.edges
|
||||||
|
const projects: Project[] = projectEdges.map(n => n.node)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SEO title="Home" />
|
||||||
|
<Hero
|
||||||
|
title={`Category: ${category.title}`}
|
||||||
|
description={category.description || ''}
|
||||||
|
></Hero>
|
||||||
|
<Typography className={classes.backButton}>
|
||||||
|
<Button component={Link} to="/" variant="contained">
|
||||||
|
Back to category list
|
||||||
|
</Button>
|
||||||
|
</Typography>
|
||||||
|
<div className={classes.wrapper}>
|
||||||
|
<Grid container spacing={2}>
|
||||||
|
{projects.map(project => {
|
||||||
|
return (
|
||||||
|
<Grid item xs={12} sm={6} key={project.id}>
|
||||||
|
<ProjectCard project={project} />
|
||||||
|
</Grid>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</Grid>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CategoryTemplate
|
||||||
|
|
||||||
|
export const pageQuery = graphql`
|
||||||
|
query CategoryPageQuery($slug: String!) {
|
||||||
|
appCategory(slug: { eq: $slug }) {
|
||||||
|
id
|
||||||
|
description
|
||||||
|
slug
|
||||||
|
title
|
||||||
|
}
|
||||||
|
|
||||||
|
allAppProject(filter: { category_ids: { eq: $slug } }) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
...ProjectCardFields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
74
gatsby/src/templates/tag.tsx
Normal file
74
gatsby/src/templates/tag.tsx
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import { createStyles, Grid, makeStyles, Theme } from '@material-ui/core'
|
||||||
|
import { graphql } from 'gatsby'
|
||||||
|
import React from 'react'
|
||||||
|
import Hero from '../components/hero'
|
||||||
|
import ProjectCard from '../components/ProjectCard'
|
||||||
|
import SEO from '../components/seo'
|
||||||
|
import { Category, Project } from '../types'
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
wrapper: {
|
||||||
|
paddingLeft: theme.spacing(2),
|
||||||
|
paddingRight: theme.spacing(2),
|
||||||
|
},
|
||||||
|
backButton: {
|
||||||
|
textAlign: 'center',
|
||||||
|
margin: theme.spacing(4),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
data: {
|
||||||
|
appCategory: Category
|
||||||
|
allAppProject: {
|
||||||
|
edges: {
|
||||||
|
node: Project
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pageContext: {
|
||||||
|
tag: string
|
||||||
|
slug: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const TagTemplate: React.FC<Props> = props => {
|
||||||
|
const classes = useStyles()
|
||||||
|
|
||||||
|
const projectEdges = props.data.allAppProject.edges
|
||||||
|
const projects: Project[] = projectEdges.map(n => n.node)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SEO title="Home" />
|
||||||
|
<Hero title={`Tag: #${props.pageContext.tag}`} description=""></Hero>
|
||||||
|
<div className={classes.wrapper}>
|
||||||
|
<Grid container spacing={2}>
|
||||||
|
{projects.map(project => {
|
||||||
|
return (
|
||||||
|
<Grid item xs={12} sm={6} key={project.id}>
|
||||||
|
<ProjectCard project={project} />
|
||||||
|
</Grid>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</Grid>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TagTemplate
|
||||||
|
|
||||||
|
export const pageQuery = graphql`
|
||||||
|
query TagPageQuery($slug: String!) {
|
||||||
|
allAppProject(filter: { tags: { eq: $slug } }) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
...ProjectCardFields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
46
gatsby/src/theme.ts
Normal file
46
gatsby/src/theme.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import deepMerge from 'deepmerge'
|
||||||
|
import { red } from '@material-ui/core/colors'
|
||||||
|
import {
|
||||||
|
createMuiTheme,
|
||||||
|
responsiveFontSizes,
|
||||||
|
ThemeOptions,
|
||||||
|
Theme,
|
||||||
|
} from '@material-ui/core/styles'
|
||||||
|
|
||||||
|
const makeTheme = (variant: ThemeOptions): Theme => {
|
||||||
|
const common = {
|
||||||
|
palette: {
|
||||||
|
primary: {
|
||||||
|
main: '#3E3F3A',
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: '#19857b',
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
main: red.A400,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const theme = createMuiTheme(deepMerge(common, variant))
|
||||||
|
return responsiveFontSizes(theme)
|
||||||
|
}
|
||||||
|
|
||||||
|
const light: ThemeOptions = {
|
||||||
|
palette: {
|
||||||
|
type: 'light',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const dark: ThemeOptions = {
|
||||||
|
palette: {
|
||||||
|
type: 'dark',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const themes = {
|
||||||
|
light: makeTheme(light),
|
||||||
|
dark: makeTheme(dark),
|
||||||
|
}
|
||||||
|
|
||||||
|
export default themes
|
25
gatsby/src/types.ts
Normal file
25
gatsby/src/types.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
export type Category = {
|
||||||
|
id: string
|
||||||
|
slug: string
|
||||||
|
parentSlug: string | null
|
||||||
|
projectCount: number
|
||||||
|
description: string | null
|
||||||
|
title: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Project = {
|
||||||
|
category_ids: string[]
|
||||||
|
date_added: string
|
||||||
|
description: string | null
|
||||||
|
homepage: string | null
|
||||||
|
id: string
|
||||||
|
itunes: string | null
|
||||||
|
lang: string | null
|
||||||
|
license: string
|
||||||
|
screenshots: string[]
|
||||||
|
source: string | null
|
||||||
|
stars: number
|
||||||
|
suggested_by: string
|
||||||
|
tags: string[]
|
||||||
|
title: string
|
||||||
|
}
|
BIN
gatsby/static/favicon.ico
Normal file
BIN
gatsby/static/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 198 B |
2
gatsby/static/robots.txt
Normal file
2
gatsby/static/robots.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
User-agent: *
|
||||||
|
Disallow:
|
19
gatsby/tsconfig.json
Normal file
19
gatsby/tsconfig.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"module": "commonjs",
|
||||||
|
"lib": ["dom", "esnext"],
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"sourceMap": true,
|
||||||
|
"removeComments": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"strict": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"],
|
||||||
|
"exclude": ["node_modules", "public", ".cache"]
|
||||||
|
}
|
13578
gatsby/yarn.lock
Normal file
13578
gatsby/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
2
netlify.toml
Normal file
2
netlify.toml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[build]
|
||||||
|
base = "gatsby"
|
Loading…
Reference in New Issue
Block a user