Update Apple-wallet pass

I have generated successfully apple wallet passes with passkit-generator library. Now i have to update generated passes.
Can anyone please explain what kind of steps i need to take to accomplish this? So my wallet’s passes can be updated easily.

Here is my code snippet, which i tried to integrate it first:-

const jwt = require("jsonwebtoken");
const fs = require("fs");
const axios = require("axios");

const teamId = "";
const keyId = "";

const privateKey = fs.readFileSync("./certs/AuthKey.p8", "utf8");

const jwtoken = jwt.sign({}, privateKey, {
    algorithm: "ES256",
    expiresIn: "24h",
    issuer: teamId,
    header: { alg: "ES256", kid: keyId },
});

module.exports = {

    updatePass: async (req, res) => {


        let serialNumber = '9876543210'

        try {

            const pushToken = ""; // 

            const bundleId = "pass.walletpass"; // 

            const apnsURL = `https://api.sandbox.push.apple.com/3/device/${pushToken}`;


            const response = await axios.post(
                apnsURL,
                {},
                {
                    headers: {
                        "Authorization": `Bearer ${jwtoken}`,
                        "apns-topic": bundleId,
                        "Content-Type": "application/json"
                    }
                }
            );

            console.log(response);

        } catch (error) {
            console.error("Error sending push notification:", error);
        }


        console.log(`Push notification sent for pass ${serialNumber}`);
        res.send("Pass update triggered.");
    }
}

But i am not able to achieve what i want. so can anyone Please Help how wallet can be updated dynamically?

Why is my React event handler and state function “working” in all aspects, except the actual conditional rendering of the component?

I am developing in react for the first time and I need to have a button in my nav bar, that toggles the visibility of my filters section. I have used an onClick listener on the button and set it up to toggle the default false state, and only render the filters when the state is true, and all that works when tested, except nothing is ever visually shown in my app. Please help with the rendering nothing I’ve tried is working and I am all out of ideas.

App.jsx

import './App.scss'
import { useState, useEffect } from 'react';
import Navigation from '../src/components/Navigation/Navigation.jsx'
import ParentContainer from '../src/components/ParentContainer/ParentContainer.jsx'
import Footer from '../src/components/Footer/Footer.jsx'
import PhotoList from '../src/components/PhotoList/PhotoList'
import Filters from './components/Filters/Filters.jsx';
import Mission from '../src/components/Mission/Mission.jsx'

export default function App() {
  const [filterToggle, setFilterToggle] = useState(false);
  useEffect(() => {
    console.log('filterToggle updated:', filterToggle);
  }, [filterToggle]); 
  
  return (
    <div className='app'>
      <Navigation filterToggle={filterToggle} setFilterToggle={setFilterToggle}/>
        <div className="app__content">
          <Mission />
          <PhotoList />
          {/* <Filters /> */}
          {filterToggle && <Filters />}
          {/* {filterToggle ? <Filters /> : null} */}
        </div>
      <Footer />
    </div>
  )
};

Navigation.jsx

import './Navigation.scss';
import Button from '../Button/Button';

export default function Navigation({ filterToggle, setFilterToggle}) {
    const filtersClicked= (event) => {
        event.preventDefault();
        setFilterToggle(!filterToggle);
        console.log("app recognizes event", filterToggle);
      };
    return (
      <>
        <section className="navigation">
            <div className="navigation__content">
                <h1 className="navigation__wordmark">Snaps</h1>
                <Button handleClick={filtersClicked} isSelected={filterToggle}/>
            </div>         
        </section>
    </>
    )
};

Button.jsx

import './Button.scss';
import filterIcon from '../../assets/images/Filter.svg';

export default function Button({handleClick, isSelected}) {
    return (
        <button className={`button ${isSelected ? 'button--selected' : ''}`} onClick={handleClick}>
            <p className="button__text">Filters</p>
            <svg className="button__icon" alt="An icon of a downward filter" viewBox="0 0 15 11" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path d="M6.66667 10.5H8.33333C8.79167 10.5 9.16667 10.125 9.16667 9.66667C9.16667 9.20833 8.79167 8.83333 8.33333 8.83333H6.66667C6.20833 8.83333 5.83333 9.20833 5.83333 9.66667C5.83333 10.125 6.20833 10.5 6.66667 10.5ZM0 1.33333C0 1.79167 0.375 2.16667 0.833333 2.16667H14.1667C14.625 2.16667 15 1.79167 15 1.33333C15 0.875 14.625 0.5 14.1667 0.5H0.833333C0.375 0.5 0 0.875 0 1.33333ZM3.33333 6.33333H11.6667C12.125 6.33333 12.5 5.95833 12.5 5.5C12.5 5.04167 12.125 4.66667 11.6667 4.66667H3.33333C2.875 4.66667 2.5 5.04167 2.5 5.5C2.5 5.95833 2.875 6.33333 3.33333 6.33333Z" fill="currentColor"/>
            </svg>  
        </button>
    )
};

Filters (for reference) commented out any other functionality

import './Filters.scss';
import FiltersList from '../FiltersList/FiltersList.jsx'

export default function Filters() {
    console.log('Filters component rendered');
    return (
        <div className="filters">
            <h2 className="filters__heading">
                Filters
            </h2>
            <FiltersList />
                {/* setFilterCards={setFilterCards}
                filterCards={filterCards} /> */}
        </div>
    )
};
// {setFilterCards, filterCards}

