1
- import { ImageIcon } from "lucide-react" ;
1
+ import {
2
+ CrossIcon ,
3
+ FileImage ,
4
+ ImageIcon ,
5
+ RotateCcw ,
6
+ RotateCw ,
7
+ } from "lucide-react" ;
2
8
import Image from "next/image" ;
3
9
import { useCallback , useEffect , useMemo , useState } from "react" ;
4
10
import { useAuth } from "./AuthContext" ;
5
11
import { environment } from "@/utils/environment" ;
6
12
import { parseCookies } from "nookies" ;
7
13
import { event } from "@/utils/gtag" ;
14
+ import { Controller , useForm } from "react-hook-form" ;
15
+ import * as Dialog from "@radix-ui/react-dialog" ;
16
+ import ImageCrop from "./ImageCrop" ;
17
+ import { useCrop } from "@/hooks/useCrop" ;
8
18
9
19
export const UploadSticker = ( ) => {
10
20
const { isLogged, user, openLogin } = useAuth ( ) ;
11
- const [ sticker , setSticker ] = useState < File | null > ( null ) ;
21
+ const [ sticker , setSticker ] = useState < File | Blob | null > ( null ) ;
12
22
const [ isLoading , setIsLoading ] = useState ( false ) ;
13
23
const [ isSubmitting , setIsSubmitting ] = useState ( false ) ;
14
-
24
+ const [ dialogSubmit , setDialogSubmit ] = useState ( false ) ;
15
25
const previewImagem = useMemo (
16
26
( ) => ( sticker ? URL . createObjectURL ( sticker ) : null ) ,
17
27
[ sticker ]
18
28
) ;
29
+ const crop = useCrop ( ) ;
30
+
31
+ const {
32
+ control,
33
+ formState : { isValid } ,
34
+ handleSubmit,
35
+ reset
36
+ } = useForm ( {
37
+ defaultValues : {
38
+ name : "" ,
39
+ } ,
40
+ } ) ;
19
41
20
42
function handleFileSelected ( event : React . ChangeEvent < HTMLInputElement > ) {
21
43
const { files } = event . target ;
@@ -33,7 +55,16 @@ export const UploadSticker = () => {
33
55
const generateFormData = useCallback ( async ( ) => {
34
56
try {
35
57
const body = new FormData ( ) ;
36
- body . append ( "file" , sticker ! ) ;
58
+ if ( ! sticker ) {
59
+ alert ( "Deu erro ao editar sua imagem." ) ;
60
+ return ;
61
+ }
62
+ body . append ( "file" , sticker ) ;
63
+ body . append ( "y" , String ( crop . croppedAreaPixels ?. y ) ) ;
64
+ body . append ( "x" , String ( crop . croppedAreaPixels ?. x ) ) ;
65
+ body . append ( "width" , String ( crop . croppedAreaPixels ?. width ) ) ;
66
+ body . append ( "height" , String ( crop . croppedAreaPixels ?. height ) ) ;
67
+ body . append ( "name" , "bruno" ) ;
37
68
38
69
const cookies = parseCookies ( ) ;
39
70
const token = cookies . phone_token ;
@@ -56,10 +87,12 @@ export const UploadSticker = () => {
56
87
setIsLoading ( false ) ;
57
88
setIsSubmitting ( false ) ;
58
89
setSticker ( null ) ;
90
+ crop . reset ( )
91
+ reset ( )
59
92
}
60
- } , [ sticker ] ) ;
93
+ } , [ crop , reset , sticker ] ) ;
61
94
62
- const handleSubmit = async ( ) => {
95
+ const onSubmit = async ( ) => {
63
96
try {
64
97
setIsLoading ( true ) ;
65
98
if ( ! isLogged || ! user ?. isAuthenticated ) {
@@ -72,6 +105,8 @@ export const UploadSticker = () => {
72
105
console . log ( error ) ;
73
106
} finally {
74
107
setIsLoading ( false ) ;
108
+ crop . reset ( )
109
+ reset ( )
75
110
}
76
111
} ;
77
112
@@ -94,6 +129,19 @@ export const UploadSticker = () => {
94
129
}
95
130
}
96
131
} ;
132
+
133
+ const handleDialogSubmit = ( open : boolean ) => {
134
+ if ( open ) {
135
+ setDialogSubmit ( true )
136
+ } else {
137
+ setDialogSubmit ( false )
138
+ setSticker ( null )
139
+ crop . reset ( )
140
+ reset ( )
141
+ }
142
+ }
143
+
144
+
97
145
useEffect ( ( ) => {
98
146
window . addEventListener ( "paste" , handlePaste ) ;
99
147
return ( ) => {
@@ -167,22 +215,109 @@ export const UploadSticker = () => {
167
215
>
168
216
Remover
169
217
</ button >
170
- < button
171
- aria-label = "Gerar figurinha para receber pelo Whatsapp"
172
- className = "rounded bg-indigo-600 px-2.5 py-1 text-sm font-semibold text-white shadow-sm ring-1 ring-inset ring-indigo-300 hover:bg-indigo-500 cursor-pointer disabled:cursor-not-allowed disabled:opacity-30"
173
- onClick = { ( ) => {
174
- event ( {
175
- action : "upload_image" ,
176
- label : "envio de imagem" ,
177
- category : "upload" ,
178
- value : 1 ,
179
- } ) ;
180
- return handleSubmit ( ) ;
181
- } }
182
- disabled = { ! sticker || isLoading }
218
+
219
+ < Dialog . Root
220
+ open = { dialogSubmit && ! ! previewImagem }
221
+ onOpenChange = { handleDialogSubmit }
183
222
>
184
- { isLoading ? "Gerando e enviando ..." : "Gerar figurinha 🪄" }
185
- </ button >
223
+ < Dialog . Trigger asChild >
224
+ < button
225
+ type = "button"
226
+ aria-label = "Gerar figurinha para receber pelo Whatsapp"
227
+ className = "rounded bg-indigo-600 px-2.5 py-1 text-sm font-semibold text-white shadow-sm ring-1 ring-inset ring-indigo-300 hover:bg-indigo-500 cursor-pointer disabled:cursor-not-allowed disabled:opacity-30"
228
+ onClick = { ( ) => {
229
+ event ( {
230
+ action : "upload_image" ,
231
+ label : "envio de imagem" ,
232
+ category : "upload" ,
233
+ value : 1 ,
234
+ } ) ;
235
+ } }
236
+ disabled = { ! sticker || isLoading }
237
+ >
238
+ { isLoading ? "Gerando e enviando ..." : "Gerar figurinha 🪄" }
239
+ </ button >
240
+ </ Dialog . Trigger >
241
+
242
+ < Dialog . Portal >
243
+ < Dialog . Overlay className = "bg-zinc-700/90 data-[state=open]:animate-overlayShow fixed inset-0" />
244
+ < Dialog . Close asChild >
245
+ < button
246
+ className = "text-violet11 hover:bg-violet4 focus:shadow-violet7 absolute top-[10px] right-[10px] inline-flex h-[25px] w-[25px] appearance-none items-center justify-center rounded-full focus:shadow-[0_0_0_2px] focus:outline-none"
247
+ aria-label = "Close"
248
+ >
249
+ < CrossIcon />
250
+ </ button >
251
+ </ Dialog . Close >
252
+ < Dialog . Content
253
+ className = "data-[state=open]:animate-contentShow fixed top-[50%] left-[50%] max-h-[85vh] w-[90vw] max-w-[900px] translate-x-[-50%] translate-y-[-50%] rounded-[6px] bg-white p-[25px] shadow-[hsl(206_22%_7%_/_35%)_0px_10px_38px_-10px,_hsl(206_22%_7%_/_20%)_0px_10px_20px_-15px] focus:outline-none grid grid-cols-2 gap-2"
254
+ asChild
255
+ >
256
+ { previewImagem && (
257
+ < form onSubmit = { handleSubmit ( onSubmit ) } noValidate >
258
+ < ImageCrop previewImagem = { previewImagem } { ...crop } />
259
+ < div >
260
+ < div className = "flex items-center gap-4" >
261
+ < span > Scale</ span >
262
+ < FileImage />
263
+ < input
264
+ type = "range"
265
+ min = { 1 }
266
+ step = { 0.1 }
267
+ max = { 3 }
268
+ onChange = { ( e ) => crop . setZoom ( Number ( e . target . value ) ) }
269
+ value = { crop . zoom }
270
+ />
271
+ < FileImage size = "30" />
272
+ </ div >
273
+ < Controller
274
+ control = { control }
275
+ name = "name"
276
+ rules = { {
277
+ required : false ,
278
+ min : 3 ,
279
+ } }
280
+ render = { ( props ) => (
281
+ < div >
282
+ < label
283
+ htmlFor = { props . field . name }
284
+ className = "block text-sm font-medium leading-6 text-gray-900"
285
+ >
286
+ Nome do sticker
287
+ </ label >
288
+ < div className = "relative mt-2 rounded-md shadow-sm" >
289
+ < input
290
+ type = "text"
291
+ id = { props . field . name }
292
+ className = "block w-full ring-1 ring-inset ring-gray-300 border-0 rounded-md py-1.5 pr-10 sm:text-sm sm:leading-6"
293
+ placeholder = "Seu nome"
294
+ { ...props . field }
295
+ />
296
+ </ div >
297
+ </ div >
298
+ ) }
299
+ />
300
+ < button
301
+ type = "submit"
302
+ className = "w-full rounded bg-white px-2.5 py-1 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 disabled:opacity-30 disabled:cursor-not-allowed"
303
+ onClick = { ( ) => {
304
+ event ( {
305
+ action : "upload_image" ,
306
+ label : "envio de imagem" ,
307
+ category : "upload" ,
308
+ value : 1 ,
309
+ } ) ;
310
+ } }
311
+ disabled = { ! isValid || isSubmitting }
312
+ >
313
+ { isSubmitting ? "Enviando ..." : "Enviar" }
314
+ </ button >
315
+ </ div >
316
+ </ form >
317
+ ) }
318
+ </ Dialog . Content >
319
+ </ Dialog . Portal >
320
+ </ Dialog . Root >
186
321
</ div >
187
322
</ div >
188
323
</ div >
0 commit comments