Merge pull request #721 from PaiJi/feat/add-gravatar-support

feat(Profile): support use Gravatar as avatar
This commit is contained in:
Mauricio Siu
2024-11-19 20:29:18 -06:00
committed by GitHub
4 changed files with 26 additions and 4 deletions

View File

@@ -117,7 +117,7 @@ For detailed documentation, visit [docs.dokploy.com](https://docs.dokploy.com).
## Video Tutorial
<a href="https://youtu.be/mznYKPvhcfw">
<img src="https://dokploy.com/banner.webp" alt="Watch the video" width="400" style="border-radius:20px;"/>
<img src="https://dokploy.com/banner.png" alt="Watch the video" width="400" style="border-radius:20px;"/>
</a>
<!-- ## Supported OS

View File

@@ -16,10 +16,11 @@ import {
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { generateSHA256Hash } from "@/lib/utils";
import { api } from "@/utils/api";
import { zodResolver } from "@hookform/resolvers/zod";
import { useTranslation } from "next-i18next";
import { useEffect } from "react";
import { useEffect, useMemo, useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
@@ -53,6 +54,14 @@ export const ProfileForm = () => {
const { data, refetch } = api.auth.get.useQuery();
const { mutateAsync, isLoading } = api.auth.update.useMutation();
const { t } = useTranslation("settings");
const [gravatarHash, setGravatarHash] = useState<string | null>(null);
const availableAvatars = useMemo(() => {
if (gravatarHash === null) return randomImages;
return randomImages.concat([
`https://www.gravatar.com/avatar/${gravatarHash}`,
]);
}, [gravatarHash]);
const form = useForm<Profile>({
defaultValues: {
@@ -70,6 +79,12 @@ export const ProfileForm = () => {
password: "",
image: data?.image || "",
});
if (data.email) {
generateSHA256Hash(data.email).then((hash) => {
setGravatarHash(hash);
});
}
}
form.reset();
}, [form, form.reset, data]);
@@ -154,7 +169,7 @@ export const ProfileForm = () => {
value={field.value}
className="flex flex-row flex-wrap gap-2 max-xl:justify-center"
>
{randomImages.map((image) => (
{availableAvatars.map((image) => (
<FormItem key={image}>
<FormLabel className="[&:has([data-state=checked])>img]:border-primary [&:has([data-state=checked])>img]:border-1 [&:has([data-state=checked])>img]:p-px cursor-pointer">
<FormControl>

View File

@@ -4,3 +4,11 @@ import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
export async function generateSHA256Hash(text: string) {
const encoder = new TextEncoder();
const data = encoder.encode(text);
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
}

View File

@@ -8,7 +8,6 @@ import { createServerSideHelpers } from "@trpc/react-query/server";
import type { GetServerSidePropsContext } from "next";
import React, { type ReactElement } from "react";
import superjson from "superjson";
import nextI18NextConfig from "../../../next-i18next.config.cjs";
const Page = () => {
return (