Skip to content

Commit fb55ad4

Browse files
committed
[DURACOM-271] Fix suggestion list pagination and add support for multiple sources
1 parent ecfe7e9 commit fb55ad4

17 files changed

Lines changed: 373 additions & 247 deletions

src/app/notifications/suggestion-targets/publication-claim/publication-claim.component.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,14 @@ export class PublicationClaimComponent implements OnInit {
5555
/**
5656
* The source for which to list targets
5757
*/
58-
@Input() source: string;
58+
@Input() source = '';
5959

6060
/**
6161
* The pagination system configuration for HTML listing.
6262
* @type {PaginationComponentOptions}
6363
*/
6464
public paginationConfig: PaginationComponentOptions = Object.assign(new PaginationComponentOptions(), {
65-
id: 'stp',
65+
id: 'stp_' + this.source,
6666
pageSizeOptions: [5, 10, 20, 40, 60],
6767
});
6868

@@ -99,11 +99,16 @@ export class PublicationClaimComponent implements OnInit {
9999
* Component initialization.
100100
*/
101101
ngOnInit(): void {
102-
this.targets$ = this.suggestionTargetsStateService.getSuggestionTargets();
103-
this.totalElements$ = this.suggestionTargetsStateService.getSuggestionTargetsTotals();
102+
this.targets$ = this.suggestionTargetsStateService.getSuggestionTargets(this.source);
103+
this.totalElements$ = this.suggestionTargetsStateService.getSuggestionTargetsTotals(this.source);
104+
}
104105

106+
/**
107+
* First Suggestion Targets loading after view initialization.
108+
*/
109+
ngAfterViewInit(): void {
105110
this.subs.push(
106-
this.suggestionTargetsStateService.isSuggestionTargetsLoaded().pipe(
111+
this.suggestionTargetsStateService.isSuggestionTargetsLoaded(this.source).pipe(
107112
take(1),
108113
).subscribe(() => {
109114
this.getSuggestionTargets();
@@ -118,7 +123,7 @@ export class PublicationClaimComponent implements OnInit {
118123
* 'true' if the targets are loading, 'false' otherwise.
119124
*/
120125
public isTargetsLoading(): Observable<boolean> {
121-
return this.suggestionTargetsStateService.isSuggestionTargetsLoading();
126+
return this.suggestionTargetsStateService.isSuggestionTargetsLoading(this.source);
122127
}
123128

124129
/**
@@ -128,7 +133,7 @@ export class PublicationClaimComponent implements OnInit {
128133
* 'true' if there are operations running on the targets (ex.: a REST call), 'false' otherwise.
129134
*/
130135
public isTargetsProcessing(): Observable<boolean> {
131-
return this.suggestionTargetsStateService.isSuggestionTargetsProcessing();
136+
return this.suggestionTargetsStateService.isSuggestionTargetsProcessing(this.source);
132137
}
133138

134139
/**
@@ -145,7 +150,7 @@ export class PublicationClaimComponent implements OnInit {
145150
* Unsubscribe from all subscriptions.
146151
*/
147152
ngOnDestroy(): void {
148-
this.suggestionTargetsStateService.dispatchClearSuggestionTargetsAction();
153+
this.suggestionTargetsStateService.dispatchClearSuggestionTargetsAction(this.source);
149154
this.subs
150155
.filter((sub) => hasValue(sub))
151156
.forEach((sub) => sub.unsubscribe());
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import {
2+
createFeatureSelector,
3+
createSelector,
4+
MemoizedSelector,
5+
} from '@ngrx/store';
6+
7+
import { SuggestionTarget } from '../../core/notifications/suggestions/models/suggestion-target.model';
8+
import { subStateSelector } from '../../submission/selectors';
9+
import {
10+
suggestionNotificationsSelector,
11+
SuggestionNotificationsState,
12+
} from '../notifications.reducer';
13+
import {
14+
SuggestionTargetEntry,
15+
SuggestionTargetState,
16+
} from './suggestion-targets.reducer';
17+
18+
/**
19+
* Returns the Reciter Suggestion Target state.
20+
* @function _getSuggestionTargetState
21+
* @param {AppState} state Top level state.
22+
* @return {SuggestionNotificationsState}
23+
*/
24+
const _getSuggestionTargetState = createFeatureSelector<SuggestionNotificationsState>('suggestionNotifications');
25+
26+
// Suggestion Targets selectors
27+
28+
/**
29+
* Returns the Suggestion Targets State.
30+
* @function suggestionTargetStateSelector
31+
* @return {SuggestionNotificationsState}
32+
*/
33+
export function suggestionTargetStateSelector(): MemoizedSelector<SuggestionNotificationsState, SuggestionTargetState> {
34+
return subStateSelector<SuggestionNotificationsState, SuggestionTargetState>(suggestionNotificationsSelector, 'suggestionTarget');
35+
}
36+
37+
/**
38+
* Returns the Reciter Suggestion source state
39+
* @function suggestionSourceSelector
40+
* @return {SuggestionTargetEntry}
41+
*/
42+
export function suggestionSourceSelector(source: string): MemoizedSelector<SuggestionNotificationsState, SuggestionTargetEntry> {
43+
return createSelector(suggestionTargetStateSelector(),(state: SuggestionTargetState) => state.sources[source]);
44+
}
45+
46+
/**
47+
* Returns the Suggestion Targets list by source.
48+
* @function suggestionTargetObjectSelector
49+
* @return {SuggestionTarget[]}
50+
*/
51+
export function suggestionTargetObjectSelector(source: string): MemoizedSelector<SuggestionNotificationsState, SuggestionTarget[]> {
52+
return createSelector(suggestionSourceSelector(source), (state: SuggestionTargetEntry) => state.targets);
53+
}
54+
55+
/**
56+
* Returns true if the Suggestion Targets are loaded.
57+
* @function isSuggestionTargetLoadedSelector
58+
* @return {boolean}
59+
*/
60+
export const isSuggestionTargetLoadedSelector = (source: string) => {
61+
return createSelector(suggestionSourceSelector(source), (state: SuggestionTargetEntry) => state?.loaded || false);
62+
};
63+
64+
/**
65+
* Returns true if the deduplication sets are processing.
66+
* @function isSuggestionTargetProcessingSelector
67+
* @return {boolean}
68+
*/
69+
export const isSuggestionTargetProcessingSelector = (source: string) => {
70+
return createSelector(suggestionSourceSelector(source), (state: SuggestionTargetEntry) => state?.processing || false);
71+
};
72+
73+
/**
74+
* Returns the total available pages of Reciter Suggestion Targets.
75+
* @function getSuggestionTargetTotalPagesSelector
76+
* @return {number}
77+
*/
78+
export const getSuggestionTargetTotalPagesSelector = (source: string) => {
79+
return createSelector(suggestionSourceSelector(source), (state: SuggestionTargetEntry) => state?.totalPages || 0);
80+
};
81+
82+
/**
83+
* Returns the current page of Suggestion Targets.
84+
* @function getSuggestionTargetCurrentPageSelector
85+
* @return {number}
86+
*/
87+
export const getSuggestionTargetCurrentPageSelector = (source: string) => {
88+
return createSelector(suggestionSourceSelector(source), (state: SuggestionTargetEntry) => state?.currentPage || 0);
89+
};
90+
91+
/**
92+
* Returns the total number of Suggestion Targets.
93+
* @function getSuggestionTargetTotalsSelector
94+
* @return {number}
95+
*/
96+
export const getSuggestionTargetTotalsSelector = (source: string) => {
97+
return createSelector(suggestionSourceSelector(source), (state: SuggestionTargetEntry) => state?.totalElements || 0);
98+
};
99+
100+
/**
101+
* Returns Suggestion Targets for the current user.
102+
* @function getCurrentUserSuggestionTargetsSelector
103+
* @return {SuggestionTarget[]}
104+
*/
105+
export const getCurrentUserSuggestionTargetsSelector = () => {
106+
return createSelector(suggestionTargetStateSelector(), (state: SuggestionTargetState) => state?.currentUserTargets || []);
107+
};
108+
109+
/**
110+
* Returns whether or not the user has consulted their suggestions
111+
* @function getCurrentUserSuggestionTargetsVisitedSelector
112+
* @return {boolean}
113+
*/
114+
export const getCurrentUserSuggestionTargetsVisitedSelector = () => {
115+
return createSelector(suggestionTargetStateSelector(), (state: SuggestionTargetState) => state?.currentUserTargetsVisited || false);
116+
};

src/app/notifications/suggestion-targets/suggestion-targets.actions.ts

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,8 @@ export const SuggestionTargetActionTypes = {
2323
MARK_USER_SUGGESTIONS_AS_VISITED: type('dspace/integration/openaire/suggestions/target/MARK_USER_SUGGESTIONS_AS_VISITED'),
2424
};
2525

26-
/* tslint:disable:max-classes-per-file */
27-
2826
/**
29-
* An ngrx action to retrieve all the Suggestion Targets.
27+
* A ngrx action to retrieve all the Suggestion Targets.
3028
*/
3129
export class RetrieveTargetsBySourceAction implements Action {
3230
type = SuggestionTargetActionTypes.RETRIEVE_TARGETS_BY_SOURCE;
@@ -56,18 +54,34 @@ export class RetrieveTargetsBySourceAction implements Action {
5654
}
5755

5856
/**
59-
* An ngrx action for retrieving 'all Suggestion Targets' error.
57+
* A ngrx action for notifying error.
6058
*/
61-
export class RetrieveAllTargetsErrorAction implements Action {
59+
export class RetrieveTargetsBySourceErrorAction implements Action {
6260
type = SuggestionTargetActionTypes.RETRIEVE_TARGETS_BY_SOURCE_ERROR;
61+
payload: {
62+
source: string;
63+
};
64+
65+
/**
66+
* Create a new RetrieveTargetsBySourceAction.
67+
*
68+
* @param source
69+
* the source for which to retrieve suggestion targets
70+
*/
71+
constructor(source: string) {
72+
this.payload = {
73+
source,
74+
};
75+
}
6376
}
6477

6578
/**
66-
* An ngrx action to load the Suggestion Target objects.
79+
* A ngrx action to load the Suggestion Target objects.
6780
*/
6881
export class AddTargetAction implements Action {
6982
type = SuggestionTargetActionTypes.ADD_TARGETS;
7083
payload: {
84+
source: string;
7185
targets: SuggestionTarget[];
7286
totalPages: number;
7387
currentPage: number;
@@ -77,6 +91,8 @@ export class AddTargetAction implements Action {
7791
/**
7892
* Create a new AddTargetAction.
7993
*
94+
* @param source
95+
* the source of suggestion targets
8096
* @param targets
8197
* the list of targets
8298
* @param totalPages
@@ -86,8 +102,9 @@ export class AddTargetAction implements Action {
86102
* @param totalElements
87103
* the total available Suggestion Targets
88104
*/
89-
constructor(targets: SuggestionTarget[], totalPages: number, currentPage: number, totalElements: number) {
105+
constructor(source: string, targets: SuggestionTarget[], totalPages: number, currentPage: number, totalElements: number) {
90106
this.payload = {
107+
source,
91108
targets,
92109
totalPages,
93110
currentPage,
@@ -98,7 +115,7 @@ export class AddTargetAction implements Action {
98115
}
99116

100117
/**
101-
* An ngrx action to load the user Suggestion Target object.
118+
* A ngrx action to load the user Suggestion Target object.
102119
* Called by the ??? effect.
103120
*/
104121
export class AddUserSuggestionsAction implements Action {
@@ -120,7 +137,7 @@ export class AddUserSuggestionsAction implements Action {
120137
}
121138

122139
/**
123-
* An ngrx action to reload the user Suggestion Target object.
140+
* A ngrx action to reload the user Suggestion Target object.
124141
* Called by the ??? effect.
125142
*/
126143
export class RefreshUserSuggestionsAction implements Action {
@@ -135,21 +152,34 @@ export class RefreshUserSuggestionsErrorAction implements Action {
135152
}
136153

137154
/**
138-
* An ngrx action to Mark User Suggestions As Visited.
155+
* A ngrx action to Mark User Suggestions As Visited.
139156
* Called by the ??? effect.
140157
*/
141158
export class MarkUserSuggestionsAsVisitedAction implements Action {
142159
type = SuggestionTargetActionTypes.MARK_USER_SUGGESTIONS_AS_VISITED;
143160
}
144161

145162
/**
146-
* An ngrx action to clear targets state.
163+
* A ngrx action to clear targets state.
147164
*/
148165
export class ClearSuggestionTargetsAction implements Action {
149166
type = SuggestionTargetActionTypes.CLEAR_TARGETS;
150-
}
167+
payload: {
168+
source: string;
169+
};
151170

152-
/* tslint:enable:max-classes-per-file */
171+
/**
172+
* Create a new ClearSuggestionTargetsAction.
173+
*
174+
* @param source
175+
* the source of suggestion targets
176+
*/
177+
constructor(source: string) {
178+
this.payload = {
179+
source,
180+
};
181+
}
182+
}
153183

154184
/**
155185
* Export a type alias of all actions in this action group
@@ -161,5 +191,5 @@ export type SuggestionTargetsActions
161191
| ClearSuggestionTargetsAction
162192
| MarkUserSuggestionsAsVisitedAction
163193
| RetrieveTargetsBySourceAction
164-
| RetrieveAllTargetsErrorAction
194+
| RetrieveTargetsBySourceErrorAction
165195
| RefreshUserSuggestionsAction;

src/app/notifications/suggestion-targets/suggestion-targets.effects.ts

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,21 @@ import {
1414
tap,
1515
} from 'rxjs/operators';
1616

17+
import {
18+
AuthActionTypes,
19+
RetrieveAuthenticatedEpersonSuccessAction,
20+
} from '../../core/auth/auth.actions';
1721
import { PaginatedList } from '../../core/data/paginated-list.model';
22+
import { EPerson } from '../../core/eperson/models/eperson.model';
1823
import { SuggestionTarget } from '../../core/notifications/suggestions/models/suggestion-target.model';
1924
import { NotificationsService } from '../../shared/notifications/notifications.service';
2025
import { SuggestionsService } from '../suggestions.service';
2126
import {
2227
AddTargetAction,
2328
AddUserSuggestionsAction,
2429
RefreshUserSuggestionsErrorAction,
25-
RetrieveAllTargetsErrorAction,
2630
RetrieveTargetsBySourceAction,
31+
RetrieveTargetsBySourceErrorAction,
2732
SuggestionTargetActionTypes,
2833
} from './suggestion-targets.actions';
2934

@@ -45,13 +50,13 @@ export class SuggestionTargetsEffects {
4550
action.payload.currentPage,
4651
).pipe(
4752
map((targets: PaginatedList<SuggestionTarget>) =>
48-
new AddTargetAction(targets.page, targets.totalPages, targets.currentPage, targets.totalElements),
53+
new AddTargetAction(action.payload.source, targets.page, targets.totalPages, targets.currentPage, targets.totalElements),
4954
),
5055
catchError((error: unknown) => {
5156
if (error instanceof Error) {
5257
console.error(error.message);
5358
}
54-
return of(new RetrieveAllTargetsErrorAction());
59+
return of(new RetrieveTargetsBySourceErrorAction(action.payload.source));
5560
}),
5661
);
5762
}),
@@ -67,16 +72,27 @@ export class SuggestionTargetsEffects {
6772
}),
6873
), { dispatch: false });
6974

75+
/**
76+
* Show a notification on error.
77+
*/
78+
retrieveUserTargets$ = createEffect(() => this.actions$.pipe(
79+
ofType(AuthActionTypes.RETRIEVE_AUTHENTICATED_EPERSON_SUCCESS),
80+
switchMap((action: RetrieveAuthenticatedEpersonSuccessAction) => {
81+
return this.suggestionsService.retrieveCurrentUserSuggestions(action.payload).pipe(
82+
map((suggestionTargets: SuggestionTarget[]) => new AddUserSuggestionsAction(suggestionTargets)),
83+
);
84+
})));
85+
7086
/**
7187
* Fetch the current user suggestion
7288
*/
7389
refreshUserSuggestionsAction$ = createEffect(() => this.actions$.pipe(
7490
ofType(SuggestionTargetActionTypes.REFRESH_USER_SUGGESTIONS),
7591
switchMap(() => {
76-
return this.store$.select((state: any) => state.core.auth.userId)
92+
return this.store$.select((state: any) => state.core.auth.user)
7793
.pipe(
78-
switchMap((userId: string) => {
79-
return this.suggestionsService.retrieveCurrentUserSuggestions(userId)
94+
switchMap((user: EPerson) => {
95+
return this.suggestionsService.retrieveCurrentUserSuggestions(user.uuid)
8096
.pipe(
8197
map((suggestionTargets: SuggestionTarget[]) => new AddUserSuggestionsAction(suggestionTargets)),
8298
catchError((error: unknown) => {

0 commit comments

Comments
 (0)