at the end of the day, it was inevitable
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
import React from 'react'
|
||||
import { createDevTools } from 'redux-devtools'
|
||||
import LogMonitor from 'redux-devtools-log-monitor'
|
||||
import DockMonitor from 'redux-devtools-dock-monitor'
|
||||
|
||||
export default createDevTools(
|
||||
<DockMonitor
|
||||
defaultIsVisible={false}
|
||||
toggleVisibilityKey='ctrl-h'
|
||||
changePositionKey='ctrl-q' >
|
||||
<LogMonitor preserveScrollTop={false} />
|
||||
</DockMonitor>
|
||||
)
|
||||
@@ -0,0 +1,46 @@
|
||||
import {createAction} from 'redux-actions'
|
||||
import {addAlert} from '../modules/common/alerts'
|
||||
|
||||
export const tokenInject = (fn) =>
|
||||
(dispatch, getState) =>
|
||||
fn(dispatch, getState, getState().getIn(['common', 'auth', 'token']))
|
||||
|
||||
export const thunkAction = (actionName, actionMethod, emitPending = false, customPendingAction = false) => {
|
||||
const fulfilledAction = createAction(`${actionName} fulfilled`)
|
||||
const pendingAction = customPendingAction ||
|
||||
createAction(`${actionName} pending`, (isPending, success) => ({isPending, success}))
|
||||
|
||||
return (...args) => {
|
||||
return tokenInject((dispatch, getState, token) => {
|
||||
|
||||
const fulfilled = (...fArgs) => {
|
||||
dispatch(fulfilledAction(...fArgs))
|
||||
emitPending && dispatch(pendingAction(false, true))
|
||||
}
|
||||
|
||||
const onError = (errors) => {
|
||||
dispatch(addAlert(errors))
|
||||
emitPending && dispatch(pendingAction(false, false))
|
||||
}
|
||||
|
||||
emitPending && dispatch(pendingAction(true))
|
||||
|
||||
let result
|
||||
try {
|
||||
result = actionMethod(...args, {dispatch, getState, token, fulfilled})
|
||||
} catch (e) {
|
||||
console.error('Error in thunkAction()')
|
||||
console.error(e)
|
||||
throw e
|
||||
}
|
||||
if (result instanceof Promise) {
|
||||
result.catch(onError)
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const routerSelectLocationState = (state) => {
|
||||
return state.get('routing').toJS()
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import { connect } from 'react-redux'
|
||||
import { rootActions } from '../root'
|
||||
|
||||
//This is recursive version of standard redux bindActionCreators
|
||||
function bindActionCreatorsRecursive (actions, dispatch) {
|
||||
if (typeof dispatch !== 'function') {
|
||||
throw new TypeError('Action wrapper needs a dispatch function')
|
||||
}
|
||||
return Object.keys(actions).reduce(function (acc, key) {
|
||||
if (typeof actions[key] === 'function') {
|
||||
acc[key] = function () {
|
||||
return dispatch(actions[key].apply(null, arguments))
|
||||
}
|
||||
} else if (actions[key] !== null && typeof actions[key] === 'object') {
|
||||
acc[key] = bindActionCreatorsRecursive(actions[key], dispatch)
|
||||
}
|
||||
|
||||
return acc
|
||||
}, {})
|
||||
}
|
||||
|
||||
export default function reduxConnect (storePropName = 'store', storePath) {
|
||||
return (component) => {
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
[storePropName]: storePath ? state.getIn(storePath).toJS() : state.toJS()
|
||||
})
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
actions: bindActionCreatorsRecursive(rootActions, dispatch)
|
||||
})
|
||||
|
||||
return connect(mapStateToProps, mapDispatchToProps)(component)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export function reduxActions () {
|
||||
return (component) => {
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
actions: bindActionCreatorsRecursive(rootActions, dispatch)
|
||||
})
|
||||
|
||||
return connect(null, mapDispatchToProps)(component)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
export const ADV_FILTERS_LIMIT = 7 //for advanced filters client-side paging
|
||||
|
||||
export const filtersFromServerFormat = function (advancedFilters) {
|
||||
const allFilters = {}
|
||||
const pages = {}
|
||||
for (let groupName in advancedFilters) {
|
||||
let filters = advancedFilters[groupName].data
|
||||
allFilters[groupName] = filters
|
||||
pages[groupName] = {count: ADV_FILTERS_LIMIT, totalCount: filters.length}
|
||||
}
|
||||
return {pages, allFilters}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
import {fromJS} from 'immutable'
|
||||
import {ADV_FILTERS_LIMIT} from '../../modules/appState/search'
|
||||
/**
|
||||
* Commenting helpers
|
||||
*/
|
||||
|
||||
export const indexById = (arr, id) => arr.findIndex((a) => a.id === id)
|
||||
|
||||
const changeArticleComments = (fn) => {
|
||||
return (articles, articleId, comment) => {
|
||||
let result = articles.toJS()
|
||||
const articleIndex = indexById(result, articleId)
|
||||
if (articleIndex !== -1) {
|
||||
fn(result[articleIndex].comments, comment)
|
||||
} else {
|
||||
console.error(`search.js - cannot find article with id ${articleId}`)
|
||||
}
|
||||
return fromJS(result)
|
||||
}
|
||||
}
|
||||
|
||||
export const loadMoreComments = changeArticleComments((comments, newComments) => {
|
||||
comments.data = comments.data.concat(newComments)
|
||||
comments.count += newComments.length
|
||||
})
|
||||
|
||||
export const addComment = changeArticleComments((comments, comment) => {
|
||||
comments.data.unshift(comment)
|
||||
comments.count++
|
||||
comments.totalCount++
|
||||
})
|
||||
|
||||
export const updateComment = changeArticleComments((comments, comment) => {
|
||||
const commentIndex = indexById(comments.data, comment.id)
|
||||
if (commentIndex !== -1) {
|
||||
comments.data[commentIndex] = comment
|
||||
} else {
|
||||
console.error(`search.js::updateComment() cannot find comment with id ${comment.id}`)
|
||||
}
|
||||
})
|
||||
|
||||
export const deleteComment = changeArticleComments((comments, commentId) => {
|
||||
const commentIndex = indexById(comments.data, commentId)
|
||||
if (commentIndex !== -1) {
|
||||
comments.data.splice(commentIndex, 1)
|
||||
comments.count--
|
||||
comments.totalCount--
|
||||
} else {
|
||||
console.error(`search.js::deleteComment() cannot find comment with id ${commentId}`)
|
||||
}
|
||||
})
|
||||
|
||||
//End commenting helpers
|
||||
|
||||
//Ensure that "selectedFilters" is always in "allFilters"
|
||||
//allFilters = {groupName: [{value: "", count: ""}] }
|
||||
//selectedFilters = {groupName: {"value": "count"}}
|
||||
export const mergeAdvancedFilters = (allFilters, selectedFilters, pages) => {
|
||||
|
||||
const _insertFilter = (groupName, value, count) => {
|
||||
let group = allFilters[groupName] || (allFilters[groupName] = [])
|
||||
|
||||
const found = group.find((item) => item.value === value)
|
||||
if (!found) {
|
||||
group.unshift({value, count})
|
||||
let pageGroup = pages[groupName] || (pages[groupName] = {count: ADV_FILTERS_LIMIT, totalCount: 0})
|
||||
pageGroup.totalCount++
|
||||
}
|
||||
}
|
||||
|
||||
for (let groupName in selectedFilters) {
|
||||
if (selectedFilters.hasOwnProperty(groupName)) {
|
||||
let group = selectedFilters[groupName]
|
||||
|
||||
for (let value in group) {
|
||||
if (group.hasOwnProperty(value)) {
|
||||
_insertFilter(groupName, value, group[value])
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
import {fromJS} from 'immutable'
|
||||
|
||||
const categoriesFromState = (fn) => {
|
||||
return (state, ...args) => {
|
||||
const categories = state.getIn(['appState', 'sidebar', 'categories']).toJS()
|
||||
return fromJS(fn(categories, ...args))
|
||||
}
|
||||
}
|
||||
|
||||
const walkCategories = (fn) => {
|
||||
return (categories, ...args) => {
|
||||
const walker = (array) => {
|
||||
return array.map((category) => {
|
||||
category.childes = walker(category.childes)
|
||||
fn(category, ...args)
|
||||
return category
|
||||
})
|
||||
}
|
||||
return walker(categories)
|
||||
}
|
||||
}
|
||||
|
||||
const feedHelper = (fn) => categoriesFromState(walkCategories(fn))
|
||||
|
||||
//return categories without deleted feed
|
||||
export const deleteFeed = feedHelper((category, feedCategoryId, feedId) => {
|
||||
if (category.id === feedCategoryId) {
|
||||
category.feeds = category.feeds.filter((feed) => feed.id !== feedId)
|
||||
}
|
||||
})
|
||||
|
||||
export const addFeed = feedHelper((category, feedCategoryId, feed) => {
|
||||
if (category.id === feedCategoryId) {
|
||||
category.feeds.push(feed)
|
||||
}
|
||||
})
|
||||
|
||||
export const renameFeed = feedHelper((category, feedId, newFeedName, feedCategoryId) => {
|
||||
if (category.id === feedCategoryId) {
|
||||
category.feeds = category.feeds.map((feed) => {
|
||||
if (feed.id === feedId) {
|
||||
feed.name = newFeedName
|
||||
}
|
||||
return feed
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const _deleteCategory = (categories, categoryId) => {
|
||||
return categories.map((category) => {
|
||||
if (category.childes.length > 0) {
|
||||
const initChildesLength = category.childes.length
|
||||
category.childes = category.childes.filter((childCategory) => {
|
||||
return childCategory.id !== categoryId
|
||||
})
|
||||
const childesLengthAfterFilter = category.childes.length
|
||||
// if there wasn't deletion continue to search
|
||||
if (childesLengthAfterFilter === initChildesLength) {
|
||||
category.childes = _deleteCategory(category.childes, categoryId)
|
||||
}
|
||||
}
|
||||
return category
|
||||
})
|
||||
}
|
||||
|
||||
//return categories without deleted category
|
||||
export const deleteCategory = categoriesFromState(_deleteCategory)
|
||||
|
||||
export const addCategory = feedHelper((category, parentId, newCategory) => {
|
||||
if (category.id === parentId) {
|
||||
category.childes.push(newCategory)
|
||||
}
|
||||
})
|
||||
|
||||
export const renameCategory = feedHelper((category, categoryId, newCategoryName) => {
|
||||
if (category.id === categoryId) {
|
||||
category.name = newCategoryName
|
||||
}
|
||||
})
|
||||
|
||||
export const checkIfDraggedCategoryDragToItsChild = (categories, newCategoryId) => {
|
||||
return categories.find((category) => {
|
||||
if (category.childes.length > 0) {
|
||||
checkIfDraggedCategoryDragToItsChild(category.childes, newCategoryId)
|
||||
}
|
||||
|
||||
return category.id === newCategoryId
|
||||
})
|
||||
}
|
||||
|
||||
export const findFeedById = (categories, feedId) => {
|
||||
let feed = null
|
||||
categories.forEach((category) => {
|
||||
if (!feed && category.feeds.length > 0) {
|
||||
const findResult = category.feeds.filter(feed => feed.id === feedId)
|
||||
feed = findResult[0] || null
|
||||
if (feed) {
|
||||
feed.category = category.id
|
||||
}
|
||||
|
||||
if (!feed && category.childes.length > 0) {
|
||||
feed = findFeedById(category.childes, feedId)
|
||||
}
|
||||
}
|
||||
})
|
||||
return feed
|
||||
}
|
||||
Reference in New Issue
Block a user