It is not an issue with css (hidden or overflow issues) especially since I am trying to hide the filter at the component level, not the css class level. I also tried to use a ternary to show/hide the filters component and that does not work either. I need my filters component to render on click (true) and to not render on flase, this wont work and I have no idea where to start.

How to properly implement an animated theme change?

I’m going to implement a similar theme change for my site as part of my study.I managed to implement a smooth change of theme in the form of a circle. But I do not understand how to make the theme change in the form of a chaotic strip, if you have the skills or knowledge, please tell me. I have attached a compressed code for a compact launch of the shift that I got.

    const container = document.querySelector(".cards");

    for (let i = 1; i <= 6; i++) {
        const card = document.createElement("div");
        card.className = "card";

        const title = document.createElement("h2");
        title.textContent = `Title${i}`;

        const paragraph = document.createElement("p");
        paragraph.textContent = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";

        card.appendChild(title);
        card.appendChild(paragraph);
        container.appendChild(card);
    }

    document.addEventListener("DOMContentLoaded", () => {
        const btn = document.querySelector(".toggle-button");

        const isDarkMode = () => document.documentElement.getAttribute("data-theme") === "dark";
        const setDarkMode = (enabled) => {
            document.documentElement.setAttribute("data-theme", enabled ? "dark" : "light");
        };

        btn.addEventListener("click", async () => {
            if (!document.startViewTransition || window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
                return setDarkMode(!isDarkMode());
            }

            await document.startViewTransition(() => {
                setDarkMode(!isDarkMode());
            }).ready;

            const {top, left, width, height} = btn.getBoundingClientRect();
            const right = window.innerWidth - left;
            const bottom = window.innerHeight - top;
            const maxRadius = Math.hypot(Math.max(left, right), Math.max(top, bottom));

            document.documentElement.animate(
                {
                    clipPath: [
                        `circle(0px at ${left + width / 2}px ${top + height / 2}px)`,
                        `circle(${maxRadius}px at ${left + width / 2}px ${top + height / 2}px)`,
                    ],
                },
                {
                    duration: 1500,
                    easing: "ease-in",
                    pseudoElement: "::view-transition-new(root)",
                }
            );
        });

        setDarkMode(isDarkMode());
    });
      :root {
          color-scheme: dark;
          --theme-text: white;
          --theme-bg: black;

          --bg-card: #29292d;
          --border-card: #3b3b41;
      }

      [data-theme="light"] {
          color-scheme: light;
          --theme-text: black;
          --theme-bg: white;

          --bg-card: #dedfe1;
          --border-card: #cacbcc;
      }

      ::view-transition-old(root),
      ::view-transition-new(root) {
          animation: none;
          mix-blend-mode: normal;
      }

      body {
          background: var(--theme-bg);
      }

      .header {
          display: flex;
          justify-content: end;
          margin-bottom: 10px;
      }

      .cards {
          display: grid;
          gap: 16px;
          grid-template-columns: repeat(3, minmax(0, 1fr));
      }

      .card {
          border: 1px solid var(--border-card);
          color: var(--theme-text);
          background: var(--bg-card);
          border-radius: 14px;
          padding: 6px 10px;
      }
<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div class="header">
  <button class="toggle-button">Change theme</button>
</div>

<div class="cards"></div>

</body>
</html>

Gemini API: INVALID_ARGUMENT error when using JavaScript fetch but works with cURL and Go

I can successfully call the Gemini API OpenAI compatibility using this cURL command:

curl --request POST 
  --url https://generativelanguage.googleapis.com/v1beta/openai/chat/completions 
  --header 'authorization: Bearer MY_API_KEY' 
  --header 'content-type: application/json' 
  --data '{
  "model": "gemini-1.5-flash",
  "messages": [
    {
      "role": "user",
      "content": "Explain how AI works"
    }
  ]
}'

But when I try using JavaScript fetch, I get a 400 INVALID_ARGUMENT error:

{
  "error": {
    "code": 400,
    "message": "Request contains an invalid argument.",
    "status": "INVALID_ARGUMENT"
  }
}

here is my code(generated by Bruno API Client):

const url = 'https://generativelanguage.googleapis.com/v1beta/openai/chat/completions';
const options = {
  method: 'POST',
  headers: {
    'content-type': 'application/json',
    authorization: 'Bearer MY_API_KEY'
  },
  body: '{"model":"gemini-1.5-flash","messages":[{"role":"user","content":"Explain how AI works"}]}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}

The same API works fine with a Go client. What am I missing here? Why is the JavaScript fetch not working?

I confirmed that the API key is valid and has the required permissions.

I don’t want to use the SDK since it’s too large for my app.

How to import Stream.Transform in Webpack >5?

I have a project with dependencies that rely on node.js type libraries, like Buffer and Stream.

I have added the following to Webpack config:

       cfg.resolve.fallback = {
          ...cfg.resolve.fallback,
          buffer: require.resolve('buffer'),
          stream: require.resolve('stream-browserify'),
          _stream_duplex: require.resolve('readable-stream/lib/_stream_duplex'),
          _stream_passthrough: require.resolve('readable-stream/lib/_stream_passthrough'),
          _stream_readable: require.resolve('readable-stream/lib/_stream_readable'),
          _stream_transform: require.resolve('readable-stream/lib/_stream_transform'),
          _stream_writable: require.resolve('readable-stream/lib/_stream_writable')
        }

        cfg.plugins.push(new webpack.ProvidePlugin({
          Buffer: ['buffer', 'Buffer']
        }))

        cfg.plugins.push(new NodePolyfillPlugin())

I can import the Stream class:

const stream = require('stream') // this works

but I cannot see the Stream.Transform class:

const Transform = require('stream').Transform /// undefined

Has anybody idea how to fix this? I have been trying for 2 days now without progress.

Thanks!

How do I get NestJS microservice to communicate with RabbitMQ in Kubernetes

Setting up my development environment with Docker compose is really simple. However I am trying to setup the same project with Kubernetes. To make things simple I have broke down the setup to a single microservice with RabbitMQ. Below is deployment yaml file where I have attempted to declare all the things I believe I need and it does not work.

Deployment file:

apiVersion: v1
kind: Service
metadata:
  name: rabbitmq
spec:
  ports:
    - port: 5672
      targetPort: 5672
  selector:
    app: rabbitmq

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: rabbitmq
spec:
  replicas: 1
  selector:
    matchLabels:
      app: rabbitmq
  template:
    metadata:
      labels:
        app: rabbitmq
    spec:
      containers:
        - name: rabbitmq
          image: rabbitmq:3-management
          ports:
            - containerPort: 5672
            - containerPort: 15672

---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: notification
  name: notification
spec:
  replicas: 1
  selector:
    matchLabels:
      app: notification
  template:
    metadata:
      labels:
        app: notification
    spec:
      containers:
        - image: aaronbalthaser/notification:v0.5
          name: notification
          env:
            - name: RABBITMQ_URI
              value: amqp://rabbitmq:5672
            - name: MAILGUN_API_KEY
              value: "ea0d74d8da769f9c405b02c85fb3dd37-7113c52e-98a7f729"
            - name: MAILGUN_DOMAIN
              value: "sandbox37f3b689954a497e8c19af2282730630.mailgun.org"
            - name: DOMAIN
              value: "http://localhost:4200"

Main TS file:

async function bootstrap() {
  const app = await NestFactory.create(NotificationModule);

  const configService = app.get(ConfigService);

  app.connectMicroservice({
    transport: Transport.RMQ,
    options: {
      urls: [configService.getOrThrow("RABBITMQ_URI")],
      queue: NOTIFICATION_SERVICE,
    },
  });

  await app.startAllMicroservices();
}

bootstrap();

Dockerfile:

###################
# DEVELOPMENT
###################

FROM node:alpine As development

WORKDIR /usr/src/app

COPY package.json ./
COPY pnpm-lock.yaml ./
COPY tsconfig.json ./
COPY nest-cli.json ./
COPY ca.pem ./

RUN npm install -g pnpm

COPY apps/notification apps/notification
COPY libs libs

RUN pnpm install 

RUN pnpm run build notification

###################
# PRODUCTION
###################

FROM node:alpine as production

ARG NODE_ENV=production
ENV NODE_ENV=${NODE_ENV}

WORKDIR /usr/src/app

COPY package.json ./
COPY pnpm-lock.yaml ./
COPY ca.pem ./

RUN npm install -g pnpm

RUN pnpm install --prod

COPY --from=development /usr/src/app/dist ./dist

CMD ["node", "dist/apps/notification/main"]

When attempting to run the file I get the following output. The console log is me outputting those host address I am passing in from the Config service.

enter image description here

For context below is the output I see when I use Docker compose:enter image description here

Onesignal login() function creates a seperate user record

I’m trying to use the OneSignal Web SDK to send push notifications and identify users across devices by an external id.
When a user is successfully logged in, I call ‘Onesignal.login(externalID)’ to associate their external id with Onesignal. Instead of associating the current user_id (which is created the moment Onesignal loads) with the external id, Onesignal creates a seperate user record instead.
As shown here: Onesignal User Records

So I have two records, one with the id but no push subscription, and another with the valid push:subscribed state but no externalId. Only when I log out and log back in does Onesignal associate the two records as one.
This is my code which is called after the user has successfully logged in (I’m using Vue javascript)

    OneSignalDeferred.push(async function (OneSignal) {
          const id = await getUserId()
    
          console.log(id)
          console.log(OneSignal.User.onesignalId)
    
          await OneSignal.login(id).catch((error) => {
            console.error('OneSignal login error: ', error)
          })
        })

I checked that the user_id and OneSignal.User.onesignalId were all properly loaded in and accurate, which they were.

When logging out and then logging back in, the two records are associated correctly after the second login attempt, but not on the first login.

Expected Behavior:
The user should be logged in only once, with the externalId correctly associated with the existing push subscription.
There should not be multiple user records created in OneSignal.

Running express typescript development server throws TypeErrors but not when i build and run it

I have extended the native Express Request object using

// types/express.d.ts

    import { User } from '../UserTypes'; // Ensure this is correct
    
    declare global {
      namespace Express {
        interface Request {
          user: User;
        }
      }
    }

but my code throws error from file

import { NextFunction, Request, Response } from 'express';
import jwt from 'jsonwebtoken';
import { prisma } from '../prismaClient';

const authMiddleware = async (
  req: Request,
  res: Response,
  next: NextFunction,
): Promise<any> => {
  const token = req.cookies.jwt;

  if (!token) {
    return res.status(401).json({ message: "Unauthorized: No token provided" });
  }

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET as string) as {
      id: string;
    };

    const credential = await prisma.credential.findUnique({
      where: { id: decoded.id },
      include: {
        student: true,
        faculty: true,
        admin: true,
      },
    });

    if (!credential) {
      return res.status(401).json({ message: "Unauthorized: User not found" });
    }

    req.user = {
      id: credential.id,
      role: credential.role,
      student: credential.student,
      faculty: credential.faculty,
      admin: credential.admin,
    };

    next();
  } catch (error) {
    console.error("JWT verification or database error:", error);
    return res.status(401).json({ message: "Unauthorized: Invalid token" });
  }
};

