Basic redux integration for thread component (#141)
This commit is contained in:
committed by
Aleksei Gurianov
parent
b6cda91dd9
commit
a02b1502a6
@@ -0,0 +1,28 @@
|
||||
import { siteId, url } from 'common/settings';
|
||||
import { THREAD_SET_COLLAPSE } from './thread.actions';
|
||||
import getCollapsedComments from './getCollapsedComments';
|
||||
import saveCollapsedComments from './saveCollapsedComments';
|
||||
|
||||
const collapsedCommentsMiddleware = ({ getState }) => next => action => {
|
||||
if (action.type === THREAD_SET_COLLAPSE) {
|
||||
const state = getState();
|
||||
const currentCollapsed = state[action.comment.id];
|
||||
|
||||
if (action.collapsed !== currentCollapsed) {
|
||||
const lsCollapsedID = `${siteId}_${url}_${action.comment.id}`;
|
||||
let collapsedComments = getCollapsedComments();
|
||||
|
||||
if (action.collapsed) {
|
||||
collapsedComments = [...new Set(collapsedComments.concat(lsCollapsedID))];
|
||||
} else {
|
||||
collapsedComments = collapsedComments.filter(id => id !== lsCollapsedID);
|
||||
}
|
||||
|
||||
saveCollapsedComments(collapsedComments);
|
||||
}
|
||||
}
|
||||
|
||||
next(action);
|
||||
};
|
||||
|
||||
export default collapsedCommentsMiddleware;
|
||||
@@ -0,0 +1,5 @@
|
||||
import { LS_COLLAPSE_KEY } from 'common/constants';
|
||||
|
||||
const getCollapsedComments = () => JSON.parse(localStorage.getItem(LS_COLLAPSE_KEY) || '[]');
|
||||
|
||||
export default getCollapsedComments;
|
||||
@@ -1 +1,8 @@
|
||||
import { collapsedThreads } from './thread.reducers';
|
||||
import collapsedCommentsMiddleware from './collapsedCommentsMiddleware';
|
||||
|
||||
export const threadReducers = { collapsedThreads };
|
||||
|
||||
export const threadMiddlewares = [collapsedCommentsMiddleware];
|
||||
|
||||
export { default } from './thread';
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { LS_COLLAPSE_KEY } from 'common/constants';
|
||||
|
||||
const saveCollapsedComments = comments => localStorage.setItem(LS_COLLAPSE_KEY, JSON.stringify(comments));
|
||||
|
||||
export default saveCollapsedComments;
|
||||
@@ -0,0 +1,6 @@
|
||||
export const THREAD_SET_COLLAPSE = 'THREAD/COLLAPSE_SET';
|
||||
export const setCollapse = (comment, collapsed) => ({
|
||||
type: THREAD_SET_COLLAPSE,
|
||||
comment,
|
||||
collapsed,
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
import store from 'common/store';
|
||||
|
||||
export const getThreadIsCollapsed = (state, comment) => {
|
||||
let collapsed = state.collapsedThreads[comment.id];
|
||||
|
||||
if (collapsed !== null && collapsed !== undefined) {
|
||||
return collapsed;
|
||||
}
|
||||
|
||||
const config = store.get('config') || {};
|
||||
const score = comment.score || 0;
|
||||
|
||||
return score <= config.critical_score;
|
||||
};
|
||||
@@ -1,63 +1,24 @@
|
||||
/** @jsx h */
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import { LS_COLLAPSE_KEY } from 'common/constants';
|
||||
import { siteId, url } from 'common/settings';
|
||||
import store from 'common/store';
|
||||
import { connect } from 'preact-redux';
|
||||
|
||||
import Comment from 'components/comment';
|
||||
import { setCollapse } from './thread.actions';
|
||||
import { getThreadIsCollapsed } from './thread.getters';
|
||||
|
||||
export default class Thread extends Component {
|
||||
class Thread extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
if (this.props.data && this.props.data.comment) {
|
||||
this.updateCollapsedState(this.props.data.comment);
|
||||
}
|
||||
|
||||
this.onCollapseToggle = this.onCollapseToggle.bind(this);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.data && nextProps.data.comment) {
|
||||
this.updateCollapsedState(nextProps.data.comment);
|
||||
}
|
||||
}
|
||||
|
||||
updateCollapsedState(comment) {
|
||||
const config = store.get('config') || {};
|
||||
const score = comment.score || 0;
|
||||
|
||||
this.lsCollapsedID = `${siteId}_${url}_${comment.id}`;
|
||||
|
||||
this.state = {
|
||||
collapsed:
|
||||
(!this.state.isCollapsedChanged && score <= config.critical_score) ||
|
||||
getCollapsedComments().includes(this.lsCollapsedID),
|
||||
isCollapsedChanged: true,
|
||||
};
|
||||
}
|
||||
|
||||
onCollapseToggle() {
|
||||
const collapsed = !this.state.collapsed;
|
||||
|
||||
this.setState({ collapsed: !this.state.collapsed });
|
||||
|
||||
let collapsedComments = getCollapsedComments();
|
||||
|
||||
if (collapsed) {
|
||||
if (!collapsedComments.includes(this.lsCollapsedID)) {
|
||||
collapsedComments = collapsedComments.concat(this.lsCollapsedID);
|
||||
}
|
||||
} else {
|
||||
collapsedComments = collapsedComments.filter(id => id !== this.lsCollapsedID);
|
||||
}
|
||||
|
||||
saveCollapsedComments(collapsedComments);
|
||||
this.props.setCollapse(this.props.data.comment, !this.props.collapsed);
|
||||
}
|
||||
|
||||
render(props, { collapsed }) {
|
||||
render(props) {
|
||||
const {
|
||||
collapsed,
|
||||
data: { comment, replies = [] },
|
||||
mods = {},
|
||||
} = props;
|
||||
@@ -79,7 +40,7 @@ export default class Thread extends Component {
|
||||
{!collapsed &&
|
||||
!!replies.length &&
|
||||
replies.map(thread => (
|
||||
<Thread
|
||||
<ConnectedThread
|
||||
key={thread.comment.id}
|
||||
data={thread}
|
||||
mods={{ level: mods.level < 5 ? mods.level + 1 : mods.level }}
|
||||
@@ -92,10 +53,9 @@ export default class Thread extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
function getCollapsedComments() {
|
||||
return JSON.parse(localStorage.getItem(LS_COLLAPSE_KEY) || '[]');
|
||||
}
|
||||
const ConnectedThread = connect(
|
||||
(state, props) => ({ collapsed: getThreadIsCollapsed(state, props.data.comment) }),
|
||||
{ setCollapse }
|
||||
)(Thread);
|
||||
|
||||
function saveCollapsedComments(comments) {
|
||||
localStorage.setItem(LS_COLLAPSE_KEY, JSON.stringify(comments));
|
||||
}
|
||||
export default ConnectedThread;
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { siteId, url } from 'common/settings';
|
||||
import { THREAD_SET_COLLAPSE } from './thread.actions';
|
||||
import getCollapsedComments from './getCollapsedComments';
|
||||
|
||||
const collapsedCommentIds = getCollapsedComments()
|
||||
.map(comment => comment.split('_'))
|
||||
.filter(components => components[0] === siteId && components[1] === url)
|
||||
.map(component => component[2]);
|
||||
|
||||
const initialState = collapsedCommentIds.reduce((acc, id) => ({ ...acc, [id]: true }), {});
|
||||
|
||||
export const collapsedThreads = (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case THREAD_SET_COLLAPSE:
|
||||
return {
|
||||
...state,
|
||||
[action.comment.id]: action.collapsed,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import { setCollapse } from './thread.actions';
|
||||
import { collapsedThreads } from './thread.reducers';
|
||||
import { getThreadIsCollapsed } from './thread.getters';
|
||||
|
||||
describe('collapsedThreads', () => {
|
||||
const comment = { id: 1 };
|
||||
|
||||
it('should set collapsed to true', () => {
|
||||
const collapsed = true;
|
||||
const action = setCollapse(comment, collapsed);
|
||||
|
||||
const newState = {
|
||||
collapsedThreads: collapsedThreads({}, action),
|
||||
};
|
||||
|
||||
expect(getThreadIsCollapsed(newState, comment)).toEqual(collapsed);
|
||||
});
|
||||
|
||||
it('should set collapsed to false', () => {
|
||||
const collapsed = false;
|
||||
const action = setCollapse(comment, collapsed);
|
||||
|
||||
const newState = {
|
||||
collapsedThreads: collapsedThreads({}, action),
|
||||
};
|
||||
|
||||
expect(getThreadIsCollapsed(newState, comment)).toEqual(collapsed);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user