浏览器 extension 插件开发系列(14) -- 点击reply出现回复小窗口

前言

通过 浏览器 extension 插件开发系列(13) -- 实现消息过来出现桌面通知 我们知道在收到文本消息,或者文件消息的时候,这时候会有一个回复操作。点击,就会弹出一个回复小窗口

png

png

然后输入要回复的消息,按enter直接回复。 所以我们要在打开这个reply窗体的时候,还要把这一条消息的内容传过去。 接下来我们讲讲怎么实现这个过程。

打开 reply 页面

打开回复窗口的代码在 notificationsManage.js 中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// 打开回复窗口
openQuickReply: function(obj){
// 打开回复窗口
var spec = {
'url': 'reply.html?webuid=' + obj.webuid,
'width': 320,
'height': 420
};

var lastScreenX = Airdroid.Util.LocalStorage.getLocalStorageItem('quickReplyScreenX');
var lastScreenY = Airdroid.Util.LocalStorage.getLocalStorageItem('quickReplyScreenY');
if (lastScreenX && lastScreenY) {
spec.top = parseInt(lastScreenY);
spec.left = parseInt(lastScreenX);
} else {
spec.top = Math.floor((window.screen.availHeight / 2) - (spec.height / 2)) - 100;
spec.left = Math.floor((window.screen.availWidth / 2) - (spec.width / 2)) + 100;
}

if (window.chrome) {
spec.type = 'popup';
spec.focused = true;

var listener = function(message, sender, sendResponse) {
chrome.runtime.onMessage.removeListener(listener);

if (message.webuid == obj.webuid) {
sendResponse(obj);
}
};

chrome.runtime.onMessage.addListener(listener);

chrome.windows.create(spec, function(window) {
chrome.windows.update(window.id, { 'focused': true }, function() {
});
});
} else if (window.safari) {
var listener = function(e) {
if (e.name == 'request_conversation_push') {
// 这边要去掉里面的数组
delete obj.buttons;
console.log("接受其他页面的数据请求==>" + JSON.stringify(obj));
e.target.page.dispatchMessage('conversation_push', JSON.stringify(obj));
safari.application.removeEventListener('message', listener, false);
}
};

safari.application.addEventListener('message', listener, false);

var w = safari.application.openBrowserWindow();
w.activeTab.url = safari.extension.baseURI + spec.url + '&width=' + spec.width + '&height=' + spec.height;
} else {
spec.notification = obj;
// firefox
Airdroid.Event.dispatchEvent(Airdroid.Event.FireFoxEvent.open_quickreply, spec);
}
},

可以看到不同浏览器的打开处理方式又是不一样的。这个稍后分析,反正实现的效果肯定是一样。 当打开 reply.html 这个页面之后,接下来就进行一些逻辑操作,主要是在 reply.js 上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
(function () {
console.log("start reply");

function getUrlParam (key, url) {
key = key
.replace(/[\[]/, "\\\[")
.replace(/[\]]/, "\\\]");

var r = new RegExp("[\\?&]" + key + "=([^&#]*)");
var values = r.exec(url || window.location.href);
return values == null ? "" : decodeURIComponent(values[1]);
}

if(getUrlParam("height")){
window.resizeTo(parseInt(getUrlParam("width")), parseInt(getUrlParam("height")));
}
// 主程序
var Reply = BasePage.extend({
// 绑定事件
events: {
"keydown #reply": "replyHandle"
},
// 初始化程序
afterInit: function () {
var self = this;
$("body").prepend(new Tpl('reply/reply').render());
// 获取消息的id
var webuid = getUrlParam("webuid");

if (window.chrome) {
chrome.runtime.sendMessage({ 'webuid': webuid }, function(response) {
self.render(response);
});
} else if (window.safari) {
var listener = function(e) {
if (e.name == 'conversation_push') {
self.render(JSON.parse(e.message));
safari.self.removeEventListener('message', listener, false);
}
};
safari.self.addEventListener('message', listener, false);
safari.self.tab.dispatchMessage('request_conversation_push');
} else {
// 触发请求数据事件
self.eventObj.emit('request_conversation_push');
self.eventObj.on('conversation_push', function(push) {
self.render(push);
});
}
},
// 渲染界面
render: function(obj){
this.options = obj;
document.getElementById('container').style.display = 'block';

document.getElementById('title').textContent = obj.title;
document.getElementById('desc').textContent = 'Via AirDroid';

var message = document.getElementById('message');
message.textContent = obj.message;
message.scrollTop = message.scrollHeight;

var heightNeeded = document.getElementById('container').offsetHeight - window.innerHeight;
window.resizeBy(0, heightNeeded);
},
// 回复操作
replyHandle: function(e){
var self = this;
if (e.keyCode == 13 && !e.shiftKey) {
if (document.getElementById('reply').value.length > 0) {
var msg = document.getElementById('reply').value;
var cb = function(){
setTimeout(function() {
if(window.chrome || window.safari){
window.close();
}else{
// 关闭窗口
self.eventObj.emit("close");
}
}, 120);
};
if(this.options.pushType == self.Airdroid.PUSH_TYPE.NOTIFICATION){
// 发送信息
self.server.sendIMMsg(this.options, msg).done(function(){
cb();
});
}else{
// 发送信息
self.server.sendNotePushMsg(msg, this.options.deviceId).done(function(){
cb();
});
}
}
return false;
}
}
});
Reply.init();
// 关掉的时候,释放资源
window.onunload = function(){

};
})();

其实逻辑非常简单,除了渲染页面之外,就只有一个回复操作的监听。

Chrome 打开小窗口并请求数据

对于Chrome 来说,打开一个新窗口直接用 chrome.windows.create。 然后在背景页开启一个监听,如果过来的webuid一样,就把消息内容传过去, 这样就可以在 reply 页面初始化的时候,请求数据的时候,背景页就可以将一些内容传过去了:

1
2
3
4
5
6
7
8
9
var listener = function(message, sender, sendResponse) {
chrome.runtime.onMessage.removeListener(listener);

if (message.webuid == obj.webuid) {
sendResponse(obj);
}
};

chrome.runtime.onMessage.addListener(listener);

然后在 reply.js 初始化完成之后,就开始请求数据:

1
2
3
chrome.runtime.sendMessage({ 'webuid': webuid }, function(response) {
self.render(response);
});

Safari 打开小窗口并请求数据

对于Safari来说,调用safari.application.openBrowserWindow() 来开一个新窗口,然后触发request_conversation_push并监听conversation_push 从而实现数据交换,所以在背景页是这样子的:

1
2
3
4
5
6
7
8
9
10
11
var listener = function(e) {
if (e.name == 'request_conversation_push') {
// 这边要去掉里面的数组
delete obj.buttons;
console.log("接受其他页面的数据请求==>" + JSON.stringify(obj));
e.target.page.dispatchMessage('conversation_push', JSON.stringify(obj));
safari.application.removeEventListener('message', listener, false);
}
};

safari.application.addEventListener('message', listener, false);

然后前端页面在初始化之后,直接触发request_conversation_push来像背景页请求数据,并且通过监听conversation_push来得到背景页返回的数据:

1
2
3
4
5
6
7
8
var listener = function(e) {
if (e.name == 'conversation_push') {
self.render(JSON.parse(e.message));
safari.self.removeEventListener('message', listener, false);
}
};
safari.self.addEventListener('message', listener, false);
safari.self.tab.dispatchMessage('request_conversation_push');

Firefox 打开小窗口并请求数据

对于Firefox来说,打开窗口也是在中转页面(main.js)进行的

1
Airdroid.Event.dispatchEvent(Airdroid.Event.FireFoxEvent.open_quickreply, spec);

具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// 打开快速回复窗口
// http://stackoverflow.com/questions/22002010/addon-sdk-way-to-make-a-dialog
page.port.on('open_quickreply', function(data) {
var info = data.detail;
var url = info.url;
if (url.indexOf('http') == -1) {
url = self.data.url(url);
}

var size = '';
if (info.width && info.height) {
size = ',width=' + info.width + ',height=' + info.height;
}

var popup = require('sdk/window/utils').openDialog({
'features': Object.keys({
'chrome': true,
'centerscreen': true,
'resizable': false,
'scrollbars': true
}).join() + size,
name: info.notification.webuid
});

popup.addEventListener('load', function() {
tabs.activeTab.on('ready', function(tab) {
var worker = tab.attach({
'contentScriptFile': [
self.data.url('js/lib/jquery/jquery.js'),
self.data.url('js/lib/jquery/jquery.toast.js'),
self.data.url('js/lib/underscore/underscore.js'),
self.data.url('js/util/tplHelper.js'),
self.data.url('js/web/base.js'),
self.data.url('js/web/reply.js')
]
});

attachListeners(worker);
// 请求对应的数据
worker.port.on('request_conversation_push', function() {
worker.port.emit('conversation_push', info.notification);
});

worker.port.on('close', function(reply) {
popup.close();
});
});

tabs.activeTab.url = url;
});
});

这边还要重新加载跟reply.html一样的js文件。 通信方式开始在这个方法里面设置的:

1
2
3
4
5
6
7
8
// 请求对应的数据
worker.port.on('request_conversation_push', function() {
worker.port.emit('conversation_push', info.notification);
});

worker.port.on('close', function(reply) {
popup.close();
});

前端页面只要触发就行了:

1
2
3
4
self.eventObj.emit('request_conversation_push');
self.eventObj.on('conversation_push', function(push) {
self.render(push);
});

eventObj 就是 Firefoxself.port

总结

本节实现了怎么在各个浏览器打开一个新的窗体页面,并且实现数据交换。


系列文章:
浏览器 extension 插件开发系列(01) -- 前言和确认需求
浏览器 extension 插件开发系列(02) -- Chrome 插件的启动以及调试
浏览器 extension 插件开发系列(03) -- Firefox 插件的启动以及调试
浏览器 extension 插件开发系列(04) -- Safari 插件的添加以及调试
浏览器 extension 插件开发系列(05) -- Safari 插件申请开发者证书
浏览器 extension 插件开发系列(06) -- 各浏览器导航栏按钮的配置的点击出现的panel
浏览器 extension 插件开发系列(07) -- 获取各浏览器端的背景页
浏览器 extension 插件开发系列(08) -- 背景页启动和登录持久化
浏览器 extension 插件开发系列(09) -- popup以及其他前端页面的启动
浏览器 extension 插件开发系列(10) -- 事件驱动模型
浏览器 extension 插件开发系列(11) -- 登录模块(包括第三方登录和弹框)
浏览器 extension 插件开发系列(12) -- 实现右键菜单推送消息
浏览器 extension 插件开发系列(13) -- 实现消息过来出现桌面通知
浏览器 extension 插件开发系列(14) -- 点击reply出现回复小窗口
浏览器 extension 插件开发系列(15) -- chrome多文件上传(拖拽上传或者点击上传)
浏览器 extension 插件开发系列(16) -- Firefox 遇到的问题
浏览器 extension 插件开发系列(17) -- Safari 遇到的问题