Can now parse 'morechildren'-comments

This commit is contained in:
Jesper Wrang 2018-01-16 01:12:14 +01:00
parent 4492064184
commit 9bab14ffcb
10 changed files with 250 additions and 163 deletions

View file

@ -18,6 +18,7 @@
"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": [
"error",
@ -46,6 +47,7 @@
"allowForLoopAfterthoughts": true
}
],
"no-prototype-builtins": "off",
"no-script-url": "off",
"no-underscore-dangle": "off",
"no-unused-vars": "warn",

View file

@ -10,6 +10,7 @@
<link href="/images/favicon.ico" rel="shotcut icon">
</head>
<body>
<script>console.time('scripts loaded')</script>
<div id="app"></div>
</body>
</html>

View file

@ -4,6 +4,7 @@ import CommentSection from 'components/CommentSection'
import {
getThread,
getCommentIDs,
extractHead,
} from 'reddit'
import {
getThread as getRemovedThread,
@ -23,12 +24,13 @@ export default class Thread extends React.Component {
componentDidMount() {
const { subreddit, threadID } = this.props.match.params
console.timeEnd('scripts loaded')
let pushshiftCommentIDs
Promise.all([
// Get thread from reddit
getThread(subreddit, threadID)
.then(extractHead)
.then(thread => {
this.setState({ thread })
@ -40,15 +42,15 @@ export default class Thread extends React.Component {
this.setState({ thread: removedThread })
})
}
}),
})
// Get comment ids from reddit
.then(() => getCommentIDs(subreddit, threadID)),
// Get comment ids from pushshift
getAllCommentIDs(threadID)
.then(commentIDs => { pushshiftCommentIDs = commentIDs }),
])
.then(() => {
// Get comment ids from reddit
getCommentIDs()
// .then(console.log)
})
}

104
src/js/reddit/comment.js Normal file
View file

@ -0,0 +1,104 @@
import { json, fetchMultiple, jsonMultiple, unique } from 'utils'
import { redditAuth } from 'reddit/token'
import { getThread, extractPost } from './thread'
export const commentLookup = {}
const handleThreadComment = (comment, results) => {
if (comment.kind === 't1') {
// Has comment replies
if (comment.data.replies) {
// Handle all replies in the same way
comment.data.replies.data.children.forEach(reply => handleThreadComment(reply, results))
delete comment.data.replies
}
// Add to the return object
results.ids.push(comment.data.id)
// Store commment in lookup table
commentLookup[comment.data.id] = comment.data
} else if (comment.kind === 'more') {
if (comment.data.id === '_') {
// "continue this thread" comment
results.continueThisThreadIDs.push(comment.data.parent_id);
} else if (comment.data.children.length < comment.data.count) {
// "Load more"-comment (that is missing some of its children)
results.morechildrenIDs.push(comment.data.children)
}
// Always add the "more"-comments children
results.ids.push(...comment.data.children)
} else {
console.error('WTF', comment.kind)
}
return results
}
const handleMoreChildren = (results, threadID) => (
// console.log(results.morechildrenIDs.map(idArray => idArray))
Promise.all(results.morechildrenIDs.map(idArray =>
fetchMultiple(`https://oauth.reddit.com/api/morechildren?link_id=t3_${threadID}&children=`, idArray, redditAuth)))
.then(responsesArrays => Promise.all(responsesArrays.map(jsonMultiple)))
.then(responsesArrays => {
// Reset the array and parse for new morechildren-comments
results.morechildrenIDs = []
responsesArrays.forEach(responses => responses.forEach(response => response.jquery[10][3][0].forEach(comment => {
handleThreadComment(comment, results)
})))
})
.then(() => {
if (results.morechildrenIDs.length !== 0) {
return handleMoreChildren(results, threadID)
}
return Promise.resolve()
})
)
const extractComments = thread => thread[1].data.children
const debug = res => {
console.log('IDs:', res.ids)
console.log('Continue:', res.continueThisThreadIDs)
console.log('Morechildren:', res.morechildrenIDs)
}
const handleThread = thread => {
const comments = extractComments(thread)
// Ugly "hack", using this object as a pointer for storing comments.
// Not very js-like
const results = {
ids: [],
continueThisThreadIDs: [],
morechildrenIDs: [],
}
comments.map(comment => handleThreadComment(comment, results))
const { subreddit, id: threadID } = extractPost(thread)
return Promise.all([
...results.continueThisThreadIDs.map(id => getThread(subreddit, threadID, id)),
// .then(subthreads => subthreads.map(subthread => handleThread(subthread)))),
handleMoreChildren(results, threadID),
])
.then(resultsArray => {
// Removed handleMoreChildren element
// This will result in resultsArray containing a list of comments
// from all 'continue this thread' posts
console.log(resultsArray)
resultsArray.pop()
})
// zip
// )
// .then(
// return results
// )
}
export const getCommentIDs = (subreddit, threadID) => (
getThread(subreddit, threadID)
.then(thread => handleThread(thread))
)

View file

@ -1,79 +1,5 @@
import { json, flatten } from 'utils'
import { fetchToken, redditAuth } from 'reddit/token'
let cachedThread = {}
export const lookup = {}
export const getThread = (subreddit, threadID) => (
fetchToken()
.then(() => fetch(`https://oauth.reddit.com/r/${subreddit}/comments/${threadID}`, redditAuth))
.then(json)
.then(results => {
// Save the comments for later
cachedThread = results
// Return the thread
return results[0].data.children[0].data
})
)
const handleThreadComment = (comment, results) => {
if (comment.kind === 't1') {
// Has comment replies
if (comment.data.replies) {
// Handle all replies in the same way
comment.data.replies.data.children.forEach(reply => handleThreadComment(reply, results))
delete comment.data.replies
}
// Add to the return object
results.ids.push(comment.data.id)
// Store commment in lookup table
lookup[comment.data.id] = comment.data
} else if (comment.kind === 'more') {
if (comment.data.id === '_') {
// "continue this thread" comment
results.continueThisThreadIDs.push(comment.data.parent_id);
} else if (comment.data.children.length < comment.data.count) {
// "Load more"-comment (that is missing some of its children)
results.morechildrenIDs.push(comment.data.children)
}
// Always add the "more"-comments children
results.ids.push(...comment.data.children)
} else {
console.error('WTF', comment.kind)
}
return results
}
const handleMoreChildren = () => {
}
const handleThread = thread => {
const comments = thread[1].data.children
// Ugly "hack", using this object as a pointer for storing comments.
// Not very js-like
const results = {
ids: [],
continueThisThreadIDs: [],
morechildrenIDs: [],
}
comments.map(comment => handleThreadComment(comment, results))
const { ids, continueThisThreadIDs, morechildrenIDs } = results
console.log(ids)
console.log(continueThisThreadIDs)
console.log(morechildrenIDs)
handleMoreChildren()
}
export const getCommentIDs = () => handleThread(cachedThread)
export { getThread, extractPost } from './thread'
export { getCommentIDs } from './comment'
// return HandleIDs.morechildren()
// .catch(function(error){
@ -229,11 +155,6 @@ export const getCommentIDs = () => handleThread(cachedThread)
// // ------------------------------------------------------------------------------
// // ----------------- Handle comments from different requests --------------------
// // ------------------------------------------------------------------------------
// var HandleIDs = (function(){
// var normal = function(thread){
// return _.flatten(_.map(thread[1].data.children, Extract.normal))
// };
// var morechildren = function(){
// return Promise.all(_.map(_.uniq(Comments.morechildren), function(idArray){
// return Fetch2.multiple(URLs.format(URLs.moreChildren, idArray), Reddit.init);

36
src/js/reddit/thread.js Normal file
View file

@ -0,0 +1,36 @@
import { json } from 'utils'
import { fetchToken, redditAuth } from 'reddit/token'
const cachedThreads = {}
export const getThread = (subreddit, threadID, continueThisThreadID = '') => {
// We have already downloaded the thread and can use a cached copy
if (cachedThreads.hasOwnProperty(threadID)) {
if (cachedThreads[threadID].hasOwnProperty(continueThisThreadID)) {
return Promise.resolve(cachedThreads[threadID][continueThisThreadID])
}
}
const url = `https://oauth.reddit.com/r/${subreddit}/comments/${threadID}/_/${continueThisThreadID}`
// Fetch thread from reddit
return (
fetchToken()
.then(() => fetch(url, redditAuth))
.then(json)
.then(thread => {
// Save the thread for later
if (!cachedThreads.hasOwnProperty(threadID)) {
cachedThreads[threadID] = {}
}
cachedThreads[threadID][continueThisThreadID] = thread
// Return the thread
return thread
})
)
}
// Thread = Post + Comments
// Return the post itself
export const extractPost = thread => thread[0].data.children[0].data

View file

@ -8,6 +8,30 @@ export const flatten = arr => arr.reduce(
[]
)
export const unique = arr => arr.filter((value, index) => arr.indexOf(value) === index)
// Take on big array and split it into an array of chunks with correct size
export const chunk = (arr, size) => {
const chunks = []
for (let i = 0; i < arr.length; i += size) {
chunks.push(arr.slice(i, i + size))
}
return chunks
}
// JSON parsing for fetch
export const json = x => x.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
export const fetchMultiple = (url, arr, header, size = 100) => {
const subArrays = chunk(arr, size)
return Promise.all(subArrays.map(subArr => fetch(url + subArr.join(), header)))
}
export const jsonMultiple = responses => Promise.all(responses.map(json))
// Change bases
export const toBase36 = number => parseInt(number, 10).toString(36)
export const toBase10 = numberString => parseInt(numberString, 36)
@ -18,9 +42,6 @@ export const isDeleted = testString => testString === '[deleted]'
// Default thumbnails for reddit threads
export const redditThumbnails = ['self', 'default', 'image', 'nsfw']
// JSON parsing for fetch
export const json = x => x.json()
// Parse comments
export const parse = text => markdown.render(text)

View file

@ -5,71 +5,71 @@ const CleanWebpackPlugin = require('clean-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const extractSass = new ExtractTextPlugin({
filename: 'bundle.[contenthash].css',
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
},
}
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,
},
}

View file

@ -3,7 +3,7 @@ const merge = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
devServer: {
historyApiFallback: true
},
});
devServer: {
historyApiFallback: true,
},
});

View file

@ -4,10 +4,10 @@ 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')
})
]
});
plugins: [
new UglifyJSPlugin(),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
],
});