Skip to content

Instantly share code, notes, and snippets.

@afalchi82
Created May 18, 2016 17:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save afalchi82/34b8354f360f0d7e5bb82866c823fc5c to your computer and use it in GitHub Desktop.
Save afalchi82/34b8354f360f0d7e5bb82866c823fc5c to your computer and use it in GitHub Desktop.
Vocal chatroom (WIP)
section#angular-app
.container
.row
.col-xs-12
h1 Vocal Chatroom <i class="fa fa-microphone" aria-hidden="true"></i>
small speak in your browser's voice (<strong>turn on the volume <i class="fa fa-smile-o" aria-hidden="true"></i></strong>)
// h4 CHAT ID:
strong {{chat_id}}
//
.row
.col-xs-12
| Chats: {{chats.length}} <br>
| ChatID: {{user.chatroom}}
br
pre | Auth: {{authObj | json}}
ul
li(ng-repeat="chat in chatrooms")
.row(ng-show="usernameIsSet")
.col-xs-12
ul.users
li(ng-repeat="user in users")
.avatar
.img(style="background-image:url({{user.picture}})", ng-class="{speak: isSpeaking && isSpeakingUser === user.id}")
p {{user.name}}
.row(ng-show="!usernameIsSet")
.col-xs-12
.avatar(ng-class="user.name")
.img(style="background-image:url({{user.picture}}); margin-left:auto; margin-right:auto; width:120px; height: 120px;", ng-class="{speak: isSpeaking && isSpeakingUser === user.id}")
p {{user.name}}
// LOGIN FORM
.row
.col-xs-12
.well(ng-show="!usernameIsSet")
form(name="login", ng-submit="login.$valid && setUser(user.chatroom)")
.form-group
label
i.fa.fa-user
| Username
input.form-control(type="text", novalidate, required, maxlength="15", ng-model="user.name", placeholder="Choose a username", ng-disabled="usernameIsSet")
.form-group(ng-show="!usernameIsSet")
label
i.fa.fa-picture-o
| Profile picture URL
input.form-control(type="text", novalidate, ng-model="user.picture", placeholder="picture", ng-disabled="usernameIsSet")
.form-group(ng-show="!usernameIsSet")
label
i.fa.fa-comments-o
| Chatrooms
.new-room.button.btn.btn-default.btn-sm.btn-link.pull-right(ng-click="createRoom()")
| Create new room
br
div
label.btn.btn-default(ng-repeat="chat in chats")
input#option1(type='radio', name='options', value="{{chat.$id}}", ng-model="user.chatroom", required, ng-disabled="countUsers(chat) >= 4")
ul.users__list.list-reset
li(ng-repeat="user in chat.users")
// img(ng-src="{{user.picture}}" style="width:30px")
// | {{user.name}}
.avatar(ng-class="user.name")
.img(style="background-image:url({{user.picture}}); margin-left:auto; margin-right:auto; ", ng-class="{speak: isSpeaking && isSpeakingUser === user.name}")
p.avatar__name {{user.name}}
small.chat__is-full(ng-if="countUsers(chat) >= 4") This chatroom is full. Please join another or create a new one.
// | Chat: {{user.chatroom}}
.form-group
button.btn.btn-success.btn-lg(type="submit", ng-if="!usernameIsSet")
| Start!
// /LOGIN FORM
.row(ng-show="usernameIsSet")
.col-xs-12
form(ng-submit="submit()")
.form-group
input.form-control(type="text", novalidate, required, maxlength="50", placeholder="Your message" ng-model="user.message")
.form-group
// button.btn.btn-primary(type="submit")
| Send message
.row
.col-xs-12
// Articoli
article.media(ng-repeat="item in chatMessages") {{item.text}}
//button.btn.btn-sm.btn-danger(ng-click="clear()") remove
//
.row
.col-xs-12
pre
| {{users | json}}
//
.row
.col-xs-12
a(href="https://twitter.com/share", class="twitter-share-button", data-via="afalchi82") Tweet
script
| !function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');
.row
.col-xs-12
a(href="https://m1sc.firebaseio.com/chat/temp", target="_blank") Inspect this firebase
'use strict';
// Console log utility
var cl = function($m){ console.log($m);};
console.clear();
angular.module('myApp', ['firebase'])
.run(function($rootScope, chatMessages, speechSynt, users, ChatsFB) {
var vm = $rootScope;
// Chatrooms
vm.chats = ChatsFB.arr();
vm.createRoom = function () {
vm.chats.$add({messages: ''});
};
vm.user = {
chatroom: '',
name: 'Happy Uncle',
message: '',
id: Date.now(),
picture: 'https://cdn.pbrd.co/images/P93JuF9.png'
};
// Set ChatID
var chatID = vm.user.chatroom;
vm.setUser = function ($chatID) {
vm.usernameIsSet = true;
vm.chatMessages = chatMessages.ref(vm.user.chatroom);
vm.chatMessagesArr = chatMessages.arr(vm.user.chatroom);
vm.users = ChatsFB.currentChatUsersArr($chatID);
vm.authObj = users.authObj;
vm.chat_id = chatMessages.id;
vm.isSpeaking = false;
vm.chatID = chatID;
// Save user in current chatroom
var currentChat = ChatsFB.currentChatUsersArr($chatID);
currentChat.$add(vm.user);
// Start speech
speechInit();
};
vm.submit = function () {
vm.chatMessagesArr.$add({
user: vm.user.name,
text: vm.user.message,
id: vm.user.id,
date: Date.now()
});
vm.user.message = '';
};
vm.clear = function () {
vm.chatMessages.ref.set('');
};
speechSynt.speech.onend = function () {
vm.$applyAsync(function () {
vm.isSpeaking = false;
});
};
// Read message
function speechInit () {
chatMessages.ref(vm.user.chatroom).limitToLast(1).on('child_added', function(childSnapshot, prevChildKey) {
speechSynt.talk(childSnapshot.val().text);
vm.$applyAsync(function () {
vm.isSpeaking = true;
vm.isSpeakingUser = childSnapshot.val().id;
cl(childSnapshot.val().id);
});
});
}
// User count method
vm.countUsers = function (chat) {
var count = 0;
for (var obj in chat.users) {
count++;
}
return count;
};
})
/* ---------------------------------------------------------
Services
----------------------------------------------------------*/
.value('chatID', '')
.value('FBRoot', 'https://m1sc.firebaseio.com/chat/temp')
.factory('speechSynt', function() {
var voices = window.speechSynthesis.getVoices(),
speech = new SpeechSynthesisUtterance();
speech.voice = voices[2]; // Note: some voices don't support altering params
speech.voice = speechSynthesis.getVoices().filter(function(voice) {return voice.name == 'Whisper';})[0]
speech.voiceURI = 'native';
speech.volume = .8; // 0 to 1
speech.rate = 1; // 0.1 to 10
speech.pitch = 1; //0 to 2
speech.lang = 'it-IT';
// speech.lang = 'en-US';
return {
speech: speech,
talk: function ($m) {
speech.text = $m;
speechSynthesis.speak(speech);
}
};
})
.factory('users', function(FBRoot, $firebaseArray){
var ref = new Firebase(FBRoot + '/users'),
authObj;
ref.authAnonymously(function(error, authData) {
if (error) {
// console.log("Login Failed!", error);
} else {
// console.log("Authenticated successfully with payload:", authData);
authObj = authData;
}
})
return {
fbArr: $firebaseArray(ref),
authObj: authObj
};
})
.factory('ChatsFB', function(FBRoot, $firebaseArray){
return {
arr: function () {
var ref = new Firebase(FBRoot + '/chatrooms');
return $firebaseArray(ref);
},
currentChatUsersArr: function ($id) {
var ref = new Firebase(FBRoot + '/chatrooms/' + $id + '/users');
return $firebaseArray(ref);
}
};
})
.factory("Presence", function() {
return {
ref: function (id) {
var ref = new Firebase(FBRoot + '/chatrooms/' + id + '/messages');
return ref;
},
arr: function (id) {
var ref = new Firebase(FBRoot + '/chatrooms/' + id + '/messages');
return $firebaseArray(ref);
}
};
})
.factory("chatMessages", function(FBRoot, $firebaseArray) {
return {
ref: function (id) {
var ref = new Firebase(FBRoot + '/chatrooms/' + id + '/messages');
return ref;
},
arr: function (id) {
var ref = new Firebase(FBRoot + '/chatrooms/' + id + '/messages');
return $firebaseArray(ref);
}
};
})
;
/* --------------------------------------------------------------- */
// Manual bootstrapping Angular to check browser speech support first
/* --------------------------------------------------------------- */
if ('speechSynthesis' in window) {
angular.element(document).ready(function() {
angular.bootstrap(document, ['myApp']);
});
} else {
console.log('else');
document.body.innerHTML = '<i class="fa fa-chrome" aria-hidden="true"></i> Please use Google Chrome.';
}
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script src="https://code.angularjs.org/1.3.15/angular.min.js"></script>
<script src="https://cdn.firebase.com/js/client/2.2.4/firebase.js"></script>
<script src="https://cdn.firebase.com/libs/angularfire/1.2.0/angularfire.min.js"></script>
@import url(https://fonts.googleapis.com/css?family=Work+Sans:400,300,500);
@mixin size($w, $h) { width: $w; height: $h;}
@mixin square($s) { width: $s; height: $s;}
$accent: #F12E8F;
body {
font-family: 'Work Sans', sans-serif;
padding: 50px 0;
text-align: center;
// background-color: #5CB8B3;
}
.container {
max-width: 480px;
}
h1 {
font-weight: 800;
font-size: 45px;
margin-bottom: 30px;
.fa {
color: $accent;
}
small {
text-transform: uppercase;
font-size: 12px;
display: block;
}
}
form {
margin: 0 0 20px;
label, label.btn {
display: block;
text-align: left;
}
label.btn {
margin-bottom: 5px;
}
input {
// text-align: center;
}
[type="radio"] {
width:12px;
}
}
.avatar {
margin-bottom: 30px;
text-align: center;
text-transform: capitalize;
.img, .img:after {
background-size: cover;
border-radius: 60px;
}
.img {
margin: 30px 10px 10px;
transition: all .5s linear;
transform: scale(1);
position: relative;
width: 60px;
height: 60px;
z-index: 2;
&:after {
// background: url('https://cdn.pbrd.co/images/YowzOyh.png');
background-size: cover;
content:'';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 0;
}
}
}
.users {
margin: 0;
padding: 0;
text-align: center;
li {
display:inline-block;
list-style:none;
}
}
.users__list {
margin:0; padding: 0;
li {
list-style: none;
display: inline;
margin: 0 8px;
}
.avatar {
display: inline-block;
margin: 0 auto;
.img {
@include square(50px);
margin: 0 5px;
}
&__name {font-size:10px;}
}
}
.new-room {
margin-top: -5px;
}
.chat {
&__is-full {
font-size: 10px;
color: red;
}
}
.speak {
animation-name: speakAnim;
animation-duration: .1s;
animation-timing-function: ease-out;
animation-iteration-count: infinite;
animation-direction: alternate;
}
@keyframes speakAnim {
from {
transform: scale(1.2);
box-shadow: 0 0 0px $accent;
}
to {
transform: scale(1.22);
box-shadow: 0 0 5px $accent;
}
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.5.1/animate.min.css" rel="stylesheet" />
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.2/css/font-awesome.min.css" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment