diff --git a/frontend/app/src/app/app.component.ts b/frontend/app/src/app/app.component.ts index 09df9a16..ebaf9295 100644 --- a/frontend/app/src/app/app.component.ts +++ b/frontend/app/src/app/app.component.ts @@ -17,6 +17,10 @@ import {Router} from '@angular/router'; import {App, URLOpenListenerEvent} from '@capacitor/app'; import {Platform, ToastController} from '@ionic/angular/standalone'; import {SettingsProvider} from './modules/settings/settings.provider'; +import { + increaseSessionCount, + startInappRatingIfFeasible, +} from './modules/settings/inapp-rating/inapp-rating.provider.js'; import {AuthHelperService} from './modules/auth/auth-helper.service'; import {environment} from '../environments/environment'; import {Capacitor} from '@capacitor/core'; @@ -24,6 +28,7 @@ import {ScheduleSyncService} from './modules/background/schedule/schedule-sync.s import {Keyboard, KeyboardResize} from '@capacitor/keyboard'; import {AppVersionService} from './modules/about/app-version.service'; import {SplashScreen} from '@capacitor/splash-screen'; +import {StorageProvider} from './modules/storage/storage.provider.js'; /** * TODO @@ -55,6 +60,7 @@ export class AppComponent implements AfterContentInit { constructor( private readonly platform: Platform, private readonly settingsProvider: SettingsProvider, + private readonly storageProvider: StorageProvider, private readonly router: Router, private readonly zone: NgZone, private readonly authHelper: AuthHelperService, @@ -86,6 +92,8 @@ export class AppComponent implements AfterContentInit { async hideSplash() { if (Capacitor.isNativePlatform()) { void SplashScreen.hide(); + void increaseSessionCount(this.storageProvider); + void startInappRatingIfFeasible(this.storageProvider); } } diff --git a/frontend/app/src/app/modules/settings/inapp-rating/inapp-rating.provider.ts b/frontend/app/src/app/modules/settings/inapp-rating/inapp-rating.provider.ts new file mode 100644 index 00000000..25802e57 --- /dev/null +++ b/frontend/app/src/app/modules/settings/inapp-rating/inapp-rating.provider.ts @@ -0,0 +1,106 @@ +import {InAppReview} from '@capacitor-community/in-app-review'; +import {StorageProvider} from '../../storage/storage.provider'; + +export const INAPP_RATING_COOLDOWN_DAYS = 365; +export const INAPP_RATING_NECESSARY_SESSIONS = 2; + +export const INAPP_RATING_SETTINGS_KEY = 'inapp-rating'; +export const INAPP_RATING_SESSIONS_KEY = 'sessions'; +export const INAPP_RATING_LAST_RATING_KEY = 'last-rating'; +export type INAPP_RATING_KEYS = typeof INAPP_RATING_SESSIONS_KEY | typeof INAPP_RATING_LAST_RATING_KEY; + +/** + * + */ +function inappRatingSettingStorageKey(key: string): string { + return `${INAPP_RATING_SETTINGS_KEY}.${key}`; +} + +/** + * + */ +async function getInappRatingSetting( + storageProvider: StorageProvider, + key: INAPP_RATING_KEYS, + defaultValue: T, +): Promise { + try { + return await storageProvider.get(inappRatingSettingStorageKey(key)); + } catch { + return defaultValue; + } +} + +/** + * + */ +async function putInappRatingSetting(storageProvider: StorageProvider, key: INAPP_RATING_KEYS, value: T) { + return storageProvider.put(inappRatingSettingStorageKey(key), value); +} + +/** + * + */ +async function putInappRatingSessions(storageProvider: StorageProvider, value: number) { + return putInappRatingSetting(storageProvider, INAPP_RATING_SESSIONS_KEY, value); +} + +/** + * + */ +async function getInappRatingSessions(storageProvider: StorageProvider): Promise { + return getInappRatingSetting(storageProvider, INAPP_RATING_SESSIONS_KEY, 0); +} + +/** + * + */ +async function putInappRatingLastRating(storageProvider: StorageProvider, value: Date) { + return putInappRatingSetting(storageProvider, INAPP_RATING_SESSIONS_KEY, value.getTime()); +} + +/** + * + */ +async function getInappRatingLastRating(storageProvider: StorageProvider): Promise { + return getInappRatingSetting(storageProvider, INAPP_RATING_SESSIONS_KEY, 0).then(timestamp => { + return new Date(timestamp); + }); +} + +/** + * + */ +export async function increaseSessionCount(storageProvider: StorageProvider, increment = 1): Promise { + try { + const currentSessions = await getInappRatingSessions(storageProvider); + await putInappRatingSessions(storageProvider, currentSessions + increment); + return currentSessions + increment; + } catch { + return -1; + } +} + +/** + * + */ +export async function startInappRatingIfFeasible(storageProvider: StorageProvider): Promise { + try { + const currentSessions = await getInappRatingSessions(storageProvider); + const lastRating = await getInappRatingLastRating(storageProvider); + const dateDiffMillis = Math.abs(lastRating.getTime() - Date.now()); + const diffDays = Math.ceil(dateDiffMillis / (1000 * 3600 * 24)); + + if (currentSessions < INAPP_RATING_NECESSARY_SESSIONS || diffDays < INAPP_RATING_COOLDOWN_DAYS) { + return false; + } + + await InAppReview.requestReview(); + + await putInappRatingLastRating(storageProvider, new Date()); + + return true; + } catch { + return false; + } +} diff --git a/frontend/app/src/app/modules/storage/storage.provider.ts b/frontend/app/src/app/modules/storage/storage.provider.ts index 324886b1..c5a19a37 100644 --- a/frontend/app/src/app/modules/storage/storage.provider.ts +++ b/frontend/app/src/app/modules/storage/storage.provider.ts @@ -120,8 +120,9 @@ export class StorageProvider { * Puts a value of type T into the storage using provided key * @param key Unique identifier * @param value Resource to store under the key + * @returns Returns a promise that resolves when the key and value are set */ - async put(key: string, value: T): Promise { + async put(key: string, value: T) { return this.storage.set(key, value); }