Optimizing this function to reduce dead band when reading a pressure transducer that has calibration device under test information

When reading a pressure transducer, I’m currently using the following code to apply the “device under test data” from the calibration report. This significantly improves any dead band across the whole scope; vs just using the entire pressure range. What I’m looking to understand is there a better way to optimize this vs a for loop with javascript? Is there a better way to determine which range segment of calibration data the output voltage is with in?

The application is using a web worker to read the values from the transducer every 250ms, pushes them into an array with a set length, to be averaged every 1000ms and then returned to application and plotted to a graph.

Current code to process readings (code works, just looking for input & feedback):

    /**
     * Example device under test calibration data
     * 4-20ma 0-10,000psi Transducer
     * Column 0: Voltage in volts
     * Column 1: Pressure in psi
     */
    const CALIBRATION_DATA = [
      [4.01, 0.0],
      [7.20, 2000.1],
      [10.41, 4000.0],
      [13.62, 6000.2],
      [16.81, 8000.4],
      [20.02, 9999.0],
    ];

    let activeSegmentIndex = 0;

    function getPsi(volts: number, CALIBRATION_DATA: number[][]): number {
      let lower = CALIBRATION_DATA[activeSegmentIndex];
      let upper = CALIBRATION_DATA[activeSegmentIndex + 1];
    
      if (!(volts >= lower[0] && volts <= upper[0])) {
        for (let i = 0; i < CALIBRATION_DATA.length - 1; i++) {
          if (volts >= CALIBRATION_DATA[i][0] && volts <= CALIBRATION_DATA[i + 1][0]) {
            // Found the segment for the current voltage
            activeSegmentIndex = i;
            lower = CALIBRATION_DATA[i];
            upper = CALIBRATION_DATA[i + 1];
            break;
          }
        }
      }
      // Calculate slope and offset for the segment
      const slope = (upper[1] - lower[1]) / (upper[0] - lower[0]);
      const offset = lower[1] - (slope * lower[0]);
      const calculatedValue = volts * slope + offset;
      return Number(calculatedValue.toFixed(0));
     }

Most common formula using entire pressure range but introduces dead band.

  • P=PR∗(Vr−VL)/(Vu−VL)
  • P = Pressure
  • PR = Pressure Range (Transducer lowestValue to HighestValue i.e 0-10k)
  • Vr = Volts reading
  • VL = Volts lower limit
  • Vu = Volts upper limit

And for sake of entirety here is the entire WebWorker

/// <reference lib="webworker" />

import { DoWork, runWorker } from 'observable-webworker';
import { fromFetch } from 'rxjs/fetch';
import { Observable, of } from "rxjs";
import { catchError, delay, map, repeat, scan, switchMap, tap } from "rxjs/operators";
import { WebRelayStateModel } from '@dis/webrelay';
import { PreferencesStateModel } from '../../pattern/state/preferences/preferences.state';


export class WebRelayWorker implements DoWork<any, any> {
  public work(input$: Observable<PreferencesStateModel>) {
    return input$.pipe(
      switchMap(input => {
        const { webRelayIp, pollInterval, observedInput, inputs } = input;
        const webRelayState$ = getRelayState(webRelayIp, pollInterval);
        let webRelayState: WebRelayStateModel;
        return webRelayState$.pipe(
            tap(_webRelayState => webRelayState = _webRelayState),

            // Map the web relay state to the input being monitored;
            map((webRelayState: WebRelayStateModel) => Number(webRelayState[`analogInput${observedInput + 1}`])),
            // tap(console.log),

            // Calculate to psi apply offset and drop any negative numbers
            map(value => getPsi(value, inputs[observedInput].dutData)),

            // Calculate offset from zeroing data
            map(value => value - Number(inputs[observedInput].offset)),
            //tap(console.log),

            map(value => value > 0 ? value : 0),
            //tap(console.log),

            // Buffer the amount of readings that is set in preferences
            scan((acc, curr) => {
              acc.push(curr);
              if (acc.length > Number(inputs[observedInput].bufferArrayLength)) {
                acc.shift();
              }
              return acc;
            }, []),
            // tap(console.log),
            // Avervage the collected readings into current pressure;
            map(arr => Number((arr.reduce((acc, current) => acc + current, 0) / arr.length).toFixed(0))),
            // tap(console.log),
            // Return the web relay state and the calculated value
            map(value => [webRelayState, value]),
          )
      })
    )
  }
}

