feat: app release notes

This commit is contained in:
Thea Schöbl
2023-12-01 12:34:20 +00:00
parent 5d47a17629
commit 37945f7d19
18 changed files with 335 additions and 7 deletions

View File

@@ -17,7 +17,7 @@
import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {Platform} from '@ionic/angular';
import {ModalController, Platform} from '@ionic/angular';
import {TranslateService} from '@ngx-translate/core';
import {ThingTranslateService} from './translation/thing-translate.service';
@@ -45,6 +45,7 @@ describe('AppComponent', () => {
let platformIsSpy;
let storageProvider: jasmine.SpyObj<StorageProvider>;
let simpleBrowser: jasmine.SpyObj<SimpleBrowser>;
let modalController: jasmine.SpyObj<ModalController>;
beforeEach(() => {
platformReadySpy = Promise.resolve();
@@ -71,6 +72,7 @@ describe('AppComponent', () => {
ngxLogger = jasmine.createSpyObj('NGXLogger', ['log', 'error', 'warn']);
storageProvider = jasmine.createSpyObj('StorageProvider', ['init', 'get', 'has', 'put']);
simpleBrowser = jasmine.createSpyObj('SimpleBrowser', ['open']);
modalController = jasmine.createSpyObj('ModalController', ['create', 'dismiss', 'getTop']);
TestBed.configureTestingModule({
imports: [RouterTestingModule.withRoutes([]), HttpClientTestingModule, AuthModule],
@@ -85,6 +87,7 @@ describe('AppComponent', () => {
{provide: NGXLogger, useValue: ngxLogger},
{provide: StorageProvider, useValue: storageProvider},
{provide: SimpleBrowser, useValue: simpleBrowser},
{provide: ModalController, useValue: modalController},
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
}).compileComponents();

View File

@@ -22,6 +22,7 @@ import {environment} from '../environments/environment';
import {Capacitor} from '@capacitor/core';
import {ScheduleSyncService} from './modules/background/schedule/schedule-sync.service';
import {Keyboard, KeyboardResize} from '@capacitor/keyboard';
import {AppVersionService} from './modules/about/app-version.service';
import {SplashScreen} from '@capacitor/splash-screen';
/**
@@ -59,13 +60,19 @@ export class AppComponent implements AfterContentInit {
private readonly authHelper: AuthHelperService,
private readonly toastController: ToastController,
private readonly scheduleSyncService: ScheduleSyncService,
private readonly versionService: AppVersionService,
) {
void this.initializeApp();
}
ngAfterContentInit() {
async ngAfterContentInit() {
this.scheduleSyncService.init();
void this.scheduleSyncService.enable();
this.versionService.getPendingReleaseNotes().then(notes => {
if (notes) {
this.versionService.presentReleaseNotes(notes);
}
});
if (document.readyState === 'complete') {
requestIdleCallback(this.hideSplash.bind(this));

View File

@@ -18,6 +18,8 @@ import {SCAboutPage, SCAppConfiguration} from '@openstapps/core';
import {ConfigProvider} from '../../config/config.provider';
import packageJson from '../../../../../package.json';
import config from 'capacitor.config';
import {App} from '@capacitor/app';
import {Capacitor} from '@capacitor/core';
@Component({
selector: 'about-page',
@@ -27,9 +29,11 @@ import config from 'capacitor.config';
export class AboutPageComponent implements OnInit {
content: SCAboutPage;
appName = config.appName;
name = config.appName;
version = packageJson.version;
stappsVersion = packageJson.version;
version: string;
constructor(private readonly route: ActivatedRoute, private readonly configProvider: ConfigProvider) {}
@@ -37,5 +41,6 @@ export class AboutPageComponent implements OnInit {
const route = this.route.snapshot.url.map(it => it.path).join('/');
this.content =
(this.configProvider.getValue('aboutPages') as SCAppConfiguration['aboutPages'])[route] ?? {};
this.version = Capacitor.getPlatform() === 'web' ? 'Web' : await App.getInfo().then(info => info.version);
}
}

View File

@@ -25,7 +25,7 @@
</ion-toolbar>
</ion-header>
<ion-content parallax *ngIf="content">
<ion-text>{{ appName }} v{{ version }}</ion-text>
<ion-text>{{ 'about.VERSION_INFO' | translate: {name, version, stappsVersion} }}</ion-text>
<div class="page-content">
<about-page-content *ngFor="let element of content.content" [content]="element"></about-page-content>
</div>

View File

@@ -0,0 +1,60 @@
import {Injectable} from '@angular/core';
import {StorageProvider} from '../storage/storage.provider';
import {ConfigProvider} from '../config/config.provider';
import {ModalController} from '@ionic/angular';
import {Capacitor} from '@capacitor/core';
import {ReleaseNotesComponent} from './release-notes.component';
import {SCAppVersionInfo} from '@openstapps/core';
import {App} from '@capacitor/app';
export const RELEASE_NOTES_SHOWN_KEY = 'release_notes_shown';
@Injectable({providedIn: 'root'})
export class AppVersionService {
constructor(
private storage: StorageProvider,
private config: ConfigProvider,
private modalController: ModalController,
) {}
/**
* Get the release notes of the latest published version
*/
get publishedVersions() {
const platform = Capacitor.getPlatform() as 'android' | 'ios' | 'web';
return this.config.config.app.versionHistory?.filter(({published}) => published[platform] !== undefined);
}
/**
* Get the latest release notes that have not been presented yet
*/
async getPendingReleaseNotes() {
if (Capacitor.getPlatform() === 'web') {
return;
}
const storedVersion = (await this.storage.has(RELEASE_NOTES_SHOWN_KEY))
? await this.storage.get<string>(RELEASE_NOTES_SHOWN_KEY)
: '';
const currentVersion = await App.getInfo().then(info => info.version);
return this.publishedVersions?.find(({version}) => {
const wasNotShown = version.localeCompare(storedVersion, undefined, {numeric: true}) === 1;
const isNotFutureVersion = version.localeCompare(currentVersion, undefined, {numeric: true}) <= 0;
return wasNotShown && isNotFutureVersion;
});
}
/**
* Present release notes
*/
async presentReleaseNotes(version: SCAppVersionInfo) {
const modal = await this.modalController.create({
component: ReleaseNotesComponent,
componentProps: {
versionInfo: version,
},
});
await modal.present();
await modal.onDidDismiss();
await this.storage.put(RELEASE_NOTES_SHOWN_KEY, version.version);
}
}

View File

@@ -0,0 +1,21 @@
import {ChangeDetectionStrategy, Component, Input} from '@angular/core';
import {SCAppVersionInfo} from '@openstapps/core';
import {MarkdownModule} from 'ngx-markdown';
import {ThingTranslateModule} from '../../translation/thing-translate.module';
import {IonicModule, ModalController} from '@ionic/angular';
import {TranslateModule} from '@ngx-translate/core';
import {UtilModule} from '../../util/util.module';
@Component({
selector: 'stapps-release-notes',
templateUrl: 'release-notes.html',
styleUrls: ['release-notes.scss'],
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [UtilModule, MarkdownModule, ThingTranslateModule, IonicModule, TranslateModule],
})
export class ReleaseNotesComponent {
@Input() versionInfo: SCAppVersionInfo;
constructor(readonly modalController: ModalController) {}
}

View File

@@ -0,0 +1,16 @@
<ion-header>
<ion-toolbar>
<ion-title><span class="ion-text-wrap">{{'releaseNotes.TITLE_UPDATED' | translate}}</span></ion-title>
<ion-buttons slot="end">
<ion-button [strong]="true" (click)="modalController.dismiss()"
>{{'modal.DISMISS_NEUTRAL' | translate}}</ion-button
>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content parallax>
<markdown
class="content-card ion-padding"
[data]="'releaseNotes' | translateSimple: versionInfo"
></markdown>
</ion-content>

View File

@@ -0,0 +1,13 @@
@import '../../../theme/util/mixins';
ion-title {
padding-block: var(--spacing-md);
}
.content-card {
@include border-radius-in-parallax(var(--border-radius-default));
display: block;
margin: var(--spacing-md);
background: var(--ion-item-background);
}

View File

@@ -21,7 +21,6 @@ import {DaytimeKeyPipe} from './daytime-key.pipe';
import {LazyPipe} from './lazy.pipe';
import {NextDateInListPipe} from './next-date-in-list.pipe';
import {EditModalComponent} from './edit-modal.component';
import {BrowserModule} from '@angular/platform-browser';
import {IonicModule} from '@ionic/angular';
import {TranslateModule} from '@ngx-translate/core';
import {ElementSizeChangeDirective} from './element-size-change.directive';
@@ -33,10 +32,11 @@ import {SectionComponent} from './section.component';
import {RouterModule} from '@angular/router';
import {IonContentParallaxDirective} from './ion-content-parallax.directive';
import {FormatDistanceToNowStrictPipeModule, FormatRelativeToNowPipeModule} from 'ngx-date-fns';
import {CommonModule} from '@angular/common';
@NgModule({
imports: [
BrowserModule,
CommonModule,
IonicModule,
TranslateModule,
ThingTranslateModule.forChild(),

View File

@@ -28,6 +28,12 @@
"toast": {
"TITLE_COPIED": "In die Zwischenablage kopiert"
},
"about": {
"VERSION_INFO": "{{name}} {{version}} basierend auf StApps {{stappsVersion}}"
},
"releaseNotes": {
"TITLE_UPDATED": "Deine App wurde aktualisiert!"
},
"app": {
"ui": {
"CLOSE": "Schließen",

View File

@@ -28,6 +28,12 @@
"toast": {
"TITLE_COPIED": "Copied to clipboard"
},
"about": {
"VERSION_INFO": "{{name}} {{version}} based on StApps {{stappsVersion}}"
},
"releaseNotes": {
"TITLE_UPDATED": "Your app was updated!"
},
"app": {
"ui": {
"CLOSE": "Close",