-
Notifications
You must be signed in to change notification settings - Fork 0
/
index2.js
298 lines (264 loc) · 11.3 KB
/
index2.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
/**
* 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() {
// Shortcuts to Firebase SDK features.
this.auth = firebase.auth();
this.database = firebase.database();
this.storage = firebase.storage();
// Initiates Firebase auth and listen to auth state changes.
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 20 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(20).on('child_added', setMessage);
this.messagesRef.limitToLast(20).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) {
imgElement.src = imageUri;
// 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.
this.storage.ref(currentUser.uid + '/' + Date.now() + '/' + file.name)
.put(file, {contentType: file.type})
.then(function(snapshot) {
// Get the file's Storage URI and update the chat message placeholder.
var filePath = snapshot.metadata.fullPath;
data.update({imageUrl: this.storage.ref(filePath).toString()});
}.bind(this)).catch(function(error) {
console.error('There was an error uploading a file to Firebase Storage:', error);
});
}.bind(this));
}
};
// Signs-in Friendly Chat.
FriendlyChat.prototype.signIn = function() {
// Sign in Firebase using popup auth and Google as the identity provider.
var provider = new firebase.auth.GoogleAuthProvider();
this.auth.signInWithPopup(provider);
};
// Signs-out of Friendly Chat.
FriendlyChat.prototype.signOut = function() {
//signout 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('WELCOME.');
} 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. ' +
'You may also need to visit the Storage tab and paste the name of your bucket which is ' +
'displayed there.');
}
};
window.onload = function() {
window.friendlyChat = new FriendlyChat();
};