Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
post: views count (#5)
* post: views count in blog posts

* analytics: don't track non-production environments
  • Loading branch information
arturocr committed Nov 24, 2020
1 parent c63bff4 commit ec82768
Show file tree
Hide file tree
Showing 11 changed files with 95 additions and 40 deletions.
5 changes: 2 additions & 3 deletions components/post-seo.js
@@ -1,4 +1,5 @@
import { NextSeo, ArticleJsonLd } from 'next-seo';
import { siteBaseUrl } from '@/lib/constants';

const PostSeo = ({
author,
Expand All @@ -11,9 +12,7 @@ const PostSeo = ({
}) => {
const publishedAt = new Date(date).toISOString();
const featuredImage = {
url: image
? `https://arturocampos.dev${image}`
: 'https://arturocampos.dev/image/og.png',
url: image ? `${siteBaseUrl}${image}` : `${siteBaseUrl}/image/og.png`,
alt: title,
};

Expand Down
4 changes: 4 additions & 0 deletions i18n/strings.js
Expand Up @@ -23,6 +23,8 @@ const LangStrings = {
slogan: 'Arturo Campos - Web Development & Technology Blog',
sorry: 'Sorry',
uses: 'Uses',
view: 'view',
views: 'views',
},
es: {
about: 'Acerca de',
Expand All @@ -48,6 +50,8 @@ const LangStrings = {
slogan: 'Arturo Campos - Blog de Desarrollo Web y Tecnología',
sorry: 'Lo siento',
uses: 'Uso',
view: 'vista',
views: 'vistas',
},
};

Expand Down
11 changes: 10 additions & 1 deletion lib/constants.js
Expand Up @@ -4,4 +4,13 @@ const dateOptions = {
day: 'numeric',
};

export { dateOptions };
const description =
'Front-end Engineer, JavaScript enthusiast, and proud father';

const domain = 'arturocampos.dev';

const siteBaseUrl = `https://${domain}`;

const title = 'Arturo Campos – Front-end Engineer';

export { dateOptions, description, domain, siteBaseUrl, title };
26 changes: 22 additions & 4 deletions lib/goat-counter.js
@@ -1,7 +1,25 @@
import { domain } from '@/lib/constants';

const pageView = url => {
window.goatcounter?.count({
path: url,
});
if (window?.location?.host === domain) {
window.goatcounter?.count({
path: url,
});
}
};

const getPathViews = async path => {
try {
const views = await fetch(
`https://arturocampos.goatcounter.com/counter/${encodeURIComponent(
path
)}.json`
);
const { count_unique } = await views.json();
return count_unique;
} catch (_) {
return 0;
}
};

export { pageView };
export { pageView, getPathViews };
9 changes: 4 additions & 5 deletions lib/util.js
Expand Up @@ -2,7 +2,6 @@ import Image from 'next/image';

import Vimeo from '@/components/vimeo';
import YouTube from '@/components/youtube';
import { siteBaseUrl } from '../next-seo.config.js';

const checkForComponentUse = (tagName, str) => {
const exp = new RegExp(`<${tagName}`);
Expand Down Expand Up @@ -32,9 +31,9 @@ const getComponents = (hydrationComponentsList = []) => {
return componentsList;
};

// Returns the localized URL given a locale and a path
const getLocalizedUrl = ({ defaultLocale, locale, asPath: path }) => {
return `${siteBaseUrl}${defaultLocale == locale ? '' : `/${locale}`}${path}`;
// Returns the localized path given a locale and a path
const getLocalizedPath = ({ defaultLocale, locale, asPath: path }) => {
return `${defaultLocale == locale ? '' : `/${locale}`}${path}`;
};

export { getComponents, getHydrationComponentsList, getLocalizedUrl };
export { getComponents, getHydrationComponentsList, getLocalizedPath };
6 changes: 1 addition & 5 deletions next-seo.config.js
@@ -1,7 +1,4 @@
const title = 'Arturo Campos – Front-end Engineer';
const description =
'Front-end Engineer, JavaScript enthusiast, and proud father';
const siteBaseUrl = 'https://arturocampos.dev';
import { description, siteBaseUrl, title } from '@/lib/constants';

const SEO = {
title,
Expand Down Expand Up @@ -29,5 +26,4 @@ const SEO = {
},
};

export { siteBaseUrl };
export default SEO;
11 changes: 11 additions & 0 deletions pages/_document.js
@@ -1,4 +1,5 @@
import Document, { Html, Head, Main, NextScript } from 'next/document';
import { domain } from '@/lib/constants';
import theme from '@/lib/theme';

class MyDocument extends Document {
Expand Down Expand Up @@ -39,6 +40,16 @@ class MyDocument extends Document {
rel='stylesheet'
href='https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap'
/>
<script
dangerouslySetInnerHTML={{
__html: `
// Only load on production environment.
if (window.location.host !== '${domain}') {
window.goatcounter = {no_onload: true};
}
`,
}}
/>
<script
async
data-goatcounter='https://arturocampos.goatcounter.com/count'
Expand Down
31 changes: 24 additions & 7 deletions pages/blog/[slug].js
@@ -1,3 +1,4 @@
import { useEffect, useState } from 'react';
import Link from 'next/link';
import Image from 'next/image';
import { useRouter } from 'next/router';
Expand All @@ -7,22 +8,33 @@ import Heading from '@/components/heading';
import PostSeo from '@/components/post-seo';
import useTranslation from '@/i18n/useTranslation';
import { getAllPostSlugs, getContent } from '@/lib/content';
import { dateOptions } from '@/lib/constants';
import { getComponents, getLocalizedUrl } from '@/lib/util';
import { dateOptions, siteBaseUrl } from '@/lib/constants';
import { getPathViews } from '@/lib/goat-counter';
import { getComponents, getLocalizedPath } from '@/lib/util';

const Post = ({ mdxSource, frontMatter, hydrationComponentsList }) => {
const { t } = useTranslation();
const router = useRouter();
const { locale } = router;
const [views, setViews] = useState(0);
const content = hydrate(mdxSource, {
components: getComponents(hydrationComponentsList),
});
const localizedUrl = getLocalizedUrl(router);
const localizedPath = getLocalizedPath(router);
const { image: imagePath } = frontMatter;

useEffect(async () => {
const viewsCount = await getPathViews(localizedPath);
setViews(viewsCount);
}, []);

return (
<>
<PostSeo locale={locale} url={localizedUrl} {...frontMatter} />
<PostSeo
locale={locale}
url={`${siteBaseUrl}${localizedPath}`}
{...frontMatter}
/>
<div className='flex items-center px-2 py-1 mb-4 space-x-1 overflow-hidden text-xs font-medium text-gray-600 uppercase bg-gray-100 border border-gray-300 rounded-md'>
<Link href='/'>
<a className='inline-flex items-center'>{t('home')}</a>
Expand All @@ -36,9 +48,14 @@ const Post = ({ mdxSource, frontMatter, hydrationComponentsList }) => {
</div>
<article>
<Heading>{frontMatter.title}</Heading>
<time className='block my-2 text-sm text-center text-gray-600 md:text-left'>
{new Date(frontMatter.date).toLocaleDateString(locale, dateOptions)}
</time>
<div className='flex justify-between my-2 text-sm text-gray-600'>
<time>
{new Date(frontMatter.date).toLocaleDateString(locale, dateOptions)}
</time>
<span>
{views} {views !== 1 ? t('views') : t('view')}
</span>
</div>
{imagePath ? (
<picture className='block mx-auto my-3 max-w-media'>
<Image
Expand Down
14 changes: 7 additions & 7 deletions pages/blog/index.js
Expand Up @@ -7,15 +7,15 @@ import { NextSeo } from 'next-seo';
import Heading from '@/components/heading';
import ReadMore from '@/components/read-more';
import useTranslation from '@/i18n/useTranslation';
import { dateOptions } from '@/lib/constants';
import { dateOptions, siteBaseUrl } from '@/lib/constants';
import { getSortedPostsData } from '@/lib/content';
import { getLocalizedUrl } from '@/lib/util';
import { getLocalizedPath } from '@/lib/util';

const Blog = ({ allPostsData = [] }) => {
const { t } = useTranslation();
const router = useRouter();
const { locale } = router;
const localizedUrl = getLocalizedUrl(router);
const localizedPath = getLocalizedPath(router);
const title = `${t('blog')} - Arturo Campos`;
const description = t('blog-description');

Expand All @@ -32,10 +32,10 @@ const Blog = ({ allPostsData = [] }) => {
<>
<NextSeo
title={title}
canonical={localizedUrl}
canonical={`${siteBaseUrl}${localizedPath}`}
description={description}
openGraph={{
url: localizedUrl,
url: `${siteBaseUrl}${localizedPath}`,
locale,
title,
description,
Expand All @@ -45,10 +45,10 @@ const Blog = ({ allPostsData = [] }) => {
pagedPosts.map(post => (
<article key={post.slug} className='mb-4 border-b last:border-b-0'>
<Heading linkTo={`/blog/${post.slug}`}>
{post.frontMatter.title}
{post.frontMatter?.title}
</Heading>
<time className='block my-2 text-sm text-center text-gray-600 md:text-left'>
{new Date(post.frontMatter.date).toLocaleDateString(
{new Date(post.frontMatter?.date).toLocaleDateString(
locale,
dateOptions
)}
Expand Down
9 changes: 5 additions & 4 deletions pages/index.js
Expand Up @@ -3,27 +3,28 @@ import { NextSeo } from 'next-seo';
import hydrate from 'next-mdx-remote/hydrate';

import Heading from '@/components/heading';
import { siteBaseUrl } from '@/lib/constants';
import { getContent } from '@/lib/content';
import { getComponents, getLocalizedUrl } from '@/lib/util';
import { getComponents, getLocalizedPath } from '@/lib/util';

const Home = ({ mdxSource, frontMatter, hydrationComponentsList }) => {
const router = useRouter();
const { locale } = router;
const content = hydrate(mdxSource, {
components: getComponents(hydrationComponentsList),
});
const localizedUrl = getLocalizedUrl(router);
const localizedPath = getLocalizedPath(router);
const { description } = frontMatter;

return (
<>
<NextSeo
canonical={localizedUrl}
canonical={`${siteBaseUrl}${localizedPath}`}
description={description}
openGraph={{
description,
locale,
url: localizedUrl,
url: `${siteBaseUrl}${localizedPath}`,
}}
/>
<Heading className='md:text-center'>{frontMatter.title}</Heading>
Expand Down
9 changes: 5 additions & 4 deletions pages/uses.js
Expand Up @@ -4,26 +4,27 @@ import hydrate from 'next-mdx-remote/hydrate';

import Heading from '@/components/heading';
import useTranslation from '@/i18n/useTranslation';
import { siteBaseUrl } from '@/lib/constants';
import { getContent } from '@/lib/content';
import { getLocalizedUrl } from '@/lib/util';
import { getLocalizedPath } from '@/lib/util';

const Uses = ({ mdxSource, frontMatter }) => {
const { t } = useTranslation();
const router = useRouter();
const { locale } = router;
const content = hydrate(mdxSource);
const localizedUrl = getLocalizedUrl(router);
const localizedPath = getLocalizedPath(router);
const title = `${t('uses')} - Arturo Campos`;
const { description } = frontMatter;

return (
<>
<NextSeo
title={title}
canonical={localizedUrl}
canonical={`${siteBaseUrl}${localizedPath}`}
description={description}
openGraph={{
url: localizedUrl,
url: `${siteBaseUrl}${localizedPath}`,
locale,
title,
description,
Expand Down

1 comment on commit ec82768

@vercel
Copy link

@vercel vercel bot commented on ec82768 Nov 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.