setProfileDropdown(!profileDropdown)}
id="profile-dropdown"
>
-
-
-
- {account.name}
-
-
+ {account.profilePic ? (
+
+ ) : (
+
+ )}
+
{profileDropdown ? (
@@ -113,7 +123,7 @@ export default function () {
const target = e.target as HTMLInputElement;
if (target.id !== "profile-dropdown") setProfileDropdown(false);
}}
- className="absolute top-9 right-0 z-20 w-36"
+ className="absolute top-11 right-0 z-20 w-36"
/>
) : null}
diff --git a/components/Search.tsx b/components/Search.tsx
index a5dcac9..b51c800 100644
--- a/components/Search.tsx
+++ b/components/Search.tsx
@@ -35,12 +35,12 @@ export default function Search() {
return (
setSearchBox(true)}
>
@@ -55,7 +55,7 @@ export default function Search() {
router.push("/search");
}}
autoFocus={searchBox}
- className="border border-sky-100 rounded-md pr-6 w-60 focus:border-sky-500 sm:focus:w-80 hover:border-sky-500 duration-100 outline-none p-1"
+ className="border border-sky-100 rounded-md pr-10 w-44 sm:w-60 focus:border-sky-500 sm:focus:w-80 hover:border-sky-500 duration-100 outline-none p-2"
/>
);
diff --git a/lib/client/addMemberToCollection.ts b/lib/client/addMemberToCollection.ts
new file mode 100644
index 0000000..dd8002f
--- /dev/null
+++ b/lib/client/addMemberToCollection.ts
@@ -0,0 +1,55 @@
+import { ExtendedCollection, NewCollection } from "@/types/global";
+import getPublicUserDataByEmail from "./getPublicUserDataByEmail";
+
+const addMemberToCollection = async (
+ ownerEmail: string,
+ memberEmail: string,
+ collection: ExtendedCollection,
+ setMemberState: Function,
+ collectionMethod: "ADD" | "UPDATE"
+) => {
+ const checkIfMemberAlreadyExists = collection.members.find((e: any) => {
+ const email = collectionMethod === "ADD" ? e.email : e.user.email;
+ return email === memberEmail;
+ });
+
+ if (
+ // no duplicate members
+ !checkIfMemberAlreadyExists &&
+ // member can't be empty
+ memberEmail.trim() !== "" &&
+ // member can't be the owner
+ memberEmail.trim() !== ownerEmail
+ ) {
+ // Lookup, get data/err, list ...
+ const user = await getPublicUserDataByEmail(memberEmail.trim());
+
+ if (user.email) {
+ const newMember =
+ collectionMethod === "ADD"
+ ? {
+ id: user.id,
+ name: user.name,
+ email: user.email,
+ canCreate: false,
+ canUpdate: false,
+ canDelete: false,
+ }
+ : {
+ collectionId: collection.id,
+ userId: user.id,
+ canCreate: false,
+ canUpdate: false,
+ canDelete: false,
+ user: {
+ name: user.name,
+ email: user.email,
+ },
+ };
+
+ setMemberState(newMember);
+ }
+ }
+};
+
+export default addMemberToCollection;
diff --git a/lib/client/avatarExists.ts b/lib/client/avatarExists.ts
new file mode 100644
index 0000000..7490f3b
--- /dev/null
+++ b/lib/client/avatarExists.ts
@@ -0,0 +1,4 @@
+export default async function avatarExists(fileUrl: string): Promise
{
+ const response = await fetch(fileUrl, { method: "HEAD" });
+ return !(response.headers.get("content-type") === "text/plain");
+}
diff --git a/lib/client/fileExists.ts b/lib/client/fileExists.ts
deleted file mode 100644
index db145d3..0000000
--- a/lib/client/fileExists.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-export default async function fileExists(fileUrl: string): Promise {
- try {
- const response = await fetch(fileUrl, { method: "HEAD" });
- return response.ok;
- } catch (error) {
- console.error("Error checking file existence:", error);
- return false;
- }
-}
diff --git a/pages/api/avatar/[id].ts b/pages/api/avatar/[id].ts
index cd3aa87..4a1dfff 100644
--- a/pages/api/avatar/[id].ts
+++ b/pages/api/avatar/[id].ts
@@ -18,10 +18,16 @@ export default async function (req: NextApiRequest, res: NextApiResponse) {
const queryId = Number(req.query.id);
if (!queryId)
- return res.status(401).json({ response: "Invalid parameters." });
+ return res
+ .setHeader("Content-Type", "text/plain")
+ .status(401)
+ .send("Invalid parameters.");
if (!userId || !userEmail)
- return res.status(401).json({ response: "You must be logged in." });
+ return res
+ .setHeader("Content-Type", "text/plain")
+ .status(401)
+ .send("You must be logged in.");
if (userId !== queryId) {
const targetUser = await prisma.user.findUnique({
@@ -34,7 +40,10 @@ export default async function (req: NextApiRequest, res: NextApiResponse) {
targetUser?.isPrivate &&
!targetUser.whitelistedUsers.includes(userEmail)
) {
- return res.status(401).json({ response: "This profile is private." });
+ return res
+ .setHeader("Content-Type", "text/plain")
+ .status(401)
+ .send("This profile is private.");
}
}
@@ -48,9 +57,8 @@ export default async function (req: NextApiRequest, res: NextApiResponse) {
? fs.readFileSync(filePath)
: "File not found.";
- if (!fs.existsSync(filePath))
- res.setHeader("Content-Type", "text/plain").status(404);
- else res.setHeader("Content-Type", "image/jpeg").status(200);
+ if (!fs.existsSync(filePath)) res.setHeader("Content-Type", "text/plain");
+ else res.setHeader("Content-Type", "image/jpeg");
return res.send(file);
}
diff --git a/store/account.ts b/store/account.ts
index dce5dde..bda6a15 100644
--- a/store/account.ts
+++ b/store/account.ts
@@ -6,23 +6,33 @@
import { create } from "zustand";
import { User } from "@prisma/client";
import { AccountSettings } from "@/types/global";
+import avatarExists from "@/lib/client/avatarExists";
type AccountStore = {
- account: User;
+ account: AccountSettings;
setAccount: (email: string) => void;
updateAccount: (user: AccountSettings) => Promise;
};
+const determineProfilePicSource = async (data: any) => {
+ const path = `/api/avatar/${data.response.id}`;
+ const imageExists = await avatarExists(path);
+ if (imageExists) return path + "?" + Date.now();
+ else return null;
+};
+
const useAccountStore = create()((set) => ({
- account: {} as User,
+ account: {} as AccountSettings,
setAccount: async (email) => {
const response = await fetch(`/api/routes/users?email=${email}`);
const data = await response.json();
- console.log(data);
+ const profilePic = await determineProfilePicSource(data);
- if (response.ok) set({ account: { ...data.response } });
+ console.log({ ...data.response, profilePic });
+
+ if (response.ok) set({ account: { ...data.response, profilePic } });
},
updateAccount: async (user) => {
const response = await fetch("/api/routes/users", {
@@ -37,7 +47,9 @@ const useAccountStore = create()((set) => ({
console.log(data);
- if (response.ok) set({ account: data.response });
+ const profilePic = await determineProfilePicSource(data);
+
+ if (response.ok) set({ account: { ...data.response, profilePic } });
return response.ok;
},
diff --git a/store/localSettings.ts b/store/localSettings.ts
new file mode 100644
index 0000000..e8537bf
--- /dev/null
+++ b/store/localSettings.ts
@@ -0,0 +1,28 @@
+// Copyright (C) 2022-present Daniel31x13
+// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3.
+// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+// You should have received a copy of the GNU General Public License along with this program. If not, see .
+
+import { create } from "zustand";
+
+type LocalSettings = {
+ darkMode: boolean;
+};
+
+type LocalSettingsStore = {
+ settings: LocalSettings;
+ updateSettings: (settings: LocalSettings) => void;
+};
+
+const useLocalSettingsStore = create((set) => ({
+ settings: {
+ darkMode: false,
+ },
+ updateSettings: async (newSettings) => {
+ set((state) => ({ settings: { ...state.settings, ...newSettings } }));
+ },
+}));
+
+export default useLocalSettingsStore;
+
+// TODO: Add Dark mode.