Setting the initial frame on Lottie animation based on the theme from localStorage in React

I have a React component that implements a dark mode toggle using the following Lottie animation:

https://lottiefiles.com/free-animation/button-switch-ui-AX5XJZQdq8

The animation has different frames for the light-to-dark and dark-to-light transitions which I am able to set manually with an on click, however I also need to set initial frame of the Lottie animation based on the user’s theme preference stored in localStorage.

The issue I’m having is that the Lottie always defaults to frame 0 which means that if the user’s theme is dark mode, the Lottie animation will be incorrectly displayed.

import React, { useEffect, useState, useRef } from "react";
import Lottie from "lottie-react";
import darkModeToggle from "./assets/dark-mode-lottie.json";

const DarkModeToggle = () => {
  const initialTheme = localStorage.getItem("theme");
  const [isDarkMode, setIsDarkMode] = useState(initialTheme === "dark");
  const lottieRef = useRef(null);

  useEffect(() => {
    const root = document.documentElement;
    const metaThemeColor = document.querySelector('meta[name="theme-color"]');
    const themeColor = isDarkMode ? "#000000" : "#f1f5f9";

    if (isDarkMode) {
      root.setAttribute("data-theme", "dark");
      localStorage.setItem("theme", "dark");
      metaThemeColor.content = themeColor;
    } else {
      root.removeAttribute("data-theme");
      localStorage.setItem("theme", "light");
      metaThemeColor.content = themeColor;
    }
  }, [isDarkMode]);

  const handleClick = () => {
    if (lottieRef.current) {
      if (isDarkMode) {
        setIsDarkMode((prev) => !prev);
        lottieRef.current?.playSegments([120, 150], true);
      } else {
        setIsDarkMode((prev) => !prev);
        lottieRef.current?.playSegments([30, 50], true);
      }
    }
  };

  useEffect(() => {
    if (lottieRef.current) {
      const initialFrame = initialTheme === "dark" ? 30 : 0;
      lottieRef.current?.goToAndStop(initialFrame, true);
    }
  }, []);

  return (
    <button
      onClick={handleClick}
      className="w-24 h-24 p-1 rounded focus:outline-none"
      aria-label="Toggle dark mode"
    >
      <Lottie
        lottieRef={lottieRef}
        loop={false}
        autoplay={false}
        animationData={darkModeToggle}
      />
    </button>
  );
};

export default DarkModeToggle;

I have tried a few things including a useEffect to set the initial frame on render based on the theme as per above code, but this still ends up with the Lottie animation defaulting to frame 0 every time.

I have also tried to implement initialSegment on the Lottie element but again this ended with the same result.

I am currently using lottie-react because I use the playSegments method for playing the animating the lottie animation on click, but I have also tried using react-lottie too but to no avail.

Why doesn’t the span get the updated content, or the input get the updated id?

I’m sure it’s something simple that I’m missing, but I can’t figure it out! The code is definitely being hit (confirmed by console.log statements)

I am expecting that the id for the checkbox gets updated to append _n (where n is the current position) and the span inside the label gets its content changed from ? to SPAN n. As it is, only the label is getting updated as expected and I can’t figure out why, or how to make it so that they do get updated. If I update (for example) the span directly it works, as demonstrated near the end of the JS.

Note that this is handwritten quickly as a simplified version of the real thing, so I’ve skimped on proper validation etc. for this example. I’m using querySelectorAll("*") because in the real code I don’t know what the content of the template tag will actually be, so I need to check and update all relevant tags (which is way more than in this example, this is just to highlight the issue).

https://jsfiddle.net/tvqfjokz/11/

document.getElementById("add").addEventListener("click", function() {
  let clone=document.getElementById("template").firstElementChild.cloneNode(true);
  let pos = parseInt(document.getElementById("container").dataset.pos);
  pos++;
  document.getElementById("container").dataset.pos = pos;
  let elements=clone.querySelectorAll("*");
  for (let e of elements) {
    if (e.nodeName==="LABEL") {
      e.innerHTML+=" LABEL "+pos;
    }
    else if (e.nodeName==="INPUT") {
      console.log("Modifying input");
      e.id+="_"+pos;
    }
    else if (e.nodeName==="SPAN") {
      console.log("Modifying span");
      e.innerHTML="SPAN "+pos;
      console.log("Span content is now",e.innerHTML);
    }
  }
  clone.querySelector("span").innerHTML += " This works!";
  console.log("Check id of checkbox (should be checkbox_"+pos+"): ", clone.querySelector("input").id);
  
  document.getElementById("container").appendChild(clone);
});
<div id="template" style="display:none;">
    <div> 
      <label for="checkbox"><input id="checkbox" type="checkbox"> Checkbox <span class="pos">?</span></label>
    </div>
</div>
<button id="add">Add</button>
<div id="container" data-pos="0"></div>

Side note: Originally the template div was in a template tag, I changed it to be a div to make sure the issue wasn’t related to the template tag somehow.

How to receive a “stream of input” using XMLHttpRequest object?

I have a javascript file which creates a XMLHttpRequest object and opens a connection to a PHP file and sends a form object to it.
The Javascript file is like this:

let formData = new FormData();
formData.append("field1", ["mockdata1", "mockdata2"]);

let xhr = new XMLHttpRequest();
xhr.open("POST", "../phpscripts/agent.php");
xhr.send(formData);

The PHP file does something like this:

if( !empty( $_POST['field1'] )){
  
   $result; 

   for($x=0; $x<someLimit; $x++){
      //update $result
    }

    return $result;
}

So the PHP file queries another API and procedurally updates $result. The querying process takes time (each query to that API can range from 0.5 seconds to 2 seconds)

After it is done, it returns $result to the javascript file that sent the request. And this whole process takes quite some time because it makes multiple queries. There is a gap of around 10 seconds between when the javascript makes that xhr and when it gets the final result.

What I am trying to do is create some kind of a “listening system” inside that for-loop in the PHP file. So that the javascript file can read the current status of $result (as it builds up inside the for-loop) in predetermined intervals.

What is the best way to do this? Because the XHR object will only be received by javascript once that php for-loop is over and it returns $result.

Is there some way of creating a “stream” instead of a one-time request and return (which is how XHR works)? So that I can create a setInterval in the Javascript file which reads that stream every second and gets the current state of $result.

I have thoguht about making a 2nd XHR object but I don’t know how to make the php file return to that and then continue on with the for-loop. I really feel like I’m using the wrong methods here.

Improving JavaScript Refresh Rate Estimation with `requestAnimationFrame`

I’ve written a simple JavaScript function to estimate the screen’s refresh rate using requestAnimationFrame:

let frameCount = 0;
let startTime = performance.now();
let refreshRateEstimate = 0;

function estimateRefreshRate(currentTime) {
  frameCount++;
  const elapsedTime = currentTime - startTime;

  if (elapsedTime >= 1000) {
    refreshRateEstimate = frameCount;
    console.log(`Estimated refresh rate: ${refreshRateEstimate}`);
    return;
  }

  requestAnimationFrame(estimateRefreshRate);
}

requestAnimationFrame(estimateRefreshRate);

This function increments a counter (frameCount) with each animation frame. Once a second has passed (elapsedTime >= 1000), it sets refreshRateEstimate to the current frameCount and logs the result. It then stops calling requestAnimationFrame.

While this seems to provide a basic estimate, I’m wondering if there are more robust or accurate ways to achieve this, or potential pitfalls in this approach.

What are privacy-respecting ways to implement these functionalities involving app usage tracking?

Current approach

Currently the software project in 3d graphics & design I working on uses very “naive” approaches: sending periodically bunches of app usage events, like “app usage stated”, “feature A used via hotkey”, “feature B used via gizmo” or “feature C used via toolbar Y”, or “inactive time”, “active time”, “app closed” and alike.

Problem

I noticed that some browsers like Vivaldi and extensions block such things as navigator.sendBeacon, probably any of such requests.
I have a brief understanding of why. To some extent it is basically bad reputation of navigator.sendBeacon for being used to track people across sites and other sinister cases of cross-site tracking.

Question

Exploring this problem I come to the following question(s):
What a privacy-respecting ways to implement these:

  1. collect statistics of which app features are used the most?
  2. detect errors and send details on what features usage patterns are ending up with an error?
  3. prevent too many active app usage sessions per account (e. g. 2-3 active sessions max at same time are ok, but 20 or 50 are surely sign of possible leak of account credentials or something like that).

All these goals involving usage of some background requests. The navigator.sendBeacon was default choice at that moment when this functionality 1 and 2 got implemented. The 3 is a quite recent addition. The navigator.sendBeacon was a “default” way to implement this. To detect how many active app usages per account, and record if there are account with too many active usages, like 50 active usages happening at same time).

Most used features stats

Basically, this goal is to have an answers like: “what 10 features people using the most?”, “what are 10 least used features for last 30 days?” or “what are the most used way to activating feature A?” (assuming it can be activated via hotkey, toolbar, or menu). To my understanding it requires collection of lot of small pieces data, that does not require account identifiers, just tools/features ids, ways of activation, and time.

Detect errors patterns

This is a problematic aspect, because this requires collection of more detailed information, as it requires sequential records of what tools used and how, with an “app usage id” coupling these records together, including that record about an error such as stack trace etc.

Prevent too many active app usage sessions per account

This is a relatively new requirement compared to two others. As I see, this require sending some pulse requests every short interval, e. g. every minute or 2 keeping the app usage as “active”, and these requests should have the same value of usage id to be useful.

Conclusion

I understand that any implementation of all of these can be considered as a sort of compromise between efficiency, usability for person, usability for marketing department, and privacy of person who uses the app.

I want to change implementation of these features to make them implemented in the most privacy-respecting way possible, with outcome still being useful for the goals I has been tasked with.

Thanks for help, advice, and even just reading this far.

Improper ‘this’ context in my redo function [duplicate]

This question is about the context of this

The code:

var cutPaste = (e) => {

  const undoObj = {};
    undoObj.dest = X.getLastFocusOwner();
    undoObj.tag = tag;
    undoObj.clipElem = X.clipElem;
    undoObj.redo = () => {
      X.enableUndoObserver(false);
      var canInsert = Sections.canInsertInto(undoObj.tag, undoObj.clipElem.tagName);
      var canAppendTo = Sections.canAppendTo(undoObj.tag, undoObj.clipElem.tagName);
      if (!canInsert && !canAppendTo) return;
      if (!canAppendTo && canInsert) {
        this.dest.appendChild(undoObj.clipElem);

        ...  

On the last line in the code I have posted above, the context of this is the undo/redo button that was clicked, not the undoObj object. I was hoping someone could explain why.

BTW, I changed this line to

    undoObj.dest.appendChild(undoObj.clipElem);

And it worked. So my whole question is that given that the function belongs to the object, I thought that would be the context

Rotating or codes within Javascript loop function

This is my coding for the loop function display, however instead of displaying ‘affirm’ or ‘katapult’ I need for it to display their <p>/<span> to call their javascript action.

Example output:

<p id="affirm" class="affirm-as-low-as" data-page-type="category" data-amount="{{product.price}}"></p>

var example = ['affirm', 'katapult'];
textSequence(0);

function textSequence(i) {
  if (example.length > i) {
    setTimeout(function() {
      document.getElementById("container").innerHTML = example[i];
      textSequence(++i);
    }, 5000);

  } else if (example.length === i) {
    textSequence(0);
  }
}
<div id="container"></div>

In a single page application, Is there a way to have a link that downloads a file with contents generated on the fly?

I have a single page application running from a web server that can only serve static files. This application interacts with the operator via a web browser and a certain data structure gets built. I want to make this data structure available for download, say as a JSON file. But I cannot have it on the back end as an actual file. What can I do?

React component with react-infinite-scroll-component loads posts twice on initial render

I’m using react-infinite-scroll-component in a React component that fetches posts from my backend for a specific group. The problem is that when the page loads, it shows the same posts twice. I also commented out React.StrictMode and the page still shows all the posts twice.

I can’t figure out what’s causing the duplication. Here’s the full component:

import { useState, useEffect, useContext } from "react";
import { useParams } from "react-router-dom";
import axiosInstance from "../../api/axiosInstance";

import PostForm from "../forms/PostForm";
import { AppContext } from "../../Context/AppContext";
import Post from "../post/Post";
import InfiniteScroll from "react-infinite-scroll-component";

export default function GroupsSinglePage() {
  const { group_id } = useParams();
  const { user } = useContext(AppContext);

  const [groupData, setGroupData] = useState(null);
  const [posts, setPosts] = useState([]);
  const [page, setPage] = useState(1);
  const [hasMore, setHasMore] = useState(true);
  const [loadingGroup, setLoadingGroup] = useState(true);

  const fetchGroupData = async () => {
    try {
      const response = await axiosInstance.get(`/groups/${group_id}`);
      setGroupData(response.data.data);
    } catch (error) {
      console.error("Error fetching group data:", error);
    } finally {
      setLoadingGroup(false);
    }
  };

  const fetchPosts = async () => {
    try {
      const response = await axiosInstance.get(
        `/posts/group/${group_id}?page=${page}`
      );
      const newPosts = response.data.data;

      setPosts((prev) => [...prev, ...newPosts]);

      const isMore =
        response.data.meta.current_page < response.data.meta.last_page;
      setHasMore(isMore);
    } catch (error) {
      console.error("Error fetching posts:", error);
    }
  };

  const handleSubmit = async (formData) => {
    try {
      const response = await axiosInstance.post("/posts", formData, {
        headers: {
          "Content-Type": "multipart/form-data",
        },
      });
      // Reset state and refetch fresh posts
      setPosts([]);
      setPage(1);
      setHasMore(true);
      fetchPosts();
    } catch (error) {
      console.error("Error submitting post:", error);
    }
  };

  const loadMorePosts = () => setPage((prev) => prev + 1);

  useEffect(() => {
    fetchGroupData();
  }, [group_id]);

  useEffect(() => {
    setPosts([]);
    setHasMore(true);
    setPage(1);
  }, [group_id]);

  useEffect(() => {
    fetchPosts();
  }, [page]);

  if (loadingGroup || !groupData) {
    return <div>Loading...</div>;
  }

  return (
    <>
      <div
        style={{
          backgroundImage: `linear-gradient(to bottom, transparent, #171717), url(${groupData.cover_image[0].url})`,
        }}
        className="h-[300px] bg-cover bg-center rounded-lg py-6 px-3 flex items-end"
      >
        <h1 className="text-4xl text-white">{groupData.name}</h1>
      </div>

      <div className="max-w-screen-lg mx-auto py-6">
        <PostForm onSubmit={handleSubmit} group_id={group_id} />
        <hr className="my-10" />

        {posts.length > 0 ? (
          <InfiniteScroll
            dataLength={posts.length}
            next={loadMorePosts}
            hasMore={hasMore}
            loader={
              <div className="flex justify-center py-4">
                <div className="animate-spin rounded-full h-6 w-6 border-t-2 border-b-2 border-blue-500"></div>
              </div>
            }
            endMessage={
              <p className="text-center text-gray-500 mt-4">
                No more posts to show.
              </p>
            }
          >
            {posts.map((post) => (
              <Post
                key={post.id}
                id={post.id}
                content={post.content}
                media={post.media}
                author={post.user.full_name}
                created_at={post.created_at}
                comments={post.comments}
                comments_count={post.comments_count}
              />
            ))}
          </InfiniteScroll>
        ) : (
          <p className="text-center text-gray-500">
            No posts found for this group.
          </p>
        )}
      </div>
    </>
  );
}

How do I bind element index using vue.js v-for to element model

Let’s say I have the following model:

const mymodel = ref({
  some_collection: []
});

I also have an element inside my vue.js component:

<div v-for="(div_model, index) in mymodel.some_collection" :key="index">
...
</div>

I have a button that makes more divs like this appear on screen, like this:

... @onclick="mymodel.some_collection.push({})" ...

Now what I want is to have each element of array mymodel.some_collection to have a property, let’s say index, with value equal to index key mentioned above. For the first one it is going to be index: 0, and so on.

The only way I found to do it is to add JS code to some action handler for some input inside each of my divs, like this:

<input .... @onchange="() => {div_model.index = index; handleOnChange();}" ... />

Although it actually works, I don’t like that solution at all – user might not change value and I won’t have index. Is there any way to set model property when my div is created? Something similar to onload event for <body>, if only it existed for divs.

TipTap editor in React strips mark elements when passing text into it

I’m working on a web app that contains a text editor (TipTap particularly), and does some operations on the text, like marking parts of it with <mark> elements. I’m using the Highlight extension so that TipTap recognizes the element. Here is the code of my React component that uses TipTap:

import { EditorProvider } from '@tiptap/react';
import StarterKit from '@tiptap/starter-kit';
import Placeholder from '@tiptap/extension-placeholder';
import Link from '@tiptap/extension-link';
import Highlight from '@tiptap/extension-highlight';
import EditorToolbar from './EditorToolbar';

const Tiptap = (props) => {
    const extensions = [
        StarterKit,
        Highlight,
        Link.configure({
            protocols: ['https', 'http', 'mailto', 'tel'],
            autolink: false,
            defaultProtocol: 'https',
            openOnClick: false,
        }),
        Placeholder.configure({
            placeholder: "Article goes here",
        }),
    ];
    const handleUpdate = (result) => {
        const content = result.editor.getHTML();
        if (content !== props.content) {
            props.callback(content);
        }
    }

    console.log("text in editor:");
    console.log(props.content);
    return (
        <EditorProvider
        slotBefore={<EditorToolbar />}
        extensions={extensions}
        content={props.content}
        onUpdate={handleUpdate}
        ></EditorProvider>
    )
}

export default Tiptap;

What is OK:

  • the text I want to show in editor is OK when displayed in console one line before the editor – tags are in place
  • The Highlight extension works properly – I can select some text, and when I click the button that fires the toggleHighlight command – text in editor gets highlighted.

What is not OK:
Text in editor does not contain tags when passed as initial content (although props.content value is OK when shown in console)

AI assistant did not suggest anything that solves the problem. I tried adding a custom Mark extension, tried disabling input rules, but no success.

SVG filter patterns mix randomly and use as pattern again

I want to blend two SVG patterns randomly inside an SVG filter, and use them as pattern again to fill an element (the only output rectangle). My patterns are sized 12*12. The JavaScript first draws rectangles of size 12*12, then fills each of them with a random pattern, creating a mixed result.

Something wrong with the script. This is the code I tried, problem is they don’t mix together, but fill the whole thing with one of them, randomly changes every time the page is refreshed. No error messages.

The whole HTML code:

const mixedPattern = document.getElementById('mixed-pattern');

function getMixedPattern() {
  const svgNS = "http://www.w3.org/2000/svg";

  // Add rectangles of the patterns(12*12)
  for (let i = 0; i < 4; i++) {
    for (let j = 0; j < 4; j++) {
      const rect = document.createElementNS(svgNS, "rect");
      rect.setAttribute("x", i * 3);
      rect.setAttribute("y", j * 3);
      rect.setAttribute("width", "3");
      rect.setAttribute("height", "3");

      // Randomly fill the rectangles
      const randomFill = Math.random() < 0.5 ? "url(#dark_pattern)" : "url(#dark_pattern2)";
      rect.setAttribute("fill", randomFill);

      mixedPattern.appendChild(rect);
    }
  }
}

mixedPattern.setAttribute('href', getMixedPattern());
<svg width="100%" height="500" >

  <pattern id="dark_pattern" patternUnits="userSpaceOnUse" width="12" height="12">
    <rect x="0" y="0" width="3" height="3"/>
    <rect x="6" y="0" width="3" height="3"/>
    <rect x="3" y="3" width="3" height="3"/>
    <rect x="0" y="6" width="3" height="3"/>
    <rect x="6" y="6" width="3" height="3"/>
  </pattern>

  <pattern id="dark_pattern2" patternUnits="userSpaceOnUse" width="12" height="12">
    <rect x="0" y="0" width="3" height="3"/>
    <rect x="6" y="0" width="3" height="3"/>
    <rect x="0" y="6" width="3" height="3"/>
    <rect x="6" y="6" width="3" height="3"/>
  </pattern>

  <pattern id="mixed-pattern" patternUnits="userSpaceOnUse" width="12" height="12">
    <rect x="0" y="0" width="12" height="12" fill="url(#dynamically-mixed-pattern)" />
  </pattern>

  <rect x="0" y="0" width="100%" height="100%" fill="url(#mixed-pattern)" />
</svg>

The mixed pattern should randomly choose one pattern every 12*12, and then fill the element.
Hope it will look like this:
random pattern example

Thanks in advance!

Highlighting text in input at runtime

JavaScript application.
There is an input where text is entered.
How to fill the text (only text) of this input if the color comes at runtim?
That is, how to correctly set the style of a pseudo-class of the type at runtime?

input::first-line { background-color: "red" } // instead of "red" - the value from the variable

Or any other solution if there is one.

Checking my understanding of beforeunload

I’m trying to understand what I can and can’t do with onbeforeunload. I’ve read the docs in MDN and feel I have… some understanding.

The problem I’m trying to solve:

I have this form with lots of records on it. I’m auto-saving as records are edited. However, if there’s an error that prevents saving, users don’t always notice while they’re making lots of updates.

So, naturally, I want to tell them.

My real problem is that I want to present a list of unsaved changes – lots of records, users shouldn’t have to scroll through trying to spot the highlighted ones with errors. Or even search for a string, really.

I’ve managed to do this but am a little confused. Thus, my question.

I know that I don’t control the contents of the message in the confirm alert that is triggered from the onbeforeunload handler.

But do I have any control over what happens in the case of either response (leave or stay)?

My div with unsaved changes shows, regardless of what they choose. It won’t prevent leaving, of course, if they choose to leave… but they’re confused about why it’s showing at all.

With your usual confirm prompt, you control what happens in either case, but this isn’t a normal prompt.

Am I understanding correctly? If I am, I’ll just endeavor to train them about what’s happening. They’d rather see it when they’ve already said peace and are just waiting for it to happen than not get the prompt at all.

Thanks all!