mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-25 16:22:43 +01:00
Make Notifications Realtime
Summary: Adds the node.js Aphlict server, the flash Aphlict client, and some supporting javascript. Built on top of - and requires - D2703 (which is still in progress). Will likely work with no modification on top of the final version, though. The node server is currently run with sudo node support/aphlict/server/aphlict_server.js Test Plan: tested locally Reviewers: epriestley Reviewed By: epriestley CC: allenjohnashton, keebuhm, aran, Korvin Differential Revision: https://secure.phabricator.com/D2704
This commit is contained in:
parent
2bade93b76
commit
f8f195b329
13 changed files with 235 additions and 138 deletions
|
@ -772,7 +772,7 @@ celerity_register_resource_map(array(
|
||||||
),
|
),
|
||||||
'javelin-behavior-aphlict-listen' =>
|
'javelin-behavior-aphlict-listen' =>
|
||||||
array(
|
array(
|
||||||
'uri' => '/res/6388e057/rsrc/js/application/aphlict/behavior-aphlict-listen.js',
|
'uri' => '/res/7f4bc63b/rsrc/js/application/aphlict/behavior-aphlict-listen.js',
|
||||||
'type' => 'js',
|
'type' => 'js',
|
||||||
'requires' =>
|
'requires' =>
|
||||||
array(
|
array(
|
||||||
|
@ -780,6 +780,7 @@ celerity_register_resource_map(array(
|
||||||
1 => 'javelin-aphlict',
|
1 => 'javelin-aphlict',
|
||||||
2 => 'javelin-util',
|
2 => 'javelin-util',
|
||||||
3 => 'javelin-stratcom',
|
3 => 'javelin-stratcom',
|
||||||
|
4 => 'javelin-behavior-aphlict-dropdown',
|
||||||
),
|
),
|
||||||
'disk' => '/rsrc/js/application/aphlict/behavior-aphlict-listen.js',
|
'disk' => '/rsrc/js/application/aphlict/behavior-aphlict-listen.js',
|
||||||
),
|
),
|
||||||
|
|
|
@ -736,6 +736,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorMySQLFileStorageEngine' => 'applications/files/engine/PhabricatorMySQLFileStorageEngine.php',
|
'PhabricatorMySQLFileStorageEngine' => 'applications/files/engine/PhabricatorMySQLFileStorageEngine.php',
|
||||||
'PhabricatorNotificationBuilder' => 'applications/notification/builder/PhabricatorNotificationBuilder.php',
|
'PhabricatorNotificationBuilder' => 'applications/notification/builder/PhabricatorNotificationBuilder.php',
|
||||||
'PhabricatorNotificationController' => 'applications/notification/controller/PhabricatorNotificationController.php',
|
'PhabricatorNotificationController' => 'applications/notification/controller/PhabricatorNotificationController.php',
|
||||||
|
'PhabricatorNotificationIndividualController' => 'applications/notification/controller/PhabricatorNotificationIndividualController.php',
|
||||||
'PhabricatorNotificationPanelController' => 'applications/notification/controller/PhabricatorNotificationPanelController.php',
|
'PhabricatorNotificationPanelController' => 'applications/notification/controller/PhabricatorNotificationPanelController.php',
|
||||||
'PhabricatorNotificationQuery' => 'applications/notification/PhabricatorNotificationQuery.php',
|
'PhabricatorNotificationQuery' => 'applications/notification/PhabricatorNotificationQuery.php',
|
||||||
'PhabricatorNotificationStoryView' => 'applications/notification/view/PhabricatorNotificationStoryView.php',
|
'PhabricatorNotificationStoryView' => 'applications/notification/view/PhabricatorNotificationStoryView.php',
|
||||||
|
@ -1698,6 +1699,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorMustVerifyEmailController' => 'PhabricatorAuthController',
|
'PhabricatorMustVerifyEmailController' => 'PhabricatorAuthController',
|
||||||
'PhabricatorMySQLFileStorageEngine' => 'PhabricatorFileStorageEngine',
|
'PhabricatorMySQLFileStorageEngine' => 'PhabricatorFileStorageEngine',
|
||||||
'PhabricatorNotificationController' => 'PhabricatorController',
|
'PhabricatorNotificationController' => 'PhabricatorController',
|
||||||
|
'PhabricatorNotificationIndividualController' => 'PhabricatorNotificationController',
|
||||||
'PhabricatorNotificationPanelController' => 'PhabricatorNotificationController',
|
'PhabricatorNotificationPanelController' => 'PhabricatorNotificationController',
|
||||||
'PhabricatorNotificationStoryView' => 'PhabricatorNotificationView',
|
'PhabricatorNotificationStoryView' => 'PhabricatorNotificationView',
|
||||||
'PhabricatorNotificationTestController' => 'PhabricatorNotificationController',
|
'PhabricatorNotificationTestController' => 'PhabricatorNotificationController',
|
||||||
|
|
|
@ -423,6 +423,8 @@ class AphrontDefaultApplicationConfiguration
|
||||||
|
|
||||||
'/notification/test/' => 'PhabricatorNotificationTestController',
|
'/notification/test/' => 'PhabricatorNotificationTestController',
|
||||||
'/notification/panel/' => 'PhabricatorNotificationPanelController',
|
'/notification/panel/' => 'PhabricatorNotificationPanelController',
|
||||||
|
'/notification/individual/'
|
||||||
|
=> 'PhabricatorNotificationIndividualController',
|
||||||
'/flag/' => array(
|
'/flag/' => array(
|
||||||
'' => 'PhabricatorFlagListController',
|
'' => 'PhabricatorFlagListController',
|
||||||
'view/(?P<view>[^/]+)/' => 'PhabricatorFlagListController',
|
'view/(?P<view>[^/]+)/' => 'PhabricatorFlagListController',
|
||||||
|
|
|
@ -98,6 +98,7 @@ final class PhabricatorFeedStoryPublisher {
|
||||||
|
|
||||||
if (PhabricatorEnv::getEnvConfig('notification.enabled')) {
|
if (PhabricatorEnv::getEnvConfig('notification.enabled')) {
|
||||||
$this->insertNotifications($chrono_key);
|
$this->insertNotifications($chrono_key);
|
||||||
|
$this->sendNotification($chrono_key);
|
||||||
}
|
}
|
||||||
return $story;
|
return $story;
|
||||||
}
|
}
|
||||||
|
@ -136,6 +137,17 @@ final class PhabricatorFeedStoryPublisher {
|
||||||
implode(', ', $sql));
|
implode(', ', $sql));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function sendNotification($chrono_key) {
|
||||||
|
$aphlict_url = 'http://127.0.0.1:22281/push?'; //TODO: make configurable
|
||||||
|
$future = new HTTPFuture($aphlict_url, array(
|
||||||
|
"key" => (string)$chrono_key,
|
||||||
|
// TODO: fix. \r\n appears to be appended to the final value here.
|
||||||
|
// this is a temporary workaround
|
||||||
|
"nothing" => "",
|
||||||
|
));
|
||||||
|
$future->setMethod('POST');
|
||||||
|
$future->resolve();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We generate a unique chronological key for each story type because we want
|
* We generate a unique chronological key for each story type because we want
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2012 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
final class PhabricatorNotificationIndividualController
|
||||||
|
extends PhabricatorNotificationController {
|
||||||
|
|
||||||
|
public function processRequest() {
|
||||||
|
$request = $this->getRequest();
|
||||||
|
$user = $request->getUser();
|
||||||
|
|
||||||
|
$chron_key = $request->getStr('key');
|
||||||
|
$story = id(new PhabricatorFeedStoryNotification())
|
||||||
|
->loadOneWhere('userPHID = %s AND chronologicalKey = %s',
|
||||||
|
$user->getPHID(),
|
||||||
|
$chron_key);
|
||||||
|
|
||||||
|
if ($story == null) {
|
||||||
|
$json = array( "pertinent" => false );
|
||||||
|
} else {
|
||||||
|
$json = array(
|
||||||
|
"pertinent" => true,
|
||||||
|
"primaryObjectPHID" => $story->getPrimaryObjectPHID(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return id(new AphrontAjaxResponse())->setContent($json);
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,10 +33,18 @@ final class PhabricatorNotificationPanelController
|
||||||
$builder = new PhabricatorNotificationBuilder($stories);
|
$builder = new PhabricatorNotificationBuilder($stories);
|
||||||
$notifications_view = $builder->buildView();
|
$notifications_view = $builder->buildView();
|
||||||
|
|
||||||
|
$num_unconsumed = 0;
|
||||||
|
foreach ($stories as $story) {
|
||||||
|
if (!$story->getHasViewed()) {
|
||||||
|
$num_unconsumed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$json = array(
|
$json = array(
|
||||||
"content" => $stories ?
|
"content" => $stories ?
|
||||||
$notifications_view->render() :
|
$notifications_view->render() :
|
||||||
"<b>You currently have no notifications<b>",
|
"<b>You currently have no notifications<b>",
|
||||||
|
"number" => $num_unconsumed,
|
||||||
);
|
);
|
||||||
|
|
||||||
return id(new AphrontAjaxResponse())->setContent($json);
|
return id(new AphrontAjaxResponse())->setContent($json);
|
||||||
|
|
|
@ -376,18 +376,19 @@ final class PhabricatorStandardPageView extends AphrontPageView {
|
||||||
|
|
||||||
if (PhabricatorEnv::getEnvConfig('notification.enabled') &&
|
if (PhabricatorEnv::getEnvConfig('notification.enabled') &&
|
||||||
$user->isLoggedIn()) {
|
$user->isLoggedIn()) {
|
||||||
|
|
||||||
$aphlict_object_id = 'aphlictswfobject';
|
$aphlict_object_id = 'aphlictswfobject';
|
||||||
|
|
||||||
$aphlict_content = phutil_render_tag(
|
$server_uri = new PhutilURI(PhabricatorEnv::getURI(''));
|
||||||
'object',
|
$server_domain = $server_uri->getDomain();
|
||||||
|
|
||||||
|
Javelin::initBehavior(
|
||||||
|
'aphlict-listen',
|
||||||
array(
|
array(
|
||||||
'classid' => 'clsid:d27cdb6e-ae6d-11cf-96b8-444553540000',
|
'id' => $aphlict_object_id,
|
||||||
),
|
'server' => $server_domain,
|
||||||
'<param name="movie" value="/rsrc/swf/aphlict.swf" />'.
|
'port' => 2600,
|
||||||
'<param name="allowScriptAccess" value="always" />'.
|
));
|
||||||
'<param name="wmode" value="opaque" />'.
|
|
||||||
'<embed src="/rsrc/swf/aphlict.swf" wmode="opaque" id="'.
|
|
||||||
$aphlict_object_id.'"></embed>');
|
|
||||||
|
|
||||||
Javelin::initBehavior('aphlict-dropdown', array());
|
Javelin::initBehavior('aphlict-dropdown', array());
|
||||||
|
|
||||||
|
@ -405,8 +406,7 @@ final class PhabricatorStandardPageView extends AphrontPageView {
|
||||||
$notification_header =
|
$notification_header =
|
||||||
$notification_indicator.
|
$notification_indicator.
|
||||||
'<td>'.
|
'<td>'.
|
||||||
'<div style="height:1px; width:1px;">'.
|
'<div id="aphlictswf-container" style="height:1px; width:1px;">'.
|
||||||
$aphlict_content.
|
|
||||||
'</div>'.
|
'</div>'.
|
||||||
'</td>';
|
'</td>';
|
||||||
$notification_dropdown =
|
$notification_dropdown =
|
||||||
|
|
|
@ -7,23 +7,12 @@ package {
|
||||||
import flash.events.*;
|
import flash.events.*;
|
||||||
import flash.external.ExternalInterface;
|
import flash.external.ExternalInterface;
|
||||||
|
|
||||||
import com.phabricator.*;
|
|
||||||
|
|
||||||
import vegas.strings.JSON;
|
import vegas.strings.JSON;
|
||||||
|
|
||||||
public class Aphlict extends Sprite {
|
public class Aphlict extends Sprite {
|
||||||
|
|
||||||
private var client:String;
|
private var client:String;
|
||||||
|
|
||||||
private var master:LocalConnection;
|
|
||||||
private var recv:LocalConnection;
|
|
||||||
private var send:LocalConnection;
|
|
||||||
|
|
||||||
private var receiver:AphlictReceiver;
|
|
||||||
private var loyalUntil:Number = 0;
|
|
||||||
private var subjects:Array;
|
|
||||||
private var frequency:Number = 100;
|
|
||||||
|
|
||||||
private var socket:Socket;
|
private var socket:Socket;
|
||||||
private var readBuffer:ByteArray;
|
private var readBuffer:ByteArray;
|
||||||
|
|
||||||
|
@ -47,60 +36,10 @@ package {
|
||||||
this.remoteServer = server;
|
this.remoteServer = server;
|
||||||
this.remotePort = port;
|
this.remotePort = port;
|
||||||
|
|
||||||
this.master = null;
|
this.connectToServer();
|
||||||
this.receiver = new AphlictReceiver(this);
|
return;
|
||||||
this.subjects = [];
|
|
||||||
|
|
||||||
this.send = new LocalConnection();
|
|
||||||
|
|
||||||
this.recv = new LocalConnection();
|
|
||||||
this.recv.client = this.receiver;
|
|
||||||
for (var ii:Number = 0; ii < 32; ii++) {
|
|
||||||
try {
|
|
||||||
this.recv.connect('aphlict_subject_' + ii);
|
|
||||||
this.client = 'aphlict_subject_' + ii;
|
|
||||||
} catch (x:Error) {
|
|
||||||
// Some other Aphlict client is holding that ID.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.client) {
|
|
||||||
// Too many clients open already, just exit.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.usurp();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function usurp():void {
|
|
||||||
if (this.master) {
|
|
||||||
for (var ii:Number = 0; ii < this.subjects.length; ii++) {
|
|
||||||
if (this.subjects[ii] == this.client) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
this.send.send(this.subjects[ii], 'remainLoyal');
|
|
||||||
}
|
|
||||||
} else if (this.loyalUntil < new Date().getTime()) {
|
|
||||||
var recv:LocalConnection = new LocalConnection();
|
|
||||||
recv.client = this.receiver;
|
|
||||||
try {
|
|
||||||
recv.connect('aphlict_master');
|
|
||||||
this.master = recv;
|
|
||||||
this.subjects = [this.client];
|
|
||||||
|
|
||||||
this.connectToServer();
|
|
||||||
|
|
||||||
} catch (x:Error) {
|
|
||||||
// Can't become the master.
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.master) {
|
|
||||||
this.send.send('aphlict_master', 'becomeLoyal', this.client);
|
|
||||||
this.remainLoyal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setTimeout(this.usurp, this.frequency);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function connectToServer():void {
|
public function connectToServer():void {
|
||||||
var socket:Socket = new Socket();
|
var socket:Socket = new Socket();
|
||||||
|
@ -156,9 +95,7 @@ package {
|
||||||
t.writeBytes(b, msg_len + 8);
|
t.writeBytes(b, msg_len + 8);
|
||||||
this.readBuffer = t;
|
this.readBuffer = t;
|
||||||
|
|
||||||
for (var ii:Number = 0; ii < this.subjects.length; ii++) {
|
this.receiveMessage(data);
|
||||||
this.send.send(this.subjects[ii], 'receiveMessage', data);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -166,14 +103,6 @@ package {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function remainLoyal():void {
|
|
||||||
this.loyalUntil = new Date().getTime() + (2 * this.frequency);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function becomeLoyal(subject:String):void {
|
|
||||||
this.subjects.push(subject);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function receiveMessage(msg:Object):void {
|
public function receiveMessage(msg:Object):void {
|
||||||
this.externalInvoke('receive', msg);
|
this.externalInvoke('receive', msg);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
package com.phabricator {
|
|
||||||
|
|
||||||
public class AphlictReceiver {
|
|
||||||
|
|
||||||
private var core:Object;
|
|
||||||
|
|
||||||
public function AphlictReceiver(core:Object) {
|
|
||||||
this.core = core;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function remainLoyal():void {
|
|
||||||
this.core.remainLoyal();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function becomeLoyal(subject:String):void {
|
|
||||||
this.core.becomeLoyal(subject);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function receiveMessage(msg:Object):void {
|
|
||||||
this.core.receiveMessage(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,4 +1,21 @@
|
||||||
var net = require('net');
|
var net = require('net');
|
||||||
|
var http = require('http');
|
||||||
|
var url = require('url');
|
||||||
|
var querystring = require('querystring');
|
||||||
|
var fs = require('fs');
|
||||||
|
|
||||||
|
// set up log file
|
||||||
|
logfile = fs.createWriteStream('/var/log/aphlict.log',
|
||||||
|
{ flags: 'a',
|
||||||
|
encoding: null,
|
||||||
|
mode: 0666 });
|
||||||
|
logfile.write('----- ' + (new Date()).toLocaleString() + ' -----\n');
|
||||||
|
|
||||||
|
function log(str) {
|
||||||
|
console.log(str);
|
||||||
|
logfile.write(str + '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function getFlashPolicy() {
|
function getFlashPolicy() {
|
||||||
return [
|
return [
|
||||||
|
@ -8,35 +25,113 @@ function getFlashPolicy() {
|
||||||
'<cross-domain-policy>',
|
'<cross-domain-policy>',
|
||||||
'<allow-access-from domain="*" to-ports="2600"/>',
|
'<allow-access-from domain="*" to-ports="2600"/>',
|
||||||
'</cross-domain-policy>'
|
'</cross-domain-policy>'
|
||||||
].join("\n");
|
].join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
net.createServer(function(socket) {
|
net.createServer(function(socket) {
|
||||||
socket.on('data', function() {
|
socket.on('data', function() {
|
||||||
socket.write(getFlashPolicy() + '\0');
|
socket.write(getFlashPolicy() + '\0');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
socket.on('error', function (e) {
|
||||||
|
log('Error in policy server: ' + e);
|
||||||
|
});
|
||||||
}).listen(843);
|
}).listen(843);
|
||||||
|
|
||||||
var sp_server = net.createServer(function(socket) {
|
|
||||||
function xwrite() {
|
|
||||||
var data = {hi: "hello"};
|
|
||||||
var serial = JSON.stringify(data);
|
|
||||||
|
|
||||||
var length = Buffer.byteLength(serial, 'utf8');
|
|
||||||
length = length.toString();
|
|
||||||
while (length.length < 8) {
|
|
||||||
length = "0" + length;
|
|
||||||
}
|
|
||||||
|
|
||||||
socket.write(length + serial);
|
function write_json(socket, data) {
|
||||||
|
var serial = JSON.stringify(data);
|
||||||
console.log('write : ' + length + serial);
|
var length = Buffer.byteLength(serial, 'utf8');
|
||||||
|
length = length.toString();
|
||||||
|
while (length.length < 8) {
|
||||||
|
length = '0' + length;
|
||||||
}
|
}
|
||||||
|
socket.write(length + serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var clients = {};
|
||||||
|
var current_connections = 0;
|
||||||
|
// According to the internet up to 2^53 can
|
||||||
|
// be stored in javascript, this is less than that
|
||||||
|
var MAX_ID = 9007199254740991;//2^53 -1
|
||||||
|
|
||||||
|
// If we get one connections per millisecond this will
|
||||||
|
// be fine as long as someone doesn't maintain a
|
||||||
|
// connection for longer than 6854793 years. If
|
||||||
|
// you want to write something pretty be my guest
|
||||||
|
|
||||||
|
function generate_id() {
|
||||||
|
if (typeof generate_id.current_id == 'undefined'
|
||||||
|
|| generate_id.current_id > MAX_ID) {
|
||||||
|
generate_id.current_id = 0;
|
||||||
|
}
|
||||||
|
return generate_id.current_id++;
|
||||||
|
}
|
||||||
|
|
||||||
|
var send_server = net.createServer(function(socket) {
|
||||||
|
var client_id = generate_id();
|
||||||
|
|
||||||
socket.on('connect', function() {
|
socket.on('connect', function() {
|
||||||
|
clients[client_id] = socket;
|
||||||
|
current_connections++;
|
||||||
|
log(client_id + ': connected\t\t('
|
||||||
|
+ current_connections + ' current connections)');
|
||||||
|
});
|
||||||
|
|
||||||
xwrite();
|
socket.on('close', function() {
|
||||||
setInterval(xwrite, 1000);
|
delete clients[client_id];
|
||||||
|
current_connections--;
|
||||||
|
log(client_id + ': closed\t\t('
|
||||||
|
+ current_connections + ' current connections)');
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('timeout', function() {
|
||||||
|
log(client_id + ': timed out!');
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('end', function() {
|
||||||
|
log(client_id + ': ended the connection');
|
||||||
|
// node automatically closes half-open connections
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('error', function (e) {
|
||||||
|
console.log('Uncaught error in send server: ' + e);
|
||||||
});
|
});
|
||||||
}).listen(2600);
|
}).listen(2600);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var receive_server = http.createServer(function(request, response) {
|
||||||
|
response.writeHead(200, {'Content-Type' : 'text/plain'});
|
||||||
|
|
||||||
|
if (request.method == 'POST') { // Only pay attention to POST requests
|
||||||
|
var body = '';
|
||||||
|
|
||||||
|
request.on('data', function (data) {
|
||||||
|
body += data;
|
||||||
|
});
|
||||||
|
|
||||||
|
request.on('end', function () {
|
||||||
|
var data = querystring.parse(body);
|
||||||
|
log('notification: ' + JSON.stringify(data));
|
||||||
|
broadcast(data);
|
||||||
|
response.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).listen(22281, '127.0.0.1');
|
||||||
|
|
||||||
|
function broadcast(data) {
|
||||||
|
for(var client_id in clients) {
|
||||||
|
try {
|
||||||
|
write_json(clients[client_id], data);
|
||||||
|
log(' wrote to client ' + client_id);
|
||||||
|
} catch (error) {
|
||||||
|
delete clients[client_id];
|
||||||
|
current_connections--;
|
||||||
|
log(' ERROR: could not write to client ' + client_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,13 +11,28 @@ JX.behavior('aphlict-dropdown', function(config) {
|
||||||
var dropdown = JX.$('phabricator-notification-dropdown');
|
var dropdown = JX.$('phabricator-notification-dropdown');
|
||||||
var indicator = JX.$('phabricator-notification-indicator');
|
var indicator = JX.$('phabricator-notification-indicator');
|
||||||
var visible = false;
|
var visible = false;
|
||||||
|
var request = null;
|
||||||
|
|
||||||
|
function refresh() {
|
||||||
|
if (request) { //already fetching
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
request = new JX.Request('/notification/panel/', function(response) {
|
||||||
|
indicator.textContent = '' + response.number;
|
||||||
|
if (response.number == 0) {
|
||||||
|
indicator.style.fontWeight = "";
|
||||||
|
} else {
|
||||||
|
indicator.style.fontWeight = "bold";
|
||||||
|
}
|
||||||
|
JX.DOM.setContent(dropdown, JX.$H(response.content));
|
||||||
|
request = null;
|
||||||
|
});
|
||||||
|
request.send();
|
||||||
|
}
|
||||||
|
|
||||||
//populate panel
|
//populate panel
|
||||||
(new JX.Request('/notification/panel/',
|
refresh();
|
||||||
function(response) {
|
|
||||||
JX.DOM.setContent(dropdown, JX.$H(response.content));
|
|
||||||
})).send();
|
|
||||||
|
|
||||||
|
|
||||||
JX.Stratcom.listen(
|
JX.Stratcom.listen(
|
||||||
'click',
|
'click',
|
||||||
|
@ -48,4 +63,5 @@ JX.behavior('aphlict-dropdown', function(config) {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
JX.Stratcom.listen('notification-panel-update', null, refresh);
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,19 +4,24 @@
|
||||||
* javelin-aphlict
|
* javelin-aphlict
|
||||||
* javelin-util
|
* javelin-util
|
||||||
* javelin-stratcom
|
* javelin-stratcom
|
||||||
|
* javelin-behavior-aphlict-dropdown
|
||||||
*/
|
*/
|
||||||
|
|
||||||
JX.behavior('aphlict-listen', function(config) {
|
JX.behavior('aphlict-listen', function(config) {
|
||||||
function onready() {
|
function onready() {
|
||||||
JX.log("The flash component is ready!");
|
|
||||||
|
|
||||||
var client = new JX.Aphlict(config.id, config.server, config.port)
|
var client = new JX.Aphlict(config.id, config.server, config.port)
|
||||||
.setHandler(function(type, message) {
|
.setHandler(function(type, message) {
|
||||||
if (message) {
|
if (message) {
|
||||||
JX.log("Got aphlict event '" + type + "':");
|
if (type == 'receive') {
|
||||||
JX.log(message);
|
var request = new JX.Request('/notification/individual/',
|
||||||
} else {
|
function(response) {
|
||||||
JX.log("Got aphlict event '" + type + "'.");
|
if (response.pertinent) {
|
||||||
|
JX.Stratcom.invoke('notification-panel-update', null, {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
request.addData({ "key": message.key });
|
||||||
|
request.send();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.start();
|
.start();
|
||||||
|
@ -27,4 +32,13 @@ JX.behavior('aphlict-listen', function(config) {
|
||||||
// If we just go crazy and start making calls to it before it loads, its
|
// If we just go crazy and start making calls to it before it loads, its
|
||||||
// interfaces won't be registered yet.
|
// interfaces won't be registered yet.
|
||||||
JX.Stratcom.listen('aphlict-component-ready', null, onready);
|
JX.Stratcom.listen('aphlict-component-ready', null, onready);
|
||||||
|
|
||||||
|
// Add Flash object to page
|
||||||
|
JX.$("aphlictswf-container").innerHTML =
|
||||||
|
'<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000">'
|
||||||
|
+ '<param name="movie" value="/rsrc/swf/aphlict.swf" />'
|
||||||
|
+ '<param name="allowScriptAccess" value="always" />'
|
||||||
|
+ '<param name="wmode" value="opaque" />'
|
||||||
|
+ '<embed src="/rsrc/swf/aphlict.swf" wmode="opaque" id="aphlictswfobject">'
|
||||||
|
+ '</embed></object>'; //Evan sanctioned
|
||||||
});
|
});
|
||||||
|
|
Binary file not shown.
Loading…
Reference in a new issue