diff --git a/src/app/core/latest-changes/app-version/app-version.component.spec.ts b/src/app/core/latest-changes/app-version/app-version.component.spec.ts index 61313dc626..8ec8c6b84f 100644 --- a/src/app/core/latest-changes/app-version/app-version.component.spec.ts +++ b/src/app/core/latest-changes/app-version/app-version.component.spec.ts @@ -18,53 +18,31 @@ import { async, ComponentFixture, TestBed } from "@angular/core/testing"; import { AppVersionComponent } from "./app-version.component"; -import { MockSessionService } from "../../session/session-service/mock-session.service"; -import { EntityMapperService } from "../../entity/entity-mapper.service"; -import { LoginState } from "../../session/session-states/login-state.enum"; -import { SessionService } from "app/core/session/session-service/session.service"; import { MatDialogModule } from "@angular/material/dialog"; -import { LatestChangesService } from "../latest-changes.service"; import { NoopAnimationsModule } from "@angular/platform-browser/animations"; import { ChangelogComponent } from "../changelog/changelog.component"; -import { of } from "rxjs"; -import { EntitySchemaService } from "app/core/entity/schema/entity-schema.service"; +import { LatestChangesDialogService } from "../latest-changes-dialog.service"; describe("AppVersionComponent", () => { let component: AppVersionComponent; let fixture: ComponentFixture; - let latestChangesService: LatestChangesService; - let sessionService: MockSessionService; - let entitySchemaService: EntitySchemaService; - let entityMapper: EntityMapperService; + let latestChangesDialogService: jasmine.SpyObj; beforeEach(async(() => { - entitySchemaService = new EntitySchemaService(); - sessionService = new MockSessionService(entitySchemaService); - latestChangesService = new LatestChangesService(null, null, null, null); - entityMapper = new EntityMapperService(null, null); - - spyOn(latestChangesService, "getChangelogs").and.returnValue( - of([ - { - name: "test", - tag_name: "v1.0", - body: "latest test", - published_at: "2018-01-01", - }, - ]) - ); - spyOn(sessionService.getLoginState(), "getState").and.returnValue( - LoginState.LOGGED_IN - ); + latestChangesDialogService = jasmine.createSpyObj([ + "getCurrentVersion", + "showLatestChanges", + ]); TestBed.configureTestingModule({ declarations: [AppVersionComponent, ChangelogComponent], imports: [MatDialogModule, NoopAnimationsModule], providers: [ - { provide: SessionService, useValue: sessionService }, - { provide: EntityMapperService, useValue: entityMapper }, - { provide: LatestChangesService, useValue: latestChangesService }, + { + provide: LatestChangesDialogService, + useValue: latestChangesDialogService, + }, ], }); @@ -81,10 +59,17 @@ describe("AppVersionComponent", () => { expect(component).toBeTruthy(); }); - it("should open dialog", () => { - const spy = spyOn(latestChangesService, "showLatestChanges"); + it("should load currentVersion", () => { + const testVersion = "1.9.9"; + latestChangesDialogService.getCurrentVersion.and.returnValue(testVersion); + + component.ngOnInit(); + expect(component.currentVersion).toEqual(testVersion); + }); + + it("should open dialog", () => { component.showLatestChanges(); - expect(spy.calls.count()).toBe(1, "dialog not opened"); + expect(latestChangesDialogService.showLatestChanges).toHaveBeenCalled(); }); }); diff --git a/src/app/core/latest-changes/app-version/app-version.component.ts b/src/app/core/latest-changes/app-version/app-version.component.ts index 01facf976f..b47e85d28a 100644 --- a/src/app/core/latest-changes/app-version/app-version.component.ts +++ b/src/app/core/latest-changes/app-version/app-version.component.ts @@ -16,8 +16,7 @@ */ import { Component, OnInit } from "@angular/core"; -import { EntityMapperService } from "../../entity/entity-mapper.service"; -import { LatestChangesService } from "../latest-changes.service"; +import { LatestChangesDialogService } from "../latest-changes-dialog.service"; /** * Simple component displaying the current app version @@ -32,21 +31,16 @@ export class AppVersionComponent implements OnInit { /** the current app version */ currentVersion: string; - constructor( - private _entityMapperService: EntityMapperService, - private changelog: LatestChangesService - ) {} + constructor(private changelogDialog: LatestChangesDialogService) {} ngOnInit(): void { - this.changelog - .getCurrentVersion() - .subscribe((version) => (this.currentVersion = version)); + this.currentVersion = this.changelogDialog.getCurrentVersion(); } /** * Open dialog box to display changelog information about the latest version to the user. */ public showLatestChanges(): void { - this.changelog.showLatestChanges(); + this.changelogDialog.showLatestChanges(); } } diff --git a/src/app/core/latest-changes/changelog/changelog.component.html b/src/app/core/latest-changes/changelog/changelog.component.html index 160f25fdae..31da3a2f5a 100644 --- a/src/app/core/latest-changes/changelog/changelog.component.html +++ b/src/app/core/latest-changes/changelog/changelog.component.html @@ -16,16 +16,29 @@ --> -
-

System updated!

-

- {{ currentChangelog ? currentChangelog.tag_name : "No Changelog Available" }} - {{ (currentChangelog && currentChangelog.name) ? (": " + currentChangelog.name) : ""}} -

-

- {{ currentChangelog?.published_at | date }} -

-

+

Latest Changes

- -
+ + + + {{ changelog ? changelog.name : "No Changelog Available" }} + + {{ changelog?.published_at | date }} + {{ changelog?.tag_name }} + + + + + + + + + + + + + + + + + diff --git a/src/app/core/latest-changes/changelog/changelog.component.scss b/src/app/core/latest-changes/changelog/changelog.component.scss index 7fe9683204..ae279ebcf4 100644 --- a/src/app/core/latest-changes/changelog/changelog.component.scss +++ b/src/app/core/latest-changes/changelog/changelog.component.scss @@ -15,3 +15,23 @@ * along with ndb-core. If not, see . */ +.meta-info { + padding: 4px; + margin-right: 10px; +} + +.mat-card { + margin: 4px 4px 10px 4px; +} +.mat-card-subtitle { + margin-bottom: 0; +} + +a { + color: inherit; + text-decoration: inherit; +} + +.general-actions { + justify-content: flex-end; +} diff --git a/src/app/core/latest-changes/changelog/changelog.component.spec.ts b/src/app/core/latest-changes/changelog/changelog.component.spec.ts index b52dfce7ee..738bfc6c06 100644 --- a/src/app/core/latest-changes/changelog/changelog.component.spec.ts +++ b/src/app/core/latest-changes/changelog/changelog.component.spec.ts @@ -31,22 +31,26 @@ describe("ChangelogComponent", () => { let component: ChangelogComponent; let fixture: ComponentFixture; + let mockLatestChangesService: jasmine.SpyObj; + const testChangelog = new Changelog(); + testChangelog.tag_name = "1.0.0"; testChangelog.name = "test name"; testChangelog.body = "test changes body"; testChangelog.published_at = "2018-01-01"; beforeEach(async(() => { + mockLatestChangesService = jasmine.createSpyObj([ + "getChangelogsBeforeVersion", + ]); + TestBed.configureTestingModule({ declarations: [ChangelogComponent], imports: [MatDialogModule], providers: [ { provide: MatDialogRef, useValue: {} }, - { provide: MAT_DIALOG_DATA, useValue: {} }, - { - provide: LatestChangesService, - useValue: { getChangelogs: () => of([testChangelog]) }, - }, + { provide: MAT_DIALOG_DATA, useValue: of([testChangelog]) }, + { provide: LatestChangesService, useValue: mockLatestChangesService }, ], }).compileComponents(); })); @@ -59,5 +63,15 @@ describe("ChangelogComponent", () => { it("should be created", () => { expect(component).toBeTruthy(); + expect(component.changelogs).toEqual([testChangelog]); + }); + + it("should add release info to end of array on 'show previous'", () => { + mockLatestChangesService.getChangelogsBeforeVersion.and.returnValue( + of([testChangelog]) + ); + component.loadPreviousRelease(); + + expect(component.changelogs).toEqual([testChangelog, testChangelog]); }); }); diff --git a/src/app/core/latest-changes/changelog/changelog.component.ts b/src/app/core/latest-changes/changelog/changelog.component.ts index 60c1ebe75c..424da175d3 100644 --- a/src/app/core/latest-changes/changelog/changelog.component.ts +++ b/src/app/core/latest-changes/changelog/changelog.component.ts @@ -15,10 +15,17 @@ * along with ndb-core. If not, see . */ -import { Component, Inject, OnInit } from "@angular/core"; +import { + Component, + ElementRef, + Inject, + OnInit, + ViewChild, +} from "@angular/core"; import { Changelog } from "../changelog"; import { MAT_DIALOG_DATA, MatDialogRef } from "@angular/material/dialog"; import { isObservable, Observable } from "rxjs"; +import { LatestChangesService } from "../latest-changes.service"; /** * Display information from the changelog for the latest version. @@ -30,8 +37,13 @@ import { isObservable, Observable } from "rxjs"; styleUrls: ["./changelog.component.scss"], }) export class ChangelogComponent implements OnInit { - /** The changelog entry of the version to be displayed */ - currentChangelog: Changelog; + /** The array of relevant changelog entries to be displayed */ + changelogs: Changelog[]; + + /** Display advanced information that may not be useful to normal users */ + showAdvancedDetails = false; + + @ViewChild("changelogContainer") contentContainer: ElementRef; /** * This component is to be created through a MatDialog that should pass in the relevant data. @@ -40,25 +52,37 @@ export class ChangelogComponent implements OnInit { * dialog.open(ChangelogComponent, { data: { changelogData: latestChangesService.getChangelogs() } }); * * @param dialogRef Reference to the parent dialog. - * @param data Changelog data + * @param data Changelog data to be display initially + * @param latestChangesService */ constructor( public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: Observable + @Inject(MAT_DIALOG_DATA) public data: Observable, + private latestChangesService: LatestChangesService ) {} ngOnInit(): void { if (this.data && isObservable(this.data)) { - this.data.subscribe( - (changelog) => (this.currentChangelog = changelog[0]) - ); + this.data.subscribe((changelog) => (this.changelogs = changelog)); } } /** - * Close the parent dialog box. + * Add one more previous release card to the end of the currently displayed list of changelogs. */ - onCloseClick(): void { - this.dialogRef.close(); + loadPreviousRelease() { + const lastDisplayedVersion = this.changelogs[this.changelogs.length - 1] + .tag_name; + this.latestChangesService + .getChangelogsBeforeVersion(lastDisplayedVersion, 1) + .subscribe((additionalChangelog) => { + this.changelogs.push(...additionalChangelog); + + setTimeout(() => this.scrollToBottomOfReleases()); + }); + } + + private scrollToBottomOfReleases() { + this.contentContainer.nativeElement.scrollTop = this.contentContainer.nativeElement.scrollHeight; } } diff --git a/src/app/core/latest-changes/latest-changes-dialog.service.spec.ts b/src/app/core/latest-changes/latest-changes-dialog.service.spec.ts new file mode 100644 index 0000000000..90c12d43e0 --- /dev/null +++ b/src/app/core/latest-changes/latest-changes-dialog.service.spec.ts @@ -0,0 +1,89 @@ +/* + * This file is part of ndb-core. + * + * ndb-core is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ndb-core is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ndb-core. If not, see . + */ + +import { TestBed } from "@angular/core/testing"; + +import { LatestChangesService } from "./latest-changes.service"; +import { MatDialog } from "@angular/material/dialog"; +import { LatestChangesDialogService } from "./latest-changes-dialog.service"; +import { CookieService } from "ngx-cookie-service"; +import { environment } from "../../../environments/environment"; + +describe("LatestChangesDialogService", () => { + let service: LatestChangesDialogService; + let mockLatestChangesService: jasmine.SpyObj; + let mockCookieService: jasmine.SpyObj; + let mockDialog: jasmine.SpyObj; + + beforeEach(() => { + mockLatestChangesService = jasmine.createSpyObj([ + "getLatestChangesBeforeVersion", + "getChangelogsBetweenVersions", + ]); + mockCookieService = jasmine.createSpyObj("mockCookieService", [ + "check", + "get", + "set", + ]); + mockDialog = jasmine.createSpyObj("mockDialog", ["open"]); + + TestBed.configureTestingModule({ + providers: [ + LatestChangesDialogService, + { provide: LatestChangesService, useValue: mockLatestChangesService }, + { provide: CookieService, useValue: mockCookieService }, + { provide: MatDialog, useValue: mockDialog }, + ], + }); + + service = TestBed.inject( + LatestChangesDialogService + ); + }); + + it("should be created", () => { + expect(service).toBeTruthy(); + }); + + it("should not display changes on first visit (no cookie)", () => { + mockCookieService.check.and.returnValue(false); + + service.showLatestChangesIfUpdated(); + + expect(mockDialog.open).not.toHaveBeenCalled(); + expect(mockCookieService.set).toHaveBeenCalled(); + }); + + it("should display changes if cookie version differs", () => { + mockCookieService.check.and.returnValue(true); + mockCookieService.get.and.returnValue("1.0-test"); + + service.showLatestChangesIfUpdated(); + + expect(mockDialog.open).toHaveBeenCalled(); + expect(mockCookieService.set).toHaveBeenCalled(); + }); + + it("should not display changes if cookie version matches", () => { + mockCookieService.check.and.returnValue(true); + mockCookieService.get.and.returnValue(environment.appVersion); + + service.showLatestChangesIfUpdated(); + + expect(mockDialog.open).not.toHaveBeenCalled(); + }); +}); diff --git a/src/app/core/latest-changes/latest-changes-dialog.service.ts b/src/app/core/latest-changes/latest-changes-dialog.service.ts new file mode 100644 index 0000000000..f8340e8bab --- /dev/null +++ b/src/app/core/latest-changes/latest-changes-dialog.service.ts @@ -0,0 +1,77 @@ +/* + * This file is part of ndb-core. + * + * ndb-core is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ndb-core is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ndb-core. If not, see . + */ + +import { Injectable } from "@angular/core"; +import { MatDialog } from "@angular/material/dialog"; +import { ChangelogComponent } from "./changelog/changelog.component"; +import { CookieService } from "ngx-cookie-service"; +import { environment } from "../../../environments/environment"; +import { LatestChangesService } from "./latest-changes.service"; + +/** + * Manage the changelog information and display it to the user + * on request or automatically on the first visit of a new version after update. + */ +@Injectable() +export class LatestChangesDialogService { + private static COOKIE_NAME = "AppVersion"; + + constructor( + private dialog: MatDialog, + private cookieService: CookieService, + private latestChangesService: LatestChangesService + ) {} + + /** + * Get current app version inferred from the latest changelog entry. + */ + getCurrentVersion(): string { + return environment.appVersion; + } + + /** + * Open a modal window displaying the changelog of the latest version. + * @param previousVersion (Optional) previous version back to which all changes should be displayed + */ + public showLatestChanges(previousVersion?: string): void { + this.dialog.open(ChangelogComponent, { + width: "80%", + data: this.latestChangesService.getChangelogsBetweenVersions( + this.getCurrentVersion(), + previousVersion + ), + }); + } + + /** + * Display the latest changes info box automatically if the current user has not seen this version before. + */ + public showLatestChangesIfUpdated() { + if (this.cookieService.check(LatestChangesDialogService.COOKIE_NAME)) { + const previousVersion = this.cookieService.get( + LatestChangesDialogService.COOKIE_NAME + ); + if (this.getCurrentVersion() !== previousVersion) { + this.showLatestChanges(previousVersion); + } + } + this.cookieService.set( + LatestChangesDialogService.COOKIE_NAME, + this.getCurrentVersion() + ); + } +} diff --git a/src/app/core/latest-changes/latest-changes.module.ts b/src/app/core/latest-changes/latest-changes.module.ts index 3a66cfbb63..ff42cbaaff 100644 --- a/src/app/core/latest-changes/latest-changes.module.ts +++ b/src/app/core/latest-changes/latest-changes.module.ts @@ -20,7 +20,6 @@ import { CommonModule } from "@angular/common"; import { AppVersionComponent } from "./app-version/app-version.component"; import { AlertsModule } from "../alerts/alerts.module"; import { HttpClientModule } from "@angular/common/http"; -import { LatestChangesService } from "./latest-changes.service"; import { SessionModule } from "../session/session.module"; import { MatButtonModule } from "@angular/material/button"; import { MatDialogModule } from "@angular/material/dialog"; @@ -28,6 +27,12 @@ import { MatSnackBarModule } from "@angular/material/snack-bar"; import { ChangelogComponent } from "./changelog/changelog.component"; import { SwUpdate } from "@angular/service-worker"; import { UpdateManagerService } from "./update-manager.service"; +import { FlexModule } from "@angular/flex-layout"; +import { MarkdownModule } from "ngx-markdown"; +import { MatIconModule } from "@angular/material/icon"; +import { MatCardModule } from "@angular/material/card"; +import { LatestChangesDialogService } from "./latest-changes-dialog.service"; +import { LatestChangesService } from "./latest-changes.service"; /** * Displaying app version and changelog information to the user @@ -43,18 +48,26 @@ import { UpdateManagerService } from "./update-manager.service"; MatButtonModule, MatSnackBarModule, HttpClientModule, + FlexModule, + MarkdownModule, + MatIconModule, + MatCardModule, ], declarations: [AppVersionComponent, ChangelogComponent], exports: [AppVersionComponent], - providers: [LatestChangesService, UpdateManagerService], + providers: [ + LatestChangesService, + LatestChangesDialogService, + UpdateManagerService, + ], }) export class LatestChangesModule { constructor( private updates: SwUpdate, - private latestChangesService: LatestChangesService, + private latestChangesDialogService: LatestChangesDialogService, private updateManagerService: UpdateManagerService ) { - this.latestChangesService.showLatestChangesIfUpdated(); + this.latestChangesDialogService.showLatestChangesIfUpdated(); this.updateManagerService.notifyUserWhenUpdateAvailable(); this.updateManagerService.regularlyCheckForUpdates(); diff --git a/src/app/core/latest-changes/latest-changes.service.spec.ts b/src/app/core/latest-changes/latest-changes.service.spec.ts index 0a0fefeb5c..559e063f8a 100644 --- a/src/app/core/latest-changes/latest-changes.service.spec.ts +++ b/src/app/core/latest-changes/latest-changes.service.spec.ts @@ -21,8 +21,6 @@ import { LatestChangesService } from "./latest-changes.service"; import { AlertService } from "../alerts/alert.service"; import { HttpClient } from "@angular/common/http"; import { of, throwError } from "rxjs"; -import { MatDialogModule } from "@angular/material/dialog"; -import { CookieService } from "ngx-cookie-service"; describe("LatestChangesService", () => { let service: LatestChangesService; @@ -30,15 +28,34 @@ describe("LatestChangesService", () => { let alertService: AlertService; let http: HttpClient; + const testReleases = [ + { + name: "test 2", + tag_name: "2.0", + body: "C", + published_at: "2018-01-01", + }, + { + name: "test 1b", + tag_name: "1.1", + body: "B", + published_at: "2018-01-01", + }, + { + name: "test 1", + tag_name: "1.0", + body: "A", + published_at: "2018-01-01", + }, + ]; + beforeEach(() => { alertService = new AlertService(null, null); http = new HttpClient(null); TestBed.configureTestingModule({ - imports: [MatDialogModule], providers: [ LatestChangesService, - CookieService, { provide: AlertService, useValue: alertService }, { provide: HttpClient, useValue: http }, ], @@ -51,20 +68,43 @@ describe("LatestChangesService", () => { expect(service).toBeTruthy(); }); - it("should return changelog array", (done) => { - spyOn(http, "get").and.returnValue( - of([ - { - name: "test", - tag_name: "v1.0", - body: "latest test", - published_at: "2018-01-01", - }, - ]) - ); - service.getChangelogs().subscribe((result) => { + it("should return changelog of current version", (done) => { + spyOn(http, "get").and.returnValue(of(testReleases)); + + service.getChangelogsBetweenVersions("1.1").subscribe((result) => { expect(result.length).toBe(1); - expect(result[0].name).toBe("test"); + expect(result[0].name).toBe(testReleases[1].name); + done(); + }); + }); + + it("should return changelogs between versions excluding previous version", (done) => { + spyOn(http, "get").and.returnValue(of(testReleases)); + + service.getChangelogsBetweenVersions("2.0", "1.0").subscribe((result) => { + expect(result.length).toBe(2); + expect(result[0].name).toBe(testReleases[0].name); + expect(result[1].name).toBe(testReleases[1].name); + done(); + }); + }); + + it("should return changelogs before version", (done) => { + spyOn(http, "get").and.returnValue(of(testReleases)); + + service.getChangelogsBeforeVersion("2.0", 3).subscribe((result) => { + expect(result.length).toBe(2); // cannot return more results than available at api + expect(result[0].name).toBe(testReleases[1].name); + expect(result[1].name).toBe(testReleases[2].name); + done(); + }); + }); + + it("should return empty array if no releases from api", (done) => { + spyOn(http, "get").and.returnValue(of([])); + + service.getChangelogsBetweenVersions("1.0").subscribe((result) => { + expect(result.length).toBe(0); done(); }); }); @@ -74,7 +114,7 @@ describe("LatestChangesService", () => { throwError({ status: 404, message: "not found" }) ); const alertSpy = spyOn(alertService, "addAlert"); - service.getChangelogs().subscribe( + service.getChangelogsBetweenVersions("1.0").subscribe( () => {}, (err) => { expect(alertSpy.calls.count()).toBe(1, "no Alert message created"); diff --git a/src/app/core/latest-changes/latest-changes.service.ts b/src/app/core/latest-changes/latest-changes.service.ts index e641eaee64..3099fff217 100644 --- a/src/app/core/latest-changes/latest-changes.service.ts +++ b/src/app/core/latest-changes/latest-changes.service.ts @@ -21,9 +21,7 @@ import { Observable, throwError } from "rxjs"; import { Changelog } from "./changelog"; import { AlertService } from "../alerts/alert.service"; import { HttpClient } from "@angular/common/http"; -import { MatDialog } from "@angular/material/dialog"; -import { ChangelogComponent } from "./changelog/changelog.component"; -import { CookieService } from "ngx-cookie-service"; +import { environment } from "../../../environments/environment"; /** * Manage the changelog information and display it to the user @@ -31,59 +29,124 @@ import { CookieService } from "ngx-cookie-service"; */ @Injectable() export class LatestChangesService { - private static COOKIE_NAME = "AppVersion"; + private static GITHUB_API = "https://api.github.com/repos/"; - constructor( - private http: HttpClient, - private alertService: AlertService, - private dialog: MatDialog, - private cookieService: CookieService - ) {} + constructor(private http: HttpClient, private alertService: AlertService) {} /** - * Load the changelog document. + * Load the changelog information of changes since the last update. + * @param currentVersion The current version for which changes are to be loaded. + * @param previousVersion (Optional) The older version since which changes of all versions until the currentVersion will be loaded. */ - getChangelogs(): Observable { - return this.http.get("assets/changelog.json").pipe( - map((response) => response), - catchError((error) => { - this.alertService.addWarning("Could not load latest changes: " + error); - return throwError("Could not load latest changes."); - }) + getChangelogsBetweenVersions( + currentVersion: string, + previousVersion?: string + ): Observable { + return this.getChangelogs((releases: any[]) => + this.filterReleasesBetween(releases, currentVersion, previousVersion) ); } - /** - * Get current app version inferred from the latest changelog entry. - */ - getCurrentVersion(): Observable { - return this.getChangelogs().pipe(map((changelog) => changelog[0].tag_name)); + private filterReleasesBetween( + releases: any[], + currentVersion: string, + previousVersion?: string + ) { + let relevantReleases; + + const releasesUpToCurrentVersion = releases.filter( + (r) => r.tag_name <= currentVersion + ); + if (releasesUpToCurrentVersion.length < 1) { + return []; + } + + if (previousVersion) { + const releasesBackToPreviousVersion = releasesUpToCurrentVersion.filter( + (r) => r.tag_name > previousVersion + ); + relevantReleases = releasesBackToPreviousVersion.sort((a, b) => + (b.tag_name as string).localeCompare(a.tag_name, "en") + ); + } else { + relevantReleases = [releasesUpToCurrentVersion[0]]; + } + + return relevantReleases; } /** - * Open a modal window displaying the changelog of the latest version. + * Load the changelog information of a number of releases before (excluding) the given version. + * @param version The version for which preceding releases should be returned. + * @param count The number of releases before the given version to be returned */ - public showLatestChanges(): void { - this.dialog.open(ChangelogComponent, { - width: "400px", - data: this.getChangelogs(), - }); + getChangelogsBeforeVersion( + version: string, + count: number + ): Observable { + return this.getChangelogs((releases: any) => + this.filterReleasesBefore(releases, version, count) + ); + } + + private filterReleasesBefore( + releases: any[], + version: string, + count: number + ) { + let relevantReleases; + + const releasesUpToCurrentVersion = releases.filter( + (r) => r.tag_name < version + ); + if (releasesUpToCurrentVersion.length < 1) { + return []; + } + + relevantReleases = releasesUpToCurrentVersion.sort((a, b) => + (b.tag_name as string).localeCompare(a.tag_name, "en") + ); + return relevantReleases.slice(0, count); } /** - * Display the latest changes info box automatically if the current user has not seen this version before. + * Load release information from GitHub based on a given filter to select relevant releases. + * @param releaseFilter Filter function that is selecting relevant objects from the array of GitHub releases */ - public showLatestChangesIfUpdated() { - this.getCurrentVersion().subscribe((currentVersion) => { - if (this.cookieService.check(LatestChangesService.COOKIE_NAME)) { - const previousVersion = this.cookieService.get( - LatestChangesService.COOKIE_NAME - ); - if (currentVersion !== previousVersion) { - this.showLatestChanges(); - } - } - this.cookieService.set(LatestChangesService.COOKIE_NAME, currentVersion); - }); + private getChangelogs(releaseFilter: ([]) => any[]): Observable { + return this.http + .get( + LatestChangesService.GITHUB_API + environment.repositoryId + "/releases" + ) + .pipe( + map(releaseFilter), + map((relevantReleases) => + relevantReleases.map((r) => this.parseGithubApiRelease(r)) + ), + catchError((error) => { + this.alertService.addWarning( + "Could not load latest changes: " + error + ); + return throwError("Could not load latest changes."); + }) + ); + } + + private parseGithubApiRelease(githubResponse: any): Changelog { + const releaseNotesWithoutHeading = githubResponse.body.replace( + /##[^###]*/, + "" + ); + const releaseNotesWithoutCommitRefs = releaseNotesWithoutHeading.replace( + / \(\[\w{7}\]\([^\)]*\)\)/g, + "" + ); + + return { + tag_name: githubResponse.tag_name, + name: githubResponse.name, + published_at: githubResponse.published_at, + body: releaseNotesWithoutCommitRefs, + }; } } diff --git a/src/assets/changelog.json b/src/assets/changelog.json deleted file mode 100644 index 3562f5e5c0..0000000000 --- a/src/assets/changelog.json +++ /dev/null @@ -1,247 +0,0 @@ -[ - { - "tag_name": "v2.13.0", - "name": "Meeting Attendance, School Results & Logo", - "published_at": "2020-05-20", - "body": "- Children in Notes can now be marked as present/absent for meetings\n- School History of Child can save overall grade results\n- New logo\n- Fixes for offline function and dashboard", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.12.0", - "name": "Dashboard for Recent Notes", - "published_at": "2020-04-27", - "body": "- Dashboard Widget for counter of kids with recent notes\n- Dashboard Widget for kids without any recent notes\n- show only notes of current week for faster loading", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.11.0", - "name": "Improvements to UI", - "published_at": "2020-04-22", - "body": "- fix search problems\n- improve views on different screen sizes\n- add search for mobile view", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.10.0", - "name": "Roll Call for class", - "published_at": "2020-03-15", - "body": "- filter students for roll call by class", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.9.0", - "name": "Bug Fixes & Export", - "published_at": "2020-02-16", - "body": "- fix problems with attendance roll call\n- allow export/download of children as document\n- display students in school details\n- fix display of children in notes", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.8.1", - "name": "Improved Error Logging", - "published_at": "2019-12-05", - "body": "- improved reporting of technical errors to the development team", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.8.0", - "name": "Internal Technical Improvements", - "published_at": "2019-11-30", - "body": "- reworked login, session and sync processes", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.7.4", - "name": "UI Improvements", - "published_at": "2019-11-30", - "body": "- show newly added notes in the list\n- improve Note form\n- fix empty filter buttons\n- dashboard widgets are more dense", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.7.3", - "name": "Bugfixes", - "published_at": "2019-11-26", - "body": "- sort children in daily attendance roll call by class", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.7.2", - "name": "Bugfixes", - "published_at": "2019-11-11", - "body": "- ensure datatype of 'center'", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.7.1", - "name": "Bugfixes", - "published_at": "2019-10-23", - "body": "- fixed problems with saving attendance", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.7.0", - "name": "Improved Technical Foundation", - "published_at": "2019-10-06", - "body": "- improved code base to provide extendability and test data\n- added BMI calculation to child health table\n- fixed dashboard progess widget not deleting parts", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.6.2", - "name": "Bugfixes", - "published_at": "2019-07-09", - "body": "- fixed problems with child school relations, also affecting attendance roll call\n- fixed slow loading of children lists", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.6.0", - "name": "Better usability on phones + Height & Weight Tracking", - "published_at": "2019-06-17", - "body": "- support for use on smaller screens (smartphones)\n- NEW: basic height & weight tracking for children (see 'Health' section)\n- various bug fixes", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.5.3", - "name": "New fields: blood group and interaction types", - "published_at": "2019-03-11", - "body": "- added field 'Blood Group' to Child\n-added interaction types for Note: 'Talk with Neighbours', 'Contact with other partners (club/NGO/...)'", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.5.2", - "name": "Automatic Update Notifications available now", - "published_at": "2019-02-16", - "body": "- when a new update is available on the server, a notification message is displayed to activate the update\n-fixed Updated - Latest Changes popup to only be displayed once after a new version is loaded\n- fixed symbols not getting displayed when offline\n- fixed errors in related components after a child is deleted\n- updated related libraries", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.5.1", - "name": "Multiple problems fixed", - "published_at": "2019-01-07", - "body": "- allow letters in school class\n- added Back Button to child and school details pages to go to previous page with previous filters\n- fixed Progress dashboard widget not deleting rows", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.5.0", - "name": "Attendance Manager & School Details", - "published_at": "2018-12-14", - "body": "- Attendance Register shows & summarizes attendance of lists of students\n- Schools list & details pages show & edit various details\n- New schools can be added from schools list page\n-Improvements to view the app on smaller screens", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.4.5", - "name": "Bug fixes: Saving Daily Attendance", - "published_at": "2018-10-12", - "body": "- fixed problems of changes (Daily Attendance) not getting saved\n- fixed problem showing non-existing children (database prefix bug)\n- improved errors and logs in case of database problems\n- avoid 'image not found' errors in console", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.4.4", - "name": "Bug fixes", - "published_at": "2018-09-13", - "body": "- fixed multiple formating and loading issues", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.4.3", - "name": "Current week's absences", - "published_at": "2018-09-07", - "body": "- new dashboard widget for current week's absences", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.4.2", - "name": "Bug fixes and improvements for Daily Attendance", - "published_at": "2018-09-05", - "body": "- only active children are shown during roll call\n- loading indicator", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.4.1", - "name": "Bug fixes and simplification for Daily Attendance", - "published_at": "2018-09-04", - "body": "- fixed bug creating duplicate entries for some month when adding daily attendance\n- simplified editing of daily attendance in the calendar view", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.4.0", - "name": "Daily Attendance & Live Sync", - "published_at": "2018-09-01", - "body": "- new major feature: manage daily attendance\n- database is continuously syncing with server now (live sync)\n- technical logging information is uploaded for development team in the background", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.3.4", - "name": "Minor fixes", - "published_at": "2018-08-10", - "body": "- fixed broken \"Add Child\" link\n- allow changing of school in Child Details\n- fixed broken sort of some columns in Children List", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.3.3", - "name": "Fixed 'Add Attendance' not saving", - "published_at": "2018-08-02", - "body": "- fixed saving attendance through 'Add Attendance' page", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.3.2", - "name": "Improved 'Add Attendance'", - "published_at": "2018-08-02", - "body": "- improved performance when adding attendance in bulk", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.3.1", - "name": "Hotfixes", - "published_at": "2018-08-01", - "body": "- several small, urgent bug fixes\n- added additional fields to Child", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.3.0", - "name": "Attendance, Notes & Coaching Managers", - "published_at": "2018-07-28", - "body": "- New Feature: Add monthly attendance data for a whole school/coaching batch easily\n- New Feature: Manager for Notes/Reports to be filtered by different criteria\n- Preview: Coaching Planner to easily see learning status of all children and rearrange groups\n- photos of individual children can be displayed\n- Back button can be used to return to previously filtered children list\n- various small improvements and fixes", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.2.0", - "name": "Extended Interface & Data", - "published_at": "2018-07-11", - "body": "- added Health section to Child\n- separate coaching and school attendance data for Child\n- added Materials section to Child\n- added Education & ASER Results section to Child\n- added Progress dashboard widget", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.1.4", - "name": "Improvements & Bug Fixes", - "published_at": "2018-06-29", - "body": "- added quick action button (bottom left)\n- several minor bug fixes and improvements", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.1.3", - "name": "Bug Fixes", - "published_at": "2018-06-28", - "body": "- fixed Dashboard children count\n- fixed search results not opening from details page\n- fixed some problems with attendance and reports\n- improved child details layout\n- fixed update notifications", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.1.2", - "name": "Editing Children", - "published_at": "2018-06-18", - "body": "Editing and Creating children is now possible", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.1.1", - "name": "Bug Fixes", - "published_at": "2018-06-14", - "body": "Several small bug fixes", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - }, - { - "tag_name": "v2.1.0", - "name": "Pilot Release", - "published_at": "2018-06-12", - "body": "Pilot Release: HELGO\nbasic features of handling child information, notes and attendances", - "html_url": "https://github.com/NGO-DB/ndb-core/releases/" - } -] diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index 951ae19f61..ae9ae04e0d 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -26,4 +26,5 @@ export const environment = { production: true, appVersion: "2.13.2", // replaced automatically by semantic-release + repositoryId: "Aam-Digital/ndb-core", }; diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 036e1f5133..d0b7a17a70 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -26,4 +26,5 @@ export const environment = { production: false, appVersion: "2.13.2", // replaced automatically by semantic-release + repositoryId: "Aam-Digital/ndb-core", };