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

New Feature for RepoCard: Showing Language Bar for multiple languages #4026

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions api/pin.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
CONSTANTS,
parseBoolean,
renderError,
parseArray,
} from "../src/common/utils.js";
import { fetchRepo } from "../src/fetchers/repo-fetcher.js";
import { isLocaleAvailable } from "../src/translations.js";
Expand All @@ -25,6 +26,10 @@ export default async (req, res) => {
border_radius,
border_color,
description_lines_count,
show_lang_bar,
hide,
langs_count,
hide_progress,
} = req.query;

res.setHeader("Content-Type", "image/svg+xml");
Expand Down Expand Up @@ -83,6 +88,10 @@ export default async (req, res) => {
show_owner: parseBoolean(show_owner),
locale: locale ? locale.toLowerCase() : null,
description_lines_count,
show_lang_bar: parseBoolean(show_lang_bar),
hide: parseArray(hide),
langs_count,
hide_progress: parseBoolean(hide_progress),
}),
);
} catch (err) {
Expand Down
69 changes: 67 additions & 2 deletions src/cards/repo-card.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,16 @@ import {
clampValue,
} from "../common/utils.js";
import { repoCardLocales } from "../translations.js";
import {
renderCompactLayout,
trimTopLanguages,
getDefaultLanguagesCountByLayout,
} from "./top-languages-card.js";

const ICON_SIZE = 16;
const DESCRIPTION_LINE_WIDTH = 59;
const DESCRIPTION_MAX_LINES = 3;

const CARD_PADDING = 25;
/**
* Retrieves the repository description and wraps it to fit the card width.
*
Expand All @@ -42,6 +47,18 @@ const getBadgeSVG = (label, textColor) => `
</g>
`;

/**
* Calculates extra height needed for the languages bar.
*
* @param {number} totalLangs Total number of languages.
* @param {boolean} hideProgress Flag to hide progress bar.
* @returns {number} Card height.
*/
const calculateExtraLangHeigth = (totalLangs, hideProgress) => {
const baseHeight = Math.ceil(totalLangs / 2) * 25;
return hideProgress ? baseHeight - 30 : baseHeight;
};

/**
* @typedef {import("../fetchers/types").RepositoryData} RepositoryData Repository data.
* @typedef {import("./types").RepoCardOptions} RepoCardOptions Repo card options.
Expand All @@ -64,6 +81,7 @@ const renderRepoCard = (repo, options = {}) => {
isTemplate,
starCount,
forkCount,
languagesBreakdown,
} = repo;
const {
hide_border = false,
Expand All @@ -77,8 +95,19 @@ const renderRepoCard = (repo, options = {}) => {
border_color,
locale,
description_lines_count,
show_lang_bar = false,
hide_progress = false,
layout = "compact", // add more layouts in the future
langs_count = getDefaultLanguagesCountByLayout({ layout, hide_progress }),
hide,
} = options;

const { langs, totalLanguageSize } = trimTopLanguages(
languagesBreakdown,
langs_count,
hide,
);

const lineHeight = 10;
const header = show_owner ? nameWithOwner : name;
const langName = (primaryLanguage && primaryLanguage.name) || "Unspecified";
Expand Down Expand Up @@ -149,11 +178,22 @@ const renderRepoCard = (repo, options = {}) => {
gap: 25,
}).join("");

const languageBar = renderCompactLayout(
langs,
400,
totalLanguageSize,
hide_progress,
);

const card = new Card({
defaultTitle: header.length > 35 ? `${header.slice(0, 35)}...` : header,
titlePrefixIcon: icons.contribs,
width: 400,
height,
height:
height +
(show_lang_bar
? calculateExtraLangHeigth(langs.length, hide_progress)
: 0),
border_radius,
colors,
});
Expand All @@ -167,8 +207,32 @@ const renderRepoCard = (repo, options = {}) => {
.icon { fill: ${colors.iconColor} }
.badge { font: 600 11px 'Segoe UI', Ubuntu, Sans-Serif; }
.badge rect { opacity: 0.2 }
.lang-name { font: 400 11px "Segoe UI", Ubuntu, Sans-Serif; fill: ${colors.textColor} }
`);

if (show_lang_bar) {
return card.render(`
${
isTemplate
? // @ts-ignore
getBadgeSVG(i18n.t("repocard.template"), colors.textColor)
: isArchived
? // @ts-ignore
getBadgeSVG(i18n.t("repocard.archived"), colors.textColor)
: ""
}

<text class="description" x="25" y="-5">
${descriptionSvg}
</text>
<g transform="translate(0, ${height - 90})">
<svg data-testid="lang-items" x="${CARD_PADDING}">
${languageBar}
</svg>
</g>
`);
}

return card.render(`
${
isTemplate
Expand All @@ -187,6 +251,7 @@ const renderRepoCard = (repo, options = {}) => {
<g transform="translate(30, ${height - 75})">
${starAndForkCount}
</g>

`);
};

Expand Down
1 change: 1 addition & 0 deletions src/cards/top-languages-card.js
Original file line number Diff line number Diff line change
Expand Up @@ -871,6 +871,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
};

export {
renderCompactLayout,
getLongestLang,
degreesToRadians,
radiansToDegrees,
Expand Down
5 changes: 5 additions & 0 deletions src/cards/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ export type StatCardOptions = CommonOptions & {
export type RepoCardOptions = CommonOptions & {
show_owner: boolean;
description_lines_count: number;
show_lang_bar: boolean;
hide_progress: boolean;
layout: "compact";
langs_count: number;
hide: string[];
};

export type TopLangOptions = CommonOptions & {
Expand Down
58 changes: 58 additions & 0 deletions src/fetchers/repo-fetcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,38 @@ const fetcher = (variables, token) => {
},
);
};
/**
* Language data fetcher.
*
* @param {AxiosRequestHeaders} variables Fetcher variables.
* @param {string} token GitHub token.
* @returns {Promise<AxiosResponse>} The response.
*/
const fetcherLanguage = (variables, token) => {
return request(
{
query: `
query getRepoLanguages($login: String!, $repo: String!) {
repository(owner: $login, name: $repo) {
languages(first: 10) {
edges {
node {
name
color
}
size
}
}
}
}
`,
variables,
},
{
Authorization: `token ${token}`,
},
);
};

const urlExample = "/api/pin?username=USERNAME&amp;repo=REPO_NAME";

Expand Down Expand Up @@ -91,13 +123,38 @@ const fetchRepo = async (username, reponame) => {
const isUser = data.organization === null && data.user;
const isOrg = data.user === null && data.organization;

let resLanguage = await retryer(fetcherLanguage, {
login: username,
repo: reponame,
});

const data_languages = resLanguage.data.data;

const toplanguages = data_languages.repository.languages.edges.reduce(
(acc, edge) => {
const { name, color } = edge.node;
const size = edge.size;

if (acc[name]) {
acc[name].size += size;
acc[name].count += 1;
} else {
acc[name] = { name, color, size, count: 1 };
}

return acc;
},
{},
);

if (isUser) {
if (!data.user.repository || data.user.repository.isPrivate) {
throw new Error("User Repository Not found");
}
return {
...data.user.repository,
starCount: data.user.repository.stargazers.totalCount,
languagesBreakdown: toplanguages,
};
}

Expand All @@ -111,6 +168,7 @@ const fetchRepo = async (username, reponame) => {
return {
...data.organization.repository,
starCount: data.organization.repository.stargazers.totalCount,
languagesBreakdown: toplanguages,
};
}

Expand Down
1 change: 1 addition & 0 deletions src/fetchers/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export type RepositoryData = {
id: string;
name: string;
};
languagesBreakdown: TopLangData;
forkCount: number;
starCount: number;
};
Expand Down
Loading