From 14f2dc6f563004f3d1aa57d994733f396dd61045 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Isabella=20Sko=C5=99epov=C3=A1?= <isabella@skorepova.info>
Date: Wed, 8 Jan 2020 12:33:40 +0100
Subject: [PATCH] Implement article

---
 theme-source/partials/base.js     |   5 +-
 theme-source/partials/list.js     |  81 +------------------
 theme-source/templates/article.js | 124 ++++++++++++++++++++++++++++++
 theme-source/utils.js             |  81 +++++++++++++++++++
 4 files changed, 210 insertions(+), 81 deletions(-)
 create mode 100644 theme-source/templates/article.js
 create mode 100644 theme-source/utils.js

diff --git a/theme-source/partials/base.js b/theme-source/partials/base.js
index 2826a8be..35c75ff0 100644
--- a/theme-source/partials/base.js
+++ b/theme-source/partials/base.js
@@ -3,17 +3,20 @@ import { SymbolSearch, SymbolMenu, SymbolFacebook } from './svgs'
 import { Search } from './search'
 
 export function Base({
+  prehead = null,
   head = null,
   metadata = {},
   canonicalURL,
   config,
   children,
   footerlink = null,
+  bodyClassName,
 }) {
   return (
     <html lang="en">
       <head>
         <meta httpEquiv="Content-Type" content="text/html; charset=UTF-8" />
+        {prehead}
         <meta
           name="viewport"
           content="width=device-width, initial-scale=1, maximum-scale=1.0"
@@ -157,7 +160,7 @@ export function Base({
         ) : null}
         {head}
       </head>
-      <body>
+      <body className={bodyClassName}>
         {/* % block bodyattr %}{% endblock % */}
         <div id="all">
           {/* Top nav */}
diff --git a/theme-source/partials/list.js b/theme-source/partials/list.js
index 8c250f0b..81c48901 100644
--- a/theme-source/partials/list.js
+++ b/theme-source/partials/list.js
@@ -2,86 +2,7 @@ import React from 'react'
 import moment from 'moment'
 import { Paginator } from './paginator'
 import { SymbolBackward, SymbolForward } from './svgs'
-
-function normalize(value, defaultValue) {
-  if (value === null || value === undefined || value === false) {
-    return defaultValue
-  }
-  return value
-}
-
-function truncate(input_, length_, killwords, end) {
-  let input = normalize(input_, '')
-  const length = length_ || 255
-
-  if (input.length <= length) {
-    return input
-  }
-
-  if (killwords) {
-    input = input.substring(0, length)
-  } else {
-    let idx = input.lastIndexOf(' ', length)
-    if (idx === -1) {
-      idx = length
-    }
-
-    input = input.substring(0, idx)
-  }
-
-  input += end !== undefined && end !== null ? end : '...'
-  return input
-}
-
-function trim(str) {
-  return str.replace(/^\s*|\s*$/g, '')
-}
-
-function striptags(input_, preserveLinebreaks) {
-  const input = normalize(input_, '')
-  const tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>|<!--[\s\S]*?-->/gi
-  const trimmedInput = trim(input.replace(tags, ''))
-  let res = ''
-  if (preserveLinebreaks) {
-    res = trimmedInput
-      .replace(/^ +| +$/gm, '') // remove leading and trailing spaces
-      .replace(/ +/g, ' ') // squash adjacent spaces
-      .replace(/(\r\n)/g, '\n') // normalize linebreaks (CRLF -> LF)
-      .replace(/\n\n\n+/g, '\n\n') // squash abnormal adjacent linebreaks
-  } else {
-    res = trimmedInput.replace(/\s+/gi, ' ')
-  }
-  return res
-}
-
-function dateFilter(date, format) {
-  let result
-  const errs = []
-  const args = []
-  let obj
-  Array.prototype.push.apply(args, arguments)
-  try {
-    obj = moment.utc(date)
-  } catch (err) {
-    errs.push(err)
-  }
-  if (obj) {
-    try {
-      if (obj[format] && typeof obj[format] === 'function') {
-        result = obj[format].apply(obj, args.slice(2))
-      } else {
-        result = obj.format(format)
-      }
-    } catch (err) {
-      errs.push(err)
-    }
-  }
-
-  if (errs.length) {
-    return errs.join('\n')
-  }
-  return result
-}
+import { dateFilter, truncate, striptags } from '../utils'
 
 function relURL(filename, dir_) {
   let dir = dir_
diff --git a/theme-source/templates/article.js b/theme-source/templates/article.js
new file mode 100644
index 00000000..c5b742aa
--- /dev/null
+++ b/theme-source/templates/article.js
@@ -0,0 +1,124 @@
+import React from 'react'
+import url from 'url'
+import { Base } from '../partials/base'
+import { dateFilter, truncate, striptags } from '../utils'
+
+export default function Article({
+  metadata,
+  content,
+  config,
+  file,
+  canonicalURL,
+}) {
+  return (
+    <Base
+      metadata={metadata}
+      config={config}
+      prehead={
+        <>
+          <meta property="og:type" content="article" />
+          <meta
+            property="article:published_time"
+            content={dateFilter(metadata.published)}
+          />
+          {metadata.image && (
+            <meta
+              property="og:image"
+              content={url.resolve(metadata.image, canonicalURL)}
+            />
+          )}
+          <meta
+            property="og:description"
+            content={
+              metadata.perex
+                ? metadata.perex
+                : metadata.perex_e
+                ? truncate(metadata.perex_e)
+                : content
+                ? truncate(striptags(content))
+                : undefined
+            }
+          />
+        </>
+      }
+      head={
+        <>
+          <script
+            type="text/x-mathjax-config"
+            dangerouslySetInnerHTML={{
+              __html: `
+              MathJax.Hub.Config({
+                extensions: ["tex2jax.js"],
+                jax: ["input/TeX", "output/HTML-CSS"],
+                tex2jax: {
+                  inlineMath: [ ['$','$'], ["\\(","\\)"] ],
+                  displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
+                  processEscapes: true
+                },
+                elements: $("#content article"),
+                "HTML-CSS": { availableFonts: ["TeX"] },
+                locale: "cs",
+                menuSettings: {
+                  zoom: "Hover"
+                },
+                MathEvents: {
+                  hover: 0
+                }
+              });`,
+            }}
+          />
+          <script
+            type="text/javascript"
+            async
+            src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_CHTML&"
+          ></script>
+        </>
+      }
+      bodyClassName="type-article"
+    >
+      <div class="section container" id="content">
+        <div class="row">&nbsp;</div>
+        <div class="metadata">
+          <h2 id="title">{metadata.title}</h2>
+          {metadata.nodate ? null : (
+            <a
+              class="author"
+              style={{ color: '#a2a2a2', display: 'block' }}
+              class="black-text"
+              target="_blank"
+              href={`https://git.ok1kvk.cz/ok1kvk.cz/content/tree/master/content/articles/${file
+                .replace('clanek/', '')
+                .replace(/\/$/, '')}.md`}
+            >
+              {metadata.published ? (
+                <>
+                  <span style={{ color: '#626262' }}>
+                    {dateFilter(metadata.published, 'D. M. YYYY')}
+                  </span>
+                  {metadata.author.gender === 'F' ? ' napsala ' : ' napsal '}
+                  <span style={{ color: '#626262' }}>
+                    {metadata.author.name}
+                  </span>
+                </>
+              ) : (
+                <>Článek nebyl publikován</>
+              )}
+            </a>
+          )}
+          <div class="tags">
+            {metadata.tags.map(tag =>
+              tag.url === 'tag/vse' ? null : (
+                <a href={`${config.baseurl}/${tag.url}/1/`}>
+                  <div class="chip">{tag.text}</div>
+                </a>
+              ),
+            )}
+          </div>
+        </div>
+        <article>
+          <p dangerouslySetInnerHTML={{ __html: content }} />
+        </article>
+      </div>
+    </Base>
+  )
+}
diff --git a/theme-source/utils.js b/theme-source/utils.js
new file mode 100644
index 00000000..b9dc5018
--- /dev/null
+++ b/theme-source/utils.js
@@ -0,0 +1,81 @@
+import moment from 'moment'
+
+export function dateFilter(date, format) {
+  let result
+  const errs = []
+  const args = []
+  let obj
+  Array.prototype.push.apply(args, arguments)
+  try {
+    obj = moment.utc(date)
+  } catch (err) {
+    errs.push(err)
+  }
+  if (obj) {
+    try {
+      if (obj[format] && typeof obj[format] === 'function') {
+        result = obj[format].apply(obj, args.slice(2))
+      } else {
+        result = obj.format(format)
+      }
+    } catch (err) {
+      errs.push(err)
+    }
+  }
+
+  if (errs.length) {
+    return errs.join('\n')
+  }
+  return result
+}
+
+function normalize(value, defaultValue) {
+  if (value === null || value === undefined || value === false) {
+    return defaultValue
+  }
+  return value
+}
+
+export function truncate(input_, length_, killwords, end) {
+  let input = normalize(input_, '')
+  const length = length_ || 255
+
+  if (input.length <= length) {
+    return input
+  }
+
+  if (killwords) {
+    input = input.substring(0, length)
+  } else {
+    let idx = input.lastIndexOf(' ', length)
+    if (idx === -1) {
+      idx = length
+    }
+
+    input = input.substring(0, idx)
+  }
+
+  input += end !== undefined && end !== null ? end : '...'
+  return input
+}
+
+function trim(str) {
+  return str.replace(/^\s*|\s*$/g, '')
+}
+
+export function striptags(input_, preserveLinebreaks) {
+  const input = normalize(input_, '')
+  const tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>|<!--[\s\S]*?-->/gi
+  const trimmedInput = trim(input.replace(tags, ''))
+  let res = ''
+  if (preserveLinebreaks) {
+    res = trimmedInput
+      .replace(/^ +| +$/gm, '') // remove leading and trailing spaces
+      .replace(/ +/g, ' ') // squash adjacent spaces
+      .replace(/(\r\n)/g, '\n') // normalize linebreaks (CRLF -> LF)
+      .replace(/\n\n\n+/g, '\n\n') // squash abnormal adjacent linebreaks
+  } else {
+    res = trimmedInput.replace(/\s+/gi, ' ')
+  }
+  return res
+}
-- 
GitLab