Sending replies and new messages using the annotation dialog is now possible, displayed hierarchically as message threads, layout tweaks
This commit is contained in:
parent
1bdafbc9e6
commit
00815b4b80
|
@ -30,7 +30,7 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 mb-3 pt-2 gr-color" *ngIf="threads?.length > 0">{{'ANNOTATION-DIALOG.TITLE' | translate}}</div>
|
<div class="col-12 mb-3 pt-2 gr-color" *ngIf="threads?.size > 0">{{'ANNOTATION-DIALOG.TITLE' | translate}}</div>
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div *ngFor="let thread of threads" class="row">
|
<div *ngFor="let thread of threads" class="row">
|
||||||
<!-- Parent Thread -->
|
<!-- Parent Thread -->
|
||||||
|
@ -43,12 +43,12 @@
|
||||||
<div class="row reply-content">
|
<div class="row reply-content">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="row h-100">
|
<div class="row h-100">
|
||||||
<div class="col annotation-time">{{thread.timeStamp | date:'EEEE, MMMM d, y, h:mm a'}}</div>
|
<div class="col-10 annotation-time">{{getParentAnnotation(thread).timeStamp | date:'EEEE, MMMM d, y, h:mm a'}}</div>
|
||||||
<div class="col-auto" *ngIf="thread.protectionType === annotationProtectionTypeEnum.Private" matTooltip="{{'ANNOTATION-DIALOG.PROTECTION.PRIVATE' | translate}}"><i class="material-icons protection-icon icon-{{getAnnotationProtectionType(thread)}}"></i>{{getAnnotationProtectionType(thread)}}</div>
|
<div class="col-auto" *ngIf="getParentAnnotation(thread).protectionType === annotationProtectionTypeEnum.Private" matTooltip="{{'ANNOTATION-DIALOG.PROTECTION.PRIVATE' | translate}}"><i class="material-icons protection-icon icon-{{getAnnotationProtectionType(thread)}}"></i>{{getAnnotationProtectionType(thread)}}</div>
|
||||||
<div class="col-auto" *ngIf="thread.protectionType === annotationProtectionTypeEnum.EntityAccessors" matTooltip="{{'ANNOTATION-DIALOG.PROTECTION.ENTITY-ACCESSORS' | translate}}"><i class="material-icons protection-icon icon-{{getAnnotationProtectionType(thread)}}"></i>{{getAnnotationProtectionType(thread)}}</div>
|
<div class="col-auto" *ngIf="getParentAnnotation(thread).protectionType === annotationProtectionTypeEnum.EntityAccessors" matTooltip="{{'ANNOTATION-DIALOG.PROTECTION.ENTITY-ACCESSORS' | translate}}"><i class="material-icons protection-icon icon-{{getAnnotationProtectionType(thread)}}"></i>{{getAnnotationProtectionType(thread)}}</div>
|
||||||
<div class="col-md-12 annotation-full-text">{{thread.payload}}</div>
|
<div class="col-md-12 annotation-full-text">{{getParentAnnotation(thread).payload}}</div>
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<em class="user">{{'ANNOTATION-DIALOG.THREADS.FROM-USER' | translate}} {{thread.author.name}}</em>
|
<em class="user">{{'ANNOTATION-DIALOG.THREADS.FROM-USER' | translate}} {{getParentAnnotation(thread).author.name}}</em>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -59,10 +59,10 @@
|
||||||
|
|
||||||
<!-- Previous Replies -->
|
<!-- Previous Replies -->
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div *ngFor="let annotation of annotationsPerThread[thread.threadId]; let i = index;" class="row replies">
|
<div *ngFor="let annotation of annotationsPerThread[thread]; let i = index;" class="row replies">
|
||||||
<div class="col-auto pr-0">
|
<div class="col-auto pr-0">
|
||||||
<div class="col reply child"></div>
|
<div class="col reply child"></div>
|
||||||
<div class="col reply child dummy-for-next-child" *ngIf="i != annotationsPerThread[thread.threadId].length - 1"></div>
|
<div class="col reply child dummy-for-next-child" *ngIf="i != annotationsPerThread[thread].length - 1"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col pl-0 pt-1">
|
<div class="col pl-0 pt-1">
|
||||||
<div class="parent row m-0">
|
<div class="parent row m-0">
|
||||||
|
@ -72,7 +72,7 @@
|
||||||
<div class="col reply-content">
|
<div class="col reply-content">
|
||||||
<div class="row h-100">
|
<div class="row h-100">
|
||||||
<div class="col-md-12 annotation-time">{{annotation.timeStamp | date:'EEEE, MMMM d, y, h:mm a'}}</div>
|
<div class="col-md-12 annotation-time">{{annotation.timeStamp | date:'EEEE, MMMM d, y, h:mm a'}}</div>
|
||||||
<div class="col-md-12 annotation-full-text">{{annotation.text}}</div>
|
<div class="col-md-12 annotation-full-text">{{annotation.payload}}</div>
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<em class="gr-color">{{'ANNOTATION-DIALOG.THREADS.FROM-USER' | translate}}</em>
|
<em class="gr-color">{{'ANNOTATION-DIALOG.THREADS.FROM-USER' | translate}}</em>
|
||||||
{{annotation.author.name}}
|
{{annotation.author.name}}
|
||||||
|
@ -89,11 +89,11 @@
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="row new-reply mr-0">
|
<div class="row new-reply mr-0">
|
||||||
<mat-form-field class="col pt-2 pb-2 pr-0">
|
<mat-form-field class="col pt-2 pb-2 pr-0">
|
||||||
<textarea matInput matTextareaAutosize matAutosizeMinRows="1" [formControl]="this.threadReplyTextsFG[thread.id.toString()].get('replyText')" placeholder="{{'ANNOTATION-DIALOG.THREADS.REPLY' | translate}}"></textarea>
|
<textarea matInput matTextareaAutosize matAutosizeMinRows="1" [formControl]="this.threadReplyTextsFG[thread.toString()].get('replyText')" placeholder="{{'ANNOTATION-DIALOG.THREADS.REPLY' | translate}}"></textarea>
|
||||||
<mat-error *ngIf="this.threadReplyTextsFG[thread.id.toString()]?.get('replyText')?.hasError('required')">{{'COMMONS.VALIDATION.REQUIRED' | translate}}</mat-error>
|
<mat-error *ngIf="this.threadReplyTextsFG[thread.toString()]?.get('replyText')?.hasError('required')">{{'COMMONS.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<div class="col-auto send-msg">
|
<div class="col-auto send-msg">
|
||||||
<button class="form-field-margin" mat-icon-button type="button" color="accent" (click)="replyThread(thread.threadId, thread.protectionType)" matTooltip="{{'ANNOTATION-DIALOG.THREADS.REPLY' | translate}}">
|
<button class="form-field-margin" mat-icon-button type="button" color="accent" (click)="replyThread(thread)" matTooltip="{{'ANNOTATION-DIALOG.THREADS.REPLY' | translate}}">
|
||||||
<i class="fa fa-paper-plane"></i>
|
<i class="fa fa-paper-plane"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -30,10 +30,11 @@ export class AnnotationDialogComponent extends BaseComponent {
|
||||||
private entityId: Guid;
|
private entityId: Guid;
|
||||||
private anchor: string;
|
private anchor: string;
|
||||||
private entityType: string;
|
private entityType: string;
|
||||||
// public annotations: Array<Annotation> = [];
|
|
||||||
|
|
||||||
public threads = new Array<Annotation>();
|
public comments = new Array<Annotation>();
|
||||||
|
public threads = new Set<Guid>();
|
||||||
public annotationsPerThread = {};
|
public annotationsPerThread = {};
|
||||||
|
public parentAnnotationsPerThread = {};
|
||||||
threadReplyTextsFG: Array<UntypedFormGroup>;
|
threadReplyTextsFG: Array<UntypedFormGroup>;
|
||||||
threadFormGroup: UntypedFormGroup;
|
threadFormGroup: UntypedFormGroup;
|
||||||
private formBuilder: FormBuilder = new FormBuilder();
|
private formBuilder: FormBuilder = new FormBuilder();
|
||||||
|
@ -73,6 +74,7 @@ export class AnnotationDialogComponent extends BaseComponent {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const threadToCreate: AnnotationPersist = {
|
const threadToCreate: AnnotationPersist = {
|
||||||
|
threadId: Guid.create(),
|
||||||
payload: this.threadFormGroup.get('text').value,
|
payload: this.threadFormGroup.get('text').value,
|
||||||
protectionType: this.threadFormGroup.get('protectionType').value,
|
protectionType: this.threadFormGroup.get('protectionType').value,
|
||||||
entityId: this.entityId,
|
entityId: this.entityId,
|
||||||
|
@ -86,7 +88,7 @@ export class AnnotationDialogComponent extends BaseComponent {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
replyThread(threadId: Guid, threadProtection: AnnotationProtectionType) {
|
replyThread(threadId: Guid) {
|
||||||
// if (!this.threadReplyTexts[threadId.toString()] || this.threadReplyTexts[threadId.toString()].length === 0) { return; }
|
// if (!this.threadReplyTexts[threadId.toString()] || this.threadReplyTexts[threadId.toString()].length === 0) { return; }
|
||||||
this.formService.removeAllBackEndErrors(this.threadReplyTextsFG[threadId.toString()]);
|
this.formService.removeAllBackEndErrors(this.threadReplyTextsFG[threadId.toString()]);
|
||||||
this.formService.touchAllFormFields(this.threadReplyTextsFG[threadId.toString()]);
|
this.formService.touchAllFormFields(this.threadReplyTextsFG[threadId.toString()]);
|
||||||
|
@ -96,10 +98,11 @@ export class AnnotationDialogComponent extends BaseComponent {
|
||||||
const replyToCreate: AnnotationPersist = {
|
const replyToCreate: AnnotationPersist = {
|
||||||
threadId: threadId,
|
threadId: threadId,
|
||||||
payload: this.threadReplyTextsFG[threadId.toString()].get('replyText').value,
|
payload: this.threadReplyTextsFG[threadId.toString()].get('replyText').value,
|
||||||
protectionType: threadProtection,
|
protectionType: this.parentAnnotationsPerThread[threadId.toString()].protectionType,
|
||||||
entityId: this.entityId,
|
entityId: this.entityId,
|
||||||
entityType: this.entityType,
|
entityType: this.entityType,
|
||||||
anchor: this.anchor
|
anchor: this.anchor,
|
||||||
|
parentId: this.parentAnnotationsPerThread[threadId.toString()].id
|
||||||
};
|
};
|
||||||
this.annotationService.persist(replyToCreate).pipe(takeUntil(this._destroyed))
|
this.annotationService.persist(replyToCreate).pipe(takeUntil(this._destroyed))
|
||||||
.subscribe(
|
.subscribe(
|
||||||
|
@ -118,12 +121,13 @@ export class AnnotationDialogComponent extends BaseComponent {
|
||||||
private loadThreads() {
|
private loadThreads() {
|
||||||
const lookup: AnnotationLookup = new AnnotationLookup();
|
const lookup: AnnotationLookup = new AnnotationLookup();
|
||||||
lookup.entityIds = [this.entityId];
|
lookup.entityIds = [this.entityId];
|
||||||
// lookup.anchors = [this.anchor];
|
lookup.anchors = [this.anchor];
|
||||||
lookup.entityTypes = [this.entityType];
|
lookup.entityTypes = [this.entityType];
|
||||||
lookup.project = {
|
lookup.project = {
|
||||||
fields: [
|
fields: [
|
||||||
nameof<Annotation>(x => x.id),
|
nameof<Annotation>(x => x.id),
|
||||||
nameof<Annotation>(x => x.threadId),
|
nameof<Annotation>(x => x.threadId),
|
||||||
|
nameof<Annotation>(x => x.parent.id),
|
||||||
nameof<Annotation>(x => x.timeStamp),
|
nameof<Annotation>(x => x.timeStamp),
|
||||||
nameof<Annotation>(x => x.author.name),
|
nameof<Annotation>(x => x.author.name),
|
||||||
nameof<Annotation>(x => x.payload),
|
nameof<Annotation>(x => x.payload),
|
||||||
|
@ -135,23 +139,33 @@ export class AnnotationDialogComponent extends BaseComponent {
|
||||||
.pipe(takeUntil(this._destroyed))
|
.pipe(takeUntil(this._destroyed))
|
||||||
.subscribe(
|
.subscribe(
|
||||||
data => {
|
data => {
|
||||||
// this.annotationsPerThread = {};
|
this.annotationsPerThread = {};
|
||||||
// this.threadReplyTextsFG = new Array<UntypedFormGroup>();
|
this.parentAnnotationsPerThread = {};
|
||||||
// this.resetFormGroup();
|
this.threads = new Set();
|
||||||
// this.threads = data.items.filter(item => item.id === item.threadId).sort((a1, a2) => new Date(a2.timeStamp).getTime() - new Date(a1.timeStamp).getTime());
|
this.threadReplyTextsFG = new Array<UntypedFormGroup>();
|
||||||
// this.threads.forEach(element => {
|
this.resetFormGroup();
|
||||||
// // this.threadReplyTextsFG.addControl(element.id.toString(), new FormControl(null));
|
this.comments = data.items.sort((a1, a2) => new Date(a2.timeStamp).getTime() - new Date(a1.timeStamp).getTime());
|
||||||
// this.threadReplyTextsFG[element.id.toString()] = this.fb.group({ replyText: new FormControl(null, [Validators.required]) });
|
this.comments.forEach(element => {
|
||||||
// this.annotationsPerThread[element.id.toString()] = data.items.filter(x => x.threadId === element.id && x.id !== element.id).sort((a1, a2) => new Date(a1.timeStamp).getTime() - new Date(a2.timeStamp).getTime());
|
// this.threadReplyTextsFG.addControl(element.id.toString(), new FormControl(null));
|
||||||
// });
|
this.threadReplyTextsFG[element.threadId.toString()] = this.formBuilder.group({ replyText: new FormControl(null, [Validators.required]) });
|
||||||
// // this.annotationsChanged.emit(this.threads);
|
this.annotationsPerThread[element.threadId.toString()] = data.items.filter(x => x.threadId === element.threadId && x.id !== element.id).sort((a1, a2) => new Date(a1.timeStamp).getTime() - new Date(a2.timeStamp).getTime());
|
||||||
|
this.parentAnnotationsPerThread[element.threadId.toString()] = data.items.filter(x => x.threadId === element.threadId && x.id === element.id)[0];
|
||||||
this.threads = data.items;
|
this.threads.add(element.threadId);
|
||||||
|
});
|
||||||
|
// console.log(this.comments);
|
||||||
|
// console.log(this.threads);
|
||||||
|
// console.log(this.parentAnnotationsPerThread);
|
||||||
|
// console.log(this.annotationsPerThread);
|
||||||
|
// this.annotationsChanged.emit(this.threads);
|
||||||
},
|
},
|
||||||
error => this.onCallbackError(error),
|
error => this.onCallbackError(error),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getParentAnnotation(thread: Guid): Annotation {
|
||||||
|
return this.parentAnnotationsPerThread[thread.toString()];
|
||||||
|
}
|
||||||
|
|
||||||
resetFormGroup() {
|
resetFormGroup() {
|
||||||
this.threadFormGroup.reset();
|
this.threadFormGroup.reset();
|
||||||
this.threadFormGroup.get('protectionType').setValue(AnnotationProtectionType.EntityAccessors);
|
this.threadFormGroup.get('protectionType').setValue(AnnotationProtectionType.EntityAccessors);
|
||||||
|
@ -181,8 +195,9 @@ export class AnnotationDialogComponent extends BaseComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
private onCallbackSuccess() {
|
private onCallbackSuccess() {
|
||||||
this.uiNotificationService.snackBarNotification(this.language.instant('DMP-UPLOAD.UPLOAD-SUCCESS'), SnackBarNotificationLevel.Success);
|
this.uiNotificationService.snackBarNotification(this.language.instant('ANNOTATION-DIALOG.SUCCESS'), SnackBarNotificationLevel.Success);
|
||||||
this.router.navigate(['/reload']).then(() => this.router.navigate(['/plans']));
|
this.refreshAnnotations();
|
||||||
|
// this.router.navigate(['/reload']).then(() => this.router.navigate(['/plans']));
|
||||||
// this.router.navigate(['/reload']).then(() => this.isPublic ? this.router.navigate(['/explore-plans']) : this.router.navigate(['/plans']));
|
// this.router.navigate(['/reload']).then(() => this.isPublic ? this.router.navigate(['/explore-plans']) : this.router.navigate(['/plans']));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,8 +206,8 @@ export class AnnotationDialogComponent extends BaseComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
getAnnotationProtectionType(thread: Annotation): string {
|
getAnnotationProtectionType(thread: Guid): string {
|
||||||
return this.enumUtils.toAnnotationProtectionTypeString(thread.protectionType);
|
return this.enumUtils.toAnnotationProtectionTypeString(this.parentAnnotationsPerThread[thread.toString()].protectionType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -127,6 +127,7 @@ export class DescriptionFormFieldSetComponent extends BaseComponent {
|
||||||
const dialogRef = this.dialog.open(AnnotationDialogComponent, {
|
const dialogRef = this.dialog.open(AnnotationDialogComponent, {
|
||||||
width: '40rem',
|
width: '40rem',
|
||||||
maxWidth: '90vw',
|
maxWidth: '90vw',
|
||||||
|
maxHeight: '90vh',
|
||||||
data: {
|
data: {
|
||||||
entityId: this.descriptionId,
|
entityId: this.descriptionId,
|
||||||
anchor: fieldSetId,
|
anchor: fieldSetId,
|
||||||
|
|
Loading…
Reference in New Issue