diff --git a/src/components/header/index.jsx b/src/components/header/index.jsx index 717e9bc..f187c7e 100644 --- a/src/components/header/index.jsx +++ b/src/components/header/index.jsx @@ -6,6 +6,7 @@ import ApiSelector from '../api-selector'; import UserMenu from '../user-menu'; import VersionSelector from '../version-selector'; import LookupContainer from '../lookup-container'; +import Share from '../share'; import Submit from '../submit'; const Header = () => @@ -14,6 +15,7 @@ const Header = () => + diff --git a/src/components/share/index.jsx b/src/components/share/index.jsx new file mode 100644 index 0000000..3a34682 --- /dev/null +++ b/src/components/share/index.jsx @@ -0,0 +1,57 @@ +import React, { useState } from 'react'; +import { connect } from 'react-redux'; + +import './style.css'; + +const Share = ( { api, version, method, url, pathLabeled, pathValues, queryParams } ) => { + const [ buttonText, setButtonText ] = useState( 'Share' ); + + // The API console has two methods, 1. Directly type in the URL, 2. Use the form to build the URL. + // I'm calling 2 the "madlibs" method, because it's like filling in the blanks of a story. + // This function constructs the madlibs path if we can't use the direct request.url. + const constructMadlibsPath = () => { + let path = pathLabeled; + Object.keys( pathValues ).forEach( ( key ) => { + path = path.replace( key, encodeURIComponent( pathValues[ key ] ) ); + console.log( key, pathValues[ key ], path ); + } ); + if ( queryParams ) { + const queryParamsString = new URLSearchParams( queryParams ).toString(); + path += `?${ queryParamsString }`; + } + return path; + }; + + const buildShareableLink = () => { + const finalPath = url || constructMadlibsPath(); + const baseUrl = window.location.origin; + const queryParams = new URLSearchParams( { api, version, method, url: finalPath } ).toString(); + const shareableUrl = `${ baseUrl }?${ queryParams }`; + + navigator.clipboard + .writeText( shareableUrl ) + .then( () => { + setButtonText( 'Copied!' ); + setTimeout( () => setButtonText( 'Share' ), 2000 ); + } ) + .catch( ( err ) => console.error( 'Error copying URL to clipboard', err ) ); // Error handling + }; + + return ( + + ); +}; + +const mapStateToProps = ( state ) => ( { + api: state.ui.api, + version: state.ui.version, + method: state.request.method, + url: state.request.url, + pathValues: state.request?.pathValues, + queryParams: state.request?.queryParams, + pathLabeled: state.request?.endpoint?.pathLabeled, +} ); + +export default connect( mapStateToProps )( Share ); diff --git a/src/components/share/style.css b/src/components/share/style.css new file mode 100644 index 0000000..a3819ae --- /dev/null +++ b/src/components/share/style.css @@ -0,0 +1,16 @@ +#share-button { + cursor: pointer; + min-width: 44px; + background-color: #fff; + border: 0; + color: rgb(133, 192, 224); + transition: background-color 0.2s ease, color 0.2s ease; +} +#share-button:hover, #share-button:focus { + background-color: rgb(133, 192, 224); + color: #fff; +} +#share-button:active { + background-color: rgb(103, 162, 194); + color: #fff; +} diff --git a/src/state/index.js b/src/state/index.js index b95b768..fa4a5dd 100644 --- a/src/state/index.js +++ b/src/state/index.js @@ -4,6 +4,7 @@ import thunk from 'redux-thunk'; import reducer from './reducer'; import { boot } from './security/actions'; import { loadInitialState, persistState } from '../lib/redux/cache'; +import initStateFromUrl from './init-state-from-url'; const store = createStore( reducer, @@ -12,5 +13,6 @@ const store = createStore( ); persistState( store, reducer ); store.dispatch( boot() ); +initStateFromUrl( store ); export default store; diff --git a/src/state/init-state-from-url.js b/src/state/init-state-from-url.js new file mode 100644 index 0000000..a294bcc --- /dev/null +++ b/src/state/init-state-from-url.js @@ -0,0 +1,23 @@ +import { selectApi, selectVersion } from './ui/actions'; +import { updateMethod, updateUrl } from './request/actions'; + +function initStateFromUrl( store ) { + const requestUrl = new URL( window.location ); + const api = requestUrl.searchParams.get( 'api' ); + const version = requestUrl.searchParams.get( 'version' ); + const method = requestUrl.searchParams.get( 'method' ); + const url = requestUrl.searchParams.get( 'url' ); + if ( api ) { + store.dispatch( selectApi( api ) ); + } + if ( version ) { + store.dispatch( selectVersion( version ) ); + } + if ( method ) { + store.dispatch( updateMethod( method ) ); + } + if ( url ) { + store.dispatch( updateUrl( url ) ); + } +} +export default initStateFromUrl;