runWorker(WebRelayWorker);

/**
 * Example device under test calibration data
 * Column 0: Voltage in volts
 * Column 1: Pressure in psi
 */
const CALIBRATION_DATA = [
  [4.01, 0.0],
  [7.20, 2000.1],
  [10.41, 4000.0],
  [13.62, 6000.2],
  [16.81, 8000.4],
  [20.02, 9999.0],
];

let activeSegmentIndex = 0;

function getPsi(volts: number, CALIBRATION_DATA: number[][]): number {
  let lower = CALIBRATION_DATA[activeSegmentIndex];
  let upper = CALIBRATION_DATA[activeSegmentIndex + 1];

  if (!(volts >= lower[0] && volts <= upper[0])) {
    for (let i = 0; i < CALIBRATION_DATA.length - 1; i++) {
      if (volts >= CALIBRATION_DATA[i][0] && volts <= CALIBRATION_DATA[i + 1][0]) {
        // Found the segment for the current voltage
        activeSegmentIndex = i;
        lower = CALIBRATION_DATA[i];
        upper = CALIBRATION_DATA[i + 1];
        break;
      }
    }
  }

  // Calculate slope and offset for the segment
  const slope = (upper[1] - lower[1]) / (upper[0] - lower[0]);
  const offset = lower[1] - (slope * lower[0]);
  const calculatedValue = volts * slope + offset;
  return Number(calculatedValue.toFixed(0));
}

function getRelayState(webRelayIp: string, pollInterval: number, simulateData?: boolean) {
  const webRelayUrl = `${webRelayIp}/state.json`;
  return fromFetch(webRelayUrl)
    .pipe(
      switchMap(response => {
        if (response.ok) {
          // OK return data
          return response.json();
        } else {
          // Server is returning a status requiring the client to try something else.
          return of({ error: true, message: `Error ${response.status}` });
        }
      }),
      catchError(err => {
        // Network or other error, handle appropriately
        console.error(err);
        return of({ error: true, message: err.message })
      }),
      delay(pollInterval),
      repeat(),
    );
}

How to return an array of strings that are wrapped individual in html tags? [closed]

Given an array of strings, return an array of strings that wraps each
of the original strings in an HTML-like <p></p> tag.

E.g. given: ["Bulbasaur", "Charmander", "Squirtle"]

return: ["<p>Bulbasaur</p>", "<p>Charmander</p>", "<p>Squirtle</p>"]

I tried to map of the array and return each item in the array and used .join method to join the tags but the result was an error.

Asking a question of callbacks and functions:

const addTwo = num => {
  return num + 2;
}

const checkConsistentOutput = (func, val) => {
  const checkA = val + 2;
  const checkB = func(val);
  return checkA === checkB ? func(val) : 'inconsistent results'; 
}

console.log(checkConsistentOutput(addTwo, 5));

This returns 7. So how is CheckA === to CheckB? I’m lost. Would appreciate an explanation or someone to give me a link to a video where even I could understand callbacks and higher order functions, because most of it seems like made up logic, I get stuck on that part.

Expo – previously visible ScrollView suddenly disappeared

In my React Native app I have a component (Categories) that displays emoji-labeled category pills inside a horizontal ScrollView. It was rendering fine earlier, but recently it disappeared from the screen entirely — no errors, just not visible.

After debugging, I confirmed that:

The data being mapped is valid (an array of strings from a custom emoji mapping)

