mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-17 15:03:07 +00:00
fixed an issue where a rehydrate would sometimes leave the app in a broken state
This commit is contained in:
@@ -36,16 +36,16 @@
|
|||||||
<ds-error *ngIf="(collectionData | async)?.hasFailed" message="{{'error.collection' | translate}}"></ds-error>
|
<ds-error *ngIf="(collectionData | async)?.hasFailed" message="{{'error.collection' | translate}}"></ds-error>
|
||||||
<ds-loading *ngIf="(collectionData | async)?.isLoading" message="{{'loading.collection' | translate}}"></ds-loading>
|
<ds-loading *ngIf="(collectionData | async)?.isLoading" message="{{'loading.collection' | translate}}"></ds-loading>
|
||||||
<br>
|
<br>
|
||||||
<div *ngIf="itemData?.hasSucceeded" @fadeIn>
|
<div *ngIf="(itemData | async)?.hasSucceeded" @fadeIn>
|
||||||
<h2>{{'collection.page.browse.recent.head' | translate}}</h2>
|
<h2>{{'collection.page.browse.recent.head' | translate}}</h2>
|
||||||
<ds-object-list
|
<ds-object-list
|
||||||
[config]="paginationConfig"
|
[config]="paginationConfig"
|
||||||
[sortConfig]="sortConfig"
|
[sortConfig]="sortConfig"
|
||||||
[objects]="itemData"
|
[objects]="(itemData | async)"
|
||||||
[hideGear]="false"
|
[hideGear]="false"
|
||||||
(paginationChange)="onPaginationChange($event)">
|
(paginationChange)="onPaginationChange($event)">
|
||||||
</ds-object-list>
|
</ds-object-list>
|
||||||
</div>
|
</div>
|
||||||
<ds-error *ngIf="itemData?.hasFailed" message="{{'error.recent-submissions' | translate}}"></ds-error>
|
<ds-error *ngIf="(itemData | async)?.hasFailed" message="{{'error.recent-submissions' | translate}}"></ds-error>
|
||||||
<ds-loading *ngIf="!itemData || itemData.isLoading" message="{{'loading.recent-submissions' | translate}}"></ds-loading>
|
<ds-loading *ngIf="!(itemData | async) || (itemData | async).isLoading" message="{{'loading.recent-submissions' | translate}}"></ds-loading>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -30,7 +30,7 @@ import { PaginationComponentOptions } from '../shared/pagination/pagination-comp
|
|||||||
})
|
})
|
||||||
export class CollectionPageComponent implements OnInit, OnDestroy {
|
export class CollectionPageComponent implements OnInit, OnDestroy {
|
||||||
collectionData: Observable<RemoteData<Collection>>;
|
collectionData: Observable<RemoteData<Collection>>;
|
||||||
itemData: RemoteData<Item[]>;
|
itemData: Observable<RemoteData<Item[]>>;
|
||||||
logoData: Observable<RemoteData<Bitstream>>;
|
logoData: Observable<RemoteData<Bitstream>>;
|
||||||
paginationConfig: PaginationComponentOptions;
|
paginationConfig: PaginationComponentOptions;
|
||||||
sortConfig: SortOptions;
|
sortConfig: SortOptions;
|
||||||
@@ -87,14 +87,12 @@ export class CollectionPageComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updatePage(searchOptions) {
|
updatePage(searchOptions) {
|
||||||
this.subs.push(this.itemDataService.findAll({
|
this.itemData = this.itemDataService.findAll({
|
||||||
scopeID: this.collectionId,
|
scopeID: this.collectionId,
|
||||||
currentPage: searchOptions.pagination.currentPage,
|
currentPage: searchOptions.pagination.currentPage,
|
||||||
elementsPerPage: searchOptions.pagination.pageSize,
|
elementsPerPage: searchOptions.pagination.pageSize,
|
||||||
sort: searchOptions.sort
|
sort: searchOptions.sort
|
||||||
}).subscribe((rd: RemoteData<Item[]>) => {
|
});
|
||||||
this.itemData = rd;
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
|
@@ -1,32 +1,32 @@
|
|||||||
import {
|
import {
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
Component,
|
Component,
|
||||||
|
HostListener,
|
||||||
Inject,
|
Inject,
|
||||||
ViewEncapsulation,
|
|
||||||
OnInit,
|
OnInit,
|
||||||
HostListener, SimpleChanges, OnChanges
|
ViewEncapsulation
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
|
||||||
|
|
||||||
import { Store } from '@ngrx/store';
|
import { Store } from '@ngrx/store';
|
||||||
|
|
||||||
import { TransferState } from '../modules/transfer-state/transfer-state';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { HostWindowState } from './shared/host-window.reducer';
|
|
||||||
import { HostWindowResizeAction } from './shared/host-window.actions';
|
|
||||||
import { NativeWindowRef, NativeWindowService } from './shared/window.service';
|
|
||||||
import { MetadataService } from './core/metadata/metadata.service';
|
|
||||||
|
|
||||||
import { GLOBAL_CONFIG, GlobalConfig } from '../config';
|
import { GLOBAL_CONFIG, GlobalConfig } from '../config';
|
||||||
|
|
||||||
|
import { TransferState } from '../modules/transfer-state/transfer-state';
|
||||||
|
import { MetadataService } from './core/metadata/metadata.service';
|
||||||
|
import { HostWindowResizeAction } from './shared/host-window.actions';
|
||||||
|
import { HostWindowState } from './shared/host-window.reducer';
|
||||||
|
import { NativeWindowRef, NativeWindowService } from './shared/window.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-app',
|
selector: 'ds-app',
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
styleUrls: ['./app.component.scss'],
|
styleUrls: ['./app.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.Default,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
encapsulation: ViewEncapsulation.None
|
encapsulation: ViewEncapsulation.None
|
||||||
})
|
})
|
||||||
export class AppComponent implements OnInit, OnChanges {
|
export class AppComponent implements OnInit {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(GLOBAL_CONFIG) public config: GlobalConfig,
|
@Inject(GLOBAL_CONFIG) public config: GlobalConfig,
|
||||||
@@ -71,9 +71,4 @@ export class AppComponent implements OnInit, OnChanges {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
|
||||||
console.log('AppComponent: onchanges called', changes);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
import { Observable } from 'rxjs/Observable';
|
|
||||||
|
|
||||||
import { PageInfo } from '../shared/page-info.model';
|
import { PageInfo } from '../shared/page-info.model';
|
||||||
|
import { hasValue } from '../../shared/empty.util';
|
||||||
|
|
||||||
export enum RemoteDataState {
|
export enum RemoteDataState {
|
||||||
RequestPending = 'RequestPending',
|
RequestPending = 'RequestPending',
|
||||||
@@ -26,13 +25,13 @@ export class RemoteData<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get state(): RemoteDataState {
|
get state(): RemoteDataState {
|
||||||
if (this.isSuccessFul === true) {
|
if (this.isSuccessFul === true && hasValue(this.payload)) {
|
||||||
return RemoteDataState.Success
|
return RemoteDataState.Success
|
||||||
} else if (this.isSuccessFul === false) {
|
} else if (this.isSuccessFul === false) {
|
||||||
return RemoteDataState.Failed
|
return RemoteDataState.Failed
|
||||||
} else if (this.requestPending === true) {
|
} else if (this.requestPending === true) {
|
||||||
return RemoteDataState.RequestPending
|
return RemoteDataState.RequestPending
|
||||||
} else if (this.responsePending === true || this.isSuccessFul === undefined) {
|
} else {
|
||||||
return RemoteDataState.ResponsePending
|
return RemoteDataState.ResponsePending
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { MemoizedSelector, Store } from '@ngrx/store';
|
import { createSelector, MemoizedSelector, Store } from '@ngrx/store';
|
||||||
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
import { hasValue } from '../../shared/empty.util';
|
import { hasValue } from '../../shared/empty.util';
|
||||||
@@ -9,17 +9,24 @@ import { ObjectCacheService } from '../cache/object-cache.service';
|
|||||||
import { DSOSuccessResponse, RestResponse } from '../cache/response-cache.models';
|
import { DSOSuccessResponse, RestResponse } from '../cache/response-cache.models';
|
||||||
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
import { ResponseCacheEntry } from '../cache/response-cache.reducer';
|
||||||
import { ResponseCacheService } from '../cache/response-cache.service';
|
import { ResponseCacheService } from '../cache/response-cache.service';
|
||||||
import { CoreState } from '../core.reducers';
|
import { coreSelector, CoreState } from '../core.reducers';
|
||||||
import { keySelector } from '../shared/selectors';
|
import { keySelector } from '../shared/selectors';
|
||||||
import { RequestConfigureAction, RequestExecuteAction } from './request.actions';
|
import { RequestConfigureAction, RequestExecuteAction } from './request.actions';
|
||||||
import { RestRequest } from './request.models';
|
import { RestRequest } from './request.models';
|
||||||
|
|
||||||
import { RequestEntry } from './request.reducer';
|
import { RequestEntry, RequestState } from './request.reducer';
|
||||||
|
|
||||||
function entryFromHrefSelector(href: string): MemoizedSelector<CoreState, RequestEntry> {
|
function entryFromHrefSelector(href: string): MemoizedSelector<CoreState, RequestEntry> {
|
||||||
return keySelector<RequestEntry>('data/request', href);
|
return keySelector<RequestEntry>('data/request', href);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function requestStateSelector(): MemoizedSelector<CoreState, RequestState> {
|
||||||
|
return createSelector(coreSelector, (state: CoreState) => {
|
||||||
|
return state['data/request'] as RequestState;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RequestService {
|
export class RequestService {
|
||||||
private requestsOnTheirWayToTheStore: string[] = [];
|
private requestsOnTheirWayToTheStore: string[] = [];
|
||||||
@@ -54,7 +61,6 @@ export class RequestService {
|
|||||||
|
|
||||||
configure<T extends CacheableObject>(request: RestRequest): void {
|
configure<T extends CacheableObject>(request: RestRequest): void {
|
||||||
let isCached = this.objectCache.hasBySelfLink(request.href);
|
let isCached = this.objectCache.hasBySelfLink(request.href);
|
||||||
// console.log('request.href', request.href);
|
|
||||||
if (!isCached && this.responseCache.has(request.href)) {
|
if (!isCached && this.responseCache.has(request.href)) {
|
||||||
const [successResponse, errorResponse] = this.responseCache.get(request.href)
|
const [successResponse, errorResponse] = this.responseCache.get(request.href)
|
||||||
.take(1)
|
.take(1)
|
||||||
|
@@ -17,7 +17,6 @@ export abstract class HALEndpointService {
|
|||||||
const request = new RootEndpointRequest(this.EnvConfig);
|
const request = new RootEndpointRequest(this.EnvConfig);
|
||||||
this.requestService.configure(request);
|
this.requestService.configure(request);
|
||||||
return this.responseCache.get(request.href)
|
return this.responseCache.get(request.href)
|
||||||
// .do((entry: ResponseCacheEntry) => console.log('entry.response', entry.response))
|
|
||||||
.map((entry: ResponseCacheEntry) => entry.response)
|
.map((entry: ResponseCacheEntry) => entry.response)
|
||||||
.filter((response: RootSuccessResponse) => isNotEmpty(response) && isNotEmpty(response.endpointMap))
|
.filter((response: RootSuccessResponse) => isNotEmpty(response) && isNotEmpty(response.endpointMap))
|
||||||
.map((response: RootSuccessResponse) => response.endpointMap)
|
.map((response: RootSuccessResponse) => response.endpointMap)
|
||||||
|
@@ -36,7 +36,15 @@ export function boot(cache: TransferState, appRef: ApplicationRef, store: Store<
|
|||||||
// authentication mechanism goes here
|
// authentication mechanism goes here
|
||||||
return () => {
|
return () => {
|
||||||
appRef.isStable.filter((stable: boolean) => stable).first().subscribe(() => {
|
appRef.isStable.filter((stable: boolean) => stable).first().subscribe(() => {
|
||||||
|
// isStable == true doesn't guarantee that all dispatched actions have been
|
||||||
|
// processed yet. So in those cases the store snapshot wouldn't be complete
|
||||||
|
// and a rehydrate would leave the app in a broken state
|
||||||
|
//
|
||||||
|
// This setTimeout without delay schedules the cache.inject() to happen ASAP
|
||||||
|
// after everything that's already scheduled, and it solves that problem.
|
||||||
|
setTimeout(() => {
|
||||||
cache.inject();
|
cache.inject();
|
||||||
|
}, 0);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user