From 4c5d6e7b149e4baf29b3ae7a53cf6744d25c212f Mon Sep 17 00:00:00 2001 From: Marc Seitz Date: Mon, 29 Jan 2024 19:59:53 +1100 Subject: [PATCH] feat: add avatar for visitors --- components/links/links-visitors.tsx | 14 ++---- components/ui/avatar.tsx | 24 +++++----- components/view/toolbar.tsx | 6 --- components/visitors/visitor-avatar.tsx | 64 ++++++++++++++++++++++++++ components/visitors/visitors-table.tsx | 12 ++--- lib/utils.ts | 20 ++++++++ package-lock.json | 8 ++-- package.json | 2 +- 8 files changed, 108 insertions(+), 42 deletions(-) create mode 100644 components/visitors/visitor-avatar.tsx diff --git a/components/links/links-visitors.tsx b/components/links/links-visitors.tsx index 706405f63..ce41efe44 100644 --- a/components/links/links-visitors.tsx +++ b/components/links/links-visitors.tsx @@ -1,8 +1,8 @@ import { durationFormat, timeAgo } from "@/lib/utils"; -import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { TableCell, TableRow } from "@/components/ui/table"; import { useLinkVisits } from "@/lib/swr/use-link"; import { Gauge } from "@/components/ui/gauge"; +import { VisitorAvatar } from "@/components/visitors/visitor-avatar"; export default function LinksVisitors({ linkId, @@ -23,11 +23,7 @@ export default function LinksVisitors({
- - - {view.viewerEmail?.slice(0, 2).toUpperCase()} - - +

@@ -60,11 +56,7 @@ export default function LinksVisitors({

- - - {view.viewerEmail?.slice(0, 2).toUpperCase()} - - +

diff --git a/components/ui/avatar.tsx b/components/ui/avatar.tsx index c97deac2b..7346b5eb0 100644 --- a/components/ui/avatar.tsx +++ b/components/ui/avatar.tsx @@ -1,7 +1,7 @@ -import * as React from "react" -import * as AvatarPrimitive from "@radix-ui/react-avatar" +import * as React from "react"; +import * as AvatarPrimitive from "@radix-ui/react-avatar"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; const Avatar = React.forwardRef< React.ElementRef, @@ -11,12 +11,12 @@ const Avatar = React.forwardRef< ref={ref} className={cn( "relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full", - className + className, )} {...props} /> -)) -Avatar.displayName = AvatarPrimitive.Root.displayName +)); +Avatar.displayName = AvatarPrimitive.Root.displayName; const AvatarImage = React.forwardRef< React.ElementRef, @@ -27,8 +27,8 @@ const AvatarImage = React.forwardRef< className={cn("aspect-square h-full w-full", className)} {...props} /> -)) -AvatarImage.displayName = AvatarPrimitive.Image.displayName +)); +AvatarImage.displayName = AvatarPrimitive.Image.displayName; const AvatarFallback = React.forwardRef< React.ElementRef, @@ -38,11 +38,11 @@ const AvatarFallback = React.forwardRef< ref={ref} className={cn( "flex h-full w-full items-center justify-center rounded-full bg-gray-300 dark:bg-muted", - className + className, )} {...props} /> -)) -AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName +)); +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName; -export { Avatar, AvatarImage, AvatarFallback } +export { Avatar, AvatarImage, AvatarFallback }; diff --git a/components/view/toolbar.tsx b/components/view/toolbar.tsx index 01fba2410..21bd6b120 100644 --- a/components/view/toolbar.tsx +++ b/components/view/toolbar.tsx @@ -1,14 +1,8 @@ import { useEffect, useRef, useState } from "react"; -import { Avatar, AvatarFallback } from "../ui/avatar"; -import UserRound from "../shared/icons/user-round"; import { REACTIONS } from "@/lib/constants"; import GripVertical from "../shared/icons/grip-vertical"; import Draggable from "react-draggable"; -function getKeyByValue(object: { [x: string]: any }, value: any) { - return Object.keys(object).find((key) => object[key] === value); -} - export default function Toolbar({ viewId, pageNumber, diff --git a/components/visitors/visitor-avatar.tsx b/components/visitors/visitor-avatar.tsx new file mode 100644 index 000000000..41a0cf6bc --- /dev/null +++ b/components/visitors/visitor-avatar.tsx @@ -0,0 +1,64 @@ +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; +import { generateGravatarHash } from "@/lib/utils"; + +export const VisitorAvatar = ({ + viewerEmail, +}: { + viewerEmail: string | null; +}) => { + // Convert email string to a simple hash + const hashString = (str: string) => { + let hash = 0; + + for (let i = 0; i < str.length; i++) { + const char = str.charCodeAt(i); + hash = (hash << 5) - hash + char; + hash |= 0; // Convert to 32bit integer + } + return hash; + }; + + // Get the background color from the email number hash + const getColorFromHash = (hash: number): string => { + // An array of colors you want to choose from + const colors = [ + "bg-gray-200/50", + "bg-gray-300/50", + "bg-gray-400/50", + "bg-gray-500/50", + "bg-gray-600/50", + ]; + + // Use the hash to get an index for the colors array + const index = Math.abs(hash) % colors.length; + return colors[index]; + }; + + if (!viewerEmail) { + return ( + + + AN + + + ); + } + + return ( + + + + + {viewerEmail?.slice(0, 2).toUpperCase()} + + + ); +}; diff --git a/components/visitors/visitors-table.tsx b/components/visitors/visitors-table.tsx index 36e211f1c..49f474277 100644 --- a/components/visitors/visitors-table.tsx +++ b/components/visitors/visitors-table.tsx @@ -11,14 +11,14 @@ import { CollapsibleContent, CollapsibleTrigger, } from "@/components/ui/collapsible"; -import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { Gauge } from "@/components/ui/gauge"; import { useDocumentVisits } from "@/lib/swr/use-document"; import { durationFormat, timeAgo } from "@/lib/utils"; -import { Skeleton } from "../ui/skeleton"; -import ChevronDown from "../shared/icons/chevron-down"; +import { Skeleton } from "@/components/ui/skeleton"; +import ChevronDown from "@/components/shared/icons/chevron-down"; import VisitorChart from "./visitor-chart"; +import { VisitorAvatar } from "./visitor-avatar"; export default function VisitorsTable({ numPages }: { numPages: number }) { const { views } = useDocumentVisits(); @@ -48,11 +48,7 @@ export default function VisitorsTable({ numPages }: { numPages: number }) { {/* Name */}

- - - {view.viewerEmail?.slice(0, 2).toUpperCase()} - - +

diff --git a/lib/utils.ts b/lib/utils.ts index 963384164..eaa282e54 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -7,6 +7,7 @@ import { customAlphabet } from "nanoid"; import { ThreadMessage } from "openai/resources/beta/threads/messages/messages"; import { Message } from "ai"; import { upload } from "@vercel/blob/client"; +import crypto from "crypto"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); @@ -383,3 +384,22 @@ export const uploadImage = async (file: File) => { return newBlob.url; }; + +/** + * Generates a Gravatar hash for the given email. + * @param {string} email - The email address. + * @returns {string} The Gravatar hash. + */ +export const generateGravatarHash = (email: string | null): string => { + if (!email) return ""; + // 1. Trim leading and trailing whitespace from an email address + const trimmedEmail = email.trim(); + + // 2. Force all characters to lower-case + const lowerCaseEmail = trimmedEmail.toLowerCase(); + + // 3. Hash the final string with SHA256 + const hash = crypto.createHash("sha256").update(lowerCaseEmail).digest("hex"); + + return hash; +}; diff --git a/package-lock.json b/package-lock.json index 9dbb36ee0..17cd38cb4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -60,7 +60,7 @@ "framer-motion": "^10.16.14", "fuse.js": "^6.6.2", "js-cookie": "^3.0.5", - "lucide-react": "^0.292.0", + "lucide-react": "^0.316.0", "ms": "^2.1.3", "mupdf": "^0.1.1", "nanoid": "^5.0.4", @@ -8817,9 +8817,9 @@ } }, "node_modules/lucide-react": { - "version": "0.292.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.292.0.tgz", - "integrity": "sha512-rRgUkpEHWpa5VCT66YscInCQmQuPCB1RFRzkkxMxg4b+jaL0V12E3riWWR2Sh5OIiUhCwGW/ZExuEO4Az32E6Q==", + "version": "0.316.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.316.0.tgz", + "integrity": "sha512-dTmYX1H4IXsRfVcj/KUxworV6814ApTl7iXaS21AimK2RUEl4j4AfOmqD3VR8phe5V91m4vEJ8tCK4uT1jE5nA==", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0" } diff --git a/package.json b/package.json index a4599519e..d455ed8ca 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "framer-motion": "^10.16.14", "fuse.js": "^6.6.2", "js-cookie": "^3.0.5", - "lucide-react": "^0.292.0", + "lucide-react": "^0.316.0", "ms": "^2.1.3", "mupdf": "^0.1.1", "nanoid": "^5.0.4",