├── .gitignore
├── LICENSE
├── README.md
├── example
├── .env.development
├── .env.production
├── README.md
├── gatsby-config.js
├── package.json
├── setup.js
├── src
│ └── @draftbox-co
│ │ └── gatsby-theme-ghost-attila
│ │ └── components
│ │ ├── disqus.jsx
│ │ └── fb-comments.jsx
└── static
│ ├── alternateLogo.svg
│ ├── cover.png
│ ├── facebookImage.png
│ ├── favicon.ico
│ ├── favicon.png
│ ├── images
│ └── logo.svg
│ ├── logo.svg
│ ├── robots.txt
│ └── twitterImage.png
├── gatsby-theme-ghost-attila
├── README.md
├── gatsby-browser.js
├── gatsby-config.js
├── gatsby-node.js
├── package.json
├── plugins
│ └── gatsby-plugin-ghost-manifest
│ │ ├── .babelrc
│ │ ├── common.js
│ │ ├── gatsby-node.js
│ │ ├── gatsby-ssr.js
│ │ ├── index.js
│ │ ├── package.json
│ │ ├── src
│ │ ├── common.js
│ │ ├── gatsby-node.js
│ │ └── gatsby-ssr.js
│ │ └── yarn.lock
├── src
│ ├── amp-styles
│ │ └── post.amp.css
│ ├── components
│ │ ├── Layout.jsx
│ │ ├── contact-form.jsx
│ │ ├── copy-link.jsx
│ │ ├── disqus.jsx
│ │ ├── fb-comments.jsx
│ │ ├── footer.jsx
│ │ ├── header.jsx
│ │ ├── meta
│ │ │ ├── ArticleMeta.js
│ │ │ ├── AuthorMeta.js
│ │ │ ├── ContactMeta.js
│ │ │ ├── ImageMeta.js
│ │ │ ├── MetaData.js
│ │ │ ├── WebsiteMeta.js
│ │ │ ├── getAuthorProperties.js
│ │ │ └── index.js
│ │ ├── navbar.jsx
│ │ ├── pagination.jsx
│ │ ├── post-card.jsx
│ │ └── subscribe-form.jsx
│ ├── context
│ │ └── form-context.jsx
│ ├── hook
│ │ └── useForm.jsx
│ ├── images
│ │ ├── copied.svg
│ │ ├── copy.svg
│ │ ├── ghost-icon.png
│ │ ├── publication-cover.png
│ │ └── rss.svg
│ ├── pages
│ │ ├── 404.jsx
│ │ ├── contact.jsx
│ │ └── offline.jsx
│ ├── styles
│ │ ├── font
│ │ │ ├── icons.eot
│ │ │ ├── icons.svg
│ │ │ ├── icons.ttf
│ │ │ ├── icons.woff
│ │ │ └── icons.woff2
│ │ ├── prism-theme
│ │ │ └── prism_dracula.scss
│ │ └── sass
│ │ │ ├── _colors.scss
│ │ │ ├── _fonts.scss
│ │ │ ├── _highlight.scss
│ │ │ ├── _icons.scss
│ │ │ ├── _normalize.scss
│ │ │ ├── _utilities.scss
│ │ │ └── style.scss
│ ├── templates
│ │ ├── authorTemplate.jsx
│ │ ├── indexTemplate.jsx
│ │ ├── pageTemplate.jsx
│ │ ├── postTemplate.amp.jsx
│ │ ├── postTemplate.jsx
│ │ └── tagsTemplate.jsx
│ └── utils
│ │ ├── .ghost.json
│ │ ├── fragments.js
│ │ ├── rss
│ │ └── generate-feed.js
│ │ └── siteConfig.js
└── static
│ ├── alternateLogo.svg
│ ├── cover.png
│ ├── coverImage.png
│ ├── facebookImage.png
│ ├── favicon.ico
│ ├── favicon.png
│ ├── fbCardImage.png
│ ├── images
│ └── logo.svg
│ ├── logo.svg
│ ├── robots.txt
│ ├── sw.js
│ ├── twitterCardImage.png
│ └── twitterImage.png
├── package.json
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | .cache
2 | public
3 | node_modules
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Draftbox
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://draftbox.co)
2 |
3 | # gatsby-attila-theme-ghost
4 |
5 | A Gatsby theme plugin for creating blogs from headless Ghost CMS.
6 |
7 | Turn your Ghost blog into a lightning fast static website. This Gatsby theme is a frontend replacement of the Ghost handlebars engine featuring the standard Ghost Casper skin and functionality. All content is sourced from a headless Ghost CMS.
8 |
9 | > This theme is being used at [Draftbox](https://draftbox.co). Get lightning fast, secure front-end for your WordPress or Ghost blog, in 5 minutes or less, without coding. For our fellow devs, we also provide code export feature.
10 |
11 | ## Demo
12 |
13 | Play with the [Demo](https://ghost-attila-preview.draftbox.co/) to get a first impression.
14 |
15 |
16 | ## Features
17 |
18 | - Ghost Attila skin
19 | - SEO optimized
20 | - Fully responsive
21 | - Gatsby images
22 | - Styled 404 page
23 | - RSS Feed
24 | - AMP Pages
25 | - Sitemap
26 | - Contact Form
27 | - Subscribe Form
28 | - Social Sharing
29 | - Composable and extensible
30 |
31 |
32 | ## Quick Start
33 |
34 | Head over to the [starter repo](https://github.com/draftbox-co/gatsby-attila-theme-starter) to get up and running quickly! The starter is recommended if you are creating a new site.
35 |
36 |
37 | ## Installation
38 |
39 | This repository contains the example code **and** the Gatsby theme. If you are here to install the Gatsby theme plugin in your existing project, check out the [theme specific README](/gatsby-attila-theme-ghost/README.md) for further details.
40 |
41 | In case you want to work with this repository (for local development, pull requests, etc.):
42 |
43 | 1. Clone or fork this repository:
44 | ```bash
45 | git clone https://github.com/draftbox-co/gatsby-attila-theme-ghost.git
46 | cd gatsby-attila-theme-ghost
47 | ```
48 |
49 | 2. Run `yarn` to install dependencies.
50 |
51 | 3. Run `yarn workspace example develop` to start the example locally.
52 |
53 |
54 | ## Authors
55 | - Arun Priyadarshi ([@Gunnerforlife](https://github.com/Gunnerforlife)) – [Draftbox](https://draftbox.co)
56 | - Keyur Raval ([@thandaanda](https://github.com/thandaanda)) – [Draftbox](https://draftbox.co)
57 | - Shyam Lohar ([@shyamlohar](https://github.com/shyamlohar)) – [Draftbox](https://draftbox.co)
58 | - Tanmay Desai ([@tanmaydesai89](https://github.com/tanmaydesai89)) – [Draftbox](https://draftbox.co)
59 |
60 | ## Contributions
61 | PRs are welcome! Consider contributing to this project if you are missing feature that is also useful for others.
62 |
63 | ## Credits
64 | Inspired from [gatsby-theme-try-ghost](https://github.com/styxlab/gatsby-theme-try-ghost)
65 |
66 | Theme Ported from [Attila](https://github.com/zutrinken/attila)
67 |
68 | Special Thanks to [Ghost](https://ghost.org)
69 |
70 | # Copyright & License
71 |
72 | Copyright (c) 2020 [Draftbox](https://draftbox.co) - Released under the [MIT license](LICENSE).
--------------------------------------------------------------------------------
/example/.env.development:
--------------------------------------------------------------------------------
1 | FORM_URL=http://localhost:3000/api/project/5e81b8923d8c2d64d8549add/forms
2 | GATSBY_DISQUS_SHORTNAME=
3 | GATSBY_FB_APP_ID=
4 |
--------------------------------------------------------------------------------
/example/.env.production:
--------------------------------------------------------------------------------
1 | FORM_URL=http://localhost:3000/api/project/5e81b8923d8c2d64d8549add/forms
2 | DISQUS_SHORTNAME=
3 | FB_APP_ID=
4 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # Gatsby Theme Minimal Example
2 |
3 | > See the [base README](https://github.com/draftbox-co/gatsby-attila-theme-ghost/blob/master/README.md) for instructions on how to use this example.
4 |
5 | # Copyright & License
6 |
7 | Copyright (c) 2020 Draftbox - Released under the [MIT license](LICENSE).
--------------------------------------------------------------------------------
/example/gatsby-config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [
3 | {
4 | resolve: `@draftbox-co/gatsby-theme-ghost-attila`,
5 | options: {
6 | siteConfig: {
7 | siteUrl: "https://ghost-attila-preview.draftbox.co",
8 | postsPerPage: 10,
9 | siteTitleMeta: "Built with Draftbox",
10 | siteDescriptionMeta:
11 | "Lightning fast, secure front-end for your WordPress or Ghost blog, without coding.",
12 | shareImageWidth: 1000,
13 | shareImageHeight: 523,
14 | shortTitle: "Built with Draftbox",
15 | siteIcon: "favicon.png",
16 | backgroundColor: "#e9e9e9",
17 | themeColor: "#15171A",
18 | apiUrl: "https://ghost.theasdfghjkl.com",
19 | subscribeWidget: {
20 | visible: true,
21 | title: "Subscribe to Built with Draftbox",
22 | helpText: "Get the latest posts delivered right to your inbox.",
23 | successMessage: "Thanks for subscribing to Built with Draftbox.",
24 | },
25 | header: {
26 | navigation: [
27 | {
28 | label: "Home",
29 | url: "https://ghost-attila-preview.draftbox.co/",
30 | },
31 | {
32 | label: "Contact",
33 | url: "https://ghost-attila-preview.draftbox.co/contact",
34 | },
35 | ],
36 | },
37 | footer: {
38 | copyright: "Built with Draftbox",
39 | navigation: [
40 | {
41 | label: "Home",
42 | url: "https://ghost-attila-preview.draftbox.co/",
43 | },
44 | {
45 | label: "Sitemap",
46 | url: "https://ghost-attila-preview.draftbox.co/sitemap.xml",
47 | },
48 | {
49 | label: "RSS",
50 | url: "https://ghost-attila-preview.draftbox.co/rss.xml",
51 | },
52 | {
53 | label: "Contact",
54 | url: "https://ghost-attila-preview.draftbox.co/contact",
55 | },
56 | ],
57 | },
58 | socialLinks: {
59 | twitter: "https://twitter.com/@DraftboxHQ",
60 | facebook: "",
61 | instagram: "",
62 | linkedin: "",
63 | github: "",
64 | whatsapp: "",
65 | pinterest: "",
66 | youtube: "",
67 | dribbble: "",
68 | behance: "",
69 | externalLink: "",
70 | },
71 | contactWidget: {
72 | title: "Contact Built with Draftbox",
73 | successMessage: "We’ll get in touch with you soon.",
74 | },
75 | metadata: {
76 | title: "Built with Draftbox",
77 | description:
78 | "Lightning fast, secure front-end for your WordPress or Ghost blog, without coding. Draftbox is a new-age blogging platform for everyone, built on Gatsby.",
79 | },
80 | twitterCard: {
81 | title: "Built with Draftbox",
82 | description:
83 | "Lightning fast, secure front-end for your WordPress or Ghost blog, without coding. Draftbox is a new-age blogging platform for everyone, built on Gatsby.",
84 | imageUrl: "twitterImage.png",
85 | username: "@DraftboxHQ",
86 | },
87 | facebookCard: {
88 | title: "Built with Draftbox",
89 | description:
90 | "Lightning fast, secure front-end for your WordPress or Ghost blog, without coding. Draftbox is a new-age blogging platform for everyone, built on Gatsby.",
91 | imageUrl: "facebookImage.png",
92 | appId: "",
93 | width: 1000,
94 | height: 523,
95 | },
96 | siteTitle: "Built with Draftbox",
97 | siteDescription:
98 | "Lightning fast, secure front-end for your WordPress or Ghost blog, without coding.",
99 | language: "en",
100 | logoUrl: "logo.svg",
101 | iconUrl:
102 | "https://ghost.theasdfghjkl.com/content/images/2020/05/draftbox-colored-icon.png",
103 | coverUrl: "cover.png",
104 | alternateLogoUrl: "alternateLogo.svg",
105 | themeConfig: {
106 | variables: [
107 | {
108 | varName: "--primary-color",
109 | value: "#e05431",
110 | },
111 | {
112 | varName: "--primary-color-active",
113 | value: "#d13f21",
114 | },
115 | {
116 | varName: "--cardo-font",
117 | value: "Cardo",
118 | },
119 | {
120 | varName: "--cardo-font-normal",
121 | value: "400",
122 | },
123 | {
124 | varName: "--fira-sans-font",
125 | value: "Fira Sans",
126 | },
127 | {
128 | varName: "--fira-sans-font-normal",
129 | value: "400",
130 | },
131 | {
132 | varName: "--fira-sans-font-semibold",
133 | value: "600",
134 | },
135 | {
136 | varName: "--fira-sans-font-bold",
137 | value: "700",
138 | },
139 | ],
140 | fonts: [
141 | {
142 | family: "Cardo",
143 | variants: ["400", "400i", "700"],
144 | fontDisplay: "swap",
145 | strategy: "selfHosted",
146 | },
147 | {
148 | family: "Fira Sans",
149 | variants: ["400", "500", "700"],
150 | fontDisplay: "swap",
151 | strategy: "selfHosted",
152 | },
153 | ],
154 | },
155 | },
156 | ghostConfig: {
157 | development: {
158 | apiUrl: "https://ghost.theasdfghjkl.com",
159 | contentApiKey: "3d17fad3efaa911df1ed577638",
160 | version: "v3",
161 | },
162 | production: {
163 | apiUrl: "https://ghost.theasdfghjkl.com",
164 | contentApiKey: "3d17fad3efaa911df1ed577638",
165 | version: "v3",
166 | },
167 | },
168 | },
169 | },
170 | ],
171 | };
172 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "author": "Draftbox",
6 | "license": "MIT",
7 | "scripts": {
8 | "develop": "gatsby develop",
9 | "build": "gatsby build",
10 | "serve": "gatsby serve"
11 | },
12 | "dependencies": {
13 | "@draftbox-co/gatsby-theme-ghost-attila": "^1.0.0",
14 | "disqus-react": "^1.0.7",
15 | "gatsby": "2.20.24",
16 | "node-fetch": "^2.6.0",
17 | "react": "^16.12.0",
18 | "react-dom": "^16.12.0",
19 | "react-facebook": "^8.1.4"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/example/setup.js:
--------------------------------------------------------------------------------
1 | (async () => {
2 | const gatsbyConfig = require("./gatsby-config");
3 | const fetch = require("node-fetch");
4 | const fs = require("fs");
5 | const path = require("path");
6 |
7 | if (gatsbyConfig.plugins.length) {
8 | const index = gatsbyConfig.plugins.findIndex(
9 | (obj) => obj.resolve === "@draftbox-co/gatsby-theme-ghost-attila"
10 | );
11 | console.log(index);
12 | if (index > -1) {
13 | const pluginOptions = gatsbyConfig.plugins[index];
14 | if (pluginOptions.options.ghostConfig) {
15 | console.log(process.argv[2]);
16 | const environment = process.argv[2] || "development";
17 | let config = {
18 | apiUrl: "",
19 | contentApiKey: "",
20 | version: "",
21 | };
22 | if (environment === "development") {
23 | config = Object.assign(
24 | config,
25 | pluginOptions.options.ghostConfig.development
26 | );
27 | }
28 | if (environment === "production") {
29 | config = Object.assign(
30 | config,
31 | pluginOptions.options.ghostConfig.production
32 | );
33 | }
34 | try {
35 | const apiUrl = `${config.apiUrl}/ghost/api/${config.version}/content/settings/?key=${config.contentApiKey}`;
36 | console.log(apiUrl);
37 | const resp = await fetch(apiUrl);
38 | console.log(resp.status);
39 | if (resp.status === 200) {
40 | const finalRes = await resp.json();
41 | console.log(finalRes);
42 | const siteData = finalRes.settings;
43 | const masterConfig = {
44 | subscribeWidget: {
45 | title: "",
46 | helpText: "",
47 | successMessage: "",
48 | visible: true,
49 | },
50 | footer: { copyright: "", navigation: [{ label: "", url: "" }] },
51 | header: { navigation: [{ label: "", url: "" }] },
52 | socialLinks: {
53 | twitter: "",
54 | facebook: "",
55 | instagram: "",
56 | linkedin: "",
57 | github: "",
58 | pinterest: "",
59 | youtube: "",
60 | dribbble: "",
61 | behance: "",
62 | externalLink: "",
63 | },
64 | contactWidget: { title: "", successMessage: "" },
65 | identity: {
66 | siteTitle: "",
67 | siteDescription: "",
68 | language: "en",
69 | postsPerPage: 10,
70 | logoUrl: "",
71 | iconUrl: "",
72 | coverUrl: "",
73 | alternateLogoUrl: "",
74 | },
75 | metadata: { title: "", description: "" },
76 | twitterCard: {
77 | title: "",
78 | description: "",
79 | imageUrl: "",
80 | username: "",
81 | },
82 | facebookCard: {
83 | title: "",
84 | description: "",
85 | imageUrl: "",
86 | appId: "",
87 | width: 1000,
88 | height: 523,
89 | },
90 | };
91 | const newSiteConfig = Object.assign({}, masterConfig);
92 | console.log(
93 | path.resolve(
94 | __dirname,
95 | "..",
96 | `gatsby-theme-ghost-attila`,
97 | "static"
98 | )
99 | );
100 | if (siteData.title) {
101 | newSiteConfig.subscribeWidget.title = `Subscribe to ${siteData.title}`;
102 | newSiteConfig.subscribeWidget.successMessage = `Thanks for subscribing to ${siteData.title}.`;
103 | newSiteConfig.subscribeWidget.helpText =
104 | "Get the latest posts delivered right to your inbox.";
105 | newSiteConfig.footer.copyright = siteData.title;
106 | newSiteConfig.contactWidget.title = `Contact ${siteData.title}`;
107 | newSiteConfig.contactWidget.successMessage = `We'll get in touch with you soon.`;
108 | newSiteConfig.identity.siteTitle = siteData.title || "";
109 | newSiteConfig.identity.siteDescription =
110 | siteData.description || "";
111 | newSiteConfig.identity.logoUrl = siteData.logo || "";
112 | newSiteConfig.identity.iconUrl = siteData.icon || "";
113 | newSiteConfig.identity.coverUrl = siteData.cover_image || "";
114 | newSiteConfig.identity.alternateLogoUrl = siteData.logo || "";
115 | newSiteConfig.metadata.title =
116 | siteData.meta_title || siteData.title || "";
117 | newSiteConfig.metadata.description =
118 | siteData.meta_description || siteData.description || "";
119 | newSiteConfig.twitterCard.title =
120 | siteData.twitter_title || siteData.title || "";
121 | newSiteConfig.twitterCard.description =
122 | siteData.twitter_description || siteData.description || "";
123 | newSiteConfig.twitterCard.imageUrl =
124 | siteData.twitter_image || siteData.cover_image || "";
125 | newSiteConfig.facebookCard.title =
126 | siteData.og_title || siteData.title || "";
127 | newSiteConfig.facebookCard.description =
128 | siteData.og_description || siteData.description || "";
129 | newSiteConfig.facebookCard.imageUrl =
130 | siteData.og_image || siteData.cover_image || "";
131 |
132 | const imageJSON = { "": "" };
133 | if (siteData.logo && siteData.logo !== "") {
134 | const resp = await fetch(siteData.logo);
135 | if (resp.status == 200) {
136 | const bufferData = await resp.buffer();
137 | const fileName = `logo${path.extname(siteData.logo)}`;
138 | fs.writeFileSync(
139 | path.resolve(
140 | __dirname,
141 | "..",
142 | `gatsby-theme-ghost-attila`,
143 | "static",
144 | fileName
145 | ),
146 | bufferData
147 | );
148 | imageJSON[siteData.logo] = fileName;
149 | } else {
150 | imageJSON[siteData.logo] = "";
151 | }
152 | }
153 |
154 | if (siteData.cover_image && siteData.cover_image !== "") {
155 | const resp = await fetch(siteData.cover_image);
156 | if (resp.status == 200) {
157 | const bufferData = await resp.buffer();
158 | const fileName = `coverImage${path.extname(
159 | siteData.cover_image
160 | )}`;
161 | fs.writeFileSync(
162 | path.resolve(
163 | __dirname,
164 | "..",
165 | `gatsby-theme-ghost-attila`,
166 | "static",
167 | fileName
168 | ),
169 | bufferData
170 | );
171 | imageJSON[siteData.cover_image] = fileName;
172 | } else {
173 | imageJSON[siteData.cover_image] = "";
174 | }
175 | }
176 |
177 | if (siteData.twitter_image && siteData.twitter_image !== "") {
178 | const resp = await fetch(siteData.twitter_image);
179 | if (resp.status == 200) {
180 | const bufferData = await resp.buffer();
181 | const fileName = `twitterCardImage${path.extname(
182 | siteData.twitter_image
183 | )}`;
184 | fs.writeFileSync(
185 | path.resolve(
186 | __dirname,
187 | "..",
188 | `gatsby-theme-ghost-attila`,
189 | "static",
190 | fileName
191 | ),
192 | bufferData
193 | );
194 | imageJSON[siteData.twitter_image] = fileName;
195 | } else {
196 | imageJSON[siteData.twitter_image] = "";
197 | }
198 | }
199 |
200 | if (siteData.og_image && siteData.og_image !== "") {
201 | const resp = await fetch(siteData.og_image);
202 | if (resp.status == 200) {
203 | const bufferData = await resp.buffer();
204 | const fileName = `fbCardImage${path.extname(
205 | siteData.og_image
206 | )}`;
207 | fs.writeFileSync(
208 | path.resolve(
209 | __dirname,
210 | "..",
211 | `gatsby-theme-ghost-attila`,
212 | "static",
213 | fileName
214 | ),
215 | bufferData
216 | );
217 | imageJSON[siteData.og_image] = fileName;
218 | } else {
219 | imageJSON[siteData.og_image] = "";
220 | }
221 | }
222 | console.log(imageJSON);
223 | newSiteConfig.identity.logoUrl =
224 | imageJSON[newSiteConfig.identity.logoUrl];
225 | newSiteConfig.identity.coverUrl =
226 | imageJSON[newSiteConfig.identity.coverUrl];
227 | newSiteConfig.identity.alternateLogoUrl =
228 | imageJSON[newSiteConfig.identity.alternateLogoUrl];
229 | newSiteConfig.twitterCard.imageUrl =
230 | imageJSON[newSiteConfig.twitterCard.imageUrl];
231 | newSiteConfig.facebookCard.imageUrl =
232 | imageJSON[newSiteConfig.facebookCard.imageUrl];
233 | const finalConfig = Object.assign(
234 | {},
235 | pluginOptions.options.siteConfig,
236 | siteConfig,
237 | {
238 | metadata: newSiteConfig.metadata,
239 | twitterCard: newSiteConfig.twitterCard,
240 | facebookCard: newSiteConfig.facebookCard,
241 | },
242 | newSiteConfig.identity
243 | );
244 | console.log("new json", JSON.stringify(finalConfig, null, 2));
245 | pluginOptions.options.siteConfig = finalConfig;
246 | gatsbyConfig.plugins[index] = pluginOptions;
247 | fs.writeFileSync(
248 | path.join(__dirname, "gatsby-config.js"),
249 | `module.exports = ${JSON.stringify(gatsbyConfig, null, 2)}`
250 | );
251 | console.log("done");
252 | }
253 | } else {
254 | console.error("Config is invalid");
255 | }
256 | } catch (err) {
257 | console.log(err);
258 | console.error("Config is invalid");
259 | }
260 | } else {
261 | console.error("Theme plugin option doesn't exists");
262 | }
263 | } else {
264 | console.error("Theme plugin doesn't exists");
265 | }
266 | } else {
267 | console.error("Empty plugin array");
268 | }
269 | })();
270 |
--------------------------------------------------------------------------------
/example/src/@draftbox-co/gatsby-theme-ghost-attila/components/disqus.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { DiscussionEmbed } from "disqus-react";
3 |
4 | const Disqus = props => {
5 | const disqusConfig = {
6 | shortname: process.env.GATSBY_DISQUS_SHORTNAME,
7 | identifier: props.slug,
8 | title: props.slug
9 | };
10 | return process.env.GATSBY_DISQUS_SHORTNAME ? (
11 |
12 | ) : (
13 | <>>
14 | );
15 | };
16 |
17 | export default Disqus;
18 |
--------------------------------------------------------------------------------
/example/src/@draftbox-co/gatsby-theme-ghost-attila/components/fb-comments.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { FacebookProvider, Comments } from 'react-facebook';
3 |
4 | const FbComments = props => {
5 | return process.env.GATSBY_FB_APP_ID ? (
6 |
7 |
8 |
9 | ) : (
10 | <>>
11 | );
12 | };
13 |
14 | export default FbComments;
15 |
--------------------------------------------------------------------------------
/example/static/alternateLogo.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/example/static/cover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/draftbox-co/gatsby-attila-theme-ghost/e74f112be4994e19d842d0797b878d2b4f32dbea/example/static/cover.png
--------------------------------------------------------------------------------
/example/static/facebookImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/draftbox-co/gatsby-attila-theme-ghost/e74f112be4994e19d842d0797b878d2b4f32dbea/example/static/facebookImage.png
--------------------------------------------------------------------------------
/example/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/draftbox-co/gatsby-attila-theme-ghost/e74f112be4994e19d842d0797b878d2b4f32dbea/example/static/favicon.ico
--------------------------------------------------------------------------------
/example/static/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/draftbox-co/gatsby-attila-theme-ghost/e74f112be4994e19d842d0797b878d2b4f32dbea/example/static/favicon.png
--------------------------------------------------------------------------------
/example/static/images/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/example/static/logo.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/example/static/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Allow: /
3 |
--------------------------------------------------------------------------------
/example/static/twitterImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/draftbox-co/gatsby-attila-theme-ghost/e74f112be4994e19d842d0797b878d2b4f32dbea/example/static/twitterImage.png
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/README.md:
--------------------------------------------------------------------------------
1 | [](https://draftbox.co)
2 |
3 | # gatsby-attila-theme-ghost
4 |
5 | A Gatsby theme plugin for creating blogs from headless Ghost CMS.
6 |
7 | Turn your Ghost blog into a lightning fast static website. This Gatsby theme is a frontend replacement of the Ghost handlebars engine featuring the standard Casper 3 skin and functionality. All content is sourced from a headless Ghost CMS.
8 |
9 |
10 | > This theme is being used at [Draftbox](https://draftbox.co). Get lightning fast, secure front-end for your WordPress or Ghost blog, in 5 minutes or less, without coding. For our fellow devs, we also provide code export feature.
11 |
12 | ## Demo
13 |
14 | Play with the [Demo](https://ghost-attila-preview.draftbox.co/) to get a first impression.
15 |
16 |
17 | ## Features
18 |
19 | - Ghost Attila skin and functionality
20 | - SEO optimized
21 | - Fully responsive
22 | - Gatsby images
23 | - Styled 404 page
24 | - RSS Feed
25 | - AMP Pages
26 | - Sitemap
27 | - Contact Form
28 | - Subscribe Form
29 | - Social Sharing
30 | - Composable and extensible
31 |
32 |
33 | ## Installation
34 |
35 | > Head over to the [starter repo](https://github.com/draftbox-co/gatsby-attila-theme-starter) to get up and running quickly!
36 |
37 |
38 | If you want to add this blog theme to an existing site, follow these instructions:
39 |
40 | 1. Install the blog theme
41 |
42 | ```bash
43 | yarn add @draftbox-co/gatsby-theme-ghost-attila
44 | # or
45 | npm install @draftbox-co/gatsby-theme-ghost-attila --save
46 | ```
47 |
48 | 2. Add the following configuration to your `gatsby-config.js` file
49 |
50 | ```js
51 | // gatsby-config.js
52 | module.exports = {
53 | plugins: [
54 | {
55 | resolve: `@draftbox-co/gatsby-theme-ghost-attila`,
56 | options: {
57 | siteConfig: {
58 | siteUrl: `https://your-bog.com`,
59 | postsPerPage: 12,
60 | siteTitleMeta: `Gatsby Frontend powered by headless Ghost CMS`,
61 | siteDescriptionMeta: `Turn your Ghost blog into a lightning fast static website with Gatsby`,
62 | shareImageWidth: 1000,
63 | shareImageHeight: 523,
64 | shortTitle: `Ghost`,
65 | siteIcon: `favicon.png`,
66 | backgroundColor: `#e9e9e9`,
67 | themeColor: `#15171A`,
68 | },
69 | ghostConfig: {
70 | "development": {
71 | "apiUrl": "http://localhost:2368",
72 | "contentApiKey": "9fcfdb1e5ea5b472e2e5b92942",
73 | },
74 | "production": {
75 | "apiUrl": "https://your-ghost-cms.com",
76 | "contentApiKey": "9fcfdb1e5ea5b472e2e5b92942",
77 | },
78 | },
79 | },
80 | },
81 | ],
82 | }
83 | ```
84 |
85 | 3. Update siteConfig
86 |
87 | In the configuration shown above, the most important fields to be changed are `siteUrl`, `siteTitleMeta` and `siteDescriptionMeta`. Update at least those to fit your needs. Also make sure your `favicon.png` can be found in folder `static` of your working directory.
88 |
89 | 4. Ghost Content API Keys
90 |
91 | Change the `apiUrl` value to the URL of your Ghost CMS site. Next, update the `contentApiKey` value to a key associated with the Ghost CMS site. A key can be provided by creating an integration within Ghost Admin. Navigate to Integrations and click "Add new integration". Name the integration appropriately and click create.
92 |
93 |
94 |
95 | ## Running
96 |
97 | Start the development server. You now have a Gatsby site pulling content from headless Ghost.
98 |
99 | ```bash
100 | gatsby develop
101 | ```
102 |
103 |
104 | ## Optimizing
105 |
106 | You can disable the default Ghost Handlebars Theme front-end by enabling the `Make this site private` flag within your Ghost settings. This enables password protection in front of the Ghost install and sets `` so your Gatsby front-end becomes the source of truth for SEO.
107 |
108 | ## Authors
109 | - Arun Priyadarshi ([@Gunnerforlife](https://github.com/Gunnerforlife)) – [Draftbox](https://draftbox.co)
110 | - Keyur Raval ([@thandaanda](https://github.com/thandaanda)) – [Draftbox](https://draftbox.co)
111 | - Shyam Lohar ([@shyamlohar](https://github.com/shyamlohar)) – [Draftbox](https://draftbox.co)
112 | - Tanmay Desai ([@tanmaydesai89](https://github.com/tanmaydesai89)) – [Draftbox](https://draftbox.co)
113 |
114 | ## Contributions
115 | PRs are welcome! Consider contributing to this project if you are missing feature that is also useful for others.
116 |
117 | # Copyright & License
118 |
119 | Copyright (c) 2020 [Draftbox](https://draftbox.co) - Released under the [MIT license](LICENSE).
120 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/gatsby-browser.js:
--------------------------------------------------------------------------------
1 | /* global window, document */
2 |
3 | const instaEmbedClasses = [".instagram-media"].join(",");
4 | const twitterEmbedClasses = [
5 | ".twitter-tweet",
6 | ".twitter-timeline",
7 | ".twitter-follow-button",
8 | ".twitter-share-button",
9 | ].join(",");
10 | const scrollTo = (id) => () => {
11 | const el = document.querySelector(id);
12 | if (el) return window.scrollTo(0, el.offsetTop - 20);
13 | return false;
14 | };
15 |
16 | const injectScript = function injectScript() {
17 | var js = document.createElement("script");
18 | var firstScript = document.getElementsByTagName("script")[0];
19 | js.id = "gatsby-plugin-instagram";
20 | js.src = "https://instagram.com/embed.js";
21 | firstScript.parentNode.insertBefore(js, firstScript);
22 | injected = true;
23 | if (
24 | typeof instgrm !== "undefined" &&
25 | window.instgrm.Embeds &&
26 | typeof window.instgrm.Embeds.process === "function"
27 | ) {
28 | // manual process
29 | window.instgrm.Embeds.process();
30 | }
31 | return true;
32 | };
33 |
34 | var injectTwitterScript = function injectTwitterScript() {
35 | function addJS(jsCode) {
36 | var s = document.createElement("script");
37 | s.type = "text/javascript";
38 | s.innerText = jsCode;
39 | document.getElementsByTagName("head")[0].appendChild(s);
40 | injectedTwitterScript = true;
41 |
42 | if (
43 | typeof twttr !== "undefined" &&
44 | window.twttr.widgets &&
45 | typeof window.twttr.widgets.load === "function"
46 | ) {
47 | window.twttr.widgets.load();
48 | }
49 | }
50 |
51 | addJS(
52 | '\n window.twttr = (function(d, s, id) {\n var js,\n fjs = d.getElementsByTagName(s)[0],\n t = window.twttr || {};\n if (d.getElementById(id)) return t;\n js = d.createElement(s);\n js.id = id;\n js.src = "https://platform.twitter.com/widgets.js";\n fjs.parentNode.insertBefore(js, fjs);\n t._e = [];\n t.ready = function(f) {\n t._e.push(f);\n };\n return t;\n })(document, "script", "twitter-wjs");\n '
53 | );
54 | };
55 | let injected = false;
56 | let injectedTwitterScript = false;
57 |
58 | export const onRouteUpdate = ({ location: { hash } }) => {
59 | if (hash) {
60 | window.setTimeout(scrollTo(hash), 10);
61 | }
62 |
63 | if (document.querySelector(instaEmbedClasses) !== null) {
64 | setTimeout(() => {
65 | if (!injected) {
66 | window.addEventListener(
67 | "scroll",
68 | function () {
69 | injectScript();
70 | },
71 | { once: true }
72 | );
73 | }
74 | }, 2000);
75 | }
76 |
77 | if (document.querySelector(twitterEmbedClasses) !== null) {
78 | setTimeout(() => {
79 | if (!injectedTwitterScript) {
80 | window.addEventListener(
81 | "scroll",
82 | function () {
83 | injectTwitterScript();
84 | },
85 | { once: true }
86 | );
87 | }
88 | }, 2000);
89 | }
90 | };
91 |
92 | export const registerServiceWorker = () => true;
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/gatsby-config.js:
--------------------------------------------------------------------------------
1 | const path = require(`path`);
2 |
3 | const siteConfigDefaults = require(`./src/utils/siteConfig`);
4 | const ghostConfigDefaults = require(`./src/utils/.ghost.json`);
5 |
6 | const generateRSSFeed = require(`./src/utils/rss/generate-feed`);
7 |
8 | module.exports = (themeOptions) => {
9 | const siteConfig = themeOptions.siteConfig || siteConfigDefaults;
10 | const ghostConfig = themeOptions.ghostConfig || ghostConfigDefaults;
11 | const finalConfig =
12 | process.env.NODE_ENV === `development`
13 | ? ghostConfig.development
14 | : ghostConfig.production;
15 |
16 | siteConfig.apiUrl = finalConfig.apiUrl;
17 | const configOptions = {
18 | siteMetadata: siteConfig,
19 | plugins: [
20 | {
21 | resolve: `gatsby-plugin-sass`,
22 | options: {
23 | sassRuleModulesTest: /.*\.module\.s(a|c)ss$/,
24 | },
25 | },
26 | {
27 | resolve: `gatsby-plugin-page-creator`,
28 | options: {
29 | path: path.join(__dirname, `src`, `pages`),
30 | },
31 | },
32 | {
33 | resolve: `gatsby-source-filesystem`,
34 | options: {
35 | path: path.join(__dirname, `src`, `images`),
36 | name: `images`,
37 | },
38 | },
39 | `gatsby-plugin-sharp`,
40 | `gatsby-transformer-sharp`,
41 | {
42 | resolve: `gatsby-source-ghost`,
43 | options:
44 | process.env.NODE_ENV === `development`
45 | ? ghostConfig.development
46 | : ghostConfig.production,
47 | },
48 | {
49 | resolve: `gatsby-transformer-rehype`,
50 | options: {
51 | filter: (node) =>
52 | node.internal.type === `GhostPost` ||
53 | node.internal.type === `GhostPage`,
54 | plugins: [
55 | {
56 | resolve: `gatsby-rehype-prismjs`,
57 | },
58 | {
59 | resolve: `gatsby-rehype-ghost-links`,
60 | },
61 | ],
62 | },
63 | },
64 | /**
65 | * Utility Plugins
66 | */
67 | {
68 | resolve: require.resolve(`./plugins/gatsby-plugin-ghost-manifest`),
69 | options: {
70 | short_name: siteConfig.shortTitle,
71 | start_url: `/`,
72 | background_color: siteConfig.backgroundColor,
73 | theme_color: siteConfig.themeColor,
74 | display: `minimal-ui`,
75 | icon: `static/${siteConfig.siteIcon}`,
76 | legacy: true,
77 | query: `{
78 | site {
79 | siteMetadata {
80 | siteTitle
81 | siteDescription
82 | }
83 | }
84 | }`,
85 | },
86 | },
87 | {
88 | resolve: `gatsby-plugin-feed`,
89 | options: {
90 | query: `{
91 | allGhostSettings {
92 | edges {
93 | node {
94 | title
95 | description
96 | }
97 | }
98 | }
99 | }`,
100 | feeds: [generateRSSFeed(siteConfig)],
101 | },
102 | },
103 | {
104 | resolve: `gatsby-plugin-advanced-sitemap`,
105 | options: {
106 | query: `{
107 | allGhostPost {
108 | edges {
109 | node {
110 | id
111 | slug
112 | updated_at
113 | created_at
114 | feature_image
115 | }
116 | }
117 | }
118 | allGhostPage {
119 | edges {
120 | node {
121 | id
122 | slug
123 | updated_at
124 | created_at
125 | feature_image
126 | }
127 | }
128 | }
129 | allGhostTag {
130 | edges {
131 | node {
132 | id
133 | slug
134 | feature_image
135 | }
136 | }
137 | }
138 | allGhostAuthor {
139 | edges {
140 | node {
141 | id
142 | slug
143 | profile_image
144 | }
145 | }
146 | }
147 | }
148 | `,
149 | mapping: {
150 | allGhostPost: {
151 | sitemap: `posts`,
152 | },
153 | allGhostTag: {
154 | sitemap: `tags`,
155 | },
156 | allGhostAuthor: {
157 | sitemap: `authors`,
158 | },
159 | allGhostPage: {
160 | sitemap: `pages`,
161 | },
162 | },
163 | exclude: [
164 | `/dev-404-page`,
165 | `/404`,
166 | `/404.html`,
167 | `/offline-plugin-app-shell-fallback`,
168 | `/offline`,
169 | `/offline.html`,
170 | ],
171 | createLinkInHead: true,
172 | addUncaughtPages: true,
173 | },
174 | },
175 | `gatsby-plugin-catch-links`,
176 | `gatsby-plugin-react-helmet`,
177 | `gatsby-plugin-force-trailing-slashes`,
178 | {
179 | resolve: `gatsby-plugin-postcss`,
180 | options: {
181 | postCssPlugins: [require(`cssnano`)()],
182 | },
183 | },
184 | {
185 | resolve: `@draftbox-co/gatsby-plugin-amp`,
186 | options: {
187 | canonicalBaseUrl: siteConfig.siteUrl,
188 | components: [`amp-form`],
189 | excludedPaths: [`/404*`, `/`, `/offline*`],
190 | pathIdentifier: `amp/`,
191 | relAmpHtmlPattern: `{{canonicalBaseUrl}}{{pathname}}{{pathIdentifier}}`,
192 | useAmpClientIdApi: true,
193 | dirName: __dirname,
194 | themePath: `src/amp-styles/post.amp.css`,
195 | },
196 | },
197 | {
198 | resolve: `gatsby-plugin-remove-generator`,
199 | options: {
200 | content: `Draftbox`,
201 | },
202 | },
203 | {
204 | resolve: `@draftbox-co/gatsby-plugin-css-variables`,
205 | options: {
206 | variables: siteConfig.themeConfig.variables,
207 | },
208 | },
209 | ],
210 | };
211 |
212 | if (siteConfig.themeConfig.fonts && siteConfig.themeConfig.fonts.length > 0) {
213 | configOptions.plugins.push({
214 | resolve: `@draftbox-co/gatsby-plugin-webfonts`,
215 | options: {
216 | fonts: {
217 | google: siteConfig.themeConfig.fonts,
218 | },
219 | formats: ["woff2", "woff"],
220 | useMinify: true,
221 | usePreload: true,
222 | usePreconnect: true,
223 | blacklist: ["/amp"],
224 | },
225 | });
226 | }
227 | return configOptions;
228 | };
229 |
230 |
231 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/gatsby-node.js:
--------------------------------------------------------------------------------
1 | const _ = require(`lodash`);
2 | const { paginate } = require(`gatsby-awesome-pagination`);
3 | const { createRemoteFileNode } = require(`gatsby-source-filesystem`);
4 | const readingTime = require("reading-time");
5 |
6 | /**
7 | * Here is the place where Gatsby creates the URLs for all the
8 | * posts, tags, pages and authors that we fetched from the Ghost site.
9 | */
10 |
11 | exports.createSchemaCustomization = ({ actions, schema }) => {
12 | const { createFieldExtension, createTypes } = actions;
13 | createFieldExtension({
14 | name: "readingTime",
15 | extend(options, prevFieldConfig) {
16 | return {
17 | resolve(source) {
18 | if (source.html) {
19 | const readingTimeValue = readingTime(source.html);
20 | return readingTimeValue.text;
21 | } else {
22 | return "1 min read";
23 | }
24 | },
25 | };
26 | },
27 | });
28 |
29 | createTypes(`
30 | type GhostPost implements Node {
31 | readingTime: String @readingTime
32 | }
33 | `);
34 |
35 | createTypes(`
36 | type GhostPage implements Node {
37 | readingTime: String @readingTime
38 | }
39 | `);
40 | };
41 |
42 | exports.onCreateNode = async ({
43 | node,
44 | actions,
45 | store,
46 | createNodeId,
47 | cache,
48 | }) => {
49 | // Check that we are modifying right node types.
50 | const nodeTypes = [`GhostPost`, `GhostPage`];
51 | if (!nodeTypes.includes(node.internal.type)) {
52 | return;
53 | }
54 |
55 | const { createNode } = actions;
56 |
57 | // Download image and create a File node with gatsby-transformer-sharp.
58 | if (node.feature_image) {
59 | const fileNode = await createRemoteFileNode({
60 | url: node.feature_image,
61 | store,
62 | cache,
63 | createNode,
64 | parentNodeId: node.id,
65 | createNodeId,
66 | });
67 |
68 | if (fileNode) {
69 | // Link File node to GhostPost node at field image.
70 | node.localFeatureImage___NODE = fileNode.id;
71 | }
72 | }
73 | };
74 |
75 | exports.createPages = async ({ graphql, actions }) => {
76 | const { createPage } = actions;
77 |
78 | const result = await graphql(`
79 | {
80 | allGhostPost(
81 | sort: { order: ASC, fields: published_at }
82 | filter: { slug: { ne: "data-schema" } }
83 | ) {
84 | edges {
85 | node {
86 | slug
87 | primary_tag {
88 | slug
89 | }
90 | }
91 | }
92 | }
93 | allGhostTag(sort: { order: ASC, fields: name }) {
94 | edges {
95 | node {
96 | slug
97 | url
98 | postCount
99 | }
100 | }
101 | }
102 | allGhostAuthor(sort: { order: ASC, fields: name }) {
103 | edges {
104 | node {
105 | slug
106 | url
107 | postCount
108 | }
109 | }
110 | }
111 | allGhostPage(sort: { order: ASC, fields: published_at }) {
112 | edges {
113 | node {
114 | slug
115 | url
116 | }
117 | }
118 | }
119 | site {
120 | siteMetadata {
121 | postsPerPage
122 | siteTitle
123 | }
124 | }
125 |
126 | ghostSettings {
127 | title
128 | }
129 | }
130 | `);
131 |
132 | // Check for any errors
133 | if (result.errors) {
134 | throw new Error(result.errors);
135 | }
136 |
137 | // Extract query results
138 | const tags = result.data.allGhostTag.edges;
139 | const authors = result.data.allGhostAuthor.edges;
140 | const pages = result.data.allGhostPage.edges;
141 | const posts = result.data.allGhostPost.edges;
142 | const postsPerPage = result.data.site.siteMetadata.postsPerPage;
143 | const websiteTitle = result.data.site.siteMetadata.siteTitle;
144 | //const websiteTitle = result.data.ghostSettings.title;
145 |
146 | // Load templates
147 | const indexTemplate = require.resolve(`./src/templates/indexTemplate.jsx`);
148 | const postTemplate = require.resolve("./src/templates/postTemplate.jsx");
149 | const tagsTemplate = require.resolve(`./src/templates/tagsTemplate.jsx`);
150 | const authorTemplate = require.resolve(`./src/templates/authorTemplate.jsx`);
151 | const pageTemplate = require.resolve(`./src/templates/pageTemplate.jsx`);
152 | const postAmpTemplate = require.resolve(
153 | `./src/templates/postTemplate.amp.jsx`
154 | );
155 |
156 | // Create author pages
157 | authors.forEach(({ node }) => {
158 | const totalPosts = node.postCount !== null ? node.postCount : 0;
159 | const numberOfPages = Math.ceil(totalPosts / postsPerPage);
160 |
161 | // This part here defines, that our author pages will use
162 | // a `/author/:slug/` permalink.
163 | node.url = `/author/${node.slug}/`;
164 |
165 | Array.from({ length: numberOfPages }).forEach((_, i) => {
166 | const currentPage = i + 1;
167 | const prevPageNumber = currentPage <= 1 ? null : currentPage - 1;
168 | const nextPageNumber =
169 | currentPage + 1 > numberOfPages ? null : currentPage + 1;
170 | const previousPagePath = prevPageNumber
171 | ? prevPageNumber === 1
172 | ? node.url
173 | : `${node.url}page/${prevPageNumber}/`
174 | : null;
175 | const nextPagePath = nextPageNumber
176 | ? `${node.url}page/${nextPageNumber}/`
177 | : null;
178 |
179 | createPage({
180 | path: i === 0 ? node.url : `${node.url}page/${i + 1}/`,
181 | component: authorTemplate,
182 | context: {
183 | // Data passed to context is available
184 | // in page queries as GraphQL variables.
185 | slug: node.slug,
186 | limit: postsPerPage,
187 | skip: i * postsPerPage,
188 | numberOfPages: numberOfPages,
189 | humanPageNumber: currentPage,
190 | prevPageNumber: prevPageNumber,
191 | nextPageNumber: nextPageNumber,
192 | previousPagePath: previousPagePath,
193 | nextPagePath: nextPagePath,
194 | },
195 | });
196 | });
197 | });
198 |
199 | // Create pages
200 |
201 | posts.forEach(({ node }, index, array) => {
202 | createPage({
203 | path: node.slug,
204 | component: postTemplate,
205 | context: {
206 | slug: node.slug,
207 | prev: index !== 0 ? array[index - 1].node.slug : null,
208 | next: index !== array.length - 1 ? array[index + 1].node.slug : null,
209 | },
210 | });
211 |
212 | createPage({
213 | path: `${node.slug}/amp`,
214 | component: postAmpTemplate,
215 | context: {
216 | slug: node.slug,
217 | title: websiteTitle,
218 | amp: true,
219 | },
220 | });
221 | });
222 |
223 | tags.forEach(({ node }, i) => {
224 | const totalPosts = node.postCount !== null ? node.postCount : 0;
225 | const numberOfPages = Math.ceil(totalPosts / postsPerPage);
226 | node.url = `/tag/${node.slug}/`;
227 |
228 | Array.from({ length: numberOfPages }).forEach((_, i) => {
229 | const currentPage = i + 1;
230 | const prevPageNumber = currentPage <= 1 ? null : currentPage - 1;
231 | const nextPageNumber =
232 | currentPage + 1 > numberOfPages ? null : currentPage + 1;
233 | const previousPagePath = prevPageNumber
234 | ? prevPageNumber === 1
235 | ? node.url
236 | : `${node.url}page/${prevPageNumber}/`
237 | : null;
238 | const nextPagePath = nextPageNumber
239 | ? `${node.url}page/${nextPageNumber}/`
240 | : null;
241 |
242 | createPage({
243 | path: i === 0 ? node.url : `${node.url}page/${i + 1}/`,
244 | component: tagsTemplate,
245 | context: {
246 | // Data passed to context is available
247 | // in page queries as GraphQL variables.
248 | slug: node.slug,
249 | limit: postsPerPage,
250 | skip: i * postsPerPage,
251 | numberOfPages: numberOfPages,
252 | humanPageNumber: currentPage,
253 | prevPageNumber: prevPageNumber,
254 | nextPageNumber: nextPageNumber,
255 | previousPagePath: previousPagePath,
256 | nextPagePath: nextPagePath,
257 | },
258 | });
259 | });
260 | });
261 |
262 | pages
263 | .filter(({ node }) => !node.slug.startsWith("contact"))
264 | .forEach(({ node }) => {
265 | // This part here defines, that our pages will use
266 | // a `/:slug/` permalink.
267 | node.url = `/${node.slug}/`;
268 |
269 | createPage({
270 | path: node.url,
271 | component: pageTemplate,
272 | context: {
273 | // Data passed to context is available
274 | // in page queries as GraphQL variables.
275 | slug: node.slug,
276 | },
277 | });
278 | });
279 |
280 | // Create pagination
281 | paginate({
282 | createPage,
283 | items: posts,
284 | itemsPerPage: postsPerPage,
285 | component: indexTemplate,
286 | pathPrefix: ({ pageNumber }) => {
287 | if (pageNumber === 0) {
288 | return `/`;
289 | } else {
290 | return `/page`;
291 | }
292 | },
293 | });
294 | };
295 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@draftbox-co/gatsby-theme-ghost-attila",
3 | "description": "A Gatsby theme plugin for creating blogs from headless Ghost CMS.",
4 | "version": "1.0.48",
5 | "license": "MIT",
6 | "author": "Draftbox",
7 | "homepage": "https://github.com/draftbox-co/gatsby-attila-theme-ghost",
8 | "repository": {
9 | "url": "https://github.com/draftbox-co/gatsby-attila-theme-ghost",
10 | "type": "git"
11 | },
12 | "main": "index.js",
13 | "resolutions": {
14 | "sharp": "0.23.4"
15 | },
16 | "engines": {
17 | "node": ">= 8.9.0"
18 | },
19 | "keywords": [
20 | "gatsby",
21 | "gatsby-plugin",
22 | "gatsby-theme",
23 | "ghost",
24 | "ghost-theme",
25 | "casper",
26 | "blogging",
27 | "static-site",
28 | "static-site-generator",
29 | "gatsbyjs",
30 | "react"
31 | ],
32 | "scripts": {
33 | "serve": "gatsby build && NODE_ENV=production gatsby serve",
34 | "build": "gatsby build",
35 | "develop": "gatsby develop",
36 | "lint": "eslint . --ext .js --cache",
37 | "test": "echo \"Error: no test specified\" && exit 1"
38 | },
39 | "devDependencies": {
40 | "babel-eslint": "^10.1.0",
41 | "eslint": "^6.8.0",
42 | "eslint-plugin-ghost": "^1.0.0",
43 | "eslint-plugin-react": "^7.18.3",
44 | "gatsby": "2.20.24",
45 | "react": "^16.12.0",
46 | "react-dom": "^16.12.0"
47 | },
48 | "peerDependencies": {
49 | "gatsby": "2.20.24",
50 | "react": "^16.12.0",
51 | "react-dom": "^16.12.0"
52 | },
53 | "dependencies": {
54 | "@draftbox-co/gatsby-plugin-amp": "^0.2.4",
55 | "@draftbox-co/gatsby-plugin-css-variables": "0.0.3",
56 | "@draftbox-co/gatsby-plugin-webfonts": "^1.1.3",
57 | "@tryghost/helpers": "^1.1.22",
58 | "@tryghost/url-utils": "^0.6.13",
59 | "autoprefixer": "^9.7.4",
60 | "babel-plugin-prismjs": "^2.0.1",
61 | "babel-plugin-styled-components": "^1.10.7",
62 | "babel-preset-gatsby": "^0.2.29",
63 | "cheerio": "^1.0.0-rc.3",
64 | "cssnano": "^4.1.10",
65 | "disqus-react": "^1.0.8",
66 | "gatsby-awesome-pagination": "^0.3.5",
67 | "gatsby-background-image": "^0.10.2",
68 | "gatsby-image": "^2.2.41",
69 | "gatsby-plugin-advanced-sitemap": "^1.5.1",
70 | "gatsby-plugin-catch-links": "^2.1.26",
71 | "gatsby-plugin-feed": "^2.3.27",
72 | "gatsby-plugin-force-trailing-slashes": "^1.0.4",
73 | "gatsby-plugin-manifest": "^2.2.42",
74 | "gatsby-plugin-page-creator": "^2.1.40",
75 | "gatsby-plugin-postcss": "^2.1.20",
76 | "gatsby-plugin-react-helmet": "^3.1.22",
77 | "gatsby-plugin-remove-generator": "^1.0.5",
78 | "gatsby-plugin-sass": "^2.1.29",
79 | "gatsby-plugin-sharp": "^2.4.5",
80 | "gatsby-rehype-ghost-links": "^1.1.0",
81 | "gatsby-rehype-prismjs": "^0.0.9",
82 | "gatsby-source-filesystem": "^2.1.48",
83 | "gatsby-source-ghost": "^4.0.3",
84 | "gatsby-transformer-rehype": "^1.1.0",
85 | "gatsby-transformer-sharp": "^2.3.14",
86 | "lodash": "^4.17.19",
87 | "node-sass": "^4.13.1",
88 | "prismjs": "^1.19.0",
89 | "react-helmet": "^5.2.1",
90 | "react-intersection-observer": "^8.26.2",
91 | "reading-time": "^1.2.0",
92 | "styled-components": "^5.0.1"
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/plugins/gatsby-plugin-ghost-manifest/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "babel-preset-gatsby-package",
5 | {
6 | "browser": true
7 | }
8 | ]
9 | ]
10 | }
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/plugins/gatsby-plugin-ghost-manifest/common.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var fs = require("fs"); // default icons for generating icons
4 |
5 |
6 | exports.defaultIcons = [{
7 | src: "icons/icon-48x48.png",
8 | sizes: "48x48",
9 | type: "image/png"
10 | }, {
11 | src: "icons/icon-72x72.png",
12 | sizes: "72x72",
13 | type: "image/png"
14 | }, {
15 | src: "icons/icon-96x96.png",
16 | sizes: "96x96",
17 | type: "image/png"
18 | }, {
19 | src: "icons/icon-144x144.png",
20 | sizes: "144x144",
21 | type: "image/png"
22 | }, {
23 | src: "icons/icon-192x192.png",
24 | sizes: "192x192",
25 | type: "image/png"
26 | }, {
27 | src: "icons/icon-256x256.png",
28 | sizes: "256x256",
29 | type: "image/png"
30 | }, {
31 | src: "icons/icon-384x384.png",
32 | sizes: "384x384",
33 | type: "image/png"
34 | }, {
35 | src: "icons/icon-512x512.png",
36 | sizes: "512x512",
37 | type: "image/png"
38 | }];
39 | /**
40 | * Check if the icon exists on the filesystem
41 | *
42 | * @param {String} srcIcon Path of the icon
43 | */
44 |
45 | exports.doesIconExist = function doesIconExist(srcIcon) {
46 | try {
47 | return fs.statSync(srcIcon).isFile();
48 | } catch (e) {
49 | if (e.code === "ENOENT") {
50 | return false;
51 | } else {
52 | throw e;
53 | }
54 | }
55 | };
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/plugins/gatsby-plugin-ghost-manifest/gatsby-node.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4 |
5 | var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
6 |
7 | var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
8 |
9 | var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose"));
10 |
11 | var fs = require("fs");
12 |
13 | var path = require("path");
14 |
15 | var Promise = require("bluebird");
16 |
17 | var sharp = require("sharp");
18 |
19 | var _require = require("./common.js"),
20 | defaultIcons = _require.defaultIcons,
21 | doesIconExist = _require.doesIconExist;
22 |
23 | sharp.simd(true);
24 |
25 | function generateIcons(icons, srcIcon) {
26 | return Promise.map(icons, function (icon) {
27 | var size = parseInt(icon.sizes.substring(0, icon.sizes.lastIndexOf("x")));
28 | var imgPath = path.join("public", icon.src);
29 | return sharp(srcIcon).resize(size).toFile(imgPath).then(function () {});
30 | });
31 | }
32 |
33 | exports.onPostBuild = function _callee(_ref, pluginOptions) {
34 | var graphql, icon, manifest, _ref2, data, siteTitle, iconPath;
35 |
36 | return _regenerator.default.async(function _callee$(_context) {
37 | while (1) {
38 | switch (_context.prev = _context.next) {
39 | case 0:
40 | graphql = _ref.graphql;
41 | icon = pluginOptions.icon, manifest = (0, _objectWithoutPropertiesLoose2.default)(pluginOptions, ["icon"]);
42 | _context.next = 4;
43 | return _regenerator.default.awrap(graphql(pluginOptions.query));
44 |
45 | case 4:
46 | _ref2 = _context.sent;
47 | data = _ref2.data;
48 | siteTitle = data.site.siteMetadata.siteTitle || "No Title";
49 | manifest = (0, _extends2.default)({}, manifest, {
50 | name: siteTitle
51 | }); // Delete options we won't pass to the manifest.webmanifest.
52 |
53 | delete manifest.plugins;
54 | delete manifest.legacy;
55 | delete manifest.theme_color_in_head;
56 | delete manifest.query; // If icons are not manually defined, use the default icon set.
57 |
58 | if (!manifest.icons) {
59 | manifest.icons = defaultIcons;
60 | } // Determine destination path for icons.
61 |
62 |
63 | iconPath = path.join("public", path.dirname(manifest.icons[0].src)); //create destination directory if it doesn't exist
64 |
65 | if (!fs.existsSync(iconPath)) {
66 | fs.mkdirSync(iconPath);
67 | }
68 |
69 | fs.writeFileSync(path.join("public", "manifest.webmanifest"), JSON.stringify(manifest)); // Only auto-generate icons if a src icon is defined.
70 |
71 | if (icon !== undefined) {
72 | // Check if the icon exists
73 | if (!doesIconExist(icon)) {
74 | Promise.reject("icon (" + icon + ") does not exist as defined in gatsby-config.js. Make sure the file exists relative to the root of the site.");
75 | }
76 |
77 | generateIcons(manifest.icons, icon).then(function () {
78 | //images have been generated
79 | console.log("done generating icons for manifest");
80 | Promise.resolve();
81 | });
82 | } else {
83 | Promise.resolve();
84 | }
85 |
86 | case 17:
87 | case "end":
88 | return _context.stop();
89 | }
90 | }
91 | });
92 | };
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/plugins/gatsby-plugin-ghost-manifest/gatsby-ssr.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4 |
5 | var _react = _interopRequireDefault(require("react"));
6 |
7 | var _gatsby = require("gatsby");
8 |
9 | var _common = require("./common.js");
10 |
11 | var _jsxFileName = "C:\\Users\\Arun\\Documents\\code\\gatsby-attila-theme-ghost\\gatsby-theme-ghost-attila\\plugins\\gatsby-plugin-ghost-manifest\\src\\gatsby-ssr.js";
12 |
13 | exports.onRenderBody = function (_ref, pluginOptions) {
14 | var setHeadComponents = _ref.setHeadComponents;
15 | // We use this to build a final array to pass as the argument to setHeadComponents at the end of onRenderBody.
16 | var headComponents = [];
17 | var icons = pluginOptions.icons || _common.defaultIcons; // If icons were generated, also add a favicon link.
18 |
19 | if (pluginOptions.icon) {
20 | var favicon = icons && icons.length ? icons[0].src : null;
21 |
22 | if (favicon) {
23 | headComponents.push(_react.default.createElement("link", {
24 | key: "gatsby-plugin-manifest-icon-link",
25 | rel: "shortcut icon",
26 | href: (0, _gatsby.withPrefix)(favicon),
27 | __source: {
28 | fileName: _jsxFileName,
29 | lineNumber: 17
30 | },
31 | __self: this
32 | }));
33 | }
34 | } // Add manifest link tag.
35 |
36 |
37 | headComponents.push(_react.default.createElement("link", {
38 | key: "gatsby-plugin-manifest-link",
39 | rel: "manifest",
40 | href: (0, _gatsby.withPrefix)("/manifest.webmanifest"),
41 | __source: {
42 | fileName: _jsxFileName,
43 | lineNumber: 28
44 | },
45 | __self: this
46 | })); // The user has an option to opt out of the theme_color meta tag being inserted into the head.
47 |
48 | if (pluginOptions.theme_color) {
49 | var insertMetaTag = Object.keys(pluginOptions).includes("theme_color_in_head") ? pluginOptions.theme_color_in_head : true;
50 |
51 | if (insertMetaTag) {
52 | headComponents.push(_react.default.createElement("meta", {
53 | key: "gatsby-plugin-manifest-meta",
54 | name: "theme-color",
55 | content: pluginOptions.theme_color,
56 | __source: {
57 | fileName: _jsxFileName,
58 | lineNumber: 44
59 | },
60 | __self: this
61 | }));
62 | }
63 | }
64 |
65 | if (pluginOptions.legacy) {
66 | var iconLinkTags = icons.map(function (icon) {
67 | return _react.default.createElement("link", {
68 | key: "gatsby-plugin-manifest-apple-touch-icon-" + icon.sizes,
69 | rel: "apple-touch-icon",
70 | sizes: icon.sizes,
71 | href: (0, _gatsby.withPrefix)("" + icon.src),
72 | __source: {
73 | fileName: _jsxFileName,
74 | lineNumber: 55
75 | },
76 | __self: this
77 | });
78 | });
79 | headComponents = [].concat(headComponents, iconLinkTags);
80 | }
81 |
82 | setHeadComponents(headComponents);
83 | };
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/plugins/gatsby-plugin-ghost-manifest/index.js:
--------------------------------------------------------------------------------
1 | // noop
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/plugins/gatsby-plugin-ghost-manifest/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gatsby-plugin-ghost-manifest",
3 | "description": "Gatsby plugin which adds a manifest.webmanifest to make sites progressive web apps",
4 | "version": "0.0.1",
5 | "author": "Ghost Foundation",
6 | "dependencies": {
7 | "@babel/runtime": "7.7.2",
8 | "bluebird": "3.7.1",
9 | "sharp": "0.22.1"
10 | },
11 | "devDependencies": {
12 | "@babel/cli": "7.7.0",
13 | "@babel/core": "7.7.2",
14 | "babel-preset-gatsby-package": "0.2.11",
15 | "cross-env": "6.0.3"
16 | },
17 | "keywords": [
18 | "gatsby",
19 | "gatsby-plugin",
20 | "favicon",
21 | "icons",
22 | "manifest.webmanifest",
23 | "progressive-web-app",
24 | "pwa"
25 | ],
26 | "resolutions": {
27 | "sharp": "0.22.1"
28 | },
29 | "license": "MIT",
30 | "main": "index.js",
31 | "peerDependencies": {
32 | "gatsby": ">2.0.0-alpha"
33 | },
34 | "scripts": {
35 | "build": "babel src --out-dir . --ignore **/__tests__",
36 | "prepare": "cross-env NODE_ENV=production npm run build",
37 | "watch": "babel -w src --out-dir . --ignore **/__tests__"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/plugins/gatsby-plugin-ghost-manifest/src/common.js:
--------------------------------------------------------------------------------
1 | const fs = require(`fs`)
2 |
3 | // default icons for generating icons
4 | exports.defaultIcons = [
5 | {
6 | src: `icons/icon-48x48.png`,
7 | sizes: `48x48`,
8 | type: `image/png`,
9 | },
10 | {
11 | src: `icons/icon-72x72.png`,
12 | sizes: `72x72`,
13 | type: `image/png`,
14 | },
15 | {
16 | src: `icons/icon-96x96.png`,
17 | sizes: `96x96`,
18 | type: `image/png`,
19 | },
20 | {
21 | src: `icons/icon-144x144.png`,
22 | sizes: `144x144`,
23 | type: `image/png`,
24 | },
25 | {
26 | src: `icons/icon-192x192.png`,
27 | sizes: `192x192`,
28 | type: `image/png`,
29 | },
30 | {
31 | src: `icons/icon-256x256.png`,
32 | sizes: `256x256`,
33 | type: `image/png`,
34 | },
35 | {
36 | src: `icons/icon-384x384.png`,
37 | sizes: `384x384`,
38 | type: `image/png`,
39 | },
40 | {
41 | src: `icons/icon-512x512.png`,
42 | sizes: `512x512`,
43 | type: `image/png`,
44 | },
45 | ]
46 |
47 | /**
48 | * Check if the icon exists on the filesystem
49 | *
50 | * @param {String} srcIcon Path of the icon
51 | */
52 | exports.doesIconExist = function doesIconExist(srcIcon) {
53 | try {
54 | return fs.statSync(srcIcon).isFile()
55 | } catch (e) {
56 | if (e.code === `ENOENT`) {
57 | return false
58 | } else {
59 | throw e
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/plugins/gatsby-plugin-ghost-manifest/src/gatsby-node.js:
--------------------------------------------------------------------------------
1 | const fs = require(`fs`)
2 | const path = require(`path`)
3 | const Promise = require(`bluebird`)
4 | const sharp = require(`sharp`)
5 | const { defaultIcons, doesIconExist } = require(`./common.js`)
6 |
7 | sharp.simd(true)
8 |
9 | function generateIcons(icons, srcIcon) {
10 | return Promise.map(icons, (icon) => {
11 | const size = parseInt(icon.sizes.substring(0, icon.sizes.lastIndexOf(`x`)))
12 | const imgPath = path.join(`public`, icon.src)
13 |
14 | return sharp(srcIcon)
15 | .resize(size)
16 | .toFile(imgPath)
17 | .then(() => { })
18 | })
19 | }
20 |
21 | exports.onPostBuild = async ({ graphql }, pluginOptions) => {
22 | let { icon, ...manifest } = pluginOptions
23 |
24 | const { data } = await graphql(pluginOptions.query)
25 | const siteTitle = data.site.siteMetadata.siteTitle || `No Title`
26 | manifest = {
27 | ...manifest,
28 | name: siteTitle,
29 | }
30 |
31 | // Delete options we won't pass to the manifest.webmanifest.
32 | delete manifest.plugins
33 | delete manifest.legacy
34 | delete manifest.theme_color_in_head
35 | delete manifest.query
36 |
37 | // If icons are not manually defined, use the default icon set.
38 | if (!manifest.icons) {
39 | manifest.icons = defaultIcons
40 | }
41 |
42 | // Determine destination path for icons.
43 | const iconPath = path.join(`public`, path.dirname(manifest.icons[0].src))
44 |
45 | //create destination directory if it doesn't exist
46 | if (!fs.existsSync(iconPath)) {
47 | fs.mkdirSync(iconPath)
48 | }
49 |
50 | fs.writeFileSync(
51 | path.join(`public`, `manifest.webmanifest`),
52 | JSON.stringify(manifest)
53 | )
54 |
55 | // Only auto-generate icons if a src icon is defined.
56 | if (icon !== undefined) {
57 | // Check if the icon exists
58 | if (!doesIconExist(icon)) {
59 | Promise.reject(
60 | `icon (${icon}) does not exist as defined in gatsby-config.js. Make sure the file exists relative to the root of the site.`
61 | )
62 | }
63 | generateIcons(manifest.icons, icon).then(() => {
64 | //images have been generated
65 | console.log(`done generating icons for manifest`)
66 | Promise.resolve()
67 | })
68 | } else {
69 | Promise.resolve()
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/plugins/gatsby-plugin-ghost-manifest/src/gatsby-ssr.js:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import { withPrefix } from "gatsby"
3 | import { defaultIcons } from "./common.js"
4 |
5 | exports.onRenderBody = ({ setHeadComponents }, pluginOptions) => {
6 | // We use this to build a final array to pass as the argument to setHeadComponents at the end of onRenderBody.
7 | let headComponents = []
8 |
9 | const icons = pluginOptions.icons || defaultIcons
10 |
11 | // If icons were generated, also add a favicon link.
12 | if (pluginOptions.icon) {
13 | let favicon = icons && icons.length ? icons[0].src : null
14 |
15 | if (favicon) {
16 | headComponents.push(
17 |
22 | )
23 | }
24 | }
25 |
26 | // Add manifest link tag.
27 | headComponents.push(
28 |
33 | )
34 | // The user has an option to opt out of the theme_color meta tag being inserted into the head.
35 | if (pluginOptions.theme_color) {
36 | let insertMetaTag = Object.keys(pluginOptions).includes(
37 | `theme_color_in_head`
38 | )
39 | ? pluginOptions.theme_color_in_head
40 | : true
41 |
42 | if (insertMetaTag) {
43 | headComponents.push(
44 |
49 | )
50 | }
51 | }
52 |
53 | if (pluginOptions.legacy) {
54 | const iconLinkTags = icons.map(icon => (
55 |
61 | ))
62 |
63 | headComponents = [...headComponents, ...iconLinkTags]
64 | }
65 |
66 | setHeadComponents(headComponents)
67 | }
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/components/Layout.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "../styles/sass/style.scss";
3 | import Navbar from "./navbar";
4 | import Footer from "./footer";
5 | import { ArmadaFormsProvider } from "../context/form-context";
6 | import { Helmet } from "react-helmet";
7 | import { useStaticQuery, graphql } from "gatsby";
8 |
9 | const Layout = (props) => {
10 | const data = useStaticQuery(graphql`
11 | {
12 | site {
13 | siteMetadata {
14 | language
15 | }
16 | }
17 | }
18 | `);
19 |
20 | return (
21 | <>
22 |
29 |
30 |
31 | {props.children}
32 |
33 |
34 | >
35 | );
36 | };
37 |
38 | export default Layout;
39 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/components/contact-form.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { useForm } from "../hook/useForm";
3 | import { useStaticQuery, graphql } from "gatsby";
4 |
5 | const ContactForm = () => {
6 | const {
7 | site: {
8 | siteTitle,
9 | siteMetadata: { contactWidget },
10 | },
11 | } = useStaticQuery(graphql`
12 | query {
13 | site {
14 | siteMetadata {
15 | siteTitle
16 | contactWidget {
17 | title
18 | successMessage
19 | }
20 | }
21 | }
22 | }
23 | `);
24 | const [formValues, setFormValues] = useState({
25 | name: "",
26 | email: "",
27 | message: "",
28 | });
29 |
30 | const [{ handleSubmit: submitForm, submitting, succeeded }] = useForm(
31 | "contact"
32 | );
33 |
34 | const handleSubmit = (e) => {
35 | e.preventDefault();
36 | submitForm(formValues);
37 | };
38 |
39 | const handleChange = (target, value) => {
40 | switch (target) {
41 | case "name":
42 | setFormValues({ ...formValues, name: value });
43 | break;
44 | case "email":
45 | setFormValues({ ...formValues, email: value });
46 | break;
47 | case "message":
48 | setFormValues({ ...formValues, message: value });
49 | break;
50 | default:
51 | break;
52 | }
53 | };
54 |
55 | return (
56 |
57 |
58 |
59 | {succeeded && (
60 |
61 | {contactWidget.successMessage
62 | ? contactWidget.successMessage
63 | : `We'll get in touch with you soon.`}
64 |
65 | )}
66 | {!succeeded && (
67 | <>
68 |
69 |
76 |
77 |
113 | >
114 | )}
115 |
116 |
117 |
118 | );
119 | };
120 |
121 | export default ContactForm;
122 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/components/copy-link.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import copyIcon from "../images/copy.svg";
3 | import copiedIcon from "../images/copied.svg";
4 |
5 | const CopyLink = ({ textToCopy }) => {
6 | const [copiedToClipBoard, setCopiedToClipBoard] = useState(false);
7 |
8 | const copyToClipboard = (e) => {
9 | e.preventDefault();
10 | if (window["clipboardData"] && window["clipboardData"].setData) {
11 | // Internet Explorer-specific code path to prevent textarea being shown while dialog is visible.
12 | setCopiedToClipBoard(true);
13 |
14 | setTimeout(() => {
15 | setCopiedToClipBoard(false);
16 | }, 5000);
17 | return window["clipboardData"].setData("Text", textToCopy);
18 | } else if (
19 | document.queryCommandSupported &&
20 | document.queryCommandSupported("copy")
21 | ) {
22 | var textarea = document.createElement("textarea");
23 | textarea.textContent = textToCopy;
24 | textarea.style.position = "fixed"; // Prevent scrolling to bottom of page in Microsoft Edge.
25 | document.body.appendChild(textarea);
26 | textarea.select();
27 | try {
28 | setCopiedToClipBoard(true);
29 |
30 | setTimeout(() => {
31 | setCopiedToClipBoard(false);
32 | }, 5000);
33 | return document.execCommand("copy"); // Security exception may be thrown by some browsers.
34 | } catch (ex) {
35 | console.warn("Copy to clipboard failed.", ex);
36 | return false;
37 | } finally {
38 | document.body.removeChild(textarea);
39 | }
40 | }
41 | };
42 |
43 | return (
44 |
63 | );
64 | };
65 |
66 | export default CopyLink;
67 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/components/disqus.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const Disqus = () => {
4 | return <>>;
5 | };
6 |
7 | export default Disqus;
8 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/components/fb-comments.jsx:
--------------------------------------------------------------------------------
1 | const FbComments = () => {
2 | return <>>;
3 | };
4 |
5 | export default FbComments;
6 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/components/footer.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useStaticQuery, graphql, Link } from "gatsby";
3 |
4 | const Footer = () => {
5 | const data = useStaticQuery(graphql`
6 | query {
7 | ghostSettings {
8 | title
9 | codeinjection_foot
10 | }
11 | site {
12 | siteMetadata {
13 | siteTitle
14 | siteUrl
15 | apiUrl
16 | footer {
17 | copyright
18 | navigation {
19 | label
20 | url
21 | }
22 | }
23 | subscribeWidget {
24 | visible
25 | }
26 | }
27 | }
28 | }
29 | `);
30 |
31 | const navigation = data.site.siteMetadata.footer.navigation;
32 | const siteUrl = data.site.siteMetadata.siteUrl;
33 | const apiUrl = data.site.siteMetadata.apiUrl;
34 | const copyright = data.site.siteMetadata.footer.copyright;
35 | const siteTitle = data.site.siteMetadata.siteTitle;
36 | const subscribeWidget = data.site.siteMetadata.subscribeWidget;
37 |
38 | return (
39 | <>
40 |
48 |
89 |
90 |
103 |
104 | >
105 | );
106 | };
107 |
108 | export default Footer;
109 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/components/header.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useStaticQuery, graphql } from "gatsby";
3 | import url from "url";
4 |
5 |
6 | const Header = () => {
7 | const data = useStaticQuery(graphql`
8 | query {
9 | site {
10 | siteMetadata {
11 | siteTitle
12 | siteDescription
13 | siteUrl
14 | coverUrl
15 | apiUrl
16 | footer {
17 | copyright
18 | navigation {
19 | label
20 | url
21 | }
22 | }
23 | }
24 | }
25 | }
26 | `);
27 |
28 | const {
29 | site: { siteMetadata }
30 | } = data;
31 |
32 | //const siteSettings = edges[0].node;
33 | return (
34 | <>
35 |
36 |
37 |
38 |
42 |
46 |
47 |
53 |
54 |
55 |
56 | >
57 | );
58 | };
59 |
60 | export default Header;
61 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/components/meta/ArticleMeta.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Helmet from "react-helmet";
3 | import { StaticQuery, graphql } from "gatsby";
4 | import PropTypes from "prop-types";
5 | import _ from "lodash";
6 | import url from "url";
7 |
8 | import getAuthorProperties from "./getAuthorProperties";
9 | import ImageMeta from "./ImageMeta";
10 |
11 | import { tags as tagsHelper } from "@tryghost/helpers";
12 |
13 | const ArticleMetaGhost = ({ data, settings, canonical, amp }) => {
14 | const ghostPost = data;
15 | const config = settings.site.siteMetadata;
16 | settings = settings.allGhostSettings.edges[0].node;
17 |
18 | const author = getAuthorProperties(ghostPost.primary_author);
19 | const publicTags = _.map(
20 | tagsHelper(ghostPost, { visibility: `public`, fn: (tag) => tag }),
21 | `name`
22 | );
23 | const primaryTag = publicTags[0] || ``;
24 |
25 | const postHeroImage = ghostPost.localFeatureImage?.seo?.fixed?.src;
26 |
27 | const shareImage = postHeroImage
28 | ? url.resolve(config.siteUrl, postHeroImage)
29 | : config.coverUrl ||
30 | config.facebookCard.imageUrl ||
31 | config.twitterCard.imageUrl
32 | ? url.resolve(
33 | config.siteUrl,
34 | config.coverUrl ||
35 | config.facebookCard.imageUrl ||
36 | config.twitterCard.imageUrl
37 | )
38 | : null;
39 |
40 | const publisherLogo =
41 | config.logoUrl || config.alternateLogoUrl
42 | ? url.resolve(config.siteUrl, config.logoUrl || config.alternateLogoUrl)
43 | : null;
44 |
45 | const jsonLd = {
46 | "@context": `https://schema.org/`,
47 | "@type": `Article`,
48 | author: author
49 | ? {
50 | "@type": `Person`,
51 | name: author.name,
52 | image: author.image ? author.image : undefined,
53 | sameAs: author.sameAsArray ? author.sameAsArray : undefined,
54 | }
55 | : null,
56 | keywords: publicTags.length ? publicTags.join(`, `) : undefined,
57 | headline: ghostPost.meta_title || ghostPost.title,
58 | url: canonical,
59 | datePublished: ghostPost.published_at,
60 | dateModified: ghostPost.updated_at,
61 | image: shareImage
62 | ? {
63 | "@type": `ImageObject`,
64 | url: shareImage,
65 | width: config.shareImageWidth,
66 | height: config.shareImageHeight,
67 | }
68 | : undefined,
69 | publisher: {
70 | "@type": `Organization`,
71 | name: config.siteTitle,
72 | logo: publisherLogo
73 | ? {
74 | "@type": `ImageObject`,
75 | url: publisherLogo,
76 | width: 60,
77 | height: 60,
78 | }
79 | : undefined,
80 | },
81 | description: ghostPost.meta_description || ghostPost.excerpt,
82 | mainEntityOfPage: {
83 | "@type": `WebPage`,
84 | "@id": config.siteUrl,
85 | },
86 | };
87 |
88 | return (
89 | <>
90 |
93 | {ghostPost.meta_title || ghostPost.title}
94 | {!amp && }
95 |
99 | {!amp && }
100 |
101 |
102 |
103 |
109 |
117 |
118 | {config.facebookCard.appId !== "" && (
119 |
120 | )}
121 |
125 |
126 | {publicTags.map((keyword, i) => (
127 |
128 | ))}
129 | {author.facebookUrl && (
130 |
131 | )}
132 |
133 |
139 |
147 |
148 | {author && }
149 | {author && }
150 | {primaryTag && }
151 | {primaryTag && }
152 |
153 | {config.twitterCard.username && (
154 |
155 | )}
156 | {/* {settings.twitter && (
157 |
158 | )} */}
159 | {author.twitter && (
160 |
161 | )}
162 |
165 |
166 |
167 | >
168 | );
169 | };
170 |
171 | ArticleMetaGhost.propTypes = {
172 | data: PropTypes.shape({
173 | title: PropTypes.string.isRequired,
174 | published_at: PropTypes.string.isRequired,
175 | updated_at: PropTypes.string.isRequired,
176 | meta_title: PropTypes.string,
177 | meta_description: PropTypes.string,
178 | primary_author: PropTypes.object.isRequired,
179 | feature_image: PropTypes.string,
180 | tags: PropTypes.arrayOf(
181 | PropTypes.shape({
182 | name: PropTypes.string,
183 | slug: PropTypes.string,
184 | visibility: PropTypes.string,
185 | })
186 | ),
187 | primaryTag: PropTypes.shape({
188 | name: PropTypes.string,
189 | }),
190 | og_title: PropTypes.string,
191 | og_description: PropTypes.string,
192 | twitter_title: PropTypes.string,
193 | twitter_description: PropTypes.string,
194 | excerpt: PropTypes.string.isRequired,
195 | }).isRequired,
196 | settings: PropTypes.shape({
197 | logo: PropTypes.object,
198 | title: PropTypes.string,
199 | twitter: PropTypes.string,
200 | allGhostSettings: PropTypes.object.isRequired,
201 | site: PropTypes.object.isRequired,
202 | }).isRequired,
203 | canonical: PropTypes.string.isRequired,
204 | };
205 |
206 | const ArticleMetaQuery = (props) => (
207 | }
225 | />
226 | );
227 |
228 | export default ArticleMetaQuery;
229 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/components/meta/AuthorMeta.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Helmet from 'react-helmet'
3 | import PropTypes from 'prop-types'
4 | //import _ from 'lodash'
5 | import url from "url"
6 | import { StaticQuery, graphql } from 'gatsby'
7 |
8 | import ImageMeta from './ImageMeta'
9 | import getAuthorProperties from './getAuthorProperties'
10 |
11 | const AuthorMeta = ({ data, settings, canonical }) => {
12 | const config = settings.site.siteMetadata
13 | settings = settings.allGhostSettings.edges[0].node
14 |
15 | const author = getAuthorProperties(data)
16 | const shareImage = author.image
17 | ? url.resolve(config.siteUrl, author.image)
18 | : config.coverUrl ||
19 | config.facebookCard.imageUrl ||
20 | config.twitterCard.imageUrl
21 | ? url.resolve(
22 | config.siteUrl,
23 | config.coverUrl ||
24 | config.facebookCard.imageUrl ||
25 | config.twitterCard.imageUrl
26 | )
27 | : null;
28 | const title = `${data.name} - ${config.siteTitle}`
29 | const description = data.bio || config.siteDescription || settings.description
30 |
31 | const jsonLd = {
32 | "@context": `https://schema.org/`,
33 | "@type": `Person`,
34 | name: data.name,
35 | sameAs: author.sameAsArray ? author.sameAsArray : undefined,
36 | url: canonical,
37 | image: shareImage ? {
38 | "@type": `ImageObject`,
39 | url: shareImage,
40 | width: config.shareImageWidth,
41 | height: config.shareImageHeight,
42 | } : undefined,
43 | mainEntityOfPage: {
44 | "@type": `WebPage`,
45 | "@id": config.siteUrl,
46 | },
47 | description,
48 | }
49 |
50 | return (
51 | <>
52 |
53 | {title}
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | {config.twitterCard.username && }
65 | {config.twitterCard.username && }
66 |
67 |
68 |
69 | >
70 | )
71 | }
72 |
73 | AuthorMeta.propTypes = {
74 | data: PropTypes.shape({
75 | name: PropTypes.string,
76 | bio: PropTypes.string,
77 | profile_image: PropTypes.string,
78 | website: PropTypes.string,
79 | twitter: PropTypes.string,
80 | facebook: PropTypes.string,
81 | }).isRequired,
82 | settings: PropTypes.shape({
83 | title: PropTypes.string,
84 | twitter: PropTypes.string,
85 | description: PropTypes.string,
86 | allGhostSettings: PropTypes.object.isRequired,
87 | site: PropTypes.object.isRequired,
88 | }).isRequired,
89 | canonical: PropTypes.string.isRequired,
90 | }
91 |
92 | const AuthorMetaQuery = props => (
93 | }
111 | />
112 | )
113 |
114 | export default AuthorMetaQuery
115 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/components/meta/ContactMeta.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Helmet from "react-helmet";
3 | import { graphql, useStaticQuery } from "gatsby";
4 | import url from "url";
5 |
6 | const ContactMeta = ({ location }) => {
7 | const data = useStaticQuery(graphql`
8 | query {
9 | site {
10 | siteMetadata {
11 | siteUrl
12 | siteTitle
13 | metadata {
14 | title
15 | description
16 | }
17 | twitterCard {
18 | title
19 | description
20 | imageUrl
21 | username
22 | }
23 | facebookCard {
24 | title
25 | description
26 | imageUrl
27 | appId
28 | }
29 | siteDescription
30 | language
31 | logoUrl
32 | iconUrl
33 | coverUrl
34 | alternateLogoUrl
35 | shareImageWidth
36 | shareImageHeight
37 | }
38 | }
39 | }
40 | `);
41 |
42 | const config = data.site.siteMetadata;
43 |
44 | const baseUrl = data.site.siteMetadata.siteUrl;
45 | const siteTitle = data.site.siteMetadata.siteTitle;
46 |
47 | const canonical = url.resolve(baseUrl, location.pathname);
48 |
49 | const description = config.metadata.description || config.siteDescription;
50 |
51 | const publisherLogo = url.resolve(
52 | config.siteUrl,
53 | config.logoUrl || config.alternateLogoUrl
54 | );
55 | let shareImage =
56 | config.coverUrl ||
57 | config.facebookCard.imageUrl ||
58 | config.twitterCard.imageUrl;
59 |
60 | shareImage = shareImage ? url.resolve(config.siteUrl, shareImage) : null;
61 |
62 | const facebookImageUrl = config.facebookCard.imageUrl
63 | ? url.resolve(config.siteUrl, config.facebookCard.imageUrl)
64 | : null;
65 |
66 | const twitterImageUrl = config.twitterCard.imageUrl
67 | ? url.resolve(config.siteUrl, config.twitterCard.imageUrl)
68 | : null;
69 |
70 | const jsonLd = {
71 | "@context": `https://schema.org/`,
72 | "@type": "WebPage",
73 | url: canonical,
74 | headline: 'Contact | ' + config.metadata.title || config.siteTitle,
75 | image: shareImage
76 | ? {
77 | "@type": `ImageObject`,
78 | url: shareImage,
79 | width: config.shareImageWidth,
80 | height: config.shareImageHeight,
81 | }
82 | : undefined,
83 | publisher: {
84 | "@type": `Organization`,
85 | name: siteTitle,
86 | logo: publisherLogo
87 | ? {
88 | "@type": `ImageObject`,
89 | url: publisherLogo,
90 | width: 60,
91 | height: 60,
92 | }
93 | : undefined,
94 | },
95 | mainEntityOfPage: {
96 | "@type": `WebPage`,
97 | "@id": config.siteUrl,
98 | },
99 | description,
100 | };
101 |
102 | return (
103 | <>
104 |
105 | Contact | {config.metadata.title || config.siteTitle}
106 |
110 |
111 |
112 |
113 |
121 |
129 |
130 | {config.facebookCard.imageUrl !== "" && (
131 |
132 | )}
133 | {config.facebookCard.appId !== "" && (
134 |
135 | )}
136 |
144 |
152 |
153 | {config.twitterCard.username && (
154 |
155 | )}
156 | {config.twitterCard.username && (
157 |
158 | )}
159 | {config.twitterCard.imageUrl && (
160 |
161 | )}
162 | {config.twitterCard.imageUrl && (
163 |
164 | )}
165 |
168 |
169 | >
170 | );
171 | };
172 |
173 | export default ContactMeta;
174 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/components/meta/ImageMeta.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Helmet from 'react-helmet'
3 | import PropTypes from 'prop-types'
4 |
5 | import { StaticQuery, graphql } from 'gatsby'
6 |
7 | const ImageMeta = ({ settings, image }) => {
8 | const config = settings.site.siteMetadata
9 |
10 | if (!image) {
11 | return null
12 | }
13 |
14 | return (
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | )
23 | }
24 |
25 | ImageMeta.propTypes = {
26 | settings: PropTypes.shape({
27 | site: PropTypes.object.isRequired,
28 | }).isRequired,
29 | image: PropTypes.string,
30 | }
31 |
32 | const ImageMetaQuery = props => (
33 | }
44 | />
45 | )
46 |
47 | export default ImageMetaQuery
48 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/components/meta/MetaData.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import PropTypes from "prop-types";
3 | import { StaticQuery, graphql } from "gatsby";
4 | import url from "url";
5 |
6 | import ArticleMeta from "./ArticleMeta";
7 | import WebsiteMeta from "./WebsiteMeta";
8 | import AuthorMeta from "./AuthorMeta";
9 |
10 | /**
11 | * MetaData will generate all relevant meta data information incl.
12 | * JSON-LD (schema.org), Open Graph (Facebook) and Twitter properties.
13 | *
14 | */
15 | const MetaData = ({
16 | data,
17 | settings,
18 | title,
19 | description,
20 | image,
21 | location,
22 | amp,
23 | }) => {
24 | const config = settings.site.siteMetadata;
25 | const canonical = url.resolve(config.siteUrl, location.pathname);
26 | const { ghostPost, ghostTag, ghostAuthor, ghostPage } = data;
27 | settings = settings.allGhostSettings.edges[0].node;
28 |
29 | if (ghostPost) {
30 | return ;
31 | } else if (ghostTag) {
32 | return ;
33 | } else if (ghostAuthor) {
34 | return ;
35 | } else if (ghostPage) {
36 | return ;
37 | } else {
38 | title = title || config.siteTitle;
39 | description = description || config.siteDescription;
40 | image = image || config.coverUrl || null;
41 |
42 | image = image ? url.resolve(config.siteUrl, image) : null;
43 |
44 | return (
45 |
53 | );
54 | }
55 | };
56 |
57 | MetaData.defaultProps = {
58 | data: {},
59 | };
60 |
61 | MetaData.propTypes = {
62 | data: PropTypes.shape({
63 | ghostPost: PropTypes.object,
64 | ghostTag: PropTypes.object,
65 | ghostAuthor: PropTypes.object,
66 | ghostPage: PropTypes.object,
67 | }).isRequired,
68 | settings: PropTypes.shape({
69 | allGhostSettings: PropTypes.object.isRequired,
70 | site: PropTypes.object.isRequired,
71 | }).isRequired,
72 | location: PropTypes.shape({
73 | pathname: PropTypes.string.isRequired,
74 | }).isRequired,
75 | title: PropTypes.string,
76 | description: PropTypes.string,
77 | image: PropTypes.string,
78 | };
79 |
80 | const MetaDataQuery = (props) => (
81 | }
100 | />
101 | );
102 |
103 | export default MetaDataQuery;
104 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/components/meta/WebsiteMeta.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Helmet from "react-helmet";
3 | import PropTypes from "prop-types";
4 | //import _ from 'lodash'
5 | import { StaticQuery, graphql } from "gatsby";
6 | import url from "url";
7 |
8 | //import ImageMeta from './ImageMeta'
9 |
10 | const WebsiteMeta = ({
11 | data,
12 | settings,
13 | canonical,
14 | title,
15 | description,
16 | image,
17 | type,
18 | }) => {
19 | const config = settings.site.siteMetadata;
20 | settings = settings.allGhostSettings.edges[0].node;
21 |
22 | const publisherLogo =
23 | config.logoUrl || config.alternateLogoUrl
24 | ? url.resolve(config.siteUrl, config.logoUrl || config.alternateLogoUrl)
25 | : null;
26 |
27 | let shareImage =
28 | config.coverUrl ||
29 | config.facebookCard.imageUrl ||
30 | config.twitterCard.imageUrl;
31 | shareImage = shareImage ? url.resolve(config.siteUrl, shareImage) : null;
32 |
33 | description =
34 | description ||
35 | data.meta_description ||
36 | data.description ||
37 | config.siteDescriptionMeta ||
38 | settings.description;
39 | title = settings.title;
40 |
41 | const jsonLd = {
42 | "@context": `https://schema.org/`,
43 | "@type": type,
44 | url: canonical,
45 | image: shareImage
46 | ? {
47 | "@type": `ImageObject`,
48 | url: shareImage,
49 | width: config.shareImageWidth,
50 | height: config.shareImageHeight,
51 | }
52 | : undefined,
53 | publisher: {
54 | "@type": `Organization`,
55 | name: config.siteTitle,
56 | logo: publisherLogo
57 | ? {
58 | "@type": `ImageObject`,
59 | url: publisherLogo,
60 | width: 60,
61 | height: 60,
62 | }
63 | : undefined,
64 | },
65 | mainEntityOfPage: {
66 | "@type": `WebPage`,
67 | "@id": config.siteUrl,
68 | },
69 | description: config.metadata.description || config.siteDescription,
70 | };
71 |
72 | return (
73 | // <>
74 | //
75 | // {title}
76 | //
77 | //
78 | //
79 | //
80 | //
81 | //
82 | //
83 | //
84 | //
85 | //
86 | // {settings.twitter && }
87 | // {settings.twitter && }
88 | //
89 | //
90 | //
91 | //twitter/facebook -> meta -> siteTitle
92 | //images - only if it exists
93 | // >
94 | <>
95 |
96 | {config.metadata.title || config.siteTitle}
97 |
101 |
102 |
103 |
104 |
105 |
113 |
121 |
122 | {config.facebookCard.imageUrl !== "" && (
123 |
127 | )}
128 | {config.facebookCard.width !== "" && (
129 |
130 | )}
131 | {config.facebookCard.height !== "" && (
132 |
136 | )}
137 | {config.facebookCard.appId !== "" && (
138 |
139 | )}
140 |
141 |
149 |
157 |
158 | {config.twitterCard.username && (
159 |
160 | )}
161 | {config.twitterCard.username && (
162 |
163 | )}
164 | {config.twitterCard.imageUrl !== "" && (
165 |
169 | )}
170 | {config.twitterCard.imageUrl !== "" && (
171 |
172 | )}
173 |
174 |
177 |
178 | {/* */}
179 | >
180 | );
181 | };
182 |
183 | WebsiteMeta.propTypes = {
184 | data: PropTypes.shape({
185 | title: PropTypes.string,
186 | meta_title: PropTypes.string,
187 | meta_description: PropTypes.string,
188 | name: PropTypes.string,
189 | feature_image: PropTypes.string,
190 | description: PropTypes.string,
191 | bio: PropTypes.string,
192 | profile_image: PropTypes.string,
193 | }).isRequired,
194 | settings: PropTypes.shape({
195 | logo: PropTypes.object,
196 | description: PropTypes.string,
197 | title: PropTypes.string,
198 | twitter: PropTypes.string,
199 | allGhostSettings: PropTypes.object.isRequired,
200 | site: PropTypes.object.isRequired,
201 | iconUrl: PropTypes.string,
202 | }).isRequired,
203 | canonical: PropTypes.string.isRequired,
204 | title: PropTypes.string,
205 | description: PropTypes.string,
206 | image: PropTypes.string,
207 | type: PropTypes.oneOf([`WebSite`, `Series`]).isRequired,
208 | };
209 |
210 | const WebsiteMetaQuery = (props) => (
211 | }
229 | />
230 | );
231 |
232 | export default WebsiteMetaQuery;
233 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/components/meta/getAuthorProperties.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash'
2 | import PropTypes from 'prop-types'
3 |
4 | export const getAuthorProperties = (primaryAuthor) => {
5 | let authorProfiles = []
6 |
7 | authorProfiles.push(
8 | primaryAuthor.website ? primaryAuthor.website : null,
9 | primaryAuthor.twitter ? `https://twitter.com/${primaryAuthor.twitter.replace(/^@/, ``)}/` : null,
10 | primaryAuthor.facebook ? `https://www.facebook.com/${primaryAuthor.facebook.replace(/^\//, ``)}/` : null
11 | )
12 |
13 | authorProfiles = _.compact(authorProfiles)
14 |
15 | return {
16 | name: primaryAuthor.name || null,
17 | sameAsArray: authorProfiles.length ? `["${_.join(authorProfiles, `", "`)}"]` : null,
18 | image: primaryAuthor.profile_image || null,
19 | facebookUrl: primaryAuthor.facebook ? `https://www.facebook.com/${primaryAuthor.facebook.replace(/^\//, ``)}/` : null,
20 | }
21 | }
22 |
23 | getAuthorProperties.defaultProps = {
24 | fetchAuthorData: false,
25 | }
26 |
27 | getAuthorProperties.PropTypes = {
28 | primaryAuthor: PropTypes.shape({
29 | name: PropTypes.string.isRequired,
30 | profile_image: PropTypes.string,
31 | website: PropTypes.string,
32 | twitter: PropTypes.string,
33 | facebook: PropTypes.string,
34 | }).isRequired,
35 | }
36 |
37 | export default getAuthorProperties
38 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/components/meta/index.js:
--------------------------------------------------------------------------------
1 | export { default as MetaData } from './MetaData'
2 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/components/navbar.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useStaticQuery, graphql, Link } from "gatsby";
3 | import url from "url";
4 |
5 | const Navbar = () => {
6 | const data = useStaticQuery(graphql`
7 | query {
8 | site {
9 | siteMetadata {
10 | siteUrl
11 | apiUrl
12 | logoUrl
13 | header {
14 | navigation {
15 | url
16 | label
17 | }
18 | }
19 | socialLinks {
20 | twitter
21 | facebook
22 | instagram
23 | linkedin
24 | github
25 | whatsapp
26 | pinterest
27 | youtube
28 | dribbble
29 | behance
30 | externalLink
31 | }
32 | }
33 | }
34 | }
35 | `);
36 |
37 | const {
38 | site: { siteMetadata },
39 | } = data;
40 |
41 | //const siteSettings = edges[0].node;
42 | const siteUrl = siteMetadata.siteUrl;
43 | const apiUrl = siteMetadata.apiUrl;
44 | const navigation = siteMetadata.header.navigation;
45 | const socialLinks = siteMetadata.socialLinks;
46 | return (
47 | <>
48 |
49 |
241 |
252 |
253 |
254 | >
255 | );
256 | };
257 |
258 | export default Navbar;
259 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/components/pagination.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Link } from "gatsby";
3 |
4 | const Pagination = ({ pageContext }) => {
5 | const {
6 | previousPagePath,
7 | nextPagePath,
8 | humanPageNumber,
9 | numberOfPages
10 | } = pageContext;
11 | return (
12 |
44 | );
45 | };
46 |
47 | export default Pagination;
48 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/components/post-card.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Link } from "gatsby";
3 |
4 | const PostCard = ({ post }) => {
5 | let excerpt = "";
6 |
7 | if (post.excerpt) {
8 | excerpt = post.excerpt + "...";
9 | }
10 |
11 | return (
12 |
13 |
14 |
15 |
20 |
21 |
22 |
23 |
28 |
29 |
30 |
31 | By{" "}
32 | {post.authors.map((author, index) => (
33 |
34 |
39 | {author.name}
40 |
41 | {index !== post.authors.length - 1 ? ", " : ""}
42 |
43 | ))}
44 | {post.tags.length > 0 && " in "}
45 | {post.tags.map((tag, index) => (
46 | <>
47 |
52 | {" "}
53 | {tag.name}
54 |
55 | {index !== post.tags.length - 1 ? ", " : ""}
56 | >
57 | ))}
58 | {" "}
59 | on{" "}
60 | {" "}
63 | • {post.readingTime}
64 |
65 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | );
76 | };
77 |
78 | export default PostCard;
79 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/components/subscribe-form.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useState } from "react";
3 | import { useForm } from "../hook/useForm";
4 | import { useStaticQuery, graphql } from "gatsby";
5 |
6 | const SubscribeForm = () => {
7 | const data = useStaticQuery(graphql`
8 | query {
9 | site {
10 | siteMetadata {
11 | siteTitle
12 | subscribeWidget {
13 | visible
14 | title
15 | helpText
16 | successMessage
17 | }
18 | }
19 | }
20 | }
21 | `);
22 |
23 | // const {
24 | // allGhostSettings: { edges },
25 | // } = data;
26 |
27 | //const siteSettings = edges[0].node;
28 | const subscribeWidget = data.site.siteMetadata.subscribeWidget;
29 | const siteTitle = data.site.siteMetadata.siteTitle;
30 | const [{ handleSubmit, submitting, succeeded }] = useForm("subscribe");
31 |
32 | const [email, setEmail] = useState();
33 |
34 | const onSubmit = (e) => {
35 | e.preventDefault();
36 | handleSubmit({ email });
37 | };
38 |
39 | return subscribeWidget.visible ? (
40 |
41 |
85 |
86 | ) : (
87 |
88 | );
89 | };
90 |
91 | export default SubscribeForm;
92 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/context/form-context.jsx:
--------------------------------------------------------------------------------
1 | import React, { createContext } from "react";
2 |
3 | // interface Props {
4 | // site: string;
5 | // }
6 |
7 | // interface Context {
8 | // client: any;
9 | // }
10 |
11 | export const ArmadaFormsContext = createContext({
12 | client: undefined
13 | });
14 |
15 | ArmadaFormsContext.displayName = "Armada Forms";
16 |
17 | export const ArmadaFormsProvider = ({ client, children }) => {
18 | return (
19 |
20 | {children}
21 |
22 | );
23 | };
24 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/hook/useForm.jsx:
--------------------------------------------------------------------------------
1 | import { useState, useContext } from "react";
2 | import { ArmadaFormsContext } from "../context/form-context";
3 |
4 | export const useForm = formKey => {
5 | const [submitting, setSubmitting] = useState(false);
6 | const [succeeded, setSucceeded] = useState(false);
7 | const [errors, setErrors] = useState([]);
8 | const { client } = useContext(ArmadaFormsContext);
9 |
10 | const handleSubmit = async data => {
11 | setSubmitting(true);
12 |
13 | try {
14 | let formHeaders = new Headers();
15 | formHeaders.append("Content-Type", "application/json");
16 | const response = await fetch(`${client}/${formKey}`, {
17 | method: "POST",
18 | mode: "cors",
19 | cache: "no-cache",
20 | credentials: "same-origin",
21 | body: JSON.stringify(data),
22 | headers: formHeaders
23 | });
24 |
25 | if (!response.ok) {
26 | setErrors([{ error: `error occured submitting request: ${formKey}` }]);
27 | } else {
28 | setSubmitting(false);
29 | setSucceeded(true);
30 | }
31 | } catch (error) {
32 | setErrors([{ error: `error occured submitting request: ${formKey}` }]);
33 | }
34 | };
35 |
36 | return [{ submitting, errors, succeeded, handleSubmit }];
37 | };
38 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/images/copied.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/images/copy.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/images/ghost-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/draftbox-co/gatsby-attila-theme-ghost/e74f112be4994e19d842d0797b878d2b4f32dbea/gatsby-theme-ghost-attila/src/images/ghost-icon.png
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/images/publication-cover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/draftbox-co/gatsby-attila-theme-ghost/e74f112be4994e19d842d0797b878d2b4f32dbea/gatsby-theme-ghost-attila/src/images/publication-cover.png
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/images/rss.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/pages/404.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Link } from "gatsby";
3 | import "../styles/sass/style.scss";
4 |
5 | const ErrorPage = () => {
6 | return (
7 |
8 |
9 |
Page Not Found
10 |
11 | Looks like you've followed a broken link or entered a URL that doesn't
12 | exist on this site.
13 |
14 |
15 | Back to our site →
16 |
17 |
18 |
19 | );
20 | };
21 |
22 | export default ErrorPage;
23 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/pages/contact.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Layout from "../components/Layout";
3 | import ContactForm from "../components/contact-form";
4 | import ContactMeta from "./../components/meta/ContactMeta";
5 | const Contact = ({location}) => {
6 |
7 | return (
8 |
9 |
10 |
11 |
12 | );
13 | };
14 |
15 | export default Contact;
16 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/pages/offline.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Link } from "gatsby";
3 | import "../styles/sass/style.scss";
4 |
5 | const OfflinePage = () => {
6 | return (
7 |
8 |
9 |
Offline :(
10 |
11 | Looks like you lost your connection. Please check it and try again.
12 |
13 |
14 | Try again →
15 |
16 |
17 |
18 | );
19 | };
20 |
21 | export default OfflinePage;
22 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/styles/font/icons.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/draftbox-co/gatsby-attila-theme-ghost/e74f112be4994e19d842d0797b878d2b4f32dbea/gatsby-theme-ghost-attila/src/styles/font/icons.eot
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/styles/font/icons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/draftbox-co/gatsby-attila-theme-ghost/e74f112be4994e19d842d0797b878d2b4f32dbea/gatsby-theme-ghost-attila/src/styles/font/icons.ttf
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/styles/font/icons.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/draftbox-co/gatsby-attila-theme-ghost/e74f112be4994e19d842d0797b878d2b4f32dbea/gatsby-theme-ghost-attila/src/styles/font/icons.woff
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/styles/font/icons.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/draftbox-co/gatsby-attila-theme-ghost/e74f112be4994e19d842d0797b878d2b4f32dbea/gatsby-theme-ghost-attila/src/styles/font/icons.woff2
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/styles/prism-theme/prism_dracula.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Dracula Theme originally by Zeno Rocha [@zenorocha]
3 | * https://draculatheme.com/
4 | *
5 | * Ported for PrismJS by Albert Vallverdu [@byverdu]
6 | */
7 |
8 | code[class*="language-"],
9 | pre[class*="language-"] {
10 | color: #f8f8f2;
11 | background: none;
12 | text-shadow: 0 1px rgba(0, 0, 0, 0.3);
13 | font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
14 | text-align: left;
15 | white-space: pre;
16 | word-spacing: normal;
17 | word-break: normal;
18 | word-wrap: normal;
19 | line-height: 1.5;
20 | -moz-tab-size: 4;
21 | -o-tab-size: 4;
22 | tab-size: 4;
23 | -webkit-hyphens: none;
24 | -moz-hyphens: none;
25 | -ms-hyphens: none;
26 | hyphens: none;
27 | }
28 |
29 | /* Code blocks */
30 | pre[class*="language-"] {
31 | padding: 1em;
32 | margin: 0.5em 0;
33 | overflow: auto;
34 | border-radius: 0.3em;
35 | }
36 |
37 | :not(pre) > code[class*="language-"],
38 | pre[class*="language-"] {
39 | background: #282a36;
40 | }
41 |
42 | /* Inline code */
43 | :not(pre) > code[class*="language-"] {
44 | padding: 0.1em;
45 | border-radius: 0.3em;
46 | white-space: normal;
47 | }
48 |
49 | .token.comment,
50 | .token.prolog,
51 | .token.doctype,
52 | .token.cdata {
53 | color: #6272a4;
54 | }
55 |
56 | .token.punctuation {
57 | color: #f8f8f2;
58 | }
59 |
60 | .namespace {
61 | opacity: 0.7;
62 | }
63 |
64 | .token.property,
65 | .token.tag,
66 | .token.constant,
67 | .token.symbol,
68 | .token.deleted {
69 | color: #ff79c6;
70 | }
71 |
72 | .token.boolean,
73 | .token.number {
74 | color: #bd93f9;
75 | }
76 |
77 | .token.selector,
78 | .token.attr-name,
79 | .token.string,
80 | .token.char,
81 | .token.builtin,
82 | .token.inserted {
83 | color: #50fa7b;
84 | }
85 |
86 | .token.operator,
87 | .token.entity,
88 | .token.url,
89 | .language-css .token.string,
90 | .style .token.string,
91 | .token.variable {
92 | color: #f8f8f2;
93 | }
94 |
95 | .token.atrule,
96 | .token.attr-value,
97 | .token.function,
98 | .token.class-name {
99 | color: #f1fa8c;
100 | }
101 |
102 | .token.keyword {
103 | color: #8be9fd;
104 | }
105 |
106 | .token.regex,
107 | .token.important {
108 | color: #ffb86c;
109 | }
110 |
111 | .token.important,
112 | .token.bold {
113 | font-weight: bold;
114 | }
115 |
116 | .token.italic {
117 | font-style: italic;
118 | }
119 |
120 | .token.entity {
121 | cursor: help;
122 | }
123 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/styles/sass/_colors.scss:
--------------------------------------------------------------------------------
1 | @function hsb($h-hsb, $s-hsb, $b-hsb, $a: 1) {
2 | @if $b-hsb == 0 {
3 | @return hsla(0, 0, 0, $a)
4 | } @else {
5 | $l-hsl: ($b-hsb/2) * (2 - ($s-hsb/100));
6 | $s-hsl: ($b-hsb * $s-hsb) / if($l-hsl < 50, $l-hsl * 2, 200 - $l-hsl * 2);
7 | @return hsla($h-hsb, $s-hsl, $l-hsl, $a);
8 | }
9 | }
10 | $color-neutral-98: hsb(215, 1, 98);
11 | $color-neutral-90: hsb(215, 2, 90);
12 | $color-neutral-80: hsb(215, 4, 80); // nav, menu, pagination - button border
13 | $color-neutral-60: hsb(215, 6, 60); // nav, menu, pagination - button font
14 | $color-neutral-25: hsb(215, 8, 25);
15 | $color-neutral-15: hsb(215, 10, 15);
16 | $color-yellow-100: #ffc336;
17 | $color-social-twitter: #3ea9dd;
18 | $color-social-facebook: #3c5a98;
19 | $color-social-linkedin: #0e76a8;
20 | $color-social-mail: #2997ee;
21 | $color-primary: var(--primary-color);
22 | $color-primary-active: var(--primary-color-active);
23 | $color-text-primary: $color-neutral-15;
24 | $color-text-primary-dark: $color-neutral-90;
25 | $color-text-secondary: $color-neutral-60;
26 | $color-text-secondary-dark: $color-neutral-60;
27 | $color-border: $color-neutral-90;
28 | $color-background: $color-neutral-98;
29 | $color-border-dark: $color-neutral-25;
30 | $color-background-dark: $color-neutral-15;
31 | $color-social-pinterest: #e60023;
32 | $color-social-whatsapp: #25D366;
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/styles/sass/_fonts.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/draftbox-co/gatsby-attila-theme-ghost/e74f112be4994e19d842d0797b878d2b4f32dbea/gatsby-theme-ghost-attila/src/styles/sass/_fonts.scss
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/styles/sass/_highlight.scss:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | github.com style (c) Vasily Polovnyov
4 |
5 | */
6 |
7 | .hljs {
8 | display: block;
9 | color: #66645c;
10 | -webkit-text-size-adjust: none;
11 | }
12 |
13 | .hljs-comment,
14 | .diff .hljs-header,
15 | .hljs-javadoc {
16 | color: #998;
17 | font-style: italic;
18 | }
19 |
20 | .hljs-keyword,
21 | .css .rule .hljs-keyword,
22 | .hljs-winutils,
23 | .nginx .hljs-title,
24 | .hljs-subst,
25 | .hljs-request,
26 | .hljs-status {
27 | color: #333;
28 | font-weight: bold;
29 | }
30 |
31 | .hljs-number,
32 | .hljs-hexcolor,
33 | .ruby .hljs-constant {
34 | color: #008080;
35 | }
36 |
37 | .hljs-string,
38 | .hljs-tag .hljs-value,
39 | .hljs-phpdoc,
40 | .hljs-dartdoc,
41 | .tex .hljs-formula {
42 | color: #d14;
43 | }
44 |
45 | .hljs-title,
46 | .hljs-id,
47 | .scss .hljs-preprocessor {
48 | color: #900;
49 | font-weight: bold;
50 | }
51 |
52 | .hljs-list .hljs-keyword,
53 | .hljs-subst {
54 | font-weight: normal;
55 | }
56 |
57 | .hljs-class .hljs-title,
58 | .hljs-type,
59 | .vhdl .hljs-literal,
60 | .tex .hljs-command {
61 | color: #458;
62 | font-weight: bold;
63 | }
64 |
65 | .hljs-tag,
66 | .hljs-tag .hljs-title,
67 | .hljs-rule .hljs-property,
68 | .django .hljs-tag .hljs-keyword {
69 | color: #000080;
70 | font-weight: normal;
71 | }
72 |
73 | .hljs-attribute,
74 | .hljs-variable,
75 | .lisp .hljs-body,
76 | .hljs-name {
77 | color: #008080;
78 | }
79 |
80 | .hljs-regexp {
81 | color: #009926;
82 | }
83 |
84 | .hljs-symbol,
85 | .ruby .hljs-symbol .hljs-string,
86 | .lisp .hljs-keyword,
87 | .clojure .hljs-keyword,
88 | .scheme .hljs-keyword,
89 | .tex .hljs-special,
90 | .hljs-prompt {
91 | color: #990073;
92 | }
93 |
94 | .hljs-built_in {
95 | color: #0086b3;
96 | }
97 |
98 | .hljs-preprocessor,
99 | .hljs-pragma,
100 | .hljs-pi,
101 | .hljs-doctype,
102 | .hljs-shebang,
103 | .hljs-cdata {
104 | color: #999;
105 | font-weight: bold;
106 | }
107 |
108 | .hljs-deletion {
109 | background: #fdd;
110 | }
111 |
112 | .hljs-addition {
113 | background: #dfd;
114 | }
115 |
116 | .diff .hljs-change {
117 | background: #0086b3;
118 | }
119 |
120 | .hljs-chunk {
121 | color: #aaa;
122 | }
123 |
124 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/styles/sass/_icons.scss:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: "icon";
3 | src: url("../font/icons.eot?74393752");
4 | src: url("../font/icons.eot?74393752#iefix") format("embedded-opentype"),
5 | url("../font/icons.woff2?74393752") format("woff2"),
6 | url("../font/icons.woff?74393752") format("woff"),
7 | url("../font/icons.ttf?74393752") format("truetype"),
8 | url("../font/icons.svg?74393752#icon") format("svg");
9 | font-weight: normal;
10 | font-style: normal;
11 | }
12 | /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */
13 | /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */
14 | /*
15 | @media screen and (-webkit-min-device-pixel-ratio:0) {
16 | @font-face {
17 | font-family: 'icon';
18 | src: url('../font/icon.svg?74393752#icon') format('svg');
19 | }
20 | }
21 | */
22 |
23 | [class^="icon-"]:before,
24 | [class*=" icon-"]:before {
25 | font-family: "icon";
26 | font-style: normal;
27 | font-weight: normal;
28 | speak: never;
29 |
30 | display: inline-block;
31 | text-decoration: inherit;
32 | width: 1em;
33 | margin-right: 0.2em;
34 | text-align: center;
35 | /* opacity: .8; */
36 |
37 | /* For safety - reset parent styles, that can break glyph codes*/
38 | font-variant: normal;
39 | text-transform: none;
40 |
41 | /* fix buttons height, for twitter bootstrap */
42 | line-height: 1em;
43 |
44 | /* Animation center compensation - margins should be symmetric */
45 | /* remove if not needed */
46 | margin-left: 0.2em;
47 |
48 | /* you can be more comfortable with increased icons size */
49 | /* font-size: 120%; */
50 |
51 | /* Font smoothing. That was taken from TWBS */
52 | -webkit-font-smoothing: antialiased;
53 | -moz-osx-font-smoothing: grayscale;
54 |
55 | /* Uncomment for 3D effect */
56 | /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
57 | }
58 |
59 | .icon-link:before {
60 | content: "\e800";
61 | } /* '' */
62 | .icon-mail:before {
63 | content: "\e801";
64 | } /* '' */
65 | .icon-globe:before {
66 | content: "\e802";
67 | } /* '' */
68 | .icon-left-big:before {
69 | content: "\e803";
70 | } /* '' */
71 | .icon-right-big:before {
72 | content: "\e804";
73 | } /* '' */
74 | .icon-search:before {
75 | content: "\e805";
76 | } /* '' */
77 | .icon-star:before {
78 | content: "\e806";
79 | } /* '' */
80 | .icon-twitter:before {
81 | content: "\f099";
82 | } /* '' */
83 | .icon-facebook:before {
84 | content: "\f09a";
85 | } /* '' */
86 | .icon-github-circled:before {
87 | content: "\f09b";
88 | } /* '' */
89 | .icon-rss:before {
90 | content: "\f09e";
91 | } /* '' */
92 | .icon-menu:before {
93 | content: "\f0c9";
94 | } /* '' */
95 | .icon-pinterest-circled:before {
96 | content: "\f0d2";
97 | } /* '' */
98 | .icon-linkedin:before {
99 | content: "\f0e1";
100 | } /* '' */
101 | .icon-youtube-play:before {
102 | content: "\f16a";
103 | } /* '' */
104 | .icon-instagram:before {
105 | content: "\f16d";
106 | } /* '' */
107 | .icon-dribbble:before {
108 | content: "\f17d";
109 | } /* '' */
110 | .icon-behance:before {
111 | content: "\f1b4";
112 | } /* '' */
113 | .icon-pinterest:before {
114 | content: "\f231";
115 | } /* '' */
116 | .icon-whatsapp:before {
117 | content: "\f232";
118 | } /* '' */
119 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/styles/sass/_normalize.scss:
--------------------------------------------------------------------------------
1 | /*! normalize.css v3.0.1 | MIT License | git.io/normalize */
2 | html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background:0 0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/styles/sass/_utilities.scss:
--------------------------------------------------------------------------------
1 | .break-words {
2 | overflow-wrap: break-word;
3 | }
4 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/templates/authorTemplate.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { graphql } from "gatsby";
3 | import Layout from "../components/Layout";
4 | import PostCard from "../components/post-card";
5 | import Pagination from "../components/pagination";
6 |
7 | const AuthorTemplate = ({ data, pageContext }) => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 | Author
15 | {/* {{plural ../pagination.total empty=(t "No Posts") singular=(t "% Post") plural=(t "% Posts")}} */}
16 |
17 |
18 | {data.ghostAuthor.profile_image && (
19 |
23 | )}
24 |
25 |
{data.ghostAuthor.name}
26 |
27 | {data.ghostAuthor.bio && (
28 |
32 | )}
33 |
34 |
35 | {data.ghostAuthor.website && (
36 |
42 |
43 | "Website
44 |
45 | )}
46 |
47 | {data.ghostAuthor.twitter && (
48 |
54 |
55 | Twitter
56 |
57 | )}
58 | {data.ghostAuthor.facebook && (
59 |
65 |
66 | Facebook
67 |
68 | )}
69 |
70 |
71 |
72 |
73 |
74 |
75 | {data.allGhostPost.edges.map(({ node }, i) => {
76 | return ;
77 | })}
78 |
79 |
80 |
81 |
82 | );
83 | };
84 |
85 | export default AuthorTemplate;
86 |
87 | export const pageQuery = graphql`
88 | query GhostAuthorQuery($slug: String!, $limit: Int!, $skip: Int!) {
89 | ghostAuthor(slug: { eq: $slug }) {
90 | name
91 | bio
92 | facebook
93 | twitter
94 | website
95 | profile_image
96 | slug
97 | }
98 | allGhostPost(
99 | sort: { order: DESC, fields: [published_at] }
100 | filter: { authors: { elemMatch: { slug: { eq: $slug } } } }
101 | limit: $limit
102 | skip: $skip
103 | ) {
104 | edges {
105 | node {
106 | uuid
107 | title
108 | url
109 | updated_at(formatString: "MMMM DD YYYY")
110 | authors {
111 | name
112 | }
113 | tags {
114 | name
115 | slug
116 | }
117 | excerpt
118 | readingTime
119 | slug
120 | }
121 | }
122 | }
123 | }
124 | `;
125 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/templates/indexTemplate.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { graphql } from "gatsby";
3 | import Layout from "../components/Layout";
4 | import PostCard from "../components/post-card";
5 | import Header from "../components/header";
6 | import Pagination from "../components/pagination";
7 | import { MetaData } from "../components/meta";
8 |
9 | const IndexTemplate = ({ data, pageContext, location }) => {
10 | return (
11 |
12 |
13 |
14 | {data.allGhostPost.edges.map(({ node }, i) => {
15 | return ;
16 | })}
17 |
20 |
21 | );
22 | };
23 |
24 | export default IndexTemplate;
25 |
26 | export const pageQuery = graphql`
27 | query($limit: Int!, $skip: Int!) {
28 | allGhostPost(
29 | sort: { order: DESC, fields: [published_at] }
30 | filter: { slug: { ne: "data-schema" } }
31 | limit: $limit
32 | skip: $skip
33 | ) {
34 | edges {
35 | node {
36 | uuid
37 | title
38 | url
39 | updated_at(formatString: "MMMM DD YYYY")
40 | published_at(formatString: "MMMM DD YYYY")
41 | authors {
42 | name
43 | slug
44 | }
45 | tags {
46 | name
47 | slug
48 | }
49 | excerpt
50 | slug
51 | readingTime
52 | featured
53 | }
54 | }
55 | }
56 | }
57 | `;
58 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/templates/postTemplate.amp.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { graphql, Link } from "gatsby";
3 | import { MetaData } from "../components/meta";
4 |
5 | const PostTemplate = ({ data, location, pageContext }) => {
6 | return (
7 | <>
8 |
14 |
15 |
18 |
19 |
20 |
21 |
22 | {data.ghostPost.title}
23 |
24 |
25 |
{data.ghostPost.primary_author.name}
26 |
27 |
{" "}
33 |
34 |
35 | {data.ghostPost.localFeatureImage &&
36 | data.ghostPost.localFeatureImage.childImageSharp && (
37 |
38 |
44 |
45 | )}
46 | {data.ghostPost.localFeatureImage &&
47 | data.ghostPost.localFeatureImage.extension === "svg" && (
48 |
49 |
53 |
54 | )}
55 | {data.ghostPost && data.ghostPost.html && (
56 |
60 | )}
61 | {data.ghostPost?.primary_tag && (
62 |
71 | )}
72 |
73 |
78 |
79 |
80 | >
81 | );
82 | };
83 |
84 | export default PostTemplate;
85 |
86 | export const pageQuery = graphql`
87 | query($slug: String!) {
88 | ghostPost(slug: { eq: $slug }) {
89 | title
90 | html
91 | og_title
92 | og_description
93 | slug
94 | feature_image
95 | excerpt
96 | twitter_title
97 | twitter_description
98 | meta_title
99 | meta_description
100 | primary_tag {
101 | name
102 | slug
103 | }
104 | primary_author {
105 | name
106 | profile_image
107 | slug
108 | }
109 | updated_at(formatString: "MMMM DD YYYY")
110 | published_at(formatString: "MMMM DD YYYY")
111 | feature_image
112 | localFeatureImage {
113 | childImageSharp {
114 | fluid {
115 | srcSet
116 | src
117 | }
118 | }
119 | seo: childImageSharp {
120 | fixed(width: 1200, quality: 100) {
121 | src
122 | }
123 | }
124 | publicURL
125 | extension
126 | }
127 | }
128 | }
129 | `;
130 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/templates/tagsTemplate.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { graphql } from "gatsby";
3 | import Layout from "../components/Layout";
4 | import Pagination from "../components/pagination";
5 | import PostCard from "../components/post-card";
6 |
7 | const TagsTemplate = ({ data, pageContext }) => {
8 | return (
9 | <>
10 |
11 |
32 |
33 |
34 | {data.allGhostPost.edges.map(({ node }, i) => {
35 | return ;
36 | })}
37 |
38 |
39 |
40 |
41 | >
42 | );
43 | };
44 |
45 | export default TagsTemplate;
46 |
47 | export const pageQuery = graphql`
48 | query GhostTagQuery($slug: String!, $limit: Int!, $skip: Int!) {
49 | ghostTag(slug: { eq: $slug }) {
50 | name
51 | description
52 | postCount
53 | }
54 | allGhostPost(
55 | sort: { order: DESC, fields: [published_at] }
56 | filter: { tags: { elemMatch: { slug: { eq: $slug } } } }
57 | limit: $limit
58 | skip: $skip
59 | ) {
60 | edges {
61 | node {
62 | uuid
63 | title
64 | url
65 | updated_at(formatString: "MMMM DD YYYY")
66 | authors {
67 | name
68 | }
69 | tags {
70 | name
71 | slug
72 | }
73 | excerpt
74 | readingTime
75 | slug
76 | }
77 | }
78 | }
79 | }
80 | `;
81 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/utils/.ghost.json:
--------------------------------------------------------------------------------
1 | {
2 | "development": {
3 | "apiUrl": "https://cms.gotsby.org",
4 | "contentApiKey": "387f956eaa95345f7bb484d0b8"
5 | },
6 | "production": {
7 | "apiUrl": "https://cms.gotsby.org",
8 | "contentApiKey": "387f956eaa95345f7bb484d0b8"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/utils/fragments.js:
--------------------------------------------------------------------------------
1 | import { graphql } from "gatsby";
2 |
3 | /**
4 | * These so called fragments are the fields we query on each template.
5 | * A fragment make queries a bit more reuseable, so instead of typing and
6 | * remembering every possible field, you can just use
7 | * ...GhostPostFields
8 | * for example to load all post fields into your GraphQL query.
9 | *
10 | * Further info 👉🏼 https://www.gatsbyjs.org/docs/graphql-reference/#fragments
11 | *
12 | */
13 |
14 | // Used for site config
15 | export const siteMetadataFields = graphql`
16 | fragment SiteMetadataFields on SiteSiteMetadata {
17 | siteUrl
18 | postsPerPage
19 | siteTitleMeta
20 | siteDescriptionMeta
21 | shareImageWidth
22 | shareImageHeight
23 | shortTitle
24 | siteIcon
25 | backgroundColor
26 | themeColor
27 | siteTitle
28 | siteDescription
29 | language
30 | logoUrl
31 | alternateLogoUrl
32 | coverUrl
33 | metadata {
34 | title
35 | description
36 | }
37 | twitterCard {
38 | title
39 | description
40 | imageUrl
41 | username
42 | }
43 | facebookCard {
44 | title
45 | description
46 | imageUrl
47 | appId
48 | height
49 | width
50 | }
51 | }
52 | `;
53 |
54 |
55 | // Used for settings
56 | export const ghostSettingsFields = graphql`
57 | fragment GhostSettingsFields on GhostSettings {
58 | title
59 | description
60 | logo
61 | icon
62 | cover_image
63 | facebook
64 | twitter
65 | lang
66 | timezone
67 | codeinjection_head
68 | codeinjection_foot
69 | codeinjection_styles
70 | navigation {
71 | label
72 | url
73 | }
74 | }
75 | `;
76 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/utils/rss/generate-feed.js:
--------------------------------------------------------------------------------
1 | const cheerio = require(`cheerio`);
2 | const tagsHelper = require(`@tryghost/helpers`).tags;
3 | const _ = require(`lodash`);
4 |
5 | const generateItem = function generateItem(post) {
6 | const itemUrl = post.canonical_url || post.url;
7 | const html = post.html || "";
8 | const htmlContent = cheerio.load(html, {
9 | decodeEntities: false,
10 | xmlMode: true,
11 | });
12 | const item = {
13 | title: post.title,
14 | description: post.excerpt,
15 | guid: post.id,
16 | url: itemUrl,
17 | date: post.published_at,
18 | categories: _.map(
19 | tagsHelper(post, { visibility: `public`, fn: (tag) => tag }),
20 | `name`
21 | ),
22 | author: post.primary_author ? post.primary_author.name : null,
23 | custom_elements: [],
24 | };
25 | let imageUrl;
26 |
27 | if (post.feature_image) {
28 | imageUrl = post.feature_image;
29 |
30 | // Add a media content tag
31 | item.custom_elements.push({
32 | "media:content": {
33 | _attr: {
34 | url: imageUrl,
35 | medium: `image`,
36 | },
37 | },
38 | });
39 |
40 | // Also add the image to the content, because not all readers support media:content
41 | htmlContent(`p`)
42 | .first()
43 | .before(`
`);
44 | htmlContent(`img`).attr(`alt`, post.title);
45 | }
46 |
47 | item.custom_elements.push({
48 | "content:encoded": {
49 | _cdata: htmlContent.html(),
50 | },
51 | });
52 | return item;
53 | };
54 |
55 | const generateRSSFeed = function generateRSSFeed(siteConfig) {
56 | return {
57 | serialize: ({ query: { allGhostPost } }) =>
58 | allGhostPost.edges.map((edge) =>
59 | Object.assign({}, generateItem(edge.node))
60 | ),
61 | setup: ({ query: { allGhostSettings } }) => {
62 | const siteTitle = siteConfig.siteTitle || `No Title`;
63 | const siteDescription =
64 | siteConfig.siteDescription || `No Description`;
65 | const feed = {
66 | title: siteTitle,
67 | description: siteDescription,
68 | // generator: `Ghost ` + data.safeVersion,
69 | generator: `Ghost 2.9`,
70 | feed_url: `${siteConfig.siteUrl}/rss.xml`,
71 | site_url: `${siteConfig.siteUrl}/`,
72 | image_url: `${siteConfig.siteUrl}/${siteConfig.siteIcon}`,
73 | ttl: `60`,
74 | custom_namespaces: {
75 | content: `http://purl.org/rss/1.0/modules/content/`,
76 | media: `http://search.yahoo.com/mrss/`,
77 | },
78 | };
79 | return {
80 | ...feed,
81 | };
82 | },
83 | query: `
84 | {
85 | allGhostPost(
86 | sort: {order: DESC, fields: published_at}
87 | ) {
88 | edges {
89 | node {
90 | # Main fields
91 | id
92 | title
93 | slug
94 | featured
95 | feature_image
96 |
97 | # Dates unformatted
98 | created_at
99 | published_at
100 | updated_at
101 |
102 | # SEO
103 | excerpt
104 | meta_title
105 | meta_description
106 |
107 | # Authors
108 | authors {
109 | name
110 | }
111 | primary_author {
112 | name
113 | }
114 | tags {
115 | name
116 | visibility
117 | }
118 |
119 | # Content
120 | html
121 |
122 | # Additional fields
123 | url
124 | canonical_url
125 | }
126 | }
127 | }
128 | }
129 | `,
130 | output: `/rss.xml`,
131 | };
132 | };
133 |
134 | module.exports = generateRSSFeed;
135 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/src/utils/siteConfig.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | siteUrl: `https://eloquent-bhaskara-183237.netlify.app`, // Site domain. Do not include a trailing slash!
3 |
4 | postsPerPage: 12, // Number of posts shown on paginated pages (changes this requires sometimes to delete the cache)
5 |
6 | siteTitleMeta: `Ghost Gatsby Starter`, // This allows an alternative site title for meta data for pages.
7 | siteDescriptionMeta: `A starter template to build amazing static websites with Ghost and Gatsby`, // This allows an alternative site description for meta data for pages.
8 |
9 | shareImageWidth: 1000, // Change to the width of your default share image
10 | shareImageHeight: 523, // Change to the height of your default share image
11 |
12 | shortTitle: `Ghost`, // Used for App manifest e.g. Mobile Home Screen
13 | siteIcon: `favicon.png`, // Logo in /static dir used for SEO, RSS, and App manifest
14 | backgroundColor: `#e9e9e9`, // Used for Offline Manifest
15 | themeColor: `#15171A` // Used for Offline Manifest
16 | };
17 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/static/alternateLogo.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/static/cover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/draftbox-co/gatsby-attila-theme-ghost/e74f112be4994e19d842d0797b878d2b4f32dbea/gatsby-theme-ghost-attila/static/cover.png
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/static/coverImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/draftbox-co/gatsby-attila-theme-ghost/e74f112be4994e19d842d0797b878d2b4f32dbea/gatsby-theme-ghost-attila/static/coverImage.png
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/static/facebookImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/draftbox-co/gatsby-attila-theme-ghost/e74f112be4994e19d842d0797b878d2b4f32dbea/gatsby-theme-ghost-attila/static/facebookImage.png
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/draftbox-co/gatsby-attila-theme-ghost/e74f112be4994e19d842d0797b878d2b4f32dbea/gatsby-theme-ghost-attila/static/favicon.ico
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/static/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/draftbox-co/gatsby-attila-theme-ghost/e74f112be4994e19d842d0797b878d2b4f32dbea/gatsby-theme-ghost-attila/static/favicon.png
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/static/fbCardImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/draftbox-co/gatsby-attila-theme-ghost/e74f112be4994e19d842d0797b878d2b4f32dbea/gatsby-theme-ghost-attila/static/fbCardImage.png
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/static/images/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/static/logo.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/static/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Allow: /
3 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/static/sw.js:
--------------------------------------------------------------------------------
1 | // This is the "Offline page" service worker
2 |
3 | importScripts('https://storage.googleapis.com/workbox-cdn/releases/5.0.0/workbox-sw.js');
4 |
5 | const CACHE = "attila-ghost-theme-cache";
6 |
7 | // TODO: replace the following with the correct offline fallback page i.e.: const offlineFallbackPage = "offline.html";
8 | const offlineFallbackPage = "offline/index.html";
9 |
10 | self.skipWaiting();
11 | // self.addEventListener("message", (event) => {
12 | // if (event.data && event.data.type === "SKIP_WAITING") {
13 | // self.skipWaiting();
14 | // }
15 | // });
16 |
17 | self.addEventListener('install', async (event) => {
18 | event.waitUntil(
19 | caches.open(CACHE)
20 | .then((cache) => cache.add(offlineFallbackPage))
21 | );
22 | });
23 |
24 | if (workbox.navigationPreload.isSupported()) {
25 | workbox.navigationPreload.enable();
26 | }
27 |
28 | self.addEventListener('fetch', (event) => {
29 | if (event.request.mode === 'navigate') {
30 | event.respondWith((async () => {
31 | try {
32 | const preloadResp = await event.preloadResponse;
33 |
34 | if (preloadResp) {
35 | return preloadResp;
36 | }
37 |
38 | const networkResp = await fetch(event.request);
39 | return networkResp;
40 | } catch (error) {
41 |
42 | const cache = await caches.open(CACHE);
43 | const cachedResp = await cache.match(offlineFallbackPage);
44 | return cachedResp;
45 | }
46 | })());
47 | }
48 | });
49 |
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/static/twitterCardImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/draftbox-co/gatsby-attila-theme-ghost/e74f112be4994e19d842d0797b878d2b4f32dbea/gatsby-theme-ghost-attila/static/twitterCardImage.png
--------------------------------------------------------------------------------
/gatsby-theme-ghost-attila/static/twitterImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/draftbox-co/gatsby-attila-theme-ghost/e74f112be4994e19d842d0797b878d2b4f32dbea/gatsby-theme-ghost-attila/static/twitterImage.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@draftbox-co/gatsby-starter-theme-workspace",
3 | "private": true,
4 | "version": "0.0.1",
5 | "main": "index.js",
6 | "license": "MIT",
7 | "scripts": {
8 | "build": "yarn workspace example build"
9 | },
10 | "workspaces": [
11 | "gatsby-theme-ghost-attila",
12 | "example"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------