Experimenting with the router
52
.eslintrc
|
|
@ -1,52 +0,0 @@
|
|||
{
|
||||
"env": {
|
||||
"browser": true
|
||||
},
|
||||
"extends": "airbnb",
|
||||
"rules": {
|
||||
"arrow-parens": "off",
|
||||
"comma-dangle": [
|
||||
"error",
|
||||
{
|
||||
"arrays": "always-multiline",
|
||||
"exports": "always-multiline",
|
||||
"functions": "ignore",
|
||||
"imports": "always-multiline",
|
||||
"objects": "always-multiline"
|
||||
}
|
||||
],
|
||||
"import/extensions": "off",
|
||||
"import/no-extraneous-dependencies": "off",
|
||||
"import/no-unresolved": "off",
|
||||
"import/prefer-default-export": "off",
|
||||
"jsx-a11y/anchor-has-content": "off",
|
||||
"jsx-a11y/anchor-is-valid": "off",
|
||||
"jsx-quotes": [
|
||||
"error",
|
||||
"prefer-single"
|
||||
],
|
||||
"max-len": "off",
|
||||
"no-param-reassign": "warn",
|
||||
"no-plusplus": [
|
||||
"error",
|
||||
{
|
||||
"allowForLoopAfterthoughts": true
|
||||
}
|
||||
],
|
||||
"no-prototype-builtins": "off",
|
||||
"no-script-url": "off",
|
||||
"no-underscore-dangle": "off",
|
||||
"no-unused-vars": "warn",
|
||||
"react/jsx-filename-extension": "off",
|
||||
"react/no-danger": "off",
|
||||
"react/prop-types": "off",
|
||||
"semi": [
|
||||
"error",
|
||||
"never"
|
||||
],
|
||||
"no-use-before-define": "off",
|
||||
"import/first": "off",
|
||||
"jsx-a11y/click-events-have-key-events": "off",
|
||||
"jsx-a11y/no-static-element-interactions": "off"
|
||||
}
|
||||
}
|
||||
7
.gitignore
vendored
|
|
@ -1,8 +1,7 @@
|
|||
# Autogenerated vscode config
|
||||
.vscode/
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
|
||||
# Generated JS files
|
||||
dist/
|
||||
public/main.js
|
||||
|
||||
*.log
|
||||
11343
package-lock.json
generated
54
package.json
|
|
@ -20,49 +20,23 @@
|
|||
},
|
||||
"homepage": "https://github.com/JubbeArt/removeddit#readme",
|
||||
"scripts": {
|
||||
"start": "webpack-dev-server --config webpack.dev.js",
|
||||
"build": "webpack --config webpack.dev.js",
|
||||
"build-prod": "webpack --config webpack.prod.js",
|
||||
"clean": "rm -r node_modules && rm package-lock.json",
|
||||
"gh-pages": "npm run build-prod && node publish.js"
|
||||
"start": "webpack-dev-server --mode development",
|
||||
"build": "webpack --mode production",
|
||||
"build-sass": "sass --style compressed public/sass/index.sass public/main.css"
|
||||
},
|
||||
"standard": {
|
||||
"globals": ["localStorage", "fetch"]
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "^7.0.0-beta.37",
|
||||
"react": "^16.2.0",
|
||||
"react-dom": "^16.2.0",
|
||||
"react-redux": "^5.0.6",
|
||||
"react-router-dom": "^4.2.2",
|
||||
"redux": "^3.7.2",
|
||||
"snuownd": "git+https://github.com/JordanMilne/snuownd.git",
|
||||
"whatwg-fetch": "^2.0.3"
|
||||
"lit-html": "^0.9.0",
|
||||
"navigo": "^7.1.1",
|
||||
"snudown-js": "^3.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.0.0-beta.37",
|
||||
"@babel/core": "^7.0.0-beta.37",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.0.0-beta.37",
|
||||
"@babel/plugin-transform-async-to-generator": "^7.0.0-beta.37",
|
||||
"@babel/preset-es2015": "^7.0.0-beta.37",
|
||||
"@babel/preset-react": "^7.0.0-beta.37",
|
||||
"babel-eslint": "^8.2.1",
|
||||
"babel-loader": "^8.0.0-beta.0",
|
||||
"clean-webpack-plugin": "^0.1.17",
|
||||
"copy-webpack-plugin": "^4.3.1",
|
||||
"css-loader": "^0.28.8",
|
||||
"eslint": "^4.15.0",
|
||||
"eslint-config-airbnb": "^16.1.0",
|
||||
"eslint-plugin-import": "^2.8.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.0.3",
|
||||
"eslint-plugin-react": "^7.5.1",
|
||||
"extract-text-webpack-plugin": "^3.0.2",
|
||||
"gh-pages": "^1.1.0",
|
||||
"html-loader": "^0.5.4",
|
||||
"html-webpack-plugin": "^2.30.1",
|
||||
"node-sass": "^4.7.2",
|
||||
"redux-logger": "^3.0.6",
|
||||
"sass-loader": "^6.0.6",
|
||||
"uglifyjs-webpack-plugin": "^1.1.6",
|
||||
"webpack": "^3.10.0",
|
||||
"webpack-dev-server": "^2.10.0",
|
||||
"webpack-merge": "^4.1.1"
|
||||
"sass": "^1.1.1",
|
||||
"standard": "^11.0.1",
|
||||
"webpack": "^4.5.0",
|
||||
"webpack-cli": "^2.0.14",
|
||||
"webpack-dev-server": "^3.1.2"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
27
public/index.html
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Removeddit</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta type="description" content="Display removed and deleted comments from Reddit">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<link href="/images/favicon.ico" rel="shotcut icon">
|
||||
<link href="/main.css" rel="stylesheet" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div id="header">
|
||||
<h1>
|
||||
<a href="/" data-navigo>Removeddit</a>
|
||||
</h1>
|
||||
<nav>
|
||||
<a href="/r/all" data-navigo>/r/all</a>
|
||||
<a href="/about/" data-navigo>about</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
<div id="main"></div>
|
||||
<script src="/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
1
public/main.css
Normal file
0
src/App.js
Normal file
|
|
@ -5,11 +5,11 @@ import SortBy from 'components/SortBy'
|
|||
import { connect } from 'react-redux'
|
||||
import {
|
||||
SORT_TOP, SORT_BOTTOM, SORT_NEW, SORT_OLD,
|
||||
SHOW_ALL, SHOW_REMOVED_DELETED, SHOW_REMOVED, SHOW_DELETED,
|
||||
SHOW_ALL, SHOW_REMOVED_DELETED, SHOW_REMOVED, SHOW_DELETED
|
||||
} from 'state'
|
||||
import {
|
||||
topSort, bottomSort, newSort, oldSort,
|
||||
showRemovedAndDeleted, showRemoved, showDeleted,
|
||||
showRemovedAndDeleted, showRemoved, showDeleted
|
||||
} from 'utils'
|
||||
|
||||
const arrayToLookup = (commentList, removed, deleted) => {
|
||||
|
|
@ -134,8 +134,7 @@ const commentSection = (props) => {
|
|||
|
||||
const mapStateToProps = state => ({
|
||||
sort: state.commentSection.sort,
|
||||
show: state.commentSection.show,
|
||||
show: state.commentSection.show
|
||||
})
|
||||
|
||||
|
||||
export default connect(mapStateToProps)(commentSection)
|
||||
22
src/components/SubredditSort.js
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import {html} from 'lit-html'
|
||||
|
||||
export default (props) => {
|
||||
const isSelected = time => props.time === time
|
||||
// {/* <select onchange="Vars.reload(this)"> */}
|
||||
|
||||
return html`
|
||||
<div class="subreddit-info">
|
||||
top post of <a href="${`https://www.reddit.com/r/${props.subreddit}`}">/r/${props.subreddit}</a> from:
|
||||
<select>
|
||||
<option value="hour" selected="${isSelected('hour')}" >past hour</option>
|
||||
<option value="12hour" selected="${isSelected('12hour')}">past 12 hours</option>
|
||||
<option value="day" selected="${isSelected('day')}" >past day</option>
|
||||
<option value="week" selected="${isSelected('week')}" >past week</option>
|
||||
<option value="month" selected="${isSelected('month')}" >past month</option>
|
||||
<option value="6month" selected="${isSelected('6month')}">past 6 months</option>
|
||||
<option value="year" selected="${isSelected('year')}" >past year</option>
|
||||
<option value="all" selected="${isSelected('all')}" >all time</option>
|
||||
</select>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
<!--
|
||||
_-`````-, ,- '- .
|
||||
.' .- - | | - -. `.
|
||||
/.' / `. \
|
||||
:/ : _... ..._ `` :
|
||||
:: : /._ .`:'_.._\. || :
|
||||
:: `._ ./ ,` : \ . _.'' .
|
||||
`:. / | -. \-. \\_ /
|
||||
\:._ _/ .' .@) \@) ` `\ ,.'
|
||||
_/,--' .- .\,-.`--`.
|
||||
,'/'' (( \ ` )
|
||||
/'/' \ `-' (
|
||||
'/'' `._,-----'
|
||||
''/' .,---'
|
||||
''/' ;:
|
||||
''/'' ''/
|
||||
''/''/''
|
||||
'/'/'
|
||||
`;
|
||||
-->
|
||||
<!--
|
||||
__ __
|
||||
(_ |_ _ || _ _ _ | | _ _ |__). _ |_ |_
|
||||
__)|_(_||||||(_|| ) |/\|(_|_) | \ |(_)| )|_
|
||||
_/
|
||||
-->
|
||||
<!--
|
||||
.88888888:.
|
||||
88888888.88888.
|
||||
.8888888888888888.
|
||||
888888888888888888
|
||||
88' _`88'_ `88888
|
||||
88 88 88 88 88888
|
||||
88_88_::_88_:88888
|
||||
88:::,::,:::::8888
|
||||
88`:::::::::'`8888
|
||||
.88 `::::' 8:88.
|
||||
8888 `8:888.
|
||||
.8888' `888888.
|
||||
.8888:.. .::. ...:'8888888:.
|
||||
.8888.' :' `'::`88:88888
|
||||
.8888 ' `.888:8888.
|
||||
888:8 . 888:88888
|
||||
.888:88 .: 888:88888:
|
||||
8888888. :: 88:888888
|
||||
`.::.888. :: .88888888
|
||||
.::::::.888. :: :::`8888'.:.
|
||||
::::::::::.888 ' .::::::::::::
|
||||
::::::::::::.8 ' .:8::::::::::::.
|
||||
.::::::::::::::. .:888:::::::::::::
|
||||
:::::::::::::::88:.__..:88888:::::::::::'
|
||||
`'.:::::::::::88888888888.88:::::::::'
|
||||
`':::_:' -- '' -'-' `':_::::'`
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Removeddit</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta type="description" content="Display removed and deleted comments from Reddit">
|
||||
<meta type="author" content="Jesper Wrang">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<link href="/images/favicon.ico" rel="shotcut icon">
|
||||
</head>
|
||||
<body>
|
||||
<script>console.time('scripts loaded')</script>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
||||
52
src/index.js
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import { html, render } from 'lit-html'
|
||||
import Navigo from 'navigo'
|
||||
import aboutTemplate from './templates/about'
|
||||
import threadTemplate from './templates/thread'
|
||||
import subredditTemplate from './templates/subreddit'
|
||||
import {store} from 'state'
|
||||
// import p from 'snudown-js'
|
||||
|
||||
const router = new Navigo(window.location.origin)
|
||||
// router.updatePageLinks()
|
||||
|
||||
let currentTemplate = html``
|
||||
const main = document.getElementById('main')
|
||||
|
||||
const renderMain = () => render(currentTemplate(store.getState(), store.setState), main)
|
||||
store.subscribe(() => renderMain())
|
||||
|
||||
router.on({
|
||||
'/about': () => {
|
||||
currentTemplate = aboutTemplate
|
||||
renderMain()
|
||||
},
|
||||
'/r/:subreddit/comments/:threadID/:junk/:commentID': params => {
|
||||
const {subreddit, threadID, commentID} = params
|
||||
store.setState({subreddit, threadID, commentID})
|
||||
currentTemplate = threadTemplate
|
||||
renderMain()
|
||||
},
|
||||
'/r/:subreddit/comments/:threadID/:junk': params => {
|
||||
const {subreddit, threadID} = params
|
||||
store.setState({subreddit, threadID, commentID: undefined})
|
||||
currentTemplate = threadTemplate
|
||||
renderMain()
|
||||
},
|
||||
'/r/:subreddit/comments/:threadID': params => {
|
||||
const {subreddit, threadID} = params
|
||||
store.setState({subreddit, threadID, commentID: undefined})
|
||||
currentTemplate = threadTemplate
|
||||
renderMain()
|
||||
},
|
||||
'/r/:subreddit': params => {
|
||||
const {subreddit} = params
|
||||
store.setState({subreddit, threadID: undefined, commentID: undefined})
|
||||
currentTemplate = subredditTemplate
|
||||
renderMain()
|
||||
},
|
||||
'*': () => {
|
||||
store.setState({subreddit: 'all', threadID: undefined, commentID: undefined})
|
||||
currentTemplate = subredditTemplate
|
||||
renderMain()
|
||||
}
|
||||
}).resolve()
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
import React from 'react'
|
||||
import {
|
||||
BrowserRouter,
|
||||
Switch,
|
||||
Route,
|
||||
} from 'react-router-dom'
|
||||
|
||||
import Header from 'components/Header'
|
||||
import { About, Thread, Subreddit } from 'pages'
|
||||
|
||||
export default () => (
|
||||
<BrowserRouter basename={__dirname}>
|
||||
<React.Fragment>
|
||||
<Header />
|
||||
<div className='main'>
|
||||
<Switch>
|
||||
<Route exact path='/' component={Subreddit} />
|
||||
<Route path='/about' component={About} />
|
||||
<Route path='/r/:subreddit/comments/:threadID/:junk/:commentID' component={Thread} />
|
||||
<Route path='/r/:subreddit/comments/:threadID' component={Thread} />
|
||||
<Route path='/r/:subreddit' component={Subreddit} />
|
||||
|
||||
</Switch>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
</BrowserRouter>
|
||||
)
|
||||
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
import React from 'react'
|
||||
import { prettyScore, prettyDate, parse } from 'utils'
|
||||
|
||||
const Comment = (props) => {
|
||||
let commentStyle = 'comment '
|
||||
|
||||
if (props.removed) {
|
||||
commentStyle += 'removed'
|
||||
} else if (props.deleted) {
|
||||
commentStyle += 'deleted'
|
||||
} else {
|
||||
commentStyle += props.depth % 2 === 0 ? 'comment-even' : 'comment-odd'
|
||||
}
|
||||
|
||||
const innerHTML = (props.body === '[removed]' && props.removed) ? '<p>[removed too quickly to be archived]</p>' : parse(props.body)
|
||||
const permalink = `/r/${props.subreddit}/comments/${props.link_id}/_/${props.id}/`
|
||||
|
||||
return (
|
||||
<div id={props.id} className={commentStyle}>
|
||||
<div className='comment-head'>
|
||||
<a href='#' onClick={() => false} className='author'>[–]</a>
|
||||
<span className='space' />
|
||||
<a
|
||||
href={props.author !== '[deleted]' ? `https://www.reddit.com/user/${props.author}` : undefined}
|
||||
className='author comment-author'
|
||||
>
|
||||
{props.author}
|
||||
{props.deleted && ' (deleted by user)'}
|
||||
</a>
|
||||
<span className='space' />
|
||||
<span className='comment-score'>{prettyScore(props.score)} point{(props.score !== 1) && 's'}</span>
|
||||
<span className='space' />
|
||||
<span className='comment-time'>{prettyDate(props.created_utc)}</span>
|
||||
</div>
|
||||
<div className='comment-body' dangerouslySetInnerHTML={{ __html: innerHTML }} />
|
||||
<div className='comment-links'>
|
||||
<a href={permalink}>permalink</a>
|
||||
<a href={`https://www.reddit.com${permalink}`}>reddit</a>
|
||||
<a href={`https://snew.github.io${permalink}`}>ceddit</a>
|
||||
</div>
|
||||
<div>
|
||||
{props.replies.map(comment => (
|
||||
<Comment
|
||||
key={comment.id}
|
||||
{...comment}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Comment
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
import React from 'react'
|
||||
|
||||
const getProcent = (part, total) => (total === 0 ? '0.0' : ((100 * part) / total).toFixed(1))
|
||||
|
||||
export default props => (
|
||||
<div id='comment-info'>
|
||||
<span className='removed-text'>
|
||||
removed comments: {props.removed}/{props.total} ({getProcent(props.removed, props.total)}%)
|
||||
</span>
|
||||
<br />
|
||||
<span className='deleted-text'>
|
||||
deleted comments: {props.deleted}/{props.total} ({getProcent(props.deleted, props.total)}%)
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
import React from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import StatusBox from 'containers/StatusBox'
|
||||
|
||||
export default () => (
|
||||
<header>
|
||||
<div id='header'>
|
||||
<h1>
|
||||
<Link to='/'>Removeddit</Link>
|
||||
</h1>
|
||||
<nav>
|
||||
<Link to='/r/all'>/r/all</Link>
|
||||
<Link to='/about/'>about</Link>
|
||||
</nav>
|
||||
</div>
|
||||
<StatusBox />
|
||||
</header>
|
||||
)
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
import React from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { prettyScore, prettyDate, parse, redditThumbnails, isDeleted } from 'utils'
|
||||
|
||||
export default (props) => {
|
||||
if (!props.title) {
|
||||
return <div />
|
||||
}
|
||||
|
||||
const url = props.url.replace('https://www.reddit.com', '')
|
||||
const userLink = isDeleted(props.author) ? undefined : `https://www.reddit.com/user/${props.author}`
|
||||
|
||||
let thumbnail
|
||||
const thumbnailWidth = props.thumbnail_width ? props.thumbnail_width * 0.5 : 70
|
||||
const thumbnailHeight = props.thumbnail_height ? props.thumbnail_height * 0.5 : 70
|
||||
|
||||
if (redditThumbnails.includes(props.thumbnail)) {
|
||||
thumbnail = <a href={url} className={`thumbnail thumbnail-${props.thumbnail}`} />
|
||||
} else if (props.thumbnail !== '') {
|
||||
thumbnail = (
|
||||
<a href={url}>
|
||||
<img className='thumbnail' src={props.thumbnail} width={thumbnailWidth} height={thumbnailHeight} alt='Thumbnail' />
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`thread ${props.removed && 'removed'}`}>
|
||||
{props.position &&
|
||||
<span className='post-rank'>{props.position}</span>}
|
||||
<div className='thread-score-box'>
|
||||
<div className='vote upvote' />
|
||||
<div className='thread-score'>{prettyScore(props.score)}</div>
|
||||
<div className='vote downvote' />
|
||||
</div>
|
||||
{thumbnail}
|
||||
<div className='thread-content'>
|
||||
<a className='thread-title' href={url}>{props.title}</a>
|
||||
{props.link_flair_text &&
|
||||
<span className='link-flair'>{props.link_flair_text}</span>}
|
||||
<span className='domain'>({props.domain})</span>
|
||||
<div className='thread-info'>
|
||||
submitted <span className='thread-time'>{prettyDate(props.created_utc)}</span> by
|
||||
<a className='thread-author author' href={userLink}>{props.author}</a>
|
||||
to <Link className='subreddit-link author' to={`/r/${props.subreddit}`}>/r/{props.subreddit}</Link>
|
||||
</div>
|
||||
{props.selftext &&
|
||||
<div className='thread-selftext user-text' dangerouslySetInnerHTML={{ __html: parse(props.selftext) }} />}
|
||||
<div className='total-comments'>
|
||||
<Link className='grey-link' to={props.permalink}>
|
||||
<b>{props.num_comments} comments</b>
|
||||
</Link>
|
||||
<a className='grey-link' href={`https://www.reddit.com${props.permalink}`}>
|
||||
<b>reddit</b>
|
||||
</a>
|
||||
<a className='grey-link' href={`https://snew.github.io${props.permalink}`}>
|
||||
<b>ceddit</b>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
import React from 'react'
|
||||
|
||||
export default (props) => {
|
||||
const isSelected = time => props.time === time
|
||||
|
||||
return (
|
||||
<div className='subreddit-info'>
|
||||
top post of <a href={`https://www.reddit.com/r/${props.subreddit}`}>/r/{props.subreddit}</a> from:
|
||||
{/* <select onchange='Vars.reload(this)'> */}
|
||||
<select>
|
||||
<option value='hour' selected={isSelected('hour')}>past hour</option>
|
||||
<option value='12hour' selected={isSelected('12hour')}>past 12 hours</option>
|
||||
<option value='day' selected={isSelected('day')}>past day</option>
|
||||
<option value='week' selected={isSelected('week')}>past week</option>
|
||||
<option value='month' selected={isSelected('month')}>past month</option>
|
||||
<option value='6month' selected={isSelected('6month')}>past 6 months</option>
|
||||
<option value='year' selected={isSelected('year')}>past year</option>
|
||||
<option value='all' selected={isSelected('all')}>all time</option>
|
||||
</select>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
import React from 'react'
|
||||
import { connect } from 'react-redux'
|
||||
|
||||
const StatusBox = props => (
|
||||
<div id='status'>
|
||||
{props.text &&
|
||||
<p id='status-text'>{props.text}</p>}
|
||||
{props.image &&
|
||||
<img id='status-image' src={props.image} alt='status' />}
|
||||
</div>
|
||||
)
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
text: state.status.text,
|
||||
image: state.status.image,
|
||||
})
|
||||
|
||||
export default connect(mapStateToProps)(StatusBox)
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import { Provider } from 'react-redux'
|
||||
import { store } from 'state'
|
||||
import App from 'App'
|
||||
|
||||
import '../sass/index.sass'
|
||||
|
||||
ReactDOM.render(
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>,
|
||||
document.getElementById('app')
|
||||
)
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
import React from 'react'
|
||||
|
||||
export default props => (
|
||||
<div id='main'>
|
||||
<div id='main-box'>
|
||||
<h2 className='about'>About</h2>
|
||||
<p>
|
||||
Display
|
||||
<b className='removed-text' title='Removed by mods'> removed </b>
|
||||
(by mods) and
|
||||
<b className='deleted-text' title='Deleted by users'> deleted </b>
|
||||
(by users) comments/threads from Reddit.
|
||||
</p>
|
||||
<p>
|
||||
<b>Usage</b>: Drag this bookmarklet
|
||||
<a className='bookmarklet' href="javascript: document.location = document.URL.replace('reddit.com','removeddit.com');">
|
||||
Removeddit
|
||||
</a>
|
||||
to your bookmark bar and use it to get from reddit to removeddit.
|
||||
<br /><br />
|
||||
Alternatively you can manually replace the <i>reddit</i> in the URL to <i>removeddit</i>.
|
||||
<br />
|
||||
E.g. <a href='/r/TwoXChromosomes/comments/6z1hch/'>https://www.removeddit.com/r/TwoXChromosomes/comments/6z1hch/</a>
|
||||
</p>
|
||||
<p>
|
||||
Created by
|
||||
<a href='https://github.com/JubbeArt/'> Jesper Wrang </a> and uses
|
||||
<a href='https://pushshift.io/'> Jason Baumgartner </a> service for getting removed comments.
|
||||
</p>
|
||||
<h2 className='todo'>TODO</h2>
|
||||
<ul>
|
||||
<li>Collapsing comments</li>
|
||||
<li>Maybe for specific users </li>
|
||||
</ul>
|
||||
<h2 className='contact'>Links/Contact</h2>
|
||||
<p style={{ marginBottom: '8px' }}>For feedback and bug reports:</p>
|
||||
<ul>
|
||||
<li>email: removeddit (at) gmail.com</li>
|
||||
<li>reddit: <a href='https://www.reddit.com/user/Jubbeart/'>/u/JubbeArt</a></li>
|
||||
</ul>
|
||||
<p>
|
||||
<a href='https://github.com/JubbeArt/removeddit'>Code on Github.</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
|
||||
// var radioInput = function(name, displayNames, values) {
|
||||
// var inputs = "";
|
||||
|
||||
// for(var i = 0, len = displayNames.length; i < len; i++){
|
||||
// inputs += '<span class="radioButton"'+ (Vars.lookup[name] === values[i] ? ' style="background: #239f2b; color:#fff"':'')+'>';
|
||||
// inputs += '<input type="radio" id="'+name+i+'" name="'+name+'" onchange="CSS.radio(this)"';
|
||||
// inputs += 'value="'+values[i]+'" '+(Vars.lookup[name] === values[i] ? ' checked "':'')+' >';
|
||||
// inputs += '<label for="'+name+i+'">'+displayNames[i]+'</label></span>';
|
||||
// }
|
||||
// return inputs;
|
||||
// };
|
||||
|
||||
// var textInput = function(name){
|
||||
// return '<input type="text" name="'+name+'" id="'+name+'" value="'+Vars.get(name)+'">';
|
||||
// };
|
||||
|
||||
// var label = function(name, display) {
|
||||
// return '<label for="'+name+'">'+display+': </label>';
|
||||
// };
|
||||
|
||||
// var inputRow = function(text, input) {
|
||||
// return '<div class="search-row"><span class="search-left">'+text+'</span><span>'+input+"</span></div>";
|
||||
// };
|
||||
|
||||
// var selectTime = function() {
|
||||
// var values = ["hour", "12hour", "day", "week", "month", "6month", "year", "all"];
|
||||
// var display = ["past hour", "past 12 hours", "past day", "past week", "past month", "past 6 months", "past year", "all time"];
|
||||
// var html = '<select name="time" id="time">';
|
||||
|
||||
// for(var i = 0, len = values.length; i < len; i++) {
|
||||
// html += '<option value="'+values[i]+'"' + ((Vars.get("time") === values[i]) ? " selected" : '')+'>'+display[i]+'</option>';
|
||||
// }
|
||||
|
||||
// return html + "</select>";
|
||||
// };
|
||||
|
||||
// var select = function(name, display, values){
|
||||
// var html = '<select name="'+name+'" id="'+name+'">';
|
||||
|
||||
// for(var i = 0, len = values.length; i < len; i++) {
|
||||
// html += '<option value="'+values[i]+'"' + ((Vars.get(name) === values[i]) ? " selected" : '')+'>'+display[i]+'</option>';
|
||||
// }
|
||||
|
||||
// return html + "</select>";
|
||||
// };
|
||||
|
||||
// return {
|
||||
// createSearch: function(){
|
||||
// var searchBox = document.createElement("div");
|
||||
// searchBox.id = "main-box";
|
||||
// searchBox.className = "search-box";
|
||||
// /*
|
||||
// Comments:
|
||||
// text (body) string
|
||||
// title (title) string
|
||||
// subreddit (subreddit) string
|
||||
// author (author) string
|
||||
// over_18 (over_18) sfw,nsfw,both *
|
||||
// locked (locked) bool *
|
||||
// between (after/before) "date"
|
||||
// sort (sort) asc,desc
|
||||
// */
|
||||
// var html = inputRow('Im looking for: ', radioInput("thread", ["Thread","Comment"], ["true", "false"]));
|
||||
// html += inputRow(label("text", "Text"), textInput("text"));
|
||||
// html += inputRow(label("title","Title"), textInput("title"));
|
||||
// html += inputRow(label("subreddit", "Subreddit"), textInput("subreddit"));
|
||||
// html += inputRow(label("author", "Author"), textInput("author"));
|
||||
// html += inputRow("Over 18: ", radioInput("over_18", ["Both","NSFW","SFW"], ["both","nsfw","sfw"]));
|
||||
// html += inputRow("Locked: ", radioInput("locked", ["Both","True", "False"], ["both","true","false"]));
|
||||
// html += inputRow("Removed: ", select("removed", ["Removed and non-removed", "Only removed", "Only deleted"], ["all", "removed", "deleted"]));
|
||||
// html += inputRow("From:", selectTime());
|
||||
// html += inputRow("Sort: ", select("sort", ["Highest score","Lowest score","Newest","Oldest"], ["score_desc","score_asc","time_desc","time_asc"]));
|
||||
// html += '<input type="button" value="'+(showAdvanced?'Hide':'Show')+' advanced">';
|
||||
// html += '<input type="submit" value="Search">';
|
||||
// searchBox.innerHTML = html;
|
||||
|
||||
// mainDiv.appendChild(searchBox);
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
export { default as About } from './About'
|
||||
export { default as Subreddit } from './Subreddit'
|
||||
export { default as Thread } from './Thread'
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
import { get, put } from 'utils'
|
||||
|
||||
// Sort types
|
||||
export const SORT_TOP = 'SORT_TOP'
|
||||
export const SORT_BOTTOM = 'SORT_BOTTOM'
|
||||
export const SORT_NEW = 'SORT_NEW'
|
||||
export const SORT_OLD = 'SORT_OLD'
|
||||
|
||||
// Filter types
|
||||
export const SHOW_ALL = 'SHOW_ALL'
|
||||
export const SHOW_REMOVED_DELETED = 'SHOW_REMOVED_DELETED'
|
||||
export const SHOW_REMOVED = 'SHOW_REMOVED'
|
||||
export const SHOW_DELETED = 'SHOW_DELETED'
|
||||
|
||||
// Action types
|
||||
export const COMMENT_SORT = 'COMMENT_SORT'
|
||||
export const COMMENT_SHOW = 'COMMENT_SHOW'
|
||||
|
||||
// Action creators
|
||||
export const setCommentSort = payload => ({ type: COMMENT_SORT, payload })
|
||||
export const setCommentShow = payload => ({ type: COMMENT_SHOW, payload })
|
||||
|
||||
const sortKey = 'commentSort'
|
||||
const filterKey = 'commentFilter'
|
||||
|
||||
// Init state
|
||||
const initialStatusState = {
|
||||
sort: get(sortKey, SORT_TOP),
|
||||
show: get(filterKey, SHOW_REMOVED_DELETED),
|
||||
}
|
||||
|
||||
export const commentSectionReducer = (state = initialStatusState, action) => {
|
||||
switch (action.type) {
|
||||
case COMMENT_SORT:
|
||||
put(sortKey, action.payload)
|
||||
return {
|
||||
...state,
|
||||
sort: action.payload,
|
||||
}
|
||||
case COMMENT_SHOW:
|
||||
put(filterKey, action.payload)
|
||||
return {
|
||||
...state,
|
||||
show: action.payload,
|
||||
}
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
import { createStore, combineReducers, applyMiddleware } from 'redux'
|
||||
import logger from 'redux-logger'
|
||||
import { statusReducer } from './status'
|
||||
import { commentSectionReducer } from './commentSection'
|
||||
|
||||
export { setStatusLoading, setStatusSuccess, setStatusError } from './status'
|
||||
export * from './commentSection'
|
||||
|
||||
const reducer = combineReducers({
|
||||
status: statusReducer,
|
||||
commentSection: commentSectionReducer,
|
||||
})
|
||||
|
||||
export const store = createStore(reducer, applyMiddleware(logger))
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
const images = {
|
||||
loading: '/images/loading.gif',
|
||||
error: '/images/error.png',
|
||||
success: '/images/done.png',
|
||||
}
|
||||
|
||||
// Action types
|
||||
const STATUS_SET_LOADING = 'STATUS_SET_LOADING'
|
||||
const STATUS_SET_SUCCESS = 'STATUS_SET_SUCCESS'
|
||||
const STATUS_SET_ERROR = 'STATUS_SET_ERROR'
|
||||
|
||||
// Action creators
|
||||
export const setStatusLoading = (payload = '') => ({ type: STATUS_SET_LOADING, payload })
|
||||
export const setStatusSuccess = (payload = '') => ({ type: STATUS_SET_SUCCESS, payload })
|
||||
export const setStatusError = (payload = '') => ({ type: STATUS_SET_ERROR, payload })
|
||||
|
||||
// Init state
|
||||
const initialStatusState = {
|
||||
text: null,
|
||||
image: null,
|
||||
}
|
||||
|
||||
export const statusReducer = (state = initialStatusState, action) => {
|
||||
switch (action.type) {
|
||||
case STATUS_SET_SUCCESS:
|
||||
return {
|
||||
...state,
|
||||
text: action.payload,
|
||||
image: images.success,
|
||||
}
|
||||
case STATUS_SET_LOADING:
|
||||
return {
|
||||
...state,
|
||||
text: action.payload,
|
||||
image: images.loading,
|
||||
}
|
||||
case STATUS_SET_ERROR:
|
||||
return {
|
||||
...state,
|
||||
text: action.payload,
|
||||
image: images.error,
|
||||
}
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
|
@ -12,29 +12,8 @@ const getSubredditForAPI = props => {
|
|||
return subreddit.toLowerCase()
|
||||
}
|
||||
|
||||
|
||||
export default class Subreddit extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
threads: [],
|
||||
subreddit: '',
|
||||
}
|
||||
}
|
||||
componentDidMount() {
|
||||
this.updateThreads(this.props)
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const newSubreddit = getSubredditForAPI(nextProps)
|
||||
|
||||
if (this.state.subreddit !== newSubreddit) {
|
||||
this.updateThreads(nextProps)
|
||||
}
|
||||
}
|
||||
|
||||
updateThreads(props) {
|
||||
updateThreads (props) {
|
||||
const subreddit = getSubredditForAPI(props)
|
||||
getRemovedThreadIDs(subreddit)
|
||||
.then(threadIDs => getThreads(threadIDs))
|
||||
|
|
@ -47,7 +26,7 @@ export default class Subreddit extends React.Component {
|
|||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
render () {
|
||||
const { subreddit = 'all' } = this.props.match.params
|
||||
const subredditLink = `/r/${subreddit}`
|
||||
|
||||
|
|
@ -61,9 +40,9 @@ export default class Subreddit extends React.Component {
|
|||
<a href={`https://snew.github.io${subredditLink}`} className='subreddit-title-link'>ceddit</a>
|
||||
</div>
|
||||
{
|
||||
this.state.threads.map(thread => (
|
||||
<Post key={thread.id} {...thread} />
|
||||
))
|
||||
this.state.threads.map(thread => (
|
||||
<Post key={thread.id} {...thread} />
|
||||
))
|
||||
}
|
||||
</React.Fragment>
|
||||
)
|
||||
|
|
@ -3,16 +3,16 @@ import Post from 'components/Post'
|
|||
import CommentSection from 'components/CommentSection'
|
||||
import {
|
||||
getPost,
|
||||
getComments as getRedditComments,
|
||||
getComments as getRedditComments
|
||||
} from 'api/reddit'
|
||||
import {
|
||||
getPost as getRemovedPost,
|
||||
getComments as getPushshiftComments,
|
||||
getComments as getPushshiftComments
|
||||
} from 'api/pushshift'
|
||||
import { isDeleted, isRemoved } from 'utils'
|
||||
|
||||
export default class Thread extends React.Component {
|
||||
constructor(props) {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
|
|
@ -20,11 +20,11 @@ export default class Thread extends React.Component {
|
|||
pushshiftComments: [],
|
||||
removed: [],
|
||||
deleted: [],
|
||||
loadingComments: true,
|
||||
loadingComments: true
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
componentDidMount () {
|
||||
const { subreddit, threadID } = this.props.match.params
|
||||
|
||||
Promise.all([
|
||||
|
|
@ -42,7 +42,7 @@ export default class Thread extends React.Component {
|
|||
}
|
||||
}),
|
||||
// Get comment ids from pushshift
|
||||
getPushshiftComments(threadID),
|
||||
getPushshiftComments(threadID)
|
||||
])
|
||||
.then(results => {
|
||||
const pushshiftComments = results[1]
|
||||
|
|
@ -90,12 +90,12 @@ export default class Thread extends React.Component {
|
|||
this.setState({
|
||||
removed,
|
||||
deleted,
|
||||
loadingComments: false,
|
||||
loadingComments: false
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
render () {
|
||||
let root = this.state.post.id
|
||||
|
||||
if (this.props.match.params.commentID !== undefined) {
|
||||
|
|
@ -4,7 +4,7 @@ html, body
|
|||
background-color: $background
|
||||
font-family: verdana, arial, helvetica, sans-serif
|
||||
|
||||
.main
|
||||
#main
|
||||
margin: 15px
|
||||
|
||||
.removed
|
||||
|
|
|
|||
43
src/state/index.js
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import {createStore} from './store'
|
||||
import {get, showFunctions, sortFunctions} from 'utils'
|
||||
import {html} from 'lit-html'
|
||||
|
||||
export const commentSort = {
|
||||
top: 'top',
|
||||
bottom: 'bottom',
|
||||
new: 'new',
|
||||
old: 'old'
|
||||
}
|
||||
|
||||
export const commentShow = {
|
||||
all: 'all',
|
||||
removedDeleted: 'removedDeleted',
|
||||
removed: 'removed',
|
||||
deleted: 'deleted'
|
||||
}
|
||||
|
||||
export const store = createStore({
|
||||
commentSort: get('commentSort', commentSort.top),
|
||||
commentShow: get('commentShow', commentShow.removedDeleted),
|
||||
subreddit: undefined,
|
||||
threadID: undefined,
|
||||
commentID: undefined,
|
||||
threads: [],
|
||||
thread: undefined,
|
||||
allComments: [],
|
||||
statusText: undefined,
|
||||
statusImage: undefined,
|
||||
template: html``,
|
||||
comments: () => {
|
||||
return store.getState().allComments
|
||||
.filter(showFunctions[store.getState().currentShow])
|
||||
.sort(sortFunctions[store.getState().currentSort])
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
export const stateImages = {
|
||||
loading: '/images/loading.gif',
|
||||
error: '/images/error.png',
|
||||
success: '/images/done.png'
|
||||
}
|
||||
40
src/state/store.js
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import {put} from 'utils'
|
||||
|
||||
export const createStore = (initState = {}, persistentKeys = []) => {
|
||||
let state = initState
|
||||
let callbacks = []
|
||||
|
||||
return {
|
||||
getState: () => state,
|
||||
setState: nextState => {
|
||||
Object.keys(nextState).forEach(key => {
|
||||
if (persistentKeys.includes(key)) {
|
||||
put(key, nextState[key])
|
||||
}
|
||||
})
|
||||
|
||||
state = {
|
||||
...state,
|
||||
...nextState
|
||||
}
|
||||
|
||||
callbacks.forEach(callback => {
|
||||
callback(state)
|
||||
})
|
||||
},
|
||||
subscribe: callback => {
|
||||
callbacks.push(callback)
|
||||
|
||||
return () => {
|
||||
callbacks = callbacks.filter(c => c !== callback)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// export
|
||||
|
||||
// const saveState = {
|
||||
// commentSort: true,
|
||||
// commentShow: true
|
||||
// }
|
||||
44
src/templates/about/index.js
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import {html} from 'lit-html'
|
||||
|
||||
export default () => html`
|
||||
<div id="main-box">
|
||||
<h2 class="about">About</h2>
|
||||
<p>
|
||||
Display
|
||||
<b class="removed-text" title="Removed by mods"> removed </b>
|
||||
(by mods) and
|
||||
<b class="deleted-text" title="Deleted by users"> deleted </b>
|
||||
(by users) comments/threads from Reddit.
|
||||
</p>
|
||||
<p>
|
||||
<b>Usage</b>: Drag this bookmarklet
|
||||
<a class="bookmarklet" href="javascript: document.location = document.URL.replace('reddit.com','removeddit.com');">
|
||||
Removeddit
|
||||
</a>
|
||||
to your bookmark bar and use it to get from reddit to removeddit.
|
||||
<br /><br />
|
||||
Alternatively you can manually replace the <i>reddit</i> in the URL to <i>removeddit</i>.
|
||||
<br />
|
||||
E.g. <a href="/r/TwoXChromosomes/comments/6z1hch/">https://www.removeddit.com/r/TwoXChromosomes/comments/6z1hch/</a>
|
||||
</p>
|
||||
<p>
|
||||
Created by
|
||||
<a href="https://github.com/JubbeArt/"> Jesper Wrang </a> and uses
|
||||
<a href="https://pushshift.io/"> Jason Baumgartner </a> service for getting removed comments.
|
||||
</p>
|
||||
<h2 class="todo">TODO</h2>
|
||||
<ul>
|
||||
<li>Collapsing comments</li>
|
||||
<li>Maybe for specific users </li>
|
||||
</ul>
|
||||
<h2 class="contact">Links/Contact</h2>
|
||||
<p style="margin-bottom: 8px">For feedback and bug reports:</p>
|
||||
<ul>
|
||||
<li>email: removeddit (at) gmail.com</li>
|
||||
<li>reddit: <a href="https://www.reddit.com/user/Jubbeart/">/u/JubbeArt</a></li>
|
||||
</ul>
|
||||
<p>
|
||||
<a href="https://github.com/JubbeArt/removeddit">Code on Github.</a>
|
||||
</p>
|
||||
</div>
|
||||
`
|
||||
17
src/templates/header/statusBox.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import {html} from 'lit-html'
|
||||
|
||||
const statusBox = props => html`
|
||||
<div id="status">
|
||||
${props.text &&
|
||||
html`<p id="status-text">{props.text}</p>`}
|
||||
${props.image &&
|
||||
html`<img id="status-image" src={props.image} alt="status" />`}
|
||||
</div>
|
||||
`
|
||||
|
||||
// const mapStateToProps = state => ({
|
||||
// text: state.status.text,
|
||||
// image: state.status.image
|
||||
// })
|
||||
|
||||
export default statusBox
|
||||
50
src/templates/thread/comment.js
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import {html} from 'lit-html'
|
||||
import { prettyScore, prettyDate, parse } from 'utils'
|
||||
|
||||
const comment = props => {
|
||||
let commentStyle = 'comment '
|
||||
|
||||
if (props.removed) {
|
||||
commentStyle += 'removed'
|
||||
} else if (props.deleted) {
|
||||
commentStyle += 'deleted'
|
||||
} else {
|
||||
commentStyle += props.depth % 2 === 0 ? 'comment-even' : 'comment-odd'
|
||||
}
|
||||
|
||||
const innerHTML = (props.body === '[removed]' && props.removed) ? '<p>[removed too quickly to be archived]</p>' : parse(props.body)
|
||||
const permalink = `/r/${props.subreddit}/comments/${props.link_id}/_/${props.id}/`
|
||||
|
||||
return html`
|
||||
<div id="${props.id}" class="${commentStyle}">
|
||||
<div class="comment-head">
|
||||
<a href="#" onclick="${() => false}" class="author">[–]</a>
|
||||
<span class="space"></span>
|
||||
<a
|
||||
href="${props.author !== '[deleted]' ? `https://www.reddit.com/user/${props.author}` : undefined}"
|
||||
class="author comment-author"
|
||||
>
|
||||
${props.author}
|
||||
${props.deleted && ' (deleted by user)'}
|
||||
</a>
|
||||
<span class="space"></span>
|
||||
<span class="comment-score">${prettyScore(props.score)} point${(props.score !== 1) && 's'}</span>
|
||||
<span class="space"></span>
|
||||
<span class="comment-time">${prettyDate(props.created_utc)}</span>
|
||||
</div>
|
||||
<div class="comment-body">
|
||||
${innerHTML}
|
||||
</div>
|
||||
<div class="comment-links">
|
||||
<a href="${permalink}">permalink</a>
|
||||
<a href="${`https://www.reddit.com${permalink}`}">reddit</a>
|
||||
<a href="${`https://snew.github.io${permalink}`}">ceddit</a>
|
||||
</div>
|
||||
<div>
|
||||
${props.replies.map(com => comment(com))}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
export default comment
|
||||
15
src/templates/thread/commentInfo.js
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import {html} from 'lit-html'
|
||||
|
||||
const getProcent = (part, total) => (total === 0 ? '0.0' : ((100 * part) / total).toFixed(1))
|
||||
|
||||
export default (props) => html`
|
||||
<div id="comment-info">
|
||||
<span class="removed-text">
|
||||
removed comments: ${props.removed}/${props.total} (${getProcent(props.removed, props.total)}%)
|
||||
</span>
|
||||
<br />
|
||||
<span class="deleted-text">
|
||||
deleted comments: ${props.deleted}/${props.total} (${getProcent(props.deleted, props.total)}%)
|
||||
</span>
|
||||
</div>
|
||||
`
|
||||
63
src/templates/thread/post.js
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
import {html} from 'lit-html'
|
||||
|
||||
import { prettyScore, prettyDate, parse, redditThumbnails, isDeleted } from 'utils'
|
||||
|
||||
export default (props) => {
|
||||
if (!props.title) {
|
||||
return html`<div></div>`
|
||||
}
|
||||
|
||||
const url = props.url.replace('https://www.reddit.com', '')
|
||||
const userLink = isDeleted(props.author) ? undefined : `https://www.reddit.com/user/${props.author}`
|
||||
|
||||
const thumbnailWidth = props.thumbnail_width ? props.thumbnail_width * 0.5 : 70
|
||||
const thumbnailHeight = props.thumbnail_height ? props.thumbnail_height * 0.5 : 70
|
||||
let thumbnail
|
||||
|
||||
if (redditThumbnails.includes(props.thumbnail)) {
|
||||
thumbnail = html`<a href="${url}" class="${`thumbnail thumbnail-${props.thumbnail}`}"></a>`
|
||||
} else if (props.thumbnail !== '') {
|
||||
thumbnail = html`
|
||||
<a href="${url}">
|
||||
<img class="thumbnail" src="${props.thumbnail}" width="${thumbnailWidth}" height="${thumbnailHeight}" alt="Thumbnail" />
|
||||
</a>
|
||||
`
|
||||
}
|
||||
|
||||
return html`
|
||||
<div class="${`thread ${props.removed && 'removed'}`}">
|
||||
${props.position &&
|
||||
html`<span class="post-rank">${props.position}</span>`}
|
||||
<div class="thread-score-box">
|
||||
<div class="vote upvote"></div>
|
||||
<div class="thread-score">${prettyScore(props.score)}</div>
|
||||
<div class="vote downvote"></div>
|
||||
</div>
|
||||
${thumbnail}
|
||||
<div class="thread-content">
|
||||
<a class="thread-title" href="${url}">${props.title}</a>
|
||||
${props.link_flair_text &&
|
||||
html`<span class="link-flair">${props.link_flair_text}</span>`}
|
||||
<span class="domain">(${props.domain})</span>
|
||||
<div class="thread-info">
|
||||
submitted <span class="thread-time">${prettyDate(props.created_utc)}</span> by
|
||||
<a class="thread-author author" href="${userLink}">${props.author}</a>
|
||||
to <a class="subreddit-link author" href="${`/r/${props.subreddit}`}">/r/${props.subreddit}</a>
|
||||
</div>
|
||||
${props.selftext &&
|
||||
html`<div class="thread-selftext user-text">${parse(props.selftext)}</div>`}
|
||||
<div class="total-comments">
|
||||
<a class="grey-link" href="${props.permalink}">
|
||||
<b>${props.num_comments} comments</b>
|
||||
</a>
|
||||
<a class="grey-link" href="${`https://www.reddit.com${props.permalink}`}">
|
||||
<b>reddit</b>
|
||||
</a>
|
||||
<a class="grey-link" href="${`https://snew.github.io${props.permalink}`}">
|
||||
<b>ceddit</b>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
|
@ -24,7 +24,7 @@ export const chunk = (arr, size) => {
|
|||
}
|
||||
|
||||
// JSON parsing for fetch
|
||||
export const json = x => x.json()
|
||||
export const json = response => response.json()
|
||||
|
||||
// Make multiple requests to the same url, with an array of data (usually comment IDs)
|
||||
// This is needed since there is a limit on how long a url can be
|
||||
|
|
@ -53,7 +53,7 @@ export const redditThumbnails = ['self', 'default', 'image', 'nsfw']
|
|||
export const parse = text => markdown.render(text)
|
||||
|
||||
// UTC to "Reddit time format" (e.g. 5 hours ago, just now, etc...)
|
||||
export const prettyDate = createdUTC => {
|
||||
export function prettyDate (createdUTC) {
|
||||
const currentUTC = Math.floor((new Date()).getTime() / 1000)
|
||||
const secondDiff = currentUTC - createdUTC
|
||||
const dayDiff = Math.floor(secondDiff / 86400)
|
||||
|
|
@ -74,7 +74,7 @@ export const prettyDate = createdUTC => {
|
|||
}
|
||||
|
||||
// Reddit format for scores, e.g. 12000 => 12k
|
||||
export const prettyScore = score => {
|
||||
export function prettyScore (score) {
|
||||
if (score >= 100000) {
|
||||
return `${(score / 1000).toFixed(0)}k`
|
||||
} else if (score >= 10000) {
|
||||
|
|
@ -91,33 +91,34 @@ export const get = (key, defaultValue) => (
|
|||
|
||||
export const put = (key, value) => localStorage.setItem(key, JSON.stringify(value))
|
||||
|
||||
// Filter comments
|
||||
export const showFunctions = {
|
||||
all: comment => true,
|
||||
removedDeleted: comment => comment.removed === true || comment.deleted === true,
|
||||
removed: comment => comment.removed === true,
|
||||
deleted: comment => comment.deleted === true
|
||||
}
|
||||
|
||||
// Sorting for comments
|
||||
export const topSort = (commentA, commentB) => {
|
||||
if (commentA.score > commentB.score) return -1
|
||||
if (commentA.score < commentB.score) return 1
|
||||
return 0
|
||||
export const sortFunctions = {
|
||||
top: (commentA, commentB) => {
|
||||
if (commentA.score > commentB.score) return -1
|
||||
if (commentA.score < commentB.score) return 1
|
||||
return 0
|
||||
},
|
||||
bottom: (commentA, commentB) => {
|
||||
if (commentA.score < commentB.score) return -1
|
||||
if (commentA.score > commentB.score) return 1
|
||||
return 0
|
||||
},
|
||||
new: (commentA, commentB) => {
|
||||
if (commentA.created_utc > commentB.created_utc) return -1
|
||||
if (commentA.created_utc < commentB.created_utc) return 1
|
||||
return 0
|
||||
},
|
||||
old: (commentA, commentB) => {
|
||||
if (commentA.created_utc < commentB.created_utc) return -1
|
||||
if (commentA.created_utc > commentB.created_utc) return 1
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
export const bottomSort = (commentA, commentB) => {
|
||||
if (commentA.score < commentB.score) return -1
|
||||
if (commentA.score > commentB.score) return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
export const newSort = (commentA, commentB) => {
|
||||
if (commentA.created_utc > commentB.created_utc) return -1
|
||||
if (commentA.created_utc < commentB.created_utc) return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
export const oldSort = (commentA, commentB) => {
|
||||
if (commentA.created_utc < commentB.created_utc) return -1
|
||||
if (commentA.created_utc > commentB.created_utc) return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
// Filter comments
|
||||
export const showRemoved = comment => comment.removed === true
|
||||
export const showDeleted = comment => comment.deleted === true
|
||||
export const showRemovedAndDeleted = comment => comment.removed === true || comment.deleted === true
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
const path = require('path');
|
||||
const ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
const CleanWebpackPlugin = require('clean-webpack-plugin');
|
||||
|
||||
const extractSass = new ExtractTextPlugin({
|
||||
filename: 'bundle.[contenthash].css',
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
entry: [
|
||||
'@babel/polyfill',
|
||||
'whatwg-fetch',
|
||||
'./src/js/index.js',
|
||||
],
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: 'bundle.[chunkhash].js',
|
||||
publicPath: '/',
|
||||
},
|
||||
resolve: {
|
||||
modules: [
|
||||
path.resolve('./src/js'),
|
||||
path.resolve('./node_modules'),
|
||||
],
|
||||
},
|
||||
module: {
|
||||
loaders: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
use: [
|
||||
{
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
cacheDirectory: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.html$/,
|
||||
loader: 'html-loader',
|
||||
},
|
||||
{
|
||||
test: /\.sass$/,
|
||||
loader: extractSass.extract({
|
||||
use: ['css-loader', 'sass-loader'],
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new CleanWebpackPlugin(['dist']),
|
||||
extractSass,
|
||||
new HtmlWebpackPlugin({
|
||||
template: 'src/index.html',
|
||||
}),
|
||||
new CopyWebpackPlugin([
|
||||
{
|
||||
from: 'src/404.html',
|
||||
to: '404.html',
|
||||
},
|
||||
{
|
||||
from: 'src/images',
|
||||
to: 'images/',
|
||||
},
|
||||
]),
|
||||
],
|
||||
stats: {
|
||||
colors: true,
|
||||
},
|
||||
}
|
||||
11
webpack.config.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
const path = require('path')
|
||||
|
||||
module.exports = {
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'public')
|
||||
},
|
||||
devServer: {
|
||||
contentBase: path.resolve(__dirname, 'public'),
|
||||
historyApiFallback: true
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
const webpack = require('webpack');
|
||||
const merge = require('webpack-merge');
|
||||
const common = require('./webpack.common.js');
|
||||
|
||||
module.exports = merge(common, {
|
||||
devServer: {
|
||||
historyApiFallback: true,
|
||||
},
|
||||
devtool: 'cheap-module-eval-source-map',
|
||||
});
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
const webpack = require('webpack');
|
||||
const merge = require('webpack-merge');
|
||||
const common = require('./webpack.common.js');
|
||||
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
|
||||
|
||||
module.exports = merge(common, {
|
||||
plugins: [
|
||||
new UglifyJSPlugin(),
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': JSON.stringify('production'),
|
||||
}),
|
||||
],
|
||||
});
|
||||