Raccoon, a new digital wellbeing app

At the beginning of this year, I started having RSI-associated arm pain and started wearing a wrist brace. Coincidentally, around the same time, I was experiencing headaches and eye strain while using the computer.

My computer was hurting me! Most people who work on the computer a lot have experienced these problems. After seeing an optometrist and my GP, their advice was the same: take more breaks.

The problem is that when I’m working, I get really focused and often miss notifications. As much as I’d like to take breaks, the truth is that I won’t.

I determined to build an app to force me to take breaks. That app is Raccoon, and you can download it at raccoon.technology!

a window pops up over the desktop notifying you to take a break

Raccoon is simple: it sits in your tray and every 20 minutes, your computer screen fades to white for 30 seconds. You can take a quick break then get back to it, and you can’t miss it.

It’s built with Electron, a framework for building desktop apps with Node.js. It has a unique API for spawning and managing native windows that are actually mini browsers displaying local HTML files. Here’s a brief overview of Raccoon’s architecture:

 The main Electron process

An Electron app relies on a “master” Node process running in the background. This process spawns all the browser windows which are your actual user-facing application. An important note is that because this is just one process, it’s possible for it to get blocked, so heavy labor should be done in the browser processes.

const createTrayWindow = (url, optionsOverride = {}) => {
  let window = new BrowserWindow({
    width: 300,
    height: 300,
    useContentSize: true,
    show: false,
    resizable: false,
    frame: false,
    hasShadow: false,
    alwaysOnTop: true,
    transparent: true,
    webPreferences: {
      backgroundThrottling: false
    },
    ...optionsOverride
  });

  window.loadURL(url);

  return window;
};
const showWhiteWindow = () => {
  whiteWindow.showInactive();
  whiteWindow.webContents.send('start-break', BREAK_TIME);
};

const showTrayWindow = () => {
  showTray(tray, trayWindow);
  trayWindow.webContents.send('open-tray', BREAK_TIME);
};

const skipBreak = () => {
  whiteWindow.webContents.send('skip-break');
  trayWindow.webContents.send('skip-break');
};

 The browser windows (aka renderer processes)

Browser windows are often left idling in the background and made visible with a quick API call. Each window in my app points to a route in a React app that has these components:

class WhiteOut extends React.Component {
  ...

  show = (duration = (1000 * 30)) => {
    let seconds = duration / 1000;

    this.setState({
      style: {
        animation: `fadeio ${seconds}s linear`
      }
    });
  };

  componentDidMount() {
    ipcRenderer.on('start-break', (event, duration) => {
      // add animation class
      this.show(duration);

      // remove it
      this.timeout = setTimeout(this.hide, duration);
    });

    ipcRenderer.on('skip-break', () => {
      clearTimeout(this.timeout);
      this.hide();
    });
  }

  ...

 Packaging Electron + Create-React-App for Mac

Screen Shot 2018-11-10 at 10.22.05.png

There are a few great libraries like electron-builder that you can use to turn an Electron app into a .dmg or .exe file. I found it tricky to do this with React, which has a build process of its own, and especially with Create React App, which abstracts away a lot of Webpack/Babel configuration.

I followed this guide and made a few adjustments to let me use Create React App and keep my Electron code separated; I really didn’t want to put my Electron files in the public directory as suggested by the guide.

    "preelectron-pack": "npm run build && copyfiles -u 1 electron/** electron/**/** build",
    "electron-pack": "electron-builder",

This is my build process in package.json. First, I build the React app into the build directory. Then, I copy my Electron files directly into public. Finally, I run electron-builder, which is configured to look at build. It spits out OS images in a separate dist directory.

 Check out raccoon.technology

I also built a website for Raccoon! I used Gatsby, a new React framework for building static progressive web apps. It uses GraphQL to pull in the latest release from raccoon on GitHub and displays some copy from a headless CMS called Prismic.

Raccoon is one of the most complicated apps I’ve built yet. It’s largely because it’s effectively two apps, a “server” and a “client”, that are tightly interwoven and constantly communicate with each other. So while the end result is very straightforward, it took a lot of work to connect all the little pieces and even to get it built and packed as a Mac app!

Next, I’m going to add calendar support to perform the same kind of “forceful reminder” when you have a meeting. I’m also going to try expanding support to Windows and Linux. Give it a try and if you have feedback or ideas, let me know on Twitter!

 
3
Kudos
 
3
Kudos

Now read this

Big Increments

As it goes, my blog went quiet the day I started hardcore job searching! Since then I started my first engineering gig at Ribbon, turned 26, and finished my move back to NYC. Now that I’ve been a dev for a few months and haven’t turned... Continue →