Allow newly added images to be loaded after a build in Next.js

I’ve recently rewritten a React app in Next.js (version 14.1.4). It’s a website that has a page with a list of products, each product consisting of some text and an image. The content is managed by the owner through an admin panel, which is a separate application, so products can be changed, deleted and added at any time.

After building the app on my server, I noticed that newly added images weren’t being loaded by Next.js. This made sense to me as I was using the Image component from next/image. Since my images are already optimized in the backend with the appropriate formats and sizes, I decided to take care of it myself and avoid using the Image component. However, this didn’t work as I still had the problem of new images not loading.

I couldn’t find any information in the official documentation on how to handle this properly, so I created a custom request handler that returns the appropriate image as a response:

// api/images/[itemId]/[imageName]/route.ts

import { type NextRequest } from "next/server";
import path from "path";
import fs from "fs";

export async function GET(
  request: NextRequest,
  { params }: { params: { itemId: string; imageName: string; } }
) {
  const searchParams = request.nextUrl.searchParams;
  const size = searchParams.get("size");
  const type = searchParams.get("type");
  const productType = searchParams.get("productType");

  const { itemId, imageName } = params;

  const imagePath = path.join(
    process.cwd(), "public", "uploads", `${productType}`, itemId, `${imageName}_${size}.${type}`
  );

  if (!fs.existsSync(imagePath)) {
    return new Response("Image not found", { status: 404 });
  }

  let contentType: string;
  switch (type) {
    case "jpg":
      contentType = "image/jpeg";
      break;
    case "webp":
      contentType = "image/webp";
      break;
    default:
      contentType = "image/jpeg";
  }

  const headers = {
    "Content-Type": contentType
  };

  // Create a read stream from the image file and return it as the response
  const stream = fs.createReadStream(imagePath);

  // @ts-ignore
  return new Response(stream, { headers });
}

I also created a custom Image component for this:

import clsx from "clsx";

type Props = React.ComponentPropsWithoutRef<"img"> & {
  src: string;
  alt: string;
  productType: "products" | "gallery";
  className?: string;
};

export default function CustomImage({
  src,
  alt,
  productType,
  className,
  ...rest
}: Props) {
  return !src ? <p>No image</p> : (

    <picture>
      <source
        type="image/webp"
        srcSet={`${src}?size=sm&type=webp&productType=${productType} 450w, ${src}?size=md&type=webp&productType=${productType} 900w, ${src}?size=lg&type=webp&productType=${productType} 1350w`}
        sizes="(min-width: 61.25em) 500px, 100vw" // 980px
      />
      <img
        loading="lazy"
        src={`${src}?size=sm&type=jpg&productType=${productType}`}
        alt={alt ?? ""}
        width="400"
        height="300"
        className={clsx("product-image", className)}
        {...rest}
      />
    </picture>
  );
}

This seemed to be working fine, but I noticed that the images were not always loading on my mobile device. When visiting the /products web page, the images are displayed without any problems. However, when I navigate from the product detail page /products/slug back to /products using the link provided in the breadcrumbs (it’s basically just a Link component), I’m back on the same page but without the images loading. Again, this behavior somehow only occurs on my mobile device, in all major browsers. Also worth noting is that I have the same Link component in my main navigation and everything works fine when I click on the /products link, very strange.

I find this kind of bug hard to track down, because I don’t know what’s really going on and what Next.js is doing under the hood that could be causing this error.

So, despite my attempts to gain control over the images, it seems that Next.js is still messing with my images, and I don’t want that.

Which brings me to my question: Am I on the right track with my approach to handling the images myself, or am I completely missing something and there is a much easier way to handle this?

Can I automatically switch user input method from fullwidth katakana to halfwidth hiragana when I focus in an input using javascript or PHP?

(1) I have a form of html inputs, some of which are allowed to type in halfwidth hiragana, the others are allowed to type in fullwidth katakana.

(2) Now, I need a way to automatically switch user keyboard input method between halfwidth hiragana and fullwidth katakana when user focus in the input in (1)

Is this possible or not?

Setting up a proxy with NodeJS for Chrome on Android

I am trying to setup a NodeJS http proxy for Chrome on Android phones, on my local network

Here is the code

import { createServer, request } from 'http';
import { networkInterfaces } from 'os';

const nets = networkInterfaces();

const ips = Object.entries(nets)
  .map(([type, v]) =>
    v.map(({ address, family: mode }) => ({
      type,
      mode,
      address,
    })),
  )
  .flat();

const server = createServer((req, res) => {
  const { method, url, headers, socket } = req;
  const { host } = headers;
  const { remoteAddress } = socket;

  console.log('Http request caught : ', { method, url, host, remoteAddress });

  const proxy = request({ hostname: host, port: 80, path: url, method, headers }, (proxyres) => {
    res.writeHead(proxyres.statusCode, proxyres.headers);
    proxyres.pipe(res, { end: true });
  });

  proxy.on('error', (error) => {
    console.log('Error proxying request : ');
    console.log(error);
    res.writeHead(500);
    res.end('Proxy request failed');
  });

  req.pipe(proxy, { end: true });
});

server.on('listening', () => {
  console.log('Server started on port 3000');
  console.log('Here are your IP adresses, set one as your proxy : ');
  console.log(ips);
});

server.listen(3000, '0.0.0.0');

After configuring the proxy settings on my Wi-Fi connection, I can see some requests :

Http request caught :  {
  method: 'GET',
  url: 'http://www.google.eu/generate_204',
  host: 'www.google.eu',
  remoteAddress: 'my.ip'
}
Http request caught :  {
  method: 'GET',
  url: 'http://conn-service-eu-04.allawnos.com/generate204',
  host: 'conn-service-eu-04.allawnos.com',
  remoteAddress: 'my.ip'
}

The issue is, those are random calls (I don’t know where they come from, probably some internal shenanigans), but i cannot see HTTP requests I trigger myself, those include :

  • Chrome websites
  • Discord
  • Play store
  • Some PWAs

So my question is, how can I see all HTTP requests being made from my Android phone ?

How to move a particular div to far right? [closed]

I want to display this dark magenta colored box to the far right and also I want that dark magenta colored box to shrink and grow in small/big devices.
Output

body {
    background-color: rgb(79, 211, 79);
}

.cols{
    display: flex;
    flex-basis: auto;
    flex: 1;
    flex-grow: 2em;
    gap: 2em;
    flex-shrink: 1;
    justify-content: flex-start;
}

.far-right{
    margin-left: auto;
    flex-grow: 1em;
    flex-shrink: 1em;
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="index.css">
    <script src="index.js"></script>
</head>
<body>
    <div class="bg">
        <div class="cols">
            <div style="background-color: crimson; width: 150px; height: 150px;"></div>
            <div style="background-color: rgb(31, 27, 221); width: 150px; height: 150px;"></div>
            <div id="far-right">
                <div style="background-color: rgb(146, 3, 148); width: 150px; height: 150px;"></div>
            </div>
            
        </div>
    </div>
</body>
</html>

Custom components not rendering from mdx-components.js in Next.js

I’m trying to pass custom components to my .mdx posts via the mdx-components.js file, but the custom components are not rendering.

This is my mdx-components.js file which I made based on the NextJS docs:

// my-blog/mdx-components.js

import UnderlineHoverLink from "./app/_components/Links/UnderlineHoverLink";

export function useMDXComponents(components) {
  return {
    a: ({ href, children }) => (
      <UnderlineHoverLink href={href}>{children}</UnderlineHoverLink>
    ),
    ...components,
  };
}

And this is the page where I am rendering the.mdx files:

// my-blog/app/(routes)/posts/[slug]/page.js:

import { getPostBySlug } from "@/app/_lib/mdx";

const getPageContent = async (slug) => {
  const { meta, content } = await getPostBySlug(slug);
  return { meta, content };
};

export async function generateMetadata({ params }) {
  const { meta } = await getPageContent(params.slug);
  return { title: meta.title };
}

const Page = async ({ params }) => {
  console.log(params);
  const { content } = await getPageContent(params.slug);
  return content;
};

export default Page;

and this is where I am getting the content from my filesystem where the .mdx files are:

// my-blog/app/_lib/mdx/index.js

import fs from "fs";
import path from "path";
import { compileMDX } from "next-mdx-remote/rsc";

const rootDirectory = path.join(process.cwd(), "content/posts");

export const getPostBySlug = async (slug) => {
  const realSlug = slug.replace(/.mdx$/, "");
  const filePath = path.join(rootDirectory, `${realSlug}.mdx`);

  const fileContent = fs.readFileSync(filePath, { encoding: "utf8" });

  const { frontmatter, content } = await compileMDX({
    source: fileContent,
    options: { parseFrontmatter: true },
  });

  return { meta: { ...frontmatter, slug: realSlug }, content };
};

According to the NextJS documentation:

Adding styles and components in mdx-components.tsx will affect all MDX files in your application.

I’ve been following the documentation pretty closely, but I can’t seem to get my custom link to render. Can anyone help me troubleshoot?

I inspected the output html in the browser to see if the custom link was rendering but other styling was overriding it. But it looks like the custom link is not rendering. I tried removing the mdx-components.js file to see if it wasn’t being loaded at all, but NextJS threw an error, so I know the file is being loaded.

PWA: Add user defined shortcuts to homescreen

In the manifest.json of an PWA I can define shortcuts but if all of the shortcuts are available depends on the Browser an OS. Chrome on Android only shows the first three shortcuts. So some won’t be available.

Is there a way that my PWA can give the user the possibility to add custom shortcuts to the homescreen?

How can I set performance column value depends on SDI values?

I want to set performance column value depends on the values in SDI column in Interactive grid
I tried the following :

1- created page button and created dynamic action

2- I created the following java script code when click the button execute the code but its not working and not calculating the value this is the code :

var widget = apex.region('validate').widget();
var grid = widget.interactiveGrid('getViews','grid');
var model = grid.model;
var v_amount = 0; 

model.forEach(function (r) {

if (model.getValue( r , "SDI") >= 0 &&  model.getValue( r , "SDI") <= 1 ) 
{
    
      v_amount += 1
      model.setValue('PERF_ID',v_amount)
      //$s("PERF_ID",v_amount)
}
else if 
(model.getValue( r , "SDI") <= 0 &&  model.getValue( r , "SDI") >= -1 ) 
{
    
      v_amount += 1
      model.setValue('PERF_ID',v_amount)
      //$s("PERF_ID",v_amount)
}

else if 
(model.getValue( r , "SDI") >= 1.01 &&  model.getValue( r , "SDI") <= 1.5 ) 
{
    
      v_amount += 2
      model.setValue('PERF_ID',v_amount)
      //$s("PERF_ID",v_amount)
}

else if 
(model.getValue( r , "SDI") <= -1.01 &&  model.getValue( r , "SDI") >= -1.5 ) 
{
    
      v_amount += 2
      model.setValue('PERF_ID',v_amount)
      //$s("PERF_ID",v_amount)
}
else if 
(model.getValue( r , "SDI") >= 1.6 &&  model.getValue( r , "SDI") <= 2 ) 
{
    
      v_amount += 3
      model.setValue('PERF_ID',v_amount)
      //$s("PERF_ID",v_amount)
}
else if 
(model.getValue( r , "SDI") <= -1.6 &&  model.getValue( r , "SDI") >= -2 ) 
{
    
      v_amount += 3
      model.setValue('PERF_ID',v_amount)
      //$s("PERF_ID",v_amount)
}
else if 
(model.getValue( r , "SDI") > 2 ) 
{
    
      v_amount += 4
      model.setValue('PERF_ID',v_amount)
      //$s("PERF_ID",v_amount)
}

else if 
(model.getValue( r , "SDI") < -2 ) 
{
      v_amount += 4
      model.setValue('PERF_ID',v_amount)
      //$s("PERF_ID",v_amount)
}
})

enter image description here

see the image I need when click performance button calculate and set value in
performance column

PERF_ID and SDI is the columns name

Is the code java script syntax correct ?

Sort 144 matchups into 8 rounds. Each team has to compete once only per round

I already assigned 36 teams into an array of 144 match-ups, each facing off against eight opponents. I want to divide the matchups into 8 rounds, so it has 18 matches per round, but the constraint is that each team can only compete once per round. It’s basically the new UEFA Champion League Swiss system format. I have tried a while loop to keep retrying and shuffling the matchup but after 100 trials it still doesn’t work. Some rounds have fewer or more than 18 matches. Do you think it is possible without changing the matchups? Here’s my code [jsFiddle]:

const matchups = [
    [
        "26",
        "10"
    ],
    [
        "26",
        "23"
    ],
    [
        "26",
        "7"
    ],
    [
        "28",
        "26"
    ],
    [
        "21",
        "26"
    ],
    [
        "32",
        "26"
    ],
    [
        "36",
        "26"
    ],
    [
        "26",
        "22"
    ],
    [
        "43",
        "25"
    ],
    [
        "25",
        "54"
    ],
    [
        "25",
        "11"
    ],
    [
        "37",
        "25"
    ],
    [
        "16",
        "25"
    ],
    [
        "27",
        "25"
    ],
    [
        "25",
        "20"
    ],
    [
        "25",
        "2"
    ],
    [
        "14",
        "36"
    ],
    [
        "37",
        "36"
    ],
    [
        "36",
        "43"
    ],
    [
        "36",
        "15"
    ],
    [
        "11",
        "36"
    ],
    [
        "29",
        "36"
    ],
    [
        "36",
        "41"
    ],
    [
        "23",
        "3"
    ],
    [
        "23",
        "24"
    ],
    [
        "23",
        "43"
    ],
    [
        "27",
        "23"
    ],
    [
        "10",
        "23"
    ],
    [
        "29",
        "23"
    ],
    [
        "23",
        "37"
    ],
    [
        "54",
        "13"
    ],
    [
        "48",
        "54"
    ],
    [
        "54",
        "40"
    ],
    [
        "54",
        "41"
    ],
    [
        "54",
        "18"
    ],
    [
        "34",
        "54"
    ],
    [
        "12",
        "54"
    ],
    [
        "2",
        "29"
    ],
    [
        "2",
        "12"
    ],
    [
        "24",
        "2"
    ],
    [
        "2",
        "13"
    ],
    [
        "7",
        "2"
    ],
    [
        "20",
        "2"
    ],
    [
        "2",
        "21"
    ],
    [
        "7",
        "43"
    ],
    [
        "1",
        "43"
    ],
    [
        "43",
        "15"
    ],
    [
        "43",
        "41"
    ],
    [
        "43",
        "28"
    ],
    [
        "48",
        "37"
    ],
    [
        "7",
        "37"
    ],
    [
        "22",
        "37"
    ],
    [
        "37",
        "11"
    ],
    [
        "37",
        "24"
    ],
    [
        "1",
        "27"
    ],
    [
        "11",
        "1"
    ],
    [
        "1",
        "12"
    ],
    [
        "14",
        "1"
    ],
    [
        "1",
        "21"
    ],
    [
        "9",
        "1"
    ],
    [
        "29",
        "1"
    ],
    [
        "35",
        "16"
    ],
    [
        "35",
        "20"
    ],
    [
        "10",
        "35"
    ],
    [
        "28",
        "35"
    ],
    [
        "35",
        "19"
    ],
    [
        "11",
        "35"
    ],
    [
        "22",
        "35"
    ],
    [
        "35",
        "48"
    ],
    [
        "15",
        "33"
    ],
    [
        "17",
        "15"
    ],
    [
        "15",
        "40"
    ],
    [
        "15",
        "10"
    ],
    [
        "12",
        "15"
    ],
    [
        "15",
        "9"
    ],
    [
        "33",
        "11"
    ],
    [
        "9",
        "11"
    ],
    [
        "11",
        "34"
    ],
    [
        "27",
        "19"
    ],
    [
        "48",
        "27"
    ],
    [
        "34",
        "27"
    ],
    [
        "13",
        "27"
    ],
    [
        "27",
        "33"
    ],
    [
        "9",
        "48"
    ],
    [
        "9",
        "17"
    ],
    [
        "28",
        "9"
    ],
    [
        "20",
        "9"
    ],
    [
        "14",
        "9"
    ],
    [
        "17",
        "14"
    ],
    [
        "28",
        "17"
    ],
    [
        "17",
        "48"
    ],
    [
        "17",
        "20"
    ],
    [
        "16",
        "17"
    ],
    [
        "22",
        "17"
    ],
    [
        "19",
        "3"
    ],
    [
        "19",
        "40"
    ],
    [
        "13",
        "19"
    ],
    [
        "19",
        "22"
    ],
    [
        "19",
        "29"
    ],
    [
        "33",
        "19"
    ],
    [
        "32",
        "21"
    ],
    [
        "10",
        "32"
    ],
    [
        "18",
        "32"
    ],
    [
        "32",
        "28"
    ],
    [
        "16",
        "32"
    ],
    [
        "32",
        "3"
    ],
    [
        "20",
        "32"
    ],
    [
        "22",
        "28"
    ],
    [
        "13",
        "22"
    ],
    [
        "10",
        "22"
    ],
    [
        "41",
        "14"
    ],
    [
        "41",
        "7"
    ],
    [
        "41",
        "29"
    ],
    [
        "41",
        "34"
    ],
    [
        "16",
        "41"
    ],
    [
        "3",
        "16"
    ],
    [
        "3",
        "28"
    ],
    [
        "3",
        "10"
    ],
    [
        "3",
        "7"
    ],
    [
        "24",
        "3"
    ],
    [
        "18",
        "16"
    ],
    [
        "13",
        "16"
    ],
    [
        "18",
        "7"
    ],
    [
        "7",
        "14"
    ],
    [
        "33",
        "29"
    ],
    [
        "29",
        "34"
    ],
    [
        "21",
        "13"
    ],
    [
        "21",
        "33"
    ],
    [
        "21",
        "12"
    ],
    [
        "24",
        "21"
    ],
    [
        "20",
        "48"
    ],
    [
        "48",
        "10"
    ],
    [
        "40",
        "14"
    ],
    [
        "24",
        "40"
    ],
    [
        "40",
        "12"
    ],
    [
        "40",
        "18"
    ],
    [
        "40",
        "34"
    ],
    [
        "12",
        "18"
    ],
    [
        "12",
        "20"
    ],
    [
        "34",
        "13"
    ],
    [
        "34",
        "18"
    ],
    [
        "14",
        "24"
    ],
    [
        "18",
        "33"
    ],
    [
        "33",
        "24"
    ]
]


const totalRounds = 8
const matchesEachRound = 18;
let rounds: TeamId[][][] = [];
let trial = 0;
while (trial++ < 100) {
    rounds = Array(totalRounds)
        .fill([])
        .map(() => []);

    for (const matchup of _.shuffle(matchups)) {
        let findTargetRound = rounds.find((round) => {
            return (
                (round.length < matchesEachRound &&
                    // both teams not complete in this round
                    _.intersection(round.flat(), matchup).length === 0)
            );
        });

        if (findTargetRound) {
            findTargetRound.push(matchup);
        }
    }
    if (rounds.flat().length === matchups.length) {
        break;
    }
}

console.log(rounds)

Allow Nullable Or String in Shadcn React Hook Form and Zod

I have a lot of fields that I want to populate from the API. Some of them are string, some are nullable. How do I accept them either nullable or string?

The <Input/> field is complaining saying like:

Type '{ onChange: (...event: any[]) => void; onBlur: Noop; value: string | null | undefined; disabled?: boolean | undefined; name: "contact.headline"; ref: RefCallBack; placeholder: string; }' is not assignable to type 'InputProps'.
  Types of property 'value' are incompatible.
    Type 'string | null | undefined' is not assignable to type 'string | number | readonly string[] | undefined'.ts(2322)

CODE

const formSchema = z.object({
    contact: z.object({
        first_name: z.string().min(1, { message: "Enter first name" }),
        last_name: z.string().min(1, { message: "Enter last name" }),
        title: z.string().nullable().optional(),
    }),

    organization: z.object({
        name: z.string().min(1, { message: "Enter organization name" }),
    }),
});

const form = useForm<FormData>({
    mode: "onTouched",
    resolver,
    defaultValues: {
        contact: { ...recipient.contact },
        organization: { ...recipient.organization },
    },
});

<FormField
    control={form.control}
    name="contact.title"
    render={({ field }) => (
        <FormItem>
            <FormLabel>Title</FormLabel>
            <FormControl>
                <Input placeholder="Enter title" {...field} />
            </FormControl>
            <FormMessage />
        </FormItem>
    )}
/>

Dropping the redPieces in vertical orientation

// Function to create grid
function createGrid(containerId, numRows, numCols, colorClass) {
  const gridContainer = document.getElementById(containerId);
  for (let i = 0; i < numRows; i++) {
    const row = document.createElement('div');
    row.classList.add('row');
    for (let j = 0; j < numCols; j++) {
      const square = document.createElement('div');
      square.classList.add('square', colorClass);
      // Enable dropping on the square
      square.setAttribute('ondrop', 'drop(event)');
      square.setAttribute('ondragover', 'allowDrop(event)');
      row.appendChild(square);
    }
    gridContainer.appendChild(row);
  }
}

function allowDrop(event) {
  event.preventDefault();
}

// Function to add drag and drop functionality to redPieces
function addDragDrop(redPieceId) {
  const redPiece = document.getElementById(redPieceId);
  redPiece.setAttribute('draggable', true);
  redPiece.addEventListener('dragstart', (event) => {
    event.dataTransfer.setData('text', event.target.id);
  });
}

// Add drag and drop functionality to all redPieces
const redPieceIds = ['redPiece5', 'redPiece4', 'redPiece3-1', 'redPiece3-2', 'redPiece2'];
redPieceIds.forEach((redPieceId) => {
  addDragDrop(redPieceId);
});


function drop(event) {
  event.preventDefault();
  const data = event.dataTransfer.getData('text');
  const draggableElement = document.getElementById(data);
  const target = event.target;
  // Check if the target is a grid square and doesn't have child nodes
  if (target.classList.contains('square') && target.childElementCount === 0) {
    // console.log('Dropped redPiece5 into square:', target);

    let emptySquareCount = 1; // Initialize with 1 to include the dropped square itself
    let prevSquare = target.previousElementSibling;
    // Count the number of empty grid squares before the dropped square
    while (prevSquare && prevSquare.childElementCount === 0) {
      emptySquareCount++;
      prevSquare = prevSquare.previousElementSibling;
    }

    // Move the second and subsequent children based on emptySquareCount
    if (emptySquareCount <= 1) {
      // Move the second and subsequent children to the next grid squares
      let nextSquare = target.nextElementSibling;
      for (let i = 1; i < draggableElement.children.length; i++) {
        // Check if there are no more adjacent squares
        if (!nextSquare || nextSquare.childElementCount > 0) {
          break;
        }
        const child = draggableElement.children[i].cloneNode(true);
        nextSquare.appendChild(child);
        nextSquare = nextSquare.nextElementSibling;
      }
    } else {
      // Move the second and subsequent children to the previous and next grid squares
      prevSquare = target.previousElementSibling;
      let nextSquare = target.nextElementSibling;
      for (let i = 1; i < draggableElement.children.length; i++) {
        // Move the child to the previous square if available
        if (prevSquare && prevSquare.childElementCount === 0) {
          const child = draggableElement.children[i].cloneNode(true);
          prevSquare.appendChild(child);
          prevSquare = prevSquare.previousElementSibling;
        }
        // Move the child to the next square if available
        else if (nextSquare && nextSquare.childElementCount === 0) {
          const child = draggableElement.children[i].cloneNode(true);
          nextSquare.appendChild(child);
          nextSquare = nextSquare.nextElementSibling;
        }
      }
    }

    // Append the original div (with only one child) to the target
    const originalChild = draggableElement.children[0];
    target.appendChild(originalChild);

    // Remove redPiece5 from redPieces div
    const redPiecesDiv = document.getElementById('redPieces');
    redPiecesDiv.removeChild(draggableElement);
  }
}

// Define the number of rows and columns
const rows = 7;
const columns = 9;

// Create red grid
createGrid('redGrid', rows, columns, 'red');

// Create blue grid
createGrid('blueGrid', rows, columns, 'blue');

// Function to rotate the pieces
function rotatePieces() {
  const squares = document.querySelectorAll('.square');
  squares.forEach((square) => {
    square.classList.toggle('vertical');
  });
  const redPiecesContainer = document.getElementById('redPieces');
  redPiecesContainer.classList.toggle('vertical');
}

// Event listener for the rotate button
const redRotate = document.getElementById('redRotate');
redRotate.addEventListener('click', rotatePieces);
body {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh; 
  margin: 0; 
}

#wrapper {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: row; 
  width: 100%; 
}

