Skip to content

Commit

Permalink
feat: use repository recommendations from the API (#1323)
Browse files Browse the repository at this point in the history
  • Loading branch information
brandonroberts committed Jun 30, 2023
1 parent 88ec876 commit aa8c33d
Show file tree
Hide file tree
Showing 40 changed files with 287 additions and 188 deletions.
13 changes: 7 additions & 6 deletions components/atoms/Avatar/avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,12 @@ const Avatar = (props: AvatarProps): JSX.Element => {

try {
// Checks if the avatarURL is a proper URL. If not, it will throw an error.
if(typeof props.avatarURL === "string") new URL(props.avatarURL);
if (typeof props.avatarURL === "string" && props.avatarURL.length > 0) new URL(props.avatarURL);

imageSource = props.avatarURL && props.isCached
? cachedImage(props.avatarURL as string, process.env.NEXT_PUBLIC_CLOUD_NAME)
: props.avatarURL;
imageSource =
props.avatarURL && props.isCached
? cachedImage(props.avatarURL as string, process.env.NEXT_PUBLIC_CLOUD_NAME)
: props.avatarURL;
} catch (error) {
console.error(error);
}
Expand All @@ -49,7 +50,7 @@ const CustomAvatar = ({
size,
hasBorder,
isCircle,
initialsClassName
initialsClassName,
}: AvatarProps): JSX.Element => {
return (
<div
Expand Down Expand Up @@ -89,7 +90,7 @@ const DefaultAvatar = ({
alt,
size,
hasBorder,
isCircle
isCircle,
}: AvatarProps): JSX.Element => {
return (
<div
Expand Down
108 changes: 5 additions & 103 deletions components/organisms/ContributorProfileTab/contributor-profile-tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,13 @@ import Button from "components/atoms/Button/button";
import useSupabaseAuth from "lib/hooks/useSupabaseAuth";
import uppercaseFirst from "lib/utils/uppercase-first";
import useFetchAllEmojis from "lib/hooks/useFetchAllEmojis";
import { getInterestOptions, interestsType } from "lib/utils/getInterestOptions";
import { updateUser } from "lib/hooks/update-user";
import { useToast } from "lib/hooks/useToast";

import PaginationResults from "components/molecules/PaginationResults/pagination-result";
import Pagination from "components/molecules/Pagination/pagination";
import DashContainer from "components/atoms/DashedContainer/DashContainer";
import LanguagePill from "components/atoms/LanguagePill/LanguagePill";
import RecommendedRepoCard from "components/molecules/RecommendedRepoCard/recommended-repo-card";
import recommendations from "lib/utils/recommendations";
import Title from "components/atoms/Typography/title";
import SkeletonWrapper from "components/atoms/SkeletonLoader/skeleton-wrapper";
import CollaborationRequestsWrapper from "../CollaborationRequestWrapper/collaboration-requests-wrapper";
import UserRepositoryRecommendations from "../UserRepositoryRecommendations/user-repository-recommendations";

interface ContributorProfileTabProps {
contributor?: DbUser;
Expand All @@ -51,8 +45,6 @@ const tabLinks: string[] = ["Highlights", "Contributions", "Requests", "Recommen
const ContributorProfileTab = ({
contributor,
openPrs,
prMerged,
prTotal,
prVelocity,
prsMergedPercentage,
chart,
Expand All @@ -62,50 +54,14 @@ const ContributorProfileTab = ({
}: ContributorProfileTabProps): JSX.Element => {
const { login, interests: userInterests, receive_collaboration } = contributor || {};
const { user } = useSupabaseAuth();
const [selectedInterest, setSelectedInterest] = useState<string[]>([]);
const [showInterests, setShowInterests] = useState(false);
const [loading, setLoading] = useState(false);
const [recommendedrepos, setRecommendedRepos] = useState<string[]>([]);

const { toast } = useToast();

const interests = userInterests ? userInterests.split(",") : [];
const { data: highlights, isError, isLoading, mutate, meta, setPage } = useFetchUserHighlights(login || "");
const { data: emojis } = useFetchAllEmojis();

const [inputVisible, setInputVisible] = useState(false);
const pathnameRef = useRef<string | null>();
const interestArray = getInterestOptions();
const router = useRouter();

const handleSelectInterest = (interest: string) => {
if (selectedInterest.length > 0 && selectedInterest.includes(interest)) {
setSelectedInterest((prev) => prev.filter((item) => item !== interest));
} else {
setSelectedInterest((prev) => [...prev, interest]);
}
};

const handleUpdateInterest = async () => {
setLoading(true);
const data = await updateUser({
data: { interests: selectedInterest },
params: "interests",
});
setLoading(false);
if (data) {
toast({ description: "Updated successfully", variant: "success" });
} else {
toast({ description: "An error occured!", variant: "danger" });
}
};

const getRepoFullNameByInterests = () => {
const repoFullNames = interests.map((interest) => {
return recommendations[interest as interestsType];
});
setRecommendedRepos(repoFullNames[0]);
};
const router = useRouter();

const hasHighlights = highlights?.length > 0;
pathnameRef.current = router.pathname.split("/").at(-1);
Expand All @@ -124,14 +80,8 @@ const ContributorProfileTab = ({
}
}, [highlights]);

useEffect(() => {
if (userInterests && userInterests.length > 0) setSelectedInterest(userInterests.split(","));

getRepoFullNameByInterests();
}, [userInterests]);

return (
<Tabs defaultValue={uppercaseFirst(currentPathname as string)} className="">
<Tabs defaultValue={uppercaseFirst(currentPathname as string)} className="" onValueChange={handleTabUrl}>
<TabsList className="justify-start w-full overflow-x-scroll border-b">
{tabLinks.map((tab) => (
<TabsTrigger
Expand Down Expand Up @@ -232,7 +182,7 @@ const ContributorProfileTab = ({
</div>
) : (
<DashContainer>
<div className="text-center">
<div className="text-center">
<p>
You don&apos;t have any highlights yet! <br /> Highlights are a great way to show off your
contributions. Merge any pull requests recently?
Expand Down Expand Up @@ -319,55 +269,7 @@ const ContributorProfileTab = ({

{/* Recommendation tab details */}
<TabsContent value="Recommendations">
{userInterests && userInterests.length > 0 ? (
<div className="space-y-2 ">
<Title className="!font-normal my-6" level={5}>
Here are some repositories we think would be great for you. Click on one that you like and start
contributing!
</Title>
<div className="flex flex-wrap gap-4">
{recommendedrepos.map((repo, i) => (
<RecommendedRepoCard className="md:w-[45%]" key={i.toString()} fullName={repo} />
))}
{recommendedrepos.length === 1 && (
<RecommendedRepoCard className="md:w-[45%]" fullName="open-sauced/insights" />
)}
</div>
</div>
) : (
<DashContainer>
{/* Empty Interest state */}
<div className="flex flex-col items-center gap-4">
<p className="font-normal text-center">
If you鈥檙e just getting started, recommendations are a great to find projects and start making
contributions on repositories.
<br /> <br /> Select some interests and we give you some recommendations!
</p>

{showInterests && (
<div className="flex flex-wrap justify-center w-full gap-2 mt-6 md:max-w-sm">
{interestArray.map((topic, index) => (
<LanguagePill
onClick={() => handleSelectInterest(topic)}
classNames={`${(selectedInterest || []).includes(topic) && "bg-light-orange-10 text-white"}`}
topic={topic}
key={index}
/>
))}
</div>
)}
{showInterests ? (
<Button loading={loading} className="mt-4" onClick={handleUpdateInterest} variant="primary">
Save Interests
</Button>
) : (
<Button onClick={() => setShowInterests(true)} variant="primary">
Select Interests
</Button>
)}
</div>
</DashContainer>
)}
<UserRepositoryRecommendations contributor={contributor} userInterests={userInterests} />
</TabsContent>
</>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { useEffect, useState } from "react";

import Button from "components/atoms/Button/button";
import DashContainer from "components/atoms/DashedContainer/DashContainer";
import LanguagePill from "components/atoms/LanguagePill/LanguagePill";
import Title from "components/atoms/Typography/title";
import RecommendedRepoCard from "components/molecules/RecommendedRepoCard/recommended-repo-card";

import useUserRepoRecommendations from "lib/hooks/useUserRepoRecommendations";
import { updateUser } from "lib/hooks/update-user";
import { useToast } from "lib/hooks/useToast";
import { getInterestOptions, interestsType } from "lib/utils/getInterestOptions";

interface UserRepoRecommendationsProps {
contributor?: DbUser;
userInterests?: string;
}

const UserRepositoryRecommendations = ({ contributor, userInterests }: UserRepoRecommendationsProps) => {
const { data: userRecommendedRepos, mutate: refreshUserRecommendedRepos } = useUserRepoRecommendations();
const interests = getInterestOptions();
const [selectedInterests, setSelectedInterests] = useState<string[]>([]);
const [showInterests, setShowInterests] = useState(false);
const [loading, setLoading] = useState(false);
const [recommendedrepos, setRecommendedRepos] = useState<string[]>([]);

const { toast } = useToast();

const handleSelectInterest = (interest: string) => {
if (selectedInterests.length > 0 && selectedInterests.includes(interest)) {
setSelectedInterests((prev) => prev.filter((item) => item !== interest));
} else {
setSelectedInterests((prev) => [...prev, interest]);
}
};

const handleUpdateInterest = async () => {
setLoading(true);

const data = await updateUser({
data: { interests: selectedInterests },
params: "interests",
});

setLoading(false);

if (data) {
toast({ description: "Updated successfully", variant: "success" });
} else {
toast({ description: "An error occured!", variant: "danger" });
}
};

const getRepoFullNameByInterests = () => {
const repoFullNames = interests.map((interest) => {
return userRecommendedRepos[interest as interestsType];
});

setRecommendedRepos(
Array.from(
new Set([
...repoFullNames
.flat()
.filter(Boolean)
.map((repo) => repo.full_name),
])
)
);
};

useEffect(() => {
if (userInterests && userInterests.length > 0) setSelectedInterests(userInterests.split(","));
getRepoFullNameByInterests();
}, [userInterests, userRecommendedRepos]);

useEffect(() => {
refreshUserRecommendedRepos();
}, [contributor]);

return (
<>
{userInterests && userInterests.length > 0 ? (
<div className="space-y-2 ">
<Title className="!font-normal my-6" level={5}>
Here are some repositories we think would be great for you. Click on one that you like and start
contributing!
</Title>
<div className="flex flex-wrap gap-4">
{recommendedrepos.map((repo, i) => (
<RecommendedRepoCard className="md:w-[45%]" key={i.toString()} fullName={repo} />
))}
{recommendedrepos.length <= 1 && (
<RecommendedRepoCard className="md:w-[45%]" fullName="open-sauced/insights" />
)}
</div>
</div>
) : (
<DashContainer>
{/* Empty Interest state */}
<div className="flex flex-col items-center gap-4">
<p className="font-normal text-center">
If you&apos;re just getting started, recommendations are a great to find projects and start making
contributions on repositories.
<br /> <br /> Select some interests and we give you some recommendations!
</p>

{showInterests && (
<div className="flex flex-wrap justify-center w-full gap-2 mt-6 md:max-w-sm">
{interests.map((topic, index) => (
<LanguagePill
onClick={() => handleSelectInterest(topic)}
classNames={`${(selectedInterests || []).includes(topic) && "bg-light-orange-10 text-white"}`}
topic={topic}
key={index}
/>
))}
</div>
)}
{showInterests ? (
<Button loading={loading} className="mt-4" onClick={handleUpdateInterest} variant="primary">
Save Interests
</Button>
) : (
<Button onClick={() => setShowInterests(true)} variant="primary">
Select Interests
</Button>
)}
</div>
</DashContainer>
)}
</>
);
};

export default UserRepositoryRecommendations;

0 comments on commit aa8c33d

Please sign in to comment.