kopia lustrzana https://github.com/c9/core
423 wiersze
15 KiB
JavaScript
423 wiersze
15 KiB
JavaScript
/*global window apf console*/
|
|
define(function(require, exports, module) {
|
|
"use strict";
|
|
|
|
main.consumes = ["CollabPanel", "ui", "api", "dialog.alert", "dialog.error", "c9", "panels", "collab.workspace"];
|
|
main.provides = ["notifications"];
|
|
return main;
|
|
|
|
function main(options, imports, register) {
|
|
var CollabPanel = imports.CollabPanel;
|
|
var c9 = imports.c9;
|
|
var ui = imports.ui;
|
|
var api = imports.api;
|
|
var panels = imports.panels;
|
|
var alert = imports["dialog.alert"].show;
|
|
var showError = imports["dialog.error"].show;
|
|
var workspace = imports["collab.workspace"];
|
|
|
|
var css = require("text!./notifications.css");
|
|
var staticPrefix = options.staticPrefix;
|
|
|
|
var oop = require("ace/lib/oop");
|
|
var Tree = require("ace_tree/tree");
|
|
var TreeData = require("./notificationsdp");
|
|
|
|
var plugin = new CollabPanel("Ajax.org", main.consumes, {
|
|
name: "notifications",
|
|
index: 150,
|
|
caption: "Notifications",
|
|
height: "20%"
|
|
});
|
|
|
|
// var emit = plugin.getEmitter();
|
|
|
|
// added notification types as classes below
|
|
var NOTIFICATION_TYPES = {};
|
|
|
|
var notificationsParent, notificationsTree, notificationsDataProvider;
|
|
var frame, panelButton, bubble;
|
|
// var visible = false;
|
|
|
|
var loaded = false;
|
|
function load() {
|
|
if (loaded) return;
|
|
loaded = true;
|
|
|
|
// Needed now for bubble
|
|
ui.insertCss(css, staticPrefix, plugin);
|
|
|
|
notificationsDataProvider = new TreeData();
|
|
|
|
c9.once("ready", function() {
|
|
setTimeout(loadNotifications, 10000);
|
|
});
|
|
|
|
if (!options.hosted && c9.debug) {
|
|
// standalone version test
|
|
addNotifications([
|
|
{ name: "Bas de Wachter", uid: 8, email: "bas@c9.io", type: "access_request" },
|
|
{ name: "Mostafa Eweda", uid: 1, email: "mostafa@c9.io", type: "access_request" },
|
|
{ name: "Lennart Kats", uid: 5, email: "lennart@c9.io", type: "access_request" },
|
|
{ name: "Ruben Daniels", uid: 2, email: "ruben@ajax.org", type: "access_request" },
|
|
{ name: "Fabian Jakobs", uid: 4, email: "fabian@ajax.org", type: "access_request" }
|
|
]);
|
|
}
|
|
|
|
workspace.on("notification", function(notif) {
|
|
addNotifications(notif);
|
|
postLoadedNotifications();
|
|
if (!isOpen()) {
|
|
var sound = document.getElementById("chatNotif");
|
|
sound && sound.play();
|
|
}
|
|
});
|
|
}
|
|
|
|
var drawn = false;
|
|
function draw(options) {
|
|
if (drawn) return;
|
|
drawn = true;
|
|
|
|
notificationsParent = options.html;
|
|
|
|
frame = options.aml;
|
|
|
|
// Notifications panel
|
|
notificationsTree = new Tree(notificationsParent);
|
|
notificationsTree.renderer.setScrollMargin(0, 10);
|
|
notificationsTree.renderer.setTheme({ cssClass: "notificationstree" });
|
|
notificationsTree.setOption("maxLines", 3);
|
|
// Assign the dataprovider
|
|
notificationsTree.setDataProvider(notificationsDataProvider);
|
|
|
|
notificationsTree.on("mousedown", function(e) {
|
|
var domTarget = e.domEvent.target;
|
|
|
|
var pos = e.getDocumentPosition();
|
|
var notif = notificationsDataProvider.findItemAtOffset(pos.y);
|
|
if (!notif || !domTarget)
|
|
return;
|
|
|
|
notif.handleMouseDown(e);
|
|
});
|
|
|
|
notificationsTree.on("mouseup", function(e) {
|
|
var domTarget = e.domEvent.target;
|
|
|
|
var pos = e.getDocumentPosition();
|
|
var notif = notificationsDataProvider.findItemAtOffset(pos.y);
|
|
if (!notif || !domTarget)
|
|
return;
|
|
|
|
notif.handleMouseUp(e);
|
|
});
|
|
|
|
plugin.on("hide", function(e) {
|
|
notificationsTree.renderer.freeze();
|
|
});
|
|
plugin.on("show", function(e) {
|
|
notificationsTree.renderer.unfreeze();
|
|
notificationsTree.renderer.$loop.schedule(notificationsTree.renderer.CHANGE_FULL);
|
|
});
|
|
|
|
// onNotificationsLoaded();
|
|
// notificationsDataProvider.emptyMessage = "Loading Notifications ...";
|
|
// loadNotifications();
|
|
if (!cachedNotifications.length)
|
|
frame.minimize();
|
|
postLoadedNotifications();
|
|
frame.on("resize", resize);
|
|
setTimeout(resize, 150); // HACK around apf layout bug
|
|
}
|
|
|
|
var cachedNotifications = [];
|
|
function loadNotifications() {
|
|
if (!options.isAdmin || !options.hosted)
|
|
return postLoadedNotifications();
|
|
|
|
api.collab.get("members/list?pending=1", function (err, members) {
|
|
if (err && (err.code === 0 || err.code === 503)) {
|
|
// Server still starting or CORS error; retry
|
|
return setTimeout(loadNotifications, 20000);
|
|
}
|
|
|
|
if (err) return showError(err);
|
|
|
|
if (Array.isArray(members)) {
|
|
var notifs = members.map(function(m) {
|
|
m.type = "access_request";
|
|
return m;
|
|
});
|
|
cachedNotifications = [];
|
|
addNotifications(notifs);
|
|
postLoadedNotifications();
|
|
}
|
|
});
|
|
}
|
|
|
|
function resize() {
|
|
var count = cachedNotifications.length;
|
|
|
|
if (!bubble) {
|
|
// Make sure collab panel is enabled if we have notifications
|
|
if (!panels.panels.collab.enabled && count)
|
|
panels.enablePanel("collab");
|
|
|
|
// Notification Bubble
|
|
panelButton = document.querySelector(".panelsbutton.collab");
|
|
if (panelButton) {
|
|
bubble = panelButton.appendChild(document.createElement("div"));
|
|
bubble.className = "newnotifs";
|
|
}
|
|
}
|
|
|
|
if (bubble) {
|
|
if (count) {
|
|
bubble.innerHTML = count;
|
|
bubble.style.display = "block";
|
|
bubble.className = "newnotifs size" + String(count).length;
|
|
} else {
|
|
bubble.style.display = "none";
|
|
}
|
|
}
|
|
|
|
if (drawn) {
|
|
if (frame.state[0] != "m") {
|
|
frame.$ext.style.height = count
|
|
? Math.min(count, 3) * 50 + 22 + "px"
|
|
: 50 + "px";
|
|
notificationsTree.resize();
|
|
} else {
|
|
frame.$ext.style.height = "0";
|
|
}
|
|
}
|
|
}
|
|
|
|
function postLoadedNotifications() {
|
|
resize();
|
|
if (!drawn)
|
|
return;
|
|
notificationsDataProvider.emptyMessage = "No pending notifications";
|
|
frame.setAttribute("caption",
|
|
"Notifications (" + cachedNotifications.length + ")");
|
|
|
|
onNotificationsLoaded();
|
|
}
|
|
|
|
function addNotifications(notifs) {
|
|
if (!Array.isArray(notifs))
|
|
notifs = [notifs];
|
|
|
|
if (frame)
|
|
frame.restore();
|
|
|
|
notifs.forEach(function(notif) {
|
|
var NotifConstructor = NOTIFICATION_TYPES[notif.type];
|
|
if (!NotifConstructor)
|
|
console.error("Invalid notification type:", notif.type);
|
|
cachedNotifications.push(new NotifConstructor(notif));
|
|
});
|
|
if (notificationsDataProvider)
|
|
notificationsDataProvider.setRoot(cachedNotifications);
|
|
}
|
|
|
|
function onNotificationsLoaded() {
|
|
if (frame && cachedNotifications.length)
|
|
frame.restore();
|
|
}
|
|
|
|
|
|
function isOpen() {
|
|
return panels.isActive("collab") && frame && frame.state[0] != "m";
|
|
}
|
|
|
|
/***** Notification Object *****/
|
|
|
|
function Notification(datarow) {
|
|
this.datarow = datarow;
|
|
}
|
|
|
|
(function () {
|
|
this.getHTML = function (datarow) {
|
|
throw new Error("No impl found - getHTML");
|
|
};
|
|
|
|
this.handleMouseDown = function () {
|
|
throw new Error("No impl found - handleMouseDown");
|
|
};
|
|
|
|
this.remove = function () {
|
|
var _self = this;
|
|
cachedNotifications = cachedNotifications.filter(function (notif) {
|
|
return notif !== _self;
|
|
});
|
|
notificationsDataProvider.setRoot(cachedNotifications);
|
|
postLoadedNotifications();
|
|
};
|
|
}).call(Notification.prototype);
|
|
|
|
function AccessRequestNotification(datarow) {
|
|
datarow.md5Email = datarow.email ? apf.crypto.MD5.hex_md5(datarow.email.trim().toLowerCase()) : "";
|
|
this.datarow = datarow;
|
|
}
|
|
|
|
oop.inherits(AccessRequestNotification, Notification);
|
|
|
|
(function () {
|
|
this.getHTML = function () {
|
|
var datarow = this.datarow;
|
|
var avatarImg = '<img class="gravatar-image" src="https://secure.gravatar.com/avatar/' +
|
|
datarow.md5Email + '?s=37&d=retro" />';
|
|
var html = [
|
|
"<span class='avatar'>", avatarImg, "</span>",
|
|
"<span class='body'>", "<span class='caption'>", datarow.name, "</span>",
|
|
" requests access to this workspace</span>",
|
|
"<span class='actions access_request'>",
|
|
'<div class="standalone access_control rw">',
|
|
'<div class="readbutton">R</div><div class="writebutton">RW</div>',
|
|
'</div>',
|
|
'<div class="btn-default-css3 btn-green grant">',
|
|
'<div class="caption">Grant</div>',
|
|
'</div>',
|
|
'<div class="btn-default-css3 btn-red deny">',
|
|
'<div class="caption">Deny</div>',
|
|
'</div>',
|
|
"</span>"
|
|
];
|
|
|
|
return html.join("");
|
|
};
|
|
|
|
this.acceptRequest = function (access) {
|
|
var _self = this;
|
|
if (!options.hosted)
|
|
return requestAccepted();
|
|
|
|
var datarow = this.datarow;
|
|
var uid = datarow.uid;
|
|
api.collab.post("accept_request", {
|
|
body: {
|
|
uid: uid,
|
|
access: access
|
|
}
|
|
}, function (err, data, res) {
|
|
if (err) {
|
|
var message = err.message || err;
|
|
var user = workspace.getUser(uid);
|
|
if (user && !user.pending || / isn't a pending workspace member/.test(message)) {
|
|
return _self.remove(); // it was already handled ignore
|
|
}
|
|
return alert("Error", message);
|
|
}
|
|
requestAccepted();
|
|
});
|
|
function requestAccepted() {
|
|
datarow.acl = access;
|
|
workspace.addMemberNonPubSub(datarow);
|
|
_self.remove();
|
|
}
|
|
};
|
|
|
|
this.denyRequest = function () {
|
|
var _self = this;
|
|
if (!options.hosted)
|
|
return requestDenied();
|
|
|
|
var uid = this.datarow.uid;
|
|
api.collab.post("deny_request", {
|
|
body: {
|
|
uid: uid
|
|
}
|
|
}, function (err, data, res) {
|
|
if (err) {
|
|
var message = err.message || err;
|
|
var user = workspace.getUser(uid);
|
|
if (!user || / isn't a member of project /.test(message)) {
|
|
return _self.remove(); // it was already handled ignore
|
|
}
|
|
return alert("Error", message);
|
|
}
|
|
requestDenied();
|
|
});
|
|
function requestDenied() {
|
|
_self.remove();
|
|
}
|
|
};
|
|
|
|
this.handleMouseDown = function (e) {
|
|
var target = e.domEvent.target;
|
|
if (e.domEvent.button)
|
|
return;
|
|
var className = target.classList;
|
|
if (className.contains("access_control")) {
|
|
var actionArr = className.contains("rw") ? ["rw", "r"] : ["r", "rw"];
|
|
className.remove(actionArr[0]);
|
|
className.add(actionArr[1]);
|
|
return;
|
|
}
|
|
};
|
|
|
|
this.handleMouseUp = function (e) {
|
|
var target = e.domEvent.target;
|
|
var className = target.classList;
|
|
if (e.domEvent.button)
|
|
return;
|
|
if (e.editor.$mouseHandler.isMousePressed)
|
|
return;
|
|
if (className.contains("grant")) {
|
|
var rwClassName = target.previousSibling.classList;
|
|
var access = rwClassName.contains("rw") ? "rw" : "r";
|
|
this.acceptRequest(access);
|
|
}
|
|
else if (className.contains("deny")) {
|
|
this.denyRequest();
|
|
}
|
|
};
|
|
}).call(AccessRequestNotification.prototype);
|
|
|
|
NOTIFICATION_TYPES["access_request"] = AccessRequestNotification;
|
|
|
|
/***** Lifecycle *****/
|
|
|
|
plugin.on("load", function() {
|
|
load();
|
|
plugin.once("draw", draw);
|
|
});
|
|
plugin.on("enable", function() {
|
|
|
|
});
|
|
plugin.on("disable", function() {
|
|
});
|
|
|
|
plugin.on("unload", function() {
|
|
loaded = false;
|
|
drawn = false;
|
|
cachedNotifications = [];
|
|
if (notificationsTree)
|
|
notificationsTree.destroy();
|
|
notificationsDataProvider = null;
|
|
notificationsTree = null;
|
|
notificationsParent = null;
|
|
frame = null;
|
|
panelButton = null;
|
|
bubble = null;
|
|
});
|
|
|
|
/***** Register and define API *****/
|
|
|
|
/**
|
|
* Adds File->New File and File->New Folder menu items as well as the
|
|
* commands for opening a new file as well as an API.
|
|
* @singleton
|
|
**/
|
|
plugin.freezePublicAPI({
|
|
addNotifications: addNotifications
|
|
});
|
|
|
|
register(null, {
|
|
notifications: plugin
|
|
});
|
|
}
|
|
|
|
}); |