An app where people can debate one another over serious or casual questions/issues, localized to their city.
The app is missing design polish, but its core features work. The videos below demonstrate these core features.
A chat between two users (red and blue)
Users will want to justify their arguments with links to internet sources. Incorporating link previews gives an extra layer of security versus clicking on random link text.
Backend code (Node.js) reads and scans every message for links. Upon finding a link:
The website is scraped for a title, description, and a preview image.
The preview image is fetched and stored in a known and controlled backend.
The user retrieves the image from the backend. A safe endpoint.
This is all performed asynchronously.
Frontend code displays the messages, any asynchronously loaded link previews, and locally caches any images fetched.
In the video, each user sends a message consisting of a random GUID string (ex: "c4c003af-5b33-4bfb-ba28-a5677ef86ad3") plus a link to a website (ex: "google.com").
The link preview information is displayed below the message
Upon selecting a city, or having it automatically detected, the user is presented with the current week of questions.
The question, along with the top voted "stances" (created by other people), are displayed to give people an idea of the most enticing arguments.
Clicking on a question brings them to a screen with the full list of stances, the ability to vote on the stances, as well the option to join a debate with someone over the question.
Users can also create and vote on next week's questions.
Stances that appeal to the user, and which they vote on are saved in the "history hole". Users can explore their old views on issues and how their views change over time (i.e. how they grow as a person)
A (UI) Recycler
Long conversations need to be handled efficiently. We don't want to render an entire 1000+ message conversation of chat bubbles when we can only see and fit 10 chat bubbles on the screen at a time. Instead we keep the same 10 chat bubbles and replace the content of them with different messages as we scroll through the conversation.
This Recycler handles dynamically sized entries (different sized chat bubbles/messages), growing & shrinking of entries (a message expanding to show an asynchronously loaded link preview), insertion, deletion, and auto-scrolling to any entry. It also supports querying what the current entries are on screen.
This tools was eventually extracted, polished, and published as a standalone UI Recycler tool.
Distributed Counters
Due to backend limitations, we can only have one write per second. This means if 100 people vote at the same time it will take at least 100 seconds to increment the counter to the proper value.
Instead we distribute the work over multiple counters (say 10), effectively allowing 10 writes per second over 1, and regularly update the total.
This is not so straightforward, as the first counter's value could change by the time we get to the tallying of the 10th counter.
We could lock everyone out of voting while tallying but this causes delays.
Instead, we create a new set of vote counters every tally. Once we decide to tally, we point all voters to a new set of 10 counters, and leave the 10 old counters to be summed without worry of one of their values changing while we're tallying.
Security precautions were taken to ensure user's cannot manually override the counter value through a clever mock call and bump up certain values.
Location Lookup
Using Google Places API (part of Google Cloud Platform), we can take the user's location and query the nearest city
Matchmaking
Users need to get matched with other users to debate questions.
Users write a matchmaking "ticket" to the database which gets matched to a user with the opposite "ticket".
One-on-one chat
Upon matchmaking success, users are entered into a chat (a debate) with another user.
Upon completion of the debate, users have a chance to publicly publish their conversation, allowing others in the city to peep (and vote on) what others around them are talking about.
Security is important, therefore the chat was designed to hide people's personal details from one another (and not just hiding it from the front-end, but ensuring people can't mock backend requests for another's data).
Link Preview/Web Scraping
To provide more security, any links sent in a chat will be scanned, parsed, and filled in with link preview details by the backend. This includes scraping the site for a title, description, and a preview image.
To ensure we don't take users to random internet endpoints, we copy any preview image to our backend, and ask users to load the image through our known backend.
This all occurs asynchronously. Once the link preview details are found the message containing the link is updated, and our Recycler, able to resize entries, resizes it to show the link preview.
Message Caching
We don't want to waste bandwidth with user's constantly fetching images from our backend. Therefore we locally store the last X messages per chat (or a subset of the most recent chats), and the last Y messages in general.