1- // @ts -check
2- /* eslint-disable react/prefer-stateless-function */
3- import React from 'react' ;
1+ import getTemplate from '@codesandbox/common/lib/templates' ;
42import {
53 sandboxUrl ,
64 profileUrl ,
75} from '@codesandbox/common/lib/utils/url-generator' ;
8-
9- import { observer } from 'mobx- react' ;
6+ import { observer } from 'mobx-react-lite' ;
7+ import React , { useCallback , useEffect , useRef , useState } from 'react' ;
108import EyeIcon from 'react-icons/lib/fa/eye' ;
119import GithubIcon from 'react-icons/lib/fa/github' ;
1210
13- import getTemplate from '@codesandbox/common/lib/templates' ;
14-
1511import {
1612 Container ,
1713 SandboxImageContainer ,
@@ -26,58 +22,58 @@ import {
2622} from './elements' ;
2723
2824type Props = {
25+ author : object ,
26+ description : string ,
27+ git : object ,
2928 id : string ,
3029 title : string ,
3130 viewCount : number ,
32- author : Object ,
33- git : Object ,
34- description : string ,
3531} ;
3632
37- class SandboxItem extends React . Component < Props > {
38- el : HTMLDivElement ;
39-
40- state = {
41- screenshotUrl : this . props . screenshotUrl ,
42- } ;
43-
44- requestScreenshot = ( ) => {
45- this . setState ( {
46- screenshotUrl : `/api/v1/sandboxes/${ this . props . id } /screenshot.png` ,
47- } ) ;
48- } ;
49-
50- checkScreenshot ( ) {
51- if ( ! this . state . screenshotUrl && this . hasScreenshot ( ) ) {
33+ const SandboxCard = ( {
34+ author,
35+ description,
36+ git,
37+ id,
38+ picks,
39+ pickSandbox,
40+ screenshotUrl,
41+ template,
42+ title,
43+ viewCount,
44+ } : Props ) => {
45+ const [ screenShotURL , setScreenShotURL ] = useState ( screenshotUrl ) ;
46+ const screenShotTimeout = useRef ( null ) ;
47+
48+ const hasScreenshot = useCallback ( ( ) => {
49+ const templateDefinition = getTemplate ( template ) ;
50+
51+ return ! templateDefinition . isServer ;
52+ } , [ template ] ) ;
53+
54+ const requestScreenshot = useCallback ( ( ) => {
55+ setScreenShotURL ( `/api/v1/sandboxes/${ id } /screenshot.png` ) ;
56+ } , [ id ] ) ;
57+ const checkScreenShot = useCallback ( ( ) => {
58+ if ( ! screenShotURL && hasScreenshot ( ) ) {
5259 // We only request the screenshot if the sandbox card is in view for > 1 second
53- this . screenshotTimeout = setTimeout ( ( ) => {
54- this . requestScreenshot ( ) ;
55- } , 1000 ) ;
60+ screenShotTimeout . current = setTimeout ( requestScreenshot , 1000 ) ;
5661 }
57- }
58-
59- componentDidMount ( ) {
60- this . checkScreenshot ( ) ;
61- }
62+ } , [ hasScreenshot , requestScreenshot , screenShotURL ] ) ;
63+ useEffect ( ( ) => {
64+ checkScreenShot ( ) ;
6265
63- componentWillUnmount ( ) {
64- if ( this . screenshotTimeout ) {
65- clearTimeout ( this . screenshotTimeout ) ;
66- }
67- }
66+ return ( ) => clearTimeout ( screenShotTimeout . current ) ;
67+ } , [ checkScreenShot ] ) ;
6868
69- openSandbox = ( ) => {
70- const url = sandboxUrl ( { id : this . props . id } ) ;
71- window . open ( url , '_blank' ) ;
72- } ;
69+ const openSandbox = useCallback ( ( ) => {
70+ const url = sandboxUrl ( { id } ) ;
7371
74- openUser = username => {
75- const url = profileUrl ( username ) ;
7672 window . open ( url , '_blank' ) ;
77- } ;
73+ } , [ id ] ) ;
7874
79- getImageMessage = ( ) => {
80- const templateDefinition = getTemplate ( this . props . template ) ;
75+ const getImageMessage = useCallback ( ( ) => {
76+ const templateDefinition = getTemplate ( template ) ;
8177
8278 if ( templateDefinition . isServer ) {
8379 return `Container Sandbox` ;
@@ -88,116 +84,89 @@ class SandboxItem extends React.Component<Props> {
8884 }
8985
9086 return `Generating Screenshot...` ;
91- } ;
87+ } , [ template ] ) ;
9288
93- hasScreenshot = ( ) => {
94- const templateDefinition = getTemplate ( this . props . template ) ;
89+ const openUser = useCallback ( username => {
90+ const url = profileUrl ( username ) ;
9591
96- if ( templateDefinition . isServer ) {
97- return false ;
98- }
92+ window . open ( url , '_blank' ) ;
93+ } , [ ] ) ;
94+
95+ const templateInfo = getTemplate ( template ) ;
9996
100- return true ;
101- } ;
102-
103- render ( ) {
104- const {
105- id,
106- title,
107- viewCount,
108- template,
109- author,
110- git,
111- description,
112- pickSandbox,
113- picks,
114- } = this . props ;
115-
116- const { screenshotUrl } = this . state ;
117-
118- const templateInfo = getTemplate ( template ) ;
119-
120- return (
121- < div
122- style = { {
123- padding : 2 ,
124- borderRadius : 2 ,
125- backgroundColor : 'transparent' ,
126- } }
127- >
128- < Container style = { { outline : 'none' } } >
129- < SandboxImageContainer
130- role = "button"
131- tabIndex = { 0 }
132- onClick = { this . openSandbox }
133- >
134- < ImageMessage > { this . getImageMessage ( ) } </ ImageMessage >
135-
136- { this . hasScreenshot ( ) && (
137- < SandboxImage
138- style = { {
139- backgroundImage : `url(${ screenshotUrl } )` ,
140- } }
141- />
142- ) }
143- </ SandboxImageContainer >
144- < SandboxInfo >
145- < div
146- style = { {
147- position : 'absolute' ,
148- top : 0 ,
149- bottom : 0 ,
150- left : 0 ,
151- width : 2 ,
152- height : 'calc(100% + 34px)' ,
153- backgroundColor : templateInfo . color ( ) ,
154- } }
97+ return (
98+ < div
99+ style = { { backgroundColor : 'transparent' , borderRadius : 2 , padding : 2 } }
100+ >
101+ < Container style = { { outline : 'none' } } >
102+ < SandboxImageContainer onClick = { openSandbox } role = "button" tabIndex = { 0 } >
103+ < ImageMessage > { getImageMessage ( ) } </ ImageMessage >
104+
105+ { hasScreenshot ( ) && (
106+ < SandboxImage
107+ style = { { backgroundImage : `url(${ screenShotURL } )` } }
155108 />
156- < div style = { { flex : 1 } } >
157- < div role = "button" tabIndex = { 0 } onClick = { this . openSandbox } >
158- < SandboxTitle > { title || id } </ SandboxTitle >
159- { description }
160- </ div >
161- < Details >
162- { author ? (
163- < FlexCenter
164- role = "button"
165- tabIndex = { 0 }
166- onClick = { ( ) => this . openUser ( author . username ) }
167- >
168- < Avatar src = { author . avatarUrl } alt = { author . username } />
169- { author . name || author . username }
170- </ FlexCenter >
171- ) : null }
109+ ) }
110+ </ SandboxImageContainer >
111+
112+ < SandboxInfo >
113+ < div
114+ style = { {
115+ backgroundColor : templateInfo . color ( ) ,
116+ bottom : 0 ,
117+ height : 'calc(100% + 34px)' ,
118+ left : 0 ,
119+ position : 'absolute' ,
120+ top : 0 ,
121+ width : 2 ,
122+ } }
123+ />
124+ < div style = { { flex : 1 } } >
125+ < div role = "button" tabIndex = { 0 } onClick = { openSandbox } >
126+ < SandboxTitle > { title || id } </ SandboxTitle >
127+ { description }
128+ </ div >
129+
130+ < Details >
131+ { author ? (
172132 < FlexCenter
133+ onClick = { ( ) => openUser ( author . username ) }
173134 role = "button"
174135 tabIndex = { 0 }
175- onClick = { this . openSandbox }
176136 >
177- < EyeIcon style = { { marginRight : '0.5rem' } } />
178- { viewCount }
137+ < Avatar alt = { author . username } src = { author . avatarUrl } />
138+
139+ { author . name || author . username }
179140 </ FlexCenter >
180- { git ? (
181- < FlexCenter >
182- < a
183- href = { `https://github.com/${ git . username } /${ git . repo } ` }
184- target = "_blank"
185- rel = "noopener noreferrer"
186- >
187- < GithubIcon style = { { marginRight : '0.5rem' } } />
188- </ a >
189- </ FlexCenter >
190- ) : null }
191- </ Details >
192- </ div >
193- </ SandboxInfo >
194- < Pick small onClick = { ( ) => pickSandbox ( id , title , description ) } >
195- { ! picks . length ? '✨ Pick Sandbox' : '✨ Pick Sandbox again' }
196- </ Pick >
197- </ Container >
198- </ div >
199- ) ;
200- }
201- }
202-
203- export default observer ( SandboxItem ) ;
141+ ) : null }
142+
143+ < FlexCenter onClick = { openSandbox } role = "button" tabIndex = { 0 } >
144+ < EyeIcon style = { { marginRight : '0.5rem' } } />
145+
146+ { viewCount }
147+ </ FlexCenter >
148+
149+ { git ? (
150+ < FlexCenter >
151+ < a
152+ href = { `https://github.com/${ git . username } /${ git . repo } ` }
153+ rel = "noopener noreferrer"
154+ target = "_blank"
155+ >
156+ < GithubIcon style = { { marginRight : '0.5rem' } } />
157+ </ a >
158+ </ FlexCenter >
159+ ) : null }
160+ </ Details >
161+ </ div >
162+ </ SandboxInfo >
163+
164+ < Pick onClick = { ( ) => pickSandbox ( id , title , description ) } small >
165+ { ! picks . length ? '✨ Pick Sandbox' : '✨ Pick Sandbox again' }
166+ </ Pick >
167+ </ Container >
168+ </ div >
169+ ) ;
170+ } ;
171+
172+ export default observer ( SandboxCard ) ;
0 commit comments