Showing
5 changed files
with
214 additions
and
41 deletions
custom.d.ts
0 → 100644
| ... | @@ -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%; | ... | ... |
src/public/close.svg
0 → 100644
| 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 |
src/public/copy.svg
0 → 100644
| 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 |
-
Please register or login to post a comment