Set individual sound for each aframe entity

I wanna realize next functionality: user are scanning one image (it is a target, all ones are 5 item), and after than as the picture will be scan – will start playing sound (related to this image).I am using Arame library (creating scene and sound objects) and mindAR (detect of scaned photos); All are based on React function components. For test I`ve created mock:

<a-scene ref={sceneRef} mindar-image={`imageTargetSrc: http://localhost:5000/wild%20nature/wild_nature.mind; autoStart: false; uiLoading: true; uiError: true; uiScanning: true; maxTrack: 3`} color-space="sRGB" embedded renderer="colorManagement: true, physicallyCorrectLights" vr-mode-ui="enabled: false" device-orientation-permission-ui="enabled: false">
              <a-assets>
  
                   <audio id="lion" src="http://localhost:5000/wild%20nature/lion/lion.mp3" preload='auto'></audio>
                   <audio id="owl" src="http://localhost:5000/wild%20nature/owl/owl.mp3" preload='auto'></audio>


              </a-assets>
              

              <a-camera position="0 0 0" look-controls="enabled: true"></a-camera>

             
                <a-entity 
                  mindar-image-target="targetIndex: 4"
                  geometry="primitive: plane" 
                  position=" 0 0 0" 
                  sound="src:#lion;" 
                />
                <a-entity 
                  mindar-image-target="targetIndex: 3"
                  geometry="primitive: plane" 
                  position=" 0 0 0" 
                  sound="src:#owl;" 
                />
            </a-scene>

At this moment:
When I scan a lion photo – all works correctly, but when I change scaned photo to owl – sound doesn`t play.

import React, { useCallback, useEffect, useRef, useState } from 'react';
import 'aframe';
import 'mind-ar/dist/mindar-image-aframe.prod.js';
import { getChildrenByGroup } from './../utils/helpers'; 

const MindARViewer = ({children_AR_list}) => {
  
  const sceneRef = useRef(null);
  const sceneRefering= useRef([]);
  const [targetFound, setTargetFound] = useState("");

  let [targetFoundCounter,setTargetFoundCounter] = useState(0);
  const [groupedChildren, setGroupedChildren] = useState([]);

  useEffect(() => {
    setGroupedChildren(getChildrenByGroup(children_AR_list))
    
  }, []);

  useEffect(() => {
    const sceneEl = sceneRef.current;
    const arSystem = sceneEl.systems?.["mindar-image-system"];
    
    const entity = document.querySelector('a-entity[sound]');

    // Start AR system when scene starts rendering
    sceneEl.addEventListener('loaded', () => {
      arSystem.start();
    });

    // Handle target detection events
    
    sceneEl.addEventListener('targetFound', () => {
      setTargetFound(entity?.components.sound.parsingAttrValue)
      console.log(`Target found:`, entity?.components.sound.parsingAttrValue);
      
      // setTargetFoundCounter(targetFoundCounter++)

      entity?.components.sound?.playSound();
    });



    sceneEl.addEventListener('targetLost', () => {
      entity?.components.sound?.pause();
    });

    return () => {
      arSystem.stop();
      entity?.components.sound.pause();
    }
  }, []);

   



  return (
    <>
    
    {
      
      
          <>
           <a-scene ref={sceneRef} mindar-image={`imageTargetSrc: http://localhost:5000/wild%20nature/wild_nature.mind; autoStart: false; uiLoading: true; uiError: true; uiScanning: true; maxTrack: 3`} color-space="sRGB" embedded renderer="colorManagement: true, physicallyCorrectLights" vr-mode-ui="enabled: false" device-orientation-permission-ui="enabled: false">
              <a-assets>
  
                   <audio id="lion" src="http://localhost:5000/wild%20nature/lion/lion.mp3" preload='auto'></audio>
                   <audio id="owl" src="http://localhost:5000/wild%20nature/owl/owl.mp3" preload='auto'></audio>


              </a-assets>
              

              <a-camera position="0 0 0" look-controls="enabled: true"></a-camera>

             
                <a-entity 
                  mindar-image-target="targetIndex: 4"
                  geometry="primitive: plane" 
                  position=" 0 0 0" 
                  sound="src:#lion;" 
                />
                <a-entity 
                  mindar-image-target="targetIndex: 3"
                  geometry="primitive: plane" 
                  position=" 0 0 0" 
                  sound="src:#owl;" 
                />
            </a-scene>
          </>
     
    }
   
    
    </>
  )
}

export default MindARViewer;

Audio is not working in when remove from recent

our team built a React Native calling app using sip.js and WebRTC. Everything works fine when the app is active.

However, when a call is ongoing and the user swipes the app from recents (kill), the audio stops immediately.

We’ve already implemented a foreground service on Android to keep the app alive, but the JavaScript engine is stopped, which seems to break WebRTC audio.

❓ How can we keep the audio stream (or SIP call) active after the app is removed from recents — like WhatsApp or Microsoft Teams do?

Are we expected to move SIP and audio handling fully to native Android? Or is there a reliable pattern to keep sip.js/WebRTC alive in React Native apps?

Would appreciate insights from anyone who has solved this in VoIP apps.

