-
-
{title}
-
-
-
+
)
}
@@ -26,6 +22,8 @@ class PageTemplate extends React.Component {
export default PageTemplate
+export const Head = createHead()
+
export const pageQuery = graphql`
query PageBySlug($slug: String!) {
markdownRemark(fields: { slug: { eq: $slug } }) {
diff --git a/src/templates/PostCategoryTemplate/createPages.js b/src/templates/PostCategoryTemplate/createPages.js
index 200a442..7d58990 100644
--- a/src/templates/PostCategoryTemplate/createPages.js
+++ b/src/templates/PostCategoryTemplate/createPages.js
@@ -2,6 +2,7 @@ const _ = require('lodash')
const path = require('path')
const { SidebarLinks, CategoryLinks } = require('../../consts/menuLinks')
+const headContext = require('../../components/Head/headContext')
module.exports = (graphql, createPage) => (resolve, reject) => {
graphql(`
@@ -29,8 +30,11 @@ module.exports = (graphql, createPage) => (resolve, reject) => {
context: {
languageId,
categoryId: categoryLink.id,
- categoryLabel: categoryLink.label,
sidebarLinkId: SidebarLinks[languageId].Blog.id,
+ ...headContext({
+ languageId,
+ subtitle: categoryLink.label,
+ })
},
})
}
diff --git a/src/templates/PostCategoryTemplate/index.jsx b/src/templates/PostCategoryTemplate/index.jsx
index f890b79..ab0dfe5 100644
--- a/src/templates/PostCategoryTemplate/index.jsx
+++ b/src/templates/PostCategoryTemplate/index.jsx
@@ -1,19 +1,20 @@
import React from 'react'
import { graphql } from 'gatsby'
import PostList from '../../components/PostList'
+import createHead from '../../components/Head'
class PostCategoryTemplate extends React.Component {
render() {
- const { categoryLabel } = this.props.pageContext
-
return (
-
+
)
}
}
-
+
export default PostCategoryTemplate
+export const Head = createHead()
+
export const pageQuery = graphql`
query PostCategoryTemplateQuery($categoryId: String) {
site {
@@ -23,22 +24,14 @@ export const pageQuery = graphql`
}
allMarkdownRemark(
limit: 1000
- filter: {
- frontmatter: {
- category: { eq: $categoryId }
- layout: { eq: "post" }
- }
- }
- sort: { order: DESC, fields: [frontmatter___date] }
+ filter: {frontmatter: {category: {eq: $categoryId}, layout: {eq: "post"}}}
+ sort: {frontmatter: {date: DESC}}
) {
edges {
node {
+ rawMarkdownBody
fields {
slug
- readingTime {
- text
- minutes
- }
}
frontmatter {
demo
diff --git a/src/templates/PostSeriesTemplate/createPages.js b/src/templates/PostSeriesTemplate/createPages.js
index 959cbfa..13935cc 100644
--- a/src/templates/PostSeriesTemplate/createPages.js
+++ b/src/templates/PostSeriesTemplate/createPages.js
@@ -4,6 +4,7 @@ const path = require('path')
const { findById } = require('../../consts/languages')
const { SidebarLinks, seriesLink } = require('../../consts/menuLinks')
const { parseDemoType } = require('../../consts/demo')
+const headContext = require('../../components/Head/headContext')
module.exports = (graphql, createPage) => (resolve, reject) => {
graphql(`
@@ -13,10 +14,8 @@ module.exports = (graphql, createPage) => (resolve, reject) => {
demo
}
}
- allMarkdownRemark(
- filter: { frontmatter: { layout: { eq: "post" } } }
- ) {
- group(field: frontmatter___series___path) {
+ allMarkdownRemark(filter: {frontmatter: {layout: {eq: "post"}}}) {
+ group(field: {frontmatter: {series: {path: SELECT}}}) {
edges {
node {
frontmatter {
@@ -56,16 +55,20 @@ module.exports = (graphql, createPage) => (resolve, reject) => {
createPage({
path: seriesLink(series.path, findById(series.languageId)),
component: path.join(__dirname, 'index.jsx'),
- context: {
+ context: {
seriesName: series.name,
seriesPath: series.path,
languageId: series.languageId,
categoryId: series.categoryId,
sidebarLinkId: SidebarLinks[series.languageId].Blog.id,
+ ...headContext({
+ languageId: series.languageId,
+ subtitle: series.name
+ })
},
})
}
})
resolve()
})
-}
\ No newline at end of file
+}
diff --git a/src/templates/PostSeriesTemplate/index.jsx b/src/templates/PostSeriesTemplate/index.jsx
index 2b0cf70..9bf0537 100644
--- a/src/templates/PostSeriesTemplate/index.jsx
+++ b/src/templates/PostSeriesTemplate/index.jsx
@@ -2,6 +2,7 @@ import React from 'react'
import { graphql } from 'gatsby'
import PostList from '../../components/PostList'
import './style.scss'
+import createHead from '../../components/Head'
class PostSeriesTemplate extends React.Component {
render() {
@@ -14,45 +15,42 @@ class PostSeriesTemplate extends React.Component {
)
return (
-
+
{title}
)
}
}
-
+
export default PostSeriesTemplate
+export const Head = createHead()
+
export const pageQuery = graphql`
query PostSeriesTemplateQuery($seriesPath: String) {
- site {
- siteMetadata {
- demo
- }
+ site {
+ siteMetadata {
+ demo
}
- allMarkdownRemark(
- filter: { frontmatter: {
- series: { path: { eq: $seriesPath }}
- }}
- sort: { order: ASC, fields: [frontmatter___series___order] }
- ) {
- edges {
- node {
- fields {
- slug
- readingTime {
- text
- minutes
- }
- }
- frontmatter {
- demo
- title
- date
- tags
- }
+ }
+ allMarkdownRemark(
+ filter: {frontmatter: {series: {path: {eq: $seriesPath}}}}
+ sort: {frontmatter: {series: {order: ASC}}}
+ ) {
+ edges {
+ node {
+ rawMarkdownBody
+ fields {
+ slug
+ }
+ frontmatter {
+ demo
+ title
+ date
+ tags
}
}
}
+ }
}
-`
\ No newline at end of file
+`
diff --git a/src/templates/PostTemplate/createPages.js b/src/templates/PostTemplate/createPages.js
index 7759b52..425326e 100644
--- a/src/templates/PostTemplate/createPages.js
+++ b/src/templates/PostTemplate/createPages.js
@@ -3,6 +3,7 @@ const path = require('path')
const { SidebarLinks } = require('../../consts/menuLinks')
const { parseDemoType } = require('../../consts/demo')
+const headContext = require('../../components/Head/headContext')
module.exports = (graphql, createPage) => (resolve, reject) => {
graphql(`
@@ -22,9 +23,16 @@ module.exports = (graphql, createPage) => (resolve, reject) => {
slug
}
frontmatter {
+ title
+ description
demo
language
category
+ featuredImage {
+ childImageSharp {
+ gatsbyImageData(quality: 100)
+ }
+ }
}
}
}
@@ -40,22 +48,35 @@ module.exports = (graphql, createPage) => (resolve, reject) => {
_.each(result.data.allMarkdownRemark.edges, edge => {
const { slug } = edge.node.fields
- const { demo, language: languageId , category: categoryId } = edge.node.frontmatter
+ const {
+ demo,
+ language: languageId,
+ category: categoryId,
+ title,
+ description,
+ featuredImage,
+ } = edge.node.frontmatter
const sidebarLinkId = SidebarLinks[languageId].Blog.id
if (parseDemoType(demo).matchDemoMode(demoMode)) {
createPage({
path: slug,
component: path.join(__dirname, 'index.jsx'),
- context: {
+ context: {
slug,
languageId,
categoryId,
sidebarLinkId,
+ ...headContext({
+ languageId,
+ subtitle: title,
+ description,
+ featuredImage,
+ }),
},
})
}
})
resolve()
})
-}
\ No newline at end of file
+}
diff --git a/src/templates/PostTemplate/index.jsx b/src/templates/PostTemplate/index.jsx
index 896254d..fae4fa2 100644
--- a/src/templates/PostTemplate/index.jsx
+++ b/src/templates/PostTemplate/index.jsx
@@ -2,48 +2,47 @@ import React from 'react'
import { graphql } from 'gatsby'
import moment from 'moment'
import 'moment/locale/he'
-import LittleFoot from '../../components/Littlefoot'
import Utterances from '../../components/Utterances'
-import PageHelmet from '../../components/PageHelmet'
import SharePanel from '../../components/SharePanel'
import MobileShareButton from '../../components/MobileShareButton'
import PostSeriesBox from '../../components/PostSeriesBox'
import Context from '../../components/Context'
import './style.scss'
+import createHead from '../../components/Head'
+import { parseReadingTimeText } from '../../utils/readtimeTime'
class PostTemplate extends React.Component {
render() {
const { utterances } = this.props.data.site.siteMetadata
const post = this.props.data.markdownRemark
const { title, tags, series } = post.frontmatter
- const readingTime = post.fields.readingTime
const url = this.props.location.href
const language = this.context.layout.language.get()
- let featuredImage = post.frontmatter.featuredImage
+ const readingTimeText = parseReadingTimeText(this.props.data.markdownRemark.rawMarkdownBody, language)
const titleBlock = (
- {post.frontmatter.title}
+ {post.frontmatter.title}
)
const dateBlock = (
-
+
{moment(post.frontmatter.date).locale(language.locale).format('MMMM D, YYYY')}
)
-
+
const readTimeBlock = (
- {readingTime.text}
+ {readingTimeText}
)
-
+
const tagsBlock = (
-
-
+
+
{tags &&
tags.map(tag => (
- -
+
-
{tag}
))}
@@ -52,19 +51,19 @@ class PostTemplate extends React.Component {
)
const sharePanel = (
-
-
+
+
)
const header = (
-
+
{titleBlock}
-
+
{dateBlock}
-
·
+
·
{readTimeBlock}
-
+
{tagsBlock}
{sharePanel}
@@ -75,29 +74,26 @@ class PostTemplate extends React.Component {
)
const body = (
-
-
-
+
)
- const mobileShare =
+ const mobileShare =
const commentsBlock = (
-
+
)
const footer = (
-
+
{commentsBlock}
)
return (
-
-
+
{header}
{mobileShare}
{body}
@@ -111,6 +107,8 @@ PostTemplate.contextType = Context
export default PostTemplate
+export const Head = createHead()
+
export const pageQuery = graphql`
query PostBySlug($slug: String!) {
site {
@@ -121,12 +119,9 @@ export const pageQuery = graphql`
markdownRemark(fields: { slug: { eq: $slug } }) {
id
html
+ rawMarkdownBody
fields {
slug
- readingTime {
- text
- minutes
- }
}
frontmatter {
title
@@ -138,13 +133,6 @@ export const pageQuery = graphql`
path
order
}
- featuredImage {
- childImageSharp {
- fluid(quality: 100) {
- src
- }
- }
- }
}
}
}
diff --git a/src/templates/PostTemplate/style.scss b/src/templates/PostTemplate/style.scss
index a765824..ef16a75 100644
--- a/src/templates/PostTemplate/style.scss
+++ b/src/templates/PostTemplate/style.scss
@@ -68,16 +68,27 @@
}
& figure {
margin-bottom: 1rem;
- & blockquote {
- font-style: italic;
- text-align: center;
+ }
+ & blockquote {
+ font-style: italic;
+ margin-top: 0;
+ padding-top: 1rem;
+ & p {
+ @include font-size(1.1);
margin-top: 0;
- padding-top: 1rem;
- & p {
- @include font-size(1.75);
- margin-top: 0;
- margin-bottom: 1rem;
- line-height: 2.5rem;
+ margin-bottom: 1rem;
+ line-height: 2rem;
+ }
+ @include ltr {
+ text-align: left;
+ & cite {
+ text-align: left;
+ }
+ }
+ @include rtl {
+ text-align: right;
+ & cite {
+ text-align: right;
}
}
}
diff --git a/src/utils/deviceDetection.js b/src/utils/deviceDetection.js
new file mode 100644
index 0000000..6983057
--- /dev/null
+++ b/src/utils/deviceDetection.js
@@ -0,0 +1,13 @@
+/**
+ * @see https://stackoverflow.com/a/11381730
+ */
+export function isMobile() {
+ let check = false;
+ // eslint-disable-next-line no-useless-escape
+ (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera);
+ return check;
+}
+
+export function hasShareFeature() {
+ return typeof navigator !== 'undefined' && navigator.share;
+}
diff --git a/src/utils/readtimeTime.js b/src/utils/readtimeTime.js
new file mode 100644
index 0000000..1b61ab3
--- /dev/null
+++ b/src/utils/readtimeTime.js
@@ -0,0 +1,21 @@
+import { Languages } from '../consts/languages'
+import readingTime from 'reading-time'
+
+export function parseReadingTimeText(text, lang) {
+ const { text: readingText, minutes: rawMinutes } = readingTime(text)
+
+ switch (lang) {
+ case Languages.English:
+ return readingText
+ case Languages.Hebrew:
+ const minutes = Math.round(rawMinutes)
+ if (minutes < 2) {
+ return 'דקת קריאה אחת'
+ } else {
+ return `${minutes} דקות קריאה`
+ }
+ default:
+ console.error(`Reading time: unrecognized language '${lang}'`)
+ return ''
+ }
+}
diff --git a/src/utils/useRefState.js b/src/utils/useRefState.js
new file mode 100644
index 0000000..225dcdb
--- /dev/null
+++ b/src/utils/useRefState.js
@@ -0,0 +1,17 @@
+import { useRef, useState } from 'react'
+
+export function useRefState(initialValue) {
+ const [_state, _setState] = useState(initialValue)
+ const ref = useRef(_state)
+
+ function setState(value) {
+ ref.current = value
+ _setState(value)
+ }
+
+ function getState() {
+ return ref.current
+ }
+
+ return [getState, setState]
+}