Skip to content

Commit 201bb56

Browse files
committed
Adds some realtime, removes some styles
1 parent 6580526 commit 201bb56

File tree

9 files changed

+171
-156
lines changed

9 files changed

+171
-156
lines changed

example/next-storage/README.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@
77
## Database schema
88

99
```sql
10-
create table public.profiles (
10+
create table profiles (
1111
id uuid references auth.users not null,
12+
updated_at timestamp with time zone,
1213
username text unique,
1314
avatar_url text,
14-
dob date,
15+
website text,
1516

1617
primary key (id),
1718
unique(username),
@@ -21,4 +22,15 @@ alter table profiles enable row level security;
2122
create policy "Public profiles are viewable by everyone." on profiles for select using (true);
2223
create policy "Users can insert their own profile." on profiles for insert with check (auth.uid() = id);
2324
create policy "Users can update own profile." on profiles for update using (auth.uid() = id);
25+
26+
27+
-- Set up Realtime!
28+
begin;
29+
drop publication if exists supabase_realtime;
30+
create publication supabase_realtime;
31+
commit;
32+
33+
alter publication supabase_realtime add table profiles;
34+
35+
2436
```

example/next-storage/components/Account.tsx

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,16 @@
11
import { useState, useEffect, ChangeEvent } from 'react'
22
import { supabase } from '../lib/api'
33
import UploadButton from '../components/UploadButton'
4-
import Avatar from '../components/Avatar'
4+
import Avatar from './Avatar'
55
import styles from '../styles/Home.module.css'
66
import { AuthSession } from '../../../dist/main'
7-
import { DEFAULT_AVATARS_BUCKET } from '../lib/constants'
8-
9-
export type Profile = {
10-
id: string
11-
avatar_url: string
12-
username: string
13-
dob: string
14-
}
7+
import { DEFAULT_AVATARS_BUCKET, Profile } from '../lib/constants'
158

