Closes #625: Filter and sorts direct messages and private groups.

pull/2537/head
Marcelo Schmidt 10 years ago
parent b79a08aeeb
commit ca8d55e1f5
  1. 1
      .jshintrc
  2. 1
      HISTORY.md
  3. 10
      i18n/en.i18n.json
  4. 17
      packages/rocketchat-lib/server/models/Subscriptions.coffee
  5. 7
      packages/rocketchat-theme/assets/stylesheets/base.less
  6. 2
      packages/rocketchat-ui-sidenav/package.js
  7. 2
      packages/rocketchat-ui-sidenav/side-nav/channels.html
  8. 4
      packages/rocketchat-ui-sidenav/side-nav/directMessages.coffee
  9. 1
      packages/rocketchat-ui-sidenav/side-nav/directMessages.html
  10. 67
      packages/rocketchat-ui-sidenav/side-nav/listChannelsFlex.coffee
  11. 27
      packages/rocketchat-ui-sidenav/side-nav/listChannelsFlex.html
  12. 53
      packages/rocketchat-ui-sidenav/side-nav/listDirectMessagesFlex.html
  13. 90
      packages/rocketchat-ui-sidenav/side-nav/listDirectMessagesFlex.js
  14. 49
      packages/rocketchat-ui-sidenav/side-nav/listPrivateGroupsFlex.coffee
  15. 31
      packages/rocketchat-ui-sidenav/side-nav/listPrivateGroupsFlex.html
  16. 2
      packages/rocketchat-ui-sidenav/side-nav/privateGroups.html

@ -97,6 +97,7 @@
"swal" : true,
"t" : true,
"TAPi18n" : true,
"Template" : true,
"toastr" : true,
"Tracker" : true,
"TimeSync" : true

@ -1,6 +1,7 @@
## NEXT
- Fixes #2477: Admin settings, plain-text SMTP password.
- Closes #625: Filter and sorts direct messages and private groups.
## 0.22.0, 2016-Mar-14

@ -89,6 +89,7 @@
"Administration" : "Administration",
"After_OAuth2_authentication_users_will_be_redirected_to_this_URL" : "After OAuth2 authentication, users will be redirected to this URL",
"Alias" : "Alias",
"All" : "All",
"All_channels" : "All channels",
"Allow_Invalid_SelfSigned_Certs" : "Allow Invalid Self-Signed Certs",
"Allow_Invalid_SelfSigned_Certs_Description" : "Allow invalid and self-signed SSL certificate's for link validation and previews.",
@ -172,6 +173,7 @@
"Desktop_Notifications_Enabled" : "Desktop Notifications are Enabled",
"Direct_message_someone" : "Direct message someone",
"Direct_Messages" : "Direct Messages",
"Direct_Messages_list" : "Direct Messages",
"Disable_Favorite_Rooms" : "Disable Favorites",
"Disable_New_Message_Notification" : "Disable New Message Notification",
"Disable_New_Room_Notification" : "Disable New Room Notification",
@ -235,6 +237,7 @@
"GoogleTagManager_id" : "Google Tag Manager Id",
"Has_more" : "Has more",
"Have_your_own_chat" : "Have your own web chat. Developed with Meteor.com, the Rocket.Chat is a great solution for developers looking forward to build and evolve their own chat platform.",
"Hidden" : "Hidden",
"Hide_Group_Warning" : "Are you sure you want to hide the group \"%s\"?",
"Hide_Private_Warning" : "Are you sure you want to hide the discussion with \"%s\"?",
"Hide_room" : "Hide room",
@ -286,6 +289,7 @@
"Join_audio_call" : "Join audio call",
"Join_the_Community" : "Join the Community",
"Join_video_call" : "Join video call",
"Joined" : "Joined",
"Jump" : "Jump",
"Jump_to_first_unread" : "Jump to first unread",
"Jump_to_message" : "Jump to message",
@ -294,6 +298,7 @@
"Language_Version" : "English Version",
"Last_login" : "Last login",
"Last_message" : "Last message",
"Last_seen" : "Last seen",
"Layout" : "Layout",
"Layout_Home_Body" : "Home Body",
"Layout_Home_Title" : "Home Title",
@ -350,6 +355,7 @@
"Leave_room" : "Leave room",
"Leave_Room_Warning" : "Are you sure you want to leave the room \"%s\"?",
"line" : "line",
"List_of_Direct_Messages" : "List of Direct Messages",
"Load_more" : "Load more",
"Loading..." : "Loading...",
"Loading_more_from_history" : "Loading more from history",
@ -401,6 +407,7 @@
"Meta_robots" : "Robots",
"minutes" : "minutes",
"More_channels" : "More channels",
"More_direct_messages" : "More direct messages",
"More_groups" : "More private groups",
"More_unreads" : "More unreads",
"Msgs" : "Msgs",
@ -444,6 +451,7 @@
"Old_and_new_password_required" : "You need to provide both old and new password for changing your password.",
"Online" : "Online",
"Only_you_can_see_this_message" : "Only you can see this message",
"Open" : "Open",
"Oops!" : "Oops",
"Opt_out_statistics" : "Don't send my statistics to Rocket.Chat",
"Opt_out_statistics_warning" : "By sending your statistics, you'll help us identify how many instances of Rocket.Chat are deployed, as well as how good the system is behaving, so we can further improve it. Don't worry, as no user information is sent and all the information we receive is kept confidential. If you want to continue sending us your statistics, uncheck the above checkbox. Thank you.",
@ -550,7 +558,9 @@
"Script_Enabled" : "Script Enabled",
"Search" : "Search",
"Search_Channels" : "Search Channels",
"Search_Direct_Messages" : "Search Direct Messages",
"Search_Messages" : "Search Messages",
"Search_Private_Groups" : "Search Private Groups",
"Search_settings" : "Search settings",
"seconds" : "seconds",
"See_all" : "See all",

