Skip to content

Commit ebf9469

Browse files
authored
Merge pull request DSpace#3060 from alexandrevryghem/w2p-113560_edit-item-add-relationships-one-by-one_contribute-main
Fixed creation & deletion of relationships not working correctly & added same entity type relationship support
2 parents 123b6b7 + 62e07e7 commit ebf9469

30 files changed

Lines changed: 1197 additions & 572 deletions

src/app/app.component.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
Observable,
3333
} from 'rxjs';
3434
import {
35+
delay,
3536
distinctUntilChanged,
3637
take,
3738
withLatestFrom,
@@ -136,7 +137,10 @@ export class AppComponent implements OnInit, AfterViewInit {
136137
}
137138

138139
ngAfterViewInit() {
139-
this.router.events.subscribe((event) => {
140+
this.router.events.pipe(
141+
// delay(0) to prevent "Expression has changed after it was checked" errors
142+
delay(0),
143+
).subscribe((event) => {
140144
if (event instanceof NavigationStart) {
141145
distinctNext(this.isRouteLoading$, true);
142146
} else if (

src/app/core/data/object-updates/object-updates.reducer.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ export interface VirtualMetadataSource {
6161

6262
export interface RelationshipIdentifiable extends Identifiable {
6363
nameVariant?: string;
64+
originalItem: Item;
65+
originalIsLeft: boolean
6466
relatedItem: Item;
6567
relationship: Relationship;
6668
type: RelationshipType;

src/app/core/data/object-updates/object-updates.service.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
filter,
1616
map,
1717
switchMap,
18+
take,
1819
} from 'rxjs/operators';
1920

2021
import {
@@ -212,17 +213,29 @@ export class ObjectUpdatesService {
212213
* @param url The page's URL for which the changes are saved
213214
* @param field An updated field for the page's object
214215
*/
215-
saveAddFieldUpdate(url: string, field: Identifiable) {
216+
saveAddFieldUpdate(url: string, field: Identifiable): Observable<boolean> {
217+
const update$: Observable<boolean> = this.getFieldUpdatesExclusive(url, [field]).pipe(
218+
filter((fieldUpdates: FieldUpdates) => fieldUpdates[field.uuid].changeType === FieldChangeType.ADD),
219+
take(1),
220+
map(() => true),
221+
);
216222
this.saveFieldUpdate(url, field, FieldChangeType.ADD);
223+
return update$;
217224
}
218225

219226
/**
220227
* Calls the saveFieldUpdate method with FieldChangeType.REMOVE
221228
* @param url The page's URL for which the changes are saved
222229
* @param field An updated field for the page's object
223230
*/
224-
saveRemoveFieldUpdate(url: string, field: Identifiable) {
231+
saveRemoveFieldUpdate(url: string, field: Identifiable): Observable<boolean> {
232+
const update$: Observable<boolean> = this.getFieldUpdatesExclusive(url, [field]).pipe(
233+
filter((fieldUpdates: FieldUpdates) => fieldUpdates[field.uuid].changeType === FieldChangeType.REMOVE),
234+
take(1),
235+
map(() => true),
236+
);
225237
this.saveFieldUpdate(url, field, FieldChangeType.REMOVE);
238+
return update$;
226239
}
227240

228241
/**

src/app/core/data/relationship-data.service.ts

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,11 @@ export class RelationshipDataService extends IdentifiableDataService<Relationshi
155155
* @param id the ID of the relationship to delete
156156
* @param copyVirtualMetadata whether to copy this relationship's virtual metadata to the related Items
157157
* accepted values: none, all, left, right, configured
158+
* @param shouldRefresh refresh the cache for the items in the relationship after creating
159+
* it. Disable this if you want to add relationships in bulk, and
160+
* want to refresh the cachemanually at the end
158161
*/
159-
deleteRelationship(id: string, copyVirtualMetadata: string): Observable<RemoteData<NoContent>> {
162+
deleteRelationship(id: string, copyVirtualMetadata: string, shouldRefresh = true): Observable<RemoteData<NoContent>> {
160163
return this.getRelationshipEndpoint(id).pipe(
161164
isNotEmptyOperator(),
162165
take(1),
@@ -167,7 +170,11 @@ export class RelationshipDataService extends IdentifiableDataService<Relationshi
167170
sendRequest(this.requestService),
168171
switchMap((restRequest: RestRequest) => this.rdbService.buildFromRequestUUID(restRequest.uuid)),
169172
getFirstCompletedRemoteData(),
170-
tap(() => this.refreshRelationshipItemsInCacheByRelationship(id)),
173+
tap(() => {
174+
if (shouldRefresh) {
175+
this.refreshRelationshipItemsInCacheByRelationship(id);
176+
}
177+
}),
171178
);
172179
}
173180

@@ -178,8 +185,11 @@ export class RelationshipDataService extends IdentifiableDataService<Relationshi
178185
* @param item2 The second item of the relationship
179186
* @param leftwardValue The leftward value of the relationship
180187
* @param rightwardValue The rightward value of the relationship
188+
* @param shouldRefresh refresh the cache for the items in the relationship after creating it.
189+
* Disable this if you want to add relationships in bulk, and want to refresh
190+
* the cache manually at the end
181191
*/
182-
addRelationship(typeId: string, item1: Item, item2: Item, leftwardValue?: string, rightwardValue?: string): Observable<RemoteData<Relationship>> {
192+
addRelationship(typeId: string, item1: Item, item2: Item, leftwardValue?: string, rightwardValue?: string, shouldRefresh = true): Observable<RemoteData<Relationship>> {
183193
const options: HttpOptions = Object.create({});
184194
let headers = new HttpHeaders();
185195
headers = headers.append('Content-Type', 'text/uri-list');
@@ -194,8 +204,12 @@ export class RelationshipDataService extends IdentifiableDataService<Relationshi
194204
sendRequest(this.requestService),
195205
switchMap((restRequest: RestRequest) => this.rdbService.buildFromRequestUUID(restRequest.uuid)),
196206
getFirstCompletedRemoteData(),
197-
tap(() => this.refreshRelationshipItemsInCache(item1)),
198-
tap(() => this.refreshRelationshipItemsInCache(item2)),
207+
tap(() => {
208+
if (shouldRefresh) {
209+
this.refreshRelationshipItemsInCache(item1);
210+
this.refreshRelationshipItemsInCache(item2);
211+
}
212+
}),
199213
) as Observable<RemoteData<Relationship>>;
200214
}
201215

@@ -223,7 +237,7 @@ export class RelationshipDataService extends IdentifiableDataService<Relationshi
223237
* Method to remove an item that's part of a relationship from the cache
224238
* @param item The item to remove from the cache
225239
*/
226-
public refreshRelationshipItemsInCache(item) {
240+
public refreshRelationshipItemsInCache(item: Item): void {
227241
this.objectCache.remove(item._links.self.href);
228242
this.requestService.removeByHrefSubstring(item.uuid);
229243
observableCombineLatest([
@@ -336,7 +350,19 @@ export class RelationshipDataService extends IdentifiableDataService<Relationshi
336350
} else {
337351
findListOptions.searchParams = searchParams;
338352
}
339-
return this.searchBy('byLabel', findListOptions, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
353+
354+
// always set reRequestOnStale to false here, so it doesn't happen automatically in BaseDataService
355+
const result$ = this.searchBy('byLabel', findListOptions, useCachedVersionIfAvailable, false, ...linksToFollow);
356+
357+
// add this result as a dependency of the item, meaning that if the item is invalided, this
358+
// result will be as well
359+
this.addDependency(result$, item._links.self.href);
360+
361+
// do the reRequestOnStale call here, to ensure any re-requests also get added as dependencies
362+
return result$.pipe(
363+
this.reRequestStaleRemoteData(reRequestOnStale, () =>
364+
this.getItemRelationshipsByLabel(item, label, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow)),
365+
);
340366
}
341367

342368
/**

src/app/item-page/edit-item-page/abstract-item-update/abstract-item-update.component.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ export class AbstractItemUpdateComponent extends AbstractTrackableComponent impl
5555
*/
5656
updates$: Observable<FieldUpdates>;
5757

58+
hasChanges$: Observable<boolean>;
59+
60+
isReinstatable$: Observable<boolean>;
61+
5862
/**
5963
* Route to the item's page
6064
*/
@@ -101,10 +105,9 @@ export class AbstractItemUpdateComponent extends AbstractTrackableComponent impl
101105
}
102106

103107
this.discardTimeOut = environment.item.edit.undoTimeout;
104-
this.url = this.router.url;
105-
if (this.url.indexOf('?') > 0) {
106-
this.url = this.url.substr(0, this.url.indexOf('?'));
107-
}
108+
this.url = this.router.url.split('?')[0];
109+
this.hasChanges$ = this.hasChanges();
110+
this.isReinstatable$ = this.isReinstatable();
108111
this.hasChanges().pipe(first()).subscribe((hasChanges) => {
109112
if (!hasChanges) {
110113
this.initializeOriginalFields();

src/app/item-page/edit-item-page/item-bitstreams/item-bitstreams.component.html

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,21 @@
66
class="fas fa-upload"></i>
77
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.bitstreams.upload-button" | translate}}</span>
88
</button>
9-
<button class="btn btn-warning" *ngIf="isReinstatable() | async"
9+
<button class="btn btn-warning" *ngIf="isReinstatable$ | async"
1010
[attr.aria-label]="'item.edit.bitstreams.reinstate-button' | translate"
1111
(click)="reinstate()"><i
1212
class="fas fa-undo-alt"></i>
1313
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.bitstreams.reinstate-button" | translate}}</span>
1414
</button>
15-
<button class="btn btn-primary" [disabled]="(hasChanges() | async) !== true || submitting"
15+
<button class="btn btn-primary" [disabled]="(hasChanges$ | async) !== true || submitting"
1616
[attr.aria-label]="'item.edit.bitstreams.save-button' | translate"
1717
(click)="submit()"><i
1818
class="fas fa-save"></i>
1919
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.bitstreams.save-button" | translate}}</span>
2020
</button>
21-
<button class="btn btn-danger" *ngIf="(isReinstatable() | async) !== true"
21+
<button class="btn btn-danger" *ngIf="(isReinstatable$ | async) !== true"
2222
[attr.aria-label]="'item.edit.bitstreams.discard-button' | translate"
23-
[disabled]="(hasChanges() | async) !== true || submitting"
23+
[disabled]="(hasChanges$ | async) !== true || submitting"
2424
(click)="discard()"><i
2525
class="fas fa-times"></i>
2626
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.bitstreams.discard-button" | translate}}</span>
@@ -52,21 +52,21 @@
5252

5353
<div class="button-row bottom">
5454
<div class="mt-4 float-right space-children-mr ml-gap">
55-
<button class="btn btn-warning" *ngIf="isReinstatable() | async"
55+
<button class="btn btn-warning" *ngIf="isReinstatable$ | async"
5656
[attr.aria-label]="'item.edit.bitstreams.reinstate-button' | translate"
5757
(click)="reinstate()"><i
5858
class="fas fa-undo-alt"></i>
5959
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.bitstreams.reinstate-button" | translate}}</span>
6060
</button>
61-
<button class="btn btn-primary" [disabled]="(hasChanges() | async) !== true || submitting"
61+
<button class="btn btn-primary" [disabled]="(hasChanges$ | async) !== true || submitting"
6262
[attr.aria-label]="'item.edit.bitstreams.save-button' | translate"
6363
(click)="submit()"><i
6464
class="fas fa-save"></i>
6565
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.bitstreams.save-button" | translate}}</span>
6666
</button>
67-
<button class="btn btn-danger" *ngIf="(isReinstatable() | async) !== true"
67+
<button class="btn btn-danger" *ngIf="(isReinstatable$ | async) !== true"
6868
[attr.aria-label]="'item.edit.bitstreams.discard-button' | translate"
69-
[disabled]="(hasChanges() | async) !== true || submitting"
69+
[disabled]="(hasChanges$ | async) !== true || submitting"
7070
(click)="discard()"><i
7171
class="fas fa-times"></i>
7272
<span class="d-none d-sm-inline">&nbsp;{{"item.edit.bitstreams.discard-button" | translate}}</span>

0 commit comments

Comments
 (0)