.colourContainer{
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: row; 
}

.piecesBox {
  width: 210px;
  height: 210px;
  border: 1px dashed black;
}

.gridContainer {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 30px;
}

.grid {
  display: flex;
  flex-direction: column;
  gap: 3px; 
  margin: 10px;
}

.row {
  display: flex;
  gap: 3px;
}

.square {
  width: 30px; 
  height: 30px; 
  border-radius: 5px;
}

.red {
  background-color: crimson; 
}

.blue {
  background-color: royalblue; 
}

.green {
  background-color: green; 
}

.yellow {
  background-color: rgb(241, 241, 14); 
}

#greenPieces, #yellowPieces {
  display: none;
}
.lightcoral {
  background-color: lightcoral;
}

.piecesBox{
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column; 
  border-radius: 15px;
}

.redPieceses{
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: row; 
  margin: 5px;
  margin-block: 2px;
}
.bluePieceses{
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: row; 
  margin: 5px;
  margin-block: 2px;
}

.pieceses{
  margin-right: 3px;
}

#redPiece5{
  order: 4;
}
#redPiece4{
  order: 5;
}
#redPiece3-1{
  order: 2;
}
#redPiece3-2{
  order: 3;
}
#redPiece2{
  order: 1;
}

#bluePiece5{
  order: 4;
}
#bluePiece4{
  order: 5;
}
#bluePiece3-1{
  order: 2;
}
#bluePiece3-2{
  order: 3;
}
#bluePiece2{
  order: 1;
}

.vertical {
  transform: rotate(90deg);
}

.vertical #redPieces {
  flex-direction: column;
}
/* #redRotate.vertical {
  transform: rotate(-90deg);
} */
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Grid Example</title>
  <link rel="stylesheet" href="./styles/main.css">
</head>
<body>


  <div id="wrapper">


    
    <div class="gridContainer">


      <div id="redContainer" class="colourContainer">
        <div id="redPieces" class="piecesBox">
          <div id="redPiece5" class="redPieceses" draggable="true">
            <div class="square lightcoral pieceses"></div>
            <div class="square lightcoral pieceses"></div>
            <div class="square lightcoral pieceses"></div>
            <div class="square lightcoral pieceses"></div>
            <div class="square lightcoral pieceses"></div>
          </div> <button id="redRotate">Rotate</button>
        
          <div id="redPiece4" class="redPieceses" draggable="true">
            <div class="square lightcoral pieceses"></div>
            <div class="square lightcoral pieceses"></div>
            <div class="square lightcoral pieceses"></div>
            <div class="square lightcoral pieceses"></div>
          </div>
        
          <div id="redPiece3-1" class="redPieceses" draggable="true">
            <div class="square lightcoral pieceses"></div>
            <div class="square lightcoral pieceses"></div>
            <div class="square lightcoral pieceses"></div>
          </div>
        
          <div id="redPiece3-2" class="redPieceses" draggable="true">
            <div class="square lightcoral pieceses"></div>
            <div class="square lightcoral pieceses"></div>
            <div class="square lightcoral pieceses"></div>
          </div>
        
          <div id="redPiece2" class="redPieceses" draggable="true">
            <div class="square lightcoral pieceses"></div>
            <div class="square lightcoral pieceses"></div>
          </div>
        </div>
        <div class="grid" id="redGrid"></div>
      </div>

      <div id="blueContainer" class="colourContainer">
        <div class="grid" id="blueGrid"></div>
        <div id="bluePieces" class="piecesBox">
          <div id="bluePiece5" class="bluePieceses" draggable="true">
            <div class="square blue pieceses"></div>
            <div class="square blue pieceses"></div>
            <div class="square blue pieceses"></div>
            <div class="square blue pieceses"></div>
            <div class="square blue pieceses"></div>
          </div>
        
          <div id="bluePiece4" class="bluePieceses" draggable="true">
            <div class="square blue pieceses"></div>
            <div class="square blue pieceses"></div>
            <div class="square blue pieceses"></div>
            <div class="square blue pieceses"></div>
          </div>
        
          <div id="bluePiece3-1" class="bluePieceses" draggable="true">
            <div class="square blue pieceses"></div>
            <div class="square blue pieceses"></div>
            <div class="square blue pieceses"></div>
          </div>
        
          <div id="bluePiece3-2" class="bluePieceses" draggable="true"> 
            <div class="square blue pieceses"></div>
            <div class="square blue pieceses"></div>
            <div class="square blue pieceses"></div>
          </div>
        
          <div id="bluePiece2" class="bluePieceses" draggable="true">
            <div class="square blue pieceses"></div>
            <div class="square blue pieceses"></div>
          </div>
        </div>
      </div>

      <!-- <div class="grid" id="greenGrid"></div>
      <div class="grid" id="yellowGrid"></div> -->
    </div>



  </div>

  <script src="./scripts/main.js"></script>

</body>
</html>

The above snippet only includes Horzitonal dropping.

When the pieces are horizontal, use the existing drop function, but when it is vertical add a drop that adds pieces into the grid in vertical positioning. So for adding I gave row class. which is the parent of the dropping grid piece. You can see in the HTML. so when it is vertical, we will add the next piece in the same element index but in the next row. Use if-else for toggling and drop

    // Function to create grid
function createGrid(containerId, numRows, numCols, colorClass) {
  const gridContainer = document.getElementById(containerId);
  for (let i = 0; i < numRows; i++) {
    const row = document.createElement('div');
    row.classList.add('row');
    for (let j = 0; j < numCols; j++) {
      const square = document.createElement('div');
      square.classList.add('square', colorClass);
      // Enable dropping on the square
      square.setAttribute('ondrop', 'drop(event)');
      square.setAttribute('ondragover', 'allowDrop(event)');
      row.appendChild(square);
    }
    gridContainer.appendChild(row);
  }
}

function allowDrop(event) {
  event.preventDefault();
}

// Function to add drag and drop functionality to redPieces
function addDragDrop(redPieceId) {
  const redPiece = document.getElementById(redPieceId);
  redPiece.setAttribute('draggable', true);
  redPiece.addEventListener('dragstart', (event) => {
    event.dataTransfer.setData('text', event.target.id);
  });
}

// Add drag and drop functionality to all redPieces
const redPieceIds = ['redPiece5', 'redPiece4', 'redPiece3-1', 'redPiece3-2', 'redPiece2'];
redPieceIds.forEach((redPieceId) => {
  addDragDrop(redPieceId);
});

function drop(event) {
  event.preventDefault();
  const data = event.dataTransfer.getData('text');
  const draggableElement = document.getElementById(data);
  const target = event.target;

  // Check if the redRotate button is in vertical position
  const isVertical = redRotate.classList.contains('vertical');

  // If in horizontal position, use the existing drop function
  if (!isVertical) {
    // Check if the target is a grid square and doesn't have child nodes
    if (target.classList.contains('square') && target.childElementCount === 0) {
      const originalChild = draggableElement.children[0].cloneNode(true);
      target.appendChild(originalChild);
      draggableElement.removeChild(originalChild);

      // Remove redPiece element if all children are dropped
      if (draggableElement.childElementCount === 0) {
        const redPiecesDiv = document.getElementById('redPieces');
        redPiecesDiv.removeChild(draggableElement);
      }
    }
  } else {
    // If in vertical position, add the piece to the grid vertically
    if (target.classList.contains('row')) {
      const row = target;
      const squares = row.querySelectorAll('.square');
      let emptySquare = null;
      squares.forEach(square => {
        if (square.childElementCount === 0 && emptySquare === null) {
          emptySquare = square;
        }
      });

      // If an empty square is found, drop the piece
      if (emptySquare !== null) {
        const originalChild = draggableElement.children[0].cloneNode(true);
        emptySquare.appendChild(originalChild);
        draggableElement.removeChild(originalChild);

        // Remove redPiece element if all children are dropped
        if (draggableElement.childElementCount === 0) {
          const redPiecesDiv = document.getElementById('redPieces');
          redPiecesDiv.removeChild(draggableElement);
        }
      }
    }
  }
}


// Define the number of rows and columns
const rows = 7;
const columns = 9;

// Create red grid
createGrid('redGrid', rows, columns, 'red');

// Create blue grid
createGrid('blueGrid', rows, columns, 'blue');

// Function to rotate the pieces
function rotatePieces() {
  const squares = document.querySelectorAll('.square');
  squares.forEach((square) => {
    square.classList.toggle('vertical');
  });
  const redPiecesContainer = document.getElementById('redPieces');
  redPiecesContainer.classList.toggle('vertical');
  // Toggle the class for the redRotate button
  redRotate.classList.toggle('vertical');
}

// Event listener for the rotate button
const redRotate = document.getElementById('redRotate');
redRotate.addEventListener('click', rotatePieces);

function drop(event) {
  event.preventDefault();
  const data = event.dataTransfer.getData('text');
  const draggableElement = document.getElementById(data);
  const target = event.target;
  
  // Check if the redRotate button is in vertical position
  const isVertical = redRotate.classList.contains('vertical');

  // If in horizontal position, use the existing drop function
  if (!isVertical) {
    // Existing drop function logic
    // ...
  } else {
    // If in vertical position, add the piece to the grid vertically
    if (target.classList.contains('row')) {
      // Append the original div (with only one child) to the target row
      const originalChild = draggableElement.children[0];
      target.appendChild(originalChild);
    }
  }
}

 

I tried this in else for vertical but it does not work.

React redux state is not rendering wtih React-Router library

I am creating a survey questionnaire form with reusable React components for the page layout with state coming from my Redux store. My state has updated, but the updated state is not rendering properly on the page. Specifically on the review route, my item.value are missing. How can I get values to render?

App.jsx

import React, { useEffect, useState } from 'react';
import axios from 'axios';
import './App.css';
import { useDispatch, useSelector } from 'react-redux';
import { Route, useHistory } from 'react-router-dom';

function App() {
  const history = useHistory();
  const dispatch = useDispatch();
  const feedbackSchema = useSelector((store) => store.feedbackSchema);
  const [inputValue, setInputValue] = useState('');

  useEffect(() => {
    fetchFeedback();
  }, []);

  const fetchFeedback = () => {
    axios({
      method: 'GET',
      url: 'api/feedback',
    })
      .then((response) => {
        dispatch({
          type: 'SET_FEEDBACK',
          payload: response.data,
        });
      })
      .catch((error) => {
        console.log('You had a axios GET error', error);
      });
  };

  const handleSubmit = (item, index) => {
    console.log(item, inputValue);
    dispatch({
      type: 'SET_VALUE',
      payload: [item, inputValue],
    });
    history.push(
      feedbackSchema[index + 1]?.route
        ? `/${feedbackSchema[index + 1]?.route}`
        : '/review'
    );
  };

  return (
    <div className="App">
      <header className="App-header">
        <h1 className="App-title">Feedback!</h1>
        <h4>Don't forget it!</h4>
      </header>
      {feedbackSchema.map((item, index) => {
        return (
          <div key={item.key} className="feedback-container">
            <Route exact path={`/${item.route}`}>
              <h1>{item.header}</h1>
              <form>
                <div className="feedback-input">
                  <p className="feedback-topic">{item.topic}</p>
                  <input
                    type="text"
                    value={inputValue}
                    onChange={(event) => setInputValue(event.target.value)}
                  />
                  <br />
                  <button
                    type="button"
                    onClick={() => handleSubmit(item.key, index)}
                  >
                    Next
                  </button>
                </div>
              </form>
            </Route>
          </div>
        );
      })}
      <Route exact path={`/review`}>
        <h1>Review Your Feedback</h1>
        {feedbackSchema.map((feedback) => {
          return (
            <p key={feedback.key}>
              {JSON.stringify(feedback)}
              {feedback.key}: {feedback.value}
            </p>
          );
        })}
      </Route>
    </div>
  );
}

export default App;

Store.js

import { applyMiddleware, combineReducers, createStore } from "redux";
import logger from "redux-logger";

const feedbackList = (state = [], action) => {
    if (action.type === 'SET_FEEDBACK') {
        return action.payload;
    }
    return state;
}

const feedbackSchema = (state = [
    { key: "feeling", route: "", header: "How are you feeling today?", topic: "Feeling?", value: "" },
    { key: "understanding", route: "understanding", header: "How well are you understanding the content?", topic: "Understanding?", value: "" },
    { key: "support", route: "support", header: "How well are you being supported?", topic: "Support?", value: "" },
    { key: "comments", route: "comments", header: "Any comments you want to leave?", topic: "Comments", value: "" }

], action) => {
    if (action.type === 'SET_VALUE') {
        state.map((feedback) => {
            if (feedback.key === action.payload[0]) {
                feedback.value = action.payload[1]
            }
            return feedback;
        })
    }
    return state;
}

const store = createStore(
    combineReducers({
        feedbackList,
        feedbackSchema,
    }),
    applyMiddleware(logger)
)

export default store;

index.jsx

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './components/App/App';
import { Provider } from 'react-redux';
import { HashRouter as Router } from 'react-router-dom';
import store from './store';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <Provider store={store}>
      <Router>
        <App />
      </Router>
    </Provider>
  </React.StrictMode>
);

UI not rendering state/data

How can I use JavaScript to convert a byte array (ArrayBuffer) into a live stream video?

I have created a screen recording application in Java, which works fine and encodes the video stream using MediaCodec. Then, I send the bytes through a socket connection to JavaScript. However, I am having trouble playing the live stream after converting the bytes to a stream using JavaScript libraries such as Bradway.js and Jmuxer.js.

Here is some code that has been tested:

/Bradway.js/

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Live Video Stream</title>
    <style>
  
        #videoCanvas {
            display: block;
            width: 640px; /* Adjust as needed */
            height: 480px; /* Adjust as needed */
            border: 1px solid black; /* Optional border for visualization */
        }
    </style>
    <script src="./Decoder.js"></script>
    <script src="./Player.js"></script>
    <script src="./YUVCanvas.js"></script>
</head>
<body>
    <!-- Video player element -->
    <canvas id="videoCanvas"></canvas>

    <!-- Include jmuxer library -->
   <!-- <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jmuxer.min.js"></script> -->
    <script src="/socket.io/socket.io.js"></script>

    <script>
        // Function to check if the data is H.264 encoded
        function isH264(data) {
            // H.264 NAL unit start code is 0x00 00 01 or 0x00 00 00 01
            if (data.length < 4) {
                return false; // Not enough data
            }
            if (data[0] === 0x00 && data[1] === 0x00 && (data[2] === 0x01 || (data[2] === 0x00 && data[3] === 0x01))) {
                return true; // H.264 start code found
            }
            return false; // Not H.264 encoded
        }

        const socket = io();
        var player;

        socket.on('RemoteVideoByte', bufferdata => {
            if (!player) {
                player = new Player({
                    useWorker: true,
                    workerFile: './Decoder.js',
                    size: { width: 640, height: 480 },
                    webgl: 'auto',
                });
                var canvas = document.getElementById('videoCanvas');
                canvas.appendChild(player.canvas);
            }
  socket.binaryType = 'arraybuffer';
            var data = new Uint8Array(bufferdata);
            player.decode(data);

            // Check if the decoded data is H.264 encoded
            if (isH264(data)) {
                console.log("Decoded data is H.264 encoded.");
            } else {
                console.log("Decoded data is not H.264 encoded.");
            }
        });

        // Handle any WebSocket errors
        socket.on('error', error => {
            console.error('WebSocket error:', error);
        });
    </script>
</body>
</html>

/Jmuxer.js/

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Live Video Stream</title>
    <style>
        #player {
            display: block;
            width: 640px;
            height: 480px;
            border: 1px solid black;
        }
    </style>
</head>
<body>
    <div id="player"></div>

 <script src="/socket.io/socket.io.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jmuxer.min.js"></script>

    <script>
        const socket = io();
        socket.binaryType = 'arraybuffer';

        socket.on('connect', () => {
            console.log('Socket connected');
        });

        socket.on('RemoteVideoByte', bufferdata => {
            jmuxer.feed({
                video: new Uint8Array(bufferdata)
            });
        });

        socket.on('error', error => {
            console.error('WebSocket error:', error);
        });

        var jmuxer = new JMuxer({
            node: 'player',
            mode: 'video',
            flushingTime: 1000,
            fps: 30,
            debug: false
        });
    </script>
</body>
</html>

Can you please help me solve this issue? It would be greatly appreciated.

I cannot change the styles of the page on inspector

I wanted to change the styles of some elements in Mercedes-Benz’s page with inpsector. But when I entered the class names in the Inspector, I couldn’t make changes neither in JavaScript nor in CSS. Even though I tried the !important definition, it didn’t work. I wanted to access the Load More button here, but I couldn’t. How can I do this?

Thanks!

I wanted to access the button on the Mercedes-Benz’s page but I cannot.