Tilt train to Maryborough & what I miss about Australia

I’m on the train to Maryborough now. At the last minute I decided to get the tilt train straight to Maryborough West rather than the slower, cheaper Gympie North train then drive the extra 45 minutes north.

I’ve spent the past week camping on Ben’s floor and as much as I love and appreciate that man, I’m ready for… camping on my parents floor. Okay, not much has changed there, but I’ll have a room to myself and I won’t be in the way all the time.

Living out of a suitcase is hard y’all! I wasn’t expecting it to be amazing, but after three weeks now I’m just about ready to head back to Amsterdam. I’m leaving on Sunday and I’m sure I’ll have changed my mind by then.

I’m of two minds about heading back to Amsterdam. On one hand it’s where my home is, but on the other I’ve lately not been enjoying my time there for reasons I can’t really put my finger on. I think the thing I miss the most is being able to roam anywhere and do as I please; in the Netherlands it’s either too cold or too crowded for outdoor activities. The second the sun comes out the entire country is out in force to sunbathe in city parks and dine crammed together on narrow city streets.

The other thing I’ve really missed is the food. I had a nightmare the other night that I went to a Dutch restaurant and they served croquette on mashed potato, the whole speaking indecipherable Dutch. I miss pub food, Asian cuisines, fresh vegetables. Arnott’s Shapes ffs. The only thing interesting about the food in Amsterdam is how expensive it is.

That’s not to say I’m not enjoying my time in Europe, but you know. I don’t think I’m going to stay there forever. I just don’t know where forever is going to be (or how long, given our current climate).

Yeah, I’m being a bit of a downer. I’m just exhausted at the moment, so hopefully a few nights with my parents should cheer me up.

Singapore

Singapore was fun. I landed in Changi Terminal 2, and took the underpass to the MRT, where I found I needed to withdraw cash. A quick escalator to Terminal 3 and a Starbucks OJ later, I was downstairs again at the MRT.

The ticket machine was not good. The EFT terminal was broken or not in use, and it would only accept up to ten dollar notes. Thankfully my OJ split my fifty and I had enough cash to get me to the hotel.

The MRT trip made me feel all kinds of nostalgia. It was mostly empty, but I got to (kinda) watch the sun rise through the window. I had to transfer from the airport line at Tanah Merah, at which point the MRT was super full and I stood up most of the way.

As we arrived closer to the centre, things started to get really familiar. The thick jungle grass, the ERP tolls, the checkerboard kerb painting. This is the Singapore I remember.

My hotel was a block or two from the metro station and it was a great relief to drop my bags and check out the amazing views from the tower.

That morning I headed out and immediately stumbled on a Japanesse style pancake place, probably the most extravagant pancakes I’ve had in my life despite living in Amsterdam. Afterwards I realised that most places in Singapore only opened after 10am and changed my plan to just walk around the city.

I saw a lot of cool stuff including Raffles Hotel and the Fountain of Wealth, but probably the highlight of my day was heading to Giant Hyperfresh, a giant supermarket with a LOT of cool Asian goodies.

raffles

Heading into the trip, one of the pieces of advice I received was to take it slowly. Spend a few hours out, then retreat to the aircon to recover. I think my morning trip was too ambitious after the plane flight because I was thoroughly shattered once I got back to my hotel.

That evening after a lot of water and several hours napping, I headed out to Clark Quay, had a chicken noodle dinner, and walked the length of the quay taking time lapses and admiring the city.

merlion(1)

As it was only a brief trip, the next day was my last day to see the city.

I started by heading out to Kampong Glam to check out the famed photogenic streets. I poked around the shops, and avoided the temptation to pick up tat, and thoroughly enjoyed the cosy streets, intense decorating, and colourful street art. I hadn’t been to this part of town the last time, and it shifted my perspective away from wanting to see the same old things to discovering new and exciting bits.

