soames-gatsby-theme 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,90 +9,40 @@ const html_react_parser_1 = __importDefault(require("html-react-parser"));
9
9
  const Layout_1 = __importDefault(require("../components/Layout"));
10
10
  const Seo_1 = __importDefault(require("../components/Seo"));
11
11
  const HeroHeader_1 = __importDefault(require("../components/HeroHeader"));
12
+ const Bio_1 = __importDefault(require("../components/Bio"));
13
+ const BlogSidebar_1 = __importDefault(require("../components/BlogSidebar"));
12
14
  const Shortcodes_1 = require("../utils/shortcodes/Shortcodes");
13
- const POST_PREVIEW_QUERY = `
14
- query PreviewPost($id: ID!) {
15
- post(id: $id, idType: DATABASE_ID, asPreview: true) {
16
- title
17
- content
18
- excerpt
19
- date
20
- featuredImage {
21
- node {
22
- sourceUrl
23
- altText
24
- }
25
- }
26
- }
27
- }
28
- `;
29
- const PAGE_PREVIEW_QUERY = `
30
- query PreviewPage($id: ID!) {
31
- page(id: $id, idType: DATABASE_ID, asPreview: true) {
32
- title
33
- content
34
- excerpt
35
- featuredImage {
36
- node {
37
- sourceUrl
38
- altText
39
- }
40
- }
41
- }
42
- }
43
- `;
15
+ require("../styles/vendor/wordpress-blocks.css");
44
16
  const PreviewPage = () => {
45
17
  const [content, setContent] = (0, react_1.useState)(null);
46
- const [contentType, setContentType] = (0, react_1.useState)("post");
47
18
  const [loading, setLoading] = (0, react_1.useState)(true);
48
19
  const [error, setError] = (0, react_1.useState)(null);
49
20
  (0, react_1.useEffect)(() => {
50
21
  if (typeof window === "undefined")
51
22
  return;
52
23
  const params = new URLSearchParams(window.location.search);
53
- const id = params.get("id");
54
- const type = (params.get("type") || "post").toLowerCase();
55
24
  const token = params.get("token");
56
- setContentType(type);
57
- if (!id) {
58
- setError("No preview ID provided. Please use the Preview button in WordPress admin.");
25
+ if (!token) {
26
+ setError("No preview token provided. Please use the Preview button in WordPress admin.");
59
27
  setLoading(false);
60
28
  return;
61
29
  }
62
- const wpGraphQLUrl = process.env.GATSBY_WORDPRESS_URL;
63
- if (!wpGraphQLUrl) {
30
+ const wpUrl = process.env.GATSBY_WORDPRESS_URL;
31
+ if (!wpUrl) {
64
32
  setError("WordPress URL is not configured.");
65
33
  setLoading(false);
66
34
  return;
67
35
  }
68
- const query = type === "page" ? PAGE_PREVIEW_QUERY : POST_PREVIEW_QUERY;
69
- fetch(wpGraphQLUrl, {
70
- method: "POST",
71
- credentials: token ? "omit" : "include",
72
- headers: {
73
- "Content-Type": "application/json",
74
- ...(token ? { Authorization: `Bearer ${token}` } : {}),
75
- },
76
- body: JSON.stringify({ query, variables: { id } }),
77
- })
36
+ const wpOrigin = new URL(wpUrl).origin;
37
+ const endpoint = `${wpOrigin}/wp-json/soames/v1/preview?token=${encodeURIComponent(token)}`;
38
+ fetch(endpoint)
78
39
  .then((res) => res.json())
79
40
  .then((data) => {
80
- const node = data?.data?.post ?? data?.data?.page ?? null;
81
- if (!node) {
82
- const firstError = data?.errors?.[0]?.message;
83
- if (firstError &&
84
- (firstError.toLowerCase().includes("forbidden") ||
85
- firstError.toLowerCase().includes("authorization"))) {
86
- setError("Preview token expired or invalid. Please click Preview again from WordPress admin.");
87
- }
88
- else {
89
- setError(firstError
90
- ? `Could not load preview: ${firstError}`
91
- : "Could not load preview content. The post or page may not exist.");
92
- }
41
+ if (data.code) {
42
+ setError(data.message ?? "Could not load preview content.");
93
43
  }
94
44
  else {
95
- setContent(node);
45
+ setContent(data);
96
46
  }
97
47
  setLoading(false);
98
48
  })
@@ -107,8 +57,21 @@ const PreviewPage = () => {
107
57
  if (error || !content) {
108
58
  return ((0, jsx_runtime_1.jsxs)(Layout_1.default, { children: [(0, jsx_runtime_1.jsx)(Seo_1.default, { title: "Preview Unavailable" }), (0, jsx_runtime_1.jsxs)("section", { style: { padding: "4rem 2rem", textAlign: "center" }, children: [(0, jsx_runtime_1.jsx)("h2", { children: "Preview Unavailable" }), (0, jsx_runtime_1.jsx)("p", { children: error ?? "No preview content found." })] })] }));
109
59
  }
110
- const backgroundImage = content.featuredImage?.node?.sourceUrl ?? null;
111
- const backgroundImageTitle = content.title ?? null;
112
- return ((0, jsx_runtime_1.jsxs)(Layout_1.default, { children: [(0, jsx_runtime_1.jsx)(Seo_1.default, { title: `Preview: ${content.title ?? ""}` }), (0, jsx_runtime_1.jsx)(HeroHeader_1.default, { title: content.title ? (0, html_react_parser_1.default)(content.title) : "Preview", subhead: content.excerpt ? (0, html_react_parser_1.default)(content.excerpt) : "", backgroundImage: backgroundImage, backgroundImageTitle: backgroundImageTitle }), contentType === "post" ? ((0, jsx_runtime_1.jsx)("section", { children: (0, jsx_runtime_1.jsx)("div", { className: "media-container-row", children: (0, jsx_runtime_1.jsx)("div", { className: "col-12 col-lg-8", children: (0, jsx_runtime_1.jsx)("section", { id: "soames-gatsby-content-container", className: "soames-gatsby-blog-content", children: (0, jsx_runtime_1.jsxs)("article", { className: "blog-post", children: [(0, jsx_runtime_1.jsxs)("header", { children: [(0, jsx_runtime_1.jsx)("h1", { children: content.title ? (0, html_react_parser_1.default)(content.title) : "" }), content.date && (0, jsx_runtime_1.jsx)("p", { children: content.date })] }), content.content && ((0, jsx_runtime_1.jsx)("section", { className: "blog-post-content", children: (0, html_react_parser_1.default)(content.content) }))] }) }) }) }) })) : (content.content && ((0, jsx_runtime_1.jsx)("section", { id: "soames-gatsby-content-container", className: "soames-gatsby-content", children: (0, jsx_runtime_1.jsx)(Shortcodes_1.Shortcodes, { children: content.content }) })))] }));
60
+ const isPost = content.type === "post";
61
+ // For posts, HeroHeader mirrors the Blog archive page hero (matching blog-post.tsx).
62
+ // For pages, use the page's own title and featured image (matching page.tsx).
63
+ const heroTitle = isPost
64
+ ? (content.blogHero?.title ? (0, html_react_parser_1.default)(content.blogHero.title) : "Blog")
65
+ : (content.title ? (0, html_react_parser_1.default)(content.title) : "Preview");
66
+ const heroSubhead = isPost
67
+ ? (content.blogHero?.excerpt ? (0, html_react_parser_1.default)(content.blogHero.excerpt) : "")
68
+ : (content.excerpt ? (0, html_react_parser_1.default)(content.excerpt) : "");
69
+ const heroBg = isPost
70
+ ? (content.blogHero?.guid ?? null)
71
+ : (content.featuredImage?.sourceUrl ?? null);
72
+ const heroBgTitle = isPost
73
+ ? (content.blogHero?.title ?? null)
74
+ : (content.title ?? null);
75
+ return ((0, jsx_runtime_1.jsxs)(Layout_1.default, { children: [(0, jsx_runtime_1.jsx)(Seo_1.default, { title: `Preview: ${content.title ?? ""}` }), (0, jsx_runtime_1.jsx)(HeroHeader_1.default, { title: heroTitle, subhead: heroSubhead, backgroundImage: heroBg, backgroundImageTitle: heroBgTitle }), isPost ? ((0, jsx_runtime_1.jsx)("section", { children: (0, jsx_runtime_1.jsxs)("div", { className: "media-container-row", children: [(0, jsx_runtime_1.jsx)("div", { className: "col-12 col-lg-8", children: (0, jsx_runtime_1.jsx)("section", { id: "soames-gatsby-content-container", className: "soames-gatsby-blog-content", children: (0, jsx_runtime_1.jsxs)("article", { className: "blog-post", itemScope: true, itemType: "http://schema.org/Article", children: [(0, jsx_runtime_1.jsxs)("header", { children: [(0, jsx_runtime_1.jsx)("h1", { itemProp: "headline", children: content.title ? (0, html_react_parser_1.default)(content.title) : "" }), content.date && (0, jsx_runtime_1.jsx)("p", { children: content.date })] }), content.content && ((0, jsx_runtime_1.jsx)("section", { itemProp: "articleBody", className: "blog-post-content", children: (0, html_react_parser_1.default)(content.content) })), (0, jsx_runtime_1.jsx)("hr", {}), (0, jsx_runtime_1.jsx)("footer", { children: (0, jsx_runtime_1.jsx)(Bio_1.default, {}) })] }) }) }), (0, jsx_runtime_1.jsx)("div", { className: "col-12 col-lg-4", children: (0, jsx_runtime_1.jsxs)("section", { id: "soames-gatsby-sidebar-container", className: "soames-gatsby-sidebar", children: [content.featuredImage?.sourceUrl && ((0, jsx_runtime_1.jsx)("img", { src: content.featuredImage.sourceUrl, alt: content.featuredImage.altText || "", style: { marginBottom: 50, width: "100%" } })), (0, jsx_runtime_1.jsx)("h1", { children: "Recent Posts" }), (0, jsx_runtime_1.jsx)(BlogSidebar_1.default, { postId: "" })] }) })] }) })) : (content.content && ((0, jsx_runtime_1.jsx)("section", { id: "soames-gatsby-content-container", className: "soames-gatsby-content", children: (0, jsx_runtime_1.jsx)(Shortcodes_1.Shortcodes, { children: content.content }) })))] }));
113
76
  };
114
77
  exports.default = PreviewPage;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "soames-gatsby-theme",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "A customizable Gatsby theme for personal websites using WordPress as a headless CMS.",
5
5
  "main": "dist/gatsby-config.js",
6
6
  "scripts": {
@@ -3,59 +3,34 @@ import parse from "html-react-parser";
3
3
  import Layout from "../components/Layout";
4
4
  import Seo from "../components/Seo";
5
5
  import HeroHeader from "../components/HeroHeader";
6
+ import Bio from "../components/Bio";
7
+ import BlogSidebar from "../components/BlogSidebar";
6
8
  import { Shortcodes } from "../utils/shortcodes/Shortcodes";
9
+ import "../styles/vendor/wordpress-blocks.css";
7
10
 
8
11
  interface FeaturedImage {
9
- node: {
10
- sourceUrl?: string;
11
- altText?: string;
12
- };
12
+ sourceUrl?: string;
13
+ altText?: string;
14
+ }
15
+
16
+ interface BlogHero {
17
+ title?: string | null;
18
+ excerpt?: string | null;
19
+ guid?: string | null;
13
20
  }
14
21
 
15
22
  interface PreviewContent {
23
+ type: string;
16
24
  title?: string;
17
25
  content?: string;
18
26
  excerpt?: string;
19
27
  date?: string;
20
28
  featuredImage?: FeaturedImage | null;
29
+ blogHero?: BlogHero | null;
21
30
  }
22
31
 
23
- const POST_PREVIEW_QUERY = `
24
- query PreviewPost($id: ID!) {
25
- post(id: $id, idType: DATABASE_ID, asPreview: true) {
26
- title
27
- content
28
- excerpt
29
- date
30
- featuredImage {
31
- node {
32
- sourceUrl
33
- altText
34
- }
35
- }
36
- }
37
- }
38
- `;
39
-
40
- const PAGE_PREVIEW_QUERY = `
41
- query PreviewPage($id: ID!) {
42
- page(id: $id, idType: DATABASE_ID, asPreview: true) {
43
- title
44
- content
45
- excerpt
46
- featuredImage {
47
- node {
48
- sourceUrl
49
- altText
50
- }
51
- }
52
- }
53
- }
54
- `;
55
-
56
32
  const PreviewPage: React.FC = () => {
57
33
  const [content, setContent] = useState<PreviewContent | null>(null);
58
- const [contentType, setContentType] = useState<string>("post");
59
34
  const [loading, setLoading] = useState(true);
60
35
  const [error, setError] = useState<string | null>(null);
61
36
 
@@ -63,60 +38,33 @@ const PreviewPage: React.FC = () => {
63
38
  if (typeof window === "undefined") return;
64
39
 
65
40
  const params = new URLSearchParams(window.location.search);
66
- const id = params.get("id");
67
- const type = (params.get("type") || "post").toLowerCase();
68
41
  const token = params.get("token");
69
42
 
70
- setContentType(type);
71
-
72
- if (!id) {
43
+ if (!token) {
73
44
  setError(
74
- "No preview ID provided. Please use the Preview button in WordPress admin."
45
+ "No preview token provided. Please use the Preview button in WordPress admin."
75
46
  );
76
47
  setLoading(false);
77
48
  return;
78
49
  }
79
50
 
80
- const wpGraphQLUrl = process.env.GATSBY_WORDPRESS_URL;
81
- if (!wpGraphQLUrl) {
51
+ const wpUrl = process.env.GATSBY_WORDPRESS_URL;
52
+ if (!wpUrl) {
82
53
  setError("WordPress URL is not configured.");
83
54
  setLoading(false);
84
55
  return;
85
56
  }
86
57
 
87
- const query = type === "page" ? PAGE_PREVIEW_QUERY : POST_PREVIEW_QUERY;
88
-
89
- fetch(wpGraphQLUrl, {
90
- method: "POST",
91
- credentials: token ? "omit" : "include",
92
- headers: {
93
- "Content-Type": "application/json",
94
- ...(token ? { Authorization: `Bearer ${token}` } : {}),
95
- },
96
- body: JSON.stringify({ query, variables: { id } }),
97
- })
58
+ const wpOrigin = new URL(wpUrl).origin;
59
+ const endpoint = `${wpOrigin}/wp-json/soames/v1/preview?token=${encodeURIComponent(token)}`;
60
+
61
+ fetch(endpoint)
98
62
  .then((res) => res.json())
99
63
  .then((data) => {
100
- const node = data?.data?.post ?? data?.data?.page ?? null;
101
- if (!node) {
102
- const firstError: string | undefined = data?.errors?.[0]?.message;
103
- if (
104
- firstError &&
105
- (firstError.toLowerCase().includes("forbidden") ||
106
- firstError.toLowerCase().includes("authorization"))
107
- ) {
108
- setError(
109
- "Preview token expired or invalid. Please click Preview again from WordPress admin."
110
- );
111
- } else {
112
- setError(
113
- firstError
114
- ? `Could not load preview: ${firstError}`
115
- : "Could not load preview content. The post or page may not exist."
116
- );
117
- }
64
+ if (data.code) {
65
+ setError(data.message ?? "Could not load preview content.");
118
66
  } else {
119
- setContent(node as PreviewContent);
67
+ setContent(data as PreviewContent);
120
68
  }
121
69
  setLoading(false);
122
70
  })
@@ -148,19 +96,33 @@ const PreviewPage: React.FC = () => {
148
96
  );
149
97
  }
150
98
 
151
- const backgroundImage = content.featuredImage?.node?.sourceUrl ?? null;
152
- const backgroundImageTitle = content.title ?? null;
99
+ const isPost = content.type === "post";
100
+
101
+ // For posts, HeroHeader mirrors the Blog archive page hero (matching blog-post.tsx).
102
+ // For pages, use the page's own title and featured image (matching page.tsx).
103
+ const heroTitle = isPost
104
+ ? (content.blogHero?.title ? parse(content.blogHero.title) : "Blog")
105
+ : (content.title ? parse(content.title) : "Preview");
106
+ const heroSubhead = isPost
107
+ ? (content.blogHero?.excerpt ? parse(content.blogHero.excerpt) : "")
108
+ : (content.excerpt ? parse(content.excerpt) : "");
109
+ const heroBg = isPost
110
+ ? (content.blogHero?.guid ?? null)
111
+ : (content.featuredImage?.sourceUrl ?? null);
112
+ const heroBgTitle = isPost
113
+ ? (content.blogHero?.title ?? null)
114
+ : (content.title ?? null);
153
115
 
154
116
  return (
155
117
  <Layout>
156
118
  <Seo title={`Preview: ${content.title ?? ""}`} />
157
119
  <HeroHeader
158
- title={content.title ? parse(content.title) : "Preview"}
159
- subhead={content.excerpt ? parse(content.excerpt) : ""}
160
- backgroundImage={backgroundImage}
161
- backgroundImageTitle={backgroundImageTitle}
120
+ title={heroTitle}
121
+ subhead={heroSubhead}
122
+ backgroundImage={heroBg}
123
+ backgroundImageTitle={heroBgTitle}
162
124
  />
163
- {contentType === "post" ? (
125
+ {isPost ? (
164
126
  <section>
165
127
  <div className="media-container-row">
166
128
  <div className="col-12 col-lg-8">
@@ -168,19 +130,48 @@ const PreviewPage: React.FC = () => {
168
130
  id="soames-gatsby-content-container"
169
131
  className="soames-gatsby-blog-content"
170
132
  >
171
- <article className="blog-post">
133
+ <article
134
+ className="blog-post"
135
+ itemScope
136
+ itemType="http://schema.org/Article"
137
+ >
172
138
  <header>
173
- <h1>{content.title ? parse(content.title) : ""}</h1>
139
+ <h1 itemProp="headline">
140
+ {content.title ? parse(content.title) : ""}
141
+ </h1>
174
142
  {content.date && <p>{content.date}</p>}
175
143
  </header>
176
144
  {content.content && (
177
- <section className="blog-post-content">
145
+ <section
146
+ itemProp="articleBody"
147
+ className="blog-post-content"
148
+ >
178
149
  {parse(content.content)}
179
150
  </section>
180
151
  )}
152
+ <hr />
153
+ <footer>
154
+ <Bio />
155
+ </footer>
181
156
  </article>
182
157
  </section>
183
158
  </div>
159
+ <div className="col-12 col-lg-4">
160
+ <section
161
+ id="soames-gatsby-sidebar-container"
162
+ className="soames-gatsby-sidebar"
163
+ >
164
+ {content.featuredImage?.sourceUrl && (
165
+ <img
166
+ src={content.featuredImage.sourceUrl}
167
+ alt={content.featuredImage.altText || ""}
168
+ style={{ marginBottom: 50, width: "100%" }}
169
+ />
170
+ )}
171
+ <h1>Recent Posts</h1>
172
+ <BlogSidebar postId="" />
173
+ </section>
174
+ </div>
184
175
  </div>
185
176
  </section>
186
177
  ) : (