diff --git a/news/components/ArticleMeta.js b/news/components/ArticleMeta.js new file mode 100644 index 0000000000..4d1d527b49 --- /dev/null +++ b/news/components/ArticleMeta.js @@ -0,0 +1,80 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Helmet from 'react-helmet'; +import differenceInMinutes from 'date-fns/difference_in_minutes'; +import differenceInHours from 'date-fns/difference_in_hours'; +import differenceInDays from 'date-fns/difference_in_calendar_days'; +import format from 'date-fns/format'; + +const propTypes = { + article: PropTypes.object +}; + +const styles = ` + +.meta-wrapper { + padding-top: 10px; +} + +.meta-wrapper span, +.meta-wrapper a { + font-size: 16px; +} + +.meta-item { + margin-right: 20px; +} + +`; + +function pluralise(singular, count) { + return `${singular}${count === 1 ? '' : 's'}`; +} + +function getTimeString(pubDate) { + const now = new Date(Date.now()); + const minuteDiff = differenceInMinutes(now, pubDate); + console.log(typeof minuteDiff); + if (minuteDiff < 60) { + return `${minuteDiff} ${pluralise('minute', minuteDiff)} ago`; + } + const hourDiff = differenceInHours(now, pubDate); + if (hourDiff < 24) { + return `${hourDiff} ${pluralise('hour', hourDiff)} ago`; + } + const dayDiff = differenceInDays(now, pubDate); + if (dayDiff < 8) { + return `${dayDiff} ${pluralise('day', dayDiff)} ago`; + } + + if (dayDiff < 365) { + return format(pubDate, 'MMM D'); + } + + return format(pubDate, 'MMM D YYYY'); +} + +function ArticleMeta({ + article: { viewCount, author, meta, firstPublishedDate } +}) { + return ( +
+ + + +
+ By {author.name} + {getTimeString(firstPublishedDate)} + {`${meta.readTime} minute read`} + {viewCount >= 100 ? ( + {`${viewCount} views`} + ) : null} +
+
+ ); +} + +ArticleMeta.displayName = 'ArticleMeta'; +ArticleMeta.propTypes = propTypes; + +export default ArticleMeta; diff --git a/news/routes/Featured/Featured.js b/news/routes/Featured/Featured.js index 0e2fa41c86..20da0a11ab 100644 --- a/news/routes/Featured/Featured.js +++ b/news/routes/Featured/Featured.js @@ -1,4 +1,5 @@ import React, { Component } from 'react'; +import PropTypes from 'prop-types'; import { withRouter } from 'react-router-dom'; import { Image } from 'react-bootstrap'; import Helmet from 'react-helmet'; @@ -6,17 +7,36 @@ import Helmet from 'react-helmet'; import { getFeaturedList } from '../../utils/ajax'; import { Loader, Spacer } from '../../../common/app/helperComponents'; import BannerWide from '../../components/BannerWide'; +import ArticleMeta from '../../components/ArticleMeta'; -const propTypes = {}; +const propTypes = { + history: PropTypes.shape({ + push: PropTypes.func.isRequired + }) +}; const styles = ` .featured-list { list-style: none; padding-left: 0; + margin-top: 40px; + } + + .featured-list-item { + padding-bottom: 20px; } .featured-list-item .title { color: #333; + padding-bottom: 20px; + } + + .featured-list-item a { + padding-top: 5px; + } + + .featured-list-image { + margin: 0 auto; } .featured-list-item a:hover, @@ -29,19 +49,6 @@ const styles = ` .featured-list-item a:focus > .meta-wrapper { color: #006400; } - - .meta-wrapper { - display: flex; - justify-content: space-between; - } - - .meta-wrapper span { - font-size: 14px; - } - - .meta-stats { - text-align: end; - } `; class Featured extends Component { @@ -100,16 +107,24 @@ class Featured extends Component { '--', article.shortId ); - const { featureImage, shortId, author, title, meta } = article; + const { featureImage, shortId, title } = article; return (
  • - {featureImage && featureImage.src ? () : ()} - +

    {title}

    -
    - By {author.name} - {`${meta.readTime} minute read`}
    {`${article.viewCount} views`}
    -
    + {featureImage && featureImage.src ? ( + + ) : ( + + )} +
  • @@ -134,12 +149,7 @@ class Featured extends Component { -

    Welcome to freeCodeCamp News

    -
    -

    Featured Articles

    - + ); } diff --git a/news/routes/Show/Show.js b/news/routes/Show/Show.js index f9e44c5224..12421f288e 100644 --- a/news/routes/Show/Show.js +++ b/news/routes/Show/Show.js @@ -14,7 +14,8 @@ const propTypes = { push: PropTypes.func.isRequired }), location: PropTypes.shape({ - state: PropTypes.object + state: PropTypes.object, + pathname: PropTypes.string }), match: PropTypes.shape({ params: PropTypes.shape({ @@ -33,6 +34,20 @@ const youtubeOpts = { const styles = ` + .show-article figure { + display: flex; + flex-direction: column; + align-items: center; + } + + .show-article figcaption > * { + font-size: 16px; + } + + .show-article figcaption { + padding-top: 5px; + } + .feature-image-wrapper { padding-top: 32px; } @@ -70,6 +85,7 @@ class ShowArticle extends Component { } componentDidMount() { + window.scrollTo(0, 0); const { history, match: { @@ -142,13 +158,8 @@ class ShowArticle extends Component { render() { const { fetchState: { pending, complete, errored }, - currentArticle: { - title, - renderableContent, - youtubeId, - featureImage, - author - } + currentArticle: { title, renderableContent, youtubeId, featureImage }, + currentArticle } = this.state; if (pending || !complete) { return ; @@ -159,11 +170,11 @@ class ShowArticle extends Component { } return ( -
    +
    - +

    {title}

    @@ -179,7 +190,6 @@ class ShowArticle extends Component { ) : null}
    -
    {youtubeId ? ( diff --git a/news/routes/Show/components/Author.js b/news/routes/Show/components/Author.js index 91e7fdf5a7..2f69893d90 100644 --- a/news/routes/Show/components/Author.js +++ b/news/routes/Show/components/Author.js @@ -2,8 +2,12 @@ import React from 'react'; import PropTypes from 'prop-types'; import Helmet from 'react-helmet'; +import ArticleMeta from '../../../components/ArticleMeta'; + const propTypes = { - author: PropTypes.objectOf(PropTypes.string) + article: PropTypes.shape({ + author: PropTypes.objectOf(PropTypes.string) + }) }; const styles = ` @@ -33,7 +37,10 @@ const styles = ` } `; -function Author({ author: { name, avatar, bio } }) { +function Author({ article }) { + const { + author: { avatar, bio } + } = article; return (
    @@ -41,9 +48,7 @@ function Author({ author: { name, avatar, bio } }) {
    - - By {name} - + {bio.slice(0, 101)}