Skip to content

Commit cbee67c

Browse files
authored
Merge pull request DSpace#3059 from alexandrevryghem/w2p-113560_edit-item-add-relationships-one-by-one_contribute-7_x
[Port dspace-7_x] Fixed creation & deletion of relationships not working correctly & added same entity type relationship support
2 parents 5580c81 + bedfe02 commit cbee67c

30 files changed

Lines changed: 1171 additions & 541 deletions

src/app/app.component.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { distinctUntilChanged, take, withLatestFrom } from 'rxjs/operators';
1+
import { distinctUntilChanged, take, withLatestFrom, delay } from 'rxjs/operators';
22
import { DOCUMENT, isPlatformBrowser } from '@angular/common';
33
import {
44
AfterViewInit,
@@ -114,7 +114,10 @@ export class AppComponent implements OnInit, AfterViewInit {
114114
}
115115

116116
ngAfterViewInit() {
117-
this.router.events.subscribe((event) => {
117+
this.router.events.pipe(
118+
// delay(0) to prevent "Expression has changed after it was checked" errors
119+
delay(0)
120+
).subscribe((event) => {
118121
if (event instanceof NavigationStart) {
119122
distinctNext(this.isRouteLoading$, true);
120123
} 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
@@ -58,6 +58,8 @@ export interface VirtualMetadataSource {
5858

5959
export interface RelationshipIdentifiable extends Identifiable {
6060
nameVariant?: string;
61+
originalItem: Item;
62+
originalIsLeft: boolean
6163
relatedItem: Item;
6264
relationship: Relationship;
6365
type: RelationshipType;

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

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {
1919
SetEditableFieldUpdateAction,
2020
SetValidFieldUpdateAction
2121
} from './object-updates.actions';
22-
import { distinctUntilChanged, filter, map, switchMap } from 'rxjs/operators';
22+
import { distinctUntilChanged, filter, map, switchMap, take } from 'rxjs/operators';
2323
import {
2424
hasNoValue,
2525
hasValue,
@@ -198,17 +198,29 @@ export class ObjectUpdatesService {
198198
* @param url The page's URL for which the changes are saved
199199
* @param field An updated field for the page's object
200200
*/
201-
saveAddFieldUpdate(url: string, field: Identifiable) {
201+
saveAddFieldUpdate(url: string, field: Identifiable): Observable<boolean> {
202+
const update$: Observable<boolean> = this.getFieldUpdatesExclusive(url, [field]).pipe(
203+
filter((fieldUpdates: FieldUpdates) => fieldUpdates[field.uuid].changeType === FieldChangeType.ADD),
204+
take(1),
205+
map(() => true),
206+
);
202207
this.saveFieldUpdate(url, field, FieldChangeType.ADD);
208+
return update$;
203209
}
204210

205211
/**
206212
* Calls the saveFieldUpdate method with FieldChangeType.REMOVE
207213
* @param url The page's URL for which the changes are saved
208214
* @param field An updated field for the page's object
209215
*/
210-
saveRemoveFieldUpdate(url: string, field: Identifiable) {
216+
saveRemoveFieldUpdate(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.REMOVE),
219+
take(1),
220+
map(() => true),
221+
);
211222
this.saveFieldUpdate(url, field, FieldChangeType.REMOVE);
223+
return update$;
212224
}
213225

214226
/**

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

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,11 @@ export class RelationshipDataService extends IdentifiableDataService<Relationshi
114114
* @param id the ID of the relationship to delete
115115
* @param copyVirtualMetadata whether to copy this relationship's virtual metadata to the related Items
116116
* accepted values: none, all, left, right, configured
117+
* @param shouldRefresh refresh the cache for the items in the relationship after creating
118+
* it. Disable this if you want to add relationships in bulk, and
119+
* want to refresh the cachemanually at the end
117120
*/
118-
deleteRelationship(id: string, copyVirtualMetadata: string): Observable<RemoteData<NoContent>> {
121+
deleteRelationship(id: string, copyVirtualMetadata: string, shouldRefresh = true): Observable<RemoteData<NoContent>> {
119122
return this.getRelationshipEndpoint(id).pipe(
120123
isNotEmptyOperator(),
121124
take(1),
@@ -126,7 +129,11 @@ export class RelationshipDataService extends IdentifiableDataService<Relationshi
126129
sendRequest(this.requestService),
127130
switchMap((restRequest: RestRequest) => this.rdbService.buildFromRequestUUID(restRequest.uuid)),
128131
getFirstCompletedRemoteData(),
129-
tap(() => this.refreshRelationshipItemsInCacheByRelationship(id)),
132+
tap(() => {
133+
if (shouldRefresh) {
134+
this.refreshRelationshipItemsInCacheByRelationship(id);
135+
}
136+
}),
130137
);
131138
}
132139

@@ -137,8 +144,11 @@ export class RelationshipDataService extends IdentifiableDataService<Relationshi
137144
* @param item2 The second item of the relationship
138145
* @param leftwardValue The leftward value of the relationship
139146
* @param rightwardValue The rightward value of the relationship
147+
* @param shouldRefresh refresh the cache for the items in the relationship after creating it.
148+
* Disable this if you want to add relationships in bulk, and want to refresh
149+
* the cache manually at the end
140150
*/
141-
addRelationship(typeId: string, item1: Item, item2: Item, leftwardValue?: string, rightwardValue?: string): Observable<RemoteData<Relationship>> {
151+
addRelationship(typeId: string, item1: Item, item2: Item, leftwardValue?: string, rightwardValue?: string, shouldRefresh = true): Observable<RemoteData<Relationship>> {
142152
const options: HttpOptions = Object.create({});
143153
let headers = new HttpHeaders();
144154
headers = headers.append('Content-Type', 'text/uri-list');
@@ -153,8 +163,12 @@ export class RelationshipDataService extends IdentifiableDataService<Relationshi
153163
sendRequest(this.requestService),
154164
switchMap((restRequest: RestRequest) => this.rdbService.buildFromRequestUUID(restRequest.uuid)),
155165
getFirstCompletedRemoteData(),
156-
tap(() => this.refreshRelationshipItemsInCache(item1)),
157-
tap(() => this.refreshRelationshipItemsInCache(item2)),
166+
tap(() => {
167+
if (shouldRefresh) {
168+
this.refreshRelationshipItemsInCache(item1);
169+
this.refreshRelationshipItemsInCache(item2);
170+
}
171+
}),
158172
) as Observable<RemoteData<Relationship>>;
159173
}
160174

@@ -182,7 +196,7 @@ export class RelationshipDataService extends IdentifiableDataService<Relationshi
182196
* Method to remove an item that's part of a relationship from the cache
183197
* @param item The item to remove from the cache
184198
*/
185-
public refreshRelationshipItemsInCache(item) {
199+
public refreshRelationshipItemsInCache(item: Item): void {
186200
this.objectCache.remove(item._links.self.href);
187201
this.requestService.removeByHrefSubstring(item.uuid);
188202
observableCombineLatest([
@@ -295,7 +309,19 @@ export class RelationshipDataService extends IdentifiableDataService<Relationshi
295309
} else {
296310
findListOptions.searchParams = searchParams;
297311
}
298-
return this.searchBy('byLabel', findListOptions, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
312+
313+
// always set reRequestOnStale to false here, so it doesn't happen automatically in BaseDataService
314+
const result$ = this.searchBy('byLabel', findListOptions, useCachedVersionIfAvailable, false, ...linksToFollow);
315+
316+
// add this result as a dependency of the item, meaning that if the item is invalided, this
317+
// result will be as well
318+
this.addDependency(result$, item._links.self.href);
319+
320+
// do the reRequestOnStale call here, to ensure any re-requests also get added as dependencies
321+
return result$.pipe(
322+
this.reRequestStaleRemoteData(reRequestOnStale, () =>
323+
this.getItemRelationshipsByLabel(item, label, options, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow)),
324+
);
299325
}
300326

301327
/**

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
@@ -35,6 +35,10 @@ export class AbstractItemUpdateComponent extends AbstractTrackableComponent impl
3535
*/
3636
updates$: Observable<FieldUpdates>;
3737

38+
hasChanges$: Observable<boolean>;
39+
40+
isReinstatable$: Observable<boolean>;
41+
3842
/**
3943
* Route to the item's page
4044
*/
@@ -81,10 +85,9 @@ export class AbstractItemUpdateComponent extends AbstractTrackableComponent impl
8185
}
8286

8387
this.discardTimeOut = environment.item.edit.undoTimeout;
84-
this.url = this.router.url;
85-
if (this.url.indexOf('?') > 0) {
86-
this.url = this.url.substr(0, this.url.indexOf('?'));
87-
}
88+
this.url = this.router.url.split('?')[0];
89+
this.hasChanges$ = this.hasChanges();
90+
this.isReinstatable$ = this.isReinstatable();
8891
this.hasChanges().pipe(first()).subscribe((hasChanges) => {
8992
if (!hasChanges) {
9093
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) || 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)"
21+
<button class="btn btn-danger" *ngIf="(isReinstatable$ | async) !== true"
2222
[attr.aria-label]="'item.edit.bitstreams.discard-button' | translate"
23-
[disabled]="!(hasChanges() | async) || 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) || 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)"
67+
<button class="btn btn-danger" *ngIf="(isReinstatable$ | async) !== true"
6868
[attr.aria-label]="'item.edit.bitstreams.discard-button' | translate"
69-
[disabled]="!(hasChanges() | async) || 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)