collection-dropdown.component.ts (dspace-angular-dspace-7.0) | : | collection-dropdown.component.ts (dspace-angular-dspace-7.1) | ||
---|---|---|---|---|
import { | import { | |||
ChangeDetectorRef, | ChangeDetectorRef, | |||
Component, | Component, | |||
ElementRef, | ElementRef, | |||
EventEmitter, | EventEmitter, | |||
HostListener, | HostListener, | |||
Input, | ||||
OnDestroy, | OnDestroy, | |||
OnInit, | OnInit, | |||
Output | Output | |||
} from '@angular/core'; | } from '@angular/core'; | |||
import { FormControl } from '@angular/forms'; | import { FormControl } from '@angular/forms'; | |||
import { BehaviorSubject, Observable, Subscription } from 'rxjs'; | import { BehaviorSubject, from as observableFrom, Observable, of as observableOf | |||
import { debounceTime, distinctUntilChanged, map, mergeMap, reduce, startWith, s | , Subscription } from 'rxjs'; | |||
witchMap } from 'rxjs/operators'; | import { debounceTime, distinctUntilChanged, map, mergeMap, reduce, startWith, s | |||
witchMap, take } from 'rxjs/operators'; | ||||
import { hasValue } from '../empty.util'; | import { hasValue } from '../empty.util'; | |||
import { RemoteData } from '../../core/data/remote-data'; | import { RemoteData } from '../../core/data/remote-data'; | |||
import { FindListOptions } from '../../core/data/request.models'; | import { FindListOptions } from '../../core/data/request.models'; | |||
import { PaginatedList } from '../../core/data/paginated-list.model'; | import { PaginatedList } from '../../core/data/paginated-list.model'; | |||
import { Community } from '../../core/shared/community.model'; | import { Community } from '../../core/shared/community.model'; | |||
import { CollectionDataService } from '../../core/data/collection-data.service'; | import { CollectionDataService } from '../../core/data/collection-data.service'; | |||
import { Collection } from '../../core/shared/collection.model'; | import { Collection } from '../../core/shared/collection.model'; | |||
import { followLink } from '../utils/follow-link-config.model'; | import { followLink } from '../utils/follow-link-config.model'; | |||
import { | import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from | |||
getFirstSucceededRemoteDataPayload, | '../../core/shared/operators'; | |||
getFirstSucceededRemoteWithNotEmptyData | ||||
} from '../../core/shared/operators'; | ||||
/** | /** | |||
* An interface to represent a collection entry | * An interface to represent a collection entry | |||
*/ | */ | |||
interface CollectionListEntryItem { | interface CollectionListEntryItem { | |||
id: string; | id: string; | |||
uuid: string; | uuid: string; | |||
name: string; | name: string; | |||
} | } | |||
skipping to change at line 92 | skipping to change at line 90 | |||
/** | /** | |||
* The list of collection to render | * The list of collection to render | |||
*/ | */ | |||
searchListCollection: CollectionListEntry[] = []; | searchListCollection: CollectionListEntry[] = []; | |||
@Output() selectionChange = new EventEmitter<CollectionListEntry>(); | @Output() selectionChange = new EventEmitter<CollectionListEntry>(); | |||
/** | /** | |||
* A boolean representing if the loader is visible or not | * A boolean representing if the loader is visible or not | |||
*/ | */ | |||
isLoadingList: BehaviorSubject<boolean> = new BehaviorSubject(false); | isLoading: BehaviorSubject<boolean> = new BehaviorSubject(false); | |||
/** | /** | |||
* A numeric representig current page | * A numeric representing current page | |||
*/ | */ | |||
currentPage: number; | currentPage: number; | |||
/** | /** | |||
* A boolean representing if exist another page to render | * A boolean representing if exist another page to render | |||
*/ | */ | |||
hasNextPage: boolean; | hasNextPage: boolean; | |||
/** | /** | |||
* Current seach query used to filter collection list | * Current search query used to filter collection list | |||
*/ | */ | |||
currentQuery: string; | currentQuery: string; | |||
/** | ||||
* If present this value is used to filter collection list by entity type | ||||
*/ | ||||
@Input() entityType: string; | ||||
/** | ||||
* Emit to notify whether search is complete | ||||
*/ | ||||
@Output() searchComplete = new EventEmitter<any>(); | ||||
/** | ||||
* Emit to notify the only selectable collection. | ||||
*/ | ||||
@Output() theOnlySelectable = new EventEmitter<CollectionListEntry>(); | ||||
constructor( | constructor( | |||
private changeDetectorRef: ChangeDetectorRef, | private changeDetectorRef: ChangeDetectorRef, | |||
private collectionDataService: CollectionDataService, | private collectionDataService: CollectionDataService, | |||
private el: ElementRef | private el: ElementRef | |||
) { } | ) { } | |||
/** | /** | |||
* Method called on mousewheel event, it prevent the page scroll | * Method called on mousewheel event, it prevent the page scroll | |||
* when arriving at the top/bottom of dropdown menu | * when arriving at the top/bottom of dropdown menu | |||
* | * | |||
skipping to change at line 135 | skipping to change at line 148 | |||
} | } | |||
if (event.wheelDelta < 0 && this.scrollableBottom) { | if (event.wheelDelta < 0 && this.scrollableBottom) { | |||
event.preventDefault(); | event.preventDefault(); | |||
} | } | |||
} | } | |||
/** | /** | |||
* Initialize collection list | * Initialize collection list | |||
*/ | */ | |||
ngOnInit() { | ngOnInit() { | |||
this.isLoading.next(false); | ||||
this.subs.push(this.searchField.valueChanges.pipe( | this.subs.push(this.searchField.valueChanges.pipe( | |||
debounceTime(500), | debounceTime(500), | |||
distinctUntilChanged(), | distinctUntilChanged(), | |||
startWith('') | startWith('') | |||
).subscribe( | ).subscribe( | |||
(next) => { | (next) => { | |||
if (hasValue(next) && next !== this.currentQuery) { | if (hasValue(next) && next !== this.currentQuery) { | |||
this.resetPagination(); | this.resetPagination(); | |||
this.currentQuery = next; | this.currentQuery = next; | |||
this.populateCollectionList(this.currentQuery, this.currentPage); | this.populateCollectionList(this.currentQuery, this.currentPage); | |||
skipping to change at line 163 | skipping to change at line 177 | |||
* Check if dropdown scrollbar is at the top or bottom of the dropdown list | * Check if dropdown scrollbar is at the top or bottom of the dropdown list | |||
* | * | |||
* @param event | * @param event | |||
*/ | */ | |||
onScroll(event) { | onScroll(event) { | |||
this.scrollableBottom = (event.target.scrollTop + event.target.clientHeight === event.target.scrollHeight); | this.scrollableBottom = (event.target.scrollTop + event.target.clientHeight === event.target.scrollHeight); | |||
this.scrollableTop = (event.target.scrollTop === 0); | this.scrollableTop = (event.target.scrollTop === 0); | |||
} | } | |||
/** | /** | |||
* Method used from infitity scroll for retrive more data on scroll down | * Method used from infinity scroll for retrieve more data on scroll down | |||
*/ | */ | |||
onScrollDown() { | onScrollDown() { | |||
if ( this.hasNextPage ) { | if ( this.hasNextPage ) { | |||
this.populateCollectionList(this.currentQuery, ++this.currentPage); | this.populateCollectionList(this.currentQuery, ++this.currentPage); | |||
} | } | |||
} | } | |||
/** | /** | |||
* Emit a [selectionChange] event when a new collection is selected from list | * Emit a [selectionChange] event when a new collection is selected from list | |||
* | * | |||
* @param event | * @param event | |||
* the selected [CollectionListEntry] | * the selected [CollectionListEntry] | |||
*/ | */ | |||
onSelect(event: CollectionListEntry) { | onSelect(event: CollectionListEntry) { | |||
this.isLoading.next(true); | ||||
this.selectionChange.emit(event); | this.selectionChange.emit(event); | |||
} | } | |||
/** | /** | |||
* Method called for populate the collection list | * Method called for populate the collection list | |||
* @param query text for filter the collection list | * @param query text for filter the collection list | |||
* @param page page number | * @param page page number | |||
*/ | */ | |||
populateCollectionList(query: string, page: number) { | populateCollectionList(query: string, page: number) { | |||
this.isLoadingList.next(true); | this.isLoading.next(true); | |||
// Set the pagination info | // Set the pagination info | |||
const findOptions: FindListOptions = { | const findOptions: FindListOptions = { | |||
elementsPerPage: 10, | elementsPerPage: 10, | |||
currentPage: page | currentPage: page | |||
}; | }; | |||
this.searchListCollection$ = this.collectionDataService | let searchListService$: Observable<RemoteData<PaginatedList<Collection>>>; | |||
.getAuthorizedCollection(query, findOptions, true, false, followLink('pare | if (this.entityType) { | |||
ntCommunity')) | searchListService$ = this.collectionDataService | |||
.pipe( | .getAuthorizedCollectionByEntityType( | |||
getFirstSucceededRemoteWithNotEmptyData(), | query, | |||
switchMap((collections: RemoteData<PaginatedList<Collection>>) => { | this.entityType, | |||
if ( (this.searchListCollection.length + findOptions.elementsPerPage) | findOptions, | |||
>= collections.payload.totalElements ) { | true, | |||
followLink('parentCommunity')); | ||||
} else { | ||||
searchListService$ = this.collectionDataService | ||||
.getAuthorizedCollection(query, findOptions, true, true, followLink('paren | ||||
tCommunity')); | ||||
} | ||||
this.searchListCollection$ = searchListService$.pipe( | ||||
getFirstCompletedRemoteData(), | ||||
switchMap((collectionsRD: RemoteData<PaginatedList<Collection>>) => { | ||||
this.searchComplete.emit(); | ||||
if (collectionsRD.hasSucceeded && collectionsRD.payload.totalElements | ||||
> 0) { | ||||
if ( (this.searchListCollection.length + findOptions.elementsPerPage | ||||
) >= collectionsRD.payload.totalElements ) { | ||||
this.hasNextPage = false; | ||||
this.emitSelectionEvents(collectionsRD); | ||||
return observableFrom(collectionsRD.payload.page).pipe( | ||||
mergeMap((collection: Collection) => collection.parentCommunity. | ||||
pipe( | ||||
getFirstSucceededRemoteDataPayload(), | ||||
map((community: Community) => ({ | ||||
communities: [{ id: community.id, name: community.name }], | ||||
collection: { id: collection.id, uuid: collection.id, name | ||||
: collection.name } | ||||
}) | ||||
))), | ||||
reduce((acc: any, value: any) => [...acc, value], []), | ||||
); | ||||
} | ||||
} else { | ||||
this.hasNextPage = false; | this.hasNextPage = false; | |||
return observableOf([]); | ||||
} | } | |||
return collections.payload.page; | }) | |||
}), | ||||
mergeMap((collection: Collection) => collection.parentCommunity.pipe( | ||||
getFirstSucceededRemoteDataPayload(), | ||||
map((community: Community) => ({ | ||||
communities: [{ id: community.id, name: community.name }], | ||||
collection: { id: collection.id, uuid: collection.id, name: collecti | ||||
on.name } | ||||
}) | ||||
))), | ||||
reduce((acc: any, value: any) => [...acc, value], []), | ||||
startWith([]) | ||||
); | ); | |||
this.subs.push(this.searchListCollection$.subscribe( | this.subs.push( | |||
(next) => { this.searchListCollection.push(...next); }, undefined, | this.searchListCollection$.subscribe((list: CollectionListEntry[]) => { | |||
() => { this.hideShowLoader(false); this.changeDetectorRef.detectChanges() | this.searchListCollection.push(...list); | |||
; } | this.hideShowLoader(false); | |||
)); | this.changeDetectorRef.detectChanges(); | |||
}) | ||||
); | ||||
} | } | |||
/** | /** | |||
* Unsubscribe from all subscriptions | * Unsubscribe from all subscriptions | |||
*/ | */ | |||
ngOnDestroy(): void { | ngOnDestroy(): void { | |||
this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe()) ; | this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe()) ; | |||
} | } | |||
/** | /** | |||
skipping to change at line 248 | skipping to change at line 284 | |||
this.currentQuery = ''; | this.currentQuery = ''; | |||
this.hasNextPage = true; | this.hasNextPage = true; | |||
this.searchListCollection = []; | this.searchListCollection = []; | |||
} | } | |||
/** | /** | |||
* Hide/Show the collection list loader | * Hide/Show the collection list loader | |||
* @param hideShow true for show, false otherwise | * @param hideShow true for show, false otherwise | |||
*/ | */ | |||
hideShowLoader(hideShow: boolean) { | hideShowLoader(hideShow: boolean) { | |||
this.isLoadingList.next(hideShow); | this.isLoading.next(hideShow); | |||
} | ||||
/** | ||||
* Emit events related to the number of selectable collections. | ||||
* hasChoice containing whether there are more then one selectable collections | ||||
. | ||||
* theOnlySelectable containing the only collection available. | ||||
* @param collections | ||||
* @private | ||||
*/ | ||||
private emitSelectionEvents(collections: RemoteData<PaginatedList<Collection>> | ||||
) { | ||||
if (collections.payload.totalElements === 1) { | ||||
const collection = collections.payload.page[0]; | ||||
collections.payload.page[0].parentCommunity.pipe( | ||||
getFirstSucceededRemoteDataPayload(), | ||||
take(1) | ||||
).subscribe((community: Community) => { | ||||
this.theOnlySelectable.emit({ | ||||
communities: [{ id: community.id, name: community.name, uuid: communit | ||||
y.id }], | ||||
collection: { id: collection.id, uuid: collection.id, name: collection | ||||
.name } | ||||
}); | ||||
}); | ||||
} | ||||
} | } | |||
} | } | |||
End of changes. 17 change blocks. | ||||
38 lines changed or deleted | 104 lines changed or added |