@unvired/react-native-unvired-sdk 0.0.11 → 0.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BuildNo.txt +1 -1
- package/README.md +884 -255
- package/dist/PlatformAdapter.d.ts +57 -0
- package/dist/PlatformAdapter.js +116 -0
- package/dist/database/Database.d.ts +71 -0
- package/dist/database/Database.js +156 -0
- package/dist/database/DatabaseManager.d.ts +32 -0
- package/dist/database/DatabaseManager.js +323 -0
- package/dist/database/index.d.ts +6 -0
- package/dist/database/index.js +5 -0
- package/dist/database/types.d.ts +94 -0
- package/dist/database/types.js +4 -0
- package/dist/device-info/BaseDeviceInfo.d.ts +39 -0
- package/dist/device-info/BaseDeviceInfo.js +116 -0
- package/dist/device-info/DeviceInfo.d.ts +9 -0
- package/dist/device-info/DeviceInfo.js +11 -0
- package/dist/device-info/DeviceInfo.types.d.ts +11 -0
- package/dist/device-info/DeviceInfo.types.js +3 -0
- package/dist/device-info/index.d.ts +3 -0
- package/dist/device-info/index.js +3 -0
- package/dist/file-system/BaseFileSystem.d.ts +60 -0
- package/dist/file-system/BaseFileSystem.js +194 -0
- package/dist/file-system/FileSystem.d.ts +9 -0
- package/dist/file-system/FileSystem.js +11 -0
- package/dist/file-system/FileSystem.types.d.ts +11 -0
- package/dist/file-system/FileSystem.types.js +3 -0
- package/dist/file-system/index.d.ts +3 -0
- package/dist/file-system/index.js +3 -0
- package/dist/local-storage/index.d.ts +1 -0
- package/dist/local-storage/index.js +2 -0
- package/dist/local-storage/localStorage.d.ts +48 -0
- package/dist/local-storage/localStorage.js +123 -0
- package/dist/logger/BaseLogger.js +8 -13
- package/dist/logger/{Logger.js → index.js} +1 -4
- package/dist/logger/services/LoggerNative.js +5 -5
- package/dist/logger/services/LoggerWindows.d.ts +20 -0
- package/dist/logger/services/LoggerWindows.js +225 -0
- package/dist/main.d.ts +13 -0
- package/dist/main.js +28 -0
- package/dist/push-notification/BasePushNotification.d.ts +52 -0
- package/dist/push-notification/BasePushNotification.js +180 -0
- package/dist/push-notification/PushNotification.d.ts +9 -0
- package/dist/push-notification/PushNotification.js +11 -0
- package/dist/push-notification/PushNotification.types.d.ts +12 -0
- package/dist/push-notification/PushNotification.types.js +3 -0
- package/dist/push-notification/index.d.ts +3 -0
- package/dist/push-notification/index.js +3 -0
- package/example/USAGE_EXAMPLE.ts +167 -0
- package/package.json +17 -5
- package/dist/index.d.ts +0 -3
- package/dist/index.js +0 -4
- /package/dist/logger/{Logger.d.ts → index.d.ts} +0 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import * as pako from 'pako';
|
|
2
|
+
// Import with null safety
|
|
3
|
+
let RNFS = null;
|
|
4
|
+
try {
|
|
5
|
+
RNFS = require('@dr.pogodin/react-native-fs');
|
|
6
|
+
if (!RNFS || !RNFS.DocumentDirectoryPath) {
|
|
7
|
+
console.error('❌ @dr.pogodin/react-native-fs is not properly linked.');
|
|
8
|
+
RNFS = null;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
catch (e) {
|
|
12
|
+
console.error('❌ @dr.pogodin/react-native-fs module not found.');
|
|
13
|
+
}
|
|
14
|
+
export class LoggerWindows {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.isRotating = false; // Rotation lock to prevent concurrent rotations
|
|
17
|
+
this.isAvailable = false;
|
|
18
|
+
this.writeQueue = [];
|
|
19
|
+
this.isWriting = false;
|
|
20
|
+
const dir = 'unvired_logs';
|
|
21
|
+
const file = 'applog.txt'; // main log file
|
|
22
|
+
if (!RNFS) {
|
|
23
|
+
console.warn('⚠️ LoggerWindows: RNFS not available, logging to file system disabled');
|
|
24
|
+
this.logDir = '';
|
|
25
|
+
this.logFile = '';
|
|
26
|
+
this.backupZip = '';
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
this.isAvailable = true;
|
|
30
|
+
// Windows specifically uses DocumentDirectoryPath (AppData/Local/Packages/.../LocalState)
|
|
31
|
+
this.logDir = `${RNFS.DocumentDirectoryPath}/${dir}`;
|
|
32
|
+
this.logFile = `${this.logDir}/${file}`;
|
|
33
|
+
this.backupZip = `${this.logDir}/backuplog.zip`;
|
|
34
|
+
}
|
|
35
|
+
async init() {
|
|
36
|
+
if (!this.isAvailable || !this.logDir || !RNFS)
|
|
37
|
+
return;
|
|
38
|
+
try {
|
|
39
|
+
if (!(await RNFS.exists(this.logDir)))
|
|
40
|
+
await RNFS.mkdir(this.logDir);
|
|
41
|
+
if (!(await RNFS.exists(this.logFile)))
|
|
42
|
+
await RNFS.writeFile(this.logFile, '', 'utf8');
|
|
43
|
+
}
|
|
44
|
+
catch (e) {
|
|
45
|
+
console.error('❌ LoggerWindows init error:', e);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async writeLine(line, maxSize) {
|
|
49
|
+
if (!this.isAvailable || !RNFS)
|
|
50
|
+
return;
|
|
51
|
+
// Push to queue
|
|
52
|
+
this.writeQueue.push(line);
|
|
53
|
+
// Trigger processing if not already running
|
|
54
|
+
this.processWriteQueue(maxSize);
|
|
55
|
+
}
|
|
56
|
+
async processWriteQueue(maxSize) {
|
|
57
|
+
if (this.isWriting)
|
|
58
|
+
return;
|
|
59
|
+
this.isWriting = true;
|
|
60
|
+
try {
|
|
61
|
+
await this.ensureDir();
|
|
62
|
+
while (this.writeQueue.length > 0) {
|
|
63
|
+
// Peek at the chunk of lines to write?
|
|
64
|
+
// Writing one by one is safe but slow.
|
|
65
|
+
// Let's write one by one for safety first.
|
|
66
|
+
const line = this.writeQueue.shift();
|
|
67
|
+
if (!line)
|
|
68
|
+
continue;
|
|
69
|
+
try {
|
|
70
|
+
await RNFS.appendFile(this.logFile, line + '\n', 'utf8');
|
|
71
|
+
// Check rotation (only occasionally to avoid stat spam?)
|
|
72
|
+
// We can check every N writes or if queue is empty
|
|
73
|
+
if (!this.isRotating && this.writeQueue.length === 0) {
|
|
74
|
+
try {
|
|
75
|
+
const stat = await RNFS.stat(this.logFile);
|
|
76
|
+
if (Number(stat.size) >= maxSize) {
|
|
77
|
+
this.rotateLog();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (statErr) {
|
|
81
|
+
// ignore
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch (filesErr) {
|
|
86
|
+
// If file is locked, we might want to put line back?
|
|
87
|
+
// or just retry?
|
|
88
|
+
// For now, retry once then drop to avoid infinite loop
|
|
89
|
+
console.warn('⚠️ Write failed, retrying once:', filesErr.message);
|
|
90
|
+
try {
|
|
91
|
+
await new Promise(r => setTimeout(r, 50)); // small delay
|
|
92
|
+
await RNFS.appendFile(this.logFile, line + '\n', 'utf8');
|
|
93
|
+
}
|
|
94
|
+
catch (retryErr) {
|
|
95
|
+
console.error('❌ Failed to write log line after retry:', retryErr);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch (e) {
|
|
101
|
+
console.error('❌ LoggerWindows queue error:', e);
|
|
102
|
+
}
|
|
103
|
+
finally {
|
|
104
|
+
this.isWriting = false;
|
|
105
|
+
// If logs came in while we were finishing, trigger again
|
|
106
|
+
if (this.writeQueue.length > 0) {
|
|
107
|
+
this.processWriteQueue(maxSize);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async ensureDir() {
|
|
112
|
+
if (!this.isAvailable || !this.logDir || !RNFS)
|
|
113
|
+
return;
|
|
114
|
+
try {
|
|
115
|
+
if (!(await RNFS.exists(this.logDir)))
|
|
116
|
+
await RNFS.mkdir(this.logDir);
|
|
117
|
+
}
|
|
118
|
+
catch (e) {
|
|
119
|
+
// Ignore if dir already exists race condition
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
async rotateLog() {
|
|
123
|
+
if (!this.isAvailable || !RNFS) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
// Prevent concurrent rotations
|
|
127
|
+
if (this.isRotating) {
|
|
128
|
+
console.log('⏭️ Rotation already in progress, skipping...');
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
this.isRotating = true;
|
|
132
|
+
try {
|
|
133
|
+
console.log('🔄 Starting log rotation (using pako gzip)...');
|
|
134
|
+
// Check if log file exists
|
|
135
|
+
if (!(await RNFS.exists(this.logFile))) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
// Step 1: Read current log content
|
|
139
|
+
// Reading whole file into memory might be heavy for huge files,
|
|
140
|
+
// but for mobile logs generally acceptable (e.g. 5-10MB limit)
|
|
141
|
+
const content = await RNFS.readFile(this.logFile, 'utf8');
|
|
142
|
+
// Step 2: Compress content using Pako (gzip)
|
|
143
|
+
const binaryData = pako.gzip(content);
|
|
144
|
+
// Step 3: Convert binary to base64 for writing using Buffer (more robust)
|
|
145
|
+
// React Native environment usually has Buffer polyfill, or we use a chunked approach
|
|
146
|
+
const base64 = Buffer.from(binaryData).toString('base64');
|
|
147
|
+
// Step 4: Write to backup file (overwriting old one)
|
|
148
|
+
// We rename extensions to .gz to be accurate, or keep .zip if requirement is strict
|
|
149
|
+
// (but technically it IS a gzip stream, not a PKZip archive)
|
|
150
|
+
// User asked for base64 return, so storing it purely is fine.
|
|
151
|
+
await RNFS.writeFile(this.backupZip, base64, 'base64');
|
|
152
|
+
console.log('✅ Backup created successfully');
|
|
153
|
+
// Step 5: Clear current log
|
|
154
|
+
await RNFS.writeFile(this.logFile, '', 'utf8');
|
|
155
|
+
console.log('✔ Log rotation completed successfully');
|
|
156
|
+
}
|
|
157
|
+
catch (e) {
|
|
158
|
+
console.error('❌ Logger rotation error:', e);
|
|
159
|
+
// Recovery: ensure log file exists
|
|
160
|
+
try {
|
|
161
|
+
if (!(await RNFS.exists(this.logFile))) {
|
|
162
|
+
await RNFS.writeFile(this.logFile, '', 'utf8');
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch { }
|
|
166
|
+
}
|
|
167
|
+
finally {
|
|
168
|
+
this.isRotating = false;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// ------------------------
|
|
172
|
+
// Public accessors
|
|
173
|
+
// ------------------------
|
|
174
|
+
async getLogFileURL() {
|
|
175
|
+
if (!this.isAvailable)
|
|
176
|
+
return '';
|
|
177
|
+
return this.logFile;
|
|
178
|
+
}
|
|
179
|
+
async getLogFileContent() {
|
|
180
|
+
if (!this.isAvailable || !RNFS)
|
|
181
|
+
return '';
|
|
182
|
+
try {
|
|
183
|
+
if (await RNFS.exists(this.logFile)) {
|
|
184
|
+
return await RNFS.readFile(this.logFile, 'utf8');
|
|
185
|
+
}
|
|
186
|
+
return '';
|
|
187
|
+
}
|
|
188
|
+
catch (e) {
|
|
189
|
+
return '';
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
async getBackupLogFileContent() {
|
|
193
|
+
if (!this.isAvailable || !RNFS)
|
|
194
|
+
return null;
|
|
195
|
+
try {
|
|
196
|
+
// Do NOT force rotation. Only return existing backup if present.
|
|
197
|
+
// await this.rotateLog();
|
|
198
|
+
if (await RNFS.exists(this.backupZip)) {
|
|
199
|
+
// It's already written as base64 in rotateLog step via writeFile('base64'),
|
|
200
|
+
// but readFile('base64') reads the file content and returns base64.
|
|
201
|
+
// Wait, writeFile(path, data, 'base64') expects data to BE base64.
|
|
202
|
+
// readFile(path, 'base64') reads binary and RETURNS base64.
|
|
203
|
+
// Yes, so we can just read it back.
|
|
204
|
+
return await RNFS.readFile(this.backupZip, 'base64');
|
|
205
|
+
}
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
catch (e) {
|
|
209
|
+
console.error('❌ LoggerWindows getBackupLogFileContent error:', e);
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
async clear() {
|
|
214
|
+
if (!this.isAvailable || !RNFS)
|
|
215
|
+
return;
|
|
216
|
+
try {
|
|
217
|
+
await RNFS.writeFile(this.logFile, '', 'utf8');
|
|
218
|
+
if (await RNFS.exists(this.backupZip))
|
|
219
|
+
await RNFS.unlink(this.backupZip);
|
|
220
|
+
}
|
|
221
|
+
catch (e) {
|
|
222
|
+
// silent
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
package/dist/main.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { Logger, logger } from './logger';
|
|
2
|
+
export type { LogLevel, ILogger, ILoggerImplementation } from './logger';
|
|
3
|
+
export { LocalStorage } from './local-storage';
|
|
4
|
+
export { DatabaseManager, Database, createDatabase } from './database';
|
|
5
|
+
export type { IDatabaseAdapter, DatabaseOptions, ExecuteOptions, SaveWebDBOptions, ExportWebDBOptions, DeleteUserDataOptions, SuccessCallback, ErrorCallback, ResultCallback, SQLResultSet, SQLTransaction, SQLiteDatabase } from './database';
|
|
6
|
+
export { DeviceInfo } from './device-info';
|
|
7
|
+
export type { IDeviceInfo } from './device-info';
|
|
8
|
+
export { FileSystem } from './file-system';
|
|
9
|
+
export type { IFileEntry } from './file-system';
|
|
10
|
+
export { PushNotification } from './push-notification';
|
|
11
|
+
export type { IPushNotificationAdapter } from './push-notification';
|
|
12
|
+
export { ReactNativePlatformAdapter, getPlatformAdapter, resetPlatformAdapter, DatabaseType } from './PlatformAdapter';
|
|
13
|
+
export type { IPlatformAdapter, ILoggerAdapter, IStorageAdapter } from './PlatformAdapter';
|
package/dist/main.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// ============================================
|
|
2
|
+
// Logger Module
|
|
3
|
+
// ============================================
|
|
4
|
+
export { Logger, logger } from './logger';
|
|
5
|
+
// ============================================
|
|
6
|
+
// Local Storage Module
|
|
7
|
+
// ============================================
|
|
8
|
+
export { LocalStorage } from './local-storage';
|
|
9
|
+
// ============================================
|
|
10
|
+
// Database Module
|
|
11
|
+
// ============================================
|
|
12
|
+
export { DatabaseManager, Database, createDatabase } from './database';
|
|
13
|
+
// ============================================
|
|
14
|
+
// Device Info Module
|
|
15
|
+
// ============================================
|
|
16
|
+
export { DeviceInfo } from './device-info';
|
|
17
|
+
// ============================================
|
|
18
|
+
// File System Module
|
|
19
|
+
// ============================================
|
|
20
|
+
export { FileSystem } from './file-system';
|
|
21
|
+
// ============================================
|
|
22
|
+
// Push Notification Module
|
|
23
|
+
// ============================================
|
|
24
|
+
export { PushNotification } from './push-notification';
|
|
25
|
+
// ============================================
|
|
26
|
+
// Platform Adapter
|
|
27
|
+
// ============================================
|
|
28
|
+
export { ReactNativePlatformAdapter, getPlatformAdapter, resetPlatformAdapter, DatabaseType } from './PlatformAdapter';
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { IPushNotificationAdapter } from './PushNotification.types';
|
|
2
|
+
/**
|
|
3
|
+
* Base Push Notification Class
|
|
4
|
+
* Handles push notifications using Firebase Cloud Messaging
|
|
5
|
+
*/
|
|
6
|
+
export declare class BasePushNotification implements IPushNotificationAdapter {
|
|
7
|
+
private tokenRefreshCallback;
|
|
8
|
+
private messageCallback;
|
|
9
|
+
private backgroundMessageCallback;
|
|
10
|
+
/**
|
|
11
|
+
* Request push notification permission
|
|
12
|
+
*/
|
|
13
|
+
requestPermission(options?: {
|
|
14
|
+
forceShow?: boolean;
|
|
15
|
+
}): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Get FCM token
|
|
18
|
+
*/
|
|
19
|
+
getToken(): Promise<string>;
|
|
20
|
+
/**
|
|
21
|
+
* Listen for token refresh
|
|
22
|
+
*/
|
|
23
|
+
onTokenRefresh(callback: (token: string) => void): void;
|
|
24
|
+
/**
|
|
25
|
+
* Listen for foreground messages
|
|
26
|
+
*/
|
|
27
|
+
onMessage(callback: (message: any) => void): void;
|
|
28
|
+
/**
|
|
29
|
+
* Listen for background messages
|
|
30
|
+
*/
|
|
31
|
+
onBackgroundMessage(callback: (message: any) => void): void;
|
|
32
|
+
/**
|
|
33
|
+
* Check if device supports push notifications
|
|
34
|
+
*/
|
|
35
|
+
static isSupported(): Promise<boolean>;
|
|
36
|
+
/**
|
|
37
|
+
* Delete FCM token
|
|
38
|
+
*/
|
|
39
|
+
deleteToken(): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Get initial notification (app opened from notification)
|
|
42
|
+
*/
|
|
43
|
+
getInitialNotification(): Promise<any | null>;
|
|
44
|
+
/**
|
|
45
|
+
* Subscribe to topic
|
|
46
|
+
*/
|
|
47
|
+
subscribeToTopic(topic: string): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Unsubscribe from topic
|
|
50
|
+
*/
|
|
51
|
+
unsubscribeFromTopic(topic: string): Promise<void>;
|
|
52
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
// BasePushNotification.ts
|
|
2
|
+
// Base push notification implementation
|
|
3
|
+
let messaging;
|
|
4
|
+
let FirebaseMessagingTypes;
|
|
5
|
+
try {
|
|
6
|
+
const firebaseMessaging = require('@react-native-firebase/messaging');
|
|
7
|
+
messaging = firebaseMessaging.default;
|
|
8
|
+
FirebaseMessagingTypes = firebaseMessaging.FirebaseMessagingTypes;
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
console.warn('Firebase messaging not installed. Push notifications will not work.');
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Base Push Notification Class
|
|
15
|
+
* Handles push notifications using Firebase Cloud Messaging
|
|
16
|
+
*/
|
|
17
|
+
export class BasePushNotification {
|
|
18
|
+
constructor() {
|
|
19
|
+
this.tokenRefreshCallback = null;
|
|
20
|
+
this.messageCallback = null;
|
|
21
|
+
this.backgroundMessageCallback = null;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Request push notification permission
|
|
25
|
+
*/
|
|
26
|
+
async requestPermission(options = {}) {
|
|
27
|
+
if (!messaging) {
|
|
28
|
+
throw new Error('Firebase messaging is not installed');
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
const authStatus = await messaging().requestPermission();
|
|
32
|
+
const enabled = authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
|
|
33
|
+
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
|
|
34
|
+
if (enabled) {
|
|
35
|
+
console.log('Push notification permission granted:', authStatus);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
console.log('Push notification permission denied');
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
console.error('Error requesting push notification permission:', error);
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get FCM token
|
|
48
|
+
*/
|
|
49
|
+
async getToken() {
|
|
50
|
+
if (!messaging) {
|
|
51
|
+
throw new Error('Firebase messaging is not installed');
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
const token = await messaging().getToken();
|
|
55
|
+
console.log('FCM Token:', token);
|
|
56
|
+
return token;
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
console.error('Error getting FCM token:', error);
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Listen for token refresh
|
|
65
|
+
*/
|
|
66
|
+
onTokenRefresh(callback) {
|
|
67
|
+
if (!messaging)
|
|
68
|
+
return;
|
|
69
|
+
this.tokenRefreshCallback = callback;
|
|
70
|
+
messaging().onTokenRefresh((token) => {
|
|
71
|
+
console.log('FCM Token refreshed:', token);
|
|
72
|
+
if (this.tokenRefreshCallback) {
|
|
73
|
+
this.tokenRefreshCallback(token);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Listen for foreground messages
|
|
79
|
+
*/
|
|
80
|
+
onMessage(callback) {
|
|
81
|
+
if (!messaging)
|
|
82
|
+
return;
|
|
83
|
+
this.messageCallback = callback;
|
|
84
|
+
messaging().onMessage(async (remoteMessage) => {
|
|
85
|
+
console.log('Foreground message received:', remoteMessage);
|
|
86
|
+
if (this.messageCallback) {
|
|
87
|
+
this.messageCallback(remoteMessage);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Listen for background messages
|
|
93
|
+
*/
|
|
94
|
+
onBackgroundMessage(callback) {
|
|
95
|
+
if (!messaging)
|
|
96
|
+
return;
|
|
97
|
+
this.backgroundMessageCallback = callback;
|
|
98
|
+
messaging().setBackgroundMessageHandler(async (remoteMessage) => {
|
|
99
|
+
console.log('Background message received:', remoteMessage);
|
|
100
|
+
if (this.backgroundMessageCallback) {
|
|
101
|
+
this.backgroundMessageCallback(remoteMessage);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Check if device supports push notifications
|
|
107
|
+
*/
|
|
108
|
+
static async isSupported() {
|
|
109
|
+
if (!messaging)
|
|
110
|
+
return false;
|
|
111
|
+
try {
|
|
112
|
+
return await messaging().isDeviceRegisteredForRemoteMessages();
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Delete FCM token
|
|
120
|
+
*/
|
|
121
|
+
async deleteToken() {
|
|
122
|
+
if (!messaging) {
|
|
123
|
+
throw new Error('Firebase messaging is not installed');
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
await messaging().deleteToken();
|
|
127
|
+
console.log('FCM token deleted');
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
console.error('Error deleting FCM token:', error);
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Get initial notification (app opened from notification)
|
|
136
|
+
*/
|
|
137
|
+
async getInitialNotification() {
|
|
138
|
+
if (!messaging)
|
|
139
|
+
return null;
|
|
140
|
+
try {
|
|
141
|
+
return await messaging().getInitialNotification();
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
console.error('Error getting initial notification:', error);
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Subscribe to topic
|
|
150
|
+
*/
|
|
151
|
+
async subscribeToTopic(topic) {
|
|
152
|
+
if (!messaging) {
|
|
153
|
+
throw new Error('Firebase messaging is not installed');
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
await messaging().subscribeToTopic(topic);
|
|
157
|
+
console.log(`Subscribed to topic: ${topic}`);
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
console.error('Error subscribing to topic:', error);
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Unsubscribe from topic
|
|
166
|
+
*/
|
|
167
|
+
async unsubscribeFromTopic(topic) {
|
|
168
|
+
if (!messaging) {
|
|
169
|
+
throw new Error('Firebase messaging is not installed');
|
|
170
|
+
}
|
|
171
|
+
try {
|
|
172
|
+
await messaging().unsubscribeFromTopic(topic);
|
|
173
|
+
console.log(`Unsubscribed from topic: ${topic}`);
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
console.error('Error unsubscribing from topic:', error);
|
|
177
|
+
throw error;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { BasePushNotification } from './BasePushNotification';
|
|
2
|
+
/**
|
|
3
|
+
* PushNotification class
|
|
4
|
+
* Extends BasePushNotification
|
|
5
|
+
*/
|
|
6
|
+
export declare class PushNotification extends BasePushNotification {
|
|
7
|
+
}
|
|
8
|
+
export type { IPushNotificationAdapter } from './PushNotification.types';
|
|
9
|
+
export default PushNotification;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// PushNotification.ts
|
|
2
|
+
// Main push notification export
|
|
3
|
+
import { BasePushNotification } from './BasePushNotification';
|
|
4
|
+
/**
|
|
5
|
+
* PushNotification class
|
|
6
|
+
* Extends BasePushNotification
|
|
7
|
+
*/
|
|
8
|
+
export class PushNotification extends BasePushNotification {
|
|
9
|
+
}
|
|
10
|
+
// Default export
|
|
11
|
+
export default PushNotification;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Push notification adapter interface
|
|
3
|
+
*/
|
|
4
|
+
export interface IPushNotificationAdapter {
|
|
5
|
+
requestPermission(options?: {
|
|
6
|
+
forceShow?: boolean;
|
|
7
|
+
}): Promise<void>;
|
|
8
|
+
getToken(): Promise<string>;
|
|
9
|
+
onTokenRefresh(callback: (token: string) => void): void;
|
|
10
|
+
onMessage(callback: (message: any) => void): void;
|
|
11
|
+
onBackgroundMessage(callback: (message: any) => void): void;
|
|
12
|
+
}
|