Ionic XMPP Client and SQL-DB : PART 4

This is continuation of my previous article : Ionic XMPP chat Client using Strophe.js: Part 3.

So if you have any problem understanding, Please go through my previous articles in this series. This article will cover on how to implement a local database for saving the chat message. We are using WebSQL for browsers and cordova-sqlite-storage for native platforms.

You can download my complete project here :

[wpi_designer_button text=’Download’ link=’https://github.com/arjunsk/Ionic-XMPP-Chat-Client-with-SQL’ style_id=’48’ icon=’github’ target=’_blank’]

UPDATE:

For those who are interested in connecting this XMPP chat to a Remote server, follow this article :

HTML5 XMPP Client build in Ionic Cordova : PART 5

Configuring SQL

  1. Add the Ionic SQLite / Cordova SQLite plugin:
     cordova plugin add https://github.com/brodysoft/Cordova-SQLitePlugin.git
  2. Add ngCordova to your project. Download ngCordova. Copy the ng-cordova.min.js to www\lib\ngCordova\dist
    folder_view1
  3. Finally the lib folder contains all the libraries as follows.
    folder_view2
  4. Now add the libraries in the index.html.

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
        <title></title>
    
        <link href="lib/ionic/css/ionic.css" rel="stylesheet">
    	
    	<!-- compiled css output -->
    	<link href="css/ionic.app.css" rel="stylesheet">
    	
    	<!-- jQuery js -->
    	http://lib/jquery/jquery-1.12.3.min.js
    	
    	<!--adding SQLite 3 -->
        http://lib/ionic/js/ionic.bundle.js
    	http://lib/ngCordova/dist/ng-cordova.min.js
    	
        <!-- cordova script (this will be a 404 during development) -->
        http://cordova.js
    	
    	 <!--// Strophe.js-->
    	http://lib/strophe/strophe.js
  5.  Note: It’s very important to put the ng-cordova.min.js  before cordova.js.
  6. Now you need to create a SQL_DB  (if not exist) when the app opens. So add the database implementation code here:
    angular.module('app', ['ionic', 'app.controllers', 'app.routes', 'app.services', 'app.directives','ngCordova'])  // <--include ngCordova
    
    .run(function($ionicPlatform, $cordovaSQLite, $rootScope) {   //<--include $cordovaSQLite
      $ionicPlatform.ready(function() {
        // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
        // for form inputs)
        if (window.cordova && window.cordova.plugins && window.cordova.plugins.Keyboard) {
          cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
          cordova.plugins.Keyboard.disableScroll(true);
        }
        if (window.StatusBar) {
          // org.apache.cordova.statusbar required
          StatusBar.styleDefault();
        }
    	
    	//SQL lite database  //<--DATABASE Implementation
    	if (window.cordova) {
          $rootScope.db = $cordovaSQLite.openDB({ name: "chats_local.db" }); //device
        }else{
          $rootScope.db = window.openDatabase("chats_local.db", '1', 'xmpp_chat', 1024 * 1024 * 100); // browser
        }
    	
    	$cordovaSQLite.execute($rootScope.db,"CREATE TABLE IF NOT EXISTS chats(id INTEGER primary key, to_id TEXT, from_id TEXT, message TEXT, timestamp DATE DEFAULT (datetime('now','localtime')) )");
      /*
    	id -- Primary Key
    	to_id -- To whom the message is, sent to 
    	from_id -- From whom the message is recieved
    	message -- The message in text
    	timestamp -- Time when message is recieved or sent
      */
      
      
      
      });
    })

Now the database is ready to use. Let us move on to building the application.

Building Application:

SQL SERVICE :

//Include $cordovaSQLite
.factory('sql', ['$rootScope','$cordovaSQLite', function($rootScope,$cordovaSQLite){

	sqlObj={};
	sqlObj.chatlogs=[];   // Holds the chatLogs
	
	//Load Chatlogs
	//This is used in the Chats Screen, Where user previous chats are logged.
	sqlObj.loadChats = function(my) {
        
		var query = "SELECT distinct from_id FROM chats where from_id not like ? ";  
        $cordovaSQLite.execute($rootScope.db, query,[my]).then(function(res) {
            
			if(res.rows.length > 0) {
				for (var i=0 ; i<res.rows.length; i=i+1) {
					sqlObj.chatlogs.push({
						id: res.rows.item(i).from_id,
						name:  res.rows.item(i).from_id,
						lastText: 'Last Text',
						face: 'img/ben.png'
					});
                }				
					
            } else {
                console.log("No results found");
            }
			
        }, function (err) {
            console.error(err);
		});
			
		
		return sqlObj.chatlogs;
    }
	
	//Insert Chat into database ; parameters(to_id,from_id,message)
	// We use $rootScope.db for refering to the database created in app.js
	sqlObj.insertChat = function(r) {
		var query = "INSERT INTO chats (to_id,from_id,message) VALUES (?,?,?) ";
		$cordovaSQLite.execute( $rootScope.db, query, [r.to_id, r.from_id, r.message]).then(function(res) {
			console.log("Message Added");
		}, function (err) {
			console.log("DB Error");
		});
    }
	
	
	//Loads users previous chat messages
	sqlObj.showChats=function(my_id,recievers_id){
		
		sqlObj.messages=[];
	  
		var query = "SELECT * FROM chats where (to_id = ? or to_id = ?)  and (from_id = ? or from_id = ?) ";
		$cordovaSQLite.execute($rootScope.db, query,[my_id, recievers_id, my_id, recievers_id]).then(function(res) {
			if(res.rows.length > 0) {
				for (var i=0 ; i<res.rows.length; i=i+1) {
					sqlObj.messages.push({
					  userId: res.rows.item(i).from_id,
					  text: res.rows.item(i).message,
					  time: res.rows.item(i).timestamp
					});
				}				
					
			} else {
				console.log("No message found");
			}
		}, function (err) {
			console.error(err);
		});

		return sqlObj.messages;  

	}
	
	return sqlObj;
	
}])

CHAT LOGS :

1

HTML:

<ion-view style="" id="page2" title="Chats">
    <ion-content class="has-header" >
	
		<label style="" class="item item-input">
            <i class="icon ion-search placeholder-icon"></i>
            <input placeholder="" type="search">
        </label>

		
		
		<ion-list style="">
		 <ion-item class="item-remove-animate item-avatar item-icon-right" ng-repeat="chatitem in chatlogs" type="item-text-wrap" ng-click="chatD(chatitem.id)">
			<img ng-src="{{chatitem.face}}">
			<h2>{{chatitem.name}}</h2>
			<p>{{chatitem.lastText}}</p>
			<i class="icon ion-chevron-right icon-accessory"></i>

			<ion-option-button class="button-assertive" ng-click="remove(chatitem)">
			  Delete
			</ion-option-button>
			
		  </ion-item>
		</ion-list>		
		
		
		

    </ion-content>
</ion-view>

CONTROLLER (chatlogsCtrl) :

.controller('chatlogsCtrl', function($scope,sql,sharedConn, ChatDetails, $state) {
	var myid= sharedConn.getBareJid( sharedConn.getConnectObj().jid );
	$scope.chatlogs = sql.loadChats(myid); // Load the users chat log
	
	//Move to chat screen, when user click on the previous chat
	$scope.chatD=function(to_id){ 
		ChatDetails.setTo(to_id);
		$state.go('tabsController.chatDetails', {}, {location: "replace", reload: true});
	};

})

CHAT MESSAGES:

3

HTML:

<ion-view style="" id="page6" title="Chat Details">

	<ion-content class="content-stable">

		<div ng-repeat="message in messages"
           ng-class="{other: message.userId != myId}"
           class="messages">
		   
		   	<div class="message" >
				<span >{{ message.text }}</span>
			</div>


		</div>

	</ion-content>

	<ion-footer-bar keyboard-attach class="bar-stable item-input-inset">
		<label class="item-input-wrapper">
			<input type="text" placeholder="Type your message" on-return="showSendMessage(); closeKeyboard()" ng-model="data.message" on-focus="inputUp()" on-blur="inputDown()" />
		</label>
		<button class="button button-clear" ng-click="showSendMessage()">
        Send
		</button>
	</ion-footer-bar>

</ion-view>

CONTROLLER (chatDetailsCtrl):

.controller('chatDetailsCtrl', function($scope, $timeout, $ionicScrollDelegate,sharedConn,ChatDetails,sql) {

  $scope.data = {};
  $scope.myId = sharedConn.getBareJid( sharedConn.getConnectObj().jid );
  $scope.to_id=ChatDetails.getTo();
  
  //Loading Previous Conversation
  $scope.messages = sql.showChats( $scope.myId , $scope.to_id );
  $ionicScrollDelegate.scrollBottom(true);

  var isIOS = ionic.Platform.isIOS(); 
  
  	$scope.sendMsg=function(to,body){
		var to_jid  = Strophe.getBareJidFromJid(to);
		var timestamp = new Date().getTime();
		var reqChannelsItems = $msg({id:timestamp, to:to_jid , type: 'chat' })
								   .c("body").t(body);
		sharedConn.getConnectObj().send(reqChannelsItems.tree());
	
	};
  
  

  $scope.showSendMessage = function() {
	  
	$scope.sendMsg($scope.to_id,$scope.data.message);  

    var d = new Date();
    d = d.toLocaleTimeString().replace(/:\d+ /, ' ');
	
	//Adding the message to UI
    $scope.messages.push({
      userId: $scope.myId,
      text: $scope.data.message,
      time: d
    });
	
	
	//SQL -- MSG SEND
	sql.insertChat({
		to_id: $scope.to_id,
		from_id:$scope.myId,
		message: $scope.data.message
	});	

    delete $scope.data.message;
    $ionicScrollDelegate.scrollBottom(true);

  };
  
  
  //On message Recieve
  $scope.messageRecieve=function(msg){	
  
	var from = msg.getAttribute('from');
	from=sharedConn.getBareJid(from);
	
	var type = msg.getAttribute('type');
	var elems = msg.getElementsByTagName('body');
  
	var d = new Date();
    d = d.toLocaleTimeString().replace(/:\d+ /, ' ');

	if (type == "chat" && elems.length > 0 ) {
		  
		var body = elems[0];
		var textMsg = Strophe.getText(body);
		
		
		//SQL -- MSG RECIEVE
		sql.insertChat({
			to_id: $scope.myId,
			from_id: from,
			message: textMsg
		});
		
		// Checks if user is chating with sender.	
		// if not the message is saved in the database, and not shown in the user screen 
		if( from == $scope.to_id ){
			
			// MSG-- UI
			$scope.messages.push({
			  userId: from,
			  text: textMsg,
			  time: d
			});
			
			$ionicScrollDelegate.scrollBottom(true);
			$scope.$apply();
			
			console.log($scope.messages);
			console.log('Message recieved from ' + from + ': ' + textMsg);
		}
		
	}
		
  }
  
  
   $scope.$on('msgRecievedBroadcast', function(event, data) {
		$scope.messageRecieve(data);
    })


  $scope.inputUp = function() {
    if (isIOS) $scope.data.keyboardHeight = 216;
    $timeout(function() {
      $ionicScrollDelegate.scrollBottom(true);
    }, 300);

  };

  $scope.inputDown = function() {
    if (isIOS) $scope.data.keyboardHeight = 0;
    $ionicScrollDelegate.resize();
  };

  $scope.closeKeyboard = function() {
    // cordova.plugins.Keyboard.close();
  };




});


NOTE:

For Debugging Purpose, I use Google Chrome –> Developer Tools ( Ctrl + Shift + I )

  • You can use console readings for debugging.

chrome 2

  • You can view the WebSQL Database Created inside Chrome.

chrome

Advertisement

3 thoughts on “Ionic XMPP Client and SQL-DB : PART 4

  1. thanks for this tutoral, i tried this it’s works on browser, but when i run on android, the connection to ejabberd server does’nt work, i’m using a remote connection. thanks

    Like

    1. In order to use this client on an online XMPP server, try changing BOSH_URL in the services.js

      SharedConnObj.BOSH_SERVICE = 'https://conversejs.org/http-bind/';

      and hostname in controller.js

      var XMPP_DOMAIN = 'im.koderoot.net';

      I have tried connecting with ‘im.koderoot.net’ and it worked fine.

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s