I found a nice Turkish restaurant to have breakfast. More specifically the man out the front gesturing to the menu caught my attention, then the laid back outdoor dining and large industrial fans maintained it. I was looking for breakfast, so the Menemen sounded great (it’s like shakshouka). The wait was fairly long, but it was worth it to sit in the shade and watch the hustle and bustle of the street. The meal itself was delicious and I would definitely recommend the place if you’re in the area.

After that I wandered a bit more, checking out the street art and the local shops, and finally the 7-Eleven for some fluids in the form of aloe drink.

sim lim

The other thing I wanted to check out from my youth was the Sim Lim Square electronics emporium thing. It was a few blocks walk in the beating sun, but once I got there it was an immediate air conditioned blast.

Sim Lim was exactly what I remembered, except instead of PC components it was a lot more mobile accessories. My favourites were the vast displays of USB fans, and the little upmarket photography/videography shop.

I only ended up buying one thing, which was a nice pink aluminium iPad stand for my mum for Christmas.

After this I walked back to the hotel, realising I’d barely travelled a few blocks in my entire stay. There’s so much to do in Singapore, apparently, and I wouldn’t mind spending more time there in the future.

Retro nostalgia & why my new website looks like Window 9x

For a while I’ve been wanting to update my website, but I’m really not a designer and I knew any attempts to improve on what I already had would be a haphazard mess.

I was looking for a new job as a React developer and really wanted to hone my skills, so I thought what better way than to build a new site in React?

As for design… why not pay homage to one of the most influential operating systems of my youth: Windows 9x. And for fun, why not make it all fit on a floppy disk.


The rise of retro nostalgia

Windows 9x is the loose name for the operating systems from Windows 95 through ME. They were pretty shoddily built on top of MS-DOS and kinda sucked. But they were revolutionary at the time, and we didn’t know better.

The design aesthetic, particularly in the Windows 98 era was something to behold.

In present day, retro tech is really making a comeback. One of my favourite examples of this is Paul Verbeek-Mast’s horrible excellent website which was kind of an inspiration for me through my design process.

But there are plenty of other amazing examples of retro nostalgia including the gorgeous poolside.fm streaming radio, and this fun game concept:

I spoke about this stuff at the October QueerJS meetup in Amsterdam.


It’s running on a floppy disk you guyz

Ultimately the entire site is designed to fit on a 3.5″ floppy disk, attached to a Raspberry Pi running nginx, sitting on the shelf under my TV.

That means the entire site is 1.44 mb (or less) at any given time, and served to you straight from the ’90s.

The site is using Hexo to render out the static content, which includes a bunch of custom theming to make the data hook together nicely.

It’s also using Netlify for builds and Cloudflare as a CDN, so chances are you’ll never actually have to wait for the magnetic drive to spin up. But you never know! I get a little thrill out of that.

Update: this is back on Netlify while I’m at Fronteers Conference since I don’t have time to put the pi back together.


React & open source

This site was largely built with Preact (A fast 3kB alternative to React with the same modern API). The content is built with Hexo then progressively enhanced, so you can disable javascript (with the skip link for accessibility, or in the Start menu just for kicks) and the site still mostly works.

The interface is inspired by the more nostalgic bits of Windows 98 and ME, which were my operating system of choice in my more formative years.

If I’m honest, this was a terrible choice because the (p)react lifting state/render model is not great for large applications like this, and I led myself into an architecture that’s super inefficient and hard to maintain. But at this point I dont care, it’s working pretty well.

The UI components and some of the apps have been released on Github as a library called ui95. It’s a bit rough but you can use the library to create your own sites, apps, or just as a learning tool. Interestingly Artur Bień has been working on a parallel component library of Windows 95 styled components as well, so that’s probably worth a check-out too.

Some apps were built by third parties, including Paint and originally I was planning on including Webamp but it was too big to fit in my size budget. You can check each app for license information.


Where to from here?

Not sure. I’d like to post more on my blog and maybe find a local computer group.

