并非所有客户都显示聊天类型指示器

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是数据库中状态的表示。


注意:我过度简化了一些事情,根据记忆编写代码,并删除了之前的 fadeInfadeOut 行为。 请根据您的需要编辑代码,我建议使用如下包制作动画: gwendall:template-animations,或 gwendall:ui-hooks