PageSourceSearch

https://tradeintelx.com/firebase-messaging-sw.js

js tradeintelx.com collected 2026-10-02 01:48:04 UTC 8,316 bytes, 238 lines download raw bytes

1// Firebase Cloud Messaging Service Worker � Production Grade
2// Handles push notifications when the app is in the background or closed.
3// Covers all notification types: messages, price alerts, news, AI insights,
4// education goals, analytics goals, achievements.
5
6importScripts('https://www.gstatic.com/firebasejs/12.4.0/firebase-app-compat.js');
7importScripts('https://www.gstatic.com/firebasejs/12.4.0/firebase-messaging-compat.js');
8
9firebase.initializeApp({
10  apiKey: "AIzaSyBKmutM0ialeKxZvGaKx56rISFCKpBdmf4",
11  authDomain: "tradeintelx.com",
12  projectId: "tradeintexd",
13  storageBucket: "tradeintexd.firebasestorage.app",
14  messagingSenderId: "788757341214",
15  appId: "1:788757341214:web:7c3644d2c33d975cf5fa0f"
16});
17
18const messaging = firebase.messaging();
19
20// Deep-link URL map by notification type — keep in sync with nativePushService.js and pushNotifications.js
21const URL_MAP = {
22  // Chat / messages
23  message:              '/chat',
24  direct_message:       '/chat',
25  group_message:        '/chat',
26  mentor_message:       '/chat',
27  chat:                 '/chat',
28  missed_call:          '/chat',
29  // Watchlist / price alerts
30  price_alert:          '/watchlist',
31  watchlist_alert:      '/watchlist',
32  // News / AI
33  bullish_news:         '/news',
34  news_alert:           '/news',
35  market_news:          '/news',
36  ai_insight:           '/trade-analytics?tab=ai',
37  ai_alert:             '/trade-analytics?tab=behavior',
38  pattern_alert:        '/trade-analytics?tab=decisions',
39  // Trade logger
40  trade:                '/trade-logger',
41  trade_win:            '/trade-logger',
42  trade_loss:           '/trade-logger',
43  broker_trade_import:  '/trade-logger',
44  streak_milestone:     '/trade-logger',
45  streak_broken:        '/trade-logger',
46  // Enforcement / violations
47  violation:            '/enforcement',
48  guardrail:            '/enforcement',
49  // Today / session
50  session:              '/today',
51  session_start:        '/today',
52  daily_goal_met:       '/today',
53  habit_reminder:       '/today',
54  journal_reminder:     '/today',
55  rule_review:          '/today',
56  // Analytics
57  analytics_goal:       '/trade-analytics',
58  weekly_summary:       '/trade-analytics',
59  daily_summary:        '/today?recap=1',
60  // Achievements
61  achievement:          '/trade-analytics?tab=xp',
62  achievement_unlocked: '/trade-analytics?tab=xp',
63  level_up:             '/trade-analytics?tab=xp',
64  // Education
65  education_goal:       '/education',
66  education_badge:      '/education',
67  coaching_session:     '/education',
68  // Briefing
69  briefing_ready:       '/today?briefing=1',
70  proactive_nudge:      '/trade-analytics?tab=ai',
71  // Agent / Bot signals
72  pending_signal:       '/agent?workspace=pipeline',
73  copy_signal:          '/agent?workspace=pipeline',
74  bot_post:             '/community?tab=feed',
75  // Social / community
76  new_follower:         '/community?tab=feed',
77  social_like:          '/community?tab=feed',
78  social_comment:       '/community?tab=feed',
79  social_mention:       '/community?tab=feed',
80  post_like:            '/community?tab=feed',
81  post_comment:         '/community?tab=feed',
82  mention:              '/community?tab=feed',
83  community:            '/community?tab=feed',
84  // System
85  system:               '/',
86};
87
88// Vibration patterns per type
89const VIBRATION = {
90  message:           [100, 50, 100],
91  direct_message:    [100, 50, 100],
92  group_message:     [100, 50, 100, 50, 100],
93  price_alert:       [300, 100, 300, 100, 300],
94  watchlist_alert:   [300, 100, 300, 100, 300],
95  bullish_news:      [150, 75, 150, 75, 150],
96  ai_insight:        [200, 100, 200],
97  achievement:       [100, 50, 100, 50, 200],
98  education_goal:    [100, 50, 100, 50, 200],
99  analytics_goal:    [100, 50, 100, 50, 200],
100  missed_call:       [200, 100, 200, 100, 200],
101  default:           [100, 50, 100],
102};
103
104function getActions(type) {
105  switch (type) {
106    case 'message':
107    case 'direct_message':
108    case 'group_message':
109    case 'mentor_message':
110      return [
111        { action: 'reply',   title: 'Reply'     },
112        { action: 'dismiss', title: 'Dismiss'   },
113      ];
114    case 'price_alert':
115    case 'watchlist_alert':
116      return [
117        { action: 'view',    title: 'View Watchlist' },
118        { action: 'dismiss', title: 'Dismiss'   },
119      ];
120    case 'bullish_news':
121    case 'news_alert':
122    case 'ai_insight':
123      return [
124        { action: 'view',    title: 'Read Now'  },
125        { action: 'dismiss', title: 'Dismiss'   },
126      ];
127    case 'achievement':
128    case 'education_goal':
129    case 'education_badge':
130    case 'analytics_goal':
131      return [
132        { action: 'view',    title: 'View'      },
133      ];
134    case 'missed_call':
135      return [
136        { action: 'callback', title: '📞 Call Back' },
137        { action: 'dismiss',  title: 'Dismiss'     },
138      ];
139    default:
140      return [
141        { action: 'open',    title: 'Open App'  },
142        { action: 'dismiss', title: 'Dismiss'   },
143      ];
144  }
145}
146
147// Background message handler � fires when app is NOT in the foreground
148messaging.onBackgroundMessage((payload) => {
149  console.log('[FCM-SW] Background message received:', payload);
150
151  const { notification, data = {} } = payload;
152  const title  = notification?.title || data?.title || 'Redline';
153  const body   = notification?.body  || data?.body  || 'You have a new notification';
154  const type   = data?.type || 'system';
155  const icon   = notification?.icon  || data?.icon  || '/icons/icon-192x192.png';
156
157  let targetUrl = data?.url || data?.actionUrl || URL_MAP[type] || '/';
158
159  if (['message','direct_message','group_message','mentor_message','chat'].includes(type) && data?.conversationId) {
160    targetUrl = '/chat?conversation=' + encodeURIComponent(data.conversationId);
161  }
162  if (['bullish_news','news_alert','ai_insight'].includes(type) && data?.articleUrl) {
163    targetUrl = '/news?url=' + encodeURIComponent(data.articleUrl);
164  }
165
166  const tag = data?.tag || (type + '-' + Date.now());
167
168  return self.registration.showNotification(title, {
169    body,
170    icon,
171    badge:              '/icons/icon-96x96.png',
172    image:              notification?.image || data?.image || undefined,
173    tag,
174    renotify:           true,
175    requireInteraction: data?.priority === 'critical' || data?.priority === 'high',
176    silent:             false,
177    vibrate:            VIBRATION[type] || VIBRATION.default,
178    data:               Object.assign({}, data, { url: targetUrl, type }),
179    actions:            getActions(type),
180    timestamp:          Date.now(),
181  });
182});
183
184// Notification click handler
185self.addEventListener('notificationclick', (event) => {
186  event.notification.close();
187  if (event.action === 'dismiss') return;
188
189  const data = event.notification.data || {};
190  // Resolve URL: explicit url field → actionUrl → type-based map → home
191  const type      = data.type || 'system';
192  const targetUrl = data.url || data.actionUrl || URL_MAP[type] || '/';
193  // Absolute URL → strip to path+search+hash for same-origin navigation
194  let navPath = targetUrl;
195  try {
196    if (targetUrl.startsWith('http')) {
197      const u = new URL(targetUrl);
198      navPath = u.pathname + u.search + u.hash;
199    }
200  } catch (_) { navPath = '/'; }
201
202  event.waitUntil(
203    clients.matchAll({ type: 'window', includeUncontrolled: true }).then((clientList) => {
204      for (var i = 0; i < clientList.length; i++) {
205        var client = clientList[i];
206        if (client.url.indexOf(self.location.origin) === 0 && 'focu
206s' in client) {
207          return client.focus().then(function(c) { return c.navigate(navPath); });
208        }
209      }
210      if (clients.openWindow) {
211        return clients.openWindow(navPath);
212      }
213    })
214  );
215});
216
217// Notification dismissed
218self.addEventListener('notificationclose', (event) => {
219  console.log('[FCM-SW] Notification dismissed:', event.notification.tag);
220});
221
222// SW lifecycle
223self.addEventListener('message', (event) => {
224  if (event.data && event.data.type === 'SKIP_WAITING') {
225    console.log('[FCM-SW] Skip waiting');
226    self.skipWaiting();
227  }
228});
229
230self.addEventListener('install', function() {
231  console.log('[FCM-SW] Installed');
232  self.skipWaiting();
233});
234
235self.addEventListener('activate', function(e) {
236  console.log('[FCM-SW] Activated');
237  e.waitUntil(self.clients.claim());
238});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.