Compare commits

...

4 Commits

22 changed files with 512 additions and 318 deletions

View File

@ -17,7 +17,8 @@
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-helmet": "^5.2.1",
"styled-components": "^5.2.1"
"styled-components": "^5.2.1",
"zustand": "^3.3.1"
},
"scripts": {
"build": "gatsby build",

50
src/components/Alert.js Normal file
View File

@ -0,0 +1,50 @@
import React from 'react';
import styled from 'styled-components';
import useStore from '../store';
import Emoji from './Emoji';
const bgs = {
green: {
light: 'beige',
dark: 'darkolivegreen'
},
blue: {
light: 'aliceblue',
dark: 'steelblue',
},
orange: {
light: 'linen',
dark: 'sienna'
},
red: {
light: 'lightpink',
dark: 'mediumvioletred'
},
white: {
light: 'floralwhite',
dark: 'darkslategrey'
},
}
const StyledAlert = styled.div`
margin: 30px 0px;
padding: 10px;
border-radius: 3px;
background-color: ${p => bgs[p.colour][p.theme]};
color: ${p => p.theme === 'dark' ? 'white' : null};
h3{
margin-top: 0px;
}
`;
const Alert = ({ emoji, title, colour, children }) => {
const theme = useStore(s => s.theme);
return (
<StyledAlert colour={colour} theme={theme}>
<h3>{emoji && <Emoji e={emoji} />} {title}</h3>
{children}
</StyledAlert>
);
}
export default Alert;

View File

@ -2,16 +2,20 @@ import React from 'react'
import { Link } from 'gatsby'
import moment from 'moment';
import Emoji from './Emoji';
import Alert from './Alert';
import Tag from './Tag';
const BlogPostHeader = ({ post }) => (
<div style={{marginBottom: '1.45rem', background: 'floralwhite', padding: 3, borderRadius: 3}}>
<h3><Emoji e='📝' /> <Link to={post.fields.slug}>{post.frontmatter.title}</Link> <small>{moment(post.frontmatter.date).format('D MMMM YYYY')}</small></h3>
<Alert
colour='white'
title={<span><Emoji e='📝' /> <Link to={post.fields.slug}>{post.frontmatter.title}</Link> <small>{moment(post.frontmatter.date).format('D MMMM YYYY')}</small></span>}
>
<div>
<p><em>{post.excerpt}</em></p>
{post.frontmatter.tags && post.frontmatter.tags.map(tag =>
<Link style={{marginRight: 10}} to={`/tags/${tag}`}><Emoji e="🏷️" /> #{tag}</Link>
)}
{post.frontmatter.tags && post.frontmatter.tags.map(tag => <Tag key={tag} tag={tag} />)}
</div>
</Alert>
)
export default BlogPostHeader;

View File

@ -3,9 +3,8 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons'
const ExternalLink = (props) => {
const isExternal = props.href.indexOf('http') === 0 && props.href.indexOf('wilw.dev') === -1;
return (
<a {...props} href={props.href} target="_blank" rel="noopener noreferrer">{props.children} {isExternal && <FontAwesomeIcon icon={faExternalLinkAlt} />}</a>
<a {...props} href={props.href} target="_blank" rel="noopener noreferrer">{props.children} <FontAwesomeIcon icon={faExternalLinkAlt} /></a>
);
}

View File

@ -2,8 +2,10 @@ import React from 'react'
import { Link } from 'gatsby'
import styled from 'styled-components';
import Emoji from './Emoji';
import useStore from '../store';
import { themes } from '../components/Layout/Layout'
import Will from '../images/will.jpg';
import Will from '../images/will.jpeg';
const StyledHeader = styled.header`
text-align: left;
@ -53,8 +55,23 @@ const StyledNavBar = styled.div`
font-size: 20px;
}
`;
const StyledThemeSelector = styled.select`
padding: 4px;
background: white;
border: 1px solid gray;
border-radius: 5px;
cursor: pointer;
`;
const Header = () => (
const Header = () => {
const store = useStore();
const switchTheme = themeName => {
store.setTheme(themeName);
localStorage.setItem('theme', themeName);
};
return (
<StyledHeader>
<StyledHeaderArea>
<Link to='/'><StyledAvatar /></Link>
@ -71,8 +88,14 @@ const Header = () => (
<Link to='/notes' activeClassName='active-link'>notes</Link>
<Link to='/projects' activeClassName='active-link'>projects</Link>
<Link to='/research' activeClassName='active-link'>research</Link>
<StyledThemeSelector value={store.theme} onChange={e => switchTheme(e.target.value)}>
{Object.keys(themes).map(themeName =>
<option key={themeName} value={themeName}>{themes[themeName].name}</option>
)}
</StyledThemeSelector>
</StyledNavBar>
</StyledHeader>
)
);
}
export default Header

View File

@ -12,19 +12,6 @@ html {
overflow-y: scroll;
}
a {
color: #01BAEF;
background-color: transparent;
-webkit-text-decoration-skip: objects;
}
a:active,
a:hover {
outline-width: 0;
}
a.active-link{
color:rgb(50,50,50);
}
body {
margin: 0;
font-family: 'Recursive', monospace;
@ -84,6 +71,14 @@ h4 {
line-height: 1.3;
}
a {
background-color: transparent;
-webkit-text-decoration-skip: objects;
}
a:active, a:hover {
outline-width: 0;
}
p {
margin: 0px 0px 15px 0px;
}

View File

@ -1,11 +1,47 @@
import React from 'react'
import React, { useEffect } from 'react'
import Helmet from 'react-helmet'
import styled from 'styled-components'
import styled, { createGlobalStyle } from 'styled-components'
import Header from '../Header'
import Footer from '../Footer';
import useStore from '../../store';
import Will from '../../images/will.png';
import './Layout.css'
import './Layout.css';
export const themes = {
light: {
name: '☀️ Light Theme',
background: 'white',
text: 'black',
links: '#01BAEF',
activeLinks: 'rgb(50,50,50)',
},
dark: {
name: '🌒 Dark Theme',
background: '#0f0e17',
text: '#a7a9be',
headers: '#fffffe',
links: '#ff8906',
activeLinks: 'rgb(200,200,200)',
}
}
const GlobalStyle = createGlobalStyle`
body{
background-color: ${({ theme }) => theme.background};
color: ${({ theme }) => theme.text};
transition: background-color 0.25s, color 0.25s;
}
h1,h2,h3,h4 {
color: ${({ theme }) => theme.headers};
}
a {
color: ${({ theme }) => theme.links};
&.active-link {
color: ${({ theme }) => theme.activeLinks};
}
}
`;
const StyledMain = styled.div`
margin: 0px auto;
@ -13,8 +49,23 @@ const StyledMain = styled.div`
padding: 0px 1.1rem 1.45rem;
`;
const TemplateWrapper = ({ children }) => (
const TemplateWrapper = ({ children }) => {
const theme = useStore(s => s.theme);
const setTheme = useStore(s => s.setTheme);
useEffect(() => {
const rememberedTheme = localStorage.getItem('theme');
if (rememberedTheme && themes[rememberedTheme]) {
setTheme(rememberedTheme);
} else {
const isDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
if (isDarkMode) setTheme('dark');
}
}, [setTheme]);
return (
<div>
<GlobalStyle theme={themes[theme]} />
<Helmet titleTemplate="%s | Will Webberley" defaultTitle="Will Webberley">
<link rel="icon" type="image/png" href={Will} />
</Helmet>
@ -22,6 +73,7 @@ const TemplateWrapper = ({ children }) => (
<StyledMain>{children}</StyledMain>
<Footer />
</div>
)
);
}
export default TemplateWrapper

View File

@ -2,16 +2,15 @@ import React from 'react'
import { Link } from 'gatsby'
import moment from 'moment';
import Emoji from './Emoji';
import Alert from './Alert';
import Tag from './Tag';
const NoteHeader = ({ post }) => (
<div style={{marginBottom: '1.45rem', background: 'aliceblue', padding: 3, borderRadius: 3}}>
<h3><Emoji e='📔' /> <Link to={post.fields.slug}>{post.frontmatter.title}</Link> <small>(last updated {moment(post.frontmatter.date).format('D MMMM YYYY')})</small></h3>
<Alert colour='blue'
title={<span><Emoji e='📔' /> <Link to={post.fields.slug}>{post.frontmatter.title}</Link> <small>(last updated {moment(post.frontmatter.date).format('D MMMM YYYY')})</small></span>}>
<p>{post.frontmatter.description}</p>
{post.frontmatter.tags && post.frontmatter.tags.map(tag =>
<Link style={{marginRight: 10}} to={`/tags/${tag}`}><Emoji e="🏷️" /> #{tag}</Link>
)}
</div>
)
{post.frontmatter.tags && post.frontmatter.tags.map(tag => <Tag key={tag} tag={tag} />)}
</Alert>
);
export default NoteHeader;

