Skip to content

Commit b9dbd0e

Browse files
Refactor Curator to functional components with hooks (codesandbox#1959)
* Refactor Curator to functional components with hooks * Update packages/app/src/app/pages/Curator/index.js Co-Authored-By: Sara Vieira <[email protected]>
1 parent 7d87cd9 commit b9dbd0e

File tree

2 files changed

+245
-273
lines changed

2 files changed

+245
-273
lines changed
Lines changed: 120 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
1-
// @ts-check
2-
/* eslint-disable react/prefer-stateless-function */
3-
import React from 'react';
1+
import getTemplate from '@codesandbox/common/lib/templates';
42
import {
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';
108
import EyeIcon from 'react-icons/lib/fa/eye';
119
import GithubIcon from 'react-icons/lib/fa/github';
1210

13-
import getTemplate from '@codesandbox/common/lib/templates';
14-
1511
import {
1612
Container,
1713
SandboxImageContainer,
@@ -26,58 +22,58 @@ import {
2622
} from './elements';
2723

2824
type 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

Comments
 (0)