Skip to content

Instantly share code, notes, and snippets.

View ccnokes's full-sized avatar

Cameron Nokes ccnokes

View GitHub Profile
@ccnokes
ccnokes / CacheStore.ts
Created April 12, 2021 22:40
A persistent, async store based on Cache API
/**
* A persistent, async store based on Cache API
* NOTE this is experimental
**/
type Options = {
name: string
version: number
userId: string
type: "json" | "text" | "blob"
@ccnokes
ccnokes / wait-with-abortsignal.js
Last active February 12, 2021 13:50
Use AbortController to implement custom async task cancelling logic. Expounded upon here: https://cameronnokes.com/blog/cancelling-async-tasks-with-abortcontroller/
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);
});
}
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 / preload-example.js
Created February 1, 2017 03:03
Electron preload script
// in preload scripts, we have access to node.js and electron APIs
// the remote web app will not have access, so this is safe
const { ipcRenderer: ipc, remote } = require('electron');
init();
function init() {
// Expose a bridging API to by setting an global on `window`.
// We'll add methods to it here first, and when the remote web app loads,
// it'll add some additional methods as well.
@ccnokes
ccnokes / memoize.ts
Last active November 1, 2020 18:31
A memoize function is only as good as its cache
// 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 / app-with-storage.js
Created September 17, 2016 22:14
Sample electron app demonstrating how to save user data to a file.
const { app, BrowserWindow } = require('electron');
const path = require('path');
const Store = require('./store.js');
let mainWindow; //do this so that the window object doesn't get GC'd
// First instantiate the class
const store = new Store({
// We'll call our data file 'user-preferences'
configName: 'user-preferences',
defaults: {
@ccnokes
ccnokes / vanilla-online-offline.js
Last active September 22, 2020 02:35
Online/offline emitter in vanilla JS
function createOnlineEmitter() {
let cbs = []; //array of registered callbacks for the event
let unsub; //function for removing the main event listener
//this is the main event listener that gets registered with window.online/offline event
const mainListener = (isOnline) => {
//call all the subscribed callbacks
cbs.forEach(cb => cb(isOnline));
};
@ccnokes
ccnokes / url-search-params-utils.js
Last active June 23, 2020 23:31
Utils for URLSearchParams
function objToParams(obj) {
const params = new URLSearchParams();
for(let key in obj) {
if(Array.isArray(obj[key])) {
obj[key].forEach(item => params.append(key, item));
}
else if(typeof obj[key] === 'object') {
continue;
}
else {
@ccnokes
ccnokes / event-emitter.js
Created October 30, 2019 15:50
Event emitter using the native DOM APIs: EventTarget and CustomEvent
// 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 / useCallIfMounted.tsx
Last active April 6, 2020 16:38
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 🤷‍♂️.
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]);
}