Giter Club home page Giter Club logo

oddchatter's Introduction

Odd Chatter

A callback-enabled chat UI for use while watching Odd Salon.


Odd Chatter is a Firebase project, https://firebase.google.com/docs. All data and configuration is stored in Cloud Firestore data, and the HTML/JS/CSS frontend is hosted on Firebase Hosting.

This web app consists of a chat window with pre-defined "callbacks", which the users type or click based on what they see in the talk. (These are callbacks in the style of live-performance Rocky Horror Picture Show, not as in programming.) When enough users activate a callback within a predefined period of time, the app shows a short YouTube clip that contains an audio recording of that callback.

In addition, there is an "admin mode" that allows certain users to delete messages, and a pre-show mode that shows a banner and marketing links instead of the chat UI.

User authentication is through Google Accounts.

Compiling and deploying

Prerequisites

You need to install Firebase locally both to compile and deploy the code: npm install --save firebase.

The client-side code is JS, using the google-closure-library (install using npm install --save google-closure-library) and compiled using google-closure-compiler (install using npm install --save google-closure-compiler).

Compiling

The correct magic incantation to google-closure-compiler is stored in compile.bat (for Windows) andcompile.sh (for Bash systems); the right script for your system can be run with npm run build. Additional parameters to these scripts are added to the parameters to google-closure-compiler, eg:

  • --define=config.DEBUG_MODE simplifies local testing by, eg, bypassing the 'enabled' check in the configuration stored in Firestore:

    $ npm run build -- --define=config=DEBUG_MODE

Running locally

Firebase supports running a web app on localhost, against a hosted database. Make sure you're running against the dev environment by using the following commands:

$ firebase use dev
$ firebase serve --only hosting:odd-chatter-dev

Then your app will be available at http://localhost:5000.

Deploying to dev environment

A dev version of this app lives at https://odd-chatter-dev.web.app/. You can deploy the compiled code to this site using the following commands:

$ firebase use dev
$ firebase deploy

or in one line:

$ firebase deploy -P dev

Deploying to production

