Skip to content

Commit 3396d15

Browse files
authored
feat: display custom uploaded emojis WEB-2500 (#621)
* feat: display better user mention WEB-2500 - smaller size to better align with regular text - better padding to avoid overlapping with the background color - remove the hover effect * test: remove temporary speedSteps - it was not working and it was consuming a lot of time in testing - to review later on with a proper strategy for performance unit testing * feat: display custom uploaded emojis - scss style to display emoji aligned to first line of the title of the page - new env variable CPV_CONFLUENCE_EMOJI_COLLECTION to define the emoji collection for uploaded icons - unofficial API to retrieve Atlassian and custom uploaded emojis - fix emojis in the body of the page displaying also custom uploaded icons - fix emojis in the title of the page displaying also custom uploaded icons * refactor: improve code as per sonarqube reco * test: refactor conditional operation resolves WEB-2500
1 parent 66b6896 commit 3396d15

17 files changed

+134
-127
lines changed

src/assets/scss/_links.scss

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,15 +92,13 @@ img.link-card-image {
9292

9393
// User mention with @ ===================================
9494
a.user-mention {
95+
color: var(--user-mention-color);
9596
background: var(--user-mention-bg);
96-
padding: 2px;
97-
border-radius: 15px;
98-
color: var(--font-color);
9997
font-size: var(--user-mention-size);
98+
padding: 1 8 2 5;
99+
margin-left: 1;
100+
border-radius: 15px;
100101
white-space: nowrap;
101-
&:hover {
102-
color: white;
103-
}
104102
&::before {
105103
content: '@ ';
106104
}

src/assets/scss/_macro-roadmap.scss

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
// Title with link to Page
99
.roadmap-macro-view .bar-title-page {
10-
padding-top: 10px;
1110
overflow: hidden;
1211
text-overflow: ellipsis;
1312
text-align: center;
@@ -17,4 +16,10 @@
1716
.roadmap-macro-view .bar-title {
1817
line-height: 12px !important;
1918
cursor: auto !important;
19+
a {
20+
color: var(--highlight-green);
21+
}
22+
a:hover {
23+
font-weight: bold;
24+
}
2025
}

src/assets/scss/_variables.scss

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ $small-text: 12px;
2222
--main-font-size: #{$main-font-screen};
2323
--main-font-weight: #{$main-font-weight};
2424
--title-font-size: #{$title-font-screen};
25-
--user-mention-bg: #92b5f7;
2625
--primary-color: #a262c2;
2726
--primary-color-link: #2e95c4;
2827
--secondary-color: #7785ab;
@@ -35,6 +34,9 @@ $small-text: 12px;
3534
--highlight-green: #ddfade;
3635
--highlight-red: #ffe7e7;
3736
--highlight-yellow: #ffffdd;
37+
--user-mention-bg: #d7edd3;
38+
--user-mention-size: #{$main-font-mobile};
39+
--user-mention-color: #424242;
3840
--reading-progress-color: #61a786;
3941
--toc-background-color:#efd2f9;
4042
--toc-color: #d786f2;
@@ -91,7 +93,6 @@ $small-text: 12px;
9193
}
9294

9395
[data-theme='dark'] {
94-
--user-mention-bg: #75a2f7;
9596
--primary-color: #f1f1f1; // #9a97f3;
9697
--primary-color-link: #9792f5;
9798
--secondary-color: #818cab;
@@ -104,6 +105,9 @@ $small-text: 12px;
104105
--highlight-green: #2e8e31;
105106
--highlight-red: #9f2f2f;
106107
--highlight-yellow: #ebeb3f;
108+
--user-mention-bg: #d7edd3;
109+
--user-mention-size: #{$main-font-mobile};
110+
--user-mention-color: #424242;
107111

108112
// external links
109113
// or alternatively just an emoji works --external-link-symbol: '⇢';

src/assets/scss/custom.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ h1.titlePage {
4242
padding-top: 0.8rem;
4343

4444
& .specialAtlassian {
45-
display: flex;
45+
display: inline-block;
4646
align-items: center;
4747
img {
4848
width: 32px;

src/config/config.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ interface Config {
1818
apiToken: string;
1919
apiTimeOut: string;
2020
apiMaxRedirects: string;
21+
emojiCollection: string;
2122
};
2223

2324
konviw: {

src/config/configuration.test.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,40 @@ export default (): Config => ({
44
env: 'test',
55
version: '1.2.0',
66
web: {
7-
port: (process.env.PORT && parseInt(process.env.PORT, 10)) || 3000,
7+
port: (process.env.PORT && parseInt(process.env.PORT, 10)) ?? 3000,
88
basePath: '/cpv',
9-
baseHost: process.env.CPV_BASEHOST || '',
9+
baseHost: process.env.CPV_BASEHOST ?? '',
1010
absoluteBasePath: `${process.env.CPV_BASEHOST}${process.env.CPV_BASEPATH}`,
1111
},
1212
confluence: {
1313
baseURL: 'https://test.atlassian.net',
14-
apiUsername: process.env.CPV_CONFLUENCE_API_USERNAME || '',
15-
apiToken: process.env.CPV_CONFLUENCE_API_TOKEN || '',
14+
apiUsername: process.env.CPV_CONFLUENCE_API_USERNAME ?? '',
15+
apiToken: process.env.CPV_CONFLUENCE_API_TOKEN ?? '',
1616
apiTimeOut: process.env.CPV_CONFLUENCE_API_TIMEOUT,
17-
apiMaxRedirects: process.env.CPV_CONFLUENCE_API_MAX_REDIRECTS || '5',
17+
apiMaxRedirects: process.env.CPV_CONFLUENCE_API_MAX_REDIRECTS ?? '5',
18+
emojiCollection: process.env.CPV_CONFLUENCE_EMOJI_COLLECTION ?? '',
1819
},
1920
konviw: {
2021
private: process.env.CPV_KONVIW_PRIVATE_PAGE,
2122
},
2223
matomo: {
23-
baseURL: process.env.CPV_MATOMO_BASE_URL || '',
24-
idSite: process.env.CPV_MATOMO_ID_SITE || '',
24+
baseURL: process.env.CPV_MATOMO_BASE_URL ?? '',
25+
idSite: process.env.CPV_MATOMO_ID_SITE ?? '',
2526
},
2627
google: {
27-
tag: process.env.CPV_GOOGLE_ANALYTICS || '',
28+
tag: process.env.CPV_GOOGLE_ANALYTICS ?? '',
2829
},
2930
cache: {
3031
cacheTTL:
31-
(process.env.CACHE_TTL && parseInt(process.env.CACHE_TTL, 10)) || 86400,
32+
(process.env.CACHE_TTL && parseInt(process.env.CACHE_TTL, 10)) ?? 86400,
3233
cacheMax:
33-
(process.env.CACHE_MAX && parseInt(process.env.CACHE_MAX, 10)) || 10,
34+
(process.env.CACHE_MAX && parseInt(process.env.CACHE_MAX, 10)) ?? 10,
3435
},
3536
logging: {
3637
enableLoggerMiddleware: process.env.ENABLE_LOGGER_MIDDLEWARE === 'true',
3738
},
3839
jiraIssues: {
39-
apiReaderUsername: process.env.CPV_JIRA_READER_API_USERNAME || '',
40-
apiReaderToken: process.env.CPV_JIRA_READER_API_TOKEN || '',
40+
apiReaderUsername: process.env.CPV_JIRA_READER_API_USERNAME ?? '',
41+
apiReaderToken: process.env.CPV_JIRA_READER_API_TOKEN ?? '',
4142
},
4243
});

src/config/configuration.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export default (): Config => ({
1717
apiToken: process.env.CPV_CONFLUENCE_API_TOKEN,
1818
apiTimeOut: process.env.CPV_CONFLUENCE_API_TIMEOUT,
1919
apiMaxRedirects: process.env.CPV_CONFLUENCE_API_MAX_REDIRECTS || '5',
20+
emojiCollection: process.env.CPV_CONFLUENCE_EMOJI_COLLECTION,
2021
},
2122
konviw: {
2223
private: process.env.CPV_KONVIW_PRIVATE_PAGE,

src/confluence/confluence.service.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export class ConfluenceService {
5050
if (contentType) {
5151
const params = { version };
5252

53-
params['space-id'] = (spaceContent) ? spaceContent.id : null;
53+
params['space-id'] = spaceContent?.id ?? null;
5454
// get-draft parameter expected by the new API v2
5555
// https://developer.atlassian.com/cloud/confluence/rest/v2/api-group-page/#api-pages-id-get
5656
params['get-draft'] = (status === 'draft');
@@ -63,7 +63,7 @@ export class ConfluenceService {
6363

6464
const [authorContent, versionAuthorContent] = await Promise.all([
6565
this.getAccountDataById((pageContent as Content['pageContent']).ownerId
66-
?? (pageContent as Content['pageContent']).authorId),
66+
?? (pageContent as Content['pageContent']).authorId),
6767
this.getAccountDataById((pageContent as Content['pageContent']).version.authorId),
6868
]);
6969

@@ -394,11 +394,36 @@ export class ConfluenceService {
394394
);
395395
const results = response.data?.emojis ?? [];
396396
if (image) {
397-
return results.find(({ id }) => id === image);
397+
const imageData = results.find(({ id }) => id === image);
398+
const { imagePath } = imageData.representation;
399+
return imagePath;
398400
}
399401
return results;
400402
}
401403

404+
async getSpecialUploadedIcons(image?: string): Promise<any> {
405+
const response: AxiosResponse = await firstValueFrom(
406+
// Special custom emojis are uploaded to a specific collection per site, so we set it up
407+
// via env variable emojiCollection
408+
this.http.get<Content>(
409+
`/gateway/api/emoji/${this.config.get('confluence.emojiCollection')}/site`
410+
+ '?scale=XHDPI&altScale=XXXHDPI&preferredRepresentation=IMAGE',
411+
),
412+
);
413+
// retrieve the custom uploaded emojis and image path
414+
const results = response.data?.emojis ?? [];
415+
// retrieve the metadata for retrieving the images from the media library, specially JWT token and client
416+
const meta = response.data?.meta ?? {};
417+
if (image) {
418+
const imageData = results.find(({ id }) => id === image);
419+
const baseImagePath = imageData.representation.imagePath;
420+
const imagePath = `${baseImagePath}&token=${meta.mediaApiToken.jwt}&client=${meta.mediaApiToken.clientId}`;
421+
422+
return imagePath;
423+
}
424+
return response.data;
425+
}
426+
402427
private async getSpacesAccountByPermissions(data) {
403428
const permissionsDefinedAsUser = data.filter((permission) => permission.principal.type === 'user');
404429
return Promise.all(permissionsDefinedAsUser.map(async (permission) => ({

src/context/context.interface.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,11 @@ export interface User {
1212
}
1313

1414
export type ApiVersion = 'v1' | 'v2';
15+
16+
export type IconType = 'atlassian' | 'upload' | 'standard';
17+
18+
export interface EmojiType {
19+
code: string;
20+
type: IconType;
21+
path: string;
22+
}

src/context/context.service.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import * as cheerio from 'cheerio';
33
import { performance, PerformanceObserver } from 'perf_hooks';
44
import { ConfigService } from '@nestjs/config';
55
import { Content, Label } from '../confluence/confluence.interface';
6-
import { ApiVersion, Version } from './context.interface';
6+
import {
7+
ApiVersion, Version, EmojiType, IconType,
8+
} from './context.interface';
79
import {
810
contentAppearancePublishedHelper,
911
coverPictureIdPublishedHelper,
@@ -78,7 +80,7 @@ export class ContextService {
7880

7981
private headerImage = '';
8082

81-
private headerEmoji = '';
83+
private headerEmoji : EmojiType;
8284

8385
private observer: PerformanceObserver;
8486

@@ -155,7 +157,7 @@ export class ContextService {
155157
if (emojiTitlePublished) {
156158
this.setHeaderEmoji(emojiTitlePublished);
157159
logger.log(
158-
`GET emoji-title-published to set context 'headerEmoji' to ${this.getHeaderEmoji()}`,
160+
`GET emoji-title-published to set context 'headerEmoji' to ${this.getHeaderEmoji().code}`,
159161
);
160162
} else {
161163
this.setHeaderEmoji('');
@@ -383,11 +385,26 @@ export class ContextService {
383385
return this.headerImage;
384386
}
385387

386-
setHeaderEmoji(code: string): void {
387-
this.headerEmoji = code ? `&#x${code};` : '';
388+
setHeaderEmoji(code: string) {
389+
let emojiType : IconType = 'standard';
390+
let hexCode = '';
391+
if (code.length > 12) {
392+
// Either is a special emoji from Atlassian with ID like 'atlassian-logo_confluence'
393+
// or a manually uploaded one with ID like '16183a4b-bad2-4f3f-8c7c-3fd9d1c1ccdf'
394+
emojiType = code.startsWith('atlassian') ? 'atlassian' : 'upload';
395+
this.headerEmoji = { code, type: emojiType, path: '' };
396+
} else {
397+
// ! unclear and to be determined when this is yet needed (probably for a certain type of emoji
398+
hexCode = `&#x${code};`;
399+
if (hexCode === '&#x;') {
400+
this.headerEmoji = { code: '', type: emojiType, path: '' };
401+
} else {
402+
this.headerEmoji = { code: hexCode, type: emojiType, path: '' };
403+
}
404+
}
388405
}
389406

390-
getHeaderEmoji(): string {
407+
getHeaderEmoji(): EmojiType {
391408
return this.headerEmoji;
392409
}
393410

src/jira/jira.service.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,8 @@ export class JiraService {
136136
this.apiToken = process.env[`${key}_API_TOKEN`];
137137
}
138138
}
139-
const url = `${this.baseUrl}/rest/api/3/search?jql=${encodeURIComponent(jqlSearch)}&fields=${fields}&maxResults=${maxResult}&startAt=${startAt}&expand=${expand}`;
139+
const url = `${this.baseUrl}/rest/api/3/search?jql=${encodeURIComponent(jqlSearch)}`
140+
+ `&fields=${fields}&maxResults=${maxResult}&startAt=${startAt}&expand=${expand}`;
140141
this.logger.log(
141142
`endpoint findtickets - URL: ${url} - Confluence Username: ${this.apiUsername}`,
142143
);

src/proxy-api/dto/GetCustomContentsInSpaceQuery.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { ApiProperty } from '@nestjs/swagger';
22
import { Type } from 'class-transformer';
33
import {
4-
IsInt,
54
IsNotEmpty,
65
IsOptional,
76
IsString,

0 commit comments

Comments
 (0)