Skip to content

Instantly share code, notes, and snippets.

Avatar

Cameron Nokes ccnokes

View GitHub Profile
@ccnokes
ccnokes / CacheStore.ts
Created Apr 12, 2021
A persistent, async store based on Cache API
View CacheStore.ts
/**
* A persistent, async store based on Cache API
* NOTE this is experimental
**/
type Options = {
name: string
version: number
userId: string
type: "json" | "text" | "blob"
View localeUtils.ts
function getMonths(locale: string, format: 'long' | 'short' = 'long') {
const monthNames = [];
const currentYear = new Date().getFullYear();
for (let i = 0; i < 12; i++) {
monthNames.push(
new Date(currentYear, i, 1).toLocaleDateString(locale, {
month: format
}));
}
return monthNames;
@ccnokes
ccnokes / heic_to_jpeg.sh
Last active Apr 15, 2021
Bash script that converts .HEIC to .jpg files
View heic_to_jpeg.sh
#!/bin/bash
set -eu -o pipefail
count=$(find . -depth 1 -name "*.HEIC" | wc -l | sed 's/[[:space:]]*//')
echo "converting $count files .HEIC files to .jpg"
magick mogrify -monitor -format jpg *.HEIC
echo "Remove .HEIC files? [y/n]"
@ccnokes
ccnokes / useCallIfMounted.tsx
Last active Apr 6, 2020
React hook that only runs a function if the component is still mounted. React says this is an anti-pattern, but vanilla Promises and async DOM APIs don't give you a way to cancel async actions 🤷‍♂️.
View useCallIfMounted.tsx
function useCallIfMounted() {
let isMounted = React.useRef(false);
React.useEffect(() => {
isMounted.current = true;
return () => isMounted.current = false;
}, [isMounted]);
return React.useCallback((fn) => isMounted.current && fn(), [isMounted]);
}
@ccnokes
ccnokes / memoize.ts
Last active Nov 1, 2020
A memoize function is only as good as its cache
View memoize.ts
// NOTE can't use lodash.memoize because we need 2 caches and v2 doesn't support replacing the cache implementation
// TS type note: the below type is inspired by Lodash v4 types
function memoize<T extends (...args: any) => any>(fn: T, resolver?: (...args: any[]) => any) {
let objectCache = new WeakMap<any, any>(); // WeakMap can only store objects as keys and allows the objects to get GC'ed out
let primitiveCache = new Map<any, any>(); // Map can store anything as a key but no auto GC
return function(...args: Parameters<T>): ReturnType<T> {
// just in case things got out of hand, dump the cache if too many entries
if (primitiveCache.size > 10000) {
primitiveCache.clear();
}
@ccnokes
ccnokes / check-for-electron-apps.sh
Last active Aug 15, 2021
Bash script that checks for apps that use Electron. Detailed a bit more here: https://cameronnokes.com/blog/how-to-know-if-a-desktop-app-uses-electron/
View check-for-electron-apps.sh
#!/bin/bash
target="${1:-/Applications}"
check() {
stat "$1/Contents/Frameworks/Electron Framework.framework" &> /dev/null
if [[ $? = 0 ]]; then
echo "$1 uses Electron"
fi
}
@ccnokes
ccnokes / ObservableMap.ts
Last active Nov 5, 2019
A Map class with built-in event emitter that emits a `update` event whenever something changes
View ObservableMap.ts
class ObservableMap<K, V> extends Map<K, V> {
readonly events = new EventEmitter();
set(key: K, value: V) {
let previousValue = this.get(key);
super.set(key, value);
this.events.emit("update", {
key,
action: "set",
previousValue,
currentValue: this.get(key)
View event-emitter.js
class EventEmitter {
constructor() {
this.target = new EventTarget();
}
on(eventName, listener) {
return this.target.addEventListener(eventName, listener);
}
once(eventName, listener) {
return this.target.addEventListener(eventName, listener, { once: true });
}
@ccnokes
ccnokes / event-emitter.js
Created Oct 30, 2019
Event emitter using the native DOM APIs: EventTarget and CustomEvent
View event-emitter.js
// Who needs eventemitter3, mitt, or some other library when you can use native DOM APIs? 😁
let eventEmitter = new EventTarget();
eventEmitter.addEventListener('test', console.log); // CustomEvent { type: 'test', detail: 123, ... }
eventEmitter.dispatchEvent(new CustomEvent('test', { detail: 123 }));
@ccnokes
ccnokes / wait-with-abortsignal.js
Last active Feb 12, 2021
Use AbortController to implement custom async task cancelling logic. Expounded upon here: https://cameronnokes.com/blog/cancelling-async-tasks-with-abortcontroller/
View wait-with-abortsignal.js
function wait(ms, opts = {}) {
return new Promise((resolve, reject) => {
let timerId = setTimeout(resolve, ms);
if (opts.signal) {
// implement aborting logic for our async operation
opts.signal.addEventListener('abort', event => {
clearTimeout(timerId);
reject(event);
});
}