TypeError (is not iterable) when adding SVG to FabricJS canvas

I have a fabricjs canvas, and I’m trying to add an SVG to it. But whatever I do, I get problems. It keeps saying:
“Uncaught (in promise) TypeError: t is not iterable”.

let svg = `<svg viewBox="-8 -8 136 136">
<path stroke="#000000" stroke-width="8" d="m0 51.82677l0 0c0 -28.623135 23.203636 -51.82677 51.82677 -51.82677l0 0c13.745312 0 26.927654 5.4603047 36.64706 15.17971c9.719406 9.719404 15.17971 22.901749 15.17971 36.64706l0 0c0 28.623135 -23.203636 51.82677 -51.82677 51.82677l0 0c-28.623135 0 -51.82677 -23.203636 -51.82677 -51.82677zm25.913385 0l0 0c0 14.311565 11.60182 25.913387 25.913385 25.913387c14.311565 0 25.913387 -11.601822 25.913387 -25.913387c0 -14.311565 -11.601822 -25.913385 -25.913387 -25.913385l0 0c-14.311565 0 -25.913385 11.60182 -25.913385 25.913385z" fill="none"></path>
</svg>`;

let canvas = new fabric.Canvas("canvas");
let path = fabric.loadSVGFromString(svg, function(objects, options) {
    let obj = fabric.util.groupSVGElements(objects, options);
    
    obj.set({
        left: canvas.width / 2 - obj.width / 2,
        top: canvas.height / 2 - obj.height / 2
    });
    canvas.add(obj).renderAll();
});

Hopefully someone can help me out

How do I wait for action complete on introjs onbeforechange

In my intro tour, I have a section where I need to expand a div with a slider before I can run intro on the internal elements. I use onbeforechange to expand the div, but it is a jquery animation. The problem is that the top coordinate of the div is off screen when I get to this step of my tour. I want to wait until the animation is complete before drawing the next introjs box. Is there a way to do this? Here is my code…

       }).onbeforechange(function(targetElement) {
            // Check if you want to trigger a click on the current step
            if (this._currentStep === 3 || this._currentStep === 4 ||this._currentStep === 5 ||this._currentStep === 6 ||this._currentStep === 7) {
                if ($('.btn-arrow').text() == 'Click for ↓ Details') {
                    const elementToClick = document.querySelector(".btn-arrow");
                    if (elementToClick) {
                        elementToClick.click();
                    }
                }   
            }

The conditional is a test for whether the div is expanded yet. It is saying “if not, then expand before change.” but introjs does not wait for the expand to complete since it is asynchronous. I guess what I need is a ‘pause’ introjs, then a ‘restart’ that I can trigger on complete of my animation?

Thanks for any hints.

JavaScript Fullscreen: fullscreenEnabled is always false and fullscreenElement is not the element requested on

There is a [Lit] WebComponent that I call requestFullscreen() on (this.requestFullscreen()). It’s a toggle, so when the same action is clicked again I check for if (document.fullscreenElement === this), since this is the element that requested fullscreen.

However, document.fullscreenElement is actually the next highest ancestor WebComponent, not even any of the native HTML ancestors between these two WebComponents. So I decided to use document.fullscreenEnabled, just to see if it was and toggle fullscreen off. Turns out, document.fullscreenEnabled is always false.

For now my workaround, because document.fullscreenElement is either that ancestor or null, I am just checking if (!!document.fullscreenElement). This is kinda kludgy, IMO, but works.

Why is document.fullscreenEnabled always false and document.fullscreenElement not this when I called this.requestFullscreen() to invoke fullscreen?

trace cookie creation in browser devtools

I’m trying to understand a website’s login process

After I login, it creates a cookie but I can’t find out which code is creating this cookie

I used firefox devTools and I can see that after posting data to login page the next request contains that specific cookie but none of the previous request’s responses have any header to create that cookie

I searched for document.cookie in every js file, and set breakpoint on every line but none of them triggered for that cookie creation

I injected this code in console before login, but still no luck

origDescriptor = Object.getOwnPropertyDescriptor(Document.prototype, 'cookie');
Object.defineProperty(document, 'cookie', {
  get() {
    return origDescriptor.get.call(this);
  },
  set(value) {
    debugger;
    return origDescriptor.set.call(this, value);
  },
  enumerable: true,
  configurable: true
});

The website is written with angular I guess (ng-version=”17.0.8″)

Is there any other way to create cookies? I’m a web developer myself and I’ve always thought that it’s only possible to create cookie through js and http response headers

P.S: I was googling around and saw some posts about firebug and the ability to create breakpoint on cookie creation but it seems it’s replaced by firefox dev edition and this one doesn’t have that ability

Unexpected behaviour with JavaScript’s “onanimationend” – follow-up

This is a follow-up to this question along with its accepted answer.

This time there is one more list level and the list style item is also highlighted.

Please have a look at this code snippet:

const highlight = (element) => {
  element.classList.remove('highlight');
  setTimeout(() => {
    element.classList.add('highlight');
  }, 0);
  element.onanimationend = e => {
    e.stopPropagation();
    element.classList.remove('highlight');
  }
}
document.querySelector('body').addEventListener('click', event => {
  if (event.target.hash) {
    const element = document.querySelector(event.target.hash);
    highlight(element);
  }
});
@keyframes highlight {
  from {
    background: red;
  }
  to {
    background: transparent;
  }
}

ol {
  counter-reset: enumeration;
  list-style: none;
}

ol > li {
  position: relative;
}

ol > li::before {
  position: absolute;
  margin-left: -2em;
  width: 1em;
  counter-increment: enumeration;
  content: counter(enumeration) '.';
}

ol > li > ol {
  counter-reset: alphabet;
  list-style: none;
}

ol > li > ol > li {
  position: relative;
}

ol > li > ol > li::before {
  position: absolute;
  margin-left: -2em;
  width: 1em;
  counter-increment: alphabet;
  content: counter(alphabet, lower-alpha) ')';
}

div.highlight,
ol > li.highlight,
ol > li.highlight::before,
ol > li:has(.highlight)::before {
  animation: highlight 5s;
}
<ul>
  <li>
    <a href="#foo">Foo</a>
    <div>
      <ul>
        <li>
          <a href="#foo-1">Foo 1</a>
          <ul>
            <li><a href="#foo-1-1">Foo 1.1</a></li>
            <li><a href="#foo-1-2">Foo 1.2</a></li>
            <li><a href="#foo-1-3">Foo 1.3</a></li>
          </ul>
        </li>
        <li><a href="#foo-2">Foo 2</a></li>
        <li><a href="#foo-3">Foo 3</a></li>
      </ul>
    </div>
  </li>
  <li><a href="#bar">Bar</a></li>
  <li><a href="#baz">Baz</a></li>
</ul>

<hr>

<div id="foo">
  <h2>Foo</h2>
  <ol>
    <li id="foo-1">
      Foo 1
      <ol>
        <li id="foo-1-1">Foo 1.1</li>
        <li id="foo-1-2">Foo 1.2</li>
        <li id="foo-1-3">Foo 1.3</li>
      </ol>
    </li>
    <li id="foo-2"> Foo 2</li>
    <li id="foo-3"> Foo 3</li>
  </ol>
</div>

<div id="bar">
  <h2>Bar</h2>
</div>

<div id="baz">
  <h2>Baz</h2>
</div>

Then please run the code snippet (preferably on full page) and try:

Click on “Foo 1.1” (or “Foo 1.2” or “Foo 1.3”), wait 3 seconds and then click on “Foo”. As you can see, the background color animation of “Foo” ends at the same time as the background color animation of “Foo 1.1” (or “Foo 1.2” or “Foo 1.3”). One could also say that the CSS class “highlight” is removed from “Foo” too early.

Furthermore, if you initially click on “Foo 1.1” (or “Foo 1.2” or “Foo 1.3”), the list style item of “Foo” is highlighted as expected (and intended). But if you click on “Foo 1.1” (or “Foo 1.2” or “Foo 1.3”) again within 5 seconds, the list style item of “Foo” is not highlighted again. Only after waiting 5 seconds at least and then clicking on “Foo 1.1” (or “Foo 1.2” or “Foo 1.3”) again, the list style item of “Foo” is highlighted again.

This strange behaviour may be due to the pseudo-element ::before. So it is about ol > li:has(.highlight)::before. stopPropagation() doesn’t seem to affect pseudo-elements because they are not part of the actual DOM.

Any idea how to fix this?

Can’t figure out how to configure Chakra-UI

I have this to templates main.jsx and app.jsx

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.jsx";
import { Provider } from "@chakra-ui/react";

ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <Provider>
      <App />
    </Provider>
  </React.StrictMode>
);

and app.jsx

import { Button } from "@chakra-ui/react";

function App() {
  return (
    <>
      <Button>Hello!</Button>
    </>
  );
}

export default App;

I’m trying to configure and use Chakra-UI but that button won’t be added there with everything that I tried
I also tried this in main.js

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.jsx";
import ChakraProvider from "@chakra-ui/react";

ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <ChakraProvider>
      <App />
    </ChakraProvider>
  </React.StrictMode>
);

The error for the first type of main.jsx is

The requested module '/node_modules/.vite/deps/@chakra-ui_react.js?v=88ddfe76' does not provide an export named 'Provider' (at main.jsx:4:10)

And for the second type

 The requested module '/node_modules/.vite/deps/@chakra-ui_react.js?v=88ddfe76' does not provide an export named 'default' (at main.jsx:4:8)

Please help me with some ideas…

Javascript array with data inside but ’empty header’ [duplicate]

I’m new to JS. I created the 1st array with loop and push, from cloud database. Then I manually declared the 2nd array with the same data. But in firefox console, when you console.log() them, it is like this:

enter image description here

As it shows, the 1st one has an ’empty header’ and length is empty too.

The difference I have found so far is that when you run:

console.log(JSON.stringify(array1));
console.log(JSON.stringify(array2));

The output is gonna be:
console outputs

My question is: How to transform (or fix) the 1st array to make it exactly same as the 2nd array?

——————————–
Updated the code to fetch data from Firebase.

        // Function to fetch locations from Firebase
        const loclist = [];
        async function getLocs() {
            const dbRef = await db.ref('chatbot')

            await dbRef.once('value', (snapshot) => {
                snapshot.forEach((childSnapshot) => {
                    const childData = childSnapshot.val();
                    if (childData && childData.user_loc) { // Check if loc exists
                        loclist.push(childData.user_loc); // Add loc to the list
                    }
                });

                // Display in the HTML
                const userlocElement = document.getElementById("loclist_html");
                loclist.forEach(loc => {
                    const li = document.createElement("li");
                    li.textContent = loc; // Set loc as the text content of the list item
                    userlocElement.appendChild(li); // Append list item to the list
                });
            });
        };

        getLocs();

How to get pixel position in non-geographical Leaflet map?

I have a problem, getting pixel coordinates in Leaflet. Any help would be greatly appreciated!

What I want to achieve:

I want to display an image using leaflet (no tiles – just a image, non-geographical map) and the user should have the option to click on the image, set a marker and I need the pixel position of that marker. For testing purposes, I’ve created a JSFiddle, that uses this image.

Displaying the image and setting the marker is not a problem. There are plenty of tutorials out there for that.

But getting that pixel position is – at least for me – somehow impossible. I’ve read every post / article / … about the different methods provided by Leaflet, but still no success.

What I achieved so far:

In an image editing program I can see, that the top of this tower on the image has roughly these pixel coordinates: x=1350, y=625.

Image showing the pixel position of the top of the tower

So when the user clicks on the top of the tower, I get the LatLng of this point and I can set the marker. When the user does not change the zoom level, my code is actually working. Clicking on the top of the tower, I get pixel coordinates, that are correct (see the blue marker on the image, showing where i clicked, and the pixels calculated displayed on the console):

Image showing the correct calculated position, if the user didn’t zoom

My problem

But when the user starts zooming, the calculated pixel coordinates are definitely wrong:

Image showing the wrong calculated position, after the user zoomed

My code

I’ve created this JSFiddle to share my code: https://jsfiddle.net/q36f1ytu/73/

The code that handles the click event is basically this (see my JSFiddle for the whole code):

map.on("click", function (e) {
  const marker = new L.Marker(e.latlng).addTo(map)

    // Here I want to get the pixel coordinate of the image
  var imagePixelCoords = map.project(e.latlng, map.getZoom())
  console.log("imagePixelCoords: " + imagePixelCoords)
})

I am quite lost, how to calculate the pixel coordinates correctly, when the user zooms.

Thank you very much in advance!

Best regards

How to achieve smooth frame rate independent animations

I have the following animation that I would like to make smooth and frame rate independent:

const duration = 25;
const friction = 0.68;
const target = 400;

let velocity = 0;
let location = 0;

function update() {
  const displacement = target - location;

  velocity += displacement / duration;
  velocity *= friction;
  location += velocity;

  element.style.transform = `translate3d(${location}px, 0px, 0px)`;
}

This is what I want to achieve:

  1. The animation duration should be the same regardless of the device refresh rate whether it’s 30Hz, 60Hz or 120Hz or higher. Small insignificant fluctuations in milliseconds are acceptable.
  2. The animation should be smooth on all devices with 60Hz refresh rate or higher.
  3. The animation behavior should remain the same while achieving point 1-2 above.

How do I go about solving this?


What I’ve tried

I’ve tried implementing the by many devs praised technique in the Fix Your Timestep! article, which decouples the update process and rendering. This is supposed to make the animation smooth regardless of the device refresh rate:

function runAnimation() {
  const squareElement = document.getElementById('square');
  const timeStep = 1000 / 60;
  const duration = 25;
  const friction = 0.68;
  const target = 400;
  const settleThreshold = 0.001;

  let location = 0;
  let previousLocation = 0;
  let velocity = 0;
  let lastTimeStamp = 0;
  let lag = 0;
  let animationFrame = 0;

  function animate(timeStamp) {
    if (!animationFrame) return;
    if (!lastTimeStamp) lastTimeStamp = timeStamp;

    const elapsed = timeStamp - lastTimeStamp;
    lastTimeStamp = timeStamp;
    lag += elapsed;

    while (lag >= timeStep) {
      update();
      lag -= timeStep;
    }

    const lagOffset = lag / timeStep;
    render(lagOffset);

    if (animationFrame) {
      animationFrame = requestAnimationFrame(animate);
    }
  }

  function update() {
    const displacement = target - location;
    previousLocation = location;

    velocity += displacement / duration;
    velocity *= friction;
    location += velocity;
  }

  function render(lagOffset) {
    const interpolatedLocation =
      location * lagOffset + previousLocation * (1 - lagOffset);

    squareElement.style.transform = `translate3d(${interpolatedLocation}px, 0px, 0px)`;

    if (Math.abs(target - location) < settleThreshold) {
      cancelAnimationFrame(animationFrame);
    }
  }

  animationFrame = requestAnimationFrame(animate);
}

runAnimation();
body {
  background-color: black;
}

#square {
  background-color: cyan;
  width: 100px;
  height: 100px;
}
<div id="square"></div>

…However, devs claim that the animation runs smoothly on devices with 60Hz refresh rate but that the animation is stuttering/is choppy on devices with 120Hz refresh rates and up. So I tried to plot the animation curve on different refresh rates to see if there’s something obvious that I’m doing wrong, but judging from the charts, it seems like the animation should be smooth regardless of refresh rate?

function plotCharts() {
  function randomIntFromInterval(min, max) {
    return Math.floor(Math.random() * (max - min + 1) + min);
  }

  function simulate(hz, color) {
    const chartData = [];

    const targetLocation = 400;
    const settleThreshold = 0.001;

    const duration = 25;
    const friction = 0.68;
    const fixedFrameRate = 1000 / 60;
    const deltaTime = 1000 / hz;

    let location = 0;
    let previousLocation = location;
    let interpolatedLocation = location;

    let velocity = 0;
    let timeElapsed = 0;
    let lastTimeStamp = 0;
    let lag = 0;

    function update() {
      const displacement = targetLocation - location;
      previousLocation = location;

      velocity += displacement / duration;
      velocity *= friction;
      location += velocity;
    }

    function shouldSettle() {
      const displacement = targetLocation - location;
      return Math.abs(displacement) < settleThreshold;
    }

    while (!shouldSettle()) {
      const timeStamp = performance.now();

      if (!lastTimeStamp) {
        lastTimeStamp = timeStamp;
        update();
      }

      /* 
      Number between -1 to 1 including numbers with 3 decimal points
      The deltaTimeFluctuation variable simulates the fluctuations of deltaTime that real devices have. For example, if the device has a refresh rate of 60Hz, the time between frames will almost never be exactly 16,666666666666667 (1000 / 60).
      */
      const deltaTimeFluctuation =
        randomIntFromInterval(-1000, 1000) / 1000;
      const elapsed = deltaTime + deltaTimeFluctuation;

      lastTimeStamp = timeStamp;
      lag += elapsed;

      while (lag >= fixedFrameRate) {
        update();
        lag -= fixedFrameRate;
      }

      const lagOffset = lag / fixedFrameRate;

      interpolatedLocation =
        location * lagOffset + previousLocation * (1 - lagOffset);

      timeElapsed += elapsed;

      chartData.push({
        time: parseFloat((timeElapsed / 1000).toFixed(2)),
        position: interpolatedLocation,
      });
    }

    const timeData = chartData.map((point) => point.time);
    const positionData = chartData.map((point) => point.position);

    const canvas = document.createElement("canvas");
    canvas.width = 600;
    canvas.height = 400;
    const ctx = canvas.getContext("2d");
    document.body.appendChild(canvas);

    const chart = new Chart(ctx, {
      type: "line",
      data: {
        labels: timeData,
        datasets: [{
          label: `${hz}Hz (with Interpolation)`,
          data: positionData,
          borderColor: color,
          fill: false,
        }, ],
      },
      options: {
        scales: {
          x: { title: { display: true, text: "Time (seconds)" } },
          y: { title: { display: true, text: "Position (px)" } },
        },
      },
    });
  }

  const simulations = [{
      hz: 30,
      color: "yellow"
    },
    {
      hz: 60,
      color: "blue"
    },
    {
      hz: 120,
      color: "red"
    },
    {
      hz: 240,
      color: "cyan"
    },
    {
      hz: 360,
      color: "purple"
    },
  ];

  simulations.forEach((simulation) => {
    simulate(simulation.hz, simulation.color);
  });
}

plotCharts()
body {
  background-color: black;
}
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

I’m probably doing something wrong so any help is appreciated! Please note that alternative approaches to the fixed time step with render interpolation are welcome, as long as the animation behaves the same.

Can React props be used in CSS?

I have a display: grid;, set to the body of my react-app.
I have a component that uses a part of the grid that I want to repeat itself two more times side by side.
How do I used props for the CSS property, grid-area: , so I can change the position of other two within the grid, slightly modifying the reusable component?

I tried using props the usual way but this only affects the html and am confused on how to change a css property for a reusable component.

Here is the code:

function App() {

  return (
    <>
      <Nav />
      <Sidebar />
      <Main />
      <Content h1="Content1"/>
      
      <Footer/>
    </>
  )
}

export default App
import './Content.css'

function Content(props) {
    return (
        <div className="content">
            <h1>{props.h1}</h1>
            <p>
                Lorem, ipsum dolor sit amet consectetur adipisicing elit. 
                Nemo laborum sapiente placeat dolorem a quae!
            </p>
        </div>
    );
}
export default Content
.content {
    grid-area: content1;
}
.content h1 {
    padding: 1rem;
    background: orange;
    color: white;
    font-weight: 700;
    border-radius: 0.25rem 0.25rem 0 0;
}
.content p {
    padding: 1rem;
    line-height: 1.6;
}

The layout is working as expected,
I just want to change the CSS property grid-area: to a newer position.
Do you know the syntax for modifying a specific CSS property when styling with react props?

Microphone Access and Audio Recording Not Working in Telegram Mini App Web View

I’m developing a Telegram Mini App that requires microphone access to record audio and transcribe it. I’ve been trying to use the MediaRecorder API for this functionality. However, the recorded audio is either empty or not functioning as expected when the app runs in the Telegram Mini App web view (in-app browser)

Here’s the basic setup I’m using to request microphone access and record audio with MediaRecorder:

const AudioRecorderComponent = () => {
  const [isRecording, setIsRecording] = useState(false);
  const [audioBlob, setAudioBlob] = useState(null);

  const startRecording = async () => {
    try {
      const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
      const mediaRecorder = new MediaRecorder(stream);
      const chunks = [];

      mediaRecorder.ondataavailable = (event) => {
        chunks.push(event.data);
      };

      mediaRecorder.onstop = () => {
        const audio = new Blob(chunks, { type: 'audio/wav' });
        setAudioBlob(audio);
        stream.getTracks().forEach(track => track.stop());
      };

      mediaRecorder.start();
      setIsRecording(true);

      // Stop recording after 5 seconds as a test
      setTimeout(() => {
        mediaRecorder.stop();
        setIsRecording(false);
      }, 5000);
    } catch (error) {
      console.error("Microphone access error:", error);
    }
  };

  return (
    <div>
      <button onClick={startRecording} disabled={isRecording}>
        {isRecording ? 'Recording...' : 'Start Recording'}
      </button>
      {audioBlob && <audio controls src={URL.createObjectURL(audioBlob)} />}
    </div>
  );
};

export default AudioRecorderComponent;

Questions

Does Telegram Mini App support microphone access via MediaRecorder? I couldn’t find documentation specifically addressing this for Telegram Mini Apps.
Are there any workarounds or alternative approaches to reliably access the microphone in Telegram’s web view?
Has anyone successfully implemented audio recording in a Telegram Mini App, and if so, what approach did you use?

What I’ve Tried
Testing in Different Environments: The code works perfectly in a regular browser (both mobile and desktop), but not in the Telegram Mini App web view.
Checking Permissions: I verified that microphone permissions are being requested and granted. The MediaRecorder API seems to initialize without errors, but the recorded Blob always has the same size, and there’s no audible playback.
Console Logs for Debugging:
Added logs in the ondataavailable event to check chunk sizes – each chunk has a size of 0 in the Telegram web view.
Verified that navigator.mediaDevices and MediaRecorder are supported in the environment, as indicated by the lack of initialization errors.

Resetting auth During Unit Testing

I am trying to run unit tests for a few portions of my Firebase web app, but I am running into issues with authentication. I have the mock authentication working (triggering onAuthStateChanged as expected). The problem I am having is that it seems the Firebase SDK is holding onto the auth object I pass in first, which makes sense other than I can’t seem to sign out or reset the auth object in between tests.

My onAuthStateChanged observer:

onAuthStateChanged(auth, async (user) => {
    console.log('onAuthStateChanged', auth, user);

    if (!user || (user && !user.currentUser.emailVerified)) {
        window.location.href = 'index.html';
    } else {
        await showInputs();
    }
});

My first test is:

const mockAuth = { currentUser: { uid: true, emailVerified: true } };

getAuth.mockReturnValue(mockAuth);
        
require('./register.js');

await onAuthStateChanged.mock.calls[0][1](mockAuth);

As expected, console.log prints

onAuthStateChanged
auth: { currentUser: { uid: true, emailVerified: true } }
user: { currentUser: { uid: true, emailVerified: true } }

But, on the next test mockAuth is set to

const mockAuth = { currentUser: { uid: true, emailVerified: false } };
                                                            ^^^^^

and console.log prints:

onAuthStateChanged
auth: { currentUser: { uid: true, emailVerified: true } }
user: { currentUser: { uid: true, emailVerified: false } }

Clearly, the SDK is holding onto the auth state, (again, this makes sense given I haven’t logged out), but I have tried getAuth.mockReset(), signOut(getAuth()), getAuth.mockReturnValue(null), and a few others to no avail.

It isn’t so much that I need to sign out, as much as I need to alter the auth object for other tests. These tests are merely checking authentication and are working as expected (they are simply easy examples to illustrate my problem).

I need to be able to add/update the auth.currentUser.email field for later tests. Since the SDK is hanging on to the auth object I initially provide (ignoring getAuth.mockReturnValue(mockAuth) on subsequent calls), I cannot test my code fully.

For this particular test, in my beforeEach call, I have:

getAuth.mockReset();

mockAuth = { currentUser: { uid: true, emailVerified: true, email: "[email protected]" } };

getAuth.mockReturnValue(mockAuth);

But, when executed, auth.currentUser.email is undefined.

The problem function:

onValue(child(firebaseRef, `items`), items => {
    document.getElementById('itemList').innerHTML = '';

    // fill item list
    if (auth.currentUser) {
        items.forEach(i => {
            if (i.child('seller').val() === auth.currentUser.email && !i.hasChild('inactive')) {
                addItemToList(i);
            }
        });
    }
});

auth.currentUser.email is always undefined because when it is set in my first authentication test, I do not need nor provide an email property, but that is the auth object used for the entirety of the test run. If I change the mockAuth object and re-run the test, auth will have the new values (so maybe it doesn’t have anything to do with ‘signing out’??).

Next.js Middleware and Cookie Issue: Logout Not Clearing Cookie Consistently

Problem Statement

I’m working on a Next.js app where I need to manage user authentication with cookies. I have set up login, logout, and middleware files for handling JWT tokens and securing routes. However, I’m facing an issue where logging out does not always clear the cookie, and it seems inconsistent in detecting or removing the cookie in certain cases. The logout works seamlessly in localhost, but in Vercel deployment it doesn’t work.

Code Setup

Here’s my code for each relevant file:

  1. Login Route (login/route.ts)

    • This endpoint creates and sets a JWT token in the cookies when the user logs in.
    import { NextResponse, NextRequest } from "next/server";
    import jwt from 'jsonwebtoken';
    import { cookies } from 'next/headers';
    
    export async function POST(request: NextRequest) {
        try {
            const body = await request.json();
            const token = jwt.sign(body.user, 'secret', { expiresIn: '1d' });
            const isLocalhost = request.url.includes('localhost');
    
            cookies().set('token', token, {
                httpOnly: true,
                path: '/',
                sameSite: 'lax',
                secure: !isLocalhost,
                maxAge: 7 * 24 * 60 * 60,  // Adjusted for clarity
                ...(isLocalhost ? {} : { domain: 'swiftship-nine.vercel.app' })
            });
    
            return NextResponse.json({ message: "Success" }, { status: 200 });
        } catch (error) {
            return NextResponse.json({ message: "Error setting cookie" }, { status: 500 });
        }
    }
    
  2. Logout Route (logout/route.ts)

    • This endpoint is supposed to clear the JWT token from cookies.
    import { NextResponse, NextRequest } from "next/server";
    
    export function POST(request: NextRequest) {
        try {
            const response = NextResponse.json({ message: "Success" }, { status: 200 });
            response.cookies.set('token', '', { expires: new Date(0), path: '/' }); // Ensure matching path for deletion
            return response;
        } catch (error) {
            return NextResponse.json({ message: "Failed to log out" }, { status: 400 });
        }
    }
    
  3. Middleware (middleware.ts)

    • This file is responsible for verifying the JWT token for protected routes and redirecting accordingly.
    import { NextResponse } from 'next/server';
    import type { NextRequest } from 'next/server';
    import { jwtVerify } from 'jose';
    
    export async function middleware(request: NextRequest) {
        const { pathname } = request.nextUrl;
        const token = request.cookies.get("token")?.value;
        const secret = new TextEncoder().encode("secret");
        if (pathname.startsWith("/api/logout")) {
            // Clear the token cookie
            const res = NextResponse.next();
            res.cookies.set("token", "", { expires: new Date(0) });
        }
    
        // Check paths requiring logged-in user
        if (pathname.startsWith("/cart") || pathname.startsWith("/checkout") ||
            pathname.startsWith("/track") || pathname.startsWith("/profile")) {
    
            if (!token) return NextResponse.redirect(new URL('/login', request.url));
    
            try {
                const { payload } = await jwtVerify(token, secret);
    
                if (!payload || (payload.user_type !== 1 && pathname !== "/profile")) {
                    return NextResponse.redirect(new URL('/login', request.url));
                }
                return NextResponse.next();
            } catch (error) {
                return NextResponse.redirect(new URL('/login', request.url));
            }
    
        // Check paths for vendor/admin access
        } else if (pathname.startsWith("/vendor/") || pathname.startsWith("/vendorincoming") ||
                   pathname.startsWith("/admin")) {
    
            if (!token) return NextResponse.redirect(new URL('/', request.url));
    
            try {
                const { payload } = await jwtVerify(token, secret);
    
                if (!payload || pathname.replace("/admin/", "") !== payload.id || payload.user_type !== 2) {
                    return NextResponse.redirect(new URL('/', request.url));
                }
                return NextResponse.next();
            } catch (error) {
                return NextResponse.redirect(new URL('/login', request.url));
            }
    
        // Redirect logged-in users from login/signup pages
        } else if (pathname.startsWith("/login") || pathname.startsWith("/signup") || pathname.startsWith("/vendorob")) {
            if (token) return NextResponse.redirect(new URL('/', request.url));
        }
    
        return NextResponse.next();
    }
    
    // Middleware configuration for matching routes
    export const config = {
        matcher: [
            "/",
            "/cart",
            "/checkout",
            "/login",
            "/signup",
            "/track/:path*",
            "/menu/:path*",
            "/profile/:path*",
            "/admin/:path*",
            "/vendor/:path*",
            "/vendorincoming/:path*",
            "/vendorob",
        ],
    };
    

Issue

Despite setting expires: new Date(0) to clear the cookie in logout/route.ts and attempting to handle it in middleware, I sometimes notice:

  • The cookie doesn’t seem to be cleared immediately, leading to inconsistent behavior.
  • Protected routes don’t always redirect correctly after logging out, which suggests that the cookie might still exist momentarily.

What I’ve Tried

  • Verified the path in the cookie deletion matches the one used during setting.
  • Used both set with an empty value and expires: new Date(0) to delete the cookie.
  • Adjusted middleware to handle token verification errors and clear cookies conditionally.
  • Tested on both localhost and deployed environments to check for domain-specific behavior.

Question

What am I missing to ensure the cookie is consistently deleted upon logout? Could there be an issue with my middleware.ts handling or something specific to Next.js cookies? I have gone through different questions but they just point out how they the poster is deleting the request side cookie and not the response side cookie but I have considered that.

Any guidance on best practices for managing cookies in Next.js with middleware would be highly appreciated.

How to Rotate the header except 2 first columns in datatable in R

I followed some questions R datatable rotate header not aligning, but I am not sure how I can add parameter start, end to the headerCallback. I do have some columns no need to rotate 180 degree.

headerCallback <- c(
  "function(thead, data, start, end, display){",
  "  var $ths = $(thead).find('th');",
  "  $ths.css({'vertical-align': 'bottom', 'white-space': 'nowrap'});",
  "  var betterCells = [];",
  "  $ths.each(function(){",
  "    var cell = $(this);",
  "    var newDiv = $('<div>', {height: 'auto', width: cell.height()});",
  "    var newInnerDiv = $('<div>', {text: cell.text()});",
  "    newDiv.css({margin: 'auto'});",
  "    newInnerDiv.css({",
  "      transform: 'rotate(180deg)',",
  "      'writing-mode': 'tb-rl',",
  "      'white-space': 'nowrap'",
  "    });",
  "    newDiv.append(newInnerDiv);",
  "    betterCells.push(newDiv);",
  "  });",
  "  $ths.each(function(i){",
  "    $(this).html(betterCells[i]);",
  "  });",
  "}"
)

datatable(mtcars %>% mutate(name = row.names(mtcars),
                            id = row_number()) %>% select(id,name, everything()), 
          rownames = F, 
          class = list(stripe = FALSE),
          options = list(
            paging = F, autoWidth = F,
            searching= FALSE,
            scrollX = F,
            # initComplete = JS("function(settings, json) {
            #                     $(this.api().table().header()).css({
            #                       'font-size' : '12px'});}"),
            headerCallback = JS(headerCallback))) %>% 
  formatStyle(columns = c(1:10), `font-size` = '12px')

Problem with converting base64 pdf into url [closed]

I have problem with converting base64 pdf document into URL.

Here is code:

if (pdfDocumentBase64) {
    const binary = atob(pdfDocumentBase64.replace(/s/g, ''));

    const len = binary.length;
    const buffer = new ArrayBuffer(len);
    var view = new Uint8Array(buffer);
    for (var i = 0; i < len; i++) {
      view[i] = binary.charCodeAt(i);
    }
         
    const blob = new Blob([view], { type: "application/pdf" });
    url = URL.createObjectURL(blob);
  }
  console.log(url);

And here is result:

blob:null/34277b46-7323-4007-b87e-b130478c36ff

What I’m doing wrong?