|
|
|
@ -14,20 +14,45 @@ |
|
|
|
/// limitations under the License.
|
|
|
|
///
|
|
|
|
|
|
|
|
import { Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; |
|
|
|
import { |
|
|
|
ChangeDetectorRef, |
|
|
|
Component, |
|
|
|
ElementRef, |
|
|
|
EventEmitter, |
|
|
|
Input, |
|
|
|
OnDestroy, |
|
|
|
OnInit, |
|
|
|
Output, |
|
|
|
Renderer2, |
|
|
|
ViewChild, |
|
|
|
ViewContainerRef, |
|
|
|
ViewEncapsulation |
|
|
|
} from '@angular/core'; |
|
|
|
import { PageComponent } from '@shared/components/page.component'; |
|
|
|
import { FormBuilder } from '@angular/forms'; |
|
|
|
import { Store } from '@ngrx/store'; |
|
|
|
import { AppState } from '@core/core.state'; |
|
|
|
import { EntitiesVersionControlService } from '@core/http/entities-version-control.service'; |
|
|
|
import { EntityId } from '@shared/models/id/entity-id'; |
|
|
|
import { TranslateService } from '@ngx-translate/core'; |
|
|
|
import { getAceDiff } from '@shared/models/ace/ace.models'; |
|
|
|
import { TbPopoverComponent } from '@shared/components/popover.component'; |
|
|
|
import { entityExportDataToJsonString, VersionLoadResult } from '@shared/models/vc.models'; |
|
|
|
import { Ace } from 'ace-builds'; |
|
|
|
import { MatButton } from '@angular/material/button'; |
|
|
|
import { TbPopoverService } from '@shared/components/popover.service'; |
|
|
|
import { EntityVersionRestoreComponent } from '@home/components/vc/entity-version-restore.component'; |
|
|
|
|
|
|
|
interface DiffInfo { |
|
|
|
leftStartLine: number; |
|
|
|
leftEndLine: number; |
|
|
|
rightStartLine: number; |
|
|
|
rightEndLine: number; |
|
|
|
} |
|
|
|
|
|
|
|
@Component({ |
|
|
|
selector: 'tb-entity-version-diff', |
|
|
|
templateUrl: './entity-version-diff.component.html', |
|
|
|
styleUrls: ['./entity-version-diff.component.scss'] |
|
|
|
styleUrls: ['./entity-version-diff.component.scss'], |
|
|
|
encapsulation: ViewEncapsulation.None |
|
|
|
}) |
|
|
|
export class EntityVersionDiffComponent extends PageComponent implements OnInit, OnDestroy { |
|
|
|
|
|
|
|
@ -43,50 +68,260 @@ export class EntityVersionDiffComponent extends PageComponent implements OnInit, |
|
|
|
@Input() |
|
|
|
versionId: string; |
|
|
|
|
|
|
|
@Input() |
|
|
|
entityId: EntityId; |
|
|
|
|
|
|
|
@Input() |
|
|
|
externalEntityId: EntityId; |
|
|
|
|
|
|
|
@Output() |
|
|
|
versionRestored = new EventEmitter<void>(); |
|
|
|
|
|
|
|
@Input() |
|
|
|
onClose: () => void; |
|
|
|
|
|
|
|
@Input() |
|
|
|
popoverComponent: TbPopoverComponent; |
|
|
|
|
|
|
|
differ: AceDiff; |
|
|
|
|
|
|
|
contentReady = false; |
|
|
|
|
|
|
|
preferredDiffHeight = '332px'; |
|
|
|
|
|
|
|
isFullscreen = false; |
|
|
|
|
|
|
|
hasNext = false; |
|
|
|
hasPrevious = false; |
|
|
|
|
|
|
|
diffCount = 0; |
|
|
|
|
|
|
|
constructor(protected store: Store<AppState>, |
|
|
|
private entitiesVersionControlService: EntitiesVersionControlService, |
|
|
|
private translate: TranslateService, |
|
|
|
private fb: FormBuilder) { |
|
|
|
private cd: ChangeDetectorRef, |
|
|
|
private renderer: Renderer2, |
|
|
|
private elementRef: ElementRef, |
|
|
|
private viewContainerRef: ViewContainerRef, |
|
|
|
private popoverService: TbPopoverService) { |
|
|
|
super(store); |
|
|
|
} |
|
|
|
|
|
|
|
ngOnInit(): void { |
|
|
|
getAceDiff().subscribe((aceDiff) => { |
|
|
|
this.differ = new aceDiff.default( |
|
|
|
{ |
|
|
|
element: this.diffViewerElmRef.nativeElement, |
|
|
|
left: { |
|
|
|
copyLinkEnabled: false, |
|
|
|
editable: false, |
|
|
|
content: 'Left content!' |
|
|
|
}, |
|
|
|
right: { |
|
|
|
copyLinkEnabled: false, |
|
|
|
editable: false, |
|
|
|
content: 'Right content!' |
|
|
|
} |
|
|
|
} as AceDiff.AceDiffConstructorOpts |
|
|
|
); |
|
|
|
this.entitiesVersionControlService |
|
|
|
.compareEntityDataToVersion(this.branch, this.entityId, this.versionId).subscribe((diffData) => { |
|
|
|
const leftContent = entityExportDataToJsonString(diffData.currentVersion); |
|
|
|
const rightContent = entityExportDataToJsonString(diffData.otherVersion); |
|
|
|
const leftLines = leftContent.split('\n').length; |
|
|
|
const rightLines = leftContent.split('\n').length; |
|
|
|
const totalLines = Math.max(leftLines, rightLines); |
|
|
|
let preferredLines = Math.max(10, totalLines); |
|
|
|
preferredLines = Math.min(40, preferredLines); |
|
|
|
this.preferredDiffHeight = (132 + preferredLines * 16) + 'px'; |
|
|
|
getAceDiff().subscribe((aceDiff) => { |
|
|
|
this.contentReady = true; |
|
|
|
this.cd.detectChanges(); |
|
|
|
if (this.popoverComponent) { |
|
|
|
this.popoverComponent.updatePosition(); |
|
|
|
} |
|
|
|
setTimeout(() => { |
|
|
|
this.differ = new aceDiff.default( |
|
|
|
{ |
|
|
|
element: this.diffViewerElmRef.nativeElement, |
|
|
|
mode: 'ace/mode/json', |
|
|
|
left: { |
|
|
|
copyLinkEnabled: false, |
|
|
|
editable: false, |
|
|
|
content: leftContent |
|
|
|
}, |
|
|
|
right: { |
|
|
|
copyLinkEnabled: false, |
|
|
|
editable: false, |
|
|
|
content: rightContent |
|
|
|
} |
|
|
|
} as AceDiff.AceDiffConstructorOpts |
|
|
|
); |
|
|
|
const leftEditor: Ace.Editor = this.differ.getEditors().left; |
|
|
|
const rightEditor: Ace.Editor = this.differ.getEditors().right; |
|
|
|
leftEditor.setShowFoldWidgets(false); |
|
|
|
rightEditor.setShowFoldWidgets(false); |
|
|
|
$('.acediff__left .ace_scrollbar-v', this.elementRef.nativeElement).on('scroll', () => { |
|
|
|
rightEditor.getSession().setScrollTop(leftEditor.getSession().getScrollTop()); |
|
|
|
}); |
|
|
|
$('.acediff__right .ace_scrollbar-v', this.elementRef.nativeElement).on('scroll', () => { |
|
|
|
leftEditor.getSession().setScrollTop(rightEditor.getSession().getScrollTop()); |
|
|
|
}); |
|
|
|
$('.acediff__left .ace_scrollbar-h', this.elementRef.nativeElement).on('scroll', () => { |
|
|
|
rightEditor.getSession().setScrollLeft(leftEditor.getSession().getScrollLeft()); |
|
|
|
}); |
|
|
|
$('.acediff__right .ace_scrollbar-h', this.elementRef.nativeElement).on('scroll', () => { |
|
|
|
leftEditor.getSession().setScrollLeft(rightEditor.getSession().getScrollLeft()); |
|
|
|
}); |
|
|
|
leftEditor.getSession().getSelection().on('changeCursor', () => this.updateHasNextAndPrevious()); |
|
|
|
rightEditor.getSession().getSelection().on('changeCursor', () => this.updateHasNextAndPrevious()); |
|
|
|
setTimeout(() => { |
|
|
|
this.diffCount = this.differ.getNumDiffs(); |
|
|
|
this.updateHasNextAndPrevious(); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
versionIdContent(): string { |
|
|
|
let versionId = this.versionId; |
|
|
|
if (versionId.length > 7) { |
|
|
|
versionId = versionId.slice(0, 7); |
|
|
|
} |
|
|
|
return versionId + ' (' + this.versionName + ')'; |
|
|
|
} |
|
|
|
|
|
|
|
prevDifference($event: Event) { |
|
|
|
if ($event) { |
|
|
|
$event.stopPropagation(); |
|
|
|
} |
|
|
|
this.moveToDiff(false); |
|
|
|
} |
|
|
|
|
|
|
|
nextDifference($event: Event) { |
|
|
|
if ($event) { |
|
|
|
$event.stopPropagation(); |
|
|
|
} |
|
|
|
this.moveToDiff(true); |
|
|
|
} |
|
|
|
|
|
|
|
private moveToDiff(next: boolean) { |
|
|
|
const currentRow = this.getCurrentRow(); |
|
|
|
const diff = next ? this.findNextLine(currentRow) : this.findPrevLine(currentRow); |
|
|
|
if (diff) { |
|
|
|
const leftEditor: Ace.Editor = this.differ.getEditors().left; |
|
|
|
const rightEditor: Ace.Editor = this.differ.getEditors().right; |
|
|
|
leftEditor.scrollToLine(diff.leftStartLine + 1, true, true, () => {}); |
|
|
|
leftEditor.gotoLine(diff.leftStartLine + 1, 0, true); |
|
|
|
rightEditor.scrollToLine(diff.rightStartLine + 1, true, true, () => {}); |
|
|
|
rightEditor.gotoLine(diff.rightStartLine + 1, 0, true); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
onFullscreenChanged(fullscreen: boolean) { |
|
|
|
if (fullscreen) { |
|
|
|
this.resizeEditors(); |
|
|
|
} else { |
|
|
|
setTimeout(() => { |
|
|
|
this.resizeEditors(); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private getDiffs(): DiffInfo[] { |
|
|
|
if (this.differ) { |
|
|
|
// @ts-ignore
|
|
|
|
return this.differ.diffs as DiffInfo[] || []; |
|
|
|
} else { |
|
|
|
return []; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private getCurrentRow(): {row: number, left: boolean} { |
|
|
|
const leftEditor: Ace.Editor = this.differ.getEditors().left; |
|
|
|
const rightEditor: Ace.Editor = this.differ.getEditors().right; |
|
|
|
let currentRow = 0; |
|
|
|
let left = true; |
|
|
|
const leftRow = leftEditor.getSession().getSelection().getCursor().row; |
|
|
|
const rightRow = rightEditor.getSession().getSelection().getCursor().row; |
|
|
|
if (leftRow >= leftEditor.getFirstVisibleRow() && leftRow <= leftEditor.getLastVisibleRow()) { |
|
|
|
currentRow = leftRow; |
|
|
|
} else if (rightRow >= rightEditor.getFirstVisibleRow() && rightRow <= rightEditor.getLastVisibleRow()) { |
|
|
|
currentRow = rightRow; |
|
|
|
left = false; |
|
|
|
} else { |
|
|
|
currentRow = leftRow; |
|
|
|
} |
|
|
|
return {row: currentRow, left}; |
|
|
|
} |
|
|
|
|
|
|
|
private nextDiff(currentLine: {row: number, left: boolean}): DiffInfo | undefined { |
|
|
|
const diffs = this.getDiffs(); |
|
|
|
return diffs.find((diff) => (currentLine.left ? diff.leftStartLine : diff.rightStartLine) > currentLine.row); |
|
|
|
} |
|
|
|
|
|
|
|
private prevDiff(currentLine: {row: number, left: boolean}): DiffInfo | undefined { |
|
|
|
const diffs = this.getDiffs(); |
|
|
|
return [...diffs].reverse().find((diff) => (currentLine.left ? diff.leftEndLine : diff.rightEndLine) < currentLine.row); |
|
|
|
} |
|
|
|
|
|
|
|
private findNextLine(currentLine: {row: number, left: boolean}): DiffInfo | undefined { |
|
|
|
let res = this.nextDiff(currentLine); |
|
|
|
const diffs = this.getDiffs(); |
|
|
|
if (!res && diffs.length) { |
|
|
|
res = diffs[diffs.length - 1]; |
|
|
|
} |
|
|
|
return res; |
|
|
|
} |
|
|
|
|
|
|
|
private findPrevLine(currentLine: {row: number, left: boolean}): DiffInfo | undefined { |
|
|
|
let res = this.prevDiff(currentLine); |
|
|
|
const diffs = this.getDiffs(); |
|
|
|
if (!res && diffs.length) { |
|
|
|
res = diffs[0]; |
|
|
|
} |
|
|
|
return res; |
|
|
|
} |
|
|
|
|
|
|
|
private updateHasNextAndPrevious() { |
|
|
|
const currentRow = this.getCurrentRow(); |
|
|
|
this.hasNext = !!this.nextDiff(currentRow); |
|
|
|
this.hasPrevious = !!this.prevDiff(currentRow); |
|
|
|
this.cd.markForCheck(); |
|
|
|
} |
|
|
|
|
|
|
|
private resizeEditors() { |
|
|
|
if (this.differ) { |
|
|
|
this.differ.diff(); |
|
|
|
const leftEditor: Ace.Editor = this.differ.getEditors().left; |
|
|
|
const rightEditor: Ace.Editor = this.differ.getEditors().right; |
|
|
|
leftEditor.resize(); |
|
|
|
leftEditor.renderer.updateFull(); |
|
|
|
rightEditor.resize(); |
|
|
|
rightEditor.renderer.updateFull(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
ngOnDestroy(): void { |
|
|
|
if (this.differ) { |
|
|
|
this.differ.destroy(); |
|
|
|
this.differ = null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
close(): void { |
|
|
|
if (this.onClose) { |
|
|
|
this.onClose(); |
|
|
|
if (this.popoverComponent) { |
|
|
|
this.popoverComponent.hide(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
toggleRestoreEntityVersion($event: Event, restoreVersionButton: MatButton) { |
|
|
|
if ($event) { |
|
|
|
$event.stopPropagation(); |
|
|
|
} |
|
|
|
const trigger = restoreVersionButton._elementRef.nativeElement; |
|
|
|
if (this.popoverService.hasPopover(trigger)) { |
|
|
|
this.popoverService.hidePopover(trigger); |
|
|
|
} else { |
|
|
|
const restoreVersionPopover = this.popoverService.displayPopover(trigger, this.renderer, |
|
|
|
this.viewContainerRef, EntityVersionRestoreComponent, 'left', true, null, |
|
|
|
{ |
|
|
|
branch: this.branch, |
|
|
|
versionName: this.versionName, |
|
|
|
versionId: this.versionId, |
|
|
|
externalEntityId: this.externalEntityId, |
|
|
|
onClose: (result: Array<VersionLoadResult> | null) => { |
|
|
|
restoreVersionPopover.hide(); |
|
|
|
if (result && result.length) { |
|
|
|
this.close(); |
|
|
|
this.versionRestored.emit(); |
|
|
|
} |
|
|
|
} |
|
|
|
}, {}, {}, {}, false); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|