Email have stopped being sent in OJS(open journal system) website

I am working on https://journal.iba-suk.edu.pk:8089/SIBAJournals/index.php/sjet. I am having an issue which is that emails have stopped being sent or they are no longer being sent since April 29. Therefore, we are unable to receive emails regarding new manuscript submission. We also can not receive forgot password email. I am running windows apache xamph php server. following is the code of my config.inc.php file

;;;;;;;;;;;;;;;;;;
; Email Settings ;
;;;;;;;;;;;;;;;;;;

[email]    
; Use SMTP for sending mail instead of mail()
 smtp = On

; SMTP server settings
; smtp_server = mail.example.com
; smtp_port = 25
 smtp_server = smtp.gmail.com
 smtp_port = 465

;smtp_port = 587


; Enable SMTP authentication
; Supported smtp_auth: ssl, tls (see PHPMailer SMTPSecure)
; smtp_auth = ssl
; smtp_username = username
; smtp_password = password
 smtp_auth = ssl
; smtp_auth = PLAIN
 
 smtp_secure = tls
 log_email=On
 smtp_username = *********
 smtp_password = *********
;
; Supported smtp_authtype: RAM-MD5, LOGIN, PLAIN, XOAUTH2 (see PHPMailer AuthType)
; (Leave blank to try them in that order)
; smtp_authtype =

; The following are required for smtp_authtype = XOAUTH2 (e.g. GMail OAuth)
; (See https://github.com/PHPMailer/PHPMailer/wiki/Using-Gmail-with-XOAUTH2)
; smtp_oauth_provider = Google
; smtp_oauth_email =
; smtp_oauth_clientid =
; smtp_oauth_clientsecret =
; smtp_oauth_refreshtoken =

; Enable suppressing verification of SMTP certificate in PHPMailer
; Note: this is not recommended per PHPMailer documentation
; smtp_suppress_cert_check = On

; Allow envelope sender to be specified
; (may not be possible with some server configurations)
 allow_envelope_sender = On

; Default envelope sender to use if none is specified elsewhere
 default_envelope_sender = [email protected]

; Force the default envelope sender (if present)
; This is useful if setting up a site-wide no-reply address
; The reply-to field will be set with the reply-to or from address.
; force_default_envelope_sender = Off

; Force a DMARC compliant from header (RFC5322.From)
; If any of your users have email addresses in domains not under your control
; you may need to set this to be compliant with DMARC policies published by
; those 3rd party domains.
; Setting this will move the users address into the reply-to field and the
; from field wil be rewritten with the default_envelope_sender.
; To use this you must set force_default_enveloper_sender = On and
; default_envelope_sender must be set to a valid address in a domain you own.
; force_dmarc_compliant_from = Off

; The display name to use with a DMARC compliant from header
; By default the DMARC compliant from will have an empty name but this can
; be changed by adding a text here.
; You can use '%n' to insert the users name from the original from header
; and '%s' to insert the localized sitename.
; dmarc_compliant_from_displayname = '%n via %s'

; Amount of time required between attempts to send non-editorial emails
; in seconds. This can be used to help prevent email relaying via OJS.
time_between_emails = 3600

; Maximum number of recipients that can be included in a single email
; (either as To:, Cc:, or Bcc: addresses) for a non-privileged user
max_recipients = 100

; If enabled, email addresses must be validated before login is possible.
require_validation = Off

; Maximum number of days before an unvalidated account expires and is deleted
validation_timeout = 14



 

How to fix issue with executing multiple queries in SQLite3 using PHP ADOdb?

I’m trying to execute multiple SQL queries in a single request using PHP with the ADOdb library, and I’m having trouble getting the second query to run. Here’s the code I’m using to connect to an SQLite3 database:

require_once 'ADOdb/adodb.inc.php';

// Create a connection to the SQLite3 database
$db = NewADOConnection('sqlite3');

// Database path
$databasePath = 'test.db';
$db->Connect($databasePath);

// Trying to run multiple queries
$query = "PRAGMA table_info('test'); SELECT 1;";
$result = $db->Execute($query);
echo $result;
?>

What I’m trying to do:
I want to execute two SQL queries in a single execution: PRAGMA table_info(‘test’) and SELECT 1;.

The first query works, but the second query (SELECT 1;) does not return any result.

Problem:
When I run the above code, the first query (PRAGMA table_info(‘test’)) works fine and returns the table structure, but the second query (SELECT 1) doesn’t execute, and I don’t get any output for it.

I am not sure how to handle multiple queries in a single execution using ADOdb for SQLite3.

What I’ve tried:
I attempted running both queries in a single string, but only the first query seems to be executed.

I tried using separate calls for each query, but I still couldn’t figure out a way to make it work correctly.
Environment Details:
PHP version: [Add your PHP version here, e.g., PHP 7.4]

ADOdb version: [Add the version you’re using, e.g., ADOdb 5.20]

Database: SQLite3
What I’ve tried:
I’ve attempted running both queries in a single string as shown above, but only the first query (PRAGMA table_info(‘test’)) seems to work, and the second query (SELECT 1) is ignored.

I also tried running the queries separately but still encountered the issue.

Desired Result:
I expect the following results:

The first query (PRAGMA table_info(‘test’)) should output the table structure of the test table.

The second query (SELECT 1) should output the number 1.

Environment Details:
PHP version: [Your PHP version, e.g., PHP 7.4]

ADOdb version: [Your ADOdb version, e.g., ADOdb 5.20]

Database: SQLite3

“Am I correctly setting up a Laravel REST API project with artisan commands and XAMPP database config?

Is this a good way to setup a laravel project?

composer create-project laravel/laravel [project_name]

Then, navigate into the newly created project directory:
php artisan install:api # (If prompted, feel free to answer: no)
php artisan migrate:reset

Delete the contents of the database/migrations directory.

.env settings — these may depend on your XAMPP/PhpMyAdmin configuration (e.g., port number, user, password):
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=webvill DB_USERNAME=root DB_PASSWORD=

Create a model, controller, and migration with:
php artisan make:model -mc --api [Name]

Start the development server:
php artisan serve
In routes/api.php — define your routes without the /api prefix!

All requests should use URLs like:
http://localhost:8000/api/something

I tried this method, but I’m not sure it’s working correctly.

Unable to retrieve a value after a PRAGMA statment using sqlite3

I’m trying to learn to use php interacting with db.
To connect to sqlite3 I’m using the ADOdb library.
This is the PHP snippet I’m using.


require_once 'ADOdb/adodb.inc.php';

$db = NewADOConnection('sqlite3');

$databasePath = 'test.db';

$db->Connect($databasePath);

$query = $_GET['table'];
$result = $db->Execute($query);
echo $result;
?> 

Using this script I’m able to run queries without any problem, but until I try to run a query like this,
PRAGMA table_info('test');SELECT 1;,
I’m unable to retrieve the value of the second query. Could anyone tell me why? Thanks in advance.

How does this popunder technique work and how to prevent it?

The following js code is given openly by an ad company (which I’d rather not name, though it’s not difficult to find). I just formatted it for readability. It is supposed to allow the website owner to display popunder ads.

I want to know how it works, but I’m not a js expert and this is a bit over my head, though I have basic js knowledge. Can someone please break down how it works step by step, when using firefox for example, and explain how it can circumvent adblock filters and browser settings (to block popups) ?

From the user point of view, is it possible, using a browser extension (adblock, …), or native settings, to block such popunders, or does it use a bug in the browsers that need fixing at the browser level, or is it just the web protocols that currently allow such behavior ?

(function() {

    //version 6.0.0
  
    var adConfig = {
      "ads_host": "a.pemsrv.com",
      "syndication_host": "s.pemsrv.com",
      "idzone": 1234567,
      "popup_fallback": false,
      "popup_force": false,
      "chrome_enabled": true,
      "new_tab": false,
      "frequency_period": 1,
      "frequency_count": 1,
      "trigger_method": 3,
      "trigger_class": "",
      "trigger_delay": 0,
      "capping_enabled": true,
      "tcf_enabled": true,
      "only_inline": false,
      "tags": "cartoon"
    };
  
    window.document.querySelectorAll || (document.querySelectorAll = document.body.querySelectorAll = Object.querySelectorAll = function(e, o, t, i, n) {
      var r = document,
        a = r.createStyleSheet();
      for (n = r.all, o = [], t = (e = e.replace(/[forb/gi, "[htmlFor").split(",")).length; t--;) {
        for (a.addRule(e[t], "k:v"), i = n.length; i--;) n[i].currentStyle.k && o.push(n[i]);
        a.removeRule(0)
      }
      return o
    });
    var popMagic = {
      version: 6,
      cookie_name: "",
      url: "",
      config: {},
      open_count: 0,
      top: null,
      browser: null,
      venor_loaded: !1,
      venor: !1,
      tcfData: null,
      configTpl: {
        ads_host: "",
        syndication_host: "",
        idzone: "",
        frequency_period: 720,
        frequency_count: 1,
        trigger_method: 1,
        trigger_class: "",
        popup_force: !1,
        popup_fallback: !1,
        chrome_enabled: !0,
        new_tab: !1,
        cat: "",
        tags: "",
        el: "",
        sub: "",
        sub2: "",
        sub3: "",
        only_inline: !1,
        trigger_delay: 0,
        capping_enabled: !0,
        tcf_enabled: !1,
        cookieconsent: !0,
        should_fire: function() {
          return !0
        }
      },
      init: function(e) {
        if (void 0 !== e.idzone && e.idzone) {
          void 0 === e.customTargeting && (e.customTargeting = []), window.customTargeting = e.customTargeting || null;
          var o = Object.keys(e.customTargeting).filter((function(e) {
            return e.search("ex_") >= 0
          }));
          for (var t in o.length && o.forEach(function(e) {
              return this.configTpl[e] = null
            }.bind(this)), this.configTpl) Object.prototype.hasOwnProperty.call(this.configTpl, t) && (void 0 !== e[t] ? this.config[t] = e[t] : this.config[t] = this.configTpl[t]);
          if (void 0 !== this.config.idzone && "" !== this.config.idzone) {
            !0 !== this.config.only_inline && this.loadHosted();
            var i = this;
            this.checkTCFConsent((function() {
              "complete" === document.readyState ? i.preparePopWait() : i.addEventToElement(window, "load", i.preparePop)
            }))
          }
        }
      },
      getCountFromCookie: function() {
        if (!this.config.cookieconsent) return 0;
        var e = popMagic.getCookie(popMagic.cookie_name),
          o = void 0 === e ? 0 : parseInt(e);
        return isNaN(o) && (o = 0), o
      },
      getLastOpenedTimeFromCookie: function() {
        var e = popMagic.getCookie(popMagic.cookie_name),
          o = null;
        if (void 0 !== e) {
          var t = e.split(";")[1];
          o = t > 0 ? parseInt(t) : 0
        }
        return isNaN(o) && (o = null), o
      },
      shouldShow: function() {
        if (!popMagic.config.capping_enabled) {
          var e = !0,
            o = popMagic.config.should_fire;
          try {
            "function" == typeof o && (e = Boolean(o()))
          } catch (e) {
            console.error("Error executing should fire callback function:", e)
          }
          return e && 0 === popMagic.open_count
        }
        if (popMagic.open_count >= popMagic.config.frequency_count) return !1;
        var t = popMagic.getCountFromCookie(),
          i = popMagic.getLastOpenedTimeFromCookie(),
          n = Math.floor(Date.now() / 1e3),
          r = i + popMagic.config.trigger_delay;
        return !(i && r > n) && (popMagic.open_count = t, !(t >= popMagic.config.frequency_count))
      },
      venorShouldShow: function() {
        return popMagic.venor_loaded && "0" === popMagic.venor
      },
      setAsOpened: function(e) {
        var o = e ? e.target || e.srcElement : null,
          t = {
            id: "",
            tagName: "",
            classes: "",
            text: "",
            href: "",
            elm: ""
          };
        void 0 !== o && null != o && (t = {
          id: void 0 !== o.id && null != o.id ? o.id : "",
          tagName: void 0 !== o.tagName && null != o.tagName ? o.tagName : "",
          classes: void 0 !== o.classList && null != o.classList ? o.classList : "",
          text: void 0 !== o.outerText && null != o.outerText ? o.outerText : "",
          href: void 0 !== o.href && null != o.href ? o.href : "",
          elm: o
        });
        var i = new CustomEvent("creativeDisplayed-" + popMagic.config.idzone, {
          detail: t
        });
        if (document.dispatchEvent(i), popMagic.config.capping_enabled) {
          var n = 1;
          n = 0 !== popMagic.open_count ? popMagic.open_count + 1 : popMagic.getCountFromCookie() + 1;
          var r = Math.floor(Date.now() / 1e3);
          popMagic.config.cookieconsent && popMagic.setCookie(popMagic.cookie_name, n + ";" + r, popMagic.config.frequency_period)
        } else ++popMagic.open_count
      },
      loadHosted: function() {
        var e = document.createElement("script");
        for (var o in e.type = "application/javascript", e.async = !0, e.src = "//" + this.config.ads_host + "/popunder1000.js", e.id = "popmagicldr", this.config) Object.prototype.hasOwnProperty.call(this.config, o) && "ads_host" !== o && "syndication_host" !== o && e.setAttribute("data-exo-" + o, this.config[o]);
        var t = document.getElementsByTagName("body").item(0);
        t.firstChild ? t.insertBefore(e, t.firstChild) : t.appendChild(e)
      },
      preparePopWait: function() {
        setTimeout(popMagic.preparePop, 400)
      },
      preparePop: function() {
        if ("object" != typeof exoJsPop101 || !Object.prototype.hasOwnProperty.call(exoJsPop101, "add")) {
          if (popMagic.top = self, popMagic.top !== self) try {
            top.document.location.toString() && (popMagic.top = top)
          } catch (e) {}
          if (popMagic.cookie_name = "zone-cap-" + popMagic.config.idzone, popMagic.config.capping_enabled || (document.cookie = popMagic.cookie_name + "=;expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/"), popMagic.shouldShow()) {
            var e = new XMLHttpRequest;
            e.onreadystatechange = function() {
              e.readyState == XMLHttpRequest.DONE && (popMagic.venor_loaded = !0, 200 == e.status ? popMagic.venor = e.responseText : popMagic.venor = "0")
            };
            var o = "https:" !== document.location.protocol && "http:" !== document.location.protocol ? "https:" : document.location.protocol;
            e.open("GET", o + "//" + popMagic.config.syndication_host + "/venor.php", !0);
            try {
              e.send()
            } catch (e) {
              popMagic.venor_loaded = !0
            }
          }
          if (popMagic.buildUrl(), popMagic.browser = popMagic.browserDetector.getBrowserInfo(), popMagic.config.chrome_enabled || !popMagic.browser.isChrome) {
            var t = popMagic.getPopMethod(popMagic.browser);
            popMagic.addEvent("click", t)
          }
        }
      },
      getPopMethod: function(e) {
        return popMagic.config.popup_force || popMagic.config.popup_fallback && e.isChrome && e.version >= 68 && !e.isMobile ? popMagic.methods.popup : e.isMobile ? popMagic.methods.default : e.isChrome ? popMagic.methods.chromeTab : popMagic.methods.default
      },
      checkTCFConsent: function(e) {
        if (this.config.tcf_enabled && "function" == typeof window.__tcfapi) {
          var o = this;
          window.__tcfapi("addEventListener", 2, (function(t, i) {
            i && (o.tcfData = t, "tcloaded" !== t.eventStatus && "useractioncomplete" !== t.eventStatus || (window.__tcfapi("removeEventListener", 2, (function() {}), t.listenerId), e()))
          }))
        } else e()
      },
      buildUrl: function() {
        var e, o = "https:" !== document.location.protocol && "http:" !== document.location.protocol ? "https:" : document.location.protocol,
          t = top === self ? document.URL : document.referrer,
          i = {
            type: "inline",
            name: "popMagic",
            ver: this.version
          },
          n = "";
        customTargeting && Object.keys(customTargeting).length && ("object" == typeof customTargeting ? Object.keys(customTargeting) : customTargeting).forEach((function(o) {
          "object" == typeof customTargeting ? e = customTargeting[o] : Array.isArray(customTargeting) && (e = scriptEl.getAttribute(o));
          var t = o.replace("data-exo-", "");
          n += "&" + t + "=" + e
        }));
        var r = this.tcfData && this.tcfData.gdprApplies && !0 === this.tcfData.gdprApplies ? 1 : 0;
        this.url = o + "//" + this.config.syndication_host + "/v1/link.php?cat=" + this.config.cat + "&idzone=" + this.config.idzone + "&type=8&p=" + encodeURIComponent(t) + "&sub=" + this.config.sub + ("" !== this.config.sub2 ? "&sub2=" + this.config.sub2 : "") + ("" !== this.config.sub3 ? "&sub3=" + this.config.sub3 : "") + "&block=1&el=" + this.config.el + "&tags=" + this.config.tags + "&scr_info=" + function(e) {
          var o = e.type + "|" + e.name + "|" + e.ver;
          return encodeURIComponent(btoa(o))
        }(i) + n + "&gdpr=" + r + "&cb=" + Math.floor(1e9 * Math.random()), this.tcfData && this.tcfData.tcString ? this.url += "&gdpr_consent=" + encodeURIComponent(this.tcfData.tcString) : this.url += "&cookieconsent=" + this.config.cookieconsent
      },
      addEventToElement: function(e, o, t) {
        e.addEventListener ? e.addEventListener(o, t, !1) : e.attachEvent ? (e["e" + o + t] = t, e[o + t] = function() {
          e["e" + o + t](window.event)
        }, e.attachEvent("on" + o, e[o + t])) : e["on" + o] = e["e" + o + t]
      },
      getTriggerClasses: function() {
        var e, o = []; - 1 === popMagic.config.trigger_class.indexOf(",") ? e = popMagic.config.trigger_class.split(" ") : e = popMagic.config.trigger_class.replace(/s/g, "").split(",");
        for (var t = 0; t < e.length; t++) "" !== e[t] && o.push("." + e[t]);
        return o
      },
      addEvent: function(e, o) {
        var t;
        if ("3" != popMagic.config.trigger_method)
          if ("2" != popMagic.config.trigger_method || "" == popMagic.config.trigger_method)
            if ("4" != popMagic.config.trigger_method || "" == popMagic.config.trigger_method) popMagic.addEventToElement(document, e, o);
            else {
              var n = popMagic.getTriggerClasses();
              popMagic.addEventToElement(document, e, (function(e) {
                n.some((function(o) {
                  return null !== e.target.closest(o)
                })) || o.call(e.target, e)
              }))
            }
        else {
          var r = popMagic.getTriggerClasses();
          for (t = document.querySelectorAll(r.join(", ")), i = 0; i < t.length; i++) popMagic.addEventToElement(t[i], e, o)
        } else
          for (t = document.querySelectorAll("a"), i = 0; i < t.length; i++) popMagic.addEventToElement(t[i], e, o)
      },
      setCookie: function(e, o, t) {
        if (!this.config.cookieconsent) return !1;
        t = parseInt(t, 10);
        var i = new Date;
        i.setMinutes(i.getMinutes() + parseInt(t));
        var n = encodeURIComponent(o) + "; expires=" + i.toUTCString() + "; path=/";
        document.cookie = e + "=" + n
      },
      getCookie: function(e) {
        if (!this.config.cookieconsent) return !1;
        var o, t, i, n = document.cookie.split(";");
        for (o = 0; o < n.length; o++)
          if (t = n[o].substr(0, n[o].indexOf("=")), i = n[o].substr(n[o].indexOf("=") + 1), (t = t.replace(/^s+|s+$/g, "")) === e) return decodeURIComponent(i)
      },
      randStr: function(e, o) {
        for (var t = "", i = o || "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", n = 0; n < e; n++) t += i.charAt(Math.floor(Math.random() * i.length));
        return t
      },
      isValidUserEvent: function(e) {
        return !(!("isTrusted" in e) || !e.isTrusted || "ie" === popMagic.browser.name || "safari" === popMagic.browser.name) || 0 != e.screenX && 0 != e.screenY
      },
      isValidHref: function(e) {
        if (void 0 === e || "" == e) return !1;
        return !/s?javascripts?:/i.test(e)
      },
      findLinkToOpen: function(e) {
        var o = e,
          t = !1;
        try {
          for (var i = 0; i < 20 && !o.getAttribute("href") && o !== document && "html" !== o.nodeName.toLowerCase();) o = o.parentNode, i++;
          var n = o.getAttribute("target");
          n && -1 !== n.indexOf("_blank") || (t = o.getAttribute("href"))
        } catch (e) {}
        return popMagic.isValidHref(t) || (t = !1), t || window.location.href
      },
      getPuId: function() {
        return "ok_" + Math.floor(89999999 * Math.random() + 1e7)
      },
      browserDetector: {
        browserDefinitions: [
          ["firefox", /Firefox/([0-9.]+)(?:s|$)/],
          ["opera", /Opera/([0-9.]+)(?:s|$)/],
          ["opera", /OPR/([0-9.]+)(:?s|$)$/],
          ["edge", /Edg(?:e|)/([0-9._]+)/],
          ["ie", /Trident/7.0.*rv:([0-9.]+)).*Gecko$/],
          ["ie", /MSIEs([0-9.]+);.*Trident/[4-7].0/],
          ["ie", /MSIEs(7.0)/],
          ["safari", /Version/([0-9._]+).*Safari/],
          ["chrome", /(?!Chrom.*Edg(?:e|))Chrom(?:e|ium)/([0-9.]+)(:?s|$)/],
          ["chrome", /(?!Chrom.*OPR)Chrom(?:e|ium)/([0-9.]+)(:?s|$)/],
          ["bb10", /BB10;sTouch.*Version/([0-9.]+)/],
          ["android", /Androids([0-9.]+)/],
          ["ios", /Version/([0-9._]+).*Mobile.*Safari.*/],
          ["yandexbrowser", /YaBrowser/([0-9._]+)/],
          ["crios", /CriOS/([0-9.]+)(:?s|$)/]
        ],
        isChromeOrChromium: function() {
          var e = window.navigator,
            o = (e.userAgent || "").toLowerCase(),
            t = e.vendor || "";
          if (-1 !== o.indexOf("crios")) return !0;
          if (e.userAgentData && Array.isArray(e.userAgentData.brands) && e.userAgentData.brands.length > 0) {
            var i = e.userAgentData.brands,
              n = i.some((function(e) {
                return "Google Chrome" === e.brand
              })),
              r = i.some((function(e) {
                return "Chromium" === e.brand
              })) && 2 === i.length;
            return n || r
          }
          var a = !!window.chrome,
            c = -1 !== o.indexOf("edg"),
            p = !!window.opr || -1 !== o.indexOf("opr"),
            s = !(!e.brave || !e.brave.isBrave),
            g = -1 !== o.indexOf("vivaldi"),
            l = -1 !== o.indexOf("yabrowser"),
            d = -1 !== o.indexOf("samsungbrowser"),
            u = -1 !== o.indexOf("ucbrowser");
          return a && "Google Inc." === t && !c && !p && !s && !g && !l && !d && !u
        },
        getBrowserInfo: function() {
          var e = window.navigator.userAgent,
            o = {
              name: "other",
              version: "1.0",
              versionNumber: 1,
              isChrome: this.isChromeOrChromium(),
              isMobile: !!e.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile|WebOS|Windows Phone/i)
            };
          for (var t in this.browserDefinitions) {
            var i = this.browserDefinitions[t];
            if (i[1].test(e)) {
              var n = i[1].exec(e),
                r = n && n[1].split(/[._]/).slice(0, 3),
                a = Array.prototype.slice.call(r, 1).join("") || "0";
              r && r.length < 3 && Array.prototype.push.apply(r, 1 === r.length ? [0, 0] : [0]), o.name = i[0], o.version = r.join("."), o.versionNumber = parseFloat(r[0] + "." + a);
              break
            }
          }
          return o
        }
      },
      methods: {
        default: function(e) {
          if (!popMagic.shouldShow() || !popMagic.venorShouldShow() || !popMagic.isValidUserEvent(e)) return !0;
          var o = e.target || e.srcElement,
            t = popMagic.findLinkToOpen(o);
          return window.open(t, "_blank"), popMagic.setAsOpened(e), popMagic.top.document.location = popMagic.url, void 0 !== e.preventDefault && (e.preventDefault(), e.stopPropagation()), !0
        },
        chromeTab: function(e) {
          if (!popMagic.shouldShow() || !popMagic.venorShouldShow() || !popMagic.isValidUserEvent(e)) return !0;
          if (void 0 === e.preventDefault) return !0;
          e.preventDefault(), e.stopPropagation();
          var o = top.window.document.createElement("a"),
            t = e.target || e.srcElement;
          o.href = popMagic.findLinkToOpen(t), document.getElementsByTagName("body")[0].appendChild(o);
          var i = new MouseEvent("click", {
            bubbles: !0,
            cancelable: !0,
            view: window,
            screenX: 0,
            screenY: 0,
            clientX: 0,
            clientY: 0,
            ctrlKey: !0,
            altKey: !1,
            shiftKey: !1,
            metaKey: !0,
            button: 0
          });
          i.preventDefault = void 0, o.dispatchEvent(i), o.parentNode.removeChild(o), window.open(popMagic.url, "_self"), popMagic.setAsOpened(e)
        },
        popup: function(e) {
          if (!popMagic.shouldShow() || !popMagic.venorShouldShow() || !popMagic.isValidUserEvent(e)) return !0;
          var o = "";
          if (popMagic.config.popup_fallback && !popMagic.config.popup_force) {
            var t = Math.max(Math.round(.8 * window.innerHeight), 300);
            o = "menubar=1,resizable=1,width=" + Math.max(Math.round(.7 * window.innerWidth), 300) + ",height=" + t + ",top=" + (window.screenY + 100) + ",left=" + (window.screenX + 100)
          }
          var i = document.location.href,
            n = window.open(i, popMagic.getPuId(), o);
          setTimeout((function() {
            n.location.href = popMagic.url
          }), 200), popMagic.setAsOpened(e), void 0 !== e.preventDefault && (e.preventDefault(), e.stopPropagation())
        }
      }
    };
    popMagic.init(adConfig);
  })();

tanstack table selected rows incorrect order after sorting table by col

I have a react.js Table.js file which uses TanStack table. I need it to do three things:

  1. allow drag-and-drop row reordering
  2. allow column sort toggling (ascending/descending)
  3. always retrieve 100% accurate order selected rows

https://codesandbox.io/p/devbox/row-reorder-working-forked-2dm95l?workspaceId=ws_F2QxELJDBKDDSAxdPEPbQt

Currently, 1. and 2. both work, but the selected row order is always incorrect if the table has been sorted by a col header.

Here is a visual example of the working row drag & drop:
enter image description here

But when I click a col header to sort the table as ascending/descending:
enter image description here

Now the ‘selected rows’ output is not accurate
enter image description here

How can I fix my Table.js file from my codeandbox so that I can print an accurate table row order even when a col sort is toggled on?

How to stream PCM audio from AWS Bedrock Nova Sonic to a web browser without noise artifacts?

I’m building a voice assistant application using AWS Bedrock’s Nova Sonic API. I have two implementations:

A Streamlit app that works perfectly using PyAudio to play the PCM audio chunks directly:

async def play_audio(self):
"""Play audio responses."""
p = pyaudio.PyAudio()
stream = p.open(
    format=FORMAT,
    channels=CHANNELS,
    rate=OUTPUT_SAMPLE_RATE,
    output=True
)

