How to choose the correct Firebase JS SDK version for firebase-messaging-sw.js in a Flutter Web app?

I have a Flutter/Firebase web app where I am trying to add firebase cloud messaging (notifications).

Part of this setup is to create a service worker file “firebase-messaging-sw.js”, with example contents:

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

firebase.initializeApp({
  apiKey: "YOUR_API_KEY",
  authDomain: "your-app.firebaseapp.com",
  projectId: "your-app",
  storageBucket: "your-app.appspot.com",
  messagingSenderId: "YOUR_SENDER_ID",
  appId: "YOUR_APP_ID"
});

const messaging = firebase.messaging(); 

But how am I supposed to know what exact version (other than 10.3.0) that I should be using?

I assume it relates to the version of firebase messaging in my pubspec.yaml… and that there is a some matrix somewhere online but I cant find it. I keep going in circles through docs, youtube videos, and chat gpt.

There is this doc with a note:

// Please see this file for the latest firebase-js-sdk version: //
https://github.com/firebase/flutterfire/blob/main/packages/firebase_core/firebase_core_web/lib/src/firebase_sdk_version.dart

… but that just tells you the latest version, not the version that matches your project… so I’m nervous to use it… or is it always backward compatible?

Finding the first timestamp where sum of ceiling functions exceeds a threshold

Context:

I’m modeling repeated button presses for an incremental game. There will be buttons in-game set to press repeatedly. These buttons have:

  • variable presses/sec speeds, or rates.
  • variable “speed limits” (max presses/sec allowed).
  • variable cooldown timers when the speed limits are exceeded.
  • variable presses/press multipliers that amplify each press (not the same as multiplying the presses/sec value directly)

In practice, the game calculates in discrete time chunks (ticking). I need to determine exactly when during a tick the speed limit is first broken, so I can apply cooldowns precisely. (speed limit broken -> overheat and reset tally -> cooldown, rinse and repeat).

Some impl details:

To keep presses independent from the size of the tick calculation window, Presses are “aligned” to the next timestamp divisible by (1000/pressesPerSec), i.e. milliseconds per press.
For example, 4 presses/sec would align presses to timestamps % 250 = 0, e.g. … 9750, 10000, 10250, 10500, …

I am storing the repeating presses as press objects (pressObjs), containing the timestamp pressing starts, the timestamp pressing stops, and the presses/sec rate.

I intend for the presses/sec, cooldown timer, etc. to be taken to the extremes; think 10^1000 and 10^-1000. (Thanks break_infinity.js)

The problem:

I’ve reduced this to: Find the smallest timestamp x where f(x) = sum of ceiling functions > threshold

Each group of presses, defined by a pressObj, is effectively a piecewise function with ceiling operations. When multiple press objects overlap, I need to find when their sum first exceeds the speed limit.

Example:

A button is upgraded from 10 presses/sec to 14 presses/sec. The button has a presses/press multiplier = 2 and a speed limit = 25 presses/sec.

Press objects (pressObjs) are created every 210 ms starting at the timestamp 2500. There are a total of 12 press objects; The first six perform 10 presses/sec, and the second six perform 14 presses/sec.

There will be some “uneven behavior” to the running presses/sec tally as it transitions. It will cross the 25 presses/sec speed limit multiple times.

This Desmos visualization shows the limit is first broken around the timestamp 4285.71429. Likely approximating ≈ 30,000 / 7.

Desmos graph of example showing the cumulative sums of piecewise functions as stacked colors, highlighting the eyeballed approximate timestamp at which the cumulative sum (running presses per second tally) first goes over 25.

(In this example, because we see the limit is broken on the ninth press object, the last three press objects would not be created during the run of the program, unless cooldown time was tiny.)

My thought process:

Each press object effectively creates a piecewise function based on four breakpoints where its effect on the running presses/sec tally changes:

  1. repeated pressing starts — incrementing the running tally,
  2. repeated presses stop, or expire (happen over a second ago) at the same rate they are performed — plateauing the running tally,
  3. presses have stopped and the last presses begin expiring — decrementing the running tally,
  4. the last press has expired — tally is back to 0.

The pseudocode for constructing the piecewise function from a repeated press object:

tsStartPressing = pressObj.startPressingTimestamp
tsStopPressing = pressObj.stopPressingTimestamp
ppMs = pressObj.pressesPerSecond / 1000
mspp = 1 / ppMs
pressMul = pressObj.pressMul

// The four breakpoints
tsStart
tsPlateau = min( tsStop, tsStart + 1000 )
tsStartDecrease = max( tsStop, tsStart + 1000 )
tsEnd = tsStop + 1000

// The first performed press is aligned to the next timestamp divisible by milliseconds/press
tsAlignedFirstPress = ceil(tsStartPressing / mspp) * mspp

plateauTally = ceil((tsPlateau - tsAlignedFirstPress) * ppMs) * pressMul

// Breakpoints 1-2: Increment -- presses 'ramp up'
F_inc(t) = ceil((t - tsAlignedFirstPress) * ppMs) * pressMul

// Breakpoints 2-3: Plateau -- presses stop, or expire at the same rate they are pressed
F_plateau(t) = plateauTally

// Breakpoints 3-4: Decrement -- presses 'ramp down' as no more occur and they expire
F_dec(t) = plateauTally - ceil((t - (tsStartDecrease - tsStartPressing) - tsAlignedFirstPress) * ppMs) * pressMul

// Piecewise:
F(t) = {
    F_inc(t)      |  tsStart <= t < tsPlateau
    F_plateau(t)  |  tsPlateau <= t < tsStartDecrease
    F_dec(t)      |  tsStartDecrease <= t < tsEnd
    0             |  else
}

My current approach:

After processing overlapping piecewise functions’ segments — summing plateau segments as constants, and cancelling out increment and decrement ceiling functions with equal presses/sec — I am left with ceiling functions with unequal presses/sec.

My current algorithm splits every ceiling function into many flat segments, which creates insanely many breakpoints when presses/sec values are large (e.g., 10^10 presses/sec). This becomes computationally expensive.

As a sanity check I put the above 10 p/s -> 14 p/s example into Mathematica to use its fancy Minimize, NMinimize, and ArgMin functions. (I can post the full Mathematica code if desired.) The results it produced were rather inconsistent. The best was 4293, not even close to 30,000 / 7 ≈ 4285.71429.

I know optimizing nonlinear or non-convex problems is very hard analytically. There are several posts on math.stackexchange.com regarding sums with floor()s and ceil()s, but none regarding the specific case of finding the smallest x that causes f(x) > threshold.

Question:

How can I efficiently find the first timestamp where the sum of the ceiling functions exceeds a threshold?

Alternatively, is there a better approach for modeling this game mechanic that avoids the ceiling function complexity entirely?

The core issue is that ceiling functions create discontinuous jumps, and their sum can exceed the threshold at any of these discontinuity points. I need an efficient way to check these critical points without evaluating every possible breakpoint.

Angular NBG Modal appears at the bottom of the screen

I have an Angular app using NgbModal from ng-bootstrap, but the modal appears incorrectly: instead of centering and overlapping the parent, it renders at the bottom of the screen underneath the parent component.

Inspecting the modal’s computed styles in devtools shows it’s missing expected positioning CSS like position: fixed|absolute, top, left, and transform: translate(-50%, -50%), which ng-bootstrap normally applies to center modals.

When inspecting the modal element in the browser devtools, I see that the computed styles do not include any position: fixed|absolute, top, left, or transform: translate(-50%, -50%) rules that it would normally have. The only CSS rules I can find on the modal or its parents are font and margin/padding styles — nothing about positioning.

I suspect that either the modal CSS from ng-bootstrap is missing, not loaded properly,
or some other CSS is overriding or removing the positioning styles.

This is the parent:

 <button class="add-user-btn" (click)="openNewUserModal(null)">Add User</button>

...

import {NgbModal} from "@ng-bootstrap/ng-bootstrap";
export class UsersComponent implements OnInit {

  constructor(
    private modalService: NgbModal
  ) { }

