Commit f2d3dea0 authored by Vladimir Baranov's avatar Vladimir Baranov

rref #5392. Add instant messaging, add actions for message/channels, fixes and improvements

parent 470a5726
......@@ -2203,7 +2203,7 @@ table.dataTable thead .sorting:after {
padding-left: 40px;
}
.chat-app .side-navigation ul.direct-messages li.active {
.chat-app .side-navigation ul.direct-messages li.active, .chat-app .side-navigation ul.channels li.active {
background-color: #B51533;
}
......@@ -2521,7 +2521,7 @@ table.dataTable thead .sorting:after {
color:#000000;
}
.chat-app .create-new-message-window .search-results {
.chat-app .create-new-message-window .search-results, .chat-app .add-user-unit .search-results {
width: 50%;
margin-left: 25%;
margin-top: 25px;
......@@ -2529,7 +2529,7 @@ table.dataTable thead .sorting:after {
overflow: auto;
}
.chat-app .create-new-message-window .search-results .user {
.chat-app .create-new-message-window .search-results .user, .chat-app .add-user-unit .search-results .user {
width: 100%;
display: inline-block;
padding: 10px;
......@@ -2537,18 +2537,18 @@ table.dataTable thead .sorting:after {
cursor:pointer;
}
.chat-app .create-new-message-window .search-results .user:hover {
.chat-app .create-new-message-window .search-results .user:hover, .chat-app .add-user-unit .search-results .user:hover{
background-color:#efefef;
}
.chat-app .create-new-message-window .search-results .user img {
.chat-app .create-new-message-window .search-results .user img, .chat-app .add-user-unit .search-results .user img{
width: 45px;
height: 45px;
border-radius: 100%;
float: left;
}
.chat-app .create-new-message-window .search-results .user .user-name {
.chat-app .create-new-message-window .search-results .user .user-name, .chat-app .add-user-unit .search-results .user .user-name {
float: left;
margin-left: 15px;
font-family: 'robotobold';
......@@ -2556,6 +2556,29 @@ table.dataTable thead .sorting:after {
margin-top: 9px;
}
.chat-app-button {
display: block;
position: fixed;
bottom: 20px;
right: 20px;
background: white;
border: none;
border-radius: 50%;
width: 50px;
height: 50px;
padding: 0;
z-index: 1000;
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
text-align: center;
cursor: pointer;
}
.chat-app-button i {
top: 16px;
color: #333;
font-size: 20px;
}
.chat-app .create-new-channel-window h3 {
width: 400px;
margin-left: auto;
......
angular.module("ulakbus.messaging")
.directive('messaging', function (Generator, MessagingService, $log, $rootScope, MessagingPopup) {
.directive('messaging', function (Generator, MessagingService, $log, $rootScope, MessagingPopup, Utils) {
function getKey (channel) {
if (!channel) return;
if (!angular.isObject(channel)) return channel;
var channelKey = channel.channel_key;
if (channel.hasOwnProperty('key')){
channelKey = channel.key; // direct channel
......@@ -25,54 +27,104 @@ angular.module("ulakbus.messaging")
return {
templateUrl: 'components/messaging/templates/index.html',
restrict: 'E',
replace: true,
scope: {},
link: function(iScope, iElem, iAttrs){
iScope.chatAppIsHidden = true;
// shared object to populate models through scopes
iScope.shared = {};
var popupRootElement = $(iElem).find('.popup-placeholder');
iScope.deleteConfirmation = function(){
function editChannelPopup (channel){
return MessagingPopup.show({
templateUrl: "components/messaging/templates/create_channel.html",
rootElement: popupRootElement,
link: function(scope){
scope.channel = channel||{};
scope.title = "Edit channel";
scope.actionTitle = "Edit";
if (!channel){
scope.title = "Create new channel";
scope.actionTitle = "Create";
}
}
})
}
function updateLastMessage(message){
if (!message && iScope.selectedChannel && iScope.selectedChannel.messages.length > 0){
var last = iScope.selectedChannel.messages.length - 1;
return iScope.lastMessage = iScope.selectedChannel.messages[last];
}
return iScope.lastMessage = message;
}
function appendMessage(channel, message){
if (channel && getKey(channel) == getKey(iScope.selectedChannel)){
if (channel.messages){
channel.messages.push(message);
}
}
updateLastMessage(message);
}
function updateAndSelect(channelKey){
channelKey = getKey(channelKey);
return iScope.updateChannelsList().then(function(){
return iScope.selectChannel(channelKey);
})
}
function deleteMessageLocally(messageKey){
if (iScope.selectedChannel){
Utils.deleteWhere(iScope.selectedChannel.messages, {'key': messageKey});
}
}
iScope.deleteConfirmation = function(title){
return MessagingPopup.show({
templateUrl: "components/messaging/templates/delete_confirmation.html",
link: function(scope){
scope.title = title || "Are you sure you want to delete?";
},
rootElement: popupRootElement
})
};
iScope.searchUser = function(){
MessagingPopup.show({
templateUrl: "components/messaging/templates/search_user.html",
rootElement: popupRootElement,
link: function(scope){
scope.onChange = function(query){
searchWrapper(scope, function(){
return MessagingService.search_user(query);
})
};
scope.onChange("");
}
}).then(function(user){
return iScope.createDirectChannel(user);
iScope.updateChannelsList = function(){
return MessagingService.list_channels().then(function (groupedChannels) {
iScope.publicChannels = groupedChannels[MessagingService.CHANNEL_TYPE.PUBLIC];
iScope.notificationsChannel = groupedChannels[MessagingService.CHANNEL_TYPE.NOTIFICATION][0];
iScope.directChannels = groupedChannels[MessagingService.CHANNEL_TYPE.DIRECT];
});
}
this.createDirectChannel = function(user){
// user format is ['username', 'key', 'avatarUrl']
var key = user[1];
MessagingService.create_direct_channel(key)
.then(function(result){
updateAndSelect(getKey(result));
})
};
iScope.createChannel = function(){
MessagingPopup.show({
templateUrl: "components/messaging/templates/create_channel.html",
rootElement: popupRootElement,
link: function(scope){
scope.channel = {};
}
}).then(function(channel){
return MessagingService.create_channel(channel.name, channel.description||"");
});
iScope.createDirectChannel = this.createDirectChannel;
iScope.hideApp = function(){
iScope.chatAppIsHidden = true;
};
iScope.addUserToChannel = function(channel){
iScope.showApp = function(){
iScope.chatAppIsHidden = false;
iScope.updateChannelsList();
}
iScope.searchUser = function(){
MessagingPopup.show({
templateUrl: "components/messaging/templates/add_user_unit.html",
templateUrl: "components/messaging/templates/search_user.html",
rootElement: popupRootElement,
link: function(scope){
scope.title = "Add User";
scope.placeholder = "Search User to Add";
scope.onChange = function(query){
searchWrapper(scope, function(){
return MessagingService.search_user(query);
......@@ -80,91 +132,167 @@ angular.module("ulakbus.messaging")
};
scope.onChange("");
}
}).then(function(userKey){
return MessagingService.add_members([userKey]);
}).then(function(user){
return iScope.createDirectChannel(user);
});
};
iScope.addUnitToChannel = function(){
MessagingPopup.show({
templateUrl: "components/messaging/templates/add_user_unit.html",
rootElement: popupRootElement,
link: function(scope){
scope.title = "Add Unit";
scope.placeholder = "Search Unit to Add";
scope.onChange = function(query){
searchWrapper(scope, function(){
return MessagingService.search_unit(query);
})
};
scope.onChange("");
}
}).then(function(unitKey){
var channelKey = getKey(iScope.selectedChannel);
return MessagingService.add_members(channelKey, unitKey);
});
iScope.createChannel = function(){
return editChannelPopup().then(function(channel){
return MessagingService.create_channel(channel.name, channel.description||"")
.then(function(newChannel){
updateAndSelect(newChannel)
});
})
};
},
controller: function ($scope) {
iScope.applyChannelAction = function(channel, action){
var actionView = action[1];
$scope.hidden = true;
switch (actionView) {
// shared object to populate models through scopes
$scope.shared = {};
case '_zops_pin_channel':
MessagingService.pin_channel(getKey(channel));
break;
MessagingService.list_channels().then(function (groupedChannels) {
$scope.publicChannels = groupedChannels[MessagingService.CHANNEL_TYPE.PUBLIC];
$scope.notificationsChannel = groupedChannels[MessagingService.CHANNEL_TYPE.NOTIFICATION][0];
$scope.directChannels = groupedChannels[MessagingService.CHANNEL_TYPE.DIRECT];
});
case '_zops_delete_channel':
iScope.deleteConfirmation('Are you sure you want to delete channel?')
.then(function(){
MessagingService.delete_channel(getKey(channel));
});
break;
this.createDirectChannel = function(user){
// user format is ['username', 'key', 'avatarUrl']
var key = user[1];
MessagingService.create_direct_channel(key)
.then(function(result){
return selectChannel(result.channel_key);
});
};
case '_zops_edit_channel':
editChannelPopup(channel).then(function(channelData){
return MessagingService.edit_channel(getKey(channelData), channelData.name, channelData.description||"");
});
break;
$scope.createDirectChannel = this.createDirectChannel;
case '_zops_add_members':
MessagingPopup.show({
templateUrl: "components/messaging/templates/add_user_unit.html",
rootElement: popupRootElement,
link: function(scope){
scope.title = "Add User to channel";
scope.placeholder = "Search User to Add";
scope.onChange = function(query){
searchWrapper(scope, function(){
return MessagingService.search_user(query);
})
};
scope.onChange("");
}
}).then(function(userKey){
return MessagingService.add_members(getKey(channel), [userKey]);
});
break;
$scope.hideApp = function(){
$scope.hidden = true;
case "_zops_add_unit_to_channel":
MessagingPopup.show({
templateUrl: "components/messaging/templates/add_user_unit.html",
rootElement: popupRootElement,
link: function(scope){
scope.title = "Add Unit";
scope.placeholder = "Search Unit to Add";
scope.onChange = function(query){
searchWrapper(scope, function(){
return MessagingService.search_unit(query);
})
};
scope.onChange("");
}
}).then(function(unitKey){
var channelKey = getKey(iScope.selectedChannel);
return MessagingService.add_members(channelKey, unitKey);
});
break;
}
};
function selectChannel(channelKey){
$scope.loadingChannel = true;
function selectChannel(channelKey, silent){
if (!silent) iScope.loadingChannel = true;
return MessagingService.show_channel(channelKey).then(function(result){
return result;
}).finally(function(){
$scope.loadingChannel = false;
iScope.loadingChannel = false;
})
}
$scope.selectChannel = function(channel){
iScope.selectChannel = function(channel, silent){
var channelKey = getKey(channel);
$scope.selectedChannel = channel;
selectChannel(channelKey).then(function(result){
channel.messages = result.last_messages;
selectChannel(channelKey, silent).then(function(result){
iScope.selectedChannel = result;
iScope.selectedChannel.messages = result.last_messages;
updateLastMessage(channel.messages);
});
};
iScope.isChannelSelected = function(channel){
return getKey(channel) == getKey(iScope.selectedChannel);
}
$scope.sendMessage = function(content){
iScope.sendMessage = function(content){
if (!content) return;
var channelKey = getKey($scope.selectedChannel);
var channelKey = getKey(iScope.selectedChannel);
// select message type: 2 - direct message, 4 - channel message;
var msgType = $scope.selectedChannel.type == MessagingService.CHANNEL_TYPE.DIRECT ? 2 : 4;
MessagingService.create_message(channelKey, msgType, content).then(function(result){
$scope.shared.message = "";
var msgType = iScope.selectedChannel.type == MessagingService.CHANNEL_TYPE.DIRECT ? 2 : 4;
MessagingService.create_message(channelKey, msgType, content).then(function(){
iScope.shared.message = "";
});
}
};
$scope.canAddUserAndChannel = function (channel) {
return channel && channel.type == MessagingService.CHANNEL_TYPE.PUBLIC;
}
iScope.applyMessageAction = function(message, action){
var actionView = action[1];
switch (actionView) {
case "_zops_favorite_message":
MessagingService.add_to_favorites(message.key)
.then(function(){
// force actions to reload
message.actions = null;
});
break;
case "_zops_flag_message":
MessagingService.flag_message(message.key, true)
.then(function(){
// force actions to reload
message.actions = null;
});
break;
case "_zops_unflag_message":
MessagingService.flag_message(message.key, false)
.then(function(){
// force actions to reload
message.actions = null;
});
break;
case "_zops_delete_message":
iScope.deleteConfirmation("Are you sure you want to delete message?")
.then(function(){
return MessagingService.delete_message(message.key).then(function(){
deleteMessageLocally(message.key);
})
});
break;
case "_zops_edit_message":
break;
}
};
iScope.getMessageActions = function(message){
if (message.actions) return;
MessagingService.get_message_actions(message.key).then(function(result){
message.actions = result.actions;
})
};
// listen to new messages and add them to selected channel if any
$rootScope.$on("message", function(e, message){
appendMessage(iScope.selectedChannel, MessagingService.prepareMessage(message));
});
// notifications in messaging window are processed as ordinary messages
$rootScope.$on("notifications", function(e, notification){
appendMessage(iScope.selectedChannel, MessagingService.prepareMessage(notification));
});
}
};
})
......@@ -173,4 +301,23 @@ angular.module("ulakbus.messaging")
return function(datetime){
return Moment(datetime).fromNow();
}
});
})
.directive("scrollDownWhenUpdate", function($timeout){
return {
scope: {
changesWatcher: "&scrollDownWhenUpdate"
},
link: function(iScope, iElem, iAttrs){
var elem = $(iElem);
iScope.$watch(iScope.changesWatcher, function(value){
if (value){
// update on next digest
$timeout(function(){
elem.scrollTop(elem[0].scrollHeight);
}, 0);
}
});
}
}
})
......@@ -12,8 +12,8 @@ angular.module('ulakbus.messaging', ['ui.bootstrap'])
/**
* @memberof ulakbus.messaging
* @ngdoc factory
* @name Generator
* @description form service's Generator factory service handles all generic form operations
* @name MessagingService
* @description Service handles all stuff related to messaging
*/
.factory('MessagingService', function ($q, $timeout, $sce, $location, $route, $compile, $log, $rootScope, Moment, WSOps, Utils) {
var msg = {};
......@@ -44,16 +44,21 @@ angular.module('ulakbus.messaging', ['ui.bootstrap'])
})
}
// prepare messages for UI
function prepareMessages (messages){
for (var i = 0; i < messages.length; i++){
var message = messages[i];
// FIXME: process timezone properly
var ts = message.timestamp.replace(/\.0+Z$/, "");
message.moment = Moment(ts);
msg.prepareMessage(message);
}
}
// prepare message to show in UI
msg.prepareMessage = function(message){
var ts = message.timestamp.replace(/\.0+Z$/, "");
// FIXME: process timezone properly
message.moment = Moment(ts);
return message;
};
msg.list_channels = function list_channels (){
/**
* request channels list as below;
......@@ -83,6 +88,7 @@ angular.module('ulakbus.messaging', ['ui.bootstrap'])
view: '_zops_list_channels'
};
return wsRequest(outgoing).then(function (data) {
console.error("channels: ", data.channels);
return Utils.groupBy(data.channels||[], "type");
});
};
......@@ -174,14 +180,14 @@ angular.module('ulakbus.messaging', ['ui.bootstrap'])
msg.edit_channel = function (channelKey, name, desription) {
var outgoing = {
view:'_zops_create_channel',
view:'_zops_edit_channel',
channel_key: channelKey,
name: name,
description: desription
};
return wsRequest(outgoing).then(function(result){
$log.info("Channel ", channelKey, " edited: ", result);
$log.info("Channel ", channelKey, " edited: ", outgoing, result);
return result;
})
};
......@@ -198,12 +204,14 @@ angular.module('ulakbus.messaging', ['ui.bootstrap'])
};
msg.show_channel = function(channelKey){
var outgoing = {
view: '_zops_show_channel',
channel_key: channelKey
};
return wsRequest(outgoing).then(function(result){
$log.info("Show channel ", channelKey, ": ", result);
console.error("channel: ", result);
prepareMessages(result.last_messages);
return result;
})
......@@ -225,7 +233,7 @@ angular.module('ulakbus.messaging', ['ui.bootstrap'])
var outgoing = {
channel_key: channelKey,
msg_key: msgKey,
timestamp: timestamp,
timestamp: timestamp
};
return wsRequest(outgoing).then(function(result){
$log.info("Report last seen message ", channelKey, msgKey, timestamp, ": ", result);
......@@ -293,10 +301,8 @@ angular.module('ulakbus.messaging', ['ui.bootstrap'])
msg.flag_message = function (msgKey, flag) {
var outgoing = {
view: '_zops_flag_message',
message: {
'key': msgKey,
'flag': flag
}
key: msgKey,
flag: flag
};
return wsRequest(outgoing).then(function(result){
$log.info("Flag message ", msgKey, flag, ":", result);
......@@ -318,7 +324,7 @@ angular.module('ulakbus.messaging', ['ui.bootstrap'])
msg.add_to_favorites = function (msgKey) {
var outgoing = {
view: '_zops_add_to_favorites',
message_key: msgKey
key: msgKey
};
return wsRequest(outgoing).then(function(result){
$log.info("Add message ", msgKey, " to favorites: ", result);
......@@ -329,7 +335,7 @@ angular.module('ulakbus.messaging', ['ui.bootstrap'])
msg.remove_from_favorites = function (msgKey) {
var outgoing = {
view: '_zops_remove_to_favorites',
message_key: msgKey
key: msgKey
};
return wsRequest(outgoing).then(function(result){
$log.info("Remove message ", msgKey, " from favorites: ", result);
......@@ -346,7 +352,7 @@ angular.module('ulakbus.messaging', ['ui.bootstrap'])
$log.info("List favorites for channel", channelKey, ": ", result);
return result;
});
}
};
return msg;
})
......
<div class="chat-popup-window add-user-unit" style="display:block;">
<div class="close-chat-popup-window"><span class="glyphicon glyphicon-remove"></span></div>
<div class="close-chat-popup-window" ng-click="cancel()"><span class="glyphicon glyphicon-remove"></span></div>
<h3>{{title}}</h3>
<div class="text-center">
<input type="text" placeholder="{{placeholder}}">
<div class="search-results">
<span class="loader" ng-show="loading"></span>
<div class="user" ng-repeat="item in searchResult" ng-click="done(item[1])">
<img ng-src="{{item[2]}}" ng-show="item[2]">
<div class="user-name">{{item[0]}}</div>
</div>
<input type="text" ng-model="query" ng-change="onChange(query)" placeholder="{{placeholder}}">
</div>
<div class="search-results">
<span class="loader" ng-show="loading"></span>
<div class="user" ng-repeat="item in searchResult" ng-click="done(item[1])">
<img ng-src="{{item[2]}}" ng-show="item[2]">
<div class="user-name">{{item[0]}}</div>
</div>
</div>
</div>
......@@ -13,7 +13,7 @@
<div class="conversation-header">
<div class="conversation-user">
<div class="user-photo" ng-show="selectedChannel.avatar_url"><img ng-src="{{selectedChannel.avatar_url}}"></div>
<div class="user-name">{{selectedChannel.name}}</div>
<div class="user-name" title="{{selectedChannel.description}}">{{selectedChannel.name}}</div>
</div>
<div class="conversation-search">
<input type="text" placeholder="Arama Yap">
......@@ -23,7 +23,7 @@
<span class="glyphicon glyphicon-option-vertical"></span>
</div>
<ul class="dropdown-menu" ng-show="selectedChannel.actions.length > 0" aria-labelledby="chat-app-actions" style="left: inherit; top: 53px; right: 66px;" >
<li><a ng-click="applyChannelAction(action)" ng-repeat="action in selectedChannel.actions">{{action[0]}}</a></li>
<li><a ng-click="applyChannelAction(selectedChannel, action)" ng-repeat="action in selectedChannel.actions">{{action[0]}}</a></li>
</ul>
</div>
<div class="close-chat-app" ng-click="hideApp()">
......@@ -31,7 +31,7 @@
</div>
</div>
<div class="conversation-body">
<div class="conversation-body" scroll-down-when-update="lastMessage">
<div class="conversation-body-inner">
<div class="beginning-of-conversation">
......@@ -41,17 +41,16 @@
<div class="conversation-block clearfix" ng-repeat="msg in selectedChannel.messages">
<div class="conversation-actions">
<div class="action"><span class="glyphicon glyphicon-star-empty"></span></div>
<div class="action dropdown-toggle" data-toggle="dropdown">
<div class="action dropdown-toggle" data-toggle="dropdown" ng-click="getMessageActions(msg)">
<span class="glyphicon glyphicon-option-horizontal"></span>
</div>
<ul class="dropdown-menu" style="left:-86px;">
<li><a href="#">Edit</a></li>
<li><a href="#" ng-click="deleteConfirmation()">Delete</a></li>
<ul class="dropdown-menu" style="left:-86px;" >
<li ng-repeat="act in msg.actions"><a href="#" ng-click="applyMessageAction(msg, act)">{{act[0]}}</a></li>
</ul>
</div>
<div class="user-photo">
<img src="../../../img/erkan.jpg">
<img ng-src="{{msg.avatar_url}}" ng-show="msg.avatar_url">
</div>
<div class="user-message">
<div class="message-header clearfix">
......
<div class="chat-popup-window create-new-channel-window">
<div class="close-chat-popup-window" ng-click="cancel()"><span class="glyphicon glyphicon-remove"></span></div>
<h3>Create New Channel</h3>
<h3>{{title}}</h3>
<div class="text-center">
<input type="text" placeholder="Channel Name" ng-model="channel.name"><br>
<textarea placeholder="Channel Description" ng-model="channel.description"></textarea>
<button class="btn btn-success" ng-click="done(channel)">Create</button>
<button class="btn btn-success" ng-click="done(channel)">{{actionTitle}}</button>
</div>
</div>
......@@ -2,7 +2,7 @@
<div class="close-chat-popup-window" ng-click="cancel()"><span class="glyphicon glyphicon-remove"></span></div>
<div class="text-center" style="margin-top: 150px;font-size: 26px;">
<p>Are you sure you want to delete?</p>
<p>{{title}}</p>
<button class="btn btn-success" style="font-size: 20px;" ng-click="done()">Yes</button>
<button class="btn btn-default" style="font-size: 20px;" ng-click="cancel()">Cancel</button>
</div>
......
<div>
<button class="chat-app-button" ng-show="hidden" ng-click="hidden = false"><i class="glyphicon glyphicon-comment"></i></button>
<div class="chat-app" ng-hide="hidden">
<a class="chat-app-button" ng-show="chatAppIsHidden" ng-click="showApp()"><i class="glyphicon glyphicon-comment"></i></a>
<div class="chat-app" ng-hide="chatAppIsHidden">
<div class="chat-app-container">
......@@ -11,12 +11,12 @@
<ul class="channels">
<li class="title">CHANNELS <span class="add-action glyphicon glyphicon-plus-sign" ng-click="createChannel()"></span></li>
<li ng-class="{'notification': ch.unread, 'active': selectedChannel == ch}" ng-repeat="ch in publicChannels" ng-click="selectChannel(ch)">{{ch.name}}</li>
<li ng-class="{'notification': ch.unread, 'active': isChannelSelected(ch)}" title="{{ch.description}}" ng-repeat="ch in publicChannels" ng-click="selectChannel(ch)">{{ch.name}}</li>
</ul>
<ul class="direct-messages">
<li class="title">DIRECT MESSAGES <span class="add-action glyphicon glyphicon-plus-sign" ng-click="searchUser()"></span></li>
<li ng-class="{'notification': userChannel.unread, 'active': selectedChannel == userChannel}" ng-repeat="userChannel in directChannels" ng-click="selectChannel(userChannel)">{{userChannel.name}}</li>
<li ng-class="{'notification': userChannel.unread, 'active': isChannelSelected(userChannel), 'online': userChannel.is_online}" ng-repeat="userChannel in directChannels" ng-click="selectChannel(userChannel)">{{userChannel.name}}</li>
</ul>
</div>
......@@ -24,7 +24,6 @@
<ng-include src="'components/messaging/templates/conversation.html'"></ng-include>
<div class="popup-placeholder"></div>
</div>
</div>
</div>
......@@ -12,6 +12,17 @@ angular.module("ulakbus")
.service("Utils", function(){
var self = this;
// check if obj1 has properties values equal to corresponding properties in obj2
function hasEqualProperties(obj1, obj2){
var result = true
for (var prop in obj2){
if(obj2.hasOwnProperty(prop)){
result = result && obj2[prop] == obj1[prop];
}
}
return result;
}
/**
* @param list {Array} Array of objects to group
* @param propName {String} property name to group array by
......@@ -24,4 +35,18 @@ angular.module("ulakbus")
return acc;
}, {});
};
/**
* @param list {Array} Array of objects to group
* @param condition {Object} conditions object. If object in collection has same values as in conditions object it will be removed
* @returns {Object}|undefined removed object or undefined
*/
this.deleteWhere = function(list, condition){
for (var i = 0; i < list.length; i++){
if (hasEqualProperties(list[i], condition)){
list.splice(i, 1);
return list[i];
}
}
}
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment