diff --git a/package.json b/package.json index c8d9269..07a16ed 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wischlist", - "version": "0.1.3", + "version": "0.1.4", "private": true, "scripts": { "dev": "next dev", @@ -10,7 +10,9 @@ }, "dependencies": { "@next/font": "^13.0.7", + "@types/lodash": "^4.17.1", "firebase": "^10.11.1", + "lodash": "^4.17.21", "next": "^13.0.6", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/src/components/AddCard/AddCard.tsx b/src/components/AddCard/AddCard.tsx index 1b54bd3..69a9aab 100644 --- a/src/components/AddCard/AddCard.tsx +++ b/src/components/AddCard/AddCard.tsx @@ -1,6 +1,6 @@ "use client" -import { useAuth } from "lib/auth" +import { useUser } from "lib/auth" import React from "react" import styles from "./AddCard.module.scss" @@ -10,7 +10,7 @@ interface AddCardParams { } const AddCard = ({ callback }: AddCardParams) => { - const { user, loading } = useAuth() + const { user, loading } = useUser() const [isLoading, setLoading] = React.useState(false) const addEmptyCard = () => { setLoading(true) diff --git a/src/components/Auth/Login.tsx b/src/components/Auth/Login.tsx index 224f844..64c1dbf 100644 --- a/src/components/Auth/Login.tsx +++ b/src/components/Auth/Login.tsx @@ -4,11 +4,12 @@ import Link from "next/link" import React, { FormEvent, useEffect, useState } from "react" import styles from "./Auth.module.scss" import Loading from "components/Loading/Loading" -import { useAuth } from "lib/auth" +import { useAuth, useUser } from "lib/auth" import { useRouter } from "next/navigation" const Login = () => { - const { user, login } = useAuth() + const { user } = useUser() + const { login } = useAuth() const router = useRouter() const emailRef = React.useRef(null) const passwordRef = React.useRef(null) diff --git a/src/components/Auth/Register.tsx b/src/components/Auth/Register.tsx index 935b833..93c8685 100644 --- a/src/components/Auth/Register.tsx +++ b/src/components/Auth/Register.tsx @@ -1,8 +1,9 @@ "use client" -import { useAuth } from "lib/auth" +import { useAuth, useUser } from "lib/auth" import Link from "next/link" -import React, { FormEvent, useState } from "react" +import { useRouter } from "next/navigation" +import React, { FormEvent, useState, useEffect } from "react" import styles from "./Auth.module.scss" const Register = () => { @@ -11,9 +12,15 @@ const Register = () => { const passwordRef = React.useRef(null) const { register } = useAuth() + const { user } = useUser() + const router = useRouter() const [loading, setLoading] = useState(false) + useEffect(() => { + if (user) router.push("/profile") + }, [user]) + const authorize = async (e: FormEvent) => { setLoading(true) e.preventDefault() @@ -22,14 +29,12 @@ const Register = () => { const password = passwordRef.current?.value if (!email || !username || !password) return - - - const response = await register( + await register( email, password, + username ) - - alert(response.user.email) + setLoading(false) } diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx index 7abab99..242bb0d 100644 --- a/src/components/Header/Header.tsx +++ b/src/components/Header/Header.tsx @@ -4,12 +4,12 @@ import Image from "next/image" import styles from "./Header.module.scss" import Link from "next/link" import { Indie_Flower } from "@next/font/google" -import { useAuth } from "lib/auth" +import { useUser } from "lib/auth" const indieFlower = Indie_Flower({ weight: "400", subsets: ["latin"] }) const Header = () => { - const { user, loading } = useAuth() + const { user, loading } = useUser() return ( <> diff --git a/src/components/List/List.tsx b/src/components/List/List.tsx index 716040c..3ceae3d 100644 --- a/src/components/List/List.tsx +++ b/src/components/List/List.tsx @@ -4,7 +4,7 @@ import AddCard from "components/AddCard/AddCard" import Card from "components/Card/Card" import { DeleteTrashCan } from "components/DeleteTrashCan/DeleteTrashCan" import Loading from "components/Loading/Loading" -import { useAuth } from "lib/auth" +import { useUser } from "lib/auth" import { useEntries } from "lib/entries" import { useList } from "lib/list" @@ -19,7 +19,7 @@ const indieFlowerFont = Indie_Flower({ weight: "400", subsets: ["latin"] }) const List = ({ params }: { params: { listId: string } }) => { const router = useRouter() - const { user, loading } = useAuth() + const { user, loading } = useUser() const { listId } = params diff --git a/src/components/ListOverview/ListOverview.tsx b/src/components/ListOverview/ListOverview.tsx index 61eb6bc..6929a28 100644 --- a/src/components/ListOverview/ListOverview.tsx +++ b/src/components/ListOverview/ListOverview.tsx @@ -1,6 +1,6 @@ "use client" import Loading from "components/Loading/Loading" -import { useAuth } from "lib/auth" +import { useUser } from "lib/auth" import { useLists } from "lib/lists" @@ -9,7 +9,7 @@ import React from "react" import styles from "./ListOverview.module.scss" const ListOverview = () => { - const { user, loading } = useAuth() + const { user, loading } = useUser() const { lists, addList } = useLists() diff --git a/src/components/Profile/Profile.tsx b/src/components/Profile/Profile.tsx index eb68088..23b8079 100644 --- a/src/components/Profile/Profile.tsx +++ b/src/components/Profile/Profile.tsx @@ -1,11 +1,13 @@ "use client" -import { useAuth } from "lib/auth" +import { useAuth, useUser } from "lib/auth" import Image from "next/image" import { useRouter } from "next/navigation" +import { useCallback } from "react" const Profile = () => { - const { user, logout } = useAuth() + const { user, updateUserName } = useUser() + const { logout } = useAuth() const router = useRouter() const handleLogout = async () => { @@ -13,6 +15,16 @@ const Profile = () => { router.push("/login") } + const handleChangeName = useCallback(async () => { + const value = prompt("Dein Nutzername:", user?.displayName || "") + if (value === null) return + + await updateUserName(value) + + }, [user?.displayName, updateUserName]) + + console.log(user?.displayName) + return (
@@ -26,7 +38,17 @@ const Profile = () => { width="150" height="150" /> -

{user?.displayName}

+ +
+ +

Mein Wischlist Profil

diff --git a/src/lib/auth.tsx b/src/lib/auth.tsx index 822dbaf..5437c06 100644 --- a/src/lib/auth.tsx +++ b/src/lib/auth.tsx @@ -1,8 +1,9 @@ "use client" import { auth } from "lib/firebase"; -import { signInWithEmailAndPassword, createUserWithEmailAndPassword, signOut, User } from "firebase/auth" -import { createContext, PropsWithChildren, useContext, useEffect, useState } from "react"; +import { signInWithEmailAndPassword, createUserWithEmailAndPassword, signOut, User, updateProfile } from "firebase/auth" +import { createContext, PropsWithChildren, useCallback, useContext, useEffect, useState } from "react"; +import _ from "lodash"; const AuthContext = createContext<{ user: User | null, loading: boolean }>({ user: null, loading: false }) @@ -12,13 +13,23 @@ export const AuthProvider = ({ children }: PropsWithChildren) => { const [loading, setLoading] = useState(true) useEffect(() => { - const unsubscribe = auth.onAuthStateChanged((user) => { - setUser(user); + const unsubscribe = auth.onAuthStateChanged((updatedUser) => { + setUser(updatedUser); setLoading(false); }); + const unsubscribeToken = auth.onIdTokenChanged(updatedUser => { + // `updatedUser` appears to be a reference of user + // so setting the updatedUser is not trigger a rerender + // cloning the object removes the reference and it triggers again + const clone = _.cloneDeep(updatedUser) - return unsubscribe; - }, []) + setUser(clone) + }) + return () => { + unsubscribe() + unsubscribeToken() + }; + }, [auth, setLoading]) return ( @@ -29,26 +40,51 @@ export const AuthProvider = ({ children }: PropsWithChildren) => { /** - * Custom hook to provide authentication methods. + * Custom hook to provide user. */ -export const useAuth = () => { +export const useUser = () => { const { user, loading } = useContext(AuthContext) + const updateUserName = useCallback(async (displayName: string) => { + if (!user) return + + await updateProfile(user, { + displayName, + }) + + return await user.reload() + + }, [user, user?.displayName]) + + return { + user, + loading, + updateUserName + } +} + +/** + * + * @returns + */ +export const useAuth = () => { const login = async (email: string, password: string) => await signInWithEmailAndPassword(auth, email, password) const logout = async () => await signOut(auth) - const register = async (email: string, password: string) => - await createUserWithEmailAndPassword(auth, email, password) - + const register = async (email: string, password: string, displayName: string) => { + const { user } = await createUserWithEmailAndPassword(auth, email, password) + await updateProfile(user, { + displayName + }) + return await user.reload() + } return { - user, - loading, login, logout, register, } -} +} \ No newline at end of file diff --git a/src/lib/lists.ts b/src/lib/lists.ts index 354a355..a3fb2de 100644 --- a/src/lib/lists.ts +++ b/src/lib/lists.ts @@ -1,14 +1,14 @@ import { ref, onValue, push, child, remove, query, equalTo, orderByChild } from "firebase/database" import { useCallback, useEffect, useState } from "react"; import { List } from "types"; -import { useAuth } from "./auth"; +import { useUser } from "./auth"; import { database } from "./firebase" export const useLists = () => { const [lists, setLists] = useState | null>(null) - const { user } = useAuth() + const { user } = useUser() const userId = user?.uid useEffect(() => { diff --git a/yarn.lock b/yarn.lock index 49d3cf5..d79f2a2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -630,6 +630,11 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== +"@types/lodash@^4.17.1": + version "4.17.1" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.1.tgz#0fabfcf2f2127ef73b119d98452bd317c4a17eb8" + integrity sha512-X+2qazGS3jxLAIz5JDXDzglAF3KpijdhFxlf/V1+hEsOUc+HnWi81L/uv/EvGuV90WY+7mPGFCUDGfQC3Gj95Q== + "@types/node@18.11.4": version "18.11.4" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.4.tgz#7017a52e18dfaad32f55eebd539993014441949c" @@ -1778,6 +1783,11 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + long@^5.0.0: version "5.2.1" resolved "https://registry.yarnpkg.com/long/-/long-5.2.1.tgz#e27595d0083d103d2fa2c20c7699f8e0c92b897f"