feat: add referral user count

feat/referral
Arik Chakma 3 weeks ago
parent f20334b0de
commit 9d9d70de76
  1. 71
      src/components/AccountStreak/AccountStreak.tsx
  2. 19
      src/components/AuthenticationFlow/EmailSignupForm.tsx
  3. 1
      src/stores/streak.ts

@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from 'react';
import { isLoggedIn } from '../../lib/jwt';
import { httpGet } from '../../lib/http';
import { useToast } from '../../hooks/use-toast';
import { Flame, X, Zap, ZapOff } from 'lucide-react';
import { Flame, UserPlus, X, Zap, ZapOff } from 'lucide-react';
import { useOutsideClick } from '../../hooks/use-outside-click';
import { StreakDay } from './StreakDay';
import {
@ -11,15 +11,9 @@ import {
} from '../../stores/page.ts';
import { useStore } from '@nanostores/react';
import { cn } from '../../lib/classname.ts';
import { $accountStreak } from '../../stores/streak.ts';
type StreakResponse = {
count: number;
longestCount: number;
previousCount?: number | null;
firstVisitAt: Date;
lastVisitAt: Date;
};
import { $accountStreak, type StreakResponse } from '../../stores/streak.ts';
import { useCopyText } from '../../hooks/use-copy-text.ts';
import { useAuth } from '../../hooks/use-auth.ts';
type AccountStreakProps = {};
@ -27,6 +21,8 @@ export function AccountStreak(props: AccountStreakProps) {
const toast = useToast();
const dropdownRef = useRef(null);
const user = useAuth();
const { copyText } = useCopyText();
const [isLoading, setIsLoading] = useState(true);
const accountStreak = useStore($accountStreak);
const [showDropdown, setShowDropdown] = useState(false);
@ -77,6 +73,16 @@ export function AccountStreak(props: AccountStreakProps) {
return null;
}
const shareReferralLink = () => {
const referralLink = new URL(
`/signup?rc=${user?.id}`,
import.meta.env.DEV ? 'http://localhost:3000' : 'https://roadmap.sh',
).toString();
copyText(referralLink);
toast.success('Referral link copied to clipboard');
};
let { count: currentCount = 0 } = accountStreak || {};
const previousCount =
accountStreak?.previousCount || accountStreak?.count || 0;
@ -184,11 +190,52 @@ export function AccountStreak(props: AccountStreakProps) {
<p className="-mt-[0px] mb-[1.5px] text-center text-xs tracking-wide text-slate-500">
Visit every day to keep your streak going!
</p>
<p className='text-xs mt-1.5 text-center'>
<a href="/leaderboard" className="text-purple-400 hover:underline underline-offset-2">
<p className="mt-1.5 text-center text-xs">
<a
href="/leaderboard"
className="text-purple-400 underline-offset-2 hover:underline"
>
See how you compare to others
</a>
</p>
{accountStreak?.refByUserCount ? (
<div className="mt-5">
<div className="flex items-center gap-2 text-sm text-slate-500">
<UserPlus className="size-4" />
<p>
<span className="text-slate-200">
{accountStreak?.refByUserCount || 0}
</span>{' '}
user(s) joined through{' '}
<button
className="text-slate-200 no-underline underline-offset-2 hover:underline"
onClick={shareReferralLink}
>
your referral
</button>
.
</p>
</div>
</div>
) : null}
{!accountStreak?.refByUserCount && (
<div className="mt-5">
<div className="flex items-center gap-2 text-sm text-slate-500">
<UserPlus className="size-4" />
<p>
<button
className="text-slate-200 no-underline underline-offset-2 hover:underline"
onClick={shareReferralLink}
>
Share your referral
</button>{' '}
link with your friends.
</p>
</div>
</div>
)}
</div>
</div>
)}

@ -1,5 +1,7 @@
import { type FormEvent, useState } from 'react';
import { type FormEvent, useEffect, useState } from 'react';
import { httpPost } from '../../lib/http';
import { deleteUrlParam, getUrlParams } from '../../lib/browser';
import { isLoggedIn, setAIReferralCode } from '../../lib/jwt';
type EmailSignupFormProps = {
isDisabled?: boolean;
@ -9,6 +11,9 @@ type EmailSignupFormProps = {
export function EmailSignupForm(props: EmailSignupFormProps) {
const { isDisabled, setIsDisabled } = props;
const { rc: referralCode } = getUrlParams() as {
rc?: string;
};
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [name, setName] = useState('');
@ -47,6 +52,16 @@ export function EmailSignupForm(props: EmailSignupFormProps) {
)}`;
};
useEffect(() => {
if (!referralCode || isLoggedIn()) {
deleteUrlParam('rc');
return;
}
setAIReferralCode(referralCode);
deleteUrlParam('rc');
}, []);
return (
<form className="flex w-full flex-col gap-2" onSubmit={onSubmit}>
<label htmlFor="name" className="sr-only">
@ -72,7 +87,7 @@ export function EmailSignupForm(props: EmailSignupFormProps) {
type="email"
autoComplete="email"
required
className="block w-full rounded-lg border border-gray-300 px-3 py-2 outline-none placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
className="block w-full rounded-lg border border-gray-300 px-3 py-2 outline-none placeholder:text-gray-400 focus:ring-2 focus:ring-black focus:ring-offset-1"
placeholder="Email Address"
value={email}
onInput={(e) => setEmail(String((e.target as any).value))}

@ -6,6 +6,7 @@ export type StreakResponse = {
previousCount?: number | null;
firstVisitAt: Date;
lastVisitAt: Date;
refByUserCount: number;
};
export const $accountStreak = atom<StreakResponse | undefined>();

Loading…
Cancel
Save