From a4ddbe3f2f49a7db53975c86642d377fc3956504 Mon Sep 17 00:00:00 2001 From: Abdullah Atta Date: Fri, 1 Mar 2024 16:42:26 +0500 Subject: [PATCH] web: edit profile directly --- apps/web/src/common/dialog-controller.tsx | 4 +- ...og.tsx => edit-profile-picture-dialog.tsx} | 174 +++++++++--------- apps/web/src/dialogs/index.ts | 4 +- apps/web/src/dialogs/prompt.tsx | 9 +- .../settings/components/user-profile.tsx | 92 ++++++--- 5 files changed, 167 insertions(+), 116 deletions(-) rename apps/web/src/dialogs/{edit-profile-dialog.tsx => edit-profile-picture-dialog.tsx} (50%) diff --git a/apps/web/src/common/dialog-controller.tsx b/apps/web/src/common/dialog-controller.tsx index 366716a3f..652ab0321 100644 --- a/apps/web/src/common/dialog-controller.tsx +++ b/apps/web/src/common/dialog-controller.tsx @@ -470,8 +470,8 @@ export function showAttachmentsDialog() { )); } -export function showEditProfileDialog(profile?: Profile) { - return showDialog("EditProfileDialog", (Dialog, perform) => ( +export function showEditProfilePictureDialog(profile?: Profile) { + return showDialog("EditProfilePictureDialog", (Dialog, perform) => ( perform(res)} profile={profile} /> )); } diff --git a/apps/web/src/dialogs/edit-profile-dialog.tsx b/apps/web/src/dialogs/edit-profile-picture-dialog.tsx similarity index 50% rename from apps/web/src/dialogs/edit-profile-dialog.tsx rename to apps/web/src/dialogs/edit-profile-picture-dialog.tsx index 2d9bd03b2..c02ed1de2 100644 --- a/apps/web/src/dialogs/edit-profile-dialog.tsx +++ b/apps/web/src/dialogs/edit-profile-picture-dialog.tsx @@ -20,9 +20,8 @@ along with this program. If not, see . import { Perform } from "../common/dialog-controller"; import Dialog from "../components/dialog"; import { Profile } from "@notesnook/core"; -import Field from "../components/field"; import AvatarEditor from "react-avatar-editor"; -import { Avatar, Button, Flex, Slider } from "@theme-ui/components"; +import { Button, Flex, Slider } from "@theme-ui/components"; import { User } from "../components/icons"; import { useRef, useState } from "react"; import { showFilePicker } from "../utils/file-picker"; @@ -30,20 +29,19 @@ import { db } from "../common/db"; import { useStore as useUserStore } from "../stores/user-store"; import { showToast } from "../utils/toast"; -export type EditProfileDialogProps = { +export type EditProfilePictureDialogProps = { onClose: Perform; profile?: Profile; }; -export default function EditProfileDialog(props: EditProfileDialogProps) { +export default function EditProfilePictureDialog( + props: EditProfilePictureDialogProps +) { const { profile } = props; const [profilePicture, setProfilePicture] = useState< File | string | undefined >(profile?.profilePicture); const profileRef = useRef(null); - const [fullName, setFullName] = useState( - profile?.fullName - ); const [scale, setScale] = useState(1); const [isLoading, setIsLoading] = useState(false); const [clearProfilePicture, setClearProfilePicture] = useState(false); @@ -51,7 +49,7 @@ export default function EditProfileDialog(props: EditProfileDialogProps) { return ( props.onClose(false)} positiveButton={{ @@ -61,17 +59,19 @@ export default function EditProfileDialog(props: EditProfileDialogProps) { onClick: async () => { setIsLoading(true); try { - await db.user.setProfile({ - fullName, - profilePicture: profileRef.current - ? profileRef.current - .getImageScaledToCanvas() - .toDataURL("image/jpeg", 1) - : clearProfilePicture + const pic = profileRef.current + ? profileRef.current + .getImageScaledToCanvas() + .toDataURL("image/jpeg", 0.8) + : clearProfilePicture ? undefined - : profile?.profilePicture + : profile?.profilePicture; + await db.user.setProfile({ + profilePicture: pic + }); + useUserStore.setState({ + profile: { ...profile, profilePicture: pic } }); - await useUserStore.getState().refreshUser(); showToast("success", "Profile updated!"); props.onClose(true); } catch (e) { @@ -82,84 +82,86 @@ export default function EditProfileDialog(props: EditProfileDialogProps) { } } }} - width={400} negativeButton={{ text: "Cancel", onClick: () => props.onClose(false) }} > - - + + {profilePicture ? ( + + ) : ( + + + + )} + + {profilePicture ? ( - - ) : ( - - - - )} - - {profilePicture ? ( - - ) : null} - - {profilePicture ? ( - setScale(e.target.valueAsNumber)} - /> + } else setProfilePicture(profile?.profilePicture); + setScale(1); + }} + > + {typeof profilePicture === "string" ? "Clear" : "Reset"} + ) : null} - setFullName(e.target.value)} - /> + {profilePicture ? ( + setScale(e.target.valueAsNumber)} + /> + ) : null} ); diff --git a/apps/web/src/dialogs/index.ts b/apps/web/src/dialogs/index.ts index ff2bc5dc4..2b000d56c 100644 --- a/apps/web/src/dialogs/index.ts +++ b/apps/web/src/dialogs/index.ts @@ -53,7 +53,7 @@ const EmailChangeDialog = React.lazy(() => import("./email-change-dialog")); const AddTagsDialog = React.lazy(() => import("./add-tags-dialog")); const ThemeDetailsDialog = React.lazy(() => import("./theme-details-dialog")); const CreateColorDialog = React.lazy(() => import("./create-color-dialog")); -const EditProfileDialog = React.lazy(() => import("./edit-profile-dialog")); +const EditProfilePictureDialog = React.lazy(() => import("./edit-profile-picture-dialog")); export const Dialogs = { AddNotebookDialog, @@ -83,5 +83,5 @@ export const Dialogs = { SettingsDialog, ThemeDetailsDialog, CreateColorDialog, - EditProfileDialog + EditProfilePictureDialog }; diff --git a/apps/web/src/dialogs/prompt.tsx b/apps/web/src/dialogs/prompt.tsx index ab6f86c4d..1b326ec18 100644 --- a/apps/web/src/dialogs/prompt.tsx +++ b/apps/web/src/dialogs/prompt.tsx @@ -44,7 +44,14 @@ export default function Prompt(props: PromptDialogProps) { }} negativeButton={{ text: "Cancel", onClick: () => props.onClose(false) }} > - + { + if (e.key == "Enter") props.onSave(inputRef.current?.value || ""); + }} + /> ); } diff --git a/apps/web/src/dialogs/settings/components/user-profile.tsx b/apps/web/src/dialogs/settings/components/user-profile.tsx index a978b0aac..61977a447 100644 --- a/apps/web/src/dialogs/settings/components/user-profile.tsx +++ b/apps/web/src/dialogs/settings/components/user-profile.tsx @@ -25,7 +25,13 @@ import { getFormattedDate } from "@notesnook/common"; import { SUBSCRIPTION_STATUS } from "../../../common/constants"; import dayjs from "dayjs"; import { useMemo } from "react"; -import { showEditProfileDialog } from "../../../common/dialog-controller"; +import { + showEditProfileDialog, + showEditProfilePictureDialog, + showPromptDialog +} from "../../../common/dialog-controller"; +import { db } from "../../../common/db"; +import { showToast } from "../../../utils/toast"; export function UserProfile() { const user = useUserStore((store) => store.user); @@ -104,7 +110,11 @@ export function UserProfile() { mr: 2, size: 60, borderRadius: 80, - overflow: "hidden" + overflow: "hidden", + position: "relative", + ":hover #profile-picture-edit": { + visibility: "visible" + } }} > {profile?.profilePicture ? ( @@ -115,6 +125,30 @@ export function UserProfile() { ) : ( )} + { + await showEditProfilePictureDialog(profile); + }} + > + + Edit + + 0 && (isPro || isProCancelled) ? `PRO` : remainingDays > 0 && isTrial - ? "TRIAL" - : isBeta - ? "BETA TESTER" - : "BASIC"} + ? "TRIAL" + : isBeta + ? "BETA TESTER" + : "BASIC"} - {profile?.fullName ? ( - <> - {profile?.fullName} - - {user.email} • Member since{" "} - {getFormattedDate(getObjectIdTimestamp(user.id), "date")} - - - ) : ( - <> - {user.email} - - Member since - {getFormattedDate(getObjectIdTimestamp(user.id), "date")} - - - )} + + {profile?.fullName || "Your full name"}{" "} + { + const fullName = await showPromptDialog({ + title: "Edit your full name", + description: + "Your profile data is 100% end-to-end encrypted.", + defaultValue: profile?.fullName + }); + useUserStore.setState({ + profile: { ...profile, fullName: fullName || undefined } + }); + await db.user.setProfile({ fullName: fullName || undefined }); + showToast("success", "Full name updated!"); + }} + /> + + + {user.email} • Member since{" "} + {getFormattedDate(getObjectIdTimestamp(user.id), "date")} + - + */} ); }