Does anyone know how to build this vertical text ticker? [closed]

Recently I ran across this infinite scroll vertical text ticker/carousel that highlights one word in the center of the container as you scroll. The rest of the words fade up or down from the bottom depending on the direction of scroll. Does anyone know how they achieved this effect? I have been looking for a similar script but have been unsuccessful in my search.

The website I saw it on is https://www.warp.dev/. It is in the 3rd from the last content block on that page. Below is an image of the block on that page containing the effect.

View Image of Ticker

How to correctly write onChange for an input that stores data in an array field?

I have a form for adding books in React. On the backend, the API expects this object:

public record AppendBookRequest(
    string Title,
    string Name,
    string Description,
    string ImageUrl,
    decimal Price,
    List<string> Category
);

So the Category field must be an array of strings.
On the frontend, my code looks like this:

const AddBook = (
  bookImgUrl,
  bookTitle,
  bookNameFirst,
  bookNameLast,
  bookPrice,
  bookCategory,
  bookDescription
) => {
  const item = {
    img: bookImgUrl,
    id: crypto.randomUUID(),
    title: bookTitle,
    name: `${bookNameFirst} ${bookNameLast}`,
    price: bookPrice,
    category: Array.isArray(bookCategory) ? bookCategory : [bookCategory],
    description: bookDescription
  };
  axios.post(url, item);
  setBooks([...books, item]);
};

And in the bookDetails component:

<div className="mb-3">
  <label className="form-label">Book genre:</label>
  <input 
    className="form-control"
    type="text"
    value={book.category ? book.category.join(', ') : ''}
    onChange={(e) => { }} - have problem with it
  />
</div>

If I just store a string in category, then later when I call join I get:
book.category.join is not a function
I tried storing category as a string, but the server returns 500 because it expects an array of strings. I also tried using split(‘,’), but got other issues when converting.

.Replace() is not capturing every instance of occurance [duplicate]

I have the following string that have tokens which are replaced by data inside of a function. Example of the tokens are @1 @2 @3. So as I am debugging I am noticing that some tokens are not being removed by using .replace(). However, if a .replace() is performed again it finally finds the token.

Example of code:

        template = template.replace("@4", batchId);
        if(template.indexOf("@4") != -1){
          template = template.replace("@4", batchId);
       }            
       template = template.replace("@5", batchId);

Example of string:(IPL)

‘Rx1bCW816x1bPFB1;f0;o139,287;c18,2,L,8;w11;h11;d3,133638850151022199H2;f0;o229,666;c26;b0;h23;w23;d3,133638850151022199L3;f1;o511,822;l812;w3L4;f0;o512,415;l705;w3B5;f0;o709,135;c18,2,L,8;w11;h11;d3,05E.103.022.AK-WIPH6;f0;o804,54;c26;b0;h17;w17;d3,@0B7;f0;o709,543;c18,2,L,8;w11;h11;d3,05E.103.022.Q:-FGH8;f0;o804,462;c26;b0;h17;w17;d3,@1H9;f0;o225,178;c26;b0;h26;w26;d3,@6D0Rl13x1bE,111′

React Router With Browser Extension Popup Window?

I’m trying to incorporate React Router into my Firefox browser extension to be able to display different pages in the extension’s popup window. However, I keep getting the warning, You should call navigate() in a React.useEffect(), not when your component is first rendered., which causes React Router to ignore the navigation attempt.

The thing is, I’m not directly calling navigate. I’m using React Router’s provided Link component, in a manner that seems to me to be entirely consistent with the doucmentation. Notably, much like in my own implementation, the documentation suggests that using Link eliminates any need to use useEffect or listen for a change in state.

In an effort to bypass this, I did try emulating a fix found for a similar issue, described here, where they used a toy state prop to make sure that no navigation occurred until the second time rendering the component. However, this didn’t change the outcome at all. Below is the relevant code.

How could I fix my code to allow the browser extension to navigate within the popup window? Is something wrong with my implementation, or is this a limitation of Firefox extensions that prevents navigation with React Router?

Any and all assistance would be much appreciated!

import React, { useEffect, useState } from "react";
import ReactDOM from "react-dom"
import { StaticRouter as Router, Routes, Route, Link } from "react-router";

import Button, { ButtonType } from "./ui/widgets/Button.jsx";

/* ********* *
 * CONSTANTS *
 *************/

const PATH_ROOT = "/";
const PATH_OPTIONS = "/options"

/* **************** *
 * REACT COMPONENTS *
 ********************/

function MainMenu() {
    const [rendered, setRendered] = useState(false);
    console.log("Rendering...");

    const menu = <div className="mainMenu">
        <Link to={PATH_OPTIONS}><Button>Options</Button></Link>
    </div>

    useEffect(() => {
        setRendered(true);
        console.log("Rendered!");
    }, [])

    return menu;
}

function OptionsMenu() {
    console.log("Attempting to render OptionsMenu");
    return <div>
        <h1>Options</h1>
        <Link to={PATH_ROOT}>
            <Button>Back</Button>
        </Link>
    </div>
}

function Error() {
    console.log("Attempting to render ErrorMenu");
    return <h1>Error: URL not found</h1>
}

function PopupApp() {
    return <Router>
        <Routes>
            <Route path={PATH_ROOT} element={<MainMenu />} />
            <Route path={PATH_OPTIONS} element={<OptionsMenu />} />
            <Route path="*" element={<Error />} />
        </Routes>
    </Router>
}

export default PopupApp;

Promise containing boolean not reading as true using async/await, not reading false without async/await [closed]

I have a function that right now just contains an if statement with a console.log, it looks like this

async function embed(){
      if(await getLive()){
        console.log(live)
      }
    }

My issue in particular is that when getLive() is true in this codeblock, the if statement doesn’t read it as such. Additionally when I write it as this

      if(getLive()){
        console.log(live)
      }

it is read as always true, even with getLive() returns a promise with a false value, I’m pretty sure this is only happening because objects are always truthy, but is that not what async/await is supposed to fix?
EDIT:
This is what is inside the getLive() function:

async function getLive(){
        const request = new Request("https://api.twitch.tv/helix/search/channels?query=ishowspeed", {
          headers: {
            'Client-ID': '[client-id]',
            'Authorization': '[auth token]'
          },
        })
    
      const response = await fetch(request)
      const value = await response.json()
      var count=0;
      while(value.data[count].id != 220476955){
        count ++;
      }
        return value.data[count].is_live
      }

ishowspeed is just who I was using at the moment for a currently live twitch channel

And in the debug console this is the result I’m given, it’s a separate console.log() from what’s shown in the embed function, its called after that function

Promise {[[PromiseState]]: 'pending', [[PromiseResult]]: undefined}
[[PromiseResult]] = true

Adobe Express Script Not Selecting one Particular object in Function

I’m self-taught when it comes to the Adobe Scripts, so I don’t know what’s going on.

This is a shortened version of the function I’m working on; the full version will have different objects being placed in different groups. The function should:

  1. receive two variables, a color indicator string for naming purposes, side name which is the layer and determines some if statements and a target doc that the objects will be copied to.
  2. The boundry variables are hidden guides that makes movement easier.
  3. The gathered array is meant to hold all objects until the end and then the for loop will select the objects and the function groupAndMoveToLayer is ran

Theoretically this works, but when I run it the obj1and6 is consistently not selected through this for loop. This also occurs in the longer version, but always with the 2nd object pushed to gathered, so for example, I might have 6 objects, but the 2nd object is always missed.

function side5and6movement(obj1and6, obj2and7, colorName, sideName, targetDoc){

    //declare variables
    var temp;
    var tempGroup;

    var gathered = [];
    var boundry = getObject("DO NOT TOUCH2", sideName + "Boundry");

    if(boundry == null){
        alert("boundry " + sideName + " not found" );
        earlyExit = true;
        return;
    }

    gathered[0] = boundry.duplicate();
    if(earlyExit){
        return;
    }

    gathered[0].locked = false;
    var index = 0;

    if(obj1and6 != null){// check for 1/6
        temp = obj1and6.duplicate();
        doc.selection = null;

        tempGroup = doc.groupItems.add();
        temp.moveToBeginning(tempGroup);        
        
        if(sideName == "Side5"){
            boundry = getObject("DO NOT TOUCH2", "Item6-Boundry");
            temp = boundry.duplicate();
            temp.locked = false;
            temp.moveToBeginning(tempGroup);

            tempGroup.name = "obj_6_GS_" + colorName;
        } else {
            boundry = getObject("DO NOT TOUCH2", "Item1-Boundry");
            temp = boundry.duplicate();
            temp.locked = false;
            temp.moveToBeginning(tempGroup);

            tempGroup.name = "obj_1_GS_" + colorName;
        }

        tempGroup.locked = false;
        tempGroup.visible = true;
        tempGroup.selected = true;
        
        gathered.push(tempGroup);        
        index ++;
    }

    if(obj2and7 != null){// check for 2/7
        temp = obj2and7.duplicate();
        doc.selection = null;

        tempGroup = doc.groupItems.add();
        temp.moveToBeginning(tempGroup);        
        
        if(sideName == "Side5"){
            boundry = getObject("DO NOT TOUCH2", "Item7-Boundry");
            temp = boundry.duplicate();
            temp.locked = false;
            temp.moveToBeginning(tempGroup);

            tempGroup.name = "obj_7_GS_" + colorName;
        } else {
            boundry = getObject("DO NOT TOUCH2", "Item2-Boundry");
            temp = boundry.duplicate();
            temp.locked = false;
            temp.moveToBeginning(tempGroup);

            tempGroup.name = "obj_2_GS_" + colorName;
        }

        tempGroup.locked = false;
        tempGroup.visible = true;
        tempGroup.selected = true;
        
        gathered.push(tempGroup);        
        index ++;
    }

    //move to link page
    moveToDoc(gathered, targetDoc, colorName);
    app.activeDocument = doc;

    // move to new layer
    doc.selection = null;
    for(var i = 0; i < gathered.length; i++){
        gathered[i].selected = true;
    }
    
    temp = groupAndMoveToLayer("etching" + sideName);
    
    if(earlyExit){
        return;
    }

    temp.name = colorName;
    return;
}

I’ve tried all that I can think of, I’ve even put a sleep command for 2 seconds to make sure it isn’t asynchronous, but I can’t seem to get rid of this problem. I can include more code, the project is currently around 1600 lines and a large mess.

React Router : How to navigate from a table “View” button and pass an id to the detail component?

I have a table that lists modules. Each row has a View button. When the user clicks View, I want to:

Navigate to a detail page (/modules/:id)
Use that id in the detail component to fetch/load the module data.

What’s the correct way to pass the id and read it in the target component with React Router

I have pasted all of my code here so you will be able to give me some suggestion.

import React, { useEffect, useState } from "react";
import { listOfCourseModules } from "../api/fileService";
import { useNavigate } from "react-router-dom";


export const ModulesListComponent = () => {

  const [modules, setModules] = useState([]);

  const navigator = useNavigate();


 useEffect(() => {
  listOfCourseModules().then((response) => {
    setModules(response.data);
    console.log(response.data)
  });
},[]);


const viewPdf = (id) =>{  // this is the part that I am trying  to  figure out.
  navigator("/page/",{id})
}

  return (
    <div className="container">
      <h2 className="text-center">list of modules</h2>
      <br />
      <table className="table table-striped table-bordered">
        <thead>
          <tr>
            <th>module id</th>
            <th>module name</th>
            <th>module image</th>
            <th>module file name</th>
            <th>description</th>
            <th>author</th>
          </tr>
        </thead>
        <tbody>
          {modules.map((module) => (
            <tr key={module.moduleId}>
              <td>{module.moduleId}</td>
              <td>{module.moduleName}</td>
              <td>{module.imagePath}</td>
              <td>{module.filePath}</td>
              <td>{module.description}</td>
              <td>{module.author}</td>
              <td><button className="btn btn-primary" onClick={()=>viewPdf(module.moduleId)}>view</button></td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
};







my other component....


import React from "react";
import { useParams } from "react-router-dom";
import PdfViewer from "./PdfViewer";

export const PdfPage = (id) => {
  const pageNumber = parseInt(page) || 1;

  return (
    <>
      <PdfViewer
      id
      />
    </>
  );
};




My app.jsx....


import { useState } from "react";
import "./App.css";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import { PdfPage } from './components/PdfPage';
import { FileUploadFormComponent } from "./components/FileUploadFormComponent";
import { ModulesListComponent } from "./components/ModulesListComponent";


function App() {
  const [count, setCount] = useState(0);

  return (
    <>
      <Router>
        <Routes>
          <Route path="/page" element={<PdfPage />} />
          <Route path="*" element={<div>Select a PDF file</div>} />
          <Route path="/upload" element={<FileUploadFormComponent/>} />
          <Route path="/all" element={<ModulesListComponent/>} />
        </Routes>
      </Router>
    </>
  );
}

export default App;







pdfViewer.jsx....

import React, { useEffect, useState, useRef } from "react";
import * as pdfjsLib from "pdfjs-dist/build/pdf.mjs";
import workerUrl from "pdfjs-dist/build/pdf.worker.min.mjs?url";
import { getPdf, getPdfPage, totalPageNumber } from "../api/fileService";
import { Loader } from "./Loader";
pdfjsLib.GlobalWorkerOptions.workerSrc = workerUrl;

export default function PdfViewer({ moduleId, initialPage = 1 }) {
  const [currentPage, setCurrentPage] = useState(initialPage);
  const [numPages, setNumPages] = useState(0);
  const canvasRef = useRef(null);
  const [isPageLoading, setIsPageLoading] = useState(false);

  // Fetch total pages when file changes
  useEffect(() => {
    if (!id) return;
    const fetchTotalPages = async () => {
      try {
        const res = await totalPageNumber(moduleId);
        setNumPages(res.data);
        console.log(res.data);
      } catch (err) {
        console.error("Failed to get total pages:", err);
      }
    };

    fetchTotalPages();
    setCurrentPage(initialPage);
  }, [fileName, initialPage]);

  // Load current page

  const loadPage = async (page) => {
    try {
      setIsPageLoading(true);
      const response = await getPdfPage(moduleId);

      const pdf = await pdfjsLib.getDocument({ data: response.data }).promise;

      
      const pdfPage = await pdf.getPage(1);
      const viewport = pdfPage.getViewport({ scale: 1.5 });

      const canvas = canvasRef.current;
      canvas.height = viewport.height;
      canvas.width = viewport.width;

      await pdfPage.render({
        canvasContext: canvas.getContext("2d"),
        viewport,
      }).promise;
    } catch (err) {
      console.error(err);
      alert("Page locked or unavailable");
    } finally {
      setIsPageLoading(false);
    }
  };

  useEffect(() => {
    if (fileName && currentPage > 0) {
      loadPage(currentPage);
    }
  }, [fileName, currentPage]);

  const handleNext = () => {
    if (currentPage < numPages) setCurrentPage((prev) => prev + 1);
  };

  const handlePrev = () => {
    if (currentPage > 1) setCurrentPage((prev) => prev - 1);
  };

  if (!filename) return <div>Please select a PDF file</div>;

  return (
    <div>
      <div style={{ position: "relative" }}>
        <canvas ref={canvasRef}></canvas>
        {isPageLoading && (
          <div className="loader">
            <Loader />
          </div>
        )}
      </div>
      <div>
        <button onClick={handlePrev} disabled={currentPage === 1}>
          Previous
        </button>
        <span>
          Page {currentPage} / {numPages}
        </span>
        <button onClick={handleNext} disabled={currentPage === numPages}>
          Next
        </button>
      </div>
    </div>
  );
}

Stripe webhook error: “No stripe-signature header value was provided” in Express app

I’m integrating Stripe webhooks in my Node.js + Express project.

My route looks like this:

router.post(
  '/webhook',
  express.raw({ type: 'application/json' }),
  webhookCreate
);
exports.webhookCreate = async (req, res) => { 
  const sig = req.headers['stripe-signature']; 
  const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET
    
  try {
    const event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
    
    if (event.type === 'checkout.session.completed') {
      const session = event.data.object;
      console.log('✅ Checkout session completed:', session.id);
    }
    
    res.json({ received: true });
    
  } catch (err) {
    console.error(❌ Webhook signature verification failed:, err.message);
    res.status(400).send(Webhook Error: ${err.message}); 
  } 
};

But when I trigger a test event with Stripe CLI:

stripe trigger checkout.session.completed

I get this error:

❌Webhook Error: No stripe-signature header value was provided.
❌Webhook signature verification failed: No webhook payload was provided.
❌Webhook Error: No webhook payload was provided.

What I tried:

  • Using express.raw({ type: 'application/json' }) for the webhook route.
  • Making sure express.json() isn’t applied before the webhook route.
  • Running the Stripe CLI with stripe listen --forward-to localhost:3011/api/stripe/webhook
  • Updating my .env with the latest whsec_… from Stripe CLI.

Don’t include private-external packages in Rollup builds

I have following files:

// src/_react/useValue.ts

import { obj } from "../default";
import React, { useCallback, useEffect } from "react";

const useValue = () => {
  const [value, setValue] = React.useState(obj.value);

  useEffect(() => {
    obj.value = value;
  }, [value]);

  const increment = useCallback(() => {
    setValue((prev) => prev + 1);
  }, []);

  const decrement = useCallback(() => {
    setValue((prev) => prev - 1);
  }, [])

  return { value, increment, decrement };
}

export default useValue;
// src/_react/index.ts
export { default as useValue } from "./useValue";
// src/default/value.ts
const obj = {
  value: 0
}

export { obj }
// src/default/index.ts
export { obj } from './value'

I want to build default and _react separately.

I have following Rollup configuration:

// rollup.config.mjs
import commonJS from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import terser from '@rollup/plugin-terser';
import typescript from '@rollup/plugin-typescript';
import external from 'rollup-plugin-peer-deps-external';

export default [
  {
    input: 'src/default/index.ts',
    output: [
      {
        file: `dist/default/cjs/index.js`,
        format: 'cjs',
        sourcemap: true,
      },
      {
        file: `dist/default/esm/index.js`,
        format: 'esm',
        sourcemap: true,
      },
    ],
    plugins: [
      external(),
      resolve(),
      commonJS(),
      typescript({
        tsconfig: './tsconfig.json',
      }),
      terser(),
    ],
  },
  {
    input: 'src/_react/index.ts',
    output: [
      {
        file: `dist/react/cjs/index.js`,
        format: 'cjs',
        sourcemap: true
      },
      {
        file: `dist/react/esm/index.js`,
        format: 'esm',
        sourcemap: true
      },
    ],
    plugins: [
      external(),
      resolve(),
      commonJS(),
      typescript({
        tsconfig: './tsconfig.json',
      }),
      // terser(),
    ],
  },
];
// tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "module": "esnext",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "jsx": "react",
    "sourceMap": true,
    "outDir": "dist",
    "strict": true,
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "baseUrl": "src"
  }
}

I build it and …here’s a catch.

This is my build for _react

// dist/react/esm/index.js
import React, { useEffect, useCallback } from 'react';

var obj = {
    value: 0
};

var useValue = function () {
    var _a = React.useState(obj.value), value = _a[0], setValue = _a[1];
    useEffect(function () {
        obj.value = value;
    }, [value]);
    var increment = useCallback(function () {
        setValue(function (prev) { return prev + 1; });
    }, []);
    var decrement = useCallback(function () {
        setValue(function (prev) { return prev - 1; });
    }, []);
    return { value: value, increment: increment, decrement: decrement };
};

export { useValue };
//# sourceMappingURL=index.js.map

I don’t want to move

var obj = {
    value: 0
};

into this file.
I want to import obj from ‘default’ to have something like this:

import React, { useEffect, useCallback } from 'react';
import { obj } from '../../default/esm' // import obj from default/esm

var useValue = function () {
    var _a = React.useState(obj.value), value = _a[0], setValue = _a[1];
    useEffect(function () {
        obj.value = value;
    }, [value]);
    var increment = useCallback(function () {
        setValue(function (prev) { return prev + 1; });
    }, []);
    var decrement = useCallback(function () {
        setValue(function (prev) { return prev - 1; });
    }, []);
    return { value: value, increment: increment, decrement: decrement };
};

export { useValue };
//# sourceMappingURL=index.js.map

Is this possible? What should I change and where?

How do I fix my SVG path getting cut off by the paper during the print dialog?

I wanted to apply a fancy border with concave corners, so I used SVG. Everything looks fine on screen, but on the print dialog, the border is cut off by the right edge of the page. The border doesn’t get cut off if I give the card a max-width less than the width of the paper, but I don’t want to do that. I tried on Chrome and Edge.

svg border cut off in print preview

I thought the div’s bounding box wasn’t fully calculated, so I tried requestAnimationFrame() during the print trigger, but no luck.

Below is a minimal example. I gave comments for code changes I tried.

function applyCardBorders() {
  let oldCardBorders = document.getElementsByClassName('card-border');
  Array.from(oldCardBorders).forEach(cardBorder => cardBorder.remove());

  let maxCornerRadius = 12;
  let cards = document.getElementsByClassName('card');

  Array.from(cards).forEach(card => {
    drawBorder(card, maxCornerRadius);
  });
}

function drawBorder(card, maxCornerRadius) {
  // tried it this way too
  // let w = card.offsetWidth;
  // let h = card.offsetHeight;
  let w = card.getBoundingClientRect().width;
  let h = card.getBoundingClientRect().height;
  let cornerRadius = Math.min(h / 3, maxCornerRadius);

  const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
  svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
  svg.style.position = "absolute";
  svg.style.left = "0px";
  svg.style.top = "0px";
  svg.style.pointerEvents = "none";
  svg.classList.add('card-border');
  // tried it this way too
  // svg.setAttribute("width", "100%");
  // svg.setAttribute("height", "100%");
  svg.setAttribute("width", w);
  svg.setAttribute("height", h);

  const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
  path.setAttribute("d", `M ${w-cornerRadius},${1} 
                A ${cornerRadius+2},${cornerRadius+2} 0 0 0 ${w-1},${cornerRadius} 
                L ${w-1},${h-cornerRadius} 
                A ${cornerRadius+2},${cornerRadius+2} 0 0 0 ${w-cornerRadius},${h-1} 
                L ${cornerRadius},${h-1} 
                A ${cornerRadius+2},${cornerRadius+2} 0 0 0 ${1},${h-cornerRadius} 
                L ${1},${cornerRadius} 
                A ${cornerRadius+2},${cornerRadius+2} 0 0 0 ${cornerRadius},${1} 
                Z`);
  path.setAttribute("stroke", "#666461");
  path.setAttribute("stroke-width", "2");
  path.setAttribute("fill", "none");

  svg.appendChild(path);
  card.appendChild(svg);
}

window.addEventListener('load', async () => {
  requestAnimationFrame(() => {
    applyCardBorders();
  });
});

window.addEventListener('resize', () => {
  applyCardBorders();
});

// tried it this way too
// const mediaQueryList = window.matchMedia("print");
// mediaQueryList.addEventListener("change", (mql) => {
//  requestAnimationFrame(() => {
//      applyCardBorders();
//  });
// });
window.addEventListener("beforeprint", () => {
  applyCardBorders();
});
window.addEventListener("afterprint", () => {
  applyCardBorders();
});
.card {
  /* don't want to do this because it is paper size dependent, but it works */
  /* max-width: 6in; */
  background-color: #b7cece;
  padding: 8px;
  position: relative;
}
<div class="card">
  <p>lorem ipsum</p>
  <p>lorem ipsum</p>
</div>

Nodemailer SMTP Connection Error: Missing credentials for “PLAIN”

I’m trying to send OTP emails using Nodemailer in my Node.js project, but I keep getting the following error:

SMTP Connection Error: Error: Missing credentials for “PLAIN”
at SMTPConnection._formatError (…)
at SMTPConnection.login (…)

code: ‘EAUTH’,
command: ‘API’

I’ve enabled 2FA and created an App Password for Gmail.

Question:
Why am I still getting the Missing credentials for “PLAIN” error and how can I fix it?

how to remove the red warning icon on Android app’s UI?

I’m developing an Android application and noticed that a red warning icon (triangle with an exclamation mark) appears when opening an external browser via Linking or when a system permission popup appears. This happens only in Android apps. Can anyone help me understand why this happens and how to resolve it?

Warning icon below the permission popup in red box

I have checked the code nothing seeps to be helpful, changes some setting in androidManifest.xml and also checked the mainActivity.kt files

How to make dependency injection lazy and simple using property hooks in php

The problem is to use many objects in constructor but in way that only while path of script goes to one of the condition, then the service will be really created (initialised). I my opinion it is very valuable thing because sometimes one service can throw only the exception according do given data but all dependencies had to be set while constructing which is a big waste of resources.
So how to accomplished it using php?

Server Error The booking service is currently unavailable. Please try again later or contact support [closed]

The main purpose of the code below is for a booking session. Each time I click on the book a session button on the pop-up modal window I get Server Error The booking service is currently unavailable. Please try again later or contact support.

–booking.php–

<?php
// booking.php
require_once 'config.php';

use PHPMailerPHPMailerPHPMailer;
use PHPMailerPHPMailerException;

require __DIR__ . '/../assets/vendor/autoload.php';

header('Content-Type: application/json; charset=UTF-8');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, X-Requested-With');
header('Access-Control-Allow-Credentials: true');

// Handle preflight OPTIONS
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {

    http_response_code(200);
    exit;
}

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    echo json_encode(["status" => "error", "message" => "Invalid request method"]);
    exit;
}

// Parse JSON
$raw = file_get_contents("php://input");
$input = json_decode($raw, true);

if (!$input) {
    echo json_encode([
        "status" => "error",
        "message" => "Invalid or empty JSON",
        "debug" => (APP_ENV === 'development' ? $raw : null)
    ]);

    exit;
}

// Required fields
$required = ['full_name', 'email', 'phone', 'subject', 'session_type', 'date', 'time', 'details'];

foreach ($required as $field) {

    if (empty($input[$field])) {
        echo json_encode([
            "status" => "error",
            "message" => "Missing required field: $field",
            "debug" => (APP_ENV === 'development' ? $input : null)
        ]);

        exit;
    }
}

// Core fields
$full_name = trim($input['full_name']);
$email = filter_var($input['email'], FILTER_VALIDATE_EMAIL);
$phone = trim($input['phone']);
$subject = trim($input['subject']);
$session_type = trim($input['session_type']);

$date = $input['date'];
$time = $input['time'];
$details = trim($input['details']);
$role = $input['role'] ?? null;

if (!$email) {
    echo json_encode(["status" => "error", "message" => "Invalid email address"]);
    exit;
}

// Optional fields (set null if missing)
$optionalFields = [
'current_challenges','personal_goals','group_size','group_type','group_objectives',
'institution_type','institution_name','educational_goals','org_size','org_sector',
'dev_focus','leadership_level','leadership_challenges','dev_areas','transformation_scope',
'current_state','desired_outcome','timeline'
];


foreach ($optionalFields as $field) {
    $$field = $input[$field] ?? null;
}

try {
    // Save booking
    $stmt = $pdo->prepare("INSERT INTO bookings 
   (full_name,email,phone,subject,session_type,date,time,details,role,
current_challenges,personal_goals,group_size,group_type,group_objectives,
         institution_type,institution_name,educational_goals,
         org_size,org_sector,dev_focus,
         leadership_level,leadership_challenges,dev_areas,
         transformation_scope,current_state,desired_outcome,timeline)
        VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");

    $stmt->execute([
        $full_name, $email, $phone, $subject, $session_type, $date, $time, $details, $role,
$current_challenges,$personal_goals,$group_size,$group_type,$group_objectives,
        $institution_type,$institution_name,$educational_goals,
        $org_size,$org_sector,$dev_focus,
        $leadership_level,$leadership_challenges,$dev_areas,
        $transformation_scope,$current_state,$desired_outcome,$timeline
    ]);

    $booking_id = $pdo->lastInsertId();

    // Send Admin Email
    try {
        $mail = new PHPMailer(true);
        $mail->isSMTP();
        $mail->Host = 'smtp.gmail.com';
        $mail->SMTPAuth = true;
        $mail->Username = MAIL_FROM;
        $mail->Password = MAIL_PASS;
        $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
        $mail->Port = 587;
        $mail->setFrom(MAIL_FROM, 'Booking System');
        $mail->addAddress(MAIL_TO);
        $mail->isHTML(true);
        $mail->Subject = "New Booking (#$booking_id): $session_type by $full_name";

        $mail->Body = "
            <h2>New Booking Received</h2>
            <p><strong>Name:</strong> $full_name</p>
            <p><strong>Email:</strong> $email</p>
            <p><strong>Phone:</strong> $phone</p>
            <p><strong>Subject:</strong> $subject</p>
            <p><strong>Session:</strong> $session_type</p>
            <p><strong>Date:</strong> $date</p>
            <p><strong>Time:</strong> $time</p>
            <p><strong>Details:</strong><br>" . nl2br($details) . "</p>";
        $mail->send();

    } catch (Exception $e) {
        error_log("Email Error (Admin): " . $e->getMessage());
        echo json_encode(["status"=>"error","message"=>"Email error (admin)","debug"=>(APP_ENV==='development'?$e->getMessage():null)]);
        exit;
    }

    // Send Customer Confirmation
    try {
        $mail = new PHPMailer(true);
        $mail->isSMTP();
        $mail->Host = 'smtp.gmail.com';
        $mail->SMTPAuth = true;
        $mail->Username = MAIL_FROM;
        $mail->Password = MAIL_PASS;
        $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
        $mail->Port = 587;

        $mail->setFrom(MAIL_FROM, '360 Sages');
        $mail->addAddress($email);
        $mail->isHTML(true);
        $mail->Subject = "Booking Confirmation - $session_type";
        $mail->Body = "
            <h2>Hello $full_name,</h2>
            <p>Thank you for booking a <strong>$session_type</strong> session with us.</p>
            <p><strong>Date:</strong> $date</p>
            <p><strong>Time:</strong> $time</p>
            <p>We’ll contact you soon with further details.</p>
            <br>
            <p>Best regards,<br>360 Sages Team</p>";

        $mail->send();
    } catch (Exception $e) {
        error_log("Email Error (Customer): " . $e->getMessage());
        echo json_encode(["status"=>"error","message"=>"Email error (customer)","debug"=>(APP_ENV==='development'?$e->getMessage():null)]);
        exit;
    }

    echo json_encode(["status"=>"success","message"=>"Booking submitted successfully!"]);

} catch (PDOException $e) {
    error_log("Database Error: " . $e->getMessage());
    echo json_encode([
        "status" => "error",
        "message" => "Database error while saving booking",
        "debug" => (APP_ENV === 'development' ? $e->getMessage() : null)
    ]);

    exit;
}

--script.js--
document.addEventListener("DOMContentLoaded", function () {
  const bookingForm = document.getElementById("bookingForm");
  const sessionTypeSelect = document.getElementById("session_type");

  // Inject CSS for shake + label highlight + SweetAlert error color + success glow

  if (!document.getElementById("error-style")) {
    const style = document.createElement("style");
    style.id = "error-style";
    style.textContent = `

      .field-error {
        border: 2px solid red !important;
        animation: shake 0.3s ease-in-out;
      }

      .label-error {
        color: red !important;
        font-weight: bold;
      }

      .form-success {
        animation: glowSuccess 1s ease-in-out;
      }

      .swal2-popup.swal2-toast .swal2-title,
      .swal2-popup .swal2-title,
      .swal2-popup .swal2-html-container {
        color: red !important;
      }

      @keyframes shake {
        0% { transform: translateX(0); }
        25% { transform: translateX(-5px); }
        50% { transform: translateX(5px); }
        75% { transform: translateX(-5px); }
        100% { transform: translateX(0); }
      }

      @keyframes glowSuccess {
        0% { box-shadow: 0 0 0px rgba(0,255,0,0); }
        50% { box-shadow: 0 0 15px rgba(0,255,0,0.7); }
        100% { box-shadow: 0 0 0px rgba(0,255,0,0); }
      }
    `;

    document.head.appendChild(style);
  }

  if (bookingForm) {
    bookingForm.addEventListener("submit", async (e) => {
      e.preventDefault();
      const formDataObj = {};
      const invalidFields = [];

      function getFieldLabel(el) {
        const label = bookingForm.querySelector(`label[for="${el.id}"]`);
        return label ? label.textContent.trim() : el.name.replace("_", " ");
      }

      bookingForm.querySelectorAll("input, textarea, select").forEach((el) => {

        if (el.name) {
          const value = el.value.trim();
          formDataObj[el.name] = value;
          const label = bookingForm.querySelector(`label[for="${el.id}"]`);

          if (label) label.classList.remove("label-error");

          el.classList.remove("field-error");

          if (el.hasAttribute("required") && !value) {

            invalidFields.push({ field: el, message: `${getFieldLabel(el)} is required.` });
          }

          if (el.type === "email" && value) {
            const emailRegex = /^[^s@]+@[^s@]+.[^s@]+$/;

            if (!emailRegex.test(value)) {
              invalidFields.push({ field: el, message: `Please enter a valid ${getFieldLabel(el)}.` });
            }
          }

          if (el.type === "tel" && value) {
            const phoneDigits = value.replace(/D/g, "");

            if (phoneDigits.length < 7) {
              invalidFields.push({ field: el, message: `${getFieldLabel(el)} must have at least 7 digits.` });
            }
          }
        }
      });

      formDataObj.session_type = sessionTypeSelect.value;

      for (const invalid of invalidFields) {
        const field = invalid.field;
        const label = bookingForm.querySelector(`label[for="${field.id}"]`);

        field.classList.add("field-error");

        if (label) label.classList.add("label-error");

        field.scrollIntoView({ behavior: "smooth", block: "center" });

        await Swal.fire({
          icon: "error",
          title: "Invalid Field",
          html: `<span style="color:red; font-weight:bold;">⚠️ ${invalid.message}</span>`,
          confirmButtonColor: "#152942",
          didClose: () => {
            field.focus();
            field.classList.remove("field-error");

            if (label) label.classList.remove("label-error");
          },
        });
      }

      if (invalidFields.length > 0) return;

      Swal.fire({
        title: "Submitting...",
        text: "Please wait while we process your booking.",
        allowOutsideClick: false,
        didOpen: () => Swal.showLoading(),
      });

      try {
        const apiUrl = new URL(
          "includes/booking.php",
          window.location.origin + window.location.pathname
        ).href;

        const response = await fetch(apiUrl, {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(formDataObj),
        });

        const result = await response.json();

        if (result.status === "success") {
          window.scrollTo({ top: 0, behavior: "smooth" });

          // Add subtle green glow animation
          bookingForm.classList.add("form-success");
          setTimeout(() => bookingForm.classList.remove("form-success"), 1000);


          Swal.fire({
            icon: "success",
            title: "Booking Confirmed",
            text: result.message,
            confirmButtonColor: "#152942",
          });

          bookingForm.reset();

          if (typeof closeModal === "function") closeModal();

        } else {
          let errorMsg = "Booking failed. Please try again.";

          if (result.message.includes("Database")) errorMsg = "Database Error: Unable to save your booking.";

          else if (result.message.includes("Email error (admin)")) errorMsg = "Email Error: Failed to notify admin.";

          else if (result.message.includes("Email error (customer)")) errorMsg = "Email Error: Failed to send confirmation to your email.";

          Swal.fire({
            icon: "error",
            title: "Error",
            html: `<span style="color:red; font-weight:bold;">${errorMsg}</span>`,
            confirmButtonColor: "#152942",
          });

          if (result.debug) console.error("Debug Info:", result.debug);
        }
      } catch (error) {
        console.error("Error:", error);

        Swal.fire({
          icon: "error",
          title: "Server Error",
          html: `<span style="color:red; font-weight:bold;">⚠️ The booking service is unavailable.</span>`,
          confirmButtonColor: "#152942",
        });
      }
    });
  }
}
);