Add login info + temporary work

This commit is contained in:
Arthur Belleville 2025-06-10 21:05:22 +02:00
parent ed12c934b5
commit 28816a0e86
No known key found for this signature in database
12 changed files with 135 additions and 73 deletions

View file

@ -0,0 +1,5 @@
ALTER TABLE profiles
DROP CONSTRAINT IF EXISTS profiles_username_key;
-- ALTER TABLE profiles
-- ADD CONSTRAINT profiles_username_key UNIQUE (username);

View file

@ -0,0 +1,4 @@
ALTER TABLE profiles
ALTER COLUMN full_name SET DEFAULT NULL,
ALTER COLUMN avatar_url SET DEFAULT NULL,
ALTER COLUMN updated_at SET DEFAULT CURRENT_TIMESTAMP;

2
sql/03_add_email.sql Normal file
View file

@ -0,0 +1,2 @@
ALTER TABLE profiles
ADD COLUMN email varchar;

14
sql/04_add_trigger.sql Normal file
View file

@ -0,0 +1,14 @@
CREATE FUNCTION
public.create_profile_for_new_user()
RETURNS TRIGGER AS
$$
BEGIN
INSERT INTO public.profiles (id, full_name, email)
VALUES (
NEW.id,
NEW.raw_user_meta_data ->> 'user_name',
NEW.email
);
RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

View file

@ -1,6 +1,6 @@
import React, { useState } from "react";
import { StyleSheet, View, Text } from "react-native";
import { Button, Input } from "@rneui/themed";
import { Button, Input } from "@rn-vui/themed";
import { useAuth } from "@/stores/auth";
import { Link } from "expo-router";

View file

@ -1,6 +1,6 @@
import React, { useState } from "react";
import { StyleSheet, View, Text } from "react-native";
import { Button, Input } from "@rneui/themed";
import { Button, Input } from "@rn-vui/themed";
import { useAuth } from "@/stores/auth";
import { Link } from "expo-router";

View file

