1+ /* eslint-disable max-len */
12/* eslint-disable react/no-unused-state */
23/**
34 * The core article rendering.
@@ -13,6 +14,7 @@ import markdown from 'utils/markdown';
1314import ContentfulLoader from 'containers/ContentfulLoader' ;
1415import LoadingIndicator from 'components/LoadingIndicator' ;
1516import YouTubeVideo from 'components/YouTubeVideo' ;
17+ import Viewport from 'components/Contentful/Viewport' ;
1618import moment from 'moment' ;
1719import localStorage from 'localStorage' ;
1820import { Helmet } from 'react-helmet' ;
@@ -33,6 +35,11 @@ const htmlToText = require('html-to-text');
3335const CONTENT_PREVIEW_LENGTH = 110 ;
3436// Votes local storage key
3537const LOCAL_STORAGE_KEY = 'VENBcnRpY2xlVm90ZXM=' ;
38+ // def banner image
39+ const DEFAULT_BANNER_IMAGE = 'https://images.ctfassets.net/piwi0eufbb2g/7v2hlDsVep7FWufHw0lXpQ/2505e61a880e68fab4e80cd0e8ec1814/0C37CB5E-B253-4804-8935-78E64E67589E.png' ;
40+ // random ads banner - left sidebar
41+ const RANDOM_BANNERS = [ '6G8mjiTC1mzeSQ2YoUG1gB' , '1DnDD02xX1liHfSTf5Vsn8' , 'HQZ3mN0rR92CbNTkKTHJ5' , '1OLoX8ZsvjAnn4TdGbZESD' , '77jn01UGoQe2gqA7x0coQD' ] ;
42+ const RANDOM_BANNER = RANDOM_BANNERS [ _ . random ( 0 , 4 ) ] ;
3643
3744export default class Article extends React . Component {
3845 componentDidMount ( ) {
@@ -131,33 +138,45 @@ export default class Article extends React.Component {
131138 < meta name = "description" property = "og:description" content = { description } />
132139 < meta name = "description" property = "description" content = { description } />
133140 < meta name = "twitter:description" content = { description } />
134- < meta name = "image" property = "og:image" content = { fields . featuredImage ? `https:${ subData . assets . items [ fields . featuredImage . sys . id ] . fields . file . url } ` : null } />
135- < meta name = "twitter:image" content = { fields . featuredImage ? `https:${ subData . assets . items [ fields . featuredImage . sys . id ] . fields . file . url } ` : null } />
141+ < meta name = "image" property = "og:image" content = { fields . featuredImage ? `https:${ subData . assets . items [ fields . featuredImage . sys . id ] . fields . file . url } ` : DEFAULT_BANNER_IMAGE } />
142+ < meta name = "twitter:image" content = { fields . featuredImage ? `https:${ subData . assets . items [ fields . featuredImage . sys . id ] . fields . file . url } ` : DEFAULT_BANNER_IMAGE } />
136143 </ Helmet >
137- { /* Banner */ }
138- {
139- fields . featuredImage ? (
140- < div className = { theme . bannerContainer } >
141- < svg viewBox = "0 25 1050 600" version = "1.1" preserveAspectRatio = "none" className = { theme [ 'site-header-background' ] } >
142- < defs >
143- < clipPath id = "user-space" clipPathUnits = "userSpaceOnUse" >
144- < path id = "jagged-top" d = "M955.643,455.426c113.929-152.899,130.923-281.812-19.966-387.73 C883.769,31.258,814.91-10.997,685,3c-87.558,9.434-218,32-332,9c-48.207-9.726-146.137-5.765-167.796,6.768 C45.296,99.719-82.626,352.551,69.262,473.459c151.887,120.908,379.734,0.979,533.623,75.92 C756.773,624.319,841.715,608.326,955.643,455.426" />
145- </ clipPath >
146- </ defs >
147- < image width = "100%" height = "100%" preserveAspectRatio = "none" href = { subData . assets . items [ fields . featuredImage . sys . id ] . fields . file . url } clipPath = "url(#user-space)" />
148- </ svg >
144+ < div className = { theme . wrapper } >
145+ { /* Banner */ }
146+ < div className = { fields . featuredImage ? theme . bannerContainer : theme . bannerContainerDefaultImage } >
147+ < div className = { theme . bannerInner } >
148+ < div className = { theme . bannerInnerLeft } >
149+ < h4 className = { theme . articleDate } > { moment ( fields . creationDate ) . format ( 'MMMM D, YYYY' ) } </ h4 >
150+ < h1 className = { theme . articleTitle } > { fields . title } </ h1 >
151+ </ div >
152+ < div className = { theme . bannerInnerRight } >
153+ {
154+ fields . featuredImage ? (
155+ < div className = { theme [ 'site-header-background' ] } >
156+ < svg className = { theme . bannerSvg } >
157+ < clipPath id = "thrive-banner-clip-path" clipPathUnits = "objectBoundingBox" >
158+ < path d = "M0.477,1 C0.72,0.999,1,0.804,1,0.56 C1,0.316,0.766,-0.067,0.528,0.021 C0.343,0.089,0.145,-0.088,0.076,0.063 C0.016,0.193,-0.071,0.456,0.101,0.618 C0.274,0.782,0.234,1,0.477,1" />
159+ </ clipPath >
160+ </ svg >
161+ < div className = { theme . bannerClippedImageHolder } style = { { backgroundImage : `url(${ subData . assets . items [ fields . featuredImage . sys . id ] . fields . file . url } )` } } />
162+ </ div >
163+ ) : (
164+ < img src = { DEFAULT_BANNER_IMAGE } alt = "Thrive - default banner" className = { theme [ 'site-header-background' ] } />
165+ )
166+ }
167+ </ div >
149168 </ div >
150- ) : null
151- }
152- < div
153- className = { fields . featuredImage
154- ? theme . contentContainerWithBanner : theme . contentContainer }
155- style = { fixStyle ( fields . extraStylesForContainer ) }
156- >
157- < div className = { theme . contentLeftBar } >
158- { /* Authors */ }
159- < div className = { theme . authorContainer } >
160- {
169+ < img src = "https://images.ctfassets.net/piwi0eufbb2g/3StLyQh5ne1Lk9H7C1oVxv/52f17a02122212052e44585d3e79fcf7/29320408-E820-48E1-B0FD-539EAC296910.svg" alt = "Thrive banner shape" className = { theme . bannerBottShape } />
170+ </ div >
171+ < div
172+ className = { fields . featuredImage
173+ ? theme . contentContainerWithBanner : theme . contentContainer }
174+ style = { fixStyle ( fields . extraStylesForContainer ) }
175+ >
176+ < div className = { theme . contentLeftBar } >
177+ { /* Authors */ }
178+ < div className = { theme . authorContainer } >
179+ {
161180 _ . map ( fields . contentAuthor , author => (
162181 < div key = { author . sys . id } className = { theme . authorWrapper } >
163182 {
@@ -189,72 +208,89 @@ export default class Article extends React.Component {
189208 </ div >
190209 ) )
191210 }
192- </ div >
193- < div className = { theme . separator } />
194- < h3 className = { theme . label } > DURATION</ h3 >
195- < span className = { theme . duration } > { fields . readTime } </ span >
196- < div className = { theme . separator } />
197- < h3 className = { theme . label } > categories & Tags </ h3 >
198- { /* Tags */ }
199- < div className = { theme . tagContainer } >
200- {
211+ </ div >
212+ < div className = { theme . separator } />
213+ < h3 className = { theme . label } > DURATION</ h3 >
214+ < span className = { theme . duration } > { fields . readTime } </ span >
215+ < div className = { theme . separator } />
216+ < h3 className = { theme . label } > categories</ h3 >
217+ { /* Cats */ }
218+ < div className = { theme . tagContainer } >
219+ {
220+ _ . map ( fields . contentCategory , cat => (
221+ < div className = { theme . tagItem } key = { cat . sys . id } title = { `Search for articles in ${ cat . fields . trackParent } :${ cat . fields . name } category` } >
222+ < Link to = { `${ config . TC_EDU_BASE_PATH } ${ config . TC_EDU_TRACKS_PATH } ?${ qs . stringify ( { track : cat . fields . trackParent , tax : cat . fields . name } ) } ` } key = { `${ cat . sys . id } ` } className = { theme . catLink } > { cat . fields . name } </ Link >
223+ </ div >
224+ ) )
225+ }
226+ </ div >
227+ < div className = { theme . separator } />
228+ < h3 className = { theme . label } > Tags</ h3 >
229+ { /* Tags */ }
230+ < div className = { theme . tagContainer } >
231+ {
201232 _ . map ( fields . tags , tag => (
202233 < div className = { theme . tagItem } key = { tag } title = { `Search for articles labelled as ${ tag } ` } >
203234 < Link to = { `${ config . TC_EDU_BASE_PATH } ${ config . TC_EDU_SEARCH_PATH } ?${ qs . stringify ( { tags : tag } ) } ` } key = { `${ tag } ` } > { tag } </ Link >
204235 </ div >
205236 ) )
206237 }
238+ </ div >
239+ < div className = { theme . separator } />
240+ < h3 className = { theme . label } > share</ h3 >
241+ < div className = { theme . shareButtons } >
242+ < a href = { `https://www.linkedin.com/sharing/share-offsite/?url=${ shareUrl } ` } target = "_blank" rel = "noopener noreferrer" >
243+ < IconLinkedIn />
244+ </ a >
245+ < a href = { `https://www.facebook.com/sharer/sharer.php?u=${ shareUrl } &src=share_button` } target = "_blank" rel = "noopener noreferrer" >
246+ < IconFacebook />
247+ </ a >
248+ < a href = { `https://twitter.com/intent/tweet?url=${ shareUrl } ` } target = "_blank" rel = "noopener noreferrer" >
249+ < IconTwitter />
250+ </ a >
251+ </ div >
252+ < div className = { theme . mobileSeparator } />
253+ < div className = { theme . leftSidebarContent } >
254+ < Viewport
255+ id = { fields . leftSidebarContent
256+ ? fields . leftSidebarContent . sys . id : RANDOM_BANNER }
257+ preview = { preview }
258+ spaceName = { spaceName }
259+ environment = { environment }
260+ />
261+ </ div >
207262 </ div >
208- < div className = { theme . separator } />
209- < h3 className = { theme . label } > share</ h3 >
210- < div className = { theme . shareButtons } >
211- < a href = { `https://www.linkedin.com/sharing/share-offsite/?url=${ shareUrl } ` } target = "_blank" rel = "noopener noreferrer" >
212- < IconLinkedIn />
213- </ a >
214- < a href = { `https://www.facebook.com/sharer/sharer.php?u=${ shareUrl } &src=share_button` } target = "_blank" rel = "noopener noreferrer" >
215- < IconFacebook />
216- </ a >
217- < a href = { `https://twitter.com/intent/tweet?url=${ shareUrl } ` } target = "_blank" rel = "noopener noreferrer" >
218- < IconTwitter />
219- </ a >
220- </ div >
221- < div className = { theme . mobileSeparator } />
222- </ div >
223- { /* Content */ }
224- < div className = { theme . articleContent } >
225- < div className = { theme . articleContentTop } >
226- < h4 className = { theme . articleDate } > { moment ( fields . creationDate ) . format ( 'MMMM D, YYYY' ) } </ h4 >
227- < h1 className = { theme . articleTitle } > { fields . title } </ h1 >
228- </ div >
229- < MarkdownRenderer markdown = { fields . content } { ...contentfulConfig } />
230- {
263+ { /* Content */ }
264+ < div className = { theme . articleContent } >
265+ < MarkdownRenderer markdown = { fields . content } { ...contentfulConfig } />
266+ {
231267 fields . type === 'Video' && fields . contentUrl ? (
232268 < YouTubeVideo src = { fields . contentUrl } />
233269 ) : null
234270 }
235- { /* Voting */ }
236- < div className = { theme . actionContainer } >
237- < div className = { theme . action } >
238- < div tabIndex = { 0 } role = "button" className = { theme . circleGreenIcon } onClick = { ( ) => this . updateVote ( 'up' ) } onKeyPress = { ( ) => this . updateVote ( 'up' ) } >
239- < GestureIcon />
240- </ div >
241- < span >
242- {
271+ { /* Voting */ }
272+ < div className = { theme . actionContainer } >
273+ < div className = { theme . action } >
274+ < div tabIndex = { 0 } role = "button" className = { theme . circleGreenIcon } onClick = { ( ) => this . updateVote ( 'up' ) } onKeyPress = { ( ) => this . updateVote ( 'up' ) } >
275+ < GestureIcon />
276+ </ div >
277+ < span >
278+ {
243279 upvotes
244280 }
245- </ span >
246- </ div >
247- < div className = { theme . action } >
248- < div tabIndex = { 0 } role = "button" className = { theme . circleRedIcon } onClick = { ( ) => this . updateVote ( 'down' ) } onKeyPress = { ( ) => this . updateVote ( 'down' ) } >
249- < GestureIcon />
281+ </ span >
282+ </ div >
283+ < div className = { theme . action } >
284+ < div tabIndex = { 0 } role = "button" className = { theme . circleRedIcon } onClick = { ( ) => this . updateVote ( 'down' ) } onKeyPress = { ( ) => this . updateVote ( 'down' ) } >
285+ < GestureIcon />
286+ </ div >
287+ < span > { downvotes } </ span >
250288 </ div >
251- < span > { downvotes } </ span >
252289 </ div >
253290 </ div >
254291 </ div >
255- </ div >
256- { /* Recommended */ }
257- {
292+ { /* Recommended */ }
293+ {
258294 fields . recommended ? (
259295 < div className = { theme . recommendedContainer } >
260296 < div className = { theme . recommendedTopShape } />
@@ -331,6 +367,7 @@ export default class Article extends React.Component {
331367 </ div >
332368 ) : null
333369 }
370+ </ div >
334371 </ React . Fragment >
335372 ) ;
336373 }
0 commit comments