From 4022917e7d5071d5d992185b5b80855ddba4f3d9 Mon Sep 17 00:00:00 2001 From: AKharytonchyk Date: Fri, 8 Nov 2024 09:19:51 +0100 Subject: [PATCH] feat: Lable coloring --- package-lock.json | 80 +++++++++++++++++++++++ package.json | 1 + src/components/MultiselectFilter.tsx | 5 +- src/components/PullRequestCard.tsx | 97 +++++++++++++++++++++------- src/service/gitService.ts | 9 ++- src/types/emoji-dictionary.d.ts | 6 ++ src/utils/RateLimiterQueue.ts | 12 +++- src/utils/getContractColor.ts | 32 +++++++++ src/utils/replaceEmoticons.ts | 7 ++ tsconfig.app.json | 1 + tsconfig.node.json | 1 + 11 files changed, 221 insertions(+), 30 deletions(-) create mode 100644 src/types/emoji-dictionary.d.ts create mode 100644 src/utils/getContractColor.ts create mode 100644 src/utils/replaceEmoticons.ts diff --git a/package-lock.json b/package-lock.json index ec3d94b..93cf4d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,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", @@ -2655,6 +2656,70 @@ "dev": true, "license": "ISC" }, + "node_modules/emoji-chars": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/emoji-chars/-/emoji-chars-1.0.12.tgz", + "integrity": "sha512-1t7WbkKzQ1hV4dHWM4u8g0SFHSAbxx+8o/lvXisDLTesljSFaxl2wLgMtx4wH922sNcIuLbFty/AuqUDJORd1A==", + "license": "MIT", + "dependencies": { + "emoji-unicode-map": "^1.0.0" + } + }, + "node_modules/emoji-dictionary": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/emoji-dictionary/-/emoji-dictionary-1.0.11.tgz", + "integrity": "sha512-pVTiN0StAU2nYy+BtcX/88DavMDjUcONIA6Qsg7/IyDq8xOsRFuC49F7XLUPr7Shlz4bt0/RAqPjuqjpsj3vbA==", + "license": "MIT", + "dependencies": { + "emoji-chars": "^1.0.0", + "emoji-name-map": "^1.0.0", + "emoji-names": "^1.0.1", + "emoji-unicode-map": "^1.0.0", + "emojilib": "^2.0.2" + } + }, + "node_modules/emoji-dictionary/node_modules/emojilib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", + "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==", + "license": "MIT" + }, + "node_modules/emoji-name-map": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/emoji-name-map/-/emoji-name-map-1.2.9.tgz", + "integrity": "sha512-MSM8y6koSqh/2uEMI2VoKA+Ac0qL5RkgFGP/pzL6n5FOrOJ7FOZFxgs7+uNpqA+AT+WmdbMPXkd3HnFXXdz4AA==", + "license": "MIT", + "dependencies": { + "emojilib": "^2.0.2", + "iterate-object": "^1.3.1", + "map-o": "^2.0.1" + } + }, + "node_modules/emoji-name-map/node_modules/emojilib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", + "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==", + "license": "MIT" + }, + "node_modules/emoji-names": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/emoji-names/-/emoji-names-1.0.12.tgz", + "integrity": "sha512-ABXVMPYU9h1/0lNNE9VaT9OxxWXXAv/By8gVMzQYIx7jrhWjyLFVyC34CAN+EP/1e+5WZCklvClo5KSklPCAeg==", + "license": "MIT", + "dependencies": { + "emoji-name-map": "^1.0.0" + } + }, + "node_modules/emoji-unicode-map": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/emoji-unicode-map/-/emoji-unicode-map-1.1.11.tgz", + "integrity": "sha512-GWcWILFyDfR8AU7FRLhKk0lnvcljoEIXejg+XY3Ogz6/ELaQLMo0m4d9R3i79ikIULVEysHBGPsOEcjcFxtN+w==", + "license": "MIT", + "dependencies": { + "emoji-name-map": "^1.1.0", + "iterate-object": "^1.3.1" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -3702,6 +3767,12 @@ "dev": true, "license": "ISC" }, + "node_modules/iterate-object": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/iterate-object/-/iterate-object-1.3.4.tgz", + "integrity": "sha512-4dG1D1x/7g8PwHS9aK6QV5V94+ZvyP4+d19qDv43EzImmrndysIl4prmJ1hWWIGCqrZHyaHBm6BSEWHOLnpoNw==", + "license": "MIT" + }, "node_modules/jest-diff": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", @@ -3886,6 +3957,15 @@ "lz-string": "bin/bin.js" } }, + "node_modules/map-o": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/map-o/-/map-o-2.0.10.tgz", + "integrity": "sha512-BxazE81fVByHWasyXhqKeo2m7bFKYu+ZbEfiuexMOnklXW+tzDvnlTi/JaklEeuuwqcqJzPaf9q+TWptSGXeLg==", + "license": "MIT", + "dependencies": { + "iterate-object": "^1.3.0" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", diff --git a/package.json b/package.json index b38248a..fd0ef30 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/components/MultiselectFilter.tsx b/src/components/MultiselectFilter.tsx index 6f9736e..87b759f 100644 --- a/src/components/MultiselectFilter.tsx +++ b/src/components/MultiselectFilter.tsx @@ -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; @@ -60,7 +61,7 @@ export const MultiselectFilter: React.FC = ({ renderValue={(selected) => ( {selected.map((value) => ( - + ))} )} @@ -68,7 +69,7 @@ export const MultiselectFilter: React.FC = ({ > {options.map((option) => ( - {option} + {replaceEmoticons(option)} ))} diff --git a/src/components/PullRequestCard.tsx b/src/components/PullRequestCard.tsx index 7899c40..d4a3fc5 100644 --- a/src/components/PullRequestCard.tsx +++ b/src/components/PullRequestCard.tsx @@ -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; @@ -32,14 +34,6 @@ const PullRequestCard: React.FC = ({ 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 ( @@ -58,9 +52,22 @@ const PullRequestCard: React.FC = ({ pr }) => { size="small" sx={{ marginRight: "auto" }} /> - {pr.locked && } - {pr.draft && } - {pr.labels.map((label) => ( ))} + {pr.locked && } + {pr.draft && ( + + + + )} + + {pr.labels.map((label) => ( + + ))} + = ({ pr }) => { sx={{ marginY: 1 }} /> - + PR by {pr.user.login} - #{pr.number} {pr.title} + + #{pr.number} + {" "} + {pr.title} - - - Days in review:{" "} + + + Days in review: = ({ pr }) => { /> {"|"} - + {"|"} - + {"|"} - - - - + + + + + + + + + + + + diff --git a/src/service/gitService.ts b/src/service/gitService.ts index d749754..3e6a36b 100644 --- a/src/service/gitService.ts +++ b/src/service/gitService.ts @@ -71,7 +71,8 @@ export class GitService { repo, ref: `pull/${prNumber}/head`, filter: "latest", - }) + }), + true ); } @@ -81,7 +82,8 @@ export class GitService { owner, repo, pull_number: prNumber, - }) + }), + true ); return mergeConflicts.data; } @@ -92,7 +94,8 @@ export class GitService { owner, repo, pull_number: prNumber, - }) + }), + true ); if ( diff --git a/src/types/emoji-dictionary.d.ts b/src/types/emoji-dictionary.d.ts new file mode 100644 index 0000000..9b9728a --- /dev/null +++ b/src/types/emoji-dictionary.d.ts @@ -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[]; +} diff --git a/src/utils/RateLimiterQueue.ts b/src/utils/RateLimiterQueue.ts index a144a69..00f9b8b 100644 --- a/src/utils/RateLimiterQueue.ts +++ b/src/utils/RateLimiterQueue.ts @@ -15,9 +15,9 @@ class RateLimiterQueue { }, 60000); } - async enqueue(requestFunction: () => Promise): Promise { + async enqueue(requestFunction: () => Promise, unshift = false): Promise { return new Promise((resolve, reject) => { - this.queue.push(async () => { + const queueAction = async () => { try { this.activeRequests++; const result = await requestFunction(); @@ -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(); }); } diff --git a/src/utils/getContractColor.ts b/src/utils/getContractColor.ts new file mode 100644 index 0000000..d688304 --- /dev/null +++ b/src/utils/getContractColor.ts @@ -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; diff --git a/src/utils/replaceEmoticons.ts b/src/utils/replaceEmoticons.ts new file mode 100644 index 0000000..0a952b4 --- /dev/null +++ b/src/utils/replaceEmoticons.ts @@ -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; diff --git a/tsconfig.app.json b/tsconfig.app.json index 90a1a7b..bf8c564 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "typeRoots": ["./node_modules/@types", "./src/types"], "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", "target": "ES2020", "useDefineForClassFields": true, diff --git a/tsconfig.node.json b/tsconfig.node.json index abcd7f0..8bd7e13 100644 --- a/tsconfig.node.json +++ b/tsconfig.node.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "typeRoots": ["./node_modules/@types", "./src/types"], "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", "target": "ES2022", "lib": ["ES2023"],