이정민

Merge branch 'feat/web' into develop

1 +declare module "*.svg" {
2 + // eslint-disable-next-line @typescript-eslint/no-explicit-any
3 + const content: any;
4 + export default content;
5 +}
...@@ -3,6 +3,8 @@ import styled from "styled-components"; ...@@ -3,6 +3,8 @@ import styled from "styled-components";
3 import TuiImageEditor from "tui-image-editor"; 3 import TuiImageEditor from "tui-image-editor";
4 import "gif-generator/dist/gif-generator"; 4 import "gif-generator/dist/gif-generator";
5 import { postGif } from "api"; 5 import { postGif } from "api";
6 +import Close from "public/close.svg";
7 +import Copy from "public/copy.svg";
6 8
7 declare global { 9 declare global {
8 interface Window { 10 interface Window {
...@@ -23,6 +25,9 @@ const GifEditor = ({ previewURL }) => { ...@@ -23,6 +25,9 @@ const GifEditor = ({ previewURL }) => {
23 const [isUploadLoading, setIsUploadLoading] = useState(false); 25 const [isUploadLoading, setIsUploadLoading] = useState(false);
24 const [viewLink, setViewLink] = useState(null); 26 const [viewLink, setViewLink] = useState(null);
25 27
28 + const [unableToUpload, setUnableToUpload] = useState(false);
29 + const [isModalOpened, setIsModalOpened] = useState(false);
30 +
26 useEffect(() => { 31 useEffect(() => {
27 if (window) { 32 if (window) {
28 setImageEditor( 33 setImageEditor(
...@@ -58,7 +63,9 @@ const GifEditor = ({ previewURL }) => { ...@@ -58,7 +63,9 @@ const GifEditor = ({ previewURL }) => {
58 gifGenerator.make().then( 63 gifGenerator.make().then(
59 (blob: Blob) => { 64 (blob: Blob) => {
60 setBlob(blob); 65 setBlob(blob);
66 + if (blob.size > 5000000) setUnableToUpload(true);
61 setDownload(window.URL.createObjectURL(blob)); 67 setDownload(window.URL.createObjectURL(blob));
68 + setIsModalOpened(true);
62 }, 69 },
63 (error) => { 70 (error) => {
64 alert(error); 71 alert(error);
...@@ -77,37 +84,77 @@ const GifEditor = ({ previewURL }) => { ...@@ -77,37 +84,77 @@ const GifEditor = ({ previewURL }) => {
77 setViewLink(`https://gif-generator.bu.to/${res.id}`); 84 setViewLink(`https://gif-generator.bu.to/${res.id}`);
78 }; 85 };
79 86
87 + const handleCloseModal = () => {
88 + setIsModalOpened(false);
89 + setIsMakeStarted(false);
90 + setDownload(null);
91 + setPercent(0);
92 + setViewLink(null);
93 + };
94 +
95 + const clipboardCopy = (text: string) => {
96 + if (!document.queryCommandSupported("copy")) {
97 + return alert("클립보드가 지원되지 않는 브라우저입니다.");
98 + }
99 + const textarea = document.createElement("textarea");
100 + textarea.value = text;
101 + document.body.appendChild(textarea);
102 + // 사파리 브라우저 서포팅
103 + textarea.focus();
104 + // 사용자가 입력한 내용을 영역을 설정할 때 필요
105 + textarea.select();
106 + document.execCommand("copy");
107 + document.body.removeChild(textarea);
108 +
109 + return null;
110 + };
111 +
80 return ( 112 return (
81 <> 113 <>
82 <Wrapper> 114 <Wrapper>
83 {((isMakeStarted && !download) || isUploadLoading) && ( 115 {((isMakeStarted && !download) || isUploadLoading) && (
84 <> 116 <>
85 <div className="background" /> 117 <div className="background" />
118 + <div className="modal">
86 <div className="download"> 119 <div className="download">
87 - loading... {!isUploadLoading && percent}% 120 + loading... {!isUploadLoading && `${percent}%`}
121 + </div>
88 </div> 122 </div>
89 </> 123 </>
90 )} 124 )}
91 {!isUploadLoading && viewLink && ( 125 {!isUploadLoading && viewLink && (
92 - <div className="download" style={{ zIndex: 200 }}>
93 - <a href={viewLink}>{viewLink}</a>
94 - </div>
95 - )}
96 - {download && !isUploadLoading && (
97 <> 126 <>
98 <div className="background" /> 127 <div className="background" />
128 + <div className="modal">
99 <div className="download"> 129 <div className="download">
100 - <div className="download__btn"> 130 + <div className="modal__close" onClick={handleCloseModal}>
101 - <a href={download} download="new_gif.gif"> 131 + <Close />
102 - Download a File 132 + </div>
103 - </a> 133 + <div className="download__explain">Click to Copy the Link!</div>
134 + <div
135 + className="download__copy"
136 + onClick={() => clipboardCopy(viewLink)}
137 + >
138 + {viewLink}
139 + <div style={{ marginLeft: "0.5rem" }}>
140 + <Copy />
141 + </div>
104 </div> 142 </div>
105 - <div className="download__btn">
106 - <div onClick={handleUpload}>Upload to Server</div>
107 </div> 143 </div>
108 </div> 144 </div>
109 </> 145 </>
110 )} 146 )}
147 + {download && !isUploadLoading && isModalOpened && !viewLink && (
148 + <NextStepModal
149 + {...{
150 + unableToUpload,
151 + download,
152 + handleUpload,
153 + blob,
154 + handleCloseModal,
155 + }}
156 + />
157 + )}
111 <div onClick={makeGif} className="make"> 158 <div onClick={makeGif} className="make">
112 Make a Gif 159 Make a Gif
113 </div> 160 </div>
...@@ -117,6 +164,44 @@ const GifEditor = ({ previewURL }) => { ...@@ -117,6 +164,44 @@ const GifEditor = ({ previewURL }) => {
117 ); 164 );
118 }; 165 };
119 166
167 +const NextStepModal = ({
168 + unableToUpload,
169 + download,
170 + handleUpload,
171 + blob,
172 + handleCloseModal,
173 +}) => {
174 + const url = window.URL.createObjectURL(blob);
175 + return (
176 + <ModalWrapper {...{ unableToUpload }}>
177 + <div className="background" />
178 + <div className="modal">
179 + <div className="download">
180 + <div className="modal__close" onClick={handleCloseModal}>
181 + <Close />
182 + </div>
183 + <img src={url} width={500} />
184 + <div className="buttons">
185 + <div className="buttons__btn">
186 + <a href={download} download="new_gif.gif">
187 + Download a File
188 + </a>
189 + </div>
190 + <div className="buttons__btn">
191 + <div onClick={!unableToUpload && handleUpload}>
192 + Upload to Server
193 + </div>
194 + </div>
195 + </div>
196 + {unableToUpload && (
197 + <div className="warning">5MB 미만만 업로드 가능합니다!</div>
198 + )}
199 + </div>
200 + </div>
201 + </ModalWrapper>
202 + );
203 +};
204 +
120 const Wrapper = styled.div` 205 const Wrapper = styled.div`
121 position: fixed; 206 position: fixed;
122 width: 90%; 207 width: 90%;
...@@ -126,10 +211,71 @@ const Wrapper = styled.div` ...@@ -126,10 +211,71 @@ const Wrapper = styled.div`
126 display: flex; 211 display: flex;
127 flex-direction: column; 212 flex-direction: column;
128 align-items: center; 213 align-items: center;
214 + .background {
215 + position: fixed;
216 + top: 0;
217 + left: 0;
218 + width: 100%;
219 + height: 100vh;
220 + background-color: black;
221 + opacity: 0.7;
222 + z-index: 100;
223 + }
224 + .modal {
225 + width: 100%;
226 + height: 100vh;
227 + position: fixed;
228 + top: 0;
229 + left: 0;
230 + z-index: 100;
231 + display: flex;
232 + align-items: center;
233 + justify-content: center;
234 + background: transparent;
235 + &__close {
236 + cursor: pointer;
237 + position: absolute;
238 + width: 2.5rem;
239 + height: 2.5rem;
240 + background: white;
241 + border-radius: 50%;
242 + box-shadow: ${({ theme }) => theme.boxShadow.normal};
243 + display: flex;
244 + align-items: center;
245 + justify-content: center;
246 + right: -0.7rem;
247 + top: -0.7rem;
248 + z-index: 103;
249 + }
250 + }
251 + .download {
252 + position: absolute;
253 + z-index: 100;
254 + background-color: white;
255 + padding: 1.5rem 2rem;
256 + border-radius: 2rem;
257 + &__explain {
258 + font-size: 1.2rem;
259 + margin-bottom: 0.5rem;
260 + font-weight: 800;
261 + }
262 + &__copy {
263 + cursor: pointer;
264 + display: flex;
265 + align-items: center;
266 + :hover {
267 + text-decoration: underline;
268 + }
269 + }
270 + }
129 a { 271 a {
130 color: black; 272 color: black;
131 text-decoration: none; 273 text-decoration: none;
132 } 274 }
275 + .unable {
276 + cursor: not-allowed;
277 + opacity: 0.5;
278 + }
133 .make { 279 .make {
134 font: 800 11.5px Arial; 280 font: 800 11.5px Arial;
135 position: absolute; 281 position: absolute;
...@@ -137,7 +283,6 @@ const Wrapper = styled.div` ...@@ -137,7 +283,6 @@ const Wrapper = styled.div`
137 top: 0; 283 top: 0;
138 width: 120px; 284 width: 120px;
139 height: 40px; 285 height: 40px;
140 - background: red;
141 z-index: 10; 286 z-index: 10;
142 border-radius: 20px; 287 border-radius: 20px;
143 margin: 8px; 288 margin: 8px;
...@@ -150,34 +295,7 @@ const Wrapper = styled.div` ...@@ -150,34 +295,7 @@ const Wrapper = styled.div`
150 text-decoration: underline; 295 text-decoration: underline;
151 } 296 }
152 } 297 }
153 - .background { 298 +
154 - position: fixed;
155 - top: 0;
156 - left: 0;
157 - width: 100%;
158 - height: 100vh;
159 - background-color: black;
160 - opacity: 0.7;
161 - z-index: 100;
162 - }
163 - .download {
164 - position: absolute;
165 - top: 15rem;
166 - z-index: 100;
167 - display: flex;
168 - background-color: white;
169 - padding: 1.5rem 2rem;
170 - border-radius: 2rem;
171 - &__btn {
172 - cursor: pointer;
173 - :last-child {
174 - margin-left: 1rem;
175 - }
176 - :hover {
177 - text-decoration: underline;
178 - }
179 - }
180 - }
181 .tui-image-editor-container { 299 .tui-image-editor-container {
182 border-radius: 1.5rem; 300 border-radius: 1.5rem;
183 } 301 }
...@@ -196,4 +314,51 @@ const Wrapper = styled.div` ...@@ -196,4 +314,51 @@ const Wrapper = styled.div`
196 } 314 }
197 `; 315 `;
198 316
317 +const ModalWrapper = styled.div<{ unableToUpload: boolean }>`
318 + .download {
319 + position: absolute;
320 + z-index: 100;
321 + background-color: white;
322 + padding: 1.5rem 2rem;
323 + border-radius: 2rem;
324 + }
325 + .warning {
326 + box-sizing: border-box;
327 + padding-right: 1rem;
328 + width: 100%;
329 + text-align: end;
330 + margin-top: 1rem;
331 + }
332 + .buttons {
333 + display: flex;
334 + flex: 1;
335 + margin-top: 1rem;
336 + &__btn {
337 + flex: 0.5;
338 + text-align: center;
339 + cursor: pointer;
340 + border-radius: 2rem;
341 + padding: 1.5rem;
342 + background-color: #fdba3b;
343 + display: flex;
344 + align-items: center;
345 + justify-content: center;
346 + cursor: pointer;
347 + :hover {
348 + text-decoration: underline;
349 + }
350 + :last-child {
351 + margin-left: 1rem;
352 + cursor: ${({ unableToUpload }) =>
353 + unableToUpload ? "not-allowed" : "pointer"};
354 + :hover {
355 + text-decoration: ${({ unableToUpload }) =>
356 + !unableToUpload ? "underline" : "none"};
357 + }
358 + opacity: ${({ unableToUpload }) => unableToUpload && 0.5};
359 + }
360 + }
361 + }
362 +`;
363 +
199 export default GifEditor; 364 export default GifEditor;
......
...@@ -6,6 +6,7 @@ const Header = () => { ...@@ -6,6 +6,7 @@ const Header = () => {
6 6
7 const Container = styled.div` 7 const Container = styled.div`
8 position: fixed; 8 position: fixed;
9 + box-sizing: border-box;
9 top: 0; 10 top: 0;
10 left: 0; 11 left: 0;
11 width: 100%; 12 width: 100%;
......
1 +<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M24 20.188l-8.315-8.209 8.2-8.282-3.697-3.697-8.212 8.318-8.31-8.203-3.666 3.666 8.321 8.24-8.206 8.313 3.666 3.666 8.237-8.318 8.285 8.203z"/></svg>
...\ No newline at end of file ...\ No newline at end of file
1 +<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M22 6v16h-16v-16h16zm2-2h-20v20h20v-20zm-24 17v-21h21v2h-19v19h-2z"/></svg>
...\ No newline at end of file ...\ No newline at end of file