Added WIP for new News page Panels

• Added:
- WIP for new News page Panels: GabNewsPanel, LatestFromGabPanel, PopularLinks, TrendsFeedsPanel, TrendsHeadlinesPanel
- all imports for above in async_components

• Todo:
- Remove tests
This commit is contained in:
mgabdev 2020-11-06 23:28:30 -06:00
parent a4ee21282d
commit 4c4894ac5e
6 changed files with 395 additions and 1 deletions

@ -0,0 +1,90 @@
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { injectIntl, defineMessages } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import { fetchGabNews } from '../../actions/news'
import PanelLayout from './panel_layout'
import NewsItem from '../news_item'
class GabNewsPanel extends ImmutablePureComponent {
state = {
fetched: false,
}
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.shouldLoad && !prevState.fetched) {
return { fetched: true }
}
return null
}
componentDidUpdate(prevProps, prevState) {
if (!prevState.fetched && this.state.fetched && this.props.isLazy) {
this.props.dispatch(fetchGabNews())
}
}
componentDidMount() {
if (!this.props.isLazy) {
this.props.dispatch(fetchGabNews())
this.setState({ fetched: true })
}
}
render() {
const {
intl,
isLoading,
items,
} = this.props
const { fetched } = this.state
const count = !!items ? items.count() : 0
if (count === 0 && fetched) return null
return (
<PanelLayout
noPadding
title={intl.formatMessage(messages.title)}
headerButtonTitle={intl.formatMessage(messages.readMore)}
headerButtonHref='https://news.gab.com'
footerButtonTitle={intl.formatMessage(messages.readMore)}
footerButtonHref='https://news.gab.com'
>
<div className={[_s.d, _s.borderTop1PX, _s.borderBottom1PX, _s.borderColorSecondary, _s.flexRow, _s.w100PC, _s.overflowXScroll, _s.py15, _s.pl15].join(' ')}>
{
count > 0 &&
items.slice(0, 5).map((news, i) => (
<NewsItem news={news} key={`news-panel-item-${i}`} />
))
}
</div>
</PanelLayout>
)
}
}
const messages = defineMessages({
title: { id: 'gab_news.title', defaultMessage: 'Gab News' },
readMore: { id: 'status.read_more', defaultMessage: 'Read more' },
})
const mapStateToProps = (state) => ({
isLoading: state.getIn(['news', 'gab_news', 'isLoading']),
isFetched: state.getIn(['news', 'gab_news', 'isFetched']),
items: state.getIn(['news', 'gab_news', 'items']),
})
GabNewsPanel.propTypes = {
intl: PropTypes.object.isRequired,
isLazy: PropTypes.bool,
isLoading: PropTypes.bool,
isFetched: PropTypes.bool,
items: ImmutablePropTypes.list.isRequired,
}
export default injectIntl(connect(mapStateToProps)(GabNewsPanel))

@ -0,0 +1,77 @@
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import { fetchLatestFromGabTimeline } from '../../actions/news'
import PanelLayout from './panel_layout'
import StatusContainer from '../../containers/status_container'
class LatestFromGabPanel extends ImmutablePureComponent {
state = {
fetched: false,
}
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.shouldLoad && !prevState.fetched) {
return { fetched: true }
}
return null
}
componentDidUpdate(prevProps, prevState) {
if (!prevState.fetched && this.state.fetched && this.props.isLazy) {
this.props.dispatch(fetchLatestFromGabTimeline())
}
}
componentDidMount() {
if (!this.props.isLazy) {
this.props.dispatch(fetchLatestFromGabTimeline())
this.setState({ fetched: true })
}
}
render() {
const { items } = this.props
const { fetched } = this.state
const count = !!items ? items.count() : 0
if (count === 0 && fetched) return null
return (
<PanelLayout
noPadding
title='Latest from @Gab'
headerButtonTo='/gab'
headerButtonTitle='Go to @gab'
>
{
items.map((statusId, i) => (
<div
className={[_s.d, _s.w100PC, _s.px15, _s.pt5, _s.pb10].join(' ')}
key={`latest-from-gab-status-${i}`}
>
<StatusContainer
id={statusId}
isChild
/>
</div>
))
}
</PanelLayout>
)
}
}
const mapStateToProps = (state) => ({
items: state.getIn(['news', 'latest_from_gab', 'items']),
})
LatestFromGabPanel.propTypes = {
//
}
export default connect(mapStateToProps)(LatestFromGabPanel)

