Today I Learned...

#10   Vue3: handle page refresh (F5) in your application

Posted: 01.12.2020

I recently noticed that there was an issue with cross device data in WATCH3R. Adding/removing list items on one device would not update the app’s (cached) state on another device. Logging out and back in would resolve this issue, but refreshing the page wouldn’t.

After a bit of research, I found out that there wasn’t really a “best practice” on how to handle page refresh in Vue, so I simply tried some things that seemed promising.

What I ended up with is the following:

const updateList = () => {
  if (mode.value) { // double check 'mode' here, just in case
    store.dispatch('list/readList', mode.value);
  }
}

onMounted(() => {
  setTimeout(updateList, 250) // timeout is required here; otherwise 'route.meta.mode' is undefined
})

I placed this code directly in App.vue, which is the entrypoint of my application and gets re-loaded on page refresh. route.meta.mode controls which list’s data to load - I decided against re-loading all list data, as that wouldn’t be in line with the app’s caching strategy.

#9   GoTrue Admin methods

Posted: 26.11.2020

Something I noticed trying to get a list of my app’s users from Netlify’s Identity service.

It’s not a big challenge to write the necessary function (see: GitHub readme), but what it doesn’t mention (clearly enough…) is that this admin function needs to be deployed to your site to work.

I struggled for a bit, looking for mistakes that weren’t there while receiving 401 authorization errors…

NB: deploy the admin function/s, keep the UI to use them offline and disable the function when not needed anymore (I resorted to renaming it function.js.bak just in case I need it again in the future).

Update Dec. 3rd 2020: I created a pull request and the readme (see link above) was updated.

#8   Vue3: <select> default value

Posted: 06.11.2020

Cost me more time than I anticipated, esp. due to the fact that it only happened for production builds.

So, in order for a <select> (with an Object-based v-model) to properly display its default value in Vue 3 (3.0.2), this had to be done:

<select ... v-model.lazy="selected" @change="update(selected)">
  <option disabled :value="{}" :selected="selected === {}">Select Something...</option>
  <option v-for="(item, index) in list" :key="index" :value="item">{{ item.title }}</option>
</select>

The :selected="selected === {}" was necessary, as the <select> would otherwise display as a blank box once the v-for rendered <option> elements came in.

Again, this only happened in production, local dev builds did not behave that way which made it extremely frustrating to debug.

Probably worthy of further investigation and a report to the Vue team, but I don’t have time to try and get a reproducible example done at the moment.

#7   Vue3: "click outside" directive

Posted: 10.10.2020

Directives are quite different in Vue 3 - see: Vue 3 docs

Here’s what I came up with for a simple “close an element (modal etc.) when a click outside of this element registers” directive. Clicks on elements within the target element and elements with the class click-outside-ignore will be ignored.

The function/method to close the element is registered as binding.value, so when using it on a component, it should look like this: <Component v-click-outside="closeComponent" />

let handleOutsideClick = null;

app.directive('click-outside', {
  beforeMount(el, binding, vnode) {
    handleOutsideClick = (e) => {
      e.stopPropagation();
      if(!el.contains(e.target) && !e.target.classList.contains('click-outside-ignore')) {
        binding.value();
      }
    }
    document.addEventListener('click', handleOutsideClick);
    document.addEventListener('touchstart', handleOutsideClick);
  },
  beforeUnmount() {
    document.removeEventListener('click', handleOutsideClick);
    document.removeEventListener('touchstart', handleOutsideClick);
  }
});

#6   Hugo: RSS feed duplication

Posted: 01.09.2020

Checking Google Search Console earlier this week made me notice that Hugo created duplicates of my RSS feed in strange places like /tags/feed.xml and /tags/tagname/feed.xml.

After a quick look at the Hugo documentation, I found out that this behaviour seems to be the default for pages of the kind taxonomy and term - seems I forgot about that or never even noticed it in the first place…

Conclusion: I had to change the [outputs] section of my config.toml from this:

[outputs]
    home = ["HTML","RSS"]
    section  = ["HTML"]

To this:

[outputs]
    home = ["HTML","RSS"]
    section  = ["HTML"]
    taxonomy  = ["HTML"]
    term  = ["HTML"]

#5   Android: use ADB to uninstall bloatware

Posted: 14.08.2020

I recently got the Android 10 update and had to factory reset my phone thanks to the phone’s language settings not sticking anymore. Factory reset unearthed all the bloatware again that I had gotten rid of years ago, so I also had to do that again.

Here’s a quick reminder of how to do that:

  1. Get ADB platform tools for your OS
  2. On your phone: enable developer mode and USB debudding
  3. Connect phone
  4. Test if connected successfully and allow debugging (will be prompted): adb devices
  5. adb shell to get into the phone
  6. List packages with pm list packages | grep 'pkg.name.etc' or search for them like this: pm list packages -f TESTPKG
  7. Uninstall bloatware like this: pm uninstall -k --user 0 com.android.google.youtube
  8. See bloatware disappear 😁

Based on this guide at xda: How to Uninstall Carrier/OEM Bloatware Without Root Access

#4   Shell Script: launch VS Code for a specific folder/repository

Posted: 24.07.2020

I wanted to have an easy way to launch VS Code for specific repositories and came across the code PATH-TO-REPO command.

This little shell script will open a specific folder/repository based on an argument given to it or based on a directory listing of the /home/user/Repos folder:

#!/bin/bash

# get param
repo=$1

if [ -z "$repo" ]
  then
    # check first
    ls /home/USER/Repos/

    # ask user for input
    echo -n "which repo: "
    read repo
  else
    echo "opening "$repo
fi

code /home/USER/Repos/$repo

I’ve also configured an alias for that script; simply typing lcode REPO-NAME now opens VS Code for that folder/repository.

#3   Vue2: handle component's click event in its parent

Posted: 17.07.2020

Use case: a component creates nothing but a <button> with a slot that handles its display state internally but hasn’t got any actual functionality; the handler method is defined in the parent.

In this case, @click.native has to be used to call the method defined in the parent:

// in parent component

<cButton @click.native="theMethod(praram)">Button Name</cButton>

#2   Manjaro Linux mount script: BitLocker encrypted external drive

Posted: 12.07.2020

Got a new external USB 3.0 drive and set up BitLocker encryption. Manjaro (Dolphin) couldn’t really do anything with it, kept prompting for the pwd but didn’t mount it.

I ended up writing my own mount script that works well so far:

#!/bin/bash

# check first
lsblk -f

# ask user for input
echo -n "input details (fstype bitlocker): "
read device
read -sp 'Magic powder: ' mgk

# mount according to user input
sudo fusermount -u /media/data/drive/ # unmount in case it's still mounted for some reason
sudo dislocker -V /dev/$device -u$mgk -- /media/data/drive
sudo mount -o loop /media/data/drive/dislocker-file /media/data/drive-mnt

echo mounted drive

It lists connected block devices first (lsblk), so it’s not hardcoded to sda/sdb in case that ever changes. Need selection of whatever mentions BitLocker, then asks for the encryption key/pwd and proceeds to mount the drive with dislocker. Folders used in /media/data/ need to exist before the script is executed.

#1   Manjaro Linux: .service file error when setting up KeeWeb

Posted: 10.07.2020

Installed KeeWeb on Manjaro and got the following error:

The name org.freedesktop.secrets was not provided by any .service file

Resolution: had to install a package called gnome-keyring.