PLT-3366 Holding down the ALT key and clicking on a message adds a new messages indicator (squashed) (#3374)

Этот коммит содержится в:
samogot
2016-07-14 15:19:27 +03:00
коммит произвёл Joram Wilander
родитель 6abc9601be
Коммит 9b9facd3d2
14 изменённых файлов: 296 добавлений и 37 удалений

Просмотреть файл

@@ -44,6 +44,7 @@ func InitChannel() {
BaseRoutes.NeedChannel.Handle("/add", ApiUserRequired(addMember)).Methods("POST")
BaseRoutes.NeedChannel.Handle("/remove", ApiUserRequired(removeMember)).Methods("POST")
BaseRoutes.NeedChannel.Handle("/update_last_viewed_at", ApiUserRequired(updateLastViewedAt)).Methods("POST")
BaseRoutes.NeedChannel.Handle("/set_last_viewed_at", ApiUserRequired(setLastViewedAt)).Methods("POST")
}
func createChannel(c *Context, w http.ResponseWriter, r *http.Request) {
@@ -791,6 +792,34 @@ func deleteChannel(c *Context, w http.ResponseWriter, r *http.Request) {
}
}
func setLastViewedAt(c *Context, w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
id := params["channel_id"]
data := model.StringInterfaceFromJson(r.Body)
newLastViewedAt := int64(data["last_viewed_at"].(float64))
Srv.Store.Channel().SetLastViewedAt(id, c.Session.UserId, newLastViewedAt)
preference := model.Preference{
UserId: c.Session.UserId,
Category: model.PREFERENCE_CATEGORY_LAST,
Name: model.PREFERENCE_NAME_LAST_CHANNEL,
Value: id,
}
Srv.Store.Preference().Save(&model.Preferences{preference})
message := model.NewWebSocketEvent(c.TeamId, id, c.Session.UserId, model.WEBSOCKET_EVENT_CHANNEL_VIEWED)
message.Add("channel_id", id)
go Publish(message)
result := make(map[string]string)
result["id"] = id
w.Write([]byte(model.MapToJson(result)))
}
func updateLastViewedAt(c *Context, w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
id := params["channel_id"]

Просмотреть файл

@@ -557,7 +557,7 @@
},
{
"id": "api.command_shortcuts.list",
"translation": "### Keyboard Shortcuts\n\n#### Navigation\n\nALT+UP: Previous channel or direct message in left hand sidebar\nALT+DOWN: Next channel or direct message in left hand sidebar\nALT+SHIFT+UP: Previous channel or direct message in left hand sidebar with unread messages\nALT+SHIFT+DOWN: Next channel or direct message in left hand sidebar with unread messages\nCTRL/CMD+K: Open a quick channel switcher dialog\nCTRL/CMD+SHIFT+A: Open account settings\nCTRL/CMD+SHIFT+M: Open recent mentions\n\n#### Files\n\nCTRL/CMD+U: Upload file(s)\n\n#### Messages\n\nCTRL/CMD+UP (in empty input field): Reprint the previous message or slash command you entered\nCTRL/CMD+DOWN (in empty input field): Reprint the next message or slash command you entered\nUP (in empty input field): Edit your last message in the current channel\n@[character]+TAB: Autocomplete @username beginning with [character]\n:[character]+TAB: Autocomplete emoji beginning with [character]\n\n#### Built-in Browser Commands\n\nALT+LEFT/CMD+[: Previous channel in your history\nALT+RIGHT/CMD+]: Next channel in your history\nCTRL/CMD+PLUS: Increase font size (zoom in)\nCTRL/CMD+MINUS: Decrease font size (zoom out)\nSHIFT+UP (in input field): Highlight text to the previous line\nSHIFT+DOWN (in input field): Highlight text to the next line\nSHIFT+ENTER (in input field): Create a new line\n"
"translation": "### Keyboard Shortcuts\n\n#### Navigation\n\nALT+UP: Previous channel or direct message in left hand sidebar\nALT+DOWN: Next channel or direct message in left hand sidebar\nALT+SHIFT+UP: Previous channel or direct message in left hand sidebar with unread messages\nALT+SHIFT+DOWN: Next channel or direct message in left hand sidebar with unread messages\nCTRL/CMD+K: Open a quick channel switcher dialog\nCTRL/CMD+SHIFT+A: Open account settings\nCTRL/CMD+SHIFT+M: Open recent mentions\n\n#### Files\n\nCTRL/CMD+U: Upload file(s)\n\n#### Messages\n\nALT+Click: Set message as unread\nESC: Set all messages in channel as read\nCTRL/CMD+UP (in empty input field): Reprint the previous message or slash command you entered\nCTRL/CMD+DOWN (in empty input field): Reprint the next message or slash command you entered\nUP (in empty input field): Edit your last message in the current channel\n@[character]+TAB: Autocomplete @username beginning with [character]\n:[character]+TAB: Autocomplete emoji beginning with [character]\n\n#### Built-in Browser Commands\n\nALT+LEFT/CMD+[: Previous channel in your history\nALT+RIGHT/CMD+]: Next channel in your history\nCTRL/CMD+PLUS: Increase font size (zoom in)\nCTRL/CMD+MINUS: Decrease font size (zoom out)\nSHIFT+UP (in input field): Highlight text to the previous line\nSHIFT+DOWN (in input field): Highlight text to the next line\nSHIFT+ENTER (in input field): Create a new line\n"
},
{
"id": "api.command_shortcuts.name",
@@ -3515,6 +3515,10 @@
"id": "store.sql_channel.save_member.save.app_error",
"translation": "We couldn't save the channel member"
},
{
"id": "store.sql_channel.set_last_viewed_at.app_error",
"translation": "We couldn't set the last viewed at time"
},
{
"id": "store.sql_channel.update.app_error",
"translation": "We couldn't update the channel"

Просмотреть файл

@@ -856,6 +856,58 @@ func (s SqlChannelStore) CheckOpenChannelPermissions(teamId string, channelId st
return storeChannel
}
func (s SqlChannelStore) SetLastViewedAt(channelId string, userId string, newLastViewedAt int64) StoreChannel {
storeChannel := make(StoreChannel)
go func() {
result := StoreResult{}
var query string
if utils.Cfg.SqlSettings.DriverName == model.DATABASE_DRIVER_POSTGRES {
query = `UPDATE
ChannelMembers
SET
MentionCount = 0,
MsgCount = Channels.TotalMsgCount - (SELECT COUNT(*)
FROM Posts
WHERE ChannelId = :ChannelId
AND CreateAt > :NewLastViewedAt),
LastViewedAt = :NewLastViewedAt
FROM
Channels
WHERE
Channels.Id = ChannelMembers.ChannelId
AND UserId = :UserId
AND ChannelId = :ChannelId`
} else if utils.Cfg.SqlSettings.DriverName == model.DATABASE_DRIVER_MYSQL {
query = `UPDATE
ChannelMembers, Channels
SET
ChannelMembers.MentionCount = 0,
ChannelMembers.MsgCount = Channels.TotalMsgCount - (SELECT COUNT(*)
FROM Posts
WHERE ChannelId = :ChannelId
AND CreateAt > :NewLastViewedAt),
ChannelMembers.LastViewedAt = :NewLastViewedAt
WHERE
Channels.Id = ChannelMembers.ChannelId
AND UserId = :UserId
AND ChannelId = :ChannelId`
}
_, err := s.GetMaster().Exec(query, map[string]interface{}{"ChannelId": channelId, "UserId": userId, "NewLastViewedAt": newLastViewedAt})
if err != nil {
result.Err = model.NewLocAppError("SqlChannelStore.SetLastViewedAt", "store.sql_channel.set_last_viewed_at.app_error", nil, "channel_id="+channelId+", user_id="+userId+", "+err.Error())
}
storeChannel <- result
close(storeChannel)
}()
return storeChannel
}
func (s SqlChannelStore) UpdateLastViewedAt(channelId string, userId string) StoreChannel {
storeChannel := make(StoreChannel)

Просмотреть файл

@@ -99,6 +99,7 @@ type ChannelStore interface {
CheckOpenChannelPermissions(teamId string, channelId string) StoreChannel
CheckPermissionsToByName(teamId string, channelName string, userId string) StoreChannel
UpdateLastViewedAt(channelId string, userId string) StoreChannel
SetLastViewedAt(channelId string, userId string, newLastViewedAt int64) StoreChannel
IncrementMentionCount(channelId string, userId string) StoreChannel
AnalyticsTypeCount(teamId string, channelType string) StoreChannel
ExtraUpdateByUser(userId string, time int64) StoreChannel

Просмотреть файл

@@ -5,6 +5,8 @@ import {browserHistory} from 'react-router/es6';
import * as Utils from 'utils/utils.jsx';
import TeamStore from 'stores/team_store.jsx';
import UserStore from 'stores/user_store.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import * as AsyncClient from 'utils/async_client.jsx';
import Client from 'utils/web_client.jsx';
export function goToChannel(channel) {
@@ -24,3 +26,13 @@ export function goToChannel(channel) {
export function executeCommand(channelId, message, suggest, success, error) {
Client.executeCommand(channelId, message, suggest, success, error);
}
export function setChannelAsRead(channelIdParam) {
const channelId = channelIdParam || ChannelStore.getCurrentId();
AsyncClient.updateLastViewedAt();
ChannelStore.resetCounts(channelId);
ChannelStore.emitChange();
if (channelId === ChannelStore.getCurrentId()) {
ChannelStore.emitLastViewed(Number.MAX_VALUE, false);
}
}

Просмотреть файл

@@ -6,7 +6,9 @@ import AppDispatcher from 'dispatcher/app_dispatcher.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import PostStore from 'stores/post_store.jsx';
import TeamStore from 'stores/team_store.jsx';
import UserStore from 'stores/user_store.jsx';
import * as PostUtils from 'utils/post_utils.jsx';
import Constants from 'utils/constants.jsx';
const ActionTypes = Constants.ActionTypes;
@@ -62,3 +64,53 @@ export function handleNewPost(post, msg) {
websocketMessageProps
});
}
export function setUnreadPost(channelId, postId) {
let lastViewed = 0;
let ownNewMessage = false;
const post = PostStore.getPost(channelId, postId);
const posts = PostStore.getVisiblePosts(channelId).posts;
var currentUsedId = UserStore.getCurrentId();
if (currentUsedId === post.user_id || PostUtils.isSystemMessage(post)) {
for (const otherPostId in posts) {
if (lastViewed < posts[otherPostId].create_at && currentUsedId !== posts[otherPostId].user_id && !PostUtils.isSystemMessage(posts[otherPostId])) {
lastViewed = posts[otherPostId].create_at;
}
}
if (lastViewed === 0) {
lastViewed = Number.MAX_VALUE;
} else if (lastViewed > post.create_at) {
lastViewed = post.create_at - 1;
ownNewMessage = true;
} else {
lastViewed -= 1;
}
} else {
lastViewed = post.create_at - 1;
}
if (lastViewed === Number.MAX_VALUE) {
AsyncClient.updateLastViewedAt();
ChannelStore.resetCounts(ChannelStore.getCurrentId());
ChannelStore.emitChange();
} else {
let unreadPosts = 0;
for (const otherPostId in posts) {
if (posts[otherPostId].create_at > lastViewed) {
unreadPosts += 1;
}
}
const member = ChannelStore.getMember(channelId);
const channel = ChannelStore.get(channelId);
member.last_viewed_at = lastViewed;
member.msg_count = channel.total_msg_count - unreadPosts;
member.mention_count = 0;
ChannelStore.setChannelMember(member);
ChannelStore.setUnreadCount(channelId);
AsyncClient.setLastViewedAt(lastViewed, channelId);
}
if (channelId === ChannelStore.getCurrentId()) {
ChannelStore.emitLastViewed(lastViewed, ownNewMessage);
}
}

Просмотреть файл

@@ -37,6 +37,11 @@ class EditPostModal extends React.Component {
this.handleEditPostEvent = this.handleEditPostEvent.bind(this);
this.handleKeyDown = this.handleKeyDown.bind(this);
this.onPreferenceChange = this.onPreferenceChange.bind(this);
this.onModalHidden = this.onModalHidden.bind(this);
this.onModalShow = this.onModalShow.bind(this);
this.onModalShown = this.onModalShown.bind(this);
this.onModalHide = this.onModalHide.bind(this);
this.onModalKeyDown = this.onModalKeyDown.bind(this);
this.state = {editText: '', originalText: '', title: '', post_id: '', channel_id: '', comments: 0, refocusId: '', typing: false};
}
@@ -116,46 +121,55 @@ class EditPostModal extends React.Component {
ctrlSend: PreferenceStore.getBool(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'send_on_ctrl_enter')
});
}
componentDidMount() {
var self = this;
$(ReactDOM.findDOMNode(this.refs.modal)).on('hidden.bs.modal', () => {
self.setState({editText: '', originalText: '', title: '', channel_id: '', post_id: '', comments: 0, refocusId: '', error: '', typing: false});
onModalHidden() {
this.setState({editText: '', originalText: '', title: '', channel_id: '', post_id: '', comments: 0, refocusId: '', error: '', typing: false});
}
onModalShow(e) {
var button = e.relatedTarget;
if (!button) {
return;
}
this.setState({
editText: $(button).attr('data-message'),
originalText: $(button).attr('data-message'),
title: $(button).attr('data-title'),
channel_id: $(button).attr('data-channelid'),
post_id: $(button).attr('data-postid'),
comments: $(button).attr('data-comments'),
refocusId: $(button).attr('data-refocusid'),
typing: false
});
$(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', (e) => {
var button = e.relatedTarget;
if (!button) {
return;
}
self.setState({
editText: $(button).attr('data-message'),
originalText: $(button).attr('data-message'),
title: $(button).attr('data-title'),
channel_id: $(button).attr('data-channelid'),
post_id: $(button).attr('data-postid'),
comments: $(button).attr('data-comments'),
refocusId: $(button).attr('data-refocusid'),
typing: false
}
onModalShown() {
this.refs.editbox.focus();
}
onModalHide() {
if (this.state.refocusId !== '') {
setTimeout(() => {
$(this.state.refocusId).get(0).focus();
});
});
$(ReactDOM.findDOMNode(this.refs.modal)).on('shown.bs.modal', () => {
self.refs.editbox.focus();
});
$(ReactDOM.findDOMNode(this.refs.modal)).on('hide.bs.modal', () => {
if (self.state.refocusId !== '') {
setTimeout(() => {
$(self.state.refocusId).get(0).focus();
});
}
});
}
}
onModalKeyDown(e) {
if (e.which === Constants.KeyCodes.ESCAPE) {
e.stopPropagation();
}
}
componentDidMount() {
$(this.refs.modal).on('hidden.bs.modal', this.onModalHidden);
$(this.refs.modal).on('show.bs.modal', this.onModalShow);
$(this.refs.modal).on('shown.bs.modal', this.onModalShown);
$(this.refs.modal).on('hide.bs.modal', this.onModalHide);
$(this.refs.modal).on('keydown', this.onModalKeyDown);
PostStore.addEditPostListener(this.handleEditPostEvent);
PreferenceStore.addChangeListener(this.onPreferenceChange);
}
componentWillUnmount() {
$(this.refs.modal).off('hidden.bs.modal', this.onModalHidden);
$(this.refs.modal).off('show.bs.modal', this.onModalShow);
$(this.refs.modal).off('shown.bs.modal', this.onModalShown);
$(this.refs.modal).off('hide.bs.modal', this.onModalHide);
$(this.refs.modal).off('keydown', this.onModalKeyDown);
PostStore.removeEditPostListner(this.handleEditPostEvent);
PreferenceStore.removeChangeListener(this.onPreferenceChange);
}

Просмотреть файл

@@ -10,6 +10,7 @@ const ActionTypes = Constants.ActionTypes;
import * as Utils from 'utils/utils.jsx';
import * as PostUtils from 'utils/post_utils.jsx';
import AppDispatcher from 'dispatcher/app_dispatcher.jsx';
import * as PostActions from 'actions/post_actions.jsx';
import React from 'react';
@@ -20,6 +21,7 @@ export default class Post extends React.Component {
this.handleCommentClick = this.handleCommentClick.bind(this);
this.handleDropdownOpened = this.handleDropdownOpened.bind(this);
this.forceUpdateInfo = this.forceUpdateInfo.bind(this);
this.handlePostClick = this.handlePostClick.bind(this);
this.state = {
dropdownOpened: false
@@ -47,6 +49,12 @@ export default class Post extends React.Component {
this.refs.info.forceUpdate();
this.refs.header.forceUpdate();
}
handlePostClick(e) {
if (e.altKey) {
e.preventDefault();
PostActions.setUnreadPost(this.props.post.channel_id, this.props.post.id);
}
}
shouldComponentUpdate(nextProps, nextState) {
if (!Utils.areObjectsEqual(nextProps.post, this.props.post)) {
return true;
@@ -213,6 +221,7 @@ export default class Post extends React.Component {
<div
id={'post_' + post.id}
className={'post ' + sameUserClass + ' ' + compactClass + ' ' + rootUser + ' ' + postType + ' ' + currentUserCss + ' ' + shouldHighlightClass + ' ' + systemMessageClass + ' ' + hideControls + ' ' + dropdownOpenedClass}
onClick={this.handlePostClick}
>
<div className={'post__content ' + centerClass}>
{profilePicContainer}

Просмотреть файл

@@ -15,6 +15,8 @@ import * as Utils from 'utils/utils.jsx';
import * as PostUtils from 'utils/post_utils.jsx';
import DelayedAction from 'utils/delayed_action.jsx';
import * as ChannelActions from 'actions/channel_actions.jsx';
import Constants from 'utils/constants.jsx';
const ScrollTypes = Constants.ScrollTypes;
@@ -41,6 +43,7 @@ export default class PostList extends React.Component {
this.handleResize = this.handleResize.bind(this);
this.scrollToBottom = this.scrollToBottom.bind(this);
this.scrollToBottomAnimated = this.scrollToBottomAnimated.bind(this);
this.handleKeyDown = this.handleKeyDown.bind(this);
this.jumpToPostNode = null;
this.wasAtBottom = true;
@@ -61,6 +64,13 @@ export default class PostList extends React.Component {
}
}
handleKeyDown(e) {
if (e.which === Constants.KeyCodes.ESCAPE && $('.popover.in,.modal.in').length === 0) {
e.preventDefault();
ChannelActions.setChannelAsRead();
}
}
isAtBottom() {
// consider the view to be at the bottom if it's within this many pixels of the bottom
const atBottomMargin = 10;
@@ -297,7 +307,7 @@ export default class PostList extends React.Component {
);
}
if (postUserId !== userId &&
if ((postUserId !== userId || this.props.ownNewMessage) &&
this.props.lastViewed !== 0 &&
post.create_at > this.props.lastViewed &&
!renderedLastViewed) {
@@ -417,10 +427,12 @@ export default class PostList extends React.Component {
}
window.addEventListener('resize', this.handleResize);
window.addEventListener('keydown', this.handleKeyDown);
}
componentWillUnmount() {
window.removeEventListener('resize', this.handleResize);
window.removeEventListener('keydown', this.handleKeyDown);
this.scrollStopAction.cancel();
}
@@ -515,7 +527,8 @@ export default class PostList extends React.Component {
}
PostList.defaultProps = {
lastViewed: 0
lastViewed: 0,
ownNewMessage: false
};
PostList.propTypes = {
@@ -529,6 +542,7 @@ PostList.propTypes = {
showMoreMessagesTop: React.PropTypes.bool,
showMoreMessagesBottom: React.PropTypes.bool,
lastViewed: React.PropTypes.number,
ownNewMessage: React.PropTypes.bool,
postsToHighlight: React.PropTypes.object,
displayNameType: React.PropTypes.string,
displayPostsInCenter: React.PropTypes.bool,

Просмотреть файл

@@ -27,6 +27,7 @@ export default class PostViewController extends React.Component {
this.onPostsChange = this.onPostsChange.bind(this);
this.onEmojisChange = this.onEmojisChange.bind(this);
this.onPostsViewJumpRequest = this.onPostsViewJumpRequest.bind(this);
this.onSetNewMessageIndicator = this.onSetNewMessageIndicator.bind(this);
this.onPostListScroll = this.onPostListScroll.bind(this);
this.onActivate = this.onActivate.bind(this);
this.onDeactivate = this.onDeactivate.bind(this);
@@ -50,6 +51,7 @@ export default class PostViewController extends React.Component {
profiles,
atTop: PostStore.getVisibilityAtTop(channel.id),
lastViewed,
ownNewMessage: false,
scrollType: ScrollTypes.NEW_MESSAGE,
displayNameType: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', 'false'),
displayPostsInCenter: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.CHANNEL_DISPLAY_MODE, Preferences.CHANNEL_DISPLAY_MODE_DEFAULT) === Preferences.CHANNEL_DISPLAY_MODE_CENTERED,
@@ -117,6 +119,7 @@ export default class PostViewController extends React.Component {
PostStore.addChangeListener(this.onPostsChange);
PostStore.addPostsViewJumpListener(this.onPostsViewJumpRequest);
EmojiStore.addChangeListener(this.onEmojisChange);
ChannelStore.addLastViewedListener(this.onSetNewMessageIndicator);
}
onDeactivate() {
@@ -125,6 +128,7 @@ export default class PostViewController extends React.Component {
PostStore.removeChangeListener(this.onPostsChange);
PostStore.removePostsViewJumpListener(this.onPostsViewJumpRequest);
EmojiStore.removeChangeListener(this.onEmojisChange);
ChannelStore.removeLastViewedListener(this.onSetNewMessageIndicator);
}
componentWillReceiveProps(nextProps) {
@@ -149,6 +153,7 @@ export default class PostViewController extends React.Component {
this.setState({
channel,
lastViewed,
ownNewMessage: false,
profiles: JSON.parse(JSON.stringify(profiles)),
postList: JSON.parse(JSON.stringify(PostStore.getVisiblePosts(channel.id))),
displayNameType: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', 'false'),
@@ -178,6 +183,10 @@ export default class PostViewController extends React.Component {
}
}
onSetNewMessageIndicator(lastViewed, ownNewMessage) {
this.setState({lastViewed, ownNewMessage});
}
onPostListScroll(atBottom) {
if (atBottom) {
this.setState({scrollType: ScrollTypes.BOTTOM});
@@ -219,6 +228,10 @@ export default class PostViewController extends React.Component {
return true;
}
if (nextState.ownNewMessage !== this.state.ownNewMessage) {
return true;
}
if (nextState.showMoreMessagesTop !== this.state.showMoreMessagesTop) {
return true;
}
@@ -277,6 +290,7 @@ export default class PostViewController extends React.Component {
useMilitaryTime={this.state.useMilitaryTime}
lastViewed={this.state.lastViewed}
emojis={this.state.emojis}
ownNewMessage={this.state.ownNewMessage}
/>
);
}

Просмотреть файл

@@ -128,6 +128,7 @@ export default class SuggestionBox extends React.Component {
e.preventDefault();
} else if (e.which === KeyCodes.ESCAPE) {
GlobalActions.emitClearSuggestions(this.suggestionId);
e.stopPropagation();
} else if (this.props.onKeyDown) {
this.props.onKeyDown(e);
}

Просмотреть файл

@@ -178,6 +178,10 @@
border-radius: 0;
font-weight: 400;
position: relative;
&.unread-title {
font-weight: 600;
}
}
}
}

Просмотреть файл

@@ -13,6 +13,7 @@ const CHANGE_EVENT = 'change';
const LEAVE_EVENT = 'leave';
const MORE_CHANGE_EVENT = 'change';
const EXTRA_INFO_EVENT = 'extra_info';
const LAST_VIEVED_EVENT = 'last_viewed';
class ChannelStoreClass extends EventEmitter {
constructor(props) {
@@ -32,6 +33,9 @@ class ChannelStoreClass extends EventEmitter {
this.emitLeave = this.emitLeave.bind(this);
this.addLeaveListener = this.addLeaveListener.bind(this);
this.removeLeaveListener = this.removeLeaveListener.bind(this);
this.emitLastViewed = this.emitLastViewed.bind(this);
this.addLastViewedListener = this.addLastViewedListener.bind(this);
this.removeLastViewedListener = this.removeLastViewedListener.bind(this);
this.findFirstBy = this.findFirstBy.bind(this);
this.get = this.get.bind(this);
this.getMember = this.getMember.bind(this);
@@ -109,6 +113,18 @@ class ChannelStoreClass extends EventEmitter {
this.removeListener(LEAVE_EVENT, callback);
}
emitLastViewed(lastViewed, ownNewMessage) {
this.emit(LAST_VIEVED_EVENT, lastViewed, ownNewMessage);
}
addLastViewedListener(callback) {
this.on(LAST_VIEVED_EVENT, callback);
}
removeLastViewedListener(callback) {
this.removeListener(LAST_VIEVED_EVENT, callback);
}
findFirstBy(field, value) {
return this.doFindFirst(field, value, this.getChannels());
}

Просмотреть файл

@@ -145,6 +145,43 @@ export function updateLastViewedAt(id) {
);
}
export function setLastViewedAt(lastViewedAt, id) {
let channelId;
if (id) {
channelId = id;
} else {
channelId = ChannelStore.getCurrentId();
}
if (channelId == null) {
return;
}
if (lastViewedAt == null) {
return;
}
if (isCallInProgress(`setLastViewedAt${channelId}${lastViewedAt}`)) {
return;
}
callTracker[`setLastViewedAt${channelId}${lastViewedAt}`] = utils.getTimestamp();
Client.setLastViewedAt(
channelId,
lastViewedAt,
() => {
callTracker.setLastViewedAt = 0;
ErrorStore.clearLastError();
},
(err) => {
callTracker.setLastViewedAt = 0;
var count = ErrorStore.getConnectionErrorCount();
ErrorStore.setConnectionErrorCount(count + 1);
dispatchError(err, 'setLastViewedAt');
}
);
}
export function getMoreChannels(force) {
if (isCallInProgress('getMoreChannels')) {
return;