@ -0,0 +1,79 @@
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { fetchPopularLinks } from '../../actions/links'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import PanelLayout from './panel_layout'
import PreviewCardItem from '../preview_card_item'
class PopularLinksPanel extends ImmutablePureComponent {
state = {
fetched: false,
}
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.shouldLoad && !prevState.fetched) {
return { fetched: true }
}
return null
}
componentDidUpdate(prevProps, prevState) {
if (!prevState.fetched && this.state.fetched && this.props.isLazy) {
this.props.dispatch(fetchPopularLinks())
}
}
componentDidMount() {
if (!this.props.isLazy) {
this.props.dispatch(fetchPopularLinks())
this.setState({ fetched: true })
}
}
render() {
const {
intl,
popularLinksIsLoading,
popularLinksItems,
} = this.props
const { fetched } = this.state
const count = !!popularLinksItems ? popularLinksItems.count() : 0
// : TESTING :
// if (count === 0 && fetched) return null
return (
<PanelLayout
title='Trending Links on Gab'
subtitle='Most popular links on Gab in last 15 minutes'
>
<div className={[_s.d, _s.w100PC].join(' ')}>
{ /*
popularLinksItems.map((id, i) => (
<PreviewCardItem id={id} key={`preview-card-${id}-${i}`} />
))
*/ }
<PreviewCardItem id={'144'} />
<PreviewCardItem id={'144'} />
<PreviewCardItem id={'144'} />
</div>
</PanelLayout>
)
}
}
const mapStateToProps = (state) => ({
popularLinksIsLoading: state.getIn(['links', 'popular', 'isLoading']),
popularLinksItems: state.getIn(['links', 'popular', 'items']),
})
PopularLinksPanel.propTypes = {
popularLinksIsLoading: PropTypes.bool.isRequired,
popularLinksItems: ImmutablePropTypes.list.isRequired,
}
export default connect(mapStateToProps)(PopularLinksPanel)

@ -0,0 +1,43 @@
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import { TRENDS_RSS_SOURCES } from '../../constants'
import PanelLayout from './panel_layout'
import Button from '../button'
import Text from '../text'
class TrendsFeedsPanel extends ImmutablePureComponent {
render() {
return (
<PanelLayout
title='All News Feeds'
subtitle='Click on each one to see a feed of each'
>
<div className={[_s.d, _s.flexRow, _s.flexWrap].join(' ')}>
{
TRENDS_RSS_SOURCES.map((block, i) => (
<Button
isNarrow
to={`/news/view/${block.id}`}
color='primary'
backgroundColor='tertiary'
className={[_s.mr10, _s.mb10].join(' ')}
key={`trends-feeds-panel-${i}`}
>
<Text color='inherit'>
{block.title}
</Text>
</Button>
))
}
</div>
</PanelLayout>
)
}
}
export default TrendsFeedsPanel

@ -0,0 +1,97 @@
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import { CX } from '../../constants'
import PanelLayout from './panel_layout'
import Button from '../button'
import Text from '../text'
import Image from '../image'
// depends on TrendsBreakingPanel atm.
class TrendsHeadlinesPanel extends ImmutablePureComponent {
render() {
const {
isFetched,
isLoading,
items,
trendsLeadline,
} = this.props
const count = !!items ? items.count() : 0
const trendsLeadlineTitle = trendsLeadline.get('title')
const trendsLeadlineImage = trendsLeadline.get('image')
const trendsLeadlineUrl = trendsLeadline.get('trends_url')
if ((count === 0 && isFetched) && (!trendsLeadlineTitle || !trendsLeadlineTitle || !trendsLeadlineTitle)) return null
const leadlineButtonClasses = CX({
d: 1,
cursorPointer: 1,
noUnderline: 1,
bgPrimary: 1,
w100PC: 1,
border1PX: 1,
borderColorSecondary: 1,
radiusSmall: 1,
overflowHidden: 1,
bgSubtle_onHover: 1,
mb5: 1,
mt10: count > 0,
})
return (
<PanelLayout
title='Headlines'
subtitle='Headlines about yadda'
>
{
count > 0 &&
items.slice(0, 5).map((headline, i) => (
<Button
isText
backgroundColor='none'
color='primary'
className={[_s.d, _s.pb15].join(' ')}
href={headline.get('trends_url')}
key={`headline-news-item-${i}`}
>
<Text color='inherit' size='small'>
{headline.get('title')}
</Text>
</Button>
))
}
<Button
noClasses
href={''}
className={leadlineButtonClasses}
>
<Image
src='https://trends.gab.com/image/5fa5d8badf30e602384b08ed'
/>
<Text className={[_s.px15, _s.py15, _s.w100PC, _s.borderTop1PX, _s.borderColorSecondary].join(' ')}>
Lawsuit: At Least 21K Dead People on Pennsylvania Voter Rolls
</Text>
</Button>
</PanelLayout>
)
}
}
const mapStateToProps = (state) => ({
isLoading: state.getIn(['news', 'trends_headlines', 'isLoading']),
isFetched: state.getIn(['news', 'trends_headlines', 'isFetched']),
items: state.getIn(['news', 'trends_headlines', 'items']),
trendsLeadline: state.getIn(['news', 'trends_leadline']),
})
TrendsHeadlinesPanel.propTypes = {
isLoading: PropTypes.bool,
isFetched: PropTypes.bool,
items: ImmutablePropTypes.list.isRequired,
}
export default connect(mapStateToProps)(TrendsHeadlinesPanel)