@ -34,6 +34,23 @@ RocketChat.models.Subscriptions = new class extends RocketChat.models._Base
return @find query, options
findByType: (types, options) ->
query =
t:
$in: types
return @find query, options
findByNameContainingAndTypes: (name, types, options) ->
nameRegex = new RegExp s.trim(s.escapeRegExp(name)), "i"
query =
t:
$in: types
name: nameRegex
return @find query, options
getLastSeen: (options = {}) ->
query = { ls: { $exists: 1 } }
options.sort = { ls: -1 }

@ -339,7 +339,7 @@ blockquote {
}
.icon-search,
.icon-right-open-small,
.icon-sort-alt-up {
.icon-sort-alt-up, .icon-comment {
position: absolute;
left: 2px;
top: 10px;
@ -1542,6 +1542,11 @@ a.github-fork {
.icon-logout {
margin-left: 1px;
}
&.fixed {
opacity: 1;
.transform(translateX(0));
}
}
i {
font-size: 14px;

@ -29,6 +29,7 @@ Package.onUse(function(api) {
api.addFiles('side-nav/directMessages.html', 'client');
api.addFiles('side-nav/directMessagesFlex.html', 'client');
api.addFiles('side-nav/listChannelsFlex.html', 'client');
api.addFiles('side-nav/listDirectMessagesFlex.html', 'client');
api.addFiles('side-nav/listPrivateGroupsFlex.html', 'client');
api.addFiles('side-nav/privateGroups.html', 'client');
api.addFiles('side-nav/privateGroupsFlex.html', 'client');
@ -44,6 +45,7 @@ Package.onUse(function(api) {
api.addFiles('side-nav/directMessages.coffee', 'client');
api.addFiles('side-nav/directMessagesFlex.coffee', 'client');
api.addFiles('side-nav/listChannelsFlex.coffee', 'client');
api.addFiles('side-nav/listDirectMessagesFlex.js', 'client');
api.addFiles('side-nav/listPrivateGroupsFlex.coffee', 'client');
api.addFiles('side-nav/privateGroups.coffee', 'client');
api.addFiles('side-nav/privateGroupsFlex.coffee', 'client');

@ -12,5 +12,5 @@
<p class="empty">{{_ "No_channels_yet" }}</p>
{{/each}}
</ul>
<a href="" class="more more-channels">{{_ "More_channels"}} ..</a>
<a href="" class="more more-channels">{{_ "More_channels"}}...</a>
</template>

@ -14,3 +14,7 @@ Template.directMessages.events
'click .add-room': (e, instance) ->
SideNav.setFlex "directMessagesFlex"
SideNav.openFlex()
'click .more-direct-messages': ->
SideNav.setFlex "listDirectMessagesFlex"
SideNav.openFlex()

@ -9,5 +9,6 @@
{{else}}
<p class="empty">{{_ "No_direct_messages_yet" }}</p>
{{/each}}
<a href="" class="more more-direct-messages">{{_ "More_direct_messages"}}...</a>
</ul>
</template>

@ -3,10 +3,16 @@ Template.listChannelsFlex.helpers
return Template.instance().channelsList?.get()
hasMore: ->
return Template.instance().hasMore.get()
tSearchChannels: ->
return t('Search_Channels')
sortSelected: (sort) ->
return Template.instance().sort.get() is sort
sortChannelsSelected: (sort) ->
return Template.instance().sortChannels.get() is sort
sortSubscriptionsSelected: (sort) ->
return Template.instance().sortSubscriptions.get() is sort
showSelected: (show) ->
return Template.instance().show.get() is show
member: ->
return !!RocketChat.models.Subscriptions.findOne({ name: @name, open: true })
hidden: ->
return !!RocketChat.models.Subscriptions.findOne({ name: @name, open: false })
Template.listChannelsFlex.events
'click header': ->
@ -26,7 +32,7 @@ Template.listChannelsFlex.events
SideNav.leaveArrow()
'scroll .content': _.throttle (e, t) ->
if e.target.scrollTop >= e.target.scrollHeight - e.target.clientHeight
if t.hasMore.get() and e.target.scrollTop >= e.target.scrollHeight - e.target.clientHeight
t.limit.set(t.limit.get() + 50)
, 200
@ -34,20 +40,53 @@ Template.listChannelsFlex.events
instance.nameFilter.set($(e.currentTarget).val())
, 300
'change #sort': (e, instance) ->
instance.sort.set($(e.currentTarget).val())
'change #sort-channels': (e, instance) ->
instance.sortChannels.set($(e.currentTarget).val())
'change #sort-subscriptions': (e, instance) ->
instance.sortSubscriptions.set($(e.currentTarget).val())
'change #show': (e, instance) ->
show = $(e.currentTarget).val()
if show is 'joined'
instance.$('#sort-channels').hide();
instance.$('#sort-subscriptions').show();
else
instance.$('#sort-channels').show();
instance.$('#sort-subscriptions').hide();
instance.show.set(show)
Template.listChannelsFlex.onCreated ->
@channelsList = new ReactiveVar []
@hasMore = new ReactiveVar true
@limit = new ReactiveVar 50
@nameFilter = new ReactiveVar ''
@sort = new ReactiveVar 'msgs'
@sortChannels = new ReactiveVar 'msgs'
@sortSubscriptions = new ReactiveVar 'name'
@show = new ReactiveVar 'joined'
@autorun =>
Meteor.call 'channelsList', @nameFilter.get(), @limit.get(), @sort.get(), (err, result) =>
if result
@hasMore.set true
@channelsList.set result.channels
if result.channels.length < @limit.get()
@hasMore.set false
if @show.get() is 'joined'
@hasMore.set true
options = { fields: { name: 1 } }
if _.isNumber @limit.get()
options.limit = @limit.get()
if _.trim(@sortSubscriptions.get())
switch @sortSubscriptions.get()
when 'name'
options.sort = { name: 1 }
when 'ls'
options.sort = { ls: -1 }
@channelsList.set RocketChat.models.Subscriptions.find({
name: new RegExp s.trim(s.escapeRegExp(@nameFilter.get())), "i"
t: 'c'
}, options).fetch()
if @channelsList.get().length < @limit.get()
@hasMore.set false
else
Meteor.call 'channelsList', @nameFilter.get(), @limit.get(), @sortChannels.get(), (err, result) =>
if result
@hasMore.set true
@channelsList.set result.channels
if result.channels.length < @limit.get()
@hasMore.set false

@ -10,16 +10,27 @@
<div class="search">
<form class="search-form" role="form">
<div class="input-line search">
<input type="text" id="channel-search" class="search" placeholder="{{tSearchChannels}}" autocomplete="off" />
<input type="text" id="channel-search" class="search" placeholder="{{_ "Search_Channels"}}" autocomplete="off" />
<i class="icon-right-open-small"></i>
</div>
<div class="input-line search">
<select class="c-select" id="sort">
<option value="name" selected="{{sortSelected 'name'}}">{{_ "Name"}}</option>
<option value="msgs" selected="{{sortSelected 'msgs'}}">{{_ "Number_of_messages"}}</option>
<select class="c-select" id="sort-channels" style="display: none">
<option value="name" selected="{{sortChannelsSelected 'name'}}">{{_ "Name"}}</option>
<option value="msgs" selected="{{sortChannelsSelected 'msgs'}}">{{_ "Number_of_messages"}}</option>
</select>
<select class="c-select" id="sort-subscriptions">
<option value="name" selected="{{sortSubscriptionsSelected 'name'}}">{{_ "Name"}}</option>
<option value="ls" selected="{{sortSubscriptionsSelected 'ls'}}">{{_ "Last_seen"}}</option>
</select>
<i class="icon-sort-alt-up"></i>
</div>
<div class="input-line search">
<select class="c-select" id="show">
<option value="joined" selected="{{showSelected 'joined'}}">{{_ "Joined"}}</option>
<option value="all" selected="{{showSelected 'all'}}">{{_ "All"}}</option>
</select>
<i class="icon-comment"></i>
</div>
</form>
</div>
</div>
@ -30,6 +41,14 @@
<a href="{{pathFor 'channel' name=name}}" class="channel-link">
<i class="icon-hash"></i>
{{name}}
<span class='opt fixed'>
{{#if member}}
<i class="icon-eye" title="{{_ "Open"}}" aria-label="{{_ "Open"}}"></i>
{{/if}}
{{#if hidden}}
<i class="icon-eye-off" title="{{_ "Hidden"}}" aria-label="{{_ "Hidden"}}"></i>
{{/if}}
</span>
</a>
</li>
{{/each}}

@ -0,0 +1,53 @@
<template name="listDirectMessagesFlex">
<header>
<div>
<h4>{{_ "Direct_Messages"}}</h4>
</div>
</header>
<div class="content">
<div class="wrapper">
<div class="flex-control">
<div class="search">
<form class="search-form" role="form">
<div class="input-line search">
<input type="text" id="channel-search" class="search" placeholder="{{_ "Search_Direct_Messages"}}" autocomplete="off" />
<i class="icon-right-open-small"></i>
</div>
<div class="input-line search">
<select class="c-select" id="sort">
<option value="name" selected="{{sortSelected 'name'}}">{{_ "Name"}}</option>
<option value="ls" selected="{{sortSelected 'ls'}}">{{_ "Last_seen"}}</option>
</select>
<i class="icon-sort-alt-up"></i>
</div>
</form>
</div>
</div>
<h4>{{_ "List_of_Direct_Messages"}}</h4>
<ul>
{{#each rooms}}
<li>
<a href="{{pathFor 'direct' username=name}}" class="channel-link">
<i class="icon-at {{userStatus}}"></i>
{{name}}
<span class='opt fixed'>
{{#if hidden}}
<i class="icon-eye-off" title="{{_ "Hidden"}}" aria-label="{{_ "Hidden"}}"></i>
{{/if}}
</span>
</a>
</li>
{{/each}}
{{#if hasMore}}
<li class="load-more">
{{#if Template.subscriptionsReady}}
<a href="">{{_ "Has_more"}}...</a>
{{else}}
<div class="load-more-loading">{{_ "Loading..."}}</div>
{{/if}}
</li>
{{/if}}
</ul>
</div>
</div>
</template>

@ -0,0 +1,90 @@
/* globals SideNav */
Template.listDirectMessagesFlex.helpers({
rooms() {
return Template.instance().roomsList.get();
},
sortSelected(sort) {
return Template.instance().sort.get() === sort;
},
hasMore() {
return Template.instance().hasMore.get();
},
userStatus() {
return 'status-' + (Session.get('user_' + this.name + '_status') || 'offline');
},
hidden() {
return !!RocketChat.models.Subscriptions.findOne({ name: this.name, open: false });
}
});
Template.listDirectMessagesFlex.events({
'click header': function() {
SideNav.closeFlex();
},
'click .channel-link': function() {
SideNav.closeFlex();
},
'click footer .create': function() {
SideNav.setFlex('privateGroupsFlex');
},
'mouseenter header': function() {
SideNav.overArrow();
},
'mouseleave header': function() {
SideNav.leaveArrow();
},
'keyup #channel-search': _.debounce((e, instance) => {
instance.nameFilter.set($(e.currentTarget).val());
}, 300),
'scroll .content': _.throttle((e, instance) => {
if (instance.hasMore.get() && e.target.scrollTop >= e.target.scrollHeight - e.target.clientHeight) {
instance.limit.set(instance.limit.get() + 50);
}
} , 200),
'change #sort': (e, instance) => {
instance.sort.set($(e.currentTarget).val());
}
});
Template.listDirectMessagesFlex.onCreated(function() {
this.limit = new ReactiveVar(50);
this.sort = new ReactiveVar('name');
this.hasMore = new ReactiveVar(true);
this.nameFilter = new ReactiveVar('');
this.roomsList = new ReactiveVar([]);
this.autorun(() => {
this.hasMore.set(true);
let options = { fields: { name: 1 } };
if (_.isNumber(this.limit.get())) {
options.limit = this.limit.get();
}
if(s.trim(this.sort.get())) {
switch (this.sort.get()) {
case 'name':
options.sort = { name: 1 };
break;
case 'ls':
options.sort = { ls: -1 };
break;
}
}
let query = { t: 'd' };
if (s.trim(this.nameFilter.get())) {
query.name = new RegExp(s.trim(s.escapeRegExp(this.nameFilter.get())), 'i');
}
this.roomsList.set(RocketChat.models.Subscriptions.find(query, options).fetch());
if (this.roomsList.get().length < this.limit.get()) {
this.hasMore.set(false);
}
});
});

@ -1,6 +1,12 @@
Template.listPrivateGroupsFlex.helpers
groups: ->
return ChatSubscription.find { t: { $in: ['p']}, f: { $ne: true }, archived: { $ne: true } }, { sort: 't': 1, 'name': 1 }
return Template.instance().groups.get()
hasMore: ->
return Template.instance().hasMore.get()
sortSelected: (sort) ->
return Template.instance().sort.get() is sort
hidden: ->
return !!RocketChat.models.Subscriptions.findOne({ name: @name, open: false })
Template.listPrivateGroupsFlex.events
'click header': ->
@ -17,3 +23,44 @@ Template.listPrivateGroupsFlex.events
'mouseleave header': ->
SideNav.leaveArrow()
'scroll .content': _.throttle (e, t) ->
if t.hasMore.get() and e.target.scrollTop >= e.target.scrollHeight - e.target.clientHeight
t.limit.set(t.limit.get() + 50)
, 200
'keyup #channel-search': _.debounce (e, instance) ->
instance.nameFilter.set($(e.currentTarget).val())
, 300
'change #sort': (e, instance) ->
instance.sort.set($(e.currentTarget).val())
Template.listPrivateGroupsFlex.onCreated ->
@groups = new ReactiveVar []
@hasMore = new ReactiveVar true
@limit = new ReactiveVar 50
@nameFilter = new ReactiveVar ''
@sort = new ReactiveVar 'name'
@autorun =>
@hasMore.set true
options = { fields: { name: 1 } }
if _.isNumber @limit.get()
options.limit = @limit.get()
if _.trim(@sort.get())
switch @sort.get()
when 'name'
options.sort = { name: 1 }
when 'ls'
options.sort = { ls: -1 }
query = { t: { $in: ['p']}, f: { $ne: true }, archived: { $ne: true } }
@groups.set RocketChat.models.Subscriptions.find({
name: new RegExp s.trim(s.escapeRegExp(@nameFilter.get())), "i"
t: 'p'
archived: { $ne: true }
}, options).fetch()
if @groups.get().length < @limit.get()
@hasMore.set false

@ -6,6 +6,23 @@
</header>
<div class="content">
<div class="wrapper">
<div class="flex-control">
<div class="search">
<form class="search-form" role="form">
<div class="input-line search">
<input type="text" id="channel-search" class="search" placeholder="{{_ "Search_Private_Groups"}}" autocomplete="off" />
<i class="icon-right-open-small"></i>
</div>
<div class="input-line search">
<select class="c-select" id="sort">
<option value="name" selected="{{sortSelected 'name'}}">{{_ "Name"}}</option>
<option value="ls" selected="{{sortSelected 'ls'}}">{{_ "Last_seen"}}</option>
</select>
<i class="icon-sort-alt-up"></i>
</div>
</form>
</div>
</div>
<h4>{{_ "Private_Groups_list"}}</h4>
<ul>
{{#each groups}}
@ -13,9 +30,23 @@
<a href="{{pathFor 'group' name=name}}" class="channel-link">
<i class="icon-lock"></i>
{{name}}
<span class='opt fixed'>
{{#if hidden}}
<i class="icon-eye-off" title="{{_ "Hidden"}}" aria-label="{{_ "Hidden"}}"></i>
{{/if}}
</span>
</a>
</li>
{{/each}}
{{#if hasMore}}
<li class="load-more">
{{#if Template.subscriptionsReady}}
<a href="">{{_ "Has_more"}}...</a>
{{else}}
<div class="load-more-loading">{{_ "Loading..."}}</div>
{{/if}}
</li>
{{/if}}
</ul>
</div>
</div>

@ -13,6 +13,6 @@
{{/each}}
</ul>
{{#if $gt total totalOpen}}
<a href="" class="more more-groups">{{_ "More_groups"}} ..</a>
<a href="" class="more more-groups">{{_ "More_groups"}}...</a>
{{/if}}
</template>

Loading…
Cancel
Save