The mapping function works (getEmoji() returns valid emojis)

The component is mounted, but nothing renders visually

This issue only happens when ScrollView is horizontal

import React, { useEffect } from "react";
import { View, Text, SafeAreaView, StyleSheet, TextStyle, ScrollView, TouchableOpacity } from "react-native";
import emojiList from 'emoji.json';

const emojiMap: Record<string, string> = {
  Birthday: 'party popper',
  Bridal: 'bouquet',
  Halloween: 'jack-o-lantern',
  'Book Day': 'open book',
  Engaged: 'ring',
  'Dad': 'necktie',
  Graduate: 'graduation cap',
  'Mum': 'red heart',
  'New Born': 'baby bottle',
  Religious: 'crescent moon',
  Wedding: 'love letter',
};

type EmojiItem = { name: string; char: string };

function getEmoji(label: string) {
  const name = emojiMap[label];
  if (!name) return '❓';
  const found = (emojiList as EmojiItem[]).find(e => e.name.toLowerCase() === name.toLowerCase());
  return found ? found.char : '❓';
}


export default function Categories(){
    useEffect(() => {
        categories.forEach(label => {
            console.log(`${label}:`, getEmoji(label));
          });
    }, [])
    const categories = Object.keys(emojiMap);
    return (
        <>
            <View style={styles.subTitleContainer}>
                <Text style={{fontSize: 18, fontFamily: 'Lexend-SemiBold', color: "#3A3D46"}}>Explore by Theme</Text>
                {/* <Text style={{fontSize: 16, fontFamily: 'Inter-Medium', color: "#93939B"}}>View All</Text> */}
            </View>
                <ScrollView horizontal showsHorizontalScrollIndicator={false} >
                    <View style={{marginHorizontal: 10, flexDirection: "row"}}>
                        {categories.map((label: string) => (
                            <TouchableOpacity key={label} style={styles.categoryPill}>
                                <View style={styles.categoryIconContainer}>
                                    <Text style={styles.emoji}>{getEmoji(label)}</Text>
                                </View>
                                <Text style={styles.categoryLabel}>{label}</Text>
                            </TouchableOpacity>
                        ))}
                    </View>
                </ScrollView>
        </>
    )
}

const styles = StyleSheet.create({
    subTitleContainer: {
        flexDirection: "row",
        justifyContent: "space-between",
        marginTop: 20,
        marginLeft: 15,
        marginRight: 25,
        alignItems: "center",
    },

    categoryContainer: {
        alignItems: "center",
        marginTop: 25,
        marginHorizontal: 4,
        width: 85,
    },

    categoryPill: {
        flexDirection: 'row',
        alignItems: 'center',
        backgroundColor: '#f65c3b',
        borderRadius: 25,
        paddingVertical: 6,
        paddingHorizontal: 12,
        marginTop: 25,
        marginHorizontal: 6,
        minWidth: 120,
    },

    categoryIconContainer: {
        width: 40,
        height: 40,
        borderRadius: 20,
        backgroundColor: '#fff',
        alignItems: 'center',
        justifyContent: 'center',
        marginRight: 8,
    },

    emoji: {
        fontSize: 20,
        textAlign: 'center',
    },
    
    categoryLabel: {
        fontSize: 14,
        fontFamily: 'Lexend-SemiBold',
        color: '#FFFFFF',
        flex: 1,
    }
});

Get previewed image’s element in mobile Google image search

On a modern mobile browser, when you are on Google’s image search results page, if you click an image, it’ll bring up a swipeable panel that previews the image result with, at most, 8 related images below it. Using JavaScript, how can I get the image element of the previewed image in that panel? I looked at the DOM using developer tools, but I couldn’t make out any way to identify it. The CSS classes are random and I couldn’t figure out a path to get it with XPath.

Power BI report not embedding inside ASP.Update panel

I am using the ASP.NET WebForms framework and embedding the report using a JavaScript SDK approach.

