Skip to content

Commit

Permalink
Merge branch 'w2p-112185_Fix-show-more-search-list' into 'atmire-base…
Browse files Browse the repository at this point in the history
…-7.6'

112185: Add support to use next page to atmireshowmore

See merge request templates/dspace-angular-7!153
  • Loading branch information
artlowel committed Feb 28, 2024
2 parents 490c205 + b65345d commit d9fd9fc
Show file tree
Hide file tree
Showing 9 changed files with 170 additions and 33 deletions.
4 changes: 3 additions & 1 deletion src/app-atmire/atmire-app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { SectionHeaderMenuItemComponent } from './shared/menu/menu-item/section-
import { AtmireMenuService } from './shared/menu/atmire-menu.service';
import { HtmlWithRouterLinksDirective } from './shared/utils/html-with-router-links.directive';
import { AtmirePlaceholderComponent } from './shared/placeholder/atmire-placeholder.component';
import { AtmireSearchService } from './core/shared/search/atmire-search.service';

const DECLARATIONS = [
SectionHeaderMenuItemComponent,
Expand All @@ -38,7 +39,8 @@ const PROVIDERS = [
provide: DSOEditMenuResolver,
useClass: AtmireDsoEditMenuResolver,
},
AtmireMenuService
AtmireMenuService,
AtmireSearchService,
];

