File size: 3,153 Bytes
bc0330e
 
a3b619b
bc0330e
92f037b
 
 
 
 
d0a1b70
92f037b
 
 
 
38448fc
bc0330e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92f037b
 
 
 
 
 
 
 
 
26c4b30
 
 
38448fc
26c4b30
 
 
bc0330e
a3b619b
 
 
 
 
 
 
 
 
 
 
 
bc0330e
 
6b8f69a
 
bc0330e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92f037b
38448fc
54a4eaa
 
92f037b
 
 
 
 
 
 
 
 
 
c3e8f3d
 
92f037b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import {
  generateVideoThumbnails,
  getVideoDurationFromVideoFile,
} from '@rajesh896/video-thumbnails-generator';
import { useCallback, useState } from 'react';
import { DropzoneOptions, useDropzone } from 'react-dropzone';
import { toast } from 'react-hot-toast';
import { fetcher } from '../utils';
import { SignedPayload } from '../types';

const useMediaUpload = (
  onUpload: (uploadUrl: string) => void,
  options?: Partial<DropzoneOptions>,
) => {
  const [isUploading, setUploading] = useState(false);

  const upload = useCallback(async (file: File, chatId?: string) => {
    const { id, signedUrl, publicUrl, fields } = await fetcher<SignedPayload>(
      '/api/sign',
      {
        method: 'POST',
        body: JSON.stringify({
          id: chatId,
          fileType: file.type,
          fileName: file.name,
        }),
      },
    );
    const formData = new FormData();
    Object.entries(fields).forEach(([key, value]) => {
      formData.append(key, value as string);
    });
    formData.append('file', file);

    const uploadResponse = await fetch(signedUrl, {
      method: 'POST',
      body: formData,
    });
    if (!uploadResponse.ok) {
      toast.error(uploadResponse.statusText);
      return;
    }
    return {
      id,
      publicUrl,
    };
  }, []);

  const { getRootProps, getInputProps, isDragActive, open } = useDropzone({
    accept: {
      'image/*': ['.jpeg', '.png'],
      'video/mp4': ['.mp4', '.MP4'],
    },
    noClick: true,
    noKeyboard: true,
    multiple: false,
    onDrop: async files => {
      if (files.length !== 1) {
        throw new Error('Only one image can be uploaded at a time');
      }
      setUploading(true);
      const reader = new FileReader();
      reader.readAsDataURL(files[0]);
      reader.onload = async () => {
        const file = files[0];
        if (file.type === 'video/mp4') {
          const duration = await getVideoDurationFromVideoFile(file);
          if (duration > 30) {
            setUploading(false);
            toast.error('Video duration must be less than 30 seconds');
            return;
          }
        } else if (file.size > 2.5 * 1024 * 1024) {
          setUploading(false);
          toast.error('Image size must be less than 2.5MB');
          return;
        }
        const resp = await upload(file);
        if (!resp) {
          return;
        }
        if (file.type === 'video/mp4') {
          const thumbnails = await generateVideoThumbnails(file, 1, 'file');
          fetch(thumbnails[0])
            .then(res => res.blob())
            .then(blob => {
              const thumbnailFile = new File(
                [blob],
                file.name.replace('.mp4', '.png').replace('.MP4', '.png'),
                {
                  type: 'image/png',
                },
              );
              return upload(thumbnailFile, resp.id);
            });
        }
        onUpload(resp.publicUrl);
        setUploading(false);
      };
    },
    ...options,
  });

  return {
    getRootProps,
    getInputProps,
    isDragActive,
    isUploading,
    openUpload: open,
  };
};

export default useMediaUpload;