Sunday, May 31, 2020

adding git branch info to macos zsh prompt, and using pico

I'm trying to get a more fluent with command-line git, and it's pretty easy to get zsh to show you the current git branch as your prompt. Here's my new ~/.zlogin:

alias ls="ls -F"

autoload -Uz vcs_info
precmd() { vcs_info }

# Format the vcs_info_msg_0_ variable
zstyle ':vcs_info:git:*' formats ' 🛰 %b'

# Set up the prompt (with git branch name)
setopt PROMPT_SUBST
PROMPT='${PWD/#$HOME/~}${vcs_info_msg_0_} 🚀'
I like using emoji as concise and obvious at a glance prompt delimiters.

Also I have to be sure to set a default editor I am comfortable with:
git config --global core.editor "nano"
Interesting that emacs doesn't ship with MacOS, but I'm not so enamored of it that I'm going to seek it out. Never got my mind around the modality of vim. nano/pico feel kind of clunky and almost DOS-ish, but for the small number of tasks where it's not worth firing up Visual Studio Code, it's fine.

Saturday, May 30, 2020

for people considering a career in ux

I was contacted on LinkedIn by a person thinking of doing a UX Designer bootcamp. And I have another friend going down the same route.

I know it's easy to think of the career choice as "UI for people who are too intimidated by front end programming" but that's not fair - there are a lot of skills a good fulltime UX designer has that I only have in a rudimentary form (my current job title of "UX Engineer" not withstanding... I do fear they are using the aspirational sense of UX vs UI vs the in the trenches one.)

My partner M. got a bit frustrated with her role as UX Researcher.  She finds Nobody told me UX would be like this to be pretty accurate. It's not meant to scare people off, but to have realistic expectations - many places don't embrace UX expertise as much as they should, since often it can run counters to the "gut knowledge" of developers and business planners.

A Day In The Life Of A UX Designer is a bit more sanguine.

Also M says if you're in Boston, check out some of the meetups (not sure if they are still operating in quarantine times in a virtual form or what) - UXPA BostonBostonCHI, and Ladies That UX Boston.

Friday, May 29, 2020

combining css modules, sass, and context providers for swappable themes with locally scoped css

Esay Silva's How to use Sass and CSS Modules with create-react-app does a great job of setting up Sass with create-react-app and then using them as CSS modules... node-sass does a good job of letting your create-react-app created react app use Sass - if you name a css file Foo.module.css or Foo.module.scss, it gets automagically locally scoped classnames, so your react code can be like

import BrandStyle from "./GreenCo/Theme.module.scss";

and then later 

<button className = {BrandStyle.btn}>Assuming .btn was the original class name</button>
The css sent to the browser will prefix .btn with a unique bit of randomness, so you can safely have lots of components with css code that refers to "btn" and they won't interfere with each other in the browser.

There's nothing too magic about that object you get from the import (the one I'm calling BrandStyle here) - it's just a map from the name as it appears in your original .scss or .css to the name it shows up as in the css that is sent to the browser. (And you can further refer to multiple class names, either with the lightweight classnames utility or just by gluing various parts together with spaces.)

But if you're thinking in terms of a customizable Component Library where you would like developers to be able to override styles of some basic components, but without passing in the overridden styles or CSS to every child, you can do it via context.

I was  rusty on React Contexts - at first I thought by wrapping a segment of JSX/Dom in a Context, each child would get new properties automagically... nope! "Context" is its own beast and doesn't use props to do its thing.

It seems best to make up a trivial custom Context object that can be referenced both by the parent / grandparent app or component as well as the children that will be reading from it...

ThemeContext.js:
import React from "react";
const ThemeContext = React.createContext();
export default ThemeContext;

and then use it as a Provider in the parent:

App.js
import BrandStyle from "./GreenCo/Theme.module.scss";
import ThemeContext from "./core-ux/ThemeContext.js";
//... 
      <ThemeContext.Provider value={{ brandstyle: BrandStyle }}>
        <header>
          <PushButton>Button by Theme....</PushButton>
        </header>
      </ThemeContext.Provider>

and then refer to it in the child:

PushButton.js:
import styles from "./PushButton.module.scss";

import ThemeContext from "./ThemeContext.js";

