From 4c4894ac5e5a608e5434aab3005de2b80f77ba36 Mon Sep 17 00:00:00 2001 From: mgabdev <> Date: Fri, 6 Nov 2020 23:28:30 -0600 Subject: [PATCH] Added WIP for new News page Panels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Added: - WIP for new News page Panels: GabNewsPanel, LatestFromGabPanel, PopularLinks, TrendsFeedsPanel, TrendsHeadlinesPanel - all imports for above in async_components • Todo: - Remove tests --- .../components/panel/gab_news_panel.js | 90 +++++++++++++++++ .../components/panel/latest_from_gab_panel.js | 77 +++++++++++++++ .../components/panel/popular_links_panel.js | 79 +++++++++++++++ .../components/panel/trends_feeds_panel.js | 43 ++++++++ .../panel/trends_headlines_panel.js | 97 +++++++++++++++++++ .../features/ui/util/async_components.js | 10 +- 6 files changed, 395 insertions(+), 1 deletion(-) create mode 100644 app/javascript/gabsocial/components/panel/gab_news_panel.js create mode 100644 app/javascript/gabsocial/components/panel/latest_from_gab_panel.js create mode 100644 app/javascript/gabsocial/components/panel/popular_links_panel.js create mode 100644 app/javascript/gabsocial/components/panel/trends_feeds_panel.js create mode 100644 app/javascript/gabsocial/components/panel/trends_headlines_panel.js diff --git a/app/javascript/gabsocial/components/panel/gab_news_panel.js b/app/javascript/gabsocial/components/panel/gab_news_panel.js new file mode 100644 index 00000000..0cd5adc8 --- /dev/null +++ b/app/javascript/gabsocial/components/panel/gab_news_panel.js @@ -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 ( + +
+ { + count > 0 && + items.slice(0, 5).map((news, i) => ( + + )) + } +
+
+ ) + } +} + +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)) \ No newline at end of file diff --git a/app/javascript/gabsocial/components/panel/latest_from_gab_panel.js b/app/javascript/gabsocial/components/panel/latest_from_gab_panel.js new file mode 100644 index 00000000..99e66ada --- /dev/null +++ b/app/javascript/gabsocial/components/panel/latest_from_gab_panel.js @@ -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 ( + + { + items.map((statusId, i) => ( +
+ +
+ )) + } +
+ ) + } +} + +const mapStateToProps = (state) => ({ + items: state.getIn(['news', 'latest_from_gab', 'items']), +}) + +LatestFromGabPanel.propTypes = { + // +} + +export default connect(mapStateToProps)(LatestFromGabPanel) \ No newline at end of file diff --git a/app/javascript/gabsocial/components/panel/popular_links_panel.js b/app/javascript/gabsocial/components/panel/popular_links_panel.js new file mode 100644 index 00000000..a700f98a --- /dev/null +++ b/app/javascript/gabsocial/components/panel/popular_links_panel.js @@ -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 ( + +
+ { /* + popularLinksItems.map((id, i) => ( + + )) + */ } + + + +
+
+ ) + } +} + +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) \ No newline at end of file diff --git a/app/javascript/gabsocial/components/panel/trends_feeds_panel.js b/app/javascript/gabsocial/components/panel/trends_feeds_panel.js new file mode 100644 index 00000000..7a161c4a --- /dev/null +++ b/app/javascript/gabsocial/components/panel/trends_feeds_panel.js @@ -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 ( + +
+ { + TRENDS_RSS_SOURCES.map((block, i) => ( + + )) + } +
+
+ ) + } + +} + +export default TrendsFeedsPanel \ No newline at end of file diff --git a/app/javascript/gabsocial/components/panel/trends_headlines_panel.js b/app/javascript/gabsocial/components/panel/trends_headlines_panel.js new file mode 100644 index 00000000..ed1c92e4 --- /dev/null +++ b/app/javascript/gabsocial/components/panel/trends_headlines_panel.js @@ -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 ( + + { + count > 0 && + items.slice(0, 5).map((headline, i) => ( + + )) + } + + + ) + } +} + +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) \ No newline at end of file diff --git a/app/javascript/gabsocial/features/ui/util/async_components.js b/app/javascript/gabsocial/features/ui/util/async_components.js index 089154fd..9985166a 100644 --- a/app/javascript/gabsocial/features/ui/util/async_components.js +++ b/app/javascript/gabsocial/features/ui/util/async_components.js @@ -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') }