@churchapps/apphelper 0.6.14 → 0.6.16
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/DisplayBox.d.ts +1 -1
- package/dist/components/DisplayBox.d.ts.map +1 -1
- package/dist/components/DisplayBox.js +2 -4
- package/dist/components/DisplayBox.js.map +1 -1
- package/dist/components/ErrorMessages.d.ts.map +1 -1
- package/dist/components/ErrorMessages.js +1 -1
- package/dist/components/ErrorMessages.js.map +1 -1
- package/dist/components/ExportLink.d.ts.map +1 -1
- package/dist/components/ExportLink.js +10 -10
- package/dist/components/ExportLink.js.map +1 -1
- package/dist/components/FloatingSupport.js.map +1 -1
- package/dist/components/FormCardPayment.d.ts.map +1 -1
- package/dist/components/FormCardPayment.js +20 -5
- package/dist/components/FormCardPayment.js.map +1 -1
- package/dist/components/FormSubmissionEdit.d.ts.map +1 -1
- package/dist/components/FormSubmissionEdit.js +8 -10
- package/dist/components/FormSubmissionEdit.js.map +1 -1
- package/dist/components/HelpIcon.d.ts.map +1 -1
- package/dist/components/HelpIcon.js.map +1 -1
- package/dist/components/ImageEditor.d.ts.map +1 -1
- package/dist/components/ImageEditor.js +10 -10
- package/dist/components/ImageEditor.js.map +1 -1
- package/dist/components/InputBox.d.ts +2 -1
- package/dist/components/InputBox.d.ts.map +1 -1
- package/dist/components/InputBox.js +5 -7
- package/dist/components/InputBox.js.map +1 -1
- package/dist/components/Loading.d.ts.map +1 -1
- package/dist/components/Loading.js.map +1 -1
- package/dist/components/PageHeader.js +14 -14
- package/dist/components/PersonAvatar.d.ts.map +1 -1
- package/dist/components/PersonAvatar.js +9 -16
- package/dist/components/PersonAvatar.js.map +1 -1
- package/dist/components/QuestionEdit.js +3 -3
- package/dist/components/QuestionEdit.js.map +1 -1
- package/dist/components/SmallButton.d.ts.map +1 -1
- package/dist/components/SmallButton.js +2 -1
- package/dist/components/SmallButton.js.map +1 -1
- package/dist/components/SupportModal.js.map +1 -1
- package/dist/components/gallery/GalleryModal.js +10 -10
- package/dist/components/gallery/GalleryModal.js.map +1 -1
- package/dist/components/gallery/StockPhotos.js +12 -12
- package/dist/components/gallery/StockPhotos.js.map +1 -1
- package/dist/components/header/Banner.d.ts.map +1 -1
- package/dist/components/header/Banner.js.map +1 -1
- package/dist/components/header/PrimaryMenu.js +4 -4
- package/dist/components/header/PrimaryMenu.js.map +1 -1
- package/dist/components/header/SecondaryMenu.d.ts.map +1 -1
- package/dist/components/header/SecondaryMenu.js.map +1 -1
- package/dist/components/header/SecondaryMenuAlt.d.ts.map +1 -1
- package/dist/components/header/SecondaryMenuAlt.js.map +1 -1
- package/dist/components/header/SiteHeader.d.ts.map +1 -1
- package/dist/components/header/SiteHeader.js +7 -7
- package/dist/components/header/SiteHeader.js.map +1 -1
- package/dist/components/header/SupportDrawer.js +1 -1
- package/dist/components/header/SupportDrawer.js.map +1 -1
- package/dist/components/notes/AddNote.js +14 -14
- package/dist/components/notes/Note.d.ts.map +1 -1
- package/dist/components/notes/Note.js +7 -7
- package/dist/components/notes/Note.js.map +1 -1
- package/dist/components/notes/Notes.d.ts.map +1 -1
- package/dist/components/notes/Notes.js +4 -4
- package/dist/components/notes/Notes.js.map +1 -1
- package/dist/components/wrapper/AppList.d.ts.map +1 -1
- package/dist/components/wrapper/AppList.js.map +1 -1
- package/dist/components/wrapper/ChurchList.js +5 -5
- package/dist/components/wrapper/ChurchList.js.map +1 -1
- package/dist/components/wrapper/NavItem.js +1 -1
- package/dist/components/wrapper/NavItem.js.map +1 -1
- package/dist/components/wrapper/NewPrivateMessage.d.ts.map +1 -1
- package/dist/components/wrapper/NewPrivateMessage.js +7 -6
- 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 +12 -14
- package/dist/components/wrapper/Notifications.js.map +1 -1
- package/dist/components/wrapper/PrivateMessageDetails.js +10 -10
- package/dist/components/wrapper/PrivateMessages.d.ts.map +1 -1
- package/dist/components/wrapper/PrivateMessages.js +119 -123
- package/dist/components/wrapper/PrivateMessages.js.map +1 -1
- package/dist/components/wrapper/UserMenu.d.ts.map +1 -1
- package/dist/components/wrapper/UserMenu.js +15 -15
- package/dist/components/wrapper/UserMenu.js.map +1 -1
- package/dist/helpers/AnalyticsHelper.d.ts.map +1 -1
- package/dist/helpers/AnalyticsHelper.js +3 -3
- package/dist/helpers/AnalyticsHelper.js.map +1 -1
- package/dist/helpers/ArrayHelper.d.ts.map +1 -1
- package/dist/helpers/ArrayHelper.js.map +1 -1
- package/dist/helpers/CurrencyHelper.d.ts +4 -0
- package/dist/helpers/CurrencyHelper.d.ts.map +1 -1
- package/dist/helpers/CurrencyHelper.js +65 -0
- package/dist/helpers/CurrencyHelper.js.map +1 -1
- package/dist/helpers/ErrorHelper.d.ts.map +1 -1
- package/dist/helpers/ErrorHelper.js.map +1 -1
- package/dist/helpers/EventHelper.d.ts.map +1 -1
- package/dist/helpers/EventHelper.js +1 -1
- package/dist/helpers/EventHelper.js.map +1 -1
- package/dist/helpers/FileHelper.d.ts.map +1 -1
- package/dist/helpers/FileHelper.js +3 -1
- package/dist/helpers/FileHelper.js.map +1 -1
- package/dist/helpers/Locale.d.ts.map +1 -1
- package/dist/helpers/Locale.js +10 -16
- package/dist/helpers/Locale.js.map +1 -1
- package/dist/helpers/NotificationService.js +4 -4
- package/dist/helpers/PersonHelper.d.ts.map +1 -1
- package/dist/helpers/PersonHelper.js +5 -4
- package/dist/helpers/PersonHelper.js.map +1 -1
- package/dist/helpers/SlugHelper.d.ts.map +1 -1
- package/dist/helpers/SlugHelper.js +5 -3
- package/dist/helpers/SlugHelper.js.map +1 -1
- package/dist/helpers/SocketHelper.d.ts.map +1 -1
- package/dist/helpers/SocketHelper.js +5 -10
- package/dist/helpers/SocketHelper.js.map +1 -1
- package/dist/helpers/UniqueIdHelper.d.ts.map +1 -1
- package/dist/helpers/UniqueIdHelper.js +132 -7
- package/dist/helpers/UniqueIdHelper.js.map +1 -1
- package/dist/helpers/UserHelper.d.ts.map +1 -1
- package/dist/helpers/UserHelper.js +2 -2
- package/dist/helpers/UserHelper.js.map +1 -1
- package/dist/hooks/useMountedState.d.ts.map +1 -1
- package/dist/hooks/useMountedState.js +1 -1
- package/dist/hooks/useMountedState.js.map +1 -1
- package/dist/hooks/useNotifications.d.ts +2 -2
- package/dist/hooks/useNotifications.js +5 -5
- package/dist/public/locales/de.json +15 -7
- package/dist/public/locales/es.json +193 -190
- package/dist/public/locales/fr.json +15 -7
- package/dist/public/locales/hi.json +15 -7
- package/dist/public/locales/it.json +15 -7
- package/dist/public/locales/ko.json +15 -7
- package/dist/public/locales/no.json +15 -7
- package/dist/public/locales/pt.json +15 -7
- package/dist/public/locales/ru.json +15 -7
- package/dist/public/locales/tl.json +15 -7
- package/dist/public/locales/zh.json +15 -7
- package/package.json +106 -73
- package/dist/helpers/DateHelper.d.ts +0 -18
- package/dist/helpers/DateHelper.d.ts.map +0 -1
- package/dist/helpers/DateHelper.js +0 -102
- package/dist/helpers/DateHelper.js.map +0 -1
- package/public/css/cropper.css +0 -309
- package/public/css/styles.css +0 -112
- package/public/locales/de.json +0 -273
- package/public/locales/en.json +0 -281
- package/public/locales/es.json +0 -278
- package/public/locales/fr.json +0 -273
- package/public/locales/hi.json +0 -273
- package/public/locales/it.json +0 -273
- package/public/locales/ko.json +0 -273
- package/public/locales/no.json +0 -273
- package/public/locales/pt.json +0 -273
- package/public/locales/ru.json +0 -273
- package/public/locales/tl.json +0 -273
- package/public/locales/zh.json +0 -273
- package/src/components/DisplayBox.tsx +0 -83
- package/src/components/ErrorMessages.tsx +0 -28
- package/src/components/ExportLink.tsx +0 -81
- package/src/components/FloatingSupport.tsx +0 -18
- package/src/components/FormCardPayment.tsx +0 -184
- package/src/components/FormSubmissionEdit.tsx +0 -168
- package/src/components/HelpIcon.tsx +0 -12
- package/src/components/ImageEditor.tsx +0 -167
- package/src/components/InputBox.tsx +0 -96
- package/src/components/Loading.tsx +0 -31
- package/src/components/PageHeader.tsx +0 -111
- package/src/components/PersonAvatar.tsx +0 -78
- package/src/components/QuestionEdit.tsx +0 -99
- package/src/components/SmallButton.tsx +0 -42
- package/src/components/SupportModal.tsx +0 -32
- package/src/components/TabPanel.tsx +0 -28
- package/src/components/gallery/GalleryModal.tsx +0 -170
- package/src/components/gallery/StockPhotos.tsx +0 -96
- package/src/components/gallery/index.ts +0 -2
- package/src/components/header/Banner.tsx +0 -11
- package/src/components/header/PrimaryMenu.tsx +0 -101
- package/src/components/header/SecondaryMenu.tsx +0 -23
- package/src/components/header/SecondaryMenuAlt.tsx +0 -40
- package/src/components/header/SiteHeader.tsx +0 -205
- package/src/components/header/SupportDrawer.tsx +0 -112
- package/src/components/header/index.tsx +0 -2
- package/src/components/index.tsx +0 -20
- package/src/components/notes/AddNote.tsx +0 -185
- package/src/components/notes/Note.tsx +0 -84
- package/src/components/notes/Notes.tsx +0 -208
- package/src/components/notes/index.ts +0 -3
- package/src/components/wrapper/AppList.tsx +0 -18
- package/src/components/wrapper/ChurchList.tsx +0 -145
- package/src/components/wrapper/NavItem.tsx +0 -47
- package/src/components/wrapper/NewPrivateMessage.tsx +0 -249
- package/src/components/wrapper/Notifications.tsx +0 -220
- package/src/components/wrapper/PrivateMessageDetails.tsx +0 -106
- package/src/components/wrapper/PrivateMessages.tsx +0 -574
- package/src/components/wrapper/UserMenu.tsx +0 -384
- package/src/components/wrapper/index.tsx +0 -8
- package/src/helpers/AnalyticsHelper.ts +0 -44
- package/src/helpers/AppearanceHelper.ts +0 -73
- package/src/helpers/ArrayHelper.ts +0 -87
- package/src/helpers/CurrencyHelper.ts +0 -10
- package/src/helpers/DateHelper.ts +0 -104
- package/src/helpers/ErrorHelper.ts +0 -41
- package/src/helpers/EventHelper.ts +0 -49
- package/src/helpers/FileHelper.ts +0 -31
- package/src/helpers/Locale.ts +0 -457
- package/src/helpers/NotificationService.ts +0 -233
- package/src/helpers/PersonHelper.ts +0 -62
- package/src/helpers/SlugHelper.ts +0 -27
- package/src/helpers/SocketHelper.ts +0 -183
- package/src/helpers/UniqueIdHelper.ts +0 -36
- package/src/helpers/UserHelper.ts +0 -103
- package/src/helpers/createEmotionCache.ts +0 -17
- package/src/helpers/index.ts +0 -58
- package/src/hooks/index.ts +0 -3
- package/src/hooks/useMountedState.ts +0 -18
- package/src/hooks/useNotifications.ts +0 -95
- package/src/index.ts +0 -3
- package/src/types/interface-extensions.d.ts +0 -12
- package/tsconfig.json +0 -32
|
@@ -1,233 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { PersonInterface, ContactInfoInterface, CommonEnvironmentHelper } from "@churchapps/helpers";
|
|
2
|
-
import { Locale } from "./Locale";
|
|
3
|
-
|
|
4
|
-
export class PersonHelper {
|
|
5
|
-
|
|
6
|
-
static getPhotoUrl(person: PersonInterface) {
|
|
7
|
-
if (!person?.photo) return "/images/sample-profile.png"
|
|
8
|
-
else return (person?.photo?.startsWith("data:image/png;base64,") || person.photo?.indexOf("://") > -1)
|
|
9
|
-
? person.photo
|
|
10
|
-
: CommonEnvironmentHelper.ContentRoot + person.photo;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
static getAge(birthdate: Date): string {
|
|
14
|
-
if (birthdate !== undefined && birthdate !== null) {
|
|
15
|
-
let ageDifMs = Date.now() - new Date(birthdate).getTime();
|
|
16
|
-
let ageDate = new Date(ageDifMs);
|
|
17
|
-
let years = Math.abs(ageDate.getUTCFullYear() - 1970);
|
|
18
|
-
return years + " " + Locale.label("person.years");
|
|
19
|
-
}
|
|
20
|
-
else return "";
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
static getDisplayName(firstName: string, lastName: string, nickName: string): string {
|
|
24
|
-
if (nickName !== undefined && nickName !== null && nickName.length > 0) return firstName + ' "' + nickName + '" ' + lastName;
|
|
25
|
-
else return firstName + " " + lastName;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
public static compareAddress(address1: ContactInfoInterface, address2: ContactInfoInterface): boolean {
|
|
29
|
-
const displayAddress1: string = this.addressToString(address1).trim();
|
|
30
|
-
const displayAddress2: string = this.addressToString(address2).trim();
|
|
31
|
-
|
|
32
|
-
if (displayAddress1 !== displayAddress2) {
|
|
33
|
-
return true
|
|
34
|
-
}
|
|
35
|
-
return false
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
public static addressToString(address: ContactInfoInterface): string {
|
|
39
|
-
return `${address.address1 || ""} ${address.address2 || ""} ${address.city || ""}${(address.city && address.state) ? "," : ""} ${address.state || ""} ${address.zip || ""}`
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
public static changeOnlyAddress(contactInfo1: ContactInfoInterface, contactInfo2: ContactInfoInterface): ContactInfoInterface {
|
|
43
|
-
const updatedAddress: ContactInfoInterface = {
|
|
44
|
-
...contactInfo1,
|
|
45
|
-
address1: contactInfo2.address1,
|
|
46
|
-
address2: contactInfo2.address2,
|
|
47
|
-
city: contactInfo2.city,
|
|
48
|
-
state: contactInfo2.state,
|
|
49
|
-
zip: contactInfo2.zip
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return updatedAddress
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
public static checkAddressAvailabilty(person: PersonInterface): boolean {
|
|
56
|
-
const addressString: string = this.addressToString(person.contactInfo).trim();
|
|
57
|
-
if (addressString !== "") {
|
|
58
|
-
return true;
|
|
59
|
-
}
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import slug from "slug";
|
|
2
|
-
|
|
3
|
-
export class SlugHelper {
|
|
4
|
-
|
|
5
|
-
static slugifyString(string: string, type: "urlPath" | "urlSlug", removeCharacters?: string[]) {
|
|
6
|
-
const charactersToRemove = removeCharacters ? removeCharacters : ["for", "and", "nor", "but", "or", "yet", "so", "the", "a", "an"];
|
|
7
|
-
const characStr = charactersToRemove.join("|");
|
|
8
|
-
if (type === "urlPath") {
|
|
9
|
-
slug.extend({'/': '/'}); //To keep '/' in the url since it's a special character.
|
|
10
|
-
}
|
|
11
|
-
const initialSlug = slug(string, { remove: new RegExp('\\b(' + characStr + ')\\b', 'gi') });
|
|
12
|
-
const verfiedSlug = this.numerifySlug(initialSlug);
|
|
13
|
-
return verfiedSlug;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
//remove long sequences of single-digit numbers (e.g., 1-2-3-4 becomes 1), but preserve normal number patterns
|
|
17
|
-
static numerifySlug(slug: string) {
|
|
18
|
-
let initialString = slug;
|
|
19
|
-
// Only match sequences of 3+ single-digit numbers separated by hyphens (e.g., 1-2-3, 1-2-3-4)
|
|
20
|
-
// This avoids mangling things like chapter-verse references (6-11) or dates
|
|
21
|
-
const regex = /\b(\d)-(\d)-(\d)(?:-\d)*\b/g;
|
|
22
|
-
initialString = initialString.replace(regex, "$1");
|
|
23
|
-
|
|
24
|
-
return initialString;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
}
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
import { ConnectionInterface, SocketActionHandlerInterface, SocketPayloadInterface, ApiHelper, ArrayHelper, CommonEnvironmentHelper } from "@churchapps/helpers";
|
|
2
|
-
|
|
3
|
-
export class SocketHelper {
|
|
4
|
-
static socket: WebSocket;
|
|
5
|
-
static socketId: string;
|
|
6
|
-
static actionHandlers: SocketActionHandlerInterface[] = [];
|
|
7
|
-
private static personIdChurchId: { personId: string, churchId: string } = { personId: "", churchId: "" };
|
|
8
|
-
private static isCleanedUp: boolean = false;
|
|
9
|
-
|
|
10
|
-
static setPersonChurch = (pc: { personId: string, churchId: string }) => {
|
|
11
|
-
|
|
12
|
-
if (pc?.personId && pc.personId && pc.churchId !== this.personIdChurchId.churchId && pc.personId !== this.personIdChurchId.personId) {
|
|
13
|
-
this.personIdChurchId = pc;
|
|
14
|
-
this.createAlertConnection();
|
|
15
|
-
} else {
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
static createAlertConnection = () => {
|
|
20
|
-
if (SocketHelper.personIdChurchId.personId && SocketHelper.socketId) {
|
|
21
|
-
const connection: ConnectionInterface = {
|
|
22
|
-
conversationId: "alerts",
|
|
23
|
-
churchId: SocketHelper.personIdChurchId.churchId,
|
|
24
|
-
displayName: "Test",
|
|
25
|
-
socketId: SocketHelper.socketId,
|
|
26
|
-
personId: SocketHelper.personIdChurchId.personId
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
ApiHelper.postAnonymous("/connections", [connection], "MessagingApi");
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
static init = async () => {
|
|
34
|
-
SocketHelper.cleanup();
|
|
35
|
-
SocketHelper.isCleanedUp = false;
|
|
36
|
-
|
|
37
|
-
if (SocketHelper.socket && SocketHelper.socket.readyState !== SocketHelper.socket.CLOSED) {
|
|
38
|
-
try {
|
|
39
|
-
SocketHelper.socket.close();
|
|
40
|
-
} catch { /* ignore */ }
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
await new Promise((resolve, reject) => {
|
|
44
|
-
try {
|
|
45
|
-
SocketHelper.socket = new WebSocket(CommonEnvironmentHelper.MessagingApiSocket);
|
|
46
|
-
|
|
47
|
-
SocketHelper.socket.onmessage = (event) => {
|
|
48
|
-
if (SocketHelper.isCleanedUp) return;
|
|
49
|
-
|
|
50
|
-
try {
|
|
51
|
-
const payload = JSON.parse(event.data);
|
|
52
|
-
SocketHelper.handleMessage(payload);
|
|
53
|
-
} catch { /* ignore parse errors */ }
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
SocketHelper.socket.onopen = async () => {
|
|
57
|
-
SocketHelper.socket.send("getId");
|
|
58
|
-
|
|
59
|
-
setTimeout(() => {
|
|
60
|
-
resolve(null);
|
|
61
|
-
}, 3000);
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
SocketHelper.socket.onclose = async () => { };
|
|
65
|
-
|
|
66
|
-
SocketHelper.socket.onerror = (error) => {
|
|
67
|
-
reject(error);
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
} catch (error) {
|
|
71
|
-
reject(error);
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
static addHandler = (action: string, id: string, handleMessage: (data: any) => void) => {
|
|
77
|
-
const existing = ArrayHelper.getOne(SocketHelper.actionHandlers, "id", id);
|
|
78
|
-
if (existing !== null) {
|
|
79
|
-
existing.handleMessage = handleMessage;
|
|
80
|
-
} else {
|
|
81
|
-
SocketHelper.actionHandlers.push({ action, id, handleMessage });
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
static removeHandler = (id: string) => {
|
|
86
|
-
SocketHelper.actionHandlers = SocketHelper.actionHandlers.filter(handler => handler.id !== id);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
static removeHandlersByAction = (action: string) => {
|
|
90
|
-
SocketHelper.actionHandlers = SocketHelper.actionHandlers.filter(handler => handler.action !== action);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
static clearAllHandlers = () => {
|
|
94
|
-
SocketHelper.actionHandlers = [];
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
static handleMessage = (payload: SocketPayloadInterface) => {
|
|
98
|
-
if (SocketHelper.isCleanedUp) return;
|
|
99
|
-
|
|
100
|
-
try {
|
|
101
|
-
if (payload.action === "socketId") {
|
|
102
|
-
SocketHelper.socketId = payload.data;
|
|
103
|
-
SocketHelper.createAlertConnection();
|
|
104
|
-
}
|
|
105
|
-
else {
|
|
106
|
-
const matchingHandlers = ArrayHelper.getAll(SocketHelper.actionHandlers, "action", payload.action);
|
|
107
|
-
matchingHandlers.forEach((handler) => {
|
|
108
|
-
try {
|
|
109
|
-
handler.handleMessage(payload.data);
|
|
110
|
-
} catch (error) {
|
|
111
|
-
console.error(`Error in handler ${handler.id}:`, error);
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
} catch (error) {
|
|
116
|
-
console.error("Error handling socket message:", error);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
static cleanup = () => {
|
|
121
|
-
SocketHelper.isCleanedUp = true;
|
|
122
|
-
|
|
123
|
-
// Close socket connection
|
|
124
|
-
if (SocketHelper.socket && SocketHelper.socket.readyState !== SocketHelper.socket.CLOSED) {
|
|
125
|
-
try {
|
|
126
|
-
SocketHelper.socket.close();
|
|
127
|
-
} catch { /* ignore */ }
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Clear references but preserve handlers - they should persist across reconnects
|
|
131
|
-
SocketHelper.socket = null;
|
|
132
|
-
SocketHelper.socketId = null;
|
|
133
|
-
SocketHelper.personIdChurchId = { personId: "", churchId: "" };
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
static disconnect = () => {
|
|
137
|
-
SocketHelper.cleanup();
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
static isConnected = (): boolean => {
|
|
141
|
-
return SocketHelper.socket && SocketHelper.socket.readyState === SocketHelper.socket.OPEN;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
static getConnectionState = (): string => {
|
|
145
|
-
if (!SocketHelper.socket) return "UNINITIALIZED";
|
|
146
|
-
|
|
147
|
-
switch (SocketHelper.socket.readyState) {
|
|
148
|
-
case SocketHelper.socket.CONNECTING:
|
|
149
|
-
return "CONNECTING";
|
|
150
|
-
case SocketHelper.socket.OPEN:
|
|
151
|
-
return "OPEN";
|
|
152
|
-
case SocketHelper.socket.CLOSING:
|
|
153
|
-
return "CLOSING";
|
|
154
|
-
case SocketHelper.socket.CLOSED:
|
|
155
|
-
return "CLOSED";
|
|
156
|
-
default:
|
|
157
|
-
return "UNKNOWN";
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Global cleanup on window unload
|
|
162
|
-
static setupGlobalCleanup = () => {
|
|
163
|
-
if (typeof window !== "undefined") {
|
|
164
|
-
const cleanup = () => {
|
|
165
|
-
SocketHelper.cleanup();
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
window.addEventListener("beforeunload", cleanup);
|
|
169
|
-
window.addEventListener("unload", cleanup);
|
|
170
|
-
|
|
171
|
-
// Also cleanup on page visibility change (when tab is closed)
|
|
172
|
-
document.addEventListener("visibilitychange", () => {
|
|
173
|
-
if (document.visibilityState === "hidden") {
|
|
174
|
-
// Optional: cleanup when tab becomes hidden
|
|
175
|
-
// SocketHelper.cleanup();
|
|
176
|
-
}
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
return cleanup;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
export class UniqueIdHelper {
|
|
2
|
-
|
|
3
|
-
static chars = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
|
|
4
|
-
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
|
|
5
|
-
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0",
|
|
6
|
-
"-", "_"];
|
|
7
|
-
|
|
8
|
-
static alphanumericChars = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
|
|
9
|
-
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
|
|
10
|
-
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"];
|
|
11
|
-
|
|
12
|
-
public static isMissing(obj: any) {
|
|
13
|
-
if (obj === undefined || obj === null) return true;
|
|
14
|
-
else if (obj.toString() === "") return true;
|
|
15
|
-
else return false;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
public static shortId() {
|
|
19
|
-
return this.generate(UniqueIdHelper.chars, 11);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
public static generateAlphanumeric() {
|
|
23
|
-
return this.generate(UniqueIdHelper.alphanumericChars, 11);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
public static generate(charList: string[], length: number) {
|
|
27
|
-
let result = "";
|
|
28
|
-
for (let i = 0; i < length; i++) {
|
|
29
|
-
const idx = Math.floor(Math.random() * charList.length);
|
|
30
|
-
const c = charList[idx];
|
|
31
|
-
result += c;
|
|
32
|
-
}
|
|
33
|
-
return result;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
}
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { ApiHelper } from "@churchapps/helpers"
|
|
2
|
-
import { UserInterface, UserContextInterface, IApiPermission, PersonInterface, LoginUserChurchInterface } from "@churchapps/helpers";
|
|
3
|
-
|
|
4
|
-
export class UserHelper {
|
|
5
|
-
static currentUserChurch: LoginUserChurchInterface;
|
|
6
|
-
static userChurches: LoginUserChurchInterface[];
|
|
7
|
-
static user: UserInterface;
|
|
8
|
-
static churchChanged: boolean = false;
|
|
9
|
-
static person: PersonInterface;
|
|
10
|
-
|
|
11
|
-
static selectChurch = async (context?: UserContextInterface, churchId?: string, keyName?: string) => {
|
|
12
|
-
let userChurch = null;
|
|
13
|
-
|
|
14
|
-
// Ensure userChurches is initialized
|
|
15
|
-
if (!UserHelper.userChurches || !Array.isArray(UserHelper.userChurches)) {
|
|
16
|
-
console.error('UserHelper.userChurches is not initialized or not an array:', UserHelper.userChurches);
|
|
17
|
-
// Try to get userChurches from context if available
|
|
18
|
-
if (context?.userChurches && Array.isArray(context.userChurches)) {
|
|
19
|
-
UserHelper.userChurches = context.userChurches;
|
|
20
|
-
} else {
|
|
21
|
-
console.error('Cannot select church: no valid userChurches available');
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (churchId) {
|
|
27
|
-
UserHelper.userChurches.forEach(uc => {
|
|
28
|
-
if (uc.church.id === churchId) userChurch = uc;
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
else if (keyName) UserHelper.userChurches.forEach(uc => { if (uc.church.subDomain === keyName) userChurch = uc; });
|
|
32
|
-
else userChurch = UserHelper.userChurches[0];
|
|
33
|
-
if (!userChurch) return;
|
|
34
|
-
else {
|
|
35
|
-
UserHelper.currentUserChurch = userChurch;
|
|
36
|
-
UserHelper.setupApiHelper(UserHelper.currentUserChurch);
|
|
37
|
-
// TODO - remove context code from here and perform the logic in the component itself.
|
|
38
|
-
if (context) {
|
|
39
|
-
if (context.userChurch !== null) UserHelper.churchChanged = true;
|
|
40
|
-
context.setUserChurch(UserHelper.currentUserChurch);
|
|
41
|
-
|
|
42
|
-
// Also ensure user is still set in context
|
|
43
|
-
if (UserHelper.user && context.setUser) {
|
|
44
|
-
context.setUser(UserHelper.user);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Update person if available
|
|
48
|
-
if (UserHelper.person && context.setPerson) {
|
|
49
|
-
context.setPerson(UserHelper.person);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
static setupApiHelper(userChurch: LoginUserChurchInterface) {
|
|
56
|
-
ApiHelper.setDefaultPermissions(userChurch.jwt);
|
|
57
|
-
userChurch.apis.forEach(api => { ApiHelper.setPermissions(api.keyName, api.jwt, api.permissions); });
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
static setupApiHelperNoChurch(user: LoginUserChurchInterface) {
|
|
61
|
-
ApiHelper.setDefaultPermissions(user.jwt);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
static checkAccess({ api, contentType, action }: IApiPermission): boolean {
|
|
65
|
-
const config = ApiHelper.getConfig(api);
|
|
66
|
-
if (!config) return false;
|
|
67
|
-
|
|
68
|
-
const permissions = config.permissions || [];
|
|
69
|
-
|
|
70
|
-
let result = false;
|
|
71
|
-
if (permissions !== undefined) {
|
|
72
|
-
permissions.forEach((element: any) => {
|
|
73
|
-
if (element.contentType === contentType && element.action === action) result = true;
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
return result;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
static createAppUrl(appUrl: string, returnUrl: string) {
|
|
80
|
-
const jwt = ApiHelper.getConfig("MembershipApi")?.jwt;
|
|
81
|
-
|
|
82
|
-
if (jwt) {
|
|
83
|
-
return `${appUrl}/login/?jwt=${jwt}&returnUrl=${encodeURIComponent(returnUrl)}`;
|
|
84
|
-
} else {
|
|
85
|
-
return `${appUrl}/login/?returnUrl=${encodeURIComponent(returnUrl)}`;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
static redirectToLogin(returnUrl?: string, handleRedirect?: (url: string) => void) {
|
|
90
|
-
if (typeof window !== "undefined") {
|
|
91
|
-
const currentUrl = returnUrl || window.location.pathname + window.location.search;
|
|
92
|
-
const encodedReturnUrl = encodeURIComponent(currentUrl);
|
|
93
|
-
const loginUrl = `/login?returnUrl=${encodedReturnUrl}`;
|
|
94
|
-
|
|
95
|
-
// Use handleRedirect function if available, otherwise fallback to window.location
|
|
96
|
-
if (handleRedirect) {
|
|
97
|
-
handleRedirect(loginUrl);
|
|
98
|
-
} else {
|
|
99
|
-
window.location.href = loginUrl;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import createCache from "@emotion/cache";
|
|
2
|
-
|
|
3
|
-
const isBrowser = typeof document !== "undefined";
|
|
4
|
-
|
|
5
|
-
// On the client side, Create a meta tag at the top of the <head> and set it as insertionPoint.
|
|
6
|
-
// This assures that MUI styles are loaded first.
|
|
7
|
-
// It allows developers to easily override MUI styles with other styling solutions, like CSS modules.
|
|
8
|
-
export function createEmotionCache() {
|
|
9
|
-
let insertionPoint;
|
|
10
|
-
|
|
11
|
-
if (isBrowser) {
|
|
12
|
-
const emotionInsertionPoint = document.querySelector<HTMLMetaElement>('meta[name="emotion-insertion-point"]');
|
|
13
|
-
insertionPoint = emotionInsertionPoint ?? undefined;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return createCache({ key: "mui-style", insertionPoint });
|
|
17
|
-
}
|