Issue with using SplashScreen with Expo in React Native App

I am facing an issue while using SplashScreen with the expo-splash-screen library in my React-Native application. When I run the app, I get an error message indicating that preventAutoHideAsync is not defined.

Here’s the code I’m working with:

import React, { useEffect } from 'react';
import { View, StyleSheet, ActivityIndicator } from 'react-native';
import WelcomeMessager from '../components/screens/wellcomemassge';
import { useFonts } from 'expo-font';
import * as SplashScreen from 'expo-splash-screen'; // Updated import

const WelcomeScreen = ({ navigation }) => {
  SplashScreen.preventAutoHideAsync();
  const [loaded, error] = useFonts({
    'Hedvig': require('../assets/Fonts/HedvigLettersSans-Regular.ttf'),
  });

  useEffect(() => {
    if (loaded || error) {
      SplashScreen.hideAsync();
    }
  }, [loaded, error]);

  if (!loaded && !error) {
    return null;
  }
  
  if (!loaded) {
    return (
      <View style={styles.container}>
        <ActivityIndicator size="large" color="#0000ff" />
      </View>
    );
  }

  return (
    <View style={styles.container}>
      <WelcomeMessager navigation={navigation} />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#5CE84D',
    width: '100%',
    paddingBottom: 50,
    flexDirection: 'column',
    alignItems: 'stretch',
    justifyContent: 'center',
  },
});

export default WelcomeScreen;

I get the following error message when running the app:

ERROR TypeError: Cannot read property ‘preventAutoHideAsync’ of null

I am using expo-splash-screen in my Expo-based project. I need a solution to this issue so that I can use the splash screen correctly.

  • Implemented Splash Screen: I followed the documentation for using expo-splash-screen, including importing it and calling preventAutoHideAsync() at the beginning of my component.

  • Checked Package Versions: I made sure that I have the correct versions of expo and expo-splash-screen installed in my project.

  • Used useEffect: I implemented useEffect to hide the splash screen once the fonts are loaded or if there was an error.

  • Restarted the Development Server: I restarted the Metro bundler and ensured my emulator was running properly.

Requesting Notification permission token with Firebase Cloud Messaging

I’m trying to request a Notification permission token with Firebase Cloud Messaging with JavaScript. My current code works, but as explained in the code comment, there seems to be a strange behavior regarding the steps.

What should happen:

  1. User clicks on send reminders html button,
  2. browser asks for notifications permissions,
  3. user clicks on allow,
  4. user receives the current_token value.

What is actually happening:

  1. User clicks on send reminders html button,
  2. browser asks for notifications permissions,
  3. user clicks on allow,
  4. user doesn’t receive the current_token value,
  5. user has to click on send reminders html button again,
  6. this time the user receives the current_token value (of course without the need of click on allow, because the permissions were previously granted).

To my best understanding, when messaging.getToken is executed, the code inside then(function(current_token) is only reached if the user has previously granted notifications permissions, but this should happen in one single action.

Here’s my current code:

html

<button type="button" id="remind_me">send reminders</button>
<script src="https://www.gstatic.com/firebasejs/10.0.0/firebase-app-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/10.0.0/firebase-messaging-compat.js"></script>

javascript

if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/firebase-messaging-sw.js', {
        scope: '/'
    }).then(function(registration) {
        console.log('fcm: service worker registered successfully');
    }).catch(function(error) {
        console.error('fcm: service worker registration failed:', error);
    });
    navigator.serviceWorker.ready.then(function(registration) {
        console.log('fcm: service worker is ready');
        window.onload = function() {
            console.log('fcm: service worker is ready and page is fully loaded');
            if (navigator.serviceWorker.controller) {
                console.log('fcm: service worker active and controlling page');
                sw_ready();
            } else {
                console.log('fcm: service worker ready but not yet controlling page');
                registration.addEventListener('controllerchange', function () {
                    console.log('fcm: service worker active and controlling page');
                    sw_ready();
                });
            }
        };
    });
}

// default sw ready
let sw_ready = function () {
    $('#remind_me').click(function(e) {
        // request permission for firebase cloud messaging
        console.log('fcm: attemping to get token');
        messaging.getToken({
            vapidKey: config.firebase.messaging // this is a valid vapidKey
        }).then(function(current_token) {

            // THIS PART OF THE CODE IS ONLY REACHED
            // THE 2ND TIME #remind_me IS CLICKED ON
            
            // check token
            if (current_token) {
                console.log('fcm: token accquired', current_token);
            } else {
                // request denied
                console.log('fcm: user denied permission for notications');
            }

        }).catch(function(error) {
            // error while retrieving token
        });
    });
}

Any ideas? Thanks!

I want learning how to make simply ruler (measurement) on Leaflet

I want create simply ruler based on example below:

enter image description here

I know many plugin about measurement in Leaflet, but for some reasons, I want make itself without plugin. I need advise and help for this.

This my function code:

var markersRulerGroup = L.layerGroup();
markersRulerGroup.addTo(map);

var tooltipRulerGroup = L.layerGroup();
tooltipRulerGroup.addTo(map);

var rulerIcon = L.divIcon({className: 'bi bi-dot text-ruler-map', iconSize: [0, 0], iconAnchor: [26, 36]});
var markerRuler;
var markersCount;
var tooltipRuler;