@NgModule({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export class AtmireShowMoreComponent<T extends ListableCacheableObject> extends

isLastPage: boolean;
currentPage: number;
next: string;
results$: BehaviorSubject<ListableObject[]>;

constructor(
Expand All @@ -62,6 +63,7 @@ export class AtmireShowMoreComponent<T extends ListableCacheableObject> extends
* This method will retrieve the next page of search results from the external SearchService#search call.
* It'll retrieve the currentPage from the class variables and it'll add the next page to the already existing one.
* If the currentPage variable is undefined, we'll set it to 1 and retrieve the first page.
* If the previous RemoteData contained a "next" link, it'll use this link instead to directly retrieve the next page
*
* Adapted from FileSectionComponent#getNextPage()
*/
Expand All @@ -80,11 +82,18 @@ export class AtmireShowMoreComponent<T extends ListableCacheableObject> extends
this.resultSub.unsubscribe();
}

this.resultSub =
this.source.getList(
{...this.paginationOptions, currentPage: this.currentPage},
let list$;
if (hasValue(this.next)) {
list$ = this.source.getListByHref(this.next);
} else {
list$ = this.source.getList(
{ ...this.paginationOptions, currentPage: this.currentPage },
this.sortOptions,
).pipe(
);
}

this.resultSub =
list$.pipe(
getFirstCompletedRemoteData()
).subscribe((resultRD: RemoteData<PaginatedList<ListableObject>>) => {
if (resultRD.errorMessage) {
Expand Down Expand Up @@ -112,8 +121,14 @@ export class AtmireShowMoreComponent<T extends ListableCacheableObject> extends
this.setError('atmire.object-collection.error.rendering-some');
}

this.isLastPage = this.currentPage === this.paginationOptions.maxSize
|| this.currentPage === resultRD.payload.totalPages;
this.next = resultRD.payload.next;

if (hasValue(this.next)) {
this.isLastPage = false;
} else {
this.isLastPage = this.currentPage >= this.paginationOptions.maxSize
|| this.currentPage >= resultRD.payload.totalPages;
}

}
this.isLoading$.next(false);
Expand All @@ -122,6 +137,7 @@ export class AtmireShowMoreComponent<T extends ListableCacheableObject> extends

collapse(): void {
this.currentPage = undefined;
this.next = undefined;
this.showMore();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { PaginatedList } from '../../../app/core/data/paginated-list.model';
import { ListableObjectSourceType } from '../listable-object-sources';
import { CacheableObject } from '../../../app/core/cache/cacheable-object.model';
import { ListableCacheableObject } from '../base/listable-cachable-object.model';
import { HrefOnlyDataService } from '../../../app/core/data/href-only-data.service';


export interface AbstractSourceKwargs<T extends ListableObject & CacheableObject> {
Expand Down Expand Up @@ -52,7 +53,8 @@ export abstract class AbstractListableObjectSource<T extends ListableCacheableOb
protected reRequestOnStale? = true;
protected linksToFollow?: FollowLinkConfig<T>[] = [];

protected constructor(model?: AbstractListableObjectSourceModel<T>) {
protected constructor(protected hrefOnlyDataService: HrefOnlyDataService,
model?: AbstractListableObjectSourceModel<T>) {
this.useCachedVersionIfAvailable = model.useCachedVersionIfAvailable;
this.reRequestOnStale = model.reRequestOnStale;
this.linksToFollow = model.linksToFollow;
Expand All @@ -72,4 +74,20 @@ export abstract class AbstractListableObjectSource<T extends ListableCacheableOb
abstract getList(
paginationOptions: Partial<PaginationComponentOptions>, sort: Partial<SortOptions>,
): Observable<RemoteData<PaginatedList<ListableObject>>>;

/**
* Retrieve a list of objects from a link
* The link is expected to contain all necessary parameters already
* An example usage of this is using the "next" link on paginated lists to retrieve the next page directly
* @param href Full link to the list of objects
*/
public getListByHref(href: string): Observable<RemoteData<PaginatedList<ListableObject>>> {
return this.hrefOnlyDataService.findListByHref<T>(
href,
undefined,
this.useCachedVersionIfAvailable,
this.reRequestOnStale,
...this.linksToFollow
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ export class AtmireHrefListableObjectSource<T extends ListableObject & Cacheable
private readonly href: string;

constructor(
protected hrefOnlyDataService: HrefOnlyDataService,
model: AtmireHrefListableObjectSourceModel<T>,
private hrefOnlyDataService: HrefOnlyDataService,
) {
super(model);
super(hrefOnlyDataService, model);
this.href = model.href;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@
*/
import { PaginationComponentOptions } from '../../../app/shared/pagination/pagination-component-options.model';
import { SortOptions } from '../../../app/core/cache/models/sort-options.model';
import { Observable } from 'rxjs/internal/Observable';
import { RemoteData } from '../../../app/core/data/remote-data';
import { PaginatedList } from '../../../app/core/data/paginated-list.model';
import { PaginatedSearchOptions } from '../../../app/shared/search/models/paginated-search-options.model';
import { SearchService } from '../../../app/core/shared/search/search.service';
import { SearchOptions } from '../../../app/shared/search/models/search-options.model';
import { AtmireSearchListableObjectSourceModel } from '../listable-object-sources';
import { AbstractListableObjectSource, AbstractListableObjectSourceModel } from './abstract-listable-object-source';
import { map } from 'rxjs/operators';
import { DSpaceObject } from '../../../app/core/shared/dspace-object.model';
import { ListableObject } from '../../../app/shared/object-collection/shared/listable-object.model';
import { BehaviorSubject } from 'rxjs';
import { BehaviorSubject, Observable, of as observableOf } from 'rxjs';
import { environment } from '../../../environments/environment';
import { AtmireSearchService } from '../../core/shared/search/atmire-search.service';
import { HrefOnlyDataService } from '../../../app/core/data/href-only-data.service';

/**
* Retrieves objects via {@link SearchService}.
Expand All @@ -31,10 +31,11 @@ export class AtmireSearchListableObjectSource<T extends DSpaceObject> extends Ab
private readonly href$: BehaviorSubject<string> = new BehaviorSubject<string>('search');

constructor(
protected hrefOnlyDataService: HrefOnlyDataService,
model: AtmireSearchListableObjectSourceModel<T>,
private searchService: SearchService,
protected searchService: AtmireSearchService,
) {
super(model as AbstractListableObjectSourceModel<T>);
super(hrefOnlyDataService, model as AbstractListableObjectSourceModel<T>);
this.searchOptions = model.searchOptions;
this.displayAsSearchResults = model.displayAsSearchResults;

Expand Down Expand Up @@ -72,6 +73,14 @@ export class AtmireSearchListableObjectSource<T extends DSpaceObject> extends Ab
);
}

getListByHref(href: string): Observable<RemoteData<PaginatedList<ListableObject>>> {
return this.searchService.searchByHref(observableOf(href),
undefined,
this.useCachedVersionIfAvailable,
this.reRequestOnStale,
...this.linksToFollow);
}

public get params() {
return {
href: this.href$.getValue(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
* https://www.atmire.com/software-license/
*/
import { Injectable } from '@angular/core';
import { AtmireSearchService } from '../../core/shared/search/atmire-search.service';
import { HrefOnlyDataService } from '../../../app/core/data/href-only-data.service';
import { SearchService } from '../../../app/core/shared/search/search.service';
import { ListableObjectSource, ListableObjectSourceModel, ListableObjectSourceType } from '../listable-object-sources';
import { AtmireHrefListableObjectSource } from './atmire-href-listable-object-source';
import { AtmireSearchListableObjectSource } from './atmire-search-listable-object-source';
Expand All @@ -20,7 +20,7 @@ import { AtmireSearchListableObjectSource } from './atmire-search-listable-objec
export class ListableObjectSourceFactoryService {
constructor(
private hrefOnlyDataService: HrefOnlyDataService,
private searchService: SearchService,
private searchService: AtmireSearchService,
) {
}

Expand All @@ -31,10 +31,10 @@ export class ListableObjectSourceFactoryService {
getSource(model: ListableObjectSourceModel): ListableObjectSource {
switch (model.source) {
case ListableObjectSourceType.HREF: {
return new AtmireHrefListableObjectSource(model, this.hrefOnlyDataService);
return new AtmireHrefListableObjectSource(this.hrefOnlyDataService, model);
}
case ListableObjectSourceType.SEARCH: {
return new AtmireSearchListableObjectSource(model, this.searchService);
return new AtmireSearchListableObjectSource(this.hrefOnlyDataService, model, this.searchService);
}
}
}
Expand Down
92 changes: 92 additions & 0 deletions src/app-atmire/core/shared/search/atmire-search.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE_ATMIRE and NOTICE_ATMIRE files at the root of the source
* tree and available online at
*
* https://www.atmire.com/software-license/
*/
import { SearchService } from '../../../../app/core/shared/search/search.service';
import { Injectable } from '@angular/core';
import { RouteService } from '../../../../app/core/services/route.service';
import { RequestService } from '../../../../app/core/data/request.service';
import { RemoteDataBuildService } from '../../../../app/core/cache/builders/remote-data-build.service';
import { LinkService } from '../../../../app/core/cache/builders/link.service';
import { HALEndpointService } from '../../../../app/core/shared/hal-endpoint.service';
import { CommunityDataService } from '../../../../app/core/data/community-data.service';
import { DSpaceObjectDataService } from '../../../../app/core/data/dspace-object-data.service';
import { PaginationService } from '../../../../app/core/pagination/pagination.service';
import { SearchConfigurationService } from '../../../../app/core/shared/search/search-configuration.service';
import { PaginatedSearchOptions } from '../../../../app/shared/search/models/paginated-search-options.model';
import { Observable } from 'rxjs';
import { map, switchMap, take } from 'rxjs/operators';
import { hasValue, isNotEmpty } from '../../../../app/shared/empty.util';
import { Angulartics2 } from 'angulartics2';
import { DSpaceObject } from '../../../../app/core/shared/dspace-object.model';
import { FollowLinkConfig } from '../../../../app/shared/utils/follow-link-config.model';
import { RemoteData } from '../../../../app/core/data/remote-data';
import { SearchObjects } from '../../../../app/shared/search/models/search-objects.model';
import { URLCombiner } from '../../../../app/core/url-combiner/url-combiner';
import { GenericConstructor } from '../../../../app/core/shared/generic-constructor';
import { ResponseParsingService } from '../../../../app/core/data/parsing.service';

@Injectable()
export class AtmireSearchService extends SearchService {
constructor(
protected routeService: RouteService,
protected requestService: RequestService,
protected rdb: RemoteDataBuildService,
protected linkService: LinkService,
protected halService: HALEndpointService,
protected communityService: CommunityDataService,
protected dspaceObjectService: DSpaceObjectDataService,
protected paginationService: PaginationService,
protected searchConfigurationService: SearchConfigurationService,
protected angulartics2: Angulartics2,
) {
super(routeService, requestService, rdb, halService, dspaceObjectService, paginationService, searchConfigurationService, angulartics2);
}


/**
* Overridden method to split up into searchByHref() to allow for searches with just a raw href
*/
search<T extends DSpaceObject>(searchOptions?: PaginatedSearchOptions, responseMsToLive?: number, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<T>[]): Observable<RemoteData<SearchObjects<T>>> {
return this.searchByHref(this.getEndpoint(searchOptions), responseMsToLive, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}

/**
* Search by href
*/
searchByHref<T extends DSpaceObject>(href$: Observable<string>, responseMsToLive?: number, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<T>[]): Observable<RemoteData<SearchObjects<T>>> {
href$.pipe(
take(1),
map((href: string) => {
const args = this.searchDataService.addEmbedParams(href, [], ...linksToFollow);
if (isNotEmpty(args)) {
return new URLCombiner(href, `?${args.join('&')}`).toString();
} else {
return href;
}
})
).subscribe((url: string) => {
const request = new this.request(this.requestService.generateRequestId(), url);

const getResponseParserFn: () => GenericConstructor<ResponseParsingService> = () => {
return this.parser;
};

Object.assign(request, {
responseMsToLive: hasValue(responseMsToLive) ? responseMsToLive : request.responseMsToLive,
getResponseParser: getResponseParserFn,
});

this.requestService.send(request, useCachedVersionIfAvailable);
});

const sqr$ = href$.pipe(
switchMap((href: string) => this.rdb.buildFromHref<SearchObjects<T>>(href))
);

return this.directlyAttachIndexableObjects(sqr$, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
}
4 changes: 2 additions & 2 deletions src/app/core/core.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ import { NonHierarchicalBrowseDefinition } from './shared/non-hierarchical-brows
import { BulkAccessConditionOptions } from './config/models/bulk-access-condition-options.model';
import { AtmireObjectUpdatesService } from '../../app-atmire/core/data/object-updates/atmire-object-updates.service';
import { AtmireMenuService } from '../../app-atmire/shared/menu/atmire-menu.service';
import { AtmireSearchService } from '../../app-atmire/core/shared/search/atmire-search.service';

/**
* When not in production, endpoint responses can be mocked for testing purposes
Expand Down Expand Up @@ -257,7 +258,7 @@ const PROVIDERS = [
ObjectUpdatesService,
{ provide: MenuService, useClass: AtmireMenuService },
{ provide: ObjectUpdatesService, useClass: AtmireObjectUpdatesService },
SearchService,
{ provide: SearchService, useClass: AtmireSearchService },
RelationshipDataService,
MyDSpaceGuard,
RoleService,
Expand All @@ -268,7 +269,6 @@ const PROVIDERS = [
EntityTypeDataService,
ContentSourceResponseParsingService,
ItemTemplateDataService,
SearchService,
SidebarService,
SearchFilterService,
SearchFilterService,
Expand Down
24 changes: 12 additions & 12 deletions src/app/core/shared/search/search.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,37 +65,37 @@ export class SearchService implements OnDestroy {
/**
* Endpoint link path for retrieving general search results
*/
private searchLinkPath = 'discover/search/objects';
protected searchLinkPath = 'discover/search/objects';

/**
* The ResponseParsingService constructor name
*/
private parser: GenericConstructor<ResponseParsingService> = SearchResponseParsingService;
protected parser: GenericConstructor<ResponseParsingService> = SearchResponseParsingService;

/**
* The RestRequest constructor name
*/
private request: GenericConstructor<RestRequest> = GetRequest;
protected request: GenericConstructor<RestRequest> = GetRequest;

/**
* Subscription to unsubscribe from
*/
private sub;
protected sub;

/**
* Instance of SearchDataService to forward data service methods to
*/
private searchDataService: SearchDataService;
protected searchDataService: SearchDataService;

constructor(
private routeService: RouteService,
protected routeService: RouteService,
protected requestService: RequestService,
private rdb: RemoteDataBuildService,
private halService: HALEndpointService,
private dspaceObjectService: DSpaceObjectDataService,
private paginationService: PaginationService,
private searchConfigurationService: SearchConfigurationService,
private angulartics2: Angulartics2,
protected rdb: RemoteDataBuildService,
protected halService: HALEndpointService,
protected dspaceObjectService: DSpaceObjectDataService,
protected paginationService: PaginationService,
protected searchConfigurationService: SearchConfigurationService,
protected angulartics2: Angulartics2,
) {
this.searchDataService = new SearchDataService();
}
Expand Down

0 comments on commit d9fd9fc

Please sign in to comment.