Created
January 5, 2024 19:27
-
-
Save simon-marcus/76c18127f03846050429d56950a3f113 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use client'; | |
import { Card, Form, Input, Button, message, ConfigProvider, Upload } from 'antd'; | |
import { useRouter } from 'next/navigation'; | |
import React, { useState } from 'react'; | |
import { sanitize } from "isomorphic-dompurify";import { useSupabase } from '../supabase-provider'; | |
import mytheme from '../theme/themeConfig'; | |
import { User } from '@supabase/supabase-js'; | |
import { useImageUploader } from '@/hooks/useImageUploader'; | |
import ImgCrop from 'antd-img-crop'; | |
import { LoadingOutlined, PlusOutlined } from '@ant-design/icons'; | |
export default function ProfileTab({ user }: { user: User }) { | |
const { supabase, session } = useSupabase(); | |
const userId = session?.user.id || {}; | |
const [saving, setSaving] = useState(false); | |
const [canSave, setCanSave ] = useState(true); | |
const [form] = Form.useForm(); | |
const [loading, setLoading] = useState(false); | |
const [newAvatarUrl, setNewAvatarUrl] = useState<string>(); | |
const router = useRouter(); | |
const [messageApi, contextHolder] = message.useMessage(); | |
const key = 'updatable'; | |
const { handleUploadImage, createBase64Url, beforeUpload } = useImageUploader(userId as string); | |
const avatarUrl = session?.user.user_metadata.avatar_url; | |
const uploadButton = ( | |
<div style={{ borderRadius: '100%' }}> | |
{loading ? <LoadingOutlined /> : <PlusOutlined />} | |
<div>Upload</div> | |
</div> | |
); | |
const openSaveMessage = () => { | |
messageApi.open({ | |
key, | |
type: 'loading', | |
content: 'Saving your changes...', | |
}); | |
}; | |
const onFinish = async (values: any) => { | |
openSaveMessage(); | |
setSaving(true); | |
if (session && session.user) { | |
if (!avatarUrl && !newAvatarUrl ){ | |
return message.error('Please upload an icon image.'); | |
} | |
let avatar_url: string | undefined; | |
const getImage = new Promise<string | undefined>(async (resolve, reject) => { | |
if (newAvatarUrl) { | |
avatar_url = await handleUploadImage(newAvatarUrl as string, user.id, 'avatar_url', 500, 500); | |
if (avatar_url == undefined) { | |
message.error('Error uploading image'); | |
reject(undefined); | |
} | |
else { | |
resolve(avatar_url); | |
} | |
} else { | |
resolve(undefined);} | |
}); | |
const updatedUser = await supabase.auth.updateUser({ | |
data: { | |
full_name: sanitize(values.full_name).trim(), | |
website: sanitize(values.website).trim(), | |
avatar_url: newAvatarUrl ? await getImage : values.avatar_url, | |
payment_link: sanitize(values.payment_link).trim(), | |
}, | |
}); | |
if (updatedUser.error) { | |
return message.error('Error updating profile. Please try again.'); | |
} | |
message.success('Profile updated successfully'); | |
const { data: { session }, error } = await supabase.auth.refreshSession(); // Refresh session to ensure user metadata is up to date with current user | |
router.push('/myapps'); | |
} | |
}; | |
return (user ? ( | |
<ConfigProvider theme={mytheme}> | |
{contextHolder} | |
<Form form={form} onFinish={onFinish} layout='vertical' | |
initialValues={{ | |
email: user.email, | |
full_name: user.user_metadata.full_name, | |
avatar_url: user.user_metadata.avatar_url, | |
website: user.user_metadata.website, | |
payment_link: user.user_metadata.payment_link, | |
}} | |
> | |
<Card title="Profile" > | |
<Form.Item label="Email" name="email"> | |
<Input | |
disabled | |
type="email" | |
name="email" | |
placeholder="Your email address" | |
/> | |
</Form.Item> | |
<Form.Item label="Name" | |
name="full_name" | |
id="full_name" | |
required={true} | |
hasFeedback | |
tooltip="Your full name, or the name you want to be credited for the app" | |
rules={[ | |
{ required: true, message: 'Please complete this field.' }, | |
{ | |
type: 'string', | |
min: 3, | |
message: 'Please use at least 3 characters.', | |
}, | |
{ | |
type: 'string', | |
max: 70, | |
message: 'Please use at most 70 characters.', | |
}, | |
{ | |
pattern: /^[a-zA-Z\s'-.]*$/, | |
message: 'Please use only letters, hyphens, apostrophes and periods in your name.', | |
} | |
]}> | |
<Input | |
type="text" | |
name="full_name" | |
id="full_name" | |
placeholder="Your preferred name" | |
/> | |
</Form.Item> | |
<Form.Item label="Website" | |
name="website" | |
id="website" | |
tooltip="Your personal or business website or portfolio" | |
rules={[ | |
{ | |
type: 'url', | |
message: 'Please enter a valid URL.', | |
} | |
]}> | |
<Input | |
type="url" | |
name="website" | |
id="website" | |
placeholder="https://yourwebsite.com" | |
/> | |
</Form.Item> | |
<Form.Item | |
required | |
label="Icon" | |
name="avatar_url" | |
valuePropName="fileList" | |
getValueFromEvent={(e) => { | |
if (Array.isArray(e)) { | |
return e; | |
} | |
return e && e.fileList; | |
}} | |
> | |
<ImgCrop | |
quality={0.8} | |
rotationSlider | |
showReset | |
cropShape="round" | |
> | |
<Upload name="avatar_url" listType="picture-circle" multiple={false} maxCount={1} showUploadList={false} style={{ borderRadius: '100%' }} | |
beforeUpload={beforeUpload} | |
customRequest={async ({ file }) => { | |
try { | |
const imageUrl = await createBase64Url([file as File]) | |
if (imageUrl) { | |
setNewAvatarUrl(imageUrl); | |
} else { | |
return new Error('Error uploading image. Please try again.'); | |
} | |
} catch (error) { | |
console.error(error); | |
return new Error('Error uploading image. Please try again.'); | |
} | |
}} | |
> | |
{newAvatarUrl ? <img src={newAvatarUrl} alt="avatar" style={{ width: '150px', borderRadius: '100%' }} /> : avatarUrl ? <img src={avatarUrl} alt="avatar" style={{ width: '150px', borderRadius: '100%' }} /> : uploadButton} | |
</Upload> | |
</ImgCrop> | |
</Form.Item> | |
<Form.Item | |
label="Payment Link" | |
name="payment_link" | |
tooltip="Link to PayPal, Venmo, CashApp, or other payment service. Will appear on your app page for users to send you tips." | |
rules={[ | |
{ | |
type: 'url', | |
message: 'Please enter a valid URL.', | |
} | |
]}> | |
<Input | |
type="url" | |
name="payment_link" | |
id="payment_link" | |
placeholder="https://paypal.me/yourname" | |
/> | |
</Form.Item> | |
</Card > | |
<div className="flex justify-between items-center my-3"> | |
<Button size='large' type='text' onClick={() => router.push('/myapps')}> | |
Cancel | |
</Button> | |
<Button size='large' type='primary' disabled={!canSave} htmlType="submit"> | |
Save | |
</Button> | |
</div> | |
</Form> | |
</ConfigProvider > | |
) : null); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment