Dynamic Routing in NextJS and Typescript

After creating a dynamic route [id] it contains 2 pages, a server side component where the error is and a client side PolicyVerificationClient.tsx which takes the params Certificatenumber and id.

import {
    doc,
    getDoc
} from 'firebase/firestore';
import {
    db
} from '@/lib/firebase';
import PolicyVerificationClient from './PolicyVerificationClient';
type Props = {
    params: Promise < {
        id: string
    } > ;
    searchParams: {
        [key: string]: string
    };
}
export default async function PolicyVerificationPage({
    params
}: Props) {
    const resolvedParams = await params;
    // Fetch initial data on the server
    let certificateNumber = '';
    try {
        const docRef = doc(db, 'records', resolvedParams.id);
        const docSnap = await getDoc(docRef);
        if (docSnap.exists()) {
            const data = docSnap.data();
            certificateNumber = data.certificateNumber || '';
        }
    } catch (error) {
        console.error('Error fetching record:', error);
    }
    return <PolicyVerificationClient id={resolvedParams.id} initialCertificateNumber={certificateNumber} />;
}

I keep getting a type error: Type ‘Props’ does not satisfy the constraint ‘PageProps’.
Types of property ‘searchParams’ are incompatible.
Type ‘{ [key: string]: string | string[] | undefined; }’ is missing the following properties from type ‘Promise’: then, catch, finally, [Symbol.toStringTag]

I tried this but it did not fix the problem searchParams: Promise<{ [key: string]: string | string[] | undefined }>; // Updated to be a Promise

Place Header with Checkbox & bulleted List After Specific Text in Google Doc Using Script

I attempting to have a button (linked image or text) users can use to insert a templated paragraph of text into a specific section of a google doc. The templated text is a Header which has a checklist checkbox (when used, strikes out line), and includes an expandable bulleted list.
Example of the text script should input

I’ve mostly been able to build out how I want the template text to look, but haven’t found a solution online for placing it after specific text while remaining formatted, and also making the header a checklist.
I’ve found ways to place unformatted text, and ways to create checkbox lists, but nothing that includes the combinations I’m looking for.
I’ve found ways to place the cursor in a specific location and ways to “findtext”, but none that I was able to make work along with the specific formatted text I’m using.

Here’s an example google doc that includes the script I have so far.

Here’s the script so far:

function insertText() {
  var headerFormatOn = {"BOLD": true, "BACKGROUND_COLOR": "#000000", "FOREGROUND_COLOR": "#FFFFFF"};
  var formatOn = {"BOLD": true, "BACKGROUND_COLOR": "#000000", "FOREGROUND_COLOR": "#FFFFFF"};
  var formatOff = {"BOLD": false, "BACKGROUND_COLOR": "#000000", "FOREGROUND_COLOR": "#FFFFFF"};
  var header = "Enter_Item_Title_Here"
  var bulletBody = ": ___________"
  var bulletOneHeader = "Agenda Item Owner(s): ___________"
  var bulletTwoHeader = "Discussant: ___________"
  var bulletThreeHeader = "Discussion Date: ___________"
  var bulletFourHeader = "External follow up: ___________"
  var bulletFiveHeader = "Notes: ___________"
  var bulletSixHeader = "Action Items: ___________"

  var attributes1 = Object.entries(headerFormatOn).reduce((o, [k, v]) => Object.assign(o, {[k]: v}), {});
  ///var attributes2 = Object.entries(formatOn).reduce((o, [k, v]) => Object.assign(o, {[k]: v}), {});
  ///var attributes3 = Object.entries(formatOff).reduce((o, [k, v]) => Object.assign(o, {[k]: v}), {});

  var cursor = DocumentApp.getActiveDocument().getActiveTab().asDocumentTab().getBody();
  var text1 = cursor.appendParagraph(header);
  text1.setHeading(DocumentApp.ParagraphHeading.HEADING3);
  ///var text2 = cursor.insertText(bulletBody);
  ///text2.setAttributes(attributes3);
  var text3 = cursor.appendListItem(bulletOneHeader);
    //text3.setAttributes(attributes2);
    text3.setGlyphType(DocumentApp.GlyphType.BULLET).setGlyphType(DocumentApp.GlyphType.BULLET);

  var text4 = cursor.appendListItem(bulletTwoHeader);
    //text4.setAttributes(attributes2);
    text4.setGlyphType(DocumentApp.GlyphType.BULLET);
  var text5 = cursor.appendListItem(bulletThreeHeader);
  text5.setGlyphType(DocumentApp.GlyphType.BULLET);
  var text6 = cursor.appendListItem(bulletFourHeader);
  text6.setGlyphType(DocumentApp.GlyphType.BULLET);
  var text7 = cursor.appendListItem(bulletFiveHeader);
  text7.setGlyphType(DocumentApp.GlyphType.BULLET);
  var text8 = cursor.appendListItem(bulletSixHeader);
  text8.setGlyphType(DocumentApp.GlyphType.BULLET);
  var text9 = cursor.appendListItem(bulletSixHeader);
  text9.setGlyphType(DocumentApp.GlyphType.BULLET);
}

I have a feeling some of it might have to do with “append” vs “insert”, or some of the variations I’ve read about in my searching, but I haven’t been able to figure out which pieces need to change in order for it to work.

Plotly refresh prevents me from doing certain actions

I’m currently developing an Angular app which displays customizable real-time charts. Basically we’re fetching data from a database that get its data from a MQTT server.

To customize the chart, I added a little button which opens a PrimeNG modal, where you can change trace color (handled with a HTML color input) and the time range over which data is displayed.

The issue is that, since it’s real time data, it get updated every seconds or so (this will be configurable as well), which has for now two disadvantages :

  • the little toolbar at the top returns to default (if one select the drag tool, it returns to the zoom tool)
  • the color picker on the modal is removed if it was clicked

This is the update function, which is called everytime the component receive a new data from the parent component :

private _dataGraph!: GraphLastDataTopicsDetailsModel[] | null;
@Input()
get iDataGraph(): GraphLastDataTopicsDetailsModel[] | null {
  return this._dataGraph;
}
set iDataGraph(data: GraphLastDataTopicsDetailsModel[] | null) {
  if (data) {
    this._dataGraph = data;
    this.fillData(data);
  }
}
fillData(dataGraphReceived: GraphLastDataTopicsDetailsModel[]) {
  // Get data with correct title
  let dataReceived = dataGraphReceived.find(el => el.name === this._layout.title.text);

  if (dataReceived) {

    let newTraces: PlotlyDataModel[] = [];
    let oldData: PlotlyDataModel[] = this._data;

    for (let currentTopic of dataReceived.dataTopics) {
      const dataTopicGraph = oldData.find(el => el.name === currentTopic.topic);
      if (dataTopicGraph) {

        let newX = [...dataTopicGraph.x, currentTopic.date];
        let newY = [...dataTopicGraph.y, currentTopic.dataDouble];

        // To do : create a different way of cleaning the array after x seconds
        if (newX.length > this._maxValues) {
          newX.shift();
        }
        if (newY.length > this._maxValues) {
          newY.shift();
        }
        // Push the current values in the trace array
        newTraces.push(
          {
            ...dataTopicGraph,
            x: newX,
            y: newY,
          }
        );
      }
      else {
        // On init (no data yet), we push the values as well as the trace's options
        this.rgbStringToHex(this.colors[newTraces.length])
        newTraces.push(
          {
            x: [currentTopic.date],
            y: [currentTopic.dataDouble],
            type: 'scatter',
            name: currentTopic.topic,
            marker: { color: this.colors[newTraces.length] },
            line: { shape: 'spline' },
            mode: 'lines+markers',
            connectgaps: true,
            showlegend: true,
          }
        );
      }
    }

This is the HTML part :

<plotly-plot class="w-full" [data]="_data" [layout]="_layout" [config]="{ scrollZoom: true, displaylogo: false }" />

This is how the data is updated :

serviceSubscribe(): void {
  // Get all words for screen
  this._subscription.add(
    this._getLastDataTopic$.pipe(
      concatMap(variables => {
        return this.getLastDataTopicService.getLastDataTopic(variables);
      }),
      tap(data => {
        if (data) {
          this._data = data;
          // this._intervalTest = this._intervalTest + 1000;
          this.realTimeService.setIntervalDuration(this._intervalTest);
        } else {
          console.warn('No data received');
          // TODO: Ajouter un message toast ici pour notifier l'utilisateur
        }
      })
    ).subscribe()
  );

  this._subscription.add(
    this.realTimeService.getRealTimeData().subscribe((data) => {
      this.realTimeData = data;
      this._getLastDataTopic$.next([
        { name: "Chart n°1", topicsName: ['adress/equipment1/Process1/temp'] },
        { name: "Chart n°2", topicsName: ['adress/equipment1/Process1/pressure1', 'adress/equipment1/Process1/pressure2'] }
      ]);
    }));
}

We had to fetch data this way because we’ve had some issues with components completely refreshing each time otherwise. I’d like to know if there’s any solution to prevent this behavior (even if it only fixes the issue with the color picker it’d be amazing).

How to Avoid “Cannot Read Property ‘map’ of Undefined” in React When State is Initially Undefined?

I’m building a React application where I want to map over an array of items and render them as a list. The issue I’m facing is when the state is initially undefined, the map() function throws the following error:


Uncaught TypeError: Cannot read property 'map' of undefined

Here is the simplified version of my code:

import React, { useState, useEffect } from 'react';

function ItemList() {
  const [items, setItems] = useState(); // State initially undefined

  useEffect(() => {
    // Simulating an API call
    setTimeout(() => {
      setItems(['Item 1', 'Item 2', 'Item 3']);
    }, 2000);
  }, []);

  return (
    <div>
      <h1>Item List</h1>
      <ul>
        {items.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
    </div>
  );
}

export default ItemList;

What I Tried

  • I know the error occurs because the state items is initially undefined, and map() cannot be called on undefined.
  • I tried adding a conditional check (if (items)), but it still causes issues when items is undefined for a short period after the initial render.
  • I also tried initializing the state with an empty array (useState([])), but I would like to understand the best practice for handling such cases where the state might be undefined for a brief moment.

Question

What is the best way to avoid this error while ensuring that the component works smoothly even when the data is being fetched asynchronously? Should I use a default value for the state or apply another method to handle this case efficiently?

(complex) javascript array function to groep items based on time

I have the following array example

userShifts = [
    { userId: 1, startTime: "05:00", endTime: "13:00" },
    { userId: 2, startTime: "05:00", endTime: "13:00" },
    { userId: 3, startTime: "05:00", endTime: "13:00" },
    { userId: 4, startTime: "13:00", endTime: "21:00" },
    { userId: 5, startTime: "13:00", endTime: "21:00" },
    { userId: 6, startTime: "13:00", endTime: "21:00" },
    { userId: 7, startTime: "21:00", endTime: "00:00" },
    { userId: 8, startTime: "21:00", endTime: "00:00" },
    { userId: 9, startTime: "21:00", endTime: "00:00" },
    { userId: 10, startTime: "07:00", endTime: "15:00" },
    { userId: 11, startTime: "07:00", endTime: "15:00" },
    { userId: 12, startTime: "03:00", endTime: "11:00" },
  ];

I need a function that will create a shift blokcs of 24 hours so from 00:to
00:00 and the next user start time cannot be before the end time fo the previous one so the output of the above array would be

userShiftBlocks = [
  [
    { userId: 1, startTime: "05:00", endTime: "13:00" },
    { userId: 4, startTime: "13:00", endTime: "21:00" },
    { userId: 7, startTime: "21:00", endTime: "00:00" }
  ],
  [
    { userId: 2, startTime: "05:00", endTime: "13:00" },
    { userId: 5, startTime: "13:00", endTime: "21:00" },
    { userId: 8, startTime: "21:00", endTime: "00:00" }
  ],
  [
    { userId: 3, startTime: "05:00", endTime: "13:00" },
    { userId: 6, startTime: "13:00", endTime: "21:00" },
    { userId: 9, startTime: "21:00", endTime: "00:00" }
  ],
  [
    { userId: 10, startTime: "07:00", endTime: "15:00" }
  ],
  [
    { userId: 11, startTime: "07:00", endTime: "15:00" }
  ],
  [
    { userId: 12, startTime: "03:00", endTime: "11:00" }
  ]
];

So for each block a 24 hour shift period , the start time of the next user is after or the same as the previous user end time. So an array like below where not everythign matched start end exactly should also work

userShifts = [
    { userId: 1, startTime: "05:00", endTime: "13:00" },
    { userId: 2, startTime: "05:00", endTime: "13:30" },
    { userId: 3, startTime: "05:00", endTime: "13:00" },
    { userId: 4, startTime: "13:00", endTime: "21:00" },
    { userId: 5, startTime: "14:30", endTime: "21:00" },
    { userId: 6, startTime: "13:00", endTime: "21:00" },
    { userId: 7, startTime: "21:30", endTime: "00:00" },
    { userId: 8, startTime: "21:00", endTime: "00:00" },
    { userId: 9, startTime: "21:00", endTime: "00:00" },
    { userId: 10, startTime: "07:00", endTime: "15:00" },
    { userId: 11, startTime: "07:00", endTime: "15:00" },
    { userId: 12, startTime: "03:00", endTime: "11:00" },
  ];

I am not gettig the results I want yet and chatgpt completely looses the plotso help is appreciated

Calculator. It is not possible to convert letters to a point

I can’t understand why my return doesn’t return a dot from cases with the letters ‘b’ ‘y’ and a comma’,’ I put console.log, looked, shows a dot but at some level it’s still a comma and my functions don’t work with it. I need these symbols to turn into a dot both on the program code and visually. If anyone can solve the problem, please write why this is happening, I want to figure it out. Thanks!

    const displayEl = document.querySelector('.display');
const buttonEls = [...document.querySelectorAll('.button')];
const cleanBtnEl = document.querySelector('.clean-btn');
const operators = ['+', '-', '*', '/'];

const focusInput = () => displayEl.focus();
focusInput();

window.addEventListener('click', () => focusInput());

displayEl.addEventListener('keydown', (e) => {

    if (e.key === ' ') e.preventDefault();
    if (displayEl.value.includes('e') || displayEl.value === 'Error') displayEl.value = '';

    const key = e.key.toLowerCase();
    const valueStr = handlerKey(key);
    console.log(valueStr);
    // if (valueStr === '.') {
    //     displayEl.value += valueStr;
    // }

    if (!displayEl.value) {
        if (!blockFirstPoint(valueStr)) e.preventDefault();
    }

    if (!checkPointBeforeAndAfterOperator(valueStr)) e.preventDefault();

    if (valueStr === '.') {
        if (!limitPoints(valueStr)) e.preventDefault();
    }

    if (!displayEl.value) {
        if (!blockFirstOperator(valueStr)) e.preventDefault();
    }

    if (operators.includes(valueStr)) {
        if (!blockSpamOperators(valueStr)) e.preventDefault();
    }

    if (!replaceAndLimitZero(valueStr)) e.preventDefault();

    if (valueStr === 'delete') clear();

    if (valueStr === 'enter' || valueStr === '=') calc();
});

const handlerKey = (key) => {
    switch (key) {
        case 'f5':
        case 'f12':
            return key;
        case 'enter':
            return '=';
        case ',':
        case 'б':
        case 'ю':
            return '.';
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
        case '+':
        case '-':
        case '*':
        case '/':
        case '.':
        case 'backspace':
        case 'delete':
        case 'tab':
        case 'arrowleft':
        case 'arrowright':
        case 'delete':
            return key;
        default:
            return null;
    }
};

buttonEls.forEach(button => {
    button.addEventListener('click', () => {

        if (displayEl.value.includes('e') || displayEl.value === 'Error') displayEl.value = '';

        const valueStr = button.innerText;

        if (!displayEl.value) {
            if (!blockFirstPoint(valueStr)) return;
        }

        if (!displayEl.value) {
            if (!blockFirstOperator(valueStr)) return;
        }

        if (!replaceAndLimitZero(valueStr)) return;

        if (valueStr === '=') {
            if (!calc()) return;
        }

        if (operators.includes(valueStr)) {
            if (!blockSpamOperators(valueStr)) return;
        }

        if (!checkPointBeforeAndAfterOperator(valueStr)) return;

        if (button.innerText === '.') {
            if (!limitPoints(valueStr)) return;
        }

        displayEl.value += valueStr;
    });
});

const blockFirstPoint = (valueStr) => {
    if (valueStr === '.') return false;
    return true;
};

const blockFirstOperator = (valueStr) => {
    if (operators.includes(valueStr)) return false;
    return true;
};

const replaceAndLimitZero = (valueStr) => {
    const currentValue = displayEl.value;
    const arrayNumbers = ['1', '2', '3', '4', '5', '6', '7', '8', '9'];

    if (currentValue === '0' && valueStr === '0') {
        return false;
    } else if (currentValue === '0' && arrayNumbers.includes(valueStr)) {
        displayEl.value = '';
        return true;
    }

    const lastOperatorIndex = Math.max(...operators.map(op => currentValue.lastIndexOf(op)));

    const afterLastOperator = currentValue.slice(lastOperatorIndex + 1);

    if (lastOperatorIndex !== -1) {
        if (afterLastOperator === '0' && valueStr === '0') {
            return false;
        } else if (afterLastOperator === '0' && arrayNumbers.includes(valueStr)) {
            displayEl.value = currentValue.slice(0, -1);
        }
    }
    return true;
};

const blockSpamOperators = (valueStr) => {
    const currentValue = displayEl.value;
    const lastChair = currentValue[currentValue.length - 1];

    if (operators.includes(lastChair)) displayEl.value = currentValue.slice(0, -1) + valueStr;

    if (operators.some(op => currentValue.includes(op))) return false;
    return true;
};

const checkPointBeforeAndAfterOperator = (valueStr) => {
    const currentValue = displayEl.value;
    let lastChar = displayEl.value[currentValue.length - 1];

    if (operators.includes(lastChar)) {
        if (valueStr === '.') return false;
        return true;
    }
    if (lastChar === '.' && operators.includes(valueStr)) return false;
    return true;
};

const limitPoints = (point) => {
    const lastOperator = operators.find(op => displayEl.value.includes(op));
    const currentOperator = lastOperator ? displayEl.value.split(lastOperator).pop() : displayEl.value;

    if (currentOperator.includes(point)) return false;
    return true;
};

const calc = () => {

    const currentValue = displayEl.value;
    const lastChar = displayEl.value[currentValue.length - 1];

    if (lastChar === '.') return false;

    const operator = operators.find(op => currentValue.includes(op));
    const [firstValueStr, secondValueStr] = currentValue.split(operator);

    const firstNumber = parseFloat(firstValueStr);
    const secondNumber = parseFloat(secondValueStr);

    let result;

    if (isNaN(secondNumber)) return;

    switch (operator) {
        case '+':
            result = firstNumber + secondNumber;
            break;
        case '-':
            result = firstNumber - secondNumber;
            if (0 > result) {
                displayEl.value = 'Error';
                return;
            }
            break;
        case '*':
            result = firstNumber * secondNumber;
            break;
        case '/':
            if (secondNumber === 0) {
                displayEl.value = 'Error';
                return;
            }
            result = firstNumber / secondNumber;
            break;
        default:
            displayEl.value = 'Error';
    }

    const res = result.toString();
    if (res.includes('e')) {
        displayEl.value = result;
        return;
    }
    displayEl.value = Math.round(result * 100_000_000) / 100_000_000;
};

cleanBtnEl.addEventListener('pointerdown', () => {
    deleteLastCharacter();
    let clearIntervalId;

    const clearTimeoutId = setTimeout(() => {
        clearIntervalId = setInterval(() => {
            deleteLastCharacter();
        }, 50);
    }, 500);

    const clearTime = () => {
        clearTimeout(clearTimeoutId);
        clearInterval(clearIntervalId);
    };

    cleanBtnEl.addEventListener('pointerup', clearTime);
    window.addEventListener('pointerup', clearTime);
});

const deleteLastCharacter = () => {
    const currentValue = displayEl.value;
    const cursorPosition = displayEl.selectionStart;

    if (cursorPosition === 0) {
        return;
    } else {
        displayEl.value = currentValue.slice(0, cursorPosition - 1) + currentValue.slice(cursorPosition);
    }
    displayEl.setSelectionRange(cursorPosition - 1, cursorPosition - 1);
};

const clear = () => displayEl.value = '';

Not successfully rendering state change after navigate() with react-router-dom

I have a problem with rendering a component after a performing a navigate with state to another page. Everything works as expected when I am already on the page that I am navigating to, but I would like for it to work when I navigate from another page as well.

I have a “globally” defined keyboard listener that should navigate me to the desired page upon a sequence of keys (I’m using a barcode scanner).

const barcodeString = useRef("")
const recordingBarcode = useRef(false)

const scannerCallback = useCallback(
    (e) => {
        if (e.key === "$") {
            recordingBarcode.current = true;
        } else if (e.key === "Enter" && recordingBarcode.current) {
            recordingBarcode.current = false;
            navigate("/box/build", {state:{barcode:barcodeString.current}});
            barcodeString.current = ""
        } else if (recordingBarcode.current) {
            barcodeString.current = barcodeString.current + e.key;
        }
    },
    [],
);

useEffect(() => {
    document.addEventListener('keydown', scannerCallback);

    return () => {
        document.removeEventListener('keydown', scannerCallback);
    };
}, [scannerCallback]);

If I press the sequence “$4Enter” it successfully navigates to the "/box/build" page

                <Route path='/box/build'
                   element={<BuildBoxPage buildBoxRequest={buildBoxRequest}
                                          getRecipesRequest={getRecipesRequest}
                   />}/>

In the BuildBoxPage I do the following

const BuildBoxPage = ({buildBoxRequest, getRecipesRequest}) => {
const {state} = useLocation();
const [box, setBox] = useState();
const [boxes, setBoxes] = useState([]);
const [boxNumber, setBoxNumber] = useState();
const [recipes, setRecipes] = useState([]);
const [ingredients, setIngredients] = useState([]);
const [supplements, setSupplements] = useState([]);

useEffect(() => {
    if (state != undefined) {
        updateBox(state.barcode)
    }
}, [state]);

const updateBox = (barcode) => {
    setBoxNumber(barcode);
    // == to compare a string value with an int on purpose
    setBox(boxes.find(box => box.box_number == barcode));
}
... 
}

This updates the select with the state value (boxNumber) that I am using for choosing a box, but doesn’t render the the BuildBoxForm component in response to choosing a select value.

    return (<section className="bg-indigo-50 min-h-[calc(100vh-81px)]">
    <div className="container m-auto max-w-2xl py-10">
        <div
            className="bg-white px-6 py-8 mb-4 shadow-md rounded-md border m-4 md:m-0"
        >
            <form autoComplete="off" onSubmit={submitForm}>
                <h2 className="text-3xl text-center font-semibold mb-6">Build box</h2>
                <div className="mb-4">
                    <label className="block text-gray-700 font-bold mb-2"
                    >Box Number</label
                    >
                    <select
                        id="boxNumber"
                        name="boxNumber"
                        className="border rounded w-full py-2 px-3 mb-2"
                        required
                        value={boxNumber}
                        defaultValue={"def"}
                        onChange={(e) => updateBox(e.target.value)}
                    >
                        <option hidden value={"def"}>Select a box to build</option>
                        {boxes.map((box) => (
                            <option key={box.box_number} value={box.box_number}>{box.box_number}</option>
                        ))}
                    </select>
                </div>
                {
                    box !== undefined ?
                        <BuildBoxForm box={box} recipe={recipes[box.recipe_number - 1]} ingredients={ingredients}
                                      setIngredients={setIngredients} supplements={supplements}
                                      setSupplements={setSupplements}/> : <></>
                }
            </form>
        </div>
    </div>
</section>)

The result is the following if I am on the same page while navigating. This is the expected result and also the result I get if I manually select a box number in the select.

Expected result after navigating

This is what I get if I am on another page while navigating, note that the number does update in the select but the BuildBoxForm is not rendered (because box is still undefined).

Undesired result navigating from another page

Any ideas of where I am going wrong here?

Telegram,Do a shake function on your phone,How to use an accelerometer to achieve [closed]

Please help me, how to use an Accelerometer to shake it

I want to achieve that when I shake my phone, the data increases and vibrates continuously. The data will continue to increase and vibrate continuously

Here is the code I am currently using. Although it responds when shaken, it is not smooth

    if (window.Telegram?.WebApp?.Accelerometer) {
            window.Telegram.WebApp.Accelerometer
                .start({
                    frequency: 90
                }, (started) => {
                    if (started) {
                        console.log('ok');
                        this.checkAccelerometer();
                    } else {
                        console.log('no');
                    }
                });
        }
checkAccelerometer() {
            if (!window.Telegram?.WebApp?.Accelerometer?.isStarted) {
                this.isShaking = false;
                return;
            }

            const currentTime = new Date().getTime();
            const x = window.Telegram.WebApp.Accelerometer.x;
            const y = window.Telegram.WebApp.Accelerometer.y;
            const z = window.Telegram.WebApp.Accelerometer.z;

            if ((currentTime - this.lastUpdate) > this.timeThreshold) {
                const diffTime = currentTime - this.lastUpdate;
                const diffX = this.lastX - x;
                const diffY = this.lastY - y;
                const diffZ = this.lastZ - z;

        
                const speed = Math.sqrt(diffX * diffX + diffY * diffY + diffZ * diffZ) / diffTime * 10000;

            
                if (speed < this.minThreshold) {
                    if (this.isShaking) {
                        this.isShaking = false;
                        console.log('stop');
                    }
                }
        
                else if (speed > this.shakeThreshold &&
                    currentTime - this.lastShakeTime > this.shakeCooldown) {
                    this.onShake();
                    this.lastShakeTime = currentTime;
                }

                this.lastUpdate = currentTime;
                this.lastX = x;
                this.lastY = y;
                this.lastZ = z;
            }

            requestAnimationFrame(this.checkAccelerometer);
        },

Why is catch function called twice on the same promise?

I’m trying to understand how promises work under the hood in JS and faced with this example:

const promise = Promise.reject();

promise
  .catch(() => console.log(1));
  
promise
  .catch(() => console.log(2));

// Output:
// 1
// 2
const promise = Promise.reject();

promise
  .catch(() => console.log(1))
  .catch(() => console.log(2));

// Output:
// 1

Why do we have different outputs in similar examples?
Why catch is called second time in the first example?
Because promise is already rejected after the first catch, so it should be logical that the second catch should not be called in both cases…
I think the main reason here is how microtasks queue works but I’m not sure.

Errors using relativeTime Dayjs plugin in a large Vue 3 app

I do not seem to be able to use methods from the relativeTime Dayjs plugin in a large Vue 3 app. I have followed the instructions here: https://day.js.org/docs/en/plugin/relative-time

We have a vendors file which loads some external libs and adds them to Vue using the install method. In here I am loading the relativeTime and duration plugins and extending dayjs with them, like this:

import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import duration from 'dayjs/plugin/duration';

export default {
    install: (app) => {
        dayjs.extend(relativeTime);
        dayjs.extend(duration);

        app.config.globalProperties.$dayjs = dayjs;
    }
}

Then, in a component elsewhere in the app, I am trying to use the from method like this:

const currentDate = this.$dayjs();
const prevDate = this.$dayjs(oldDate);
const datesDiff = this.$dayjs(prevDate).from(currentDate);

But the third line throws the error:

this.$dayjs(…).from is not a function.

I also tried to use it like this: prevDate.from(currentDate) but this gives the same error.

If I log out currentDate and prevDate, both are valid dayjs objects. I’m using TypeScript also, and if I hover the pointer on the from method, I can see the correct type for the method ((method) Dayjs.from(compared: ConfigType, withoutSuffix?: boolean): string), and I don’t get any TS intellisense errors in the editor or during compilation. It is only at runtime that I see the error.

On the other hand, I am able to use the duration plugin without any issues, so elsewhere in the app, I can call this.$dayjs.duration(date2 - date1) without any errors.

Am I doing something wrong, or is this a bug with the plugin?

Edit

Based on the comment from @estus, even if I import dayjs and the relativeTime plugin directly in my component, and use the exact example shown on the relativeTime plugin docs page like this:

import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';

export default {
    name: 'my-component',

    beforeMount() {
        dayjs.extend(relativeTime);
    },

    mounted() {
        console.log(dayjs().from(dayjs("1990-01-01"), true));
    },
}

Then I still see the exact same error that from is not a function.

I am using Vue 3.5.6 and dayjs 1.11.13

How to show tooltip for points with null values in chart js?

I’m using Chart.js to display a line chart with custom tooltips. I have implemented the custom tooltip rendering logic using the external option in the tooltip plugin. However, when there are points with value: null, the external callback is not triggered, and no tooltip is shown for those points.

Here’s my current configuration:

plugins: {
    tooltip: {
        enabled: false,
        // other options...
        external: (context) => {
            const { chart, tooltip } = context;
            // my custom tooltip render logic...
        }
    }
}

I’ve tried various approaches but couldn’t find a way to make the external callback fire for null values. Is there any solution or workaround available?

Any help would be greatly appreciated!

Can I make .gltf or .glb file interactive? [closed]

I am working on a Web-GIS Project using three.js. I have a 3D Model in both .glb and .gltf formats that I have to display on a 3D OSM map. I have to add a functionality on the map where when I click on the model, it should open a popup displaying the information that will be input. All the other functionalities are working fine. But any functionality I give to the Model, it doesnt appear. I tried giving it a popup on mouseClick, hover, popup on keypress, color change on keypress, etc. I even tried making a Sidebar and having it open when I click on the Model. But none of it works, I am seeing no errors in the console either. Its my first time working with 3D Models so its really confusing for me.

Any guidance would be really helpful.

WebOTP API Auto Read fails after Denying first time

I have a hook to read OTP from sms in nextJS

import { useRouter } from 'next/router'
import { useEffect } from 'react'

const useAutoReadOtp = (handleSetOtp = (_: string) => {}, enabled = true) => {
  const { isReady } = useRouter()

  const ac = new AbortController()

  useEffect(() => {
    if (isReady && 'OTPCredential' in window && enabled) {

      // Fetch OTP from SMS
      navigator.credentials
        .get({
          otp: { transport: ['sms'] },
          signal: ac.signal,
        } as CredentialRequestOptions)
        .then((otp: any) => {
          if (otp && otp.code) {
            handleSetOtp(otp.code)
          }
        })
        .catch((err) => {
          console.log('Error reading OTP:', err)
        })
    } else {
      alert('Not supported Web OTP')
    }
    // Cleanup the AbortController on unmount or retry
    return () => {
      ac.abort()
    }
  }, [isReady, enabled, handleSetOtp])
}

export default useAutoReadOtp

If we allow the browser to read OTP from the popup then it works perfectly. But after denying on first try in next tries it opens the popup but does not work on clicking Allow.

I have tried to see if navigator.credentials.get is getting resolved or throwing error or anything by adding finally to the promise but it is not executed. Also nothing inside error, .catch or else is getting executed when I click on allow after clicking on Deny.

How to add react-native VPN connection to Socks5/VLess protocols

Can anyone give me an example for how to add Socks5/VLess connection in my react-native application?
I have an VPN application, with button “Connect” but can’t find any info about how to make connections in react-native.

My current const to make connections:

const connectVPN = async () => {
        // If already connected, reset timer
        if (isConnected) {
            resetTimer();
            return;
        }

        setConnecting(true);
        // Retry logic up to maxAttempts
        for (let i = 0; i < maxAttempts; i++) {
            try {
                const res = await fetch('http://localhost:8080/api/v1/credentials');
                if (!res.ok) throw new Error("Failed to get credentials");
                const data = await res.json();
                const {username, password} = data;
                const success = true;

                if (success) {
                    setIsConnected(true);
                    setConnectionAttempts(0);
                    setConnecting(false);
                    resetTimer();
                    startTimer();
                    return;
                } else {
                    throw new Error("VPN connect failed");
                }
            } catch (err) {
                console.log(err);
                setConnectionAttempts(i + 1);
            }
        }
        setConnecting(false);
        alert(t('error'));
    };

After the credentials step I need to set connection to VPNServer using Socks5 or VLess protocols