-
Partiamo dal repository https://github.com/SoNet-2016/social-prototype-maps per aggiungere la possibilità di chattare con altri utenti loggati al nostro servizio.
-
Prima di tutto andiamo su firebase per aggiungere un campo che indichi se un utente è loggato o no. Su firebase abbiamo 2 nodi: pizzas e users. Nel secondo ci sono informazioni riguardanti i vari utenti registrati al servizio: aggiungiamo un nuovo campo "logged". Esso conterrà valore true se l'utente è loggato, false se non lo è.
-
A questo punto apriamo il nostro progetto con WebStorm e aggiungiamo un servizio che setti il campo logged a true quando l'utente effettua il login ed a false quando effettua il logout
-
creiamo la cartella users (posizione: app/components/users)
-
creiamo un servizio "padre" (users.js) in components/users/ che dichiari la dipendenza dal servizio che vogliamo effettivamente implementare (users-chat-login-service)
users.js 'use strict'; angular.module('pizzApp.users', [ 'pizzApp.users.chat.login.service', ]);
-
creiamo il servizio pizzApp.users.chat.login.service con 2 funzioni: una (registerLogin) per registrare il valore true e l'altra (registerLogout) per registrare il valore false.
'use strict'; angular.module('pizzApp.users.chat.login.service', []) .factory('UsersChatLoginManager', function usersChatLoginManager(FBURL, $firebaseArray, $firebaseObject) { return { registerLogin: function(uid) { var ref = new Firebase(FBURL + '/users/' + uid + '/logged'); // create a transaction ref.transaction(function () { return true; }); }, registerLogout: function(uid) { var ref = new Firebase(FBURL + '/users/' + uid + '/logged'); // create a transaction ref.transaction(function () { return false; }); } }; });
-
aggiungiamo la dipendenza al servizio padre in app.js
angular.module('pizzApp' [ ... pizzApp.users, ... ])
-
aggiungiamo la dipendenza da entrambi gli script in index.html
<script src="components/users/users.js"></script> <script src="components/users/users-chat-login-service.js"></script>
-
A questo punto dobbiamo richiamare la funzione registerLogin quando l'utente effettua il login. Quindi andiamo in /app/login/login.js e la modifichiamo in questo modo:
'use strict'; angular.module('pizzApp.login', ['ngRoute', 'pizzApp.users']) // Route Config .config(['$routeProvider', function($routeProvider) { $routeProvider.when('/login', { templateUrl: 'login/login.html', controller: 'LoginCtrl' }); }]) // Controller .controller('LoginCtrl', ['$scope', 'Auth', '$location', '$log','UsersChatLoginManager', function($scope, Auth, $location, $log, UsersChatLoginManager) { $scope.auth = Auth; // Function: login $scope.login = function() { $scope.error = null; // try to login with the given mail and password $scope.auth.$authWithPassword($scope.user).then(function(authData) { // login successful: save login on firebase + redirect to the pizza list UsersChatLoginManager.registerLogin(authData.uid); $location.path("/pizzas"); }).catch(function(error) { // print and log the error $scope.error = error.message; $log.error(error.message); }); }; }]);
In pratica abbiamo aggiunto la dipendenza dal servizio in cui la funzione è implementata e poi richiamiamo la funzione registerLogin con UsersChatLoginManager.registerLogin(authData.uid).
-
Facciamo la stessa cosa per il logout: modifichiamo app/user-profile/user-profile.js aggiungendo la funzione logout
$scope.logout = function () { UsersChatLoginManager.registerLogout(currentAuth.uid); };
-
E poi la richiamiamo quando l'utente preme il pulsante logout:
user-profile.html ... <button class="center-block btn btn-danger" ng-click="logout();auth.$unauth();">Logout</button> ...
-
-
Creiamo una "scheda" nella nostra web app che stampi l'elenco degli utenti loggati
-
creiamo un servizio che carichi la lista degli utenti da firebase (users-service.js):
'use strict'; angular.module('pizzApp.users.service', []) .factory('UsersService', function usersService(FBURL, $firebaseArray, $firebaseObject) { var ref = new Firebase(FBURL + '/users'); return { getUsers: function() { return $firebaseArray(ref); } }; });
-
inseriamo i vari riferimenti nel servizio padre (/app/components/users/users.js) e in index.html
-
creiamo una nuova cartella "users-list" (posizione: app/users-list).
-
creiamo il modulo della nuova vista (per il routing: il modulo verrà caricato quando viene richiamato il path /chat):
users-list.js 'use strict'; angular.module('pizzApp.users.list', ['ngRoute', 'pizzApp.users']) // Route Config .config(['$routeProvider', function($routeProvider) { $routeProvider.when('/chat', { templateUrl: 'users-list/users-list.html', controller: 'UsersListCtrl', resolve: { // controller will not be loaded until $requireAuth resolves // Auth refers to our $firebaseAuth wrapper in app.js 'currentAuth': ['Auth', function(Auth) { // $requireAuth returns a promise so the resolve waits for it to complete // if the promise is rejected, it will throw a $stateChangeError return Auth.$requireAuth(); }] } }); }]) // Controller .controller('UsersListCtrl', ['$scope', '$log', 'UsersService', 'currentAuth', function($scope, $log, UsersService, currentAuth ) { //get users list from firebase (using "UsersService" service) $scope.userId = currentAuth.uid; $scope.users = UsersService.getUsers(); } ]);
Il controller si preoccupa di caricare la lista degli utenti in una variabile "users". ATTENZIONE: non dimentichiamo di dichiare la dipendenza da questo modulo in app.js.
- Creiamo la pagina html associata in app/users-list/users-list.html ed usiamo i filtri per far stampare solo gli utenti loggati e, chiaramente, non se stessi.
<div class="row"> <div class="col-xs-12 col-md-4"> <ul class="media-list"> <li class="media" ng-repeat="user in testValue=(users | filter:{logged:true} | filter:{$id: '!' + userId})"> <a class="plain-link" href="#/chat/{{user.$id}}"> <div class="media-body"> <h4 class="media-heading"><strong>{{user.name}}</strong></h4> <p>{{user.logged}}</p> </div> </a> <hr/> </li> </ul> <p ng-show="!testValue.length">No available users</p> </div> </div>
Inoltre stampiamo il messaggio No available users quando non ci sono utenti loggati. Qui abbiamo già inserito il link alla vista che ci permetterà effettivamente di chattare: "#/chat/{{user.$id}}"
-
aggiungiamo un pulsante per raggiungere l'elenco degli utenti in index.html
<footer class="navbar navbar-default navbar-fixed-bottom" nav-menu> ... <li><a href="#/chat"><span class="glyphicon glyphicon-comment"></span></a></li> ... </footer>
-
-
Creiamo la vista per chattare con l'utente selezionato
-
aggiungiamo un nodo alla lista Firebase per salvare i vari messaggi:
Esempio: pizza-prototype messages -KHsxHNkaRghLRQA-mL- receiver: "09646c5a-bb75-4b5e-a1b2-c18cf2e1744d" sender: "19da485f-ce9f-431a-85e6-5467b2b6417a" senderName: "Teodoro" text: "ciao" utctime: "2016-05-16-09:45:52"
-
Creiamo un nuovo servizio (app/components/users/users-chat-service.js) per inserire un nuovo messaggio alla chat e per ottenere i messaggi esistenti
'use strict'; angular.module('pizzApp.users.chat.service', []) .factory('UsersChatService', function usersChatService(FBURL, $firebaseArray, $firebaseObject) { var ref = new Firebase(FBURL + '/messages'); return { getMessages: function() { return $firebaseArray(ref.limit(20)); }, getUserInfo: function(userId) { var userRef = new Firebase(FBURL + '/users/'+userId); return $firebaseObject(userRef); }, createMessage: function(sender, senderName, receiver, text){ var newMessage = {}; newMessage['sender'] = sender; newMessage['senderName'] = senderName; newMessage['receiver'] = receiver; newMessage['text'] = text; var today = new Date(); var day = today.getUTCDate(); var month = today.getUTCMonth()+1; //January is 0! var year = today.getUTCFullYear(); var hours = today.getUTCHours(); var minutes = today.getUTCMinutes(); var seconds = today.getUTCSeconds(); if(day<10) { day='0'+day; } if(month<10) { month='0'+month; } if(hours<10) { hours='0'+hours; } if(minutes<10) { minutes='0'+minutes; } if(seconds<10) { seconds='0'+seconds; } var currentDate = year.toString()+'-'+month.toString()+'-'+day.toString()+'-'+hours.toString()+':'+minutes.toString()+':'+seconds.toString(); newMessage['utctime'] = currentDate; return newMessage; }, addMessage: function(message) { return $firebaseArray(ref).$add(message); } }; });
-
aggiungiamo la dipendenza nel servizio padre (app/components/users/users.js) e in index.html
-
creiamo la nuova cartella "users-chat" con il modulo pizzApp.users.chat definito nel file /app/users-chat/users-chat.js
'use strict'; angular.module('pizzApp.users.chat', ['ngRoute', 'pizzApp.users']) // Route Config .config(['$routeProvider', function($routeProvider) { $routeProvider.when('/chat/:userId', { templateUrl: 'users-chat/users-chat.html', controller: 'UsersChatCtrl', resolve: { // controller will not be loaded until $requireAuth resolves // Auth refers to our $firebaseAuth wrapper in app.js 'currentAuth': ['Auth', function(Auth) { // $requireAuth returns a promise so the resolve waits for it to complete // if the promise is rejected, it will throw a $stateChangeError return Auth.$requireAuth(); }] } }); }]) // Controller .controller('UsersChatCtrl', ['$scope', '$log', 'UsersChatService', 'currentAuth','$routeParams', function($scope, $log, UsersChatService, currentAuth, $routeParams ) { //get the userId from currentAuth $scope.userId = currentAuth.uid; //get the interlocutor Id from the parameters contained in the url $scope.interlocutorId = $routeParams.userId; $scope.orderProp = 'utctime'; $scope.userInfo = UsersChatService.getUserInfo($scope.userId); //get messages from firebase $scope.messages = UsersChatService.getMessages(); //function that add a message on firebase $scope.addMessage = function(e) { if (e.keyCode != 13) return; //create the JSON structure that should be sent to Firebase var newMessage = UsersChatService.createMessage($scope.userId, $scope.userInfo.name, $routeParams.userId, $scope.msg); UsersChatService.addMessage(newMessage); $scope.msg = ""; }; } ]);
Il controller si preoccuperà di a) caricare i messaggi esistenti nella variabile message b) aggiungere un nuovo messaggio quando viene richiamata la funzione addMessage.
-
Creiamo la vista vera e propria che stamperà solo i messaggi scambiati tra l'utente e l'interlocutore selezionato. Nella vista, inoltre, troviamo una casella di testo con un pulsante per aggiungere nuovi messaggi.
<div class="row"> <div class="col-xs-12 col-md-4"> <div id="messagesDiv"> <div ng-repeat="msg in messages | orderBy:orderProp" ng-if="[userId, interlocutorId].indexOf(msg.sender) > -1 && [userId, interlocutorId].indexOf(msg.receiver) > -1 "> <em>{{msg.utctime}} - {{msg.senderName}}</em>: {{msg.text}} </div> </div> {{userInfo.name}}: <input type="text" ng-model="msg" ng-keydown="addMessage($event)" placeholder="Message..."> </div> </div>
-
aggiungiamo le dipendenze in app.js e in index.html
-