@ -29,6 +29,7 @@ export function Followers() { return import(/* webpackChunkName: "features/follo
export function Following() { return import(/* webpackChunkName: "features/following" */'../../following') }
export function FollowRequests() { return import(/* webpackChunkName: "features/follow_requests" */'../../follow_requests') }
export function LikedStatuses() { return import(/* webpackChunkName: "features/liked_statuses" */'../../liked_statuses') }
export function GabNewsPanel() { return import(/* webpackChunkName: "components/gab_news_panel" */'../../../components/panel/gab_news_panel') }
export function GenericNotFound() { return import(/* webpackChunkName: "features/generic_not_found" */'../../generic_not_found') }
export function GlobalFooter() { return import(/* webpackChunkName: "components/global_footer" */'../../../components/global_footer') }
export function GroupCategoriesInjection() { return import(/* webpackChunkName: "components/group_categories_injection" */'../../../components/timeline_injections/group_categories_injection') }
@ -60,6 +61,7 @@ export function HomeTimelineSettingsModal() { return import(/* webpackChunkName:
export function HotkeysModal() { return import(/* webpackChunkName: "components/hotkeys_modal" */'../../../components/modal/hotkeys_modal') }
export function Introduction() { return import(/* webpackChunkName: "features/introduction" */'../../introduction') }
export function Investors() { return import(/* webpackChunkName: "features/about/investors" */'../../about/investors') }
export function LatestFromGabPanel() { return import(/* webpackChunkName: "components/latest_from_gab_panel" */'../../../components/panel/latest_from_gab_panel') }
export function LinkFooter() { return import(/* webpackChunkName: "components/link_footer" */'../../../components/link_footer') }
export function LinkTimeline() { return import(/* webpackChunkName: "features/link_timeline" */'../../link_timeline') }
export function ListAddUserModal() { return import(/* webpackChunkName: "features/list_add_user_modal" */'../../../components/modal/list_add_user_modal') }
@ -76,12 +78,15 @@ export function ListTimelineSettingsModal() { return import(/* webpackChunkName:
export function MediaGallery() { return import(/* webpackChunkName: "components/media_gallery" */'../../../components/media_gallery') }
export function MediaGalleryPanel() { return import(/* webpackChunkName: "components/media_gallery_panel" */'../../../components/panel/media_gallery_panel') }
export function MediaModal() { return import(/* webpackChunkName: "components/media_modal" */'../../../components/modal/media_modal') }
export function Messages() { return import(/* webpackChunkName: "features/messages" */'../../messages') }
export function Mutes() { return import(/* webpackChunkName: "features/mutes" */'../../mutes') }
export function MuteModal() { return import(/* webpackChunkName: "modals/mute_modal" */'../../../components/modal/mute_modal') }
export function NavSettingsPopover() { return import(/* webpackChunkName: "modals/nav_settings_popover" */'../../../components/popover/nav_settings_popover') }
export function News() { return import(/* webpackChunkName: "features/news" */'../../news') }
export function NewsView() { return import(/* webpackChunkName: "features/news_view" */'../../news_view') }
export function Notifications() { return import(/* webpackChunkName: "features/notifications" */'../../notifications') }
export function NotificationFilterPanel() { return import(/* webpackChunkName: "components/notification_filter_panel" */'../../../components/panel/notification_filter_panel') }
export function PopularLinksPanel() { return import(/* webpackChunkName: "components/popular_links_panel" */'../../../components/panel/popular_links_panel') }
export function Press() { return import(/* webpackChunkName: "features/about/press" */'../../about/press') }
export function PrivacyPolicy() { return import(/* webpackChunkName: "features/about/privacy_policy" */'../../about/privacy_policy') }
export function ProTimeline() { return import(/* webpackChunkName: "features/pro_timeline" */'../../pro_timeline') }
@ -120,7 +125,10 @@ export function Suggestions() { return import(/* webpackChunkName: "features/sug
export function TermsOfSale() { return import(/* webpackChunkName: "features/about/terms_of_sale" */'../../about/terms_of_sale') }
export function TermsOfService() { return import(/* webpackChunkName: "features/about/terms_of_service" */'../../about/terms_of_service') }
export function TimelineInjectionOptionsPopover() { return import(/* webpackChunkName: "components/timeline_injection_options_popover" */'../../../components/popover/timeline_injection_options_popover') }
export function TrendsPanel() { return import(/* webpackChunkName: "components/trends_panel" */'../../../components/panel/trends_panel') }
export function TrendsBreakingPanel() { return import(/* webpackChunkName: "components/trends_breaking_panel" */'../../../components/panel/trends_breaking_panel') }
export function TrendsFeedsPanel() { return import(/* webpackChunkName: "components/trends_feeds_panel" */'../../../components/panel/trends_feeds_panel') }
export function TrendsHeadlinesPanel() { return import(/* webpackChunkName: "components/trends_headlines_panel" */'../../../components/panel/trends_headlines_panel') }
export function TrendsRSSPanel() { return import(/* webpackChunkName: "components/trends_headlines_panel" */'../../../components/panel/trends_rss_panel') }
export function UnauthorizedModal() { return import(/* webpackChunkName: "components/unauthorized_modal" */'../../../components/modal/unauthorized_modal') }
export function UnfollowModal() { return import(/* webpackChunkName: "components/unfollow_modal" */'../../../components/modal/unfollow_modal') }
export function UserInfoPopover() { return import(/* webpackChunkName: "components/user_info_popover" */'../../../components/popover/user_info_popover') }