Skip to content

Commit

Permalink
fix: Use all initials to create avatar when setting is `UI_Use_Name_A…
Browse files Browse the repository at this point in the history
…vatar` is true (#34124)

Co-authored-by: gabriellsh <[email protected]>
  • Loading branch information
KevLehman and gabriellsh authored Dec 17, 2024
1 parent e1bb39d commit f750e21
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 8 deletions.
5 changes: 5 additions & 0 deletions .changeset/nervous-fireants-wash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rocket.chat/meteor": patch
---

Allows default avatars to be generated with more than one inital (limited to first 3) when setting `Use Full Name Initials to Generate Default Avatar` is true.
8 changes: 6 additions & 2 deletions apps/meteor/server/routes/avatar/user.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,9 @@ describe('#userAvatarById()', () => {
await userAvatarById(request, response, next);

expect(mocks.utils.setCacheAndDispositionHeaders.calledWith(request, response)).to.be.true;
expect(mocks.utils.serveSvgAvatarInRequestedFormat.calledWith({ nameOrUsername: 'Doe', req: request, res: response })).to.be.true;
expect(
mocks.utils.serveSvgAvatarInRequestedFormat.calledWith({ nameOrUsername: 'Doe', req: request, res: response, useAllInitials: true }),
).to.be.true;
});
});

Expand Down Expand Up @@ -286,6 +288,8 @@ describe('#userAvatarByUsername()', () => {
await userAvatarByUsername(request, response, next);

expect(mocks.utils.setCacheAndDispositionHeaders.calledWith(request, response)).to.be.true;
expect(mocks.utils.serveSvgAvatarInRequestedFormat.calledWith({ nameOrUsername: 'Doe', req: request, res: response })).to.be.true;
expect(
mocks.utils.serveSvgAvatarInRequestedFormat.calledWith({ nameOrUsername: 'Doe', req: request, res: response, useAllInitials: true }),
).to.be.true;
});
});
4 changes: 2 additions & 2 deletions apps/meteor/server/routes/avatar/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export const userAvatarByUsername = async function (request: IncomingMessage, re
});

if (user?.name) {
serveSvgAvatarInRequestedFormat({ nameOrUsername: user.name, req, res });
serveSvgAvatarInRequestedFormat({ nameOrUsername: user.name, req, res, useAllInitials: true });
return;
}
}
Expand Down Expand Up @@ -126,7 +126,7 @@ export const userAvatarById = async function (request: IncomingMessage, res: Ser

// Use real name for SVG letters
if (settings.get('UI_Use_Name_Avatar') && user?.name) {
serveSvgAvatarInRequestedFormat({ nameOrUsername: user.name, req, res });
serveSvgAvatarInRequestedFormat({ nameOrUsername: user.name, req, res, useAllInitials: true });
return;
}

Expand Down
23 changes: 23 additions & 0 deletions apps/meteor/server/routes/avatar/utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,29 @@ describe('#renderSvgLetters', () => {
expect(renderSVGLetters('Bob', 32)).to.include('viewBox="0 0 32 32"');
expect(renderSVGLetters('yan', 64)).to.include('viewBox="0 0 64 64"');
});
it('should return a default size of 125 for a single letter', () => {
expect(renderSVGLetters('a', 200)).to.include('font-size="125"');
});
it('should render a single letter when useAllInitials is false', () => {
expect(renderSVGLetters('arthur', 16, false)).to.include('>\nA\n');
});
it('should render a single letter when useAllInitials is true but username has no spaces', () => {
expect(renderSVGLetters('arthur', 16, true)).to.include('>\nA\n');
});
it('should render more than one letter when useAllInitials is true', () => {
expect(renderSVGLetters('arthur void', 16, true)).to.include('>\nAV\n');
expect(renderSVGLetters('arthur void jackson', 16, true)).to.include('>\nAVJ\n');
});
it('should cap generated avatar to 3 letters at most', () => {
expect(renderSVGLetters('arthur void jackson billie', 16, true)).to.include('>\nAVJ\n');
expect(renderSVGLetters('arthur void jackson billie jean', 16, true)).to.include('>\nAVJ\n');
});
it('should decrease the font size when username has more than 1 word', () => {
expect(renderSVGLetters('arthur void', 200, true)).to.include('font-size="100"');
});
it('should decrease the font size when username has 3 words', () => {
expect(renderSVGLetters('this is three_words', 200, true)).to.include('font-size="80"');
});
});

describe('#setCacheAndDispositionHeaders', () => {
Expand Down
14 changes: 10 additions & 4 deletions apps/meteor/server/routes/avatar/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const cookie = new Cookies();

export const MAX_SVG_AVATAR_SIZE = 1024;
export const MIN_SVG_AVATAR_SIZE = 16;
const MAX_SVG_AVATAR_INITIALS = 3;

export const serveAvatarFile = (file: IUpload, req: IIncomingMessage, res: ServerResponse, next: NextFunction) => {
res.setHeader('Content-Security-Policy', "default-src 'none'");
Expand Down Expand Up @@ -56,13 +57,15 @@ export const serveSvgAvatarInRequestedFormat = ({
nameOrUsername,
req,
res,
useAllInitials = false,
}: {
nameOrUsername: string;
req: IIncomingMessage;
res: ServerResponse;
useAllInitials?: boolean;
}) => {
const size = getAvatarSizeFromRequest(req);
const avatar = renderSVGLetters(nameOrUsername, size);
const avatar = renderSVGLetters(nameOrUsername, size, useAllInitials);
res.setHeader('Last-Modified', FALLBACK_LAST_MODIFIED);

const { format } = req.query;
Expand Down Expand Up @@ -125,7 +128,9 @@ const getFirstLetter = (name: string) =>
.substr(0, 1)
.toUpperCase();

export const renderSVGLetters = (username: string, viewSize = 200) => {
const getInitials = (name: string) => name.split(' ').slice(0, MAX_SVG_AVATAR_INITIALS).map(getFirstLetter).join('');

export const renderSVGLetters = (username: string, viewSize = 200, useAllInitials = false) => {
let color = '';
let initials = '';

Expand All @@ -134,10 +139,11 @@ export const renderSVGLetters = (username: string, viewSize = 200) => {
initials = username;
} else {
color = getAvatarColor(username);
initials = getFirstLetter(username);
initials = !useAllInitials ? getFirstLetter(username) : getInitials(username);
}

const fontSize = viewSize / 1.6;
const reductionFactor = initials.length > 1 ? Math.pow(initials.length, 2) / 10 : 0;
const fontSize = viewSize / (1.6 + reductionFactor);

return `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 ${viewSize} ${viewSize}\">\n<rect width=\"100%\" height=\"100%\" fill=\"${color}\"/>\n<text x=\"50%\" y=\"50%\" dy=\"0.36em\" text-anchor=\"middle\" pointer-events=\"none\" fill=\"#ffffff\" font-family=\"'Helvetica', 'Arial', 'Lucida Grande', 'sans-serif'\" font-size="${fontSize}">\n${initials}\n</text>\n</svg>`;
};
Expand Down

0 comments on commit f750e21

Please sign in to comment.