并非所有客户都显示聊天类型指示器
Chat type indicator doesn’t appear for all clients
我正在开发一个书写指示器,每当用户在文本区域内书写内容时,该指示器就会显示在在线列表中的姓名旁边。问题是,它只显示给写作的人,而不显示给其他 tabs/clients。
这是我的代码:
在线列表模板:imports/ui/components/chat/chat_onlinelist.html
<template name="onlineliste">
<div id="online-liste" class="onlineList">
{{#each characters}}
<div class="characterBasicInteraction" oncontextmenu="return false;">
<span class="typeIndicator" id="typeInd_{{name}}">✎</span>
<!-- TypeIndicator shows up here, code gets id with this.name
and uses it to change jquery fadeIn/fadeOut -->
<a href="/c/{{name}}" target="_blank" class="{{name}}" {{checkIfFriend}}><li>{{name}}</li></a>
<div id="panel_{{name}}" class="panelChat">
<span><b>{{name}}</b></span>
<span id="whisp">Flüstern</span>
{{{checkIfFriendButton}}}
<span>Bookmark</span>
<span>Ignore</span>
<span>Report</span>
</div>
</div>
{{/each}}
</div>
</template>
到目前为止,我尝试了 3 种方法,结果都与上述相同。
第一种方法,imports/ui/components/chat/chat.js
中的事件按键文本区域
'keydown textarea'(event, template) {
var character = Session.get("activeChar");
var getIndicator = "#typeInd_"+character;
//setup before functions
var typingTimer; //timer identifier
var doneTypingInterval = 5000; //time in ms (5 seconds)
//on keyup, start the countdown
template.$('textarea').keyup(function(){
clearTimeout(typingTimer);
if (template.$('textarea').val()) {
typingTimer = setTimeout(doneTyping, doneTypingInterval);
}
});
//user is "finished typing,"
function doneTyping () {
template.$(getIndicator).fadeOut();
}
template.$(getIndicator).fadeIn();
template.$(".input-box_text").focusout(function() {
template.$(getIndicator).fadeOut();
})
}
方法 2:在 imports/api/chat/chat.js 中编写函数以将其放在服务器上(?)并在 [=41 中加载它=].js
typeIndicator = function typeIndicator (character, getIndicator) {
var typingTimer; //timer identifier
var doneTypingInterval = 5000; //time in ms (5 seconds)
//on keyup, start the countdown
$('textarea').keyup(function(){
clearTimeout(typingTimer);
if ($('textarea').val()) {
typingTimer = setTimeout(doneTyping, doneTypingInterval);
}
});
//user is "finished typing," do something
function doneTyping () {
$(getIndicator).fadeOut();
}
//$(getIndicator).fadeIn();
$(getIndicator).fadeIn();
$(".input-box_text").focusout(function() {
$(getIndicator).fadeOut();
});
};
'keydown textarea'(event, template) {
var character = Session.get("activeChar");
var getIndicator = "#typeInd_"+character;
TypeIndicator(character, getIndicator);
}
方法三:与方法二基本相同,但这次我没有使用blaze-template.events helper
document.addEventListener("keydown", event => {
var character = Session.get("activeChar");
var getIndicator = "#typeInd_"+character;
typeIndicator(character, getIndicator);
});
所以看起来这些更改并没有什么不同。有人可以帮忙吗?谢谢!
如果我理解正确的话,您希望与所有其他连接的客户端实时共享一个指标。这确实是 Meteor 可以轻松发光的功能类型。
您可能想念的是,要使此类功能起作用,必须在您的 MongoDB 中提供要共享的数据/指标,以便您的服务器可以将其实时推送到所有客户端(通过 db 的这个要求是由于 Meteor 服务器的设计方式,所以即使像你的 "writing status" 这样的瞬态指标仍然必须记录在 db 中。
参见流星指南中的发布和订阅。
从您的不同方法来看,似乎对 Web 应用程序和同构存在根本性的误解 javascript。
每个客户端单独加载并 运行s 您应用程序的客户端版本。他们 运行 在一个孤立的环境中(浏览器选项卡)。
当您 运行 在该应用程序中编写代码时,它只能影响自身。
例如,你目前的做法都是这样的:
┌─────┐
┌────▼───┐ │ ┌────────┐ ┌────────┐
│ Client │ │ │ Client │ │ Client │
└────────┘ │ └────────┘ └────────┘
└─────┘
┌────────┐
│ Server │
└────────┘
客户端只是在自言自语。这就是为什么其他客户端不更新聊天指示器的原因。
(注意 Session
也隔离到每个客户端实例)
我们想要的是客户端告诉服务器状态已经改变,然后服务器可以告诉其他客户端。然后,这些客户可以更新他们的 ui 以响应更改:
┌────────┐ ┌────────┐ ┌────────┐
│ Client │ │ Client │ │ Client │
└────────┘ └────────┘ └────────┘
│ ▲ ▲ ▲
▼ │ │ │
┌────────┐ │ │
│ Server │───────┴───────────┘
└────────┘
要使应用程序的单个实例与服务器或其他实例进行通信,它需要发出网络请求。
这通常是一个 HTTP 请求(例如 XHR、fetch、jquery\$.http),尽管在 Meteor 的情况下,我们使用 DDP over websockets。
(请注意,您可以让客户端直接相互交谈,但真正的 p2p 复杂得多)
在 Meteor 中,推荐的与服务器通信的方式是使用 Meteor.methods
。
向客户端实时发送数据的推荐方法是使用 pub/sub 和 Mongo 集合。
当客户端 订阅 服务器正在 发布 的数据提要时,服务器将通过 websockets 向客户端发送更新。
为了解决您的聊天指示器问题,让我们创建一个包含聊天状态的集合,设置一个方法,然后 pub/sub
import { Meteor } from "meteor/meteor";
import { Mongo } from "meteor/mongo";
// Create the collection
export const ChatStatus = new Mongo.Collection("chat-status");
// Set up the method
Meteor.methods({
updateStatus(characterId, status) {
ChatStatus.upsert({
characterId: characterId,
status: status,
});
},
});
// Publications are server-only
if (Meteor.isServer) {
Meteor.publish("chat-status", function() {
return ChatStatus.find();
});
}
// Subscriptions are client only. Normally you would put this in template code, not here.
if (Meteor.isClient) {
Meteor.subscribe("chat-status");
}
因为我们要在服务器(用于长期存储)和客户端(以便我们可以显示指标)上访问此数据,我们将其放在:
/both/chat-status.js
并将该文件 (import '/both/chat-status.js'
) 导入
/client/main.js
和
/server/main.js
这就是同构的意思。我们把代码写在一个地方,然后在服务器端和客户端都加载。
现在让您的模板通过导入来访问集合,并添加一个助手来检查状态是否正在编辑
// chat.js;
import { ChatStatus } from "/both/chat-status.js";
Template.chat_onlinelist.helpers({
isEditing: function(characterId) {
const document = ChatStatus.findOne({ characterId: characterId });
if (document) {
return document.status === "editing";
} else {
return false;
}
},
});
并更新模板代码以使用新的助手:
{{#each characters}}
<div class="characterBasicInteraction" oncontextmenu="return false;">
{{#if isEditing _id }}
<span class="typeIndicator" id="typeInd_{{name}}">✎</span>
{{/if}}
<!-- TypeIndicator shows up here, code gets id with this.name
and uses it to change jquery fadeIn/fadeOut -->
<a href="/c/{{name}}" target="_blank" class="{{name}}" {{checkIfFriend}}><li>{{name}}</li></a>
<div id="panel_{{name}}" class="panelChat">
<span><b>{{name}}</b></span>
<span id="whisp">Flüstern</span>
{{{checkIfFriendButton}}}
<span>Bookmark</span>
<span>Ignore</span>
<span>Report</span>
</div>
</div>
{{/each}}
现在所有客户端的模板代码都取决于数据库中的值。
最后一件事是通过调用 meteor 方法更新数据库中的那个值:
Template.chat_onlinelist.events({
"keydown textarea"(event, templateInstance) {
var character = Session.get("activeChar");
var doneTypingInterval = 5000; //time in ms (5 seconds)
// Assuming that you're storing the whole document?
Meteor.call("update-status", character._id, "editing");
// set the timer identifier on the template instance so we can get it later
templateInstance.typingTimer = setTimeout(() => {
// use empty string to denote no status
Meteor.call("update-status", character._id, "");
}, doneTypingInterval);
},
"focusout textarea"(event, templateInstance) {
if (templateInstance.typingTimer) {
clearTimeout(templateInstance.typingTimer);
}
Meteor.call("update-status", character._id, "");
},
});
所以现在数据库中的值是由事件设置的,UI是数据库中状态的表示。
注意:我过度简化了一些事情,根据记忆编写代码,并删除了之前的 fadeIn
、fadeOut
行为。
请根据您的需要编辑代码,我建议使用如下包制作动画:
gwendall:template-animations,或
gwendall:ui-hooks
我正在开发一个书写指示器,每当用户在文本区域内书写内容时,该指示器就会显示在在线列表中的姓名旁边。问题是,它只显示给写作的人,而不显示给其他 tabs/clients。
这是我的代码:
在线列表模板:imports/ui/components/chat/chat_onlinelist.html
<template name="onlineliste">
<div id="online-liste" class="onlineList">
{{#each characters}}
<div class="characterBasicInteraction" oncontextmenu="return false;">
<span class="typeIndicator" id="typeInd_{{name}}">✎</span>
<!-- TypeIndicator shows up here, code gets id with this.name
and uses it to change jquery fadeIn/fadeOut -->
<a href="/c/{{name}}" target="_blank" class="{{name}}" {{checkIfFriend}}><li>{{name}}</li></a>
<div id="panel_{{name}}" class="panelChat">
<span><b>{{name}}</b></span>
<span id="whisp">Flüstern</span>
{{{checkIfFriendButton}}}
<span>Bookmark</span>
<span>Ignore</span>
<span>Report</span>
</div>
</div>
{{/each}}
</div>
</template>
到目前为止,我尝试了 3 种方法,结果都与上述相同。
第一种方法,imports/ui/components/chat/chat.js
中的事件按键文本区域 'keydown textarea'(event, template) {
var character = Session.get("activeChar");
var getIndicator = "#typeInd_"+character;
//setup before functions
var typingTimer; //timer identifier
var doneTypingInterval = 5000; //time in ms (5 seconds)
//on keyup, start the countdown
template.$('textarea').keyup(function(){
clearTimeout(typingTimer);
if (template.$('textarea').val()) {
typingTimer = setTimeout(doneTyping, doneTypingInterval);
}
});
//user is "finished typing,"
function doneTyping () {
template.$(getIndicator).fadeOut();
}
template.$(getIndicator).fadeIn();
template.$(".input-box_text").focusout(function() {
template.$(getIndicator).fadeOut();
})
}
方法 2:在 imports/api/chat/chat.js 中编写函数以将其放在服务器上(?)并在 [=41 中加载它=].js
typeIndicator = function typeIndicator (character, getIndicator) {
var typingTimer; //timer identifier
var doneTypingInterval = 5000; //time in ms (5 seconds)
//on keyup, start the countdown
$('textarea').keyup(function(){
clearTimeout(typingTimer);
if ($('textarea').val()) {
typingTimer = setTimeout(doneTyping, doneTypingInterval);
}
});
//user is "finished typing," do something
function doneTyping () {
$(getIndicator).fadeOut();
}
//$(getIndicator).fadeIn();
$(getIndicator).fadeIn();
$(".input-box_text").focusout(function() {
$(getIndicator).fadeOut();
});
};
'keydown textarea'(event, template) {
var character = Session.get("activeChar");
var getIndicator = "#typeInd_"+character;
TypeIndicator(character, getIndicator);
}
方法三:与方法二基本相同,但这次我没有使用blaze-template.events helper
document.addEventListener("keydown", event => {
var character = Session.get("activeChar");
var getIndicator = "#typeInd_"+character;
typeIndicator(character, getIndicator);
});
所以看起来这些更改并没有什么不同。有人可以帮忙吗?谢谢!
如果我理解正确的话,您希望与所有其他连接的客户端实时共享一个指标。这确实是 Meteor 可以轻松发光的功能类型。
您可能想念的是,要使此类功能起作用,必须在您的 MongoDB 中提供要共享的数据/指标,以便您的服务器可以将其实时推送到所有客户端(通过 db 的这个要求是由于 Meteor 服务器的设计方式,所以即使像你的 "writing status" 这样的瞬态指标仍然必须记录在 db 中。
参见流星指南中的发布和订阅。
从您的不同方法来看,似乎对 Web 应用程序和同构存在根本性的误解 javascript。
每个客户端单独加载并 运行s 您应用程序的客户端版本。他们 运行 在一个孤立的环境中(浏览器选项卡)。 当您 运行 在该应用程序中编写代码时,它只能影响自身。 例如,你目前的做法都是这样的:
┌─────┐
┌────▼───┐ │ ┌────────┐ ┌────────┐
│ Client │ │ │ Client │ │ Client │
└────────┘ │ └────────┘ └────────┘
└─────┘
┌────────┐
│ Server │
└────────┘
客户端只是在自言自语。这就是为什么其他客户端不更新聊天指示器的原因。
(注意 Session
也隔离到每个客户端实例)
我们想要的是客户端告诉服务器状态已经改变,然后服务器可以告诉其他客户端。然后,这些客户可以更新他们的 ui 以响应更改:
┌────────┐ ┌────────┐ ┌────────┐
│ Client │ │ Client │ │ Client │
└────────┘ └────────┘ └────────┘
│ ▲ ▲ ▲
▼ │ │ │
┌────────┐ │ │
│ Server │───────┴───────────┘
└────────┘
要使应用程序的单个实例与服务器或其他实例进行通信,它需要发出网络请求。 这通常是一个 HTTP 请求(例如 XHR、fetch、jquery\$.http),尽管在 Meteor 的情况下,我们使用 DDP over websockets。 (请注意,您可以让客户端直接相互交谈,但真正的 p2p 复杂得多)
在 Meteor 中,推荐的与服务器通信的方式是使用 Meteor.methods
。
向客户端实时发送数据的推荐方法是使用 pub/sub 和 Mongo 集合。
当客户端 订阅 服务器正在 发布 的数据提要时,服务器将通过 websockets 向客户端发送更新。
为了解决您的聊天指示器问题,让我们创建一个包含聊天状态的集合,设置一个方法,然后 pub/sub
import { Meteor } from "meteor/meteor";
import { Mongo } from "meteor/mongo";
// Create the collection
export const ChatStatus = new Mongo.Collection("chat-status");
// Set up the method
Meteor.methods({
updateStatus(characterId, status) {
ChatStatus.upsert({
characterId: characterId,
status: status,
});
},
});
// Publications are server-only
if (Meteor.isServer) {
Meteor.publish("chat-status", function() {
return ChatStatus.find();
});
}
// Subscriptions are client only. Normally you would put this in template code, not here.
if (Meteor.isClient) {
Meteor.subscribe("chat-status");
}
因为我们要在服务器(用于长期存储)和客户端(以便我们可以显示指标)上访问此数据,我们将其放在:
/both/chat-status.js
并将该文件 (import '/both/chat-status.js'
) 导入
/client/main.js
和
/server/main.js
这就是同构的意思。我们把代码写在一个地方,然后在服务器端和客户端都加载。
现在让您的模板通过导入来访问集合,并添加一个助手来检查状态是否正在编辑
// chat.js;
import { ChatStatus } from "/both/chat-status.js";
Template.chat_onlinelist.helpers({
isEditing: function(characterId) {
const document = ChatStatus.findOne({ characterId: characterId });
if (document) {
return document.status === "editing";
} else {
return false;
}
},
});
并更新模板代码以使用新的助手:
{{#each characters}}
<div class="characterBasicInteraction" oncontextmenu="return false;">
{{#if isEditing _id }}
<span class="typeIndicator" id="typeInd_{{name}}">✎</span>
{{/if}}
<!-- TypeIndicator shows up here, code gets id with this.name
and uses it to change jquery fadeIn/fadeOut -->
<a href="/c/{{name}}" target="_blank" class="{{name}}" {{checkIfFriend}}><li>{{name}}</li></a>
<div id="panel_{{name}}" class="panelChat">
<span><b>{{name}}</b></span>
<span id="whisp">Flüstern</span>
{{{checkIfFriendButton}}}
<span>Bookmark</span>
<span>Ignore</span>
<span>Report</span>
</div>
</div>
{{/each}}
现在所有客户端的模板代码都取决于数据库中的值。 最后一件事是通过调用 meteor 方法更新数据库中的那个值:
Template.chat_onlinelist.events({
"keydown textarea"(event, templateInstance) {
var character = Session.get("activeChar");
var doneTypingInterval = 5000; //time in ms (5 seconds)
// Assuming that you're storing the whole document?
Meteor.call("update-status", character._id, "editing");
// set the timer identifier on the template instance so we can get it later
templateInstance.typingTimer = setTimeout(() => {
// use empty string to denote no status
Meteor.call("update-status", character._id, "");
}, doneTypingInterval);
},
"focusout textarea"(event, templateInstance) {
if (templateInstance.typingTimer) {
clearTimeout(templateInstance.typingTimer);
}
Meteor.call("update-status", character._id, "");
},
});
所以现在数据库中的值是由事件设置的,UI是数据库中状态的表示。
注意:我过度简化了一些事情,根据记忆编写代码,并删除了之前的 fadeIn
、fadeOut
行为。
请根据您的需要编辑代码,我建议使用如下包制作动画:
gwendall:template-animations,或
gwendall:ui-hooks