But in seriousness, this was a fun project and I learned a lot putting it together. I hope you get some inspiration out of it and bring back a little of the whimsy in the retro web.

The reason I cancelled my love affair with Google Home

In 2018 I wanted to buy a Google Home because I was working at ABC News on chatbots and figured immersing myself in the voice assistant hype would give me a better perspective on how to create for them.

A Google Home sits on a table in a home

Maybe I could write an app! Or at least understand better how they could fit into people's lives. I was never especially convinced of the broader applications, but it was cheap enough so I figured it couldn't hurt.

After buying a second for the bedroom and using it for a year and a half, I finally tipped over the edge and given up on the platform for good.

What's so good about Google Home?

Ultimately voice assistants have different use cases for everyone.

One of my friends uses an Alexa for music and managing the contents of their fridge. Another uses it to control home automation (and terrorize the cat).

Personally my main uses were checking the (variable Dutch) weather and asking about the time. The latter actually surprised me, it's super useful when you're in the shower or in the dead of night and don't want to open your eyes to look at a clock.

The problem was that aside from turning on and off my lights, it really wasn't doing much for me. I'm not interested in radio, could never get podcasts working, and the news is easier to read online. On top of that, finding apps on Google Home is downright impossible.

I'd say outside of Google's main offering there were no killer apps. It's not a great ecosystem.

Then the bugs

From the get-go I couldn't use the full set of functionality because I have a Google Apps account rather than a plain old Google account.

For a while I could create calendar events, but that feature disappeared unceremoniously one day. I couldn't send messages, dictate emails, or receive notifications and there was no real integration with any major Google features. I'm not sure how much of this was due to my Apps account, and how much was just missing features in general.

But the thing that frustrated me the most was the reliability of the system. In the past few months it seems to have completely tanked.

At various points I've had the assistant light up and start listening for no reason at all, switch to another gender and accent, and more recently it stopped recognizing devices on my network like my TV and smart lights.

As an isolated event this was frustrating, but the frequency it was happening killed my faith in the system.

Web & App Activity

One of the biggest sticking points for me was that for Google Assistant to work, you needed to enable Web & App Activity on your Google account.

Google app activity

This is an all-encompassing feature that logs all your interactions with Google, including searches. It's not just for your voice assistant.

I was initially hesitant to turn this on because it's super creepy having your Google searches stored in perpetuity, especially when dealing with sensitive or embarrassing topics. But I did it because I wanted the hardware assistant to, you know, actually work.

While you can delete Web & App Activity from the My Activity site, it was still kinda chilling and I started using a lot more tools like Duck Duck Go, more private windows, and Firefox Focus (a private browser for mobile which I highly recommend).

But last month after a period of Google Assistant constantly misunderstanding, getting things wrong, and at one point playing loud rock music instead of white noise in the middle of the night, I decided to turn off Web & App Activity and see what happened.

Using Google Assistant without Web & App Activity enabled

Spoiler: not much changed when I turned off Web & App Activty, which surprised me a little.

When I first started using the device this was a mandatory feature, and it wouldn't work without it. But it seems they've some done work on making the hardware devices work without logging enabled.

That said, it wasn't perfect. I lost access to my third party integrations such as LIFX lights and Chromecast controls so it wasn't a complete solution and made the devices pretty useless beyond just the time and weather.

Quitting Assistant

After a few weeks of this, I decided Google Assistant was not worth the hassle and unplugged all my devices.

The news that Google, Microsoft, Apple and Amazon all have fairly lax privacy mechanisms in place around recordings certainly helped.

So my two Google Home Minis have been sitting on my bedside table for the past week and I'm not sure what to do now.

I like the idea of voice assistants and would consider getting another in the future, but right now the Google Assistant isn't very useful. I'm not a big fan of Amazon, and I hear Siri is pretty useless too so I don't hold out hope on things changing any time soon.

