init commit,
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
'use client'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { createClient } from '@/utils/supabase/client'
|
||||
import { type User } from '@supabase/supabase-js'
|
||||
import Avatar from './avatar'
|
||||
|
||||
export default function AccountForm({ user }: { user: User | null }) {
|
||||
const supabase = createClient()
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [fullname, setFullname] = useState<string | null>(null)
|
||||
const [username, setUsername] = useState<string | null>(null)
|
||||
const [website, setWebsite] = useState<string | null>(null)
|
||||
const [avatar_url, setAvatarUrl] = useState<string | null>(null)
|
||||
|
||||
const getProfile = useCallback(async () => {
|
||||
try {
|
||||
setLoading(true)
|
||||
|
||||
const { data, error, status } = await supabase
|
||||
.from('profiles')
|
||||
.select(`full_name, username, website, avatar_url`)
|
||||
.eq('id', user?.id)
|
||||
.single()
|
||||
|
||||
if (error && status !== 406) {
|
||||
console.log(error)
|
||||
throw error
|
||||
}
|
||||
|
||||
if (data) {
|
||||
setFullname(data.full_name)
|
||||
setUsername(data.username)
|
||||
setWebsite(data.website)
|
||||
setAvatarUrl(data.avatar_url)
|
||||
}
|
||||
} catch (error) {
|
||||
alert('Error loading user data!')
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}, [user, supabase])
|
||||
|
||||
useEffect(() => {
|
||||
getProfile()
|
||||
}, [user, getProfile])
|
||||
|
||||
async function updateProfile({
|
||||
username,
|
||||
website,
|
||||
avatar_url,
|
||||
}: {
|
||||
username: string | null
|
||||
fullname: string | null
|
||||
website: string | null
|
||||
avatar_url: string | null
|
||||
}) {
|
||||
try {
|
||||
setLoading(true)
|
||||
|
||||
const { error } = await supabase.from('profiles').upsert({
|
||||
id: user?.id as string,
|
||||
full_name: fullname,
|
||||
username,
|
||||
website,
|
||||
avatar_url,
|
||||
updated_at: new Date().toISOString(),
|
||||
})
|
||||
if (error) throw error
|
||||
alert('Profile updated!')
|
||||
} catch (error) {
|
||||
alert('Error updating the data!')
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="form-widget">
|
||||
<Avatar
|
||||
uid={user?.id ?? null}
|
||||
url={avatar_url}
|
||||
size={150}
|
||||
onUpload={(url) => {
|
||||
setAvatarUrl(url)
|
||||
updateProfile({ fullname, username, website, avatar_url: url })
|
||||
}}
|
||||
/>
|
||||
<div>
|
||||
<label htmlFor="email">Email</label>
|
||||
<input id="email" type="text" value={user?.email} disabled />
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="fullName">Full Name</label>
|
||||
<input
|
||||
id="fullName"
|
||||
type="text"
|
||||
value={fullname || ''}
|
||||
onChange={(e) => setFullname(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="username">Username</label>
|
||||
<input
|
||||
id="username"
|
||||
type="text"
|
||||
value={username || ''}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="website">Website</label>
|
||||
<input
|
||||
id="website"
|
||||
type="url"
|
||||
value={website || ''}
|
||||
onChange={(e) => setWebsite(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button
|
||||
className="button primary block"
|
||||
onClick={() => updateProfile({ fullname, username, website, avatar_url })}
|
||||
disabled={loading}
|
||||
>
|
||||
{loading ? 'Loading ...' : 'Update'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<form action="/auth/signout" method="post">
|
||||
<button className="button block" type="submit">
|
||||
Sign out
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
@@ -0,0 +1,97 @@
|
||||
'use client'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { createClient } from '@/utils/supabase/client'
|
||||
import Image from 'next/image'
|
||||
|
||||
export default function Avatar({
|
||||
uid,
|
||||
url,
|
||||
size,
|
||||
onUpload,
|
||||
}: {
|
||||
uid: string | null
|
||||
url: string | null
|
||||
size: number
|
||||
onUpload: (url: string) => void
|
||||
}) {
|
||||
const supabase = createClient()
|
||||
const [avatarUrl, setAvatarUrl] = useState<string | null>(url)
|
||||
const [uploading, setUploading] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
async function downloadImage(path: string) {
|
||||
try {
|
||||
const { data, error } = await supabase.storage.from('avatars').download(path)
|
||||
if (error) {
|
||||
throw error
|
||||
}
|
||||
|
||||
const url = URL.createObjectURL(data)
|
||||
setAvatarUrl(url)
|
||||
} catch (error) {
|
||||
console.log('Error downloading image: ', error)
|
||||
}
|
||||
}
|
||||
|
||||
if (url) downloadImage(url)
|
||||
}, [url, supabase])
|
||||
|
||||
const uploadAvatar: React.ChangeEventHandler<HTMLInputElement> = async (event) => {
|
||||
try {
|
||||
setUploading(true)
|
||||
|
||||
if (!event.target.files || event.target.files.length === 0) {
|
||||
throw new Error('You must select an image to upload.')
|
||||
}
|
||||
|
||||
const file = event.target.files[0]
|
||||
const fileExt = file.name.split('.').pop()
|
||||
const filePath = `${uid}-${Math.random()}.${fileExt}`
|
||||
|
||||
const { error: uploadError } = await supabase.storage.from('avatars').upload(filePath, file)
|
||||
|
||||
if (uploadError) {
|
||||
throw uploadError
|
||||
}
|
||||
|
||||
onUpload(filePath)
|
||||
} catch (error) {
|
||||
alert('Error uploading avatar!')
|
||||
} finally {
|
||||
setUploading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{avatarUrl ? (
|
||||
<Image
|
||||
width={size}
|
||||
height={size}
|
||||
src={avatarUrl}
|
||||
alt="Avatar"
|
||||
className="avatar image"
|
||||
style={{ height: size, width: size }}
|
||||
/>
|
||||
) : (
|
||||
<div className="avatar no-image" style={{ height: size, width: size }} />
|
||||
)}
|
||||
<div style={{ width: size }}>
|
||||
<label className="button primary block" htmlFor="single">
|
||||
{uploading ? 'Uploading ...' : 'Upload'}
|
||||
</label>
|
||||
<input
|
||||
style={{
|
||||
visibility: 'hidden',
|
||||
position: 'absolute',
|
||||
}}
|
||||
type="file"
|
||||
id="single"
|
||||
accept="image/*"
|
||||
onChange={uploadAvatar}
|
||||
disabled={uploading}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
import AccountForm from './account-form'
|
||||
import { createClient } from '@/utils/supabase/server'
|
||||
|
||||
export default async function Account() {
|
||||
const supabase = createClient()
|
||||
|
||||
const {
|
||||
data: { user },
|
||||
} = await supabase.auth.getUser()
|
||||
|
||||
return <AccountForm user={user} />
|
||||
}
|
Reference in New Issue
Block a user