modules/session-flow/src/session-flow.ts
Session flow service. Track information about user activity, such as:
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(router: any, isBrowser: any)
|
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 :
Returns:
void
|
Private getCookie |
getCookie(cname: any)
|
Get cookie
Parameters :
Returns:
void
|
Private createDeviceId |
createDeviceId()
|
Create device id
Returns:
void
|
addUserClick |
addUserClick(e: any)
|
Add new user click
Parameters :
Returns:
void
|
click |
click: |
RxJs subject for user clicks |
deviceId |
deviceId: |
Current device id |
isMobile |
isMobile: |
Is mobile user |
Private lastRoute |
lastRoute: |
Default value: /
|
Last route. For default "/" |
Private lastRouteTimeStart |
lastRouteTimeStart: |
Start time of enter for last route |
reffererUrl |
reffererUrl: |
Refferer url |
sessionId |
sessionId: |
Current session id |
userId |
userId: |
Current user id |
visitedRoute |
visitedRoute: |
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);
}
}