Skip to content

Commit

Permalink
feat: Lable coloring
Browse files Browse the repository at this point in the history
  • Loading branch information
AKharytonchyk committed Nov 8, 2024
1 parent ca55a69 commit 4022917
Show file tree
Hide file tree
Showing 11 changed files with 221 additions and 30 deletions.
80 changes: 80 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.2",
"@uiw/react-json-view": "^2.0.0-alpha.27",
"emoji-dictionary": "^1.0.11",
"lz-string": "^1.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
5 changes: 3 additions & 2 deletions src/components/MultiselectFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
SelectChangeEvent,
} from "@mui/material";
import React from "react";
import replaceEmoticons from "../utils/replaceEmoticons";

const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
Expand Down Expand Up @@ -60,15 +61,15 @@ export const MultiselectFilter: React.FC<MultiselectFilterProps> = ({
renderValue={(selected) => (
<Box sx={{ display: "flex", flexWrap: "wrap", gap: 0.5 }}>
{selected.map((value) => (
<Chip key={value} label={value} size="small"/>
<Chip key={value} label={replaceEmoticons(value)} size="small"/>
))}
</Box>
)}
MenuProps={MenuProps}
>
{options.map((option) => (
<MenuItem key={option} value={option}>
{option}
{replaceEmoticons(option)}
</MenuItem>
))}
</Select>
Expand Down
97 changes: 75 additions & 22 deletions src/components/PullRequestCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import { DesignServices, FileOpen, GitHub, Lock, Visibility } from "@mui/icons-m
import { PullRequestChecks } from "./PullRequestChecks";
import { PullRequestsApprovals } from "./PullRequestsApprovals";
import { PullRequestMergeCheck } from "./PullRequestMergeCheck";
import getContrastColor from "../utils/getContractColor";
import replaceEmoticons from "../utils/replaceEmoticons";

interface PullRequestCardProps {
pr: PullRequest;
Expand All @@ -32,14 +34,6 @@ const PullRequestCard: React.FC<PullRequestCardProps> = ({ pr }) => {
return red[500];
};


const getLabelColor = (label: string) => {
if (/(don't|do not)/gi.test(label)) return "warning";
if (/(wip|work in progress)/gi.test(label)) return "secondary";
if (/(ready for review|Review Neede|Ready For Testing)/gi.test(label)) return "success";

return "default";
}

return (
<Card sx={{ display: "flex", flexDirection: "column", height: "100%" }}>
Expand All @@ -58,26 +52,65 @@ const PullRequestCard: React.FC<PullRequestCardProps> = ({ pr }) => {
size="small"
sx={{ marginRight: "auto" }}
/>
{pr.locked && <Lock /> }
{pr.draft && <Tooltip title="Draft PR"><DesignServices color="secondary"/></Tooltip>}
{pr.labels.map((label) => (<Chip key={label.id} label={label.name} size="small" color={getLabelColor(label.name)}/> ))}
{pr.locked && <Lock />}
{pr.draft && (
<Tooltip title="Draft PR">
<DesignServices color="secondary" />
</Tooltip>
)}
<Box sx={{ display: "flex", flexShrink: "1", flexWrap: 'wrap', gap: 1 }}>
{pr.labels.map((label) => (
<Chip
key={label.id}
label={replaceEmoticons(label.name)}
size="small"
style={{ backgroundColor: `#${label.color}`,color: getContrastColor(`#${label.color}`) }}
/>
))}
</Box>
<Chip
label={pr.state.toUpperCase()}
color={pr.state === "open" ? "success" : "default"}
size="small"
sx={{ marginY: 1 }}
/>
</CardActions>
<CardContent sx={{ display: "flex", flexDirection: "column" , paddingBottom: 1, height: "100%" }}>
<CardContent
sx={{
display: "flex",
flexDirection: "column",
paddingBottom: 1,
height: "100%",
}}
>
<Typography sx={{ fontSize: 14 }} color="text.secondary" gutterBottom>
PR by {pr.user.login}
</Typography>
<Typography variant="h5" component="div">
<Link href={pr.html_url} target="_blank" rel="noopener">#{pr.number}</Link> {pr.title}
<Link href={pr.html_url} target="_blank" rel="noopener">
#{pr.number}
</Link>{" "}
{pr.title}
</Typography>
<Box sx={{ display: "flex", gap: 1, alignItems: "center", justifyContent: "space-between", paddingTop: 2, marginTop: 'auto' }}>
<Box sx={{ display: "flex", alignItems: "center", flexDirection: "row", gap: 1}}>
<Typography color="text.secondary">Days in review:{" "}</Typography>
<Box
sx={{
display: "flex",
gap: 1,
alignItems: "center",
justifyContent: "space-between",
paddingTop: 2,
marginTop: "auto",
}}
>
<Box
sx={{
display: "flex",
alignItems: "center",
flexDirection: "row",
gap: 1,
}}
>
<Typography color="text.secondary">Days in review: </Typography>
<Chip
label={Math.floor(
(new Date().getTime() - new Date(pr.created_at).getTime()) /
Expand All @@ -88,14 +121,34 @@ const PullRequestCard: React.FC<PullRequestCardProps> = ({ pr }) => {
/>
</Box>
{"|"}
<PullRequestChecks owner = {pr.base.repo.owner.login} repo = {pr.base.repo.name} prNumber = {pr.number}/>
<PullRequestChecks
owner={pr.base.repo.owner.login}
repo={pr.base.repo.name}
prNumber={pr.number}
/>
{"|"}
<PullRequestMergeCheck owner={pr.base.repo.owner.login} repo={pr.base.repo.name} prNumber={pr.number} />
<PullRequestMergeCheck
owner={pr.base.repo.owner.login}
repo={pr.base.repo.name}
prNumber={pr.number}
/>
{"|"}
<PullRequestsApprovals owner = {pr.base.repo.owner.login} repo = {pr.base.repo.name} prNumber = {pr.number}/>
<Box gap={2} display={'flex'}>
<Link href={pr.html_url} target="_blank" rel="noopener"><Tooltip title="View/Open PR"><Visibility/></Tooltip></Link>
<Link href={pr.html_url + "/files"} target="_blank" rel="noopener"><Tooltip title="View Changes"><FileOpen /></Tooltip></Link>
<PullRequestsApprovals
owner={pr.base.repo.owner.login}
repo={pr.base.repo.name}
prNumber={pr.number}
/>
<Box gap={2} display={"flex"}>
<Link href={pr.html_url} target="_blank" rel="noopener">
<Tooltip title="View/Open PR">
<Visibility />
</Tooltip>
</Link>
<Link href={pr.html_url + "/files"} target="_blank" rel="noopener">
<Tooltip title="View Changes">
<FileOpen />
</Tooltip>
</Link>
</Box>
</Box>
</CardContent>
Expand Down
9 changes: 6 additions & 3 deletions src/service/gitService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ export class GitService {
repo,
ref: `pull/${prNumber}/head`,
filter: "latest",
})
}),
true
);
}

Expand All @@ -81,7 +82,8 @@ export class GitService {
owner,
repo,
pull_number: prNumber,
})
}),
true
);
return mergeConflicts.data;
}
Expand All @@ -92,7 +94,8 @@ export class GitService {
owner,
repo,
pull_number: prNumber,
})
}),
true
);

if (
Expand Down
6 changes: 6 additions & 0 deletions src/types/emoji-dictionary.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
declare module "emoji-dictionary" {
export function getUnicode(name: string): string;
export function getName(unicode: string): string;
export function getShortcode(unicode: string): string;
export function getAllNames(): string[];
}
12 changes: 9 additions & 3 deletions src/utils/RateLimiterQueue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ class RateLimiterQueue {
}, 60000);
}

async enqueue<T>(requestFunction: () => Promise<T>): Promise<T> {
async enqueue<T>(requestFunction: () => Promise<T>, unshift = false): Promise<T> {
return new Promise((resolve, reject) => {
this.queue.push(async () => {
const queueAction = async () => {
try {
this.activeRequests++;
const result = await requestFunction();
Expand All @@ -29,7 +29,13 @@ class RateLimiterQueue {
this.requestTimestamps.push(Date.now());
this.processQueue();
}
});
};

if (unshift) {
this.queue.unshift(queueAction);
} else {
this.queue.push(queueAction);
}
this.processQueue();
});
}
Expand Down
32 changes: 32 additions & 0 deletions src/utils/getContractColor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const getContrastColor = (hexColor: string): string => {
hexColor = hexColor.replace(/^#/, '');

const r = parseInt(hexColor.substring(0, 2), 16);
const g = parseInt(hexColor.substring(2, 4), 16);
const b = parseInt(hexColor.substring(4, 6), 16);

const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;

const colors = [
{ color: '#000000', luminance: 0 }, // black
{ color: '#FFFFFF', luminance: 1 }, // white
{ color: '#FF0000', luminance: 0.2126 }, // red
{ color: '#00FF00', luminance: 0.7152 }, // green
{ color: '#0000FF', luminance: 0.0722 }, // blue
];

let bestColor = colors[0];
let bestContrast = 0;

for (const color of colors) {
const contrast = Math.abs(luminance - color.luminance);
if (contrast > bestContrast) {
bestContrast = contrast;
bestColor = color;
}
}

return bestColor.color;
};

export default getContrastColor;
7 changes: 7 additions & 0 deletions src/utils/replaceEmoticons.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import emoji from "emoji-dictionary";

const replaceEmoticons = (text: string) => {
return text.replace(/:\w+:/g, (match) => emoji.getUnicode(match) || match);
};

export default replaceEmoticons;
1 change: 1 addition & 0 deletions tsconfig.app.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"compilerOptions": {
"typeRoots": ["./node_modules/@types", "./src/types"],
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2020",
"useDefineForClassFields": true,
Expand Down
1 change: 1 addition & 0 deletions tsconfig.node.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"compilerOptions": {
"typeRoots": ["./node_modules/@types", "./src/types"],
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2022",
"lib": ["ES2023"],
Expand Down

0 comments on commit 4022917

Please sign in to comment.