init commit,
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { supabase } from '../lib/supabase'
|
||||
import { StyleSheet, View, Alert } from 'react-native'
|
||||
import { Button, Input } from '@rneui/themed'
|
||||
import { Session } from '@supabase/supabase-js'
|
||||
import Push from './Push'
|
||||
|
||||
export default function Account({ session }: { session: Session }) {
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [username, setUsername] = useState('')
|
||||
const [website, setWebsite] = useState('')
|
||||
const [avatarUrl, setAvatarUrl] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
if (session) getProfile()
|
||||
}, [session])
|
||||
|
||||
async function getProfile() {
|
||||
try {
|
||||
setLoading(true)
|
||||
if (!session?.user) throw new Error('No user on the session!')
|
||||
|
||||
const { data, error, status } = await supabase
|
||||
.from('profiles')
|
||||
.select(`username, website, avatar_url`)
|
||||
.eq('id', session?.user.id)
|
||||
.single()
|
||||
if (error && status !== 406) {
|
||||
throw error
|
||||
}
|
||||
|
||||
if (data) {
|
||||
setUsername(data.username)
|
||||
setWebsite(data.website)
|
||||
setAvatarUrl(data.avatar_url)
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
Alert.alert(error.message)
|
||||
}
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
async function updateProfile({
|
||||
username,
|
||||
website,
|
||||
avatar_url,
|
||||
}: {
|
||||
username: string
|
||||
website: string
|
||||
avatar_url: string
|
||||
}) {
|
||||
try {
|
||||
setLoading(true)
|
||||
if (!session?.user) throw new Error('No user on the session!')
|
||||
|
||||
const updates = {
|
||||
id: session?.user.id,
|
||||
username,
|
||||
website,
|
||||
avatar_url,
|
||||
updated_at: new Date(),
|
||||
}
|
||||
|
||||
const { error } = await supabase.from('profiles').upsert(updates)
|
||||
|
||||
if (error) {
|
||||
throw error
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
Alert.alert(error.message)
|
||||
}
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={[styles.verticallySpaced, styles.mt20]}>
|
||||
<Input label="Email" value={session?.user?.email} disabled />
|
||||
</View>
|
||||
<View style={styles.verticallySpaced}>
|
||||
<Input label="Username" value={username || ''} onChangeText={(text) => setUsername(text)} />
|
||||
</View>
|
||||
<View style={styles.verticallySpaced}>
|
||||
<Input label="Website" value={website || ''} onChangeText={(text) => setWebsite(text)} />
|
||||
</View>
|
||||
|
||||
<View style={[styles.verticallySpaced, styles.mt20]}>
|
||||
<Button
|
||||
title={loading ? 'Loading ...' : 'Update'}
|
||||
onPress={() => updateProfile({ username, website, avatar_url: avatarUrl })}
|
||||
disabled={loading}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View style={styles.verticallySpaced}>
|
||||
<Button title="Sign Out" onPress={() => supabase.auth.signOut()} />
|
||||
</View>
|
||||
|
||||
<View style={[styles.verticallySpaced, { height: 200 }]}>
|
||||
<Push session={session} />
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
marginTop: 40,
|
||||
padding: 12,
|
||||
},
|
||||
verticallySpaced: {
|
||||
paddingTop: 4,
|
||||
paddingBottom: 4,
|
||||
alignSelf: 'stretch',
|
||||
},
|
||||
mt20: {
|
||||
marginTop: 20,
|
||||
},
|
||||
})
|
@@ -0,0 +1,83 @@
|
||||
import React, { useState } from 'react'
|
||||
import { Alert, StyleSheet, View } from 'react-native'
|
||||
import { supabase } from '../lib/supabase'
|
||||
import { Button, Input } from '@rneui/themed'
|
||||
|
||||
export default function Auth() {
|
||||
const [email, setEmail] = useState('')
|
||||
const [password, setPassword] = useState('')
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
async function signInWithEmail() {
|
||||
setLoading(true)
|
||||
const { error } = await supabase.auth.signInWithPassword({
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
|
||||
if (error) Alert.alert(error.message)
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
async function signUpWithEmail() {
|
||||
setLoading(true)
|
||||
const {
|
||||
data: { session },
|
||||
error,
|
||||
} = await supabase.auth.signUp({
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
|
||||
if (error) Alert.alert(error.message)
|
||||
if (!session) Alert.alert('Please check your inbox for email verification!')
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={[styles.verticallySpaced, styles.mt20]}>
|
||||
<Input
|
||||
label="Email"
|
||||
leftIcon={{ type: 'font-awesome', name: 'envelope' }}
|
||||
onChangeText={(text) => setEmail(text)}
|
||||
value={email}
|
||||
placeholder="email@address.com"
|
||||
autoCapitalize={'none'}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.verticallySpaced}>
|
||||
<Input
|
||||
label="Password"
|
||||
leftIcon={{ type: 'font-awesome', name: 'lock' }}
|
||||
onChangeText={(text) => setPassword(text)}
|
||||
value={password}
|
||||
secureTextEntry={true}
|
||||
placeholder="Password"
|
||||
autoCapitalize={'none'}
|
||||
/>
|
||||
</View>
|
||||
<View style={[styles.verticallySpaced, styles.mt20]}>
|
||||
<Button title="Sign in" disabled={loading} onPress={() => signInWithEmail()} />
|
||||
</View>
|
||||
<View style={styles.verticallySpaced}>
|
||||
<Button title="Sign up" disabled={loading} onPress={() => signUpWithEmail()} />
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
marginTop: 40,
|
||||
padding: 12,
|
||||
},
|
||||
verticallySpaced: {
|
||||
paddingTop: 4,
|
||||
paddingBottom: 4,
|
||||
alignSelf: 'stretch',
|
||||
},
|
||||
mt20: {
|
||||
marginTop: 20,
|
||||
},
|
||||
})
|
@@ -0,0 +1,131 @@
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { Text, View, Button, Platform } from "react-native";
|
||||
import * as Device from "expo-device";
|
||||
import * as Notifications from "expo-notifications";
|
||||
import Constants from "expo-constants";
|
||||
import { Session } from "@supabase/supabase-js";
|
||||
import { supabase } from "../lib/supabase";
|
||||
|
||||
Notifications.setNotificationHandler({
|
||||
handleNotification: async () => ({
|
||||
shouldShowAlert: true,
|
||||
shouldPlaySound: false,
|
||||
shouldSetBadge: false,
|
||||
}),
|
||||
});
|
||||
|
||||
// Can use this function below or use Expo's Push Notification Tool from: https://expo.dev/notifications
|
||||
async function sendPushNotification(expoPushToken: string) {
|
||||
const message = {
|
||||
to: expoPushToken,
|
||||
sound: "default",
|
||||
title: "Original Title",
|
||||
body: "And here is the body!",
|
||||
data: { someData: "goes here" },
|
||||
};
|
||||
|
||||
await fetch("https://exp.host/--/api/v2/push/send", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
"Accept-encoding": "gzip, deflate",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(message),
|
||||
});
|
||||
}
|
||||
|
||||
async function registerForPushNotificationsAsync() {
|
||||
let token;
|
||||
|
||||
if (Platform.OS === "android") {
|
||||
Notifications.setNotificationChannelAsync("default", {
|
||||
name: "default",
|
||||
importance: Notifications.AndroidImportance.MAX,
|
||||
vibrationPattern: [0, 250, 250, 250],
|
||||
lightColor: "#FF231F7C",
|
||||
});
|
||||
}
|
||||
|
||||
if (Device.isDevice) {
|
||||
const { status: existingStatus } =
|
||||
await Notifications.getPermissionsAsync();
|
||||
let finalStatus = existingStatus;
|
||||
if (existingStatus !== "granted") {
|
||||
const { status } = await Notifications.requestPermissionsAsync();
|
||||
finalStatus = status;
|
||||
}
|
||||
if (finalStatus !== "granted") {
|
||||
alert("Failed to get push token for push notification!");
|
||||
return "";
|
||||
}
|
||||
token = await Notifications.getExpoPushTokenAsync({
|
||||
projectId: Constants?.expoConfig?.extra?.eas.projectId,
|
||||
});
|
||||
console.log(token);
|
||||
} else {
|
||||
alert("Must use physical device for Push Notifications");
|
||||
}
|
||||
|
||||
return token?.data ?? "";
|
||||
}
|
||||
|
||||
export default function Push({ session }: { session: Session }) {
|
||||
const [expoPushToken, setExpoPushToken] = useState("");
|
||||
const [notification, setNotification] =
|
||||
useState<Notifications.Notification>();
|
||||
const notificationListener = useRef<Notifications.Subscription>();
|
||||
const responseListener = useRef<Notifications.Subscription>();
|
||||
|
||||
useEffect(() => {
|
||||
registerForPushNotificationsAsync().then(async (token) => {
|
||||
setExpoPushToken(token);
|
||||
|
||||
const { error } = await supabase
|
||||
.from("profiles")
|
||||
.upsert({ id: session?.user.id, expo_push_token: token });
|
||||
console.log(error);
|
||||
});
|
||||
|
||||
notificationListener.current =
|
||||
Notifications.addNotificationReceivedListener((notification) => {
|
||||
setNotification(notification);
|
||||
});
|
||||
|
||||
responseListener.current =
|
||||
Notifications.addNotificationResponseReceivedListener((response) => {
|
||||
console.log(response);
|
||||
});
|
||||
|
||||
return () => {
|
||||
Notifications.removeNotificationSubscription(
|
||||
notificationListener.current!
|
||||
);
|
||||
Notifications.removeNotificationSubscription(responseListener.current!);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{ flex: 1, alignItems: "center", justifyContent: "space-around" }}
|
||||
>
|
||||
<Text>Your expo push token: {expoPushToken}</Text>
|
||||
<View style={{ alignItems: "center", justifyContent: "center" }}>
|
||||
<Text>
|
||||
Title: {notification && notification.request.content.title}{" "}
|
||||
</Text>
|
||||
<Text>Body: {notification && notification.request.content.body}</Text>
|
||||
<Text>
|
||||
Data:{" "}
|
||||
{notification && JSON.stringify(notification.request.content.data)}
|
||||
</Text>
|
||||
</View>
|
||||
<Button
|
||||
title="Press to Send Notification"
|
||||
onPress={async () => {
|
||||
await sendPushNotification(expoPushToken);
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
Reference in New Issue
Block a user