function measureDistance(){
  if ($('#measureDistanceCheckedState').is(":checked")) {
      $('#measureDistanceCheckedState').prop('checked', false);
      btnMeasureDistance.style.backgroundColor = '#ffffff';
      
      // markersRulerGroup.removeLayer(markerRuler);
      map.eachLayer(function(layer) {
          if(layer.options.alt === "markerRuler") layer.removeFrom(markersRulerGroup);
          if(layer.options.alt === "tooltipRuler") layer.removeFrom(tooltipRulerGroup);
      });
      
      map.off('click');
  }   else {
      $('#measureDistanceCheckedState').prop('checked', true);
      btnMeasureDistance.style.backgroundColor = '#dbdbdb';
      
      map.on('click', function(e){
      markerRuler = new L.marker(e.latlng, {alt: 'markerRuler', icon: rulerIcon});
      markersRulerGroup.addLayer(markerRuler);
      
      markersCount = markersRulerGroup.getLayers().length;
      
      tooltipRuler = new L.tooltip(e.latlng, {alt: 'tooltipRuler', permanent: true, direction:'bottom', offset:[1,6]})
      .setContent("THIS MARKER:" +markersCount);
      tooltipRulerGroup.addLayer(tooltipRuler);
      });
  };
};

Based on my function above, when i click on map, will adding marker like as below picture:

enter image description here

My problem actually is, I don’t know how to define marker[before] and marker[after]. This important for calculating distance and adding line between two markers. If I know to define marker[before] and marker[after], i expected my result next are below:

enter image description here

How come these multiple asynchronous fs.createWriteStreams.write() tasks writing to the same file at the same time are not overlapping and jumbling?

I have written a simple node.js application and I have a question.

Here is the general flow of the program.

  1. A local file exists with a list of URLs, each on a new line. Let’s assume there are 1,000 URLs for example.

  2. As each new line (URL) is read (actually in a read stream, one line at a time using the ‘readLine’ module), the callback takes the URL string and passes it to another async function that makes an http request.

  3. Inside this http request function, an https.request() is made. In the callback of the request, I simply take the response and transform the JSON object a bit. In fact, I simply stringify() the JSON object and turn its values into CSV format which is just a string.

  4. I pass this string to a final writeData() function which is intended to write this data to a SINGLE .csv file. Yes, the same file will be used to take in all of these async https request calls and store some local data. Nothing surprising here. Inside this writeData() function, I use fs.createWriteStream(). I pass it a single file which is ‘output.csv’ and run ws.write(‘n’ + csvString).

  • Now here is my question/concern… Since many different invocations of this fs.createWriteStream().write() function will be called asynchronously (remember, 1000 URLs), therefore causing many OS level writes to occur, how come no two writeStreams are writing to the file at the exact same time and therefore jumbling and truncating into each other? It appears that each writeStream is appending its data to the file in a nice, pretty, orderly fashion. The callback of the writeStream.write() method is and the next I expected that while one write stream is writing to the file, another write stream would be simultaneously created and write to the same file at the same time, jumbling the file contents.

Keep in mind, I don’t care about the order of the data being written, but I do care that two writes do not occur on the file at the same time on the OS level, jumbling the file up.

**Here is the writeData function, for reference:

const fs = require("fs");
const writeData = (csvString) => {
  const ws = fs.createWriteStream(output.txt), { flags: "a" });
  ws.write("n" + csvString, () => {
     console.log("A buffer has been written to the file: ");
     console.log(csvString);
  });
}

module.exports = writeData;

Expectations vs. reality:

Here is what is actually being output…which appears GOOD, orderly, almost synchronous appearing.


"A buffer has been written to the file: "
<csvString prints here>
"A buffer has been written to the file: "
<next csvString prints here>
"A buffer has been written to the file: "
<next csvString prints here>
"A buffer has been written to the file: "
<next csvString prints here>

Here is what I expected to be output…which would be BAD, jumbled and multiple async write operations appending to the file at random based on however much time the OS decides to give the async process/thread:

"A buffer has been written to the file: "
<csvString prints here>
 been written to the file: "
<next csvString prints here>
String prints here>
"A buffer has been wri
A buffer has been written to the file: "
<next csvStr
String prints here>
"A buffer has been written to the file: 
"r has been written to the file: "
<next csv

WriteFileSync()??

Also I just realized after thinking about it, perhaps writeFileSync() would clear all concerns out of my mind because then we can be certain that only ONE operation is ever writing/appending to the file at a time. The “blocking” isn’t a huge issue for this application as the write size per object to output.csv isn’t large.

Import Script and Convert HTML, JS and CSS Code To Run In Laravel

I have a laravel project and I want to run a html, js and css code in the laravel blade frontend but am running into some issues. I am new to laravel and moving from angular and nodejs so my apology in advance if this is a basic question.

I want to get a hosted IFrame sample from clover to work on my laravel project. The IFrame project in reference can be found here. I have done extensive research as well as found and tried a lot of solutions but none seem to solve my problem.

I am trying to add the component in a checkout blade file. The issue I am running into are:

  1. The script in the html tag is not being processed by laravel.
  2. I am Unable to link and get the field and functionality working for binding the elements.

I have tried putting the js and cs files in the corresponding folder in the public directory, /public/css/checkout.css and /public/js/checkout.js. But the scripts are failing to load.

