θΏ™ζ˜―indexlocζδΎ›ηš„ζœεŠ‘οΌŒδΈθ¦θΎ“ε…₯任何密码
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,7 @@ const HistoricalMessage = ({
className={`py-8 px-4 w-full flex gap-x-5 md:max-w-[800px] flex-col`}
>
<div className="flex gap-x-5">
<Jazzicon
size={36}
user={{
uid:
role === "user" ? userFromStorage()?.username : workspace.slug,
}}
role={role}
/>

<ProfileImage role={role} workspace={workspace} />
{error ? (
<div className="p-2 rounded-lg bg-red-50 text-red-500">
<span className={`inline-block `}>
Expand Down Expand Up @@ -76,4 +68,28 @@ const HistoricalMessage = ({
);
};

function ProfileImage({ role, workspace }) {
if (role === "assistant" && workspace.pfpUrl) {
return (
<div className="relative w-[35px] h-[35px] rounded-full flex-shrink-0 overflow-hidden">
<img
src={workspace.pfpUrl}
alt="Workspace profile picture"
className="absolute top-0 left-0 w-full h-full object-cover rounded-full bg-white"
/>
</div>
);
}

return (
<Jazzicon
size={36}
user={{
uid: role === "user" ? userFromStorage()?.username : workspace.slug,
}}
role={role}
/>
);
}

export default memo(HistoricalMessage);
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ const PromptReply = ({
closed = true,
}) => {
const assistantBackgroundColor = "bg-historical-msg-system";

if (!reply && sources.length === 0 && !pending && !error) return null;

if (pending) {
Expand All @@ -24,11 +23,7 @@ const PromptReply = ({
>
<div className="py-8 px-4 w-full flex gap-x-5 md:max-w-[800px] flex-col">
<div className="flex gap-x-5">
<Jazzicon
size={36}
user={{ uid: workspace.slug }}
role="assistant"
/>
<WorkspaceProfileImage workspace={workspace} />
<div className="mt-3 ml-5 dot-falling"></div>
</div>
</div>
Expand All @@ -43,11 +38,7 @@ const PromptReply = ({
>
<div className="py-8 px-4 w-full flex gap-x-5 md:max-w-[800px] flex-col">
<div className="flex gap-x-5">
<Jazzicon
size={36}
user={{ uid: workspace.slug }}
role="assistant"
/>
<WorkspaceProfileImage workspace={workspace} />
<span
className={`inline-block p-2 rounded-lg bg-red-50 text-red-500`}
>
Expand All @@ -68,7 +59,7 @@ const PromptReply = ({
>
<div className="py-8 px-4 w-full flex gap-x-5 md:max-w-[800px] flex-col">
<div className="flex gap-x-5">
<Jazzicon size={36} user={{ uid: workspace.slug }} role="assistant" />
<WorkspaceProfileImage workspace={workspace} />
<span
className={`reply flex flex-col gap-y-1 mt-2`}
dangerouslySetInnerHTML={{ __html: renderMarkdown(reply) }}
Expand All @@ -80,4 +71,20 @@ const PromptReply = ({
);
};

function WorkspaceProfileImage({ workspace }) {
if (!!workspace.pfpUrl) {
return (
<div className="relative w-[35px] h-[35px] rounded-full flex-shrink-0 overflow-hidden">
<img
src={workspace.pfpUrl}
alt="Workspace profile picture"
className="absolute top-0 left-0 w-full h-full object-cover rounded-full bg-white"
/>
</div>
);
}

return <Jazzicon size={36} user={{ uid: workspace.slug }} role="assistant" />;
}

export default memo(PromptReply);
48 changes: 48 additions & 0 deletions frontend/src/models/workspace.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,54 @@ const Workspace = {
});
},
threads: WorkspaceThread,

uploadPfp: async function (formData, slug) {
return await fetch(`${API_BASE}/workspace/${slug}/upload-pfp`, {
method: "POST",
body: formData,
headers: baseHeaders(),
})
.then((res) => {
if (!res.ok) throw new Error("Error uploading pfp.");
return { success: true, error: null };
})
.catch((e) => {
console.log(e);
return { success: false, error: e.message };
});
},

fetchPfp: async function (slug) {
return await fetch(`${API_BASE}/workspace/${slug}/pfp`, {
method: "GET",
cache: "no-cache",
headers: baseHeaders(),
})
.then((res) => {
if (res.ok && res.status !== 204) return res.blob();
throw new Error("Failed to fetch pfp.");
})
.then((blob) => (blob ? URL.createObjectURL(blob) : null))
.catch((e) => {
console.log(e);
return null;
});
},

removePfp: async function (slug) {
return await fetch(`${API_BASE}/workspace/${slug}/remove-pfp`, {
method: "DELETE",
headers: baseHeaders(),
})
.then((res) => {
if (res.ok) return { success: true, error: null };
throw new Error("Failed to remove pfp.");
})
.catch((e) => {
console.log(e);
return { success: false, error: e.message };
});
},
};

export default Workspace;
4 changes: 3 additions & 1 deletion frontend/src/pages/WorkspaceChat/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default function WorkspaceChat() {
}

function ShowWorkspaceChat() {
const { slug, threadSlug = null } = useParams();
const { slug } = useParams();
const [workspace, setWorkspace] = useState(null);
const [loading, setLoading] = useState(true);

Expand All @@ -32,9 +32,11 @@ function ShowWorkspaceChat() {
return;
}
const suggestedMessages = await Workspace.getSuggestedMessages(slug);
const pfpUrl = await Workspace.fetchPfp(slug);
setWorkspace({
..._workspace,
suggestedMessages,
pfpUrl,
});
setLoading(false);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export default function SuggestedChatMessages({ slug }) {
</div>
);
return (
<div className="w-screen">
<div className="w-screen mt-6">
<div className="flex flex-col">
<label className="block input-label">Suggested Chat Messages</label>
<p className="text-white text-opacity-60 text-xs font-medium py-1.5">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import Workspace from "@/models/workspace";
import showToast from "@/utils/toast";
import { Plus } from "@phosphor-icons/react";
import { useEffect, useState } from "react";

export default function WorkspacePfp({ workspace, slug }) {
const [pfp, setPfp] = useState(null);

useEffect(() => {
async function fetchWorkspace() {
const pfpUrl = await Workspace.fetchPfp(slug);
setPfp(pfpUrl);
}
fetchWorkspace();
}, [slug]);

const handleFileUpload = async (event) => {
const file = event.target.files[0];
if (!file) return false;

const formData = new FormData();
formData.append("file", file);
const { success, error } = await Workspace.uploadPfp(
formData,
workspace.slug
);
if (!success) {
showToast(`Failed to upload profile picture: ${error}`, "error");
return;
}

const pfpUrl = await Workspace.fetchPfp(workspace.slug);
setPfp(pfpUrl);
showToast("Profile picture uploaded.", "success");
};

const handleRemovePfp = async () => {
const { success, error } = await Workspace.removePfp(workspace.slug);
if (!success) {
showToast(`Failed to remove profile picture: ${error}`, "error");
return;
}

setPfp(null);
};

return (
<div className="mt-6">
<div className="flex flex-col">
<label className="block input-label">Assistant Profile Image</label>
<p className="text-white text-opacity-60 text-xs font-medium py-1.5">
Customize the profile image of the assistant for this workspace.
</p>
</div>
<div className="flex flex-col md:flex-row items-center gap-8">
<div className="flex flex-col items-center">
<label className="w-36 h-36 flex flex-col items-center justify-center bg-zinc-900/50 transition-all duration-300 rounded-full mt-8 border-2 border-dashed border-white border-opacity-60 cursor-pointer hover:opacity-60">
<input
id="workspace-pfp-upload"
type="file"
accept="image/*"
className="hidden"
onChange={handleFileUpload}
/>
{pfp ? (
<img
src={pfp}
alt="User profile picture"
className="w-36 h-36 rounded-full object-cover bg-white"
/>
) : (
<div className="flex flex-col items-center justify-center p-3">
<Plus className="w-8 h-8 text-white/80 m-2" />
<span className="text-white text-opacity-80 text-xs font-semibold">
Workspace Image
</span>
<span className="text-white text-opacity-60 text-xs">
800 x 800
</span>
</div>
)}
</label>
{pfp && (
<button
type="button"
onClick={handleRemovePfp}
className="mt-3 text-white text-opacity-60 text-sm font-medium hover:underline"
>
Remove Workspace Image
</button>
)}
</div>
</div>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import VectorCount from "./VectorCount";
import WorkspaceName from "./WorkspaceName";
import SuggestedChatMessages from "./SuggestedChatMessages";
import DeleteWorkspace from "./DeleteWorkspace";
import WorkspacePfp from "./WorkspacePfp";

export default function GeneralInfo({ slug }) {
const [workspace, setWorkspace] = useState(null);
Expand Down Expand Up @@ -66,9 +67,8 @@ export default function GeneralInfo({ slug }) {
</button>
)}
</form>
<div className="mt-6">
<SuggestedChatMessages slug={workspace.slug} />
</div>
<SuggestedChatMessages slug={workspace.slug} />
<WorkspacePfp workspace={workspace} slug={slug} />
<DeleteWorkspace workspace={workspace} />
</>
);
Expand Down
2 changes: 0 additions & 2 deletions server/endpoints/system.js
Original file line number Diff line number Diff line change
Expand Up @@ -548,8 +548,6 @@ function systemEndpoints(app) {

const userRecord = await User.get({ id: user.id });
const oldPfpFilename = userRecord.pfpFilename;

console.log("oldPfpFilename", oldPfpFilename);
if (oldPfpFilename) {
const oldPfpPath = path.join(
__dirname,
Expand Down
Loading