-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #166 from taylrj/add-podcast-section
feat(index-page): add podcast box section
- Loading branch information
Showing
8 changed files
with
278 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
154 changes: 154 additions & 0 deletions
154
packages/index-page/src/components/podcast-box-section.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
import { finalMedia } from '../utils/style-utils' | ||
import { sourceHanSansTC as fontWeight } from '@twreporter/core/lib/constants/font-weight' | ||
import ArrowIcon from '../static/icon-podcast-arrow-white.svg' | ||
import PodcastLandingPageLinkWithUtm from '@twreporter/react-components/lib/podcast-link-with-utm' | ||
import React from 'react' | ||
import app from '../constants/app' | ||
import styled from 'styled-components' | ||
|
||
const mockup = { | ||
defaultWidth: 320, | ||
contentWidth: 238, | ||
} | ||
|
||
const mobileContentWidthPct = (mockup.contentWidth / mockup.defaultWidth) * 100 | ||
|
||
const Container = styled.div` | ||
background-image: url(${app.assetsPath}/PodcastBox_Desktop.jpg); | ||
background-size: contain; | ||
padding-top: 30px; | ||
padding-bottom: 30px; | ||
${finalMedia.mobile` | ||
background-image: url(${app.assetsPath}/PodcastBox_Mobile.jpg); | ||
padding-top: 40px; | ||
padding-bottom: 60px; | ||
`} | ||
${finalMedia.tablet` | ||
background-image: url(${app.assetsPath}/PodcastBox_Tablet.jpg); | ||
`} | ||
${finalMedia.overDesktop` | ||
background-image: url(${app.assetsPath}/PodcastBox_DesktopHD.jpg); | ||
`} | ||
` | ||
|
||
const ContentContainer = styled.div` | ||
display: flex; | ||
justify-content: space-between; | ||
align-items: center; | ||
max-width: 1024px; | ||
margin: 0 auto; | ||
padding: 0 100px; | ||
color: #000; | ||
/* ff-tisa-web-prop is for english text */ | ||
font-family: ff-tisa-web-pro, source-han-sans-traditional, sans-serif; | ||
h3{ | ||
margin: 0; | ||
font-size: 32px; | ||
font-weight: ${fontWeight.bold}; | ||
line-height: 1.63; | ||
} | ||
p{ | ||
margin-top: 10px; | ||
font-size: 14px; | ||
line-height: 1.57; | ||
text-align: left; | ||
} | ||
${finalMedia.overDesktop` | ||
max-width: 1440px; | ||
padding: 0 219px; | ||
`} | ||
${finalMedia.tablet` | ||
position: relative; | ||
padding: 0 110px; | ||
p{ | ||
margin-top: 17px; | ||
} | ||
`} | ||
${finalMedia.mobile` | ||
max-width: ${mobileContentWidthPct}%; | ||
display: block; | ||
text-align: center; | ||
padding: 0; | ||
h3{ | ||
font-size: 24px; | ||
line-height: 2.17; | ||
} | ||
p{ | ||
margin-top: 0; | ||
} | ||
`} | ||
` | ||
|
||
const ListenButton = styled.div` | ||
a { | ||
width: 116px; | ||
height: 40px; | ||
border-radius: 20px; | ||
background: #a67a44; | ||
border: none; | ||
color: #ffffff; | ||
font-size: 16px; | ||
cursor: pointer; | ||
display: table; | ||
${finalMedia.tablet` | ||
position: absolute; | ||
right: 110px; | ||
top: 9px; | ||
`} | ||
${finalMedia.mobile` | ||
margin: 40px auto 0 auto; | ||
`} | ||
span { | ||
display: table-cell; | ||
vertical-align: middle; | ||
text-align: center; | ||
font-weight: ${fontWeight.bold}; | ||
} | ||
} | ||
` | ||
|
||
const TextColumn = styled.div` | ||
${finalMedia.desktop` | ||
max-width: 608px; | ||
`} | ||
` | ||
|
||
const Icon = styled.div` | ||
display: inline-block; | ||
margin-left: 8.6px; | ||
width: 7px; | ||
height: 12px; | ||
` | ||
|
||
const listenNow = ( | ||
<span> | ||
立即收聽 | ||
<Icon> | ||
<ArrowIcon /> | ||
</Icon> | ||
</span> | ||
) | ||
|
||
class PodcastBoxSection extends React.PureComponent { | ||
render() { | ||
return ( | ||
<Container> | ||
<ContentContainer> | ||
<TextColumn> | ||
<h3>聽Podcast,感受真實</h3> | ||
<p> | ||
報導者Podcast節目,透過記者、事件當事人的第一手告白,和來自現場的收音紀錄,帶你走進新聞幕後、故事現場,感受更完整的真實。 | ||
</p> | ||
</TextColumn> | ||
<ListenButton> | ||
<PodcastLandingPageLinkWithUtm utmMedium="index"> | ||
{listenNow} | ||
</PodcastLandingPageLinkWithUtm> | ||
</ListenButton> | ||
</ContentContainer> | ||
</Container> | ||
) | ||
} | ||
} | ||
|
||
export default PodcastBoxSection |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
packages/index-page/src/static/icon-podcast-arrow-white.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import PropTypes from 'prop-types' | ||
import React from 'react' | ||
import linkWithParams from './utils/link-with-params' | ||
|
||
// TODO(taylrj): update to the topic landing page link | ||
const podcastTopicLandingPageLink = 'https://www.twreporter.org/a/podcast-list' | ||
|
||
class PodcastLandingPageLinkWithUtm extends React.PureComponent { | ||
static propTypes = { | ||
children: PropTypes.node, | ||
utmMedium: PropTypes.string, | ||
utmSource: PropTypes.string, | ||
utmCampaign: PropTypes.string, | ||
} | ||
|
||
state = { | ||
isClient: false, | ||
} | ||
|
||
componentDidMount() { | ||
this.setState({ | ||
isClient: true, | ||
}) | ||
} | ||
|
||
getLinkWithSearchParams(originalUrl) { | ||
const { utmSource, utmMedium, utmCampaign } = this.props | ||
try { | ||
const params = { | ||
utm_source: utmSource || window.location.host, | ||
utm_medium: utmMedium, | ||
utm_campaign: utmCampaign, | ||
} | ||
return linkWithParams(originalUrl, params) | ||
} catch (e) { | ||
console.warn('Can not get podcast landing page url with utm param', e) | ||
return originalUrl | ||
} | ||
} | ||
|
||
render() { | ||
const { children } = this.props | ||
const { isClient } = this.state | ||
let podcastLandingPageURL = podcastTopicLandingPageLink | ||
|
||
if (isClient) { | ||
// client side rendering | ||
podcastLandingPageURL = this.getLinkWithSearchParams( | ||
podcastLandingPageURL | ||
) | ||
} | ||
|
||
return ( | ||
<a href={podcastLandingPageURL} target="_blank" rel="noopener noreferrer"> | ||
{children} | ||
</a> | ||
) | ||
} | ||
} | ||
|
||
export default PodcastLandingPageLinkWithUtm |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import url from 'url' | ||
|
||
/** | ||
* @param { string } originalUrl - The original url | ||
* @param { Object } params - search params to be set | ||
* @return { string } originalUrl with search params | ||
*/ | ||
export default function linkWithParams(originalUrl, params) { | ||
const urlObj = new URL(originalUrl) | ||
for (let key in params) { | ||
if (params[key]) { | ||
urlObj.searchParams.set(key, params[key]) | ||
} | ||
} | ||
return url.format(urlObj) | ||
} |