15
src/components/Tag.js Normal file
View File

@ -0,0 +1,15 @@
import React from 'react';
import { Link } from 'gatsby';
import styled from 'styled-components';
import Emoji from '../components/Emoji';
const StlyedTag = styled(Link)`
margin-left: 10px;
`;
const Tag = ({ tag }) => (
<StlyedTag key={tag} to={`/tags/${tag}`}><Emoji e="🏷️" /> #{tag}</StlyedTag>
);
export default Tag;

BIN
src/images/will.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -4,8 +4,8 @@ import Layout from '../components/Layout/Layout.js';
const ErrorPage = () => (
<Layout>
<h2 style={{fontFamily:'Courier, Monospace'}}>~/errors/404</h2>
<p>The requested file was not found.</p>
<h2>Not found</h2>
<p>The requested file could not be found.</p>
</Layout>
)

View File

@ -1,12 +1,18 @@
import React from "react";
import { graphql, Link } from 'gatsby';
import Helmet from 'react-helmet'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faRss } from '@fortawesome/free-solid-svg-icons'
import Helmet from 'react-helmet';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faRss } from '@fortawesome/free-solid-svg-icons';
import styled from 'styled-components';
import Layout from '../../components/Layout/Layout.js';
import BlogPostHeader from '../../components/BlogPostHeader';
const StyledHeader = styled.h2`
display: flex;
justify-content: space-between;
`;
export default class Blog extends React.Component {
render() {
@ -15,12 +21,15 @@ export default class Blog extends React.Component {
<Helmet title='Blog'>
<meta name="description" content="Blog post entries and articles" />
</Helmet>
<h2>Blog posts
<small style={{float: 'right'}}><FontAwesomeIcon icon={faRss} /> <Link to='/feeds'>RSS feeds</Link></small>
</h2>
<StyledHeader>
<span>Blog posts</span>
<small>
<FontAwesomeIcon icon={faRss} /> <Link to='/feeds'>RSS feeds</Link>
</small>
</StyledHeader>
{this.props.data.allMarkdownRemark.edges.map(({ node }) => {
if (node.fields.slug.includes('/blog/')) {
return <BlogPostHeader post={node} />;
return <BlogPostHeader key={node.fields.slug} post={node} />;
} else return null;
})}

View File

@ -7,6 +7,7 @@ import styled from 'styled-components';
import Layout from '../components/Layout/Layout.js';
import ExternalLink from '../components/ExternalLink';
import Emoji from '../components/Emoji';
import Alert from '../components/Alert';
const StyledButton = styled(ExternalLink)`
display: inline-block;
@ -64,25 +65,20 @@ const FeedsPage = () => {
<p>RSS lets you subscribe to receive new articles (blog posts, notes, etc.) as they are published.</p>
<p>My site has a number of different RSS feeds you can subscribe to, so you can choose the one(s) you want and avoid the content you're not interested in.</p>
<div style={{background: 'linen', padding: 10, borderRadius: 3, margin: '20px 0px'}}>
<h3>Do you need an RSS reader?</h3>
<Alert colour='orange' title='Do you need an RSS reader?'>
<p>There are lots of good ones. Just <ExternalLink href="https://duckduckgo.com/?q=rss+reader">search</ExternalLink> for one for your device.
I use <ExternalLink href="https://www.reederapp.com/">Reeder 5</ExternalLink> on my <ExternalLink href="https://itunes.apple.com/app/id1529448980">Mac</ExternalLink> and <ExternalLink href="https://apps.apple.com/app/id1529445840">iPhone</ExternalLink>.</p>
</div>
</Alert>
<h3><Emoji e='🌍' /> Recommended: enter <code>wilw.dev <StyledCopyButton onClick={copyFeedsUrl}><FontAwesomeIcon icon={faCopy} /></StyledCopyButton></code> into your RSS reader &amp; it should list the available feeds</h3>
<p>Alternatively you can choose a feed from the options below.</p>
<div style={{marginTop: 20}}>
{feeds.map(feed =>
<div style={{background: 'whitesmoke', padding: 10, borderRadius: 3, margin: '20px 0px'}}>
<h3><FontAwesomeIcon icon={faRssSquare} /> {feed.name}</h3>
<Alert key={feed.url} colour='green' title={<span><FontAwesomeIcon icon={faRssSquare} /> {feed.name}</span>}>
<p>{feed.description}</p>
<StyledButton href={feed.url}>Subscribe</StyledButton>
</div>
</Alert>
)}
</div>
</Layout>
);
}

View File

@ -6,6 +6,7 @@ import { faRss } from '@fortawesome/free-solid-svg-icons'
import Layout from '../components/Layout/Layout.js';
import Emoji from '../components/Emoji';
import Alert from '../components/Alert';
import ArticleHeader from '../components/ArticleHeader';
import ExternalLink from '../components/ExternalLink';
@ -17,11 +18,12 @@ const IndexPage = (props) => (
<p><Emoji n="Live long" e='🖖' /> Hello and welcome. I'm a tech lead/enthusiast based in Wales, UK. My main interests are indie and open-source projects, web and mobile technologies, serverless, IoT and automation.</p>
<div style={{background: 'beige', padding: '2px', borderRadius: 3, margin: '40px 0px'}}>
<h3><Emoji e='💯' /> 100 Days to Offload</h3>
<Alert colour='green'
title={<span><Emoji e='💯' /> 100 Days to Offload</span>}
>
<p>I'm taking part in the <ExternalLink href="https://100daystooffload.com/">100 Days to Offload challenge</ExternalLink> in 2021.</p>
<p><Link to='/blog/2021/01/29/100-days-to-offload'>Read more about it</Link> and <Link to='/tags/100daystooffload'>see what I've written so far</Link> for this challenge.</p>
</div>
</Alert>
<h3>Quick background</h3>
<p><Emoji e='💡' /> Since 2016 I have been Chief Technology Officer at enterprise SaaS company <ExternalLink href="https://simplydo.co.uk">Simply Do Ideas</ExternalLink>. Before this I was a software engineer at <ExternalLink href="https://www.chaserhq.com">Chaser</ExternalLink>.</p>
@ -46,7 +48,7 @@ const IndexPage = (props) => (
<h3>Recent posts</h3>
{props.data.recentPosts.edges
.map(({ node }) => node.fields.slug.indexOf('blog') > -1 ? <ArticleHeader post={node} /> : null)}
.map(({ node }) => node.fields.slug.indexOf('blog') > -1 ? <ArticleHeader key={node.fields.slug} post={node} /> : null)}
<p><Link to='/blog'>See more</Link></p>
</Layout>
)

View File

@ -17,7 +17,7 @@ export default class Notes extends React.Component {
<p>These are notes that I try and keep up-to-date. View <Link to='/blog'>my blog</Link> to see a post stream.</p>
{this.props.data.notesQuery.edges.map(({ node }) => {
if (node.fields.slug.includes('/notes/')) {
return <NoteHeader post={node} />;
return <NoteHeader key={node.fields.slug} post={node} />;
} else return null;
})}

View File

@ -1,16 +1,16 @@
import React from 'react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faLaptop } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faLaptop } from '@fortawesome/free-solid-svg-icons';
import { faLinux, faApple, faAndroid, faGithub } from '@fortawesome/free-brands-svg-icons';
import Helmet from 'react-helmet'
import Layout from '../components/Layout/Layout.js';
import Helmet from 'react-helmet';
import styled from 'styled-components';
import Layout from '../components/Layout/Layout.js';
import treadlIcon from '../images/treadl.png';
import dottyIcon from '../images/dotty.png';
import ssotoolsIcon from '../images/ssotools.png';
const ProjectsPage = () => {
const projects = [
const projects = [
{
name: 'Treadl',
logo: treadlIcon,
@ -41,9 +41,9 @@ const ProjectsPage = () => {
{ name: 'Web', icon: faLaptop }, { name: 'Android', icon: faAndroid, url: 'https://play.google.com/store/apps/details?id=app.trilo' }, { name: 'iOS', icon: faApple, url: 'https://itunes.apple.com/gb/app/trilo/id1460738681' }
]
},*/
];
];
const otherProjects = [
const otherProjects = [
{
name: 'Gower Tides',
description: 'An Android app for displaying daily tidal patterns, along with weather and surf conditions, for the sea around the Gower Peninsula in South Wales. The app was aimed at surfers and other sea-users, and was available for several years on Google Play, but is now discontinued.',
@ -77,30 +77,53 @@ const ProjectsPage = () => {
description: 'A project that supported simultaneous audio output (e.g. for music) across devices on a network. The project hooked into PulseAudio and made uss of RTP to broadcast sound to low-powered devices (such as a Raspberry Pi). Whilst I used to use this fairly frequently, the project is no longer being maintained.',
source: 'https://github.com/willwebberley/CasaStream'
}
];
];
return (
const StyledProjects = styled.div`
display: grid;
grid-column-gap: 20px;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
`;
const StyledProject = styled.div`
display: flex;
flex-direction: row;
margin-bottom: 30px;
border-bottom: 1px solid rgba(150,150,150,0.2);
`;
const StyledProjectImage = styled.div`
padding-right: 30px;
padding-top: 30px;
img{
width: 100px;
}
`;
const StyledPlatform = styled.a`
text-decoration: none;
margin-right: 15px;
`;
const ProjectsPage = () => (
<Layout>
<Helmet title='Projects'>
<meta name="description" content="Projects I'm working on now and in the past" />
</Helmet>
<h2>Projects</h2>
<div style={{display:'grid', gridColumnGap: 20, gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))'}}>
<StyledProjects>
<div>
<p>Some of the things I am currently working on. Please <a href='https://twitter.com/willwebberley' target='_blank' rel='noopener noreferrer'>get in touch</a> if you are interested in finding out more about these (or if you'd like to help out or get involved!).</p>
{projects.map((p, i) =>
<div key={i} style={{display: 'flex', flexDirection: 'row', marginBottom: 30, borderBottom: '1px solid rgb(250,250,250)'}}>
<div style={{paddingRight: 30, paddingTop: 30}}>
<img alt={p.name} src={p.logo} style={{width: 100}} />
</div>
<StyledProject key={i}>
<StyledProjectImage>
<img alt={p.name} src={p.logo} />
</StyledProjectImage>
<div>
<h3><a href={p.url} target='_blank' rel='noopener noreferrer'>{p.name}</a></h3>
<p>{p.description}</p>
<p>{p.availableFor && p.availableFor.map((a, j) =>
<a key={j} href={a.url || p.url} target="_blank" rel="noopener noreferrer" style={{textDecoration: 'none', marginRight: 15}}>{a.icon && <FontAwesomeIcon icon={a.icon} />} {a.name}</a>
<StyledPlatform key={j} href={a.url || p.url} target="_blank" rel="noopener noreferrer">{a.icon && <FontAwesomeIcon icon={a.icon} />} {a.name}</StyledPlatform>
)}</p>
</div>
</div>
</StyledProject>
)}
</div>
<div>
@ -113,9 +136,8 @@ const ProjectsPage = () => {
</div>
)}
</div>
</div>
</StyledProjects>
</Layout>
)
}
);
export default ProjectsPage

View File

@ -1,10 +1,10 @@
import React from 'react'
import Helmet from 'react-helmet'
import React from 'react';
import Helmet from 'react-helmet';
import styled from 'styled-components';
import Layout from '../components/Layout/Layout.js';
const ProjectsPage = () => {
const outputs = [
const outputs = [
{
name: 'Retweeting: A Study of Message-Forwarding in Twitter',
date: '2011',
@ -102,17 +102,39 @@ const ProjectsPage = () => {
journal: 'IEEE Transactions on Computational Social Systems',
url: 'https://ieeexplore.ieee.org/iel7/6570650/6780646/08232468.pdf'
}
];
];
return (
const StyledResearch = styled.div`
display: grid;
grid-column-gap: 20px;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
`;
const StyledPublication = styled.div`
display: flex;
flex-direction: row;
margin-bottom: 10px;
border-bottom: 1px solid rgba(150,150,150,0.2);
`;
const PublicationTitle = styled.h4`
margin-top: 0px;
`;
const PublicationAuthors = styled.p`
margin-bottom: 0px;
font-size: 15px;
`;
const PublicationAttribution = styled.p`
font-size: 13px;
`;
const ProjectsPage = () => (
<Layout>
<Helmet title='Research'>
<meta name="description" content="Research work papers and outputs" />
</Helmet>
<h2>Research</h2>
<div style={{display:'grid', gridColumnGap: 20, gridTemplateColumns: 'repeat(auto-fit, minmax(240px, 1fr))'}}>
<StyledResearch>
<div>
<h3 style={{marginTop: 0}}>Projects</h3>
<h3>Projects</h3>
<p>I have been involved in a number of research projects.</p>
<h4>NIS/DAIS-ITA project</h4>
@ -123,20 +145,19 @@ const ProjectsPage = () => {
</div>
<div>
<h3 style={{marginTop: 0}}>Publications</h3>
<h3>Publications</h3>
{outputs.reverse().map((o, i) =>
<div key={i} style={{display: 'flex', flexDirection: 'row', marginBottom: 10, borderBottom: '1px solid rgb(250,250,250)'}}>
<StyledPublication key={i}>
<div>
<h4 style={{marginTop:0}}><a href={o.url} target='_blank' rel='noopener noreferrer'>{o.name}</a></h4>
<p style={{marginBottom:0, fontSize:15}}>{o.authors}</p>
<p style={{fontSize: 13}}>{o.journal}, {o.date}</p>
</div>
<PublicationTitle><a href={o.url} target='_blank' rel='noopener noreferrer'>{o.name}</a></PublicationTitle>
<PublicationAuthors>{o.authors}</PublicationAuthors>
<PublicationAttribution>{o.journal}, {o.date}</PublicationAttribution>
</div>
</StyledPublication>
)}
</div>
</div>
</StyledResearch>
</Layout>
)
}
);
export default ProjectsPage

View File

@ -29,7 +29,8 @@ const ThisPage = () => {
<p>This is a static site built using <ExternalLink href="https://www.gatsbyjs.com">GatsbyJS</ExternalLink> - an open-source generator that leverages React to build performant websites.</p>
<p>I deploy the site using <ExternalLink href="https://vercel.com">Vercel</ExternalLink>, which uses its network of CDN nodes to serve the static site assets.</p>
<p>I use the <ExternalLink href="https://www.recursive.design/">Recursive</ExternalLink> font.</p>
<p>The content and layout/theming is hand-made by me. You can check out <ExternalLink href="https://git.wilw.dev/wilw/wilw.dev">the source code</ExternalLink> if you're interested.</p>
<p><ExternalLink href="https://www.happyhues.co">Happy Hues</ExternalLink> provides the inspiration for some of the colour themes.</p>
<p>The content and layout is hand-made by me. You can check out <ExternalLink href="https://git.wilw.dev/wilw/wilw.dev">the source code</ExternalLink> if you're interested.</p>
<h2><Emoji e='' /> Other remarks</h2>
<p>The content on this site represents my own thoughts and opinions, and does not necessarily reflect the opinions of the people and companies I work with and for.</p>

6
src/store.js Normal file
View File

@ -0,0 +1,6 @@
import create from 'zustand'
export default create(set => ({
theme: 'light',
setTheme: theme => set({ theme }),
}));

View File

@ -8,7 +8,8 @@ import { config } from "@fortawesome/fontawesome-svg-core"
import "@fortawesome/fontawesome-svg-core/styles.css"
import Layout from '../components/Layout/Layout.js';
import Emoji from '../components/Emoji';
import Tag from '../components/Tag';
import Alert from '../components/Alert';
import ExternalLink from '../components/ExternalLink';
config.autoAddCss = false;
@ -26,35 +27,30 @@ export default class BlogPost extends React.Component {
<h1>{post.frontmatter.title}</h1>
<h4>{moment(post.frontmatter.date).format('D MMMM YYYY')} <i><small>({moment(post.frontmatter.date).fromNow()})</small></i>
{post.frontmatter.tags && post.frontmatter.tags.map(tag =>
<Link style={{marginLeft: 10}} to={`/tags/${tag}`}><Emoji e="🏷️" /> #{tag}</Link>
)}
{post.frontmatter.tags && post.frontmatter.tags.map(tag => <Tag tag={tag} key={tag} />)}
</h4>
{post.frontmatter.tags && post.frontmatter.tags.indexOf('100daystooffload') > -1 &&
<div style={{background: 'beige', padding: 4, borderRadius: 3, margin: '20px 0px'}}>
<h3><Emoji e='💯' /> 100 Days to Offload</h3>
<Alert colour='green' emoji='💯' title='100 Days to Offload'>
<p><b>This article is one of a series of posts in the <ExternalLink href="https://100daystooffload.com">100 Days to Offload challenge</ExternalLink></b>.
The challenge focuses on writing <strong>frequency</strong> rather than <strong>quality</strong>, and so posts may not always be fully planned out. They are simply a way to offload thoughts.</p>
<p><Link to='/tags/100daystooffload'>View other articles in this series</Link></p>
</div>
</Alert>
}
{post.frontmatter.tags && post.frontmatter.tags.indexOf('book') > -1 &&
<div style={{background: 'linen', padding: 4, borderRadius: 3, margin: '20px 0px'}}>
<h3><Emoji e='📚' /> This article is about a book</h3>
<Alert colour='orange' emoji='📚' title='This article is about a book'>
<p>A quick warning: I always try to avoid giving away spoilers but be careful if you're worried about finding out too much.</p>
</div>
</Alert>
}
{moment(post.frontmatter.date).isBefore(moment().subtract(12, 'months')) &&
<div style={{background: 'lightpink', padding: 4, borderRadius: 3, margin: '20px 0px'}}>
<h3><Emoji e='🕰️' /> This is an old post</h3>
<Alert colour='pink' emoji='🕰️' title='This is an old post'>
<p>Please note that this article was posted quite a while ago and may now be out-of-date or inaccurate.</p>
</div>
</Alert>
}
<article className='post' style={{marginTop: 10}}>
<article>
<div dangerouslySetInnerHTML={{ __html: post.html }} />
</article>
@ -66,17 +62,15 @@ export default class BlogPost extends React.Component {
}
{post.frontmatter.tags && post.frontmatter.tags.indexOf('book') > -1 &&
<div style={{background: 'linen', padding: 4, borderRadius: 3, margin: '20px 0px'}}>
<h3><Emoji e='📚' /> I try and read a wide variety of books</h3>
<Alert colour='orange' emoji='📚' title='I try and read a wide variety of books'>
<p>If you're interested in seeing what else I read you can <a href="https://www.goodreads.com/user/show/22390023-will" target="_blank" rel="noopener noreferrer">check out my Goodreads profile</a>.</p>
</div>
</Alert>
}
<div style={{background: 'honeydew', padding: 4, borderRadius: 3, marginTop: 10}}>
<h3><Emoji e='📲' /> Enjoyed this article? Subscribe to updates!</h3>
<Alert colour='blue' emoji='📲' title='Enjoyed this article? Subscribe to updates!'>
<p>If you would like to read more posts like this, then you can subscribe via RSS.</p>
<p><FontAwesomeIcon icon={faRss} /> <Link to='/feeds'>Subscribe to an RSS feed</Link></p>
</div>
</Alert>
</Layout>
);
}

View File

@ -7,6 +7,8 @@ import { faArrowLeft } from '@fortawesome/free-solid-svg-icons'
import Layout from '../components/Layout/Layout.js';
import Emoji from '../components/Emoji';
import Alert from '../components/Alert';
import Tag from '../components/Tag';
export default class Note extends React.Component {
@ -21,19 +23,17 @@ export default class Note extends React.Component {
<h1>{note.frontmatter.title}</h1>
<h4>Last updated {moment(note.frontmatter.date).format('D MMMM YYYY')} <i><small>({moment(note.frontmatter.date).fromNow()})</small></i>
{note.frontmatter.tags && note.frontmatter.tags.map(tag =>
<Link style={{marginLeft: 10}} to={`/tags/${tag}`}><Emoji e="🏷️" /> #{tag}</Link>
)}
{note.frontmatter.tags && note.frontmatter.tags.map(tag => <Tag key={tag} tag={tag} />)}
</h4>
{moment(note.frontmatter.date).isBefore(moment().subtract(12, 'months')) &&
<div style={{background: 'lightpink', padding: 4, borderRadius: 3, margin: '20px 0px'}}>
<Alert colour='pink' emoji='🕰️' title='This is an old note'>
<h3><Emoji e='🕰️' /> This is an old note</h3>
<p>Please note that this article was last updated quite a while ago and may now be out-of-date or inaccurate.</p>
</div>
</Alert>
}
<article className='post' style={{marginTop: 10}}>
<article>
<div dangerouslySetInnerHTML={{ __html: note.html }} />
</article>
</Layout>

View File

@ -12378,6 +12378,11 @@ yurnalist@^2.1.0:
read "^1.0.7"
strip-ansi "^5.2.0"
zustand@^3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/zustand/-/zustand-3.3.1.tgz#de5c4b51112b84e0f819d8b3f336fbfbc087d758"
integrity sha512-o0rgrBsi29nCkPHdhtkAHisCIlmRUoXOV+1AmDMeCgkGG0i5edFSpGU0KiZYBvFmBYycnck4Z07JsLYDjSET9g==
zwitch@^1.0.0:
version "1.0.5"
resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920"