File

modules/session-flow/src/session-flow.ts

Description

Session flow service. Track information about user activity, such as:

  1. Session id
  2. Device id
  3. User id
  4. Is mobile user
  5. Refferer url
  6. Saves visited routes
  7. Saves user clicks

To enable Session Flow for tracking inject DAL and Session Flow into root AppComponent. If you want to track user click also add HostListener on click:

export class AppComponent {

\@HostListener('click', ['$event']) onClick(e){
this.sessionFlow.addUserClick(e);
}


constructor(
private dal: DbAbstractionLayer,
private sessionFlow: SessionFlow
) {
}

ngOnInit() {
this.dal.checkOldSessionFlow(this.sessionFlow.deviceId);
this.dal.connectSessionFlowToDB(this.sessionFlow, this.sessionFlow.deviceId, this.sessionFlow.sessionId);
}

Constructor

constructor(router: any, isBrowser: any)

Methods

createVisitedRoute
createVisitedRoute()

Create new visited route

Returns: void
Private trackRouteChange
trackRouteChange()

Tracks router changes

Returns: void
Private createSessionId
createSessionId()

Create session id

Returns: void
Private guid
guid()

Create guid for session id and device id

Returns: void
Private detectMobileDevice
detectMobileDevice()

Return true if user use mobile device

Returns: void
Private setCookie
setCookie(cname: any, cvalue: any, exdays: any)

Set cookie

Parameters :
  • cname

    cookie name

  • cvalue

    cookie value

  • exdays : integer

    expires days

Returns: void
Private getCookie
getCookie(cname: any)

Get cookie

Parameters :
  • cname

    cookie name

Returns: void
Private createDeviceId
createDeviceId()

Create device id

Returns: void
addUserClick
addUserClick(e: any)

Add new user click

Parameters :
  • e

    browser click event

Returns: void

Properties

click
click: any

RxJs subject for user clicks

deviceId
deviceId: string

Current device id

isMobile
isMobile: boolean

Is mobile user

Private lastRoute
lastRoute: string
Default value: /

Last route. For default "/"

Private lastRouteTimeStart
lastRouteTimeStart: Date

Start time of enter for last route

reffererUrl
reffererUrl: string

Refferer url

sessionId
sessionId: string

Current session id

userId
userId: string

Current user id

visitedRoute
visitedRoute: any

RxJs subject for visited routes

import {Injectable, Inject} from "@angular/core";
import {VisitedRoute} from "./structures/visited-route";
import {Subject} from "rxjs";
import {Router, NavigationStart, NavigationEnd} from "@angular/router";
import {UserClick} from "./structures/click";

/**
 * Session flow service. Track information about user activity, such as:
 * 1. Session id
 * 2. Device id
 * 3. User id
 * 3. Is mobile user
 * 4. Refferer url
 * 5. Saves visited routes
 * 6. Saves user clicks
 * 
 * To enable `Session Flow` for tracking inject `DAL` and `Session Flow` into root `AppComponent`. If you want to track user click also add HostListener on click:
 * ```
 * export class AppComponent {
 *
 * \@HostListener('click', ['$event']) onClick(e){
 *    this.sessionFlow.addUserClick(e);
 * }
 *
 * 
 * constructor(
 *            private dal: DbAbstractionLayer,
 *            private sessionFlow: SessionFlow
 *            ) {
 * }
 *
 * ngOnInit() {
 *    this.dal.checkOldSessionFlow(this.sessionFlow.deviceId);
 *    this.dal.connectSessionFlowToDB(this.sessionFlow, this.sessionFlow.deviceId, this.sessionFlow.sessionId);
 * }
 * ```
 * 
 */
@Injectable()
export class SessionFlow{

  /**
   * Current session id
   */
  sessionId: string;

  /**
   * Current device id
   */
  deviceId: string;

  /**
   * Is mobile user
   */
  isMobile: boolean;
  
  /**
   * Refferer url
   */
  reffererUrl: string = '';

  /**
   * RxJs subject for visited routes
   */
  visitedRoute: Subject<VisitedRoute> = new Subject<VisitedRoute>();

