Skip to content

michaeldrotar/the-pub

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Description

This is a sample chat app that I wrote following the tutorial on socket.io in order to learn the concepts of using websockets with nodejs so that I can start making online games.

It's far from being complete, but I took it far enough to learn what I wanted to learn and make a reasonable attempt at something.

Development Notes

Early on I decided I'd just use bootstrap for the styling, but I later decided to research some nice color palettes. After looking through about 100 of them, I decided on one that I liked and applied it to the main areas. The form controls never got finished and some text could still use some tweaks.

I originally wanted each user to be able to pick their own font color/size/family and I imagined using a modal popup to pick those settings along with the user's own name. I was using a randomcolor lib to generate random defaults, but the colors just didn't look good for text so I dropped that idea in favor of an actual theme of colors and I changed the renaming to a simple /name command. Not providing a name allows the server to auto-name the person.

There's no handling of empty messages or trimming or anything, though the browser will condense whitespace. There's no limitation on what the name could be so users could get really obnoxious... but handling that stuff is fairly trivial and I've done it before.

The naming of things was fun.. I called it 'The Pub' because I thought of it as entering a big room full of people (where everybody knows your name). The first thing you do is introduce yourself so the first command you send to the server is the 'hello' command to provide your name. Similarly, leaving triggers a 'goodbye' command and when you talk you use the 'say' command. I could support 'whisper' and 'yell' and other such commands in the future.

I had written a token lib to generate a random 16 character string using all the letters and numbers and symbols on the keyboard.. and it ensured uniqueness by storing them in a hash (I did some performance testing with arrays.. hash scaled significantly better than indexOf).. this was going to serve as an authorization token so that users couldn't impersonate each other.. and the token could be recycled when a socket dropped. But once I learned more about how the sockets worked, I decided that wasn't necessary... and the sockets already provide a unique id.. so I scrapped the lib. I do use a similar lib to generate the random 5 digit number for users that don't name themselves.

Another major feature I did want to do was having the names auto-update in the log. Coming from an IRC background, I never liked that it simply printed 'Bob changed his name to Robert' and all past messages still said Bob. I first used Rivets to add data-binding, and I really liked that it used plain objects since that played well with lodash, but I struggled with getting the user list to sort and filter. One drawback to data-binding was that I needed to keep a record of every user that had ever been in the chat, even if they disconnected, because I'm binding that user to various messages like the 'user has entered' message. It's not plain text.. it's bound to the user.name property. So that meant I had to keep track of who was currently connected and filter my list down to only those people.. and then also sort it. To me, this is a display concern.. a different display might want to do things differently (and I was considering different themes and layouts).. so adding a computed property wasn't the ideal solution. I wanted something like {{ users | filter: 'connected' | sortBy: 'name' }} .. due to an error on my part, it didn't look like rivets supported this at first, so I tried using knockout (I've used it in the past). Halfway through the implementation I was annoyed because knockout uses functions for its observables in order to support older browsers.. but I don't care about those.. and functions drastically change things.. so then I went to angular. Angular was a ton more overhead that I wanted, but it worked.. and I liked the filter syntax since it could support objects.. so in the future I could do something like filter: { connected: true } or sortBy: [ 'name', 'asc' ] .. while implementing Angular, I noticed the bug on my end that was preventing it from working so I started to move back to Rivets, but Rivets doesn't use the colon for it's filters so it's less good at parsing and simply chops up the whitespace.. so filter { connected: true } would pass undefined, undefined, true, undefined as the arguments. I'd need to come up with a different format for passing it all and it would inflate my wrapper over lodash.. so I stuck with Angular and the binding and templating did turn out pretty nicely. I'm still bothered that I had to wrap the socket in order for that to play nice with the $scope .. wrapping things just seems like an immediate sign that you're doing it wrong.

I borrowed two features from Google Hangouts. First, if I kept typing many messages within a short window, I didn't want it to keep repeating my name. I wanted it to just append them. If I waited long enough between messages, then it could repeat my name. Second, I wanted intermittent timestamps. I originally timestamped every message but it looked awful and took up too much space, especially on mobile. So I got the momentjs lib to provide nice 'X minutes ago' messages and wrapped that in a function to mix in my own messages and eventually display a full date and time after a long enough amount of time has passed. I wanted both of these to be a view concern, but Angular doesn't provide a whole lot of good logic for doing assignments in the view. I read you can do {{ myVar = 'something'; ""} but that's nasty.. you need the double quote so that it prints the empty string rather than your assignment. So after some fiddling I realized that if I add the logic to the client to store the extra messages along with the original message then an alternate view that doesn't want to group them could still treat them as separate messages and repeat the user's name. Then I had the client also add timestamp messages intermittently so the view could then decide to not show the timestamp on each individual message but only show the dedicated timestamp messages instead. These worked out pretty well... I also didn't want it to show a timestamp if one had been shown in the past minute or so but implementing that got a little buggy and I didn't take the time to iron it out... ultimately increasing the thresholds might work better cause 10 seconds is pretty brief in practice, but the logic is all there so I'm happy with that.

Overall I learned a great deal from this exercise and I'm fairly happy with the current state of things. I wasn't concerned about how to organize my files, I just wanted to focus on how to pass commands around and calculate things based on the messages I got and bind the data. I'm really tempted to flesh this out, but I have no use for a chat app at the moment. Perhaps once I get a multiplayer online game fleshed out then having a lobby chat and in-game chat might be useful and then I'll really need to modularize this thing.

About

sample chat app using socket.io

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published