diff --git a/apps/picsa-apps/extension-app-native/android/.idea/.gitignore b/apps/picsa-apps/extension-app-native/android/.idea/.gitignore deleted file mode 100644 index 26d33521a..000000000 --- a/apps/picsa-apps/extension-app-native/android/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/apps/picsa-apps/extension-app-native/android/.idea/compiler.xml b/apps/picsa-apps/extension-app-native/android/.idea/compiler.xml deleted file mode 100644 index b589d56e9..000000000 --- a/apps/picsa-apps/extension-app-native/android/.idea/compiler.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/apps/picsa-apps/extension-app-native/android/.idea/jarRepositories.xml b/apps/picsa-apps/extension-app-native/android/.idea/jarRepositories.xml deleted file mode 100644 index de647e0db..000000000 --- a/apps/picsa-apps/extension-app-native/android/.idea/jarRepositories.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/apps/picsa-apps/extension-app-native/android/.idea/misc.xml b/apps/picsa-apps/extension-app-native/android/.idea/misc.xml deleted file mode 100644 index d64a7f74c..000000000 --- a/apps/picsa-apps/extension-app-native/android/.idea/misc.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/apps/picsa-apps/extension-app-native/android/app/capacitor.build.gradle b/apps/picsa-apps/extension-app-native/android/app/capacitor.build.gradle index 26842e92c..b673116e3 100644 --- a/apps/picsa-apps/extension-app-native/android/app/capacitor.build.gradle +++ b/apps/picsa-apps/extension-app-native/android/app/capacitor.build.gradle @@ -22,6 +22,7 @@ dependencies { implementation project(':capacitor-screen-orientation') implementation project(':capacitor-camera') implementation project(':capacitor-share') + implementation project(':capacitor-push-notifications') implementation "androidx.webkit:webkit:1.4.0" implementation "androidx.legacy:legacy-support-v4:1.0.0" } diff --git a/apps/picsa-apps/extension-app-native/android/capacitor.settings.gradle b/apps/picsa-apps/extension-app-native/android/capacitor.settings.gradle index 9a8d4b1b4..f79333c67 100644 --- a/apps/picsa-apps/extension-app-native/android/capacitor.settings.gradle +++ b/apps/picsa-apps/extension-app-native/android/capacitor.settings.gradle @@ -40,3 +40,6 @@ project(':capacitor-camera').projectDir = new File('../../../../node_modules/@ca include ':capacitor-share' project(':capacitor-share').projectDir = new File('../../../../node_modules/@capacitor/share/android') + +include ':capacitor-push-notifications' +project(':capacitor-push-notifications').projectDir = new File('../../../../node_modules/@capacitor/push-notifications/android') diff --git a/apps/picsa-apps/extension-app-native/android/gradle/wrapper/gradle-wrapper.properties b/apps/picsa-apps/extension-app-native/android/gradle/wrapper/gradle-wrapper.properties index d951fac2b..18330fcba 100644 --- a/apps/picsa-apps/extension-app-native/android/gradle/wrapper/gradle-wrapper.properties +++ b/apps/picsa-apps/extension-app-native/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/apps/picsa-apps/extension-app-native/capacitor.config.ts b/apps/picsa-apps/extension-app-native/capacitor.config.ts index fd7f436cc..30fdd4268 100644 --- a/apps/picsa-apps/extension-app-native/capacitor.config.ts +++ b/apps/picsa-apps/extension-app-native/capacitor.config.ts @@ -1,3 +1,4 @@ +/// import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { @@ -31,7 +32,13 @@ const config: CapacitorConfig = { '@capacitor/screen-orientation', '@capacitor/camera', "@capacitor/share", + '@capacitor/push-notifications', ], + plugins:{ + PushNotifications: { + presentationOptions: ["alert"], + }, + }, // Enable app to use native http for requests (bypass cors) // https://capacitorjs.com/docs/apis/http // TODO - check if resources still work as intended once enabled @@ -49,6 +56,7 @@ const config: CapacitorConfig = { */ cleartext: true, }, + }; /** diff --git a/apps/picsa-apps/extension-app/src/app/app.component.ts b/apps/picsa-apps/extension-app/src/app/app.component.ts index 451b971e7..73233a3d3 100644 --- a/apps/picsa-apps/extension-app/src/app/app.component.ts +++ b/apps/picsa-apps/extension-app/src/app/app.component.ts @@ -1,5 +1,5 @@ /* eslint-disable @nx/enforce-module-boundaries */ -import { Component, Injector, OnInit, signal } from '@angular/core'; +import { AfterViewInit, Component, Injector, OnInit, signal } from '@angular/core'; import { Router } from '@angular/router'; import { ENVIRONMENT } from '@picsa/environments'; import { PicsaMigrationService } from '@picsa/migrations'; @@ -8,6 +8,7 @@ import { ResourcesToolService } from '@picsa/resources/src/app/services/resource import { AnalyticsService } from '@picsa/shared/services/core/analytics.service'; import { CrashlyticsService } from '@picsa/shared/services/core/crashlytics.service'; import { PerformanceService } from '@picsa/shared/services/core/performance.service'; +import { PicsaPushNotificationService } from '@picsa/shared/services/core/push-notifications.service'; @Component({ selector: 'picsa-root', @@ -15,7 +16,7 @@ import { PerformanceService } from '@picsa/shared/services/core/performance.serv styleUrls: ['./app.component.scss'], standalone: false, }) -export class AppComponent implements OnInit { +export class AppComponent implements OnInit, AfterViewInit { title = 'extension-toolkit'; public ready = signal(false); public showLoader = signal(false); @@ -28,21 +29,29 @@ export class AppComponent implements OnInit { private resourcesService: ResourcesToolService, private monitoringService: MonitoringToolService, private migrationService: PicsaMigrationService, + private pushNotificationService: PicsaPushNotificationService, private injector: Injector ) {} async ngOnInit() { + // wait for migrations to run + await this.runMigrations(); + + this.ready.set(true); + } + async ngAfterViewInit() { this.performanceService.setEnabled({ enabled: ENVIRONMENT.production }); - this.crashlyticsService.ready().then(() => null); + this.crashlyticsService.ready(); // eagerly enable analytics collection this.analyticsService.init(this.router); - // wait for migrations to run - await this.runMigrations(); // eagerly load resources service to populate hardcoded resources this.resourcesService.ready(); // eagerly load monitoring service to sync form data this.monitoringService.ready(); - this.ready.set(true); + // delay push notification as will prompt for permissions + setTimeout(() => { + this.pushNotificationService.initializePushNotifications(); + }, 1000); } private async runMigrations() { diff --git a/libs/shared/src/services/core/push-notifications.service.ts b/libs/shared/src/services/core/push-notifications.service.ts new file mode 100644 index 000000000..6546d9cbe --- /dev/null +++ b/libs/shared/src/services/core/push-notifications.service.ts @@ -0,0 +1,72 @@ +import { Injectable } from '@angular/core'; +import { ActionPerformed, PushNotifications, PushNotificationSchema, Token } from '@capacitor/push-notifications'; + +@Injectable({ + providedIn: 'root', +}) +export class PicsaPushNotificationService { + //constructor() {} + + public async initializePushNotifications() { + try { + // Check if permission is already granted + const permResult = await PushNotifications.checkPermissions(); + + if (permResult.receive === 'prompt' || permResult.receive === 'prompt-with-rationale') { + // Request permissions + const reqResult = await PushNotifications.requestPermissions(); + if (reqResult.receive !== 'granted') { + console.error('Push notification permission was denied'); + return; + } + } + // Register with Apple / Google to receive push via FCM + await PushNotifications.register(); + + // Remove any existing listeners to prevent duplicates + await PushNotifications.removeAllListeners(); + + // Add listeners + PushNotifications.addListener('registration', (token: Token) => { + console.log('Push registration success'); + // in case we have logic to save and update device tokens + //this.sendTokenToServer(token.value); + }); + + PushNotifications.addListener('registrationError', (error: any) => { + // handle error logic + console.error('Error on registration:', error); + }); + + PushNotifications.addListener('pushNotificationReceived', (notification: PushNotificationSchema) => { + console.log('Push received'); + // Handle foreground notification if required + this.handleForegroundNotification(notification); + }); + + PushNotifications.addListener('pushNotificationActionPerformed', (notification: ActionPerformed) => { + console.log('Push action performed'); + // Handle notification click + this.handleNotificationClick(notification); + }); + } catch (err) { + console.error('Error initializing push notifications:', err); + } + } + + private async sendTokenToServer(token: string) { + //TODO: Implement sending token to your backend if required + //guidence on where we can save this + } + + private handleForegroundNotification(notification: PushNotificationSchema) { + // Implement custom foreground notification handling + } + + private handleNotificationClick(actionPerformed: ActionPerformed) { + // Implement navigation or other actions when notification is clicked + const notification = actionPerformed.notification; + // in the case notification has extra infromations like a route to navigate to + // this.router.navigate([notification.data.route]); + } +} diff --git a/package.json b/package.json index b9e16c8ef..c624d7ac0 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,7 @@ "@capacitor/filesystem": "^6.0.2", "@capacitor/geolocation": "^6.1.0", "@capacitor/network": "^6.0.3", + "@capacitor/push-notifications": "^6.0.4", "@capacitor/screen-orientation": "^6.0.3", "@capacitor/share": "^6.0.3", "@ngx-translate/core": "~16.0.4", diff --git a/yarn.lock b/yarn.lock index d1a6670c3..e17a948ac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2299,6 +2299,15 @@ __metadata: languageName: node linkType: hard +"@capacitor/push-notifications@npm:^6.0.4": + version: 6.0.4 + resolution: "@capacitor/push-notifications@npm:6.0.4" + peerDependencies: + "@capacitor/core": ^6.0.0 + checksum: 10c0/c16315ebebfc0ec15f171f0330aeced3b851761f96522fefaca79c16b260ab9606198e021a3370b419308307efedec522af3dc5be4bfeec34d103836e79086b9 + languageName: node + linkType: hard + "@capacitor/screen-orientation@npm:^6.0.3": version: 6.0.3 resolution: "@capacitor/screen-orientation@npm:6.0.3" @@ -19013,6 +19022,7 @@ __metadata: "@capacitor/filesystem": "npm:^6.0.2" "@capacitor/geolocation": "npm:^6.1.0" "@capacitor/network": "npm:^6.0.3" + "@capacitor/push-notifications": "npm:^6.0.4" "@capacitor/screen-orientation": "npm:^6.0.3" "@capacitor/share": "npm:^6.0.3" "@ngx-translate/core": "npm:~16.0.4"