diff --git a/locales/en/common.json b/locales/en/common.json index 4f85b64..dafed8a 100644 --- a/locales/en/common.json +++ b/locales/en/common.json @@ -207,7 +207,7 @@ "seo": { "url": "dashboard", "title": "Dashboard", - "description": "Find all the metrics related to all my activities and interests" + "description": "Find all the metrics related to all my activities and interests." }, "sections": { "coding": "Coding / Open source", @@ -223,6 +223,8 @@ } }, "books": { + "currently": "Currently reading", + "next": "To read next", "sections": { "viewAll": "→ View my profile" } @@ -232,5 +234,23 @@ "latest_videos": "Latest Youtube videos", "viewAll": "→ Access my Youtube channel" } + }, + "newsletter": { + "title": "Subscribe to my Newsletter", + "description": "Get emails from me about A11y, web development, tech and expatriation!", + "subscribers": "{{number}} subscribers", + "form": { + "input": { + "placeholder": "username@email.com", + "label": "Email for newsletter" + }, + "submit": { + "text": "Subscribe" + }, + "success": { + "text": "You're now on the list! Don't forget to validate your subscription with the email you probably received." + } + }, + "all_issues": "Access all past issues" } } diff --git a/locales/fr/common.json b/locales/fr/common.json index c222d1b..6132244 100644 --- a/locales/fr/common.json +++ b/locales/fr/common.json @@ -50,12 +50,12 @@ "path": "/fr", "seo": { "title": "The David Dias | Développeur Front-End, podcasteur & créateur de contenu", - "description": "Salut toi! Je m'appele David Dias. Je suis développeur Front-End, podcasteur, créateur de contenu numérique passioné pour résoudre les problèmes digitaux et humains! J'aime rencontré de nouvelles personnes, bâtir des communautées et parler de tech, d'expatriation et de web." + "description": "Salut toi! Je m'appele David Dias. Je suis développeur Front-End, podcasteur, créateur de contenu numérique passioné pour résoudre les problèmes digitaux et humains! J'aime rencontrer de nouvelles personnes, bâtir des communautées et parler de tech, d'expatriation et de web." }, "hero": { "greetings1": "Salut toi!", "greetings2": "Je m'appelle David Dias", - "presentation": "Je suis développeur Front-End, podcasteur, créateur de contenu numérique passioné pour résoudre les problèmes digitaux et humains! J'aime rencontré de nouvelles personnes, bâtir des communautées et parler de tech, d'expatriation et de web." + "presentation": "Je suis développeur Front-End, podcasteur, créateur de contenu numérique passioné pour résoudre les problèmes digitaux et humains! J'aime rencontrer de nouvelles personnes, bâtir des communautées et parler de tech, d'expatriation et de web." }, "sections": { "podcast": { @@ -217,5 +217,36 @@ "downloads": "Téléchargement sur Unsplash", "views": "Vue de mon profile sur Unsplash" } + }, + "books": { + "currently": "En train de lire", + "next": "À lire prochainement", + "sections": { + "viewAll": "→ Voir mon profil" + } + }, + "youtube": { + "sections": { + "latest_videos": "Dernières vidéos Youtube", + "viewAll": "→ Consulter ma chaîne Youtube" + } + }, + "newsletter": { + "title": "S'abonner à mon bulletin digital", + "description": "Recevez des courriels en liens avec l'accessibilité, le dévelopement web, les technologies et l'expatriation! (en anglais)", + "subscribers": "{{number}} abonnés", + "form": { + "input": { + "placeholder": "nom@courriel.com", + "label": "Courriel pour le bulletin" + }, + "submit": { + "text": "S'abonner" + }, + "success": { + "text": "Youpi! Vous êtes maintenant sur la liste! Pensez à valider l'email que vous avez dû recevoir." + } + }, + "all_issues": "Voir tous les bulletins précédents" } } diff --git a/public/rss/feed.xml b/public/rss/feed.xml index b1f0ae6..fa0b969 100644 --- a/public/rss/feed.xml +++ b/public/rss/feed.xml @@ -4,7 +4,7 @@ The David Dias | Front-End Developer, podcaster & content creator https://thedaviddias.dev Hey, I'm David Dias! Front-End Developer based in Toronto/Canada. I love talking about code, technology, expatriation and life. - Sat, 13 Aug 2022 14:52:06 GMT + Sat, 13 Aug 2022 16:45:23 GMT https://validator.w3.org/feed/docs/rss2.html https://github.com/jpmonette/feed en diff --git a/public/rss/fr/feed.json b/public/rss/fr/feed.json index 5ac7f19..924e181 100644 --- a/public/rss/fr/feed.json +++ b/public/rss/fr/feed.json @@ -3,7 +3,7 @@ "title": "The David Dias | Développeur Front-End, podcasteur & créateur de contenu", "home_page_url": "https://thedaviddias.dev", "feed_url": "https://thedaviddias.dev/rss/fr/feed.json", - "description": "Salut toi! Je m'appele David Dias. Je suis développeur Front-End, podcasteur, créateur de contenu numérique passioné pour résoudre les problèmes digitaux et humains! J'aime rencontré de nouvelles personnes, bâtir des communautées et parler de tech, d'expatriation et de web.", + "description": "Salut toi! Je m'appele David Dias. Je suis développeur Front-End, podcasteur, créateur de contenu numérique passioné pour résoudre les problèmes digitaux et humains! J'aime rencontrer de nouvelles personnes, bâtir des communautées et parler de tech, d'expatriation et de web.", "icon": "https://thedaviddias.dev/favicons/android-chrome-144x144.png", "author": { "name": "David Dias", diff --git a/public/rss/fr/feed.xml b/public/rss/fr/feed.xml index 5591bcf..7e81fa6 100644 --- a/public/rss/fr/feed.xml +++ b/public/rss/fr/feed.xml @@ -3,8 +3,8 @@ The David Dias | Développeur Front-End, podcasteur & créateur de contenu https://thedaviddias.dev - Salut toi! Je m'appele David Dias. Je suis développeur Front-End, podcasteur, créateur de contenu numérique passioné pour résoudre les problèmes digitaux et humains! J'aime rencontré de nouvelles personnes, bâtir des communautées et parler de tech, d'expatriation et de web. - Sat, 13 Aug 2022 14:52:06 GMT + Salut toi! Je m'appele David Dias. Je suis développeur Front-End, podcasteur, créateur de contenu numérique passioné pour résoudre les problèmes digitaux et humains! J'aime rencontrer de nouvelles personnes, bâtir des communautées et parler de tech, d'expatriation et de web. + Sat, 13 Aug 2022 16:45:23 GMT https://validator.w3.org/feed/docs/rss2.html https://github.com/jpmonette/feed fr diff --git a/sentry.client.config.ts b/sentry.client.config.ts index 35f1e6a..06d01ea 100644 --- a/sentry.client.config.ts +++ b/sentry.client.config.ts @@ -15,5 +15,6 @@ Sentry.init({ // ignore hydration issues 'Minified React error #418;', 'Minified React error #423;', + 'Minified React error #425;', ], }) diff --git a/src/components/CurrentlyReading/CurrentlyReading.tsx b/src/components/CurrentlyReading/CurrentlyReading.tsx index 1e5cea1..715254b 100644 --- a/src/components/CurrentlyReading/CurrentlyReading.tsx +++ b/src/components/CurrentlyReading/CurrentlyReading.tsx @@ -1,12 +1,15 @@ +import useTranslation from 'next-translate/useTranslation' import GoodreadsBookshelf from 'react-goodreads-shelf' -import { H5 } from '../Headings' +import { H5 } from '@/components/Headings' export const CurrentlyReading = ({ limit = 3 }) => { + const { t } = useTranslation('common') + return (
-
Currently Reading
+
{t('books.currently')}
diff --git a/src/components/ErrorMessage/ErrorMessage.tsx b/src/components/ErrorMessage/ErrorMessage.tsx new file mode 100644 index 0000000..f5472fd --- /dev/null +++ b/src/components/ErrorMessage/ErrorMessage.tsx @@ -0,0 +1,23 @@ +type ErrorMessageProps = { + children: React.ReactNode +} + +export default function ErrorMessage({ children }: ErrorMessageProps) { + return ( +

+ + + + {children} +

+ ) +} diff --git a/src/components/ErrorMessage/index.ts b/src/components/ErrorMessage/index.ts new file mode 100644 index 0000000..3448419 --- /dev/null +++ b/src/components/ErrorMessage/index.ts @@ -0,0 +1 @@ +export * from './ErrorMessage' diff --git a/src/components/Newsletter/Newsletter.tsx b/src/components/Newsletter/Newsletter.tsx deleted file mode 100644 index ebaca0d..0000000 --- a/src/components/Newsletter/Newsletter.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { FormEvent, useState } from 'react' - -// import useSWR from 'swr' -// import fetcher from '@/utils/fetcher' -import { H2 } from '@/components/Headings' - -export enum Form { - Initial, - Loading, - Success, - Error, -} - -export type FormState = { - state: Form - message?: string -} - -export const Newsletter = () => { - const [form, setForm] = useState({ state: Form.Initial }) - // const { data } = useSWR('/api/newsletter/subscribers', fetcher) - - const subscribe = async (e: FormEvent) => { - e.preventDefault() - setForm({ state: Form.Loading }) - - const res = await fetch('/api/newsletter/subscribe', { - body: JSON.stringify({ - // email: e.currentTarget.elements['email'].value, - }), - headers: { - 'Content-Type': 'application/json', - }, - method: 'POST', - }) - - const { error, message } = await res.json() - if (error) { - setForm({ - state: Form.Error, - message: error, - }) - return - } - - setForm({ - state: Form.Success, - message, - }) - } - - return ( -
-
-