From my brief question on Twitter, it doesn't seem like many others are using them for much more than basic tasks. While race to the bottom in terms of price and smartphone ubiquity has contributed to these things being in everyone's homes, I genuinely wonder where the voice assistant revolution is heading from here.

Why are React PropTypes inconsistently named?

I'm reasonably new to PropTypes in my React code and I'm always messing up the naming.

Sure "number" and "string" are easy enough, but why are "function" and "boolean" in a different format to all the others?

PropTypes cheat sheet

According to the Typechecking with PropTypes article the following types are available:

array primitive type
bool primitive type
func primitive type
number primitive type
object primitive type
string primitive type
symbol primitive type
node Anything that can be rendered: numbers, strings, elements, etc.
element An instance of a React component
elementType An element constructor (I think)
instanceOf(x) An instance of class _x_
oneOf([
  'News',
  'Photos'
])
One of the given values
oneOfType([
  PropTypes.string
])
One of the given types
PropTypes.arrayOf(
  PropTypes.string
)
An array of the given types
PropTypes.objectOf(
  PropTypes.number
)
An object with certain property types
shape({ 
  a: PropTypes.string
})
An object of a given shape
PropTypes.exact({
  a: PropTypes.string
})
An object that exact matches the given shape

Why "func" and "bool", not "function" and "boolean"?

I'm always tripped up on the spelling of "func" and "bool". Mainly because the rest of the PropTypes use full names whereas these two don't.

After asking on Twitter, a few folks suggested it might be to avoid Javascript symbol names

But that still didn't answer the question because while "function" is a reserved token in Javsascript, "boolean" definitely isn't.

Eg. assigning to function throws:

> const function = 'error';
Thrown:
const function = 'error';
      ^^^^^^^^

SyntaxError: Unexpected token function

But assigning to Boolean is totally fine:

> const boolean = 'truly an allowed keyword';
undefined
> boolean
'truly an allowed keyword'
> Boolean(boolean)
true

Further, these tokens are both allowed in an object definition:

> const ParpToots = { function: 1, boolean: 2 }
undefined
> ParpToots.function
1

The plot thickens

I wasn't really happy with the answers I was getting, so I did some Googling.

The search came up empty until I stumbled on this question on the PropTypes Github issue tracker from 2017:

Hi, I've searched a bit in the Readme and in the issues here, I did not find why we do not use Proptypes.function and Proptypes.boolean like we do for object (vs. obj), etc.

Why the shortnames? Are they reserved words? If not, it would be nice to create aliases maybe for these two ones no?

Which was followed up a few hours later with the answer:

Yes, you can't do const { function, bool } = PropTypes in JS because they're reserved words.

Which… is a little more satisfying.

Except we've already shown boolean isn't a reserved word. So what's going on? 🤔

boolean: a reserved word in ES3

Having found the reason why PropTypes doesn't use boolean, I needed to connect the dots. Why is it considered a reserved word?

I eventually landed on the MDN Docs on Javascript lexical grammar which lists the full set of reserved words for Javascript, as well as some previously reserved words from older specs.

And wouldn't you know; there's boolean sitting in a list of "future reserved words" from the ECMAScript Language Specification edition 3, direct from the year 2000.

7.5.3 Future Reserved Words

The following words are used as keywords in proposed extensions and are therefore reserved to allow for the possibility of future adoption of those extensions.

abstract enum       int       short
boolean  export     interface static
byte     extends    long      super
char     final      native    synchronized
class    float      package   throws
const    goto       private   transient
debugger implements protected volatile
double   import     public

The bingo card of Javascript features

Looking at the list there's a good mix of keywords that eventually made it into the spec. const, class, import, all big ticket items.

"boolean", however, was eventually removed from the list and is no longer reserved.

I'm not sure what it would have been for, but alongside "int" and "short" you could wager it was intended to be part of a fully typed Javascript spec.

In fact, peering through history I found a bunch of resources around typed Javascript as early as 2000 (Microsoft had an optionally typed implementation of JScript for .NET 🤯), and there's some fascinating papers from around 2005 that talk about what sounds a lot like modern day Typescript.

Whatever alternate history we avoided, "boolean" is no longer a reserved word. Regardless, it left its legacy on the PropTypes package and many a Failed prop type: prop type is invalid error in our consoles.

Vue & React lifecycle method comparison

🤔 This is a slightly older post. It deals with Vue 2 and React class components. This is probably not what you need if you’re building a new app today.

React and Vue both have fairly well defined lifecycle events which we can use to successfully navigate the mysteries of the virtual DOM.

So without further ado, let’s get down to the React vs Vue lifecycle events smackdown!

Vue and React fighting in an animated fashion. A caption reads "Bam!"

Vue lifecycle events visualised

The following demo logs out the Vue lifecycle events when a component mounts and updates.

It’s actually a fairly nice API in that everything is consistently named, even if not all of the events are strictly useful.

Vue lifecycle events on codepen

React lifecycle events visualised

React is actually the more esoteric of the two in terms of naming, but actually offers more powerful functionality (such as my particular favourite, shouldComponentUpdate).

Vue lifecycle events on codepen

Component mount compared

The basic workflow for a component is pre-mount → render → mount.

Vue has more events, whereas React is more Javascripty with an actual ES constructor.

ReactVueDescription
constructorbeforeCreateRoughly synonymous with each other. The constructor sets up the React class, whereas Vue handles the class creation for you.
dataSet data. Vue recursively converts these properties into getter/setters to make them “reactive”.
createdData observation, computed properties, methods, watch/event callbacks have been set up.
beforeMountRight before the mounting begins: the render function is about to be called for the first time.
getDerivedStateFromPropsInvoked right before calling the render method. It should return an object to update the state, or null to update nothing.
renderrenderThe virtual DOM is rendered and inserted into the actual DOM.
componentDidMountmountedThe component is now mounted. We can make any direct DOM manipulations at this point.

We can see from our lifecycle that the perfect time to hook into the process is once the component has been mounted (in React’s componentDidMount or Vue’s mounted event).

Component update compared

Component update generally follows a pre-update → render → updated workflow. Easy!

ReactVue 
getDerivedStateFromPropsSame as when mounting.
shouldComponentUpdateLet React know if a component’s output is not affected by the current change in state or props. We can use this to prevent React blowing away our changes.
beforeUpdateCalled when data changes, before the DOM is patched.
renderrenderThe virtual DOM is rendered and patched into the actual DOM.
getSnapshotBeforeUpdateRight before the most recently rendered output is committed to the DOM. Lets you save the previous state of the DOM for use after the component has updated.
componentDidUpdateupdatedAfter the DOM has been updated

Component unmount compared

When your component is removed from the page, sometimes you need to remove event handlers or clean up after any manual DOM manipulation.

ReactVueDescription
deactivatedWhen using Vue keep-alive, the component is removed from the page but not destroyed so that we can load it again later without the overhead of component mount.
activatedThe previously deactivated component is reactivated.
componentWillUnmountbeforeDestroyWhen a component is being removed from the DOM
destroyedThe component is completely gone.

Handling errors

This is something I’ve not looked too much into, but it’s possible to catch errors from child components and change the render accordingly.

This would be most useful for a top-level component (above the routes, maybe) to show an “Aw Snap” error message into your app and stop the error bubbling up.

ReactVueDescription
componentDidCatch
getDerivedStateFromError
errorCapturedAn error occurred in a child component.

Conclusion

Each has their own benefits, neither is objectively better or worse. Personally I prefer the Vue naming, but prefer the power of the React API.

After pulling this info together I’m really interested to try out Vue’s keep-alive for render-intensive jobs. It’s a cool feature I didn’t know existed.

I’m also excited to play with component-level error handling, especially for larger apps. It makes a lot of sense to catch errors in the framework rather than waiting for them to bubble up to the global error handler 😅

