Progess on Gab Deck

Progess on Gab Deck
This commit is contained in:
mgabdev 2020-12-08 23:57:51 -05:00
parent 998f00ae48
commit bb7348fc61
10 changed files with 127 additions and 67 deletions

View File

@ -11,12 +11,19 @@ class DeckColumn extends React.PureComponent {
icon, icon,
children, children,
index, index,
noButtons,
} = this.props } = this.props
return ( return (
<div className={[_s.d, _s.w360PX, _s.px2, _s.bgSecondary, _s.h100VH].join(' ')}> <div className={[_s.d, _s.w360PX, _s.px2, _s.bgSecondary, _s.h100VH].join(' ')}>
<div className={[_s.d, _s.w100PC, _s.bgPrimary, _s.h100VH].join(' ')}> <div className={[_s.d, _s.w100PC, _s.bgPrimary, _s.h100VH].join(' ')}>
<DeckColumnHeader title={title} subtitle={subtitle} icon={icon} index={index} /> <DeckColumnHeader
title={title}
subtitle={subtitle}
icon={icon}
index={index}
noButtons={noButtons}
/>
<div className={[_s.d, _s.w100PC, _s.overflowYScroll, _s.boxShadowNone, _s.posAbs, _s.top60PX, _s.left0, _s.right0, _s.bottom0].join(' ')}> <div className={[_s.d, _s.w100PC, _s.overflowYScroll, _s.boxShadowNone, _s.posAbs, _s.top60PX, _s.left0, _s.right0, _s.bottom0].join(' ')}>
{children} {children}
</div> </div>
@ -32,6 +39,7 @@ DeckColumn.propTypes = {
subtitle: PropTypes.string, subtitle: PropTypes.string,
icon: PropTypes.string, icon: PropTypes.string,
index: PropTypes.number, index: PropTypes.number,
noButtons: PropTypes.bool,
} }
export default DeckColumn export default DeckColumn

View File

@ -22,6 +22,7 @@ class DeckColumnHeader extends React.PureComponent {
subtitle, subtitle,
icon, icon,
children, children,
noButtons,
} = this.props } = this.props
return ( return (
@ -40,7 +41,7 @@ class DeckColumnHeader extends React.PureComponent {
{ !!subtitle && <Text className={_s.ml5} color='secondary'>{subtitle}</Text> } { !!subtitle && <Text className={_s.ml5} color='secondary'>{subtitle}</Text> }
</div> </div>
{ {
!!title && !!title && !noButtons &&
<div className={[_s.d, _s.flexRow, _s.aiCenter, _s.mlAuto, _s.jcCenter].join(' ')}> <div className={[_s.d, _s.flexRow, _s.aiCenter, _s.mlAuto, _s.jcCenter].join(' ')}>
<Button <Button
isNarrow isNarrow
@ -71,6 +72,7 @@ DeckColumnHeader.propTypes = {
subtitle: PropTypes.string, subtitle: PropTypes.string,
icon: PropTypes.string, icon: PropTypes.string,
index: PropTypes.number, index: PropTypes.number,
noButtons: PropTypes.bool,
} }
export default connect()(DeckColumnHeader) export default connect()(DeckColumnHeader)

View File

@ -21,6 +21,9 @@ class DeckColumnAddModal extends React.PureComponent {
case 'group': case 'group':
// //
break break
case 'hashtag':
//
break
default: default:
this.props.dispatch(setDeckColumnAtIndex(column)) this.props.dispatch(setDeckColumnAtIndex(column))
this.props.onClose() this.props.onClose()
@ -51,11 +54,16 @@ class DeckColumnAddModal extends React.PureComponent {
<DeckColumnAddModalButton icon='like' type='Likes' onClick={() => this.onAdd('likes')} /> <DeckColumnAddModalButton icon='like' type='Likes' onClick={() => this.onAdd('likes')} />
<DeckColumnAddModalButton icon='bookmark' type='Bookmarks' onClick={() => this.onAdd('bookmarks')} /> <DeckColumnAddModalButton icon='bookmark' type='Bookmarks' onClick={() => this.onAdd('bookmarks')} />
</div> </div>
<div className={[_s.d, _s.pl10, _s.pb10, _s.flexRow, _s.aiCenter, _s.jcCenter].join(' ')}> <div className={[_s.d, _s.pl10, _s.borderBottom1PX, _s.borderColorSecondary, _s.flexRow, _s.aiCenter, _s.jcCenter].join(' ')}>
<DeckColumnAddModalButton icon='pro' type='PRO Timeline' onClick={() => this.onAdd('pro')} /> <DeckColumnAddModalButton icon='pro' type='PRO Timeline' onClick={() => this.onAdd('pro')} />
<DeckColumnAddModalButton icon='pencil' type='Compose' onClick={() => this.onAdd('compose')} /> <DeckColumnAddModalButton icon='pencil' type='Compose' onClick={() => this.onAdd('compose')} />
<DeckColumnAddModalButton icon='group' type='Group Timeline' onClick={() => this.onAdd('group')} /> <DeckColumnAddModalButton icon='group' type='Group Timeline' onClick={() => this.onAdd('group')} />
</div> </div>
<div className={[_s.d, _s.pl10, _s.pb10, _s.flexRow, _s.aiCenter, _s.jcCenter].join(' ')}>
<DeckColumnAddModalButton icon='apps' type='Hashtag' onClick={() => this.onAdd('hashtag')} />
<DeckColumnAddModalButton icon='explore' type='Explore' onClick={() => this.onAdd('explore')} />
<DeckColumnAddModalButton icon='news' type='News' onClick={() => this.onAdd('news')} />
</div>
</div> </div>
</ModalLayout> </ModalLayout>
) )

View File

@ -49,6 +49,8 @@ class DeckSidebar extends ImmutablePureComponent {
render() { render() {
const { account, logoDisabled } = this.props const { account, logoDisabled } = this.props
const isPro = !!account ? account.get('is_pro') : false
return ( return (
<div className={[_s.d, _s.z4, _s.w76PX, _s.w100PC].join(' ')}> <div className={[_s.d, _s.z4, _s.w76PX, _s.w100PC].join(' ')}>
@ -83,7 +85,7 @@ class DeckSidebar extends ImmutablePureComponent {
<Divider isSmall /> <Divider isSmall />
<NavigationBarButton title='&nbsp;' icon='add' onClick={this.handleOnOpenNewColumnModel} /> { isPro && <NavigationBarButton title='&nbsp;' icon='add' onClick={this.handleOnOpenNewColumnModel} /> }
</div> </div>
<div className={[_s.d, _s.mtAuto, _s.aiCenter].join(' ')}> <div className={[_s.d, _s.mtAuto, _s.aiCenter].join(' ')}>

View File

@ -166,7 +166,8 @@ export const TIMELINE_INJECTION_WEIGHT_MULTIPLIER = 100
export const TIMELINE_INJECTION_WEIGHT_SUBTRACTOR = 0.005 export const TIMELINE_INJECTION_WEIGHT_SUBTRACTOR = 0.005
export const TIMELINE_INJECTION_WEIGHT_MIN = 0.01 export const TIMELINE_INJECTION_WEIGHT_MIN = 0.01
export const GAB_DECK_OPTIONS = ['home', 'user.id', 'notifications', 'list.id', 'likes', 'bookmarks', 'pro', 'compose', 'group.id'] export const GAB_DECK_MAX_ITEMS = 8
export const GAB_DECK_OPTIONS = ['home', 'user.id', 'notifications', 'list.id', 'likes', 'bookmarks', 'pro', 'compose', 'group.id', 'explore', 'news', 'news.id', 'hashtag.id']
export const TRENDS_RSS_SOURCES = [ export const TRENDS_RSS_SOURCES = [
{'id':'5daf64b18e955e2433b0f5ce','title':'Breitbart'}, {'id':'5daf64b18e955e2433b0f5ce','title':'Breitbart'},

View File

@ -8,16 +8,20 @@ import {
sortableElement, sortableElement,
} from 'react-sortable-hoc' } from 'react-sortable-hoc'
import { me, meUsername} from '../initial_state' import { me, meUsername} from '../initial_state'
import {
GAB_DECK_MAX_ITEMS,
MODAL_PRO_UPGRADE,
} from '../constants'
import { import {
deckConnect, deckConnect,
deckDisconnect, deckDisconnect,
updateDeckColumnAtIndex, updateDeckColumnAtIndex,
} from '../actions/deck' } from '../actions/deck'
import { import { saveSettings } from '../actions/settings'
saveSettings, import { openModal } from '../actions/modal'
} from '../actions/settings'
import WrappedBundle from './ui/util/wrapped_bundle' import WrappedBundle from './ui/util/wrapped_bundle'
import DeckColumn from '../components/deck_column' import DeckColumn from '../components/deck_column'
import Text from '../components/text'
import { import {
AccountTimeline, AccountTimeline,
Compose, Compose,
@ -27,6 +31,8 @@ import {
HashtagTimeline, HashtagTimeline,
BookmarkedStatuses, BookmarkedStatuses,
ProTimeline, ProTimeline,
News,
ExploreTimeline,
} from './ui/util/async_components' } from './ui/util/async_components'
class Deck extends React.PureComponent { class Deck extends React.PureComponent {
@ -35,22 +41,21 @@ class Deck extends React.PureComponent {
router: PropTypes.object, router: PropTypes.object,
} }
componentDidMount () {
this.props.connectDeck()
}
componentWillUnmount() {
this.props.disconnectDeck()
}
componentDidMount () { componentDidMount () {
this.props.dispatch(deckConnect()) this.props.dispatch(deckConnect())
console.log("this.props.isPro:", this.props)
if (!this.props.isPro) this.handleOnOpenProUpgradeModal()
} }
componentWillUnmount() { componentWillUnmount() {
this.props.dispatch(deckDisconnect()) this.props.dispatch(deckDisconnect())
} }
handleOnOpenProUpgradeModal = () => {
console.log("handleOnOpenProUpgradeModal")
this.props.dispatch(openModal(MODAL_PRO_UPGRADE))
}
onSortEnd = ({oldIndex, newIndex}) => { onSortEnd = ({oldIndex, newIndex}) => {
this.props.dispatch(updateDeckColumnAtIndex(oldIndex, newIndex)) this.props.dispatch(updateDeckColumnAtIndex(oldIndex, newIndex))
} }
@ -66,7 +71,7 @@ class Deck extends React.PureComponent {
} }
getDeckColumn = (deckColumn, index) => { getDeckColumn = (deckColumn, index) => {
if (!deckColumn) return null if (!deckColumn || !isPro) return null
let Component = null let Component = null
let componentParams = {} let componentParams = {}
@ -105,10 +110,22 @@ class Deck extends React.PureComponent {
icon = 'pro' icon = 'pro'
Component = ProTimeline Component = ProTimeline
break break
case 'news':
title = 'News'
icon = 'news'
Component = News
componentParams = { isSmall: true }
break
case 'explore':
title = 'Explore'
icon = 'explore'
Component = ExploreTimeline
break
default: default:
break break
} }
// : todo :
if (!Component) { if (!Component) {
if (deckColumn.indexOf('user.') > -1) { if (deckColumn.indexOf('user.') > -1) {
@ -116,6 +133,10 @@ class Deck extends React.PureComponent {
} else if (deckColumn.indexOf('group.') > -1) { } else if (deckColumn.indexOf('group.') > -1) {
} else if (deckColumn.indexOf('news.') > -1) {
} else if (deckColumn.indexOf('hashtag.') > -1) {
} }
} }
@ -135,12 +156,9 @@ class Deck extends React.PureComponent {
} }
render () { render () {
const { gabDeckOrder } = this.props const { gabDeckOrder, isPro } = this.props
console.log("gabDeckOrder:", gabDeckOrder)
const isEmpty = gabDeckOrder.size === 0 const isEmpty = gabDeckOrder.size === 0
// : todo : max: 12
return ( return (
<SortableContainer <SortableContainer
@ -150,17 +168,27 @@ class Deck extends React.PureComponent {
shouldCancelStart={this.onShouldCancelStart} shouldCancelStart={this.onShouldCancelStart}
> >
{ {
isEmpty && (isEmpty || !isPro) &&
<React.Fragment> <React.Fragment>
<DeckColumn title='Compose' icon='pencil'> <DeckColumn title='Compose' icon='pencil' noButtons>
<WrappedBundle component={Compose} /> <WrappedBundle component={Compose} />
</DeckColumn> </DeckColumn>
{
!isPro &&
<DeckColumn title='Gab Deck for GabPRO' icon='pro' noButtons>
<div className={[_s.d, _s.px15, _s.py15].join(' ')}>
<Text>
Gab Deck for GabPRO
</Text>
</div>
</DeckColumn>
}
<DeckColumn /> <DeckColumn />
</React.Fragment> </React.Fragment>
} }
{ {
!isEmpty && !isEmpty && isPro &&
gabDeckOrder.map((deckOption, i) => this.getDeckColumn(deckOption, i)) gabDeckOrder.splice(0, GAB_DECK_MAX_ITEMS).map((deckOption, i) => this.getDeckColumn(deckOption, i))
} }
</SortableContainer> </SortableContainer>
) )
@ -181,10 +209,12 @@ const SortableContainer = sortableContainer(({children}) => (
)) ))
const mapStateToProps = (state) => ({ const mapStateToProps = (state) => ({
isPro: state.getIn(['accounts', me, 'is_pro']),
gabDeckOrder: state.getIn(['settings', 'gabDeckOrder']), gabDeckOrder: state.getIn(['settings', 'gabDeckOrder']),
}) })
Deck.propTypes = { Deck.propTypes = {
isPro: PropTypes.bool.isRequired,
gabDeckOrder: ImmutablePropTypes.list, gabDeckOrder: ImmutablePropTypes.list,
} }

View File

@ -86,7 +86,7 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(openPopover(POPOVER_CHAT_CONVERSATION_OPTIONS, { dispatch(openPopover(POPOVER_CHAT_CONVERSATION_OPTIONS, {
chatConversationId, chatConversationId,
targetRef, targetRef,
position: 'bottom', position: 'left-end',
})) }))
}, },
}) })

View File

@ -11,8 +11,6 @@ import Button from '../components/button'
import Text from '../components/text' import Text from '../components/text'
import TrendsItem from '../components/trends_item' import TrendsItem from '../components/trends_item'
import PreviewCardItem from '../components/preview_card_item' import PreviewCardItem from '../components/preview_card_item'
import ResponsiveClassesComponent from './ui/util/responsive_classes_component'
import Responsive from './ui/util/responsive_component'
import WrappedBundle from './ui/util/wrapped_bundle' import WrappedBundle from './ui/util/wrapped_bundle'
import { import {
GabNewsPanel, GabNewsPanel,
@ -23,11 +21,15 @@ import {
TrendsHeadlinesPanel, TrendsHeadlinesPanel,
TrendsRSSPanel, TrendsRSSPanel,
} from './ui/util/async_components' } from './ui/util/async_components'
import { getWindowDimension } from '../utils/is_mobile'
const initialState = getWindowDimension()
class News extends React.PureComponent { class News extends React.PureComponent {
state = { state = {
lazyLoaded: false, lazyLoaded: false,
width: initialState.width,
} }
componentDidMount() { componentDidMount() {
@ -35,10 +37,22 @@ class News extends React.PureComponent {
this.documentElement = document.scrollingElement || document.documentElement this.documentElement = document.scrollingElement || document.documentElement
this.window.addEventListener('scroll', this.handleScroll) this.window.addEventListener('scroll', this.handleScroll)
this.handleResize()
window.addEventListener('keyup', this.handleKeyUp, false)
window.addEventListener('resize', this.handleResize, false)
} }
componentWillUnmount() { componentWillUnmount() {
this.detachScrollListener() this.detachScrollListener()
window.removeEventListener('keyup', this.handleKeyUp)
window.removeEventListener('resize', this.handleResize, false)
}
handleResize = () => {
const { width } = getWindowDimension()
this.setState({ width })
} }
detachScrollListener = () => { detachScrollListener = () => {
@ -57,56 +71,50 @@ class News extends React.PureComponent {
}, 150, { trailing: true }) }, 150, { trailing: true })
render() { render() {
const { children } = this.props const { children, isSmall } = this.props
const { lazyLoaded } = this.state const { lazyLoaded, width } = this.state
// const orderXS = ['headlines', 'breaking', 'trending_links', 'latest_from_gab', 'gab_news', 'trends_feeds'] const isXS = width <= BREAKPOINT_EXTRA_SMALL
return ( if (isXS || isSmall) {
<div className={[_s.d, _s.w100PC].join(' ')}> return (
<div className={[_s.d, _s.w100PC].join(' ')}>
<ResponsiveClassesComponent <div className={[_s.d, _s.pt15].join(' ')}>
classNames={[_s.d, _s.flexRow, _s.w100PC, _s.overflowHidden].join(' ')} <div className={[_s.d, _s.w100PC].join(' ')}>
classNamesXS={[_s.d, _s.pt15].join(' ')}
>
<ResponsiveClassesComponent
classNames={[_s.d, _s.pr15, _s.w50PC].join(' ')}
classNamesXS={[_s.d, _s.w100PC].join(' ')}
>
{ /* DESKTOP */ }
<Responsive min={BREAKPOINT_EXTRA_SMALL}>
<WrappedBundle component={TrendsHeadlinesPanel} />
<WrappedBundle component={TrendsBreakingPanel} componentParams={{ hideReadMore: 1 }} />
<WrappedBundle component={LatestFromGabPanel} componentParams={{ isLazy: true, shouldLoad: lazyLoaded }} />
</Responsive>
{ /* MOBILE */ }
<Responsive max={BREAKPOINT_EXTRA_SMALL}>
<WrappedBundle component={TrendsHeadlinesPanel} /> <WrappedBundle component={TrendsHeadlinesPanel} />
<WrappedBundle component={TrendsBreakingPanel} componentParams={{ hideReadMore: 1 }} /> <WrappedBundle component={TrendsBreakingPanel} componentParams={{ hideReadMore: 1 }} />
<WrappedBundle component={PopularLinksPanel} componentParams={{ isLazy: true, shouldLoad: lazyLoaded }} /> <WrappedBundle component={PopularLinksPanel} componentParams={{ isLazy: true, shouldLoad: lazyLoaded }} />
<WrappedBundle component={LatestFromGabPanel} componentParams={{ isLazy: true, shouldLoad: lazyLoaded }} /> <WrappedBundle component={LatestFromGabPanel} componentParams={{ isLazy: true, shouldLoad: lazyLoaded }} />
<WrappedBundle component={GabNewsPanel} componentParams={{ isLazy: true, shouldLoad: lazyLoaded }} /> <WrappedBundle component={GabNewsPanel} componentParams={{ isLazy: true, shouldLoad: lazyLoaded }} />
<WrappedBundle component={TrendsFeedsPanel} /> <WrappedBundle component={TrendsFeedsPanel} />
</Responsive>
</ResponsiveClassesComponent>
{ /* DESKTOP */ }
<Responsive min={BREAKPOINT_EXTRA_SMALL}>
<div classNames={[_s.d, _s.w50PC].join(' ')}>
<WrappedBundle component={PopularLinksPanel} />
<WrappedBundle component={TrendsFeedsPanel} />
<WrappedBundle component={GabNewsPanel} componentParams={{ isLazy: true, shouldLoad: lazyLoaded }} />
</div> </div>
</Responsive> </div>
</div>
</ResponsiveClassesComponent> )
}
return (
<div className={[_s.d, _s.w100PC].join(' ')}>
<div className={[_s.d, _s.flexRow, _s.w100PC, _s.overflowHidden].join(' ')}>
<div className={[_s.d, _s.pr15, _s.w50PC].join(' ')}>
<WrappedBundle component={TrendsHeadlinesPanel} />
<WrappedBundle component={TrendsBreakingPanel} componentParams={{ hideReadMore: 1 }} />
<WrappedBundle component={LatestFromGabPanel} componentParams={{ isLazy: true, shouldLoad: lazyLoaded }} />
</div>
<div classNames={[_s.d, _s.w50PC].join(' ')}>
<WrappedBundle component={PopularLinksPanel} />
<WrappedBundle component={TrendsFeedsPanel} />
<WrappedBundle component={GabNewsPanel} componentParams={{ isLazy: true, shouldLoad: lazyLoaded }} />
</div>
</div>
</div> </div>
) )
} }
} }
News.propTypes = {
isSmall: PropTypes.bool,
}
export default News export default News

View File

@ -18,6 +18,7 @@ import {
TIMELINE_INJECTION_PWA, TIMELINE_INJECTION_PWA,
TIMELINE_INJECTION_SHOP, TIMELINE_INJECTION_SHOP,
TIMELINE_INJECTION_USER_SUGGESTIONS, TIMELINE_INJECTION_USER_SUGGESTIONS,
GAB_DECK_MAX_ITEMS,
} from '../constants' } from '../constants'
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable' import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'
import uuid from '../utils/uuid' import uuid from '../utils/uuid'
@ -92,9 +93,8 @@ export default function settings(state = initialState, action) {
case LIST_DELETE_SUCCESS: case LIST_DELETE_SUCCESS:
return filterDeadListColumns(state, action.id) return filterDeadListColumns(state, action.id)
case DECK_SET_COLUMN_AT_INDEX: case DECK_SET_COLUMN_AT_INDEX:
// : todo : max: 12
const sizeOfDeck = state.get('gabDeckOrder', ImmutableList()).size const sizeOfDeck = state.get('gabDeckOrder', ImmutableList()).size
const newIndex = Math.max(action.index || 0, sizeOfDeck) const newIndex = Math.min(Math.max(action.index || 0, sizeOfDeck), GAB_DECK_MAX_ITEMS)
return state.setIn(['gabDeckOrder', newIndex + 1], action.column).set('saved', false) return state.setIn(['gabDeckOrder', newIndex + 1], action.column).set('saved', false)
case DECK_DELETE_COLUMN_AT_INDEX: case DECK_DELETE_COLUMN_AT_INDEX:
return state.deleteIn(['gabDeckOrder', action.index]) return state.deleteIn(['gabDeckOrder', action.index])

View File

@ -56,7 +56,7 @@
"cssnano": "^4.1.10", "cssnano": "^4.1.10",
"detect-passive-events": "^1.0.2", "detect-passive-events": "^1.0.2",
"dotenv": "^8.0.0", "dotenv": "^8.0.0",
"draft-js": "^0.11.4", "draft-js": "^0.11.7",
"draftjs-to-markdown": "^0.6.0", "draftjs-to-markdown": "^0.6.0",
"emoji-mart": "Gargron/emoji-mart#build", "emoji-mart": "Gargron/emoji-mart#build",
"es6-symbol": "^3.1.1", "es6-symbol": "^3.1.1",
@ -113,6 +113,7 @@
"react-redux-loading-bar": "^4.0.8", "react-redux-loading-bar": "^4.0.8",
"react-router-dom": "^4.1.1", "react-router-dom": "^4.1.1",
"react-router-scroll-4": "^1.0.0-beta.2", "react-router-scroll-4": "^1.0.0-beta.2",
"react-sortable-hoc": "^1.11.0",
"react-stickynode": "^3.0.4", "react-stickynode": "^3.0.4",
"react-swipeable-views": "^0.13.9", "react-swipeable-views": "^0.13.9",
"react-textarea-autosize": "^7.1.0", "react-textarea-autosize": "^7.1.0",