Category Archives for computers

Install Docker on Ubuntu 20.04

That’s an easy and quick think to do!

sudo apt update
sudo apt install docker.io

# start docker now, and on system reboot
sudo systemctl start docker
sudo systemctl enable docker

# check the version
docker --version
  Docker version 19.03.8, build afacb8b7f0

Don’t forget to add your user to the docker group, otherwise you’ll get permission denied errors

docker ps
  Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.40/containers/json: dial unix /var/run/docker.sock: connect: permission denied

To add your account to the docker group:

sudo usermod -a -G docker paul

Don’t forget to logout and login again!

Typescript: Not all constituents of type ‘boolean | (() => void)’ are callable

The full error:

This expression is not callable.  Not all constituents of type 'boolean | (() => void)' are callable.    Type 'false' has no call signatures.  TS2349

This is the code that was triggering the error:

export const useLoading = (defaultValue: boolean = false) => {
    const [loading, setLoading ] = useState(defaultValue)

    const startLoading = () => setLoading(true)
    const stopLoading = () => setLoading(false)

    return [
        loading,
        startLoading,
        stopLoading
    ]
}

CAUSE: the return of the array. Having arrays without a specific type gave me this error as wel: Line 0: Parsing error: : Cannot read property ‘map’ of undefined. The solution was to typehint the array.

SOLUTION: don’t return the array just like that, add ‘as const` at the end of that return statement.

export const useLoading = (defaultValue: boolean = false) => {
    const [loading, setLoading ] = useState(defaultValue)

    const startLoading = () => setLoading(true)
    const stopLoading = () => setLoading(false)

    return [
        loading,
        startLoading,
        stopLoading
    ] as const
}

This is the post that showed me where to find it: https://github.com/microsoft/TypeScript/issues/35423

Line 0: Parsing error: : Cannot read property ‘map’ of undefined

the error displayed: Cannot read property 'map' of undefined

If you’re using React and typescript, try these few actions:

  1. Reinstall the dependencies: rm -rf node_modules; yarn install
  2. Remove the cache that’s inside the build directory: remove the build directory and restart the node service
  3. This did it for me (and Trat Westerholt) : don’t type your variables/constants as ‘bare’ arrays:

This failed:

// don't do this - a 'bare' array as type
const customers: [] = []

SOLUTION: It was fixed by:

// add a type to the array
const customers: string[] = []

Add typescript to your React project

If you’re working with a Create React App (CRA) and you want to add typescript to it, run the following command:

yarn add \
  typescript \
  @types/node \
  @types/react \
  @types/react-dom \
  @types/react-router-dom

Now, change the file you want to use typescript in to have the extension .tsx

Add toast messages to your React app

It’s nice to have good looking messages in your app. You can have this too with react-toastify.

react-toastify example

To have these toast messages in your app, do the following:

yarn add react-toastify

Then, in your toplevel file (sth like index.js or App.js, etc.);

import React from 'react';

  import { ToastContainer, toast } from 'react-toastify';
  import 'react-toastify/dist/ReactToastify.css';
  
  function App(){
    const notify = () => toast("Wow so easy !");

    return (
      <div>
        <button onClick={notify}>Notify !</button>
        <ToastContainer />
      </div>
    );
  }

NGINX is not showing PHP 500 error log (nor does php-fpm)

Problem

I received a 500-error on my API request to a PHP backend. The result was returned as a 500 html status.

When searching the logs, there was no mention of this 500 error, other than in the access log of nginx.

Where does nginx or php-fpm put the backtrace of PHP errors?

Solution

Turns out, you need to enable catch_workers_output = yes in your www.conf file, typically located over at /etc/php/7.4/fpm/pool.d/

# file /etc/php/7.4/fpm/pool.d/www.conf
...
catch_workers_output = yes
...

The comments above that line explains it pretty well:

; Redirect worker stdout and stderr into main error log. If not set,
; stdout and stderr will be redirected to /dev/null according to
; FastCGI specs.
; Note: on highloaded environement, this can cause some delay in
; the page process time (several ms).
; Default Value: no

Add multiple languages to your React app with i18next

Install the needed packages

Install i18next

yarn add i18next \
  react-i18next \
  i18next-http-backend \
  i18next-browser-languagedetector

Configuration

Add Suspense

Wrap your code with <Suspense /> like the following:

// file: src/index.js
(...)
ReactDOM.render(
    <Suspense fallback="loading">
        <Router>
            <Switch>
                <Route 
                  exact 
                  path='/login' 
                  component={Login}
                />
                <Route component={App}/>
                <ToastContainer/>
            </Switch>
        </Router>
    </Suspense>,
    document.getElementById('root')
)

Initializing i18next in your app

Place the following initilisation code in a file i18n.js

// file: ./js/helpers/i18n.js
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import Backend from 'i18next-http-backend'
import LanguageDetector from 'i18next-browser-languagedetector'

i18n
  .use(Backend)
  .use(LanguageDetector)
  .use(initReactI18next) // passes i18n down to react-i18next
  .init({
    fallbackLng: 'en',
    debug: false,

    backend: {
      loadPath: '/locales/{{lng}}.json',
      // path to post missing resources
      addPath: '/locales/add/{{lng}}.json',
    },

    interpolation: {
      escapeValue: false, // not needed for react as it escapes by default
    }
  });

export default i18n;

The parser

Add the config file in your root directory: ./i18next-parser.config.js

This will:

  • put the translatable strings inside ./public/locales/<language>.json
  • support en and nl countrycodes for language
  • use the key (the translatable string) as the translation when a new translatable string is found
// file: ./i18next-parser.config.js
module.exports = {
  
  // see below for more details
  lexers: {
    // hbs: ['HandlebarsLexer'],
    // handlebars: ['HandlebarsLexer'],

    htm: ['HTMLLexer'],
    html: ['HTMLLexer'],

    // mjs: ['JavascriptLexer'],
    js: ['JavascriptLexer'], // if you're writing jsx inside .js files, change this to JsxLexer
    ts: ['JavascriptLexer'],
    jsx: ['JsxLexer'],
    tsx: ['JsxLexer'],

    default: ['JavascriptLexer']
  },

  locales: ['en', 'nl'],
  // An array of the locales in your applications

  output: 'public/locales/$LOCALE.json',
  // output: 'locales/$LOCALE/$NAMESPACE.json',
  // Supports $LOCALE and $NAMESPACE injection
  // Supports JSON (.json) and YAML (.yml) file formats
  // Where to write the locale files relative to process.cwd()

  useKeysAsDefaultValue: true,
  // Whether to use the keys as the default value; ex. "Hello": "Hello", "World": "World"
  // This option takes precedence over the `defaultValue` and `skipDefaultValues` options

  verbose: true,
  // Display info about the parsing including some stats
}

Scan the code for translatable strings

Install the scanner application with npm.

npm install -g i18next-parser

Assuming that you placed the config file in the root of your project (i18next-parser.config.js), run the following command:

i18next --config ./i18next-parser.config.js \
    'src/**/*.js' \
    '!src/js/bootstrap.min.js' 

--config provide the path to your config. In this example it is not needed as the configuration has the default name and is in the default location
src/**/*.js provide the glob which finds all your *.js files. Add what you need (.ts, .jsx, etc.)
!src/js/bootstrap.min.js an example of how to exclude a certain file

Now you can find the translatable strings in the following files: ./public/locales/<language>.json

Change the language in the application

Change the language with i18next

// file: ./index.js
import './js/helpers/i18n'
import i18next from 'i18next'

...

// change the language
i18next.changeLanguage('nl')

Install oh-my-zsh on Mac with iterm2

When I followed the guides I found online on this topic, this was what I ended up with:

zsh with powerline showing squares with questionmarks

Clearly, something went wrong. The zsh shell was showing squares with questionmarks in it.

After fixing, this is what oh my zsh looks like in iterm on my Macbook Pro running macOS Catalina.

oh-my-zsh now showing icons correctly

Follow the instructions on this page to get the same result.

Install oh-my-zsh

Directions taken from: https://github.com/ohmyzsh/ohmyzsh

sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

Set the the theme

I used the powerlevel9k theme from here: https://github.com/Powerlevel9k/powerlevel9k/wiki/install-instructions

git clone https://github.com/bhilburn/powerlevel9k.git ~/.oh-my-zsh/custom/themes/powerlevel9k

Now select this theme in your ~/.zshrc file

ZSH_THEME="powerlevel9k/powerlevel9k"

Install the fonts

This is the solution to get rid of the squares with questionmarks in your oh-my-zsh installation.

It appears that the used font in the powerline uses characters which need a special / patched font. See: https://github.com/powerline/fonts

git clone https://github.com/powerline/fonts
cd fonts
./install.sh

Now configure your iTerm profile to use the Droid Sans Mono for Powerline font.

Go to Preferences > Profiles > Text > Font and select (or type) the Droid Sans Mono for Powerline font.

Sort an array in javascript by property of one of the object elements

Say you have an array consisting of objects. And you want to order that array by one of the properties of these objects.

Take this array:

[
    {
        id: 4,
        title: "Lorem ipsum",
        created: "2020-04-10T14:59:00+00:00"
    },
    {
        id: 2,
        title: "Iptum quanto",
        created: "2020-05-29T13:17:48+00:00"
    },
    {
        id: 1,
        title: "Dolor samet",
        created: "2020-05-29T13:16:20+00:00"
    },
    {
        id: 3,
        title: "Tenari fluptum",
        created: "2020-05-29T13:08:39+00:00"
    }
]

Let’s sort it by the string created property of each element

const orderedChapters = chapters.sort((a, b) => {
  return a.created.localeCompare(b.created))
}

OR sort it by the numerical id property of each element

const orderedChapters = chapters.sort((a, b) => {
  return (a.id > b.id) ? 1 : ((b.id > a.id) ? -1 : 0)
}