i want when i kill app my audio is not disconnect

Video Form Upload With Preview & Submit html

I am facing issues in Coding. I have Creating a Video upload Form and trying to submit in localhost server. but i am getting error messages Continuously. I am facing problems in Javascript code. Please help me resolve this. I also want to know how to change the existing video title

const input = document.getElementById('file-input');
const video = document.getElementById('video');
const videoSource = document.createElement('source');

input.addEventListener('change', function () {
  const files = this.files || [];

  if (!files.length) return;

  const reader = new FileReader();

  reader.onload = function (e) {
    videoSource.setAttribute('src', e.target.result);
    video.appendChild(videoSource);
    video.load();
    video.play();
  };

  reader.onprogress = function (e) {
    console.log('progress: ', Math.round((e.loaded * 100) / e.total));
  };

  reader.readAsDataURL(files[0]);
});

function updateFileName(input) {
  let file = input.files[0];

  let newName = 'new_filename'; // Set your desired new filename

  document.getElementById('fileNameDisplay').textContent = newName;
}

JavaScript – The JSON object is not printing

I want to display the JSON object to the JSP page via JavaScript function. The function is invoked when search button is clicked. I want to search the patient details and display it on the same webpage. Please check the code below and let me know if any correction can be done.

function searchPatient() {
const query = document.getElementById("searchInput").value.trim();
const resultsContainer = document.getElementById("searchResults");

if (!query) {
    alert("Please enter ID or contact number.");
    return;
}

resultsContainer.innerHTML = "<p>Searching...</p>";
const url = "searchPatient.jsp?q=" + encodeURIComponent(query);
fetch(url)
    .then(response => {
        if (!response.ok) {
            throw new Error("Network response was not ok");
        }
        return response.json();
    })
   
    .then(data => {
        resultsContainer.innerHTML = ""; // Clear previous or loading message
        
        if (!data || data.length === 0) {
            resultsContainer.innerHTML = "<p>No patient found.</p>";
            return;
        }
        resultsContainer.innerHTML = "<p>Found " + data.length + " patient(s).</p>";

        data.forEach(patient => {
            const patientDiv = document.createElement("div");
            patientDiv.className = "patient-card";
            patientDiv.innerHTML = `
                <p><strong>ID:</strong> ${patient.id}</p>
                <p><strong>Name:</strong> ${patient.full_name}</p>
                <p><strong>Contact:</strong> ${patient.contact_number}</p>
                <p><strong>Concern:</strong> ${patient.concern}</p>
            `;
            resultsContainer.appendChild(patientDiv);
        });
    })
    .catch(error => {
        console.error("Search error:", error);
        resultsContainer.innerHTML = "<p>Error fetching patient data.</p>";
    });
 }

This is my jsp code to search patients from the MySQL database:

<%@page import="action.Database"%>
<%@ page import="java.sql.*, org.json.*, java.io.*" %>
<%@ page contentType="application/json" pageEncoding="UTF-8" %>

<%
response.setCharacterEncoding("UTF-8");

String query = request.getParameter("q");
JSONArray patients = new JSONArray();

if (query != null && !query.trim().isEmpty()) {
    Connection conn = null;
    PreparedStatement stmt = null;
    ResultSet rs = null;

    try {
        conn = Database.getConnection();
        String sql = "SELECT * FROM patients WHERE id = ? OR contact_number = ?";
        stmt = conn.prepareStatement(sql);
        stmt.setString(1, query);
        stmt.setString(2, query);
        rs = stmt.executeQuery();

        while (rs.next()) {
            JSONObject patient = new JSONObject();
            patient.put("id", rs.getInt("id"));
            patient.put("full_name", rs.getString("full_name"));
            patient.put("contact_number", rs.getString("contact_number"));
            patient.put("concern", rs.getString("chief_complaint"));
            patients.put(patient);
        }
    } catch (Exception e) {
        e.printStackTrace();  // Logs to server console
    } finally {
        try { if (rs != null) rs.close(); } catch (Exception e) {}
        try { if (stmt != null) stmt.close(); } catch (Exception e) {}
        try { if (conn != null) conn.close(); } catch (Exception e) {}
    }
}
out.print(patients.toString());
%>

There is no error, but the output is not printed in the searchResults div.

How to avoid notches with View Area Context in React Native apps

For the life of me, I cannot figure out how to remove these white bars, notches (or padding) at the top and bottom of the screen. But if I change the background color of the container to say “red”, it changes.

What I’ve Tried:

  • Setting the width and height of the Lottie animation using Dimensions.get(‘window’)
  • Using position: ‘absolute’ on both the container and animation
  • Replacing View with SafeAreaView
  • Using StatusBar with translucent and backgroundColor=”transparent”
  • Tried removing flex: 1 and explicitly positioning via top, bottom, etc.
  • Tried using useSafeAreaInsets() from react-native-safe-area-context

Relevant Files:
App.js

import { createStackNavigator } from '@react-navigation/stack';
import { NavigationContainer } from '@react-navigation/native';
import Home from './screens/Home'

const Stack = createStackNavigator()

export default function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator initialRouteName = 'Home'>
          <Stack.Screen
            name='Home'
            component={Home}
            options={{ headerShown: false }}
          />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

Home.js

import React from 'react';
import LottieView from 'lottie-react-native';
import { View, Text, TouchableOpacity, StyleSheet, Dimensions } from 'react-native';

export default function Home({ navigation }) {
  return (
    <View style={styles.container} >
      <LottieView
        source={require('../assets/home_splash_2.json')}
        autoPlay
        loop
        style={styles.background}
      />
      <View style={styles.overlay}>
        <Text style={styles.title}>Welcome to Vitola</Text>
        <Text style={styles.subtitle}>Find the best cigars for every moment.</Text>
        <TouchableOpacity style={styles.button} onPress={() => navigation.navigate('MainApp')}>
          <Text style={styles.buttonText}>Get Started</Text>
        </TouchableOpacity>
        <TouchableOpacity onPress={() => navigation.navigate('Login')}>
          <Text style={styles.loginText}>Already have an account? Log in</Text>
        </TouchableOpacity>
      </View>
    </View>
  );
}

const { width, height } = Dimensions.get('window');

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  background: {
    width,
    height,
    position: 'absolute',
  },
  overlay: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    paddingHorizontal: 30,
  },
  title: {
    fontSize: 28,
    fontWeight: 'bold',
    color: '#fff',
    marginBottom: 10,
  },
  subtitle: {
    fontSize: 16,
    color: '#ddd',
    marginBottom: 40,
    textAlign: 'center',
  },
  button: {
    backgroundColor: '#fff',
    paddingVertical: 12,
    paddingHorizontal: 40,
    borderRadius: 25,
    marginBottom: 20,
  },
  buttonText: {
    color: '#000',
    fontWeight: 'bold',
    fontSize: 16,
  },
  loginText: {
    color: '#fff',
    textDecorationLine: 'underline',
  },
});

How do I make the Lottie animation truly fill the screen edge-to-edge without white bars at the top or bottom — no matter the device or OS? Is there a specific way to account for status bar and safe area properly?

Any advice or example from someone who’s implemented this successfully would be much appreciatedenter image description here

Postgres UPDATE table SET jsonb_set returning invalid input syntax for type json using javascript

im using this package to CRUD im postgres DB
https://github.com/porsager/postgres

my query is

POSGRES_SQL`UPDATE pedidos
    SET destinatario = jsonb_set(
        destinatario,
        '{endereco}',
        '${POSGRES_SQL({
            cep,
            bairro,
            localidade,
            complemento,
            uf,
            logradouro,
            regiao,
            numero,
            referencia,
        })}'
    )
    WHERE _id = ${_id} RETURNING _id;`

but, that return invalid input syntax for type json, and change to this

POSGRES_SQL`UPDATE pedidos
    SET destinatario = jsonb_set(
        destinatario,
        '{endereco}',
        '{
            "cep": "",
            "bairro": "",
            "localidade": "",
            "complemento": "",
            "uf": "",
            "logradouro": "",
            "regiao": "",
            "numero": "",
            "referencia": ""
        }'
    )
    WHERE _id = ${_id} RETURNING _id;`

this work updating the entrie but without values, and change to this

POSGRES_SQL`UPDATE pedidos
    SET destinatario = jsonb_set(
        destinatario,
        '{endereco}',
        '{
            "cep": "${String(cep)}",
            "bairro": "${String(bairro)}",
            "localidade": "${String(localidade)}",
            "complemento": "${String(complemento)}",
            "uf": "${String(uf)}",
            "logradouro": "${String(logradouro)}",
            "regiao": "${String(regiao)}",
            "numero": "${String(numero)}",
            "referencia": "${String(referencia)}"
        }'
    )
    WHERE _id = ${_id} RETURNING _id;`

return could not determine data type of parameter $1

my insert use the

POSGRES_SQL`INSERT INTO pedidos ${POSGRES_SQL({
    _id,
    uid,
    created_at: Date(),
    destinatario,
    objeto: {},
    origem: {},
  })} RETURNING _id;`

and work normally, need help to update the entrie destinatario.endereco in postgres using this package https://github.com/porsager/postgres for cloudflare work.

the collum destinatario is an jsonb format and contain more entries that endereco

how to enqueue/localize wordpress admin ajax url without using wp_enqueue_script or wp_localize_script

I want to make use of Modules in our wordpress site. I was able to successfully make a test module, import it into my main file and call the module function. I accomplished this by adding wp_enqueue_script_module():

function addScript(){
    if(is_single(11084) || is_single(10952) || is_single(18184) || is_single(18239) || is_single(18510)){
        //wp_enqueue_script( 'my-script', get_stylesheet_directory_uri() . '/bsd-js.js', array ( 'jquery' ), false, true);
        //wp_localize_script( 'my-script', 'my_ajax_object', array( 'ajax_url' => admin_url( 'admin-ajax.php' ) ) );

        //testing modules
        //wp_enqueue_script('jquery');
        wp_enqueue_script_module('modTest',get_stylesheet_directory_uri() . '/bsd-js.js');
        //wp_localize_script( 'modTest', 'my_ajax_object', array( 'ajax_url' => admin_url( 'admin-ajax.php' ) ) );
        wp_enqueue_script_module('modTest2',get_stylesheet_directory_uri() . '/Modules/testMod.js');
    }

Now I tried to add my ajax url to be available in the front end like I have been doing (commentted out lines) but per the documentation wp_localize_script can only be used in conjunction with wp_enqueue_script. Can anyone help me in determining how I can use modules and still have use/enqueue my ajax url back to Javascript? I know what the URL is so I could hard-code it in JS but I really don’t think that’s a good idea.

How to register client’s webauthn credentials in a browser keepass alike authenticator

The webauthn.io website demonstrate webauthn registration and authentication. In my case, the credentials are stored in my password manager (Enpass). The link between the password manager and the browser is made through a plugin in my browser.

On webauthn.io, when I register a client, the password manager plugin is automatically triggered and asks for a fingerprint to open Enpass.

I do not manage to reproduce this behavior with a simple javscript in an html page as below. Running the code below triggers a pop-up asking to insert a usb key whereas I was expecting a popup asking for a fingerprint and the password manager plugin to pop-up together.

<!DOCTYPE HTML>
<html>
    <head> 
        <meta charset="UTF-8">
    </head>
    <body>
        <button id = "my_button">click</button>
        <h1>Console</h1>
        <p id = "console">empty</p>

        <script charset="UTF-8"> 
            bouton = document.getElementById("my_button");
            console = document.getElementById("console");
            console.innerHTML = "Console initialised";
            bouton.addEventListener("click", myFunction); 
            // help at : https://stackoverflow.blog/2022/11/16/biometric-authentication-for-web-devs/ 
            // demo : https://github.com/duo-labs/webauthn.io
            // atelier : https://developers.google.com/codelabs/webauthn-reauth?hl=fr#0 
            
            async function myFunction () { 
                const srvchallenge = new Uint8Array(20);
                const id_buffer = new Uint8Array(5);
                id_buffer[0] = 0;
                id_buffer[1] = 1;
                id_buffer[2] = 2;
                id_buffer[3] = 3;
                id_buffer[4] = 4;
                for(let i = 0; i < 20; i++) {
                    srvchallenge[i] = Math.floor(Math.random() * (Math.pow(2,8)-1));
                }
                options = {
                    publicKey: {
                        attestation: "none", 
                        challenge: srvchallenge, 
                        pubKeyCredParams: [{alg:-8, type : "public-key"},{alg:-7, type:"public-key"},{alg:-257, type : "public-key"}], 
                        rp : { name : "test.home", id : "test.home"},
                        timeout : 6000,
                        user: {displayName: "Tonio", id : id_buffer, name: "Tonio8924"},
                        hints : [], 
                        extensions: { "credProps" : true},
                        authenticatorSelection : {
                            "requireResidentKey": false,
                            "residentKey" : "preferred",
                            "userVerification": "preferred"
                        }
                    },
                };
                try {
                    const capabilities = await PublicKeyCredential.getClientCapabilities();
                    for (const [key, value] of Object.entries(capabilities)) {
                         console.innerHTML = console.innerHTML + "<br>" + key + " " + value;
                    }
                    const creds = await navigator.credentials.create(options);
                    if (creds == null)
                    {
                        console.innerHTML = "Cannot create credentials";
                    }
                    else
                    {
                        console.innerHTML = "credential create done.<br>";
                        console.innerHTML += "Authenticator attachement : " + creds.authenticatorAttachement + "<br>";
                        console.innerHTML += creds.toJSON();
                    }
                }
                catch (error) {
                    console.innerHTML = error;
                }
                if (creds == null) { 
                    console.innerHTML = "no password credentials available";
                }
                else {
                    console.innerHTML = creds.id + " " + creds.type;
                }
            };
        </script>
    </body>
</html>

Debugging webauthn.io javascript, I understood something had be done to properly handle the webauthn API call interception by the password manager plugin within the browser. I slightly (and not really understanding the issues) modified my script registering an AbortSignal to the options structure as below :

<!DOCTYPE HTML>
<html>
    <head> 
        <meta charset="UTF-8">
    </head>
    <body>
        <button id = "my_button">click</button>
        <h1>Console</h1>
        <p id = "console">empty</p>

        <script charset="UTF-8"> 
            bouton = document.getElementById("my_button");
            console = document.getElementById("console");
            console.innerHTML = "Console initialised";
            bouton.addEventListener("click", myFunction); 
            // help at : https://stackoverflow.blog/2022/11/16/biometric-authentication-for-web-devs/ 
            // demo : https://github.com/duo-labs/webauthn.io
            // atelier : https://developers.google.com/codelabs/webauthn-reauth?hl=fr#0 
            class s extends Error {
                constructor({message: e, code: t, cause: r, name: n}) {
                    super(e, {
                        cause: r
                    }),
                    Object.defineProperty(this, "code", {
                        enumerable: !0,
                        configurable: !0,
                        writable: !0,
                        value: void 0
                    }),
                    this.name = n ?? r.name,
                    this.code = t
                }
            }
            
            const c = new class {
                constructor() {
                    Object.defineProperty(this, "controller", {
                        enumerable: !0,
                        configurable: !0,
                        writable: !0,
                        value: void 0
                    })
                }
                createNewAbortSignal() {
                    if (this.controller) {
                        const e = new Error("Cancelling existing WebAuthn API call for new one");
                        e.name = "AbortError",
                        this.controller.abort(e)
                    }
                    const e = new AbortController;
                    return this.controller = e,
                    e.signal
                }
                cancelCeremony() {
                    if (this.controller) {
                        const e = new Error("Manually cancelling existing WebAuthn API call");
                        e.name = "AbortError",
                        this.controller.abort(e),
                        this.controller = void 0
                    }
                }
            };
            async function myFunction () { 
                const srvchallenge = new Uint8Array(20);
                const id_buffer = new Uint8Array(5);
                id_buffer[0] = 0;
                id_buffer[1] = 1;
                id_buffer[2] = 2;
                id_buffer[3] = 3;
                id_buffer[4] = 4;
                for(let i = 0; i < 20; i++) {
                    srvchallenge[i] = Math.floor(Math.random() * (Math.pow(2,8)-1));
                }
                options = {
                    publicKey: {
                        attestation: "none", 
                        challenge: srvchallenge, 
                        pubKeyCredParams: [{alg:-8, type : "public-key"},{alg:-7, type:"public-key"},{alg:-257, type : "public-key"}], 
                        rp : { name : "test.home", id : "test.home"},
                        timeout : 6000,
                        user: {displayName: "Tonio", id : id_buffer, name: "Tonio8924"},
                        hints : [], 
                        extensions: { "credProps" : true},
                        authenticatorSelection : {
                            "requireResidentKey": false,
                            "residentKey" : "preferred",
                            "userVerification": "preferred"
                        }
                    },
                };
                options.signal = c.createNewAbortSignal();
                try {
                    const capabilities = await PublicKeyCredential.getClientCapabilities();
                    for (const [key, value] of Object.entries(capabilities)) {
                         console.innerHTML = console.innerHTML + "<br>" + key + " " + value;
                    }
                    const creds = await navigator.credentials.create(options);
                    if (creds == null)
                    {
                        console.innerHTML = "Cannot create credentials";
                    }
                    else
                    {
                        console.innerHTML = "credential create done.<br>";
                        console.innerHTML += "Authenticator attachement : " + creds.authenticatorAttachement + "<br>";
                        console.innerHTML += creds.toJSON();
                    }
                }
                catch (error) {
                    console.innerHTML = error;
                }
                if (creds == null) { 
                    console.innerHTML = "no password credentials available";
                }
                else {
                    console.innerHTML = creds.id + " " + creds.type;
                }
            };
        </script>
    </body>
</html>

This is indeed a progress, because now the pop-up asks for a fingerprint instead of a usb key, but the password manager plugin does not pop-up and when I provide my fingerprint, nothing is registered in my password manager neither.

So the question is, what am I missing in this Credentials Container API that all webauthn demos have implemented and that I cannot reproduce with this simple js script ?

react onClick event is not firing in test case for Button

This is my component. I am trying to write a test case for this component. The toggleMenu method I used for the onclick event of the button is not firing the onclick event when I write test cases. I tried to debug it, but it’s not firing the onclick event.

import { arrayOf, bool, object, string } from 'prop-types';
import { bindActionCreators } from 'redux';
import { useDispatch, useSelector } from 'react-redux';

import classNames from 'classnames';
import { isEqual, throttle } from 'lodash';

import { Button, Icon, Spinner, Text } from 'bsd-react-ui-kit';
import { Search, TooltipButton } from 'bve-react-ui-kit';
import search from '@bsd/ui-utils/search';
import { sort, sortTypes } from '@bsd/ui-utils/sort';

import { clickTracking } from '@bsd/ui-utils/tracking';
import { matchesMin, screen } from '@bsd/ui-utils/window';

import { actions as appActions } from 'modules/application';
import { actions as commonActions } from 'modules/application/common';
import { actions as siteActions } from 'modules/account/sites';

import applicationSelectors from 'modules/application/selectors';
import siteSelectors from 'modules/account/sites/selectors';

import * as commonConstants from 'modules/application/common/constants';
import * as constants from 'modules/account/sites/constants';

import {
  convertStringToNumber,
  getPathname,
  searchAnalyticsOnBlur,
  searchAnalyticsOnFocus,
  truncateWord
} from 'modules/utilities';

const SiteSwitcher = ({
  availableSites,
  content,
  dashboard,
  defaultSiteId,
  path
}) => {
  const baseClass = 'bve-site-switcher';
  const locationClass = dashboard ? '' : `${baseClass}-no-dashboard`;
  const siteList = useRef();

  const dispatch = useDispatch();
  const actions = bindActionCreators(
    {
      ...appActions,
      ...commonActions,
      ...siteActions
    },
    dispatch
  );

  const searchShowCount = useSelector(applicationSelectors.getSearchShowCount);
  const searchTerm = useSelector(siteSelectors.getSitesSearchTerm);
  const selectedSiteId = useSelector(siteSelectors.getSelectedSiteId);
  const spinnerStatus = useSelector(siteSelectors.getDefaultLoaded);

  const [data, setData] = useState(availableSites);
  const [isDesktop, setIsDesktop] = useState(matchesMin(screen.medium));
  const [isTablet, setIsTablet] = useState(matchesMin(screen.small));
  const [expanded, setExpanded] = useState(false);
  const [spinner, setSpinner] = useState(
    availableSites.map(site => ({
      id: site.siteId,
      show: false
    }))
  );

  const activeSite = availableSites.find(
    site => site.siteId === selectedSiteId
  );
  const disabled = availableSites.length <= 1;

  const handleResize = useCallback(
    throttle(() => {
      setIsDesktop(matchesMin(screen.medium));
      setIsTablet(matchesMin(screen.small));
    }, 100),
    []
  );

  const handleClickOutside = ({ target }) => {
    const { className, tagName } = target;
    const node = siteList?.current;
    const ignoredElements = ['svg', 'use'];

    if (!node) {
      // Exit early if node is unavailable
      return false;
    }

    const ignore =
      ignoredElements.includes(tagName) ||
      constants.SITE_SWITCHER.EXCLUSIONS.some(name => className.includes(name));

    if (ignore) {
      // Exit if target is inside clear search button
      return false;
    }

    if (node && !node.contains(target)) {
      // Collapse if target is outside menu and not SVG icon
      actions.setShowOverlay(false);
      setExpanded(false);
    }
  };

  const opened = useRef(false);

  const handleDocumentClick = useCallback(
    event => {
      if (opened.current) {
        opened.current = false;
      }
      handleClickOutside(event);
    },
    [handleClickOutside]
  );

  useEffect(() => {
    if (!expanded) {
      return;
    }
    document.addEventListener('click', handleDocumentClick);

    return () => {
      document.removeEventListener('click', handleDocumentClick);
    };
  }, [expanded]);

  useEffect(() => {
    window.addEventListener('resize', handleResize);

    return () => {
      actions.setShowOverlay(false);
      window.removeEventListener('resize', handleResize);
    };
  }, []);

  const prevSpinnerStatus = useRef(spinnerStatus);

  useEffect(() => {
    if (!isEqual(prevSpinnerStatus.current, spinnerStatus)) {
      const { id, status } = spinnerStatus;

      if (status === commonConstants.IN_PROGRESS) {
        setSpinner(
          spinner.map(item =>
            item.id === id ? { ...item, show: true } : { ...item, show: false }
          )
        );
      } else {
        setSpinner(
          spinner.map(spin => ({
            ...spin,
            show: false
          }))
        );
      }
      prevSpinnerStatus.current = spinnerStatus;
    }
  }, [expanded, spinnerStatus]);

  const toggleMenu = event => {
    // event.preventDefault();
    console.log('Toggle menu called');

    if (!expanded && event?.currentTarget === document) {
      return false;
    }

    if (!disabled) {
      opened.current = true;
      const nextExpanded = !expanded;

      actions.setShowOverlay(nextExpanded);
      actions.setSitesSearch('');
      setExpanded(nextExpanded);
      setData([
        ...availableSites.filter(site => site.siteId === defaultSiteId),
        ...sort(
          availableSites.filter(site => site.siteId !== defaultSiteId),
          sortTypes.ALPHABETICAL,
          constants.SITE_SWITCHER.OPTION_KEY
        )
      ]);
    }
  };

  const secondaryLabel = () => {
    if (!dashboard) {
      return null;
    }
    const address = activeSite?.[constants.SITE_SWITCHER.SUBLABEL_KEY];

    return truncateWord(address, 40);
  };

  const optionsLabel = () => {
    const labelClass = `${baseClass}-dropdown-label`;
    const optionCount = ` (${data.length})`;

    return (
      <Text
        className={labelClass}
      >{`${content.optionsLabel}${optionCount}`}</Text>
    );
  };

  const handleSearch = term => {
    if (term.length) {
      actions.setSitesSearch(term);
    } else {
      actions.setSitesSearch('');
    }
  };

  const searchLabel = () => {
    const searchLabelClass = `${baseClass}-dropdown-search-label`;

    return (
      <Text className={searchLabelClass} modifier="bold" size="s">
        {content.siteSearchLabel}
      </Text>
    );
  };

  const searchBar = () => {
    const count = searchShowCount
      ? convertStringToNumber(searchShowCount)
      : constants.SITE_SWITCHER.SHOW_SEARCH;

    if (availableSites?.length <= count) {
      return null;
    }

    const searchClass = `${baseClass}-dropdown-search`;

    return (
      <div className={searchClass}>
        {searchLabel()}
        <Search
          onBlur={() => searchAnalyticsOnBlur(actions)}
          onChange={handleSearch}
          onFocus={() => searchAnalyticsOnFocus(actions)}
          onSearch={handleSearch}
          placeholder={content.siteSearchPlaceholder}
        />
      </div>
    );
  };

  const renderOption = option => {
    const { siteAddress, siteName } = option;

    return (
      <>
        <Text
          modifier={option.siteId === defaultSiteId ? 'bold' : null}
          size="l"
        >
          {truncateWord(siteName, 35)}
        </Text>
        <Text
          className={`${baseClass}-option-sublabel`}
          modifier="block"
          size="m"
        >
          {truncateWord(siteAddress, 40)}
        </Text>
      </>
    );
  };

  const handleOptionClick = (event, buttonValue) => {
    if (buttonValue.siteId !== selectedSiteId) {
      actions.setSitesSearch('');

      actions.switchSite({
        groupId: buttonValue?.services[0].groupId,
        path,
        siteId: buttonValue.siteId
      });
    }

    toggleMenu(event);
  };

  const getFilteredData = () => {
    let result;

    if (searchTerm) {
      result = search(searchTerm, data, constants.SITE_SWITCHER.SEARCH_KEYS);
    } else {
      result = data;
    }

    return Array.isArray(result) ? result : [];
  };

  const noSearchResults = () => {
    return (
      <Text className={`${baseClass}-no-results`} modifier="bold" size="l">
        {content.searchEmptyHeading}
      </Text>
    );
  };

  const defaultBadge = () => {
    const { defaultBadgeText, tooltipText } = content;

    const top = !isDesktop && !isTablet ? 30 : 60;
    const left = !isDesktop && !isTablet ? 60 : -150;

    return (
      <div className={`${baseClass}-default-badge`}>
        <TooltipButton
          autoOrient={false}
          enableClick
          iconFill="primary"
          iconName="information"
          label={
            <Text key="tooltip-label" className={`${baseClass}-default-text`}>
              {defaultBadgeText}
            </Text>
          }
          orientation="bottom"
          tooltipProps={{
            style: {
              top,
              left
            }
          }}
        >
          {tooltipText}
        </TooltipButton>
      </div>
    );
  };

  const handleDefaultClick = selectedDefault => {
    const { siteId } = selectedDefault;

    actions.updateDefaultSite(siteId);
  };

  const renderMakeDefaultLink = option => {
    if (defaultSiteId === option.siteId) {
      return defaultBadge();
    }

    return (
      <Button
        className={`${baseClass}-default-link`}
        modifier="link"
        onClick={() => handleDefaultClick(option)}
        size="s"
      >
        {content.defaultLinkText}
      </Button>
    );
  };

  const defaultOption = () => {
    const defaultClass = `${baseClass}-option-default`;
    const containerClass = `${baseClass}-option-container`;

    const newDefault = data.find(site => site.siteId === defaultSiteId);

    return (
      <li key={defaultSiteId}>
        <div className={containerClass}>
          <Button
            className={defaultClass}
            modifier="text"
            onClick={event => handleOptionClick(event, newDefault)}
          >
            {renderOption(newDefault)}
          </Button>
          {renderMakeDefaultLink(newDefault)}
        </div>
      </li>
    );
  };

  const dropdownOptions = () => {
    if (searchTerm && !getFilteredData().length) {
      return <li className={`${baseClass}-no-results`}>{noSearchResults()}</li>;
    }

    return getFilteredData().map((option, index) => {
      if (option.siteId === defaultSiteId) {
        return defaultOption();
      }

      const optionClass = `${baseClass}-option`;
      const textOption = renderOption(option);
      const containerClass = `${optionClass}-container`;
      const showSpinner = spinner.find(
        spinnerItem => spinnerItem.id === option.siteId
      ).show;
      const { SITE_SELECTION_CLICK } = commonConstants.default.tracking;

      return (
        <li key={index}>
          <div className={containerClass}>
            <Button
              className={optionClass}
              data-testid={`option-${index}`}
              data-tracking={clickTracking(SITE_SELECTION_CLICK)}
              fullWidth={!isTablet}
              modifier="text"
              onClick={event => handleOptionClick(event, option)}
            >
              {textOption}
            </Button>
            <div className="bve-spinner">
              <Spinner
                id={option.siteId}
                modifier="button"
                show={showSpinner}
              />
              {!showSpinner && renderMakeDefaultLink(option)}
            </div>
          </div>
        </li>
      );
    });
  };

  const customDropdown = () => {
    console.log('Custom dropdown called');
    console.log('availableSites.length:', availableSites.length);
    const siteLabel = activeSite?.[constants.SITE_SWITCHER.OPTION_KEY];

    const isMyProfileRoute = getPathname().includes('my-profile');

    console.log('checking isMyProfileRoute:', isMyProfileRoute);
    console.log('disabled', disabled);

    let selectClass = classNames({
      [`${baseClass}-select`]: !disabled,
      [`${baseClass}-select--disabled`]: disabled
    });

    if (isMyProfileRoute) {
      selectClass = `${baseClass}-select--disabled`;
    }

    const dropdownClass = `${baseClass}-dropdown`;
    const labelClass = classNames({
      [`${selectClass}-label`]: !expanded,
      [`${selectClass}-label--expanded`]: expanded
    });
    const icon = 'marker-outline';
    const { SITE_SWITCHER_CLICK } = commonConstants.default.tracking;

    return (
      <div className={baseClass}>
        <Button
          className={selectClass}
          data-testid="site-address"
          data-tracking={clickTracking(SITE_SWITCHER_CLICK)}
          disabled={disabled || isMyProfileRoute}
          fullWidth={!isTablet}
          onClick={event => toggleMenu(event)}
        >
          <Icon
            description={`${icon}-icon`}
            modifier="control"
            name={icon}
            size="xl"
          />
          <div className={`${selectClass}-heading`}>
            <Text
              className={labelClass}
              data-testid="site-label"
              modifier="bold"
              size="l"
            >
              {truncateWord(siteLabel, 35)}
            </Text>
            {secondaryLabel()}
          </div>
        </Button>
        {expanded && (
          <div className={dropdownClass}>
            {optionsLabel()}
            {searchBar()}
            <ul ref={siteList} className={`${dropdownClass}-list`}>
              {dropdownOptions()}
            </ul>
          </div>
        )}
      </div>
    );
  };

  return (
    <div className={classNames('bsd-form-field', locationClass)}>
      {customDropdown()}
    </div>
  );
};

SiteSwitcher.propTypes = {
  availableSites: arrayOf(object),
  content: object,
  dashboard: bool,
  defaultSiteId: string,
  path: string
};

SiteSwitcher.defaultProps = {
  dashboard: false
};

export default SiteSwitcher;

test case:
it.only(‘renders and shows dropdown when site switcher is clicked’, async () => {
render(

<SiteSwitcher {…props} />

);
const button = screen.getByTestId(‘site-address’);

fireEvent.click(button);
screen.debug();
expect(button).toBeInTheDocument();

await waitFor(() => {
  // expect(
  //   container.querySelector('.bve-site-switcher-dropdown')
  // ).toBeInTheDocument();
  expect(screen.getByText('Philadelphia')).toBeInTheDocument();
  expect(screen.getByText('Chicago')).toBeInTheDocument();
});
// waitFor(() => {
//   fireEvent.click(button);
//   expect(
//     document.querySelector('.bve-site-switcher-dropdown')
//   ).toBeInTheDocument();
// });

// failing test case logic
// fireEvent.click(button);
// waitFor(() => {
//   expect(document.querySelector('.bve-site-switcher-dropdown')).toBeInTheDocument();
// });

});

How Can I Optimize Collision Detection In A Dreader Game Using JavaScript?

I’m working on a JavaScript-based 2D game inspired by the dreader game concept, where players navigate a maze with moving obstacles. My current collision detection system uses a simple bounding box method, but it’s causing performance issues with multiple objects. Here’s my code snippet:

function checkCollision(player, obstacle) {
    return player.x < obstacle.x + obstacle.width &&
           player.x + player.width > obstacle.x &&
           player.y < obstacle.y + obstacle.height &&
           player.y + player.height > obstacle.y;
}

I’m running this check for each obstacle in a loop, but with 50+ obstacles, the game lags. I’ve tried reducing the frequency of checks, but it affects accuracy. How can I optimize this collision detection to improve performance while maintaining precision? Any specific algorithms or libraries (like Matter.js) that could help? Please include a minimal example if possible.

Why does MediaRecorder.ondataavailable return empty blobs (event.data.size === 0) on some devices?

I’m building a web application that captures microphone audio using MediaRecorder and sends it to the backend for processing. Everything works fine on most devices (desktop and mobile), but on some devices, the MediaRecorder.ondataavailable callback is called with an empty Blob (event.data.size === 0), making it impossible to capture usable audio.

Here’s the relevant code:

const recorder = new MediaRecorder(stream, {
  mimeType: 'audio/webm;codecs=opus',
})

recorder.ondataavailable = (event) => {
  if (event.data.size > 0) {
    chunksRef.current.push(event.data)
    dataReadyRef.current = true
  } else {
    console.warn('Empty blob update:', event.data)
  }
}

I’m calling recorder.start(1000) to get audio chunks every second. Later I use recorder.stop() to finalize the blob.

On affected devices, I frequently get the warning:

Empty blob update: Blob {size: 0, type: 'audio/webm;codecs=opus'}

My questions:
1. Why would ondataavailable be triggered with empty blobs?
2. Are there any known limitations with MediaRecorder across devices/browsers?
3. Is there a recommended fallback strategy to ensure we still get usable audio blobs?

Any help or advice would be appreciated.

I’m 32 years old and want to learn programming – where should I start? [closed]

I’m 32 years old and would really like to learn programming, but I’m not sure how to get started.

Should I buy a book or follow online courses? What are some good beginner resources?
Also, which programming language would be the best to start with if I don’t have any prior experience?

My goal is to eventually build useful things (maybe web apps or small tools), but I’m open to suggestions.

What is a good JS library for polar charts with several decorations and filled regions? [closed]

I’m trying to make a pretty specific chart in JS for a web page, and am having trouble finding a JS library whose examples show what I’m looking for. I’d like it to be a polar chart with these characteristics:

  • I’ve got angle, distance, value triplets which show up as dots on graph in color gradient based on value
  • I’d like to be able to put a certain number of small black triangle at given angle, distance
  • Some segments of the chart based on start angle, end angle, start distance, end distance should be colored in

It would look like this:

Actual chart

Hover actions would also be great, but baby steps. So the real question here is: what might be a good JS library for a chart like this?