并非所有客户都可以看到聊天类型指示器

时间:2019-08-22 21:34:30

标签: meteor

我正在研究一个书写指示器,每当用户在文本区域内书写内容时,该指示器就会显示在在线列表上的名称旁边。问题是,它只对撰写者显示,而对其他选项卡/客户端不显示。

这是我的代码:

在线列表模板: 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文本区域

 '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 中编写该函数以使其在服务器上(?),并将其加载到 imports / ui / components / chat / chat.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); 
}

方法3:与2基本相同,但是这次我没有使用blaze-template.events帮助器

document.addEventListener("keydown", event => {
  var character = Session.get("activeChar");
  var getIndicator = "#typeInd_"+character;
  typeIndicator(character, getIndicator);

});

因此,这些更改似乎没有什么不同。有人可以帮忙吗?谢谢!

2 个答案:

答案 0 :(得分:2)

从您的不同方法来看,似乎对Web应用程序和同构javascript有基本的误解。

每个客户端分别加载并运行您的应用程序的客户端版本。它们在隔离的环境(浏览器选项卡)中运行。 在该应用程序中运行代码时,它只会影响自身。 例如,您当前的方法看起来像这样:

     ┌─────┐
┌────▼───┐ │ ┌────────┐  ┌────────┐
│ Client │ │ │ Client │  │ Client │
└────────┘ │ └────────┘  └────────┘
     └─────┘
┌────────┐
│ Server │
└────────┘

客户只是在自言自语。这就是为什么其他客户端不更新聊天指示器的原因。 (请注意,Session也隔离到每个客户端实例)

我们想要的是让客户端告诉服务器状态已更改,然后服务器可以告诉其他客户端。然后,这些客户端可以根据更改来更新其ui:

┌────────┐  ┌────────┐  ┌────────┐
│ Client │  │ Client │  │ Client │
└────────┘  └────────┘  └────────┘
   │   ▲         ▲           ▲
   ▼   │         │           │
┌────────┐       │           │
│ Server │───────┴───────────┘
└────────┘

为使应用程序的该单个实例与服务器或其他实例进行通信,它需要发出网络请求。 这通常是一个HTTP请求(例如XHR,fetch,jquery \ $。http),尽管在Meteor的情况下,我们在Websocket上使用了DDP。 (请注意,您可以让客户直接相互交谈,但是真正的p2p复杂得多)

在Meteor中,建议与服务器通信的方法是使用Meteor.methods。 实时向客户端发送数据的推荐方法是将pub/sub与Mongo Collections配合使用。 当客户端订阅服务器正在发布的数据馈送时,服务器将通过网络套接字向客户端发送更新。

要针对您的聊天指示器问题执行此操作,请创建具有聊天状态的集合,设置方法并发布/订阅

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}}

现在,所有客户端的模板代码都取决于数据库中的值。 最后一件事是通过调用流星方法来更新数据库中的值:

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

答案 1 :(得分:1)

如果我理解正确,则希望与所有其他连接的客户端实时共享指标。这确实是流星可以轻松发光的功能类型。

您可能会错过的是,要使该功能正常工作,必须在MongoDB中提供要共享的数据/指示器,以便您的服务器随后可以将其实时推送给所有客户端(通过db进行此要求是必需的)有关Meteor服务器的设计方式,因此,即使瞬时指示符(如“写入状态”)也必须记录在db中。)

请参阅《流星》指南中的发布和订阅。