Commit 299dfc77 authored by Vladimir Baranov's avatar Vladimir Baranov

Merge branch 'feature/messaging' into develop

parents 2619b847 145d9e37
......@@ -128,6 +128,7 @@ module.exports = function (grunt) {
"app/zetalib/error_service.js",
"app/zetalib/action_service.js",
"app/zetalib/socket.js",
"app/zetalib/utils_service.js",
"app/shared/directives.js",
"app/components/auth/auth_controller.js",
"app/components/auth/auth_service.js",
......@@ -140,7 +141,8 @@ module.exports = function (grunt) {
"app/components/wf/wf_controller.js",
"app/components/version/version.js",
"app/components/version/interpolate-filter.js",
"app/components/version/version-directive.js"
"app/components/version/version-directive.js",
"app/components/messaging/*.js"
],
dest: 'dist/app.js'
},
......@@ -196,7 +198,8 @@ module.exports = function (grunt) {
'app/shared/scripts/theme.js',
'app/shared/directives.js',
'app/components/**/*controller.js',
'app/components/**/*service.js'
'app/components/**/*service.js',
'app/components/messaging/*.js',
],
dest: 'dist/<%= grunt.branchname %>/app.js'
},
......@@ -426,4 +429,4 @@ module.exports = function (grunt) {
'uglify:branch'
]);
});
};
\ No newline at end of file
};
......@@ -2160,6 +2160,18 @@ table.dataTable thead .sorting:after {
font-family: 'robotoblack';
}
.chat-app .side-navigation ul li.title .add-action {
display: inline-block;
position: absolute;
right: 21px;
top: 6px;
/*margin-left: 4px;*/
/*top: 2px;*/
/*position: relative;*/
/*display: inline-block;*/
}
.chat-app .side-navigation ul li.compose {
background-color: #6f0d20;
margin: 5px 10px;
......@@ -2191,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;
}
......@@ -2277,7 +2289,7 @@ table.dataTable thead .sorting:after {
margin-top: 15px;
}
.chat-app .conversation-header .close-chat-app {
.chat-app .conversation-header .close-chat-app, .chat-app .conversation-section-empty .close-chat-app {
display: inline-block;
float: left;
width: 40px;
......@@ -2288,6 +2300,21 @@ table.dataTable thead .sorting:after {
margin-left: 10px;
border-radius: 3px;
margin-top:15px;
cursor: pointer;
}
.chat-app .conversation-section-empty .close-chat-app {
display: block;
position: absolute;
right: 15px;
top: 0px;
}
.chat-app .conversation-section-empty .close-chat-app {
display: block;
position: absolute;
right: 15px;
top: 0px;
}
.chat-app .conversation-header .chat-app-actions {
......@@ -2309,6 +2336,10 @@ table.dataTable thead .sorting:after {
overflow-x: hidden;
}
.chat-app .conversation-body.readonly {
height: calc(100% - 100px);
}
.chat-app .conversation-footer {
height: 70px;
width: 560px;
......@@ -2361,7 +2392,7 @@ table.dataTable thead .sorting:after {
border-radius:100%;
}
.chat-app .conversation-section .conversation-body .beginning-of-conversation {
.chat-app .conversation-section .conversation-body .beginning-of-conversation, .chat-app .conversation-section-empty .conversation-body {
text-align: center;
font-family: 'robotobold';
font-size: 20px;
......@@ -2371,6 +2402,10 @@ table.dataTable thead .sorting:after {
border-bottom: 1px solid #f3f3f3;
}
.chat-app .conversation-section-empty .conversation-body {
padding-top: 100px;
}
.chat-app .conversation-section .conversation-body .conversation-block {
display:block;
position:relative;
......@@ -2456,7 +2491,7 @@ table.dataTable thead .sorting:after {
z-index: 11;
background-color: rgba(255,255,255,0.97);
border-radius:3px;
display:none;
/*display:none;*/
}
.chat-app .create-new-message-window input {
......@@ -2490,7 +2525,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;
......@@ -2498,7 +2533,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;
......@@ -2506,18 +2541,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';
......@@ -2525,6 +2560,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;
......@@ -2570,7 +2628,7 @@ table.dataTable thead .sorting:after {
}
.chat-app .add-user-unit input {
width: 400px;
width: 400px;
margin-top: 40px;
height: 50px;
padding-left: 15px;
......@@ -2579,6 +2637,33 @@ table.dataTable thead .sorting:after {
outline: none;
}
.chat-app-close-btn {
display: block;
position: absolute;
right: 5px;
top: 5px;
}
.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: 9999;
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
}
.chat-app-button i {
margin-top: 3px;
font-size: 18px;
}
/* END OF CHAT APPLICATION */
......
......@@ -28,7 +28,7 @@ angular.module(
'ngSanitize',
'ngCookies',
'ulakbus.formService',
'ulakbus.messagingService',
'ulakbus.messaging',
'ulakbus.dashboard',
'ulakbus.auth',
'ulakbus.error_pages',
......
angular.module("ulakbus.messaging")
.directive('messaging', function (Generator, MessagingService, $log, $rootScope, MessagingPopup, Utils) {
// get channel key
function getKey (channel) {
if (!channel) return;
if (!angular.isObject(channel)) return channel;
var channelKey = channel.channel_key;
if (!channelKey && channel.hasOwnProperty('key')){
channelKey = channel.key;
}
return channelKey;
}
function searchWrapper(scope, promiseWrapper){
scope.loading = true;
scope.searchResult = [];
promiseWrapper()
.then(function(result){
scope.searchResult = result;
})
.finally(function(){
scope.loading = false;
})
}
return {
templateUrl: 'components/messaging/templates/index.html',
restrict: 'E',
scope: {},
link: function(iScope, iElem, iAttrs){
iScope.chatAppIsHidden = true;
// shared object to populate models through scopes
iScope.shared = {};
var popupRootElement = $(iElem).find('.popup-placeholder');
function editChannelPopup(channel){
return MessagingPopup.show({
templateUrl: "components/messaging/templates/create_channel.html",
rootElement: popupRootElement,
link: function(scope){
scope.channel = channel||{};
scope.title = "Kanalı düzenle";
scope.actionTitle = "Düzenle";
if (!channel){
scope.title = "Yeni Kanal Oluştur";
scope.actionTitle = "Oluştur";
}
}
})
}
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(message) == getKey(channel)){
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});
}
}
function reportLastSeenMessage(){
if (!iScope.lastMessage || !iScope.selectedChannel) return;
// instantly received messages haven't timestamp. Use moment
// FIXME: change to proper moment processing
// var ts = iScope.lastMessage.moment.toISOString();
var ts = iScope.lastMessage.moment.format("YYYY-MM-DDTHH:mm:ss");
MessagingService.report_last_seen_message(getKey(iScope.selectedChannel), iScope.lastMessage.key, ts);
};
iScope.deleteConfirmation = function(title){
return MessagingPopup.show({
templateUrl: "components/messaging/templates/delete_confirmation.html",
link: function(scope){
scope.title = title || "Silmek istediğinize emin misiniz?";
},
rootElement: popupRootElement
})
};
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.createDirectChannel = this.createDirectChannel;
iScope.hideApp = function(){
iScope.chatAppIsHidden = true;
};
iScope.showApp = function(){
iScope.chatAppIsHidden = false;
iScope.updateChannelsList();
}
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.createChannel = function(){
return editChannelPopup().then(function(channel){
return MessagingService.create_channel(channel.name, channel.description||"")
.then(function(newChannel){
updateAndSelect(newChannel)
});
})
};
iScope.applyChannelAction = function(channel, action){
var actionView = action[1];
switch (actionView) {
case '_zops_pin_channel':
MessagingService.pin_channel(getKey(channel));
break;
case '_zops_delete_channel':
iScope.deleteConfirmation('Kanalı silmek istediğinize emin misiniz?')
.then(function(){
MessagingService.delete_channel(getKey(channel));
});
break;
case '_zops_edit_channel':
editChannelPopup(channel).then(function(channelData){
return MessagingService.edit_channel(getKey(channelData), channelData.name, channelData.description||"");
});
break;
case '_zops_add_members':
MessagingPopup.show({
templateUrl: "components/messaging/templates/add_user_unit.html",
rootElement: popupRootElement,
link: function(scope){
scope.title = "Kanala kullanıcı ekle";
scope.placeholder = "Eklemek için kullanıcı ara";
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;
case "_zops_add_unit_to_channel":
MessagingPopup.show({
templateUrl: "components/messaging/templates/add_user_unit.html",
rootElement: popupRootElement,
link: function(scope){
scope.title = "Birim Ekle";
scope.placeholder = "Kanala eklemek için birim ara";
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){
if (!silent) iScope.loadingChannel = true;
return MessagingService.show_channel(channelKey).then(function(result){
return result;
}).finally(function(){
iScope.loadingChannel = false;
})
}
iScope.selectChannel = function(channel, silent){
var channelKey = getKey(channel);
selectChannel(channelKey, silent).then(function(result){
iScope.selectedChannel = result;
iScope.selectedChannel.read_only = channel.read_only;
iScope.selectedChannel.messages = result.last_messages;
updateLastMessage(channel.messages);
reportLastSeenMessage();
});
};
iScope.isChannelSelected = function(channel){
return iScope.selectedChannel && getKey(channel) == getKey(iScope.selectedChannel);
}
iScope.sendMessage = function(content){
if (!content) return;
var channelKey = getKey(iScope.selectedChannel);
// select message type: 2 - direct message, 4 - channel message;
var msgType = iScope.selectedChannel.type == MessagingService.CHANNEL_TYPE.DIRECT ? 2 : 4;
MessagingService.create_message(channelKey, msgType, content).then(function(){
iScope.shared.message = "";
});
};
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("Mesajı silmek istediğinize emin misiniz?")
.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));
});
$rootScope.$on("user_ready", function(){
// init service after user logged in
iScope.selectedChannel = null;
iScope.publicChannels = [];
iScope.notificationsChannel = [];
iScope.directChannels = []
iScope.hideApp();
});
}
};
})
.filter('fromNow', function(Moment){
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);
}
});
}
}
})
/**
* Copyright (C) 2015 ZetaOps Inc.
*
* This file is licensed under the GNU General Public License v3
* (GPLv3). See LICENSE.txt for details.
*
* @author Evren Kutar
*/
angular.module('ulakbus.messaging', ['ui.bootstrap'])
/**
* @memberof ulakbus.messaging
* @ngdoc factory
* @name MessagingService
* @description Service handles all stuff related to messaging
*/
.factory('MessagingService', function ($q, $timeout, $compile, $log, $rootScope, Moment, WSOps, Utils) {
var msg = {};
var notificationsChannelKey;
msg.CHANNEL_TYPE = {
"PUBLIC": 15,
"DIRECT": 10,
"NOTIFICATION": 5
};
function wsReady () {
/**
* wait until websocket will be open
*/
var deferred = $q.defer();
var dismissWatcher = $rootScope.$watch('websocketIsOpen', function(isOpen){
if (isOpen){
dismissWatcher();
deferred.resolve();
}
});
return deferred.promise;
}
function wsRequest (outgoing){
return wsReady().then(function(){
return WSOps.request(outgoing);
})
}
function prepareMessages (messages){
for (var i = 0; i < messages.length; i++){
var message = messages[i];
msg.prepareMessage(message);
}
}
// prepare message to show in UI
msg.prepareMessage = function(message){
if (!message.timestamp){
message.moment = Moment();
} else {
var ts = message.timestamp.replace("Z", "");
message.moment = Moment(ts);
}
return message;
};
msg.get_notifications_channel_key = function(){
return notificationsChannelKey;
};
/**
* API
*
* */
msg.list_channels = function list_channels (){
/**
* request channels list as below;
* {
* 'view':'_zops_list_channels',
* }
*
* wait for response
*
* {
* 'channels': [
* {'name': string, // name of channel
* 'key': key, // key of channel
* 'unread': int, // unread message count
* 'type': int, // channel type,
* // 15: public channels (chat room/broadcast channel distinction comes from "read_only" flag)
* // 10: direct channels
* // 5: one and only private channel which can be "Notifications"
* 'read_only': boolean, // true if this is a read-only subscription to a broadcast channel
* // false if it's a public chat room
*
* 'actions':[('action name', 'view name'),]
* }
*
*/
var outgoing = {
view: '_zops_list_channels'
};
return wsRequest(outgoing).then(function (data) {
console.error("channels: ", data.channels);
var grouped = Utils.groupBy(data.channels||[], "type");
// save notifications channel key
notificationsChannelKey = grouped[msg.CHANNEL_TYPE.NOTIFICATION][0].key;
return grouped;
});
};
msg.search_user = function (query) {
var outgoing = {
view: '_zops_search_user',
query: query
};
return wsRequest(outgoing).then(function (data) {
return data.results
});
};
msg.search_unit = function (query) {
var outgoing = {
view: '_zops_search_unit',
query: query
};
return wsRequest(outgoing).then(function (data) {
return data.results
});
};
msg.create_direct_channel = function (key) {
var outgoing = {
'view': '_zops_create_direct_channel',
'user_key': key
};
return wsRequest(outgoing).then(function(result){
$log.info("Create direct channel result: ", result);
return result;
})
};
msg.create_channel = function (name, desription) {
var outgoing = {
view:'_zops_create_channel',
name: name,
description: desription
};
return wsRequest(outgoing).then(function(result){
$log.info("Channel ", name, "created: ", result);
return result;
})
};
msg.add_members = function (channelKey, members, readOnly) {
var outgoing = {
view:'_zops_add_members',
channel_key: channelKey,
read_only: !!readOnly,
members: members
};
return wsRequest(outgoing).then(function(result){
$log.info("Members ", members, " added to channel ", channelKey, ": ", result);
return result;
})
};
msg.add_unit_to_channel = function (channelKey, unitKey, readOnly) {
var outgoing = {
view: '_zops_add_unit_to_channel',
channel_key: channelKey,
unit_key: unitKey,
read_only: !!readOnly
};
return wsRequest(outgoing).then(function(result){
$log.info("Unit ", unitKey, " added to channel ", channelKey, ": ", result);
return result;
});
};
msg.delete_channel = function (channelKey) {
var outgoing = {
view: '_zops_delete_channel',
channel_key: channelKey
};
return wsRequest(outgoing).then(function(result){
$log.info("Channel ", channelKey, " deleted: ", result);
return result;
})
};
msg.edit_channel = function (channelKey, name, desription) {
var outgoing = {
view:'_zops_edit_channel',
channel_key: channelKey,
name: name,
description: desription
};
return wsRequest(outgoing).then(function(result){
$log.info("Channel ", channelKey, " edited: ", outgoing, result);
return result;
})
};
msg.pin_channel = function (channelKey) {
var outgoing = {
view: '_zops_pin_channel',
channel_key: channelKey
};
return wsRequest(outgoing).then(function(result){
$log.info("Channel ", channelKey, " pinned: ", result);
return result;
})
};
msg.show_channel = function(channelKey){
var outgoing = {
view: '_zops_show_channel',
key: channelKey
};
return wsRequest(outgoing).then(function(result){
$log.info("Show channel ", channelKey, ": ", result);
prepareMessages(result.last_messages);
return result;
})
};
msg.channel_history = function (channelKey, lastMessageTimestamp) {
var outgoing = {
view:'_zops_channel_history',
channel_key: channelKey,
timestamp: lastMessageTimestamp
};
return wsRequest(outgoing).then(function(result){
$log.info("Load channel ", channelKey, "history: ", result);
return result;
})
};
msg.get_unread_messages_count = function(){
var outgoing = {
'view': '_zops_unread_count'
};
return wsRequest(outgoing).then(function(result){
$log.info("Get unread messages count: ", result);
return result;
})
};
msg.report_last_seen_message = function (channelKey, msgKey, timestamp){
var outgoing = {
view: '_zops_report_last_seen_message',
channel_key: channelKey,
msg_key: msgKey,
timestamp: timestamp
};
return wsRequest(outgoing).then(function(result){
$log.info("Report last seen message ", channelKey, msgKey, timestamp, ": ", result);
return result;
})
};
msg.create_message = function(channelKey, msgType, body, attachments){
var outgoing = {
view: '_zops_create_message',
message: {
channel: channelKey,
type: msgType,
title: "",
receiver: "",
body: body,
attachments: attachments
}
};
return wsRequest(outgoing).then(function(result){
$log.info("Message sent: ", result);
return result;
})
};
msg.find_message = function (channelKey, query, pageNumber) {
var outgoing = {
view: '_zops_search_find_message',
channel_key: channelKey,
query: query,
page: pageNumber
};
return wsRequest(outgoing).then(function(result){
$log.info("Find message: ", result);
return result;
});
};
msg.delete_message = function (msgKey) {
var outgoing = {
view: '_zops_delete_message',
key: msgKey
};
return wsRequest(outgoing).then(function(result){
$log.info("Delete message ", msgKey,":", result);
return result;
});
};
msg.edit_message = function(msgKey, body) {
var outgoing = {
view: '_zops_edit_message',
message: {
key: msgKey,
body: body
}
};
return wsRequest(outgoing).then(function(result){
$log.info("Edit message", msgKey, ":", result);
return result;
});
};
msg.flag_message = function (msgKey, flag) {
var outgoing = {
view: '_zops_flag_message',
key: msgKey,
flag: flag
};
return wsRequest(outgoing).then(function(result){
$log.info("Flag message ", msgKey, flag, ":", result);
return result;
});
};
msg.get_message_actions = function (msgKey) {
var outgoing = {
view: '_zops_get_message_actions',
key: msgKey
};
return wsRequest(outgoing).then(function(result){
$log.info("Get message actions", msgKey, ":", result);
return result;
});
};
msg.add_to_favorites = function (msgKey) {
var outgoing = {
view: '_zops_add_to_favorites',
key: msgKey
};
return wsRequest(outgoing).then(function(result){
$log.info("Add message ", msgKey, " to favorites: ", result);
return result;
});
};
msg.remove_from_favorites = function (msgKey) {
var outgoing = {
view: '_zops_remove_to_favorites',
key: msgKey
};
return wsRequest(outgoing).then(function(result){
$log.info("Remove message ", msgKey, " from favorites: ", result);
return result;
});
};
msg.list_favorites = function (channelKey) {
var outgoing = {
view: '_zops_list_favorites',
channel_key: channelKey
};
return wsRequest(outgoing).then(function(result){
$log.info("List favorites for channel", channelKey, ": ", result);
return result;
});
};
return msg;
})
.service("MessagingPopup", function($q, $compile, $http, $rootScope){
function compile(template, config){
var resultDeferred = $q.defer();
var scope = config.scope || $rootScope.$new(true);
var rootElement = config.rootElement;
var element = $compile(template)(scope);
if (config.link){
config.link(scope);
}
scope.done = function(result){
resultDeferred.resolve.apply(this, arguments);
};
scope.cancel = function(){
resultDeferred.reject.apply(this, arguments);
};
rootElement.empty();
rootElement.append(element);
resultDeferred.promise._done = scope.done;
resultDeferred.promise._cancel = scope.cancel;
return resultDeferred.promise.finally(function(){
rootElement.empty();
scope.$destroy();
});
}
this.show = function(config){
if (config.templateUrl){
return $http({method: 'GET', url: config.templateUrl, cache: true}).then(function(result){
return compile(result.data, config)
});
}
return compile(config.template, config);
};
});
<div class="chat-popup-window add-user-unit" style="display:block;">
<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" 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>
<span class="loader" ng-show="loadingChannel"></span>
<div class="conversation-section conversation-section-empty" ng-hide="selectedChannel">
<div class="close-chat-app" ng-click="hideApp()">
<span class="glyphicon glyphicon-remove" ></span>
</div>
<div class="conversation-body" >
Konuşmaya başlamak için bir kanal seçin ya da oluşturun.
</div>
</div>
<div class="conversation-section" ng-hide="loadingChannel || !selectedChannel">
<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" title="{{selectedChannel.description}}">{{selectedChannel.name}}</div>
</div>
<div class="conversation-search">
<input type="text" placeholder="Arama Yap">
</div>
<div class="dropdown">
<div class="chat-app-actions dropdown-toggle" data-toggle="dropdown" id="chat-app-actions">
<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(selectedChannel, action)" ng-repeat="action in selectedChannel.actions">{{action[0]}}</a></li>
</ul>
</div>
<div class="close-chat-app" ng-click="hideApp()">
<span class="glyphicon glyphicon-remove"></span>
</div>
</div>
<div class="conversation-body" ng-class="{readonly: selectedChannel.read_only}" scroll-down-when-update="lastMessage">
<div class="conversation-body-inner">
<div class="beginning-of-conversation">
Burası yazışmanın başı!
</div>
<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" ng-click="getMessageActions(msg)">
<span class="glyphicon glyphicon-option-horizontal"></span>
</div>
<ul class="dropdown-menu" style="left:-86px;" >
<li ng-repeat="act in msg.actions"><a ng-click="applyMessageAction(msg, act)">{{act[0]}}</a></li>
</ul>
</div>
<div class="user-photo">
<img ng-src="{{msg.avatar_url}}" ng-show="msg.avatar_url">
</div>
<div class="user-message">
<div class="message-header clearfix">
<div class="user-name">{{::msg.sender_name}}</div>
<div class="message-time">{{::msg.moment|fromNow}}</div>
</div>
<div class="message-content">
{{::msg.content}}
</div>
</div>
</div>
</div>
</div>
<div class="conversation-footer" ng-show="!selectedChannel.read_only">
<textarea placeholder="Mesajını buraya yaz..." ng-model="shared.message" on-enter-pressed="sendMessage(shared.message)"></textarea>
<div class="add-attachment">
<span class="glyphicon glyphicon-send" ng-click="sendMessage(shared.message);"></span>
<div class="dropup" style="float:left;">
<span class="glyphicon glyphicon-paperclip dropdown-toggle" data-toggle="dropdown" id="attachment"></span>
<ul class="dropdown-menu" aria-labelledby="attachment" style="left:-104px;">
<li><a >Görsel</a></li>
<li><a >Dosya</a></li>
</ul>
</div>
</div>
</div>
</div>
<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>{{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)">{{actionTitle}}</button>
</div>
</div>
<div class="chat-popup-window confirmation-window">
<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>{{title}}</p>
<button class="btn btn-success" style="font-size: 20px;" ng-click="done()">Evet</button>
<button class="btn btn-default" style="font-size: 20px;" ng-click="cancel()">Vazgeç</button>
</div>
</div>
<div>
<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">
<div class="side-navigation">
<ul class="channels">
<li class="title" ng-click="selectChannel(notificationsChannel)">BİLDİRİMLER <span class="badge" ng-show="notificationsChannel.unread">{{notificationsChannel.unread}}</span></li>
</ul>
<ul class="channels">
<li class="title">KANALLAR <span class="add-action glyphicon glyphicon-plus-sign" ng-click="createChannel()"></span></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">MESAJLAR <span class="add-action glyphicon glyphicon-plus-sign" ng-click="searchUser()"></span></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>
<ng-include src="'components/messaging/templates/conversation.html'"></ng-include>
<div class="popup-placeholder"></div>
</div>
</div>
</div>
<div class="chat-popup-window create-new-message-window">
<div class="close-chat-popup-window" ng-click="cancel()"><span class="glyphicon glyphicon-remove"></span></div>
<input type="text" placeholder="Birini ismi ile ara..." ng-model="query" ng-change="onChange(query)">
<div class="search-results">
<span class="loader" ng-show="loading"></span>
<div class="user" ng-repeat="user in searchResult" ng-click="done(user)">
<img ng-src="{{user[2]}}" ng-show="user[2]">
<div class="user-name">{{user[0]}}</div>
</div>
</div>
</div>
......@@ -113,11 +113,11 @@
<script src="shared/directives.js"></script>
<script src="zetalib/interceptors.js"></script>
<script src="zetalib/form_service.js"></script>
<script src="zetalib/messagingService.js"></script>
<script src="zetalib/form_constraints.js"></script>
<script src="zetalib/action_service.js"></script>
<script src="zetalib/error_service.js"></script>
<script src="zetalib/socket.js"></script>
<script src="zetalib/utils_service.js"></script>
<!-- components -->
......@@ -125,6 +125,8 @@
<script src="components/auth/auth_service.js"></script>
<script src="components/dashboard/dashboard_controller.js"></script>
<script src="components/dashboard/dashboard_widgets_directives.js"></script>
<script src="components/messaging/messaging_service.js"></script>
<script src="components/messaging/messaging.js"></script>
<script src="components/crud/crud_controller.js"></script>
<script src="components/crud/crud_widgets.js"></script>
<script src="components/debug/debug_controller.js"></script>
......@@ -135,5 +137,7 @@
<script src="components/version/version-directive.js"></script>
<script src="components/version/version.js"></script>
</body>
</html>
......@@ -120,11 +120,11 @@
<script src="shared/directives.js"></script>
<script src="zetalib/interceptors.js"></script>
<script src="zetalib/form_service.js"></script>
<script src="zetalib/messagingService.js"></script>
<script src="zetalib/form_constraints.js"></script>
<script src="zetalib/action_service.js"></script>
<script src="zetalib/error_service.js"></script>
<script src="zetalib/socket.js"></script>
<script src="zetalib/utils_service.js"></script>
<!-- components -->
......@@ -132,6 +132,8 @@
<script src="components/auth/auth_service.js"></script>
<script src="components/dashboard/dashboard_controller.js"></script>
<script src="components/dashboard/dashboard_widgets_directives.js"></script>
<script src="components/messaging/messaging_service.js"></script>
<script src="components/messaging/messaging.js"></script>
<script src="components/crud/crud_controller.js"></script>
<script src="components/crud/crud_widgets.js"></script>
<script src="components/debug/debug_controller.js"></script>
......
......@@ -28,7 +28,7 @@ angular.module(
'ngSanitize',
'ngCookies',
'ulakbus.formService',
'ulakbus.messagingService',
'ulakbus.messaging',
'ulakbus.dashboard',
'ulakbus.auth',
'ulakbus.error_pages',
......
......@@ -32,78 +32,28 @@ angular.module('ulakbus')
* @ngdoc directive
* @name headerNotification
* @description This directive is responsible to get and show notification.
* It calls API's /notify path with given interval and broadcasts `notifications` application-wide.
* There are 4 types of notifications:
* 1: tasks, 2: messages, 3: announcements, 4: recents
* It calls API's '_zops_unread_count' view to init its state and updates state when 'message' or 'notifications' broadcast message received *
* - Notifications can be disabled in /dev/settings page
*/
.directive('headerNotification', function (WSOps, $rootScope, $cookies, $interval, RESTURL, $uibModal) {
.directive('headerNotification', function ($rootScope, $uibModal, MessagingService) {
return {
templateUrl: 'shared/templates/directives/header-notification.html',
restrict: 'E',
replace: true,
scope: {},
controller: function ($scope, $log) {
// notification categories:
// 1: tasks, 2: messages, 3: announcements, 4: recents
$scope.notifications = {1: [], 2: [], 3: [], 4: []};
/**
* Group notifications
* @param notifications
*/
$scope.popModal = function(item){
var modalInstance = $uibModal.open({
animation: true,
templateUrl: 'shared/templates/notificationsModalContent.html',
controller: function($scope){
$scope.notification = item;
$scope.cancel = function() {
modalInstance.dismiss('cancel');
};
},
size: 'lg'
});
}
$scope.groupNotifications = function (notifications) {
$scope.notifications = {1: [], 2: [], 3: [], 4: []};
angular.forEach(notifications, function (value, key) {
$scope.notifications[value.type].push(value);
});
$scope.$apply();
$scope.count = {
messages: 0,
notifications: 0
};
/**
* When "notifications" send via websocket, parse notifications by type.
*/
$scope.$on("notifications", function (event, data) {
$log.debug("Notification!", data);
$scope.groupNotifications(data);
});
/**
* When clicked mark the notification as read.
* @param items
* @todo: do it in detail page of notification
*/
$scope.markAsRead = function (event,item, group, index) {
//Added event parameter to stop propagate, so that behaviour of outsideClick won't be interrupted.
event.stopPropagation();
WSOps.doSend(angular.toJson({data: {view: 'notify', id:item.id}}));
$scope.notifications[group].splice(index,1);
$event.preventDefault();
$event.stopPropagation();
return false;
};
// if markasread triggered outside the directive
// $scope.$on("markasread", function (event, data) {
// $scope.markAsRead(data);
// });
function initCounters(){
MessagingService.get_unread_messages_count()
.then(function(result){
$scope.count.messages = result.messages;
$scope.count.notifiations = result.notifications;
})
}
initCounters();
}
};
})
......@@ -663,64 +613,20 @@ angular.module('ulakbus')
}
}
})
// listen for message in directives below
.directive('messaging', function (Generator, MessagingService, $log, $rootScope) {
return {
templateUrl: 'shared/templates/directives/messaging/index.html',
restrict: 'E',
replace: true,
scope: {},
controller: function ($scope) {
$scope.messages = [];
$scope.$on("messages", function (event, data) {
$log.debug("Message List Received", data);
$scope.messages(data);
});
$scope.$on("message", function (event, data) {
$log.debug("Message Received", data);
// do relevant action here
});
}
};
})
.directive('messageDetail', function (Generator, MessagingService, $log, $rootScope) {
return {
templateUrl: 'shared/templates/directives/messaging/detail.html',
restrict: 'E',
replace: true,
scope: {},
controller: function ($scope) {
$scope.messages = [];
$scope.$on("detailMessages", function (event, data) {
$log.debug("Detail Message Received", data);
$scope.messages = data;
});
$scope.$on("message", function (event, data) {
$log.debug("Message Received", data);
// do relevant action here
// if channel_key belongs to detail screen then append msg to the end of the thread
});
}
};
})
.directive('timetableActionSelector', function($timeout){
.directive('timetableActionSelector', function($timeout) {
// Display/hide popover with actions
// global listener used to close popover when user clicks outside of the popover
$('html').on('click', function(e) {
$('html').on('click', function (e) {
var target = $(e.target);
if (target.parents().is('.action-selector')){
if (target.parents().is('.action-selector')) {
target.parents('.action-selector').children('.popover').toggleClass('ng-hide');
return;
}
if (target.hasClass('action-selector')){
if (target.hasClass('action-selector')) {
target.children('.popover').toggleClass('ng-hide');
return;
};
}
;
$('.course-prg-scheduler .action-selector>.popover').toggleClass('ng-hide', true);
});
......@@ -730,29 +636,29 @@ angular.module('ulakbus')
externalModel: '=ngModel',
onChange: "&ngChange"
},
link: function(iScope, iElem, iAttrs){
link: function (iScope, iElem, iAttrs) {
var valueToClassMap = {
1: 'action-indicator_appropriate',
2: 'action-indicator_uncertain',
3: 'action-indicator_busy'
};
if (iAttrs.hasOwnProperty('readonly')){
iAttrs.$observe('readonly', function(v){
if (iAttrs.hasOwnProperty('readonly')) {
iAttrs.$observe('readonly', function (v) {
if (v && v == 'false') v = false;
iScope.readonly = v;
});
}
iScope.$watch('externalModel', function(value){
iScope.$watch('externalModel', function (value) {
iScope.value = valueToClassMap[value];
});
iScope.setModelValue = function(value){
iScope.setModelValue = function (value) {
var oldValue = iScope.externalModel;
iScope.externalModel = value;
// call change in next digest
$timeout(function(){
$timeout(function () {
if (iScope.onChange && value != oldValue) {
iScope.onChange();
}
......@@ -761,4 +667,25 @@ angular.module('ulakbus')
}
}
}
})
/**
* @memberof ulakbus
* @ngdoc directive
* @name onEnterPressed
* @description Fire action when enter pressed on element
*/
.directive("onEnterPressed", function () {
return {
link: function (scope, element, attrs) {
element.bind("keydown keypress", function (event) {
if(event.which === 13 && !event.ctrlKey) {
scope.$apply(function (){
scope.$eval(attrs.onEnterPressed);
});
event.preventDefault();
}
})
}
}
});
......@@ -32,9 +32,9 @@
</li>-->
<li uib-dropdown auto-close="outsideClick">
<a uib-dropdown-toggle>
<div class="badge" ng-show="notifications[2].length > 0">{{notifications[2].length}}</div>
<i class="fa fa-envelope fa-fw" tooltip-placement="bottom" uib-tooltip="Mesajlar"></i> <i
class="fa fa-caret-down"></i>
<div class="badge" ng-show="count.messages > 0">{{count.messages}}</div>
<i class="fa fa-envelope fa-fw" tooltip-placement="bottom" uib-tooltip="Mesajlar"></i>
<!--<i class="fa fa-caret-down"></i>-->
</a>
<ul class="dropdown-messages" uib-dropdown-menu ng-show="notifications[2].length > 0">
<li ng-repeat="notify in notifications[2] | limitTo: '8'">
......@@ -62,47 +62,47 @@
<!-- /.dropdown-messages -->
</li>
<!-- /.dropdown -->
<li uib-dropdown auto-close="outsideClick">
<a uib-dropdown-toggle>
<div class="badge" ng-if="notifications[1].length > 0">{{notifications[1].length}}</div>
<i class="fa fa-tasks fa-fw" tooltip-placement="bottom" uib-tooltip="Görevler"></i> <i
class="fa fa-caret-down"></i>
</a>
<ul class="dropdown-tasks" uib-dropdown-menu ng-if="notifications[1].length > 0">
<li ng-repeat="notify in notifications[1] | limitTo: '8'">
<a href="{{notify.url}}">
<div>
<p>
<strong>{{notify.title}}</strong>
<span class="pull-right text-muted">{{notify.body}}</span>
<span ng-click="markAsRead($event,notify, 1, $index)" class="pull-right fa fa-times"></span>
</p>
<!-- todo: progress bar will be used in future developments-->
<!--<div class="progress progress-striped active">-->
<!--<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="40" aria-valuemin="0" aria-valuemax="100" style="width: 40%">-->
<!--<span class="sr-only">40% Complete (success)</span>-->
<!--</div>-->
<!--</div>-->
<!--<li uib-dropdown auto-close="outsideClick">-->
<!--<a uib-dropdown-toggle>-->
<!--<div class="badge" ng-if="notifications[1].length > 0">{{notifications[1].length}}</div>-->
<!--<i class="fa fa-tasks fa-fw" tooltip-placement="bottom" uib-tooltip="Görevler"></i> <i-->
<!--class="fa fa-caret-down"></i>-->
<!--</a>-->
<!--<ul class="dropdown-tasks" uib-dropdown-menu ng-if="notifications[1].length > 0">-->
<!--<li ng-repeat="notify in notifications[1] | limitTo: '8'">-->
<!--<a href="{{notify.url}}">-->
<!--<div>-->
<!--<p>-->
<!--<strong>{{notify.title}}</strong>-->
<!--<span class="pull-right text-muted">{{notify.body}}</span>-->
<!--<span ng-click="markAsRead($event,notify, 1, $index)" class="pull-right fa fa-times"></span>-->
<!--</p>-->
<!--&lt;!&ndash; todo: progress bar will be used in future developments&ndash;&gt;-->
<!--&lt;!&ndash;<div class="progress progress-striped active">&ndash;&gt;-->
<!--&lt;!&ndash;<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="40" aria-valuemin="0" aria-valuemax="100" style="width: 40%">&ndash;&gt;-->
<!--&lt;!&ndash;<span class="sr-only">40% Complete (success)</span>&ndash;&gt;-->
<!--&lt;!&ndash;</div>&ndash;&gt;-->
<!--&lt;!&ndash;</div>&ndash;&gt;-->
</div>
</a>
</li>
<li class="divider"></li>
<li>
<a class="text-center">
<strong>See All Tasks</strong>
<i class="fa fa-angle-right"></i>
</a>
</li>
</ul>
<!--</div>-->
<!--</a>-->
<!--</li>-->
<!--<li class="divider"></li>-->
<!--<li>-->
<!--<a class="text-center">-->
<!--<strong>See All Tasks</strong>-->
<!--<i class="fa fa-angle-right"></i>-->
<!--</a>-->
<!--</li>-->
<!--</ul>-->
<!-- /.dropdown-tasks -->
</li>
<!-- /.dropdown -->
<li uib-dropdown auto-close="outsideClick">
<a uib-dropdown-toggle>
<div class="badge" ng-if="notifications[3].length > 0">{{notifications[3].length}}</div>
<i class="fa fa-bell fa-fw" tooltip-placement="bottom" uib-tooltip="Duyurular"></i> <i
class="fa fa-caret-down"></i>
<div class="badge" ng-if="count.notifications > 0">{{count.notifications}}</div>
<i class="fa fa-bell fa-fw" tooltip-placement="bottom" uib-tooltip="Duyurular"></i>
<!--<i class="fa fa-caret-down"></i>-->
</a>
<ul class="dropdown-alerts" uib-dropdown-menu ng-if="notifications[3].length > 0">
<li ng-repeat="notify in notifications[3] | limitTo: '8'">
......@@ -114,6 +114,13 @@
</div>
</a>
</li>
<li class="divider"></li>
<li>
<a class="text-center">
<strong>See All Notifications</strong>
<i class="fa fa-angle-right"></i>
</a>
</li>
</ul>
<!-- /.dropdown-alerts -->
</li>
......
......@@ -19,7 +19,7 @@
</ul>
</div>
<div class="close-chat-app">
<span class="glyphicon glyphicon-remove"></span>
<span class="glyphicon glyphicon-remove" ng-click="hideApp()"></span>
</div>
</div>
......@@ -76,4 +76,4 @@
</div>
</div>
\ No newline at end of file
</div>
<div class="chat-app">
<div class="chat-app" ng-hide="hidden">
<div class="chat-app-container">
<div class="side-navigation">
......@@ -55,6 +55,5 @@
</div>
</div>
<!-- end of create-new-message-window -->
</div>
</div>
\ No newline at end of file
</div>
/**
* Copyright (C) 2015 ZetaOps Inc.
*
* This file is licensed under the GNU General Public License v3
* (GPLv3). See LICENSE.txt for details.
*
* @author Evren Kutar
*/
angular.module('ulakbus.messagingService', ['ui.bootstrap'])
/**
* @memberof ulakbus.formService
* @ngdoc factory
* @name Generator
* @description form service's Generator factory service handles all generic form operations
*/
.factory('MessagingService', function ($q, $timeout, $sce, $location, $route, $compile, $log, $rootScope, Moment, WSOps) {
var msg = {};
msg.send = function (msg) {
/**
* send the message as following structure;
*
* MSG_TYPES can be follwing;
*
* MSG_TYPES = (
* (2, "Direct Message"),
* (3, "Broadcast Message"),
* (4, "Channel Message")
* )
*
* {
* 'view':'_zops_create_message',
* 'message': {
* 'channel': "code_name of the channel",
* 'receiver': "Key of receiver. Can be blank for non-direct messages",
* 'client_id': "Client side unique id for referencing this message",
* 'title': "Title of the message. Can be blank.",
* 'body': "Message body.",
* 'type': zengine.messaging.model.MSG_TYPES,
* 'attachments': [{
* 'description': "Can be blank.",
* 'name': "File name with extension.",
* 'content': "base64 encoded file content"
* }]
* }
*
* wait for response as
*
* {
* 'msg_key': "Key of the just created message object",
* }
*
*/
function prepMsg(msg) {
var outgoing = {
form_params: {
view: '_zops_create_message',
message: {
'channel': msg.channel, // this can be both channel and direct msg. remember direct msg is channel
'receiver': msg.receiver,
'client_id': msg.client_id, // "Client side unique id for referencing this message",
'title': msg.title, // "Title of the message. Can be blank.",
'body': msg.body, // "Message body.",
'type': msg.TYPE, // type can be one of the above
// 'attachments': [{ // do it with fileread directive
// 'description': "Can be blank.",
// 'name': "File name with extension.",
// 'content': "base64 encoded file content"
// }]
}
}
};
return outgoing;
}
WSOps.request(prepMsg(msg)).then(function (data) {
$log.debug("message sent:", data);
});
};
msg.incoming = function () {
/**
*
*/
};
msg.update = function (msg, action) {
/**
* update / delete a message here
*/
var outgoing = {
form_params: {
view: '_zops_' + action + '_message',
message: {
'channel': msg.channel, // this can be both channel and direct msg. remember direct msg is channel
'receiver': msg.receiver,
'client_id': msg.client_id, // "Client side unique id for referencing this message",
'title': msg.title, // "Title of the message. Can be blank.",
'body': msg.body, // "Message body.",
'type': msg.TYPE // type can be one of the above
}
}
};
return WSOps.request(outgoing).then(function (data) {
$log.debug("update request sent");
return data;
})
};
/**
* use this method to get all messages of channel and direct messages
* REMEMBER; direct messages are also channels, everything is channel on backend!
* @param chnls
* @returns {*}
*/
msg.get_channel = function (chnls) {
/**
* request channels as below;
*
* {
* 'view':'_zops_show_channel',
* 'channel_key': "Key of the requested channel"
* }
*
* wait for response
*
* {
* 'channel_key': "key of channel",
* 'description': string,
* 'no_of_members': int,
* 'member_list': [
* {'name': string,
* 'is_online': bool,
* 'avatar_url': string,
* }],
* 'last_messages': [
* {'content': string,
* 'key': string,
* 'actions':[
* {'title': string,
* 'cmd': string
* }
* ]
* }
* ]
* }
*
*/
var outgoing = {
form_params: {
view: '_zops_show_channel',
channel_key: chnls.key
}
};
return WSOps.request(outgoing).then(function (data) {
$log.debug("message sent:", data);
return data;
});
};
return msg;
});
\ No newline at end of file
......@@ -147,7 +147,9 @@ angular.module('ulakbus')
// this way it broadcasts to relevant listener
// i group messages and notifications into 2 groups
// necessary actions will taken where it is listened
$rootScope.$broadcast(type[msg_data["type"]], msg_data);
$timeout(function(){
$rootScope.$broadcast(type[msg_data["type"]], msg_data);
});
},
dashboard: function () {
// dashboard consists of menu and user specifications
......@@ -255,4 +257,4 @@ angular.module('ulakbus')
};
return wsOps;
});
\ No newline at end of file
});
/**
* Copyright (C) 2015 ZetaOps Inc.
*
* This file is licensed under the GNU General Public License v3
* (GPLv3). See LICENSE.txt for details.
*
* @author Vladimir Baranov
*/
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
* @returns {Object}
*/
this.groupBy = function (list, propName) {
return list.reduce(function(acc, item) {
(acc[item[propName]] = acc[item[propName]] || []).push(item);
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