Tumgik
#but its just static on my phone...... frickin..........
hgggggggggggggh
#like dude!!!!!!!! ive been trying to work on and finish this thing since december!!!!#and i had to completely overhaul it!!!!!! but i just finished it finally!!!!!!!!!!!!#but!!!!!! apparently you dont wanna send the gif through messages???#i tested it by sending it to myself through jgft and it shows up on my computer (not when you click on it to see it better)#but its just static on my phone...... frickin..........#so i cant even send it to the person i wanna send it to because ya might screw it up..... fr kci ng.......#and the gif breaks when i try to put it in a post......#god fricking dang it >:(#i am what the kids these days call.... *snaps fingers* ..bothered.#....im supposed to be working on other things too...im surprised i even worked up enough motivation and concentration to finish this thing#aaa..... @ everyone i should be doing things for: i am so very sorry i promise im doing as best i can but i dont think its quite enough..#im gonna..... slam my head into my desk and see if that helps yeah yeah yeah yeah (but not really i wont dont worry sorry)#a green tangent!!#what the heck greenookie#edit: the gif isnt breaking in a post when i look at in on mobile....#why is it broken on pc but not on mobile.... what...#id rather not have to post it because its kinda....... gotta.... lil tiny bit of s/e/lf mut/ilat/ion (is that how you censor things oh no)#and i dunno.... doubt anyone else would wanna see it rather than the person i wanna try to send it to..#but i dont wanna make a fool out of myself if it breaks on me ya know? ;;v;;?#EDIT 2: HE LL O TURNS OUT IT WAS A FILE SIZE PROBLEM  i t was about 5.3mb i think and this dumb site handles up to 3mb#so i resized it to be Smaller and now it works ;;v;;#(thank you mango for being smarter than me and suggesting this tha n ky ou)
1 note · View note
lady-divine-writes · 4 years
Text
ACITW AU one-shot “Together Apart” (Rated M)
Summary: With Kurt in New York, performing in New Year's Rockin' Eve, and Sebastian partying with his brother in Westerville like old times, it looks like Kurt and Sebastian will be spending this holiday apart ... and Kurt is not okay with that. (2515 words)
Notes: I always liked this one because it showed that just because Sebastian is rich and could easily take care of Kurt for the rest of his life, Kurt never gave up his dreams. He went to NYADA and is now performing on Broadway. Besides, I'm a sap for stories that manage to pull it all together at the buzzer, so to speak. Fluff and romance with plenty of throwbacks to the original story.
Read on AO3.
“Stupid … frickin’ … useless … WiFi …” Kurt mutters, slapping his phone against the palm of his hand with each word, as if battering the thing will jostle the electronics and force it to work. “Overpriced … piece … of garbage …”
“Still can’t get FaceTime to connect?”
“No,” Kurt growls. “I’ve deleted it and downloaded it about a dozen times and it keeps freezing up on me. Instagram, too. Dammit! Why do we let AT&T soak us for Broadband if it’s not going to work during the important times, like bank holidays and inclement weather?”
“That’s my bad,” Sebastian says. “I forgot to select the Defy Act of God add-on when I renewed our service.”
“Bastard. Always letting me down.” Kurt spins on his his heel and flops down on his back on the bed. “I guess we’re going to have to talk into the phone … like cavemen.”
“Ooo … cavemen,” Sebastian purrs. “We haven’t roleplayed that before. Sounds primal. Now that’s a concept I can get behind.”
“It’d be easier for you to get behind it if you were actually here.”
“I know, babe, I know. But on the bright side, phone sex is a viable option.”
Kurt closes his eyes and sighs, deflating into his pillowcase and his down duvet. The fingers of his free hand find his forehead and massage, attempting to knead away the pounding in his sleep deprived brain. “Are we really going to do this over the phone?”
“Yes. Hence the mention of phone sex.”
“No, I mean celebrate New Year’s. The way things stand, we’re better off calling it a night, wait till you get here tomorrow to celebrate. I really want to take off my clothes, hop in a hot shower, and boil the skin off my bones.”
“Without me?”
“Again, you’re not here …”
“Exactly! We’ve never missed a New Year’s together! Even when we were separated, you were my New Year’s kiss! Now I know you’ve spent yet another taxing evening as a winged marmoset but I’m sorry. You’re going to have to wait one more hour to turn yourself into human stew because I am not spending New Year’s Eve without my kiss!”
Kurt bites his lower lip, holding back a laugh. He doesn’t want to encourage Sebastian. But he ends up snorting which, in terms of laughter, is much worse. Sometimes Kurt thinks Sebastian should have attended NYADA and been the musical theater major since he’s the real drama queen in their relationship. “And how do you intend on getting a kiss from me from over five hundred miles away when we can’t even FaceTime?”
“May I bring your attention back to the topic of phone sex? It’s something I know you’re exceedingly familiar with.”
“Ha … ha …”
From somewhere in the distance, a wave of laughter erupts, as if half the population of Westerville has been listening to Sebastian ply his adolescent wit. Kurt rolls his eyes, grimacing at his phone so hard, his head goes from dull pang to steady throb.
“Why bother?” he sniffs. “From the sounds of it, you’re having the time of your life at your folks’. What? Did Julian and Cooper rope you into one of your famous parties while your parents are away? Trying to recapture the good old days?”
Sebastian makes a non-committal noise. “It’s not a party without you. Besides, I’m not about that life anymore.” He huffs. “Even when I was about that life, I wasn’t about that life.”
“Liar.”
“Fine. Let’s say I wasn’t about that life after I fell head over heels in love with you.”
“And when was that?”
“Earlier than you’re willing to believe.”
“Sure,” Kurt grumbles, proving Sebastian right. It’s not that he doesn’t believe Sebastian. He’s too bitter at the moment to think favorably about anything. He sighs again, debating between climbing underneath the covers fully clothed or trundling off to the kitchen for a bottle of water and a couple aspirin.
Neither wins.
“So what is going on where you are right now?” he asks, his insides roaring with jealousy before he even gets an answer.
“Where I am right now?” Sebastian repeats, singling those words out - the lynchpin to a loophole. Because the party of the century could very well be bumping in the house around him, but if Sebastian has holed himself up in a bathroom, or his old bedroom, then technically he’s not partaking in the festivities. But from the constant static of music and laughter behind him, Kurt doesn’t think that’s the case. “Not much. Hanging with a bunch of drunk randos I don’t know, listening to music that makes me want to puke in my shoes.”
“There’s an image.” Kurt chuckles, not for long but loud enough to regret it. “Can I assume then that you’re a bit sloshed yourself?”
“Not at all. I’ve had the sum total of one Seagram’s.”
Kurt makes a face. If that’s what Julian is supplying their guests, he’s really scraping the bottom of the barrel. Or did they run out of the good stuff early on and have to make a 7-11 run? Or, more to the point, have some poor schlub from DoorDash make a 7-11 run? “Would that be vodka or gin?”
“Uh … no.” Sebastian clears his throat uncomfortably. “More like … uh …”
Amusement and confusion burn a slow smile across Kurt’s tired mouth. What in the world could it be that it’s taking Sebastian this long to answer? “Come on, Smythe! Cough it up! What was this mystery drink?”
“It was … an … ahem … Orange Sassy Swirl.”
The last three words tumble out of Sebastian’s mouth like a skydiver without a parachute.
“Oh, Sebastian. No,” Kurt commiserates earnestly, wondering at what level of desperation Sebastian would actively submit to imbibing anything that goes by the name Orange Sassy Swirl when he had once balked at ordering Kurt a much more respectable apple-tini. “Say it ain’t so.”
“What about you?” Sebastian side-steps swiftly, obviously unwilling to divulge what led him to pick a beverage with such a ludicrous name. “Hit up any noteworthy shindigs?”
“Nope. I took off my makeup after we recorded our part for New Year’s Rockin’ Eve and headed straight back to our place.”
“None of your theater buddies had anything planned? You guys have some heavy hitters in your cast of Wicked. Not a one of them is throwing a party?”
“It’s not that. Idina and Kristin both had prior engagements, of course, but most of the cast had somewhere to go. A few invitations made it my way, I won’t lie. Being a Flying Monkey has its perks after all. But that’s not the point.”
“And what is the point, monkey man?”
“The point is that even though I’m living the dream, I’d much rather be with you, drinking your brother’s wacky alcoholic concoctions in his never ending quest to get me as drunk as possible. We’d stumble upstairs to slow dance in your old bedroom till midnight then, right when the ball drops …”
“Yeah?”
“We’d bone.”
Sebastian barks a laugh the way Kurt had hoped. God, he misses that laugh, the smile that accompanies it, the way both light up Sebastian’s face. With his eyes shut and Sebastian’s voice in his ear, Kurt can see his face so clearly it makes his heart hurt.
“There’s my hopeless romantic,” Sebastian says when he catches his breath.
“And even though New Year’s is a completely made up holiday …”
“Kurt! They’re all made up!”
“I mean the whole concept of a holiday that celebrates the passage of time without the inclusion of some sort of harvest because time itself is a man-made construct …”
“Here we go again …” Sebastian mumbles under his breath. Oddly, he sounds like he’s out of breath and racing through traffic. Most likely multitasking, Kurt thinks. Playing a video game while talking on the phone. Kurt remembers way back when when he, Sebastian, Julian, Finn, and Puck would spend the bulk of Julian and Sebastian’s annual New Year’s Eve blowout kicking each others’ asses at GTA - much to the dismay of their hornier guests, who’d been banking on some kind of show from the Smythes. They eventually did get one since their friendly game turned into strip GTA once Julian, Finn, and Puck got decently buzzed. Any fans of Sebastian’s went away disappointed though since that was around the time he’d squirrel Kurt upstairs so they could ring in the New Year in private.
“There was a time when the calendar didn’t have twelve months. If it wasn’t for the tremendous egos of the Caesar bros …”
“Otherwise known as the salad mavens of ancient Rome …”
“There’d only be ten months!”
“Not to put a wrench in your tirade,” a suddenly winded Sebastian interrupts, “but I don’t think that’s entirely accurate ...”
“I’d be 56 years old!” Kurt rails, uncaring.
“On the up side,” Sebastian says, abandoning his point, “you’d look magnificent for your age. As would I.”
A comfortable but tense silence settles between them, Kurt straining to hear more of what’s going on on Sebastian’s end of the phone while picturing what sort of bedlam Julian has unleashed. But the murmurs of celebration Sebastian is caught up in sound fuzzy and disjointed, shifting and changing as Sebastian (presumably) walks through the house in search of a quiet place to converse.
“Come on, babe,” he says finally. “Tell me what’s on your mind. What’s got you so down on this joyous non-holiday? You usually don’t wax historical unless you can tie it back to the moral of a Sondheim musical.”
“I …” Kurt struggles to come up with a lighthearted, funny response to complement Sebastian’s jab, maybe some mention of his obsession with Indie rock, but he can’t come up with anything. He’s crashing, physically and emotionally, but it’s the emotionally that’s threatening to dismember him on impact. He suddenly can’t help himself his feelings. They’re too overwhelming to control. He knows that the long hours he spent practicing over the past few days set him up for this; the fact that he skipped out on dinner and then completely bypassed the craft services table in his eagerness to get home didn’t help. But it’s the prospect of starting this New Year off alone, for some reason, that’s become too soul shattering to bear. They’re not in high school anymore, where every emotion becomes ratcheted beyond its limits, every moment feels do or die. This shouldn’t be as big a deal as it is. “I miss you. I know we’ve only been apart three days and I know I’m going to see you tomorrow - like, eight short hours from now but … I really miss you. I learned a long time ago that I don’t like being away from you for too long, especially on nights like tonight when pretty much every person in the world is paired up with someone, preparing to share a kiss come midnight, real holiday or no.”
“I feel you …”
Kurt frowns as the sound of a car horn drowns out the end of Sebastian’s sentence. If he’s not actually out in traffic, Kurt has to say the sound effects in GTA 5 are incredible. With that in mind, he wonders if the makers of GTA 5 included a slurring crowd counting down the seconds till midnight as some kind of too-on-the-nose Easter Egg.
“And so it begins.” Sebastian exhales long and deep, and for the first time that night, he doesn’t sound anywhere near festive. He sounds defeated. “How about you count it off for us, babe?”
“Yeah, all right,” Kurt agrees, clearing a sharp-cornered sob from his throat. “10 … 9 … 8 …” He counts by rote, not really listening to himself but to Sebastian’s breathing over the phone, waiting for Kurt to reach one so he can make some ridiculous ‘Mwah!’ noise and go back to his game. Behind closed eyelids, Kurt imagines being at the Smythe house with him, arms wrapped around his waist, lips ghosting his neck as he tries his best to distract him.
He’d succeed, but Sebastian would still win his game. He’s that good.
“... 5 …” Kurt’s voice wavers, his eyes beginning to burn “… 4 … 3 … 2 …”
“... 1 …”
A familiar voice and the press of warm lips against Kurt’s mouth make his eyelids spring open. Moss-green eyes peer into his, steeped in the same level of exhaustion, but even more so, the same level of longing. With his eyes shut, Kurt didn’t see Sebastian come in; didn’t hear him unlock the front door or open the bedroom door over the revelry going on outside, echoing from the TV that he’d put on for white noise and forgotten all about. Besides, Sebastian could walk as quietly as the dead when he wanted to - a talent garnered from years of sneaking out of his house, climbing down trellises and jumping off rooftops in the middle of the night with his parents none the wiser.
“You’re here,” Kurt whispers in a hoarse, relieved voice.
“I am,” Sebastian replies with the addition of another kiss … then another as he climbs onto the bed and straddles his boyfriend.
“You … you didn’t tell me you were coming. You didn’t even hint that you were in the city.”
“I didn’t know if I would make it in time. I wanted it to be a surprise.”
“How did you get here?”
“Train. Then an Uber. Until the roads became blocked by pedestrians and I had to get out and hoof it.”
Kurt nods slowly. That explains the incongruous sounds of people interspersed with cars and traffic on his end of the phone. “You’re an asshole.”
“True. But I’m your ...” Sebastian’s face pinches, stuck somewhere between a laugh and scowl when he thinks about the way his comment is about to come across out loud. “You know what? Let’s just say jerk.”
“It would have been nice to have something to look forward to,” Kurt says, shaky arms creeping up around Sebastian’s neck.
“I know.” Sebastian runs the tip of his nose lightly against Kurt’s. “But on the off chance things didn’t work out, I didn’t want to let you down.”
“Makes sense, I guess,” Kurt says with the slightest of shrugs.
“Are you happy I’m here?”
“I’ll be happier in five minutes.”
Puzzled, Sebastian’s brows pull together while he fights not to yawn. But he’s so comfortable here in his own bed, with Kurt’s body underneath him, miles away from the mounds of people vying for his attention back in Ohio. “Why? What’s happening in five minutes?”
Kurt’s sad, tired expression grows into a smile that’s positively devious. “You’ll be ready for round two.”
Sebastian grins, reaching over Kurt for the remote to turn off the lights, willing to admit that he walked straight into that burn with his eyes wide open. But an entire morning spent entwined in the arms of the man he loves? That’s worth a little sizzle. “Ouch.”
34 notes · View notes
nancydsmithus · 5 years
Text
Designing And Building A Progressive Web Application Without A Framework (Part 2)
Designing And Building A Progressive Web Application Without A Framework (Part 2)
Ben Frain
2019-07-25T14:00:59+02:002019-07-25T12:06:45+00:00
The raison d’être of this adventure was to push your humble author a little in the disciplines of visual design and JavaScript coding. The functionality of the application I’d decided to build was not dissimilar to a ‘to do’ application. It is important to stress that this wasn’t an exercise in original thinking. The destination was far less important than the journey.
Want to find out how the application ended up? Point your phone browser at https://io.benfrain.com.
Read Part One of Designing And Building A Progessive Web Application Without A Framework.
Here is a summary of what we will cover in this article:
The project set-up and why I opted for Gulp as a build tool;
Application design patterns and what they mean in practice;
How to store and visualize application state;
how CSS was scoped to components;
what UI/UX niceties were employed to make the things more ‘app-like’;
How the remit changed through iteration.
Let’s start with the build tools.
Build Tools
In order to get my basic tooling of TypeScipt and PostCSS up and running and create a decent development experience, I would need a build system.
In my day job, for the last five years or so, I have been building interface prototypes in HTML/CSS and to a lesser extent, JavaScript. Until recently, I have used Gulp with any number of plugins almost exclusively to achieve my fairly humble build needs.
Typically I need to process CSS, convert JavaScript or TypeScript to more widely supported JavaScript, and occasionally, carry out related tasks like minifying code output and optimizing assets. Using Gulp has always allowed me to solve those issues with aplomb.
For those unfamiliar, Gulp lets you write JavaScript to do ‘something’ to files on your local file system. To use Gulp, you typically have a single file (called gulpfile.js) in the root of your project. This JavaScript file allows you to define tasks as functions. You can add third-party ‘Plugins’, which are essentially further JavaScript functions, that deal with specific tasks.
An Example Gulp Task
An example Gulp task might be using a plugin to harness PostCSS to process to CSS when you change an authoring style sheet (gulp-postcss). Or compiling TypeScript files to vanilla JavaScript (gulp-typescript) as you save them. Here is a simple example of how you write a task in Gulp. This task uses the ‘del’ gulp plugin to delete all the files in a folder called ‘build’:
var del = require("del"); gulp.task("clean", function() { return del(["build/**/*"]); });
The require assigns the del plugin to a variable. Then the gulp.task method is called. We name the task with a string as the first argument (“clean”) and then run a function, which in this case uses the ‘del’ method to delete the folder passed to it as an argument. The asterisk symbols there are ‘glob’ patterns which essentially say ‘any file in any folder’ of the build folder.
Gulp tasks can get heaps more complicated but in essence, that is the mechanics of how things are handled. The truth is, with Gulp, you don’t need to be a JavaScript wizard to get by; grade 3 copy and paste skills are all you need.
I’d stuck with Gulp as my default build tool/task runner for all these years with a policy of ‘if it ain’t broke; don’t try and fix it’.
However, I was worried I was getting stuck in my ways. It’s an easy trap to fall into. First, you start holidaying the same place every year, then refusing to adopt any new fashion trends before eventually and steadfastly refusing to try out any new build tools.
I’d heard plenty of chatter on the Internets about ‘Webpack’ and thought it was my duty to try a project using the new-fangled toast of the front-end developer cool-kids.
Webpack
I distinctly remember skipping over to the webpack.js.org site with keen interest. The first explanation of what Webpack is and does started like this:
import bar from './bar';
Say what? In the words of Dr. Evil, “Throw me a frickin’ bone here, Scott”.
I know it’s my own hang-up to deal with but I’ve developed a revulsion to any coding explanations that mention ‘foo’, ‘bar’ or ‘baz’. That plus the complete lack of succinctly describing what Webpack was actually for had me suspecting it perhaps wasn’t for me.
Digging a little further into the Webpack documentation, a slightly less opaque explanation was offered, “At its core, webpack is a static module bundler for modern JavaScript applications”.
Hmmm. Static module bundler. Was that what I wanted? I wasn’t convinced. I read on but the more I read, the less clear I was. Back then, concepts like dependency graphs, hot module reloading, and entry points were essentially lost on me.
A couple of evenings of researching Webpack later, I abandoned any notion of using it.
I’m sure in the right situation and more experienced hands, Webpack is immensely powerful and appropriate but it seemed like complete overkill for my humble needs. Module bundling, tree-shaking, and hot-module reloading sounded great; I just wasn’t convinced I needed them for my little ‘app’.
So, back to Gulp then.
On the theme of not changing things for change sake, another piece of technology I wanted to evaluate was Yarn over NPM for managing project dependencies. Until that point, I had always used NPM and Yarn was getting touted as a better, faster alternative. I don’t have much to say about Yarn other than if you are currently using NPM and everything is OK, you don’t need to bother trying Yarn.
One tool that arrived too late for me to appraise for this application is Parceljs. With zero configuration and a BrowserSync like browser reloading backed in, I’ve since found great utility in it! In addition, in Webpack’s defense, I'm told that v4 onwards of Webpack doesn’t require a configuration file. Anecdotally, in a more recent poll I ran on Twitter, of the 87 respondents, over half chose Webpack over Gulp, Parcel or Grunt.
I started my Gulp file with basic functionality to get up and running.
A ‘default’ task would watch the ‘source’ folders of style sheets and TypeScript files and compile them out to a build folder along with the basic HTML and associated source maps.
I got BrowserSync working with Gulp too. I might not know what to do with a Webpack configuration file but that didn’t mean I was some kind of animal. Having to manually refresh the browser while iterating with HTML/CSS is soooo 2010 and BrowserSync gives you that short feedback and iteration loop that is so useful for front-end coding.
Here is the basic gulp file as of 11.6.2017
You can see how I tweaked the Gulpfile nearer to the end of shipping, adding minification with ugilify:
Project Structure
By consequence of my technology choices, some elements of code organization for the application were defining themselves. A gulpfile.js in the root of the project, a node_modules folder (where Gulp stores plugin code) a preCSS folder for the authoring style sheets, a ts folder for the TypeScript files, and a build folder for the compiled code to live.
The idea was to have an index.html that contained the ‘shell’ of the application, including any non-dynamic HTML structure and then links to the styles and the JavaScript file that would make the application work. On disk, it would look something like this:
build/ node_modules/ preCSS/ img/ partials/ styles.css ts/ .gitignore gulpfile.js index.html package.json tsconfig.json
Configuring BrowserSync to look at that build folder meant I could point my browser at localhost:3000 and all was good.
With a basic build system in place, files organization settled and some basic designs to make a start with, I had run-out of procrastination fodder I could legitimately use to prevent me from actually building the thing!
Writing An Application
The principle of how the application would work was this. There would be a store of data. When the JavaScript loaded it would load that data, loop through each player in the data, creating the HTML needed to represent each player as a row in the layout and placing them in the appropriate in/out section. Then interactions from the user would move a player from one state to another. Simple.
When it came to actually writing the application, the two big conceptual challenges that needed to be understood were:
How to represent the data for an application in a manner that could be easily extended and manipulated;
How to make the UI react when data was changed from user input.
One of the simplest ways to represent a data structure in JavaScript is with object notation. That sentence reads a little computer science-y. More simply, an ‘object’ in JavaScript lingo is a handy way of storing data.
Consider this JavaScript object assigned to a variable called ioState (for In/Out State):
var ioState = { Count: 0, // Running total of how many players RosterCount: 0; // Total number of possible players ToolsExposed: false, // Whether the UI for the tools is showing Players: [], // A holder for the players }
If you don’t really know JavaScript that well, you can probably at least grasp what’s going on: each line inside the curly braces is a property (or ‘key’ in JavaScript parlance) and value pair. You can set all sorts of things to a JavaScript key. For example, functions, arrays of other data or nested objects. Here’s an example:
var testObject = { testFunction: function() { return "sausages"; }, testArray: [3,7,9], nestedtObject { key1: "value1", key2: 2, } }
The net result is that using that kind of data structure you can get, and set, any of the keys of the object. For example, if we want to set the count of the ioState object to 7:
ioState.Count = 7;
If we want to set a piece of text to that value, the notation works like this:
aTextNode.textContent = ioState.Count;
You can see that getting values and setting values to that state object is simple in the JavaScript side of things. However, reflecting those changes in the User Interface is less so. This is the main area where frameworks and libraries seek to abstract away the pain.
In general terms, when it comes to dealing with updating the user interface based upon state, it’s preferable to avoid querying the DOM, as this is generally considered a sub-optimal approach.
Consider the In/Out interface. It’s typically showing a list of potential players for a game. They are vertically listed, one under the other, down the page.
Perhaps each player is represented in the DOM with a label wrapping a checkbox input. This way, clicking a player would toggle the player to ‘In’ by virtue of the label making the input ‘checked’.
To update our interface, we might have a ‘listener’ on each input element in the JavaScript. On a click or change, the function queries the DOM and counts how many of our player inputs are checked. On the basis of that count, we would then update something else in the DOM to show the user how many players are checked.
Let’s consider the cost of that basic operation. We are listening on multiple DOM nodes for the click/check of an input, then querying the DOM to see how many of a particular DOM type are checked, then writing something into the DOM to show the user, UI wise, the number of players we just counted.
The alternative would be to hold the application state as a JavaScript object in memory. A button/input click in the DOM could merely update the JavaScript object and then, based on that change in the JavaScript object, do a single-pass update of the all interface changes that are needed. We could skip querying the DOM to count the players as the JavaScript object would already hold that information.
So. Using a JavaScript object structure for the state seemed simple but flexible enough to encapsulate the application state at any given time. The theory of how this could be managed seemed sound enough too – this must be what phrases like ‘one-way data flow’ were all about? However, the first real trick would be in creating some code that would automatically update the UI based on any changes to that data.
The good news is that smarter people than I have already figured this stuff out (thank goodness!). People have been perfecting approaches to this kind of challenge since the dawn of applications. This category of problems is the bread and butter of ‘design patterns’. The moniker ‘design pattern’ sounded esoteric to me at first but after digging just a little it all started to sound less computer science and more common sense.
Design Patterns
A design pattern, in computer science lexicon, is a pre-defined and proven way of solving a common technical challenge. Think of design patterns as the coding equivalent of a cooking recipe.
Perhaps the most famous literature on design patterns is "Design Patterns: Elements of Reusable Object-Oriented Software" from back in 1994. Although that deals with C++ and smalltalk the concepts are transferable. For JavaScript, Addy Osmani’s "Learning JavaScript Design Patterns" covers similar ground. You can also read it online for free here.
Observer Pattern
Typically design patterns are split into three groups: Creational, Structural and Behavioural. I was looking for something Behavioural that helped to deal with communicating changes around the different parts of the application.
More recently, I have seen and read a really great deep-dive on implementing reactivity inside an app by Gregg Pollack. There is both a blog post and video for your enjoyment here.
When reading the opening description of the ‘Observer’ pattern in Learning JavaScript Design Patterns I was pretty sure it was the pattern for me. It is described thus:
The Observer is a design pattern where an object (known as a subject) maintains a list of objects depending on it (observers), automatically notifying them of any changes to state. When a subject needs to notify observers about something interesting happening, it broadcasts a notification to the observers (which can include specific data related to the topic of the notification).
The key to my excitement was that this seemed to offer some way of things updating themselves when needed.
Suppose the user clicked a player named “Betty” to select that she was ‘In’ for the game. A few things might need to happen in the UI:
Add 1 to the playing count
Remove Betty from the ‘Out’ pool of players
Add Betty to the ‘In’ pool of players
The app would also need to update the data that represented the UI. What I was very keen to avoid was this:
playerName.addEventListener("click", playerToggle); function playerToggle() { if (inPlayers.includes(e.target.textContent)) { setPlayerOut(e.target.textContent); decrementPlayerCount(); } else { setPlayerIn(e.target.textContent); incrementPlayerCount(); } }
The aim was to have an elegant data flow that updated what was needed in the DOM when and if the central data was changed.
With an Observer pattern, it was possible to send out updates to the state and therefore the user interface quite succinctly. Here is an example, the actual function used to add a new player to the list:
function itemAdd(itemString: string) { let currentDataSet = getCurrentDataSet(); var newPerson = new makePerson(itemString); io.items[currentDataSet].EventData.splice(0, 0, newPerson); io.notify({ items: io.items }); }
The part relevant to the Observer pattern there being the io.notify method. As that shows us modifying the items part of the application state, let me show you the observer that listened for changes to ‘items’:
io.addObserver({ props: ["items"], callback: function renderItems() { // Code that updates anything to do with items... } });
We have a notify method that makes changes to the data and then Observers to that data that respond when properties they are interested in are updated.
With this approach, the app could have observables watching for changes in any property of the data and run a function whenever a change occurred.
If you are interested in the Observer pattern I opted for, I describe it more fully here.
There was now an approach for updating the UI effectively based on state. Peachy. However, this still left me with two glaring issues.
One was how to store the state across page reloads/sessions and the fact that despite the UI working, visually, it just wasn’t very ‘app like’. For example, if a button was pressed the UI instantly changed on screen. It just wasn’t particularly compelling.
Let’s deal with the storage side of things first.
Saving State
My primary interest from a development side entering into this centered on understanding how app interfaces could be built and made interactive with JavaScript. How to store and retrieve data from a server or tackle user-authentication and logins was ‘out of scope’.
Therefore, instead of hooking up to a web service for the data storage needs, I opted to keep all data on the client. There are a number of web platform methods of storing data on a client. I opted for localStorage.
The API for localStorage is incredibly simple. You set and get data like this:
// Set something localStorage.setItem("yourKey", "yourValue"); // Get something localStorage.getItem("yourKey");
LocalStorage has a setItem method that you pass two strings to. The first is the name of the key you want to store the data with and the second string is the actual string you want to store. The getItem method takes a string as an argument that returns to you whatever is stored under that key in localStorage. Nice and simple.
However, amongst the reasons to not use localStorage is the fact that everything has to be saved as a ‘string’. This means you can’t directly store something like an array or object. For example, try running these commands in your browser console:
// Set something localStorage.setItem("myArray", [1, 2, 3, 4]); // Get something localStorage.getItem("myArray"); // Logs "1,2,3,4"
Even though we tried to set the value of ‘myArray’ as an array; when we retrieved it, it had been stored as a string (note the quote marks around ‘1,2,3,4’).
You can certainly store objects and arrays with localStorage but you need to be mindful that they need converting back and forth from strings.
So, in order to write state data into localStorage it was written to a string with the JSON.stringify() method like this:
const storage = window.localStorage; storage.setItem("players", JSON.stringify(io.items));
When the data needed retrieving from localStorage, the string was turned back into usable data with the JSON.parse() method like this:
const players = JSON.parse(storage.getItem("players"));
Using localStorage meant everything was on the client and that meant no 3rd party services or data storage concerns.
Data was now persisting refreshes and sessions — Yay! The bad news was that localStorage does not survive a user emptying their browser data. When someone did that, all their In/Out data would be lost. That’s a serious shortcoming.
It’s not hard to appreciate that `localStorage` probably isn’t the best solution for 'proper' applications. Besides the aforementioned string issue, it is also slow for serious work as it blocks the 'main thread'. Alternatives are coming, like KV Storage but for now, make a mental note to caveat its use based on suitability.
Despite the fragility of saving data locally on a users device, hooking up to a service or database was resisted. Instead, the issue was side-stepped by offering a ‘load/save’ option. This would allow any user of In/Out to save their data as a JSON file which could be loaded back into the app if needed.
This worked well on Android but far less elegantly for iOS. On an iPhone, it resulted in a splurge of text on screen like this:
Tumblr media
(Large preview)
As you can imagine, I was far from alone in berating Apple via WebKit about this shortcoming. The relevant bug was here.
At the time of writing this bug has a solution and patch but has yet to make its way into iOS Safari. Allegedly, iOS13 fixes it but it’s that’s in Beta as I write.
So, for my minimum viable product, that was storage addressed. Now it was time to attempt to make things more ‘app-like’!
App-I-Ness
Turns out after many discussions with many people, defining exactly what ‘app like’ means is quite difficult.
Ultimately, I settled on ‘app-like’ being synonymous with a visual slickness usually missing from the web. When I think of the apps that feel good to use they all feature motion. Not gratuitous, but motion that adds to the story of your actions. It might be the page transitions between screens, the manner in which menus pop into existence. It’s hard to describe in words but most of us know it when we see it.
The first piece of visual flair needed was shifting player names up or down from ‘In’ to ‘Out’ and vice-versa when selected. Making a player instantly move from one section to the other was straightforward but certainly not ‘app-like’. An animation as a player name was clicked would hopefully emphasize the result of that interaction – the player moving from one category to another.
Like many of these kinds of visual interactions, their apparent simplicity belies the complexity involved in actually getting it working well.
It took a few iterations to get the movement right but the basic logic was this:
Once a ‘player’ is clicked, capture where that player is, geometrically, on the page;
Measure how far away the top of the area is the player needs to move to if going up (‘In’) and how far away the bottom is, if going down (‘Out’);
If going up, a space equal to the height of the player row needs to be left as the player moves up and the players above should collapse downwards at the same rate as the time it takes for the player to travel up to land in the space vacated by the existing ‘In’ players (if any exist) coming down;
If a player is going ‘Out’ and moving down, everything else needs to move up to the space left and the player needs to end up below any current ‘Out’ players.
Phew! It was trickier than I thought in English — never mind JavaScript!
There were additional complexities to consider and trial such as transition speeds. At the outset, it wasn’t obvious whether a constant speed of movement (e.g. 20px per 20ms), or a constant duration for the movement (e.g. 0.2s) would look better. The former was slightly more complicated as the speed needed to be computed ‘on the fly’ based upon how far the player needed to travel — greater distance requiring a longer transition duration.
However, it turned out that a constant transition duration was not just simpler in code; it actually produced a more favorable effect. The difference was subtle but these are the kind of choices you can only determine once you have seen both options.
Every so often whilst trying to nail this effect, a visual glitch would catch the eye but it was impossible to deconstruct in real time. I found the best debugging process was creating a QuickTime recording of the animation and then going through it a frame at a time. Invariably this revealed the problem quicker than any code based debugging.
Looking at the code now, I can appreciate that on something beyond my humble app, this functionality could almost certainly be written more effectively. Given that the app would know the number of players and know the fixed height of the slats, it should be entirely possible to make all distance calculations in the JavaScript alone, without any DOM reading.
It’s not that what was shipped doesn’t work, it’s just that it isn’t the kind of code solution you would showcase on the Internet. Oh, wait.
Other ‘app like’ interactions were much easier to pull off. Instead of menus simply snapping in and out with something as simple as toggling a display property, a lot of mileage was gained by simply exposing them with a little more finesse. It was still triggered simply but CSS was doing all the heavy lifting:
.io-EventLoader { position: absolute; top: 100%; margin-top: 5px; z-index: 100; width: 100%; opacity: 0; transition: all 0.2s; pointer-events: none; transform: translateY(-10px); [data-evswitcher-showing="true"] & { opacity: 1; pointer-events: auto; transform: none; } }
There when the data-evswitcher-showing="true" attribute was toggled on a parent element, the menu would fade in, transform back into its default position and pointer events would be re-enabled so the menu could receive clicks.
ECSS Style Sheet Methodology
You’ll notice in that prior code that from an authoring point of view, CSS overrides are being nested within a parent selector. That’s the way I always favor writing UI style sheets; a single source of truth for each selector and any overrides for that selector encapsulated within a single set of braces. It’s a pattern that requires the use of a CSS processor (Sass, PostCSS, LESS, Stylus, et al) but I feel is the only positive way to make use of nesting functionality.
I’d cemented this approach in my book, Enduring CSS and despite there being a plethora of more involved methods available to write CSS for interface elements, ECSS has served me and the large development teams I work with well since the approach was first documented way back in 2014! It proved just as effective in this instance.
Partialling The TypeScript
Even without a CSS processor or superset language like Sass, CSS has had the ability to import one or more CSS files into another with the import directive:
@import "other-file.css";
When beginning with JavaScript I was surprised there was no equivalent. Whenever code files get longer than a screen or so high, it always feels like splitting it into smaller pieces would be beneficial.
Another bonus to using TypeScript was that it has a beautifully simple way of splitting code into files and importing them when needed.
This capability pre-dated native JavaScript modules and was a great convenience feature. When TypeScript was compiled it stitched it all back to a single JavaScript file. It meant it was possible to easily break up the application code into manageable partial files for authoring and import then into the main file easily. The top of the main inout.ts looked like this:
/// <reference path="defaultData.ts" /> /// <reference path="splitTeams.ts" /> /// <reference path="deleteOrPaidClickMask.ts" /> /// <reference path="repositionSlat.ts" /> /// <reference path="createSlats.ts" /> /// <reference path="utils.ts" /> /// <reference path="countIn.ts" /> /// <reference path="loadFile.ts" /> /// <reference path="saveText.ts" /> /// <reference path="observerPattern.ts" /> /// <reference path="onBoard.ts" />
This simple house-keeping and organization task helped enormously.
Multiple Events
At the outset, I felt that from a functionality point of view, a single event, like “Tuesday Night Football” would suffice. In that scenario, if you loaded In/Out up you just added/removed or moved players in or out and that was that. There was no notion of multiple events.
I quickly decided that (even going for a minimum viable product) this would make for a pretty limited experience. What if somebody organized two games on different days, with a different roster of players? Surely In/Out could/should accommodate that need? It didn’t take too long to re-shape the data to make this possible and amend the methods needed to load in a different set.
At the outset, the default data set looked something like this:
var defaultData = [ { name: "Daz", paid: false, marked: false, team: "", in: false }, { name: "Carl", paid: false, marked: false, team: "", in: false }, { name: "Big Dave", paid: false, marked: false, team: "", in: false }, { name: "Nick", paid: false, marked: false, team: "", in: false } ];
An array containing an object for each player.
After factoring in multiple events it was amended to look like this:
var defaultDataV2 = [ { EventName: "Tuesday Night Footy", Selected: true, EventData: [ { name: "Jack", marked: false, team: "", in: false }, { name: "Carl", marked: false, team: "", in: false }, { name: "Big Dave", marked: false, team: "", in: false }, { name: "Nick", marked: false, team: "", in: false }, { name: "Red Boots", marked: false, team: "", in: false }, { name: "Gaz", marked: false, team: "", in: false }, { name: "Angry Martin", marked: false, team: "", in: false } ] }, { EventName: "Friday PM Bank Job", Selected: false, EventData: [ { name: "Mr Pink", marked: false, team: "", in: false }, { name: "Mr Blonde", marked: false, team: "", in: false }, { name: "Mr White", marked: false, team: "", in: false }, { name: "Mr Brown", marked: false, team: "", in: false } ] }, { EventName: "WWII Ladies Baseball", Selected: false, EventData: [ { name: "C Dottie Hinson", marked: false, team: "", in: false }, { name: "P Kit Keller", marked: false, team: "", in: false }, { name: "Mae Mordabito", marked: false, team: "", in: false } ] } ];
The new data was an array with an object for each event. Then in each event was an EventData property that was an array with player objects in as before.
It took much longer to re-consider how the interface could best deal with this new capability.
From the outset, the design had always been very sterile. Considering this was also supposed to be an exercise in design, I didn’t feel I was being brave enough. So a little more visual flair was added, starting with the header. This is what I mocked up in Sketch:
Tumblr media
Revised design mockup. (Large preview)
It wasn’t going to win awards but it was certainly more arresting than where it started.
Aesthetics aside, it wasn’t until somebody else pointed it out, that I appreciated the big plus icon in the header was very confusing. Most people thought it was a way to add another event. In reality, it switched to an ‘Add Player’ mode with a fancy transition that let you type in the name of the player in the same place the event name was currently.
This was another instance where fresh eyes were invaluable. It was also an important lesson in letting go. The honest truth was I had held on to the input mode transition in the header because I felt it was cool and clever. However, the fact was it was not serving the design and therefore the application as a whole.
This was changed in the live version. Instead, the header just deals with events — a more common scenario. Meanwhile, adding players is done from a sub-menu. This gives the app a much more understandable hierarchy.
The other lesson learned here was that whenever possible, it’s hugely beneficial to get candid feedback from peers. If they are good and honest people, they won’t let you give yourself a pass!
Summary: My Code Stinks
Right. So far, so normal tech-adventure retrospective piece; these things are ten a penny on Medium! The formula goes something like this: the dev details how they smashed down all obstacles to release a finely tuned piece of software into the Internets and then pick up an interview at Google or got acqui-hired somewhere. However, the truth of the matter is that I was a first-timer at this app-building malarkey so the code ultimately shipped as the ‘finished’ application stunk to high heaven!
For example, the Observer pattern implementation used worked very well. I was organized and methodical at the outset but that approach ‘went south’ as I became more desperate to finish things off. Like a serial dieter, old familiar habits crept back in and the code quality subsequently dropped.
Looking now at the code shipped, it is a less than ideal hodge-bodge of clean observer pattern and bog-standard event listeners calling functions. In the main inout.ts file there are over 20 querySelector method calls; hardly a poster child for modern application development!
I was pretty sore about this at the time, especially as at the outset I was aware this was a trap I didn’t want to fall into. However, in the months that have since passed, I’ve become more philosophical about it.
The final post in this series reflects on finding the balance between silvery-towered code idealism and getting things shipped. It also covers the most important lessons learned during this process and my future aspirations for application development.
Tumblr media
(dm, yk, il, ra)
0 notes