  /**
   * RxJs subject for user clicks
   */
  click: Subject<UserClick> = new Subject<UserClick>();

  /**
   * Current user id
   */
  userId: string;

  /**
   * Last route. For default "/"
   */
  private lastRoute: string = '/';

  /**
   * Start time of enter for last route
   */
  private lastRouteTimeStart: Date = new Date();

  constructor(protected router: Router, @Inject('isBrowser') protected isBrowser){
    this.userId = 'guest';
    if(isBrowser){
      this.reffererUrl = document.referrer;
    }
    this.createDeviceId();
    this.createSessionId();
    this.trackRouteChange();
  }

  /**
   * Create new visited route
   */
  createVisitedRoute(){
    let visitedRoute: VisitedRoute = new VisitedRoute();
    visitedRoute.route = this.lastRoute;
    visitedRoute.timeStart = this.lastRouteTimeStart;
    visitedRoute.timeEnd = new Date();
    visitedRoute.calcDuration();
    this.visitedRoute.next(visitedRoute);
  }

  /**
   * Tracks router changes 
   */
  private trackRouteChange(){
    this.router.events
      .subscribe((event) => {

        //User started to load the page
        if (event instanceof NavigationStart) {
          event = event as NavigationStart;
          this.createVisitedRoute();
        }
        //User ended to load the page
        if (event instanceof NavigationEnd){
          event = event as NavigationEnd;
          this.lastRoute = event.url;
          this.lastRouteTimeStart = new Date();
        }
      });
  }

  /**
   * Create session id 
   */
  private createSessionId() {
    this.sessionId = this.guid();
    this.isMobile = this.detectMobileDevice();
    this.setCookie('mobile', this.detectMobileDevice() + "", 0);
    this.setCookie('session_id', this.sessionId, 0);
  }

  /**
   * Create guid for session id and device id
   */
  private guid() {
    function s4() {
      return Math.floor((1 + Math.random()) * 0x10000)
        .toString(16)
        .substring(1);
    }
    return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
      s4() + '-' + s4() + s4() + s4();
  }

  /**
   * Return true if user use mobile device
   */
  private detectMobileDevice(){
    var check = false;
    if(this.isBrowser){
      ((a) => {if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor);
    }
    return check;
  }

  /**
   * Set cookie
   * @param {string} cname cookie name
   * @param {string} cvalue cookie value
   * @param {integer} exdays expires days 
   */
  private setCookie(cname, cvalue, exdays) {
    if(this.isBrowser){
      let d = new Date();
      d.setTime(d.getTime() + (exdays*24*60*60*1000));
      let expires = "expires="+ d.toUTCString();
      document.cookie = cname + "=" + cvalue + "; ";
    }
  }

  /**
   * Get cookie
   * @param {string} cname  cookie name
   */
  private getCookie(cname) {
    if(this.isBrowser){
      var name = cname + "=";
      var ca = document.cookie.split(';');
      for(var i = 0; i <ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0)==' ') {
          c = c.substring(1);
        }
        if (c.indexOf(name) == 0) {
          return c.substring(name.length,c.length);
        }
      }
    }
    return "";
  }

  /**
   * Create device id
   */
  private createDeviceId() {
    if(this.getCookie("device_id") === ""){
      this.deviceId = this.guid();
      this.setCookie("device_id", this.deviceId, 100);
    }
    else{
      this.deviceId = this.getCookie("device_id");
    }
  }

  /**
   * Add new user click
   * @param e browser click event
   */
  addUserClick(e) {
    let userClick = new UserClick();
    let targetValues = ['id', 'name', 'className', 'innerText'];
    userClick.selectorName = "undefined";
    for(let i = 0; i < targetValues.length; i++){
      let targetVal = targetValues[i];
      if(e.target[targetVal]){
        if(typeof e.target[targetVal] === 'object'){
          userClick.selectorName = e.target[targetVal].name;
        }else{
          userClick.selectorName = e.target[targetVal];
        }
        break;
      }
    }
    userClick.time = new Date();
    userClick.route = this.router.url;
    this.click.next(userClick);
  }



}

results matching ""

    No results matching ""