Permalink
Browse files

Revert "Reader: remove Cold Start" (#10974)

  • Loading branch information...
1 parent 91d8615 commit 2b95e68c52fe5c259b279438cf54abdc3020d4e6 @bluefuton bluefuton committed on GitHub Jan 26, 2017
@@ -425,6 +425,7 @@
@import 'reader/sidebar/style';
@import 'reader/site-and-author-icon/style';
@import 'reader/site-stream/style';
+@import 'reader/start/style';
@import 'reader/tag-stream/style';
@import 'reader/style';
@import 'reader/update-notice/style';
@@ -0,0 +1,29 @@
+Query Reader Start Recommendations
+===================================
+
+`<QueryReaderStartRecommendations />` is a React component used in managing network requests for Reader's /read/start (aka Cold Start).
+
+## Usage
+
+Render the component. It does not accept any children, nor does it render any elements to the page. You can use it adjacent to other sibling components which make use of the fetched data made available through the global application state.
+
+```jsx
+import React from 'react';
+import QueryReaderStartRecommendations from 'components/data/query-reader-start-recommendations';
+import MyListItem from './list-item';
+
+export default function MyReaderStartRecommendations( { recs } ) {
+ return (
+ <div>
+ <QueryReaderStartRecommendations />
+ { recs.map( ( rec ) => {
+ return (
+ <MyListItem
+ key={ rec.site_ID }
+ recommendation={ rec } />
+ );
+ } }
+ </div>
+ );
+}
+```
@@ -0,0 +1,48 @@
+/**
+ * External dependencies
+ */
+import { Component, PropTypes } from 'react';
+import { connect } from 'react-redux';
+import { bindActionCreators } from 'redux';
+
+/**
+ * Internal dependencies
+ */
+import { isRequestingRecommendations } from 'state/reader/start/selectors';
+import { requestRecommendations } from 'state/reader/start/actions';
+
+class QueryReaderStartRecommendations extends Component {
+ componentWillMount() {
+ if ( this.props.isRequestingRecommendations ) {
+ return;
+ }
+
+ this.props.requestRecommendations();
+ }
+
+ render() {
+ return null;
+ }
+}
+
+QueryReaderStartRecommendations.propTypes = {
+ isRequestingRecommendations: PropTypes.bool,
+ requestRecommendations: PropTypes.func
+};
+
+QueryReaderStartRecommendations.defaultProps = {
+ requestRecommendations: () => {}
+};
+
+export default connect(
+ ( state ) => {
+ return {
+ isRequestingRecommendations: isRequestingRecommendations( state )
+ };
+ },
+ ( dispatch ) => {
+ return bindActionCreators( {
+ requestRecommendations
+ }, dispatch );
+ }
+)( QueryReaderStartRecommendations );
@@ -0,0 +1,37 @@
+// External dependencies
+import React from 'react';
+import { numberFormat } from 'i18n-calypso';
+
+// Internal dependencies
+import SiteIcon from 'blocks/site-icon';
+import FollowButton from 'reader/follow-button';
+
+const StartCardHeader = ( { site, railcar } ) => {
+ const subscribersCount = numberFormat( site.subscribers_count );
+ const siteStreamUrl = `/read/blogs/${site.ID}`;
+ return (
+ <header className="reader-start-card__header">
+ <a href={ siteStreamUrl }>
+ <SiteIcon site={ site } size={ 30 } />
+ </a>
+ <div className="reader-start-card__site-info">
+ <a href={ siteStreamUrl }>
+ <h1 className="reader-start-card__site-title">{ site.title }</h1>
+ </a>
+ <div className="reader-start-card__follower-count">
+ <a href={ siteStreamUrl }>{ subscribersCount } followers</a>
+ </div>
+ </div>
+ <div className="reader-start-card__follow">
+ <FollowButton siteUrl={ site.URL } railcar={ railcar } />
+ </div>
+ </header>
+ );
+};
+
+StartCardHeader.propTypes = {
+ site: React.PropTypes.object.isRequired,
+ railcar: React.PropTypes.object
+};
+
+export default StartCardHeader;
@@ -0,0 +1,36 @@
+// External dependencies
+import React from 'react';
+
+// Internal dependencies
+import Card from 'components/card';
+import SiteIcon from 'blocks/site-icon';
+
+const StartCardPlaceholder = ( {} ) => {
+ return (
+ <Card className="reader-start-card is-placeholder">
+ <header className="reader-start-card__header">
+ <SiteIcon site={ null } size={ 30 } />
+ <div className="reader-start-card__site-info">
+ <h1 className="reader-start-card__site-title">Site title</h1>
+ <div className="reader-start-card__follower-count">
+ Number of followers
+ </div>
+ </div>
+ </header>
+ <article className="reader-start-post-preview">
+ <div className="reader-start-post-preview__featured-label">Featured Post</div>
+ <div className="reader-start-post-preview__featured-image is-dark"></div>
+ <div className="reader-start-post-preview__post-content">
+ <h1 className="reader-start-post-preview__title">Post title</h1>
+ <div className="reader-start-post-preview__byline">
+ <span className="reader-start-post-preview__author">by author name</span>
+ </div>
+ <p className="reader-start-post-preview__excerpt">This is where the post excerpt will
+ appear - just the first couple of lines of the post.</p>
+ </div>
+ </article>
+ </Card>
+ );
+};
+
+export default StartCardPlaceholder;
@@ -0,0 +1,86 @@
+// External dependencies
+import React from 'react';
+import { connect } from 'react-redux';
+import { bindActionCreators } from 'redux';
+import debugModule from 'debug';
+import { get } from 'lodash';
+import classnames from 'classnames';
+
+// Internal dependencies
+import Card from 'components/card';
+import StartPostPreview from './post-preview';
+import StartCardHeader from './card-header';
+import { recordRecommendationInteraction } from 'state/reader/start/actions';
+import { getRecommendationById } from 'state/reader/start/selectors';
+import { getPostBySiteAndId } from 'state/reader/posts/selectors';
+import { getSite } from 'state/reader/sites/selectors';
+import { recordTrack, recordTrackForPost, recordTracksRailcarInteract } from 'reader/stats';
+
+const debug = debugModule( 'calypso:reader:start' ); //eslint-disable-line no-unused-vars
+const tracksSource = 'recommended_cold_start';
+
+const StartCard = React.createClass( {
+ onCardInteraction() {
+ this.props.recordRecommendationInteraction(
+ this.props.recommendationId,
+ this.props.recommendation.recommended_site_ID,
+ this.props.recommendation.recommended_post_ID
+ );
+
+ if ( this.props.post ) {
+ recordTrackForPost( 'calypso_reader_startcard_clicked', this.props.post, { source: tracksSource } );
+ } else {
+ recordTrack( 'calypso_reader_startcard_clicked', {
+ blog_id: this.props.recommendation.recommended_site_ID,
+ recommendation_id: this.props.recommendationId,
+ source: tracksSource
+ } );
+ if ( this.props.recommendation.railcar ) {
+ recordTracksRailcarInteract( 'startcard_clicked', this.props.recommendation.railcar );
+ }
+ }
+ },
+
+ render() {
+ const { post, site } = this.props;
+ const hasPost = !! post;
+ const cardClasses = classnames(
+ 'reader-start-card',
+ {
+ 'has-post-preview': hasPost,
+ 'is-photo': hasPost && post.excerpt.length < 1
+ }
+ );
+ const railcar = get( this.props.recommendation, 'railcar' );
+
+ return (
+ <Card className={ cardClasses } onClick={ this.onCardInteraction }>
+ <StartCardHeader site={ site } railcar={ railcar } />
+ { hasPost && <StartPostPreview post={ post } /> }
+ </Card>
+ );
+ }
+} );
+
+StartCard.propTypes = {
+ recommendationId: React.PropTypes.number.isRequired
+};
+
+export default connect(
+ ( state, ownProps ) => {
+ const recommendation = getRecommendationById( state, ownProps.recommendationId );
+ const siteId = get( recommendation, 'recommended_site_ID' );
+ const postId = get( recommendation, 'recommended_post_ID' );
+ const site = siteId ? getSite( state, siteId ) : undefined;
+ const post = postId ? getPostBySiteAndId( state, siteId, postId ) : undefined;
+
+ return {
+ recommendation,
+ post,
+ site
+ };
+ },
+ ( dispatch ) => bindActionCreators( {
+ recordRecommendationInteraction
+ }, dispatch )
+)( StartCard );
@@ -0,0 +1,70 @@
+/**
+ * External dependencies
+ */
+import React from 'react';
+import ReactDom from 'react-dom';
+import { Provider as ReduxProvider } from 'react-redux';
+import i18n from 'i18n-calypso';
+import page from 'page';
+import qs from 'qs';
+
+/**
+ * Internal dependencies
+ */
+import feedStreamFactory from 'lib/feed-stream-store';
+import { ensureStoreLoading, trackPageLoad, trackUpdatesLoaded, trackScrollPage, setPageTitle } from 'reader/controller-helper';
+import { recordTrack } from 'reader/stats';
+
+const analyticsPageTitle = 'Reader';
+
+export function start( context ) {
+ const startComponent = require( 'reader/start/main' ),
+ basePath = '/recommendations/start',
+ fullAnalyticsPageTitle = analyticsPageTitle + ' > Start',
+ searchSlug = context.query.q,
+ mcKey = 'start';
+
+ let feedStore;
+ if ( searchSlug ) {
+ feedStore = feedStreamFactory( 'search:' + searchSlug ),
+ ensureStoreLoading( feedStore, context );
+ }
+
+ setPageTitle( context, i18n.translate( 'Start' ) );
+ trackPageLoad( basePath, fullAnalyticsPageTitle, mcKey );
+
+ if ( searchSlug ) {
+ recordTrack( 'calypso_reader_recommendations_start_search_performed', {
+ query: searchSlug
+ } );
+ } else {
+ recordTrack( 'calypso_reader_recommendations_start_loaded' );
+ }
+
+ ReactDom.render(
+ React.createElement( ReduxProvider, { store: context.store },
+ React.createElement( startComponent, {
+ key: 'start',
+ feedStore: feedStore,
+ query: searchSlug,
+ trackScrollPage: trackScrollPage.bind(
+ null,
+ basePath,
+ fullAnalyticsPageTitle,
+ analyticsPageTitle,
+ mcKey
+ ),
+ onUpdatesShown: trackUpdatesLoaded.bind( null, mcKey ),
+ showBack: false,
+ onQueryChange: function( newValue ) {
+ let searchUrl = '/recommendations/start';
+ if ( newValue ) {
+ searchUrl += '?' + qs.stringify( { q: newValue } );
+ }
+ page.replace( searchUrl );
+ }
+ } )
+ ),
+ document.getElementById( 'primary' )
+ );
+}
@@ -0,0 +1,19 @@
+/**
+ * External dependencies
+ */
+import page from 'page';
+
+/**
+ * Internal dependencies
+ */
+import { start } from './controller';
+import readerController from 'reader/controller';
+
+export default function() {
+ page( '/recommendations/start',
+ readerController.preloadReaderBundle,
+ readerController.loadSubscriptions,
+ readerController.updateLastRoute,
+ readerController.sidebar,
+ start );
+}
Oops, something went wrong.

0 comments on commit 2b95e68

Please sign in to comment.