@@ -28,7 +28,7 @@ export function ReadOnlyUserCard({ userInfo }: { userInfo: UserInfo }) {
diff --git a/frontend/src/components/common/SideBar.tsx b/frontend/src/components/common/SideBar.tsx
index dde60237..a5c9f8c3 100644
--- a/frontend/src/components/common/SideBar.tsx
+++ b/frontend/src/components/common/SideBar.tsx
@@ -82,6 +82,8 @@ export default function SideBar() {
return;
});
}
+ // router does not change
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [user]);
// obtain current path, if is login/sign up, don't render SideBar
diff --git a/frontend/src/components/forms/ProfileEditor.tsx b/frontend/src/components/forms/ProfileEditor.tsx
index 8d1d1ecc..3cec1f83 100644
--- a/frontend/src/components/forms/ProfileEditor.tsx
+++ b/frontend/src/components/forms/ProfileEditor.tsx
@@ -15,6 +15,7 @@ import {
import FileInput from "./FileInput";
import userService from "@/helpers/user-service/api-wrapper";
import { useUserContext } from "@/contexts/user-context";
+import { uploadFiles } from "@/utils/uploadthing";
export default function ProfileEditor({ userInfo }: { userInfo: UserInfo }) {
const { user, setUserContext } = useUserContext();
@@ -24,14 +25,16 @@ export default function ProfileEditor({ userInfo }: { userInfo: UserInfo }) {
return name == "";
}, [name]);
const [bio, setBio] = useState(info.bio);
- const [photo, setPhoto] = useState(info.photo);
+ const [photo, setPhoto] = useState(info.avatarUrl);
const [newPhoto, setNewPhoto] = useState();
- // userInfo is constant, do not change for now
const hasChanged = useMemo(() => {
if (name != info.name) return true;
if (bio != info.bio) return true;
- if (photo != info.photo && !(photo == "" && info.photo == undefined))
+ if (
+ photo != info.avatarUrl &&
+ !(photo == "" && info.avatarUrl == undefined)
+ )
return true;
return false;
}, [name, bio, photo, info]);
@@ -41,14 +44,14 @@ export default function ProfileEditor({ userInfo }: { userInfo: UserInfo }) {
setPhoto(URL.createObjectURL(newPhoto));
} else {
setNewPhoto(undefined);
- setPhoto(info.photo);
+ setPhoto(info.avatarUrl);
}
- }, [newPhoto, info.photo]);
+ }, [newPhoto, info.avatarUrl]);
const handleDiscard = () => {
setName(info.name);
setBio(info.bio);
- setPhoto(info.photo);
+ setPhoto(info.avatarUrl);
setNewPhoto(undefined);
};
@@ -59,22 +62,33 @@ export default function ProfileEditor({ userInfo }: { userInfo: UserInfo }) {
return;
}
- if (name == info.name && bio == info.bio && photo == info.photo) {
+ if (name == info.name && bio == info.bio && photo == info.avatarUrl) {
setMessage("Profile saved!");
return;
}
+ const dataUpdated: Record = {
+ name: name,
+ bio: bio,
+ };
+
try {
- await userService.updateUserInfo(user?.uid ?? 0, {
- name: name,
- bio: bio,
- });
+ let photoUrl = photo;
+ if (newPhoto) {
+ const fileResponse = await uploadFiles("imageUploader", {
+ files: [newPhoto],
+ });
+ photoUrl = fileResponse[0].url;
+ dataUpdated['"avatarUrl"'] = photoUrl;
+ setPhoto(photoUrl);
+ }
+ await userService.updateUserInfo(user?.uid ?? 0, dataUpdated);
setMessage("Profile saved!");
setInfo({
name: name,
email: info.email,
bio: bio,
- photo: photo!,
+ avatarUrl: photoUrl,
});
setUserContext({
diff --git a/frontend/src/contexts/user-context.tsx b/frontend/src/contexts/user-context.tsx
index 3257a831..b79de54b 100644
--- a/frontend/src/contexts/user-context.tsx
+++ b/frontend/src/contexts/user-context.tsx
@@ -14,6 +14,8 @@ interface UserContextType {
}
const initialUser: User | null = null;
+// React component generated by createContext
+// eslint-disable-next-line @typescript-eslint/naming-convention
const UserContext = createContext({
user: null,
setUserContext: () => {
diff --git a/frontend/src/helpers/user-service/api-wrapper.ts b/frontend/src/helpers/user-service/api-wrapper.ts
index 8403fbf6..4a4bb84b 100644
--- a/frontend/src/helpers/user-service/api-wrapper.ts
+++ b/frontend/src/helpers/user-service/api-wrapper.ts
@@ -72,7 +72,7 @@ const getUserInfo = async (uid: number): Promise => {
name: responseData.name,
email: responseData.email,
bio: responseData.bio || "This person doesn't have bio",
- photo: responseData.photo,
+ avatarUrl: responseData.avatarUrl,
};
return userInfo;
} else {
diff --git a/frontend/src/middleware.ts b/frontend/src/middleware.ts
index 1b5df4cd..54fa8da3 100644
--- a/frontend/src/middleware.ts
+++ b/frontend/src/middleware.ts
@@ -4,8 +4,14 @@ export const config = {
matchers: "/:path*",
};
-export default function middleware(request: NextRequest) {
- const publicRoutes = ["/_next", "/public", "/login", "/sign-up"];
+const middleware = (request: NextRequest) => {
+ const publicRoutes = [
+ "/_next",
+ "/public",
+ "/login",
+ "/sign-up",
+ "/api/uploadthing",
+ ];
const redirectRoutes = ["/"];
const path = request.nextUrl.pathname;
@@ -29,4 +35,6 @@ export default function middleware(request: NextRequest) {
}
return NextResponse.next();
-}
+};
+
+export default middleware;
diff --git a/frontend/src/types/user-service.d.ts b/frontend/src/types/user-service.d.ts
index 96362a3a..7f7f90c2 100644
--- a/frontend/src/types/user-service.d.ts
+++ b/frontend/src/types/user-service.d.ts
@@ -7,7 +7,7 @@ interface UserInfo {
name: string;
email: string;
bio: string;
- photo?: string;
+ avatarUrl?: string;
}
interface ErrorResponse {
diff --git a/frontend/src/utils/classMergeUtils.ts b/frontend/src/utils/classMergeUtils.ts
index 365058ce..ccd813e3 100644
--- a/frontend/src/utils/classMergeUtils.ts
+++ b/frontend/src/utils/classMergeUtils.ts
@@ -1,6 +1,6 @@
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
-export function cn(...inputs: ClassValue[]) {
+export const cn = (...inputs: ClassValue[]) => {
return twMerge(clsx(inputs));
-}
+};
diff --git a/frontend/src/utils/uploadthing.ts b/frontend/src/utils/uploadthing.ts
new file mode 100644
index 00000000..eb778bb9
--- /dev/null
+++ b/frontend/src/utils/uploadthing.ts
@@ -0,0 +1,5 @@
+import { OurFileRouter } from "@/app/api/uploadthing/core";
+import { generateReactHelpers } from "@uploadthing/react";
+
+export const { useUploadThing, uploadFiles } =
+ generateReactHelpers();
diff --git a/frontend/yarn.lock b/frontend/yarn.lock
index 090d23a6..05939b87 100644
--- a/frontend/yarn.lock
+++ b/frontend/yarn.lock
@@ -3020,6 +3020,35 @@
resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz"
integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==
+"@uploadthing/dropzone@0.2.1":
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/@uploadthing/dropzone/-/dropzone-0.2.1.tgz#e373496b3b59b3cf61c8993b5c12db294c48467e"
+ integrity sha512-OK4rSFnQ2woJ07t78hTfxjMxXedLoj+Jp34kIEtYPYBTPOnNwoKhDXfRojSu8cBilkQROzIe67i0p6F4B6LQhQ==
+ dependencies:
+ file-selector "^0.6.0"
+
+"@uploadthing/mime-types@0.2.7":
+ version "0.2.7"
+ resolved "https://registry.yarnpkg.com/@uploadthing/mime-types/-/mime-types-0.2.7.tgz#f4d5d7a0faa5afd4bc16fcac1e61c80e6487b638"
+ integrity sha512-HfQpL1GSyLOOnIT4WqVv8aW3HotEpFKuuCnK+dUZThMzeiFs09s9J0wk5/VBwupXvIXQwMiB//bp9aUE2p/mFg==
+
+"@uploadthing/react@^6.4.2":
+ version "6.4.2"
+ resolved "https://registry.yarnpkg.com/@uploadthing/react/-/react-6.4.2.tgz#1e8bae2654e42b8dc5d1ec5d52d1d08e44fa4a70"
+ integrity sha512-Z/LIku44nhLF9aXEBMEsknL+ADpLKC+MzeD+H8v7jVtghcbYHrW8mF2M91z2H223mVLR0Tnj77yemRoBAh7+3Q==
+ dependencies:
+ "@uploadthing/dropzone" "0.2.1"
+ "@uploadthing/shared" "6.5.0"
+ file-selector "^0.6.0"
+ tailwind-merge "^2.2.1"
+
+"@uploadthing/shared@6.5.0":
+ version "6.5.0"
+ resolved "https://registry.yarnpkg.com/@uploadthing/shared/-/shared-6.5.0.tgz#954deb113aaa674890e1b83773b99e53e8d242a6"
+ integrity sha512-RVzHODev1FRnPSEOih/rTjN89FW9ZXK1uvxSCg+q43cYkUcgJ8mF5LbEmS4dwRqRJkFpspfTPo8IcMe7WwoQ2A==
+ dependencies:
+ std-env "^3.7.0"
+
abab@^2.0.6:
version "2.0.6"
resolved "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz"
@@ -3826,6 +3855,11 @@ concurrently@^8.2.2:
tree-kill "^1.2.2"
yargs "^17.7.2"
+consola@^3.2.3:
+ version "3.2.3"
+ resolved "https://registry.yarnpkg.com/consola/-/consola-3.2.3.tgz#0741857aa88cfa0d6fd53f1cff0375136e98502f"
+ integrity sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==
+
content-disposition@0.5.2:
version "0.5.2"
resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz"
@@ -4798,6 +4832,13 @@ file-entry-cache@^6.0.1:
dependencies:
flat-cache "^3.0.4"
+file-selector@^0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.6.0.tgz#fa0a8d9007b829504db4d07dd4de0310b65287dc"
+ integrity sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw==
+ dependencies:
+ tslib "^2.4.0"
+
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz"
@@ -7577,6 +7618,11 @@ statuses@~1.4.0:
resolved "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz"
integrity sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==
+std-env@^3.7.0:
+ version "3.7.0"
+ resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.7.0.tgz#c9f7386ced6ecf13360b6c6c55b8aaa4ef7481d2"
+ integrity sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==
+
stethoskop@1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/stethoskop/-/stethoskop-1.0.0.tgz"
@@ -7810,7 +7856,7 @@ tailwind-merge@^1.14.0:
resolved "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-1.14.0.tgz"
integrity sha512-3mFKyCo/MBcgyOTlrY8T7odzZFx+w+qKSMAmdFzRvqBfLlSigU6TZnlFHK0lkMwj9Bj8OYU+9yW9lmGuS0QEnQ==
-tailwind-merge@^2.2.2:
+tailwind-merge@^2.2.1, tailwind-merge@^2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/tailwind-merge/-/tailwind-merge-2.2.2.tgz#87341e7604f0e20499939e152cd2841f41f7a3df"
integrity sha512-tWANXsnmJzgw6mQ07nE3aCDkCK4QdT3ThPMCzawoYA2Pws7vSTCvz3Vrjg61jVUGfFZPJzxEP+NimbcW+EdaDw==
@@ -8153,6 +8199,16 @@ update-browserslist-db@^1.0.13:
escalade "^3.1.1"
picocolors "^1.0.0"
+uploadthing@^6.8.0:
+ version "6.8.0"
+ resolved "https://registry.yarnpkg.com/uploadthing/-/uploadthing-6.8.0.tgz#1bfd58a2d72c836255d034cb44c020c5f1eda29c"
+ integrity sha512-89DwCDsLLdF1bhPtCX4TYgl3NCZtyTeixn9hOVUViOIc3R429nJ5sViz2Lr9wz+nN5gW66qaa4qao6E43sN/hA==
+ dependencies:
+ "@uploadthing/mime-types" "0.2.7"
+ "@uploadthing/shared" "6.5.0"
+ consola "^3.2.3"
+ std-env "^3.7.0"
+
uri-js@^4.2.2:
version "4.4.1"
resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz"