v0.12.0 codes for FCM and rebranded jama to RosterChirp
This commit is contained in:
@@ -42,7 +42,7 @@ export default function Chat() {
|
||||
const [modal, setModal] = useState(null); // 'profile' | 'users' | 'settings' | 'newchat' | 'help' | 'groupmanager'
|
||||
const [page, setPage] = useState('chat'); // 'chat' | 'schedule' | 'groupmessages'
|
||||
const [drawerOpen, setDrawerOpen] = useState(false);
|
||||
const [features, setFeatures] = useState({ branding: false, groupManager: false, scheduleManager: false, appType: 'JAMA-Chat', teamToolManagers: [], isHostDomain: false });
|
||||
const [features, setFeatures] = useState({ branding: false, groupManager: false, scheduleManager: false, appType: 'RosterChirp-Chat', teamToolManagers: [], isHostDomain: false });
|
||||
const [helpDismissed, setHelpDismissed] = useState(true); // true until status loaded
|
||||
const [isMobile, setIsMobile] = useState(window.innerWidth < 768);
|
||||
const [showSidebar, setShowSidebar] = useState(true);
|
||||
@@ -81,7 +81,7 @@ export default function Chat() {
|
||||
branding: settings.feature_branding === 'true',
|
||||
groupManager: settings.feature_group_manager === 'true',
|
||||
scheduleManager: settings.feature_schedule_manager === 'true',
|
||||
appType: settings.app_type || 'JAMA-Chat',
|
||||
appType: settings.app_type || 'RosterChirp-Chat',
|
||||
teamToolManagers: JSON.parse(settings.team_tool_managers || settings.team_group_managers || '[]'),
|
||||
isHostDomain: settings.is_host_domain === 'true',
|
||||
}));
|
||||
@@ -93,53 +93,59 @@ export default function Chat() {
|
||||
|
||||
useEffect(() => {
|
||||
loadFeatures();
|
||||
window.addEventListener('jama:settings-changed', loadFeatures);
|
||||
return () => window.removeEventListener('jama:settings-changed', loadFeatures);
|
||||
window.addEventListener('rosterchirp:settings-changed', loadFeatures);
|
||||
return () => window.removeEventListener('rosterchirp:settings-changed', loadFeatures);
|
||||
}, [loadFeatures]);
|
||||
|
||||
// Register / refresh push subscription
|
||||
// Register / refresh FCM push subscription
|
||||
useEffect(() => {
|
||||
if (!('serviceWorker' in navigator) || !('PushManager' in window)) return;
|
||||
if (!('serviceWorker' in navigator)) return;
|
||||
|
||||
const registerPush = async () => {
|
||||
try {
|
||||
const permission = Notification.permission;
|
||||
if (permission === 'denied') return;
|
||||
if (Notification.permission === 'denied') return;
|
||||
|
||||
// Fetch Firebase config from backend (returns 503 if FCM not configured)
|
||||
const configRes = await fetch('/api/push/firebase-config');
|
||||
if (!configRes.ok) return;
|
||||
const { apiKey, projectId, messagingSenderId, appId, vapidKey } = await configRes.json();
|
||||
|
||||
// Dynamically import the Firebase SDK (tree-shaken, only loaded when needed)
|
||||
const { initializeApp, getApps } = await import('firebase/app');
|
||||
const { getMessaging, getToken } = await import('firebase/messaging');
|
||||
|
||||
const firebaseApp = getApps().length
|
||||
? getApps()[0]
|
||||
: initializeApp({ apiKey, projectId, messagingSenderId, appId });
|
||||
const firebaseMessaging = getMessaging(firebaseApp);
|
||||
|
||||
const reg = await navigator.serviceWorker.ready;
|
||||
const { publicKey } = await fetch('/api/push/vapid-public').then(r => r.json());
|
||||
const token = localStorage.getItem('tc_token') || sessionStorage.getItem('tc_token');
|
||||
|
||||
let sub = await reg.pushManager.getSubscription();
|
||||
|
||||
if (!sub) {
|
||||
// First time or subscription was lost — request permission then subscribe
|
||||
const granted = permission === 'granted'
|
||||
? 'granted'
|
||||
: await Notification.requestPermission();
|
||||
if (Notification.permission !== 'granted') {
|
||||
const granted = await Notification.requestPermission();
|
||||
if (granted !== 'granted') return;
|
||||
sub = await reg.pushManager.subscribe({
|
||||
userVisibleOnly: true,
|
||||
applicationServerKey: urlBase64ToUint8Array(publicKey),
|
||||
});
|
||||
}
|
||||
|
||||
// Always re-register subscription with the server (keeps it fresh on mobile)
|
||||
const fcmToken = await getToken(firebaseMessaging, {
|
||||
vapidKey,
|
||||
serviceWorkerRegistration: reg,
|
||||
});
|
||||
if (!fcmToken) return;
|
||||
|
||||
const token = localStorage.getItem('tc_token') || sessionStorage.getItem('tc_token');
|
||||
await fetch('/api/push/subscribe', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
|
||||
body: JSON.stringify(sub.toJSON()),
|
||||
body: JSON.stringify({ fcmToken }),
|
||||
});
|
||||
console.log('[Push] Subscription registered');
|
||||
console.log('[Push] FCM subscription registered');
|
||||
} catch (e) {
|
||||
console.warn('[Push] Subscription failed:', e.message);
|
||||
console.warn('[Push] FCM subscription failed:', e.message);
|
||||
}
|
||||
};
|
||||
|
||||
registerPush();
|
||||
|
||||
// Bug A fix: re-register push subscription when app returns to foreground
|
||||
// Mobile browsers can drop push subscriptions when the app is backgrounded
|
||||
const handleVisibility = () => {
|
||||
if (document.visibilityState === 'visible') registerPush();
|
||||
};
|
||||
@@ -263,7 +269,7 @@ export default function Chat() {
|
||||
// so we force logout unconditionally — the new session will reconnect cleanly)
|
||||
localStorage.removeItem('tc_token');
|
||||
sessionStorage.removeItem('tc_token');
|
||||
window.dispatchEvent(new CustomEvent('jama:session-displaced'));
|
||||
window.dispatchEvent(new CustomEvent('rosterchirp:session-displaced'));
|
||||
};
|
||||
|
||||
// Online presence
|
||||
@@ -320,7 +326,7 @@ export default function Chat() {
|
||||
if (isMobile) {
|
||||
setShowSidebar(false);
|
||||
// Push a history entry so swipe-back returns to sidebar instead of exiting the app
|
||||
window.history.pushState({ jamaChatOpen: true }, '');
|
||||
window.history.pushState({ rosterchirpChatOpen: true }, '');
|
||||
}
|
||||
// Clear notifications and unread count for this group
|
||||
setNotifications(prev => prev.filter(n => n.groupId !== id));
|
||||
@@ -334,7 +340,7 @@ export default function Chat() {
|
||||
setShowSidebar(true);
|
||||
setActiveGroupId(null);
|
||||
// Push another entry so subsequent back gestures are also intercepted
|
||||
window.history.pushState({ jamaChatOpen: true }, '');
|
||||
window.history.pushState({ rosterchirpChatOpen: true }, '');
|
||||
}
|
||||
};
|
||||
window.addEventListener('popstate', handlePopState);
|
||||
|
||||
Reference in New Issue
Block a user