The production link that we send to Odd Salon attendees is https://odd-chatter.web.app. To release to production, follow these steps:

  1. Copy the compiled HTML/JS/CSS from public/ to release/:

    $ rm -rf release/*
    $ cp -r public/* release/*
  2. Run a local instance of the prod code against the dev database, to verify that everything looks good:

    $ firebase serve -P dev --only hosting:odd-chatter
  3. If everything looks good, run a local instance of the prod code against the prod database, and make sure everything is OK:

    $ firebase serve -P release --only hosting:odd-chatter
  4. If this looks good too, deploy everything:

    $ firebase deploy -P release
  5. Commit the new contents of the release/ directory into version control, for posterity.

Deploying the admin console

The admin console is at https://odd-chatter-admin.web.app, with a dev instance at https://odd-chatter-admin-dev.web.app. To release the admin console, follow these steps:

  1. Run a local instance of the dev admin console, at http://localhost:5000:
$ firebase use dev
$ firebase serve --only hosting:odd-chatter-admin-dev
  1. Run a local instance of the prod admin console against the dev database, to verify that everything looks good:

    $ firebase serve -P dev --only hosting:odd-chatter-admin
  2. If everything looks good, run a local instance of the prod code against the prod database, and make sure everything is OK:

    $ firebase serve -P release --only hosting:odd-chatter-admin
  3. If this looks good too, deploy everything (NB this deploys both the admin console and the chat app):

    $ firebase deploy -P release

How this works

Cloud Firestore data architecture

messages: a list of documents representing one message each.
  • id: automatically generated.
  • name, string: the display name of the user.
  • profilePicUrl, string: the profile photo to display.
  • text, string: the text of the message.
  • timestamp, firebase.firestore.Timestamp: the time the message was sent.
  • uid, string: the UID of the user.
SHIPS, MAPS, etc (one per callback): a list of documents representing the last time a user said the callback.
  • id: corresponds to the UID of the user
  • timestamp, firebase.firestore.Timestamp: the last time the user said the callback.
configuration: a bunch of configuration values, allowing the app to readjust on the fly.
  • timestamp, firebase.firestore.Timestamp: the app always uses the most recent document.
  • admin_users, array of string: UIDs of users who should see a 'delete' link next to each message (this should correspond to the uids in the ACLs in firestore.rules.)
  • callback_threshold, number: the minimum number (if threshold_is_percentage is false) or percentage (if threshold_is_percentage is true) of users who need to say the same callback within callback_window_ms milliseconds for the video to play. A callback can optionally specify a weight which is a multiplier that is applied to both this and callback_window_ms.
  • callback_window_ms, number: the length of the window in milliseconds within which callback_threshold users need to say the same callback for the video to play. A callback can optionally specify a weight which is a multiplier that is applied to both this and callback_threshold.
  • enabled, boolean: when this is false, the app stays displaying the splash screen. This allows us to enable and disable the app before and after an Odd Salon.
  • fallback_url, string: a URL to fall back to in an emergency. If this is non-empty, the app shows an error screen with a link to this URL.
  • youtube_chat, string: the video ID of a YouTube live stream. If this is non-empty, the right half of the desktop UI will embed the YouTube chat widget for that stream. If youtube_video is also set, the chat will appear below the embedded video.
  • youtube_video, string: the video ID of a YouTube live stream. If this is non-empty, the right half of the desktop UI will embed the YouTube video of that stream. If youtube_chat is also set, the embedded video will appear above the chat.

JS client architecture

The JS client is roughly MVC, where the Firebase Firestore DB is the model, view.js acts as the view by pushing data into the DOM , and controller.js acts as the controller by handling user interactions with the DOM. In addition, config.js is a layer to the Firestore configuration collection; ui.js stores the references to DOM elements that both the view and the controller need; and callbacks.js handles the logic controlling callbacks.

Sending a message

When a user sends a message, it is written into the messages collection, in oddsalon.oddchatter.controller.saveMessage_(). (The callback buttons simply send a text message with predefined strings.) In addition, if the message consists of one of the predefined strings (meaning the user typed a callback manually), the document in that callback's collection with the user's UID as its document ID is updated with the current timestamp; this check is done in oddsalon.oddchatter.controller.checkForCallbacks_().

Displaying messages

The script installs a Firestore query listener in oddsalon.oddchatter.view.loadMessages() to add a message div to the messages list for every new document in the messages collection, or delete the div when a message disappears. Currently (for speed reasons) it only shows the most recent 12 messages, deleting older divs as new messages appear.

Displaying callback videos

The script installs a Firestore query listener for each callback in oddsalon.oddchatter.view.loadCallbacks(), to display the callback's video if the most recent oddsalon.oddchatter.config.Configuration.callbackThreshold() documents in that callback's collection are all more recent than oddsalon.oddchatter.config.Configuration.callback_window_ms milliseconds ago, or the last time the callback video was played, whichever is more recent.

Onboarding

Some browsers (iOS Safari cough cough) have auto-play policies that demand that a user interaction happen before you can play the sound. So before anything works, we need the user to click on a button to explicitly trigger the sound; then we can later play the sound at will. (See: https://rosswintle.uk/2019/01/skirting-the-ios-safari-audio-auto-play-policy-for-ui-sound-effects/) To force encourage our users to actually click the buttons, we show a splash screen explaining the callbacks and asking the users to agree to the Odd Salon Code of Conduct. The "agree" button plays all the callback sounds, muted and at double speed, before unmuting the audio elements again for later use in the chat.

Configuration

The script installs a Firestore query listener in oddsalon.oddchatter.config.Configuration.loadFromFirestore() to listen to changes in the configuration collection, and update its local variables correspondingly. oddsalon.oddchatter.view.applyNewConfiguration() is called by oddsalon.oddchatter.config.Configuration whenever the configuration changes, to show or hide the appropriate UI elements.

Analytics

In theory, this app uses Google Analytics to record user behavior, logging events to track which screens are seen and which elements are used. As of 8/27/2020 this does not appear to be working.

oddchatter's People

Contributors

contextsans avatar dependabot[bot] avatar juniormonkey avatar

Stargazers

 avatar

Watchers

 avatar  avatar

oddchatter's Issues

Chat history issue on late join

If you log into a salon in progress, the chat starts at the top of the event and doesn't offer you the "jump to latest" button. You have to go scroll to the bottom to find it.

auth is finicky in safari

"noticed the auth here was finicky just now in safari. I'm in no problems now (obv) with Chrome." reported by Douglas Worley

Actually play the callback sounds that match the videos

When we had the long interactive onboarding script, we were limited to playing only the callback sounds that the onboarding script triggered - so we gave each callback a single audio element, and played that no matter which video was picked.

Now that we are initializing all the callback sounds simultaneously on the "accept code of conduct" click, we can initialize an arbitrary number of sounds, so there's nothing stopping us playing the sound that matches the randomly selected video. We should do that.

New message sound, when in background

This would have to be only when the app is in the background, and perhaps only once, or it'll get super annoying.
Might need to be configurable? We could offer a browser notification as an option too?
This needs more thought.

Support a wildcard/custom Salon-specific callback

Occasionally we have a curator introduce a new callback just for this salon. Potentially we could reserve one callback button for the "wildcard", which (with a curator's forewarning!) could be configured to a once-off callback for individual salons? This would need:

  • a wildcard callback button, with text and video/audio set at runtime
  • configuration in the admin module to specify callback text and video/audio
  • a heads-up from the curator, with a video/audio file to use for the callback

Show more than 16 messages in chat

Chat gets pretty lively. We should work out how to show all messages since the start of the salon, in a performant way. (Maybe this needs pagination or something.)

Investigate other form factors

Might be cool to have the chat window pop out, or as a pop-up over a youtube tab, or something. Chrome extension? Who knows.

Auto-thresholds

Keep track of current chat population, and make the callback thresholds auto-setting as a fraction of population.

During MISTAKES WERE MADE, '4' seemed to work pretty well for ~100 people.

Feedback from MISTAKES WERE MADE

ONBOARDING

  • (Perhaps the boo and cheer should be reversed, to end the onboarding on an upper not a downer) (unnecessary with coc)
  • Include mention of odd salon coc: https://www.oddsalon.com/about/code-of-conduct/ (done)
  • Identify tutorial as a tutorial, make it clearer you need to actually press the buttons (unnecessary)
  • Can we play all the sounds at once, from a single button click, at volume 0? Does that satisfy the iOS requirements? Then we just have users accept the COC instead of go through the whole tutorial. #9
  • If the COC single-button works, no need to play all audio individually. Make the tutorial much shorter. (Especially if I add more buttons, eg STEEN) (done)

NEW BUTTONS

  • 🎙️ = STEEEEEEEN #5
  • 🥂 =a toast (more applause?) #6

IN THE CHAT

  • Show all messages since a certain date <- will need pagination. (“Start of event” timestamp in config, don’t show messages older than) #8
  • Don't scroll to bottom whenever a new message arrives (only when you type one) #7
  • Janky on scroll-up? (Ah this is the scroll to bottom thing.)
  • (Linkify some whitelist of URLs, eg. facebook events, oddsalon.com, for easy promotion?) #21

ELSEWHERE

  • Chat transcript of each event (in a separate admin UI perhaps?) #2
  • The splash screen needs to say “come back at 5pm” or something. And link to a ticket link or FB event page or something #22
  • Dev option in config, to enable it locally but not overall #4
  • (Notifications when it’s in the background? Or new message sound? This is low priority) #24
  • (Keep track of population, make the thresholds auto-setting. Though '4' seemed to work pretty well for ca 100 people) #23
  • Long-term: is it possible make chat window appear like a pop up over the youtube page, like when chatting to a support person online #25

Make message insertion more efficient

instead of storing the timestamp in the DOM, and doing insertion sort by DOM traversal, let's use the JS Message object (or a map thereof) to keep the messages in order.

Auto-linkify some URLs in chat

It might be nice, for promo purposes, to have some URLs be rendered as a hyperlink when they appear within a message.

Eg, anything that begins with www.oddsalon.com, patreon.com/oddsalon, upcoming facebook events, eventbrite?

Investigate shorter onboarding

Can we satisfy the iOS Safari constraint by playing all the sounds at once, with volume zero temporarily? If so, we should replace the onboarding prompts with a single "reminder, the odd salon code of conduct applies" "". Then we can have a short "this is not a quiet event - try the buttons below to call back something relevant to our speakers" intro that doesn't get in peoples' way.

Don't scroll to bottom of the chat list whenever a new message arrives

During MISTAKES WERE MADE, the chat window was described as "janky on scroll-up". Other feedback observed that the messages were hard to keep up with, since it jumped to the bottom whenever a new one came in, so you couldn't scroll up to read old messages.

We should fix that. Probably we should still scroll to the bottom when the user sends a new message themselves. Or maybe an explicit button to jump to the bottom when a user has scrolled up.

Don't type out each user's callback individually

It's wasteful of scroll space to display each "SHIPS!!" in a separate message. Perhaps we could display a progress bar instead? That would also give the visual hint that more people need to say the same thing until we get to the actual video? And we could make the video replace the progress bar.

Add a CHEERS callback

🥂 =a toast

We'll need to decide what sound/video to play. more applause perhaps?

Create an admin UI

It would be nice to have a separate page for admin tasks, eg:

  • Chat transcript
  • on/off switch (we have this via firebase console, but would be nice to expose it to Annetta/Tre/etc)
  • blocked users (ditto)
  • thresholds? (ditto - though auto-setting thresholds might make this unnecessary, #23 )

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.