Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add highlights to user profile page #859

Merged
merged 38 commits into from
Feb 17, 2023
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
1d3cd4d
chore: add radix us primitives for tabs
OgDev-01 Feb 9, 2023
4caccb0
feat: create reusable tab component
OgDev-01 Feb 9, 2023
0fb9f26
feat: refactor contributor profile page to use tabs
OgDev-01 Feb 9, 2023
df7ce4e
chore: install tailwind class name and merger
OgDev-01 Feb 9, 2023
2bd8e14
chore: add DbHighlights to global interfaces
OgDev-01 Feb 10, 2023
41973a3
feat: create pull request highlight card
OgDev-01 Feb 10, 2023
da4dc40
chore: add fetch user highlights hooks
OgDev-01 Feb 10, 2023
2e6aa5c
fix: build error
OgDev-01 Feb 10, 2023
1d39c0d
refactor: update github utils function
OgDev-01 Feb 11, 2023
c55d9bd
feat: create github pull info fetch hook
OgDev-01 Feb 11, 2023
3fcf68c
refactor: update highlight card props
OgDev-01 Feb 11, 2023
76c2ef3
refactor: improve textarea input
OgDev-01 Feb 13, 2023
3a1e315
feat: add create highlight function to libs
OgDev-01 Feb 13, 2023
883288b
feat: create and implement dropdown
OgDev-01 Feb 13, 2023
220b89f
Merge branch 'beta' into 830-profile-hight=lights
OgDev-01 Feb 13, 2023
9815102
refactor: implement responsiveness on user profile
OgDev-01 Feb 13, 2023
57aa9f1
Merge branch '830-profile-hight=lights' of https://github.com/open-sa…
OgDev-01 Feb 13, 2023
34d011a
refactor: improve highlights form implementation
OgDev-01 Feb 14, 2023
1e7a4e7
refactor: imporove conditional checks
OgDev-01 Feb 14, 2023
92d5cb4
refactor: update generate pr link utils function
OgDev-01 Feb 14, 2023
294f1b2
feat: add imoji dropdown
OgDev-01 Feb 14, 2023
a0b62f7
fix: copy type message
OgDev-01 Feb 14, 2023
6ec902c
refactor: update link validation
OgDev-01 Feb 14, 2023
afa95df
Merge branch 'beta' into 830-profile-hight=lights
brandonroberts Feb 15, 2023
81262a1
refactor: improve conditional checks
OgDev-01 Feb 15, 2023
96cae47
Merge branch '830-profile-hight=lights' of https://github.com/open-sa…
OgDev-01 Feb 15, 2023
90e9ca0
feat: add share to linkedin and twitter function
OgDev-01 Feb 15, 2023
61ccf3a
refactor: improve validation for github pull url
OgDev-01 Feb 16, 2023
8605a89
Update components/molecules/ContributorHighlight/contributor-highligh…
OgDev-01 Feb 16, 2023
65abd30
Update components/molecules/ContributorHighlight/contributor-highligh…
OgDev-01 Feb 16, 2023
c62d313
Update components/molecules/ContributorHighlight/contributor-highligh…
OgDev-01 Feb 16, 2023
dd21b59
fix: github pull url validation error
OgDev-01 Feb 16, 2023
e80a2ef
Merge branch '830-profile-hight=lights' of https://github.com/open-sa…
OgDev-01 Feb 16, 2023
8c21d43
refactor: remove classnames helper libs
OgDev-01 Feb 16, 2023
8e302d0
chore: update github fetch method and add 404 image
OgDev-01 Feb 17, 2023
4b32848
refactor: logic refactor to fit new fetcher
OgDev-01 Feb 17, 2023
f6da627
fix: add revalidate on highlights post
OgDev-01 Feb 17, 2023
6e1bdea
chore: remove unused code
OgDev-01 Feb 17, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
45 changes: 45 additions & 0 deletions components/atoms/Dropdown/dropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import * as React from "react";
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
import { classNames } from "lib/utils/class-names";

const DropdownMenu = DropdownMenuPrimitive.Root;
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
const DropdownMenuPortal = DropdownMenuPrimitive.Portal;

const DropdownMenuContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
<DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={classNames(
"z-50 min-w-[8rem] overflow-hidden rounded-lg border border-slate-100 bg-white p-1 text-slate-700 shadow-md animate-in data-[side=right]:slide-in-from-left-2 data-[side=left]:slide-in-from-right-2 data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2 ",
className
)}
{...props}
/>
</DropdownMenuPrimitive.Portal>
));
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;

const DropdownMenuItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
inset?: boolean;
}
>(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Item
ref={ref}
className={classNames(
"relative flex cursor-default select-none items-center py-1.5 px-2 text-sm font-medium focus:text-sauced-orange outline-none focus:bg-orange-100 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 ",
inset && "pl-8",
className
)}
{...props}
/>
));
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;

export { DropdownMenu, DropdownMenuItem, DropdownMenuContent, DropdownMenuTrigger };
43 changes: 43 additions & 0 deletions components/atoms/Tabs/tabs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import * as React from "react";
import * as TabsPrimitive from "@radix-ui/react-tabs";

import { classNames } from "lib/utils/class-names";

const Tabs = TabsPrimitive.Root;

const TabsList = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.List>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
>(({ className, ...props }, ref) => (
<TabsPrimitive.List
ref={ref}
className={classNames("inline-flex items-center rounded-md px-1 ", className)}
{...props}
/>
));
TabsList.displayName = TabsPrimitive.List.displayName;

const TabsTrigger = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Trigger
className={classNames(
"inline-flex min-w-[100px] items-center justify-center px-3 py-1.5 text-sm font-medium text-slate-700 transition-all disabled:pointer-events-none disabled:opacity-50 data-[state=active]:text-slate-900",
className
)}
{...props}
ref={ref}
/>
));
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;

const TabsContent = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Content className={classNames("mt-2 rounded-md ", className)} {...props} ref={ref} />
));
TabsContent.displayName = TabsPrimitive.Content.displayName;

export { Tabs, TabsList, TabsTrigger, TabsContent };
21 changes: 21 additions & 0 deletions components/atoms/Textarea/text-area.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import * as React from "react";

import { classNames } from "lib/utils/class-names";

export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}

const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(({ className, ...props }, ref) => {
return (
<textarea
className={classNames(
"flex h-20 w-full rounded-md bg-transparent py-2 text-sm placeholder:text-slate-400 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref}
{...props}
/>
);
});
Textarea.displayName = "Textarea";

export { Textarea };
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import Title from "components/atoms/Typography/title";
import { generateApiPrUrl } from "lib/utils/github";
import React from "react";
import PullRequestHighlightCard from "../PullRequestHighlightCard/pull-request-highlight-card";
import { useFetchGithubPRInfo } from "lib/hooks/useFetchGithubPRInfo";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger
} from "components/atoms/Dropdown/dropdown";
import { HiOutlineEmojiHappy } from "react-icons/hi";
import { TfiMoreAlt } from "react-icons/tfi";
import { FiLinkedin, FiTwitter } from "react-icons/fi";
import { BsLink45Deg } from "react-icons/bs";
import { FaUserPlus } from "react-icons/fa";
import { GrFlag } from "react-icons/gr";
import SkeletonWrapper from "components/atoms/SkeletonLoader/skeleton-wrapper";

interface ContributorHighlightCardProps {
title?: string;
desc?: string;
prLink: string;
}
const ContributorHighlightCard = ({ title, desc, prLink }: ContributorHighlightCardProps) => {
const { isValidUrl, apiUrl } = generateApiPrUrl(prLink);
const { data, isLoading, isError } = useFetchGithubPRInfo(isValidUrl ? apiUrl : "");

return (
<article className="flex flex-col max-w-[40rem] flex-1 gap-3 lg:gap-6">
<div className="flex justify-between items-center">
{title && (
<Title className="!text-sm lg:!text-xl !text-light-slate-12" level={4}>
{title}
</Title>
)}
<div className="flex ml-auto lg:gap-3 gap-3 items-center">
OgDev-01 marked this conversation as resolved.
Show resolved Hide resolved
<DropdownMenu>
<DropdownMenuTrigger className="py-2 px-2 rounded-full data-[state=open]:bg-light-slate-7">
<HiOutlineEmojiHappy size={20} />
</DropdownMenuTrigger>
<DropdownMenuContent className="flex flex-row gap-2 rounded-3xl" side="left">
<DropdownMenuItem className="rounded-full">πŸ‘</DropdownMenuItem>
<DropdownMenuItem className="rounded-full">πŸ‘Ž</DropdownMenuItem>
<DropdownMenuItem className="rounded-full">πŸ•</DropdownMenuItem>
<DropdownMenuItem className="rounded-full">πŸ˜„</DropdownMenuItem>
<DropdownMenuItem className="rounded-full">❀️</DropdownMenuItem>
<DropdownMenuItem className="rounded-full">πŸš€</DropdownMenuItem>
<DropdownMenuItem className="rounded-full">πŸ‘€</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<DropdownMenu>
<DropdownMenuTrigger className=" py-2 px-2 rounded-full data-[state=open]:bg-light-slate-7">
<TfiMoreAlt size={24} />
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="rounded-lg flex flex-col py-2 gap-1">
<DropdownMenuItem className="rounded-md">
<div className="flex gap-2.5 py-1 items-center pl-3 pr-7">
<FiTwitter size={22} />
<span>Share to Twitter</span>
</div>
</DropdownMenuItem>
<DropdownMenuItem className="rounded-md">
<div className="flex gap-2.5 py-1 items-center pl-3 pr-7">
<FiLinkedin size={22} />
<span>Share to Linkedin</span>
</div>
</DropdownMenuItem>
<DropdownMenuItem className="rounded-md">
<div className="flex gap-2.5 py-1 items-center pl-3 pr-7">
<BsLink45Deg size={22} />
<span>Copy link</span>
</div>
</DropdownMenuItem>
<DropdownMenuItem className="rounded-md">
<div className="flex gap-2.5 py-1 items-center pl-3 pr-7">
<FaUserPlus size={22} />
<span>Follow user</span>
</div>
</DropdownMenuItem>
<DropdownMenuItem className="rounded-md">
<div className="flex gap-2.5 py-1 items-center pl-3 pr-7">
<GrFlag size={22} />
<span>Report content</span>
</div>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>

{/* Highlight body section */}
<div>
<p className="text-light-slate-11 text-sm lg:text-base font-normal">{desc}</p>
</div>
{/* Highlight Link section */}

<div>
<a href={prLink} className="underline text-sauced-orange cursor-pointer">
{prLink}
</a>
</div>

{/* Generated OG card section */}
{isLoading && <SkeletonWrapper height={250} />}
{isError && <>An error occured...</>}
{data && (
<div>
<PullRequestHighlightCard prLink={prLink} />
</div>
)}
</article>
);
};

export default ContributorHighlightCard;
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@ const ContributorProfileHeader = ({ avatarUrl, githubName, isConnected }: Contri
)}

<div className="w-full absolute -top-6 px-6 md:px-12 lg:px-16 flex flex-row items-end justify-between py-6">
<div className="translate-y-[75px]">
<div className="translate-y-[75px] hidden md:inline-flex">
<Avatar className="" hasBorder avatarURL={avatarUrl} size={184} isCircle />
</div>
<div className="translate-y-[125px] md:hidden ">
<Avatar className="" hasBorder avatarURL={avatarUrl} size={120} isCircle />
</div>
{isConnected && (
<div className="flex gap-3 flex-col md:flex-row items-center">
<div className="flex md:translate-y-0 translate-y-28 gap-3 flex-col md:flex-row items-center">
<a target="_blank" rel="noopener noreferrer" href={`https://github.com/${githubName}`}>
<Button className="!px-5 !py-2 !bg-white" type="text">
View on GitHub
Expand Down
66 changes: 54 additions & 12 deletions components/molecules/HighlightInput/highlight-input-form.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import Button from "components/atoms/Button/button";
import { Textarea } from "components/atoms/Textarea/text-area";
import { createHighlights } from "lib/hooks/createHighlights";

import { ToastTrigger } from "lib/utils/toast-trigger";
import { ChangeEvent, useEffect, useRef, useState } from "react";
import PullRequestHighlightCard from "../PullRequestHighlightCard/pull-request-highlight-card";

const HighlightInputForm = (): JSX.Element => {
const [isDivFocused, setIsDivFocused] = useState(false);
const textAreaRef = useRef<HTMLTextAreaElement>(null);
const [bodyText, setBodyText] = useState("");
const [row, setRow] = useState(1);
const [title, setTitle] = useState("");
const [pullrequestLink, setPullRequestLink] = useState("");
const ref = useRef<HTMLFormElement>(null);
let rowLomit = 5;
let messageLastScrollHeight = textAreaRef.current ? textAreaRef.current?.scrollHeight : 50;
Expand All @@ -21,12 +26,19 @@ const HighlightInputForm = (): JSX.Element => {
}
};
document.addEventListener("mousedown", checkIfClickedOutside);
const pullLink = bodyText.match(/\bhttps?:\/\/\S+/gi);
const link = pullLink && new URL(pullLink as unknown as string);
if (pullLink && pullLink.length > 0 && link?.hostname === "github.com" && link?.pathname.includes("pull")) {
setPullRequestLink(pullLink[0]);
} else {
setPullRequestLink("");
}

return () => {
// Cleanup the event listener
document.removeEventListener("mousedown", checkIfClickedOutside);
};
}, [isDivFocused, bodyText]);
}, [isDivFocused, bodyText, pullrequestLink]);

const handleTextAreaInputChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
setBodyText(e.target.value);
Expand All @@ -35,22 +47,50 @@ const HighlightInputForm = (): JSX.Element => {
} else if (row > 1 && textAreaRef.current && textAreaRef.current?.scrollHeight < messageLastScrollHeight) {
setRow((prev) => prev--);
}

if (!bodyText) setRow(1);
messageLastScrollHeight = textAreaRef.current?.scrollHeight || 60;
};

// Handle submit highlights
const handlePostHighlight = (e: React.FormEvent<HTMLFormElement>) => {
const handlePostHighlight = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setIsDivFocused(false);

// Trigger api call to post highlight
setBodyText("");
setTitle("");
const pullLink = bodyText.match(/\bhttps?:\/\/\S+/gi);
const link = pullLink && new URL(pullLink as unknown as string);
const [url] = pullLink || [];
const highlight = bodyText.replace(url as string, "");

if (
url === null ||
url === undefined ||
url === "" ||
url.length === 0 ||
link?.hostname !== "github.com" ||
!link?.pathname.includes("pull")
) {
ToastTrigger({ message: "A valid Pull request Link is required", type: "error" });
return;
} else {
const res = await createHighlights({
highlight,
title,
url: url || ""
});
setBodyText("");
setTitle("");
setIsDivFocused(false);

if (res) {
ToastTrigger({ message: "Highlight uploade success", type: "success" });
OgDev-01 marked this conversation as resolved.
Show resolved Hide resolved
} else {
ToastTrigger({ message: "An error occured!!!", type: "error" });
}
}
};

return (
<form onSubmit={handlePostHighlight} ref={ref} className="flex flex-col gap-4">
<form onSubmit={handlePostHighlight} ref={ref} className="flex flex-1 flex-col gap-4">
<div
onClick={() => {
setIsDivFocused(true);
Expand All @@ -64,17 +104,19 @@ const HighlightInputForm = (): JSX.Element => {
type="text"
placeholder={isDivFocused ? "Add title (optional)" : "Highlight your merged PRs and provide a link!"}
/>
<textarea
rows={row}
value={bodyText}
onChange={(e) => handleTextAreaInputChange(e)}
ref={textAreaRef}
<Textarea
className={`resize-none font-normal text-light-slate-11 mb-2 transition focus:outline-none rounded-lg ${
!isDivFocused ? "hidden" : ""
}`}
ref={textAreaRef}
rows={row}
value={bodyText}
onChange={(e) => handleTextAreaInputChange(e)}
/>
</div>

{pullrequestLink && isDivFocused && <PullRequestHighlightCard prLink={pullrequestLink} />}

{isDivFocused && (
<Button disabled={!bodyText} className="ml-auto" type="primary">
Post
Expand Down