Add login info + temporary work
This commit is contained in:
parent
ed12c934b5
commit
28816a0e86
12 changed files with 135 additions and 73 deletions
5
sql/01_username_is_not_unique.sql
Normal file
5
sql/01_username_is_not_unique.sql
Normal 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);
|
||||
4
sql/02_add_default_values.sql
Normal file
4
sql/02_add_default_values.sql
Normal 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
2
sql/03_add_email.sql
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE profiles
|
||||
ADD COLUMN email varchar;
|
||||
14
sql/04_add_trigger.sql
Normal file
14
sql/04_add_trigger.sql
Normal 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;
|
||||
|
|
@ -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";
|
||||
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
||||
|
|
|
|||
|
|
@ -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={{
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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: []
|
||||
|
|
|
|||
58
xtablo-expo/package-lock.json
generated
58
xtablo-expo/package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
55
xtablo-expo/providers/UserProvider.tsx
Normal file
55
xtablo-expo/providers/UserProvider.tsx
Normal 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);
|
||||
};
|
||||
Loading…
Reference in a new issue