try:
    while self.is_active:
        audio_data = await self.audio_queue.get()
        stream.write(audio_data)
except Exception as e:
    print(f"Error playing audio: {e}")
finally:
    stream.stop_stream()
    stream.close()
    p.terminate()

A web application using Flask-SocketIO to stream audio to a browser. Here’s my backend code:

elif 'audioOutput' in json_data['event']:
audio_content = json_data['event']['audioOutput']['content']
pcm_bytes = base64.b64decode(audio_content)

# Convert to WAV with proper headers
wav_buffer = io.BytesIO()
with wave.open(wav_buffer, 'wb') as wf:
    wf.setnchannels(1)
    wf.setsampwidth(2)
    wf.setframerate(24000)
    wf.writeframes(pcm_bytes)

wav_bytes = wav_buffer.getvalue()
converted_audio = base64.b64encode(wav_bytes).decode('utf-8')

socketio.emit('tts_response', {'output': converted_audio,'isLastChunk': False}, namespace='/agent-chat')

And my frontend JavaScript code:

const playText = (data) => {
if (!data?.output || data.output === "[DONE]") return;
if (data.output._placeholder) return;

if (data.format === "pcm") {
  const pcmData = atob(data.output);
  const buffer = audioContextRef.current.createBuffer(
    1,
    pcmData.length / 2,
    24000
  );
  const nowBuffering = buffer.getChannelData(0);

  // Convert PCM data to float32
  for (let i = 0; i < pcmData.length / 2; i++) {
    const sample =
      pcmData.charCodeAt(i * 2) | (pcmData.charCodeAt(i * 2 + 1) << 8);
    // Convert from int16 to float32
    nowBuffering[i] = (sample < 32768 ? sample : sample - 65536) / 32768.0;
  }

  // Play immediately
  const source = audioContextRef.current.createBufferSource();
  source.buffer = buffer;
  source.connect(audioContextRef.current.destination);
  source.start();
  return;
}

let wavBytes;

// Handle different audio formats
if (
  data.output instanceof ArrayBuffer ||
  data.output instanceof Uint8Array
) {
  wavBytes = new Uint8Array(data.output);
} else if (typeof data.output === "string") {
  try {
    const binaryString = atob(data.output);
    wavBytes = new Uint8Array(binaryString.length);
    for (let i = 0; i < binaryString.length; i++) {
      wavBytes[i] = binaryString.charCodeAt(i);
    }
  } catch (error) {
    console.error("Base64 decoding failed:", error);
    return;
  }
} else {
  console.warn("Unhandled data format:", data);
  return;
}

// Check if this is the final complete audio
const isComplete = data.isComplete === true;

// Decode and play audio
audioContextRef.current.decodeAudioData(
  wavBytes.buffer,
  (buffer) => {
    // If this is the complete audio and we have chunks in the queue,
    // we can clear the queue and play the complete audio instead
    if (isComplete && audioBufferQueueRef.current.length > 0) {
      audioBufferQueueRef.current = [];
      if (activeAudioSourceRef.current) {
        activeAudioSourceRef.current.stop();
        activeAudioSourceRef.current = null;
      }
      isPlayingRef.current = false;
    }

    audioBufferQueueRef.current.push(buffer);
    if (!isPlayingRef.current) {
      playNextInQueue();
    }
  },
  (error) => console.error("decodeAudioData failed", error)
);};

The Problem: When streaming audio to the web browser, I hear “tak tak” noise artifacts between audio chunks. I’ve tried several approaches:

  • Converting each PCM chunk to WAV and sending individually
  • Buffering multiple chunks and sending larger chunks
  • Adding crossfade between chunks
  • Sending raw PCM data directly

None of these approaches eliminate the noise. The Streamlit version works perfectly with PyAudio, but I need this to work in a web browser.

Question: How can I stream PCM audio from AWS Bedrock Nova Sonic to a web browser without the noise artifacts, i just wnat to play the streaming audio smoothly witohut any noise?

Trying to Make a Review System in FireStore but having Permission Issues

I’m trying to develop a review system.
However, I’ve been getting a permission error:

Uncaught (in promise) FirebaseError: Missing or Insufficient Permissions error.

These aren’t my full rules, but just the ones about the reviews.
The permission doc is being created, but the review doc isn’t, so I’m not quite sure what’s wrong.

My code:

  onSnapshot(qToMe, snap => {
    snap.forEach(d => { confirmedCache[d.id] = d.data(); });
    renderConfirmed();
    checkForReviewPrompt();
  });
  onSnapshot(qFromMe, snap => {
    snap.forEach(d => { confirmedCache[d.id] = d.data(); });
    renderConfirmed();
    checkForReviewPrompt();
  });

  // ——— Rating Modal Logic ———
  const ratingModal   = document.getElementById('ratingModal');
  const ratingForm    = document.getElementById('ratingForm');
  const ratingCancel  = document.getElementById('ratingCancel');
  const messagesModal = document.getElementById('messagesModal');
  const closeMessages = document.getElementById('closeMessagesModal');
  let pendingReview   = null;
  let reviewQueue     = false;

  // When chat modal closes, trigger review if queued
  closeMessages?.addEventListener('click', () => {
    if (reviewQueue) {
      ratingModal.classList.remove('hidden');
      reviewQueue = false;
    }
  });

  function checkForReviewPrompt() {
    if (!ratingModal) return;
    const today = new Date();
    const yesterday = new Date(today);
    yesterday.setDate(today.getDate() - 1);
    const cutoff = yesterday.toISOString().slice(0,10);

    for (const [meetupId, data] of Object.entries(confirmedCache)) {
      if (data.date > cutoff) continue;
      const otherUid = data.participants.find(uid => uid !== user.uid);
      if (!otherUid) continue;

      const key = `${meetupId}_${user.uid}`;
      window._reviewChecked = window._reviewChecked || {};
      if (window._reviewChecked[key]) continue;
      window._reviewChecked[key] = true;

      promptReviewIfNeeded(meetupId, otherUid);
      return;
    }
  }

  async function promptReviewIfNeeded(meetupId, otherUid) {
    const q = query(
      collection(db, 'meetupReviews'),
      where('meetupId', '==', meetupId),
      where('reviewer', '==', user.uid)
    );
    const res = await getDocs(q);
    if (res.empty && ratingModal) {
      pendingReview = { meetupId, otherUid };

      if (messagesModal && !messagesModal.classList.contains('hidden')) {
        reviewQueue = true;
      } else {
        ratingModal.classList.remove('hidden');
      }
    }
  }

  ratingCancel?.addEventListener('click', () => {
    ratingModal.classList.add('hidden');
    pendingReview = null;
  });

  ratingForm?.addEventListener('submit', async e => {
    e.preventDefault();
    if (!pendingReview) return;

    const form = new FormData(ratingForm);
    const rating = Number(form.get('rating'));
    const comment = document.getElementById('ratingComment').value.trim();
    if (!rating || rating < 1 || rating > 5) {
      return alert('Select 1–5 stars.');
    }

    // 1) Create permission doc
    const permId = `${pendingReview.meetupId}_${user.uid}`;
    await setDoc(doc(db, 'meetupReviewPermissions', permId), {
      meetupId: pendingReview.meetupId,
      reviewer: user.uid,
      reviewee: pendingReview.otherUid,
      created: Timestamp.now()
    });

    // 2) Now create the review
    await addDoc(collection(db, 'meetupReviews'), {
      meetupId: pendingReview.meetupId,
      reviewer: user.uid,
      reviewee: pendingReview.otherUid,
      rating,
      comment,
      timestamp: Timestamp.now()
    });

    ratingModal.classList.add('hidden');
    pendingReview = null;
    alert('Thanks for your feedback!');
  });

});

And here are my Firestore rules:

   rules_version = '2';
    service cloud.firestore {
      match /databases/{database}/documents {
    
        // ——— meetupReviewPermissions collection ———
        match /meetupReviewPermissions/{permId} {
          // The client now writes these on acceptance
          allow create: if request.auth != null
                        && request.auth.uid == request.resource.data.reviewer
                        && request.resource.data.reviewer != request.resource.data.reviewee
                        && request.resource.data.keys().hasAll([
                             "meetupId","reviewer","reviewee","created"
                           ])
                        && get(/databases/$(database)/documents/meetupRequests/$(request.resource.data.meetupId)).data.status == "accepted"
                        && request.auth.uid in get(/databases/$(database)/documents/meetupRequests/$(request.resource.data.meetupId)).data.participants;
          allow read: if request.auth != null
                      && (request.auth.uid == resource.data.reviewer
                          || request.auth.uid == resource.data.reviewee);
          allow update, delete: if false;
        }
    
    // ——— meetupReviews collection ———
    match /meetupReviews/{reviewId} {
      allow read: if true;
    
      allow create: if request.auth != null
                    && request.auth.uid == request.resource.data.reviewer
                    && request.resource.data.reviewer != request.resource.data.reviewee
                    && request.resource.data.keys().hasAll([
                         "meetupId","reviewer","reviewee",
                         "rating","comment","timestamp"
                       ])
                    && request.resource.data.rating >= 1
                    && request.resource.data.rating <= 5
                    && exists("meetupReviewPermissions/" + request.resource.data.meetupId + "_" + request.auth.uid);
    
      allow update, delete: if false;
    }
    }
    }

VM1186:5 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading ‘call’) Emscripten

I’ve got a really difficult problem.
I have built libusb with emscripten according to https://web.dev/articles/porting-libusb-to-webusb#build_system_and_the_first_test.
That all works great, with the test program provided, only problem I have now is I am trying to use this library with my own custom code but it is not working.

Very rough code
https://gitlab.com/-/snippets/4844863
I am compiling with this command:

    EMCC_DEBUG=1 emcc main.c ./libusb/build/install-web/lib/libusb-1.0.a 

  -I./libusb/build/install-web/include 
  -L./libusb/build/install-web/lib 
  -s USE_PTHREADS=1 
  -s PTHREAD_POOL_SIZE=4 
  -s PROXY_TO_PTHREAD=1 
  -s ALLOW_MEMORY_GROWTH=1 
  -s ASYNCIFY=1 
  -s ENVIRONMENT=web,worker 
  -sASSERTIONS=2 
  --bind 
  -g3 
  -o main.html

Then I am hosting the file, using

const http = require('http');
const fs = require('fs');
const path = require('path');

http.createServer((req, res) => {
  const filePath = path.join(__dirname, req.url === '/' ? '/main.html' : req.url);

  fs.stat(filePath, (err, stats) => {
    if (err || !stats.isFile()) {
      res.writeHead(404, { 'Content-Type': 'text/plain' });
      res.end('404 Not Found');
      return;
    }

    const ext = path.extname(filePath);
    const contentType =
      ext === '.js' ? 'application/javascript' :
      ext === '.wasm' ? 'application/wasm' :
      ext === '.html' ? 'text/html' :
      'application/octet-stream';

    res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
    res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
    res.writeHead(200, { 'Content-Type': contentType });

    fs.createReadStream(filePath).pipe(res);
  });
}).listen(8080, () => console.log('http://localhost:8080'));

When I visit the site, I get this error in chrome:

