@churchapps/apphelper 0.6.0 → 0.6.2
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/dist/components/wrapper/NewPrivateMessage.d.ts.map +1 -1
- package/dist/components/wrapper/NewPrivateMessage.js +1 -2
- package/dist/components/wrapper/NewPrivateMessage.js.map +1 -1
- package/dist/components/wrapper/Notifications.d.ts.map +1 -1
- package/dist/components/wrapper/Notifications.js +1 -2
- package/dist/components/wrapper/Notifications.js.map +1 -1
- package/dist/components/wrapper/PrivateMessageDetails.d.ts.map +1 -1
- package/dist/components/wrapper/PrivateMessageDetails.js +1 -2
- package/dist/components/wrapper/PrivateMessageDetails.js.map +1 -1
- package/dist/components/wrapper/PrivateMessages.d.ts.map +1 -1
- package/dist/components/wrapper/PrivateMessages.js +1 -2
- package/dist/components/wrapper/PrivateMessages.js.map +1 -1
- package/package.json +1 -1
- package/src/components/ImageEditor.tsx +167 -167
- package/src/components/gallery/GalleryModal.tsx +169 -169
- package/src/components/header/SiteHeader.tsx +204 -204
- package/src/components/wrapper/ChurchList.tsx +145 -145
- package/src/components/wrapper/NewPrivateMessage.tsx +12 -15
- package/src/components/wrapper/Notifications.tsx +14 -17
- package/src/components/wrapper/PrivateMessageDetails.tsx +6 -8
- package/src/components/wrapper/PrivateMessages.tsx +10 -13
- package/src/helpers/ErrorHelper.ts +41 -41
- package/src/helpers/NotificationService.ts +232 -232
- package/src/helpers/SocketHelper.ts +247 -247
|
@@ -1,233 +1,233 @@
|
|
|
1
|
-
import { SocketHelper } from "./SocketHelper";
|
|
2
|
-
import { ApiHelper, UserContextInterface } from "@churchapps/helpers";
|
|
3
|
-
|
|
4
|
-
export interface NotificationCounts {
|
|
5
|
-
notificationCount: number;
|
|
6
|
-
pmCount: number;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export class NotificationService {
|
|
10
|
-
private static instance: NotificationService;
|
|
11
|
-
private counts: NotificationCounts = { notificationCount: 0, pmCount: 0 };
|
|
12
|
-
private listeners: Array<(counts: NotificationCounts) => void> = [];
|
|
13
|
-
private isInitialized: boolean = false;
|
|
14
|
-
private currentPersonId: string | null = null;
|
|
15
|
-
private loadTimeout: any | null = null;
|
|
16
|
-
|
|
17
|
-
private constructor() {}
|
|
18
|
-
|
|
19
|
-
static getInstance(): NotificationService {
|
|
20
|
-
if (!NotificationService.instance) {
|
|
21
|
-
NotificationService.instance = new NotificationService();
|
|
22
|
-
}
|
|
23
|
-
return NotificationService.instance;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Initialize the notification service with user context
|
|
28
|
-
*/
|
|
29
|
-
async initialize(context: UserContextInterface): Promise<void> {
|
|
30
|
-
if (this.isInitialized) {
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
try {
|
|
35
|
-
// Store current person ID for conversation counting
|
|
36
|
-
this.currentPersonId = context?.person?.id || null;
|
|
37
|
-
|
|
38
|
-
// Initialize WebSocket connection
|
|
39
|
-
await SocketHelper.init();
|
|
40
|
-
|
|
41
|
-
// Set person/church context for websocket
|
|
42
|
-
if (context?.person?.id && context?.userChurch?.church?.id) {
|
|
43
|
-
SocketHelper.setPersonChurch({
|
|
44
|
-
personId: context.person.id,
|
|
45
|
-
churchId: context.userChurch.church.id
|
|
46
|
-
});
|
|
47
|
-
} else {
|
|
48
|
-
console.warn('⚠️ NotificationService: Missing person/church IDs, cannot set socket context');
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Register handlers for notification updates
|
|
52
|
-
this.registerWebSocketHandlers();
|
|
53
|
-
|
|
54
|
-
// Load initial notification counts
|
|
55
|
-
await this.loadNotificationCounts();
|
|
56
|
-
|
|
57
|
-
this.isInitialized = true;
|
|
58
|
-
|
|
59
|
-
} catch (error) {
|
|
60
|
-
console.error("❌ Failed to initialize NotificationService:", error);
|
|
61
|
-
throw error;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Register websocket handlers for real-time notification updates
|
|
67
|
-
*/
|
|
68
|
-
private registerWebSocketHandlers(): void {
|
|
69
|
-
// Handler for new private messages
|
|
70
|
-
SocketHelper.addHandler("privateMessage", "NotificationService-PM", (data: any) => {
|
|
71
|
-
try {
|
|
72
|
-
this.debouncedLoadNotificationCounts();
|
|
73
|
-
} catch (error) {
|
|
74
|
-
console.error('❌ NotificationService: Error calling debouncedLoadNotificationCounts:', error);
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
// Handler for general notifications
|
|
79
|
-
SocketHelper.addHandler("notification", "NotificationService-Notification", (data: any) => {
|
|
80
|
-
try {
|
|
81
|
-
this.debouncedLoadNotificationCounts();
|
|
82
|
-
} catch (error) {
|
|
83
|
-
console.error('❌ NotificationService: Error calling debouncedLoadNotificationCounts:', error);
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
// Handler for message updates that could affect notification counts
|
|
88
|
-
SocketHelper.addHandler("message", "NotificationService-MessageUpdate", (data: any) => {
|
|
89
|
-
// Only update counts if the message update involves the current person
|
|
90
|
-
if (data?.message?.personId === this.currentPersonId ||
|
|
91
|
-
data?.notifyPersonId === this.currentPersonId) {
|
|
92
|
-
try {
|
|
93
|
-
this.debouncedLoadNotificationCounts();
|
|
94
|
-
} catch (error) {
|
|
95
|
-
console.error('❌ NotificationService: Error calling debouncedLoadNotificationCounts:', error);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
// Handler for reconnect events
|
|
101
|
-
SocketHelper.addHandler("reconnect", "NotificationService-Reconnect", (data: any) => {
|
|
102
|
-
this.loadNotificationCounts(); // Don't debounce reconnect - need immediate update
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Load notification counts from the API with debouncing
|
|
108
|
-
*/
|
|
109
|
-
private debouncedLoadNotificationCounts(): void {
|
|
110
|
-
|
|
111
|
-
if (this.loadTimeout) {
|
|
112
|
-
clearTimeout(this.loadTimeout);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
this.loadTimeout = setTimeout(() => {
|
|
116
|
-
this.loadNotificationCounts();
|
|
117
|
-
}, 300); // 300ms debounce
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Load notification counts from the API
|
|
122
|
-
*/
|
|
123
|
-
async loadNotificationCounts(): Promise<void> {
|
|
124
|
-
|
|
125
|
-
try {
|
|
126
|
-
// Use the unreadCount endpoint which returns both notification and PM counts
|
|
127
|
-
const counts = await ApiHelper.get("/notifications/unreadCount", "MessagingApi");
|
|
128
|
-
|
|
129
|
-
const newCounts = {
|
|
130
|
-
notificationCount: counts?.notificationCount || 0,
|
|
131
|
-
pmCount: counts?.pmCount || 0
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
// Update counts and notify listeners
|
|
136
|
-
this.updateCounts(newCounts);
|
|
137
|
-
|
|
138
|
-
} catch (error) {
|
|
139
|
-
console.error("❌ Failed to load notification counts:", error);
|
|
140
|
-
console.error("❌ Error details:", {
|
|
141
|
-
message: error.message,
|
|
142
|
-
status: error.status,
|
|
143
|
-
response: error.response
|
|
144
|
-
});
|
|
145
|
-
// Don't throw - just log the error and keep existing counts
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Update counts and notify all listeners
|
|
151
|
-
*/
|
|
152
|
-
private updateCounts(newCounts: NotificationCounts): void {
|
|
153
|
-
|
|
154
|
-
const countsChanged =
|
|
155
|
-
this.counts.notificationCount !== newCounts.notificationCount ||
|
|
156
|
-
this.counts.pmCount !== newCounts.pmCount;
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (countsChanged) {
|
|
160
|
-
this.counts = { ...newCounts };
|
|
161
|
-
|
|
162
|
-
// Notify all listeners
|
|
163
|
-
this.listeners.forEach((listener, index) => {
|
|
164
|
-
try {
|
|
165
|
-
listener(this.counts);
|
|
166
|
-
} catch (error) {
|
|
167
|
-
console.error(`❌ Error in notification listener ${index}:`, error);
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Subscribe to notification count changes
|
|
175
|
-
*/
|
|
176
|
-
subscribe(listener: (counts: NotificationCounts) => void): () => void {
|
|
177
|
-
this.listeners.push(listener);
|
|
178
|
-
|
|
179
|
-
// Immediately call with current counts
|
|
180
|
-
listener(this.counts);
|
|
181
|
-
|
|
182
|
-
// Return unsubscribe function
|
|
183
|
-
return () => {
|
|
184
|
-
this.listeners = this.listeners.filter(l => l !== listener);
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Get current notification counts
|
|
190
|
-
*/
|
|
191
|
-
getCounts(): NotificationCounts {
|
|
192
|
-
return { ...this.counts };
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Manually refresh notification counts
|
|
197
|
-
*/
|
|
198
|
-
async refresh(): Promise<void> {
|
|
199
|
-
await this.loadNotificationCounts();
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Cleanup the service
|
|
204
|
-
*/
|
|
205
|
-
cleanup(): void {
|
|
206
|
-
// Clear any pending timeout
|
|
207
|
-
if (this.loadTimeout) {
|
|
208
|
-
clearTimeout(this.loadTimeout);
|
|
209
|
-
this.loadTimeout = null;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// Remove websocket handlers
|
|
213
|
-
SocketHelper.removeHandler("NotificationService-PM");
|
|
214
|
-
SocketHelper.removeHandler("NotificationService-Notification");
|
|
215
|
-
SocketHelper.removeHandler("NotificationService-MessageUpdate");
|
|
216
|
-
SocketHelper.removeHandler("NotificationService-Reconnect");
|
|
217
|
-
|
|
218
|
-
// Clear listeners
|
|
219
|
-
this.listeners = [];
|
|
220
|
-
|
|
221
|
-
// Reset state
|
|
222
|
-
this.counts = { notificationCount: 0, pmCount: 0 };
|
|
223
|
-
this.currentPersonId = null;
|
|
224
|
-
this.isInitialized = false;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Check if service is initialized
|
|
229
|
-
*/
|
|
230
|
-
isReady(): boolean {
|
|
231
|
-
return this.isInitialized && SocketHelper.isConnected();
|
|
232
|
-
}
|
|
1
|
+
import { SocketHelper } from "./SocketHelper";
|
|
2
|
+
import { ApiHelper, UserContextInterface } from "@churchapps/helpers";
|
|
3
|
+
|
|
4
|
+
export interface NotificationCounts {
|
|
5
|
+
notificationCount: number;
|
|
6
|
+
pmCount: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class NotificationService {
|
|
10
|
+
private static instance: NotificationService;
|
|
11
|
+
private counts: NotificationCounts = { notificationCount: 0, pmCount: 0 };
|
|
12
|
+
private listeners: Array<(counts: NotificationCounts) => void> = [];
|
|
13
|
+
private isInitialized: boolean = false;
|
|
14
|
+
private currentPersonId: string | null = null;
|
|
15
|
+
private loadTimeout: any | null = null;
|
|
16
|
+
|
|
17
|
+
private constructor() {}
|
|
18
|
+
|
|
19
|
+
static getInstance(): NotificationService {
|
|
20
|
+
if (!NotificationService.instance) {
|
|
21
|
+
NotificationService.instance = new NotificationService();
|
|
22
|
+
}
|
|
23
|
+
return NotificationService.instance;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Initialize the notification service with user context
|
|
28
|
+
*/
|
|
29
|
+
async initialize(context: UserContextInterface): Promise<void> {
|
|
30
|
+
if (this.isInitialized) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
// Store current person ID for conversation counting
|
|
36
|
+
this.currentPersonId = context?.person?.id || null;
|
|
37
|
+
|
|
38
|
+
// Initialize WebSocket connection
|
|
39
|
+
await SocketHelper.init();
|
|
40
|
+
|
|
41
|
+
// Set person/church context for websocket
|
|
42
|
+
if (context?.person?.id && context?.userChurch?.church?.id) {
|
|
43
|
+
SocketHelper.setPersonChurch({
|
|
44
|
+
personId: context.person.id,
|
|
45
|
+
churchId: context.userChurch.church.id
|
|
46
|
+
});
|
|
47
|
+
} else {
|
|
48
|
+
console.warn('⚠️ NotificationService: Missing person/church IDs, cannot set socket context');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Register handlers for notification updates
|
|
52
|
+
this.registerWebSocketHandlers();
|
|
53
|
+
|
|
54
|
+
// Load initial notification counts
|
|
55
|
+
await this.loadNotificationCounts();
|
|
56
|
+
|
|
57
|
+
this.isInitialized = true;
|
|
58
|
+
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.error("❌ Failed to initialize NotificationService:", error);
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Register websocket handlers for real-time notification updates
|
|
67
|
+
*/
|
|
68
|
+
private registerWebSocketHandlers(): void {
|
|
69
|
+
// Handler for new private messages
|
|
70
|
+
SocketHelper.addHandler("privateMessage", "NotificationService-PM", (data: any) => {
|
|
71
|
+
try {
|
|
72
|
+
this.debouncedLoadNotificationCounts();
|
|
73
|
+
} catch (error) {
|
|
74
|
+
console.error('❌ NotificationService: Error calling debouncedLoadNotificationCounts:', error);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Handler for general notifications
|
|
79
|
+
SocketHelper.addHandler("notification", "NotificationService-Notification", (data: any) => {
|
|
80
|
+
try {
|
|
81
|
+
this.debouncedLoadNotificationCounts();
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.error('❌ NotificationService: Error calling debouncedLoadNotificationCounts:', error);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Handler for message updates that could affect notification counts
|
|
88
|
+
SocketHelper.addHandler("message", "NotificationService-MessageUpdate", (data: any) => {
|
|
89
|
+
// Only update counts if the message update involves the current person
|
|
90
|
+
if (data?.message?.personId === this.currentPersonId ||
|
|
91
|
+
data?.notifyPersonId === this.currentPersonId) {
|
|
92
|
+
try {
|
|
93
|
+
this.debouncedLoadNotificationCounts();
|
|
94
|
+
} catch (error) {
|
|
95
|
+
console.error('❌ NotificationService: Error calling debouncedLoadNotificationCounts:', error);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Handler for reconnect events
|
|
101
|
+
SocketHelper.addHandler("reconnect", "NotificationService-Reconnect", (data: any) => {
|
|
102
|
+
this.loadNotificationCounts(); // Don't debounce reconnect - need immediate update
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Load notification counts from the API with debouncing
|
|
108
|
+
*/
|
|
109
|
+
private debouncedLoadNotificationCounts(): void {
|
|
110
|
+
|
|
111
|
+
if (this.loadTimeout) {
|
|
112
|
+
clearTimeout(this.loadTimeout);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
this.loadTimeout = setTimeout(() => {
|
|
116
|
+
this.loadNotificationCounts();
|
|
117
|
+
}, 300); // 300ms debounce
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Load notification counts from the API
|
|
122
|
+
*/
|
|
123
|
+
async loadNotificationCounts(): Promise<void> {
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
// Use the unreadCount endpoint which returns both notification and PM counts
|
|
127
|
+
const counts = await ApiHelper.get("/notifications/unreadCount", "MessagingApi");
|
|
128
|
+
|
|
129
|
+
const newCounts = {
|
|
130
|
+
notificationCount: counts?.notificationCount || 0,
|
|
131
|
+
pmCount: counts?.pmCount || 0
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
// Update counts and notify listeners
|
|
136
|
+
this.updateCounts(newCounts);
|
|
137
|
+
|
|
138
|
+
} catch (error) {
|
|
139
|
+
console.error("❌ Failed to load notification counts:", error);
|
|
140
|
+
console.error("❌ Error details:", {
|
|
141
|
+
message: error.message,
|
|
142
|
+
status: error.status,
|
|
143
|
+
response: error.response
|
|
144
|
+
});
|
|
145
|
+
// Don't throw - just log the error and keep existing counts
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Update counts and notify all listeners
|
|
151
|
+
*/
|
|
152
|
+
private updateCounts(newCounts: NotificationCounts): void {
|
|
153
|
+
|
|
154
|
+
const countsChanged =
|
|
155
|
+
this.counts.notificationCount !== newCounts.notificationCount ||
|
|
156
|
+
this.counts.pmCount !== newCounts.pmCount;
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
if (countsChanged) {
|
|
160
|
+
this.counts = { ...newCounts };
|
|
161
|
+
|
|
162
|
+
// Notify all listeners
|
|
163
|
+
this.listeners.forEach((listener, index) => {
|
|
164
|
+
try {
|
|
165
|
+
listener(this.counts);
|
|
166
|
+
} catch (error) {
|
|
167
|
+
console.error(`❌ Error in notification listener ${index}:`, error);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Subscribe to notification count changes
|
|
175
|
+
*/
|
|
176
|
+
subscribe(listener: (counts: NotificationCounts) => void): () => void {
|
|
177
|
+
this.listeners.push(listener);
|
|
178
|
+
|
|
179
|
+
// Immediately call with current counts
|
|
180
|
+
listener(this.counts);
|
|
181
|
+
|
|
182
|
+
// Return unsubscribe function
|
|
183
|
+
return () => {
|
|
184
|
+
this.listeners = this.listeners.filter(l => l !== listener);
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Get current notification counts
|
|
190
|
+
*/
|
|
191
|
+
getCounts(): NotificationCounts {
|
|
192
|
+
return { ...this.counts };
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Manually refresh notification counts
|
|
197
|
+
*/
|
|
198
|
+
async refresh(): Promise<void> {
|
|
199
|
+
await this.loadNotificationCounts();
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Cleanup the service
|
|
204
|
+
*/
|
|
205
|
+
cleanup(): void {
|
|
206
|
+
// Clear any pending timeout
|
|
207
|
+
if (this.loadTimeout) {
|
|
208
|
+
clearTimeout(this.loadTimeout);
|
|
209
|
+
this.loadTimeout = null;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Remove websocket handlers
|
|
213
|
+
SocketHelper.removeHandler("NotificationService-PM");
|
|
214
|
+
SocketHelper.removeHandler("NotificationService-Notification");
|
|
215
|
+
SocketHelper.removeHandler("NotificationService-MessageUpdate");
|
|
216
|
+
SocketHelper.removeHandler("NotificationService-Reconnect");
|
|
217
|
+
|
|
218
|
+
// Clear listeners
|
|
219
|
+
this.listeners = [];
|
|
220
|
+
|
|
221
|
+
// Reset state
|
|
222
|
+
this.counts = { notificationCount: 0, pmCount: 0 };
|
|
223
|
+
this.currentPersonId = null;
|
|
224
|
+
this.isInitialized = false;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Check if service is initialized
|
|
229
|
+
*/
|
|
230
|
+
isReady(): boolean {
|
|
231
|
+
return this.isInitialized && SocketHelper.isConnected();
|
|
232
|
+
}
|
|
233
233
|
}
|