Skip to content
This repository has been archived by the owner on Oct 18, 2024. It is now read-only.

Commit

Permalink
feat: add comment on annotation
Browse files Browse the repository at this point in the history
  • Loading branch information
instalando committed Aug 29, 2023
2 parents e91bb25 + 6a12772 commit 055a46d
Show file tree
Hide file tree
Showing 12 changed files with 221 additions and 49 deletions.
71 changes: 53 additions & 18 deletions src/components/comments/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,12 @@ describe('CommentsComponent', () => {
realtime: ABLY_REALTIME_MOCK,
localParticipant: MOCK_LOCAL_PARTICIPANT,
group: MOCK_GROUP,
config: {
...MOCK_CONFIG,
apiUrl: 'https://dev.nodeapi.superviz.com',
},
config: MOCK_CONFIG,
eventBus: EVENT_BUS_MOCK,
});

commentsComponent['element'].addAnnotation = jest.fn().mockImplementation(() => []);
commentsComponent['element'].addComment = jest.fn().mockImplementation(() => []);
});

afterEach(() => {
Expand Down Expand Up @@ -72,20 +70,20 @@ describe('CommentsComponent', () => {
});

it('should call apiService when fetch annotation', async () => {
const result = jest.spyOn(ApiService, 'fetchAnnotation');
const spy = jest.spyOn(ApiService, 'fetchAnnotation');

expect(result).toHaveBeenCalledWith(
expect.any(String),
expect.any(String),
expect(spy).toHaveBeenCalledWith(
MOCK_CONFIG.apiUrl,
MOCK_CONFIG.apiKey,
{
roomId: expect.any(String),
roomId: MOCK_CONFIG.roomId,
url: expect.any(String),
},
);
});

it('should call apiService when create annotation', async () => {
const result = jest.spyOn(ApiService, 'createAnnotations');
const spy = jest.spyOn(ApiService, 'createAnnotations');

commentsComponent['element'].dispatchEvent(new CustomEvent('create-annotation', {
detail: {
Expand All @@ -97,11 +95,11 @@ describe('CommentsComponent', () => {
},
}));

expect(result).toHaveBeenCalledWith(
expect.any(String),
expect.any(String),
expect(spy).toHaveBeenCalledWith(
MOCK_CONFIG.apiUrl,
MOCK_CONFIG.apiKey,
{
roomId: expect.any(String),
roomId: MOCK_CONFIG.roomId,
url: expect.any(String),
userId: expect.any(String),
position: expect.any(String),
Expand All @@ -110,18 +108,55 @@ describe('CommentsComponent', () => {
});

it('should call apiService when resolve annotation', async () => {
const result = jest.spyOn(ApiService, 'resolveAnnotation');
const spy = jest.spyOn(ApiService, 'resolveAnnotation');

commentsComponent['element'].dispatchEvent(new CustomEvent('resolve-annotation', {
detail: {
uuid: 'test',
},
}));

expect(spy).toHaveBeenCalledWith(
MOCK_CONFIG.apiUrl,
MOCK_CONFIG.apiKey,
'test',
);
});

it('should call apiService when resolve annotation', async () => {
const spy = jest.spyOn(ApiService, 'resolveAnnotation');

commentsComponent['element'].dispatchEvent(new CustomEvent('resolve-annotation', {
detail: {
uuid: 'test',
},
}));

expect(result).toHaveBeenCalledWith(
expect.any(String),
expect.any(String),
expect(spy).toHaveBeenCalledWith(
MOCK_CONFIG.apiUrl,
MOCK_CONFIG.apiKey,
'test',
);
});

it('should call apiService when create a new comment', async () => {
const spy = jest.spyOn(ApiService, 'createComment');

commentsComponent['element'].dispatchEvent(new CustomEvent('create-comment', {
detail: {
uuid: 'uuid-test',
text: 'text-test',
},
}));

expect(spy).toHaveBeenCalledWith(
MOCK_CONFIG.apiUrl,
MOCK_CONFIG.apiKey,
{
annotationId: 'uuid-test',
userId: expect.any(String),
text: 'text-test',
},
);
});
});
37 changes: 29 additions & 8 deletions src/components/comments/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export class CommentsComponent extends BaseComponent {
private addListeners(): void {
this.element.addEventListener('create-annotation', this.createAnnotation);
this.element.addEventListener('resolve-annotation', this.resolveAnnotation);
this.element.addEventListener('create-comment', ({ detail }: CustomEvent) => this.createComment(detail.uuid, detail.text, true));
}

/**
Expand All @@ -65,17 +66,18 @@ export class CommentsComponent extends BaseComponent {
private destroyListeners(): void {
this.element.removeEventListener('create-annotation', this.createAnnotation);
this.element.removeEventListener('resolve-annotation', this.createAnnotation);
this.element.removeEventListener('create-comment', ({ detail }: CustomEvent) => this.createComment(detail.uuid, detail.text, true));
}

/**
* @function createAnnotation
* @description Creates a new annotation and comment and adds them to the Comments component
* @param {CustomEvent} e - The event object containing the annotation text and position
* @param {CustomEvent} event - The event object containing the annotation text and position
* @returns {Promise<void>}
*/
private createAnnotation = async (e: CustomEvent): Promise<void> => {
private createAnnotation = async ({ detail }: CustomEvent): Promise<void> => {
try {
const { text, position } = e.detail;
const { text, position } = detail;
const { url } = this;

const annotation: Annotation = await ApiService.createAnnotations(config.get<string>('apiUrl'), config.get<string>('apiKey'), {
Expand Down Expand Up @@ -104,13 +106,21 @@ export class CommentsComponent extends BaseComponent {
* @param {string} text - The text content of the comment
* @returns {Promise<Comment>} - A promise that resolves with the created comment object
*/
private async createComment(annotationId: string, text: string): Promise<Comment> {
private async createComment(
annotationId: string,
text: string,
addComment = false,
): Promise<Comment> {
try {
return await ApiService.createComment(config.get<string>('apiUrl'), config.get<string>('apiKey'), {
const comment: Comment = await ApiService.createComment(config.get<string>('apiUrl'), config.get<string>('apiKey'), {
annotationId,
userId: this.localParticipant.id,
text,
});

if (addComment) this.addComment(annotationId, comment);

return comment;
} catch (error) {
this.logger.log('error when creating comment', error);
throw error;
Expand All @@ -123,10 +133,21 @@ export class CommentsComponent extends BaseComponent {
* @param {Annotation[]} annotation - An array of annotation objects to add to the component
* @returns {void}
*/
private addAnnotation(annotation: Annotation[]): void {
addAnnotation(annotation: Annotation[]): void {
this.element.addAnnotation(annotation);
}

/**
* @function addComment
* @description Adds a new comment to an annotation in the Comments component
* @param {string} annotationId - The ID of the annotation to add the comment to
* @param {Comment} comment - The comment object to add to the annotation
* @returns {void}
*/
addComment(annotationId: string, comment: Comment): void {
this.element.addComment(annotationId, comment);
}

/**
* @function fetchAnnotations
* @description Fetches annotations from the API and adds them to the Comments component
Expand Down Expand Up @@ -157,9 +178,9 @@ export class CommentsComponent extends BaseComponent {
* @param {CustomEvent} e - The custom event containing the UUID of the annotation to resolve
* @returns {Promise<void>}
*/
private async resolveAnnotation(e: CustomEvent): Promise<void> {
private async resolveAnnotation({ detail }: CustomEvent): Promise<void> {
try {
const { uuid } = e.detail;
const { uuid } = detail;
await ApiService.resolveAnnotation(
config.get('apiUrl'),
config.get('apiKey'),
Expand Down
22 changes: 21 additions & 1 deletion src/web-components/comments/comments.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { CSSResultGroup, LitElement, html } from 'lit';
import { customElement } from 'lit/decorators.js';

import { Annotation } from '../../components/comments/types';
import { Annotation, Comment } from '../../components/comments/types';
import { WebComponentsBase } from '../base';

import { commentsStyle } from './css';
Expand Down Expand Up @@ -33,6 +33,26 @@ export class Comments extends WebComponentsBaseElement {
];
}

addComment(annotationId: string, comment: Comment) {
const annotationIndex = this.annotations
.findIndex((annotation) => annotation.uuid === annotationId);

if (annotationIndex === -1) return;

const annotation = this.annotations[annotationIndex];

annotation.comments = [
...annotation.comments,
comment,
];

this.annotations = [
...this.annotations.slice(0, annotationIndex),
annotation,
...this.annotations.slice(annotationIndex + 1),
];
}

updateAnnotations(data: Annotation[]) {
this.annotations = data;
}
Expand Down
16 changes: 13 additions & 3 deletions src/web-components/comments/components/annotation-item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ export class CommentsAnnotationItem extends WebComponentsBaseElement {
this.emitEvent('select-annotation', { uuid });
};

private createComment({ detail }: CustomEvent) {
const { text } = detail;

this.emitEvent('create-comment', {
uuid: this.annotation.uuid,
text,
});
}

protected render() {
const replies = this.annotation.comments.length;

Expand Down Expand Up @@ -84,13 +93,13 @@ export class CommentsAnnotationItem extends WebComponentsBaseElement {
`;
};

const resolveAnnotation = (e: CustomEvent) => {
const resolveAnnotation = ({ detail }: CustomEvent) => {
const { uuid } = this.annotation;
const { resolved } = e.detail;
const { resolved } = detail;

this.emitEvent('resolve-annotation', {
uuid,
resolved: resolved === 'true',
resolved,
});

this.options.resolved = resolved;
Expand Down Expand Up @@ -119,6 +128,7 @@ export class CommentsAnnotationItem extends WebComponentsBaseElement {
<div class="comments-container ${shouldExpandComments}">
${this.annotation.comments.map(expandedComments)}
<superviz-comments-comment-input
@create-comment=${this.createComment}
eventType="create-comment"
></superviz-comments-comment-input>
</div>
Expand Down
8 changes: 4 additions & 4 deletions src/web-components/comments/components/annotations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ export class CommentsAnnotations extends WebComponentsBaseElement {
open: { type: Boolean },
};

private createAnnotation(e: CustomEvent) {
private createAnnotation({ detail }: CustomEvent) {
this.emitEvent('create-annotation', {
position: {},
text: e.detail.text,
text: detail.text,
});
}

Expand All @@ -29,8 +29,8 @@ export class CommentsAnnotations extends WebComponentsBaseElement {
<div class="annotations">
<span class="text text-big text-bold add-comment-btn">Click anywhere to add a comment</span>
<superviz-comments-comment-input
@create-comment=${this.createAnnotation}
eventType="create-comment"
@create-annotation=${this.createAnnotation}
eventType="create-annotation"
>
</superviz-comments-comment-input>
</div>
Expand Down
9 changes: 8 additions & 1 deletion src/web-components/comments/components/comment-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,14 @@ export class CommentsCommentInput extends WebComponentsBaseElement {
const sendBtn = this.getSendBtn();
const text = input.value;

this.emitEvent(this.eventType, { text });
this.emitEvent(
this.eventType,
{ text },
{
composed: false,
bubbles: false,
},
);

input.value = '';
sendBtn.disabled = true;
Expand Down
25 changes: 18 additions & 7 deletions src/web-components/comments/components/comment-item.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CSSResultGroup, LitElement, html } from 'lit';
import { CSSResultGroup, LitElement, PropertyValueMap, html } from 'lit';
import { customElement } from 'lit/decorators.js';
import { DateTime } from 'luxon';

Expand All @@ -12,38 +12,49 @@ const styles: CSSResultGroup[] = [WebComponentsBaseElement.styles, commentItemSt

@customElement('superviz-comments-comment-item')
export class CommentsCommentItem extends WebComponentsBaseElement {
constructor() {
super();
this.resolved = false;
}

static styles = styles;

declare avatar: string;
declare username: string;
declare text: string;
declare resolved: string;
declare resolvable: boolean;
declare resolved: boolean;
declare createdAt: string;
declare options: AnnotationOptions;

static properties = {
avatar: { type: String },
username: { type: String },
text: { type: String },
resolved: { type: String },
resolvable: { type: Boolean },
resolved: { type: Boolean },
createdAt: { type: String },
options: { type: Object },
};

updated = (changedProperties: PropertyValueMap<any>) => {
if (changedProperties.has('options')) {
this.resolved = this.options?.resolved;
}
};

protected render() {
const humanizeDate = (date: string) => {
return DateTime.fromISO(date).toFormat('yyyy-dd-MM');
};

const isResolvable = this.options?.resolvable ? 'comment-item__resolve' : 'hidden';

const iconResolve = this.options?.resolved ? 'resolved' : 'unresolved';
const iconResolve = this.resolved ? 'resolved' : 'unresolved';

const resolveAnnotation = () => {
this.resolved = !this.resolved;

this.emitEvent('resolve-annotation', {
resolved: !this.options.resolved,
resolved: this.resolved,
}, { composed: false, bubbles: false });
};

Expand Down
4 changes: 2 additions & 2 deletions src/web-components/comments/components/content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ export class CommentsContent extends WebComponentsBaseElement {
return this.annotations.length === index + 1 ? 'hidden' : '';
};

const selectAnnotation = (e: CustomEvent) => {
const { uuid } = e.detail;
const selectAnnotation = ({ detail }: CustomEvent) => {
const { uuid } = detail;
this.selectedAnnotation = uuid;
};

Expand Down
Loading

0 comments on commit 055a46d

Please sign in to comment.