Skip to content

Commit

Permalink
Add GitHub picture to profile (#1100)
Browse files Browse the repository at this point in the history
  • Loading branch information
jribbink authored Jan 22, 2025
1 parent 4bcdfa9 commit e947655
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 17 deletions.
30 changes: 27 additions & 3 deletions src/components/ProfileModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ import * as fcl from '@onflow/fcl';
import { z } from 'zod';
import { parseIdentifier, typeIdentifier } from '../utils/flow';
import { CONTRACT_IDENTIFIER_REGEX } from '../utils/constants';
import { useGithubUser } from '../hooks/use-github-user';
import { useDebounce } from '../hooks/use-debounce';
import { faUser } from '@fortawesome/free-solid-svg-icons';

interface ProfileModalProps {
isOpen: boolean;
Expand Down Expand Up @@ -82,6 +85,13 @@ const ProfileModal: React.FC<ProfileModalProps> = ({ isOpen, onClose }) => {
deployedContracts?: boolean;
}>({});

const { value: debouncedGithubHandle, isDebouncing: githubDebouncing } =
useDebounce(settings?.socials?.[SocialType.GITHUB], 1000);
const { user: githubUser, isLoading: githubFetchLoading } = useGithubUser(
debouncedGithubHandle,
);
const isGithubLoading = githubFetchLoading || githubDebouncing;

const validate = () => {
let hasErrors = false;

Expand Down Expand Up @@ -236,7 +246,19 @@ const ProfileModal: React.FC<ProfileModalProps> = ({ isOpen, onClose }) => {

return (
<Modal isOpen={isOpen} onClose={onClose} scrollable={true} title="Profile">
<div className="space-y-6">
<div className="space-y-6 flex flex-col">
<div className="flex items-center w-24 h-24 mx-auto">
{(isGithubLoading || !githubUser?.avatar_url) && (
<FontAwesomeIcon icon={faUser} size="5x" className="mx-auto" />
)}
{!isGithubLoading && githubUser?.avatar_url && (
<img
src={githubUser?.avatar_url}
className="w-24 h-24 rounded-full mx-auto"
/>
)}
</div>

<div className="space-y-4">
<Field
label="Username"
Expand Down Expand Up @@ -275,7 +297,9 @@ const ProfileModal: React.FC<ProfileModalProps> = ({ isOpen, onClose }) => {
socials,
});
}}
onBlur={() => setTouched({ ...touched, socials: true })}
onBlur={() => {
setTouched({ ...touched, socials: true });
}}
/>
</Field>
</div>
Expand Down Expand Up @@ -317,7 +341,7 @@ const ProfileModal: React.FC<ProfileModalProps> = ({ isOpen, onClose }) => {
</div>
</Field>

<div className="max-w-sm mx-auto">
<div className="max-w-sm">
<RadioGroup
options={flowSources.map((source) => source.name)}
value={settings?.referralSource || ''}
Expand Down
20 changes: 20 additions & 0 deletions src/hooks/use-debounce.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useEffect, useState } from 'react';

export function useDebounce<T = any>(value: T, delay: number) {
const [debouncedValue, setDebouncedValue] = useState(value);

useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);

return () => {
clearTimeout(handler);
};
}, [value, delay]);

return {
value: debouncedValue,
isDebouncing: debouncedValue !== value,
};
}
22 changes: 22 additions & 0 deletions src/hooks/use-github-user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import useSWR from 'swr/immutable';
import { User } from '../types/github';

export function useGithubUser(username?: string | null) {
const {
data: user,
isLoading,
error,
} = useSWR(
username ? `https://api.github.com/users/${username}` : null,
async (url) => {
const response = await fetch(url);
return response.json() as Promise<User>;
},
);

return {
user,
isLoading,
error,
};
}
65 changes: 65 additions & 0 deletions src/types/github.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
interface Plan {
collaborators: number;
name: string;
space: number;
private_repos: number;
}

interface BaseUser {
login: string;
id: number;
user_view_type: string;
node_id: string;
avatar_url: string;
gravatar_id: string | null;
url: string;
html_url: string;
followers_url: string;
following_url: string;
gists_url: string;
starred_url: string;
subscriptions_url: string;
organizations_url: string;
repos_url: string;
events_url: string;
received_events_url: string;
type: string;
site_admin: boolean;
name: string | null;
company: string | null;
blog: string | null;
location: string | null;
email: string | null;
notification_email?: string | null;
hireable: boolean | null;
bio: string | null;
twitter_username: string | null;
public_repos: number;
public_gists: number;
followers: number;
following: number;
created_at: string;
updated_at: string;
plan: Plan;
}

interface PrivateUser extends BaseUser {
private_gists: number;
total_private_repos: number;
owned_private_repos: number;
disk_usage: number;
collaborators: number;
two_factor_authentication: boolean;
business_plus?: boolean;
ldap_dn?: string;
}

interface PublicUser extends BaseUser {
private_gists?: number;
total_private_repos?: number;
owned_private_repos?: number;
disk_usage?: number;
collaborators?: number;
}

export type User = PrivateUser | PublicUser;
29 changes: 15 additions & 14 deletions src/ui/design-system/src/lib/Components/Modal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ interface ModalProps {
}

const Modal: React.FC<ModalProps> = ({
isOpen,
onClose,
title,
children,
className,
scrollable = true, // default is scrollable
}) => {
isOpen,
onClose,
title,
children,
className,
scrollable = true, // default is scrollable
}) => {
return (
<Transition appear show={isOpen} as={Fragment}>
<Dialog as="div" className="relative z-50" onClose={onClose}>
Expand Down Expand Up @@ -48,9 +48,9 @@ const Modal: React.FC<ModalProps> = ({
>
<Dialog.Panel
className={clsx(
"relative w-full max-w-md transform rounded-md border bg-gray-100 text-black shadow-2xl transition-all dark:bg-gray-800 dark:text-gray-100 dark:border-gray-700",
scrollable && "max-h-[80vh] overflow-y-auto",
className
'relative w-full max-w-md transform rounded-md border bg-gray-100 text-black shadow-2xl transition-all dark:bg-gray-800 dark:text-gray-100 dark:border-gray-700',
scrollable && 'max-h-[80vh] overflow-y-auto',
className,
)}
style={{
top: '50%',
Expand All @@ -62,16 +62,17 @@ const Modal: React.FC<ModalProps> = ({
{/* Header */}
{title && (
<div className="px-4 py-3 border-b border-gray-300 dark:border-gray-700">
<Dialog.Title as="h3" className="text-lg font-semibold text-center">
<Dialog.Title
as="h3"
className="text-lg font-semibold text-center"
>
{title}
</Dialog.Title>
</div>
)}

{/* Content */}
<div className="px-4 py-5">
{children}
</div>
<div className="px-4 py-5 pt-0">{children}</div>
</Dialog.Panel>
</Transition.Child>
</div>
Expand Down

0 comments on commit e947655

Please sign in to comment.