Sign up to my newsletter

-

Get emails from me about web development, expatriation and tech!

-
- - -
- {form.state === Form.Success &&

{form.message}

} - {form.state === Form.Error &&

{form.message} 😕

} -
-
- ) -} diff --git a/src/components/Newsletter/index.ts b/src/components/Newsletter/index.ts deleted file mode 100644 index 91d4c1a..0000000 --- a/src/components/Newsletter/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Newsletter' diff --git a/src/components/Subscribe/Subscribe.tsx b/src/components/Subscribe/Subscribe.tsx index 5dbd309..31bd97e 100644 --- a/src/components/Subscribe/Subscribe.tsx +++ b/src/components/Subscribe/Subscribe.tsx @@ -1,93 +1,115 @@ -// // import ErrorMessage from 'components/ErrorMessage' -// // import LoadingSpinner from 'components/LoadingSpinner' -// // import SuccessMessage from 'components/SuccessMessage' -// // import { trackGoal } from 'fathom-client' -// // import { Form, FormState, Subscribers } from 'lib/types' -// import { useRef, useState } from 'react' -// import useSWR from 'swr' +import useTranslation from 'next-translate/useTranslation' +import { FormEvent, useRef, useState } from 'react' -// import fetcher from '@/utils/fetcher' +import { CustomLink } from '@/components/CustomLink' +import ErrorMessage from '@/components/ErrorMessage/ErrorMessage' +import { H3 } from '@/components/Headings' +import { Loader } from '@/components/Loader' +import { Paragraph } from '@/components/Paragraph' +import SuccessMessage from '@/components/SuccessMessage/SuccessMessage' -// import { CustomLink } from '../CustomLink' -// import { H3 } from '../Headings' +export enum Form { + Initial, + Loading, + Success, + Error, +} + +export type FormState = { + state: Form + message?: string +} + +export type Subscribers = { + count: number +} -// export const Subscribe = () => { -// const [form, setForm] = useState() -// const inputEl = useRef(null) +export type Views = { + total: number +} -// const { data } = useSWR('/api/subscribers', fetcher) -// const subscriberCount = new Number(data?.count) +export const Subscribe = () => { + const { t } = useTranslation('common') + const [form, setForm] = useState() + const inputEl = useRef(null) -// const subscribe = async (e) => { -// e.preventDefault() -// setForm({ state: Form.Loading }) + const subscribe = async (e: FormEvent) => { + e.preventDefault() + setForm({ state: Form.Loading }) -// const res = await fetch('/api/subscribe', { -// body: JSON.stringify({ -// email: inputEl.current.value, -// }), -// headers: { -// 'Content-Type': 'application/json', -// }, -// method: 'POST', -// }) + const res = await fetch('/api/subscribe', { + body: JSON.stringify({ + email: inputEl.current && inputEl.current.value, + }), + headers: { + 'Content-Type': 'application/json', + }, + method: 'POST', + }) -// const { error } = await res.json() -// if (error) { -// setForm({ -// state: Form.Error, -// message: error, -// }) -// return -// } + const { error } = await res.json() -// // trackGoal('JYFUFMSF', 0) -// inputEl.current.value = '' -// setForm({ -// // state: Form.Success, -// message: `Hooray! You're now on the list.`, -// }) -// } + if (error) { + setForm({ + state: Form.Error, + message: error, + }) + return + } -// return ( -//
-//

Subscribe to my Newsletter

-//

-// Get emails from me about web development, tech, and early access to new articles. -//

-//
-//
-// -// -//
-//
-// {/* {form.state === Form.Error ? ( -// {form.message} -// ) : form.state === Form.Success ? ( -// {form.message} -// ) : ( */} -//

-// {`${subscriberCount > 0 ? subscriberCount.toLocaleString() : '-'} subscribers – `} -// Access all issues -//

-// {/* )} */} -//
-// ) -// } + if (inputEl.current) { + inputEl.current.value = '' + } -export const Subscribe = () => { - return
Subscribe
+ setForm({ + state: Form.Success, + message: t('newsletter.form.success.text'), + }) + } + + return ( +
+
+
+

{t('newsletter.title')}

+
+ + {t('newsletter.description')} + +
+
+ + + {form?.state === Form.Error ? ( + {form.message} + ) : form?.state === Form.Success ? ( + {form.message} + ) : null} +
+
+ +

+ + {t('newsletter.all_issues')} + +

+
+
+ ) } diff --git a/src/components/SuccessMessage/SuccessMessage.tsx b/src/components/SuccessMessage/SuccessMessage.tsx new file mode 100644 index 0000000..e09d712 --- /dev/null +++ b/src/components/SuccessMessage/SuccessMessage.tsx @@ -0,0 +1,23 @@ +type SuccessMessageProps = { + children: React.ReactNode +} + +export default function SuccessMessage({ children }: SuccessMessageProps) { + return ( +

+ + + + {children} +

+ ) +} diff --git a/src/components/SuccessMessage/index.ts b/src/components/SuccessMessage/index.ts new file mode 100644 index 0000000..ebd2637 --- /dev/null +++ b/src/components/SuccessMessage/index.ts @@ -0,0 +1 @@ +export * from './SuccessMessage' diff --git a/src/components/ToRead/ToRead.tsx b/src/components/ToRead/ToRead.tsx index 9e97827..ac4d7c5 100644 --- a/src/components/ToRead/ToRead.tsx +++ b/src/components/ToRead/ToRead.tsx @@ -1,12 +1,15 @@ +import useTranslation from 'next-translate/useTranslation' import GoodreadsBookshelf from 'react-goodreads-shelf' -import { H5 } from '../Headings' +import { H5 } from '@/components/Headings' export const ToRead = ({ limit = 3 }) => { + const { t } = useTranslation('common') + return (
-
To read next
+
{t('books.next')}
(url, { + method: 'POST', + headers: { + Authorization: `Token ${process.env.REVUE_API_KEY}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ email }), + }) +} + +const handler = async (req: NextApiRequest, res: NextApiResponse) => { + const { email } = req.body + + if (!email) { + return res.status(400).json({ error: 'Email is required' }) + } + + const result = await addSubscribers(email) + + if (!result) { + return res.status(500).json({ error: result }) + } + + return res.status(201).json({ error: '' }) +} + +export default withSentry(handler) diff --git a/src/pages/api/subscribers.ts b/src/pages/api/subscribers.ts deleted file mode 100644 index 5c4e38d..0000000 --- a/src/pages/api/subscribers.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { withSentry } from '@sentry/nextjs' -import type { NextApiRequest, NextApiResponse } from 'next' - -const handler = async (req: NextApiRequest, res: NextApiResponse) => { - const result = await fetch('https://www.getrevue.co/api/v2/subscribers', { - method: 'GET', - headers: { - Authorization: `Token ${process.env.REVUE_API_KEY}`, - }, - }) - const data = await result.json() - - if (!result.ok) { - return res.status(500).json({ error: 'Error retrieving subscribers' }) - } - - res.setHeader('Cache-Control', 'public, s-maxage=1200, stale-while-revalidate=600') - - return res.status(200).json({ count: data.length }) -} - -export default withSentry(handler) diff --git a/src/pages/articles/[slug].tsx b/src/pages/articles/[slug].tsx index 99fcd60..b26e26a 100644 --- a/src/pages/articles/[slug].tsx +++ b/src/pages/articles/[slug].tsx @@ -27,6 +27,7 @@ import { MDXComponents } from '@/components/MdxComponents' import { Paragraph } from '@/components/Paragraph' import { ScrollTop } from '@/components/ScrollTop' import { Share } from '@/components/Share' +import { Subscribe } from '@/components/Subscribe' import { TableOfContents } from '@/components/TableOfContents' import { Tags } from '@/components/Tags' import { Webmentions } from '@/components/Webmentions' @@ -218,6 +219,8 @@ const BlogPostPage: NextPage = ({ )}
+ + {tags && ( )} - {/* */} - {adjacentPosts && } diff --git a/src/pages/index.tsx b/src/pages/index.tsx index fea0783..30d9b1e 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -17,6 +17,7 @@ import { LatestNotesSection } from '@/components/LatestNotesSection' import { LatestPostsSection } from '@/components/LatestPostsSection' import LatestYoutubeVideos from '@/components/LatestYoutubeVideos/LatestYoutubeVideos' import { Loader } from '@/components/Loader' +import { Subscribe } from '@/components/Subscribe' import { ToRead } from '@/components/ToRead' import { routes } from '@/config/routes' @@ -29,11 +30,6 @@ const PodcastSection = dynamic(() => import('../components/PodcastSection'), { ssr: false, }) -// const LatestYoutubeVideos = dynamic(() => import('../components/LatestYoutubeVideos'), { -// loading: () => , -// ssr: false, -// }) - type HomeProps = { articles: any[] notes: any[] @@ -120,7 +116,7 @@ const Home: NextPage = ({ articles, notes, ghProjects, fallback }) => - {/* */} +