Skip to content

Commit

Permalink
Add ability to toggle readermode using the readerMode prop
Browse files Browse the repository at this point in the history
  • Loading branch information
j-mendez committed Jun 10, 2019
1 parent c84c26a commit 17013c3
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 119 deletions.
Binary file removed .DS_Store
Binary file not shown.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
node_modules
.DS_Store
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ How to use:

- Just pass in a Url into the component and your good to go.

## Installation instructions
## Installation Instructions

Follow the instructions installing react-native-webview if not already installed [Installation Instructions](https://github.com/react-native-community/react-native-webview/blob/master/docs/Getting-Started.md) or simply.
Follow the instructions installing react-native-webview if not already installed [Getting Started](https://github.com/react-native-community/react-native-webview/blob/master/docs/Getting-Started.md) or simply.

```bash
npm install react-native-webview
Expand Down
75 changes: 32 additions & 43 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,17 @@
import React, { PureComponent } from "react";
import { ActivityIndicator, StyleSheet, View } from "react-native";
import { WebView } from "react-native-webview";
import Readability from "./util/readability";
import cleanHtmlTemplate from "./util/cleanHtmlTemplate";
import cleanHtmlCss from "./util/cleanHtmlCss";

const css = `
body {
color: #2a2a2a;
font-family: sans-serif, normal, San Francisco;
}
img, figure {
display: none;
}
h1 {
border-bottom-width: 1px;
border-color: #ccc;
padding-bottom: 3px;
border-bottom-style:solid;
font-size: 1.6em;
font-weight: bold;
margin-bottom: 5px;
letter-spacing: .05em;
}
p {
letter-spacing: .03em;
}
`;
import {
cleanHtml,
cleanHtmlCss,
cleanHtmlTemplate,
defaultHtmlCss
} from "./util";

export default class ReadabilityWebView extends PureComponent {
static defaultProps = {
url: "",
htmlCss: css,
htmlCss: defaultHtmlCss,
title: "",
onError: null,
readerMode: true,
Expand All @@ -43,31 +23,18 @@ export default class ReadabilityWebView extends PureComponent {
readabilityArticle: null
};

refreshCSS = () => {
if (this.state.readabilityArticle) {
this.setState({
cleanHtmlSource: cleanHtmlTemplate(
this.props.htmlCss,
props.title || this.state.readabilityArticle.title,
this.state.readabilityArticle.content
)
});
}
};

async componentDidMount() {
const { url, htmlCss, title, readerMode } = this.props;

if (!readerMode) {
this.setState({ cleanHtmlSource: false });
this.toggleReaderMode();
return;
}
try {
const response = await fetch(url);
const html = await response.text();
let cleanHtml = "";

const readabilityArticle = await Readability.cleanHtml(html, url);
const readabilityArticle = await cleanHtml(html, url);

this.setState({
cleanHtmlSource: !readabilityArticle
Expand All @@ -88,10 +55,32 @@ export default class ReadabilityWebView extends PureComponent {

componentDidUpdate(prevProps) {
if (this.props.htmlCss !== prevProps.htmlCss) {
this.refreshCSS();
this.refreshHtml();
} else if (this.props.readerMode !== prevProps.readerMode) {
this.toggleReaderMode(this.props.readerMode);
}
}

refreshHtml = () => {
if (this.state.readabilityArticle) {
this.setState({
cleanHtmlSource: cleanHtmlTemplate(
this.props.htmlCss,
props.title || this.state.readabilityArticle.title,
this.state.readabilityArticle.content
)
});
}
};

toggleReaderMode = (cleanHtmlSource = false) => {
if (!cleanHtmlSource) {
this.setState({ cleanHtmlSource });
} else {
this.refreshHtml();
}
};

render() {
return (
<View style={styles.flex}>
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
{
"name": "react-native-webview-readability",
"version": "0.1.2",
"version": "0.1.5",
"description": "React Native package that turns a Webview into a Safari like Readability View for android and iOS",
"keywords": [
"react-native",
"ios",
"android",
"webview",
"readability"
"readability",
"react-native-readability"
],
"repository": {
"type": "git",
Expand Down
45 changes: 45 additions & 0 deletions util/clean-html-css.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
export default `
h1, h2 {
font-weight: bold;
margin-bottom: 5px;
letter-spacing: .05em
}
h1 {
font-size: 1.5em;
}
h2 {
font-size: 1em;
}
h3 {
font-size: 0.7em;
}
h4 {
font-size: 0.5em;
}
p {
letter-spacing: .03em;
}
`;

export const defaultHtmlCss = `
body {
color: #2a2a2a;
font-family: sans-serif, normal, San Francisco;
}
img, figure {
display: none;
}
h1 {
border-bottom-width: 1px;
border-color: #ccc;
padding-bottom: 3px;
border-bottom-style:solid;
font-size: 1.6em;
font-weight: bold;
margin-bottom: 5px;
letter-spacing: .05em;
}
p {
letter-spacing: .03em;
}
`;
File renamed without changes.
100 changes: 50 additions & 50 deletions util/readability.js → util/clean-html.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,54 @@ const XMLSerializer = require("xmldom-silent").XMLSerializer;
const UrlParser = require("url-parse");
const sanitizeHtml = require("sanitize-html");

exports.cleanHtml = function(html, sourceUrl) {
function convertHtmlToXhtml(html) {
const xmlSerializer = new XMLSerializer();
const xhtmlDocument = new DOMParser({
errorHandler: function(level, msg) {
if (level === "error") {
throw new Error("Unable to convert HTML to XHTML: " + msg);
}
}
}).parseFromString(html, "text/html");

return xmlSerializer.serializeToString(xhtmlDocument);
}

function createJsDomDocument(xhtml) {
const jsDomParser = new JSDOMParser();
const document = jsDomParser.parse(xhtml.trim());

if (jsDomParser.errorState) {
throw new Error(
"Unable to parse XHTML into JsDom" + jsDomParser.errorState
);
}

return document;
}

function createReadabilityUrl(sourceUrl) {
const sourceUrlParsed = new UrlParser(sourceUrl);

if (!sourceUrlParsed || sourceUrlParsed.host.length === 0) {
throw new Error("Invalid or no source url provided");
}

return {
spec: sourceUrlParsed.href,
host: sourceUrlParsed.host,
scheme: sourceUrlParsed.protocol.slice(0, -1),
prePath: `${sourceUrlParsed.protocol}//${sourceUrlParsed.host}`,
pathBase: `${sourceUrlParsed.protocol}//${
sourceUrlParsed.host
}${sourceUrlParsed.pathname.substring(
0,
sourceUrlParsed.pathname.lastIndexOf("/") + 1
)}`
};
}

export default function(html, sourceUrl) {
html = sanitizeHtml(html, {
allowedTags: [
"html",
Expand All @@ -32,11 +79,11 @@ exports.cleanHtml = function(html, sourceUrl) {
]
});
return new Promise(resolve => {
if (!html || html.length === 0) {
if (!html) {
throw new Error("Invalid or no html provided");
}

if (!sourceUrl || sourceUrl.length === 0) {
if (!sourceUrl) {
throw new Error("Invalid or no source url provided");
}

Expand All @@ -56,51 +103,4 @@ exports.cleanHtml = function(html, sourceUrl) {

resolve(cleanedHtml);
});
};

function convertHtmlToXhtml(html) {
const xmlSerializer = new XMLSerializer();
const xhtmlDocument = new DOMParser({
errorHandler: function(level, msg) {
if (level === "error") {
throw new Error("Unable to convert HTML to XHTML: " + msg);
}
}
}).parseFromString(html, "text/html");

return xmlSerializer.serializeToString(xhtmlDocument);
}

function createJsDomDocument(xhtml) {
const jsDomParser = new JSDOMParser();
const document = jsDomParser.parse(xhtml.trim());

if (jsDomParser.errorState) {
throw new Error(
"Unable to parse XHTML into JsDom" + jsDomParser.errorState
);
}

return document;
}

function createReadabilityUrl(sourceUrl) {
const sourceUrlParsed = new UrlParser(sourceUrl);

if (!sourceUrlParsed || sourceUrlParsed.host.length === 0) {
throw new Error("Invalid or no source url provided");
}

return {
spec: sourceUrlParsed.href,
host: sourceUrlParsed.host,
scheme: sourceUrlParsed.protocol.slice(0, -1),
prePath: `${sourceUrlParsed.protocol}//${sourceUrlParsed.host}`,
pathBase: `${sourceUrlParsed.protocol}//${
sourceUrlParsed.host
}${sourceUrlParsed.pathname.substring(
0,
sourceUrlParsed.pathname.lastIndexOf("/") + 1
)}`
};
}
22 changes: 0 additions & 22 deletions util/cleanHtmlCss.js

This file was deleted.

5 changes: 5 additions & 0 deletions util/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import cleanHtmlCss, { htmlCss } from "./clean-html-css";
import cleanHtml from "./clean-html";
import cleanHtmlTemplate from "./clean-html-template";

export { cleanHtml, cleanHtmlCss, cleanHtmlTemplate, htmlCss };

0 comments on commit 17013c3

Please sign in to comment.