diff --git a/web/lib/firebase/storage.ts b/web/lib/firebase/storage.ts new file mode 100644 index 00000000..e7794580 --- /dev/null +++ b/web/lib/firebase/storage.ts @@ -0,0 +1,49 @@ +import { + getStorage, + ref, + uploadBytesResumable, + getDownloadURL, +} from 'firebase/storage' + +const storage = getStorage() + +export const uploadImage = async ( + username: string, + file: File, + onProgress?: (progress: number, isRunning: boolean) => void +) => { + const storageRef = ref(storage, `user-images/${username}/${file.name}`) + const uploadTask = uploadBytesResumable(storageRef, file) + + let resolvePromise: (url: string) => void + let rejectPromise: (reason?: any) => void + + const promise = new Promise((resolve, reject) => { + resolvePromise = resolve + rejectPromise = reject + }) + + const unsubscribe = uploadTask.on( + 'state_changed', + (snapshot) => { + const progress = snapshot.bytesTransferred / snapshot.totalBytes + const isRunning = snapshot.state === 'running' + if (onProgress) onProgress(progress, isRunning) + }, + (error) => { + // A full list of error codes is available at + // https://firebase.google.com/docs/storage/web/handle-errors + rejectPromise(error) + unsubscribe() + }, + () => { + getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => { + resolvePromise(downloadURL) + }) + + unsubscribe() + } + ) + + return await promise +} diff --git a/web/pages/profile.tsx b/web/pages/profile.tsx index 368c2c08..a9c93b81 100644 --- a/web/pages/profile.tsx +++ b/web/pages/profile.tsx @@ -11,12 +11,14 @@ import { cleanUsername, } from '../../common/util/clean-username' import { changeUserInfo } from '../lib/firebase/api-call' +import { uploadImage } from '../lib/firebase/storage' export default function ProfilePage() { const user = useUser() const privateUser = usePrivateUser(user?.id) const [avatarUrl, setAvatarUrl] = useState(user?.avatarUrl || '') + const [avatarLoading, setAvatarLoading] = useState(false) const [name, setName] = useState(user?.name || '') const [username, setUsername] = useState(user?.username || '') @@ -59,32 +61,20 @@ export default function ProfilePage() { } } - const [selectedFile, setSelectedFile] = useState() + const fileHandler = async (event: any) => { + const file = event.target.files[0] - const changeHandler = (event: any) => { - setSelectedFile(event.target.files[0]) - // handleSubmission() - // } + setAvatarLoading(true) - // const handleSubmission = () => { - // if (!selectedFile) return - const formData = new FormData() - - formData.append('File', event.target.files[0]) - - fetch( - 'https://freeimage.host/api/1/upload?key=6d207e02198a847aa98d0a2a901485a5', - { - method: 'POST', - body: formData, - } - ) - .then((response) => response.json()) - .then((result) => { - console.log('Success:', result) + await uploadImage(user?.username || 'default', file) + .then(async (url) => { + await changeUserInfo({ avatarUrl: url }) + setAvatarUrl(url) + setAvatarLoading(false) }) - .catch((error) => { - console.error('Error:', error) + .catch(() => { + setAvatarLoading(false) + setAvatarUrl(user?.avatarUrl || '') }) } @@ -94,13 +84,19 @@ export default function ProfilePage() { <p> - <img - src={avatarUrl} - width={80} - height={80} - className="rounded-full bg-gray-400 flex items-center justify-center" - /> - <input type="file" name="file" onChange={changeHandler} /> + {avatarLoading ? ( + <button className="btn btn-ghost btn-lg btn-circle loading"></button> + ) : ( + <> + <img + src={avatarUrl} + width={80} + height={80} + className="rounded-full bg-gray-400 flex items-center justify-center" + /> + <input type="file" name="file" onChange={fileHandler} /> + </> + )} </p> <label className="label">