Can now link anon account with github, improvements to dev journey, maybe broke some auth stufff

This commit is contained in:
austinkelsay 2024-12-06 18:47:05 -06:00
parent 003eecb551
commit 004d388c82
No known key found for this signature in database
GPG Key ID: 44CB4EC6D9F2FA02
3 changed files with 188 additions and 64 deletions

View File

@ -65,9 +65,6 @@ const ActivityContributionChart = ({ session }) => {
}
});
console.log('All Learning Activities:', allActivities);
console.log('Activities by Date:', activityData);
setContributionData(activityData);
setTotalActivities(Object.values(activityData).reduce((a, b) => a + b, 0));
@ -112,15 +109,6 @@ const ActivityContributionChart = ({ session }) => {
const dateString = currentDate.toLocaleDateString('en-CA'); // YYYY-MM-DD format
const activityCount = contributionData[dateString] || 0;
// Debug log
if (activityCount > 0) {
console.log('Found activity:', {
date: currentDate.toDateString(),
dateString,
activityCount
});
}
calendar[weekDay].push({
date: new Date(currentDate),
count: activityCount

View File

@ -1,7 +1,8 @@
import React, { useState, useEffect } from 'react';
import { ProgressBar } from 'primereact/progressbar';
import { Accordion, AccordionTab } from 'primereact/accordion';
import { useSession } from 'next-auth/react';
import { useSession, signIn } from 'next-auth/react';
import GenericButton from '@/components/buttons/GenericButton';
const allTasks = [
{
@ -18,11 +19,10 @@ const allTasks = [
status: 'PlebDevs Starter',
completed: false,
tier: 'New Dev',
courseId: "f538f5c5-1a72-4804-8eb1-3f05cea64874",
// courseId: "f538f5c5-1a72-4804-8eb1-3f05cea64874",
courseId: "f6daa88a-53d6-4901-8dbd-d2203a05b7ab",
subTasks: [
{ status: 'Connect GitHub', completed: false },
{ status: 'Create First GitHub Repo', completed: false },
{ status: 'Push Commit', completed: false }
{ status: 'Complete the course', completed: false },
]
},
{
@ -32,7 +32,7 @@ const allTasks = [
courseId: 'f73c37f4-df2e-4f7d-a838-dce568c76136',
subTasks: [
{ status: 'Complete the course', completed: false },
{ status: 'Submit Link to completed project', completed: false },
{ status: 'Select your completed project', completed: false },
]
},
{
@ -42,19 +42,19 @@ const allTasks = [
courseId: 'f6825391-831c-44da-904a-9ac3d149b7be',
subTasks: [
{ status: 'Complete the course', completed: false },
{ status: 'Submit Link to completed project', completed: false },
{ status: 'Select your completed project', completed: false },
]
},
];
const UserProgress = () => {
const [progress, setProgress] = useState(0);
const [currentTier, setCurrentTier] = useState('Pleb');
const [currentTier, setCurrentTier] = useState(null);
const [expandedItems, setExpandedItems] = useState({});
const [completedCourses, setCompletedCourses] = useState([]);
const [tasks, setTasks] = useState([]);
const { data: session } = useSession();
const { data: session, update } = useSession();
useEffect(() => {
if (session?.user) {
@ -74,20 +74,37 @@ const UserProgress = () => {
}, [session]);
const generateTasks = (completedCourseIds) => {
const updatedTasks = allTasks.map(task => ({
...task,
completed: task.courseId === null || completedCourseIds.includes(task.courseId),
subTasks: task.subTasks ? task.subTasks.map(subTask => ({
...subTask,
completed: completedCourseIds.includes(task.courseId)
})) : undefined
}));
const updatedTasks = allTasks.map(task => {
if (task.status === 'Connect GitHub') {
return {
...task,
completed: session?.githubProfile ? true : false,
subTasks: task.subTasks ? task.subTasks.map(subTask => ({
...subTask,
completed: session?.githubProfile ? true : false
})) : undefined
};
}
return {
...task,
completed: task.courseId === null || completedCourseIds.includes(task.courseId),
subTasks: task.subTasks ? task.subTasks.map(subTask => ({
...subTask,
completed: completedCourseIds.includes(task.courseId)
})) : undefined
};
});
setTasks(updatedTasks);
};
const calculateProgress = (completedCourseIds) => {
let progressValue = 25;
let progressValue = 0;
if (session?.githubProfile) {
progressValue += 25;
}
const remainingTasks = allTasks.slice(1);
remainingTasks.forEach(task => {
@ -100,16 +117,16 @@ const UserProgress = () => {
};
const calculateCurrentTier = (completedCourseIds) => {
let tier = 'Pleb';
let tier = null;
if (completedCourseIds.includes("f538f5c5-1a72-4804-8eb1-3f05cea64874")) {
tier = 'New Dev';
}
if (completedCourseIds.includes("f73c37f4-df2e-4f7d-a838-dce568c76136")) {
tier = 'Junior Dev';
}
if (completedCourseIds.includes("f6825391-831c-44da-904a-9ac3d149b7be")) {
tier = 'Plebdev';
} else if (completedCourseIds.includes("f73c37f4-df2e-4f7d-a838-dce568c76136")) {
tier = 'Junior Dev';
} else if (completedCourseIds.includes("f6daa88a-53d6-4901-8dbd-d2203a05b7ab")) {
tier = 'New Dev';
} else if (session?.githubProfile) {
tier = 'Pleb';
}
setCurrentTier(tier);
@ -122,6 +139,12 @@ const UserProgress = () => {
}));
};
const handleGitHubLink = async () => {
if (!session?.user?.id) return;
await signIn("github");
};
return (
<div className="bg-gray-800 rounded-3xl p-6 w-[500px] max-mob:w-full max-tab:w-full mx-auto my-8">
<h1 className="text-3xl font-bold text-white mb-2">Dev Journey</h1>
@ -139,30 +162,92 @@ const UserProgress = () => {
<div className="mb-6">
<span className="text-white text-lg font-semibold">Current Tier: </span>
<span className="bg-green-500 text-white px-3 py-1 rounded-full">{currentTier}</span>
{currentTier ? (
<span className="bg-green-500 text-white px-3 py-1 rounded-full">
{currentTier}
</span>
) : (
<span className="bg-gray-700 text-gray-400 px-3 py-1 rounded-full text-sm">
Not Started
</span>
)}
</div>
<ul className="space-y-4 mb-6">
{tasks.map((task, index) => (
console.log(task),
<li key={index}>
<div className="flex items-center justify-between">
<div className="flex items-center">
{task.completed ? (
<div className="w-6 h-6 bg-green-500 rounded-full flex items-center justify-center mr-3">
<i className="pi pi-check text-white text-lg"></i>
<Accordion
activeIndex={expandedItems[index] ? 0 : null}
onTabChange={(e) => handleAccordionChange(index, e.index === 0)}
>
<AccordionTab
header={
<div className="flex items-center justify-between w-full">
<div className="flex items-center">
{task.completed ? (
<div className="w-6 h-6 bg-green-500 rounded-full flex items-center justify-center mr-3">
<i className="pi pi-check text-white text-lg"></i>
</div>
) : (
<div className="w-6 h-6 bg-gray-700 rounded-full flex items-center justify-center mr-3">
<i className="pi pi-info-circle text-white text-lg"></i>
</div>
)}
<span className={`text-lg ${task.completed ? 'text-white' : 'text-gray-400'}`}>{task.status}</span>
</div>
<span className="bg-blue-500 text-white text-xs px-2 py-1 rounded-full w-20 text-center">
{task.tier}
</span>
</div>
) : (
<div className="w-6 h-6 bg-gray-700 rounded-full flex items-center justify-center mr-3">
<i className="pi pi-info-circle text-white text-lg"></i>
}
>
{task.status === 'Connect GitHub' && !task.completed && (
<div className="mb-4">
<GenericButton
label="Connect GitHub"
icon="pi pi-github"
onClick={handleGitHubLink}
className="w-fit bg-[#24292e] hover:bg-[#2f363d] border border-[#f8f8ff] text-[#f8f8ff] font-semibold"
rounded
/>
</div>
)}
<span className={`text-lg ${task.completed ? 'text-white' : 'text-gray-400'}`}>{task.status}</span>
</div>
<span className="bg-blue-500 text-white text-xs px-2 py-1 rounded-full w-20 text-center">
{task.tier}
</span>
</div>
{task.subTasks && (
<ul className="space-y-2">
{task.subTasks.map((subTask, subIndex) => (
<li key={subIndex} className="flex items-center">
{subTask.completed ? (
<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>
</div>
) : (
<div className="w-4 h-4 bg-gray-700 rounded-full flex items-center justify-center mr-3">
<i className="pi pi-info-circle text-white text-sm"></i>
</div>
)}
<span className={`${subTask.completed ? 'text-white' : 'text-gray-400'}`}>
{subTask.status}
</span>
</li>
))}
</ul>
)}
{task.courseId && (
<div className="mt-2 flex justify-end">
<GenericButton
icon="pi pi-external-link"
onClick={() => router.push(`/courses/${task.courseId}`)}
tooltip="View Course"
tooltipOptions={{
position: "top"
}}
outlined
size="small"
/>
</div>
)}
</AccordionTab>
</Accordion>
</li>
))}
</ul>

View File

@ -12,8 +12,6 @@ import { createRole } from "@/db/models/roleModels";
import appConfig from "@/config/appConfig";
import GithubProvider from "next-auth/providers/github";
import NDK from "@nostr-dev-kit/ndk";
import { SimplePool } from "nostr-tools";
import { finalizeEvent } from "nostr-tools";
// todo: currently email accounts ephemeral privkey gets saved to db but not anon user, is this required at all given the newer auth setup?
@ -40,7 +38,7 @@ const authorize = async (pubkey) => {
dbUser = await getUserByPubkey(pubkey);
}
} else if (fields.username !== dbUser.username) {
const updatedUser = await updateUser(dbUser.id, { username: fields.username });
const updatedUser = await updateUser(dbUser.id, { username: fields.username, name: fields.username });
if (updatedUser) {
dbUser = await getUserByPubkey(pubkey);
}
@ -53,7 +51,7 @@ const authorize = async (pubkey) => {
// Create user
if (profile) {
const fields = await findKind0Fields(profile);
const payload = { pubkey, username: fields.username, avatar: fields.avatar };
const payload = { pubkey, username: fields.username, avatar: fields.avatar, name: fields.username };
if (appConfig.authorPubkeys.includes(pubkey)) {
// create a new author role for this user
@ -168,8 +166,32 @@ export const authOptions = {
// Create new user if not exists
dbUser = await createUser({
pubkey: pubkey,
username: pubkey.slice(0, 8), // Use first 8 characters of pubkey as username
username: pubkey.slice(0, 8),
});
} else {
// Check if this user has a linked GitHub account
const githubAccount = await prisma.account.findFirst({
where: {
userId: dbUser.id,
provider: 'github'
},
include: {
user: true
}
});
if (githubAccount) {
// Return the user with GitHub provider information
return {
...dbUser,
pubkey,
privkey,
// Add these fields to switch to GitHub provider
provider: 'github',
type: 'oauth',
providerAccountId: githubAccount.providerAccountId
};
}
}
// Return user object with pubkey and privkey
@ -179,11 +201,23 @@ export const authOptions = {
],
callbacks: {
async jwt({ token, user, account, trigger, profile }) {
// Add account and profile to token if they exist
if (account) {
token.account = account;
if (user?.provider === 'github') {
// User has a linked GitHub account, use that as the primary provider
token.account = {
provider: 'github',
type: 'oauth',
providerAccountId: user.providerAccountId
};
// Add GitHub profile information
token.githubProfile = {
login: user.username,
avatar_url: user.avatar,
email: user.email
};
} else if (account) {
// Store GitHub-specific information
if (account.provider === 'github') {
token.account = account;
token.githubProfile = {
login: profile?.login,
avatar_url: profile?.avatar_url,
@ -246,6 +280,22 @@ export const authOptions = {
user.email = token.githubProfile?.email;
}
if (account && account.provider === "github" && user?.id && user?.pubkey) {
// we are linking a github account to an existing account
const updatedUser = await updateUser(user.id, {
name: profile?.login,
username: profile?.login,
avatar: profile?.avatar_url,
email: profile?.email,
privkey: user.privkey || token.privkey, // Preserve the existing privkey
image: profile?.avatar_url, // Also save to image field
});
if (updatedUser) {
user = await getUserById(user.id);
}
}
if (user) {
token.user = user;
if (user.pubkey && user.privkey) {
@ -269,9 +319,10 @@ export const authOptions = {
// Override only the GitHub-specific fields
session.user = {
...dbUser, // This includes role, purchases, userCourses, userLessons, etc.
username: token.githubProfile.login,
avatar: token.githubProfile.avatar_url,
email: token.githubProfile.email
username: token.githubProfile?.login,
name: token.githubProfile?.login,
avatar: token.githubProfile?.avatar_url,
email: token.githubProfile?.email
};
} else {
// For non-GitHub sessions, use the existing token.user