export default authMiddleware;

when i try to run the development server using “nodemon index.ts” or “node-ts index.ts”
It doesnt throw this error if i build it into JS and run the build but only when i run TS directly. It doesnt even throw any error when i run “tsc –noEmit”

This is my modified tsconfig.json

{
  "compilerOptions": {
    "target": "es2016",
    "module": "commonjs",
    "outDir": "./dist",
    "resolveJsonModule": true,
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "skipLibCheck": true,
    "rootDir": "./src",
    "typeRoots": ["./node_modules/@types", "./src/types"],
    "types": ["node", "express"]
  },
  "exclude": ["src/scripts"]
}

Error Thrown is :-

PS C:Programming WorkspacesWebDevElectiveManagementSystemserver> npm run dev

> [email protected] dev
> ts-node src/index.ts

C:Programming WorkspacesWebDevElectiveManagementSystemservernode_modulests-nodesrcindex.ts:859
    return new TSError(diagnosticText, diagnosticCodes, diagnostics);
           ^
TSError: ⨯ Unable to compile TypeScript:
src/middleware/authMiddleware.ts:34:9 - error TS2339: Property 'user' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.

34     req.user = {
           ~~~~

    at createTSError (C:Programming WorkspacesWebDevElectiveManagementSystemservernode_modulests-nodesrcindex.ts:859:12)
    at reportTSError (C:Programming WorkspacesWebDevElectiveManagementSystemservernode_modulests-nodesrcindex.ts:863:19)
    at getOutput (C:Programming WorkspacesWebDevElectiveManagementSystemservernode_modulests-nodesrcindex.ts:1077:36)
    at Object.compile (C:Programming WorkspacesWebDevElectiveManagementSystemservernode_modulests-nodesrcindex.ts:1433:41)
    at Module.m._compile (C:Programming WorkspacesWebDevElectiveManagementSystemservernode_modulests-nodesrcindex.ts:1617:30)
    at Module._extensions..js (node:internal/modules/cjs/loader:1435:10)
    at Object.require.extensions.<computed> [as .ts] (C:Programming WorkspacesWebDevElectiveManagementSystemservernode_modulests-nodesrcindex.ts:1621:12)
    at Module.load (node:internal/modules/cjs/loader:1207:32)
    at Function.Module._load (node:internal/modules/cjs/loader:1023:12)
    at Module.require (node:internal/modules/cjs/loader:1235:19) {
  diagnosticCodes: [ 2339 ]
}
PS C:Programming WorkspacesWebDevElectiveManagementSystemserver> 

this is where i am using the authMiddleware.ts

import express from 'express';
import BranchController from '../controllers/BranchController';
import { authorizeRoles } from '../middleware/roleMiddleware';
import { UserRole } from '@prisma/client';

const router = express.Router();

/**
 * @swagger
 * tags:
 *   name: Branches
 *   description: API endpoints for managing branches
 */

/**
 * @swagger
 * /branches:
 *   get:
 *     summary: Get all branches
 *     tags: [Branches]
 *     parameters:
 *       - in: query
 *         name: departmentId
 *         schema:
 *           type: string
 *         description: Optional department ID to filter branches
 *     responses:
 *       200:
 *         description: Successfully retrieved all branches
 *         content:
 *           application/json:
 *             schema:
 *               type: array
 *               items:
 *                 type: object
 *       500:
 *         description: Unable to fetch branches
 */
router.get(
  "/",
  authorizeRoles([UserRole.ADMIN]),
  BranchController.getAllBranches,
);

/**
 * @swagger
 * /branches/{id}:
 *   get:
 *     summary: Get a branch by ID
 *     tags: [Branches]
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: string
 *         description: The branch ID
 *     responses:
 *       200:
 *         description: Successfully retrieved the branch
 *         content:
 *           application/json:
 *
 *       404:
 *         description: Branch not found
 *       500:
 *         description: Unable to fetch branch
 */
router.get(
  "/:id",
  authorizeRoles([UserRole.ADMIN]),
  BranchController.getBranchByID,
);

export default router;

Nested LitElement component modal closing on re-render

I have a parent component called ams-card like below. Some code removed to keep it short, but full version here

Visually, put together it looks like this:
enter image description here

When you click on a spool, it opens a modal with more information. The issue I am having though, is something is triggering it to re-render, which closes the modal. I’m assuming at this point, its coming from the parent ams-card, but I am a little unsure.

I have an instance of these where I only render the spool once in its own right, and that never gets closed due to updates, hence why I think perhaps its something to do with the repeating, and maybe some reference clashes or something. I’m honestly not sure at this point

ams-card.ts

@customElement(AMS_CARD_NAME)
export class AMS_CARD extends LitElement {
  // private states
  @state() private _subtitle;
  //etc
  @state() private _isLoading = true;

  @provide({ context: hassContext })
  @state()
  private _hass?;

  @provide({ context: deviceEntitesContext })
  private _deviceEntities: any;

  @provide({ context: infoBarContext })
  private _infoBar: {
    active: boolean;
    title: string;
    sensors: { humidity: any; temperature: any };
  } = {
    active: false,
    title: "",
    sensors: {
      humidity: null,
      temperature: null,
    },
  };

  config: any;

  set hass(hass) {
    this._hass = hass;
    this._states = hass.states;
    this.fetchDevices();
  }

  private async fetchDevices() {
    this._isLoading = true;
    try {
      await this.filterBambuDevices();
      this._infoBar.sensors = this.returnInfoBarSensors();
    } finally {
      this._isLoading = false;
    }
  }

  setConfig(config) {
    if (!config.ams) {
      throw new Error("You need to select an AMS");
    }
    this.config = config;

    this._subtitle = config.subtitle === "" ? nothing : config.subtitle;
    this._deviceEntities = config.entities;
    this._deviceId = config.ams;
    this._style = config.style;
    this._infoBar = {
      active: config.show_info_bar ? true : false,
      title: config.subtitle === "" ? nothing : config.subtitle,
      sensors: {
        humidity: null,
        temperature: null,
      },
    };
    this._showInfoBar = config.show_info_bar ? true : false;
    this._showType = config.show_type ? true : false;
    this._customHumidity = config.custom_humidity === "" ? nothing : config.custom_humidity;
    this._customTemperature =
      config.custom_temperature === "" ? nothing : config.custom_temperature;
  }

  render() {
    if (this._isLoading) return nothing;
      return html` <vector-ams-card .showType=${this._showType} /> `;
  }

  private returnInfoBarSensors() {
    const sensors: { humidity: any; temperature: any } = {
      humidity: null,
      temperature: null,
    };
    //do stuff
    return sensors;
  }
}

this returns vector-ams-card like so:

  @consume({ context: deviceEntitesContext, subscribe: true })
  private _entities;

  @property() public showType;

  static styles = styles;

  render() {
    return html`
      <ha-card class="ha-bambulab-vector-ams-card">
        <div class="v-wrapper">
          <info-bar></info-bar>
          <div class="v-ams-container">
            ${this._entities?.spools?.map(
              (spool: { entity_id: string }) => html`
                <ha-bambulab-spool
                  .key="${spool.entity_id}"
                  style="padding: 0px 5px"
                  .entity_id="${spool.entity_id}"
                  .show_type=${this.showType}
                ></ha-bambulab-spool>
              `
            )}
          </div>
        </div>
      </ha-card>
    `;
  }
}

