Compare commits
4 Commits
0d2074f1d4
...
fdc412bf15
Author | SHA1 | Date | |
---|---|---|---|
fdc412bf15 | |||
e27b58cf07 | |||
8bae9b9efd | |||
dbb433f0f2 |
@ -17,7 +17,8 @@
|
|||||||
"react": "^17.0.1",
|
"react": "^17.0.1",
|
||||||
"react-dom": "^17.0.1",
|
"react-dom": "^17.0.1",
|
||||||
"react-helmet": "^5.2.1",
|
"react-helmet": "^5.2.1",
|
||||||
"styled-components": "^5.2.1"
|
"styled-components": "^5.2.1",
|
||||||
|
"zustand": "^3.3.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "gatsby build",
|
"build": "gatsby build",
|
||||||
|
50
src/components/Alert.js
Normal file
50
src/components/Alert.js
Normal 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;
|
@ -2,16 +2,20 @@ import React from 'react'
|
|||||||
import { Link } from 'gatsby'
|
import { Link } from 'gatsby'
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import Emoji from './Emoji';
|
import Emoji from './Emoji';
|
||||||
|
import Alert from './Alert';
|
||||||
|
import Tag from './Tag';
|
||||||
|
|
||||||
const BlogPostHeader = ({ post }) => (
|
const BlogPostHeader = ({ post }) => (
|
||||||
|
|
||||||
<div style={{marginBottom: '1.45rem', background: 'floralwhite', padding: 3, borderRadius: 3}}>
|
<Alert
|
||||||
<h3><Emoji e='📝' /> <Link to={post.fields.slug}>{post.frontmatter.title}</Link> <small>{moment(post.frontmatter.date).format('D MMMM YYYY')}</small></h3>
|
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>
|
<p><em>{post.excerpt}</em></p>
|
||||||
{post.frontmatter.tags && post.frontmatter.tags.map(tag =>
|
{post.frontmatter.tags && post.frontmatter.tags.map(tag => <Tag key={tag} tag={tag} />)}
|
||||||
<Link style={{marginRight: 10}} to={`/tags/${tag}`}><Emoji e="🏷️" /> #{tag}</Link>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
</Alert>
|
||||||
)
|
)
|
||||||
|
|
||||||
export default BlogPostHeader;
|
export default BlogPostHeader;
|
||||||
|
@ -3,9 +3,8 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
|||||||
import { faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons'
|
import { faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
const ExternalLink = (props) => {
|
const ExternalLink = (props) => {
|
||||||
const isExternal = props.href.indexOf('http') === 0 && props.href.indexOf('wilw.dev') === -1;
|
|
||||||
return (
|
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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,8 +2,10 @@ import React from 'react'
|
|||||||
import { Link } from 'gatsby'
|
import { Link } from 'gatsby'
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import Emoji from './Emoji';
|
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`
|
const StyledHeader = styled.header`
|
||||||
text-align: left;
|
text-align: left;
|
||||||
@ -53,8 +55,23 @@ const StyledNavBar = styled.div`
|
|||||||
font-size: 20px;
|
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>
|
<StyledHeader>
|
||||||
<StyledHeaderArea>
|
<StyledHeaderArea>
|
||||||
<Link to='/'><StyledAvatar /></Link>
|
<Link to='/'><StyledAvatar /></Link>
|
||||||
@ -71,8 +88,14 @@ const Header = () => (
|
|||||||
<Link to='/notes' activeClassName='active-link'>notes</Link>
|
<Link to='/notes' activeClassName='active-link'>notes</Link>
|
||||||
<Link to='/projects' activeClassName='active-link'>projects</Link>
|
<Link to='/projects' activeClassName='active-link'>projects</Link>
|
||||||
<Link to='/research' activeClassName='active-link'>research</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>
|
</StyledNavBar>
|
||||||
</StyledHeader>
|
</StyledHeader>
|
||||||
)
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default Header
|
export default Header
|
||||||
|
@ -12,19 +12,6 @@ html {
|
|||||||
overflow-y: scroll;
|
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 {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: 'Recursive', monospace;
|
font-family: 'Recursive', monospace;
|
||||||
@ -84,6 +71,14 @@ h4 {
|
|||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
background-color: transparent;
|
||||||
|
-webkit-text-decoration-skip: objects;
|
||||||
|
}
|
||||||
|
a:active, a:hover {
|
||||||
|
outline-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
margin: 0px 0px 15px 0px;
|
margin: 0px 0px 15px 0px;
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,47 @@
|
|||||||
import React from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import Helmet from 'react-helmet'
|
import Helmet from 'react-helmet'
|
||||||
import styled from 'styled-components'
|
import styled, { createGlobalStyle } from 'styled-components'
|
||||||
|
|
||||||
import Header from '../Header'
|
import Header from '../Header'
|
||||||
import Footer from '../Footer';
|
import Footer from '../Footer';
|
||||||
|
import useStore from '../../store';
|
||||||
import Will from '../../images/will.png';
|
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`
|
const StyledMain = styled.div`
|
||||||
margin: 0px auto;
|
margin: 0px auto;
|
||||||
@ -13,8 +49,23 @@ const StyledMain = styled.div`
|
|||||||
padding: 0px 1.1rem 1.45rem;
|
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>
|
<div>
|
||||||
|
<GlobalStyle theme={themes[theme]} />
|
||||||
<Helmet titleTemplate="%s | Will Webberley" defaultTitle="Will Webberley">
|
<Helmet titleTemplate="%s | Will Webberley" defaultTitle="Will Webberley">
|
||||||
<link rel="icon" type="image/png" href={Will} />
|
<link rel="icon" type="image/png" href={Will} />
|
||||||
</Helmet>
|
</Helmet>
|
||||||
@ -22,6 +73,7 @@ const TemplateWrapper = ({ children }) => (
|
|||||||
<StyledMain>{children}</StyledMain>
|
<StyledMain>{children}</StyledMain>
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default TemplateWrapper
|
export default TemplateWrapper
|
||||||
|
@ -2,16 +2,15 @@ import React from 'react'
|
|||||||
import { Link } from 'gatsby'
|
import { Link } from 'gatsby'
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import Emoji from './Emoji';
|
import Emoji from './Emoji';
|
||||||
|
import Alert from './Alert';
|
||||||
|
import Tag from './Tag';
|
||||||
|
|
||||||
const NoteHeader = ({ post }) => (
|
const NoteHeader = ({ post }) => (
|
||||||
|
<Alert colour='blue'
|
||||||
<div style={{marginBottom: '1.45rem', background: 'aliceblue', padding: 3, borderRadius: 3}}>
|
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>}>
|
||||||
<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>
|
|
||||||
<p>{post.frontmatter.description}</p>
|
<p>{post.frontmatter.description}</p>
|
||||||
{post.frontmatter.tags && post.frontmatter.tags.map(tag =>
|
{post.frontmatter.tags && post.frontmatter.tags.map(tag => <Tag key={tag} tag={tag} />)}
|
||||||
<Link style={{marginRight: 10}} to={`/tags/${tag}`}><Emoji e="🏷️" /> #{tag}</Link>
|
</Alert>
|
||||||
)}
|
);
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
export default NoteHeader;
|
export default NoteHeader;
|
||||||
|
15
src/components/Tag.js
Normal file
15
src/components/Tag.js
Normal 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
BIN
src/images/will.jpeg
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
@ -4,8 +4,8 @@ import Layout from '../components/Layout/Layout.js';
|
|||||||
|
|
||||||
const ErrorPage = () => (
|
const ErrorPage = () => (
|
||||||
<Layout>
|
<Layout>
|
||||||
<h2 style={{fontFamily:'Courier, Monospace'}}>~/errors/404</h2>
|
<h2>Not found</h2>
|
||||||
<p>The requested file was not found.</p>
|
<p>The requested file could not be found.</p>
|
||||||
</Layout>
|
</Layout>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,12 +1,18 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { graphql, Link } from 'gatsby';
|
import { graphql, Link } from 'gatsby';
|
||||||
import Helmet from 'react-helmet'
|
import Helmet from 'react-helmet';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { faRss } from '@fortawesome/free-solid-svg-icons'
|
import { faRss } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import Layout from '../../components/Layout/Layout.js';
|
import Layout from '../../components/Layout/Layout.js';
|
||||||
import BlogPostHeader from '../../components/BlogPostHeader';
|
import BlogPostHeader from '../../components/BlogPostHeader';
|
||||||
|
|
||||||
|
const StyledHeader = styled.h2`
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
`;
|
||||||
|
|
||||||
export default class Blog extends React.Component {
|
export default class Blog extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -15,12 +21,15 @@ export default class Blog extends React.Component {
|
|||||||
<Helmet title='Blog'>
|
<Helmet title='Blog'>
|
||||||
<meta name="description" content="Blog post entries and articles" />
|
<meta name="description" content="Blog post entries and articles" />
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<h2>Blog posts
|
<StyledHeader>
|
||||||
<small style={{float: 'right'}}><FontAwesomeIcon icon={faRss} /> <Link to='/feeds'>RSS feeds</Link></small>
|
<span>Blog posts</span>
|
||||||
</h2>
|
<small>
|
||||||
|
<FontAwesomeIcon icon={faRss} /> <Link to='/feeds'>RSS feeds</Link>
|
||||||
|
</small>
|
||||||
|
</StyledHeader>
|
||||||
{this.props.data.allMarkdownRemark.edges.map(({ node }) => {
|
{this.props.data.allMarkdownRemark.edges.map(({ node }) => {
|
||||||
if (node.fields.slug.includes('/blog/')) {
|
if (node.fields.slug.includes('/blog/')) {
|
||||||
return <BlogPostHeader post={node} />;
|
return <BlogPostHeader key={node.fields.slug} post={node} />;
|
||||||
} else return null;
|
} else return null;
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import styled from 'styled-components';
|
|||||||
import Layout from '../components/Layout/Layout.js';
|
import Layout from '../components/Layout/Layout.js';
|
||||||
import ExternalLink from '../components/ExternalLink';
|
import ExternalLink from '../components/ExternalLink';
|
||||||
import Emoji from '../components/Emoji';
|
import Emoji from '../components/Emoji';
|
||||||
|
import Alert from '../components/Alert';
|
||||||
|
|
||||||
const StyledButton = styled(ExternalLink)`
|
const StyledButton = styled(ExternalLink)`
|
||||||
display: inline-block;
|
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>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>
|
<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'}}>
|
<Alert colour='orange' title='Do you need an RSS reader?'>
|
||||||
<h3>Do you need an RSS reader?</h3>
|
|
||||||
<p>There are lots of good ones. Just <ExternalLink href="https://duckduckgo.com/?q=rss+reader">search</ExternalLink> for one for your device.
|
<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>
|
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 & it should list the available feeds</h3>
|
<h3><Emoji e='🌍' /> Recommended: enter <code>wilw.dev <StyledCopyButton onClick={copyFeedsUrl}><FontAwesomeIcon icon={faCopy} /></StyledCopyButton></code> into your RSS reader & it should list the available feeds</h3>
|
||||||
<p>Alternatively you can choose a feed from the options below.</p>
|
<p>Alternatively you can choose a feed from the options below.</p>
|
||||||
|
|
||||||
<div style={{marginTop: 20}}>
|
|
||||||
{feeds.map(feed =>
|
{feeds.map(feed =>
|
||||||
<div style={{background: 'whitesmoke', padding: 10, borderRadius: 3, margin: '20px 0px'}}>
|
<Alert key={feed.url} colour='green' title={<span><FontAwesomeIcon icon={faRssSquare} /> {feed.name}</span>}>
|
||||||
<h3><FontAwesomeIcon icon={faRssSquare} /> {feed.name}</h3>
|
|
||||||
<p>{feed.description}</p>
|
<p>{feed.description}</p>
|
||||||
<StyledButton href={feed.url}>Subscribe</StyledButton>
|
<StyledButton href={feed.url}>Subscribe</StyledButton>
|
||||||
</div>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
</div>
|
|
||||||
|
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import { faRss } from '@fortawesome/free-solid-svg-icons'
|
|||||||
|
|
||||||
import Layout from '../components/Layout/Layout.js';
|
import Layout from '../components/Layout/Layout.js';
|
||||||
import Emoji from '../components/Emoji';
|
import Emoji from '../components/Emoji';
|
||||||
|
import Alert from '../components/Alert';
|
||||||
import ArticleHeader from '../components/ArticleHeader';
|
import ArticleHeader from '../components/ArticleHeader';
|
||||||
import ExternalLink from '../components/ExternalLink';
|
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>
|
<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'}}>
|
<Alert colour='green'
|
||||||
<h3><Emoji e='💯' /> 100 Days to Offload</h3>
|
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>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>
|
<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>
|
<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>
|
<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>
|
<h3>Recent posts</h3>
|
||||||
{props.data.recentPosts.edges
|
{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>
|
<p><Link to='/blog'>See more</Link></p>
|
||||||
</Layout>
|
</Layout>
|
||||||
)
|
)
|
||||||
|
@ -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>
|
<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 }) => {
|
{this.props.data.notesQuery.edges.map(({ node }) => {
|
||||||
if (node.fields.slug.includes('/notes/')) {
|
if (node.fields.slug.includes('/notes/')) {
|
||||||
return <NoteHeader post={node} />;
|
return <NoteHeader key={node.fields.slug} post={node} />;
|
||||||
} else return null;
|
} else return null;
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { faLaptop } from '@fortawesome/free-solid-svg-icons'
|
import { faLaptop } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { faLinux, faApple, faAndroid, faGithub } from '@fortawesome/free-brands-svg-icons';
|
import { faLinux, faApple, faAndroid, faGithub } from '@fortawesome/free-brands-svg-icons';
|
||||||
import Helmet from 'react-helmet'
|
import Helmet from 'react-helmet';
|
||||||
import Layout from '../components/Layout/Layout.js';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import Layout from '../components/Layout/Layout.js';
|
||||||
import treadlIcon from '../images/treadl.png';
|
import treadlIcon from '../images/treadl.png';
|
||||||
import dottyIcon from '../images/dotty.png';
|
import dottyIcon from '../images/dotty.png';
|
||||||
import ssotoolsIcon from '../images/ssotools.png';
|
import ssotoolsIcon from '../images/ssotools.png';
|
||||||
|
|
||||||
const ProjectsPage = () => {
|
|
||||||
const projects = [
|
const projects = [
|
||||||
{
|
{
|
||||||
name: 'Treadl',
|
name: 'Treadl',
|
||||||
@ -79,28 +79,51 @@ const ProjectsPage = () => {
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
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>
|
<Layout>
|
||||||
<Helmet title='Projects'>
|
<Helmet title='Projects'>
|
||||||
<meta name="description" content="Projects I'm working on now and in the past" />
|
<meta name="description" content="Projects I'm working on now and in the past" />
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<h2>Projects</h2>
|
<h2>Projects</h2>
|
||||||
<div style={{display:'grid', gridColumnGap: 20, gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))'}}>
|
<StyledProjects>
|
||||||
<div>
|
<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>
|
<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) =>
|
{projects.map((p, i) =>
|
||||||
<div key={i} style={{display: 'flex', flexDirection: 'row', marginBottom: 30, borderBottom: '1px solid rgb(250,250,250)'}}>
|
<StyledProject key={i}>
|
||||||
<div style={{paddingRight: 30, paddingTop: 30}}>
|
<StyledProjectImage>
|
||||||
<img alt={p.name} src={p.logo} style={{width: 100}} />
|
<img alt={p.name} src={p.logo} />
|
||||||
</div>
|
</StyledProjectImage>
|
||||||
<div>
|
<div>
|
||||||
<h3><a href={p.url} target='_blank' rel='noopener noreferrer'>{p.name}</a></h3>
|
<h3><a href={p.url} target='_blank' rel='noopener noreferrer'>{p.name}</a></h3>
|
||||||
<p>{p.description}</p>
|
<p>{p.description}</p>
|
||||||
<p>{p.availableFor && p.availableFor.map((a, j) =>
|
<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>
|
)}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</StyledProject>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@ -113,9 +136,8 @@ const ProjectsPage = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</StyledProjects>
|
||||||
</Layout>
|
</Layout>
|
||||||
)
|
);
|
||||||
}
|
|
||||||
|
|
||||||
export default ProjectsPage
|
export default ProjectsPage
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import React from 'react'
|
import React from 'react';
|
||||||
import Helmet from 'react-helmet'
|
import Helmet from 'react-helmet';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import Layout from '../components/Layout/Layout.js';
|
import Layout from '../components/Layout/Layout.js';
|
||||||
|
|
||||||
const ProjectsPage = () => {
|
|
||||||
const outputs = [
|
const outputs = [
|
||||||
{
|
{
|
||||||
name: 'Retweeting: A Study of Message-Forwarding in Twitter',
|
name: 'Retweeting: A Study of Message-Forwarding in Twitter',
|
||||||
@ -104,15 +104,37 @@ const ProjectsPage = () => {
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
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>
|
<Layout>
|
||||||
<Helmet title='Research'>
|
<Helmet title='Research'>
|
||||||
<meta name="description" content="Research work papers and outputs" />
|
<meta name="description" content="Research work papers and outputs" />
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<h2>Research</h2>
|
<h2>Research</h2>
|
||||||
<div style={{display:'grid', gridColumnGap: 20, gridTemplateColumns: 'repeat(auto-fit, minmax(240px, 1fr))'}}>
|
<StyledResearch>
|
||||||
<div>
|
<div>
|
||||||
<h3 style={{marginTop: 0}}>Projects</h3>
|
<h3>Projects</h3>
|
||||||
<p>I have been involved in a number of research projects.</p>
|
<p>I have been involved in a number of research projects.</p>
|
||||||
|
|
||||||
<h4>NIS/DAIS-ITA project</h4>
|
<h4>NIS/DAIS-ITA project</h4>
|
||||||
@ -123,20 +145,19 @@ const ProjectsPage = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h3 style={{marginTop: 0}}>Publications</h3>
|
<h3>Publications</h3>
|
||||||
{outputs.reverse().map((o, i) =>
|
{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>
|
<div>
|
||||||
<h4 style={{marginTop:0}}><a href={o.url} target='_blank' rel='noopener noreferrer'>{o.name}</a></h4>
|
<PublicationTitle><a href={o.url} target='_blank' rel='noopener noreferrer'>{o.name}</a></PublicationTitle>
|
||||||
<p style={{marginBottom:0, fontSize:15}}>{o.authors}</p>
|
<PublicationAuthors>{o.authors}</PublicationAuthors>
|
||||||
<p style={{fontSize: 13}}>{o.journal}, {o.date}</p>
|
<PublicationAttribution>{o.journal}, {o.date}</PublicationAttribution>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</StyledPublication>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</StyledResearch>
|
||||||
</Layout>
|
</Layout>
|
||||||
)
|
);
|
||||||
}
|
|
||||||
|
|
||||||
export default ProjectsPage
|
export default ProjectsPage
|
||||||
|
@ -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>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 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>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>
|
<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>
|
<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
6
src/store.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import create from 'zustand'
|
||||||
|
|
||||||
|
export default create(set => ({
|
||||||
|
theme: 'light',
|
||||||
|
setTheme: theme => set({ theme }),
|
||||||
|
}));
|
@ -8,7 +8,8 @@ import { config } from "@fortawesome/fontawesome-svg-core"
|
|||||||
import "@fortawesome/fontawesome-svg-core/styles.css"
|
import "@fortawesome/fontawesome-svg-core/styles.css"
|
||||||
|
|
||||||
import Layout from '../components/Layout/Layout.js';
|
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';
|
import ExternalLink from '../components/ExternalLink';
|
||||||
|
|
||||||
config.autoAddCss = false;
|
config.autoAddCss = false;
|
||||||
@ -26,35 +27,30 @@ export default class BlogPost extends React.Component {
|
|||||||
|
|
||||||
<h1>{post.frontmatter.title}</h1>
|
<h1>{post.frontmatter.title}</h1>
|
||||||
<h4>{moment(post.frontmatter.date).format('D MMMM YYYY')} <i><small>({moment(post.frontmatter.date).fromNow()})</small></i>
|
<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 =>
|
{post.frontmatter.tags && post.frontmatter.tags.map(tag => <Tag tag={tag} key={tag} />)}
|
||||||
<Link style={{marginLeft: 10}} to={`/tags/${tag}`}><Emoji e="🏷️" /> #{tag}</Link>
|
|
||||||
)}
|
|
||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
{post.frontmatter.tags && post.frontmatter.tags.indexOf('100daystooffload') > -1 &&
|
{post.frontmatter.tags && post.frontmatter.tags.indexOf('100daystooffload') > -1 &&
|
||||||
<div style={{background: 'beige', padding: 4, borderRadius: 3, margin: '20px 0px'}}>
|
<Alert colour='green' emoji='💯' title='100 Days to Offload'>
|
||||||
<h3><Emoji e='💯' /> 100 Days to Offload</h3>
|
|
||||||
<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>.
|
<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>
|
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>
|
<p><Link to='/tags/100daystooffload'>View other articles in this series</Link></p>
|
||||||
</div>
|
</Alert>
|
||||||
}
|
}
|
||||||
|
|
||||||
{post.frontmatter.tags && post.frontmatter.tags.indexOf('book') > -1 &&
|
{post.frontmatter.tags && post.frontmatter.tags.indexOf('book') > -1 &&
|
||||||
<div style={{background: 'linen', padding: 4, borderRadius: 3, margin: '20px 0px'}}>
|
<Alert colour='orange' emoji='📚' title='This article is about a book'>
|
||||||
<h3><Emoji e='📚' /> This article is about a book</h3>
|
|
||||||
<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>
|
<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')) &&
|
{moment(post.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 post'>
|
||||||
<h3><Emoji e='🕰️' /> This is an old post</h3>
|
|
||||||
<p>Please note that this article was posted quite a while ago and may now be out-of-date or inaccurate.</p>
|
<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 }} />
|
<div dangerouslySetInnerHTML={{ __html: post.html }} />
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
@ -66,17 +62,15 @@ export default class BlogPost extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{post.frontmatter.tags && post.frontmatter.tags.indexOf('book') > -1 &&
|
{post.frontmatter.tags && post.frontmatter.tags.indexOf('book') > -1 &&
|
||||||
<div style={{background: 'linen', padding: 4, borderRadius: 3, margin: '20px 0px'}}>
|
<Alert colour='orange' emoji='📚' title='I try and read a wide variety of books'>
|
||||||
<h3><Emoji e='📚' /> I try and read a wide variety of books</h3>
|
|
||||||
<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>
|
<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}}>
|
<Alert colour='blue' emoji='📲' title='Enjoyed this article? Subscribe to updates!'>
|
||||||
<h3><Emoji e='📲' /> Enjoyed this article? Subscribe to updates!</h3>
|
|
||||||
<p>If you would like to read more posts like this, then you can subscribe via RSS.</p>
|
<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>
|
<p><FontAwesomeIcon icon={faRss} /> <Link to='/feeds'>Subscribe to an RSS feed</Link></p>
|
||||||
</div>
|
</Alert>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,8 @@ import { faArrowLeft } from '@fortawesome/free-solid-svg-icons'
|
|||||||
|
|
||||||
import Layout from '../components/Layout/Layout.js';
|
import Layout from '../components/Layout/Layout.js';
|
||||||
import Emoji from '../components/Emoji';
|
import Emoji from '../components/Emoji';
|
||||||
|
import Alert from '../components/Alert';
|
||||||
|
import Tag from '../components/Tag';
|
||||||
|
|
||||||
export default class Note extends React.Component {
|
export default class Note extends React.Component {
|
||||||
|
|
||||||
@ -21,19 +23,17 @@ export default class Note extends React.Component {
|
|||||||
|
|
||||||
<h1>{note.frontmatter.title}</h1>
|
<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>
|
<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 =>
|
{note.frontmatter.tags && note.frontmatter.tags.map(tag => <Tag key={tag} tag={tag} />)}
|
||||||
<Link style={{marginLeft: 10}} to={`/tags/${tag}`}><Emoji e="🏷️" /> #{tag}</Link>
|
|
||||||
)}
|
|
||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
{moment(note.frontmatter.date).isBefore(moment().subtract(12, 'months')) &&
|
{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>
|
<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>
|
<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 }} />
|
<div dangerouslySetInnerHTML={{ __html: note.html }} />
|
||||||
</article>
|
</article>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
@ -12378,6 +12378,11 @@ yurnalist@^2.1.0:
|
|||||||
read "^1.0.7"
|
read "^1.0.7"
|
||||||
strip-ansi "^5.2.0"
|
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:
|
zwitch@^1.0.0:
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920"
|
resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920"
|
||||||
|
Loading…
Reference in New Issue
Block a user