mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
[DURACOM-271] Fix suggestion list pagination and add support for multiple sources
This commit is contained in:
@@ -55,14 +55,14 @@ export class PublicationClaimComponent implements OnInit {
|
|||||||
/**
|
/**
|
||||||
* The source for which to list targets
|
* The source for which to list targets
|
||||||
*/
|
*/
|
||||||
@Input() source: string;
|
@Input() source = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The pagination system configuration for HTML listing.
|
* The pagination system configuration for HTML listing.
|
||||||
* @type {PaginationComponentOptions}
|
* @type {PaginationComponentOptions}
|
||||||
*/
|
*/
|
||||||
public paginationConfig: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
|
public paginationConfig: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
|
||||||
id: 'stp',
|
id: 'stp_' + this.source,
|
||||||
pageSizeOptions: [5, 10, 20, 40, 60],
|
pageSizeOptions: [5, 10, 20, 40, 60],
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -99,11 +99,16 @@ export class PublicationClaimComponent implements OnInit {
|
|||||||
* Component initialization.
|
* Component initialization.
|
||||||
*/
|
*/
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.targets$ = this.suggestionTargetsStateService.getSuggestionTargets();
|
this.targets$ = this.suggestionTargetsStateService.getSuggestionTargets(this.source);
|
||||||
this.totalElements$ = this.suggestionTargetsStateService.getSuggestionTargetsTotals();
|
this.totalElements$ = this.suggestionTargetsStateService.getSuggestionTargetsTotals(this.source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* First Suggestion Targets loading after view initialization.
|
||||||
|
*/
|
||||||
|
ngAfterViewInit(): void {
|
||||||
this.subs.push(
|
this.subs.push(
|
||||||
this.suggestionTargetsStateService.isSuggestionTargetsLoaded().pipe(
|
this.suggestionTargetsStateService.isSuggestionTargetsLoaded(this.source).pipe(
|
||||||
take(1),
|
take(1),
|
||||||
).subscribe(() => {
|
).subscribe(() => {
|
||||||
this.getSuggestionTargets();
|
this.getSuggestionTargets();
|
||||||
@@ -118,7 +123,7 @@ export class PublicationClaimComponent implements OnInit {
|
|||||||
* 'true' if the targets are loading, 'false' otherwise.
|
* 'true' if the targets are loading, 'false' otherwise.
|
||||||
*/
|
*/
|
||||||
public isTargetsLoading(): Observable<boolean> {
|
public isTargetsLoading(): Observable<boolean> {
|
||||||
return this.suggestionTargetsStateService.isSuggestionTargetsLoading();
|
return this.suggestionTargetsStateService.isSuggestionTargetsLoading(this.source);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -128,7 +133,7 @@ export class PublicationClaimComponent implements OnInit {
|
|||||||
* 'true' if there are operations running on the targets (ex.: a REST call), 'false' otherwise.
|
* 'true' if there are operations running on the targets (ex.: a REST call), 'false' otherwise.
|
||||||
*/
|
*/
|
||||||
public isTargetsProcessing(): Observable<boolean> {
|
public isTargetsProcessing(): Observable<boolean> {
|
||||||
return this.suggestionTargetsStateService.isSuggestionTargetsProcessing();
|
return this.suggestionTargetsStateService.isSuggestionTargetsProcessing(this.source);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -145,7 +150,7 @@ export class PublicationClaimComponent implements OnInit {
|
|||||||
* Unsubscribe from all subscriptions.
|
* Unsubscribe from all subscriptions.
|
||||||
*/
|
*/
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.suggestionTargetsStateService.dispatchClearSuggestionTargetsAction();
|
this.suggestionTargetsStateService.dispatchClearSuggestionTargetsAction(this.source);
|
||||||
this.subs
|
this.subs
|
||||||
.filter((sub) => hasValue(sub))
|
.filter((sub) => hasValue(sub))
|
||||||
.forEach((sub) => sub.unsubscribe());
|
.forEach((sub) => sub.unsubscribe());
|
||||||
|
116
src/app/notifications/suggestion-targets/selectors.ts
Normal file
116
src/app/notifications/suggestion-targets/selectors.ts
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
import {
|
||||||
|
createFeatureSelector,
|
||||||
|
createSelector,
|
||||||
|
MemoizedSelector,
|
||||||
|
} from '@ngrx/store';
|
||||||
|
|
||||||
|
import { SuggestionTarget } from '../../core/notifications/suggestions/models/suggestion-target.model';
|
||||||
|
import { subStateSelector } from '../../submission/selectors';
|
||||||
|
import {
|
||||||
|
suggestionNotificationsSelector,
|
||||||
|
SuggestionNotificationsState,
|
||||||
|
} from '../notifications.reducer';
|
||||||
|
import {
|
||||||
|
SuggestionTargetEntry,
|
||||||
|
SuggestionTargetState,
|
||||||
|
} from './suggestion-targets.reducer';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Reciter Suggestion Target state.
|
||||||
|
* @function _getSuggestionTargetState
|
||||||
|
* @param {AppState} state Top level state.
|
||||||
|
* @return {SuggestionNotificationsState}
|
||||||
|
*/
|
||||||
|
const _getSuggestionTargetState = createFeatureSelector<SuggestionNotificationsState>('suggestionNotifications');
|
||||||
|
|
||||||
|
// Suggestion Targets selectors
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Suggestion Targets State.
|
||||||
|
* @function suggestionTargetStateSelector
|
||||||
|
* @return {SuggestionNotificationsState}
|
||||||
|
*/
|
||||||
|
export function suggestionTargetStateSelector(): MemoizedSelector<SuggestionNotificationsState, SuggestionTargetState> {
|
||||||
|
return subStateSelector<SuggestionNotificationsState, SuggestionTargetState>(suggestionNotificationsSelector, 'suggestionTarget');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Reciter Suggestion source state
|
||||||
|
* @function suggestionSourceSelector
|
||||||
|
* @return {SuggestionTargetEntry}
|
||||||
|
*/
|
||||||
|
export function suggestionSourceSelector(source: string): MemoizedSelector<SuggestionNotificationsState, SuggestionTargetEntry> {
|
||||||
|
return createSelector(suggestionTargetStateSelector(),(state: SuggestionTargetState) => state.sources[source]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Suggestion Targets list by source.
|
||||||
|
* @function suggestionTargetObjectSelector
|
||||||
|
* @return {SuggestionTarget[]}
|
||||||
|
*/
|
||||||
|
export function suggestionTargetObjectSelector(source: string): MemoizedSelector<SuggestionNotificationsState, SuggestionTarget[]> {
|
||||||
|
return createSelector(suggestionSourceSelector(source), (state: SuggestionTargetEntry) => state.targets);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the Suggestion Targets are loaded.
|
||||||
|
* @function isSuggestionTargetLoadedSelector
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
export const isSuggestionTargetLoadedSelector = (source: string) => {
|
||||||
|
return createSelector(suggestionSourceSelector(source), (state: SuggestionTargetEntry) => state?.loaded || false);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the deduplication sets are processing.
|
||||||
|
* @function isSuggestionTargetProcessingSelector
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
export const isSuggestionTargetProcessingSelector = (source: string) => {
|
||||||
|
return createSelector(suggestionSourceSelector(source), (state: SuggestionTargetEntry) => state?.processing || false);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the total available pages of Reciter Suggestion Targets.
|
||||||
|
* @function getSuggestionTargetTotalPagesSelector
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
export const getSuggestionTargetTotalPagesSelector = (source: string) => {
|
||||||
|
return createSelector(suggestionSourceSelector(source), (state: SuggestionTargetEntry) => state?.totalPages || 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current page of Suggestion Targets.
|
||||||
|
* @function getSuggestionTargetCurrentPageSelector
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
export const getSuggestionTargetCurrentPageSelector = (source: string) => {
|
||||||
|
return createSelector(suggestionSourceSelector(source), (state: SuggestionTargetEntry) => state?.currentPage || 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the total number of Suggestion Targets.
|
||||||
|
* @function getSuggestionTargetTotalsSelector
|
||||||
|
* @return {number}
|
||||||
|
*/
|
||||||
|
export const getSuggestionTargetTotalsSelector = (source: string) => {
|
||||||
|
return createSelector(suggestionSourceSelector(source), (state: SuggestionTargetEntry) => state?.totalElements || 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns Suggestion Targets for the current user.
|
||||||
|
* @function getCurrentUserSuggestionTargetsSelector
|
||||||
|
* @return {SuggestionTarget[]}
|
||||||
|
*/
|
||||||
|
export const getCurrentUserSuggestionTargetsSelector = () => {
|
||||||
|
return createSelector(suggestionTargetStateSelector(), (state: SuggestionTargetState) => state?.currentUserTargets || []);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not the user has consulted their suggestions
|
||||||
|
* @function getCurrentUserSuggestionTargetsVisitedSelector
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
export const getCurrentUserSuggestionTargetsVisitedSelector = () => {
|
||||||
|
return createSelector(suggestionTargetStateSelector(), (state: SuggestionTargetState) => state?.currentUserTargetsVisited || false);
|
||||||
|
};
|
@@ -23,10 +23,8 @@ export const SuggestionTargetActionTypes = {
|
|||||||
MARK_USER_SUGGESTIONS_AS_VISITED: type('dspace/integration/openaire/suggestions/target/MARK_USER_SUGGESTIONS_AS_VISITED'),
|
MARK_USER_SUGGESTIONS_AS_VISITED: type('dspace/integration/openaire/suggestions/target/MARK_USER_SUGGESTIONS_AS_VISITED'),
|
||||||
};
|
};
|
||||||
|
|
||||||
/* tslint:disable:max-classes-per-file */
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An ngrx action to retrieve all the Suggestion Targets.
|
* A ngrx action to retrieve all the Suggestion Targets.
|
||||||
*/
|
*/
|
||||||
export class RetrieveTargetsBySourceAction implements Action {
|
export class RetrieveTargetsBySourceAction implements Action {
|
||||||
type = SuggestionTargetActionTypes.RETRIEVE_TARGETS_BY_SOURCE;
|
type = SuggestionTargetActionTypes.RETRIEVE_TARGETS_BY_SOURCE;
|
||||||
@@ -56,18 +54,34 @@ export class RetrieveTargetsBySourceAction implements Action {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An ngrx action for retrieving 'all Suggestion Targets' error.
|
* A ngrx action for notifying error.
|
||||||
*/
|
*/
|
||||||
export class RetrieveAllTargetsErrorAction implements Action {
|
export class RetrieveTargetsBySourceErrorAction implements Action {
|
||||||
type = SuggestionTargetActionTypes.RETRIEVE_TARGETS_BY_SOURCE_ERROR;
|
type = SuggestionTargetActionTypes.RETRIEVE_TARGETS_BY_SOURCE_ERROR;
|
||||||
|
payload: {
|
||||||
|
source: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new RetrieveTargetsBySourceAction.
|
||||||
|
*
|
||||||
|
* @param source
|
||||||
|
* the source for which to retrieve suggestion targets
|
||||||
|
*/
|
||||||
|
constructor(source: string) {
|
||||||
|
this.payload = {
|
||||||
|
source,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An ngrx action to load the Suggestion Target objects.
|
* A ngrx action to load the Suggestion Target objects.
|
||||||
*/
|
*/
|
||||||
export class AddTargetAction implements Action {
|
export class AddTargetAction implements Action {
|
||||||
type = SuggestionTargetActionTypes.ADD_TARGETS;
|
type = SuggestionTargetActionTypes.ADD_TARGETS;
|
||||||
payload: {
|
payload: {
|
||||||
|
source: string;
|
||||||
targets: SuggestionTarget[];
|
targets: SuggestionTarget[];
|
||||||
totalPages: number;
|
totalPages: number;
|
||||||
currentPage: number;
|
currentPage: number;
|
||||||
@@ -77,6 +91,8 @@ export class AddTargetAction implements Action {
|
|||||||
/**
|
/**
|
||||||
* Create a new AddTargetAction.
|
* Create a new AddTargetAction.
|
||||||
*
|
*
|
||||||
|
* @param source
|
||||||
|
* the source of suggestion targets
|
||||||
* @param targets
|
* @param targets
|
||||||
* the list of targets
|
* the list of targets
|
||||||
* @param totalPages
|
* @param totalPages
|
||||||
@@ -86,8 +102,9 @@ export class AddTargetAction implements Action {
|
|||||||
* @param totalElements
|
* @param totalElements
|
||||||
* the total available Suggestion Targets
|
* the total available Suggestion Targets
|
||||||
*/
|
*/
|
||||||
constructor(targets: SuggestionTarget[], totalPages: number, currentPage: number, totalElements: number) {
|
constructor(source: string, targets: SuggestionTarget[], totalPages: number, currentPage: number, totalElements: number) {
|
||||||
this.payload = {
|
this.payload = {
|
||||||
|
source,
|
||||||
targets,
|
targets,
|
||||||
totalPages,
|
totalPages,
|
||||||
currentPage,
|
currentPage,
|
||||||
@@ -98,7 +115,7 @@ export class AddTargetAction implements Action {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An ngrx action to load the user Suggestion Target object.
|
* A ngrx action to load the user Suggestion Target object.
|
||||||
* Called by the ??? effect.
|
* Called by the ??? effect.
|
||||||
*/
|
*/
|
||||||
export class AddUserSuggestionsAction implements Action {
|
export class AddUserSuggestionsAction implements Action {
|
||||||
@@ -120,7 +137,7 @@ export class AddUserSuggestionsAction implements Action {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An ngrx action to reload the user Suggestion Target object.
|
* A ngrx action to reload the user Suggestion Target object.
|
||||||
* Called by the ??? effect.
|
* Called by the ??? effect.
|
||||||
*/
|
*/
|
||||||
export class RefreshUserSuggestionsAction implements Action {
|
export class RefreshUserSuggestionsAction implements Action {
|
||||||
@@ -135,7 +152,7 @@ export class RefreshUserSuggestionsErrorAction implements Action {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An ngrx action to Mark User Suggestions As Visited.
|
* A ngrx action to Mark User Suggestions As Visited.
|
||||||
* Called by the ??? effect.
|
* Called by the ??? effect.
|
||||||
*/
|
*/
|
||||||
export class MarkUserSuggestionsAsVisitedAction implements Action {
|
export class MarkUserSuggestionsAsVisitedAction implements Action {
|
||||||
@@ -143,13 +160,26 @@ export class MarkUserSuggestionsAsVisitedAction implements Action {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An ngrx action to clear targets state.
|
* A ngrx action to clear targets state.
|
||||||
*/
|
*/
|
||||||
export class ClearSuggestionTargetsAction implements Action {
|
export class ClearSuggestionTargetsAction implements Action {
|
||||||
type = SuggestionTargetActionTypes.CLEAR_TARGETS;
|
type = SuggestionTargetActionTypes.CLEAR_TARGETS;
|
||||||
}
|
payload: {
|
||||||
|
source: string;
|
||||||
|
};
|
||||||
|
|
||||||
/* tslint:enable:max-classes-per-file */
|
/**
|
||||||
|
* Create a new ClearSuggestionTargetsAction.
|
||||||
|
*
|
||||||
|
* @param source
|
||||||
|
* the source of suggestion targets
|
||||||
|
*/
|
||||||
|
constructor(source: string) {
|
||||||
|
this.payload = {
|
||||||
|
source,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export a type alias of all actions in this action group
|
* Export a type alias of all actions in this action group
|
||||||
@@ -161,5 +191,5 @@ export type SuggestionTargetsActions
|
|||||||
| ClearSuggestionTargetsAction
|
| ClearSuggestionTargetsAction
|
||||||
| MarkUserSuggestionsAsVisitedAction
|
| MarkUserSuggestionsAsVisitedAction
|
||||||
| RetrieveTargetsBySourceAction
|
| RetrieveTargetsBySourceAction
|
||||||
| RetrieveAllTargetsErrorAction
|
| RetrieveTargetsBySourceErrorAction
|
||||||
| RefreshUserSuggestionsAction;
|
| RefreshUserSuggestionsAction;
|
||||||
|
@@ -14,7 +14,12 @@ import {
|
|||||||
tap,
|
tap,
|
||||||
} from 'rxjs/operators';
|
} from 'rxjs/operators';
|
||||||
|
|
||||||
|
import {
|
||||||
|
AuthActionTypes,
|
||||||
|
RetrieveAuthenticatedEpersonSuccessAction,
|
||||||
|
} from '../../core/auth/auth.actions';
|
||||||
import { PaginatedList } from '../../core/data/paginated-list.model';
|
import { PaginatedList } from '../../core/data/paginated-list.model';
|
||||||
|
import { EPerson } from '../../core/eperson/models/eperson.model';
|
||||||
import { SuggestionTarget } from '../../core/notifications/suggestions/models/suggestion-target.model';
|
import { SuggestionTarget } from '../../core/notifications/suggestions/models/suggestion-target.model';
|
||||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||||
import { SuggestionsService } from '../suggestions.service';
|
import { SuggestionsService } from '../suggestions.service';
|
||||||
@@ -22,8 +27,8 @@ import {
|
|||||||
AddTargetAction,
|
AddTargetAction,
|
||||||
AddUserSuggestionsAction,
|
AddUserSuggestionsAction,
|
||||||
RefreshUserSuggestionsErrorAction,
|
RefreshUserSuggestionsErrorAction,
|
||||||
RetrieveAllTargetsErrorAction,
|
|
||||||
RetrieveTargetsBySourceAction,
|
RetrieveTargetsBySourceAction,
|
||||||
|
RetrieveTargetsBySourceErrorAction,
|
||||||
SuggestionTargetActionTypes,
|
SuggestionTargetActionTypes,
|
||||||
} from './suggestion-targets.actions';
|
} from './suggestion-targets.actions';
|
||||||
|
|
||||||
@@ -45,13 +50,13 @@ export class SuggestionTargetsEffects {
|
|||||||
action.payload.currentPage,
|
action.payload.currentPage,
|
||||||
).pipe(
|
).pipe(
|
||||||
map((targets: PaginatedList<SuggestionTarget>) =>
|
map((targets: PaginatedList<SuggestionTarget>) =>
|
||||||
new AddTargetAction(targets.page, targets.totalPages, targets.currentPage, targets.totalElements),
|
new AddTargetAction(action.payload.source, targets.page, targets.totalPages, targets.currentPage, targets.totalElements),
|
||||||
),
|
),
|
||||||
catchError((error: unknown) => {
|
catchError((error: unknown) => {
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
console.error(error.message);
|
console.error(error.message);
|
||||||
}
|
}
|
||||||
return of(new RetrieveAllTargetsErrorAction());
|
return of(new RetrieveTargetsBySourceErrorAction(action.payload.source));
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
@@ -67,16 +72,27 @@ export class SuggestionTargetsEffects {
|
|||||||
}),
|
}),
|
||||||
), { dispatch: false });
|
), { dispatch: false });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a notification on error.
|
||||||
|
*/
|
||||||
|
retrieveUserTargets$ = createEffect(() => this.actions$.pipe(
|
||||||
|
ofType(AuthActionTypes.RETRIEVE_AUTHENTICATED_EPERSON_SUCCESS),
|
||||||
|
switchMap((action: RetrieveAuthenticatedEpersonSuccessAction) => {
|
||||||
|
return this.suggestionsService.retrieveCurrentUserSuggestions(action.payload).pipe(
|
||||||
|
map((suggestionTargets: SuggestionTarget[]) => new AddUserSuggestionsAction(suggestionTargets)),
|
||||||
|
);
|
||||||
|
})));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch the current user suggestion
|
* Fetch the current user suggestion
|
||||||
*/
|
*/
|
||||||
refreshUserSuggestionsAction$ = createEffect(() => this.actions$.pipe(
|
refreshUserSuggestionsAction$ = createEffect(() => this.actions$.pipe(
|
||||||
ofType(SuggestionTargetActionTypes.REFRESH_USER_SUGGESTIONS),
|
ofType(SuggestionTargetActionTypes.REFRESH_USER_SUGGESTIONS),
|
||||||
switchMap(() => {
|
switchMap(() => {
|
||||||
return this.store$.select((state: any) => state.core.auth.userId)
|
return this.store$.select((state: any) => state.core.auth.user)
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap((userId: string) => {
|
switchMap((user: EPerson) => {
|
||||||
return this.suggestionsService.retrieveCurrentUserSuggestions(userId)
|
return this.suggestionsService.retrieveCurrentUserSuggestions(user.uuid)
|
||||||
.pipe(
|
.pipe(
|
||||||
map((suggestionTargets: SuggestionTarget[]) => new AddUserSuggestionsAction(suggestionTargets)),
|
map((suggestionTargets: SuggestionTarget[]) => new AddUserSuggestionsAction(suggestionTargets)),
|
||||||
catchError((error: unknown) => {
|
catchError((error: unknown) => {
|
||||||
|
@@ -7,13 +7,21 @@ import {
|
|||||||
/**
|
/**
|
||||||
* The interface representing the OpenAIRE suggestion targets state.
|
* The interface representing the OpenAIRE suggestion targets state.
|
||||||
*/
|
*/
|
||||||
export interface SuggestionTargetState {
|
export interface SuggestionTargetEntry {
|
||||||
targets: SuggestionTarget[];
|
targets: SuggestionTarget[];
|
||||||
processing: boolean;
|
processing: boolean;
|
||||||
loaded: boolean;
|
loaded: boolean;
|
||||||
totalPages: number;
|
totalPages: number;
|
||||||
currentPage: number;
|
currentPage: number;
|
||||||
totalElements: number;
|
totalElements: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SuggestionSourcesState {
|
||||||
|
[source: string]: SuggestionTargetEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SuggestionTargetState {
|
||||||
|
sources: SuggestionSourcesState;
|
||||||
currentUserTargets: SuggestionTarget[];
|
currentUserTargets: SuggestionTarget[];
|
||||||
currentUserTargetsVisited: boolean;
|
currentUserTargetsVisited: boolean;
|
||||||
}
|
}
|
||||||
@@ -21,13 +29,17 @@ export interface SuggestionTargetState {
|
|||||||
/**
|
/**
|
||||||
* Used for the OpenAIRE Suggestion Target state initialization.
|
* Used for the OpenAIRE Suggestion Target state initialization.
|
||||||
*/
|
*/
|
||||||
const SuggestionTargetInitialState: SuggestionTargetState = {
|
const suggestionSourceTargetsInitialState: SuggestionTargetEntry = {
|
||||||
targets: [],
|
targets: [],
|
||||||
processing: false,
|
processing: false,
|
||||||
loaded: false,
|
loaded: false,
|
||||||
totalPages: 0,
|
totalPages: 0,
|
||||||
currentPage: 0,
|
currentPage: 0,
|
||||||
totalElements: 0,
|
totalElements: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SuggestionTargetInitialState: SuggestionTargetState = {
|
||||||
|
sources: {},
|
||||||
currentUserTargets: null,
|
currentUserTargets: null,
|
||||||
currentUserTargetsVisited: false,
|
currentUserTargetsVisited: false,
|
||||||
};
|
};
|
||||||
@@ -45,25 +57,42 @@ const SuggestionTargetInitialState: SuggestionTargetState = {
|
|||||||
export function SuggestionTargetsReducer(state = SuggestionTargetInitialState, action: SuggestionTargetsActions): SuggestionTargetState {
|
export function SuggestionTargetsReducer(state = SuggestionTargetInitialState, action: SuggestionTargetsActions): SuggestionTargetState {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case SuggestionTargetActionTypes.RETRIEVE_TARGETS_BY_SOURCE: {
|
case SuggestionTargetActionTypes.RETRIEVE_TARGETS_BY_SOURCE: {
|
||||||
return Object.assign({}, state, {
|
const sourceState = state.sources[action.payload.source] || Object.assign({}, suggestionSourceTargetsInitialState);
|
||||||
|
const newSourceState = Object.assign({}, sourceState, {
|
||||||
targets: [],
|
targets: [],
|
||||||
processing: true,
|
processing: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
sources:
|
||||||
|
Object.assign({}, state.sources, {
|
||||||
|
[action.payload.source]: newSourceState,
|
||||||
|
}),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
case SuggestionTargetActionTypes.ADD_TARGETS: {
|
case SuggestionTargetActionTypes.ADD_TARGETS: {
|
||||||
return Object.assign({}, state, {
|
const sourceState = state.sources[action.payload.source] || Object.assign({}, suggestionSourceTargetsInitialState);
|
||||||
targets: state.targets.concat(action.payload.targets),
|
const newSourceState = Object.assign({}, sourceState, {
|
||||||
|
targets: sourceState.targets.concat(action.payload.targets),
|
||||||
processing: false,
|
processing: false,
|
||||||
loaded: true,
|
loaded: true,
|
||||||
totalPages: action.payload.totalPages,
|
totalPages: action.payload.totalPages,
|
||||||
currentPage: state.currentPage,
|
currentPage: action.payload.currentPage,
|
||||||
totalElements: action.payload.totalElements,
|
totalElements: action.payload.totalElements,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
sources:
|
||||||
|
Object.assign({}, state.sources, {
|
||||||
|
[action.payload.source]: newSourceState,
|
||||||
|
}),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
case SuggestionTargetActionTypes.RETRIEVE_TARGETS_BY_SOURCE_ERROR: {
|
case SuggestionTargetActionTypes.RETRIEVE_TARGETS_BY_SOURCE_ERROR: {
|
||||||
return Object.assign({}, state, {
|
const sourceState = state.sources[action.payload.source] || Object.assign({}, suggestionSourceTargetsInitialState);
|
||||||
|
const newSourceState = Object.assign({}, sourceState, {
|
||||||
targets: [],
|
targets: [],
|
||||||
processing: false,
|
processing: false,
|
||||||
loaded: true,
|
loaded: true,
|
||||||
@@ -71,6 +100,13 @@ export function SuggestionTargetsReducer(state = SuggestionTargetInitialState, a
|
|||||||
currentPage: 0,
|
currentPage: 0,
|
||||||
totalElements: 0,
|
totalElements: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
sources:
|
||||||
|
Object.assign({}, state.sources, {
|
||||||
|
[action.payload.source]: newSourceState,
|
||||||
|
}),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
case SuggestionTargetActionTypes.ADD_USER_SUGGESTIONS: {
|
case SuggestionTargetActionTypes.ADD_USER_SUGGESTIONS: {
|
||||||
@@ -86,7 +122,8 @@ export function SuggestionTargetsReducer(state = SuggestionTargetInitialState, a
|
|||||||
}
|
}
|
||||||
|
|
||||||
case SuggestionTargetActionTypes.CLEAR_TARGETS: {
|
case SuggestionTargetActionTypes.CLEAR_TARGETS: {
|
||||||
return Object.assign({}, state, {
|
const sourceState = state.sources[action.payload.source] || Object.assign({}, suggestionSourceTargetsInitialState);
|
||||||
|
const newSourceState = Object.assign({}, sourceState, {
|
||||||
targets: [],
|
targets: [],
|
||||||
processing: false,
|
processing: false,
|
||||||
loaded: false,
|
loaded: false,
|
||||||
@@ -94,6 +131,13 @@ export function SuggestionTargetsReducer(state = SuggestionTargetInitialState, a
|
|||||||
currentPage: 0,
|
currentPage: 0,
|
||||||
totalElements: 0,
|
totalElements: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
sources:
|
||||||
|
Object.assign({}, state.sources, {
|
||||||
|
[action.payload.source]: newSourceState,
|
||||||
|
}),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
|
@@ -7,16 +7,16 @@ import { Observable } from 'rxjs';
|
|||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
|
|
||||||
import { SuggestionTarget } from '../../core/notifications/suggestions/models/suggestion-target.model';
|
import { SuggestionTarget } from '../../core/notifications/suggestions/models/suggestion-target.model';
|
||||||
|
import { SuggestionNotificationsState } from '../notifications.reducer';
|
||||||
import {
|
import {
|
||||||
getCurrentUserSuggestionTargetsSelector,
|
getCurrentUserSuggestionTargetsSelector,
|
||||||
getCurrentUserSuggestionTargetsVisitedSelector,
|
getCurrentUserSuggestionTargetsVisitedSelector,
|
||||||
getSuggestionTargetCurrentPageSelector,
|
getSuggestionTargetCurrentPageSelector,
|
||||||
getSuggestionTargetTotalsSelector,
|
getSuggestionTargetTotalsSelector,
|
||||||
isReciterSuggestionTargetProcessingSelector,
|
|
||||||
isSuggestionTargetLoadedSelector,
|
isSuggestionTargetLoadedSelector,
|
||||||
|
isSuggestionTargetProcessingSelector,
|
||||||
suggestionTargetObjectSelector,
|
suggestionTargetObjectSelector,
|
||||||
} from '../../suggestion-notifications/selectors';
|
} from './selectors';
|
||||||
import { SuggestionNotificationsState } from '../notifications.reducer';
|
|
||||||
import {
|
import {
|
||||||
ClearSuggestionTargetsAction,
|
ClearSuggestionTargetsAction,
|
||||||
MarkUserSuggestionsAsVisitedAction,
|
MarkUserSuggestionsAsVisitedAction,
|
||||||
@@ -42,8 +42,8 @@ export class SuggestionTargetsStateService {
|
|||||||
* @return Observable<SuggestionTarget>
|
* @return Observable<SuggestionTarget>
|
||||||
* The list of Suggestion Targets.
|
* The list of Suggestion Targets.
|
||||||
*/
|
*/
|
||||||
public getSuggestionTargets(): Observable<SuggestionTarget[]> {
|
public getSuggestionTargets(source: string): Observable<SuggestionTarget[]> {
|
||||||
return this.store.pipe(select(suggestionTargetObjectSelector()));
|
return this.store.pipe(select(suggestionTargetObjectSelector(source)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -52,9 +52,9 @@ export class SuggestionTargetsStateService {
|
|||||||
* @return Observable<boolean>
|
* @return Observable<boolean>
|
||||||
* 'true' if the targets are loading, 'false' otherwise.
|
* 'true' if the targets are loading, 'false' otherwise.
|
||||||
*/
|
*/
|
||||||
public isSuggestionTargetsLoading(): Observable<boolean> {
|
public isSuggestionTargetsLoading(source: string): Observable<boolean> {
|
||||||
return this.store.pipe(
|
return this.store.pipe(
|
||||||
select(isSuggestionTargetLoadedSelector),
|
select(isSuggestionTargetLoadedSelector(source)),
|
||||||
map((loaded: boolean) => !loaded),
|
map((loaded: boolean) => !loaded),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -65,8 +65,8 @@ export class SuggestionTargetsStateService {
|
|||||||
* @return Observable<boolean>
|
* @return Observable<boolean>
|
||||||
* 'true' if the targets are loaded, 'false' otherwise.
|
* 'true' if the targets are loaded, 'false' otherwise.
|
||||||
*/
|
*/
|
||||||
public isSuggestionTargetsLoaded(): Observable<boolean> {
|
public isSuggestionTargetsLoaded(source: string): Observable<boolean> {
|
||||||
return this.store.pipe(select(isSuggestionTargetLoadedSelector));
|
return this.store.pipe(select(isSuggestionTargetLoadedSelector(source)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -75,8 +75,8 @@ export class SuggestionTargetsStateService {
|
|||||||
* @return Observable<boolean>
|
* @return Observable<boolean>
|
||||||
* 'true' if there are operations running on the targets (ex.: a REST call), 'false' otherwise.
|
* 'true' if there are operations running on the targets (ex.: a REST call), 'false' otherwise.
|
||||||
*/
|
*/
|
||||||
public isSuggestionTargetsProcessing(): Observable<boolean> {
|
public isSuggestionTargetsProcessing(source: string): Observable<boolean> {
|
||||||
return this.store.pipe(select(isReciterSuggestionTargetProcessingSelector));
|
return this.store.pipe(select(isSuggestionTargetProcessingSelector(source)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -85,8 +85,8 @@ export class SuggestionTargetsStateService {
|
|||||||
* @return Observable<number>
|
* @return Observable<number>
|
||||||
* The number of the Suggestion Targets pages.
|
* The number of the Suggestion Targets pages.
|
||||||
*/
|
*/
|
||||||
public getSuggestionTargetsTotalPages(): Observable<number> {
|
public getSuggestionTargetsTotalPages(source: string): Observable<number> {
|
||||||
return this.store.pipe(select(getSuggestionTargetTotalsSelector));
|
return this.store.pipe(select(getSuggestionTargetTotalsSelector(source)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -95,8 +95,8 @@ export class SuggestionTargetsStateService {
|
|||||||
* @return Observable<number>
|
* @return Observable<number>
|
||||||
* The number of the current Suggestion Targets page.
|
* The number of the current Suggestion Targets page.
|
||||||
*/
|
*/
|
||||||
public getSuggestionTargetsCurrentPage(): Observable<number> {
|
public getSuggestionTargetsCurrentPage(source: string): Observable<number> {
|
||||||
return this.store.pipe(select(getSuggestionTargetCurrentPageSelector));
|
return this.store.pipe(select(getSuggestionTargetCurrentPageSelector(source)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -105,8 +105,8 @@ export class SuggestionTargetsStateService {
|
|||||||
* @return Observable<number>
|
* @return Observable<number>
|
||||||
* The number of the Suggestion Targets.
|
* The number of the Suggestion Targets.
|
||||||
*/
|
*/
|
||||||
public getSuggestionTargetsTotals(): Observable<number> {
|
public getSuggestionTargetsTotals(source: string): Observable<number> {
|
||||||
return this.store.pipe(select(getSuggestionTargetTotalsSelector));
|
return this.store.pipe(select(getSuggestionTargetTotalsSelector(source)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -130,7 +130,7 @@ export class SuggestionTargetsStateService {
|
|||||||
* The Suggestion Targets object.
|
* The Suggestion Targets object.
|
||||||
*/
|
*/
|
||||||
public getCurrentUserSuggestionTargets(): Observable<SuggestionTarget[]> {
|
public getCurrentUserSuggestionTargets(): Observable<SuggestionTarget[]> {
|
||||||
return this.store.pipe(select(getCurrentUserSuggestionTargetsSelector));
|
return this.store.pipe(select(getCurrentUserSuggestionTargetsSelector()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -140,7 +140,7 @@ export class SuggestionTargetsStateService {
|
|||||||
* True if user already visited, false otherwise.
|
* True if user already visited, false otherwise.
|
||||||
*/
|
*/
|
||||||
public hasUserVisitedSuggestions(): Observable<boolean> {
|
public hasUserVisitedSuggestions(): Observable<boolean> {
|
||||||
return this.store.pipe(select(getCurrentUserSuggestionTargetsVisitedSelector));
|
return this.store.pipe(select(getCurrentUserSuggestionTargetsVisitedSelector()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -152,9 +152,12 @@ export class SuggestionTargetsStateService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispatch an action to clear the Reciter Suggestion Targets state.
|
* Dispatch an action to clear the Reciter Suggestion Targets state.
|
||||||
|
*
|
||||||
|
* @param source
|
||||||
|
* the source of suggestion targets
|
||||||
*/
|
*/
|
||||||
public dispatchClearSuggestionTargetsAction(): void {
|
public dispatchClearSuggestionTargetsAction(source: string): void {
|
||||||
this.store.dispatch(new ClearSuggestionTargetsAction());
|
this.store.dispatch(new ClearSuggestionTargetsAction(source));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -44,8 +44,8 @@ export class SuggestionsNotificationComponent implements OnInit {
|
|||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction();
|
|
||||||
this.suggestionsRD$ = this.suggestionTargetsStateService.getCurrentUserSuggestionTargets();
|
this.suggestionsRD$ = this.suggestionTargetsStateService.getCurrentUserSuggestionTargets();
|
||||||
|
this.suggestionTargetsStateService.dispatchMarkUserSuggestionsAsVisitedAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -16,6 +16,7 @@ import {
|
|||||||
Observable,
|
Observable,
|
||||||
of,
|
of,
|
||||||
Subject,
|
Subject,
|
||||||
|
Subscription,
|
||||||
} from 'rxjs';
|
} from 'rxjs';
|
||||||
import {
|
import {
|
||||||
take,
|
take,
|
||||||
@@ -53,7 +54,7 @@ export class SuggestionsPopupComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
labelPrefix = 'notification.';
|
labelPrefix = 'notification.';
|
||||||
|
|
||||||
subscription;
|
subscription: Subscription;
|
||||||
|
|
||||||
suggestionsRD$: Observable<SuggestionTarget[]>;
|
suggestionsRD$: Observable<SuggestionTarget[]>;
|
||||||
|
|
||||||
|
@@ -16,6 +16,7 @@ import { ResourceType } from '../core/shared/resource-type';
|
|||||||
import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service';
|
import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service';
|
||||||
import { mockSuggestionPublicationOne } from '../shared/mocks/publication-claim.mock';
|
import { mockSuggestionPublicationOne } from '../shared/mocks/publication-claim.mock';
|
||||||
import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils';
|
import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils';
|
||||||
|
import { followLink } from '../shared/utils/follow-link-config.model';
|
||||||
import { SuggestionsService } from './suggestions.service';
|
import { SuggestionsService } from './suggestions.service';
|
||||||
|
|
||||||
describe('SuggestionsService test', () => {
|
describe('SuggestionsService test', () => {
|
||||||
@@ -115,7 +116,7 @@ describe('SuggestionsService test', () => {
|
|||||||
|
|
||||||
it('should retrieve current user suggestions', () => {
|
it('should retrieve current user suggestions', () => {
|
||||||
service.retrieveCurrentUserSuggestions('1234');
|
service.retrieveCurrentUserSuggestions('1234');
|
||||||
expect(researcherProfileService.findById).toHaveBeenCalledWith('1234', true);
|
expect(researcherProfileService.findById).toHaveBeenCalledWith('1234', true, true, followLink('item'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should approve and import suggestion', () => {
|
it('should approve and import suggestion', () => {
|
@@ -29,7 +29,6 @@ import { ResearcherProfile } from '../core/profile/model/researcher-profile.mode
|
|||||||
import { ResearcherProfileDataService } from '../core/profile/researcher-profile-data.service';
|
import { ResearcherProfileDataService } from '../core/profile/researcher-profile-data.service';
|
||||||
import { NoContent } from '../core/shared/NoContent.model';
|
import { NoContent } from '../core/shared/NoContent.model';
|
||||||
import {
|
import {
|
||||||
getAllSucceededRemoteDataPayload,
|
|
||||||
getFinishedRemoteData,
|
getFinishedRemoteData,
|
||||||
getFirstCompletedRemoteData,
|
getFirstCompletedRemoteData,
|
||||||
getFirstSucceededRemoteDataPayload,
|
getFirstSucceededRemoteDataPayload,
|
||||||
@@ -42,6 +41,7 @@ import {
|
|||||||
hasValue,
|
hasValue,
|
||||||
isNotEmpty,
|
isNotEmpty,
|
||||||
} from '../shared/empty.util';
|
} from '../shared/empty.util';
|
||||||
|
import { followLink } from '../shared/utils/follow-link-config.model';
|
||||||
import { getSuggestionPageRoute } from '../suggestions-page/suggestions-page-routing-paths';
|
import { getSuggestionPageRoute } from '../suggestions-page/suggestions-page-routing-paths';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -121,7 +121,7 @@ export class SuggestionsService {
|
|||||||
* @return Observable<RemoteData<PaginatedList<Suggestion>>>
|
* @return Observable<RemoteData<PaginatedList<Suggestion>>>
|
||||||
* The list of Suggestion.
|
* The list of Suggestion.
|
||||||
*/
|
*/
|
||||||
public getSuggestions(targetId: string, elementsPerPage, currentPage, sortOptions: SortOptions): Observable<PaginatedList<Suggestion>> {
|
public getSuggestions(targetId: string, elementsPerPage, currentPage, sortOptions: SortOptions): Observable<RemoteData<PaginatedList<Suggestion>>> {
|
||||||
const [source, target] = targetId.split(':');
|
const [source, target] = targetId.split(':');
|
||||||
|
|
||||||
const findListOptions: FindListOptions = {
|
const findListOptions: FindListOptions = {
|
||||||
@@ -130,9 +130,7 @@ export class SuggestionsService {
|
|||||||
sort: sortOptions,
|
sort: sortOptions,
|
||||||
};
|
};
|
||||||
|
|
||||||
return this.suggestionsDataService.getSuggestionsByTargetAndSource(target, source, findListOptions).pipe(
|
return this.suggestionsDataService.getSuggestionsByTargetAndSource(target, source, findListOptions);
|
||||||
getAllSucceededRemoteDataPayload(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -169,7 +167,7 @@ export class SuggestionsService {
|
|||||||
if (hasNoValue(userUuid)) {
|
if (hasNoValue(userUuid)) {
|
||||||
return of([]);
|
return of([]);
|
||||||
}
|
}
|
||||||
return this.researcherProfileService.findById(userUuid, true).pipe(
|
return this.researcherProfileService.findById(userUuid, true, true, followLink('item')).pipe(
|
||||||
getFirstCompletedRemoteData(),
|
getFirstCompletedRemoteData(),
|
||||||
mergeMap((profile: RemoteData<ResearcherProfile> ) => {
|
mergeMap((profile: RemoteData<ResearcherProfile> ) => {
|
||||||
if (isNotEmpty(profile) && profile.hasSucceeded && isNotEmpty(profile.payload)) {
|
if (isNotEmpty(profile) && profile.hasSucceeded && isNotEmpty(profile.payload)) {
|
||||||
|
@@ -1 +1 @@
|
|||||||
<ds-publication-claim [source]="'oaire'"></ds-publication-claim>
|
<ds-publication-claim [source]="'openaire'"></ds-publication-claim>
|
||||||
|
@@ -2,7 +2,9 @@ import { of as observableOf } from 'rxjs';
|
|||||||
|
|
||||||
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
import { DSpaceObject } from '../../core/shared/dspace-object.model';
|
||||||
import { Item } from '../../core/shared/item.model';
|
import { Item } from '../../core/shared/item.model';
|
||||||
|
import { createSuccessfulRemoteDataObject$ } from '../remote-data.utils';
|
||||||
import { SearchResult } from '../search/models/search-result.model';
|
import { SearchResult } from '../search/models/search-result.model';
|
||||||
|
import { createPaginatedList } from '../testing/utils.test';
|
||||||
|
|
||||||
// REST Mock ---------------------------------------------------------------------
|
// REST Mock ---------------------------------------------------------------------
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
@@ -1345,7 +1347,7 @@ export function getMockSuggestionNotificationsStateService(): any {
|
|||||||
export function getMockSuggestionsService(): any {
|
export function getMockSuggestionsService(): any {
|
||||||
return jasmine.createSpyObj('SuggestionsService', {
|
return jasmine.createSpyObj('SuggestionsService', {
|
||||||
getTargets: jasmine.createSpy('getTargets'),
|
getTargets: jasmine.createSpy('getTargets'),
|
||||||
getSuggestions: observableOf([]),
|
getSuggestions: createSuccessfulRemoteDataObject$(createPaginatedList([])),
|
||||||
clearSuggestionRequests: jasmine.createSpy('clearSuggestionRequests'),
|
clearSuggestionRequests: jasmine.createSpy('clearSuggestionRequests'),
|
||||||
deleteReviewedSuggestion: jasmine.createSpy('deleteReviewedSuggestion'),
|
deleteReviewedSuggestion: jasmine.createSpy('deleteReviewedSuggestion'),
|
||||||
retrieveCurrentUserSuggestions: jasmine.createSpy('retrieveCurrentUserSuggestions'),
|
retrieveCurrentUserSuggestions: jasmine.createSpy('retrieveCurrentUserSuggestions'),
|
||||||
|
@@ -1,105 +0,0 @@
|
|||||||
import {
|
|
||||||
createFeatureSelector,
|
|
||||||
createSelector,
|
|
||||||
MemoizedSelector,
|
|
||||||
} from '@ngrx/store';
|
|
||||||
|
|
||||||
import { SuggestionTarget } from '../core/notifications/suggestions/models/suggestion-target.model';
|
|
||||||
import {
|
|
||||||
suggestionNotificationsSelector,
|
|
||||||
SuggestionNotificationsState,
|
|
||||||
} from '../notifications/notifications.reducer';
|
|
||||||
import { SuggestionTargetState } from '../notifications/suggestion-targets/suggestion-targets.reducer';
|
|
||||||
import { subStateSelector } from '../submission/selectors';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the Reciter Suggestion Target state.
|
|
||||||
* @function _getSuggestionTargetState
|
|
||||||
* @param {AppState} state Top level state.
|
|
||||||
* @return {SuggestionNotificationsState}
|
|
||||||
*/
|
|
||||||
const _getSuggestionTargetState = createFeatureSelector<SuggestionNotificationsState>('suggestionNotifications');
|
|
||||||
|
|
||||||
// Reciter Suggestion Targets
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the Suggestion Targets State.
|
|
||||||
* @function suggestionTargetStateSelector
|
|
||||||
* @return {SuggestionNotificationsState}
|
|
||||||
*/
|
|
||||||
export function suggestionTargetStateSelector(): MemoizedSelector<SuggestionNotificationsState, SuggestionTargetState> {
|
|
||||||
return subStateSelector<SuggestionNotificationsState, SuggestionTargetState>(suggestionNotificationsSelector, 'suggestionTarget');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the Suggestion Targets list.
|
|
||||||
* @function suggestionTargetObjectSelector
|
|
||||||
* @return {SuggestionTarget[]}
|
|
||||||
*/
|
|
||||||
export function suggestionTargetObjectSelector(): MemoizedSelector<SuggestionNotificationsState, SuggestionTarget[]> {
|
|
||||||
return subStateSelector<SuggestionNotificationsState, SuggestionTarget[]>(suggestionTargetStateSelector(), 'targets');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the Suggestion Targets are loaded.
|
|
||||||
* @function isSuggestionTargetLoadedSelector
|
|
||||||
* @return {boolean}
|
|
||||||
*/
|
|
||||||
export const isSuggestionTargetLoadedSelector = createSelector(_getSuggestionTargetState,
|
|
||||||
(state: SuggestionNotificationsState) => state.suggestionTarget.loaded,
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the deduplication sets are processing.
|
|
||||||
* @function isDeduplicationSetsProcessingSelector
|
|
||||||
* @return {boolean}
|
|
||||||
*/
|
|
||||||
export const isReciterSuggestionTargetProcessingSelector = createSelector(_getSuggestionTargetState,
|
|
||||||
(state: SuggestionNotificationsState) => state.suggestionTarget.processing,
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the total available pages of Reciter Suggestion Targets.
|
|
||||||
* @function getSuggestionTargetTotalPagesSelector
|
|
||||||
* @return {number}
|
|
||||||
*/
|
|
||||||
export const getSuggestionTargetTotalPagesSelector = createSelector(_getSuggestionTargetState,
|
|
||||||
(state: SuggestionNotificationsState) => state.suggestionTarget.totalPages,
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current page of Suggestion Targets.
|
|
||||||
* @function getSuggestionTargetCurrentPageSelector
|
|
||||||
* @return {number}
|
|
||||||
*/
|
|
||||||
export const getSuggestionTargetCurrentPageSelector = createSelector(_getSuggestionTargetState,
|
|
||||||
(state: SuggestionNotificationsState) => state.suggestionTarget.currentPage,
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the total number of Suggestion Targets.
|
|
||||||
* @function getSuggestionTargetTotalsSelector
|
|
||||||
* @return {number}
|
|
||||||
*/
|
|
||||||
export const getSuggestionTargetTotalsSelector = createSelector(_getSuggestionTargetState,
|
|
||||||
(state: SuggestionNotificationsState) => state.suggestionTarget.totalElements,
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns Suggestion Targets for the current user.
|
|
||||||
* @function getCurrentUserSuggestionTargetSelector
|
|
||||||
* @return {SuggestionTarget[]}
|
|
||||||
*/
|
|
||||||
export const getCurrentUserSuggestionTargetsSelector = createSelector(_getSuggestionTargetState,
|
|
||||||
(state: SuggestionNotificationsState) => state.suggestionTarget.currentUserTargets,
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether or not the user has consulted their suggestions
|
|
||||||
* @function getCurrentUserSuggestionTargetSelector
|
|
||||||
* @return {boolean}
|
|
||||||
*/
|
|
||||||
export const getCurrentUserSuggestionTargetsVisitedSelector = createSelector(_getSuggestionTargetState,
|
|
||||||
(state: SuggestionNotificationsState) => state.suggestionTarget.currentUserTargetsVisited,
|
|
||||||
);
|
|
@@ -2,7 +2,8 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<ng-container *ngVar="(suggestionsRD$ | async) as suggestionsRD">
|
<ng-container *ngVar="(suggestionsRD$ | async) as suggestionsRD">
|
||||||
<div *ngIf="suggestionsRD?.pageInfo?.totalElements > 0">
|
<ds-loading *ngIf="(processing$ | async)"></ds-loading>
|
||||||
|
<div *ngIf="(processing$ | async) !== true && suggestionsRD?.pageInfo?.totalElements > 0">
|
||||||
|
|
||||||
<h1>
|
<h1>
|
||||||
{{'suggestion.suggestionFor' | translate}}
|
{{'suggestion.suggestionFor' | translate}}
|
||||||
@@ -21,7 +22,6 @@
|
|||||||
(ignoreSuggestionClicked)="ignoreSuggestionAllSelected()"></ds-suggestion-actions>
|
(ignoreSuggestionClicked)="ignoreSuggestionAllSelected()"></ds-suggestion-actions>
|
||||||
<i class='fas fa-circle-notch fa-spin' *ngIf="isBulkOperationPending"></i>
|
<i class='fas fa-circle-notch fa-spin' *ngIf="isBulkOperationPending"></i>
|
||||||
</div>
|
</div>
|
||||||
<ds-loading *ngIf="(processing$ | async)"></ds-loading>
|
|
||||||
<ds-pagination *ngIf="(processing$ | async) !== true"
|
<ds-pagination *ngIf="(processing$ | async) !== true"
|
||||||
[paginationOptions]="paginationOptions"
|
[paginationOptions]="paginationOptions"
|
||||||
[sortOptions]="paginationSortConfig"
|
[sortOptions]="paginationSortConfig"
|
||||||
@@ -41,7 +41,9 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</ds-pagination>
|
</ds-pagination>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="suggestionsRD?.pageInfo?.totalElements === 0">{{ 'suggestion.count.missing' | translate }}</div>
|
<ds-alert *ngIf="(processing$ | async) !== true && (suggestionsRD?.pageInfo?.totalElements === 0 || !suggestionsRD)" [type]="'alert-info'">
|
||||||
|
{{'suggestion.count.missing' | translate}}
|
||||||
|
</ds-alert>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,11 +1,10 @@
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
async,
|
|
||||||
ComponentFixture,
|
ComponentFixture,
|
||||||
fakeAsync,
|
fakeAsync,
|
||||||
TestBed,
|
TestBed,
|
||||||
tick,
|
waitForAsync,
|
||||||
} from '@angular/core/testing';
|
} from '@angular/core/testing';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import {
|
import {
|
||||||
@@ -70,7 +69,7 @@ describe('SuggestionPageComponent', () => {
|
|||||||
});
|
});
|
||||||
const paginationService = new PaginationServiceStub();
|
const paginationService = new PaginationServiceStub();
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
@@ -106,7 +105,7 @@ describe('SuggestionPageComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
spyOn(component, 'updatePage').and.stub();
|
spyOn(component, 'updatePage').and.callThrough();
|
||||||
|
|
||||||
scheduler.schedule(() => fixture.detectChanges());
|
scheduler.schedule(() => fixture.detectChanges());
|
||||||
scheduler.flush();
|
scheduler.flush();
|
||||||
@@ -118,70 +117,72 @@ describe('SuggestionPageComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should update page on pagination change', () => {
|
it('should update page on pagination change', () => {
|
||||||
spyOn(component, 'updatePage').and.stub();
|
spyOn(component, 'updatePage').and.callThrough();
|
||||||
|
component.targetId$ = observableOf('testid');
|
||||||
|
|
||||||
scheduler.schedule(() => fixture.detectChanges());
|
scheduler.schedule(() => component.onPaginationChange());
|
||||||
scheduler.flush();
|
scheduler.flush();
|
||||||
component.onPaginationChange();
|
|
||||||
expect(component.updatePage).toHaveBeenCalled();
|
expect(component.updatePage).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update suggestion on page update', (done) => {
|
it('should update suggestion on page update', () => {
|
||||||
spyOn(component.processing$, 'next');
|
spyOn(component.processing$, 'next');
|
||||||
spyOn(component.suggestionsRD$, 'next');
|
spyOn(component.suggestionsRD$, 'next');
|
||||||
|
|
||||||
scheduler.schedule(() => fixture.detectChanges());
|
component.targetId$ = observableOf('testid');
|
||||||
|
scheduler.schedule(() => component.updatePage().subscribe());
|
||||||
scheduler.flush();
|
scheduler.flush();
|
||||||
paginationService.getFindListOptions().subscribe(() => {
|
|
||||||
expect(component.processing$.next).toHaveBeenCalled();
|
expect(component.processing$.next).toHaveBeenCalledTimes(2);
|
||||||
expect(mockSuggestionsService.getSuggestions).toHaveBeenCalled();
|
expect(mockSuggestionsService.getSuggestions).toHaveBeenCalled();
|
||||||
expect(component.suggestionsRD$.next).toHaveBeenCalled();
|
expect(component.suggestionsRD$.next).toHaveBeenCalled();
|
||||||
expect(mockSuggestionsService.clearSuggestionRequests).toHaveBeenCalled();
|
expect(mockSuggestionsService.clearSuggestionRequests).toHaveBeenCalled();
|
||||||
done();
|
|
||||||
});
|
|
||||||
component.updatePage();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should flag suggestion for deletion', fakeAsync(() => {
|
it('should flag suggestion for deletion', fakeAsync(() => {
|
||||||
spyOn(component, 'updatePage').and.stub();
|
spyOn(component, 'updatePage').and.callThrough();
|
||||||
|
component.targetId$ = observableOf('testid');
|
||||||
|
|
||||||
scheduler.schedule(() => fixture.detectChanges());
|
scheduler.schedule(() => component.ignoreSuggestion('1'));
|
||||||
scheduler.flush();
|
scheduler.flush();
|
||||||
component.ignoreSuggestion('1');
|
|
||||||
expect(mockSuggestionsService.ignoreSuggestion).toHaveBeenCalledWith('1');
|
expect(mockSuggestionsService.ignoreSuggestion).toHaveBeenCalledWith('1');
|
||||||
expect(mockSuggestionsTargetStateService.dispatchRefreshUserSuggestionsAction).toHaveBeenCalled();
|
expect(mockSuggestionsTargetStateService.dispatchRefreshUserSuggestionsAction).toHaveBeenCalled();
|
||||||
tick(201);
|
|
||||||
expect(component.updatePage).toHaveBeenCalled();
|
expect(component.updatePage).toHaveBeenCalled();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should flag all suggestion for deletion', () => {
|
it('should flag all suggestion for deletion', () => {
|
||||||
spyOn(component, 'updatePage').and.stub();
|
spyOn(component, 'updatePage').and.callThrough();
|
||||||
|
component.targetId$ = observableOf('testid');
|
||||||
|
|
||||||
scheduler.schedule(() => fixture.detectChanges());
|
scheduler.schedule(() => component.ignoreSuggestionAllSelected());
|
||||||
scheduler.flush();
|
scheduler.flush();
|
||||||
component.ignoreSuggestionAllSelected();
|
|
||||||
expect(mockSuggestionsService.ignoreSuggestionMultiple).toHaveBeenCalled();
|
expect(mockSuggestionsService.ignoreSuggestionMultiple).toHaveBeenCalled();
|
||||||
expect(mockSuggestionsTargetStateService.dispatchRefreshUserSuggestionsAction).toHaveBeenCalled();
|
expect(mockSuggestionsTargetStateService.dispatchRefreshUserSuggestionsAction).toHaveBeenCalled();
|
||||||
expect(component.updatePage).toHaveBeenCalled();
|
expect(component.updatePage).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should approve and import', () => {
|
it('should approve and import', () => {
|
||||||
spyOn(component, 'updatePage').and.stub();
|
spyOn(component, 'updatePage').and.callThrough();
|
||||||
|
component.targetId$ = observableOf('testid');
|
||||||
|
|
||||||
scheduler.schedule(() => fixture.detectChanges());
|
scheduler.schedule(() => component.approveAndImport({ collectionId: '1234' } as unknown as SuggestionApproveAndImport));
|
||||||
scheduler.flush();
|
scheduler.flush();
|
||||||
component.approveAndImport({ collectionId: '1234' } as unknown as SuggestionApproveAndImport);
|
|
||||||
expect(mockSuggestionsService.approveAndImport).toHaveBeenCalled();
|
expect(mockSuggestionsService.approveAndImport).toHaveBeenCalled();
|
||||||
expect(mockSuggestionsTargetStateService.dispatchRefreshUserSuggestionsAction).toHaveBeenCalled();
|
expect(mockSuggestionsTargetStateService.dispatchRefreshUserSuggestionsAction).toHaveBeenCalled();
|
||||||
expect(component.updatePage).toHaveBeenCalled();
|
expect(component.updatePage).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should approve and import multiple suggestions', () => {
|
it('should approve and import multiple suggestions', () => {
|
||||||
spyOn(component, 'updatePage').and.stub();
|
spyOn(component, 'updatePage').and.callThrough();
|
||||||
|
component.targetId$ = observableOf('testid');
|
||||||
|
|
||||||
scheduler.schedule(() => fixture.detectChanges());
|
scheduler.schedule(() => component.approveAndImportAllSelected({ collectionId: '1234' } as unknown as SuggestionApproveAndImport));
|
||||||
scheduler.flush();
|
scheduler.flush();
|
||||||
component.approveAndImportAllSelected({ collectionId: '1234' } as unknown as SuggestionApproveAndImport);
|
|
||||||
expect(mockSuggestionsService.approveAndImportMultiple).toHaveBeenCalled();
|
expect(mockSuggestionsService.approveAndImportMultiple).toHaveBeenCalled();
|
||||||
expect(mockSuggestionsTargetStateService.dispatchRefreshUserSuggestionsAction).toHaveBeenCalled();
|
expect(mockSuggestionsTargetStateService.dispatchRefreshUserSuggestionsAction).toHaveBeenCalled();
|
||||||
expect(component.updatePage).toHaveBeenCalled();
|
expect(component.updatePage).toHaveBeenCalled();
|
||||||
|
@@ -26,7 +26,7 @@ import {
|
|||||||
distinctUntilChanged,
|
distinctUntilChanged,
|
||||||
map,
|
map,
|
||||||
switchMap,
|
switchMap,
|
||||||
take,
|
tap,
|
||||||
} from 'rxjs/operators';
|
} from 'rxjs/operators';
|
||||||
|
|
||||||
import { AuthService } from '../core/auth/auth.service';
|
import { AuthService } from '../core/auth/auth.service';
|
||||||
@@ -41,7 +41,10 @@ import { Suggestion } from '../core/notifications/suggestions/models/suggestion.
|
|||||||
import { SuggestionTarget } from '../core/notifications/suggestions/models/suggestion-target.model';
|
import { SuggestionTarget } from '../core/notifications/suggestions/models/suggestion-target.model';
|
||||||
import { PaginationService } from '../core/pagination/pagination.service';
|
import { PaginationService } from '../core/pagination/pagination.service';
|
||||||
import { redirectOn4xx } from '../core/shared/authorized.operators';
|
import { redirectOn4xx } from '../core/shared/authorized.operators';
|
||||||
import { getFirstSucceededRemoteDataPayload } from '../core/shared/operators';
|
import {
|
||||||
|
getFirstCompletedRemoteData,
|
||||||
|
getFirstSucceededRemoteDataPayload,
|
||||||
|
} from '../core/shared/operators';
|
||||||
import { WorkspaceItem } from '../core/submission/models/workspaceitem.model';
|
import { WorkspaceItem } from '../core/submission/models/workspaceitem.model';
|
||||||
import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service';
|
import { WorkspaceitemDataService } from '../core/submission/workspaceitem-data.service';
|
||||||
import { SuggestionActionsComponent } from '../notifications/suggestion-actions/suggestion-actions.component';
|
import { SuggestionActionsComponent } from '../notifications/suggestion-actions/suggestion-actions.component';
|
||||||
@@ -52,6 +55,7 @@ import {
|
|||||||
SuggestionBulkResult,
|
SuggestionBulkResult,
|
||||||
SuggestionsService,
|
SuggestionsService,
|
||||||
} from '../notifications/suggestions.service';
|
} from '../notifications/suggestions.service';
|
||||||
|
import { AlertComponent } from '../shared/alert/alert.component';
|
||||||
import { ThemedLoadingComponent } from '../shared/loading/themed-loading.component';
|
import { ThemedLoadingComponent } from '../shared/loading/themed-loading.component';
|
||||||
import { NotificationsService } from '../shared/notifications/notifications.service';
|
import { NotificationsService } from '../shared/notifications/notifications.service';
|
||||||
import { PaginationComponent } from '../shared/pagination/pagination.component';
|
import { PaginationComponent } from '../shared/pagination/pagination.component';
|
||||||
@@ -74,6 +78,7 @@ import { getWorkspaceItemEditRoute } from '../workflowitems-edit-page/workflowit
|
|||||||
PaginationComponent,
|
PaginationComponent,
|
||||||
SuggestionListElementComponent,
|
SuggestionListElementComponent,
|
||||||
NgForOf,
|
NgForOf,
|
||||||
|
AlertComponent,
|
||||||
],
|
],
|
||||||
standalone: true,
|
standalone: true,
|
||||||
})
|
})
|
||||||
@@ -149,14 +154,15 @@ export class SuggestionsPageComponent implements OnInit {
|
|||||||
);
|
);
|
||||||
this.targetRD$.pipe(
|
this.targetRD$.pipe(
|
||||||
getFirstSucceededRemoteDataPayload(),
|
getFirstSucceededRemoteDataPayload(),
|
||||||
).subscribe((suggestionTarget: SuggestionTarget) => {
|
tap((suggestionTarget: SuggestionTarget) => {
|
||||||
this.suggestionTarget = suggestionTarget;
|
this.suggestionTarget = suggestionTarget;
|
||||||
this.suggestionId = suggestionTarget.id;
|
this.suggestionId = suggestionTarget.id;
|
||||||
this.researcherName = suggestionTarget.display;
|
this.researcherName = suggestionTarget.display;
|
||||||
this.suggestionSource = suggestionTarget.source;
|
this.suggestionSource = suggestionTarget.source;
|
||||||
this.researcherUuid = this.suggestionService.getTargetUuid(suggestionTarget);
|
this.researcherUuid = this.suggestionService.getTargetUuid(suggestionTarget);
|
||||||
this.updatePage();
|
}),
|
||||||
});
|
switchMap(() => this.updatePage()),
|
||||||
|
).subscribe();
|
||||||
|
|
||||||
this.suggestionTargetsStateService.dispatchMarkUserSuggestionsAsVisitedAction();
|
this.suggestionTargetsStateService.dispatchMarkUserSuggestionsAsVisitedAction();
|
||||||
}
|
}
|
||||||
@@ -165,13 +171,13 @@ export class SuggestionsPageComponent implements OnInit {
|
|||||||
* Called when one of the pagination settings is changed
|
* Called when one of the pagination settings is changed
|
||||||
*/
|
*/
|
||||||
onPaginationChange() {
|
onPaginationChange() {
|
||||||
this.updatePage();
|
this.updatePage().subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the list of suggestions
|
* Update the list of suggestions
|
||||||
*/
|
*/
|
||||||
updatePage() {
|
updatePage(): Observable<RemoteData<PaginatedList<Suggestion>>> {
|
||||||
this.processing$.next(true);
|
this.processing$.next(true);
|
||||||
const pageConfig$: Observable<FindListOptions> = this.paginationService.getFindListOptions(
|
const pageConfig$: Observable<FindListOptions> = this.paginationService.getFindListOptions(
|
||||||
this.paginationOptions.id,
|
this.paginationOptions.id,
|
||||||
@@ -179,7 +185,8 @@ export class SuggestionsPageComponent implements OnInit {
|
|||||||
).pipe(
|
).pipe(
|
||||||
distinctUntilChanged(),
|
distinctUntilChanged(),
|
||||||
);
|
);
|
||||||
combineLatest([this.targetId$, pageConfig$]).pipe(
|
|
||||||
|
return combineLatest([this.targetId$, pageConfig$]).pipe(
|
||||||
switchMap(([targetId, config]: [string, FindListOptions]) => {
|
switchMap(([targetId, config]: [string, FindListOptions]) => {
|
||||||
return this.suggestionService.getSuggestions(
|
return this.suggestionService.getSuggestions(
|
||||||
targetId,
|
targetId,
|
||||||
@@ -188,12 +195,18 @@ export class SuggestionsPageComponent implements OnInit {
|
|||||||
config.sort,
|
config.sort,
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
take(1),
|
getFirstCompletedRemoteData(),
|
||||||
).subscribe((results: PaginatedList<Suggestion>) => {
|
tap((resultsRD: RemoteData<PaginatedList<Suggestion>>) => {
|
||||||
this.processing$.next(false);
|
this.processing$.next(false);
|
||||||
this.suggestionsRD$.next(results);
|
if (resultsRD.hasSucceeded) {
|
||||||
this.suggestionService.clearSuggestionRequests();
|
this.suggestionsRD$.next(resultsRD.payload);
|
||||||
});
|
} else {
|
||||||
|
this.suggestionsRD$.next(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.suggestionService.clearSuggestionRequests();
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -201,11 +214,10 @@ export class SuggestionsPageComponent implements OnInit {
|
|||||||
* @suggestionId
|
* @suggestionId
|
||||||
*/
|
*/
|
||||||
ignoreSuggestion(suggestionId) {
|
ignoreSuggestion(suggestionId) {
|
||||||
this.suggestionService.ignoreSuggestion(suggestionId).subscribe(() => {
|
this.suggestionService.ignoreSuggestion(suggestionId).pipe(
|
||||||
this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction();
|
tap(() => this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction()),
|
||||||
//We add a little delay in the page refresh so that we ensure the deletion has been propagated
|
switchMap(() => this.updatePage()),
|
||||||
setTimeout(() => this.updatePage(), 200);
|
).subscribe();
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -213,11 +225,9 @@ export class SuggestionsPageComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
ignoreSuggestionAllSelected() {
|
ignoreSuggestionAllSelected() {
|
||||||
this.isBulkOperationPending = true;
|
this.isBulkOperationPending = true;
|
||||||
this.suggestionService
|
this.suggestionService.ignoreSuggestionMultiple(Object.values(this.selectedSuggestions)).pipe(
|
||||||
.ignoreSuggestionMultiple(Object.values(this.selectedSuggestions))
|
tap((results: SuggestionBulkResult) => {
|
||||||
.subscribe((results: SuggestionBulkResult) => {
|
|
||||||
this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction();
|
this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction();
|
||||||
this.updatePage();
|
|
||||||
this.isBulkOperationPending = false;
|
this.isBulkOperationPending = false;
|
||||||
this.selectedSuggestions = {};
|
this.selectedSuggestions = {};
|
||||||
if (results.success > 0) {
|
if (results.success > 0) {
|
||||||
@@ -230,7 +240,9 @@ export class SuggestionsPageComponent implements OnInit {
|
|||||||
this.translateService.get('suggestion.ignoreSuggestion.bulk.error',
|
this.translateService.get('suggestion.ignoreSuggestion.bulk.error',
|
||||||
{ count: results.fails }));
|
{ count: results.fails }));
|
||||||
}
|
}
|
||||||
});
|
}),
|
||||||
|
switchMap(() => this.updatePage()),
|
||||||
|
).subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -238,13 +250,14 @@ export class SuggestionsPageComponent implements OnInit {
|
|||||||
* @param event contains the suggestion and the target collection
|
* @param event contains the suggestion and the target collection
|
||||||
*/
|
*/
|
||||||
approveAndImport(event: SuggestionApproveAndImport) {
|
approveAndImport(event: SuggestionApproveAndImport) {
|
||||||
this.suggestionService.approveAndImport(this.workspaceItemService, event.suggestion, event.collectionId)
|
this.suggestionService.approveAndImport(this.workspaceItemService, event.suggestion, event.collectionId).pipe(
|
||||||
.subscribe((workspaceitem: WorkspaceItem) => {
|
tap((workspaceitem: WorkspaceItem) => {
|
||||||
const content = this.translateService.instant('suggestion.approveAndImport.success', { url: getWorkspaceItemEditRoute(workspaceitem.id) });
|
const content = this.translateService.instant('suggestion.approveAndImport.success', { url: getWorkspaceItemEditRoute(workspaceitem.id) });
|
||||||
this.notificationService.success('', content, { timeOut:0 }, true);
|
this.notificationService.success('', content, { timeOut:0 }, true);
|
||||||
this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction();
|
this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction();
|
||||||
this.updatePage();
|
}),
|
||||||
});
|
switchMap(() => this.updatePage()),
|
||||||
|
).subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -253,11 +266,9 @@ export class SuggestionsPageComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
approveAndImportAllSelected(event: SuggestionApproveAndImport) {
|
approveAndImportAllSelected(event: SuggestionApproveAndImport) {
|
||||||
this.isBulkOperationPending = true;
|
this.isBulkOperationPending = true;
|
||||||
this.suggestionService
|
this.suggestionService.approveAndImportMultiple(this.workspaceItemService, Object.values(this.selectedSuggestions), event.collectionId).pipe(
|
||||||
.approveAndImportMultiple(this.workspaceItemService, Object.values(this.selectedSuggestions), event.collectionId)
|
tap((results: SuggestionBulkResult) => {
|
||||||
.subscribe((results: SuggestionBulkResult) => {
|
|
||||||
this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction();
|
this.suggestionTargetsStateService.dispatchRefreshUserSuggestionsAction();
|
||||||
this.updatePage();
|
|
||||||
this.isBulkOperationPending = false;
|
this.isBulkOperationPending = false;
|
||||||
this.selectedSuggestions = {};
|
this.selectedSuggestions = {};
|
||||||
if (results.success > 0) {
|
if (results.success > 0) {
|
||||||
@@ -270,7 +281,9 @@ export class SuggestionsPageComponent implements OnInit {
|
|||||||
this.translateService.get('suggestion.approveAndImport.bulk.error',
|
this.translateService.get('suggestion.approveAndImport.bulk.error',
|
||||||
{ count: results.fails }));
|
{ count: results.fails }));
|
||||||
}
|
}
|
||||||
});
|
}),
|
||||||
|
switchMap(() => this.updatePage()),
|
||||||
|
).subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -5,12 +5,11 @@ import {
|
|||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { find } from 'rxjs/operators';
|
|
||||||
|
|
||||||
import { RemoteData } from '../core/data/remote-data';
|
import { RemoteData } from '../core/data/remote-data';
|
||||||
import { SuggestionTarget } from '../core/notifications/suggestions/models/suggestion-target.model';
|
import { SuggestionTarget } from '../core/notifications/suggestions/models/suggestion-target.model';
|
||||||
import { SuggestionTargetDataService } from '../core/notifications/suggestions/target/suggestion-target-data.service';
|
import { SuggestionTargetDataService } from '../core/notifications/suggestions/target/suggestion-target-data.service';
|
||||||
import { hasValue } from '../shared/empty.util';
|
import { getFirstCompletedRemoteData } from '../core/shared/operators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method for resolving a suggestion target based on the parameters in the current route
|
* Method for resolving a suggestion target based on the parameters in the current route
|
||||||
@@ -26,6 +25,6 @@ export const suggestionsPageResolver: ResolveFn<RemoteData<SuggestionTarget>> =
|
|||||||
suggestionsDataService: SuggestionTargetDataService = inject(SuggestionTargetDataService),
|
suggestionsDataService: SuggestionTargetDataService = inject(SuggestionTargetDataService),
|
||||||
): Observable<RemoteData<SuggestionTarget>> => {
|
): Observable<RemoteData<SuggestionTarget>> => {
|
||||||
return suggestionsDataService.getTargetById(route.params.targetId).pipe(
|
return suggestionsDataService.getTargetById(route.params.targetId).pipe(
|
||||||
find((RD) => hasValue(RD.hasFailed) || RD.hasSucceeded),
|
getFirstCompletedRemoteData(),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user