-
-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Add user PFP support and context to logo #408
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…lways scroll to very bottom on message history change
…96-modal-sizes-too-big-in-onboarding-flow
…om:Mintplex-Labs/anything-llm into 396-modal-sizes-too-big-in-onboarding-flow
…fp-and-logo-context
frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/PasswordProtection/index.jsxInstead of using // Instead of this
if (error) {
alert(`Failed to set password: ${error}`, "error");
return;
}
// Do something like this
if (error) {
displayError(`Failed to set password: ${error}`);
return;
}Instead of using // Instead of this
window.localStorage.setItem(AUTH_TOKEN, token);
// Do something like this
window.sessionStorage.setItem(AUTH_TOKEN, token);Consider using a more descriptive name for the // Instead of this
const setNewPassword = (e) => setPassword(e.target.value);
// Do something like this
const updatePassword = (e) => setPassword(e.target.value);frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsxInstead of using the const scrollToBottom = () => {
if (chatHistoryRef.current) {
chatHistoryRef.current.scrollTop = chatHistoryRef.current.scrollHeight;
}
};frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/LLMSelection/index.jsxThe variable name async function fetchKeys() {
const systemSettings = await System.keys();
setSettings(systemSettings);
setLLMChoice(systemSettings?.LLMProvider);
setLoading(false);
}frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/AppearanceSetup/index.jsxThe variable name const { logo: initialLogo, setLogo: _setLogo } = useLogo();frontend/src/components/UserMenu/index.jsxIt's generally not a good practice to send passwords in plaintext over the network. Consider implementing a more secure way of handling password updates, such as hashing the password on the client side before sending it to the server. import bcrypt from 'bcryptjs';
// Hash password before sending to server
const salt = bcrypt.genSaltSync(10);
const hashedPassword = bcrypt.hashSync(data.password, salt);
data.password = hashedPassword;Consider caching the user's profile picture URL in the local storage or in a state management library like Redux. This way, you don't have to fetch the profile picture every time the user navigates to a different page or refreshes the page. // Save profile picture URL to local storage
localStorage.setItem('pfp', pfpUrl);frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/VectorDatabaseConnection/index.jsxInstead of using the alert function to display the error message, consider using a more user-friendly approach such as a toast notification or a modal. This will improve the user experience and make the application more interactive. if (error) {
// Use a toast notification or a modal to display the error message
toast.error(`Failed to save settings: ${error}`);
return;
}Consider wrapping the fetchKeys function inside a useCallback hook. This will prevent unnecessary re-renders and improve the performance of your application. const fetchKeys = useCallback(async () => {
const _settings = await System.keys();
setSettings(_settings);
setVectorDB(_settings?.VectorDB || "lancedb");
setLoading(false);
}, []);
useEffect(() => {
if (currentStep === "vector_database") {
fetchKeys();
}
}, [currentStep, fetchKeys]);frontend/src/models/system.jsIt's a good practice to provide more descriptive error messages and to use console.error for error logging instead of console.log. This will help in debugging and understanding the error context better. // In frontend/src/models/system.js
// Replace console.log with console.error in catch blocks
.catch((e) => {
console.error(e);
return { success: false, error: e.message };
});server/utils/files/pfp.jsIn server/utils/files/pfp.js, fs.readFileSync is used which is a blocking operation. It's better to use asynchronous file operations like fs.readFile to improve performance. // In server/utils/files/pfp.js
// Replace fs.readFileSync with fs.readFile
fs.readFile(pfpPath, (err, data) => {
if (err) throw err;
return {
buffer: data,
size: data.length,
mime: getType(pfpPath),
};
});server/prisma/migrations/20231129012019_add/migration.sqlIt's a good practice to add a NOT NULL constraint to the columns where the value is mandatory. This will prevent any null values from being inserted into the "pfpFilename" column, ensuring data integrity. ALTER TABLE "users" ADD COLUMN "pfpFilename" TEXT NOT NULL;It's a good practice to add a default value to the columns where the value is mandatory but not provided during the insertion. This will ensure that there is always a value in the "pfpFilename" column, improving data consistency. ALTER TABLE "users" ADD COLUMN "pfpFilename" TEXT NOT NULL DEFAULT 'default.png';server/prisma/schema.prismaIt would be beneficial to add a comment next to the 'pfpFilename' field to describe its purpose. This will improve the readability of the code and make it easier for other developers to understand the purpose of this field. pfpFilename String? // Profile picture filenameIt would be beneficial to add a default value to the 'pfpFilename' field. This will prevent null values from being stored in the database, which can lead to performance issues and potential errors when retrieving this field. pfpFilename String? @default("default.jpg") |
| const scrollToBottom = () => { | ||
| if (chatHistoryRef.current) { | ||
| chatHistoryRef.current.scrollTo({ | ||
| top: chatHistoryRef.current.scrollHeight, | ||
| behavior: "smooth", | ||
| }); | ||
| } | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replaced the scrollTo method with direct assignment of scrollTop to scrollHeight for better performance.
| const scrollToBottom = () => { | |
| if (chatHistoryRef.current) { | |
| chatHistoryRef.current.scrollTo({ | |
| top: chatHistoryRef.current.scrollHeight, | |
| behavior: "smooth", | |
| }); | |
| } | |
| }; | |
| const scrollToBottom = () => { | |
| if (chatHistoryRef.current) { | |
| chatHistoryRef.current.scrollTop = chatHistoryRef.current.scrollHeight; | |
| } | |
| }; |
frontend/src/pages/OnboardingFlow/OnboardingModal/Steps/AppearanceSetup/index.jsx
Show resolved
Hide resolved
| const handleUpdate = async (e) => { | ||
| e.preventDefault(); | ||
|
|
||
| const data = {}; | ||
| const form = new FormData(e.target); | ||
| for (var [key, value] of form.entries()) { | ||
| if (!value || value === null) continue; | ||
| data[key] = value; | ||
| } | ||
|
|
||
| console.log(data); | ||
| const { success, error } = await System.updateUser(data); | ||
|
|
||
| if (success) { | ||
| let storedUser = JSON.parse(localStorage.getItem(AUTH_USER)); | ||
|
|
||
| if (storedUser) { | ||
| storedUser.username = data.username; | ||
| localStorage.setItem(AUTH_USER, JSON.stringify(storedUser)); | ||
| } | ||
| window.location.reload(); | ||
| } else { | ||
| showToast(`Failed to update user: ${error}`, "error"); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hash the user's password before sending it to the server to enhance security.
| const handleUpdate = async (e) => { | |
| e.preventDefault(); | |
| const data = {}; | |
| const form = new FormData(e.target); | |
| for (var [key, value] of form.entries()) { | |
| if (!value || value === null) continue; | |
| data[key] = value; | |
| } | |
| console.log(data); | |
| const { success, error } = await System.updateUser(data); | |
| if (success) { | |
| let storedUser = JSON.parse(localStorage.getItem(AUTH_USER)); | |
| if (storedUser) { | |
| storedUser.username = data.username; | |
| localStorage.setItem(AUTH_USER, JSON.stringify(storedUser)); | |
| } | |
| window.location.reload(); | |
| } else { | |
| showToast(`Failed to update user: ${error}`, "error"); | |
| } | |
| import bcrypt from 'bcryptjs'; | |
| // Hash password before sending to server | |
| const salt = bcrypt.genSaltSync(10); | |
| const hashedPassword = bcrypt.hashSync(data.password, salt); | |
| data.password = hashedPassword; |
| 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 System.uploadPfp(formData); | ||
| if (!success) { | ||
| showToast(`Failed to upload profile picture: ${error}`, "error"); | ||
| return; | ||
| } | ||
|
|
||
| const pfpUrl = await System.fetchPfp(user.id); | ||
| setPfp(pfpUrl); | ||
|
|
||
| showToast("Profile picture uploaded successfully.", "success"); | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The profile picture URL is now being saved to local storage after being fetched. This will improve performance by reducing the number of network requests.
| 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 System.uploadPfp(formData); | |
| if (!success) { | |
| showToast(`Failed to upload profile picture: ${error}`, "error"); | |
| return; | |
| } | |
| const pfpUrl = await System.fetchPfp(user.id); | |
| setPfp(pfpUrl); | |
| showToast("Profile picture uploaded successfully.", "success"); | |
| }; | |
| 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 System.uploadPfp(formData); | |
| if (!success) { | |
| showToast(`Failed to upload profile picture: ${error}`, "error"); | |
| return; | |
| } | |
| const pfpUrl = await System.fetchPfp(user.id); | |
| setPfp(pfpUrl); | |
| // Save profile picture URL to local storage | |
| localStorage.setItem('pfp', pfpUrl); | |
| showToast("Profile picture uploaded successfully.", "success"); | |
| }; |
|
@shatfield4 can you rebase with master and solve merge conflicts? |
Resolved |
* fix sizing of onboarding modals & lint * fix extra scrolling on mobile onboarding flow * added message to use desktop for onboarding * linting * add arrow to scroll to bottom (debounced) and fix chat scrolling to always scroll to very bottom on message history change * fix for empty chat * change mobile alert copy * WIP adding PFP upload support * WIP pfp for users * edit account menu complete with change username/password and upload profile picture * add pfp context to update all instances of usePfp hook on update * linting * add context for logo change to immediately update logo * fix div with bullet points to use list-disc instead * fix: small changes * update multer file storage locations * fix: use STORAGE_DIR for filepathing --------- Co-authored-by: timothycarambat <rambat1010@gmail.com>
• Create PFP user menu and hooks to render the PFP everywhere
• Update backend to handle PFP uploads
• Update Prisma schema to support PFPs
• Create context and wrappers for PFP and logo since they function very similarly