1
+ /* eslint-disable max-len */
1
2
/* eslint-disable react/no-unused-state */
2
3
/**
3
4
* The core article rendering.
@@ -13,6 +14,7 @@ import markdown from 'utils/markdown';
13
14
import ContentfulLoader from 'containers/ContentfulLoader' ;
14
15
import LoadingIndicator from 'components/LoadingIndicator' ;
15
16
import YouTubeVideo from 'components/YouTubeVideo' ;
17
+ import Viewport from 'components/Contentful/Viewport' ;
16
18
import moment from 'moment' ;
17
19
import localStorage from 'localStorage' ;
18
20
import { Helmet } from 'react-helmet' ;
@@ -33,6 +35,11 @@ const htmlToText = require('html-to-text');
33
35
const CONTENT_PREVIEW_LENGTH = 110 ;
34
36
// Votes local storage key
35
37
const 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 ) ] ;
36
43
37
44
export default class Article extends React . Component {
38
45
componentDidMount ( ) {
@@ -131,33 +138,45 @@ export default class Article extends React.Component {
131
138
< meta name = "description" property = "og:description" content = { description } />
132
139
< meta name = "description" property = "description" content = { description } />
133
140
< 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 } />
136
143
</ 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 >
149
168
</ 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
+ {
161
180
_ . map ( fields . contentAuthor , author => (
162
181
< div key = { author . sys . id } className = { theme . authorWrapper } >
163
182
{
@@ -189,72 +208,89 @@ export default class Article extends React.Component {
189
208
</ div >
190
209
) )
191
210
}
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
+ {
201
232
_ . map ( fields . tags , tag => (
202
233
< div className = { theme . tagItem } key = { tag } title = { `Search for articles labelled as ${ tag } ` } >
203
234
< Link to = { `${ config . TC_EDU_BASE_PATH } ${ config . TC_EDU_SEARCH_PATH } ?${ qs . stringify ( { tags : tag } ) } ` } key = { `${ tag } ` } > { tag } </ Link >
204
235
</ div >
205
236
) )
206
237
}
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 >
207
262
</ 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
+ {
231
267
fields . type === 'Video' && fields . contentUrl ? (
232
268
< YouTubeVideo src = { fields . contentUrl } />
233
269
) : null
234
270
}
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
+ {
243
279
upvotes
244
280
}
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 >
250
288
</ div >
251
- < span > { downvotes } </ span >
252
289
</ div >
253
290
</ div >
254
291
</ div >
255
- </ div >
256
- { /* Recommended */ }
257
- {
292
+ { /* Recommended */ }
293
+ {
258
294
fields . recommended ? (
259
295
< div className = { theme . recommendedContainer } >
260
296
< div className = { theme . recommendedTopShape } />
@@ -331,6 +367,7 @@ export default class Article extends React.Component {
331
367
</ div >
332
368
) : null
333
369
}
370
+ </ div >
334
371
</ React . Fragment >
335
372
) ;
336
373
}
0 commit comments