The powerBIReportContainer div does not display the report when it is placed inside an <asp:UpdatePanel>.

Note: using Wizard forms as well.

When the powerBIReportContainer div is positioned outside the </asp:UpdatePanel>, the Power BI report renders correctly.

Below is my code snippet. Could you please assist me with what changes I need to make in my JavaScript or any other solutions?



</ContentTemplate>
</asp:UpdatePanel>

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/powerbi.js"></script>
<asp:Button ID="btnGenerateReport" runat="server" Text="Generate Report" CssClass="genric_btn green" OnClick="btnGenerateReport_Click" />
<div id="powerBIReportContainer" style="height:600px;width:100%;"></div>
<script type="text/javascript">
var embedUrl = '<%= EmbedUrl %>';
var embedToken = '<%= EmbedToken %>';
var reportId = '<%= ReportId %>';

function embedPowerBIReport() {
if (!embedUrl || !embedToken || !reportId) return;

var models = window['powerbi-client'].models;
var embedConfig = {
type: 'report',
isPaginatedReport: true,
id: reportId,
embedUrl: embedUrl,
accessToken: embedToken,
tokenType: models.TokenType.Embed,
settings: {
panes: {
filters: { visible: false },
pageNavigation: { visible: true }
},
navContentPaneEnabled: true
}
};
var reportContainer = document.getElementById('powerBIReportContainer');
powerbi.reset(reportContainer);
var report = powerbi.embed(reportContainer, embedConfig);

// Show page navigation after report is loaded
report.on("loaded", function () {
report.updateSettings({
panes: {
pageNavigation: { visible: true }
}
});
});
}

window.onload = function () {
embedPowerBIReport();
};

Sys.Application.add_load(embedPowerBIReport);

</script>
</div>
</div>

</asp:Content>

I assumed, this is because of partical page load of update panel and tried calling with Sys.Application.add_load(embedPowerBIReport); But still not working.

I assumed, this is because of partical page load of update panel and tried calling with Sys.Application.add_load(embedPowerBIReport); But still not working.

In my project most of the screens are with update panel and wizard steps.
we need to embed power BI report inside update panel, instead of keeping outside the panel.

How to write a relative path for fetch() that works when my site is deployed in subdirectories?

Context

I’m building a web application using ASP.NET MVC, and I need to use the fetch() API to send data from the front-end to a controller action. The tricky part is ensuring that the fetch() path works correctly when the app is deployed under different base URLs.

For example, the app might be hosted at:

  • https://localhost:5000
  • https://www.example.com/MyApp
  • https://www.example.com/qa/MyApp

Problem

If I use a path like this:

fetch("/Home/Data")
  .then(response => response.json())
  .then(data => console.log(data));

It works fine on localhost. But when deployed to a subdirectory like https://www.example.com/MyApp/ or https://www.example.com/qa/MyApp/, it tries to fetch from https://www.example.com/Home/Data — which is incorrect.

Question

How can I write the fetch() call so that it works correctly regardless of the base path the app is deployed under?

Is there a reliable approach to make the path dynamic or relative to the current folder in ASP.NET MVC apps?

How to write a relative path for fetch() that works with different base paths?

I’m using the fetch() API in a web application that may be hosted under different base paths, for example:

  • https://localhost:5000
  • https://www.example.com/MyApp
  • https://www.example.com/qa/MyApp

I want to make sure the relative path in fetch() works correctly regardless what is the base URL.

For instance, if I’m doing this:

fetch("/Home/Data")
  .then(response => response.json())
  .then(data => console.log(data));

The app works well at my localhost because the request will go to https://localhost:5000/Home/Data, but if I publish the app to either https://www.example.com/MyApp or https://www.example.com/qa/MyApp, it will end up at https://www.example.com/Home/Data. How could I write my fetch() so that it will resolve the base path correctly? What is the best practice here?

How to retain entered values across pages in MVC pagination without using a database?