which finally calls the spool component and modal here

import { customElement, property, state } from "lit/decorators.js";
import { html, LitElement, nothing } from "lit";
import styles from "./spool.styles";
import "../dialog/dialog";
import { getContrastingTextColor } from "../../../utils/helpers";
import { hassContext } from "../../../utils/context";
import { consume } from "@lit/context";

@customElement("ha-bambulab-spool")
export class Spool extends LitElement {
  @consume({ context: hassContext, subscribe: true })
  private hass;

  @property({ type: Boolean }) public show_type: boolean = false;
  @property({ type: String }) public entity_id;

  @state() private color;
  @state() private name;
  @state() private active;
  @state() private remaining;
  @state() private tag_uid;
  @state() private state;
  @state() private remainHeight: number = 95;
  @state() private resizeObserver: ResizeObserver | null = null;
  @state() private _dialogOpen: boolean = false;

  static styles = styles;

  connectedCallback() {
    super.connectedCallback();
    this.updateFromHass();

    // Create a bound instance method to avoid creating new functions on each resize
    this._handleResize = this._handleResize.bind(this);

    // Start observing the parent element for size changes
    this.resizeObserver = new ResizeObserver(this._handleResize);
    const rootNode = this.getRootNode() as ShadowRoot;
    const parent = this.parentElement || (rootNode instanceof ShadowRoot ? rootNode.host : null);
    if (parent) {
      this.resizeObserver.observe(parent);
    }
  }

  private _handleResize() {
    // Only update if the component is still connected to the DOM
    if (this.isConnected) {
      this.calculateHeights();
      this.updateLayers();
    }
  }

  disconnectedCallback() {
    super.disconnectedCallback();
    if (this.resizeObserver) {
      this.resizeObserver.disconnect();
      this.resizeObserver = null;
    }
  }

  firstUpdated() {
    this.updateLayers();
  }

  private _handleClick() {
    this._dialogOpen = true;
  }

  private _closeDialog() {
    this._dialogOpen = false;
  }

  render() {
    console.log("spool component render");
    return html`
      ${this.modal()}
      <div class="ha-bambulab-spool-card-container">
        <div
          class="ha-bambulab-spool-card-holder"
          style="border-color: ${this.active
            ? this.hass.states[this.entity_id]?.attributes.color
            : "#808080"}"
          @click=${this._handleClick}
        >
          <div class="ha-bambulab-spool-container">
            <div class="ha-bambulab-spool-side"></div>
            <div
              class="string-roll-container"
              style="${this.active ? "animation: wiggle 3s linear infinite" : nothing}"
            >
              <div
                class="v-string-roll"
                id="v-string-roll"
                style="background: ${this.hass.states[this.entity_id]?.attributes
                  .color}; height: ${this.remainHeight.toFixed(2)}%"
              >
                ${this.active ? html`<div class="v-reflection"></div>` : nothing}
                ${this.hass.states[this.entity_id]?.attributes?.remain > 0
                  ? html`
                      <div class="remaining-percent">
                        <p>${this.hass.states[this.entity_id]?.attributes?.remain}%</p>
                      </div>
                    `
                  : nothing}
              </div>
            </div>
            <div class="ha-bambulab-spool-side"></div>
          </div>
        </div>
        ${this.show_type
          ? html` <div class="ha-bambulab-spool-info-container">
              <div class="ha-bambulab-spool-info-wrapper">
                <div class="ha-bambulab-spool-info">
                  ${this.hass.states[this.entity_id]?.attributes.name}
                </div>
              </div>
            </div>`
          : nothing}
      </div>
    `;
  }

  modal() {
    if (!this._dialogOpen) return nothing;

    console.log("spool component modal rendered");
    return html`
      <ha-dialog
        id="confirmation-popup"
        .open=${this._dialogOpen}
        @closed=${this._closeDialog}
        heading="title"
      >
        <ha-dialog-header slot="heading">
          <ha-icon-button slot="navigationIcon" dialogAction="cancel"
            ><ha-icon icon="mdi:close"></ha-icon
          ></ha-icon-button>
          <div slot="title">${this.hass.states[this.entity_id].attributes.friendly_name}</div>
        </ha-dialog-header>
        <div class="ha-bambulab-spool-modal-container">
          <div class="filament-title section-title">Filament Information</div>
          <div class="div2 item-title">Filament</div>
          <div class="div3 item-value">${this.hass.states[this.entity_id].attributes.name}</div>
          <div class="div4 item-value">
            <span
              style="background-color: ${this.hass.states[this.entity_id].attributes
                .color}; color: ${getContrastingTextColor(
                this.hass.states[this.entity_id].attributes.color
              )}; padding: 5px 10px; border-radius: 5px;"
              >${this.hass.states[this.entity_id].attributes.color}</span
            >
          </div>
          <div class="div5 item-title">Color</div>
          <div class="div6 section-title">Nozzle Temperature</div>
          <div class="div7 item-title">Minimum</div>
          <div class="div8 item-value">
            ${this.hass.states[this.entity_id].attributes.nozzle_temp_min}
          </div>
          <div class="div9 item-value ">
            ${this.hass.states[this.entity_id].attributes.nozzle_temp_max}
          </div>
          <div class="div10 item-title">Maximum</div>
          <div class="action-buttons">
            <mwc-button class="action-button" @click=${this._closeDialog}>Load</mwc-button>
            <mwc-button class="action-button" @click=${this._closeDialog}>Unload</mwc-button>
          </div>
        </div>
      </ha-dialog>
    `;
  }

  updateLayers() {
    // Query the #string-roll element inside this component's shadow DOM
    const stringRoll = (this.renderRoot as ShadowRoot).getElementById("v-string-roll");
    if (!stringRoll) return;

    const stringWidth = 2; // matches .string-layer width in CSS
    const rollWidth = stringRoll.offsetWidth; // container width

    // Calculate how many lines fit
    const numLayers = Math.floor(rollWidth / (stringWidth * 2)); // 2 = line width + gap

    // Clear previous layers
    const previousLayers = this.renderRoot.querySelectorAll(".v-string-layer");
    previousLayers.forEach((layer) => layer.remove());

    // Add new layers
    for (let i = 0; i < numLayers; i++) {
      const layer = document.createElement("div");
      layer.classList.add("v-string-layer");

      // Calculate left position = (index + 1) * (width*2) - width
      const leftPosition = (i + 1) * (stringWidth * 2) - stringWidth;
      layer.style.left = `${leftPosition}px`;

      stringRoll.appendChild(layer);
    }
  }

  isAllZeros(str) {
    return /^0+$/.test(str);
  }

  calculateHeights() {
    // Skip calculation if modal is open to prevent unwanted updates
    if (this._dialogOpen) return;

    const maxHeightPercentage = 95;
    const minHeightPercentage = 12;

    // If not a Bambu Spool or remaining is less than 0
    if (
      this.isAllZeros(this.hass.states[this.entity_id]?.attributes.tag_uid) ||
      this.hass.states[this.entity_id]?.attributes?.remain < 0
    ) {
      this.remainHeight = maxHeightPercentage;
    } else {
      // Get the container's height
      const container = this.renderRoot.querySelector(
        ".string-roll-container"
      ) as HTMLElement | null;
      const containerHeight = container?.offsetHeight || 0;

      // Calculate heights in pixels
      const maxHeightPx = containerHeight * (maxHeightPercentage / 100);
      const minHeightPx = containerHeight * (minHeightPercentage / 100);

      // Calculate remain height based on the remain percentage
      const remainPercentage = Math.min(
        Math.max(this.hass.states[this.entity_id]?.attributes?.remain, 0),
        100
      );
      this.remainHeight = minHeightPx + (maxHeightPx - minHeightPx) * (remainPercentage / 100);

      // Convert back to percentage of container
      this.remainHeight = (this.remainHeight / containerHeight) * 100;
    }

    // Ensure remainHeight is always a number and doesn't exceed maxHeightPercentage
    this.remainHeight = Math.min(
      Number(this.remainHeight) || maxHeightPercentage,
      maxHeightPercentage
    );
    this.requestUpdate();
  }

