From 9fa16a9e0215416bcb3d5799e271613735c5ec4f Mon Sep 17 00:00:00 2001 From: Katherina Marcenko <45818654+KatvonRivia@users.noreply.github.com> Date: Wed, 22 Apr 2020 10:53:44 +0200 Subject: [PATCH] feat(share): add share component (#335) * refactor(compass): add new icon, rotate compass on hover * refactor(navigation): add new icons * refactor(button): remove focus style * refactor(layer-info): add font, remove icon * refactor(stories): add share button, fix styles * refactor(play-button): disable button when no stories are selected * refactor(button): add disabled property * style(button): change classnames function * feat(share): add share component * refactor(share): add border radius * refactor(share): add urls to config file * feat(share): remove instagram and youtube share * feat(share): add translation --- i18n/de.json | 5 +- i18n/en.json | 5 +- src/scripts/components/icons/copy-icon.tsx | 11 +++ .../components/icons/facebook-icon.tsx | 15 ++++ .../components/icons/instagram-icon.tsx | 11 +++ src/scripts/components/icons/twitter-icon.tsx | 11 +++ src/scripts/components/icons/youtube-icon.tsx | 11 +++ .../components/layer-info/layer-info.styl | 1 + .../components/navigation/navigation.tsx | 10 +-- src/scripts/components/overlay/overlay.styl | 5 +- src/scripts/components/overlay/overlay.tsx | 24 +++-- .../presentation-selector.tsx | 5 +- src/scripts/components/share/share.styl | 63 +++++++++++++ src/scripts/components/share/share.tsx | 88 +++++++++++++++++++ .../stories-selector/stories-selector.tsx | 8 +- src/scripts/components/story/story.tsx | 7 +- src/scripts/config/main.ts | 9 +- 17 files changed, 251 insertions(+), 38 deletions(-) create mode 100644 src/scripts/components/icons/copy-icon.tsx create mode 100644 src/scripts/components/icons/facebook-icon.tsx create mode 100644 src/scripts/components/icons/instagram-icon.tsx create mode 100644 src/scripts/components/icons/twitter-icon.tsx create mode 100644 src/scripts/components/icons/youtube-icon.tsx create mode 100644 src/scripts/components/share/share.styl create mode 100644 src/scripts/components/share/share.tsx diff --git a/i18n/de.json b/i18n/de.json index f23212e5a..33f93b1a5 100644 --- a/i18n/de.json +++ b/i18n/de.json @@ -21,11 +21,8 @@ "modes": "Modi", "offline": "Offline Modus", "download": "Anwendung herunterladen", - "twitter": "Twitter", - "facebook": "Facebook", - "whatsApp": "WhatsApp", + "share": "Teile mit der Welt", "copyLink": "Link kopieren", - "share": "Inhalt teilen", "export": "Daten exportieren", "info": "Weitere Informationen", "about": "Über das Projekt", diff --git a/i18n/en.json b/i18n/en.json index 50b5bc91b..bc543a510 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -21,11 +21,8 @@ "modes": "Modes", "offline": "Offline Mode", "download": "Download App", - "twitter": "Twitter", - "facebook": "Facebook", - "whatsApp": "WhatsApp", + "share": "Share with the world", "copyLink": "Copy Link", - "share": "Share Content", "export": "Export Data", "info": "More Information", "about": "About this project", diff --git a/src/scripts/components/icons/copy-icon.tsx b/src/scripts/components/icons/copy-icon.tsx new file mode 100644 index 000000000..ee6c43a1e --- /dev/null +++ b/src/scripts/components/icons/copy-icon.tsx @@ -0,0 +1,11 @@ +import React, {FunctionComponent} from 'react'; + +export const CopyIcon: FunctionComponent = () => ( + + + +); diff --git a/src/scripts/components/icons/facebook-icon.tsx b/src/scripts/components/icons/facebook-icon.tsx new file mode 100644 index 000000000..755225d16 --- /dev/null +++ b/src/scripts/components/icons/facebook-icon.tsx @@ -0,0 +1,15 @@ +import React, {FunctionComponent} from 'react'; + +export const FacebookIcon: FunctionComponent = () => ( + + + +); diff --git a/src/scripts/components/icons/instagram-icon.tsx b/src/scripts/components/icons/instagram-icon.tsx new file mode 100644 index 000000000..eb1ea2857 --- /dev/null +++ b/src/scripts/components/icons/instagram-icon.tsx @@ -0,0 +1,11 @@ +import React, {FunctionComponent} from 'react'; + +export const InstagramIcon: FunctionComponent = () => ( + + + +); diff --git a/src/scripts/components/icons/twitter-icon.tsx b/src/scripts/components/icons/twitter-icon.tsx new file mode 100644 index 000000000..e7d8541f1 --- /dev/null +++ b/src/scripts/components/icons/twitter-icon.tsx @@ -0,0 +1,11 @@ +import React, {FunctionComponent} from 'react'; + +export const TwitterIcon: FunctionComponent = () => ( + + + +); diff --git a/src/scripts/components/icons/youtube-icon.tsx b/src/scripts/components/icons/youtube-icon.tsx new file mode 100644 index 000000000..dbf0b9b1c --- /dev/null +++ b/src/scripts/components/icons/youtube-icon.tsx @@ -0,0 +1,11 @@ +import React, {FunctionComponent} from 'react'; + +export const YoutubeIcon: FunctionComponent = () => ( + + + +); diff --git a/src/scripts/components/layer-info/layer-info.styl b/src/scripts/components/layer-info/layer-info.styl index 5404db3f3..e538e6d3b 100644 --- a/src/scripts/components/layer-info/layer-info.styl +++ b/src/scripts/components/layer-info/layer-info.styl @@ -4,6 +4,7 @@ display: flex flex-direction: column justify-content: center + width: 50% font-family: NotesEsa .layerType diff --git a/src/scripts/components/navigation/navigation.tsx b/src/scripts/components/navigation/navigation.tsx index 3d51b980e..cfdc3e72b 100644 --- a/src/scripts/components/navigation/navigation.tsx +++ b/src/scripts/components/navigation/navigation.tsx @@ -7,10 +7,10 @@ import Menu from '../menu/menu'; import {LayersIcon} from '../icons/layers-icon'; import {StoryIcon} from '../icons/story-icon'; import showLayerSelectorAction from '../../actions/show-layer-selector'; +import Share from '../share/share'; +import {MenuIcon} from '../icons/menu-icon'; import styles from './navigation.styl'; -import {MenuIcon} from '../icons/menu-icon'; -import {ShareIcon} from '../icons/share-icon'; const Navigation: FunctionComponent = () => { const [showMenu, setShowMenu] = useState(false); @@ -30,11 +30,7 @@ const Navigation: FunctionComponent = () => { onClick={() => dispatch(showLayerSelectorAction(true))} icon={LayersIcon} /> - console.log('placeholder')} - /> + void; + onClose?: () => void; + showCloseButton?: boolean; } -const Overlay: FunctionComponent = ({children, onClose}) => { +const Overlay: FunctionComponent = ({ + children, + onClose, + showCloseButton = true +}) => { const modalElement = document.getElementById('modal'); const Content = ( - onClose()}> - onClose()} - /> + + {showCloseButton && ( + onClose && onClose()} + /> + )} {children} ); @@ -27,6 +34,7 @@ const Overlay: FunctionComponent = ({children, onClose}) => { if (modalElement) { return createPortal(Content, modalElement); } + return null; }; diff --git a/src/scripts/components/presentation-selector/presentation-selector.tsx b/src/scripts/components/presentation-selector/presentation-selector.tsx index 23481a132..66c305b36 100644 --- a/src/scripts/components/presentation-selector/presentation-selector.tsx +++ b/src/scripts/components/presentation-selector/presentation-selector.tsx @@ -3,8 +3,7 @@ import {useIntl} from 'react-intl'; import StoryList from '../story-list/story-list'; import Header from '../header/header'; -import {ShareIcon} from '../icons/share-icon'; -import Button from '../button/button'; +import Share from '../share/share'; import {StoryMode} from '../../types/story-mode'; @@ -19,7 +18,7 @@ const PresentationSelector: FunctionComponent = () => { backLink="/" backButtonId="backToDataMode" title={intl.formatMessage({id: 'presenter'})}> - console.log('placeholder')} /> + diff --git a/src/scripts/components/share/share.styl b/src/scripts/components/share/share.styl new file mode 100644 index 000000000..e66236581 --- /dev/null +++ b/src/scripts/components/share/share.styl @@ -0,0 +1,63 @@ +@require '../../../variables.styl' + +.share + display: flex + +.shareOverlay + position: relative + display: flex + flex-direction: column + align-items: center + padding: emCalc(40px) + max-width: emCalc(450px) + width: 50% + border-radius: emCalc(4px) + background-color: $darkGrey4 + +.shareButton + padding-left: emCalc(30px) + font-size: emCalc(18px) + line-height: emCalc(22px) + +.closeButton + position: absolute + top: emCalc(10px) + right: emCalc(10px) + +.title + margin: 0 + margin-bottom: emCalc(15px) + color: $textWhite + text-align: center + font-size: emCalc(36px) + font-family: NotesEsa + +.shareButtons + display: flex + flex-wrap: wrap + justify-content: center + align-items: flex-end + width: 100% + +.button + display: flex + flex-direction: column + align-items: center + padding: emCalc(15px) emCalc(20px) + outline: 0 + border: none + background: none + color: $textColor + text-transform: uppercase + font-size: emCalc(12px) + font-family: NotesEsa + + svg + padding-bottom: emCalc(10px) + fill: $main + + &:hover + color: $textWhite + + svg + fill: $textWhite diff --git a/src/scripts/components/share/share.tsx b/src/scripts/components/share/share.tsx new file mode 100644 index 000000000..20778d9b9 --- /dev/null +++ b/src/scripts/components/share/share.tsx @@ -0,0 +1,88 @@ +import React, {FunctionComponent, useState, useRef} from 'react'; +import {FormattedMessage} from 'react-intl'; + +import {TwitterIcon} from '../icons/twitter-icon'; +import {FacebookIcon} from '../icons/facebook-icon'; +import {CopyIcon} from '../icons/copy-icon'; +import {ShareIcon} from '../icons/share-icon'; +import config from '../../config/main'; +import Button from '../button/button'; +import {CloseIcon} from '../icons/close-icon'; +import Overlay from '../overlay/overlay'; +import {replaceUrlPlaceholders} from '../../libs/replace-url-placeholders'; + +import styles from './share.styl'; + +const Share: FunctionComponent = () => { + const [showShare, setShowShare] = useState(false); + const currentUrl = window.location.href; + const facebookUrl = replaceUrlPlaceholders(config.share.facebook, { + currentUrl + }); + const twitterUrl = replaceUrlPlaceholders(config.share.twitter, { + currentUrl + }); + const ref = useRef(null); + const copyUrl = () => { + if (ref.current) { + if (navigator.clipboard) { + navigator.clipboard.writeText(currentUrl); + } else { + ref.current.value = currentUrl; + ref.current.focus(); + ref.current.select(); + document.execCommand('copy'); + } + } + }; + return ( + + setShowShare(true)} + /> + {showShare && ( + + + setShowShare(false)} + /> + + + + + + + Twitter + + + + Facebook + + copyUrl()}> + + + + + + + + + + )} + + ); +}; + +export default Share; diff --git a/src/scripts/components/stories-selector/stories-selector.tsx b/src/scripts/components/stories-selector/stories-selector.tsx index 7e4e7e165..cd8eef9c2 100644 --- a/src/scripts/components/stories-selector/stories-selector.tsx +++ b/src/scripts/components/stories-selector/stories-selector.tsx @@ -3,11 +3,11 @@ import {useIntl} from 'react-intl'; import StoryList from '../story-list/story-list'; import Header from '../header/header'; -import Button from '../button/button'; -import styles from './stories-selector.styl'; +import Share from '../share/share'; import {StoryMode} from '../../types/story-mode'; -import {ShareIcon} from '../icons/share-icon'; + +import styles from './stories-selector.styl'; const StoriesSelector: FunctionComponent = () => { const intl = useIntl(); @@ -18,7 +18,7 @@ const StoriesSelector: FunctionComponent = () => { backLink="/" backButtonId="backToDataMode" title={intl.formatMessage({id: 'storyMode'})}> - console.log('placeholder')} /> + diff --git a/src/scripts/components/story/story.tsx b/src/scripts/components/story/story.tsx index 88d3e3623..ec6a1f3b8 100644 --- a/src/scripts/components/story/story.tsx +++ b/src/scripts/components/story/story.tsx @@ -13,14 +13,13 @@ import StoryVideo from '../story-video/story-video'; import setGlobeProjectionAction from '../../actions/set-globe-projection'; import setSelectedLayerIdsAction from '../../actions/set-selected-layer-id'; import setGlobeTimeAction from '../../actions/set-globe-time'; +import Share from '../share/share'; import {StoryMode} from '../../types/story-mode'; import {Slide, Story as StoryType} from '../../types/story'; import {GlobeProjection} from '../../types/globe-projection'; import styles from './story.styl'; -import Button from '../button/button'; -import {ShareIcon} from '../icons/share-icon'; const Story: FunctionComponent = () => { const storyParams = useStoryParams(); @@ -33,7 +32,7 @@ const Story: FunctionComponent = () => { selectedStory, storyListItem } = storyParams; - + const storyMode = mode === StoryMode.Stories; const storyClasses = cx( styles.story, storyParams?.mode === StoryMode.Present && styles.presentStory, @@ -81,7 +80,7 @@ const Story: FunctionComponent = () => { backLink={`/${mode.toString()}`} backButtonId="backToStories" title={storyListItem.title}> - console.log('placeholder')} /> + {storyMode && } )} diff --git a/src/scripts/config/main.ts b/src/scripts/config/main.ts index b9a0c0f4a..029bad353 100644 --- a/src/scripts/config/main.ts +++ b/src/scripts/config/main.ts @@ -1,4 +1,5 @@ import {GlobeState} from '../reducers/globe/index'; + import {GlobeProjection} from '../types/globe-projection'; const globeState: GlobeState = { @@ -35,5 +36,11 @@ export default { stories: `https://storage.googleapis.com/esa-cfs-storage/${version}/stories/stories-{lang}.json`, story: `https://storage.googleapis.com/esa-cfs-storage/${version}/stories/{id}/{id}-{lang}.json` }, - globe: globeState + globe: globeState, + share: { + facebook: + 'https://www.facebook.com/sharer/sharer.php?u={currentUrl}&text=ESAClimateFromSpace', + twitter: + 'http://twitter.com/intent/tweet?status=ESA%20Climate%20From%20Space&url={currentUrl}' + } };