简体   繁体   中英

I am unable to get the chat functionality to work in my Meteor app

Hello I am very new to Meteor and am trying to learn and get acquainted with the Meteor platform.

So I am working on building a Meteor app { https://github.com/ahadsheriff/SheriffMessenger } which is based off another example I saw { http://chatt.meteor.com/ (note: the original app was built using an older version of meteor and some of the packages were not necessary in the current version (mine)) The purpose of me cloning the example app is to experiment with meteor and the app itself, the idea is (once I get it to work) break the code down and study each individual element to understand how meteor works.

In my clone everything looks fine except two of the main features are not working in my version: the chat/messaging functionality and the ability to see which user(s) you are communicating with and to see whether they are online.

I have installed all the packages that I possibly could (packages that were supported in the latest build of Meteor), and I think my problem might be with the lack of some fundamental packages.

Anyways I was wondering if somebody could help me sift through the code and see where my problem is, so I can get the app to work and get back to breaking this code down!

Any help is appreciated!

I have posted the github link above and I will post the HTML and Javascript code below for your convenience.

Thank you, your help will be very much appreciated!!!

HTML:

<template name="layout">
  <head>
    <title>Simple chat</title>
    <link href='http://fonts.googleapis.com/css?family=Roboto' rel='stylesheet' type='text/css'>
  </head>

    <!-- Wrap all page content here -->
    {{> yield}}

</template>

<template name="channel">
    <div class="container">
      <div class="row">
        <div class="col-md-3">
          {{> presentation}}
        </div>
        <div class="col-md-6">
          {{> chat}}        
        </div>
        <div class="col-md-3">
          {{> participants}}
        </div>
      </div>
    </div>
</template>

<template name="homepage">
  <div class="container">
    <div class="row">
      <div class="col-md-6 col-md-offset-3">
        <div class="header">
          <a href="/"><h1>Sheriff Messenger</h1></a>

          <h2>Chat. Like a Sheriff.</h2>
        </div>
        <div class="landing">
          <form class="form-inline" role="form">
          <div class="form-group">
            <input type="text" class="form-control" id="channelInput" placeholder="username" maxlength="20" />
            <button type="button" id="channelButton" class="btn btn-default">enter</button>
          </div>
        </form>
        <div class="channels">
          <ul class="pager">
            {{#each channels}}
              <li><a href="/c/{{ name }}">{{ name }} - {{ participants.length }} <span class="glyphicon glyphicon-user"></span></a></li>
            {{/each}}
          </ul>

        </div>

        </div>

      </div>
    </div>

  </div>
</template>


<template name="presentation">
  <a href="/"><h1>Sheriff Messenger</h1></a>
</template>

<template name="participants">
  <h3>Participants</h3>
  <ul>
    {{#each participants}}
      <li style="list-style-type: none;"><div class="color-swatch" style="background-color:{{labelClass}};"></div> {{ profile.name }}</li>
    {{/each}}
  </ul>
  <p>Edit your name : </p>
  <input type="text" class="form-control" id="nameInput" placeholder="{{name}}" maxlength="40" />

</template>

<template name="chat">
  <div class="chat">
    <div class="conversation commentArea" id="scroll">
      {{#each messages }}
        {{#if message}}
          <div class="{{authorCss author}}">
            {{breaklines message }}<br/>
            <i class="author">{{ name }}</i>
          </div>
        {{else}}
          <div class="bubbledLeft">
            <i class="author">Sheriff Messenger</i>
          </div>
        {{/if}}
        <script> if (typeof(scrolldown) == "function") { scrolldown(); }</script>
      {{/each}}
    </div>
    <div class="conversationInput"> 
      <input type="text" id="messageInput" class="form-control text" placeholder="Message..." maxlength="300">
    </div>
  </div>


  <script type="text/javascript">
    /* Call it everytime ? ....
    around 10 times at the first call...
     */
    var scrolldown = function() {
      console.log("one scrolldown call..."); // reminder to debug...
      var elem = document.getElementById('scroll');
      elem.scrollTop = elem.scrollHeight;
    }
  </script>

</template>

Javascript:

var MeteorUser = function () {
  var userId = Meteor.userId();
  if (!userId) {
    return null;
  }
  var user = Meteor.users.findOne(userId);
  /* if (user !== undefined &&
      user.profile !== undefined &&
      user.profile.guest) {
    return null;
  } */
  return user;  
};



////////// Helpers for in-place editing //////////

// Returns an event map that handles the "escape" and "return" keys and
// "blur" events on a text input (given by selector) and interprets them
// as "ok" or "cancel".
var okCancelEvents = function (selector, callbacks) {
  var ok = callbacks.ok || function () {};
  var cancel = callbacks.cancel || function () {};

  var events = {};
  events['keyup '+selector+', keydown '+selector+', focusout '+selector] =
    function (evt) {
      if (evt.type === "keydown" && evt.which === 27) {
        // escape = cancel
        cancel.call(this, evt);

      } else if (evt.type === "keyup" && evt.which === 13 ||
                 evt.type === "focusout") {
        // blur/return/enter = ok/submit if non-empty
        var value = String(evt.target.value || "");
        if (value)
          ok.call(this, value, evt);
        else
          cancel.call(this, evt);
      }
    };

  return events;
};

var activateInput = function (input) {
  input.focus();
  input.select();
};

UI.registerHelper('breaklines', function(text){ // Should call a fonction to sanitaze the html...
  var html = "";
  if(text) { 
    html = text.replace(/(\r\n|\n|\r)/gm, '<br>');
  }
  return Spacebars.SafeString(html);
});


// Not the right way to do it ?!!!
UI.registerHelper('authorCss', function(author){
  var cssClass = "bubbledLeft";
  if(author === Meteor.userId()) { 
    cssClass = "bubbledRight";
  }
  return cssClass;
});

/////////// End Helper ///////

if (Meteor.isClient) {
  // Create collection on client
  Messages = new Meteor.Collection('messages');
  Channels = new Meteor.Collection('channels');

  Meteor.startup(function() {
      Meteor.loginVisitor(); // Guest Account
      //Meteor.insecureUserLogin('Anonymous'); // Test Account
      // We take car of the name
      Session.setDefault('name', 'Guest');
      Session.setDefault('channel', 'yo');

  });



  //////////// Chat ///////////////
  Template.chat.messages = function () {
    var messagesCursor = Messages.find({}, {sort:{timestamp:-1}, limit:42});
    var messages = messagesCursor.fetch().reverse(); // Should use observechnage to avoid over computation ?

    for (var i = messages.length - 1; i >= 0; i--) {
      var user =  Meteor.users.findOne(messages[i].author);
      if (user) {
        messages[i].name = user.profile.name;
      }
      else {
        messages[i].name = "Unknown";
      }
    };

    var conversations = [];
    var newConversation = messages[0];
    for (var i = 0; i <= messages.length - 2; i++) {
      var timedelta = messages[i+1].timestamp - messages[i].timestamp; 
      var sameauthor = (messages[i+1].author === messages[i].author);
      if (timedelta <= 30000 && sameauthor) {
        newConversation.message = newConversation.message + " \n" + messages[i+1].message;
      }
      else {
        conversations.push(newConversation);
        newConversation = messages[i+1];
      }
    };
    conversations.push(newConversation);
    // title bar alert 
    $.titleAlert("New chat message!", {requireBlur: true});
    return conversations;
  };


  Template.chat.events(okCancelEvents(
      '#messageInput',
      {
        ok: function (value, evt) {
          Messages.insert({
            author: Meteor.userId(),
            message: value,
            timestamp: (new Date()).getTime(),
            channel: Session.get('channel') 
          });
          evt.target.value = "";
        }
      }
  ));
  //////////// End Chat ///////////////


  //////////// Name ///////////////
  Template.participants.name = function () {
    Meteor.users.findOne(userId)
    var user = Meteor.users.findOne(Meteor.userId());
    if (user){
      Session.set('name', user.profile.name);
    }
    return Session.get('name');
  };

  Template.participants.participants = function() {
    var labelClass = function(id) { // Certainly not the right way to do it...
      if (id === Meteor.userId()) {
        return "#428bca";
      }
      var user = Meteor.users.findOne(id);
      if (user) {
        if (user.status.online) {
          return "#5cb85c";
        }
        else {
          return "#f0ad4e";
        }
      }
      else {
        return '#d9534f';
      }

    };

    var participants = Meteor.users.find({}).fetch();
    for (var i = participants.length - 1; i >= 0; i--) {
      participants[i].labelClass = labelClass(participants[i]._id);
    };

    return participants;
  }

  Template.participants.events(okCancelEvents(
    '#nameInput',
    {
      ok: function (value, evt) {
        if (value) {
          var user = Meteor.users.findOne(Meteor.userId());
          if (user){
            Meteor.users.update({_id:Meteor.userId()}, {$set:{"profile.name": value}})
          }
          Session.set('name', value);
        } 
      }
    }));
    //////////// End Name ///////////////


    //////////// Homepage ///////////////
    Template.homepage.events(okCancelEvents(
      '#channelInput',
      {
        ok: function (value, evt) {
          if (value) {
            Session.set('channel', value);
          }
        }
      }));

    Template.homepage.channel = function () {
      return Session.get('channel');
    };

    Template.homepage.channels = function() {
      return Channels.find({}, {limit:42});
    }

    Template.homepage.events({
      'click #channelButton': function (event, template) {
        Router.go('/c/'+Session.get('channel'));
      }
    });



    //////////// END Homepage ///////////


    //////////// Routing ///////////////

  Router.configure({
    layoutTemplate: 'layout'
  });

  Router.map(function () {
    this.route('channel', {
      path: '/c/:channel',
      template: 'channel',
      layoutTemplate: 'layout',
        waitOn: function () {
          Session.set('channel', this.params.channel);
          // Subscribe
          Meteor.subscribe("chatroomMessages", this.params.channel);
          Meteor.subscribe("channels", this.params.channel);
        },
        data: function() {
          var channel = Channels.findOne({name: this.params.channel});
          var participants = [Meteor.userId()]; // default;
          if (channel) {
            var participants  = channel.participants;
          }
          Meteor.subscribe("users", participants);
        }
    });

    this.route('home', {
      path: '/',
      template: 'homepage',
      layoutTemplate: 'layout',
      data: function() {
        Meteor.subscribe("channelslist");
      }
    });
  });

    //////////// END Routing ///////////


}




if (Meteor.isServer) {
  Meteor.startup(function () {
    Messages = new Meteor.Collection('messages');
    Channels = new Meteor.Collection('channels');

    // code to run on server at startup

  });

  Meteor.publish("channelslist", function() {
    return Channels.find({});
  });

  Meteor.publish("channels", function (channel) {
    var id;
    if (Channels.findOne({name:channel}) == null) {
      id = Channels.insert({
        name : channel,
        created_timestamp : (new Date()).getTime(),
        created_author: this.userId,
        participants: [],
        message : 0
      });
    } else {
      id = Channels.findOne({name:channel})._id;
    }

    if (id) {
      Channels.update({_id: id}, {$addToSet: { participants: this.userId}});
    }
    return Channels.find({});
  });



  Meteor.publish("users", function (listUsers) {
    return Meteor.users.find({_id: {$in: listUsers}});
  });


  Meteor.publish("chatroomMessages", function (channel) {
    return Messages.find({channel: channel}, {sort:{timestamp:-1}, limit:42});   
  });


}

And just in case...here is the CSS:

/* CSS declarations go here */
@media (min-width: 1200px) {
    [class*="span"] {
        margin-left: 20px; /* correction ??? */
    }
}
body {
    background-color: #e7e7e7;
}
.chat {
    height: 100vh; /* What is that ? */
    background-color: #fff;
    -webkit-box-shadow: 0px 3px 5px 0px rgba(0, 0, 0, 0.61);
    -moz-box-shadow:    0px 3px 5px 0px rgba(0, 0, 0, 0.61);
    box-shadow:         0px 3px 5px 0px rgba(0, 0, 0, 0.61);
}
.conversation {
    height: calc(100% - 50px); /* Use preprocessing to get a fixed .conversationInput-height */
    overflow-x:hidden;
    overflow-y: auto;
}
.conversationInput {
    background-color: #ccf;
    height: 50px;
    bottom: 0;
}


/* CSS chat 'stolen' from  http://jsfiddle.net/anuradhabajpai/x8C8S/10/ */

.commentArea {
    font: 14px Arial;
    padding: 0 10px 0px;
}

.bubbledLeft,.bubbledRight {
    margin-top: 10px;
    margin-bottom: 10px;
    padding: 5px 9px;
    max-width: 80%;
    clear: both;
    position: relative;
}

.bubbledLeft{
    float: left;
    margin-right: auto;
    -webkit-border-radius: 8px 8px 8px 0px;
    -moz-border-radius: 8px 8px 8px 0px;
    -o-border-radius: 8px 8px 8px 0px;
    -ms-border-radius: 8px 8px 8px 0px;
    border-radius: 8px 8px 8px 0px;
    background-color: #65B045;
    color: #ffffff;
}

.bubbledLeft:before {
    border-bottom: 10px solid #65B045;
    border-left: 9px solid rgba(0, 0, 0, 0);
    position: absolute;
    bottom: 0;
    left: -8px;
    content: "";
}

.bubbledRight{
    float: right;
    margin-left: auto;
    text-align: right;
    -webkit-border-radius: 8px 8px 0px 8px;
    -moz-border-radius: 8px 8px 0px 8px;
    -o-border-radius: 8px 8px 0px 8px;
    -ms-border-radius: 8px 8px 0px 8px;
    border-radius: 8px 8px 0px 8px;
    background-color: #07D;
    color: white;
}

.bubbledRight:before {
    border-bottom: 9px solid #07D;
    border-right: 9px solid rgba(0, 0, 0, 0);
    position: absolute;
    bottom: 0;
    right: -8px;
    content: "";
}

.author {
    opacity: .5;
    font-size: .9em;
}
/* END CSS Chat */
#messageInput {
    width: 100%;
    padding: 0px;
    padding-left: 16px;
    height: 100%;
    border-bottom: 0;
    border-radius: 0;
}

.color-swatch {
    width: 12px;
    height: 12px;
    display: inline-block;
    border-radius: 50%;
}
body {
    font-family: 'Roboto', sans-serif;
}
.landing {
    padding-top: 150px;
    text-align: center;
}
.channels {
    padding: 10px 10px 10px 10px;
}
h1 {
    text-decoration: none;
    text-decoration-color: none;
    color: #333;
}
.header h1 {
    text-align: center;
} 

Also here is a list of the packages I used:

meteor-platform
autopublish
insecure
mrt:moment
ostrio:user-status
accounts-base
accounts-password
artwells:accounts-guest
mizzao:accounts-testing
fortawesome:fontawesome
iron:router
twbs:bootstrap
meteorhacks:fast-render
peerlibrary:related
standard-app-packages

Once again thank you for the help!

Might not at all be the only reason, but in var participants = Meteor.users.find({}).fetch(); the fetch() shouldn't be there...

The same goes for those lines:

var messagesCursor = Messages.find({}, {sort:{timestamp:-1}, limit:42});
var messages = messagesCursor.fetch().reverse();

I am new to meteor and mongodb too, but there is only two fetch() in your code, and both where you have problems... And since I haven't seen that on mongdb, I find it highly probable that it is your problem.

Edit:

Also, you use Meteor.users.findOne(id) which should be Meteor.users.findOne({_id:id}) . Same for Meteor.users.findOne(messages[i].author);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM