mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-05-12 17:04:57 +00:00
[ACA-4729] Add infinite scroll to version list (#9248)
* [ACA-4729] Add infinite scroll to version list * [ACA-4729] CR fixes * [ACA-4729] CR fixes * [ACA-4729] Items count fix for infinite scroll datasource
This commit is contained in:
parent
2c0ad7137a
commit
a7e7934505
@ -0,0 +1,66 @@
|
|||||||
|
---
|
||||||
|
Title: Infinite Scroll Datasource
|
||||||
|
Added: v6.6.0
|
||||||
|
Status: Active
|
||||||
|
Last reviewed: 2024-01-15
|
||||||
|
---
|
||||||
|
|
||||||
|
# [Infinite Scroll Datasource](../../../lib/content-services/src/lib/infinite-scroll-datasource/infinite-scroll-datasource.ts "Defined in infinite-scroll-datasource.ts")
|
||||||
|
|
||||||
|
Contains abstract class acting as a baseline for various datasources for infinite scrolls.
|
||||||
|
|
||||||
|
## Basic Usage
|
||||||
|
|
||||||
|
First step to use infinite scroll datasource in any component is creating a datasource class extending `InfiniteScrollDatasource` using specific source of data e.g. one of the content endpoints.
|
||||||
|
```ts
|
||||||
|
export class VersionListDataSource extends InfiniteScrollDatasource<VersionEntry> {
|
||||||
|
constructor(private versionsApi: VersionsApi, private node: Node) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
getNextBatch(pagingOptions: ContentPagingQuery): Observable<VersionEntry[]> {
|
||||||
|
return from(this.versionsApi.listVersionHistory(this.node.id, pagingOptions)).pipe(
|
||||||
|
take(1),
|
||||||
|
map((versionPaging) => versionPaging.list.entries)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then in component that will have the infinite scroll define the datasource as instance of a class created in previous step, optionally you can set custom size of the items batch or listen to loading state changes:
|
||||||
|
```ts
|
||||||
|
this.versionsDataSource = new VersionListDataSource(this.versionsApi, this.node);
|
||||||
|
this.versionsDataSource.batchSize = 50;
|
||||||
|
this.versionsDataSource.isLoading.pipe(takeUntil(this.onDestroy$)).subscribe((isLoading) => this.isLoading = isLoading);
|
||||||
|
```
|
||||||
|
|
||||||
|
Final step is to add the [CdkVirtualScrollViewport](https://material.angular.io/cdk/scrolling/api#CdkVirtualScrollViewport) with [CdkVirtualFor](https://material.angular.io/cdk/scrolling/api#CdkVirtualForOf) loop displaying items from the datasource.
|
||||||
|
```html
|
||||||
|
<cdk-virtual-scroll-viewport appendOnly itemSize="88">
|
||||||
|
<div *cdkVirtualFor="let version of versionsDataSource"></div>
|
||||||
|
</cdk-virtual-scroll-viewport>
|
||||||
|
```
|
||||||
|
|
||||||
|
When user will scroll down to the bottom of the list next batch of items will be fetched until all items are visible.
|
||||||
|
|
||||||
|
## Class members
|
||||||
|
|
||||||
|
### Properties
|
||||||
|
|
||||||
|
| Name | Type | Default value | Description |
|
||||||
|
| ---- | ---- | ------------- | ----------- |
|
||||||
|
| batchSize | `number` | 100 | Determines how much items will be fetched within one batch. |
|
||||||
|
| firstItem | `T` | | Returns the first item ever fetched. |
|
||||||
|
| isLoading | [`Observable`](https://rxjs.dev/api/index/class/Observable)`<boolean>` | | Observable representing the state of loading the first batch. |
|
||||||
|
| itemsCount | `number` | | Number of items fetched so far. |
|
||||||
|
|
||||||
|
### Methods
|
||||||
|
|
||||||
|
- **connect**(collectionViewer: [`CollectionViewer`](https://material.angular.io/cdk/collections/api)): [`Observable`](https://rxjs.dev/api/index/class/Observable)`<T>`<br/>
|
||||||
|
Called by the virtual scroll viewport to receive a stream that emits the data array that should be rendered.
|
||||||
|
- collectionViewer:_ [`CollectionViewer`](https://material.angular.io/cdk/collections/api) - collection viewer providing view changes that are listened to so that next batch can be fetched
|
||||||
|
- **Returns** [`Observable`](https://rxjs.dev/api/index/class/Observable)`<T>` - Data stream containing fetched items.
|
||||||
|
- **disconnect**(): void<br/>
|
||||||
|
Called when viewport is destroyed, disconnects the datasource, unsubscribes from the view changes.
|
||||||
|
- **reset**(): void<br/>
|
||||||
|
Resets the datasource by fetching the first batch.
|
@ -0,0 +1,18 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from './public-api';
|
@ -0,0 +1,159 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ContentPagingQuery } from '@alfresco/js-api';
|
||||||
|
import { ScrollingModule } from '@angular/cdk/scrolling';
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { from, Observable } from 'rxjs';
|
||||||
|
import { ContentTestingModule } from '../testing/content.testing.module';
|
||||||
|
import { InfiniteScrollDatasource } from './infinite-scroll-datasource';
|
||||||
|
|
||||||
|
class TestData {
|
||||||
|
testId: number;
|
||||||
|
testDescription: string;
|
||||||
|
|
||||||
|
constructor(input?: Partial<TestData>) {
|
||||||
|
if (input) {
|
||||||
|
Object.assign(this, input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestDataSource extends InfiniteScrollDatasource<TestData> {
|
||||||
|
testDataBatch1: TestData[] = [
|
||||||
|
{
|
||||||
|
testId: 1,
|
||||||
|
testDescription: 'test1'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testId: 2,
|
||||||
|
testDescription: 'test2'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testId: 3,
|
||||||
|
testDescription: 'test3'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testId: 4,
|
||||||
|
testDescription: 'test4'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
testDataBatch2: TestData[] = [
|
||||||
|
{
|
||||||
|
testId: 5,
|
||||||
|
testDescription: 'test5'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testId: 6,
|
||||||
|
testDescription: 'test6'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
getNextBatch(pagingOptions: ContentPagingQuery): Observable<TestData[]> {
|
||||||
|
if (pagingOptions.skipCount === 4) {
|
||||||
|
return from([this.testDataBatch2]);
|
||||||
|
} else if (pagingOptions.skipCount === 0) {
|
||||||
|
return from([this.testDataBatch1]);
|
||||||
|
} else {
|
||||||
|
return from([]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: ` <cdk-virtual-scroll-viewport appendOnly itemSize="300" style="height: 500px; width: 100%;">
|
||||||
|
<div *cdkVirtualFor="let item of testDatasource" class="test-item" style="display: block; height: 100%; width: 100%;">
|
||||||
|
{{ item.testDescription }}
|
||||||
|
</div>
|
||||||
|
</cdk-virtual-scroll-viewport>`
|
||||||
|
})
|
||||||
|
class TestComponent implements OnInit {
|
||||||
|
testDatasource = new TestDataSource();
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.testDatasource.batchSize = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('InfiniteScrollDatasource', () => {
|
||||||
|
let fixture: ComponentFixture<TestComponent>;
|
||||||
|
let component: TestComponent;
|
||||||
|
|
||||||
|
const getRenderedItems = (): HTMLDivElement[] => fixture.debugElement.queryAll(By.css('.test-item')).map(element => element.nativeElement);
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [TranslateModule.forRoot(), ContentTestingModule, ScrollingModule],
|
||||||
|
declarations: [TestComponent]
|
||||||
|
});
|
||||||
|
fixture = TestBed.createComponent(TestComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should connect to the datasource and fetch first batch of items on init', async () => {
|
||||||
|
spyOn(component.testDatasource, 'connect').and.callThrough();
|
||||||
|
spyOn(component.testDatasource, 'getNextBatch').and.callThrough();
|
||||||
|
fixture.autoDetectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
await fixture.whenRenderingDone();
|
||||||
|
|
||||||
|
expect(component.testDatasource.connect).toHaveBeenCalled();
|
||||||
|
expect(component.testDatasource.itemsCount).toBe(4);
|
||||||
|
expect(component.testDatasource.getNextBatch).toHaveBeenCalledWith({ skipCount: 0, maxItems: 4 });
|
||||||
|
const renderedItems = getRenderedItems();
|
||||||
|
// only 3 elements fit the viewport
|
||||||
|
expect(renderedItems.length).toBe(3);
|
||||||
|
expect(renderedItems[0].innerText).toBe('test1');
|
||||||
|
expect(renderedItems[2].innerText).toBe('test3');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should load next batch when user scrolls towards the end of the list', fakeAsync(() => {
|
||||||
|
fixture.autoDetectChanges();
|
||||||
|
const stable = fixture.whenStable();
|
||||||
|
const renderingDone = fixture.whenRenderingDone();
|
||||||
|
Promise.all([stable, renderingDone]).then(() => {
|
||||||
|
spyOn(component.testDatasource, 'getNextBatch').and.callThrough();
|
||||||
|
const viewport = fixture.debugElement.query(By.css('cdk-virtual-scroll-viewport')).nativeElement;
|
||||||
|
viewport.scrollTop = 400;
|
||||||
|
tick(100);
|
||||||
|
|
||||||
|
const renderedItems = getRenderedItems();
|
||||||
|
expect(component.testDatasource.getNextBatch).toHaveBeenCalledWith({ skipCount: 4, maxItems: 4 });
|
||||||
|
expect(component.testDatasource.itemsCount).toBe(6);
|
||||||
|
expect(renderedItems[3].innerText).toBe('test4');
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should reset the datastream and fetch first batch on reset', fakeAsync(() => {
|
||||||
|
fixture.autoDetectChanges();
|
||||||
|
const stable = fixture.whenStable();
|
||||||
|
const renderingDone = fixture.whenRenderingDone();
|
||||||
|
Promise.all([stable, renderingDone]).then(() => {
|
||||||
|
spyOn(component.testDatasource, 'getNextBatch').and.callThrough();
|
||||||
|
component.testDatasource.reset();
|
||||||
|
tick(100);
|
||||||
|
|
||||||
|
const renderedItems = getRenderedItems();
|
||||||
|
expect(component.testDatasource.getNextBatch).toHaveBeenCalledWith({ skipCount: 0, maxItems: 4 });
|
||||||
|
expect(renderedItems.length).toBe(3);
|
||||||
|
expect(renderedItems[2].innerText).toBe('test3');
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
@ -0,0 +1,82 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ContentPagingQuery } from '@alfresco/js-api';
|
||||||
|
import { CollectionViewer, DataSource } from '@angular/cdk/collections';
|
||||||
|
import { BehaviorSubject, forkJoin, Observable, Subject, Subscription } from 'rxjs';
|
||||||
|
import { take, tap } from 'rxjs/operators';
|
||||||
|
|
||||||
|
export abstract class InfiniteScrollDatasource<T> extends DataSource<T> {
|
||||||
|
protected readonly dataStream = new BehaviorSubject<T[]>([]);
|
||||||
|
private isLoading$ = new Subject<boolean>();
|
||||||
|
private subscription = new Subscription();
|
||||||
|
private batchesFetched = 0;
|
||||||
|
private _itemsCount = 0;
|
||||||
|
private _firstItem: T;
|
||||||
|
|
||||||
|
/* Determines size of each batch to be fetched */
|
||||||
|
batchSize = 100;
|
||||||
|
|
||||||
|
/* Observable with initial and on reset loading state */
|
||||||
|
isLoading = this.isLoading$.asObservable();
|
||||||
|
|
||||||
|
get itemsCount(): number {
|
||||||
|
return this._itemsCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
get firstItem(): T {
|
||||||
|
return this._firstItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract getNextBatch(pagingOptions: ContentPagingQuery): Observable<T[]>;
|
||||||
|
|
||||||
|
connect(collectionViewer: CollectionViewer): Observable<T[]> {
|
||||||
|
this.reset();
|
||||||
|
this.subscription.add(
|
||||||
|
collectionViewer.viewChange.subscribe((range) => {
|
||||||
|
if (this.batchesFetched * this.batchSize <= range.end) {
|
||||||
|
forkJoin([
|
||||||
|
this.dataStream.asObservable().pipe(take(1)),
|
||||||
|
this.getNextBatch({ skipCount: this.batchSize * this.batchesFetched, maxItems: this.batchSize }).pipe(
|
||||||
|
take(1),
|
||||||
|
tap((nextBatch) => (this._itemsCount += nextBatch.length))
|
||||||
|
)
|
||||||
|
]).subscribe((batchesArray) => this.dataStream.next([...batchesArray[0], ...batchesArray[1]]));
|
||||||
|
this.batchesFetched += 1;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return this.dataStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnect(): void {
|
||||||
|
this.subscription.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
reset(): void {
|
||||||
|
this.isLoading$.next(true);
|
||||||
|
this.getNextBatch({ skipCount: 0, maxItems: this.batchSize })
|
||||||
|
.pipe(take(1))
|
||||||
|
.subscribe((firstBatch) => {
|
||||||
|
this._itemsCount = firstBatch.length;
|
||||||
|
this._firstItem = firstBatch[0];
|
||||||
|
this.dataStream.next(firstBatch);
|
||||||
|
this.isLoading$.next(false);
|
||||||
|
});
|
||||||
|
this.batchesFetched = 1;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from './infinite-scroll-datasource';
|
@ -1,55 +1,53 @@
|
|||||||
<mat-list class="adf-version-list" *ngIf="!isLoading; else loading_template">
|
<mat-progress-bar *ngIf="isLoading" data-automation-id="version-history-loading-bar" mode="indeterminate" color="accent"></mat-progress-bar>
|
||||||
<mat-list-item *ngFor="let version of versions; let idx = index; let latestVersion = first">
|
<mat-list class="adf-version-list" [hidden]="isLoading">
|
||||||
<mat-icon mat-list-icon>insert_drive_file</mat-icon>
|
<cdk-virtual-scroll-viewport #viewport itemSize="88" class="adf-version-list-viewport">
|
||||||
<p mat-line class="adf-version-list-item-name" [id]="'adf-version-list-item-name-' + version.entry.id" >{{version.entry.name}}</p>
|
<mat-list-item *cdkVirtualFor="let version of versionsDataSource; let idx = index; let latestVersion = first">
|
||||||
<p mat-line>
|
<mat-icon mat-list-icon>insert_drive_file</mat-icon>
|
||||||
<span class="adf-version-list-item-version" [id]="'adf-version-list-item-version-' + version.entry.id" >{{version.entry.id}}</span> -
|
<p mat-line class="adf-version-list-item-name" [id]="'adf-version-list-item-name-' + version.entry.id" >{{version.entry.name}}</p>
|
||||||
<span class="adf-version-list-item-date" [id]="'adf-version-list-item-date-' + version.entry.id" >{{version.entry.modifiedAt | date}}</span>
|
<p mat-line>
|
||||||
</p>
|
<span class="adf-version-list-item-version" [id]="'adf-version-list-item-version-' + version.entry.id" >{{version.entry.id}}</span> -
|
||||||
<p mat-line [id]="'adf-version-list-item-comment-'+ version.entry.id" class="adf-version-list-item-comment"
|
<span class="adf-version-list-item-date" [id]="'adf-version-list-item-date-' + version.entry.id" >{{version.entry.modifiedAt | date}}</span>
|
||||||
*ngIf="showComments">{{version.entry.versionComment}}</p>
|
</p>
|
||||||
|
<p mat-line [id]="'adf-version-list-item-comment-'+ version.entry.id" class="adf-version-list-item-comment"
|
||||||
|
*ngIf="showComments">{{version.entry.versionComment}}</p>
|
||||||
|
|
||||||
<div *ngIf="showActions">
|
<div *ngIf="showActions">
|
||||||
<mat-menu [id]="'adf-version-list-action-menu-'+version.entry.id"
|
<mat-menu [id]="'adf-version-list-action-menu-'+version.entry.id"
|
||||||
#versionMenu="matMenu" yPosition="below" xPosition="before">
|
#versionMenu="matMenu" yPosition="below" xPosition="before">
|
||||||
<ng-container *adf-acs-version="'7'">
|
<ng-container *adf-acs-version="'7'">
|
||||||
<button *ngIf="allowViewVersions"
|
<button *ngIf="allowViewVersions"
|
||||||
[id]="'adf-version-list-action-view-'+version.entry.id"
|
[id]="'adf-version-list-action-view-'+version.entry.id"
|
||||||
mat-menu-item
|
mat-menu-item
|
||||||
(click)="onViewVersion(version.entry.id)">
|
(click)="onViewVersion(version.entry.id)">
|
||||||
{{ 'ADF_VERSION_LIST.ACTIONS.VIEW' | translate }}
|
{{ 'ADF_VERSION_LIST.ACTIONS.VIEW' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<button
|
<button
|
||||||
[id]="'adf-version-list-action-restore-'+version.entry.id"
|
[id]="'adf-version-list-action-restore-'+version.entry.id"
|
||||||
[disabled]="!canUpdate() || latestVersion"
|
[disabled]="!canUpdate() || latestVersion"
|
||||||
mat-menu-item
|
|
||||||
(click)="restore(version.entry.id)">
|
|
||||||
{{ 'ADF_VERSION_LIST.ACTIONS.RESTORE' | translate }}
|
|
||||||
</button>
|
|
||||||
<button *ngIf="allowDownload"
|
|
||||||
[id]="'adf-version-list-action-download-'+version.entry.id"
|
|
||||||
mat-menu-item
|
mat-menu-item
|
||||||
(click)="downloadVersion(version.entry.id)">
|
(click)="restore(version.entry.id)">
|
||||||
{{ 'ADF_VERSION_LIST.ACTIONS.DOWNLOAD' | translate }}
|
{{ 'ADF_VERSION_LIST.ACTIONS.RESTORE' | translate }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button *ngIf="allowDownload"
|
||||||
[disabled]="!canDelete()"
|
[id]="'adf-version-list-action-download-'+version.entry.id"
|
||||||
[id]="'adf-version-list-action-delete-'+version.entry.id"
|
mat-menu-item
|
||||||
(click)="deleteVersion(version.entry.id)"
|
(click)="downloadVersion(version.entry.id)">
|
||||||
mat-menu-item>
|
{{ 'ADF_VERSION_LIST.ACTIONS.DOWNLOAD' | translate }}
|
||||||
{{ 'ADF_VERSION_LIST.ACTIONS.DELETE' | translate }}
|
</button>
|
||||||
</button>
|
<button
|
||||||
</mat-menu>
|
[disabled]="!canDelete()"
|
||||||
|
[id]="'adf-version-list-action-delete-'+version.entry.id"
|
||||||
|
(click)="deleteVersion(version.entry.id)"
|
||||||
|
mat-menu-item>
|
||||||
|
{{ 'ADF_VERSION_LIST.ACTIONS.DELETE' | translate }}
|
||||||
|
</button>
|
||||||
|
</mat-menu>
|
||||||
|
|
||||||
<button mat-icon-button [matMenuTriggerFor]="versionMenu" [id]="'adf-version-list-action-menu-button-'+version.entry.id" title="{{ 'ADF_VERSION_LIST.MANAGE_VERSION_OPTIONS' | translate }}">
|
<button mat-icon-button [matMenuTriggerFor]="versionMenu" [id]="'adf-version-list-action-menu-button-'+version.entry.id" title="{{ 'ADF_VERSION_LIST.MANAGE_VERSION_OPTIONS' | translate }}">
|
||||||
<mat-icon>more_vert</mat-icon>
|
<mat-icon>more_vert</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</mat-list-item>
|
</mat-list-item>
|
||||||
|
</cdk-virtual-scroll-viewport>
|
||||||
</mat-list>
|
</mat-list>
|
||||||
|
|
||||||
<ng-template #loading_template>
|
|
||||||
<mat-progress-bar data-automation-id="version-history-loading-bar" mode="indeterminate"
|
|
||||||
color="accent"></mat-progress-bar>
|
|
||||||
</ng-template>
|
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
.adf-version-list {
|
.adf-version-list {
|
||||||
|
&-viewport {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.mat-list-item-content {
|
.mat-list-item-content {
|
||||||
border-bottom: 1px solid #d8d8d8;
|
border-bottom: 1px solid #d8d8d8;
|
||||||
}
|
}
|
||||||
|
@ -18,13 +18,14 @@
|
|||||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
|
import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { VersionListComponent } from './version-list.component';
|
import { VersionListComponent, VersionListDataSource } from './version-list.component';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { Node, VersionPaging, NodeEntry, VersionEntry, Version } from '@alfresco/js-api';
|
import { Node, NodeEntry, VersionEntry, Version } from '@alfresco/js-api';
|
||||||
import { ContentTestingModule } from '../testing/content.testing.module';
|
import { ContentTestingModule } from '../testing/content.testing.module';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { ContentVersionService } from './content-version.service';
|
import { ContentVersionService } from './content-version.service';
|
||||||
|
import { take } from 'rxjs/operators';
|
||||||
|
|
||||||
describe('VersionListComponent', () => {
|
describe('VersionListComponent', () => {
|
||||||
let component: VersionListComponent;
|
let component: VersionListComponent;
|
||||||
@ -56,14 +57,16 @@ describe('VersionListComponent', () => {
|
|||||||
|
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
component.node = { id: nodeId, allowableOperations: ['update'] } as Node;
|
component.node = { id: nodeId, allowableOperations: ['update'] } as Node;
|
||||||
|
component.isLoading = false;
|
||||||
|
|
||||||
spyOn(component, 'downloadContent').and.stub();
|
spyOn(component, 'downloadContent').and.stub();
|
||||||
spyOn(component.nodesApi, 'getNode').and.returnValue(Promise.resolve(new NodeEntry({ entry: new Node({ id: 'nodeInfoId' }) })));
|
spyOn(component.nodesApi, 'getNode').and.returnValue(Promise.resolve(new NodeEntry({ entry: new Node({ id: 'nodeInfoId' }) })));
|
||||||
|
spyOn(VersionListDataSource.prototype, 'getNextBatch').and.callFake(() => of(versionTest));
|
||||||
|
spyOn(component.versionsApi, 'listVersionHistory').and.callFake(() => Promise.resolve({ list: { entries: versionTest } }));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should raise confirmation dialog on delete', () => {
|
it('should raise confirmation dialog on delete', () => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
component.versions = versionTest;
|
|
||||||
|
|
||||||
spyOn(dialog, 'open').and.returnValue({
|
spyOn(dialog, 'open').and.returnValue({
|
||||||
afterClosed: () => of(false)
|
afterClosed: () => of(false)
|
||||||
@ -74,15 +77,13 @@ describe('VersionListComponent', () => {
|
|||||||
expect(dialog.open).toHaveBeenCalled();
|
expect(dialog.open).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should delete the version if user confirms', () => {
|
it('should delete the version if user confirms', async () => {
|
||||||
fixture.detectChanges();
|
|
||||||
component.versions = versionTest;
|
|
||||||
spyOn(dialog, 'open').and.returnValue({
|
spyOn(dialog, 'open').and.returnValue({
|
||||||
afterClosed: () => of(true)
|
afterClosed: () => of(true)
|
||||||
} as any);
|
} as any);
|
||||||
|
|
||||||
spyOn(component.versionsApi, 'deleteVersion').and.returnValue(Promise.resolve());
|
spyOn(component.versionsApi, 'deleteVersion').and.returnValue(Promise.resolve());
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
component.deleteVersion(versionId);
|
component.deleteVersion(versionId);
|
||||||
|
|
||||||
expect(dialog.open).toHaveBeenCalled();
|
expect(dialog.open).toHaveBeenCalled();
|
||||||
@ -90,14 +91,12 @@ describe('VersionListComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should not delete version if user rejects', () => {
|
it('should not delete version if user rejects', () => {
|
||||||
component.versions = versionTest;
|
|
||||||
|
|
||||||
spyOn(dialog, 'open').and.returnValue({
|
spyOn(dialog, 'open').and.returnValue({
|
||||||
afterClosed: () => of(false)
|
afterClosed: () => of(false)
|
||||||
} as any);
|
} as any);
|
||||||
|
|
||||||
spyOn(component.versionsApi, 'deleteVersion').and.returnValue(Promise.resolve());
|
spyOn(component.versionsApi, 'deleteVersion').and.returnValue(Promise.resolve());
|
||||||
|
fixture.detectChanges();
|
||||||
component.deleteVersion(versionId);
|
component.deleteVersion(versionId);
|
||||||
|
|
||||||
expect(dialog.open).toHaveBeenCalled();
|
expect(dialog.open).toHaveBeenCalled();
|
||||||
@ -115,40 +114,35 @@ describe('VersionListComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('Version history fetching', () => {
|
describe('Version history fetching', () => {
|
||||||
it('should use loading bar', () => {
|
it('should use loading bar', (done) => {
|
||||||
spyOn(component.versionsApi, 'listVersionHistory').and.callFake(() => Promise.resolve({ list: { entries: versionTest } }));
|
fixture.detectChanges();
|
||||||
|
|
||||||
let loadingProgressBar = fixture.debugElement.query(By.css('[data-automation-id="version-history-loading-bar"]'));
|
let loadingProgressBar = fixture.debugElement.query(By.css('[data-automation-id="version-history-loading-bar"]'));
|
||||||
expect(loadingProgressBar).toBeNull();
|
expect(loadingProgressBar).toBeNull();
|
||||||
|
|
||||||
component.ngOnChanges();
|
component.versionsDataSource.isLoading.pipe(take(1)).subscribe(() => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
loadingProgressBar = fixture.debugElement.query(By.css('[data-automation-id="version-history-loading-bar"]'));
|
||||||
|
expect(loadingProgressBar).not.toBeNull();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
loadingProgressBar = fixture.debugElement.query(By.css('[data-automation-id="version-history-loading-bar"]'));
|
component.ngOnChanges();
|
||||||
expect(loadingProgressBar).not.toBeNull();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should load the versions for a given id', () => {
|
it('should load the versions for a given id', () => {
|
||||||
spyOn(component.versionsApi, 'listVersionHistory').and.callFake(() => Promise.resolve({ list: { entries: versionTest } }));
|
fixture.detectChanges();
|
||||||
|
spyOn(component.versionsDataSource, 'reset');
|
||||||
|
|
||||||
component.ngOnChanges();
|
component.ngOnChanges();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
expect(component.versionsApi.listVersionHistory).toHaveBeenCalledWith(nodeId);
|
expect(component.versionsDataSource.reset).toHaveBeenCalled();
|
||||||
|
expect(component.versionsDataSource.getNextBatch).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show the versions after loading', (done) => {
|
it('should show the versions after loading', (done) => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
spyOn(component.versionsApi, 'listVersionHistory').and.callFake(() =>
|
|
||||||
Promise.resolve(
|
|
||||||
new VersionPaging({
|
|
||||||
list: {
|
|
||||||
entries: [versionTest[0]]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
component.ngOnChanges();
|
component.ngOnChanges();
|
||||||
|
|
||||||
fixture.whenStable().then(() => {
|
fixture.whenStable().then(() => {
|
||||||
@ -165,16 +159,6 @@ describe('VersionListComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should NOT show the versions comments if input property is set not to show them', (done) => {
|
it('should NOT show the versions comments if input property is set not to show them', (done) => {
|
||||||
spyOn(component.versionsApi, 'listVersionHistory').and.callFake(() =>
|
|
||||||
Promise.resolve(
|
|
||||||
new VersionPaging({
|
|
||||||
list: {
|
|
||||||
entries: [versionTest[0]]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
component.showComments = false;
|
component.showComments = false;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
@ -190,9 +174,6 @@ describe('VersionListComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to download a version', () => {
|
it('should be able to download a version', () => {
|
||||||
spyOn(component.versionsApi, 'listVersionHistory').and.returnValue(
|
|
||||||
Promise.resolve(new VersionPaging({ list: { entries: [versionTest[0]] } }))
|
|
||||||
);
|
|
||||||
spyOn(contentVersionService.contentApi, 'getContentUrl').and.returnValue('the/download/url');
|
spyOn(contentVersionService.contentApi, 'getContentUrl').and.returnValue('the/download/url');
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@ -209,9 +190,6 @@ describe('VersionListComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should NOT be able to download a version if configured so', () => {
|
it('should NOT be able to download a version if configured so', () => {
|
||||||
spyOn(component.versionsApi, 'listVersionHistory').and.callFake(() =>
|
|
||||||
Promise.resolve(new VersionPaging({ list: { entries: [versionTest[0]] } }))
|
|
||||||
);
|
|
||||||
const spyOnDownload = spyOn(component.contentApi, 'getContentUrl').and.stub();
|
const spyOnDownload = spyOn(component.contentApi, 'getContentUrl').and.stub();
|
||||||
|
|
||||||
component.allowDownload = false;
|
component.allowDownload = false;
|
||||||
@ -232,10 +210,7 @@ describe('VersionListComponent', () => {
|
|||||||
|
|
||||||
it('should load the versions for a given id', () => {
|
it('should load the versions for a given id', () => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
component.versions = versionTest;
|
|
||||||
|
|
||||||
const spyOnRevertVersion = spyOn(component.versionsApi, 'revertVersion').and.callFake(() => Promise.resolve(versionTest[0]));
|
const spyOnRevertVersion = spyOn(component.versionsApi, 'revertVersion').and.callFake(() => Promise.resolve(versionTest[0]));
|
||||||
|
|
||||||
component.restore(versionId);
|
component.restore(versionId);
|
||||||
|
|
||||||
expect(spyOnRevertVersion).toHaveBeenCalledWith(nodeId, versionId, { majorVersion: true, comment: '' });
|
expect(spyOnRevertVersion).toHaveBeenCalledWith(nodeId, versionId, { majorVersion: true, comment: '' });
|
||||||
@ -243,8 +218,6 @@ describe('VersionListComponent', () => {
|
|||||||
|
|
||||||
it('should get node info after restoring the node', fakeAsync(() => {
|
it('should get node info after restoring the node', fakeAsync(() => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
component.versions = versionTest;
|
|
||||||
spyOn(component.versionsApi, 'listVersionHistory').and.callFake(() => Promise.resolve({ list: { entries: versionTest } }));
|
|
||||||
|
|
||||||
spyOn(component.versionsApi, 'revertVersion').and.callFake(() => Promise.resolve(versionTest[0]));
|
spyOn(component.versionsApi, 'revertVersion').and.callFake(() => Promise.resolve(versionTest[0]));
|
||||||
|
|
||||||
@ -257,8 +230,6 @@ describe('VersionListComponent', () => {
|
|||||||
|
|
||||||
it('should emit with node info data', fakeAsync(() => {
|
it('should emit with node info data', fakeAsync(() => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
component.versions = versionTest;
|
|
||||||
spyOn(component.versionsApi, 'listVersionHistory').and.callFake(() => Promise.resolve({ list: { entries: versionTest } }));
|
|
||||||
|
|
||||||
spyOn(component.versionsApi, 'revertVersion').and.callFake(() => Promise.resolve(versionTest[0]));
|
spyOn(component.versionsApi, 'revertVersion').and.callFake(() => Promise.resolve(versionTest[0]));
|
||||||
|
|
||||||
@ -273,18 +244,16 @@ describe('VersionListComponent', () => {
|
|||||||
|
|
||||||
it('should reload the version list after a version restore', fakeAsync(() => {
|
it('should reload the version list after a version restore', fakeAsync(() => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
component.versions = versionTest;
|
|
||||||
|
|
||||||
const spyOnListVersionHistory = spyOn(component.versionsApi, 'listVersionHistory').and.callFake(() =>
|
|
||||||
Promise.resolve({ list: { entries: versionTest } })
|
|
||||||
);
|
|
||||||
spyOn(component.versionsApi, 'revertVersion').and.callFake(() => Promise.resolve(null));
|
spyOn(component.versionsApi, 'revertVersion').and.callFake(() => Promise.resolve(null));
|
||||||
|
spyOn(component.versionsDataSource, 'reset');
|
||||||
|
|
||||||
component.restore(versionId);
|
component.restore(versionId);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
tick();
|
tick();
|
||||||
|
|
||||||
expect(spyOnListVersionHistory).toHaveBeenCalledTimes(1);
|
expect(component.versionsDataSource.reset).toHaveBeenCalled();
|
||||||
|
expect(component.versionsDataSource.getNextBatch).toHaveBeenCalled();
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -302,15 +271,6 @@ describe('VersionListComponent', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
versionTest[1].entry.id = '1.1';
|
versionTest[1].entry.id = '1.1';
|
||||||
spyOn(component.versionsApi, 'listVersionHistory').and.callFake(() =>
|
|
||||||
Promise.resolve(
|
|
||||||
new VersionPaging({
|
|
||||||
list: {
|
|
||||||
entries: versionTest
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('showActions', () => {
|
describe('showActions', () => {
|
||||||
@ -320,8 +280,6 @@ describe('VersionListComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should show Actions if showActions is true', (done) => {
|
it('should show Actions if showActions is true', (done) => {
|
||||||
component.versions = versionTest;
|
|
||||||
|
|
||||||
component.showActions = true;
|
component.showActions = true;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
@ -16,12 +16,29 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { AlfrescoApiService } from '@alfresco/adf-core';
|
import { AlfrescoApiService } from '@alfresco/adf-core';
|
||||||
import { Component, Input, OnChanges, ViewEncapsulation, EventEmitter, Output } from '@angular/core';
|
import { Component, Input, OnChanges, ViewEncapsulation, EventEmitter, Output, OnInit, OnDestroy, ViewChild } from '@angular/core';
|
||||||
import { VersionsApi, Node, VersionEntry, VersionPaging, NodesApi, NodeEntry, ContentApi } from '@alfresco/js-api';
|
import { VersionsApi, Node, VersionEntry, NodesApi, NodeEntry, ContentApi, ContentPagingQuery } from '@alfresco/js-api';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { ConfirmDialogComponent } from '../dialogs/confirm.dialog';
|
import { ConfirmDialogComponent } from '../dialogs/confirm.dialog';
|
||||||
import { ContentVersionService } from './content-version.service';
|
import { ContentVersionService } from './content-version.service';
|
||||||
import { ContentService } from '../common/services/content.service';
|
import { ContentService } from '../common/services/content.service';
|
||||||
|
import { InfiniteScrollDatasource } from '../infinite-scroll-datasource';
|
||||||
|
import { from, Observable, Subject } from 'rxjs';
|
||||||
|
import { map, take, takeUntil } from 'rxjs/operators';
|
||||||
|
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
||||||
|
|
||||||
|
export class VersionListDataSource extends InfiniteScrollDatasource<VersionEntry> {
|
||||||
|
constructor(private versionsApi: VersionsApi, private node: Node) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
getNextBatch(pagingOptions: ContentPagingQuery): Observable<VersionEntry[]> {
|
||||||
|
return from(this.versionsApi.listVersionHistory(this.node.id, pagingOptions)).pipe(
|
||||||
|
take(1),
|
||||||
|
map((versionPaging) => versionPaging.list.entries)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'adf-version-list',
|
selector: 'adf-version-list',
|
||||||
@ -30,8 +47,8 @@ import { ContentService } from '../common/services/content.service';
|
|||||||
encapsulation: ViewEncapsulation.None,
|
encapsulation: ViewEncapsulation.None,
|
||||||
host: { class: 'adf-version-list' }
|
host: { class: 'adf-version-list' }
|
||||||
})
|
})
|
||||||
export class VersionListComponent implements OnChanges {
|
export class VersionListComponent implements OnChanges, OnInit, OnDestroy {
|
||||||
|
private onDestroy$ = new Subject<void>();
|
||||||
private _contentApi: ContentApi;
|
private _contentApi: ContentApi;
|
||||||
get contentApi(): ContentApi {
|
get contentApi(): ContentApi {
|
||||||
this._contentApi = this._contentApi ?? new ContentApi(this.alfrescoApi.getInstance());
|
this._contentApi = this._contentApi ?? new ContentApi(this.alfrescoApi.getInstance());
|
||||||
@ -50,7 +67,8 @@ export class VersionListComponent implements OnChanges {
|
|||||||
return this._nodesApi;
|
return this._nodesApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
versions: VersionEntry[] = [];
|
versionsDataSource: VersionListDataSource;
|
||||||
|
latestVersion: VersionEntry;
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
|
|
||||||
/** The target node. */
|
/** The target node. */
|
||||||
@ -85,34 +103,48 @@ export class VersionListComponent implements OnChanges {
|
|||||||
@Output()
|
@Output()
|
||||||
viewVersion = new EventEmitter<string>();
|
viewVersion = new EventEmitter<string>();
|
||||||
|
|
||||||
constructor(private alfrescoApi: AlfrescoApiService,
|
@ViewChild('viewport')
|
||||||
private contentService: ContentService,
|
viewport: CdkVirtualScrollViewport;
|
||||||
private contentVersionService: ContentVersionService,
|
|
||||||
private dialog: MatDialog) {
|
constructor(
|
||||||
|
private alfrescoApi: AlfrescoApiService,
|
||||||
|
private contentService: ContentService,
|
||||||
|
private contentVersionService: ContentVersionService,
|
||||||
|
private dialog: MatDialog
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.versionsDataSource = new VersionListDataSource(this.versionsApi, this.node);
|
||||||
|
this.versionsDataSource.isLoading.pipe(takeUntil(this.onDestroy$)).subscribe((isLoading) => {
|
||||||
|
this.isLoading = isLoading;
|
||||||
|
this.latestVersion = this.versionsDataSource.firstItem;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges() {
|
ngOnChanges() {
|
||||||
this.loadVersionHistory();
|
if (this.versionsDataSource) {
|
||||||
|
this.loadVersionHistory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.onDestroy$.next();
|
||||||
|
this.onDestroy$.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
canUpdate(): boolean {
|
canUpdate(): boolean {
|
||||||
return this.contentService.hasAllowableOperations(this.node, 'update') && this.versions.length > 1;
|
return this.contentService.hasAllowableOperations(this.node, 'update') && this.versionsDataSource.itemsCount > 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
canDelete(): boolean {
|
canDelete(): boolean {
|
||||||
return this.contentService.hasAllowableOperations(this.node, 'delete') && this.versions.length > 1;
|
return this.contentService.hasAllowableOperations(this.node, 'delete') && this.versionsDataSource.itemsCount > 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
restore(versionId: string) {
|
restore(versionId: string) {
|
||||||
if (this.canUpdate()) {
|
if (this.canUpdate()) {
|
||||||
this.versionsApi
|
this.versionsApi
|
||||||
.revertVersion(this.node.id, versionId, { majorVersion: true, comment: '' })
|
.revertVersion(this.node.id, versionId, { majorVersion: true, comment: '' })
|
||||||
.then(() =>
|
.then(() => this.nodesApi.getNode(this.node.id, { include: ['permissions', 'path', 'isFavorite', 'allowableOperations'] }))
|
||||||
this.nodesApi.getNode(
|
|
||||||
this.node.id,
|
|
||||||
{ include: ['permissions', 'path', 'isFavorite', 'allowableOperations'] }
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.then((node) => this.onVersionRestored(node));
|
.then((node) => this.onVersionRestored(node));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -122,18 +154,16 @@ export class VersionListComponent implements OnChanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadVersionHistory() {
|
loadVersionHistory() {
|
||||||
this.isLoading = true;
|
this.viewport.scrollToIndex(0);
|
||||||
this.versionsApi.listVersionHistory(this.node.id).then((versionPaging: VersionPaging) => {
|
this.versionsDataSource.reset();
|
||||||
this.versions = versionPaging.list.entries;
|
|
||||||
this.isLoading = false;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadVersion(versionId: string) {
|
downloadVersion(versionId: string) {
|
||||||
if (this.allowDownload) {
|
if (this.allowDownload) {
|
||||||
this.contentVersionService
|
this.contentVersionService
|
||||||
.getVersionContentUrl(this.node.id, versionId, true)
|
.getVersionContentUrl(this.node.id, versionId, true)
|
||||||
.subscribe(versionDownloadUrl => this.downloadContent(versionDownloadUrl));
|
.pipe(takeUntil(this.onDestroy$))
|
||||||
|
.subscribe((versionDownloadUrl) => this.downloadContent(versionDownloadUrl));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,13 +179,14 @@ export class VersionListComponent implements OnChanges {
|
|||||||
minWidth: '250px'
|
minWidth: '250px'
|
||||||
});
|
});
|
||||||
|
|
||||||
dialogRef.afterClosed().subscribe((result) => {
|
dialogRef
|
||||||
if (result === true) {
|
.afterClosed()
|
||||||
this.versionsApi
|
.pipe(takeUntil(this.onDestroy$))
|
||||||
.deleteVersion(this.node.id, versionId)
|
.subscribe((result) => {
|
||||||
.then(() => this.onVersionDeleted(this.node));
|
if (result) {
|
||||||
}
|
this.versionsApi.deleteVersion(this.node.id, versionId).then(() => this.onVersionDeleted(this.node));
|
||||||
});
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
id="adf-version-upload-button"
|
id="adf-version-upload-button"
|
||||||
[node]="node"
|
[node]="node"
|
||||||
[newFileVersion]="newFileVersion"
|
[newFileVersion]="newFileVersion"
|
||||||
[currentVersion]="versionList?.versions[0]?.entry"
|
[currentVersion]="versionList?.latestVersion?.entry"
|
||||||
(success)="onUploadSuccess($event)"
|
(success)="onUploadSuccess($event)"
|
||||||
(cancel)="onUploadCancel()"
|
(cancel)="onUploadCancel()"
|
||||||
(error)="onUploadError($event)">
|
(error)="onUploadError($event)">
|
||||||
|
@ -55,7 +55,7 @@ describe('VersionManagerComponent', () => {
|
|||||||
|
|
||||||
it('should load the versions for a given node', () => {
|
it('should load the versions for a given node', () => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(spyOnListVersionHistory).toHaveBeenCalledWith(node.id);
|
expect(spyOnListVersionHistory).toHaveBeenCalledWith(node.id, { skipCount: 0, maxItems: 100 });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should toggle new version if given a new file as input', () => {
|
it('should toggle new version if given a new file as input', () => {
|
||||||
|
@ -27,6 +27,7 @@ import { UploadModule } from '../upload/upload.module';
|
|||||||
import { VersionCompatibilityModule } from '../version-compatibility/version-compatibility.module';
|
import { VersionCompatibilityModule } from '../version-compatibility/version-compatibility.module';
|
||||||
import { CoreModule } from '@alfresco/adf-core';
|
import { CoreModule } from '@alfresco/adf-core';
|
||||||
import { VersionComparisonComponent } from './version-comparison.component';
|
import { VersionComparisonComponent } from './version-comparison.component';
|
||||||
|
import { ScrollingModule } from '@angular/cdk/scrolling';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@ -35,7 +36,8 @@ import { VersionComparisonComponent } from './version-comparison.component';
|
|||||||
CoreModule,
|
CoreModule,
|
||||||
UploadModule,
|
UploadModule,
|
||||||
VersionCompatibilityModule,
|
VersionCompatibilityModule,
|
||||||
FormsModule
|
FormsModule,
|
||||||
|
ScrollingModule
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
VersionUploadComponent,
|
VersionUploadComponent,
|
||||||
|
@ -46,5 +46,6 @@ export * from './lib/tree/index';
|
|||||||
export * from './lib/category/index';
|
export * from './lib/category/index';
|
||||||
export * from './lib/viewer/index';
|
export * from './lib/viewer/index';
|
||||||
export * from './lib/security/index';
|
export * from './lib/security/index';
|
||||||
|
export * from './lib/infinite-scroll-datasource';
|
||||||
|
|
||||||
export * from './lib/content.module';
|
export * from './lib/content.module';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user