Skip to content

Commit 0a84c27

Browse files
author
akonovalov
committedJan 15, 2021
fix error state and add play sound
1 parent 9ee10d0 commit 0a84c27

File tree

11 files changed

+169
-87
lines changed

11 files changed

+169
-87
lines changed
 

‎packages/common/src/@types/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
export type Word = {
22
text: string;
33
translations: [string];
4-
transcription: string;
4+
transcription?: string;
55
previewUrl?: string;
66
imageUrl?: string;
77
soundUrl?: string;

‎packages/common/src/Popup/index.tsx

+89-55
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,100 @@
1-
import React, { useEffect, useState } from 'react';
1+
import React, { ReactElement, useEffect, useState } from 'react';
22
import cn from 'classnames';
33

4+
import { IconAudioPlay, IconSearch } from '../icons';
5+
import type { Word } from '../@types';
6+
47
import './styles.css';
58

9+
type Type = Word & {
10+
view?: 'loading' | 'success' | 'error';
11+
left?: string;
12+
top?: string;
13+
bottom?: string;
14+
onAdd?: () => void;
15+
};
16+
617
const onlyUnique = (value, index, self) => self.indexOf(value) === index;
718

8-
export const Popup = ({
9-
view,
10-
left,
11-
top,
12-
bottom,
13-
text,
14-
transcription,
15-
translations,
16-
onAdd,
17-
}) => {
18-
const [animated, setAnimated] = useState(false);
19+
export const Popup: React.FC<Type> = React.memo(
20+
({
21+
view = 'loading',
22+
left,
23+
top,
24+
bottom,
25+
text,
26+
transcription,
27+
translations,
28+
soundUrl,
29+
onAdd,
30+
}): ReactElement => {
31+
const [animated, setAnimated] = useState(false);
1932

20-
useEffect(() => {
21-
setAnimated(false);
22-
}, [text]);
33+
useEffect(() => {
34+
setAnimated(false);
35+
}, [text]);
2336

24-
const handleAdd = (e) => {
25-
e.stopPropagation();
37+
const handleAdd = (e) => {
38+
e.stopPropagation();
2639

27-
setAnimated(true);
28-
onAdd();
29-
};
40+
setAnimated(true);
41+
onAdd();
42+
};
3043

31-
return (
32-
<div
33-
className={cn('hw_popup', { hw_popup_animated: animated })}
34-
style={{ left, top, bottom }}
35-
>
36-
<div className="hw_popup__container">
37-
{view === 'loading' && <div className="hw_icon__loading" />}
38-
{view === 'success' && (
39-
<>
40-
{transcription && (
41-
<div className="hw_popup__transcription">{transcription}</div>
42-
)}
43-
<div className="hw_popup__main-word">{text}</div>
44-
<div className="hw_popup__translation">
45-
{translations.filter(onlyUnique).slice(0, 2).join(', ')}
46-
</div>
47-
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}
48-
<div
49-
className="hw_popup__button-add"
50-
onClick={onAdd && handleAdd}
51-
/>
52-
</>
53-
)}
54-
{view === 'error' && (
55-
<div className="hw_popup__container" style={{ left }}>
56-
<div className="hw_popup__icon__search" />
57-
<div className="hw_popup__empty-title">Translation not found</div>
58-
<div className="hw_popup__empty-description">
59-
We can't find anything for the languages
60-
</div>
61-
</div>
62-
)}
44+
const playSound = React.useCallback(() => {
45+
if (!soundUrl) return null;
46+
const audio = new Audio(soundUrl);
47+
audio.play();
48+
}, [soundUrl]);
49+
50+
return (
51+
<div
52+
className={cn('hw_popup', { hw_popup_animated: animated })}
53+
style={{ left, top, bottom }}
54+
>
55+
<div className="hw_popup__container">
56+
{view === 'loading' && <div className="hw_icon__loading" />}
57+
{view === 'success' && (
58+
<>
59+
<div className="hw_popup__header">
60+
{playSound && (
61+
<IconAudioPlay
62+
className="hw_popup__icon hw_popup__icon-sound"
63+
onClick={playSound}
64+
/>
65+
)}
66+
{transcription && (
67+
<div className="hw_popup__transcription">{transcription}</div>
68+
)}
69+
</div>
70+
<div className="hw_popup__main-word">{text}</div>
71+
<div className="hw_popup__translation">
72+
{translations.filter(onlyUnique).slice(0, 2).join(', ')}
73+
</div>
74+
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}
75+
<div
76+
className="hw_popup__button-add"
77+
onClick={onAdd && handleAdd}
78+
/>
79+
</>
80+
)}
81+
{view === 'error' && (
82+
<>
83+
<IconSearch />
84+
<div className="hw_popup__empty-title">
85+
{chrome && chrome.i18n
86+
? chrome.i18n.getMessage('translation_not_found_title')
87+
: 'Translation not found'}
88+
</div>
89+
<div className="hw_popup__empty-description">
90+
{chrome && chrome.i18n
91+
? chrome.i18n.getMessage('translation_not_found_description')
92+
: "We can't find anything for the word"}
93+
</div>
94+
</>
95+
)}
96+
</div>
6397
</div>
64-
</div>
65-
);
66-
};
98+
);
99+
}
100+
);

‎packages/common/src/Popup/styles.css

+18-10
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@
5353
/*pointer-events: none;*/
5454
}
5555

56+
.hw_popup__header {
57+
display: flex;
58+
align-items: center;
59+
}
5660

5761
.hw_popup__button-add {
5862
width: 35px;
@@ -117,16 +121,6 @@
117121
margin-top: 6px;
118122
}
119123