Anyway, hope this was helpful. I learned something.

Is this thing on?

I'll be honest, I'm just trying to see if I can post posts from Netlify CMS. Nothing much to add right now, so here's some fun tweets I like.

Hey, I moved to Europe


It happened almost by accident that I moved overseas.

I've always had it in the back of my head that I'd like to work internationally at some point in my life. Just a year ago I was honoured to be invited to speak at a conference in Amsterdam, and It was my first time leaving the Asia/Pacific region. It was a total blast and was really nice to make new friends around the world, but it kinda set a few ideas going in my head.

So when I saw an off-hand remark from a school friend about an opportunity to work in the Netherlands, I followed it up nonchalantly. This started a chain reaction that ended with me selling all my stuff, renting out my apartment, and moving to another country with little more than a general sense of confidence things would work out.

So here I am. I'm in bed at my temporary hotel, having just got home from a night of drinking with my surprisingly international (and incredibly boozy) coworkers, after my third week at the company. Things have generally been pretty good.

I'm still working my way through a bunch of issues (currently trying to get Suncorp Bank to let me make a damn bank transfer), but after three weeks things are generally looking good. I am especially excited to move into my new apartment at the start of next month, so it will be nice to have a place to call home again.

There's no set plan for what I'm doing, but from here I am looking forward to making new friends, getting to know the city, and going even further in my free time to explore the rest of Europe.

To make things a little more fun I've been trying to keep a video blog. You can keep updated by subscribing on youtube if you're interested in that sort of thing.

Aren’t we just about due another chat platform?

When I was at school the mobile phone went from a luxury item to something that was affordable and even kinda essential.

I remember that Nokia cycled from orange backlights all throught the colours of the LED rainbow until eventually you could get them in white, which was really fancy among the kids at school.

I don't recall which came first for me: IRC or ICQ.
But either way, back in the early 2000s when text messaging still cost 25 cents a pop, the freedom and possibility of being available 24/7 was super exciting (as long as the dial-up connection hadn't crapped out).

I'd stay up all night just chatting to people, and for an antisocial teen it was an amazing enabler.

But there were too many apps

Chat networks sprung up like weeds, everyone was writing them. Even I had a crack, which is to say played around with Visual Basic building what was a terrible (but in my memory quite slick looking) GUI app.

Eventually folks came up with the incredibly smart idea to reverse engineer a bunch of chat protocols and build an app that could connect to them all at once.

As the first of many apps to do this, I loved Trillian to bits (but as a skeezy high school student never managed to pay for it). Eventually I moved to Linux and Pidgin was good enough, so I ended up using that for yeeeaaarrrrss before it fell into obscurity.

Obscurity because after a period where all the disparate chat apps started federating and talking to one another, all the old guard died off and got replaced by a new and vastly less interoperable bunch of chat apps.

Which is where we are now.

Chat apps suck, and I'm so over it 🙄

Facebook (and by extension Messenger) have been tarnished by the stink of their inappropriate data collection and sharing. A lot of my friends use Instagram, but I doubt anyone is going to trust Facebook again.

Slack just shut down their IRC/XMPP gateways leaving you to use the slow, bloaty Electron app that barely works. No joke, I have to close it whenever I boot a VM because it uses so much RAM.

Twitter is trying their hardest to destroy all the goodwill of the early adopters by plastering ads and featured content in the timeline and push notifications, while at the same time killing third party API support. This is a burning platform, and I'm done with it.

Whatsapp is just plain ugly. I have at least one friend boycotting it at the moment, otherwise this would be a contender for the messaging platform most of my friends use.

Signal security is questionable at best, with at least two exploits this year that I know of, and a frustrating dependence on Electron. I've had so many issues with the Android app in the past, I don't think it's worth anyone's time.

iMessage only works on iOS and MacOS. It's the height of arrogance.

On top of these, the remainder of things I've used in the past couple of months are Discord, Hangouts (lol), Microsoft Teams, various hook-up apps (I'm only human), Skype, Meetup, and of course regular trusty old text messaging. There are jsut too many things.