Hello, World!
(index):146 Found device 0451:e012
(index):146 Tkaksdf
VM1186:5 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'call')
    at methodCaller<(int) => emscripten::val> (eval at __emval_get_method_caller (http://localhost:8080/main.js:5144:29), <anonymous>:5:17)
    at __emval_call_method (http://localhost:8080/main.js:5015:14)
    at imports.<computed> (http://localhost:8080/main.js:4747:24)
    at main.wasm.emscripten::val emscripten::val::internalCall<(emscripten::internal::EM_METHOD_CALLER_KIND)0, emscripten::val, emscripten::val emscripten::val::call<emscripten::val, int&>(char const*, int&) const::'lambda'(emscripten::internal::_EM_METHOD_CALLER*, emscripten::_EM_VAL*, emscripten::internal::_EM_DESTRUCTORS**, void const*), int&>(emscripten::val emscripten::val::call<emscripten::val, int&>(char const*, int&) const::'lambda'(emscripten::internal::_EM_METHOD_CALLER*, emscripten::_EM_VAL*, emscripten::internal::_EM_DESTRUCTORS**, void const*), int&) const (http://localhost:8080/main.wasm:wasm-function[227]:0x264dd)
    at main.wasm.int (anonymous namespace)::CachedDevice::awaitOnMain<int&>(char const*, int&) const::'lambda'()::operator()() const (http://localhost:8080/main.wasm:wasm-function[116]:0x142a7)
    at main.wasm.(anonymous namespace)::em_set_configuration(libusb_device_handle*, int) (http://localhost:8080/main.wasm:wasm-function[114]:0x13a60)
    at main.wasm.libusb_set_configuration (http://localhost:8080/main.wasm:wasm-function[66]:0x7244)
    at main.wasm.usb_get_device (http://localhost:8080/main.wasm:wasm-function[48]:0x19e1)
    at main.wasm.__original_main (http://localhost:8080/main.wasm:wasm-function[49]:0x29c9)
    at main.wasm.main (http://localhost:8080/main.wasm:wasm-function[50]:0x2d2c)

No sure why, but it stops working in the function libusb_set_configuration
just wondering if anyone has any experience with this or could help guide me on how to debug this type of stuff. Thanks.

How can I play sound with Toast in React?

I need to play a Sound together with a Toast in React. However, the Sound does not play if there is no user interaction in JavaScript. Is there a way to solve this?
This is my code.

const [audioRef] = useState(new Audio("https://firebasestorage.googleapis.com/v0/b/deligo-9f8b3.firebasestorage.app/o/audio%2FOrder_Alert.mp3?alt=media&token=c62014d6-3f2a-4cb9-a4cc-8109a53536d5"));

useEffect(() => {
  audioRef.volume = 0.8;
  audioRef.loop = true;
}, [audioRef]);

const action = () => {
  const button = document.createElement("button");
  button.style.display = "none";
  document.body.appendChild(button);
  button.addEventListener("click", () => {
    audioRef.play().catch(console.error);
  });
  button.click();
  document.body.removeChild(button);

  toast.success("Hi, there. New order just landed!", {
    position: "bottom-right", 
    autoClose: 5000,
    className: "custom-toast",
    progressClassName: "!bg-gradient-to-r from-blue-500 to-purple-500",
    icon: <img src="https://res.cloudinary.com/djjovgwyk/image/upload/v1739515599/photo_2025-02-14_08-37-33_fyib4y.png" />,
  });
}

Loop thru json file for specific data value in NodeJS [duplicate]

I am trying to loop thru a JSON file and only print out a specific value.

This is my example JSON data and I am wanting to print of every market_hash_name

{
"items": [
    { "market_hash_name": "sg-553-danger-close", "exterior": "factory new" },
    { "market_hash_name": "famas-commemoration", "exterior": "field-tested" },
    { "market_hash_name": "sawed-off-spirit-board", "exterior": "well-worn" }
  ]
}

This is my app.js file. I am just stuck on how to print the value I am after.

import { readFile } from 'fs/promises';

const data = JSON.parse(await readFile("item_names.json", "utf8"));

console.log(data)

Expected result

market_has_name: 'sg-553-danger-close'
market_has_name: 'famas-commemoration'
market_has_name: 'sawed-off-spirit-board'

Is there a way to make Google Chrome open links with certain protocols in a new window?

I am using Google Chrome and Google Business suite for my email. I have registered a custom protocol handler for all mailto links in the javascript console from the business suite page using the following code.

navigator.registerProtocolHandler("mailto", 
                                 "https://mail.google.com/mail/u/1/?extsrc=mailto&url=%s", 
                                 "Business Email");

This works just fine as far as the protocol handling is concerned. When a link is clicked, a composition window is opened using the GSuite email.

But, when I click a mailto link, the tab that is currently in use is navigated away from in favor of the composition window. I’d greatly prefer if mailto links opened in a new window and left the tab containing the link unchanged.

Is there a Chrome setting or a piece of code I can run that will force Chrome to always open mailto links in a new window?

I know I can middle click or right click and choose new window, but I’m hoping there’s a solution that makes it automatic.

After five successful POSTs, subsequent requests return 500 with missing CORS headers in production (Vue 2 + Axios + Java/Springboot))

I’m working with Vue 2 and Axios; in production the browser shows “No Access-Control-Allow-Origin” after the sixth or tenth POST request to /api/patients, and the server returns 500 and the user needs to relogging. Locally I fired fifteen consecutive requests—both from the UI and via Postman—without reproducing the error, even with the full 50 kB payload and with a reduced 5 kB version. On the front end I’ve confirmed that headers and tokens are identical on every attempt, there’s no caching or service worker interference, and my Axios interceptor isn’t altering the request. Our backend team has also reviewed their CORS configuration, run stress tests against this same endpoint, and reports everything functioning correctly on their side. Given this, I’m unsure whether the fix should come from my code, from the backend (e.g., ensuring CORS headers are sent even on 500 responses), or if it should be escalated to infrastructure/support

error message when DriveApp.getFolderByID is used

I constantly get an error message when trying to implement DriveApp.getFolderByID. My
partial code is:

'function myFunction() {
var files = DriveApp.getFolderById("https://drive.google.com/drive/folders/1H- 
ZCY71Ej3a7F_cUZ4wmSDc-WIz9gRxl?usp=drive_link
").getFiles()
while (files.hasNext()) {
var file = files.next();'

This is partial code to auto-import a number of google sheets rows at a time to another sheet in the same spreadsheet. However I get this error message. I have searched google
workspace to see if the DriveApp function was still viable and not found to the contary. I get this error message – “Exception: Unexpected error while
getting the method or property getFolderById on object DriveApp.myFunction @ Learning
Auto Import.gs:2” when I run the code. enter code hereHelp Please

How often is a TURN server needed for P2P file transfer?

I am building an app that also has a feature of p2p file transfer. The stack used is react + next.js using socket.io. File transfer works perfectly on home network but if i have 2 devices on 2 networks(regular home network ISPs) the ICE fails. AI keeps telling me i need to use a TURN server. I am hosting one so it wouldn’t be a problem but i just can’t get my mind around having to use a TURN server for each transfer. I can provide logs if needed. Thanks guys!

send page

'use client';

import { useEffect, useRef, useState } from 'react';
import { socket, registerDevice, sendOffer, sendCandidate } from '@/lib/socket';

export default function SendPage() {
    const [receiverId, setReceiverId] = useState('');
    const [status, setStatus] = useState('Waiting...');
    const fileInputRef = useRef<HTMLInputElement>(null);
    const peerConnectionRef = useRef<RTCPeerConnection | null>(null);
    const dataChannelRef = useRef<RTCDataChannel | null>(null);

    const senderDeviceId = 'abc123';

    useEffect(() => {
        registerDevice(senderDeviceId);

        async function createConnection(file: File, receiverDeviceId: string) {
            const peerConnection = new RTCPeerConnection({
                iceServers: [{ urls: "stun:stun.l.google.com:19302" }]
            });

            peerConnectionRef.current = peerConnection;

            const dataChannel = peerConnection.createDataChannel("fileTransfer");
            dataChannelRef.current = dataChannel;

            dataChannel.onopen = () => {
                setStatus("Sending file...");
                sendFile(file);
            };

            peerConnection.onicecandidate = (event) => {
                if (event.candidate) {
                    sendCandidate(receiverDeviceId, event.candidate);
                }
            };

            peerConnection.onicegatheringstatechange = () => {
                if (peerConnection.iceGatheringState === "complete" && peerConnection.localDescription) {
                    sendOffer(senderDeviceId, receiverDeviceId, peerConnection.localDescription);
                    setStatus("Offer sent. Waiting for answer...");
                }
            };

            peerConnection.oniceconnectionstatechange = () => {
                console.log("ICE connection state:", peerConnection.iceConnectionState);
            };

            peerConnection.onconnectionstatechange = () => {
                console.log("Connection state:", peerConnection.connectionState);
            };

            const offer = await peerConnection.createOffer();
            await peerConnection.setLocalDescription(offer);
        }

        function sendFile(file: File) {
            const chunkSize = 16384;
            const reader = new FileReader();
            let offset = 0;

            dataChannelRef.current?.send(JSON.stringify({
                type: "metadata",
                filename: file.name,
                filetype: file.type,
                size: file.size
            }));

            reader.onload = (e) => {
                if (e.target?.readyState !== FileReader.DONE) return;
                if (e.target.result && dataChannelRef.current) {
                    dataChannelRef.current.send(e.target.result as ArrayBuffer);
                    offset += (e.target.result as ArrayBuffer).byteLength;
                    if (offset < file.size) {
                        readSlice(offset);
                    } else {
                        setStatus("File sent successfully!");
                    }
                }
            };

            reader.onerror = () => {
                console.error("Error reading file");
                setStatus("File read error");
            };

            function readSlice(o: number) {
                const slice = file.slice(o, o + chunkSize);
                reader.readAsArrayBuffer(slice);
            }

            readSlice(0);
        }

        const clickHandler = () => {
            const file = fileInputRef.current?.files?.[0];
            if (!file || !receiverId) {
                setStatus("Missing file or device ID");
                return;
            }
            createConnection(file, receiverId);
        };

        document.getElementById("connectBtn")?.addEventListener("click", clickHandler);

        socket.on("receive_answer", async ({ answer }) => {
            const pc = peerConnectionRef.current;
            if (!pc) return;
            if (pc.signalingState === "have-local-offer") {
                await pc.setRemoteDescription(new RTCSessionDescription(answer));
            } else {
                console.warn("Ignoring answer: unexpected signaling state", pc.signalingState);
            }
        });

        socket.on("ice_candidate", ({ candidate }: { candidate: RTCIceCandidateInit }) => {
            if (peerConnectionRef.current) {
                peerConnectionRef.current.addIceCandidate(new RTCIceCandidate(candidate));
            }
        });

        return () => {
            document.getElementById("connectBtn")?.removeEventListener("click", clickHandler);
        };
    }, [receiverId]);

    return (
        <div style={{ fontFamily: 'sans-serif', margin: '2em' }}>
            <h1>Send File</h1>
            <label>
                Receiver Device ID:{' '}
                <input
                    type="text"
                    value={receiverId}
                    onChange={(e) => setReceiverId(e.target.value)}
                />
            </label>
            <br />
            <label>
                Select File: <input type="file" ref={fileInputRef} />
            </label>
            <br />
            <button id="connectBtn">Send File</button>
            <p>{status}</p>
        </div>
    );
}

receive page

'use client';

import { useEffect, useRef, useState } from 'react';
import { socket, registerDevice, sendAnswer, sendCandidate } from '@/lib/socket';

export default function ReceivePage() {
    const [deviceId, setDeviceId] = useState('Loading...');
    const [status, setStatus] = useState('Waiting for offer...');
    const peerConnectionRef = useRef<RTCPeerConnection | null>(null);
    const receivedChunksRef = useRef<Uint8Array[]>([]);
    const receivedSizeRef = useRef(0);
    const metadataRef = useRef<{ name: string; type: string; size: number } | null>(null);

    useEffect(() => {
        function generateRandomId(length = 6): string {
            const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
            return Array.from({ length }, () => chars[Math.floor(Math.random() * chars.length)]).join('');
        }

        const receiverDeviceId = generateRandomId();
        setDeviceId(receiverDeviceId);
        registerDevice(receiverDeviceId);

        socket.on("receive_offer", async ({ sender, offer }: { sender: string; offer: RTCSessionDescriptionInit }) => {
            setStatus("Offer received. Creating answer...");

            const peerConnection = new RTCPeerConnection({
                iceServers: [{ urls: "stun:stun.l.google.com:19302" }]
            });
            peerConnectionRef.current = peerConnection;

            peerConnection.oniceconnectionstatechange = () => {
                console.log("ICE connection state:", peerConnection.iceConnectionState);
            };

            peerConnection.onconnectionstatechange = () => {
                console.log("Connection state:", peerConnection.connectionState);
            };

            peerConnection.ondatachannel = (event) => {
                const channel = event.channel;

                channel.onmessage = async (event) => {
                    if (typeof event.data === "string") {
                        try {
                            const msg = JSON.parse(event.data);
                            if (msg.type === "metadata") {
                                metadataRef.current = {
                                    name: msg.filename,
                                    type: msg.filetype,
                                    size: msg.size
                                };
                                receivedChunksRef.current = [];
                                receivedSizeRef.current = 0;
                                setStatus(`Receiving ${msg.filename} (${msg.size} bytes)`);
                            }
                        } catch (e) {
                            console.warn("Invalid metadata JSON");
                        }
                    } else {
                        const chunk = event.data instanceof Blob
                            ? new Uint8Array(await event.data.arrayBuffer())
                            : new Uint8Array(event.data);
                        receivedChunksRef.current.push(chunk);
                        receivedSizeRef.current += chunk.byteLength;

                        if (metadataRef.current && receivedSizeRef.current >= metadataRef.current.size) {
                            const blob = new Blob(receivedChunksRef.current, { type: metadataRef.current.type });
                            const a = document.createElement("a");
                            a.href = URL.createObjectURL(blob);
                            a.download = metadataRef.current.name;
                            a.click();
                            setStatus("File received and downloaded!");
                        }
                    }
                };

                channel.onopen = () => {
                    setStatus("Data channel open. Receiving file...");
                };
            };

            peerConnection.onicecandidate = (event) => {
                if (event.candidate) {
                    sendCandidate(sender, event.candidate);
                }
            };

            await peerConnection.setRemoteDescription(new RTCSessionDescription(offer));
            const answer = await peerConnection.createAnswer();
            await peerConnection.setLocalDescription(answer);
            sendAnswer(sender, answer);
            setStatus("Answer sent. Waiting for file...");
        });

        socket.on("ice_candidate", ({ candidate }: { candidate: RTCIceCandidateInit }) => {
            if (peerConnectionRef.current) {
                peerConnectionRef.current.addIceCandidate(new RTCIceCandidate(candidate));
            }
        });
    }, []);

    return (
        <div style={{ fontFamily: "sans-serif", margin: "2em" }}>
            <h1>Receive File</h1>
            <p>Your device ID: <strong style={{ color: "#333" }}>{deviceId}</strong></p>
            <p>{status}</p>
        </div>
    );
}