Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cypress/e2e/my-dspace.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ describe('My DSpace page', () => {
const id = subpaths[2];

// Click the "Save for Later" button to save this submission
cy.get('ds-submission-form-footer [data-test="save-for-later"]').click();
cy.get('ds-submission-form-footer [data-test="save-and-exit"]').click();

// "Save for Later" should send us to MyDSpace
cy.url().should('include', '/mydspace');
Expand Down
2 changes: 1 addition & 1 deletion cypress/e2e/submission.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ describe('New Submission page', () => {
const id = subpaths[2];

// Even though form is incomplete, the "Save for Later" button should still work
cy.get('button#saveForLater').click();
cy.get('button#saveAndExit').click();

// "Save for Later" should send us to MyDSpace
cy.url().should('include', '/mydspace');
Expand Down
9 changes: 6 additions & 3 deletions src/app/core/auth/auth.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,10 +266,13 @@ export class AuthEffects {
filter((action: Action) => !IDLE_TIMER_IGNORE_TYPES.includes(action.type)),
// Using switchMap the effect will stop subscribing to the previous timer if a new action comes
// in, and start a new timer
switchMap(() =>
switchMap(() => {
// Calculate the timeout: timeUntilIdle minus warningTimeBeforeIdle (if configured)
const warningTime = environment.auth.ui.warningTimeBeforeIdle || 0;
const timeout = Math.max(environment.auth.ui.timeUntilIdle - warningTime, 0);
// Start a timer outside of Angular's zone
timer(environment.auth.ui.timeUntilIdle, new LeaveZoneScheduler(this.zone, asyncScheduler))
),
return timer(timeout, new LeaveZoneScheduler(this.zone, asyncScheduler));
}),
// Re-enter the zone to dispatch the action
observeOn(new EnterZoneScheduler(this.zone, queueScheduler)),
map(() => new SetUserAsIdleAction()),
Expand Down
11 changes: 9 additions & 2 deletions src/app/shared/idle-modal/idle-modal.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { IdleModalComponent } from './idle-modal.component';
import { AuthService } from '../../core/auth/auth.service';
import { By } from '@angular/platform-browser';
import { Store } from '@ngrx/store';
import { LogOutAction } from '../../core/auth/auth.actions';
import { LogOutAction, RefreshTokenAction } from '../../core/auth/auth.actions';

describe('IdleModalComponent', () => {
let component: IdleModalComponent;
Expand All @@ -19,7 +19,8 @@ describe('IdleModalComponent', () => {

beforeEach(waitForAsync(() => {
modalStub = jasmine.createSpyObj('modalStub', ['close']);
authServiceStub = jasmine.createSpyObj('authService', ['setIdle']);
authServiceStub = jasmine.createSpyObj('authService', ['setIdle', 'getToken']);
authServiceStub.getToken.and.returnValue({ access_token: 'test-token' });
storeStub = jasmine.createSpyObj('store', ['dispatch']);
TestBed.configureTestingModule({
imports: [TranslateModule.forRoot()],
Expand Down Expand Up @@ -49,6 +50,9 @@ describe('IdleModalComponent', () => {
spyOn(component.response, 'emit');
component.extendSessionPressed();
}));
it('should dispatch RefreshTokenAction', () => {
expect(storeStub.dispatch).toHaveBeenCalledWith(jasmine.any(RefreshTokenAction));
});
it('should set idle to false', () => {
expect(authServiceStub.setIdle).toHaveBeenCalledWith(false);
});
Expand Down Expand Up @@ -77,6 +81,9 @@ describe('IdleModalComponent', () => {
spyOn(component.response, 'emit');
component.closePressed();
}));
it('should dispatch RefreshTokenAction', () => {
expect(storeStub.dispatch).toHaveBeenCalledWith(jasmine.any(RefreshTokenAction));
});
it('should set idle to false', () => {
expect(authServiceStub.setIdle).toHaveBeenCalledWith(false);
});
Expand Down
7 changes: 6 additions & 1 deletion src/app/shared/idle-modal/idle-modal.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { AuthService } from '../../core/auth/auth.service';
import { hasValue } from '../empty.util';
import { Store } from '@ngrx/store';
import { AppState } from '../../app.reducer';
import { LogOutAction } from '../../core/auth/auth.actions';
import { LogOutAction, RefreshTokenAction } from '../../core/auth/auth.actions';

@Component({
selector: 'ds-idle-modal',
Expand Down Expand Up @@ -74,6 +74,11 @@ export class IdleModalComponent implements OnInit {
if (hasValue(this.graceTimer)) {
clearTimeout(this.graceTimer);
}
// Refresh the token to extend session
const token = this.authService.getToken();
if (hasValue(token)) {
this.store.dispatch(new RefreshTokenAction(token));
}
this.authService.setIdle(false);
this.closeModal();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,33 +24,41 @@
</div>
</div>
<div class="ml-2 space-children-mr">
<button type="button"
class="btn btn-secondary"
id="save"
[attr.data-test]="'save' | dsBrowserOnly"
[dsBtnDisabled]="(processingSaveStatus | async) || !(hasUnsavedModification | async)"
(click)="save($event)">
<span><i class="fas fa-save"></i> {{'submission.general.save' | translate}}</span>
</button>
<button type="button"
[class.btn-primary]="!(showDepositAndDiscard | async)"
[class.btn-secondary]="(showDepositAndDiscard | async)"
class="btn"
id="saveForLater"
[attr.data-test]="'save-for-later' | dsBrowserOnly"
[dsBtnDisabled]="(processingSaveStatus | async) || (processingDepositStatus | async)"
(click)="saveLater($event)">
<span><i class="fas fa-save"></i> {{'submission.general.save-later' | translate}}</span>
</button>
<button *ngIf="(showDepositAndDiscard | async)"
type="button"
id="deposit"
[attr.data-test]="'deposit' | dsBrowserOnly"
class="btn btn-success"
[dsBtnDisabled]="(processingSaveStatus | async) || (processingDepositStatus | async)"
(click)="deposit($event)">
<span><i class="fas fa-plus"></i> {{'submission.general.deposit' | translate}}</span>
</button>
<!-- Save button: saves progress and continues editing -->
<span [ngbTooltip]="!(hasUnsavedModification | async) ? ('submission.buttons.save.disabled.tooltip' | translate) : ('submission.buttons.save.tooltip' | translate)">
<button type="button"
class="btn btn-secondary"
id="save"
[attr.data-test]="'save' | dsBrowserOnly"
[dsBtnDisabled]="(processingSaveStatus | async) || !(hasUnsavedModification | async)"
(click)="save($event)">
<span><i class="fas fa-save"></i> {{'submission.general.save' | translate}}</span>
</button>
</span>
<!-- Save & Exit button: saves progress and exits the form -->
<span [ngbTooltip]="'submission.buttons.save-exit.tooltip' | translate">
<button type="button"
[class.btn-primary]="!(showDepositAndDiscard | async)"
[class.btn-secondary]="(showDepositAndDiscard | async)"
class="btn"
id="saveAndExit"
[attr.data-test]="'save-and-exit' | dsBrowserOnly"
[dsBtnDisabled]="(processingSaveStatus | async) || (processingDepositStatus | async)"
(click)="saveLater($event)">
<span><i class="fas fa-door-open"></i> {{'submission.general.save-and-exit' | translate}}</span>
</button>
</span>
<!-- Submit for Review button: sends entry to editor for review -->
<span *ngIf="(showDepositAndDiscard | async)" [ngbTooltip]="'submission.buttons.submit.tooltip' | translate">
<button type="button"
id="deposit"
[attr.data-test]="'deposit' | dsBrowserOnly"
class="btn btn-success"
[dsBtnDisabled]="(processingSaveStatus | async) || (processingDepositStatus | async)"
(click)="deposit($event)">
<span><i class="fas fa-check"></i> {{'submission.general.submit-review' | translate}}</span>
</button>
</span>
</div>
</div>
</div>
Expand Down
26 changes: 22 additions & 4 deletions src/assets/i18n/cs.json5
Original file line number Diff line number Diff line change
Expand Up @@ -6372,6 +6372,24 @@
// "submission.general.save-later": "Save for later",
"submission.general.save-later": "Uložit na později",

// "submission.general.save-and-exit": "Save & Exit",
"submission.general.save-and-exit": "Uložit a Ukončit",

// "submission.general.submit-review": "Submit for Review",
"submission.general.submit-review": "Odeslat ke Kontrole",

// "submission.buttons.save.tooltip": "Save progress and continue editing",
"submission.buttons.save.tooltip": "Uložit průběh a pokračovat v úpravách",

// "submission.buttons.save.disabled.tooltip": "The autosave is active, there are no pending changes to save",
"submission.buttons.save.disabled.tooltip": "Automatické ukládání je aktivní, nejsou žádné neuložené změny",

// "submission.buttons.save-exit.tooltip": "Save progress and exit the form",
"submission.buttons.save-exit.tooltip": "Uložit průběh a ukončit formulář",

// "submission.buttons.submit.tooltip": "Submit to editor for review",
"submission.buttons.submit.tooltip": "Odeslat editorovi ke kontrole",

// "submission.import-external.page.title": "Import metadata from an external source",
"submission.import-external.page.title": "Importovat metadata z externího zdroje",

Expand Down Expand Up @@ -6972,8 +6990,8 @@
// "submission.sections.general.deposit_error_notice": "There was an issue when submitting the item, please try again later.",
"submission.sections.general.deposit_error_notice": "Při odesílání záznamu došlo k problému, zkuste to prosím později.",

// "submission.sections.general.deposit_success_notice": "Submission deposited successfully.",
"submission.sections.general.deposit_success_notice": "Příspěvek úspěšně uložen.",
// "submission.sections.general.deposit_success_notice": "Submission successful! Your entry has been sent to the editor for review and will be published after approval.",
"submission.sections.general.deposit_success_notice": "Odeslání úspěšné! Váš příspěvek byl odeslán editorovi ke kontrole a bude publikován po schválení.",

// "submission.sections.general.discard_error_notice": "There was an issue when discarding the item, please try again later.",
"submission.sections.general.discard_error_notice": "Při vyřazování záznamu došlo k problému, zkuste to prosím později.",
Expand Down Expand Up @@ -9600,8 +9618,8 @@
// "submission.sections.clarin-license.head.license-more-details": "See more details for the licenses",
"submission.sections.clarin-license.head.license-more-details": "Více podrobností o licencích",

// "submission.sections.clarin-license.head.license-do-not-suits-needs": "None of these licenses suits your needs",
"submission.sections.clarin-license.head.license-do-not-suits-needs": "Žádná z těchto licencí nevyhovuje vašim potřebám",
// "submission.sections.clarin-license.head.license-do-not-suits-needs": "If none of these licenses suit your needs:",
"submission.sections.clarin-license.head.license-do-not-suits-needs": "Pokud vám žádná z těchto licencí nevyhovuje:",

// "submission.sections.clarin-license.head.license-not-offer-proceeds": "If you need to use a license we currently do not offer, proceed as follows:",
"submission.sections.clarin-license.head.license-not-offer-proceeds": "Pokud potřebujete použít licenci, kterou v současné době nenabízíme, postupujte následovně:",
Expand Down
17 changes: 15 additions & 2 deletions src/assets/i18n/en.json5
Original file line number Diff line number Diff line change
Expand Up @@ -4249,6 +4249,19 @@

"submission.general.save-later": "Save for later",

"submission.general.save-and-exit": "Save & Exit",

"submission.general.submit-review": "Submit for Review",

// Button tooltips (Issue #2 - UX Improvement)
"submission.buttons.save.tooltip": "Save progress and continue editing",

"submission.buttons.save.disabled.tooltip": "The autosave is active, there are no pending changes to save",

"submission.buttons.save-exit.tooltip": "Save progress and exit the form",

"submission.buttons.submit.tooltip": "Submit to editor for review",

"submission.import-external.page.title": "Import metadata from an external source",

"submission.import-external.title": "Import metadata from an external source",
Expand Down Expand Up @@ -4649,7 +4662,7 @@

"submission.sections.general.deposit_error_notice": "There was an issue when submitting the item, please try again later.",

"submission.sections.general.deposit_success_notice": "Submission deposited successfully.",
"submission.sections.general.deposit_success_notice": "Submission successful! Your entry has been sent to the editor for review and will be published after approval.",

"submission.sections.general.discard_error_notice": "There was an issue when discarding the item, please try again later.",

Expand Down Expand Up @@ -6362,7 +6375,7 @@

"submission.sections.clarin-license.head.license-more-details": "See more details for the licenses",

"submission.sections.clarin-license.head.license-do-not-suits-needs": "None of these licenses suits your needs",
"submission.sections.clarin-license.head.license-do-not-suits-needs": "If none of these licenses suit your needs:",

"submission.sections.clarin-license.head.license-not-offer-proceeds": "If you need to use a license we currently do not offer, proceed as follows:",

Expand Down
2 changes: 2 additions & 0 deletions src/config/auth-config.interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export interface AuthConfig extends Config {
ui: {
// The amount of time before the idle warning is shown
timeUntilIdle: number;
// The amount of time before timeUntilIdle to show a warning
warningTimeBeforeIdle?: number;
// The amount of time the user has to react after the idle warning is shown before they are logged out.
idleGracePeriod: number;
};
Expand Down
2 changes: 2 additions & 0 deletions src/config/default-app-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ export class DefaultAppConfig implements AppConfig {
ui: {
// the amount of time before the idle warning is shown
timeUntilIdle: 15 * 60 * 1000, // 15 minutes
// the amount of time before timeUntilIdle to show a warning
warningTimeBeforeIdle: 1 * 60 * 1000, // 1 minute
// the amount of time the user has to react after the idle warning is shown before they are logged out.
idleGracePeriod: 5 * 60 * 1000 // 5 minutes
},
Expand Down