I’m working on an ASP.NET MVC view where I’m displaying paginated data (10 rows per page) with editable fields (like textboxes) for each row.
When I move from one page to another, the values I enter on the previous page are lost when I navigate back.

I want to retain the entered values across pages until the user clicks a final Save button, but I do not want to use a database or session to store intermediate changes.

Is there a way to persist user input between pages (such as using client-side or view model techniques) while keeping all changes in memory until the final save?

Selected time automatically shifting and unselected

I have configured the jquery datetimepicker by cdn. The datetimepicker have shifting the time 1 hour back from the input field value for each time opened the datetimepicker.

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jquery-datetimepicker/2.5.20/jquery.datetimepicker.min.css" />
</head>
<body>

<div class="d-flex">
  <input required="required"
         name="shift-datetime"
         id="shift-datetime"
         value="Jul 15, 2025 02:30 PM"
         class="form-control"
         autocomplete="off"
         onchange="onChangeSites()">
</div>

<!-- jQuery first -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<!-- Then datetimepicker -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-datetimepicker/2.5.20/jquery.datetimepicker.full.min.js"></script>

<script>
  function onChangeSites() {
    console.log("Datetime changed to:", $('#shift-datetime').val());
  }

  $('#shift-datetime').datetimepicker({
    format: "M d, Y h:i A",
    formatTime: "h:i A",
    step: 30,
    timepicker: true,
    hours12: false,
    defaultTime: false,
    seconds: true
  });
</script>

</body>
</html>

Chained/Nested tests with jest

I have the following function i want to test:

ddb.service.ts

async batchGet(keys: DynamoBatchRequest[], sorted = true, doNotFilterTtl = false): Promise<ConfigurationItem[]> {
        const params: BatchGetCommandInput = {
            RequestItems: {},
        };
        const start = Date.now();

        LOGGER.debug(`DynamoDB before BatchGet: ${JSON.stringify(keys)}`);
        params.RequestItems[this.tableName] = { Keys: keys };

        return this.ddbClient
            .send(new BatchGetCommand(params))
            .then((data) => {
                //return this.processGetWithRetry(data, [], start, 0, doNotFilterTtl);
                return Promise.resolve([{ value: 'test1' }]);
            })
            /*.then((unsortedResult) => {
                if (!sorted) {
                    LOGGER.debug('Return unsorted result.');
                    return unsortedResult;
                }
                return this.sortBatchResult(keys, unsortedResult);
            })*/
            .catch((err) => {
                LOGGER.error(`DynamoDB BatchGet error: ${JSON.stringify(err)}, keys: ${JSON.stringify(keys)}`);
                ErrorController.abortWithInternalServerError(`Failed getting item with error: ${JSON.stringify(err)}`);
            });
}

I want to mock this.ddbClient.send to control the output of each promise in this chain, but i dont get it to work.
To make it easier for now, i commented out the second then() function.
So my first goal would be to mock the return value of the first then() function.

This is my current test:

db.service.test.ts

test.each(tests)('batchGet', async (mockedValue, result) => {
            const mock = {
                send: jest.fn().mockResolvedValueOnce(jest.fn().mockReturnValueOnce(Promise.resolve([{ value: 'test2' }]))),
            };

            // @ts-ignore
            ddbService.ddbClient = mock;

            let res = await ddbService.batchGet([], false);
            expect(res).toEqual(result);
        });

My understanding is, that this.ddbClient.send() should return a promise which also returns a promise, right? But it doesnt work…

Any help would be very much appreciated

Dynamic State-wise Coloring in India Map using amCharts 5

I’m building an interactive India map using amCharts 5, where each state should be dynamically colored based on its purchase contribution percentage. The contribution data is coming from the backend, and I’m assigning colors from a predefined palette according to each state’s ranking.

I’m successfully generating a mapData array where each object includes a fill color (using am5.color(0xRRGGBB)) along with the state name and its contribution.

While the legend correctly shows the dynamic colors, the actual map polygons do not reflect these colors. Instead, all states appear in the same default shade (usually grey), regardless of their fill values in the data.

I’m using fillField: “fill” in my MapPolygonSeries configuration, which (according to the docs) should tell amCharts to use the fill field from the data object. However, it doesn’t seem to apply the fill to the polygons as expected.

I’ve also tried using templateField: “fill” inside the polygon template configuration, but that didn’t fix the issue either.I want that state present in map also need to be the same color of the legend

I assigned a fill color to each state in mapData and set fillField: “fill” in the MapPolygonSeries config. I expected each state polygon to be filled with its corresponding color, matching the legend.

However, all states appeared in the same default color (gray), even though the legend showed the correct dynamic colors. I also tried using templateField: “fill” in the polygon template, but that didn’t change the outcome.[I want that the legend and map should be of same color

<script src="https://cdn.amcharts.com/lib/5/map.js"></script>
    <script src="https://cdn.amcharts.com/lib/5/geodata/indiaLow.js"></script>
    <script>      
        const statePurchasesData = {{{ json state_purchases_contribution }}};
        am5.ready(function () {
            try {
                console.log("[am5] Raw data:", statePurchasesData);
                const root = am5.Root.new("india-purchases-map");
                root.container.set("layout", root.horizontalLayout);
                root.setThemes([am5themes_Animated.new(root)]);
                // Map chart container
                const chart = root.container.children.push(am5map.MapChart.new(root, {
                    panX: false,
                    panY: false,
                    wheelX: "none",
                    wheelY: "none",
                    projection: am5map.geoMercator()
                }));
                console.log("[am5] MapChart initialized.");
                // ID mapping
                const idMap = {};
                am5geodata_indiaLow.features.forEach(feature => {
                    const name = feature.properties.name.trim();
                    const id = feature.id;
                    if (name && id) {
                        idMap[name] = id;
                    }
                });
                // Convert data
                const data = (statePurchasesData || []).map(item => {
                    const state = (item.state || "").replace(/ - $/, "").trim();
                    const id = idMap[state];
                    if (!id) console.warn("[am5] State not mapped to ID:", state);
                    return {
                        id,
                        name: state,
                        purchases_contribution: item.purchases_contribution
                    };
                }).filter(d => d.id);
                const colorPalette = [
                    0x004d40, 0x00796b, 0x009688, 0x26a69a, 0x4db6ac,
                    0x80cbc4, 0xb2dfdb, 0xdcedc8, 0xf0f4c3, 0xe0e0e0
                ];
                data.forEach((item, index) => {
                    item.polygonSettings = {
                        fill: am5.color(colorPalette[index] || 0xe0e0e0)
                    };
                });
                // Polygon series
                const polygonSeries = chart.series.push(am5map.MapPolygonSeries.new(root, {
                    geoJSON: am5geodata_indiaLow,
                    valueField: "purchases_contribution",
                    nameField: "name",
                    calculateAggregates: true
                }));
                polygonSeries.data.setAll(data);
                console.log("[am5] Polygon series data set.");
                polygonSeries.set("heatRules", [{
                    target: polygonSeries.mapPolygons.template,
                    dataField: "purchases_contribution",
                    min: am5.color(0x000000),
                    max: am5.color(0xFFFFFF),
                    key: "fill"
                }]);
                polygonSeries.mapPolygons.template.setAll({
                    tooltipText: "{name}: {purchases_contribution}%",
                    interactive: true
                });
                polygonSeries.mapPolygons.template.adapters.add("fill", (fill, target) => {
                    const dataItem = target.dataItem;
                    return dataItem && dataItem.dataContext.purchases_contribution != null
                        ? fill
                        : am5.color(0xe0e0e0);
                });
                // Legend
                const legend = root.container.children.push(am5.Legend.new(root, {
                    nameField: "name",
                    fillField: "fill",
                    strokeField: "fill",
                    layout: root.verticalLayout,
                    y: am5.percent(50),
                    centerY: am5.percent(50),
                    marginRight: 80,
                    width: 150
                }));
                polygonSeries.events.on("datavalidated", function () {
                    console.log("[am5] Data validated. Populating legend.");
                    const legendData = polygonSeries.dataItems
                        .filter(dataItem => dataItem.get("value") !== undefined) // Only include those with data
                        .map(dataItem => {
                            const polygon = dataItem.get("mapPolygon");
                            const dataContext = polygon.dataItem.dataContext;
                            return {
                                name: `${ dataContext.name } - ${ dataContext.purchases_contribution }%`,
                                fill: polygon.get("fill") || am5.color(0x999999)
                        };
                });
                console.log("[am5] Filtered legend data:", legendData);
                legend.data.setAll(legendData);
            });
            } catch (err) {
            console.error("[am5] Error rendering map:", err);
        }
        });
    </script>

](https://i.sstatic.net/65d6sfNB.png)

What are the sequential steps to create a design system, and what is the responsibility scope of the front-end developer? [closed]

I’m trying to understand how to approach building a design system in a structured way. I want to know:

  • What are the key steps or phases involved in creating a complete design system?
  • What parts are typically handled by front-end developers, and what is usually out of their scope?
  • Are there best practices or tools commonly used by developers when implementing a design system?

I explored the Atomic Design methodology and researched general concepts around design systems, but I’m still unclear about the exact steps developers should follow and what their responsibilities are.

I’d appreciate any insights or real-world experience on this topic.

Why is the browser main thread doing nothing?

I am encountering a problem that my main thread is unoccupied and nothing is rendered on the screen. DCL fires at 500ms, but FCP and LCP fire 2s later. In this example I am not loading any CSS, only HTML and a JS module. I don’t know what the right question is because I am utterly confused. Why is this happening? What am I missing? What is the Chrome’s behaviour?

I will appreciate any clues/suggestions/guesses.

Trace

Service worker ‘notificationclick’ event not firing on notification click (Firebase FCM)

I am trying to implement push notifications for web.I have succesfully managed to display background notification. But on clicking it, the notification does not open the window or link I want.Rather, the function just does not fire.

Below is the code of my firebase-messanging-sw.js file.

self.addEventListener('notificationclick', function(event) {
  console.log('[firebase-messaging-sw.js] Notification click received.');

event.notification.close();


const path = event.notification?.data?.url || '/';
// const fullUrl = 'http://localhost:3000' + path;

event.waitUntil(
  clients.openWindow(path)
    .catch(err => console.error('Error opening window:', err))
);
});



importScripts('https://www.gstatic.com/firebasejs/8.4.1/firebase- app.js');
importScripts('https://www.gstatic.com/firebasejs/8.4.1/firebase- messaging.js');


const firebaseConfig = {
  apiKey: "***",
  authDomain: "***",
  projectId: "***",
  storageBucket: "***",
  messagingSenderId: "***",
  appId: "***",
  measurementId: "***"
};

firebase.initializeApp(firebaseConfig);
const messaging = firebase.messaging();


messaging.onBackgroundMessage(function(payload) {
  console.log('[firebase-messaging-sw.js] Received background  message ', payload);

const notificationTitle = payload.data?.title || 'New Notification';
const notificationOptions = {
  body: payload.data?.body || 'You have a new message',
  icon: '/firebase-logo.png',
  badge: '/badge-icon.png',
  data: {
    url: payload.data?.url || '/',
  },
  actions: [
    {
      action: 'open',
      title: 'Open App'
    }
  ]
};

self.registration.showNotification(notificationTitle, notificationOptions);
});

The ‘notification click’ function is just not firing.Nothing gets printed on the console when I click on the notification.
I have tried everything mentioned on internet-

  • tried opening a different link(youtube.com)
  • tried moving the ‘notification click’ function on top
  • checked that service worker is registered and running.

please tell me any fix for this or anyhting that I may have missed