diff --git a/client/ChatterApp.jsx b/client/ChatterApp.jsx index 122a504..73a88f1 100644 --- a/client/ChatterApp.jsx +++ b/client/ChatterApp.jsx @@ -5,14 +5,23 @@ import getChatHTML from "./template-helpers/getChatHTML.jsx"; import router from "./template-helpers/router.jsx"; import Widget from "./components/Widget.jsx"; -const latestRooms = function (limit, withIds, archived) { +import { + CHATTER_CACHE_LIMIT, + CHATTER_EXPIRE_IN +} from "./global-variables.js"; + +const latestRooms = function (limit, withIds) { return { - find: {"_id": {$in: withIds}, "archived": archived}, + find: {"_id": {$in: withIds}}, options: {sort: {lastActive: -1}, limit: limit} }; }; -const chatterSubs = new SubsManager(); + +const chatterSubs = new SubsManager({ + cacheLimit: CHATTER_CACHE_LIMIT, + expireIn: CHATTER_EXPIRE_IN +}); const ChatterApp = React.createClass({ mixins: [ReactMeteorData], @@ -46,10 +55,15 @@ const ChatterApp = React.createClass({ const {activeRoomLimit, archivedRoomLimit} = this.state; if (userId) { - const userRooms = Chatter.UserRoom.find({userId}).fetch(); - const roomIds = _.pluck(userRooms, "roomId"); - const activeRoomQuery = latestRooms(activeRoomLimit, roomIds, false); - const archivedRoomQuery = latestRooms(archivedRoomLimit, roomIds, true); + const archivedUserRooms = Chatter.UserRoom.find({userId, archived: true}).fetch(); + const unarchivedUserRooms = Chatter.UserRoom.find({userId, archived: false}).fetch(); + + const archivedRoomIds = _.pluck(archivedUserRooms, "roomId"); + const unarchivedRoomIds = _.pluck(unarchivedUserRooms, "roomId"); + + const activeRoomQuery = latestRooms(activeRoomLimit, unarchivedRoomIds); + const archivedRoomQuery = latestRooms(archivedRoomLimit, archivedRoomIds); + activeRooms = Chatter.Room.find(activeRoomQuery.find, activeRoomQuery.options).fetch(); archivedRooms = Chatter.Room.find(archivedRoomQuery.find, archivedRoomQuery.options).fetch(); } diff --git a/client/components/MainSettings.jsx b/client/components/MainSettings.jsx index 4a2d2e4..0ea1b25 100644 --- a/client/components/MainSettings.jsx +++ b/client/components/MainSettings.jsx @@ -2,19 +2,35 @@ import React from 'react'; import Loader from "../components/Loader.jsx" const MainSettings = React.createClass({ + mixins: [ReactMeteorData], getInitialState: function() { return { - roomUsers: [], - archived: this.props.room.archived + roomUsers: [] }; }, + getMeteorData () { + const userId = Meteor.userId(); + const userRoomsHandle = Meteor.subscribe("chatterUserRooms"); + const subsReady = userRoomsHandle.ready(); + let room = this.props.room; + let user = null; + + if (subsReady) { + const ur = Chatter.UserRoom.findOne({roomId: this.props.room._id, userId}); + user = Meteor.user(); + room.archived = ur.archived; + } + + return { + subsReady, + room, + user + } + }, + componentDidMount() { - $(".ui.toggle.checkbox").checkbox(); - if (this.state.archived) { - $(".ui.toggle.checkbox").checkbox('check'); - } Meteor.call("room.users", this.props.room._id, (error, result) => { this.setState({roomUsers: result}); }); @@ -22,93 +38,100 @@ const MainSettings = React.createClass({ toggleArchivedState() { const params = { - archived: !this.state.archived, - roomId: this.props.room._id + archived: !this.data.room.archived, + roomId: this.props.room._id, + userId: Meteor.userId() }; + Meteor.call("room.archive", params); - this.setState({archived: !this.state.archived}); }, render() { - const user = Meteor.user(); - - const addUsersHTML = ( -
this.props.setView("addUsers")}> - -
- - Add or remove users... - -
-
- ); - - const roomUsers = this.state.roomUsers; - const roomUsersHTML = roomUsers.map(function(user) { - const statusClass = user.profile.online ? "user-status online" : "user-status offline"; - return ( -
-
- + if (this.data.subsReady) { + const user = this.data.user; + $(".ui.toggle.checkbox").checkbox(); + if (this.data.room.archived) { + $(".ui.toggle.checkbox").checkbox('check'); + } + const addUsersHTML = ( +
this.props.setView("addUsers")}> +
- - {user.profile.chatterNickname} + + Add or remove users... -
- Last logged in just now. -
); - }); + const roomUsers = this.state.roomUsers; + const roomUsersHTML = roomUsers.map(function(user) { + const statusClass = user.profile.online ? "user-status online" : "user-status offline"; + return ( +
+
+ +
+ + {user.profile.chatterNickname} + +
+ Last logged in just now. +
+
+
+ ); + }); - return ( -
-
- Channel description -
-

- {this.props.room.description} -

-

- This channel was created by {this.props.room.createdBy} on the {this.props.room.createdAt.toISOString()}. -

-
- - -
-

- Archived chats will store the conversation and stop notifications from bothering you in the future. -

-
-
- - - Channel members ({roomUsers.length}) - + return ( +
+
+ Channel description +
+

+ {this.props.room.description} +

+

+ This channel was created by {this.props.room.createdBy} on the {this.props.room.createdAt.toISOString()}. +

+
+ +
-
-
- {user.profile.isChatterAdmin ? addUsersHTML : null} -
- {roomUsers.length > 0 ? roomUsersHTML : } +

+ Archived chats will store the conversation and stop notifications from bothering you in the future. +

+
+
+ + + Channel members ({roomUsers.length}) + +
+
+
+ {user.profile.isChatterAdmin ? addUsersHTML : null} +
+ {roomUsersHTML} +
-
- ); + ); + } else { + return + } } }); diff --git a/client/components/Profile.jsx b/client/components/Profile.jsx index 01ffc3d..5e26bf6 100644 --- a/client/components/Profile.jsx +++ b/client/components/Profile.jsx @@ -2,7 +2,15 @@ import React from 'react'; import ReactDOM from 'react-dom'; import Loader from "../components/Loader.jsx" -const profileSubs = new SubsManager(); +import { + PROFILE_CACHE_LIMIT, + PROFILE_EXPIRE_IN +} from "../global-variables.js"; + +const profileSubs = new SubsManager({ + cacheLimit: PROFILE_CACHE_LIMIT, + expireIn: PROFILE_EXPIRE_IN +}); const Profile = React.createClass({ mixins: [ReactMeteorData], @@ -15,33 +23,24 @@ const Profile = React.createClass({ getMeteorData () { const usersHandle = profileSubs.subscribe("users"); - let user = {}; + const subsReady = usersHandle.ready(); + let user = null; - if (usersHandle.ready()) { + if (subsReady) { user = Meteor.users.findOne(this.props.userProfile); } return { - usersHandle, - user + user, + subsReady } }, - handleSubmit(e) { - e.preventDefault(); - const nickname = ReactDOM.findDOMNode(this.refs.nickname).value.trim(); - if (nickname.length === 0) return; - Meteor.call("user.changeNickname", nickname, (error, result) => { - if (!error) { - this.setState({nicknameChanged: true}) - } - }); - }, - - componentDidMount() { - if (this.data.usersHandle.ready() && this.props.userProfile == Meteor.userId()) { - ReactDOM.findDOMNode(this.refs.nickname).focus(); - $('.ui.form') - .form({ + initializeInput(input) { + if (input) { + this.nicknameInput = input; + input.focus(); + $('.ui.form').form( + { fields: { nickname: { identifier: 'nickname', @@ -53,17 +52,31 @@ const Profile = React.createClass({ ] } } - }); + } + ); } }, + handleSubmit(e) { + e.preventDefault(); + const nickname = this.nicknameInput.value.trim(); + if (nickname.length === 0) return; + Meteor.call("user.changeNickname", nickname, (error, result) => { + if (!error) { + this.setState({nicknameChanged: true}) + } + }); + }, + render() { - if (!this.data.usersHandle.ready()) { + if (!this.data.subsReady) { return ; } + const userId = this.data.user._id; const user = this.data.user.profile; const headerText = `${user.chatterNickname}'s Profile`; + const form = (
@@ -71,7 +84,12 @@ const Profile = React.createClass({ - +
); - - return (
@@ -98,7 +114,7 @@ const Profile = React.createClass({

{user.chatterNickname} is currently {user.online ? "online" : "offline"}.

- {this.props.userProfile == Meteor.userId() ? form : null} + {this.props.userProfile === userId ? form : null}
); } diff --git a/client/components/Room.jsx b/client/components/Room.jsx index 4dfba90..39653ea 100644 --- a/client/components/Room.jsx +++ b/client/components/Room.jsx @@ -1,11 +1,19 @@ import React from 'react'; -import Writer from "../components/Writer.jsx" -import Loader from "../components/Loader.jsx" - +import Writer from "../components/Writer.jsx"; +import Loader from "../components/Loader.jsx"; + +import { + VERY_RECENT_MSG, + RECENT_MSG, + VERY_RECENT_MSG_INTERVAL, + RECENT_MSG_INTERVAL, + ROOM_CACHE_LIMIT, + ROOM_EXPIRE_IN +} from "../global-variables.js"; const isFirstMessage = function(prevMsg, currentMsg) { - return prevMsg.userId != currentMsg.userId; + return prevMsg.userId !== currentMsg.userId; }; const timeSinceLastMsgGreaterThan = function(minutes, prevMsg, currentMsg) { @@ -15,12 +23,15 @@ const timeSinceLastMsgGreaterThan = function(minutes, prevMsg, currentMsg) { }; const timestampShouldBeDisplayed = function(currentMsg, nextMsg) { - const veryRecentMessage = currentMsg.getMinutesAgo() <= 60; - const recentMessage = currentMsg.getMinutesAgo() <= 1440; - return veryRecentMessage && timeSinceLastMsgGreaterThan(2, currentMsg, nextMsg) || recentMessage && timeSinceLastMsgGreaterThan(60, currentMsg, nextMsg); + const veryRecentMessage = currentMsg.getMinutesAgo() <= VERY_RECENT_MSG; + const recentMessage = currentMsg.getMinutesAgo() <= RECENT_MSG; + return veryRecentMessage && timeSinceLastMsgGreaterThan(VERY_RECENT_MSG_INTERVAL, currentMsg, nextMsg) || recentMessage && timeSinceLastMsgGreaterThan(RECENT_MSG_INTERVAL, currentMsg, nextMsg); } -const roomSubs = new SubsManager(); +const roomSubs = new SubsManager({ + cacheLimit: ROOM_CACHE_LIMIT, + expireIn: ROOM_EXPIRE_IN +}); const Room = React.createClass({ mixins: [ReactMeteorData], @@ -28,7 +39,7 @@ const Room = React.createClass({ getMeteorData () { const { roomId } = this.props; const messagesHandle = roomSubs.subscribe("chatterMessages", { - roomId: roomId + roomId }); const usersHandle = roomSubs.subscribe("users"); const subsReady = messagesHandle.ready() && usersHandle.ready(); @@ -47,11 +58,11 @@ const Room = React.createClass({ componentDidMount() { this.scrollDown() - Meteor.call("room.counter.reset", this.props.roomId); + Meteor.call("room.unreadMsgCount.reset", this.props.roomId); }, componentWillUnmount() { - Meteor.call("room.counter.reset", this.props.roomId); + Meteor.call("room.unreadMsgCount.reset", this.props.roomId); }, componentWillUpdate() { @@ -103,7 +114,7 @@ const Room = React.createClass({ const user = Meteor.users.findOne(message.userId); const isFirstMessageOfChat = index === 0, - isFirstMessageOfDay = index > 0 && prevMsg.getDate() != message.getDate(), + isFirstMessageOfDay = index > 0 && prevMsg.getDate() !== message.getDate(), isLastMessageChat = index === numberOfMessages - 1; // takes care of the display of dates and timestamps diff --git a/client/components/RoomList.jsx b/client/components/RoomList.jsx index 633f7fb..7d9a8c5 100644 --- a/client/components/RoomList.jsx +++ b/client/components/RoomList.jsx @@ -23,7 +23,7 @@ const RoomList = React.createClass({ componentDidMount() { $('.ui.accordion').accordion(); - Meteor.call("get.room.counts", (error, response) => { + Meteor.call("get.room.unreadMsgCount", (error, response) => { this.setState(response); }); @@ -81,6 +81,7 @@ const RoomList = React.createClass({ const newRoomBtn = (user.profile.isChatterAdmin) ? newRoomBtnHTML : null; const activeRoomsHTML = activeRooms.map(room => { + room.archived = false; return { + room.archived = true; return @@ -35,8 +33,6 @@ const router = function(scope, view) { room: { view: "room", component: () => }, addUsers: { view: "addUsers", component: () =>