  // Add willUpdate lifecycle method to handle hass changes
  willUpdate(changedProperties) {
    if (changedProperties.has("hass")) {
      // Skip the update if dialog is open to prevent unwanted re-renders
      if (!this._dialogOpen) {
        this.updateFromHass();
      }
    }
  }

  // New method to handle state updates
  private updateFromHass() {
    if (!this.hass || !this.entity_id) return;
    console.log("updateFromHass");

    const newActive =
      this.hass.states[this.entity_id]?.attributes.active ||
      this.hass.states[this.entity_id]?.attributes.in_use
        ? true
        : false;

    // Only update if the active state has changed
    if (this.active !== newActive) {
      this.active = newActive;
      // Only recalculate heights if dialog is not open
      if (!this._dialogOpen) {
        this.calculateHeights();
      }
    }
  }
}

Making a chatbot for whatsapp [closed]

My UNI has a internship that helps Software Engineering students develop their skills, I’m on the third semester and I just joined it.

My supervisors just dropped a project on my team (all starters) and I need help.

We need to develop a WhatsApp chatbot that will answer FAQs. It will be a menu navigable by typing numbers. The technologies that will be used are NodeJs, Typescript, Postgres, Prisma and React.

I am currently studying Node, I know javascript to a certain level (that seems good for me, but I know compared to most of you it is not that high) and Typescript. I’m the main backend developer, I need help. Any tips will help.

Sorry if the post isn’t specific enough, I’ll answer any questions to the best of my capabilities

Typscript infer any value from object [duplicate]

I’m attempting to create a new type that allows any string value specified in an object. I aplogize for the verbiage with this as I’m not quite sure how to word it.

I have this object:

const ANNOTATION_KEYS = { 
  SERVICE: 'website.com/service-name',
  GITHUB: 'website.com/service-repo-url',
  JIRA: 'website.com/service-jira-url'
} as const;

I am wanting to automatically generate a type that implies the following:

type AnnotationKey = "website.com/service-name" | "website.com/service-repo-url" | "website.com/service-jira-url";

As you can see, I’m trying to allow any values defined in ANNOTATION_KEYS to be allowed for the AnnotationKey type. The goal of this is to reduce duplicating these values to make things a little more maintainable.

I have been looking through TypeScript Utility Types, but I’m not seeing anything that looks like it would work.

Is there a way to easily go about this? Or would I just be better off duplicating the annotation key values (in thise scenario)?

running a script on google sheet on mobile not working

i have a script that record scores and export it to another sheet. however as i understand, google mobile sheet does not allow the pressing of button, and thus i created a checkbox to run it. When i try this, the script doesnt run. is it because the script calls other google sheet into play?

Here’s my script for reference.

function recordScores() {
  var ss1 = SpreadsheetApp.openByUrl("Unique URL 1").getSheetByName("PC 1-3");
  var ss2 = SpreadsheetApp.openByUrl("Unique URL 2");
  var ss3 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");

  var aspireLevel = Number(ss3.getRange("B6").getDisplayValue());
  var element = Number(ss3.getRange("B7").getDisplayValue());
  var judgeNumber = Number(ss3.getRange("B8").getDisplayValue());
  var athleteNumber = ss3.getRange("B11").getDisplayValue();
  var b10Value = ss3.getRange("B10").getDisplayValue();

  var r1 = ss3.getRange("B18").getDisplayValue();
  var r2 = ss3.getRange("B19").getDisplayValue();
  var r3 = ss3.getRange("B20").getDisplayValue();
  var AComments = ss3.getRange("A26").getDisplayValue();
  var scores = ss3.getRange("E18").getDisplayValue();

  if (b10Value.trim() === "") {
    SpreadsheetApp.getUi().alert("Error: No judge assigned for this level");
    return;
  }

  var sheet = (aspireLevel >= 1 && aspireLevel <= 3) ? ss1 : ss2;
  var rowToInsert = findRow(sheet, athleteNumber, aspireLevel);
  if (rowToInsert === -1) {
    SpreadsheetApp.getUi().alert("Error: Athlete number and Aspire Level not found");
    return;
  }

  var columnMap = getColumnMapping(aspireLevel, element, judgeNumber);
  if (!columnMap) {
    SpreadsheetApp.getUi().alert("Error: Invalid Element or Judge Number");
    return;
  }

  var columnMap2 = getColumnMapping2(aspireLevel, element, judgeNumber);
  if (!columnMap2) {
    SpreadsheetApp.getUi().alert("Error: Invalid Element or Judge Number");
    return;
  }

  if (aspireLevel <= 3) {
    sheet.getRange(rowToInsert, columnMap[0]).setValue(r1);
    sheet.getRange(rowToInsert, columnMap[1]).setValue(r2);
    sheet.getRange(rowToInsert, columnMap[2]).setValue(r3);
    sheet.getRange(rowToInsert, columnMap[3]).setValue(AComments);
  } else {
    sheet.getRange(rowToInsert, columnMap2[0]).setValue(scores);
    sheet.getRange(rowToInsert, columnMap2[1]).setValue(AComments);
  }

  SpreadsheetApp.getUi().alert("Scores have been recorded");
  resetValues(ss3);
}

function findRow(sheet, athleteNumber, aspireLevel) {
  var data = sheet.getDataRange().getValues();
  for (var i = 1; i < data.length; i++) {
    if (data[i][1] == athleteNumber && data[i][4] == aspireLevel) {
      return i + 1;
    }
  }
  return -1;
}

function getColumnMapping(aspireLevel, element, judgeNumber) {
  var columnMappings = {
    1: { 1: [20, 23, 26, 29], 2: [21, 24, 27, 30], 3: [22, 25, 28, 31] },
    2: { 1: [32, 35, 38, 41], 2: [33, 36, 39, 42], 3: [34, 37, 40, 43] },
    3: { 1: [44, 47, 50, 53], 2: [45, 48, 51, 54], 3: [46, 49, 52, 55] },
    4: { 1: [56, 59, 62, 65], 2: [57, 60, 63, 66], 3: [58, 61, 64, 67] },
    5: { 1: [68, 71, 74, 77], 2: [69, 72, 75, 78], 3: [70, 73, 76, 79] },
    6: { 1: [80, 83, 86, 89], 2: [81, 84, 87, 90], 3: [82, 85, 88, 91] },
    7: { 1: [92, 95, 98, 101], 2: [93, 96, 99, 102], 3: [94, 97, 100, 103] },
    8: { 1: [104, 107, 110, 113], 2: [105, 108, 111, 114], 3: [106, 109, 112, 115] }
  };
  return columnMappings[element] ? columnMappings[element][judgeNumber] : null;
}
function getColumnMapping2(aspireLevel, element, judgeNumber) {
  var columnMappings = {
    1: { 1: [20, 23], 2: [21, 24], 3: [22, 25] },
    2: { 1: [26, 29], 2: [27, 30], 3: [28, 31] },
    3: { 1: [32, 35], 2: [33, 36], 3: [34, 37] },
    4: { 1: [38, 41], 2: [39, 42], 3: [40, 43] },
    5: { 1: [44, 47], 2: [45, 48], 3: [46, 49] },
    6: { 1: [50, 53], 2: [51, 54], 3: [52, 55] },
    7: { 1: [56, 59], 2: [57, 60], 3: [58, 61] },
    8: { 1: [62, 65], 2: [63, 66], 3: [64, 67] },
    9: { 1: [68, 71], 2: [69, 72], 3: [70, 71] },
    10: { 1: [74, 77], 2: [75, 78], 3: [76, 79] },
    11: { 1: [80, 83], 2: [81, 84], 3: [82, 85] },
    12: { 1: [86, 89], 2: [87, 90], 3: [88, 91] },
  };
  return columnMappings[element] ? columnMappings[element][judgeNumber] : null;
}

function resetValues(ss3) {
  ss3.getRange("B18").setFormula("=IF(C18=TRUE,"C",IF(D18=TRUE,"NYC",""))");
  ss3.getRange("B19").setFormula("=IF(C19=TRUE,"C",IF(D19=TRUE,"NYC",""))");
  ss3.getRange("B20").setFormula("=IF(C20=TRUE,"C",IF(D20=TRUE,"NYC",""))");
  var cellsToReset = ["C18", "C19", "C20", "D18", "D19", "D20"];
  cellsToReset.forEach(cell => ss3.getRange(cell).setValue('FALSE'));
  ss3.getRange("A26").setValue('');
  ss3.getRange("E18").setValue('');
}


function onEdit(e) {
  var sheet = e.source.getSheetByName("Sheet1"); // Change to your actual sheet name
  var range = e.range;

  if (!sheet || range.getA1Notation() !== "B26") return;

  var checked = range.getValue();
  if (checked === true) {
    recordScores();
    // Optional: Uncheck after running the script
    SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1").getRange("B26").setValue(false);
  }
}

function onOpen() {
  var ui = SpreadsheetApp.getUi();
  ui.createMenu("Scoring") 
    .addItem("Record Scores", "recordScores") 
    .addToUi();
}

Any help is much appreciated

React Native 0.76.3 – NoSuchFieldError: “mBinding” in FabricUIManager

I’m facing an issue while running my React Native 0.76.3 project on Android. The app crashes with the following error:

Exception com.facebook.react.common.JavascriptException: Error: Exception in HostFunction:

java.lang.NoSuchFieldError: no type "Lcom/facebook/react/fabric/Binding;" found and so no field "mBinding"

could be found in class "Lcom/facebook/react/fabric/FabricUIManager;" or its superclasses, stack:

requireModule@1:76569 get@1:76633 anonymous@1:1248449

...

Steps I’ve Tried:

1. Clearing Metro and Gradle Cache:

npx react-native start --reset-cache
cd android && ./gradlew clean
cd ..
npx react-native run-android

2. Checking Dependencies:

npx react-native doctor

3. Updating Dependencies:

npm outdated

npm update

Questions:

  1. Is this a known issue with React Native 0.76.3?

  2. Could this be caused by an incompatible dependency?

  3. Any suggestions on resolving this error?