  openNewUserModal(user: User | null = null) {
    const modalRef = this.modalService.open(UserModalComponent);
  }

Child:

<div class="modal-header">
  <h4 class="modal-title">User Modal</h4>
  <button type="button" class="btn-close" aria-label="Close" (click)="activeModal.dismiss()"></button>
</div>
<div class="modal-body">
  Hello, minimal modal!
</div>
<div class="modal-footer">
  <button type="button" class="btn btn-primary" (click)="activeModal.close('Save click')">Save</button>
</div>


...

import {NgbActiveModal} from "@ng-bootstrap/ng-bootstrap";

@Component({
  selector: 'app-user-modal',
  templateUrl: './user-modal.component.html',
  styleUrls: ['./user-modal.component.scss']
})
export class UserModalComponent implements OnInit {

  constructor(public activeModal: NgbActiveModal) { }

}

There is no custom CSS for the modal right now.

I have confirmed a few things that I have seen suggested elsewhere:

1.) I imported the Angular Material prebuilt theme globally at the top of styles.scss:

@import "~@angular/material/prebuilt-themes/indigo-pink.css";

2.) In my angular.json file my build configuration includes both Angular Material’s and Bootstrap’s CSS files globally:

"styles": [
              "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
              "./node_modules/bootstrap/dist/css/bootstrap.min.css",
              "src/styles.scss"
                        ],

This is currently my index.html. I do not believe that I need to do anything here but I have read some answers suggesting trying some things here and I’ve tried it all only to have other styles messed up so I have not really touched it. I am open to suggestions.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>TIMS</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
  <link rel="preconnect" href="https://fonts.gstatic.com">
  <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body class="mat-typography">
  <app-root></app-root>
</body>
</html>

Venmo PayPal Smart Button shows inconsistent loader after clicking “Pay” in popup

I’m integrating Venmo via PayPal Smart Buttons (JavaScript SDK).

When a user clicks the Venmo button, a popup appears where they log in and click “Pay”. After this:

The popup closes

The onApprove function runs

I make an API call to my backend to capture the payment via /paypal/capture-order

I set a loader flag (screenLoading = true) to show the UI is processing

❗The Problem:
Sometimes the loader shows for 2–3 seconds and disappears, even though the backend is still processing

Sometimes the loader doesn’t show at all

This creates confusion for the user, who might think the payment failed or nothing is happening.

questions about callback function in javascript

So I was learning Javascript and came across callback function, from what I understand, callback function is a way of making sure that some dependent code runs after the code that it was dependent on is finished.

And so I have this code here:

//A function that takes an argument, which is another function, that will be run after everything else above is done

function requestFunc(callBackFunc){
    const request = new XMLHttpRequest();          
    request.open("GET", "https://somewebsite.com");
    request.send();

    callBackFunc(request.response);
}

//A function that logs the argument

function printFunc(response){
    console.log(response);
}

//Calling requestFunc() and passing printFunc() as the argument

requestFunc(response => {
    printFunc(response);
});

So I tested this, and the output was blank, nothing was displayed because apparently printFunc() was called before the response finished loading, but isn’t Javascript supposed to wait and run printFunc() after everything else was done?

How can I prevent my script from timing out when writing formulas to a large file?

I’m trying to automate some changes in a file that has two versions: one with about 5,000 rows and another with around 80,000 rows.

When I run my script on the smaller file, it works fine. But when I run it on the larger file, it times out.

Originally, I used a for loop to write a formula into each row one by one. To improve performance, I switched to building an array of formulas first and then writing them all at once.

Here’s the relevant part of my code.

The argument sheet comes from spreadsheet.geSheetByName(sheetName).

The main sheet contains only data, number and letters, and this function tries to import information from other 3 sheets that I have on the file through VLOOKUP, and these other sheets contain only simple data as well, numbers and letters.

function insertNewColumns(sheet){
  Logger.log("Inserting new columns: Commited Projects, Skillsets and Country PPM!");

  var startRow = (SheetType.timeUnit === "YEAR") ? 20 : 21

  sheet.insertColumnAfter(2);
  var columnC = 3;
  sheet.getRange(startRow, columnC).setValue("Committed Projects");

  sheet.insertColumnAfter(7);
  var columnH = 8;
  sheet.getRange(startRow, columnH).setValue("Skillset");

  sheet.insertColumnAfter(13);
  var columnN = 14;
  sheet.getRange(startRow, columnN).setValue("Country PPM");

  var lastRow = sheet.getRange("A:A").getLastRow();
  var numRows = lastRow-startRow;
  var formulasC = [];
  var formulasH = [];
  var formulasN = [];

  for (var row = startRow+1; row <= lastRow; row++) {

    formulasC.push([`=IFERROR(VLOOKUP(value(A${row}), 'Committed Projects'!A:B, 2, 0), "XXXXX")`]);

    formulasH.push([`=IFERROR(VLOOKUP(G${row}, 'Skillset'!A:B, 2, 0), "XXXXX")`]);

    formulasN.push([`=IFERROR(VLOOKUP(M${row}, 'Country PPM'!A:B, 2, 0), "XXXXX")`]);
  }
  sheet.getRange(startRow + 1, columnC, numRows, 1).setFormulas(formulasC); // IT TIMES OUT HERE
  sheet.getRange(startRow + 1, columnH, numRows, 1).setFormulas(formulasH);
  sheet.getRange(startRow + 1, columnN, numRows, 1).setFormulas(formulasN);
  SpreadsheetApp.flush();
}

I first tried using a for loop to write each formula directly to the cell in every iteration. I expected this to work, but it was very slow for large files and timed out. To improve this, I changed my approach to build an array of all the formulas first and then write them to the sheet in one operation. I expected this to solve the timeout issue, but it still times out on the larger file with 80,000 rows.

How I can stop the script from timing out on the larger file?

Can I download all files from a selected directory using only drive.file permission?

I’m using the Google Drive Picker to let users upload files from their own Google Drive. I’m using the https://www.googleapis.com/auth/drive.file scope for permissions.

Users can manually select individual files using the Picker, and that part works fine. However, when I try to download those files using the URLs returned by the Picker (via a simple GET request), I get a 404 File not found error — and this happens for all file types.

My main question is: Is it possible to let users select a folder and then retrieve all the files inside that folder using only the drive.file permission?

I know this is possible with drive.readonly, but I’d prefer to avoid the extra security review required for that scope. From what I understand, drive.file only gives access to files explicitly selected by the user, which is what I want.

But when a user selects a folder, I’m unable to access its contents — I don’t know how (or if it’s even allowed) to list the files within that folder using only drive.file.

If this should work, here’s a simplified version of the code I’m using, including the download logic, Picker setup, and access token handling:

import useDrivePicker from "react-google-drive-picker";
import { useGoogleLogin } from "@react-oauth/google";

// Mapping for Google file types (Docs, Sheets, etc.) to export MIME types and extensions
const GOOGLE_FILES_MIMES = {
  "application/vnd.google-apps.document": {
    exportMimeType: "application/pdf",
    fileExtension: "pdf",
  },
};

const buildDownloadUrl = (file: CallbackDoc): string | null => {
  if (file.downloadUrl) return file.downloadUrl;

  if (file.mimeType.startsWith("application/vnd.google-apps")) {
    const exportMimeType = GOOGLE_FILES_MIMES[file.mimeType]?.exportMimeType;
    if (exportMimeType) {
      return `https://www.googleapis.com/drive/v3/files/${file.id}/export?mimeType=${exportMimeType}`;
    }
  }

  return `https://www.googleapis.com/drive/v3/files/${file.id}?alt=media`;
};

const downloadMultipleSelectedFiles = async (files: CallbackDoc[]) => {
  if (!accessToken) return;

  setIsUploading(true);
  setUploadingStarted(true);
  setTotalFiles(files.length);
  setProgress(0);

  const downloadedFiles = await Promise.all(
    files.map(async (file) => {
      const downloadUrl = buildDownloadUrl(file);
      if (!downloadUrl) return null;

      try {
        const blob = await downloadWithUrl(downloadUrl, accessToken);
        let fileName = file.name;

        const fileExtension = GOOGLE_FILES_MIMES[file.mimeType]?.fileExtension;
        if (fileExtension && !fileName.endsWith(`.${fileExtension}`)) {
          fileName += `.${fileExtension}`;
        }

        return { file: new File([blob], fileName), name: fileName };
      } catch (error) {
        console.error(`Failed to download ${file.name}:`, error);
        return null;
      } finally {
        setProgress((prev) => prev + 1);
      }
    })
  );

  const fileContainers = downloadedFiles
    .filter(Boolean)
    .map((f) => ({ file: f!.file, relativePath: f!.name }));

  if (fileContainers.length > 0) {
    addAllFilesToGroup("googleDrive", fileContainers);
    onFilesUploaded(fileContainers);
  }

  setIsUploading(false);
  setUploadingStarted(false);
  return fileContainers;
};

// Login using the drive.file scope
const login = useGoogleLogin({
  onSuccess: (response) => {
    setAccessToken(response.access_token);
    setTimeout(() => buttonRef.current?.click(), 1);
  },
  onError: (error) => console.error("Login error:", error),
  scope: "https://www.googleapis.com/auth/drive.file",
});

// Open the Google Picker
openPicker({
  clientId: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID,
  developerKey: process.env.NEXT_PUBLIC_GOOGLE_DEVELOPER_KEY,
  viewId: "DOCS",
  showUploadView: true,
  showUploadFolders: true,
  supportDrives: true,
  multiselect: true,
  setIncludeFolders: false,
  setSelectFolderEnabled: false,
  setOrigin: window.location.origin,
  token: accessToken,
  callbackFunction: async (data) => {
    if (data.action !== "picked" || !data.docs.length || !accessToken) return;
    const fileContainers = await downloadMultipleSelectedFiles(data.docs);
    console.log("Downloaded:", fileContainers);
  },
});

It’s impossible to get the files because of missing access

{
  "error": {
    "code": 404,
    "message": "File not found: 1UVDy3bzMD5n0Ty2ovd6cOkX99vp7hMYx.",
    "errors": [
      {
        "message": "File not found: 1UVDy3bzMD5n0Ty2ovd6cOkX99vp7hMYx.",
        "domain": "global",
        "reason": "notFound",
        "location": "fileId",
        "locationType": "parameter"
      }
    ]
  }
}

How can I manually trigger garbage collection from an asynchronous javascript function?

I’m calling global.gc() in some unit tests, but for some reason it only seems to have any impact when called synchronously. If I call it asynchronously, I never seem to get a cleanup callback in my FinalizationRegistry.

Simplified demo case:

// Only works in Node >= v14.6.0 with --expose-gc
if (typeof gc !== 'function') {
    throw new Error('Run the test with --expose-gc flag');
}

let timeoutId;
const registry = new FinalizationRegistry(() => {
    clearTimeout(timeoutId);
    console.log("item was successfully garbage collected");
});
let element = {};
registry.register(element, 'item');

timeoutId = setTimeout(() => {
    console.error("item was not garbage collected in 3 seconds");
}, 3000);

element = null;
console.log("reference to item removed");

Promise.resolve().then(() => {
    console.log("trigger garbage collection...");
    gc();
});

JSBin link: https://jsbin.com/yobohelupe/edit?js,console (needs to be run with the –expose-gc flag)

I’ve tried both in nodejs 24 and chrome 138. Making the call to gc() synchronously works as I expect.

Please can someone help me understand what’s going wrong here? I’ve found the documentation about the gc() function much less comprehensive than other javascript functions.

[Aside: If it helps to understand my use case, I’m trying to write a test suite which checks that all my custom web components can be garbage collected, because I keep creating web components which cause big memory leaks. The use of FinalizationRegistry and global.gc() will only live in the tests, not the production code.]

How to remove a nested property from an object in Firestore

my firestore stores user data as below:

users (collection)
   uid-123 (document one per user)
     profile:
        name: 'My Name'
        options: {
                    x: 1,
                    y: 2,
                    z: 3
                 }

In a situation i want to remove y:2 from the profile.options. I am using like below:

async updateUserProfileMerged(uid: string, payload: any) {
      try{
          const userDocRef = doc(this.firestore, 'users/' + uid);
          return setDoc(userDocRef, Object.assign({},{"profile": payload}), { merge: true     });
        }catch(err){
          console.log("error in updateUserProfileMerged in data.service.ts:", err)
        }
    }

The passed payload in this case is:

 options: {
            x:1,
            z: 3
          }

This does not remove y as it is passing merge true. if i set merge to false then it will override the entire profile object. so how do i only override options?

How to show both calendar and type-in time input in one modal using react-native-modal-datetime-picker?

I’m using the react-native-modal-datetime-picker package in my React Native project and want to display a single modal that contains both:

A calendar to select the date

A text input (not a clock picker) to manually type in the time

However, the current behavior shows the date picker first, and only after pressing “OK”, it shows the time picker (clock-style). This results in two steps, which is not the UX I’m looking for.

import DateTimePickerModal from 'react-native-modal-datetime-picker';

<DateTimePickerModal
  isVisible={isDatePickerVisible1}
  mode={'datetime'}
  onConfirm={handleConfirm1}
  onCancel={hideDatePicker1}
  textColor="#00C47A"
  display={"inline"} // also tried 'default'
  is24Hour={!hourformat}
  minimumDate={duedatefield ? new Date() : undefined}
/>

Is this possible using react-native-modal-datetime-picker or patch or should I look for an alternative?

How to restart setInterval() with the same name on button?

There is a timer like this. The button should start and restart the timer. In the end, it restarts the timer, but the previous one continues, despite the same name… I read that the setInterval() function will work until the end and does not depend on other such functions, I tried clearInterval(), but it does not work. How can this be done?

function timer_start(){
  var ts = 60;
  let timer = setInterval(() => {
        if (ts > 0) {
            ts --;
            var h = ts/3600 ^ 0,
                m = (ts-h*3600)/60 ^ 0,
                s = ts-h*3600-m*60,
                time = (m<10?"0"+m:m)+":"+(s<10?"0"+s:s);
            $("#timer_").text(time);
        } else {
            clearInterval(timer);
        }
    }, 1000);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>


<div><strong>Timer</strong></div>
<div id="timer_">01:00</div>
<hr>
<button type="button" onClick="timer_start()">Timer Start</button>

Adaptive Card JS SDK: Add Properties to Default Types

I have an application that renderes AdaptiveCards in my own UI.
I have been using some default adaptive card examples from the new designer: https://adaptivecards.microsoft.com/designer

A lot of them use the targetWidth property.
When rendering them using the Microsoft Adaptive Card JS SDK it looks like it doesn’t support this property.

So I created my own custom Image Element that simply extends the default Image and overrides the existing default Image element according to the MS Docs

While my own renderer is now being called for images, the new property I added isn’t being processed:

static readonly targetWidthProperty = new StringProperty(AdaptiveCards.Versions.v1_0, "targetWidth");

@AdaptiveCards.property(CustomElement.targetWidthProperty)
targetWidth?: string;

If I register my component under a different name (e.g. CustomImage) and rename the Images in the json config to CustomImage by new property is processed properly.

It seems like the schema of default elements is somehow cached. If I try to override the Image type the populateSchema() isn’t called, if I use my own CustomImage it’s called.
I’ve tried everything, using my own SerializationContext etc. but can’t find a solution.

So how are we supposed to add missing features to the default components?

Why the toast.success is not working in my signup form

I’m trying to implement a signup form using the react-testify package to show success and error messages. While the toast. Error function works perfectly, the toast. Success function does not display the success message upon successful form submission.
Here’s the relevant code for my Signup component:

import axios from "axios";
import { useState } from "react";
import { toast, ToastContainer } from "react-toastify";
// import "../signup/signup.css";
import { Link, useNavigate } from "react-router-dom";
import "react-toastify/dist/ReactToastify.css";

const Signup = () => {
  const navigate = useNavigate();

  const [formData, setFormData] = useState({
    name: "",
    phone: "",
    email: "",
    password: "",
  });

  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData((prevState) => ({
      ...prevState,
      [name]: value,
    }));
  };

  const handleSignup = async (e) => {
    e.preventDefault();

    if (
      !formData.name ||
      !formData.phone ||
      !formData.email ||
      !formData.password
    ) {
      toast.error("Please fill in all fields.", {
        position: "top-right",
        autoClose: 3000,
      });
      return;
    }

    try {
      const res = await axios.post("http://localhost:0000/example", formData);
      console.log("Response from server:", res.data);

      toast.success("Form Submitted Successfully", {
        position: "top-right",
        autoClose: 5000,
      });

      setFormData({
        name: "",
        phone: "",
        email: "",
        password: "",
      });

      setTimeout(() => {
        navigate("/signin");
      }, 5000);
    } catch (error) {
      toast.error("Something went wrong. Please try again.", {
        position: "top-right",
        autoClose: 5000,
      });
    }
  };

  return (
    <div className="flex items-center justify-center min-h-screen bg-gray-100">
      <ToastContainer />
      <div className="bg-white shadow-lg rounded-lg w-full max-w-lg p-8">
        <h2 className="text-lg font-bold text-center mb-6">Create Account</h2>
        <form onSubmit={handleSignup}>
          <div className="mb-4">
            <input
              type="text"
              className="w-full border border-gray-300 rounded-md px-4 py-2"
              name="name"
              placeholder="Enter Full Name"
              value={formData.name}
              onChange={handleChange}
            />
          </div>
          <div className="mb-4">
            <input
              type="text"
              className="w-full border border-gray-300 rounded-md px-4 py-2"
              name="phone"
              placeholder="Enter Phone"
              value={formData.phone}
              onChange={handleChange}
            />
          </div>
          <div className="mb-4">
            <input
              type="email"
              className="w-full border border-gray-300 rounded-md px-4 py-2"
              placeholder="Enter Email"
              name="email"
              value={formData.email}
              onChange={handleChange}
            />
          </div>
          <div className="mb-6">
            <input
              type="password"
              className="w-full border border-gray-300 rounded-md px-4 py-2"
              placeholder="Enter Password"
              name="password"
              value={formData.password}
              onChange={handleChange}
            />
          </div>
          <div>
            <input
              type="submit"
              className="w-full bg-blue-600 text-white rounded-md py-2 cursor-pointer"
              value="Signup"
            />
          </div>
          <div className="text-center mt-4">
            <span>
              Already have an account?
              <Link to="/signin" className="text-blue-600 ps-1">
                Login Here
              </Link>
            </span>
          </div>
        </form>
      </div>
    </div>
  );
};

export default Signup;

Angular *ngIf not removing element even when condition becomes false DOM keeps adding duplicate elements

I’m working on an Angular app (v14+), and I’m facing a strange issue with *ngIf inside a component.

Inside my component template (post-card.component.html), I have this simple test markup:

<span>{{ post.showComments }}</span>
<span *ngIf="post.showComments">Amazing....!</span>

Output :
true Amazing....! Amazing....! Amazing....! Amazing....! Amazing....! Amazing....! Amazing....! Amazing....! Amazing....!

post is an @Input() coming from the parent posts.component.ts like this:

<app-post-card
  *ngFor="let post of posts; let i = index; trackBy: trackByPostId"
  [post]="post"
  [index]="i"
  [showComments]="post.showComments"
  ...
></app-post-card>

In the parent, I toggle post.showComments using immutable update:

this.posts = this.posts.map((p, i) => {
  if (i === index) {
    return { ...p, showComments: !p.showComments };
  }
  return p;
});

post.showComments is toggled correctly. I see the true/false value update inside the component.

ngOnChanges() logs the expected value transitions.

trackByPostId returns a stable and unique feedID string.

Ensured trackBy is correct and returns unique, stable ID

Verified that only one app-post-card is rendered per post (confirmed via logs)

Used ng-template + ViewContainerRef with manual clear() and destroy()

Logged ngOnInit and ngOnDestroy — component is not being re-instantiated

Confirmed no DOM manipulation or animation libraries are modifying the view

Expected behavior:
When post.showComments becomes false, Angular should destroy the *ngIf block and remove the from DOM. But instead, the DOM keeps accumulating duplicates.

Is there any scenario where *ngIf inside a child component doesn’t destroy its view even though the input becomes false? Is Angular’s view caching interfering due to deep nested components or incorrect diffing?

Any ideas or debugging strategies to isolate this would be super helpful.

How to control/record the order FileReader uploads files in?

I’ve created a basic JSON object as a request to ChatGPT then I loop through a bunch of images selected via an input tag and add them dynamically to the object for it to have a look at the images and write alt tag content for them.

Because the upload is async and not in any particular order in the filereader.onloadend event I count the total number of files uploaded and when it gets to last file it I POST it to ChatGPT’s api.

Problem is the alt tag content isn’t returned in the same order I uploaded it. Currently I have an array which I add the count to and then assign them based on the order of that array.

It does work, but only about 70% of the time.


            
                var nLoopIds = [];
                
                const files = document.getElementById("fileImages").files
                for (let i = 0; i < files.length; i++) 
                {
                    const fileReader = new FileReader();
                    
                    fileReader.onloadend = function(event) 
                    {       
                        // Add to data object
                        data.response_format.json_schema.schema.properties["alt_" + i] = 
                        {
                            "type": "string"
                        };
                        data.response_format.json_schema.schema.required.push("alt_" + i);
                        const b64 = event.target.result.split(",")[1]; // Extract base64 data
                        var image_url = 
                        {
                          "url": "data:image/jpeg;base64," + b64
                        }
                        var image = 
                        {
                          "type": "image_url",
                          "image_url": image_url
                        }
                        data.messages[1].content.push(image);
                        
                        // Add to show order uploaded 
                        nLoopIds.push(i);
                    
                        // Post to ChatGPT
                        if (nCount == files.length - 1)
                        {
                            // Send the POST request using fetch
                            fetch("https://api.openai.com/v1/chat/completions", {
                                method: "POST",
                                headers: {
                                    "Authorization": "Bearer api-key",
                                    "Content-Type": "application/json"
                                },
                                body: JSON.stringify(data)
                            })
                            .then((response) => response.json())
                            .then((data) => loadText(data, nLoopIds))
                            .catch((error) => alert(error));
                            
                        }
                        
                    }
                    
                    fileReader.readAsDataURL(files[i]); // Read selected file as Data URL
                } 

This is the code that receives the JSON response from ChatGPT.

        function loadText(data, nLoopIds)
        {
            var obj = JSON.parse(data["choices"][0]["message"]["content"]);
            
            document.getElementById("strTitle").value = obj["title"];
            document.getElementById("strShort").value = obj["short"];
            document.getElementById("strDescription").value = obj["description"];
            document.getElementById("strTheme").value = obj["theme"];
            document.getElementById("strTags").value = obj["tags"];
            
            for (let i = 0; i < nLoopIds.length; i++) 
            {
                document.getElementById("imgAlt-" + nLoopIds[i]).value = obj["alt_" + i];
            }
        }

Plus the original data object.

                var data = {
                    
                    model: "<?php echo $strModel; ?>",
                    //user: "<?php echo $strUser; ?>",
                    response_format: 
                    {
                        "type": "json_schema",
                        "json_schema": 
                        {
                            "name": "generate_project",
                            "strict": true,
                            "schema": 
                            {
                                "type": "object",
                                "properties": 
                                {
                                    "title": 
                                    {
                                        "type": "string"
                                    },
                                    "short": 
                                    {
                                        "type": "string"
                                    },
                                    "description": 
                                    {
                                        "type": "string"
                                    },
                                    "theme": 
                                    {
                                        "type": "string"
                                    },
                                    "tags": 
                                    {
                                        "type": "string"
                                    }
                                },
                                "required": ["title", "short", "description", "theme", "tags"],
                                "additionalProperties": false
                            }
                        }
                    },
                    messages: [
                    {
                        role: "system",
                        content: "<?php echo $strPromptBody; ?>"
                    },
                    {
                        role: "user",
                        content: [
                        {
                            "type": "text",
                            "text": document.getElementById("strPrompt").value
                        }]
                    }]
                };