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 { ...@@ -2203,7 +2203,7 @@ table.dataTable thead .sorting:after {
padding-left: 40px; 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; background-color: #B51533;
} }
...@@ -2521,7 +2521,7 @@ table.dataTable thead .sorting:after { ...@@ -2521,7 +2521,7 @@ table.dataTable thead .sorting:after {
color:#000000; 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%; width: 50%;
margin-left: 25%; margin-left: 25%;
margin-top: 25px; margin-top: 25px;
...@@ -2529,7 +2529,7 @@ table.dataTable thead .sorting:after { ...@@ -2529,7 +2529,7 @@ table.dataTable thead .sorting:after {
overflow: auto; 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%; width: 100%;
display: inline-block; display: inline-block;
padding: 10px; padding: 10px;
...@@ -2537,18 +2537,18 @@ table.dataTable thead .sorting:after { ...@@ -2537,18 +2537,18 @@ table.dataTable thead .sorting:after {
cursor:pointer; 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; 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; width: 45px;
height: 45px; height: 45px;
border-radius: 100%; border-radius: 100%;
float: left; 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; float: left;
margin-left: 15px; margin-left: 15px;
font-family: 'robotobold'; font-family: 'robotobold';
...@@ -2556,6 +2556,29 @@ table.dataTable thead .sorting:after { ...@@ -2556,6 +2556,29 @@ table.dataTable thead .sorting:after {
margin-top: 9px; 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 { .chat-app .create-new-channel-window h3 {
width: 400px; width: 400px;
margin-left: auto; margin-left: auto;
......
angular.module("ulakbus.messaging") angular.module("ulakbus.messaging")
.directive('messaging', function (Generator, MessagingService, $log, $rootScope, MessagingPopup) { .directive('messaging', function (Generator, MessagingService, $log, $rootScope, MessagingPopup, Utils) {
function getKey (channel) { function getKey (channel) {
if (!channel) return;
if (!angular.isObject(channel)) return channel;
var channelKey = channel.channel_key; var channelKey = channel.channel_key;
if (channel.hasOwnProperty('key')){ if (channel.hasOwnProperty('key')){
channelKey = channel.key; // direct channel channelKey = channel.key; // direct channel
...@@ -25,54 +27,104 @@ angular.module("ulakbus.messaging") ...@@ -25,54 +27,104 @@ angular.module("ulakbus.messaging")
return { return {
templateUrl: 'components/messaging/templates/index.html', templateUrl: 'components/messaging/templates/index.html',
restrict: 'E', restrict: 'E',
replace: true,
scope: {}, scope: {},
link: function(iScope, iElem, iAttrs){ link: function(iScope, iElem, iAttrs){
iScope.chatAppIsHidden = true;
// shared object to populate models through scopes
iScope.shared = {};
var popupRootElement = $(iElem).find('.popup-placeholder'); 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({ return MessagingPopup.show({
templateUrl: "components/messaging/templates/delete_confirmation.html", templateUrl: "components/messaging/templates/delete_confirmation.html",
link: function(scope){
scope.title = title || "Are you sure you want to delete?";
},
rootElement: popupRootElement rootElement: popupRootElement
}) })
}; };
iScope.searchUser = function(){ iScope.updateChannelsList = function(){
MessagingPopup.show({ return MessagingService.list_channels().then(function (groupedChannels) {
templateUrl: "components/messaging/templates/search_user.html", iScope.publicChannels = groupedChannels[MessagingService.CHANNEL_TYPE.PUBLIC];
rootElement: popupRootElement, iScope.notificationsChannel = groupedChannels[MessagingService.CHANNEL_TYPE.NOTIFICATION][0];
link: function(scope){ iScope.directChannels = groupedChannels[MessagingService.CHANNEL_TYPE.DIRECT];
scope.onChange = function(query){
searchWrapper(scope, function(){
return MessagingService.search_user(query);
})
};
scope.onChange("");
}
}).then(function(user){
return iScope.createDirectChannel(user);
}); });
}
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(){ iScope.createDirectChannel = this.createDirectChannel;
MessagingPopup.show({
templateUrl: "components/messaging/templates/create_channel.html", iScope.hideApp = function(){
rootElement: popupRootElement, iScope.chatAppIsHidden = true;
link: function(scope){
scope.channel = {};
}
}).then(function(channel){
return MessagingService.create_channel(channel.name, channel.description||"");
});
}; };
iScope.addUserToChannel = function(channel){ iScope.showApp = function(){
iScope.chatAppIsHidden = false;
iScope.updateChannelsList();
}
iScope.searchUser = function(){
MessagingPopup.show({ MessagingPopup.show({
templateUrl: "components/messaging/templates/add_user_unit.html", templateUrl: "components/messaging/templates/search_user.html",
rootElement: popupRootElement, rootElement: popupRootElement,
link: function(scope){ link: function(scope){
scope.title = "Add User";
scope.placeholder = "Search User to Add";
scope.onChange = function(query){ scope.onChange = function(query){
searchWrapper(scope, function(){ searchWrapper(scope, function(){
return MessagingService.search_user(query); return MessagingService.search_user(query);
...@@ -80,91 +132,167 @@ angular.module("ulakbus.messaging") ...@@ -80,91 +132,167 @@ angular.module("ulakbus.messaging")
}; };
scope.onChange(""); scope.onChange("");
} }
}).then(function(userKey){ }).then(function(user){
return MessagingService.add_members([userKey]); return iScope.createDirectChannel(user);
}); });
}; };
iScope.addUnitToChannel = function(){ iScope.createChannel = function(){
MessagingPopup.show({ return editChannelPopup().then(function(channel){
templateUrl: "components/messaging/templates/add_user_unit.html", return MessagingService.create_channel(channel.name, channel.description||"")
rootElement: popupRootElement, .then(function(newChannel){
link: function(scope){ updateAndSelect(newChannel)
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);
});
}; };
},
controller: function ($scope) { iScope.applyChannelAction = function(channel, action){
var actionView = action[1];
$scope.hidden = true; switch (actionView) {
// shared object to populate models through scopes case '_zops_pin_channel':
$scope.shared = {}; MessagingService.pin_channel(getKey(channel));
break;
MessagingService.list_channels().then(function (groupedChannels) { case '_zops_delete_channel':
$scope.publicChannels = groupedChannels[MessagingService.CHANNEL_TYPE.PUBLIC]; iScope.deleteConfirmation('Are you sure you want to delete channel?')
$scope.notificationsChannel = groupedChannels[MessagingService.CHANNEL_TYPE.NOTIFICATION][0]; .then(function(){
$scope.directChannels = groupedChannels[MessagingService.CHANNEL_TYPE.DIRECT]; MessagingService.delete_channel(getKey(channel));
}); });
break;
this.createDirectChannel = function(user){ case '_zops_edit_channel':
// user format is ['username', 'key', 'avatarUrl'] editChannelPopup(channel).then(function(channelData){
var key = user[1]; return MessagingService.edit_channel(getKey(channelData), channelData.name, channelData.description||"");
MessagingService.create_direct_channel(key) });
.then(function(result){ break;
return selectChannel(result.channel_key);
});
};
$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(){ case "_zops_add_unit_to_channel":
$scope.hidden = true; 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, silent){
function selectChannel(channelKey){ if (!silent) iScope.loadingChannel = true;
$scope.loadingChannel = true;
return MessagingService.show_channel(channelKey).then(function(result){ return MessagingService.show_channel(channelKey).then(function(result){
return result; return result;
}).finally(function(){ }).finally(function(){
$scope.loadingChannel = false; iScope.loadingChannel = false;
}) })
} }
$scope.selectChannel = function(channel){ iScope.selectChannel = function(channel, silent){
var channelKey = getKey(channel); var channelKey = getKey(channel);
$scope.selectedChannel = channel; selectChannel(channelKey, silent).then(function(result){
selectChannel(channelKey).then(function(result){ iScope.selectedChannel = result;
channel.messages = result.last_messages; 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; if (!content) return;
var channelKey = getKey($scope.selectedChannel); var channelKey = getKey(iScope.selectedChannel);
// select message type: 2 - direct message, 4 - channel message; // select message type: 2 - direct message, 4 - channel message;
var msgType = $scope.selectedChannel.type == MessagingService.CHANNEL_TYPE.DIRECT ? 2 : 4; var msgType = iScope.selectedChannel.type == MessagingService.CHANNEL_TYPE.DIRECT ? 2 : 4;
MessagingService.create_message(channelKey, msgType, content).then(function(result){ MessagingService.create_message(channelKey, msgType, content).then(function(){
$scope.shared.message = ""; iScope.shared.message = "";
}); });
} };
$scope.canAddUserAndChannel = function (channel) { iScope.applyMessageAction = function(message, action){
return channel && channel.type == MessagingService.CHANNEL_TYPE.PUBLIC; 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") ...@@ -173,4 +301,23 @@ angular.module("ulakbus.messaging")
return function(datetime){ return function(datetime){
return Moment(datetime).fromNow(); 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']) ...@@ -12,8 +12,8 @@ angular.module('ulakbus.messaging', ['ui.bootstrap'])
/** /**
* @memberof ulakbus.messaging * @memberof ulakbus.messaging
* @ngdoc factory * @ngdoc factory
* @name Generator * @name MessagingService
* @description form service's Generator factory service handles all generic form operations * @description Service handles all stuff related to messaging
*/ */
.factory('MessagingService', function ($q, $timeout, $sce, $location, $route, $compile, $log, $rootScope, Moment, WSOps, Utils) { .factory('MessagingService', function ($q, $timeout, $sce, $location, $route, $compile, $log, $rootScope, Moment, WSOps, Utils) {
var msg = {}; var msg = {};
...@@ -44,16 +44,21 @@ angular.module('ulakbus.messaging', ['ui.bootstrap']) ...@@ -44,16 +44,21 @@ angular.module('ulakbus.messaging', ['ui.bootstrap'])
}) })
} }
// prepare messages for UI
function prepareMessages (messages){ function prepareMessages (messages){
for (var i = 0; i < messages.length; i++){ for (var i = 0; i < messages.length; i++){
var message = messages[i]; var message = messages[i];
// FIXME: process timezone properly msg.prepareMessage(message);
var ts = message.timestamp.replace(/\.0+Z$/, "");
message.moment = Moment(ts);
} }
} }
// 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 (){ msg.list_channels = function list_channels (){
/** /**
* request channels list as below; * request channels list as below;
...@@ -83,6 +88,7 @@ angular.module('ulakbus.messaging', ['ui.bootstrap']) ...@@ -83,6 +88,7 @@ angular.module('ulakbus.messaging', ['ui.bootstrap'])
view: '_zops_list_channels' view: '_zops_list_channels'
}; };
return wsRequest(outgoing).then(function (data) { return wsRequest(outgoing).then(function (data) {
console.error("channels: ", data.channels);
return Utils.groupBy(data.channels||[], "type"); return Utils.groupBy(data.channels||[], "type");
}); });
}; };
...@@ -174,14 +180,14 @@ angular.module('ulakbus.messaging', ['ui.bootstrap']) ...@@ -174,14 +180,14 @@ angular.module('ulakbus.messaging', ['ui.bootstrap'])
msg.edit_channel = function (channelKey, name, desription) { msg.edit_channel = function (channelKey, name, desription) {
var outgoing = { var outgoing = {
view:'_zops_create_channel', view:'_zops_edit_channel',
channel_key: channelKey, channel_key: channelKey,
name: name, name: name,
description: desription description: desription
}; };
return wsRequest(outgoing).then(function(result){ return wsRequest(outgoing).then(function(result){
$log.info("Channel ", channelKey, " edited: ", result); $log.info("Channel ", channelKey, " edited: ", outgoing, result);
return result; return result;
}) })
}; };
...@@ -198,12 +204,14 @@ angular.module('ulakbus.messaging', ['ui.bootstrap']) ...@@ -198,12 +204,14 @@ angular.module('ulakbus.messaging', ['ui.bootstrap'])
}; };
msg.show_channel = function(channelKey){ msg.show_channel = function(channelKey){
var outgoing = { var outgoing = {
view: '_zops_show_channel', view: '_zops_show_channel',
channel_key: channelKey channel_key: channelKey
}; };
return wsRequest(outgoing).then(function(result){ return wsRequest(outgoing).then(function(result){
$log.info("Show channel ", channelKey, ": ", result); $log.info("Show channel ", channelKey, ": ", result);
console.error("channel: ", result);
prepareMessages(result.last_messages); prepareMessages(result.last_messages);
return result; return result;
}) })
...@@ -225,7 +233,7 @@ angular.module('ulakbus.messaging', ['ui.bootstrap']) ...@@ -225,7 +233,7 @@ angular.module('ulakbus.messaging', ['ui.bootstrap'])
var outgoing = { var outgoing = {
channel_key: channelKey, channel_key: channelKey,
msg_key: msgKey, msg_key: msgKey,
timestamp: timestamp, timestamp: timestamp
}; };
return wsRequest(outgoing).then(function(result){ return wsRequest(outgoing).then(function(result){
$log.info("Report last seen message ", channelKey, msgKey, timestamp, ": ", result); $log.info("Report last seen message ", channelKey, msgKey, timestamp, ": ", result);
...@@ -293,10 +301,8 @@ angular.module('ulakbus.messaging', ['ui.bootstrap']) ...@@ -293,10 +301,8 @@ angular.module('ulakbus.messaging', ['ui.bootstrap'])
msg.flag_message = function (msgKey, flag) { msg.flag_message = function (msgKey, flag) {
var outgoing = { var outgoing = {
view: '_zops_flag_message', view: '_zops_flag_message',
message: { key: msgKey,
'key': msgKey, flag: flag
'flag': flag
}
}; };
return wsRequest(outgoing).then(function(result){ return wsRequest(outgoing).then(function(result){
$log.info("Flag message ", msgKey, flag, ":", result); $log.info("Flag message ", msgKey, flag, ":", result);
...@@ -318,7 +324,7 @@ angular.module('ulakbus.messaging', ['ui.bootstrap']) ...@@ -318,7 +324,7 @@ angular.module('ulakbus.messaging', ['ui.bootstrap'])
msg.add_to_favorites = function (msgKey) { msg.add_to_favorites = function (msgKey) {
var outgoing = { var outgoing = {
view: '_zops_add_to_favorites', view: '_zops_add_to_favorites',
message_key: msgKey key: msgKey
}; };
return wsRequest(outgoing).then(function(result){ return wsRequest(outgoing).then(function(result){
$log.info("Add message ", msgKey, " to favorites: ", result); $log.info("Add message ", msgKey, " to favorites: ", result);
...@@ -329,7 +335,7 @@ angular.module('ulakbus.messaging', ['ui.bootstrap']) ...@@ -329,7 +335,7 @@ angular.module('ulakbus.messaging', ['ui.bootstrap'])
msg.remove_from_favorites = function (msgKey) { msg.remove_from_favorites = function (msgKey) {
var outgoing = { var outgoing = {
view: '_zops_remove_to_favorites', view: '_zops_remove_to_favorites',
message_key: msgKey key: msgKey
}; };
return wsRequest(outgoing).then(function(result){ return wsRequest(outgoing).then(function(result){
$log.info("Remove message ", msgKey, " from favorites: ", result); $log.info("Remove message ", msgKey, " from favorites: ", result);
...@@ -346,7 +352,7 @@ angular.module('ulakbus.messaging', ['ui.bootstrap']) ...@@ -346,7 +352,7 @@ angular.module('ulakbus.messaging', ['ui.bootstrap'])
$log.info("List favorites for channel", channelKey, ": ", result); $log.info("List favorites for channel", channelKey, ": ", result);
return result; return result;
}); });
} };
return msg; return msg;
}) })
......
<div class="chat-popup-window add-user-unit" style="display:block;"> <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> <h3>{{title}}</h3>
<div class="text-center"> <div class="text-center">
<input type="text" placeholder="{{placeholder}}"> <input type="text" ng-model="query" ng-change="onChange(query)" placeholder="{{placeholder}}">
<div class="search-results"> </div>
<span class="loader" ng-show="loading"></span> <div class="search-results">
<div class="user" ng-repeat="item in searchResult" ng-click="done(item[1])"> <span class="loader" ng-show="loading"></span>
<img ng-src="{{item[2]}}" ng-show="item[2]"> <div class="user" ng-repeat="item in searchResult" ng-click="done(item[1])">
<div class="user-name">{{item[0]}}</div> <img ng-src="{{item[2]}}" ng-show="item[2]">
</div> <div class="user-name">{{item[0]}}</div>
</div> </div>
</div> </div>
</div> </div>
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
<div class="conversation-header"> <div class="conversation-header">
<div class="conversation-user"> <div class="conversation-user">
<div class="user-photo" ng-show="selectedChannel.avatar_url"><img ng-src="{{selectedChannel.avatar_url}}"></div> <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>
<div class="conversation-search"> <div class="conversation-search">
<input type="text" placeholder="Arama Yap"> <input type="text" placeholder="Arama Yap">
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
<span class="glyphicon glyphicon-option-vertical"></span> <span class="glyphicon glyphicon-option-vertical"></span>
</div> </div>
<ul class="dropdown-menu" ng-show="selectedChannel.actions.length > 0" aria-labelledby="chat-app-actions" style="left: inherit; top: 53px; right: 66px;" > <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> </ul>
</div> </div>
<div class="close-chat-app" ng-click="hideApp()"> <div class="close-chat-app" ng-click="hideApp()">
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
</div> </div>
</div> </div>
<div class="conversation-body"> <div class="conversation-body" scroll-down-when-update="lastMessage">
<div class="conversation-body-inner"> <div class="conversation-body-inner">
<div class="beginning-of-conversation"> <div class="beginning-of-conversation">
...@@ -41,17 +41,16 @@ ...@@ -41,17 +41,16 @@
<div class="conversation-block clearfix" ng-repeat="msg in selectedChannel.messages"> <div class="conversation-block clearfix" ng-repeat="msg in selectedChannel.messages">
<div class="conversation-actions"> <div class="conversation-actions">
<div class="action"><span class="glyphicon glyphicon-star-empty"></span></div> <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> <span class="glyphicon glyphicon-option-horizontal"></span>
</div> </div>
<ul class="dropdown-menu" style="left:-86px;"> <ul class="dropdown-menu" style="left:-86px;" >
<li><a href="#">Edit</a></li> <li ng-repeat="act in msg.actions"><a href="#" ng-click="applyMessageAction(msg, act)">{{act[0]}}</a></li>
<li><a href="#" ng-click="deleteConfirmation()">Delete</a></li>
</ul> </ul>
</div> </div>
<div class="user-photo"> <div class="user-photo">
<img src="../../../img/erkan.jpg"> <img ng-src="{{msg.avatar_url}}" ng-show="msg.avatar_url">
</div> </div>
<div class="user-message"> <div class="user-message">
<div class="message-header clearfix"> <div class="message-header clearfix">
......
<div class="chat-popup-window create-new-channel-window"> <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> <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"> <div class="text-center">
<input type="text" placeholder="Channel Name" ng-model="channel.name"><br> <input type="text" placeholder="Channel Name" ng-model="channel.name"><br>
<textarea placeholder="Channel Description" ng-model="channel.description"></textarea> <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>
</div> </div>
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<div class="close-chat-popup-window" ng-click="cancel()"><span class="glyphicon glyphicon-remove"></span></div> <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;"> <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-success" style="font-size: 20px;" ng-click="done()">Yes</button>
<button class="btn btn-default" style="font-size: 20px;" ng-click="cancel()">Cancel</button> <button class="btn btn-default" style="font-size: 20px;" ng-click="cancel()">Cancel</button>
</div> </div>
......
<div> <div>
<button class="chat-app-button" ng-show="hidden" ng-click="hidden = false"><i class="glyphicon glyphicon-comment"></i></button> <a class="chat-app-button" ng-show="chatAppIsHidden" ng-click="showApp()"><i class="glyphicon glyphicon-comment"></i></a>
<div class="chat-app" ng-hide="hidden"> <div class="chat-app" ng-hide="chatAppIsHidden">
<div class="chat-app-container"> <div class="chat-app-container">
...@@ -11,12 +11,12 @@ ...@@ -11,12 +11,12 @@
<ul class="channels"> <ul class="channels">
<li class="title">CHANNELS <span class="add-action glyphicon glyphicon-plus-sign" ng-click="createChannel()"></span></li> <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>
<ul class="direct-messages"> <ul class="direct-messages">
<li class="title">DIRECT MESSAGES <span class="add-action glyphicon glyphicon-plus-sign" ng-click="searchUser()"></span></li> <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> </ul>
</div> </div>
...@@ -24,7 +24,6 @@ ...@@ -24,7 +24,6 @@
<ng-include src="'components/messaging/templates/conversation.html'"></ng-include> <ng-include src="'components/messaging/templates/conversation.html'"></ng-include>
<div class="popup-placeholder"></div> <div class="popup-placeholder"></div>
</div> </div>
</div> </div>
</div> </div>
...@@ -12,6 +12,17 @@ angular.module("ulakbus") ...@@ -12,6 +12,17 @@ angular.module("ulakbus")
.service("Utils", function(){ .service("Utils", function(){
var self = this; 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 list {Array} Array of objects to group
* @param propName {String} property name to group array by * @param propName {String} property name to group array by
...@@ -24,4 +35,18 @@ angular.module("ulakbus") ...@@ -24,4 +35,18 @@ angular.module("ulakbus")
return acc; 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