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,
children,
index,
noButtons,
} = this.props
return (
<div className={[_s.d, _s.w360PX, _s.px2, _s.bgSecondary, _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(' ')}>
{children}
</div>
@ -32,6 +39,7 @@ DeckColumn.propTypes = {
subtitle: PropTypes.string,
icon: PropTypes.string,
index: PropTypes.number,
noButtons: PropTypes.bool,
}
export default DeckColumn

View File

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

View File

@ -21,6 +21,9 @@ class DeckColumnAddModal extends React.PureComponent {
case 'group':
//
break
case 'hashtag':
//
break
default:
this.props.dispatch(setDeckColumnAtIndex(column))
this.props.onClose()
@ -51,11 +54,16 @@ class DeckColumnAddModal extends React.PureComponent {
<DeckColumnAddModalButton icon='like' type='Likes' onClick={() => this.onAdd('likes')} />
<DeckColumnAddModalButton icon='bookmark' type='Bookmarks' onClick={() => this.onAdd('bookmarks')} />
</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='pencil' type='Compose' onClick={() => this.onAdd('compose')} />
<DeckColumnAddModalButton icon='group' type='Group Timeline' onClick={() => this.onAdd('group')} />
</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>
</ModalLayout>
)

View File

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

View File

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

View File

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

View File

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

View File

@ -18,6 +18,7 @@ import {
TIMELINE_INJECTION_PWA,
TIMELINE_INJECTION_SHOP,
TIMELINE_INJECTION_USER_SUGGESTIONS,
GAB_DECK_MAX_ITEMS,
} from '../constants'
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'
import uuid from '../utils/uuid'
@ -92,9 +93,8 @@ export default function settings(state = initialState, action) {
case LIST_DELETE_SUCCESS:
return filterDeadListColumns(state, action.id)
case DECK_SET_COLUMN_AT_INDEX:
// : todo : max: 12
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)
case DECK_DELETE_COLUMN_AT_INDEX:
return state.deleteIn(['gabDeckOrder', action.index])

View File

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