I made a nice chunk of progress on the ‘To Try’ feature of HadCoffee this weekend. This lets you bookmark cafés you’ve heard of and would like to try later. By default the list on HadCoffee will sort by proximity using your current location.
Most of the recent work for this feature was on some nice-to-use buttons for toggling a cafe in your Favourites or To Try list. I had mostly built the ‘Fav star’ earlier, but improved it with a visual :focus indicator for keyboard accessibility.
See the Pen SVG Favourite by Mike (@mike_hasarms) on CodePen.0
The coffee cup icons are SVG based Vue components that receive rating data from the page and fill the cup appropriately.
I also worked on some basic animation of the button to add a cafe to the To Try list to make the transition to the active state a bit more obvious. I think little touches of movement make the thing more engaging to use as well.
I spent a bit of time improving the A11Y of these (and other features). The Vue SVG components are keyboard accessible, labelled and indicate focus. I am planning to do a more thorough A11Y test before I release, but I’m certainly not leaving all of those concerns until the end of development.
Currently the Favourites and To Try lists require a user to be logged in. I had planned to allow anonymous users to begin keeping these lists for themselves in localStorage. The idea was to lower the barrier to entry to using the site. It does however add development complexity as the app needs to be able to use both local and server storage for those lists and load in data in different ways.
As this is a mostly server-rendered app all the initial page data is sent down with the page load, and populated into the Vue components. Supporting anon users will require separate logic to request their cafe data from an API endpoint.
That’s not hugely difficult, it’s just another thing to be done before launch.
I’m keen to make some solid progress on HadCoffee over the Christmas period to hopefully get to an early 2019 v1 launch. This week I was working on the main coffee rating interface which depends on users being able to select or enter the café they visited.
I’ll add the autocomplete lookups for typed cafe names, but I also wanted some basic quick suggestions based on where the user has been before and their current location.
This would save typing for common places and maybe also hint at other nearby cafés the user mightn’t know about.
The frontend part of these feature went pretty smoothly. The Vue component watches for the users location (HTML5 Geolocation) and calls the backend to load these quick suggestions as it’s known. There’s a little debounce action to slow things down as the geolocation API can return updated positions in quick succession as it locks on a more accurate location.
Geographic Search by Proximity
The smooth progress I was making through this feature hit a wall when it came to actually querying cafés by distance though. I was hoping to use MySQL’s ST_Distance_sphere function to let the DB do that work. I’m running MariaDB though, which although it’s advertised as a ‘drop in’ replacement for MySQL does not support this feature 😞
I prefer a simpler dev environment (I’m not using Laravel Valet or Docker images) so I didn’t feel like swapping to MySQL for this project. Changing my workflow to use Valet also wasn’t very appealing when I’m otherwise happy with the setup. so I briefly tried migrating to Postgres. I know it’s a great DB, but I haven’t used it before and that’s a big change to have to make to run one type of query.
In the end I’m going with a raw SQL query to help with this. I’ll add a simple bounding box to its parameters first to avoid having to do a table scan of every cafe in the world (once my DB gets to that point 🙂)
Although it took a windy path this geo search will also provide the basis for the other cafe search features on the site such as the autocomplete (to improve relevance) and the location based search.
$query = "SELECT id, cafe_id, lat, lng, address, locality, city,
( 6371 * acos( cos( radians(:lat) ) * cos( radians( lat ) )
* cos( radians( lng ) - radians(:lng) ) + sin( radians(:lat2) ) * sin(radians(lat)) ) ) AS distance
FROM cafe_locations ";
This is a good step towards being able to add café & coffee reviews, however the next big sticking point will be letting users add new cafés as they go.
Ideally I’d like to collect a bit of meta data such as roasters, menu and seating options to help users finding cafés, but I’ll have to see how much data entry users will tolerate. I also need to be aware of how or if I can verify this community sourced data.
Photos – I’ve started to think they could be important for helping a user know if a cafe has the vibe they’re after. Implementing them does open a whole new bag of onions with regards to uploading, deleting, featuring, moderating, offloading, resizing and such.
I’m not sure if that should end up in the v1 release or if I’m just blowing up the scope unnecessarily. I’m not trying to be Instagram, but at the same time photos do tell a story of what the place is like.
Cafe Views in the Frontend
The HTML/CSS for a cafe view in the frontend are coming along. The placeholder data and characters need to be replaced with real components, but I do have the offset height panels and slanted shadow that I was after.
Seeya flat design, it’s been nice, but I need a break for a while.
I’ve made some progress on the frontend interfaces for logging a coffee/rating a cafe and the cafes section for managing your preferred cafes.
The coffee counter cup is now a working Vue component. The proportions will need a little bit of media query love to work at different sizes, but the main features of the interaction, the long shadow and the chamfer curved buttons are all in place 👌
The Cafe component of this UI will need a reasonable amount of backend work to function in order to auto-suggest cafes based on name and location.
I’m hoping that allowing users to enter new cafes not yet in the DB will be fairly straight forward. There’s a chance that managing duplicate or incorrect data could require additional processes beyond standard CRUDing. We’ll see.
This is my first use of Vue Router to provide a semi SPA type structure to this page. The Recent / Favs / To Try links are Vue Router links which dynamically swap out the component below for the appropriate content. Vue Router handles updating the browser URL to maintain back button functionality. I had to put a bit of work into detecting the current route in order to swap out the icon SVG symbol. All the icons on this screen including the active state color versions are referenced from a single SVG sprite.
I’m really happy with this approach for both performance and maintainability reasons. I can create different color variations of symbols directly in the SVG code without needing to use Sketch.
To lower the barrier to entry for using HadCoffee.com I’d like to allow anonymous users to be able to save cafes to their favs/to try lists as well. I’d likely just store cafe ids in localStorage and expand that data from the server.
Signed up users will have the more robust backend storage of all this data.