The blade file I am adding the code to contains the following code with HTML I added for the form from the iframe example.

@if ($userBe->is_checkout_login == 1)
            <div class="payment-options">
                <link rel="stylesheet" href="{{ asset('css/checkout.css') }}">
                <script src="{{ asset('js/checkout.js') }}"></script>
                <script src="https://checkout.sandbox.dev.clover.com/sdk.js"></script>
                <h4 class="mb-4">Checkout</h4>
                <head>
                    <title>GPay and Hosted Checkout</title>
                </head>
                <div class="container">
                    <form action="/charge" method="post" id="payment-form">
                        <div class="form-row top-row">
                            <div id="payment-request-button" class="payment-request-button full-width"></div>
                        </div>

                        <div class="hr">
                      <span>
                        Or Pay with Card
                      </span>
                        </div>

                        <div class="form-row top-row">
                            <div id="card-number" class="field full-width"></div>
                            <div class="input-errors" id="card-number-errors" role="alert"></div>
                        </div>

                        <div class="form-row">
                            <div id="card-date" class="field third-width"></div>
                            <div class="input-errors" id="card-date-errors" role="alert"></div>
                        </div>

                        <div class="form-row">
                            <div id="card-cvv" class="field third-width"></div>
                            <div class="input-errors" id="card-cvv-errors" role="alert"></div>
                        </div>

                        <div class="form-row">
                            <div id="card-postal-code" class="field third-width"></div>
                            <div class="input-errors" id="card-postal-code-errors" role="alert"></div>
                        </div>

                        <!-- Used to display form errors. -->
                        <div id="card-errors" role="alert"></div>

                        <!-- Testing -->
                        <div id="card-response" role="alert"></div>
                        <div class="button-container">
                            <button>Submit Payment</button>
                        </div>
                    </form>
                </div>
            </div>

            <div class="placeorder-button mt-4">
                <button class="main-btn w-100" type="button" form="payment" id="placeOrderBtn">
                <span class="btn-title">
                    {{ $keywords['Place Order'] ?? __('Place Order') }}
                </span>
                </button>
            </div>
</div>
@endif

I have tried a couple of solutions including this:
The above mentioned did not work and was unclear. <script type="text/javascript" {!! Helper::cspNonceAttr() !!}> returns helper not found and adding it to the environment doesnt load the script.

Nothing seems to work as I have tried multiple other suggestions and solutions. Am I doing it wrong or possibly missing something?

Thanks in advance for the help! 🙂

Is it possible to continue updating the page title in React even when the browser tab is switched?

I’m working on a Pomodoro application in React and I can’t figure out how to continue updating the page title (<title></title>) when the browser tab is switched. Is this even possible with React? I know that browsers throttle JavaScript execution when the browser tab is switched but I was wondering if there is a work around for this? For example, the stopwatch on Google, regardless of if the tab is switched, the title continues to update.

What I mean by update is, the tab title in “Work mode” is: Work - 01:20 and when the timer is running and the tab is active, it continues to update with the elapsed time. If I switch tabs for 10 seconds, it stays as Work - 01:20 until I switch back to the tab, then it just jumps to Work - 01:30.

NOTE: This is not a traditional Pomodoro timer, “Work mode” is a stopwatch not a timer.

For my specific application, I am using React Helmet with a state value that gets updated every second with the elapsed time.

Vue JS – Live running time elapsed from a date

Currently I have a time elapsed displayed on my blade file. However to see the updated time, I need to refresh everytime. I would like to display a dynamic time lapsed. Is there a way to do it?

My current code is just that I pass a created_date (defined from controller) to the script. From there, I will get the hours, minutes and seconds. (Might do days later). They I return it back to my blade file.

This is a part from my blade file:

<table class="table table-bordered small">
  <thead>
      <tr>
          <th width="2%">No</th>
          <th width="5%">
              Created At
          </th>
      </tr>
  </thead>
  <tbody>
      <tr v-for="(index,row) in records.data" :class="{'NPI_row_color':NPI_Order(row)}">
          <td>@{{index+1}}</td>
          <td>
              @{{ setTimer(row.created_at) }} 
          </td>
      <tr>
  </tbody>
</table>

And this is the vue js script:

<script type="text/javascript">
  $(function () {
      var vm = new Vue({
          el: '#shopify_marketplace',
          data: {
            //some of data here
          },
          methods: {
            //some other methods here
            onReload: function(){
              //few codes here and there
            },
            setTimer: function(created_at){

                const startDate = new Date(created_at);
                const now = new Date();
                const elapsedSeconds = Math.floor((now - startDate) / 1000);
                
                const day = Math.floor((elapsedSeconds / 3600));
                const hours = Math.floor(elapsedSeconds / 3600);
                const minutes = Math.floor((elapsedSeconds % 3600) / 60);
                const seconds = elapsedSeconds % 60;

                return `${hours}h ${minutes}m ${seconds}s`;
                
            },
          },
          filters: {
              autoCaps: function(value){
                  if(value != null) {
                      return value.toLocaleUpperCase();    
                  }
              },
          },
          ready: function () {
              this.onReload();
          },
      });
  });
</script>

Image below is what my current display is. But yeah need to refresh everytime to see it update. May I know how can I make the time running and ‘ticking?’. I tried to use this setInterval function from javascript but I failed miserably.

current_display

Nativescript obtaining Another (upper) View from Button of Inner Frame

Recently I used the drawerapp template using the usual instruction as mentioned here : link.

I have no problem for acquirement this until the deployment. Then, I add a new page for login :

login-structure

And make some adjustment inside the app-root.xml as the following:

The Frame showing the Login page

with extra portion layout that i want to be visible or hidden partially.

Layout that shown and hidden

And then, inside the login-page.xml i have this portion that make the button calling function onVerify.

onVerify action called by button

When i made some adjustment inside login-page.js especially for onVerify function. :

 export function onVerify(args){
  var page = args.object.page;
  var form = getViewById(page, "login-form-layout");
  page.frame.navigate({
    moduleName: 'home/home-page',
    clearHistory: true,
  });

  // but the code below didnt work!
  var menuUnlogged = page.getViewById("unlogged");
  var menuLogged = page.getViewById("logged-in");

  menuUnlogged.visibility = "collapse";
  menuLogged.visibility = "visible";
}

But it didnt work. The app crashed! Is that the referencing variable that didnt catch the view element or something else i didnt understand yet?

Hope someone could help me out of this matter. Thanks in advanced!

responsive registration form in html

i need to create a responsive registration form in html with automatic barangay and district in every city using html, css, javascript only

i expect to have responsive registration form website using html, css, and javascript. I expect the website to have input city with distinct barangay and district.

How can I get pixelate the image with pixie.js?

I’m trying to build a function, so that the user when uploading their photos can have the option to pixelate their face and I’m using pixi.js with react, but it’s not working. I’ve modified the function in many ways but it always returns me

Unhandled Promise Rejection: TypeError: undefined is not an object (evaluating ‘this.renderer.canvas’)

Please, could someone help me with this?

import { Application, Assets, Sprite, BlurFilter } from 'pixi.js';
//Function to load image from file
const loadImageElement = (src) => {
    return new Promise((resolve, reject) => {
        const img = new Image();
        img.src = src;
        img.onload = () => resolve(img);
        img.onerror = (error) => reject(error);
    });
};
//Function to pixelate and blur face
export const pixelateFace = async (imageFile, detection) => {
    //Load image using created url from file
    const img = await loadImageElement(URL.createObjectURL(imageFile));

    const app = new Application({
        width: img.width,
        height: img.height,
        backgroundColor: 0x1099bb, 
        view: document.createElement('canvas'), 
    });

    document.body.appendChild(app.view);

    const sprite = Sprite.from(img);

    sprite.width = app.screen.width;
    sprite.height = app.screen.height;
    app.stage.addChild(sprite);

    const blurFilter = new BlurFilter();
    blurFilter.blur = 10;

    const { box } = detection;
    const { x, y, width, height } = box;

    const maskSprite = new Sprite();
    maskSprite.width = width;
    maskSprite.height = height;
    maskSprite.x = x;
    maskSprite.y = y;
    maskSprite.filters = [blurFilter];

    app.stage.addChild(maskSprite);
    sprite.mask = maskSprite;

    app.render();

    const canvas = app.renderer.extract.canvas(sprite);

    return new Promise((resolve) => {
        canvas.toBlob((blob) => {
            if (blob) {
                const pixelatedImage = new File([blob], imageFile.name, {
                    type: imageFile.type,
                });
                resolve(pixelatedImage);
            } else {
                reject(new Error("Error al convertir el canvas a Blob"));
            }
        }, imageFile.type);
    });
};

I hope to be able to pixelate the photo and return a file to replace the original.

This is the parent component:

import React, {useEffect, useState} from "react";
import {Button, message, Modal, Progress, Upload} from "antd";
import {PlusOutlined} from "@ant-design/icons";
import {router} from "@inertiajs/react";
import {sightEngine} from "@/Utils/constants.js";
import * as faceapi from "face-api.js";
import axios from "axios";
import {pixelateFace} from "@/Utils/pixelateFace.jsx";
import {useAppDispatch} from "@/Redux/hooks.js";
import {addNotification} from "@/Redux/Layout/notificationSlice.js";

export default function AddPhotosToGallery() {
    const dispatch = useAppDispatch();
    const [openModal, setOpenModal] = useState(false);
    const [fileList, setFileList] = useState([]);
    const [uploading, setUploading] = useState(false);
    const [modelsLoaded, setModelsLoaded] = useState(false);
    const [progress, setProgress] = useState(0);

    const MAX_FILE_SIZE_MB = 20;
    const MAX_FILE_COUNT = 10;

    useEffect(() => {
        const loadModels = async () => {
            const MODEL_URL = '/build/assets/models';
            try {
                await faceapi.loadTinyFaceDetectorModel(MODEL_URL);
                await faceapi.loadFaceLandmarkTinyModel(MODEL_URL);
                await faceapi.loadFaceRecognitionModel(MODEL_URL);
                setModelsLoaded(true);
            } catch (error) {
                dispatch(addNotification({
                    type: 'warning',
                    message: '¡Advertencia!',
                    description: 'No se han podido cargar los modelos de face-api.'
                }));
            }
        };

        loadModels();
    }, [dispatch]);

    const handleOpenModal = () => setOpenModal(!openModal);

    const beforeUpload = (file) => {
        const isLt20MB = file.size / 1024 / 1024 < MAX_FILE_SIZE_MB;
        if (!isLt20MB) {
            message.error("Cada archivo debe ser menor a 20MB.");
        }
        return isLt20MB;
    };

    const handleChange = ({ fileList }) => {
        if (fileList.length > MAX_FILE_COUNT) {
            message.error(`Solo puedes subir un máximo de ${MAX_FILE_COUNT} fotos.`);
            return;
        }
        setFileList(fileList);
    };

    const validateWithSightEngine = async (file) => {
        const formData = new FormData();
        formData.append("media", file.originFileObj);

        try {
            const response = await axios.post(
                `https://api.sightengine.com/1.0/check-workflow.json`,
                formData,
                {
                    params: {
                        api_user: sightEngine.api_user,
                        api_secret: sightEngine.api_secret,
                        workflow: sightEngine.workflow,
                    },
                }
            );
            const result = response.data;
            const isSuitable = !(result.nudity.sexual_activity > 0.1 ||
                result.nudity.sexual_display > 0.1 ||
                result.nudity.erotica > 0.4);

            return { file, isSuitable };
        } catch (error) {
            return { file, isSuitable: false };
        }
    };

    const loadImageElement = (file) => {
        return new Promise((resolve, reject) => {
            const img = new Image();
            img.src = URL.createObjectURL(file);

            img.onload = () => resolve(img);
            img.onerror = reject;
        });
    };

    const getTinyFaceDetectorOptions = () => new faceapi.TinyFaceDetectorOptions({
        inputSize: 160,
        scoreThreshold: 0.5,
    });

    const processImages = async (fileList) => {
        if (!modelsLoaded) {
            message.error("Los modelos de detección de rostros no están cargados aún.");
            return false;
        }

        for (const file of fileList) {
            const imageElement = await loadImageElement(file.originFileObj);

            console.time("Detección de rostro");
            const detection = await faceapi.detectSingleFace(imageElement, getTinyFaceDetectorOptions());
            console.timeEnd("Detección de rostro");

            if (detection) {
                const shouldPixelate = window.confirm("¿Quieres pixelar el rostro en esta foto?");

                if (shouldPixelate) {
                    file.originFileObj = await pixelateFace(file.originFileObj, detection);
                    message.success("El rostro ha sido pixelado correctamente.");
                } else {
                    message.info("El rostro no ha sido pixelado.");
                }
            } else {
                console.log("No se detectó rostro en la imagen");
            }

            const validation = await validateWithSightEngine(file);
            if (!validation.isSuitable) {
                message.error("Esta imagen contiene contenido explícito o desnudez.");
                return false;
            }
        }

        return true;
    };

    const handleUpload = async () => {
        setUploading(true);
        const isValid = await processImages(fileList);
        if (!isValid) {
            setUploading(false);
            return;
        }

        const formData = new FormData();
        fileList.forEach((file) => {
            formData.append("files[]", file.originFileObj);
        });

        router.post(route("upload.photos"), formData, {
            forceFormData: true,
            onFinish: () => setUploading(false),
            onProgress: (event) => {
                if (event.total > 0) {
                    const percent = Math.round((event.loaded / event.total) * 100);
                    setProgress(percent);
                }
            },
        });
    };

    return (
        <>
            <Button
                type="primary"
                className="bg-blue-500 hover:!bg-blue-600 font-bold"
                onClick={handleOpenModal}
                icon={<PlusOutlined />}
            >
                Añadir fotos
            </Button>
            <Modal
                open={openModal}
                onCancel={() => {
                    setUploading(false);
                    setFileList([]);
                    handleOpenModal();
                }}
                okText="Subir"
                okButtonProps={{
                    color: 'danger',
                    loading: uploading,
                    onClick: handleUpload,
                    disabled: fileList.length === 0
                }}
                maskClosable={false}
                width={1000}
                centered
            >
                <h2>Agregar fotos a la galería</h2>
                <Upload
                    customRequest={({ onSuccess }) => onSuccess()}
                    listType="picture-card"
                    fileList={fileList}
                    onChange={handleChange}
                    beforeUpload={beforeUpload}
                    multiple
                >
                    {fileList.length < MAX_FILE_COUNT && "+ Subir"}
                </Upload>
                {uploading && (
                    <Progress percent={progress} status="active" />
                )}
            </Modal>
        </>
    );
}

Better approach to check property of object with another property of object in same list

I have below list in javascript

[
 {
    "id" : 111,
    "type": "BASE",
    "someOtherProp": "other",
    "linkedId": 222
 },
 {
    "id" : 222,
    "type": "CHILD",
    "someOtherProp": "other",
    "linkedId": 111
 },
 {
    "id" : 333,
    "type": "SOMEOTHERTYPE",
    "someOtherProp": "other",
    "linkedId": 444
 }
]

I want to check in list if type === 'BASE', get the linkedId of matching type and check in same list if CHILD is present. If CHILD is not present then break the loop
and throw the exception or alert the message.

Note : BASE’s object linkedId is CHILD’s object id.

I am planning to use nested loop but looking a better approach if any for this.

Thank you.

Data in two different pages does not get updated immediately on UI with Redux Toolkit

I have a react 18 app and I am using Redux Toolkit for state management.

Below is an image of the blogs app on the users page. When I add/create a new blog on the blogs page, the new blog gets added properly. However, when I navigate to the users page the user in the box should have 3 blogs instead of 2. So, the number of blogs created by the user does not get updated immediately. But, when I reload the page the users page shows me the new number of blogs i.e., 3 for the user in the box.

enter image description here

QUESTION: How can I make the users page also reactive to when I add a new blog without doing a reload?

Below are some of my files.

blogReducer.js file:

import { createSlice } from '@reduxjs/toolkit';
import blogService from '../services/blogs';
import { setNotification } from './notificationReducer';

const blogSlice = createSlice({
  name: 'blogs',
  initialState: [],
  reducers: {
    setBlogs(state, action) {
      const sortedBlogs = [...action.payload].sort((a, b) => b.likes - a.likes);
      return sortedBlogs;
    },
    appendBlog(state, action) {
      state.push(action.payload);
    },
    increaseLikes(state, action) {
      const id = action.payload;
      const blogToVote = state.find((blog) => blog.id === id);
      const blogUpdated = {
        ...blogToVote,
        likes: blogToVote.likes + 1,
      };
      const blogList = state.map((blog) =>
        blog.id !== id ? blog : blogUpdated,
      );
      const sortedBlogs = blogList.sort((a, b) => b.likes - a.likes);
      return sortedBlogs;
    },
    removeBlog(state, action) {
      console.log('removeBlog action payload from blogReducer', action.payload);
      return state.filter((blog) => blog.id !== action.payload);
    },
  },
});

export const { setBlogs, appendBlog, increaseLikes, removeBlog } =
  blogSlice.actions;

export const initializeBlogs = () => {
  return async (dispatch) => {
    console.log('initializing blogs from blogReducer');
    const blogs = await blogService.getAllBlogs();
    dispatch(setBlogs(blogs));
  };
};

export const createBlog = (blog) => {
  console.log('creating blog');
  return async (dispatch) => {
    try {
      const newBlog = await blogService.createBlog(blog);
      dispatch(appendBlog(newBlog));
      dispatch(
        setNotification(
          { message: `SUCCESS: Added ${newBlog.title} blog.`, isError: false },
          5000,
        ),
      );
    } catch (error) {
      console.log('ERROR when creating blog: ', error.message);
      dispatch(
        setNotification(
          { message: error.response.data.error, isError: true },
          5000,
        ),
      );
    }
  };
};

export const voteBlog = (id, title) => {
  console.log('voting blog');
  return async (dispatch) => {
    try {
      const blogToVote = await blogService.getBlogById(id);
      console.log('voted blog', blogToVote);
      const blogUpdated = {
        ...blogToVote,
        likes: blogToVote.likes + 1,
      };
      await blogService.updateBlog(blogUpdated);
      dispatch(increaseLikes(id));
    } catch (error) {
      console.log('ERROR when voting blog: ', error.message);
      dispatch(
        setNotification(
          {
            message: `Blog '${title}' was removed from the server.`,
            isError: true,
          },
          5000,
        ),
      );
    }
  };
};

export const deleteBlogById = (id) => {
  console.log('deleting blog');
  return async (dispatch) => {
    try {
      await blogService.deleteBlog(id);
      console.log('deleted blog');
      dispatch(removeBlog(id));
    } catch (error) {
      console.log('ERROR when deleting blog: ', error.message);
      alert('Sorry! Blog was already removed from the server.');
    }
  };
};

export default blogSlice.reducer;

userReducer.js file:

import { createSlice } from "@reduxjs/toolkit";
import userService from '../services/users';

const userSlice = createSlice({
  name: 'users',
  initialState: [],
  reducers: {
    setUsers(state, action) {
      const sortedUsers = [...action.payload].sort();
      return sortedUsers;
    }
  },
});

export const { setUsers } = userSlice.actions;

export const initializeUsers = () => {
  return async (dispatch) => {
    console.log('initializing users from userReducer');
    const users = await userService.getAllUsers();
    dispatch(setUsers(users));
  }
}

export default userSlice.reducer;

loginReducer.js file:

import { createSlice } from '@reduxjs/toolkit';

const loginSlice = createSlice({
  name: 'loggedInUser',
  initialState: null,
  reducers: {
    setLoginUser(state, action) {
      return action.payload;
    },
    setLogoutUser() {
      return null;
    },
  },
});

export const { setLoginUser, setLogoutUser } = loginSlice.actions;

export default loginSlice.reducer;

App.jsx file:

import { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Route, Routes } from "react-router-dom";
import { initializeBlogs } from './reducers/blogReducer';
import { initializeUsers } from './reducers/userReducer';
import Notification from './components/Notification';
import LoginPage from './pages/LoginPage';
import BlogsPage from './pages/BlogsPage';
import UsersPage from './pages/UsersPage';
import MenuBar from './components/MenuBar';

const App = () => {
  const dispatch = useDispatch();
  const loggedInUserFromStore = useSelector(({ loggedInUser }) => loggedInUser);
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');

  useEffect(() => {
    if (loggedInUserFromStore) {
      dispatch(initializeBlogs());
      dispatch(initializeUsers());
    }
  }, [loggedInUserFromStore, dispatch]);

  return (
    <div>
      <h1>Blogs App</h1>

      <MenuBar setUsername={setUsername} setPassword={setPassword} />

      <Notification />
      
      <LoginPage 
        username={username} 
        password={password} 
        setUsername={setUsername} 
        setPassword={setPassword} 
      />

      <Routes>
        <Route path='/users' element={loggedInUserFromStore && <UsersPage /> } />
        <Route path='/' element={loggedInUserFromStore && <BlogsPage /> } />
      </Routes>
    </div>
  );
};

export default App;

BlogsPage.jsx file:

import { useRef } from 'react';
import Togglable from '../components/Togglable';
import NewBlogForm from '../components/NewBlogForm';
import BlogsList from '../components/BlogsList';
import { useDispatch } from 'react-redux';
import { createBlog } from '../reducers/blogReducer';

const BlogsPage = () => {
  const dispatch = useDispatch();
  const blogFormRef = useRef();

  const addBlog = (newBlog) => {
    blogFormRef.current.toggleVisibility();
    dispatch(createBlog(newBlog));
  };

  const blogForm = () => (
    <Togglable buttonLabel="new blog" ref={blogFormRef}>
      <NewBlogForm createBlog={addBlog} />
    </Togglable>
  );

  return (
    <>
      {blogForm()}
      <BlogsList />
    </>
  );
}

export default BlogsPage;

BlogsList.jsx file:

import { useSelector } from 'react-redux';
import Blog from './Blog';

const BlogsList = () => {
  const blogsFromStore = useSelector(({ blogs }) => blogs);

  return (
    <>
      <h1>Blogs</h1>

      <ul style={{ listStyleType: 'none', padding: 0 }}>
        {blogsFromStore.map((blog) => (
          <Blog key={blog.id} blog={blog} />
        ))}
      </ul>
    </>
  );
}

export default BlogsList;

Blog.jsx file:

import { useState } from 'react';
import PropTypes from 'prop-types';
import { useDispatch, useSelector } from 'react-redux';
import { deleteBlogById, voteBlog } from '../reducers/blogReducer';

const Blog = ({ blog }) => {
  // styles
  const blogStyle = {
    paddingLeft: 10,
    paddingRight: 10,
    paddingBottom: 10,
    border: 'solid',
    borderWidth: 1,
    marginBottom: 5,
  };
  const removeBtnStyle = {
    backgroundColor: 'lightblue',
    padding: 5,
    borderRadius: 10,
    fontWeight: 'bold',
  };

  const [viewBlogInfo, setViewBlogInfo] = useState(false);
  const dispatch = useDispatch();
  const loggedInUserFromStore = useSelector(({ loggedInUser }) => loggedInUser);

  const handleLikes = () => {
    dispatch(voteBlog(blog.id, blog.title));
  };

  const handleDeleteBlog = () => {
    const toDelete = window.confirm(`Delete "${blog.title}" blog?`);
    console.log({ toDelete });

    if (toDelete) {
      dispatch(deleteBlogById(blog.id));
    }
  };

  const toggleShow = () => {
    setViewBlogInfo(!viewBlogInfo);
  };

  return (
    <li style={blogStyle} className="blog">
      <p>
        <span data-testid="title">{blog.title} </span>
        <button onClick={toggleShow} className="viewHideBtn">
          {viewBlogInfo ? 'hide' : 'view'}
        </button>
      </p>

      {viewBlogInfo && (
        <div>
          <div>
            <div>Author: {blog.author}</div>

            <div>
              <span data-testid="likes">Likes: {blog.likes}</span>
              <button onClick={handleLikes} className="likeBtn">
                like
              </button>
            </div>

            <div>URL: {blog.url}</div>

            <div>Added by: {blog.user.name}</div>
          </div>

          <br />

          {blog.user.username === loggedInUserFromStore.username && (
            <div>
              <button
                style={removeBtnStyle}
                onClick={handleDeleteBlog}
                className="removeBtn"
              >
                remove
              </button>
            </div>
          )}
        </div>
      )}
    </li>
  );
};

Blog.propTypes = {
  blog: PropTypes.shape({
    id: PropTypes.string,
    title: PropTypes.string.isRequired,
    author: PropTypes.string.isRequired,
    url: PropTypes.string.isRequired,
    likes: PropTypes.number,
    user: PropTypes.shape({
      id: PropTypes.string.isRequired,
      name: PropTypes.string.isRequired,
      username: PropTypes.string.isRequired,
    }),
  }).isRequired,
};

export default Blog;

NewBlogForm.jsx file:

import { useState } from 'react';
import PropTypes from 'prop-types';

const NewBlogForm = ({ createBlog }) => {
  const [newBlogTitle, setNewBlogTitle] = useState('');
  const [newBlogAuthor, setNewBlogAuthor] = useState('');
  const [newBlogUrl, setNewBlogUrl] = useState('');

  const addBlog = (event) => {
    event.preventDefault();
    createBlog({
      title: newBlogTitle,
      author: newBlogAuthor,
      url: newBlogUrl,
    });
    setNewBlogTitle('');
    setNewBlogAuthor('');
    setNewBlogUrl('');
  };

  return (
    <div>
      <h2>Create a new blog</h2>

      <form onSubmit={addBlog}>
        <div>
          title:
          <input
            data-testid="blog-title"
            value={newBlogTitle}
            onChange={(event) => setNewBlogTitle(event.target.value)}
            name="title"
            id="blog-title"
          />
        </div>

        <div>
          author:
          <input
            data-testid="blog-author"
            value={newBlogAuthor}
            onChange={(event) => setNewBlogAuthor(event.target.value)}
            name="author"
            id="blog-author"
          />
        </div>

        <div>
          url:
          <input
            data-testid="blog-url"
            value={newBlogUrl}
            onChange={(event) => setNewBlogUrl(event.target.value)}
            name="url"
            id="blog-url"
          />
        </div>

        <button type="submit">create</button>
      </form>
    </div>
  );
};

NewBlogForm.propTypes = {
  createBlog: PropTypes.func.isRequired,
};

export default NewBlogForm;

UsersPage.jsx file:

import UsersList from "../components/UsersList";

const UsersPage = () => {
  return <UsersList />;
}

export default UsersPage;

UsersList.jsx file:

import { useSelector } from "react-redux";

const UsersList = () => {
  const usersFromStore = useSelector(({ users }) => users);

  console.log('<UsersList /> component - usersFromStore: ', usersFromStore);

  return (
    <>
      <h1>Users</h1>

      <table>
        <thead>
          <tr>
            <th>names</th>
            <th>blogs created</th>
          </tr>
        </thead>

        <tbody>
          {usersFromStore.map(user => (
            <tr key={user.id}>
              <td>{user.name || user.username}</td>
              <td>{user.blogs.length}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </>
  );
}

export default UsersList;

Passing blob URLs from main to renderer in Electron

I want to load images from paths given by the user (possibly modify them) and to display them using electron.
I used to use Electron many years ago and then it was quite easy since access to ‘fs’ node module was still unrestricted. Now, if I want to use ‘fs’ in the renderer I get error messages like this one:

Uncaught Error: Module “fs” has been externalized for browser compatibility. Cannot access “fs.existsSync” in client code.

So my solution was to load the file in the main process to a blob, create an URL for the blob and pass it to the renderer using IPC. This is a minimal example:

main.ts

export async function loadWebpFromFile(path: string) {
    const fileContents = await fs.promises.readFile(path, null);
    const webpImage = new Blob([fileContents], {
      type: 'image/webp',
    });

    return URL.createObjectURL(webpImage);
}

ipcMain.handle(
  'getWebpUrl',
  (event: any, path: string) => loadWebpFromFile(path),
);

preload.ts

contextBridge.exposeInMainWorld('fromMain', {
  getWebpUrl: async (path: string) => {
    return ipcRenderer.invoke('getWebpUrl', path);
  },
});

App.ts

const App = () => {
  const [webpUrl, setWebpUrl] = useState<string>("");
  
  useEffect(() => {
    (async () => {
      const webpUrl = await (window as any).fromMain.getWebpUrl ("C:\images\a.webp");
      setWebpUrl(webpUrl);
    })();
  }, []);
  return (
    <div>
      <h1>ElectroVite</h1>
      <img src={webpUrl} />
    </div>
  );
};

But I get the following error in the console:

Not allowed to load local resource: blob:nodedata:985f076a-f83d-4fb8-82e3-5826401afed1 localhost/:1

So how can I exactly access this URL. Why is it even blocked? Or perhaps should I use a completely different approach?

The example is based on the following Typescript-React-Vite boilerplaite:
https://github.com/guasam/electrovite-react

clipboard content doesn’t modifiy html input files array, why?

I have a form in which I let the user add up to 5 files in a way that I only show one <input type="file"> and if the user do put a file at the <input> then I update the form adding another input and so on. It works pretty well when I use the file picker and choose a file. But now I’m trying to add an option so the user can just CTRL+V an image clipboard content directly into the <input> field and then update the form as said before. I also added a loop verifying if the input fields do have content so the user don’t update, like, the 0th input and then a new input emerges for no necessity. And that’s the point that making my code fail as shown below:

<form class="form-post" role="form" method="POST" enctype="multipart/form-data" action="/someroute">
<!-- other inputs... -->
<input class="novo-post-form-item form-post-file-input" name="arquivos[]" type="file" onchange="addNovoInputFile(this, 5)">

</form>

on the page’s JS:

var addNovoInputFile = function(elem, max){
    fileInputs = $('.form-post-file-input');

    // When this loop is reached from a copypaste event, the content of fileInputs[0].files is empty
    // this doesn't happen when I just select a file from filepicker 
    for(var i=0; i < fileInputs.length; i++)
    {
        if(fileInputs[i].files.length === 0)
            return;
    }
    // I put this loop so I don't update the form in case, for example, if the user keeps updating a file at the 0th <input>
    // but maybe my solution is just changing the logic of this loop
    
    if(fileInputs.length >= max || elem.files.length === 0)
        return;
    
    var novoInput = "<div class="form-post-file-input-box">";
    novoInput += "<input class="novo-post-form-item form-post-file-input" name="arquivos[]" type="file" onchange="addNovoInputFile(this, " + max + ")">";
    novoInput += "</div>"
    
    $(novoInput).appendTo(".form-post #form-post-file-input-div");
};

// New event I just added in order to fill the input with a printscreen content in the clipboard
window.addEventListener('paste', e => {
    let inputs = document.getElementsByName('arquivos[]');
    let inputToChange = inputs[inputs.length-1];

    if(e.clipboardData.files.length > 0){
        inputToChange.files = e.clipboardData.files;
        addNovoInputFile(inputToChange, 5);
    }
});