I don't even know

Twitter has been my go-to messenger for a little while now. I wanted it to be the universal SMS of the Internet, but right now they're more focused on trying to be the World Cup news hub (side note: exploding head 🤯 should really be a ligature so you can join it with other emojis such as rolling eyes 🙄).

The other day I cracked it and uninstalled it from my phone. Which leaves the question of which trade-off messaging app do I use to talk to my friends?

In hindsight this is kinda why I think Google's play to extend on SMS is fundamentally so smart. If it's on everyone's phone by default and works out of the box, what impetus is there to install another app?

So despite being burnt by buggy Android group texts as recently as last month, I'm just about ready to go all-in on SMS. Relive the glory days of standards and interoperability with a service I'm paying for.

So, you know. Send me a text. (International fees and roaming charges may apply)

My Twitter ad blocking experiment, DOM manipulation in someone else’s React app

Twitter as a platform is pretty neat. Twitter as a company… has its problems.

A while back they started showing ads into my timeline, which is something I'm really not here for. I would gladly pay a fee not to have that because I love the platform, but y'know. Corporate bullshit 🙄

So I've been taking it out on the advertisers audacious enough to target me, by blocking them. Apple? Blocked. Amazon? Blocked. Intel? You betcha you're gonna git blocked.


Despite my best efforts it got to the point where I was getting way too many ads to keep up with, so I decided to write a script to do it automatically.

TL/DR: I just want to install the ad blocker extension

There's an extension you can install to auto-block Twitter advertisers (providing you're using the mobile site). You can get this for:

Update 2019: I've taken it down because it stopped working.

Automating actions in someone else's react site 🤔

I mainly use the mobile Twitter site because it's way faster than desktop, but it's one of those sites that use post processing to munge class names. So instead of seeing nice
<div class="tweet"> HTML, you get something more akin to <div class="rn-1oszu61 rn-1efd50x rn-14skgim rn-rull8r []…]">

This makes it insanely difficult to automate the process of finding an ad and
blocking it. I'm not sure what ad blockers are doing, but this requires some
pretty specific DOM selection to get working.

There's two approaches you could take:

  1. Loop through all the <div> elements on the page until you find one with the
    text you're looking for. Eg. "promoted".
  2. Use weirdly specific selectors that get the job done, almost by chance.

Despite making fun on Twitter I chose the latter, because Twitter uses inline SVG elements, which means we can find promoted tweets by querying for the presence of certain SVG paths. It's completely absurd and I think React is criminally negligent for making this a standard practice.

Here's the two main selectors I'm using to find interface elements on Twitter mobile:

// The "promoted icon"
const adSelector =
  'path[d="M20.75 2H3.25A2.25 2.25 0 0 0 1 4.25v15.5A2.25 2.25 0 0 0 3.25 22h17.5A2.25 2.25 0 0 0 23 19.75V4.25A2.25 2.25 0 0 0 20.75 2zM17.5 13.504a.875.875 0 1 1-1.75-.001V9.967l-7.547 7.546a.875.875 0 0 1-1.238-1.238l7.547-7.547h-3.54a.876.876 0 0 1 .001-1.751h5.65c.483 0 .875.39.875.874v5.65z"]';

// The dropdown chevron
const dropdownSelector =
  'path[d="M20.207 7.043a1 1 0 0 0-1.414 0L12 13.836 5.207 7.043a1 1 0 0 0-1.414 1.414l7.5 7.5a.996.996 0 0 0 1.414 0l7.5-7.5a1 1 0 0 0 0-1.414z"]';

The remainder of the extension is fairly straightforward. Find stuff, click stuff, you know the deal. If you're interested in having a play with it yourself, you can check it out and give it some stars on Github.