Compare commits
10 Commits
6dc47f6672
...
92e70229fe
| Author | SHA1 | Date | |
|---|---|---|---|
| 92e70229fe | |||
| b749e58f62 | |||
| b1b2be6080 | |||
| 5ebe87dbc2 | |||
| a8a36b693e | |||
| 60eefb4b27 | |||
| 8f5dae4bdc | |||
| 89a511efc0 | |||
| 504fe745ea | |||
| 762e8a9a2e |
@@ -1,9 +1,11 @@
|
||||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import localForage from 'localforage';
|
||||
import { sourceLink, infoLine, ToggleDot } from './utils.js';
|
||||
|
||||
const VOID_ELEMENTS = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr'];
|
||||
|
||||
function Article({ cache }) {
|
||||
const { id } = useParams();
|
||||
|
||||
@@ -58,17 +60,92 @@ function Article({ cache }) {
|
||||
};
|
||||
|
||||
const isCodeBlock = (v) => {
|
||||
return v.localName === 'pre' || v.localName === 'code' || (v.children?.length === 1 && v.children[0].localName === 'code');
|
||||
if (v.localName === 'pre') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (v.localName === 'code') {
|
||||
if (v.closest('p')) {
|
||||
return false;
|
||||
}
|
||||
const parent = v.parentElement;
|
||||
if (parent) {
|
||||
const nonWhitespaceChildren = Array.from(parent.childNodes).filter(n => {
|
||||
return n.nodeType !== Node.TEXT_NODE || n.textContent.trim() !== '';
|
||||
});
|
||||
if (nonWhitespaceChildren.length === 1 && nonWhitespaceChildren[0] === v) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const nodes = useMemo(() => {
|
||||
if (story && story.text) {
|
||||
const renderNodes = (nodes, keyPrefix = '') => {
|
||||
return Array.from(nodes).map((v, k) => {
|
||||
const key = `${keyPrefix}${k}`;
|
||||
if (pConv.includes(key)) {
|
||||
return (
|
||||
<React.Fragment key={key}>
|
||||
{v.textContent.split('\n\n').map((x, i) =>
|
||||
<p key={i}>{x}</p>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
if (v.nodeName === '#text') {
|
||||
// Only wrap top-level text nodes in <p>
|
||||
if (keyPrefix === '' && v.data.trim() !== '') {
|
||||
return <p key={key}>{v.data}</p>;
|
||||
}
|
||||
return v.data;
|
||||
}
|
||||
|
||||
if (v.nodeType !== Node.ELEMENT_NODE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const Tag = v.localName;
|
||||
if (isCodeBlock(v)) {
|
||||
return (
|
||||
<React.Fragment key={key}>
|
||||
<Tag dangerouslySetInnerHTML={{ __html: v.innerHTML }} />
|
||||
<button onClick={() => pConvert(key)}>Convert Code to Paragraph</button>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
const props = { key: key };
|
||||
if (v.hasAttributes()) {
|
||||
for (const attr of v.attributes) {
|
||||
const name = attr.name === 'class' ? 'className' : attr.name;
|
||||
props[name] = attr.value;
|
||||
}
|
||||
}
|
||||
|
||||
if (VOID_ELEMENTS.includes(Tag)) {
|
||||
return <Tag {...props} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Tag {...props}>
|
||||
{renderNodes(v.childNodes, `${key}-`)}
|
||||
</Tag>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const nodes = (s) => {
|
||||
if (s && s.text) {
|
||||
let div = document.createElement('div');
|
||||
div.innerHTML = story.text;
|
||||
div.innerHTML = s.text;
|
||||
return div.childNodes;
|
||||
}
|
||||
return null;
|
||||
}, [story]);
|
||||
};
|
||||
|
||||
const storyNodes = nodes(story);
|
||||
|
||||
return (
|
||||
<div className='article-container'>
|
||||
@@ -94,25 +171,9 @@ function Article({ cache }) {
|
||||
|
||||
{infoLine(story)}
|
||||
|
||||
{nodes ?
|
||||
{storyNodes ?
|
||||
<div className='story-text'>
|
||||
{Object.entries(nodes).map(([k, v]) =>
|
||||
pConv.includes(k) ?
|
||||
<React.Fragment key={k}>
|
||||
{v.innerHTML.split('\n\n').map((x, i) =>
|
||||
<p key={i} dangerouslySetInnerHTML={{ __html: x }} />
|
||||
)}
|
||||
</React.Fragment>
|
||||
:
|
||||
(v.nodeName === '#text' ?
|
||||
<p key={k}>{v.data}</p>
|
||||
:
|
||||
<React.Fragment key={k}>
|
||||
<v.localName dangerouslySetInnerHTML={v.innerHTML ? { __html: v.innerHTML } : null} />
|
||||
{isCodeBlock(v) && <button onClick={() => pConvert(k)}>Convert Code to Paragraph</button>}
|
||||
</React.Fragment>
|
||||
)
|
||||
)}
|
||||
{renderNodes(storyNodes)}
|
||||
</div>
|
||||
:
|
||||
<p>Problem getting article :(</p>
|
||||
|
||||
Reference in New Issue
Block a user