Starting to restructure user profile around new dev journey

This commit is contained in:
austinkelsay 2024-12-11 18:04:22 -06:00
parent 6c0e09292b
commit c04a42eeb7
No known key found for this signature in database
GPG Key ID: 44CB4EC6D9F2FA02
9 changed files with 351 additions and 275 deletions

View File

@ -157,20 +157,24 @@ const ActivityContributionChart = ({ session }) => {
const weekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; const weekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
return ( return (
<div className="mx-auto py-2 px-8 max-w-[1000px] bg-gray-800 rounded-lg"> <div className="w-full mx-2 bg-gray-800 rounded-lg border border-gray-700 shadow-md">
<div className="flex flex-row justify-between items-center p-4">
<h1 className="text-2xl font-bold text-gray-200">Activity</h1>
<i className="pi pi-question-circle text-2xl cursor-pointer text-gray-200"
data-pr-tooltip="Total number of learning activities on the platform" />
<Tooltip target=".pi-question-circle" position="left" />
</div>
<div className="max-w-[910px] p-4">
<div className="flex justify-between items-center mb-3"> <div className="flex justify-between items-center mb-3">
<h4 className="text-base font-semibold text-gray-200"> <h4 className="text-base font-semibold text-gray-200">
{totalActivities} learning activities in the last year {totalActivities} learning activities in the last year
</h4> </h4>
<i className="pi pi-question-circle text-lg cursor-pointer text-gray-400 hover:text-gray-200"
data-pr-tooltip="Total number of learning activities on the platform" />
<Tooltip target=".pi-question-circle" position="top" />
</div> </div>
<div className="flex"> <div className="flex">
{/* Days of week labels */} {/* Days of week labels */}
<div className="flex flex-col gap-[3px] text-[11px] text-gray-400 pr-3"> <div className="flex flex-col gap-[3px] text-[11px] text-gray-400 pr-3">
{weekDays.map((day, index) => ( {weekDays.map((day, index) => (
<div key={day} className="h-[13px] leading-[13px]"> <div key={day} className="h-[14px] leading-[14px]">
{index % 2 === 0 && day} {index % 2 === 0 && day}
</div> </div>
))} ))}
@ -184,7 +188,7 @@ const ActivityContributionChart = ({ session }) => {
row[weekIndex] && ( row[weekIndex] && (
<div <div
key={`${weekIndex}-${dayIndex}`} key={`${weekIndex}-${dayIndex}`}
className={`w-[13px] h-[13px] ${getColor(row[weekIndex].count)} rounded-[2px] cursor-pointer transition-colors duration-100`} className={`w-[14px] h-[14px] ${getColor(row[weekIndex].count)} rounded-[2px] cursor-pointer transition-colors duration-100`}
title={`${row[weekIndex].date.toDateString()}: ${ title={`${row[weekIndex].date.toDateString()}: ${
row[weekIndex].count > 0 row[weekIndex].count > 0
? `${row[weekIndex].count} activit${row[weekIndex].count !== 1 ? 'ies' : 'y'}` ? `${row[weekIndex].count} activit${row[weekIndex].count !== 1 ? 'ies' : 'y'}`
@ -214,15 +218,16 @@ const ActivityContributionChart = ({ session }) => {
<div className="text-[11px] text-gray-400 flex items-center justify-end"> <div className="text-[11px] text-gray-400 flex items-center justify-end">
<span className="mr-2">Less</span> <span className="mr-2">Less</span>
<div className="flex gap-[3px]"> <div className="flex gap-[3px]">
<div className="w-[13px] h-[13px] bg-gray-100 rounded-[2px]"></div> <div className="w-[14px] h-[14px] bg-gray-100 rounded-[2px]"></div>
<div className="w-[13px] h-[13px] bg-green-300 rounded-[2px]"></div> <div className="w-[14px] h-[14px] bg-green-300 rounded-[2px]"></div>
<div className="w-[13px] h-[13px] bg-green-400 rounded-[2px]"></div> <div className="w-[14px] h-[14px] bg-green-400 rounded-[2px]"></div>
<div className="w-[13px] h-[13px] bg-green-600 rounded-[2px]"></div> <div className="w-[14px] h-[14px] bg-green-600 rounded-[2px]"></div>
<div className="w-[13px] h-[13px] bg-green-700 rounded-[2px]"></div> <div className="w-[14px] h-[14px] bg-green-700 rounded-[2px]"></div>
</div> </div>
<span className="ml-2">More</span> <span className="ml-2">More</span>
</div> </div>
</div> </div>
</div>
); );
}; };

View File

@ -181,16 +181,20 @@ const CombinedContributionChart = ({ session }) => {
}, [calendar]); }, [calendar]);
return ( return (
<div className="mx-auto py-2 px-8 max-w-[1000px] bg-gray-800 rounded-lg"> <div className="w-full mx-2 bg-gray-800 rounded-lg border border-gray-700 shadow-md">
{(isLoading || isFetching) && <p>Loading contribution data... ({totalContributions} total contributions / activities fetched)</p>} <div className="flex flex-row justify-between items-center p-4">
<h1 className="text-2xl font-bold text-gray-200">Activity</h1>
<i className="pi pi-question-circle text-2xl cursor-pointer text-gray-200"
data-pr-tooltip="Combined total of GitHub commits and learning activities (starting/completing courses and lessons)" />
<Tooltip target=".pi-question-circle" position="left" />
</div>
<div className="max-w-[910px] p-4">
{(isLoading || isFetching) && <h4 className="text-base font-semibold text-gray-200 mb-3">Loading contribution data... ({totalContributions} total contributions / activities fetched)</h4>}
{!isLoading && !isFetching && {!isLoading && !isFetching &&
<div className="flex justify-between items-center mb-3"> <div className="flex justify-between items-center mb-3">
<h4 className="text-base font-semibold text-gray-200"> <h4 className="text-base font-semibold text-gray-200">
{totalContributions} total contributions / activities in the last year {totalContributions} total contributions / activities in the last year
</h4> </h4>
<i className="pi pi-question-circle text-lg cursor-pointer text-gray-400 hover:text-gray-200"
data-pr-tooltip="Combined total of GitHub commits and learning activities (starting/completing courses and lessons)" />
<Tooltip target=".pi-question-circle" position="top" />
</div> </div>
} }
<div className="flex"> <div className="flex">
@ -211,9 +215,8 @@ const CombinedContributionChart = ({ session }) => {
row[weekIndex] && ( row[weekIndex] && (
<div <div
key={`${weekIndex}-${dayIndex}`} key={`${weekIndex}-${dayIndex}`}
className={`w-[13px] h-[13px] ${getColor(row[weekIndex].count)} rounded-[2px] cursor-pointer transition-colors duration-100`} className={`w-[14px] h-[14px] ${getColor(row[weekIndex].count)} rounded-[2px] cursor-pointer transition-colors duration-100`}
title={`${row[weekIndex].date.toDateString()}: ${ title={`${row[weekIndex].date.toDateString()}: ${[
[
row[weekIndex].githubCount > 0 ? `${row[weekIndex].githubCount} contribution${row[weekIndex].githubCount !== 1 ? 's' : ''}` : '', row[weekIndex].githubCount > 0 ? `${row[weekIndex].githubCount} contribution${row[weekIndex].githubCount !== 1 ? 's' : ''}` : '',
row[weekIndex].activityCount > 0 ? `${row[weekIndex].activityCount} activit${row[weekIndex].activityCount !== 1 ? 'ies' : 'y'}` : '' row[weekIndex].activityCount > 0 ? `${row[weekIndex].activityCount} activit${row[weekIndex].activityCount !== 1 ? 'ies' : 'y'}` : ''
].filter(Boolean).join(' & ') || 'No contributions or activities' ].filter(Boolean).join(' & ') || 'No contributions or activities'
@ -241,15 +244,16 @@ const CombinedContributionChart = ({ session }) => {
<div className="text-[11px] text-gray-400 flex items-center justify-end"> <div className="text-[11px] text-gray-400 flex items-center justify-end">
<span className="mr-2">Less</span> <span className="mr-2">Less</span>
<div className="flex gap-[3px]"> <div className="flex gap-[3px]">
<div className="w-[13px] h-[13px] bg-gray-100 rounded-[2px]"></div> <div className="w-[14px] h-[14px] bg-gray-100 rounded-[2px]"></div>
<div className="w-[13px] h-[13px] bg-green-300 rounded-[2px]"></div> <div className="w-[14px] h-[14px] bg-green-300 rounded-[2px]"></div>
<div className="w-[13px] h-[13px] bg-green-400 rounded-[2px]"></div> <div className="w-[14px] h-[14px] bg-green-400 rounded-[2px]"></div>
<div className="w-[13px] h-[13px] bg-green-600 rounded-[2px]"></div> <div className="w-[14px] h-[14px] bg-green-600 rounded-[2px]"></div>
<div className="w-[13px] h-[13px] bg-green-700 rounded-[2px]"></div> <div className="w-[14px] h-[14px] bg-green-700 rounded-[2px]"></div>
</div> </div>
<span className="ml-2">More</span> <span className="ml-2">More</span>
</div> </div>
</div> </div>
</div>
); );
}; };

View File

@ -129,7 +129,7 @@ const UserProgressTable = ({ session, ndk, windowWidth }) => {
emptyMessage="No Courses or Milestones completed" emptyMessage="No Courses or Milestones completed"
value={prepareProgressData()} value={prepareProgressData()}
header={header} header={header}
style={{ maxWidth: windowWidth < 768 ? "100%" : "90%", margin: "0 auto" }} style={{ margin: 8, width: "100%", borderRadius: "8px", border: "1px solid #333", boxShadow: "0 0 10px 0 rgba(0, 0, 0, 0.1)" }}
pt={{ pt={{
wrapper: { wrapper: {
className: "rounded-b-lg shadow-md" className: "rounded-b-lg shadow-md"

View File

@ -53,7 +53,7 @@ const UserPurchaseTable = ({ session, windowWidth }) => {
emptyMessage="No purchases" emptyMessage="No purchases"
value={session.user?.purchased} value={session.user?.purchased}
header={purchasesHeader} header={purchasesHeader}
style={{ maxWidth: windowWidth < 768 ? "100%" : "90%", margin: "0 auto", marginTop: "2rem" }} style={{ margin: 8, width: "100%", borderRadius: "8px", border: "1px solid #333", boxShadow: "0 0 10px 0 rgba(0, 0, 0, 0.1)" }}
pt={{ pt={{
wrapper: { wrapper: {
className: "rounded-b-lg shadow-md" className: "rounded-b-lg shadow-md"

View File

@ -37,15 +37,16 @@ const UserProfile = () => {
<h1 className="text-3xl font-bold mb-6">Profile</h1> <h1 className="text-3xl font-bold mb-6">Profile</h1>
) )
} }
<div className="w-full flex flex-col justify-center mx-auto"> <div className="w-full flex flex-row">
<UserProfileCard user={user} /> <UserProfileCard user={user} />
<div className="w-full flex flex-col justify-center mx-auto">
{account && account?.provider === "github" ? ( {account && account?.provider === "github" ? (
<CombinedContributionChart session={session} /> <CombinedContributionChart session={session} />
) : ( ) : (
<ActivityContributionChart session={session} /> <ActivityContributionChart session={session} />
)} )}
<UserProgress /> <UserProgress />
</div>
<UserProgressTable <UserProgressTable
session={session} session={session}
ndk={ndk} ndk={ndk}
@ -56,6 +57,8 @@ const UserProfile = () => {
windowWidth={windowWidth} windowWidth={windowWidth}
/> />
</div> </div>
</div>
</div>
) )
); );
}; };

View File

@ -41,8 +41,8 @@ const UserProfileCard = ({ user }) => {
]; ];
return ( return (
<> <div className="lg:w-1/4 bg-gray-800 rounded-lg p-4 border border-gray-700 shadow-md">
<div className="relative flex w-full items-center justify-center"> <div className="flex flex-row gap-4">
<Image <Image
alt="user's avatar" alt="user's avatar"
src={returnImageProxy(user.avatar, user?.pubkey || "")} src={returnImageProxy(user.avatar, user?.pubkey || "")}
@ -50,7 +50,9 @@ const UserProfileCard = ({ user }) => {
height={100} height={100}
className="rounded-full my-4" className="rounded-full my-4"
/> />
<div className="absolute top-8 right-80 max-tab:right-20 max-mob:left-0">
<div className="flex flex-col gap-2 pt-4 w-full relative">
<div className="absolute top-0 right-0">
<i <i
className="pi pi-ellipsis-h text-2xl cursor-pointer" className="pi pi-ellipsis-h text-2xl cursor-pointer"
onClick={(e) => menu.current.toggle(e)} onClick={(e) => menu.current.toggle(e)}
@ -62,29 +64,58 @@ const UserProfileCard = ({ user }) => {
id="profile-options-menu" id="profile-options-menu"
/> />
</div> </div>
</div> <h3 className="self-start">
<h1 className="text-center text-2xl my-2">
{user.username || user?.name || user?.email || "Anon"} {user.username || user?.name || user?.email || "Anon"}
</h1> </h3>
{user.pubkey && ( {
<h2 className="text-center text-xl my-2 truncate max-tab:px-4 max-mob:px-4"> user?.pubkey && (
<Tooltip target=".pubkey-tooltip" content={"this is your nostr npub"} /> <div className="flex flex-row gap-2">
{nip19.npubEncode(user.pubkey)} <i className="pi pi-question-circle text-xl pubkey-tooltip" /> <p className="truncate">
</h2> {nip19.npubEncode(user.pubkey).slice(0, 12)}...
</p>
<Tooltip target=".pubkey-tooltip" content={"this is your account pubkey"} />
<i className="pi pi-question-circle pubkey-tooltip text-xs cursor-pointer" />
</div>
)
}
{user?.createdAt && (
<p className="truncate">
Joined: {new Date(user.createdAt).toLocaleDateString()}
</p>
)} )}
{user?.lightningAddress && ( </div>
<h3 className="w-fit mx-auto text-center text-xl my-2 bg-gray-800 rounded-lg p-4"> </div>
<div className="flex flex-col gap-2">
{user?.lightningAddress ? (
<h4 className="my-2 bg-gray-900 rounded-lg p-4">
<span className="font-bold">Lightning Address:</span> {user.lightningAddress.name}@plebdevs.com <i className="pi pi-copy cursor-pointer hover:text-gray-400" onClick={() => copyToClipboard(user.lightningAddress.name + "@plebdevs.com")} /> <span className="font-bold">Lightning Address:</span> {user.lightningAddress.name}@plebdevs.com <i className="pi pi-copy cursor-pointer hover:text-gray-400" onClick={() => copyToClipboard(user.lightningAddress.name + "@plebdevs.com")} />
</h3> </h4>
) : (
<div className="flex flex-row justify-between my-2 bg-gray-900 rounded-lg p-4">
<h4 >
<span className="font-bold">Lightning Address:</span> None
</h4>
{/* todo: add tooltip */}
<Tooltip target=".lightning-address-tooltip" content={"this is your account lightning address"} />
<i className="pi pi-question-circle lightning-address-tooltip text-xs cursor-pointer" />
</div>
)} )}
{user?.nip05 && ( {user?.nip05 ? (
<h3 className="w-fit mx-auto text-center text-xl my-2 bg-gray-800 rounded-lg p-4"> <h4 className="my-2 bg-gray-900 rounded-lg p-4">
<span className="font-bold">NIP-05:</span> {user.nip05.name}@plebdevs.com <i className="pi pi-copy cursor-pointer hover:text-gray-400" onClick={() => copyToClipboard(user.nip05.name + "@plebdevs.com")} /> <span className="font-bold">NIP-05:</span> {user.nip05.name}@plebdevs.com <i className="pi pi-copy cursor-pointer hover:text-gray-400" onClick={() => copyToClipboard(user.nip05.name + "@plebdevs.com")} />
</h3> </h4>
) : (
<div className="flex flex-row justify-between my-2 bg-gray-900 rounded-lg p-4">
<h4>
<span className="font-bold">NIP-05:</span> None
</h4>
{/* todo: add tooltip */}
<Tooltip target=".nip05-tooltip" content={"this is your account nip05"} />
<i className="pi pi-question-circle nip05-tooltip text-xs cursor-pointer" />
</div>
)} )}
</> </div>
</div>
); );
}; };

View File

@ -6,6 +6,7 @@ import { useRouter } from 'next/router';
import GenericButton from '@/components/buttons/GenericButton'; import GenericButton from '@/components/buttons/GenericButton';
import UserBadges from '@/components/profile/UserBadges'; import UserBadges from '@/components/profile/UserBadges';
import UserProgressFlow from './UserProgressFlow'; import UserProgressFlow from './UserProgressFlow';
import { Tooltip } from 'primereact/tooltip';
const allTasks = [ const allTasks = [
{ {
@ -56,12 +57,14 @@ const UserProgress = () => {
const [completedCourses, setCompletedCourses] = useState([]); const [completedCourses, setCompletedCourses] = useState([]);
const [tasks, setTasks] = useState([]); const [tasks, setTasks] = useState([]);
const [showBadges, setShowBadges] = useState(false); const [showBadges, setShowBadges] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const router = useRouter(); const router = useRouter();
const { data: session, update } = useSession(); const { data: session, update } = useSession();
useEffect(() => { useEffect(() => {
if (session?.user) { if (session?.user) {
setIsLoading(true);
const user = session.user; const user = session.user;
const ids = user?.userCourses?.map(course => course?.completed ? course.courseId : null).filter(id => id !== null); const ids = user?.userCourses?.map(course => course?.completed ? course.courseId : null).filter(id => id !== null);
if (ids && ids.length > 0) { if (ids && ids.length > 0) {
@ -74,6 +77,7 @@ const UserProgress = () => {
calculateProgress([]); calculateProgress([]);
calculateCurrentTier([]); calculateCurrentTier([]);
} }
setIsLoading(false);
} }
}, [session]); }, [session]);
@ -173,8 +177,13 @@ const UserProgress = () => {
}; };
return ( return (
<div className="bg-gray-800 rounded-3xl p-6 w-[940px] max-mob:w-full max-tab:w-full mx-auto my-8"> <div className="bg-gray-800 rounded-lg p-4 m-2 w-full border border-gray-700 shadow-md">
<div className="flex flex-row justify-between items-center">
<h1 className="text-3xl font-bold text-white mb-2">Dev Journey</h1> <h1 className="text-3xl font-bold text-white mb-2">Dev Journey</h1>
<i className="pi pi-question-circle text-2xl cursor-pointer text-gray-200"
data-pr-tooltip="Track your progress from Pleb to Plebdev" />
<Tooltip target=".pi-question-circle" position="left" />
</div>
<p className="text-gray-400 mb-4">Track your progress from Pleb to Plebdev</p> <p className="text-gray-400 mb-4">Track your progress from Pleb to Plebdev</p>
<div className="flex justify-between items-center mb-2"> <div className="flex justify-between items-center mb-2">
@ -200,11 +209,9 @@ const UserProgress = () => {
)} )}
</div> </div>
<div className="mb-6"> <div className="flex gap-6 mb-6">
<UserProgressFlow tasks={tasks} /> <div className="w-1/2">
</div> <ul className="space-y-6 pt-2">
<ul className="space-y-4 mb-6">
{tasks.map((task, index) => ( {tasks.map((task, index) => (
<li key={index}> <li key={index}>
<Accordion <Accordion
@ -246,7 +253,7 @@ const UserProgress = () => {
{task.subTasks && ( {task.subTasks && (
<ul className="space-y-2"> <ul className="space-y-2">
{task.subTasks.map((subTask, subIndex) => ( {task.subTasks.map((subTask, subIndex) => (
<li key={subIndex} className="flex items-center"> <li key={subIndex} className="flex items-center pl-[28px]">
{subTask.completed ? ( {subTask.completed ? (
<div className="w-4 h-4 bg-green-500 rounded-full flex items-center justify-center mr-3"> <div className="w-4 h-4 bg-green-500 rounded-full flex items-center justify-center mr-3">
<i className="pi pi-check text-white text-sm"></i> <i className="pi pi-check text-white text-sm"></i>
@ -282,6 +289,18 @@ const UserProgress = () => {
</li> </li>
))} ))}
</ul> </ul>
</div>
<div className="w-1/2">
{isLoading ? (
<div className="h-[400px] bg-gray-800 rounded-3xl flex items-center justify-center">
<i className="pi pi-spin pi-spinner text-4xl text-gray-600"></i>
</div>
) : (
<UserProgressFlow tasks={tasks} />
)}
</div>
</div>
<button <button
className="w-full py-3 bg-gradient-to-r from-blue-500 to-purple-600 text-white rounded-full font-semibold" className="w-full py-3 bg-gradient-to-r from-blue-500 to-purple-600 text-white rounded-full font-semibold"

View File

@ -3,19 +3,20 @@ import ReactFlow, {
Background, Background,
Handle, Handle,
Position, Position,
Controls
} from 'reactflow'; } from 'reactflow';
import 'reactflow/dist/style.css'; import 'reactflow/dist/style.css';
const CustomNode = ({ data }) => ( const CustomNode = ({ data }) => (
<div className={`px-4 py-2 rounded-lg shadow-md w-48 text-center transition-all duration-300 ${ <div className={`px-4 py-2 rounded-lg shadow-md w-48 text-center transition-all duration-300 ${
data.completed data.completed
? 'bg-green-500 text-white border-2 border-green-400' ? 'bg-green-500 text-white border-2 border-green-400 bg-opacity-50'
: 'bg-gray-700 text-gray-300 border-2 border-gray-600' : 'bg-gray-700 text-gray-300 border-2 border-gray-600 bg-opacity-50'
}`}> }`}>
<Handle type="target" position={Position.Top} /> <Handle type="target" position={Position.Top} />
<div className="flex items-center justify-center gap-2"> <div className="flex items-center justify-center gap-2">
{data.completed ? ( {data.completed ? (
<div className="w-5 h-5 bg-green-400 rounded-full flex items-center justify-center"> <div className="w-5 h-5 bg-green-500 rounded-full flex items-center justify-center">
<i className="pi pi-check text-white text-sm"></i> <i className="pi pi-check text-white text-sm"></i>
</div> </div>
) : ( ) : (
@ -37,6 +38,13 @@ const nodeTypes = {
}; };
const UserProgressFlow = ({ tasks }) => { const UserProgressFlow = ({ tasks }) => {
const [mounted, setMounted] = React.useState(false);
React.useEffect(() => {
setMounted(true);
return () => setMounted(false);
}, []);
const nodes = [ const nodes = [
{ {
id: '1', id: '1',
@ -113,6 +121,8 @@ const UserProgressFlow = ({ tasks }) => {
}, },
]; ];
if (!mounted) return <div style={{ height: 400 }} className="bg-gray-800 rounded-3xl" />;
return ( return (
<div style={{ height: 400 }} className="bg-gray-800 rounded-3xl"> <div style={{ height: 400 }} className="bg-gray-800 rounded-3xl">
<ReactFlow <ReactFlow
@ -120,16 +130,20 @@ const UserProgressFlow = ({ tasks }) => {
edges={edges} edges={edges}
nodeTypes={nodeTypes} nodeTypes={nodeTypes}
fitView fitView
preventScrolling
zoomOnScroll={false}
panOnScroll={false}
nodesDraggable={false} nodesDraggable={false}
nodesConnectable={false} nodesConnectable={false}
elementsSelectable={false} elementsSelectable={false}
minZoom={0.5} panOnDrag={false}
maxZoom={2} zoomOnScroll={false}
panOnScroll={false}
selectNodesOnDrag={false}
preventScrolling
minZoom={1}
maxZoom={1}
defaultViewport={{ x: 0, y: 0, zoom: 1 }}
> >
<Background color="#4a5568" gap={16} /> <Background color="#4a5568" gap={16} />
{/* <Controls position="top-right" /> */}
</ReactFlow> </ReactFlow>
</div> </div>
); );

View File

@ -231,7 +231,8 @@ export const authOptions = {
purchased: fullUser.purchased, purchased: fullUser.purchased,
nip05: fullUser.nip05, nip05: fullUser.nip05,
lightningAddress: fullUser.lightningAddress, lightningAddress: fullUser.lightningAddress,
githubUsername: token.githubUsername githubUsername: token.githubUsername,
createdAt: fullUser.createdAt
}; };
// Add GitHub account info to session if it exists // Add GitHub account info to session if it exists
@ -249,7 +250,6 @@ export const authOptions = {
return session; return session;
}, },
async jwt({ token, user, account, profile, session }) { async jwt({ token, user, account, profile, session }) {
console.log("JWT", token, user, account, profile, session);
// If we are linking a github account to an existing email or anon account (we have privkey) // If we are linking a github account to an existing email or anon account (we have privkey)
if (account?.provider === "github" && user?.id && user?.pubkey && user?.privkey) { if (account?.provider === "github" && user?.id && user?.pubkey && user?.privkey) {
try { try {