Last active
May 30, 2016 13:42
-
-
Save ismarsantos/a506c8d8ef2601c0758e5bb945e241f8 to your computer and use it in GitHub Desktop.
Chat
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Copyright 2015 Google Inc. All Rights Reserved. | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
'use strict'; | |
// Initializes FriendlyChat. | |
function FriendlyChat() { | |
this.checkSetup(); | |
// Shortcuts to DOM Elements. | |
this.messageList = document.getElementById('messages'); | |
this.messageForm = document.getElementById('message-form'); | |
this.messageInput = document.getElementById('message'); | |
this.submitButton = document.getElementById('submit'); | |
this.submitImageButton = document.getElementById('submitImage'); | |
this.imageForm = document.getElementById('image-form'); | |
this.mediaCapture = document.getElementById('mediaCapture'); | |
this.userPic = document.getElementById('user-pic'); | |
this.userName = document.getElementById('user-name'); | |
this.signInButton = document.getElementById('sign-in'); | |
this.signOutButton = document.getElementById('sign-out'); | |
this.signInSnackbar = document.getElementById('must-signin-snackbar'); | |
// Saves message on form submit. | |
this.messageForm.addEventListener('submit', this.saveMessage.bind(this)); | |
this.signOutButton.addEventListener('click', this.signOut.bind(this)); | |
this.signInButton.addEventListener('click', this.signIn.bind(this)); | |
// Toggle for the button. | |
var buttonTogglingHandler = this.toggleButton.bind(this); | |
this.messageInput.addEventListener('keyup', buttonTogglingHandler); | |
this.messageInput.addEventListener('change', buttonTogglingHandler); | |
// Events for image upload. | |
this.submitImageButton.addEventListener('click', function() { | |
this.mediaCapture.click(); | |
}.bind(this)); | |
this.mediaCapture.addEventListener('change', this.saveImageMessage.bind(this)); | |
this.initFirebase(); | |
} | |
// Sets up shortcuts to Firebase features and initiate firebase auth. | |
FriendlyChat.prototype.initFirebase = function() { | |
this.auth = firebase.auth(); | |
this.database = firebase.database(); | |
this.storage = firebase.storage(); | |
this.auth.onAuthStateChanged(this.onAuthStateChanged.bind(this)); | |
}; | |
// Loads chat messages history and listens for upcoming ones. | |
FriendlyChat.prototype.loadMessages = function() { | |
// Reference to the /messages/ database path. | |
this.messagesRef = this.database.ref('messages'); | |
// Make sure we remove all previous listeners. | |
this.messagesRef.off(); | |
// Loads the last 12 messages and listen for new ones. | |
var setMessage = function(data) { | |
var val = data.val(); | |
this.displayMessage(data.key, val.name, val.text, val.photoUrl, val.imageUrl); | |
}.bind(this); | |
this.messagesRef.limitToLast(12).on('child_added', setMessage); | |
this.messagesRef.limitToLast(12).on('child_changed', setMessage); | |
}; | |
// Saves a new message on the Firebase DB. | |
FriendlyChat.prototype.saveMessage = function(e) { | |
e.preventDefault(); | |
// Check that the user entered a message and is signed in. | |
if (this.messageInput.value && this.checkSignedInWithMessage()) { | |
var currentUser = this.auth.currentUser; | |
// Add a new message entry to the Firebase Database. | |
this.messagesRef.push({ | |
name: currentUser.displayName, | |
text: this.messageInput.value, | |
photoUrl: currentUser.photoURL || '/images/profile_placeholder.png' | |
}).then(function() { | |
// Clear message text field and SEND button state. | |
FriendlyChat.resetMaterialTextfield(this.messageInput); | |
this.toggleButton(); | |
}.bind(this)).catch(function(error) { | |
console.error('Error writing new message to Firebase Database', error); | |
}); | |
} | |
}; | |
// Sets the URL of the given img element with the URL of the image stored in Firebase Storage. | |
FriendlyChat.prototype.setImageUrl = function(imageUri, imgElement) { | |
// If the image is a Firebase Storage URI we fetch the URL. | |
if (imageUri.startsWith('gs://')) { | |
imgElement.src = FriendlyChat.LOADING_IMAGE_URL; // Display a loading image first. | |
this.storage.refFromURL(imageUri).getMetadata().then(function(metadata) { | |
imgElement.src = metadata.downloadURLs[0]; | |
}); | |
} else { | |
imgElement.src = imageUri; | |
} | |
}; | |
// Saves a new message containing an image URI in Firebase. | |
// This first saves the image in Firebase storage. | |
FriendlyChat.prototype.saveImageMessage = function(event) { | |
var file = event.target.files[0]; | |
// Clear the selection in the file picker input. | |
this.imageForm.reset(); | |
// Check if the file is an image. | |
if (!file.type.match('image.*')) { | |
var data = { | |
message: 'You can only share images', | |
timeout: 2000 | |
}; | |
this.signInSnackbar.MaterialSnackbar.showSnackbar(data); | |
return; | |
} | |
// Check if the user is signed-in | |
if (this.checkSignedInWithMessage()) { | |
// We add a message with a loading icon that will get updated with the shared image. | |
var currentUser = this.auth.currentUser; | |
this.messagesRef.push({ | |
name: currentUser.displayName, | |
imageUrl: FriendlyChat.LOADING_IMAGE_URL, | |
photoUrl: currentUser.photoURL || '/images/profile_placeholder.png' | |
}).then(function(data) { | |
// Upload the image to Firebase Storage. | |
var uploadTask = this.storage.ref(currentUser.uid + '/' + Date.now() + '/' + file.name) | |
.put(file, {'contentType': file.type}); | |
// Listen for upload completion. | |
uploadTask.on('state_changed', null, function(error) { | |
console.error('There was an error uploading a file to Firebase Storage:', error); | |
}, function() { | |
// Get the file's Storage URI and update the chat message placeholder. | |
var filePath = uploadTask.snapshot.metadata.fullPath; | |
data.update({imageUrl: this.storage.ref(filePath).toString()}); | |
}.bind(this)); | |
}.bind(this)); | |
} | |
}; | |
// Signs-in Friendly Chat. | |
FriendlyChat.prototype.signIn = function(googleUser) { | |
var provider = new firebase.auth.GoogleAuthProvider(); | |
this.auth.signInWithPopup(provider); | |
}; | |
// Signs-out of Friendly Chat. | |
FriendlyChat.prototype.signOut = function() { | |
// Sign out of Firebase. | |
this.auth.signOut(); | |
}; | |
// Triggers when the auth state change for instance when the user signs-in or signs-out. | |
FriendlyChat.prototype.onAuthStateChanged = function(user) { | |
if (user) { | |
// User is signed in! | |
// Get profile pic and user's name from the Firebase user object. | |
var profilePicUrl = user.photoURL; | |
var userName = user.displayName; | |
// Set the user's profile pic and name. | |
this.userPic.style.backgroundImage = 'url(' + profilePicUrl + ')'; | |
this.userName.textContent = userName; | |
// Show user's profile and sign-out button. | |
this.userName.removeAttribute('hidden'); | |
this.userPic.removeAttribute('hidden'); | |
this.signOutButton.removeAttribute('hidden'); | |
// Hide sign-in button. | |
this.signInButton.setAttribute('hidden', 'true'); | |
// We load currently existing chant messages. | |
this.loadMessages(); | |
} else { | |
// User is signed out! | |
// Hide user's profile and sign-out button. | |
this.userName.setAttribute('hidden', 'true'); | |
this.userPic.setAttribute('hidden', 'true'); | |
this.signOutButton.setAttribute('hidden', 'true'); | |
// Show sign-in button. | |
this.signInButton.removeAttribute('hidden'); | |
} | |
}; | |
// Returns true if user is signed-in. Otherwise false and displays a message. | |
FriendlyChat.prototype.checkSignedInWithMessage = function() { | |
// Return true if the user is signed in Firebase | |
if (this.auth.currentUser) { | |
return true; | |
} | |
// Display a message to the user using a Toast. | |
var data = { | |
message: 'You must sign-in first', | |
timeout: 2000 | |
}; | |
this.signInSnackbar.MaterialSnackbar.showSnackbar(data); | |
return false; | |
}; | |
// Resets the given MaterialTextField. | |
FriendlyChat.resetMaterialTextfield = function(element) { | |
element.value = ''; | |
element.parentNode.MaterialTextfield.boundUpdateClassesHandler(); | |
}; | |
// Template for messages. | |
FriendlyChat.MESSAGE_TEMPLATE = | |
'<div class="message-container">' + | |
'<div class="spacing"><div class="pic"></div></div>' + | |
'<div class="message"></div>' + | |
'<div class="name"></div>' + | |
'</div>'; | |
// A loading image URL. | |
FriendlyChat.LOADING_IMAGE_URL = 'https://www.google.com/images/spin-32.gif'; | |
// Displays a Message in the UI. | |
FriendlyChat.prototype.displayMessage = function(key, name, text, picUrl, imageUri) { | |
var div = document.getElementById(key); | |
// If an element for that message does not exists yet we create it. | |
if (!div) { | |
var container = document.createElement('div'); | |
container.innerHTML = FriendlyChat.MESSAGE_TEMPLATE; | |
div = container.firstChild; | |
div.setAttribute('id', key); | |
this.messageList.appendChild(div); | |
} | |
if (picUrl) { | |
div.querySelector('.pic').style.backgroundImage = 'url(' + picUrl + ')'; | |
} | |
div.querySelector('.name').textContent = name; | |
var messageElement = div.querySelector('.message'); | |
if (text) { // If the message is text. | |
messageElement.textContent = text; | |
// Replace all line breaks by <br>. | |
messageElement.innerHTML = messageElement.innerHTML.replace(/\n/g, '<br>'); | |
} else if (imageUri) { // If the message is an image. | |
var image = document.createElement('img'); | |
image.addEventListener('load', function() { | |
this.messageList.scrollTop = this.messageList.scrollHeight; | |
}.bind(this)); | |
this.setImageUrl(imageUri, image); | |
messageElement.innerHTML = ''; | |
messageElement.appendChild(image); | |
} | |
// Show the card fading-in. | |
setTimeout(function() {div.classList.add('visible')}, 1); | |
this.messageList.scrollTop = this.messageList.scrollHeight; | |
this.messageInput.focus(); | |
}; | |
// Enables or disables the submit button depending on the values of the input | |
// fields. | |
FriendlyChat.prototype.toggleButton = function() { | |
if (this.messageInput.value) { | |
this.submitButton.removeAttribute('disabled'); | |
} else { | |
this.submitButton.setAttribute('disabled', 'true'); | |
} | |
}; | |
// Checks that the Firebase SDK has been correctly setup and configured. | |
FriendlyChat.prototype.checkSetup = function() { | |
if (!window.firebase || !(firebase.app instanceof Function) || !window.config) { | |
window.alert('You have not configured and imported the Firebase SDK. ' + 'Make sure you go through the codelab setup instructions.'); | |
} else if (config.storageBucket === '') { | |
window.alert('Your Firebase Storage bucket has not been enabled. Sorry about that. This is ' + | |
'actually a Firebase bug that occurs rarely.' + | |
'Please go and re-generate the Firebase initialisation snippet (step 4 of the codelab) ' + | |
'and make sure the storageBucket attribute is not empty.'); | |
} | |
}; | |
window.onload = function() { | |
window.friendlyChat = new FriendlyChat(); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment