@@ -4,15 +4,16 @@ import {
44 Position ,
55 Spinner ,
66 SpinnerSize ,
7- Text ,
87 Tooltip
98} from '@blueprintjs/core' ;
109import { IconNames } from '@blueprintjs/icons' ;
11- import React from 'react' ;
10+ import React , { useRef , useState } from 'react' ;
1211import * as CopyToClipboard from 'react-copy-to-clipboard' ;
12+ import { usePlaygroundConfigurationEncoder } from 'src/features/playground/shareLinks/encoder/Encoder' ;
1313import ShareLinkState from 'src/features/playground/shareLinks/ShareLinkState' ;
1414
1515import ControlButton from '../ControlButton' ;
16+ import { postSharedProgram } from '../sagas/RequestsSaga' ;
1617import Constants from '../utils/Constants' ;
1718import { showWarningMessage } from '../utils/notifications/NotificationsHelper' ;
1819import { request } from '../utils/RequestHelper' ;
@@ -35,12 +36,6 @@ type StateProps = {
3536 token : Tokens ;
3637} ;
3738
38- type State = {
39- keyword : string ;
40- isLoading : boolean ;
41- isSuccess : boolean ;
42- } ;
43-
4439type ShareLinkRequestHelperParams = RemoveLast < Parameters < typeof request > > ;
4540
4641export type Tokens = {
@@ -55,157 +50,96 @@ export const requestToShareProgram = async (
5550 return resp ;
5651} ;
5752
58- export class ControlBarShareButton extends React . PureComponent < ControlBarShareButtonProps , State > {
59- private shareInputElem : React . RefObject < HTMLInputElement > ;
60-
61- constructor ( props : ControlBarShareButtonProps ) {
62- super ( props ) ;
63- this . selectShareInputText = this . selectShareInputText . bind ( this ) ;
64- this . handleChange = this . handleChange . bind ( this ) ;
65- this . toggleButton = this . toggleButton . bind ( this ) ;
66- this . fetchUUID = this . fetchUUID . bind ( this ) ;
67- this . shareInputElem = React . createRef ( ) ;
68- this . state = { keyword : '' , isLoading : false , isSuccess : false } ;
69- }
70-
71- componentDidMount ( ) {
72- document . addEventListener ( 'keydown' , this . handleKeyDown ) ;
73- }
74-
75- componentWillUnmount ( ) {
76- document . removeEventListener ( 'keydown' , this . handleKeyDown ) ;
77- }
78-
79- handleKeyDown = ( event : any ) => {
80- if ( event . key === 'Enter' && event . ctrlKey ) {
81- // press Ctrl+Enter to generate and copy new share link directly
82- this . setState ( { keyword : 'Test' } ) ;
83- this . props . handleShortenURL ( this . state . keyword ) ;
84- this . setState ( { isLoading : true } ) ;
85- if ( this . props . shortURL || this . props . isSicp ) {
86- this . selectShareInputText ( ) ;
87- console . log ( 'link created.' ) ;
88- }
89- }
90- } ;
53+ export const ControlBarShareButton : React . FC < ControlBarShareButtonProps > = props => {
54+ const shareInputElem = useRef < HTMLInputElement > ( null ) ;
55+ const [ isLoading , setIsLoading ] = useState ( false ) ;
56+ const [ shortenedUrl , setShortenedUrl ] = useState ( '' ) ;
57+ const [ customStringKeyword , setCustomStringKeyword ] = useState ( '' ) ;
58+ const playgroundConfiguration = usePlaygroundConfigurationEncoder ( ) ;
9159
92- public render ( ) {
93- const shareButtonPopoverContent =
94- this . props . queryString === undefined ? (
95- < Text >
96- Share your programs! Type something into the editor (left), then click on this button
97- again.
98- </ Text >
99- ) : this . props . isSicp ? (
100- < div >
101- < input defaultValue = { this . props . queryString ! } readOnly = { true } ref = { this . shareInputElem } />
102- < Tooltip content = "Copy link to clipboard" >
103- < CopyToClipboard text = { this . props . queryString ! } >
104- < ControlButton icon = { IconNames . DUPLICATE } onClick = { this . selectShareInputText } />
105- </ CopyToClipboard >
106- </ Tooltip >
107- </ div >
108- ) : (
109- < >
110- { ! this . state . isSuccess || this . props . shortURL === 'ERROR' ? (
111- ! this . state . isLoading || this . props . shortURL === 'ERROR' ? (
112- < div >
113- { Constants . urlShortenerBase }
114- < input
115- placeholder = { 'custom string (optional)' }
116- onChange = { this . handleChange }
117- style = { { width : 175 } }
118- />
119- < ControlButton
120- label = "Get Link"
121- icon = { IconNames . SHARE }
122- // post request to backend, set keyword as return uuid
123- onClick = { ( ) => this . fetchUUID ( this . props . token ) }
124- />
125- </ div >
126- ) : (
127- < div >
128- < NonIdealState
129- description = "Generating Shareable Link..."
130- icon = { < Spinner size = { SpinnerSize . SMALL } /> }
131- />
132- </ div >
133- )
134- ) : (
135- < div key = { this . state . keyword } >
136- < input defaultValue = { this . state . keyword } readOnly = { true } ref = { this . shareInputElem } />
137- < Tooltip content = "Copy link to clipboard" >
138- < CopyToClipboard text = { this . state . keyword } >
139- < ControlButton icon = { IconNames . DUPLICATE } onClick = { this . selectShareInputText } />
140- </ CopyToClipboard >
141- </ Tooltip >
142- </ div >
143- ) }
144- </ >
145- ) ;
146-
147- return (
148- < Popover
149- popoverClassName = "Popover-share"
150- inheritDarkTheme = { false }
151- content = { shareButtonPopoverContent }
152- >
153- < Tooltip content = "Get shareable link" placement = { Position . TOP } >
154- < ControlButton label = "Share" icon = { IconNames . SHARE } onClick = { ( ) => this . toggleButton ( ) } />
155- </ Tooltip >
156- </ Popover >
157- ) ;
158- }
159-
160- public componentDidUpdate ( prevProps : ControlBarShareButtonProps ) {
161- if ( this . props . shortURL !== prevProps . shortURL ) {
162- this . setState ( { keyword : '' , isLoading : false } ) ;
163- }
164- }
60+ const generateLink = ( ) => {
61+ setIsLoading ( true ) ;
16562
166- private toggleButton ( ) {
167- if ( this . props . handleGenerateLz ) {
168- this . props . handleGenerateLz ( ) ;
169- }
63+ customStringKeyword ;
17064
171- // reset state
172- this . setState ( { keyword : '' , isLoading : false , isSuccess : false } ) ;
173- }
65+ return postSharedProgram ( playgroundConfiguration )
66+ . then ( ( { shortenedUrl } ) => setShortenedUrl ( shortenedUrl ) )
67+ . catch ( err => showWarningMessage ( err . toString ( ) ) )
68+ . finally ( ( ) => setIsLoading ( false ) ) ;
69+ } ;
17470
175- private handleChange ( event : React . FormEvent < HTMLInputElement > ) {
176- this . setState ( { keyword : event . currentTarget . value } ) ;
177- }
71+ const handleCustomStringChange = ( event : React . FormEvent < HTMLInputElement > ) => {
72+ setCustomStringKeyword ( event . currentTarget . value ) ;
73+ } ;
17874
179- private selectShareInputText ( ) {
180- if ( this . shareInputElem . current !== null ) {
181- this . shareInputElem . current . focus ( ) ;
182- this . shareInputElem . current . select ( ) ;
75+ // For visual effect of highlighting the text field on copy
76+ const selectShareInputText = ( ) => {
77+ if ( shareInputElem . current !== null ) {
78+ shareInputElem . current . focus ( ) ;
79+ shareInputElem . current . select ( ) ;
18380 }
184- }
185-
186- private fetchUUID ( tokens : Tokens ) {
187- const requestBody = {
188- shared_program : {
189- data : this . props . programConfig
190- }
191- } ;
192-
193- const getProgramUrl = async ( ) => {
194- const resp = await requestToShareProgram ( `shared_programs` , 'POST' , {
195- body : requestBody ,
196- ...tokens
197- } ) ;
198- if ( ! resp ) {
199- return showWarningMessage ( 'Fail to generate url!' ) ;
200- }
201- const respJson = await resp . json ( ) ;
202- this . setState ( {
203- keyword : `${ window . location . host } /playground/share/` + respJson . uuid
204- } ) ;
205- this . setState ( { isLoading : true , isSuccess : true } ) ;
206- return ;
207- } ;
208-
209- getProgramUrl ( ) ;
210- }
211- }
81+ } ;
82+
83+ const generateLinkPopoverContent = (
84+ < div >
85+ { Constants . urlShortenerBase }
86+ < input
87+ placeholder = { 'custom string (optional)' }
88+ onChange = { handleCustomStringChange }
89+ style = { { width : 175 } }
90+ />
91+ < ControlButton label = "Get Link" icon = { IconNames . SHARE } onClick = { generateLink } />
92+ </ div >
93+ ) ;
94+
95+ const generatingLinkPopoverContent = (
96+ < div >
97+ < NonIdealState
98+ description = "Generating Shareable Link..."
99+ icon = { < Spinner size = { SpinnerSize . SMALL } /> }
100+ />
101+ </ div >
102+ ) ;
103+
104+ const sicpCopyLinkPopoverContent = (
105+ < div >
106+ < input defaultValue = { props . queryString ! } readOnly = { true } ref = { shareInputElem } />
107+ < Tooltip content = "Copy link to clipboard" >
108+ < CopyToClipboard text = { props . queryString ! } >
109+ < ControlButton icon = { IconNames . DUPLICATE } onClick = { selectShareInputText } />
110+ </ CopyToClipboard >
111+ </ Tooltip >
112+ </ div >
113+ ) ;
114+
115+ const copyLinkPopoverContent = (
116+ < div key = { shortenedUrl } >
117+ < input defaultValue = { shortenedUrl } readOnly = { true } ref = { shareInputElem } />
118+ < Tooltip content = "Copy link to clipboard" >
119+ < CopyToClipboard text = { shortenedUrl } >
120+ < ControlButton icon = { IconNames . DUPLICATE } onClick = { selectShareInputText } />
121+ </ CopyToClipboard >
122+ </ Tooltip >
123+ </ div >
124+ ) ;
125+
126+ const shareButtonPopoverContent = isLoading
127+ ? generatingLinkPopoverContent
128+ : props . isSicp
129+ ? sicpCopyLinkPopoverContent
130+ : shortenedUrl
131+ ? copyLinkPopoverContent
132+ : generateLinkPopoverContent ;
133+
134+ return (
135+ < Popover
136+ popoverClassName = "Popover-share"
137+ inheritDarkTheme = { false }
138+ content = { shareButtonPopoverContent }
139+ >
140+ < Tooltip content = "Get shareable link" placement = { Position . TOP } >
141+ < ControlButton label = "Share" icon = { IconNames . SHARE } />
142+ </ Tooltip >
143+ </ Popover >
144+ ) ;
145+ } ;
0 commit comments