const PushButton = (props) => {
  const theme = React.useContext(ThemeContext);

  const comboStyle = `${styles.btn} ${theme.brandstyle.btn}`;

  return (
    <button
      className={comboStyle}
//...

So here we see how it can have its own local styles in PushButton.module.scss, grab an additional style from the ThemeContext, and then combine them in a string. (In the actual code I do more checking to make sure theme.brandstyle exists.)



making a new mac feel like home

When I replaced my 2012-era MacBook Air with one of the new models I used Apple's Migration Assistant - good results in the end.

With my new work laptop (mercifully one of the new MacBook Pros with a proper escape key) I was setting it up from scratch, so here were some notes on the quality of life improvements I make, most of which I've written about before:

Settings I fix up:
  • Add a "Quick Action" for convert to jpg (for HEIC images bumped over from iPhone)
  • Make Paste and Match Style the system wide default for cmd-V - especially noticeable for the Stickies application
  • Change the default Screenshot location to ~/Downloads
  • Arrange the Finder sidebar - I do favor ~/Downloads as my generic "temp" space so it makes sense to make it higher in the shortcut list there.
  • Get all that extra crap out of the Dock and make it automatically hide
  • Make Terminal automatically close when I exit the shell ( Go to Terminal|Preferences|Profiles|Basic/Default and select “When the shell exits:”)
  • Finder I set to always show extensions (2024: Go to Finder | Settings | Advanced, click Show all filename extensions)
  • I don't like most of the "More Gestures" under Trackpad settings except for Mission Control (3 finger swipe up to see all windows) and I enable App Exposé (3 finger swipe down for just the windows of the current app - not sure why that's disabled by default.)
  • MacOS' default shell is now zsh. I fire up `pico ~/.zlogin` and set the following:
    alias ls="ls -F"
    PROMPT='%~🚀'
    That shows me which directory entries are directories themselves, and then a nice minimalist prompt with a condensed version of the path, and the rocketship (from Edit | Emoji & Symbols) reminds me I'm on my home system.
Some Apps I add:
  • SizeUp and HyperSwitch - I set up opt-cmd-arrows to bounce around windows like a boss with the keyboard in SizeUp, and HyperSwitch restores sanity to switching to Terminal or Finder - opening a window if there aren't any there, just like clicking on the Dock.  
  • Add the convenient menu bar status menu quick note apps Tot and Tyke - (also for the first time I noticed I can use cmd-drag to rearrange that top bar! My employer has a bunch of VPN and file sharing stuff that has to live there but I can shunt it off to the side)
  • Add Haptic Touch Bar, switch over to traditional button like controls, and shove them over to the side where they won't be accidentally brushed... (still so foolish that a finger brush is the same as a click for the touch bar!)
Common Apps I need to tweak:
  • In Chrome I like the "Humble New Page Tab" for getting a view of my bookmarks, and adding the React plug in etc.
  • Install Visual Studio Code... I like the “Quiet Light” Color Theme, and then I've been using plugins Prettier - Code Formatter (making sure format on save is on), ESLint, ES7 React/Redux-Native snippets, Babel JavaScript - and I disable auto-completion of quotes and braces

Anyway, nice to have reference to the little things that make a Mac more comfortable for me.


Sunday, May 24, 2020

interesting post roundup

After this last round of job interviews, I want to be paying more attention to trade articles and what not. And it seems to make sense to post them here, even though part of me says "well shouldn't people just go to sources I got them from?" But this blog can both A. serve as a curation for other people (honestly there's a lot of stuff especially on Medium that feels a little questionable) and B. this blog has always been notes for my future and present self - future self for the obvious "in case I think of it and want to find it" and present in the sense that maybe making a link will help me remember the article in the first place.

So I'll stop apologizing for blogging, and get on with it...

Stop Writing Reusable React Components has a great point, that pre-engineering for reusability is often counter-productive. (It reminds me a bit of my anti-"buy" lean in make vs buy; sometimes using a toolkit just for one part of its featureset is like buying a whole house because you loved the modern kitchen.) Also, I'd say if the choice comes between "reusable but complex enough it well-justifies its unit tests" vs "a one-off simple enough that there's not much that can go wrong", i.e. few moving parts, choose the latter. (And amusing third hand line from the article:  To steal a line from Chantastic over at React Podcast, my code was “so DRY it chaffed.”) (here's sort of a counter-argument, or maybe a case in point; what are components doing where it would be worth handling overrides vs letting people make their own bespoke copy?)

Why You Should Be Storing Remote Data in a Cache (and Not in State) seems a pretty good answer to a question I've been thinking about, where to put data if you're not using a central store ala Redux or MobX... this pattern basically has a key of the ajax request, caches the result, and then has components rerender if the value returned from the server is different.

Learning a bit about tagged templates in this piece on styled components - boy, I am not sure "tagged templates" pull their weight - yet another damn bit of syntactic sugar to learn, with oddly crude implementations - but maybe I just haven't seen their sweet spot yet.

UPDATE:
Other things: Verdaccio is an easy way of running a local npm repository.

How to use Sass and CSS Modules with create-react-app is just what it says on the tin - great when you want to have safe and modular CSS via Sass.


Saturday, May 23, 2020

pretty good video on js most used tricks

I think I'm due for thinking more about querySelector() as well as asynch/await all that goodness.




Also I kind of like the thinking behind this custom hook to manage UI state as status - starting with the UI trope of "loading w/ spinner, then show results, or empty, or error"

Wednesday, May 20, 2020

second-guessing the modern web

Apparently making the rounds, Tom MacWright's Second-guessing the modern web.

I've been talking with many folks doing my job interviewing (mercifully drawing to an end) as well as doing a small pile of mini-projects, and the whole question of what makes a good back end and what makes a good frontend is very much on my mind, and I appreciate this semi-contrarian view.

I wonder if I'm a little insensitive to render performance, or if some people are too uptight about it. Even the level of React's Virtual DOM - my new online card game does something like declarative rendering, but just raw, and for smallish updates there's no sense of flicker, even though the whole UI is being erased and built back up.

(See also Rich Harris' rebuttal)

Tuesday, May 19, 2020

some useful react libraries

5 Awesome React.js Libraries You Should Know About. The toaster popup especially seems useful. Also this one on React Animation Libraries. I still think declarative languages are less friendly towards graceful transitions in general; re-renders natively jump from snapshot to snapshot so it takes some wrangling to think of things as persistent and changing.

Monday, May 18, 2020

#foolish-idea-friends, or how to make company slack culture fun and creative

One of the things I miss most from my old job was a certain slack channel - "#foolish-idea-friends" (channel name changed to protect the guilty.)

The heart of the slack channel was an ever-growing number list of foolish ideas - concept pitches. A few basic rules were established from the outset: no criticism of ideas were permitted... just because an idea was foolish didn't mean it was "bad"... ideas had to be original as far as we knew to get a number (later a habit of bringing in ideas from elsewhere with the tag "#SEFI" (for "someone else's foolish idea") instead of a number)

One of the fellow pioneers of the channel had written bots to scrape the ideas (one Foolish Idea was to make a "best of" book) and sent me a list of my 1000+ contributions. I had done over a quarter of the almost 4,000 the channel was up to upon my departure!

FIF #600 gives an idea of what the channel was most often used for:
  • #600 categorization system for f-i-f; semi-serious but possibly outlandish proposals, less serious proposals just for a laugh, ones added quickly to grab a big number, thinly veiled complaints about stuff in the office, general-joshing on each other, other meta-stuff
So, I read through all my entries and here were my very favorites, in roughly descending order of how much I liked 'em:
  • #1500 new html6 attribute: onlick. Like onclick, but you know, for licking things. Steve Jobs said "We made the buttons on the screen look so good you'll want to lick them." and I think modern web browsers should be ready for this.
  • #321 formation of a committee to determine the plausibility of "aggressive passive" behavior; for example, furiously hammering water
  • #212 A Street Fighter-like game but with the characters from Peanuts (Snoopy, Charlie Brown, etc)
  • #1465 Trying to declutter but Marie Kondo is too pile-centric or mumbo-jumbo-y? Arrange all your possessions in a long straight line ordered by how much you want each item, make a perpendicular line at the cut off point, and discard everything to left. DONE AND DUSTED.(Note, you may still have to dust, especially around those shelves where the cluttering items used to sit.)
  • #1576 If you have a device or thing in your life you really like and use a lot, like a laptop or iPad or video game system or something, have a loved one wrap it up so you can open it christmas day and think about how much you like it.
  • #823 A service where university professors will adopt your child so they can get free tuition at that university
  • #2565 A post-mortem service to excise and preserve specific body parts for rhetorical purposes. For example, a wedding finger for a loved one to keep, or a middle finger to send to a person you despised in life, or maybe your whole butt for a hated organization to kiss. The body part can be cremated, preserved in resin, or bronzed.
  • #2226 Life in a zero-G or microgravity environment (like on an orbiting station or a spaceship that is not manuevering) offers many challenges. When you're exercising on the treadmill or just sitting at a work console, you need to strap yourself down with elastics and velcro. Exhaled CO2 silently pooling around your head is a constant threat if the air circulation system isn't perfect. And if you lose momentum in a large open area, like thanks to air resistance, it can be nearly impossible to get moving again. The solution to all of these is clear: astronauts should always wear old-school propeller beanies at all times when not in their helmets.
Some of my ideas I'd like to see for real:
  • #1368 every damn smoke and carbon monoxide detector should have a small lcd or e-ink screen that will you tell you its status. no more of this stupid "let me communicate through infuriatingly intermittent chirps". i mean seriously
  • #1474 a film crew/biography service that parents can reserve for their young kids. Every year for your unbirthday (6 months opposite your birthday) they come and do a "day in the life" taping, as well as have their writers talk to you and write up what's on your mind and what your life is like
  • #3470 make tombstones out of wet cement and let people at the funeral scratch in their final messages to the deceased
  • #887 All light switches should have a tiny LED to show if it's on or not. (In some rooms it's obvious, but when the light switch is on the other side of the closed door...)
  • #1471 an app to help remind you to keep in contact with friends you don't see regularly. Enter your friends names, and how important they are to you, and it will create a queue of people and nudge you when you should send a "just saying hi" message
  • #1588 First Class Deluxe Junk Retirement. Trying to declutter, but stuck with stuff with sentimental value, and/or with no second-hand value? First Class Deluxe Junk Retirement will come to your house to pick it up, take a photo and send it to you, and then put it in a special mausoleum landfill. For example, my "Euclid High School English Departmental Award" medal with ribbon, from 1992. I will never do ANYTHING with this, right? And yet it's weirdly hard to get rid of. Ditto my heavy duty Varsity Jacket with built in cape/hood.
  • #1438 One of my best FIFs ever - ok - so our cat has a "food puzzle" where you put kibble in a bunch of little plastic obstacles so the cat can't just chow down, but has to work a bit for each piece. It's meant for exercise, I guess, besides the mental stimulation factor, but it would be MUCH better if you could pour the kibble into some kind of little home base thing and then have little robo mice, each carrying a single platter that could hold just about one piece of kibble, and the cat could chase each bit, knock it to get the kibble, and then it would go back to the home base to get another piece...
  • #355 see-through lids for toilets so you can ensure all the material was whisked away instead of leaving it as a gross surprise for the next person, and without aerosolizing the gross stuff either. (Or possibly a sophisticated sensor system to take of reflushes automagically)
Of course, some FIFs I ended up doing for real:
  • #1073 learn that little 'waiting' step b-boy dancers do right before they launch into something truly impressive. but never do a fancy move, just do the step over and over
  • #768 Make an MP3 of silence or someone going "that's the end!" and name the tune early in the alphabet, so when iOS has decided it has run out of audio to play and for some reason starts playing "all songs alphabetically" you don't get blasted by Michael Jackson going "A B C, easy a 1 2 3" ALL THE DAMN TIME
  • #1515 out of churros? try this delicious salty, sweet, crispy, chewy dessert: take a chocolate from the front desk (preferably dark or choc w/ caramel) and put it between two tortilla chips!
  • #2758 new drink: Apple X-Cider -- take one cup of company cider, drink while sucking on an Atomic Fireball candy
Some of my thinly-veiled complaints:
  • #270 default slack avatars that are flash GIFs that trigger epileptic seizures so users have a moral and possibly legal obligation to change them
  • #391 gamify gamification! every scoring rule you invent for whatever you are gamifying gets you a G.G. Star!
  • #485 Start postfixing updated versions of functions etc with "New". This is super great for sites that have "New" and "Used" cars, so people get to enjoy making up heuristics like "if the New is a prefix, it's probably about new cars, but if New is a postfix, it's probably just a later version of a previously existing function"
Sometimes I just went for the gag or pun:
  • #2718 Luggage with face detection that is also delighted to see you as it swings around the baggage carousel and can express that- makes you feel loved after a rough flight and helps you locate your bag. The name of this wonder-product? "emotional baggage"
  • #432 pave a road with bad intentions, see where it gets you
  • #2190 market an extra-rough brand of kleenex called "grindstone"
  • #2793 football uniforms made out of really stretchy material so if a defender grabs you by the uniform you can just keep on running. Bonus: if defender is really strong and heavy and manages to hold on, comedy as you get flung back to him like a rubber band.
  • #2567 Along with surgery to reduce snoring, surgery to increase snoring but it comic ways - giant long snort on the inhale, descending whistling tone on the exhale.
  • #38 alcoholic drinks mixed with aspirin and gatorade to get a jump on your hangover the next day
  • #250 a bumper sticker that wards off cops by saying "don't worry i've driven MUCH drinker than this"
  • #2560 combine "Now & Laters" candies with "Atomic Warheads" and call them "Apocalypse Now & Laters"
  • #2714 make a 60s/70s mashup cover group called "Earth, Blood, Wind, Sweat, Fire, and Tears."
  • #711 Find that butterfly whose wing flapping is causing tornadoes on the other side of the world and STOP IT, FFS
And finally, a few I sort of liked but didn't make the other lists:
  • #235 phone cases marketed by how they feel- texture, weight, etc- more than how they look after all you are mostly just looking at the glass screen, but how they feel to your hand whether out or in your pocket is important all the time
  • #413 A Todo App that indicates items have been there too long by overlaying them with subtle cobwebs... you can even swipe them away to indicate progress on the task or just out of frustration
  • #193 conduct a social engineering campaign to change social mores so it's ok to react when people are in the bathroom stall next to you: cheering them on if they make a particularly impressive sound, sympathizing if it sounds like they're having problems, etc
  • #1055 Micro-Time-Zones. All clocks in a timezone are set to the nearest minute based on the current Longitude. No more stupid bumps when you cross certain state borders!!!
Anyway. This is the single piece of company culture I'll most miss from my old place, and I think I have a FIF in there about launching new colonies. I'd really recommend it for any big slack organization.

Oh one bonus I almost missed:
  • #1593 Make years start on March 1. This will have 3 big advantages:
    1. meteorological seasons now line up year - starting with March/April/May spring, June/July/August summer, the school year starts with Sep/Oct/Nov Fall, and then Dec/Jan/Feb Winter
    2. September and October now fall on their appropriate Latin numbers (7 and 8)
    3. NFL season is no longer this weird ambiguity springing from regular seasons and playoffs of one "season" being in different years

Sunday, May 17, 2020

node/shell mashup to get stuff done

Once upon a time I used Perl for everything - shell-script type stuff as well as Web CGI.

For a while PHP became a similar go to for everything- it's a little unusual to do shell-script stuff in it, but it's pretty handy.

Now, I've grown some comfortable with JS/ECMAScript for handling arrays and filtering and what not I'd like to use it, but I'm a little lazy about looking up the syntax for the actual file operations - so I treat it as a text manipulation exercise, and go back and forth copy and pasting with a terminal window.

For example, for a big PDF conversion, I did an `ls` in Terminal (sometimes piping it to cat to make sure it was a single column, and then pasted that as a big quote in a .js file:

const raw = `some file.pdf
someo therfile.pdf
yet another_file.pdf`;

then something like

let c = 1;
const origs = raw.split('\n');
for (orig of origs) {
    console.log(`mv 'orig/${orig}' convert/${c}.pdf`);
    c++;
}

running that in node gives me a list of shell operations I can review and tweak if needed, and then copy and paste and run.

It ain't pretty but one off tasks don't have to be :-D

Friday, May 15, 2020

safely shuffling an associative array in php

I'm making game server endpoints in PHP because it's convenient to deploy, but I'm sort of regretting it. I should really get my node mojo working...

Anyway, I thought I had a problem with json encoding / decoding because of PHP's ambiguity of regular arrays and associative arrays, because what I wanted to be a map of keys to values was coming across as a regular array in javascript land. 

After some cursing and debugging, I realized it was because I was calling "shuffle()" on the PHP array. (Leaving aside the meh-ness of hoping to get an order of a javascript map). Anyways, shuffle() doesn't respect string keys, it just drops the keys and treats it as an ordered array. Which sort of makes sense but you think there would be a warning or something...

Anyway, this function lets you shuffle the keys of an associative array (I assume without the shuffle it's usually insertion order?)

function shuffle_assoc_array($orig) {
    $shuffled = array();
    $keys = array_keys($orig);
    shuffle($keys);
    foreach ($keys as $key) {
    $shuffled[$key] = $orig[$key];
    }
    return $shuffled;
}

Eh, good enough.

UPDATE: The above was needed for shuffling a collection of player objects (so as to not bias the judge). The main deck is a regular array... for reference here is some code that regenerates the deck (each card is a number from 1-350) after removing the cards that are already in the players hands:

#do a reshuffle if we have less than ten players per deck... 
$playercount = sizeof($game["players"]);
if(sizeof($game["deck"]) < ($playercount * 10)){
    #all cards but those currently in players' hands
    $cardsinhands = array();
    foreach($game["players"] as $player){
        $cardsinhands = array_merge($cardsinhands,$player["cards"]);
    }
    #remake entire deck, but take out the ones in players hands...
    $deck = array();
    for($i = 1; $i <= $DECKSIZE; $i++){
        array_push($deck,$i);
    }
    $deck = array_values(array_diff( $deck,$cardsinhands ));
    shuffle($deck);
    $game["deck"] = $deck;
}

Wednesday, May 13, 2020

little baby json server for testing react against

I had a few at-home programming tests and one of the better ones started with a simple create-react-app-style bit of boilerplate including clever us of json-server which gives you a baby server that applies REST-ful requests to a json file, all on your file system.

Reminds me a little of getput a tiny node-based server that I made 5 years ago, though I used my preferred method of separate files for the each "table row", which helps make sure any screw ups minimize damage...

react hooks, updating 'initial state' for repeated renders of a modal

Hooks really feel like the right thing for React and for small to medium-sized components they make it easy to bundle all your state right there in what is otherwise a pure functional component

The paradigm for tying in an input field to the state is pretty clean-

First you get the local variable and a way of changing it:
const [name, setName] = useState('');
Then you render an input field like this:
<input value={name} onChange={(e) => setName(e.target.value)} />

I ran into a gotcha with a react-modal component, one that is effectively reused across the life of the parent component. Using something like
const [name, setName] = useState(props.name);
wouldn't reset things so to speak - name was being reset to the initial value from the first render even as the props were updated and the entire component was being re-rendered.

The solution is to combine it with the useEffect hook to get that side effect action going:
Once you've called useState, do something like
useEffect(() => {
   setName(props.name ? props.name : '');
}, [props]);

That let the inputs have the right initial value while still updating the local state as the user typed.

Sunday, May 10, 2020

factor of duh - reading JSON post data

Heh. So I realized one of the reasons I shanked that tech take home, besides the weirdness of JS date transform is I had a one letter type (header not headers) in the JSON fetch code I used. I really should have sucked it up and used jQuery or some such. Talk about premature optimization...

But I stumbled over that when realizing I was behind the curve in my "use PHP for JSON endpoints... to read a JSON post, you can't rely on $_POST, you need something like:
 $POSTJSON = json_decode(file_get_contents('php://input'),true);
which will then be the associative array you might expect...


php file locking for multiuser games

So I'm messing around with making quarantine-friendly version of the like-Cards-Against-Humanity-But-With-Comics game Joking Hazard. I'm thinking each game will just be a single .json file on the server, each client will be a SPA in the browser, the endpoints will be humble PHP scripts that update the file - the clients will poll and get an updated version of the whole game state, and then modify it according to the rules.

It's surprisingly tricky to teach a computer to referee even a medium complexity board or card game (It reminds me I need to go back and update Score-Matic for Dixit, another lovely boardgame, but it's casual-friendly creative/artsy mojo is marred by a fiddly scoring system).

I think I am basically making a state machine: not counting the games red cards (a slight fork) the core game loop is something like

vvv
[create-game(username)] /* put all cards in deck */
vvv
GAME_SIGNUP (add username)  <<< [join-game(username)] /* assign 7 cards from deck */
vvv
[start-game]
vvv
NEW_ROUND <<< next judge assigned, deck-picks-card
v
v
REG_ROUND 
vvv
[judge-picks-card(cardoff, judgepos)
vvv
REG_PLAYERS_PICK <<< [reg-player-pick(cardoff)
vvv
(all players_pick)
vvv
REG_JUDGE_PICK <<< [reg-judge-pick(player)]  /*shows all picks to everyone */
vvv
END_ROUND
vvv
[judge-ends-turn]
vvv

So one challenge will be making sure each client can update the gamestate without stomping on other people's play- PHP supports filelocking, though you have to start using syntax more complicated than good ol' file_put_contents() - since there's a lot of boilerplate, I'm going to use a callbacks to a shared function:

function updateGame($gamename, $function){
    if(! preg_match('/^[A-Z][A-Z][A-Z][A-Z]$/',$gamename)) {
        echo "shenanigans!";
        return;
    }
    $filename = "games/$gamename.json";
    //Open the File Stream
    $handle = fopen($filename,"r+");
    //Lock File, error if unable to lock
    if(flock($handle, LOCK_EX)) {
        $oldguts = fread($handle, filesize($filename)); //get current state
        $newguts = call_user_func($function,$oldguts); //update state
        ftruncate($handle, 0);    //Truncate the file to 0
        rewind($handle);           //Set write pointer to beginning of file
        fwrite($handle, $newguts);    //Write the new Hit Count
        flock($handle, LOCK_UN);    //Unlock File
} else {
    echo "Could not Lock File!";
}
//Close Stream
fclose($handle);    
}
Heh, this is extremely FP! Get a copy of the state, modify it, replace the old state.

So my test code for a call back for this was like:
function addSomething($oldraw){
    $content = json_decode($oldraw,true);
    $content["foo"] = array();
    $content["foo"]["bar"] = "baz2";
    $content["foo"]["bat"] = "bag2";
    return json_encode($content);
}
It feels a little odd to pass in the function as a string, but whatevs.


rsync + fswatch for automated push to server

Like 90% of the work I do on my website, I do using a homebrew in-browser editor system (backed by PHP) I made by wrapping "Ace". It's super-convenient to be able to edit any file on any of my sites but it is ultimately limiting: I've been too reluctant to set up a build system, and so would only use technology stacks that were basically scripted, and thus the gap between my coding style at work and my coding style for fun was widening.

Baby steps would be setting up automatic file synching between my laptop and my server - plus I've really started liking VSCode's prettify module, everything just feels so professional when it's neatly reformatted...

The answer here was a good start. For my new project (an online, social-distance way of playing the excellent creative card game Joking Hazard) I ended up with a pair of alias like this:
alias joking_rsync='rsync -azP --exclude ".*/" --exclude ".*" --exclude "tmp/" /Users/LOCALUSER/data/dev/2020/jokinghazard USERNAME@SERVERNAME.com:/home/USERNAME/sites/temp.alienbill.com/'
alias joking_rsync_watch='joking_rsync; fswatch -o . | while read f; do joking_rsync; done'

It didn't work at first though because rsynch would ask for my password each time! (also I had to `brew install fswatch`)

The steps on this page worked really well to share an ssh key, and let rsynch work without bugging me. I guess I feel better about setting that up on my new mac that always requires at least a fingerprint to log in.

Earlier I had done something sort of similar and had better luck with 'filewatcher' but it has been a while for that - in this case rsync is doing more of the heavy lifting, so fswatch does the trick.

Saturday, May 9, 2020

somerville porch -- er- couchfest post-mortem

The Somerville Arts Council brought Porchfest to the Boston area in 2011, just 4 years after the first one in Ithica.  This year because of COVID-19 they switched to an online streaming-and-prepared-video model, and I helped design a robust front end. Here are some lessons learned for other porchfests and for my future self...

I started helping Somerville in 2016 by making static versions of their band list and map that would withstand the crush of traffic on the day of (very spiky traffic - a LOT of people hit your site in a short number of hours and if you have to go back to the database for each page display / band search and you're not carefully tuned, you might have problems.)

This year we asked performers to consider either: A. a live stream B. a new video made just for the event or C. selecting an existing video for the event, and then there was a fourth category of bands who had signed up before we knew it was not going to be a real-world happening, but didn't update with a new link.

I had a lot of wacky ideas about how to present this on the day of, thinking some sort of live social media feed would add excitement, but we decided to keep it relative simple. We asked for a single performance link from each band, and a description of what type of thing (live stream, new video, existing video) it was.

Here's the design I ended up with, with the top art and tile layout borrowing heavily from what was already on the live version of the site:
Traditionally the "time zone" thing has been very important to Somerville Porchfest (since when people are walking in real space, it's nice to make sure they have interesting options within walking distance.) For the virtual version, we still encouraged the use of "where you are determines what time you play live music" idea a bit (so as to stop everyone playing at the same time) but for the map, I decided to use color not for the "time" aspect but for indicating if it was a live performance (which I think is much more exciting and like a regular porchfest event) or a video (still a critical option for bands with multiple players!) And I listed the live performances first, and the premier videos after that (each list sorted by time), followed by bands who provided picked videos, and then all the other groups. Also, I put the genre information SAC had collected from each group instead of the address they were playing at. (The map at top is still interactive, you can click on a circle and see what band is located there.)

FWIW, I've started using Leaflet.js, a terrifically easy to use API - Google Maps has started a charging model that's terrible for event-based websites, when almost all the traffic happens in a short time frame... last year I started getting charged a couple hundred bucks.

Another lesson we're slow to remember year after year is that bands will always want to change things at the very last minute. When you're making a site that is a snapshot (for performance reasons) rather than reading live from the database, it's great if you can work out a way to publish changes quickly right up to show time.

Actually, we were surprised at how many bands came in the last week. For the weeks prior, it was looking like a dozen live streams, a hand full of premiers, maybe 20 odd other videos... we ended up with 46 live streams, around 20 premieres, and 20 other videos.

Some other techie and design notes:
1. I disabled "scrollwheel zoom" in leaflet, it was grabbing the scroll on laptops in an unfriendly way.
2. Originally I used softer pastel colors, and colored the whole background of the cards, but using a stronger color as trim seems to have worked out much better. (I had to swallow my ego when the people I was working with disliked the pastels :-D )
3. I couldn't find a way to change the "z index" on the map, so I had to add the circles on the map in the reverse order as they were displayed.
4. I kept this as vanilla ECMAscript. Honestly the new syntax features make it pretty easy to manipulate the data for a site and with the new quote syntax you don't even need a separate template engine.
5. A simple responsive design using flexbox worked well, setting a 500-600px viewport to start.

Next year we definitely want to switch to a model that spits out the JSON like every ten minutes or so, rather than being constantly live (which might tax the server) or static (which means we have to manually push to see changes bands make.)

Also, I think had we had most of the bands signed up when I designed this, I might have put in a text-based name/keyword filter, or more sophisticated break down by time of day. Lesson learned! But here's hoping next year will be back in full, house-to-house swing!

Friday, May 8, 2020

js one-liners

https://1loc.dev/ - some cool JS one-liners. Maybe they would have helped with problem the other day!

Thursday, May 7, 2020

dates in JS can be ugly, capital UGH

Just shanked a techie code test in part because


It was for a "can you work with maps and arrays and stuff" exercise - which I can, but I either fumbled syntax or got mildly addled at parts and combined with some failure with the above (I had to do some basic date math) made it a hella confusing nightmare I couldn't quite come back from.

I think for some reason the second Date is like, setting in GMT and then giving me the Eastern Daylight Time equivalent of midnight in England.

Now I see why some languages insist you specify the time zone always :-D

Wednesday, May 6, 2020

the new iPad trackpad cursor

Man, I wish Apple stores were open so I could experience Apple's adaptation of the iPad cursor for better use with a trackpad... I love how the article pointed out the breakthrough of the very first mouse cursors - the idea of the cursor being the user's tiny abstract avatar. (Anyone who has played Atari 2600 Adventure with its little box shaped player will immediately understand...) And the further idea that on a regular touchscreen, that avatar is actually your fingertip. (But that means when you withdraw your finger the avatar vanishes!)

Anyway, the near bio-logical clinginess of this new cursor looks like it would feel amazing. (Though hopefully more organic and less brittle than Photoshop clones' "snap-to" which can sometimes be frustrating.)

basic GET and POST with fetch

If you don't need IE support, "fetch" is a fine, library free option for doing GETs and POSTs and the like from the browser.

There's a little bit of ceremony that goes with them. Fetch returns a promise, and then many of the parsers (e.g. ".json()" and the other Body methods) also return a promise. Here's an example wrapper for function for GET:

function doJsonGet(url, fn, err) {
    fetch(url)
        .then((data) => {
            if (!data.ok) {
                throw Error(data.status);
            }
            return data.json();
        })
        .then((json) => {
            fn(json);
        })
        .catch((e) => {
            err(e);
        });
}

Here is the version for POST, which takes a bit more ceremony to set the Content-type and packup the payload...

function doJsonPost(url, content, fn, err) {
    const options = {
        method: 'POST',
        headers: {
            'Content-type': 'application/json',
        },
        body: JSON.stringify(content),
    };
    fetch(url, options)
        .then((data) => {
            if (!data.ok) {
                throw Error(data.status);
            }
            return data.json();
        })
        .then((json) => fn(json))
        .catch((e) => {
            err(e);
        });
}
I made up a cleaner and battle tested version here for GET, POST, PUT and DELETE

SEPTEMBER UPDATE:
I'm not sure why I get caught up in this code so often - subtle errors caused me to bork one take home job interview, and just now when I tried to copy and paste some other old code related to this, I had some weird problems, like sometimes what the code seemed to assume was a JSON encoded string was already an object, or a promise wasn't resolving quite right, etc.

ANYWAY. When making a JSON endpoint for an API in PHP, code like this seems to read in the POST data well:
function getJsonPOST() {
    return json_decode(file_get_contents('php://input'),true);
}

So my PHP test for POST was 
<? 
    $vals = getJsonPOST();  
    $res = array();
    $res['bar']=$vals['foo'] + 123;
    echo json_encode($res); 
   
function getJsonPOST() {
    return json_decode(file_get_contents('php://input'),true);
}
?>
and of course GET was just 
<?
    $val = $_GET['foo'];    
    $res['bar']=$val + 123;
    echo json_encode($res); 
?>
    
And I fired those off with this js:

    doJsonPost('server/test.php',{'foo':321},(res)=>{console.log(res);}, (res)=>{console.log('ERROR',res);});
    doJsonGet('server/testget.php?foo=432',(res)=>{console.log(res);}, (res)=>{console.log('ERROR',res);});
    

Tuesday, May 5, 2020

forgetful jones why are you so forgetful

Heh, making a virtual Mother's Day Card, I realize I had forgotten which trig function is good for calculating the angle between two points - atan2 was the one I was thinking of.

Curious to see if I had mentioned "atan2" before, I see this old blog entry  had some pretty cool code for a character chasing the mouse pointer by gradually turning.

note to self: setting a width for mobile via viewport only is easier than you think

I admit I haven't had a lot of luck deeply understanding the Viewport Meta Tag. I had forgotten that desktop browsers tend to ignore it, so you don't have to bend over backwards to suggest a starting width for mobile without affecting desktop...

I use a simple one column layout for a lot of my P5 toys (e.g. coraferris) and so this line is the way of starting at full width:

<meta name="viewport" content="width=560">

(Someday I should really wrap my head around all of those viewport settings, and the way it interacts with CSS @media blocks.)

Saturday, May 2, 2020

xml and giant security holes

Great story of a long term iOS security hole Apple is just now fixing. The TLDR is, iOS gives apps permissions to do things in XML "plist" files, there are different XML parsers on the device to read those permission, and while normal XML comments <!-- look like this --> the weird-o semi-comments <!---> and <!--> get handled differently among the parsers, which provides a handy wedge into all sorts of shenanigans.

Over my course as a programmer, there's always this weird vindicatory schadenfreude when a technology that just smells off to be is eventually widely recognized to be kinda terrible. The original AngularJS was one huge example for me - its early terribleness made the probably pretty ok followup versions forever tainted.

And XML was like that - until JSON came around, I would have rather used tab delimited files for everything. It's just full of all these weird pointy bits, there were way too many ways of specifying what your XML schema (the rules of what fields your documents could and couldn't have) was... it was just fundamentally a tool for over-engineering for control-freak system designers.

(Original link via the ever-great Daring Fireball.)

covering up broken images in CSS

For COVID reasons, Somerville PorchFest is Somerville CouchFest this year. Organizers are encouraging participating musicians to signup to do live streams and premiere videos and the like.

You can view my current draft of the day-of design.

Some bands are missing images, and so I hunted down a way of replacing the usual gangly "broken image" bit with something more deliberate looking...

the trick seems to be that broken images will be subject to the ::before pseudo-selector but loaded images won't be, so we can jam in some content... something like

.band img {
    position: relative;
    display: inline-block;
    vertical-align: text-bottom;
}

.band img::before {
    content: 'missing:' attr(alt);
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    line-height: 20px;
    background-color: #ccc;
    text-align: center;
    display: block;
    width: 280px;
    height: 280px;
    overflow: hidden;
}

That's assuming you have meaningful alt tags, otherwise you can just leave it blank.

This page has a deeper look.

I decided to stick with Vanilla.js... or at least the ES6 flavor of it.

UPDATE: I don't have time to mess with this now, but it looks like maybe this trick doesn't work in Firefox. Better just to use a tiny bit of javascript, in my case:
<img src='${photo}' onError="replaceImageWithPlaceholder(this)">
and
function replaceImageWithPlaceholder(what) {
    what.src = 'porchfestweb12.png';
}



I feel guilty for not turning it into a practice exercise in React or Vue, but after one person interviewing me mentioned he didn't like JSX, that templates were simpler... well, I don't agree, I think JSX are pretty elegant and simple once you get the build system setup, but man... in the meanwhile the backtick quotes and templating really pull the load that used to be carried by JQuery Handlebars. This page is pretty static, but the idea of replacing all the content of a container with something made from data is pretty dang easy to implement with that quoting and array built-ins such as map and filter...