@ -1,34 +1,18 @@
import { View, StyleSheet } from "react-native";
import { useAuth } from "@/stores/auth";
import { Avatar, Button, Input } from "@rneui/themed";
import { Card, ListItem } from "@rneui/themed";
import { Avatar, Button, Input } from "@rn-vui/themed";
import { Card, ListItem } from "@rn-vui/themed";
import { useState } from "react";
import { useGetUser } from "@/hooks/user";
import { useUser } from "@/providers/UserProvider";
export default function ProfileScreen() {
const signOut = useAuth((state) => state.signOut);
const { user } = useGetUser();
const user = useUser();
const [displayName, setDisplayName] = useState(user?.full_name || "");
const [isEditing, setIsEditing] = useState(false);
const handleSignOut = async () => {
await signOut();
};
// const handleSaveDisplayName = async () => {
// if (setUser && user) {
// await setUser({
// avatar_url: user.avatar_url || null,
// full_name: displayName || null,
// id: user.id,
// updated_at: user.updated_at || null,
// username: user.username || null,
// website: user.website || null,
// });
// setIsEditing(false);
// }
// };
console.log({ user });
return (
<View style={styles.container}>
@ -40,7 +24,7 @@ export default function ProfileScreen() {
containerStyle={styles.avatar}
/>
<Card.Title style={styles.cardTitle}>{user?.email}</Card.Title>
<Card.Title style={styles.cardTitle}>{user?.full_name}</Card.Title>
<Card.Divider />
<ListItem key="email" bottomDivider containerStyle={styles.listItem}>
@ -72,7 +56,7 @@ export default function ProfileScreen() {
/>
) : (
<ListItem.Subtitle style={styles.listItemSubtitle}>
{user?.displayName || "Not Set"}
{user?.full_name || "Not Set"}
</ListItem.Subtitle>
)}
</ListItem.Content>
@ -87,7 +71,7 @@ export default function ProfileScreen() {
if (isEditing) {
// handleSaveDisplayName();
} else {
setDisplayName(user?.displayName || "");
setDisplayName(user?.full_name || "");
setIsEditing(true);
}
}}
@ -96,7 +80,7 @@ export default function ProfileScreen() {
<Button
title="Sign Out"
onPress={handleSignOut}
onPress={signOut}
buttonStyle={styles.signOutButton}
titleStyle={styles.signOutText}
icon={{

View file

@ -1,18 +1,16 @@
import ChatProvider from "@/providers/ChatProvider";
import { Redirect, Stack } from "expo-router";
import { useAuth } from "@/stores/auth";
import { UserStoreProvider } from "@/providers/UserProvider";
import { Stack } from "expo-router";
export default function HomeLayout() {
const { session } = useAuth();
if (!session) {
return <Redirect href="/(auth)/login" />;
}
return (
<ChatProvider>
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="channel" options={{ headerShown: false }} />
</Stack>
</ChatProvider>
<UserStoreProvider>
<ChatProvider>
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="channel" options={{ headerShown: false }} />
</Stack>
</ChatProvider>
</UserStoreProvider>
);
}

View file

@ -66,26 +66,26 @@ export type Database = {
profiles: {
Row: {
avatar_url: string | null
email: string | null
full_name: string | null
id: string
updated_at: string | null
username: string | null
website: string | null
}
Insert: {
avatar_url?: string | null
email?: string | null
full_name?: string | null
id: string
updated_at?: string | null
username?: string | null
website?: string | null
}
Update: {
avatar_url?: string | null
email?: string | null
full_name?: string | null
id?: string
updated_at?: string | null
username?: string | null
website?: string | null
}
Relationships: []

View file

@ -12,8 +12,8 @@
"@react-native-async-storage/async-storage": "2.1.2",
"@react-native-community/netinfo": "11.4.1",
"@react-navigation/bottom-tabs": "^7.2.0",
"@rneui/base": "^4.0.0-rc.7",
"@rneui/themed": "^4.0.0-rc.7",
"@rn-vui/base": "^5.1.3",
"@rn-vui/themed": "^5.1.3",
"@supabase/supabase-js": "^2.49.4",
"@tanstack/react-query": "^5.75.2",
"expo": "^53.0.0",
@ -3132,17 +3132,17 @@
"nanoid": "3.3.8"
}
},
"node_modules/@rneui/base": {
"version": "4.0.0-rc.7",
"resolved": "https://registry.npmjs.org/@rneui/base/-/base-4.0.0-rc.7.tgz",
"integrity": "sha512-dffzoYek3Qp+7wJzC42QjI/Fu1HOUNxFIR88t1laDrBV5QZQB55f+Vu5zLbC80/bh1b8fYtl63HTIWpORuA3Eg==",
"node_modules/@rn-vui/base": {
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/@rn-vui/base/-/base-5.1.3.tgz",
"integrity": "sha512-Or8Cb/jXsTr7HSOJISuabhvk7DXGwByaFhmsfS9SF6YabwjERjabRRn5NjTY4IDt7/QiN/hRBpuZov4kkfR1aw==",
"license": "MIT",
"dependencies": {
"@rn-vui/ratings": "^0.4.3",
"@types/react-native-vector-icons": "^6.4.10",
"color": "^3.2.1",
"deepmerge": "^4.2.2",
"hoist-non-react-statics": "^3.3.2",
"react-native-ratings": "^8.1.0",
"react-native-size-matters": "^0.4.0"
},
"funding": {
@ -3150,11 +3150,11 @@
"url": "https://github.com/sponsors/react-native-elements"
},
"peerDependencies": {
"react-native-safe-area-context": "^3.1.9 || ^4.0.0",
"react-native-safe-area-context": ">= 3.0.0",
"react-native-vector-icons": ">7.0.0"
}
},
"node_modules/@rneui/base/node_modules/color": {
"node_modules/@rn-vui/base/node_modules/color": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
"integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
@ -3164,7 +3164,7 @@
"color-string": "^1.6.0"
}
},
"node_modules/@rneui/base/node_modules/color-convert": {
"node_modules/@rn-vui/base/node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
@ -3173,23 +3173,36 @@
"color-name": "1.1.3"
}
},
"node_modules/@rneui/base/node_modules/color-name": {
"node_modules/@rn-vui/base/node_modules/color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
"license": "MIT"
},
"node_modules/@rneui/themed": {
"version": "4.0.0-rc.8",
"resolved": "https://registry.npmjs.org/@rneui/themed/-/themed-4.0.0-rc.8.tgz",
"integrity": "sha512-8L/XOrL9OK/r+/iBLvx63TbIdZOXF8SIjN9eArMYm6kRbMr8m4BitXllDN8nBhBsSPNYvL6EAgjk+i2MfY4sBA==",
"node_modules/@rn-vui/ratings": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/@rn-vui/ratings/-/ratings-0.4.3.tgz",
"integrity": "sha512-VshSJvGgDCrc8eVx6TeIfICkhxZkK4VpELIJ7301aAiXLker5hxu6U4uoODxIbdY2RRQRVrHWkNlosfHYuh/1w==",
"license": "MIT",
"workspaces": [
"example"
],
"peerDependencies": {
"react": "*",
"react-native": "*"
}
},
"node_modules/@rn-vui/themed": {
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/@rn-vui/themed/-/themed-5.1.3.tgz",
"integrity": "sha512-wbGX/H62pWIXNwsuPE2FI4ArK+5AP7rQQbgH15NLxydl646y7VsDAcOA9s6UCqhi7rYuJ/oZam0KkJ2oB6fJQA==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/react-native-elements"
},
"peerDependencies": {
"@rneui/base": "4.0.0-rc.7"
"@rn-vui/base": "5.1.3"
}
},
"node_modules/@sinclair/typebox": {
@ -10125,19 +10138,6 @@
"simple-markdown": "^0.7.1"
}
},
"node_modules/react-native-ratings": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/react-native-ratings/-/react-native-ratings-8.1.0.tgz",
"integrity": "sha512-+QOJ4G3NjVkI1D+tk4EGx1dCvVfbD2nQdkrj9cXrcAoEiwmbep4z4bZbCKmWMpQ5h2dqbxABU8/eBnbDmvAc3g==",
"license": "MIT",
"dependencies": {
"lodash": "^4.17.15"
},
"peerDependencies": {
"react": "*",
"react-native": "*"
}
},
"node_modules/react-native-reanimated": {
"version": "3.17.5",
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.17.5.tgz",

View file

@ -19,8 +19,8 @@
"@react-native-async-storage/async-storage": "2.1.2",
"@react-native-community/netinfo": "11.4.1",
"@react-navigation/bottom-tabs": "^7.2.0",
"@rneui/base": "^4.0.0-rc.7",
"@rneui/themed": "^4.0.0-rc.7",
"@rn-vui/base": "^5.1.3",
"@rn-vui/themed": "^5.1.3",
"@supabase/supabase-js": "^2.49.4",
"@tanstack/react-query": "^5.75.2",
"expo": "^53.0.0",

View file

@ -0,0 +1,55 @@
import { createStore, StoreApi, useStore } from "zustand";
import React from "react";
import { Tables } from "@/lib/database.types";
import { useQuery } from "@tanstack/react-query";
import { supabase } from "@/lib/supabase";
import { ActivityIndicator } from "react-native";
import { Redirect } from "expo-router";
type User = Tables<"profiles">;
const UserStoreContext = React.createContext<StoreApi<User> | null>(null);
export const UserStoreProvider = ({
children,
}: {
children: React.ReactNode;
}) => {
const { data, isPending } = useQuery<User | null>({
queryKey: ["user"],
queryFn: async () => {
const { data, error } = await supabase.from("profiles").select("*");
if (error) throw error;
return data[0];
},
});
const [store] = React.useState(() => {
if (!data) {
return null;
}
return createStore<User>()(() => data);
});
if (isPending) {
return <ActivityIndicator />;
}
if (!store) {
return <Redirect href="/(auth)/login" />;
}
return (
<UserStoreContext.Provider value={store as StoreApi<User>}>
{children}
</UserStoreContext.Provider>
);
};
export const useUser = () => {
const store = React.useContext(UserStoreContext);
if (!store) {
throw new Error("Missing UserStoreProvider");
}
return useStore(store);
};