Refactor single blog posts
This commit is contained in:
		
							parent
							
								
									b99a807788
								
							
						
					
					
						commit
						2ad9e44e19
					
				| @ -5,7 +5,11 @@ | |||||||
|  */ |  */ | ||||||
| import React from "react"; | import React from "react"; | ||||||
| import Head from "next/head"; | import Head from "next/head"; | ||||||
| import type { InferGetStaticPropsType, GetStaticProps, GetStaticPaths } from "next"; | import type { | ||||||
|  |     InferGetStaticPropsType, | ||||||
|  |     GetStaticProps, | ||||||
|  |     GetStaticPaths, | ||||||
|  | } from "next"; | ||||||
| const datasquirel = require("datasquirel"); | const datasquirel = require("datasquirel"); | ||||||
| 
 | 
 | ||||||
| /** ********************************************** */ | /** ********************************************** */ | ||||||
| @ -28,7 +32,9 @@ import TextShuffler from "../../components/actions/TextShuffler"; | |||||||
|  * ============================================================================== |  * ============================================================================== | ||||||
|  * @param {Object} props - Server props |  * @param {Object} props - Server props | ||||||
|  */ |  */ | ||||||
| export default function BlogIndex({ blogPost }: InferGetStaticPropsType<typeof getStaticProps>) { | export default function BlogIndex({ | ||||||
|  |     blogPost, | ||||||
|  | }: InferGetStaticPropsType<typeof getStaticProps>) { | ||||||
|     // ## Get Contexts
 |     // ## Get Contexts
 | ||||||
| 
 | 
 | ||||||
|     /** ********************************************** */ |     /** ********************************************** */ | ||||||
| @ -52,10 +58,7 @@ export default function BlogIndex({ blogPost }: InferGetStaticPropsType<typeof g | |||||||
|         <React.Fragment> |         <React.Fragment> | ||||||
|             <Head> |             <Head> | ||||||
|                 <title>{blogPost.title} | Tben.me Blog</title> |                 <title>{blogPost.title} | Tben.me Blog</title> | ||||||
|                 <meta |                 <meta name="description" content={blogPost.excerpt} /> | ||||||
|                     name="description" |  | ||||||
|                     content={blogPost.excerpt} |  | ||||||
|                 /> |  | ||||||
|             </Head> |             </Head> | ||||||
|             <GeneralLayout> |             <GeneralLayout> | ||||||
|                 <div className="flex flex-col items-start gap-2 mb-8 max-w-6xl w-full"> |                 <div className="flex flex-col items-start gap-2 mb-8 max-w-6xl w-full"> | ||||||
| @ -74,7 +77,9 @@ export default function BlogIndex({ blogPost }: InferGetStaticPropsType<typeof g | |||||||
|                         <TextShuffler textInput={blogPost.excerpt} /> |                         <TextShuffler textInput={blogPost.excerpt} /> | ||||||
|                     </span> |                     </span> | ||||||
|                     <span className="text-base opacity-50"> |                     <span className="text-base opacity-50"> | ||||||
|                         <TextShuffler textInput={blogPost.date_created.substring(0, 24)} /> |                         <TextShuffler | ||||||
|  |                             textInput={blogPost.date_created.substring(0, 24)} | ||||||
|  |                         /> | ||||||
|                     </span> |                     </span> | ||||||
|                 </div> |                 </div> | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										49
									
								
								app/blog/[single]/Main.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								app/blog/[single]/Main.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,49 @@ | |||||||
|  | "use client"; | ||||||
|  | 
 | ||||||
|  | /////////////////////////////////////////////
 | ||||||
|  | //* IMPORTS
 | ||||||
|  | /////////////////////////////////////////////
 | ||||||
|  | import React from "react"; | ||||||
|  | import TextShuffler from "../../../components/actions/TextShuffler"; | ||||||
|  | 
 | ||||||
|  | /////////////////////////////////////////////
 | ||||||
|  | //* Main Function
 | ||||||
|  | /////////////////////////////////////////////
 | ||||||
|  | /** | ||||||
|  |  * Blog page index | ||||||
|  |  * ============================================================================== | ||||||
|  |  */ | ||||||
|  | export default function Main({ post }: { post: any }) { | ||||||
|  |     //* Main Function Return
 | ||||||
|  | 
 | ||||||
|  |     return ( | ||||||
|  |         <React.Fragment> | ||||||
|  |             <div className="flex flex-col items-start gap-2 mb-8 max-w-6xl w-full"> | ||||||
|  |                 <button | ||||||
|  |                     className="bg-transparent text-white/50 border-2 border-solid border-white/20" | ||||||
|  |                     onClick={(e) => { | ||||||
|  |                         window.history.back(); | ||||||
|  |                     }} | ||||||
|  |                 > | ||||||
|  |                     Back | ||||||
|  |                 </button> | ||||||
|  |                 <h1 className="m-0"> | ||||||
|  |                     <TextShuffler textInput={post.title} /> | ||||||
|  |                 </h1> | ||||||
|  |                 <span className="text-lg"> | ||||||
|  |                     <TextShuffler textInput={post.excerpt} /> | ||||||
|  |                 </span> | ||||||
|  |                 <span className="text-base opacity-50"> | ||||||
|  |                     <TextShuffler | ||||||
|  |                         textInput={post.date_created.substring(0, 24)} | ||||||
|  |                     /> | ||||||
|  |                 </span> | ||||||
|  |             </div> | ||||||
|  | 
 | ||||||
|  |             <span | ||||||
|  |                 className="flex flex-col items-start max-w-6xl w-full gap-4 text-xl" | ||||||
|  |                 dangerouslySetInnerHTML={{ __html: post.body }} | ||||||
|  |             ></span> | ||||||
|  |         </React.Fragment> | ||||||
|  |     ); | ||||||
|  | } | ||||||
							
								
								
									
										75
									
								
								app/blog/[single]/page.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								app/blog/[single]/page.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,75 @@ | |||||||
|  | /////////////////////////////////////////////
 | ||||||
|  | //* IMPORTS
 | ||||||
|  | /////////////////////////////////////////////
 | ||||||
|  | import React from "react"; | ||||||
|  | import { Metadata, ResolvingMetadata } from "next"; | ||||||
|  | import datasquirel from "datasquirel"; | ||||||
|  | 
 | ||||||
|  | import { redirect } from "next/navigation"; | ||||||
|  | import Main from "./Main"; | ||||||
|  | 
 | ||||||
|  | export const revalidate = 3600; | ||||||
|  | 
 | ||||||
|  | /////////////////////////////////////////////
 | ||||||
|  | //* Metadata
 | ||||||
|  | /////////////////////////////////////////////
 | ||||||
|  | 
 | ||||||
|  | async function getPost({ single }: { single: string }) { | ||||||
|  |     const postResponse = await datasquirel.get({ | ||||||
|  |         key: process.env.DATASQUIREL_API_KEY, | ||||||
|  |         db: process.env.DB_NAME, | ||||||
|  |         query: "select * from blog_posts where slug = ?", | ||||||
|  |         queryValues: [single], | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     const post = | ||||||
|  |         postResponse?.success && postResponse.payload?.[0] | ||||||
|  |             ? postResponse.payload[0] | ||||||
|  |             : null; | ||||||
|  | 
 | ||||||
|  |     return post; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export async function generateMetadata( | ||||||
|  |     { params, searchParams }: any, | ||||||
|  |     parent: ResolvingMetadata | ||||||
|  | ): Promise<Metadata> { | ||||||
|  |     const single = params.single; | ||||||
|  | 
 | ||||||
|  |     const post = await getPost({ single: single }); | ||||||
|  | 
 | ||||||
|  |     return { | ||||||
|  |         title: post.title, | ||||||
|  |         description: post.excerpt, | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /////////////////////////////////////////////
 | ||||||
|  | //* Main Function
 | ||||||
|  | /////////////////////////////////////////////
 | ||||||
|  | /** | ||||||
|  |  * Blog page index | ||||||
|  |  * ============================================================================== | ||||||
|  |  */ | ||||||
|  | export default async function BlogIndex({ | ||||||
|  |     params: { single }, | ||||||
|  | }: { | ||||||
|  |     params: any; | ||||||
|  | }) { | ||||||
|  |     //* Data fetching
 | ||||||
|  |     const post = await getPost({ single: single }); | ||||||
|  | 
 | ||||||
|  |     if (!post) { | ||||||
|  |         return redirect("/blog"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     //* Main Function Return
 | ||||||
|  |     /////////////////////////////////////////////
 | ||||||
|  |     return ( | ||||||
|  |         <React.Fragment> | ||||||
|  |             <Main post={post} /> | ||||||
|  |         </React.Fragment> | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     /** ********************************************** */ | ||||||
|  | } | ||||||
| @ -16,6 +16,8 @@ export const metadata: Metadata = { | |||||||
|     description: "Tech talks and tutorials by Tben", |     description: "Tech talks and tutorials by Tben", | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | export const revalidate = 3600; | ||||||
|  | 
 | ||||||
| /////////////////////////////////////////////
 | /////////////////////////////////////////////
 | ||||||
| //* Main Function
 | //* Main Function
 | ||||||
| /////////////////////////////////////////////
 | /////////////////////////////////////////////
 | ||||||
| @ -33,17 +35,6 @@ export default async function BlogIndex() { | |||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     const posts = postsResponse?.success ? postsResponse.payload : []; |     const posts = postsResponse?.success ? postsResponse.payload : []; | ||||||
|     try { |  | ||||||
|         const test = await fetch("http://localhost:5000/api/test"); |  | ||||||
|         const result = await test.json(); |  | ||||||
|         console.log(result); |  | ||||||
|     } catch (error: any) { |  | ||||||
|         console.log(error.message); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     console.log("Referer:", headers().get("referer")); |  | ||||||
|     console.log("Host:", headers().get("host")); |  | ||||||
|     // console.log(nextHeaders.cookies());
 |  | ||||||
| 
 | 
 | ||||||
|     //* Main Function Return
 |     //* Main Function Return
 | ||||||
|     /////////////////////////////////////////////
 |     /////////////////////////////////////////////
 | ||||||
|  | |||||||
| @ -1,8 +1,14 @@ | |||||||
|  | /** | ||||||
|  |  * @type {import("next").NextConfig} | ||||||
|  |  */ | ||||||
| module.exports = { | module.exports = { | ||||||
|     reactStrictMode: true, |     reactStrictMode: true, | ||||||
|     eslint: { |     eslint: { | ||||||
|         ignoreDuringBuilds: true, |         ignoreDuringBuilds: true, | ||||||
|     }, |     }, | ||||||
|  |     typescript: { | ||||||
|  |         ignoreBuildErrors: true, | ||||||
|  |     }, | ||||||
|     images: { |     images: { | ||||||
|         remotePatterns: [ |         remotePatterns: [ | ||||||
|             { |             { | ||||||
| @ -20,8 +26,14 @@ module.exports = { | |||||||
|                 headers: [ |                 headers: [ | ||||||
|                     { key: "Access-Control-Allow-Credentials", value: "true" }, |                     { key: "Access-Control-Allow-Credentials", value: "true" }, | ||||||
|                     { key: "Access-Control-Allow-Origin", value: "*" }, |                     { key: "Access-Control-Allow-Origin", value: "*" }, | ||||||
|                     { key: "Access-Control-Allow-Methods", value: "GET,OPTIONS,PATCH,DELETE,POST,PUT" }, |                     { | ||||||
|                     { key: "Access-Control-Allow-Headers", value: "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version" }, |                         key: "Access-Control-Allow-Methods", | ||||||
|  |                         value: "GET,OPTIONS,PATCH,DELETE,POST,PUT", | ||||||
|  |                     }, | ||||||
|  |                     { | ||||||
|  |                         key: "Access-Control-Allow-Headers", | ||||||
|  |                         value: "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version", | ||||||
|  |                     }, | ||||||
|                 ], |                 ], | ||||||
|             }, |             }, | ||||||
|         ]; |         ]; | ||||||
|  | |||||||
							
								
								
									
										1212
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1212
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -26,7 +26,6 @@ | |||||||
|     "homepage": "https://github.com/BenjaminToby/personal_site#readme", |     "homepage": "https://github.com/BenjaminToby/personal_site#readme", | ||||||
|     "dependencies": { |     "dependencies": { | ||||||
|         "@barba/core": "^2.9.7", |         "@barba/core": "^2.9.7", | ||||||
|         "contentful": "^9.1.32", |  | ||||||
|         "cors": "^2.8.5", |         "cors": "^2.8.5", | ||||||
|         "datasquirel": "^1.9.5", |         "datasquirel": "^1.9.5", | ||||||
|         "gsap": "^3.10.4", |         "gsap": "^3.10.4", | ||||||
|  | |||||||
| @ -1,178 +0,0 @@ | |||||||
| /** |  | ||||||
|  * ============================================================================== |  | ||||||
|  * Imports |  | ||||||
|  * ============================================================================== |  | ||||||
|  */ |  | ||||||
| import React from "react"; |  | ||||||
| import Head from "next/head"; |  | ||||||
| import type { InferGetStaticPropsType, GetStaticProps, GetStaticPaths } from "next"; |  | ||||||
| const datasquirel = require("datasquirel"); |  | ||||||
| 
 |  | ||||||
| /** ********************************************** */ |  | ||||||
| /** ********************************************** */ |  | ||||||
| /** ********************************************** */ |  | ||||||
| 
 |  | ||||||
| import GeneralLayout from "../../layouts/general_layout/GeneralLayout"; |  | ||||||
| import TextShuffler from "../../components/actions/TextShuffler"; |  | ||||||
| 
 |  | ||||||
| /** ****************************************************************************** */ |  | ||||||
| /** ****************************************************************************** */ |  | ||||||
| /** ****************************************************************************** */ |  | ||||||
| /** ****************************************************************************** */ |  | ||||||
| /** ****************************************************************************** */ |  | ||||||
| /** ****************************************************************************** */ |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * ============================================================================== |  | ||||||
|  * Main Component { Functional } |  | ||||||
|  * ============================================================================== |  | ||||||
|  * @param {Object} props - Server props |  | ||||||
|  */ |  | ||||||
| export default function BlogIndex({ blogPost }: InferGetStaticPropsType<typeof getStaticProps>) { |  | ||||||
|     // ## Get Contexts
 |  | ||||||
| 
 |  | ||||||
|     /** ********************************************** */ |  | ||||||
|     /** ********************************************** */ |  | ||||||
|     /** ********************************************** */ |  | ||||||
| 
 |  | ||||||
|     // ## Javascript Variables
 |  | ||||||
| 
 |  | ||||||
|     /** ********************************************** */ |  | ||||||
|     /** ********************************************** */ |  | ||||||
|     /** ********************************************** */ |  | ||||||
| 
 |  | ||||||
|     // ## React Hooks { useState, useEffect, useRef, etc ... }
 |  | ||||||
| 
 |  | ||||||
|     /** ********************************************** */ |  | ||||||
|     /** ********************************************** */ |  | ||||||
|     /** ********************************************** */ |  | ||||||
| 
 |  | ||||||
|     // ## Function Return
 |  | ||||||
|     return ( |  | ||||||
|         <React.Fragment> |  | ||||||
|             <Head> |  | ||||||
|                 <title>{blogPost.title} | Tben.me Blog</title> |  | ||||||
|                 <meta |  | ||||||
|                     name="description" |  | ||||||
|                     content={blogPost.excerpt} |  | ||||||
|                 /> |  | ||||||
|             </Head> |  | ||||||
|             <GeneralLayout> |  | ||||||
|                 <div className="flex flex-col items-start gap-2 mb-8 max-w-6xl w-full"> |  | ||||||
|                     <button |  | ||||||
|                         className="bg-transparent text-white/50 border-2 border-solid border-white/20" |  | ||||||
|                         onClick={(e) => { |  | ||||||
|                             window.history.back(); |  | ||||||
|                         }} |  | ||||||
|                     > |  | ||||||
|                         Back |  | ||||||
|                     </button> |  | ||||||
|                     <h1 className="m-0"> |  | ||||||
|                         <TextShuffler textInput={blogPost.title} /> |  | ||||||
|                     </h1> |  | ||||||
|                     <span className="text-lg"> |  | ||||||
|                         <TextShuffler textInput={blogPost.excerpt} /> |  | ||||||
|                     </span> |  | ||||||
|                     <span className="text-base opacity-50"> |  | ||||||
|                         <TextShuffler textInput={blogPost.date_created.substring(0, 24)} /> |  | ||||||
|                     </span> |  | ||||||
|                 </div> |  | ||||||
| 
 |  | ||||||
|                 <span |  | ||||||
|                     className="flex flex-col items-start max-w-6xl w-full gap-4 text-xl" |  | ||||||
|                     dangerouslySetInnerHTML={{ __html: blogPost.body }} |  | ||||||
|                 ></span> |  | ||||||
|             </GeneralLayout> |  | ||||||
|         </React.Fragment> |  | ||||||
|     ); |  | ||||||
|     /** ********************************************** */ |  | ||||||
|     /** ********************************************** */ |  | ||||||
|     /** ********************************************** */ |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** ****************************************************************************** */ |  | ||||||
| /** ****************************************************************************** */ |  | ||||||
| /** ****************************************************************************** */ |  | ||||||
| /** ****************************************************************************** */ |  | ||||||
| /** ****************************************************************************** */ |  | ||||||
| /** ****************************************************************************** */ |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Server Side Props or Static Props |  | ||||||
|  * ============================================================================== |  | ||||||
|  */ |  | ||||||
| export const getStaticProps: GetStaticProps = async ({ params }) => { |  | ||||||
|     // ## Environment processes
 |  | ||||||
| 
 |  | ||||||
|     /** ********************************************** */ |  | ||||||
|     /** ********************************************** */ |  | ||||||
|     /** ********************************************** */ |  | ||||||
| 
 |  | ||||||
|     // ## User Authentication
 |  | ||||||
| 
 |  | ||||||
|     /** ********************************************** */ |  | ||||||
|     /** ********************************************** */ |  | ||||||
|     /** ********************************************** */ |  | ||||||
| 
 |  | ||||||
|     // ## Page/Site Data Data Fetching
 |  | ||||||
|     const postsResponse = await datasquirel.get({ |  | ||||||
|         key: process.env.DATASQUIREL_API_KEY, |  | ||||||
|         db: "tbenme", |  | ||||||
|         query: `select * from blog_posts WHERE slug='${params?.single}'`, |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     const post = postsResponse.payload[0]; |  | ||||||
| 
 |  | ||||||
|     /** ********************************************** */ |  | ||||||
|     /** ********************************************** */ |  | ||||||
|     /** ********************************************** */ |  | ||||||
| 
 |  | ||||||
|     // ## Server Props Return
 |  | ||||||
|     return { |  | ||||||
|         props: { |  | ||||||
|             blogPost: post, |  | ||||||
|         }, |  | ||||||
|         revalidate: 3600, |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     /** ********************************************** */ |  | ||||||
|     /** ********************************************** */ |  | ||||||
|     /** ********************************************** */ |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /** ****************************************************************************** */ |  | ||||||
| /** ****************************************************************************** */ |  | ||||||
| /** ****************************************************************************** */ |  | ||||||
| /** ****************************************************************************** */ |  | ||||||
| /** ****************************************************************************** */ |  | ||||||
| /** ****************************************************************************** */ |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Server Side Props or Static Props |  | ||||||
|  * ============================================================================== |  | ||||||
|  */ |  | ||||||
| export const getStaticPaths: GetStaticPaths = async () => { |  | ||||||
|     /** |  | ||||||
|      * Data fetching |  | ||||||
|      * |  | ||||||
|      * @abstract fetch date from the server or externnal source |  | ||||||
|      */ |  | ||||||
|     const postsResponse = await datasquirel.get({ |  | ||||||
|         key: process.env.DATASQUIREL_API_KEY, |  | ||||||
|         db: "tbenme", |  | ||||||
|         query: `select slug from blog_posts`, |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     const posts: { slug: string }[] | null = postsResponse.payload; |  | ||||||
| 
 |  | ||||||
|     const paths = posts?.map((entry) => { |  | ||||||
|         return { |  | ||||||
|             params: { single: entry.slug }, |  | ||||||
|         }; |  | ||||||
|     }); |  | ||||||
| 
 |  | ||||||
|     return { |  | ||||||
|         paths: paths ? paths : [], |  | ||||||
|         fallback: "blocking", |  | ||||||
|     }; |  | ||||||
| }; |  | ||||||
| @ -40,7 +40,7 @@ | |||||||
|         /* JavaScript Support */ |         /* JavaScript Support */ | ||||||
|         // "allowJs": true,                                  /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ |         // "allowJs": true,                                  /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ | ||||||
|         // "checkJs": true,                                  /* Enable error reporting in type-checked JavaScript files. */ |         // "checkJs": true,                                  /* Enable error reporting in type-checked JavaScript files. */ | ||||||
|         // "maxNodeModuleJsDepth": 1,                        /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ |         "maxNodeModuleJsDepth": 10 /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */, | ||||||
|         /* Emit */ |         /* Emit */ | ||||||
|         // "declaration": true,                              /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ |         // "declaration": true,                              /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ | ||||||
|         // "declarationMap": true,                           /* Create sourcemaps for d.ts files. */ |         // "declarationMap": true,                           /* Create sourcemaps for d.ts files. */ | ||||||
| @ -106,6 +106,14 @@ | |||||||
|             } |             } | ||||||
|         ] |         ] | ||||||
|     }, |     }, | ||||||
|     "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "pages/_app.js", ".next/types/**/*.ts", "dump/index.jsx", "pages/_document.js"], |     "include": [ | ||||||
|  |         "next-env.d.ts", | ||||||
|  |         "**/*.ts", | ||||||
|  |         "**/*.tsx", | ||||||
|  |         "pages/_app.js", | ||||||
|  |         ".next/types/**/*.ts", | ||||||
|  |         "dump/index.jsx", | ||||||
|  |         "pages/_document.js" | ||||||
|  |     ], | ||||||
|     "exclude": ["node_modules"] |     "exclude": ["node_modules"] | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Benjamin Toby
						Benjamin Toby