169
export default function Account({ session }: { session: AuthSession }) {
10+
const [loading, setLoading] = useState<boolean>(true)
1711
const [avatar, setAvatar] = useState<string | null>(null)
1812
const [username, setUsername] = useState<string | null>(null)
19-
const [dob, setDob] = useState<string | null>(null)
13+
const [website, setWebsite] = useState<string | null>(null)
2014

2115
useEffect(() => {
2216
getProfile()
@@ -48,15 +42,15 @@ export default function Account({ session }: { session: AuthSession }) {
4842

4943
let { error: updateError } = await supabase.from('profiles').upsert({
5044
id: user.id,
51-
avatar_url: fileName,
45+
avatar_url: filePath,
5246
})
5347

5448
if (updateError) {
5549
throw updateError
5650
}
5751

5852
setAvatar(null)
59-
setAvatar(fileName)
53+
setAvatar(filePath)
6054
} catch (error) {
6155
alert(error.message)
6256
}
@@ -65,16 +59,17 @@ export default function Account({ session }: { session: AuthSession }) {
6559
function setProfile(profile: Profile) {
6660
setAvatar(profile.avatar_url)
6761
setUsername(profile.username)
68-
setDob(profile.dob)
62+
setWebsite(profile.website)
6963
}
7064

7165
async function getProfile() {
7266
try {
67+
setLoading(true)
7368
const user = supabase.auth.user()
7469

7570
let { data, error } = await supabase
7671
.from('profiles')
77-
.select(`username, dob, avatar_url`)
72+
.select(`username, website, avatar_url`)
7873
.eq('id', user.id)
7974
.single()
8075

@@ -85,17 +80,21 @@ export default function Account({ session }: { session: AuthSession }) {
8580
setProfile(data)
8681
} catch (error) {
8782
console.log('error', error.message)
83+
} finally {
84+
setLoading(false)
8885
}
8986
}
9087

9188
async function updateProfile() {
9289
try {
90+
setLoading(true)
9391
const user = supabase.auth.user()
9492

9593
const updates = {
9694
id: user.id,
9795
username,
98-
dob,
96+
website,
97+
updated_at: new Date(),
9998
}
10099

101100
let { error } = await supabase.from('profiles').upsert(updates, {
@@ -107,6 +106,8 @@ export default function Account({ session }: { session: AuthSession }) {
107106
}
108107
} catch (error) {
109108
alert(error.message)
109+
} finally {
110+
setLoading(false)
110111
}
111112
}
112113

@@ -123,7 +124,7 @@ export default function Account({ session }: { session: AuthSession }) {
123124
>
124125
<div className={styles.card}>
125126
<div className={styles.avatarContainer}>
126-
<Avatar avatar={avatar} />
127+
<Avatar url={avatar} size={270} />
127128
</div>
128129
<UploadButton onUpload={uploadAvatar} />
129130
</div>
@@ -142,13 +143,18 @@ export default function Account({ session }: { session: AuthSession }) {
142143
/>
143144
</div>
144145
<div>
145-
<label htmlFor="dob">Date of birth</label>
146-
<input id="dob" type="date" value={dob || ''} onChange={(e) => setDob(e.target.value)} />
146+
<label htmlFor="website">Website</label>
147+
<input
148+
id="website"
149+
type="website"
150+
value={website || ''}
151+
onChange={(e) => setWebsite(e.target.value)}
152+
/>
147153
</div>
148154

149155
<div>
150-
<button className="button block primary" onClick={() => updateProfile()}>
151-
Update profile
156+
<button className="button block primary" onClick={() => updateProfile()} disabled={loading}>
157+
{loading ? 'Loading ...' : 'Update'}
152158
</button>
153159
</div>
154160

example/next-storage/components/Auth.tsx

Lines changed: 35 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,53 @@
11
import { useState } from 'react'
22
import { supabase } from '../lib/api'
3-
import styles from '../styles/Auth.module.css'
3+
// import styles from '../styles/Auth.module.css'
44

55
export default function Auth({}) {
6+
const [loading, setLoading] = useState(false)
67
const [email, setEmail] = useState('')
7-
const [password, setPassword] = useState('')
88

9-
const handleLogin = async (type: 'LOGIN' | 'SIGNUP', email: string, password: string) => {
9+
const handleLogin = async (email: string) => {
1010
try {
11-
const { error, user } =
12-
type === 'LOGIN'
13-
? await supabase.auth.signIn({ email, password })
14-
: await supabase.auth.signUp({ email, password })
15-
if ((!error && !user) || (user && !user.confirmed_at))
16-
alert('Check your email for the login link!')
17-
if (error) alert(error.message)
18-
} catch (error) {
19-
console.log('Error thrown:', error.message)
20-
alert(error.error_description || error)
21-
}
22-
}
11+
setLoading(true)
12+
const { error, user } = await supabase.auth.signIn({ email })
2313

24-
async function forgotPassword(e: React.MouseEvent<HTMLElement>) {
25-
e.preventDefault()
26-
var email = prompt('Please enter your email:')
27-
if (email === null || email === '') {
28-
window.alert('You must enter your email.')
29-
} else {
30-
let { error } = await supabase.auth.api.resetPasswordForEmail(email)
3114
if (error) {
32-
console.log('Error: ', error.message)
33-
} else {
34-
alert('Password recovery email has been sent.')
15+
throw error
3516
}
17+
18+
console.log('user', user)
19+
alert('Check your email for the login link!')
20+
} catch (error) {
21+
console.log('Error thrown:', error.message)
22+
alert(error.error_description || error.message)
23+
} finally {
24+
setLoading(false)
3625
}
3726
}
3827

3928
return (
40-
<div className={styles.container}>
41-
<div className={styles.containerInner}>
42-
<div className={styles.inputContainer}>
43-
<label>Email</label>
44-
<input
45-
type="email"
46-
placeholder="Your email"
47-
value={email}
48-
onChange={(e) => setEmail(e.target.value)}
49-
/>
50-
</div>
51-
<div className={styles.inputContainer}>
52-
<label>Password</label>
53-
<input
54-
type="password"
55-
placeholder="Your password"
56-
value={password}
57-
onChange={(e) => setPassword(e.target.value)}
58-
/>
59-
</div>
60-
61-
<div className={styles.buttonContainer}>
62-
<button
63-
onClick={(e) => {
64-
e.preventDefault()
65-
handleLogin('SIGNUP', email, password)
66-
}}
67-
className={'button'}
68-
>
69-
Sign up
70-
</button>
71-
<button
72-
onClick={(e) => {
73-
e.preventDefault()
74-
handleLogin('LOGIN', email, password)
75-
}}
76-
className={'button'}
77-
>
78-
{password.length ? 'Sign in' : 'Send magic link'}
79-
</button>
29+
<div style={{ display: 'flex', gap: 20, flexDirection: 'column' }}>
30+
<div>
31+
<label>Email</label>
32+
<input
33+
type="email"
34+
placeholder="Your email"
35+
value={email}
36+
onChange={(e) => setEmail(e.target.value)}
37+
/>
38+
</div>
8039

81-
<a onClick={forgotPassword} className={'button'}>
82-
Forgot your password?
83-
</a>
84-
</div>
40+
<div>
41+
<button
42+
onClick={(e) => {
43+
e.preventDefault()
44+
handleLogin(email)
45+
}}
46+
className={'button block'}
47+
disabled={loading}
48+
>
49+
{loading ? 'Loading ..' : 'Send magic link'}
50+
</button>
8551
</div>
8652
</div>
8753
)
Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,15 @@
11
import { useEffect, useState } from 'react'
22
import { supabase } from '../lib/api'
3-
import { DEFAULT_AVATARS_BUCKET } from '../lib/constants'
4-
import styles from '../styles/Avatar.module.css'
53

6-
export type UploadButtonProps = {
7-
avatar?: string | null
8-
}
9-
10-
export default function Avatar(props: UploadButtonProps) {
4+
export default function Avatar({ url, size }: { url: string | null; size: number }) {
115
const [avatarUrl, setAvatarUrl] = useState<string | null>(null)
12-
const { avatar } = props
136

147
useEffect(() => {
15-
if (avatar) {
16-
console.log('avatar', avatar)
17-
downloadImage(`${DEFAULT_AVATARS_BUCKET}/${avatar}`)
8+
if (url) {
9+
console.log('url', url)
10+
downloadImage(url)
1811
}
19-
}, [avatar])
12+
}, [url])
2013

2114
async function downloadImage(path: string) {
2215
try {
@@ -32,8 +25,8 @@ export default function Avatar(props: UploadButtonProps) {
3225
}
3326

3427
return avatarUrl ? (
35-
<img src={avatarUrl} className={styles.image} />
28+
<img src={avatarUrl} className="avatar image" style={{ height: size, width: size }} />
3629
) : (
37-
<div className={styles.noImage} />
30+
<div className="avatar no-image" style={{ height: size, width: size }} />
3831
)
3932
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Profile } from '../lib/constants'
2+
import Avatar from './Avatar'
3+
4+
export default function ProfileCard({ profile }: { profile: Profile }) {
5+
return (
6+
<div className="card">
7+
<Avatar url={profile.avatar_url} size={50} />
8+
<p>{profile.id}</p>
9+
<p>{profile.username}</p>
10+
</div>
11+
)
12+
}

example/next-storage/lib/constants.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,10 @@ export const NEXT_PUBLIC_SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL
22
export const NEXT_PUBLIC_SUPABASE_KEY = process.env.NEXT_PUBLIC_SUPABASE_KEY
33

44
export const DEFAULT_AVATARS_BUCKET = 'avatars'
5+
6+
export type Profile = {
7+
id: string
8+
avatar_url: string
9+
username: string
10+
website: string
11+
}

0 commit comments

Comments
 (0)