diff --git a/README.md b/README.md index 26604ae..dced2d5 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,7 @@ nautilus | serika * [x] Add Skills: Software & IDEs widget * [ ] Make autobuilder instead of manual build and push * [ ] Count organization repositories (+ their stars) -* [ ] Truncate name if too long on profile widget +* [x] Truncate name if too long on profile widget * [ ] Make all widgets a modular size * [ ] Add Profile Tag widget * [ ] Add Skill Tag widget diff --git a/public/src/widgets/profile.js b/public/src/widgets/profile.js index f49752b..763e267 100644 --- a/public/src/widgets/profile.js +++ b/public/src/widgets/profile.js @@ -12,31 +12,52 @@ const user_stats_fetcher_1 = __importDefault(require("../fetchers/user-stats-fet const themes_1 = __importDefault(require("../data/themes")); async function profileWidget(username, data, themeString) { // Set the theme - let theme = utils_1.getTheme(themes_1.default, 'default'); + let theme = (0, utils_1.getTheme)(themes_1.default, 'default'); if (themeString) { - theme = utils_1.getTheme(themes_1.default, themeString); + theme = (0, utils_1.getTheme)(themes_1.default, themeString); } if (!theme) { - theme = utils_1.getTheme(themes_1.default, 'default'); + theme = (0, utils_1.getTheme)(themes_1.default, 'default'); } const dataOptions = data.split(','); // Return error if dataOptions argument is undefined if (dataOptions === undefined) { return new Promise((res) => { - res(error_1.default('Profile', '-25%', 'Data option is missing!', '-25%')); + res((0, error_1.default)('Profile', '-25%', 'Data option is missing!', '-25%')); }); } // Return error if more than 4 dataOptions were supplied if (dataOptions.length > 4) { return new Promise((res) => { - res(error_1.default('Profile', '-25%', `Can't have more than 4 data-options!`, '-40%')); + res((0, error_1.default)('Profile', '-25%', `Can't have more than 4 data-options!`, '-40%')); }); } const width = 842; const height = 165; + // Escapes characters that would otherwise break the SVG's XML structure. + function escapeXml(value) { + return value + .replace(/&/g, '&') + .replace(//g, '>'); + } + // Truncates the display name so it never runs into the data boxes on the right, + // which shift further left the more data options are requested. + function truncateName(name) { + const CHAR_WIDTH = 13; + const NAME_START_X = 145; + const NAME_RIGHT_MARGIN = 20; + const dataBoxesWidth = dataOptions.length * 108; + const availableWidth = width - 52 - dataBoxesWidth - NAME_START_X - NAME_RIGHT_MARGIN; + const maxChars = Math.max(1, Math.floor(availableWidth / CHAR_WIDTH)); + if (name.length <= maxChars) { + return name; + } + return name.slice(0, Math.max(1, maxChars - 1)) + '…'; + } async function getDataOptions() { let dataBoxes = ''; - const profile = await user_stats_fetcher_1.default(process.env.GITHUB_TOKEN, username); + const profile = await (0, user_stats_fetcher_1.default)(process.env.GITHUB_TOKEN, username); const stargazers = []; profile.data.user.repositories.nodes.forEach((repo, index) => { stargazers[index] = repo.stargazers.totalCount; @@ -63,7 +84,7 @@ async function profileWidget(username, data, themeString) { // Incorrect data item found default: return new Promise((res) => { - res(error_1.default('Profile', '-25%', `Invalid data item found!`, '-26%')); + res((0, error_1.default)('Profile', '-25%', `Invalid data item found!`, '-26%')); }); } } @@ -90,20 +111,20 @@ async function profileWidget(username, data, themeString) { // Create the request const response = await axios_1.default.get(`https://api.github.com/users/${username}`); // Grab the avatar - const avatar = await utils_1.requestInBase64(response.data.avatar_url); + const avatar = await (0, utils_1.requestInBase64)(response.data.avatar_url); return ` - ${card_1.default(width, height, theme.background)} + ${(0, card_1.default)(width, height, theme.background)} - ${response.data.name === null + ${escapeXml(truncateName(response.data.name === null ? response.data.login - : response.data.name} - GitHub.com/${response.data.login} + : response.data.name))} + GitHub.com/${escapeXml(response.data.login)} ${dataBoxes} @@ -112,7 +133,7 @@ async function profileWidget(username, data, themeString) { } catch (error) { return new Promise((res) => { - res(error_1.default('Profile', '-25%', 'GitHub API-call error!', '-24%')); + res((0, error_1.default)('Profile', '-25%', 'GitHub API-call error!', '-24%')); }); } } diff --git a/public/src/widgets/profile.js.map b/public/src/widgets/profile.js.map index f021809..4a73631 100644 --- a/public/src/widgets/profile.js.map +++ b/public/src/widgets/profile.js.map @@ -1 +1 @@ -{"version":3,"file":"profile.js","sourceRoot":"","sources":["../../../src/widgets/profile.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"profile.js","sourceRoot":"","sources":["../../../src/widgets/profile.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/src/widgets/profile.ts b/src/widgets/profile.ts index 08a5c96..4ba58b6 100644 --- a/src/widgets/profile.ts +++ b/src/widgets/profile.ts @@ -59,6 +59,31 @@ export default async function profileWidget( const width = 842 const height = 165 + // Escapes characters that would otherwise break the SVG's XML structure. + function escapeXml(value: string): string { + return value + .replace(/&/g, '&') + .replace(//g, '>') + } + + // Truncates the display name so it never runs into the data boxes on the right, + // which shift further left the more data options are requested. + function truncateName(name: string): string { + const CHAR_WIDTH = 13 + const NAME_START_X = 145 + const NAME_RIGHT_MARGIN = 20 + const dataBoxesWidth = dataOptions.length * 108 + const availableWidth = + width - 52 - dataBoxesWidth - NAME_START_X - NAME_RIGHT_MARGIN + const maxChars = Math.max(1, Math.floor(availableWidth / CHAR_WIDTH)) + + if (name.length <= maxChars) { + return name + } + return name.slice(0, Math.max(1, maxChars - 1)) + '…' + } + async function getDataOptions(): Promise { let dataBoxes = '' @@ -187,12 +212,16 @@ export default async function profileWidget( ${buildCard(width, height, theme.background)} - ${response.data.name === null - ? response.data.login - : response.data.name - } - GitHub.com/${response.data.login - } + ${escapeXml( + truncateName( + response.data.name === null + ? response.data.login + : response.data.name + ) + )} + GitHub.com/${escapeXml( + response.data.login + )} ${dataBoxes}