120-
.hw_popup__icon__search {
121-
content: " ";
122-
display: block;
123-
background-image: url("~public/images/icon_search.svg");
124-
background-size: contain;
125-
background-repeat: no-repeat;
126-
height: 39px;
127-
width: 39px;
128-
}
129-
130124
.hw_icon__loading {
131125
position: relative;
132126
width: 26px;
@@ -143,6 +137,17 @@
143137
animation-timing-function: linear;
144138
}
145139

140+
.hw_popup__icon {
141+
color: var(--link-color);
142+
}
143+
.hw_popup__icon:hover {
144+
color: var(--font-color);
145+
}
146+
.hw_popup__icon-sound {
147+
cursor: pointer;
148+
margin-right: 5px;
149+
}
150+
146151
.hw_icon__loading:before {
147152
content: "";
148153
position: absolute;
@@ -192,4 +197,7 @@
192197
.hw_icon__loading:before, .hw_icon__loading:after {
193198
background: var(--bg-color-dark-theme);
194199
}
200+
.hw_popup__icon:hover {
201+
color: var(--font-color-dark-theme);
202+
}
195203
}

‎packages/common/src/api.ts

+19-5
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,30 @@ export const sendTranslateRequest = (search: string): Promise<Word> =>
77
.then((response) => response.json())
88
.then((data) => {
99
if (data && data.length > 0) {
10-
return {
10+
const result = {
1111
text: data[0].text,
1212
translations: data[0].meanings
1313
.slice(0, 2)
1414
.map(({ translation: { text } }) => text),
15-
transcription: `/${data[0].meanings[0].transcription}/`,
16-
previewUrl: data[0].meanings[0].previewUrl,
17-
imageUrl: data[0].meanings[0].imageUrl,
18-
soundUrl: data[0].meanings[0].soundUrl,
1915
};
16+
17+
if (data[0].meanings[0].transcription) {
18+
result['transcription'] = `/${data[0].meanings[0].transcription}/`;
19+
}
20+
21+
if (data[0].meanings[0].previewUrl) {
22+
result['previewUrl'] = data[0].meanings[0].previewUrl;
23+
}
24+
25+
if (data[0].meanings[0].imageUrl) {
26+
result['imageUrl'] = data[0].meanings[0].imageUrl;
27+
}
28+
29+
if (data[0].meanings[0].soundUrl) {
30+
result['soundUrl'] = data[0].meanings[0].soundUrl;
31+
}
32+
33+
return result;
2034
}
2135
throw new Error('Empty data');
2236
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import React from 'react';
2+
3+
export const IconAudioPlay = (props) => (
4+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" {...props}>
5+
<g id="ic-audio-play">
6+
<path
7+
fillRule="evenodd"
8+
clipRule="evenodd"
9+
d="M8 8.61803L12 6.61803V17.382L8 15.382V8.61803ZM6.76393 6.99999L14 3.38196V20.618L6.76393 17H3C1.89543 17 1 16.1046 1 15V8.99999C1 7.89542 1.89543 6.99999 3 6.99999H6.76393ZM6 8.99999H3V15H6V8.99999ZM19.6791 3.45671C21.7136 5.48074 23 8.5681 23 12C23 15.4319 21.7136 18.5192 19.6791 20.5433L18.1031 19.2825C19.8488 17.6536 21 15.0122 21 12C21 8.98782 19.8488 6.34635 18.1031 4.71747L19.6791 3.45671ZM19 12C19 9.58933 18.0649 7.41237 16.5748 5.94019L14.9973 7.20218C16.2072 8.2917 17 10.0257 17 12C17 13.9743 16.2072 15.7083 14.9973 16.7978L16.5748 18.0598C18.0649 16.5876 19 14.4107 19 12Z"
10+
fill="currentColor"
11+
/>
12+
</g>
13+
</svg>
14+
);
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import React from 'react';
2+
3+
export const IconSearch = () => (
4+
<svg width="39" height="39" xmlns="http://www.w3.org/2000/svg">
5+
<g
6+
stroke="#807E87"
7+
strokeWidth="4.5"
8+
fill="none"
9+
fillRule="evenodd"
10+
strokeLinecap="round"
11+
strokeLinejoin="round"
12+
>
13+
<circle cx="17.63" cy="17.63" r="14.93" />
14+
<path d="M36.292 36.292l-8.118-8.118" />
15+
</g>
16+
</svg>
17+
);

‎packages/common/src/icons/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './IconAudioPlay';
2+
export * from './IconSearch';

‎packages/extension/src/components/icons/IconHeart.tsx

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import React from 'react';
22

3-
export const IconHeart = () => (
4-
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24">
3+
export const IconHeart = ({ className }) => (
4+
<svg
5+
className={className}
6+
xmlns="http://www.w3.org/2000/svg"
7+
width="24"
8+
height="24"
9+
>
510
<g
611
id="ic-heart"
712
fill="none"

‎packages/extension/static/_locales/en/messages.json

+1-4
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,6 @@
4848
"message": "Translation not found"
4949
},
5050
"translation_not_found_description": {
51-
"message": "We can't find anything for the languages"
52-
},
53-
"translation_not_found_languages": {
54-
"message": "$1-$2"
51+
"message": "We can't find anything for the word"
5552
}
5653
}

‎packages/extension/static/_locales/ru/messages.json

+1-4
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,6 @@
4848
"message": "Перевод не найден"
4949
},
5050
"translation_not_found_description": {
51-
"message": "Мы не смогли ничего найти по языкам"
52-
},
53-
"translation_not_found_languages": {
54-
"message": "$1-$2"
51+
"message": "Мы не смогли ничего найти по этому слову"
5552
}
5653
}

‎public/images/icon_search.svg

-6
This file was deleted.

0 commit comments

Comments
 (0)
Please sign in to comment.