diff --git a/src/app/client_config.js b/src/app/client_config.js index de04a57b2..8991daab9 100644 --- a/src/app/client_config.js +++ b/src/app/client_config.js @@ -1,4 +1,4 @@ -import { List } from 'immutable'; +import { fromJSOrdered } from './utils/immutable'; // sometimes it's impossible to use html tags to style coin name, hence usage of _UPPERCASE modifier export const APP_NAME = 'SteemLeo'; @@ -12,7 +12,12 @@ export const APP_ICON = 'steemleo'; export const APP_URL = 'https://www.steemleo.com'; export const APP_DOMAIN = 'www.steemleo.com'; export const SCOT_TAG = 'steemleo'; -export const TAG_LIST = List(['steemleo']); +export const TAG_LIST = fromJSOrdered({ + steemleo: ['dailyleo', 'learnleo', 'leowritingcontest'], + crypto: ['blockchain', 'steem', 'bitcoin'], + investing: ['news', 'politics', 'personalfinance'], + trading: ['analysis'], +}); export const LIQUID_TOKEN = 'Leo'; // sometimes it's impossible to use html tags to style coin name, hence usage of _UPPERCASE modifier export const LIQUID_TOKEN_UPPERCASE = 'LEO'; diff --git a/src/app/components/pages/PostsIndex.jsx b/src/app/components/pages/PostsIndex.jsx index 207cb8102..23666b63c 100644 --- a/src/app/components/pages/PostsIndex.jsx +++ b/src/app/components/pages/PostsIndex.jsx @@ -4,7 +4,7 @@ import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { Link } from 'react-router'; import tt from 'counterpart'; -import { List } from 'immutable'; +import { List, OrderedMap } from 'immutable'; import { actions as fetchDataSagaActions } from 'app/redux/FetchDataSaga'; import constants from 'app/redux/constants'; import shouldComponentUpdate from 'app/utils/shouldComponentUpdate'; @@ -139,13 +139,67 @@ class PostsIndex extends React.Component { onShowSpam = () => { this.setState({ showSpam: !this.state.showSpam }); }; + + searchCategories(cat, parent, categories) { + if (!cat) return { par: parent, cats: categories, found: false }; + + // leaf nodes + if (List.isList(categories)) { + if (categories.includes(cat)) + return { par: parent, cats: categories, found: true }; + else return { par: parent, cats: null, found: false }; + } else { + for (const c of categories.keys()) { + const v = categories.get(c); + if (cat === c && v !== null && !v.isEmpty()) { + return { par: parent, cats: v, found: true }; + } else { + const { par, cats, found } = this.searchCategories( + cat, + c, + v + ); + if (cats !== null && !cats.isEmpty()) { + return { par, cats, found }; + } + } + } + return { par: parent, cats: null, found: false }; + } + } + + buildCategories(cat, parent, categories) { + if (!categories) return this.props.categories; + + if (!cat) { + return categories; + } else { + let cats = OrderedMap(); + if (categories.includes(cat)) cats = categories; + else cats = cats.set(cat, categories); + if (parent !== null) { + const children = cats; + cats = OrderedMap(); + cats = cats.set(parent, children); + } + return cats; + } + } + render() { let { category, order = constants.DEFAULT_SORT_ORDER, } = this.props.routeParams; - const { categories, discussions, pinned } = this.props; + const { discussions, pinned } = this.props; + const { par, cats, found } = this.searchCategories( + category, + null, + this.props.categories + ); + const categories = this.buildCategories(category, par, cats); + const max_levels = category && found ? 3 : 2; let topics_order = order; let posts = List(); @@ -266,6 +320,7 @@ class PostsIndex extends React.Component { current={category} categories={categories} compact={true} + levels={max_levels} /> @@ -322,6 +377,7 @@ class PostsIndex extends React.Component { compact={false} username={this.props.username} categories={categories} + levels={max_levels} /> { const handleChange = selectedOption => { browserHistory.push(selectedOption.value); @@ -33,6 +35,40 @@ const Topics = ({ return opts['default']; }; + const buildPrefix = level => { + let a = ''; + for (let i = 0; i < level; i++) { + a = a + '>'; + } + return a; + }; + + const buildCategories = (categories, level, max) => { + const prefix = buildPrefix(level); + if (List.isList(categories)) { + return categories.map(c => prefix + c); + } else { + let c_list = List(); + categories.mapKeys((c, v) => { + c_list = c_list.push(prefix + c); + // only display max levels + if (level < max - 1) { + c_list = c_list.concat(buildCategories(v, level + 1, max)); + } + }); + return c_list; + } + }; + + const parseCategory = cat => { + const tag = cat.replace(/\>/g, ''); + const label = cat.replace(/\>/g, '\u00a0\u00a0\u00a0'); + return { tag, label }; + }; + + const max_levels = levels || 3; + categories = buildCategories(categories, 0, levels); + if (compact) { const extras = username => { const ex = { @@ -53,8 +89,9 @@ const Topics = ({ const opts = extras(username).concat( categories .map(cat => { - const link = order ? `/${order}/${cat}` : `/${cat}`; - return { value: link, label: cat }; + const { tag, label } = parseCategory(cat); + const link = order ? `/${order}/${tag}` : `/${tag}`; + return { value: link, label: label }; }) .toJS() ); @@ -68,15 +105,16 @@ const Topics = ({ ); } else { const categoriesLinks = categories.map(cat => { - const link = order ? `/${order}/${cat}` : `/hot/${cat}`; + const { tag, label } = parseCategory(cat); + const link = order ? `/${order}/${tag}` : `/hot/${tag}`; return ( -
  • +
  • - {cat} + {label}
  • ); diff --git a/src/app/utils/immutable.js b/src/app/utils/immutable.js new file mode 100644 index 000000000..2cf7d946c --- /dev/null +++ b/src/app/utils/immutable.js @@ -0,0 +1,13 @@ +import { Seq } from 'immutable'; + +export function fromJSOrdered(js) { + return typeof js !== 'object' || js === null + ? js + : Array.isArray(js) + ? Seq(js) + .map(fromJSOrdered) + .toList() + : Seq(js) + .map(fromJSOrdered) + .toOrderedMap(); +}