Implementing Permalinks and jumping to post from search. Performance
improvements.
Этот коммит содержится в:
@@ -37,6 +37,8 @@ type Page struct {
|
|||||||
ClientCfg map[string]string
|
ClientCfg map[string]string
|
||||||
User *model.User
|
User *model.User
|
||||||
Team *model.Team
|
Team *model.Team
|
||||||
|
Channel *model.Channel
|
||||||
|
PostID string
|
||||||
SessionTokenIndex int64
|
SessionTokenIndex int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
36
api/post.go
36
api/post.go
@@ -23,6 +23,7 @@ func InitPost(r *mux.Router) {
|
|||||||
l4g.Debug("Initializing post api routes")
|
l4g.Debug("Initializing post api routes")
|
||||||
|
|
||||||
r.Handle("/posts/search", ApiUserRequired(searchPosts)).Methods("GET")
|
r.Handle("/posts/search", ApiUserRequired(searchPosts)).Methods("GET")
|
||||||
|
r.Handle("/posts/{post_id}", ApiUserRequired(getPostById)).Methods("GET")
|
||||||
|
|
||||||
sr := r.PathPrefix("/channels/{id:[A-Za-z0-9]+}").Subrouter()
|
sr := r.PathPrefix("/channels/{id:[A-Za-z0-9]+}").Subrouter()
|
||||||
sr.Handle("/create", ApiUserRequired(createPost)).Methods("POST")
|
sr.Handle("/create", ApiUserRequired(createPost)).Methods("POST")
|
||||||
@@ -767,6 +768,41 @@ func getPost(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getPostById(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
params := mux.Vars(r)
|
||||||
|
|
||||||
|
postId := params["post_id"]
|
||||||
|
if len(postId) != 26 {
|
||||||
|
c.SetInvalidParam("getPostById", "postId")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if result := <-Srv.Store.Post().Get(postId); result.Err != nil {
|
||||||
|
c.Err = result.Err
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
list := result.Data.(*model.PostList)
|
||||||
|
|
||||||
|
if len(list.Order) != 1 {
|
||||||
|
c.Err = model.NewAppError("getPostById", "Unable to get post", "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
post := list.Posts[list.Order[0]]
|
||||||
|
|
||||||
|
cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, post.ChannelId, c.Session.UserId)
|
||||||
|
if !c.HasPermissionsToChannel(cchan, "getPostById") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if HandleEtag(list.Etag(), w, r) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set(model.HEADER_ETAG_SERVER, list.Etag())
|
||||||
|
w.Write([]byte(list.ToJson()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func deletePost(c *Context, w http.ResponseWriter, r *http.Request) {
|
func deletePost(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
params := mux.Vars(r)
|
params := mux.Vars(r)
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,13 @@
|
|||||||
import TutorialIntroScreens from './tutorial/tutorial_intro_screens.jsx';
|
import TutorialIntroScreens from './tutorial/tutorial_intro_screens.jsx';
|
||||||
import CreatePost from './create_post.jsx';
|
import CreatePost from './create_post.jsx';
|
||||||
import PostsViewContainer from './posts_view_container.jsx';
|
import PostsViewContainer from './posts_view_container.jsx';
|
||||||
|
import PostFocusView from './post_focus_view.jsx';
|
||||||
import ChannelHeader from './channel_header.jsx';
|
import ChannelHeader from './channel_header.jsx';
|
||||||
import Navbar from './navbar.jsx';
|
import Navbar from './navbar.jsx';
|
||||||
import FileUploadOverlay from './file_upload_overlay.jsx';
|
import FileUploadOverlay from './file_upload_overlay.jsx';
|
||||||
|
|
||||||
import PreferenceStore from '../stores/preference_store.jsx';
|
import PreferenceStore from '../stores/preference_store.jsx';
|
||||||
|
import ChannelStore from '../stores/channel_store.jsx';
|
||||||
import UserStore from '../stores/user_store.jsx';
|
import UserStore from '../stores/user_store.jsx';
|
||||||
|
|
||||||
import Constants from '../utils/constants.jsx';
|
import Constants from '../utils/constants.jsx';
|
||||||
@@ -20,26 +22,48 @@ export default class CenterPanel extends React.Component {
|
|||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.onPreferenceChange = this.onPreferenceChange.bind(this);
|
this.onPreferenceChange = this.onPreferenceChange.bind(this);
|
||||||
|
this.onChannelChange = this.onChannelChange.bind(this);
|
||||||
|
|
||||||
const tutorialPref = PreferenceStore.getPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), {value: '999'});
|
const tutorialPref = PreferenceStore.getPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), {value: '999'});
|
||||||
this.state = {showTutorialScreens: parseInt(tutorialPref.value, 10) === TutorialSteps.INTRO_SCREENS};
|
this.state = {
|
||||||
|
showTutorialScreens: parseInt(tutorialPref.value, 10) === TutorialSteps.INTRO_SCREENS,
|
||||||
|
showPostFocus: ChannelStore.getPostMode() === ChannelStore.POST_MODE_FOCUS
|
||||||
|
};
|
||||||
}
|
}
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
PreferenceStore.addChangeListener(this.onPreferenceChange);
|
PreferenceStore.addChangeListener(this.onPreferenceChange);
|
||||||
|
ChannelStore.addChangeListener(this.onChannelChange);
|
||||||
}
|
}
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
PreferenceStore.removeChangeListener(this.onPreferenceChange);
|
PreferenceStore.removeChangeListener(this.onPreferenceChange);
|
||||||
|
ChannelStore.removeChangeListener(this.onChannelChange);
|
||||||
}
|
}
|
||||||
onPreferenceChange() {
|
onPreferenceChange() {
|
||||||
const tutorialPref = PreferenceStore.getPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), {value: '999'});
|
const tutorialPref = PreferenceStore.getPreference(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), {value: '999'});
|
||||||
this.setState({showTutorialScreens: parseInt(tutorialPref.value, 10) <= TutorialSteps.INTRO_SCREENS});
|
this.setState({showTutorialScreens: parseInt(tutorialPref.value, 10) <= TutorialSteps.INTRO_SCREENS});
|
||||||
}
|
}
|
||||||
|
onChannelChange() {
|
||||||
|
this.setState({showPostFocus: ChannelStore.getPostMode() === ChannelStore.POST_MODE_FOCUS});
|
||||||
|
}
|
||||||
render() {
|
render() {
|
||||||
let postsContainer;
|
let postsContainer;
|
||||||
|
let createPost;
|
||||||
if (this.state.showTutorialScreens) {
|
if (this.state.showTutorialScreens) {
|
||||||
postsContainer = <TutorialIntroScreens />;
|
postsContainer = <TutorialIntroScreens />;
|
||||||
|
createPost = null;
|
||||||
|
} else if (this.state.showPostFocus) {
|
||||||
|
postsContainer = <PostFocusView />;
|
||||||
|
createPost = null;
|
||||||
} else {
|
} else {
|
||||||
postsContainer = <PostsViewContainer />;
|
postsContainer = <PostsViewContainer />;
|
||||||
|
createPost = (
|
||||||
|
<div
|
||||||
|
className='post-create__container'
|
||||||
|
id='post-create'
|
||||||
|
>
|
||||||
|
<CreatePost />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -62,12 +86,7 @@ export default class CenterPanel extends React.Component {
|
|||||||
<ChannelHeader />
|
<ChannelHeader />
|
||||||
</div>
|
</div>
|
||||||
{postsContainer}
|
{postsContainer}
|
||||||
<div
|
{createPost}
|
||||||
className='post-create__container'
|
|
||||||
id='post-create'
|
|
||||||
>
|
|
||||||
<CreatePost />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -22,6 +22,17 @@ export default class ChannelInviteModal extends React.Component {
|
|||||||
|
|
||||||
this.state = this.getStateFromStores();
|
this.state = this.getStateFromStores();
|
||||||
}
|
}
|
||||||
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
|
if (!Utils.areObjectsEqual(this.props, nextProps)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Utils.areObjectsEqual(this.state, nextState)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
getStateFromStores() {
|
getStateFromStores() {
|
||||||
function getId(user) {
|
function getId(user) {
|
||||||
return user.id;
|
return user.id;
|
||||||
@@ -105,7 +116,7 @@ export default class ChannelInviteModal extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.setState({inviteError: null, memberIds, nonmembers});
|
this.setState({inviteError: null, memberIds, nonmembers});
|
||||||
AsyncClient.getChannelExtraInfo(true);
|
AsyncClient.getChannelExtraInfo();
|
||||||
},
|
},
|
||||||
(err) => {
|
(err) => {
|
||||||
this.setState({inviteError: err.message});
|
this.setState({inviteError: err.message});
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ export default class ChannelLoader extends React.Component {
|
|||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
/* Initial aysnc loads */
|
/* Initial aysnc loads */
|
||||||
AsyncClient.getPosts(ChannelStore.getCurrentId());
|
AsyncClient.getPosts(ChannelStore.getCurrentId());
|
||||||
AsyncClient.getChannels(true, true);
|
AsyncClient.getChannels();
|
||||||
AsyncClient.getChannelExtraInfo(true);
|
AsyncClient.getChannelExtraInfo();
|
||||||
AsyncClient.findTeams();
|
AsyncClient.findTeams();
|
||||||
AsyncClient.getMyTeam();
|
AsyncClient.getMyTeam();
|
||||||
setTimeout(() => AsyncClient.getStatuses(), 3000); // temporary until statuses are reworked a bit
|
setTimeout(() => AsyncClient.getStatuses(), 3000); // temporary until statuses are reworked a bit
|
||||||
|
|||||||
@@ -25,6 +25,17 @@ export default class ChannelMembersModal extends React.Component {
|
|||||||
state.showInviteModal = false;
|
state.showInviteModal = false;
|
||||||
this.state = state;
|
this.state = state;
|
||||||
}
|
}
|
||||||
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
|
if (!Utils.areObjectsEqual(this.props, nextProps)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Utils.areObjectsEqual(this.state, nextState)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
getStateFromStores() {
|
getStateFromStores() {
|
||||||
const users = UserStore.getActiveOnlyProfiles();
|
const users = UserStore.getActiveOnlyProfiles();
|
||||||
const memberList = ChannelStore.getCurrentExtraInfo().members;
|
const memberList = ChannelStore.getCurrentExtraInfo().members;
|
||||||
@@ -74,6 +85,7 @@ export default class ChannelMembersModal extends React.Component {
|
|||||||
if ($(window).width() > 768) {
|
if ($(window).width() > 768) {
|
||||||
$(ReactDOM.findDOMNode(this.refs.modalBody)).perfectScrollbar();
|
$(ReactDOM.findDOMNode(this.refs.modalBody)).perfectScrollbar();
|
||||||
}
|
}
|
||||||
|
this.onChange();
|
||||||
}
|
}
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
if (this.props.show && !prevProps.show) {
|
if (this.props.show && !prevProps.show) {
|
||||||
@@ -130,7 +142,7 @@ export default class ChannelMembersModal extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.setState({memberList, nonmemberList});
|
this.setState({memberList, nonmemberList});
|
||||||
AsyncClient.getChannelExtraInfo(true);
|
AsyncClient.getChannelExtraInfo();
|
||||||
},
|
},
|
||||||
(err) => {
|
(err) => {
|
||||||
this.setState({inviteError: err.message});
|
this.setState({inviteError: err.message});
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import FilePreview from './file_preview.jsx';
|
|||||||
import TutorialTip from './tutorial/tutorial_tip.jsx';
|
import TutorialTip from './tutorial/tutorial_tip.jsx';
|
||||||
|
|
||||||
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
|
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
|
||||||
|
import * as EventHelpers from '../dispatcher/event_helpers.jsx';
|
||||||
import * as Client from '../utils/client.jsx';
|
import * as Client from '../utils/client.jsx';
|
||||||
import * as AsyncClient from '../utils/async_client.jsx';
|
import * as AsyncClient from '../utils/async_client.jsx';
|
||||||
import * as Utils from '../utils/utils.jsx';
|
import * as Utils from '../utils/utils.jsx';
|
||||||
@@ -19,6 +20,7 @@ import PreferenceStore from '../stores/preference_store.jsx';
|
|||||||
import SocketStore from '../stores/socket_store.jsx';
|
import SocketStore from '../stores/socket_store.jsx';
|
||||||
|
|
||||||
import Constants from '../utils/constants.jsx';
|
import Constants from '../utils/constants.jsx';
|
||||||
|
|
||||||
const Preferences = Constants.Preferences;
|
const Preferences = Constants.Preferences;
|
||||||
const TutorialSteps = Constants.TutorialSteps;
|
const TutorialSteps = Constants.TutorialSteps;
|
||||||
const ActionTypes = Constants.ActionTypes;
|
const ActionTypes = Constants.ActionTypes;
|
||||||
@@ -176,9 +178,7 @@ export default class CreatePost extends React.Component {
|
|||||||
|
|
||||||
const channel = ChannelStore.get(this.state.channelId);
|
const channel = ChannelStore.get(this.state.channelId);
|
||||||
|
|
||||||
PostStore.storePendingPost(post);
|
EventHelpers.emitUserPostedEvent(post);
|
||||||
PostStore.storeDraft(channel.id, null);
|
|
||||||
PostStore.jumpPostsViewToBottom();
|
|
||||||
this.setState({messageText: '', submitting: false, postError: null, previews: [], serverError: null});
|
this.setState({messageText: '', submitting: false, postError: null, previews: [], serverError: null});
|
||||||
|
|
||||||
Client.createPost(post, channel,
|
Client.createPost(post, channel,
|
||||||
@@ -190,10 +190,7 @@ export default class CreatePost extends React.Component {
|
|||||||
member.last_viewed_at = Date.now();
|
member.last_viewed_at = Date.now();
|
||||||
ChannelStore.setChannelMember(member);
|
ChannelStore.setChannelMember(member);
|
||||||
|
|
||||||
AppDispatcher.handleServerAction({
|
EventHelpers.emitPostRecievedEvent(data);
|
||||||
type: ActionTypes.RECIEVED_POST,
|
|
||||||
post: data
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
(err) => {
|
(err) => {
|
||||||
const state = {};
|
const state = {};
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ export default class Post extends React.Component {
|
|||||||
} else {
|
} else {
|
||||||
commentRootId = post.id;
|
commentRootId = post.id;
|
||||||
}
|
}
|
||||||
for (let postId in posts) {
|
for (const postId in posts) {
|
||||||
if (posts[postId].root_id === commentRootId) {
|
if (posts[postId].root_id === commentRootId) {
|
||||||
commentCount += 1;
|
commentCount += 1;
|
||||||
}
|
}
|
||||||
@@ -114,53 +114,58 @@ export default class Post extends React.Component {
|
|||||||
return commentCount;
|
return commentCount;
|
||||||
}
|
}
|
||||||
render() {
|
render() {
|
||||||
var post = this.props.post;
|
const post = this.props.post;
|
||||||
var parentPost = this.props.parentPost;
|
const parentPost = this.props.parentPost;
|
||||||
var posts = this.props.posts;
|
const posts = this.props.posts;
|
||||||
|
|
||||||
if (!post.props) {
|
if (!post.props) {
|
||||||
post.props = {};
|
post.props = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
var type = 'Post';
|
let type = 'Post';
|
||||||
if (post.root_id && post.root_id.length > 0) {
|
if (post.root_id && post.root_id.length > 0) {
|
||||||
type = 'Comment';
|
type = 'Comment';
|
||||||
}
|
}
|
||||||
|
|
||||||
const commentCount = this.getCommentCount(this.props);
|
const commentCount = this.getCommentCount(this.props);
|
||||||
|
|
||||||
var rootUser;
|
let rootUser;
|
||||||
if (this.props.sameRoot) {
|
if (this.props.sameRoot) {
|
||||||
rootUser = 'same--root';
|
rootUser = 'same--root';
|
||||||
} else {
|
} else {
|
||||||
rootUser = 'other--root';
|
rootUser = 'other--root';
|
||||||
}
|
}
|
||||||
|
|
||||||
var postType = '';
|
let postType = '';
|
||||||
if (type !== 'Post') {
|
if (type !== 'Post') {
|
||||||
postType = 'post--comment';
|
postType = 'post--comment';
|
||||||
} else if (commentCount > 0) {
|
} else if (commentCount > 0) {
|
||||||
postType = 'post--root';
|
postType = 'post--root';
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentUserCss = '';
|
let currentUserCss = '';
|
||||||
if (UserStore.getCurrentId() === post.user_id && !post.props.from_webhook) {
|
if (UserStore.getCurrentId() === post.user_id && !post.props.from_webhook) {
|
||||||
currentUserCss = 'current--user';
|
currentUserCss = 'current--user';
|
||||||
}
|
}
|
||||||
|
|
||||||
var userProfile = UserStore.getProfile(post.user_id);
|
const userProfile = UserStore.getProfile(post.user_id);
|
||||||
|
|
||||||
var timestamp = UserStore.getCurrentUser().update_at;
|
let timestamp = UserStore.getCurrentUser().update_at;
|
||||||
if (userProfile) {
|
if (userProfile) {
|
||||||
timestamp = userProfile.update_at;
|
timestamp = userProfile.update_at;
|
||||||
}
|
}
|
||||||
|
|
||||||
var sameUserClass = '';
|
let sameUserClass = '';
|
||||||
if (this.props.sameUser) {
|
if (this.props.sameUser) {
|
||||||
sameUserClass = 'same--user';
|
sameUserClass = 'same--user';
|
||||||
}
|
}
|
||||||
|
|
||||||
var profilePic = null;
|
let shouldHighlightClass = '';
|
||||||
|
if (this.props.shouldHighlight) {
|
||||||
|
shouldHighlightClass = 'post--highlight';
|
||||||
|
}
|
||||||
|
|
||||||
|
let profilePic = null;
|
||||||
if (!this.props.hideProfilePic) {
|
if (!this.props.hideProfilePic) {
|
||||||
let src = '/api/v1/users/' + post.user_id + '/image?time=' + timestamp + '&' + utils.getSessionIndex();
|
let src = '/api/v1/users/' + post.user_id + '/image?time=' + timestamp + '&' + utils.getSessionIndex();
|
||||||
if (post.props && post.props.from_webhook && global.window.mm_config.EnablePostIconOverride === 'true') {
|
if (post.props && post.props.from_webhook && global.window.mm_config.EnablePostIconOverride === 'true') {
|
||||||
@@ -182,7 +187,7 @@ export default class Post extends React.Component {
|
|||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
id={'post_' + post.id}
|
id={'post_' + post.id}
|
||||||
className={'post ' + sameUserClass + ' ' + rootUser + ' ' + postType + ' ' + currentUserCss}
|
className={'post ' + sameUserClass + ' ' + rootUser + ' ' + postType + ' ' + currentUserCss + ' ' + shouldHighlightClass}
|
||||||
>
|
>
|
||||||
<div className='post__content'>
|
<div className='post__content'>
|
||||||
<div className='post__img'>{profilePic}</div>
|
<div className='post__img'>{profilePic}</div>
|
||||||
@@ -218,5 +223,6 @@ Post.propTypes = {
|
|||||||
sameUser: React.PropTypes.bool,
|
sameUser: React.PropTypes.bool,
|
||||||
sameRoot: React.PropTypes.bool,
|
sameRoot: React.PropTypes.bool,
|
||||||
hideProfilePic: React.PropTypes.bool,
|
hideProfilePic: React.PropTypes.bool,
|
||||||
isLastComment: React.PropTypes.bool
|
isLastComment: React.PropTypes.bool,
|
||||||
|
shouldHighlight: React.PropTypes.bool
|
||||||
};
|
};
|
||||||
|
|||||||
110
web/react/components/post_focus_view.jsx
Обычный файл
110
web/react/components/post_focus_view.jsx
Обычный файл
@@ -0,0 +1,110 @@
|
|||||||
|
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
import PostsView from './posts_view.jsx';
|
||||||
|
|
||||||
|
import PostStore from '../stores/post_store.jsx';
|
||||||
|
import ChannelStore from '../stores/channel_store.jsx';
|
||||||
|
import * as EventHelpers from '../dispatcher/event_helpers.jsx';
|
||||||
|
|
||||||
|
export default class PostFocusView extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.onChannelChange = this.onChannelChange.bind(this);
|
||||||
|
this.onPostsChange = this.onPostsChange.bind(this);
|
||||||
|
this.handlePostsViewScroll = this.handlePostsViewScroll.bind(this);
|
||||||
|
this.loadMorePostsTop = this.loadMorePostsTop.bind(this);
|
||||||
|
this.loadMorePostsBottom = this.loadMorePostsBottom.bind(this);
|
||||||
|
|
||||||
|
const focusedPostId = PostStore.getFocusedPostId();
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
scrollType: PostsView.SCROLL_TYPE_POST,
|
||||||
|
scrollPostId: focusedPostId,
|
||||||
|
postList: PostStore.getVisiblePosts(focusedPostId),
|
||||||
|
atTop: PostStore.getVisibilityAtTop(focusedPostId),
|
||||||
|
atBottom: PostStore.getVisibilityAtBottom(focusedPostId)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
ChannelStore.addChangeListener(this.onChannelChange);
|
||||||
|
PostStore.addChangeListener(this.onPostsChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
ChannelStore.removeChangeListener(this.onChannelChange);
|
||||||
|
PostStore.removeChangeListener(this.onPostsChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
onChannelChange() {
|
||||||
|
this.setState({
|
||||||
|
scrollType: PostsView.SCROLL_TYPE_POST
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onPostsChange() {
|
||||||
|
const focusedPostId = PostStore.getFocusedPostId();
|
||||||
|
if (focusedPostId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
scrollPostId: focusedPostId,
|
||||||
|
postList: PostStore.getVisiblePosts(focusedPostId),
|
||||||
|
atTop: PostStore.getVisibilityAtTop(focusedPostId),
|
||||||
|
atBottom: PostStore.getVisibilityAtBottom(focusedPostId)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePostsViewScroll() {
|
||||||
|
this.setState({scrollType: PostsView.SCROLL_TYPE_FREE});
|
||||||
|
}
|
||||||
|
|
||||||
|
loadMorePostsTop() {
|
||||||
|
EventHelpers.emitLoadMorePostsFocusedTopEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
loadMorePostsBottom() {
|
||||||
|
EventHelpers.emitLoadMorePostsFocusedBottomEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
getIntroMessage() {
|
||||||
|
return (
|
||||||
|
<div className='channel-intro'>
|
||||||
|
<h4 className='channel-intro__title'>{'Beginning of Channel'}</h4>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const postsToHighlight = {};
|
||||||
|
postsToHighlight[this.state.scrollPostId] = true;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div id='post-list'>
|
||||||
|
<PostsView
|
||||||
|
key={'postfocusview'}
|
||||||
|
isActive={true}
|
||||||
|
postList={this.state.postList}
|
||||||
|
scrollType={this.state.scrollType}
|
||||||
|
scrollPostId={this.state.scrollPostId}
|
||||||
|
postViewScrolled={this.handlePostsViewScroll}
|
||||||
|
loadMorePostsTopClicked={this.loadMorePostsTop}
|
||||||
|
loadMorePostsBottomClicked={this.loadMorePostsBottom}
|
||||||
|
showMoreMessagesTop={!this.state.atTop}
|
||||||
|
showMoreMessagesBottom={!this.state.atBottom}
|
||||||
|
introText={this.getIntroMessage()}
|
||||||
|
messageSeparatorTime={0}
|
||||||
|
postsToHighlight={postsToHighlight}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PostFocusView.defaultProps = {
|
||||||
|
};
|
||||||
|
|
||||||
|
PostFocusView.propTypes = {
|
||||||
|
};
|
||||||
@@ -3,20 +3,28 @@
|
|||||||
|
|
||||||
import DeletePostModal from './delete_post_modal.jsx';
|
import DeletePostModal from './delete_post_modal.jsx';
|
||||||
import UserStore from '../stores/user_store.jsx';
|
import UserStore from '../stores/user_store.jsx';
|
||||||
import * as utils from '../utils/utils.jsx';
|
import TeamStore from '../stores/team_store.jsx';
|
||||||
|
import * as Utils from '../utils/utils.jsx';
|
||||||
import TimeSince from './time_since.jsx';
|
import TimeSince from './time_since.jsx';
|
||||||
|
|
||||||
import Constants from '../utils/constants.jsx';
|
import Constants from '../utils/constants.jsx';
|
||||||
|
|
||||||
|
const OverlayTrigger = ReactBootstrap.OverlayTrigger;
|
||||||
|
const Popover = ReactBootstrap.Popover;
|
||||||
|
|
||||||
export default class PostInfo extends React.Component {
|
export default class PostInfo extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {};
|
this.state = {
|
||||||
|
copiedLink: false
|
||||||
|
};
|
||||||
|
|
||||||
|
this.handlePermalinkCopy = this.handlePermalinkCopy.bind(this);
|
||||||
}
|
}
|
||||||
createDropdown() {
|
createDropdown() {
|
||||||
var post = this.props.post;
|
var post = this.props.post;
|
||||||
var isOwner = UserStore.getCurrentId() === post.user_id;
|
var isOwner = UserStore.getCurrentId() === post.user_id;
|
||||||
var isAdmin = utils.isAdmin(UserStore.getCurrentUser().roles);
|
var isAdmin = Utils.isAdmin(UserStore.getCurrentUser().roles);
|
||||||
|
|
||||||
if (post.state === Constants.POST_FAILED || post.state === Constants.POST_LOADING || post.state === Constants.POST_DELETED) {
|
if (post.state === Constants.POST_FAILED || post.state === Constants.POST_LOADING || post.state === Constants.POST_DELETED) {
|
||||||
return '';
|
return '';
|
||||||
@@ -113,6 +121,21 @@ export default class PostInfo extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
handlePermalinkCopy() {
|
||||||
|
const textBox = $(ReactDOM.findDOMNode(this.refs.permalinkbox));
|
||||||
|
textBox.select();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const successful = document.execCommand('copy');
|
||||||
|
if (successful) {
|
||||||
|
this.setState({copiedLink: true});
|
||||||
|
} else {
|
||||||
|
this.setState({copiedLink: false});
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
this.setState({copiedLink: false});
|
||||||
|
}
|
||||||
|
}
|
||||||
render() {
|
render() {
|
||||||
var post = this.props.post;
|
var post = this.props.post;
|
||||||
var comments = '';
|
var comments = '';
|
||||||
@@ -143,6 +166,37 @@ export default class PostInfo extends React.Component {
|
|||||||
|
|
||||||
var dropdown = this.createDropdown();
|
var dropdown = this.createDropdown();
|
||||||
|
|
||||||
|
const permalink = TeamStore.getCurrentTeamUrl() + '/pl/' + post.id;
|
||||||
|
const copyButtonText = this.state.copiedLink ? (<div>{'Copy '}<i className='fa fa-check'/></div>) : 'Copy';
|
||||||
|
const permalinkOverlay = (
|
||||||
|
<Popover
|
||||||
|
id='permalink-overlay'
|
||||||
|
className='permalink-popover'
|
||||||
|
placement='left'
|
||||||
|
title=''
|
||||||
|
>
|
||||||
|
<div className='form-inline'>
|
||||||
|
<input
|
||||||
|
type='text'
|
||||||
|
readOnly='true'
|
||||||
|
ref='permalinkbox'
|
||||||
|
className='permalink-text form-control no-resize min-height input-large'
|
||||||
|
rows='1'
|
||||||
|
value={permalink}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
data-copy-btn='true'
|
||||||
|
type='button'
|
||||||
|
className='btn btn-primary'
|
||||||
|
onClick={this.handlePermalinkCopy}
|
||||||
|
data-clipboard-text={permalink}
|
||||||
|
>
|
||||||
|
{copyButtonText}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ul className='post__header post__header--info'>
|
<ul className='post__header post__header--info'>
|
||||||
<li className='col'>
|
<li className='col'>
|
||||||
@@ -152,6 +206,15 @@ export default class PostInfo extends React.Component {
|
|||||||
</li>
|
</li>
|
||||||
<li className='col col__reply'>
|
<li className='col col__reply'>
|
||||||
{comments}
|
{comments}
|
||||||
|
<OverlayTrigger
|
||||||
|
trigger='click'
|
||||||
|
placement='left'
|
||||||
|
rootClose={true}
|
||||||
|
overlay={permalinkOverlay}
|
||||||
|
>
|
||||||
|
<i className={'permalink-icon fa fa-link ' + showCommentClass}/>
|
||||||
|
</OverlayTrigger>
|
||||||
|
|
||||||
<div className='dropdown'>
|
<div className='dropdown'>
|
||||||
{dropdown}
|
{dropdown}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// See License.txt for license information.
|
// See License.txt for license information.
|
||||||
|
|
||||||
import UserStore from '../stores/user_store.jsx';
|
import UserStore from '../stores/user_store.jsx';
|
||||||
|
import * as EventHelpers from '../dispatcher/event_helpers.jsx';
|
||||||
import * as Utils from '../utils/utils.jsx';
|
import * as Utils from '../utils/utils.jsx';
|
||||||
import Post from './post.jsx';
|
import Post from './post.jsx';
|
||||||
import Constants from '../utils/constants.jsx';
|
import Constants from '../utils/constants.jsx';
|
||||||
@@ -13,6 +14,7 @@ export default class PostsView extends React.Component {
|
|||||||
this.handleScroll = this.handleScroll.bind(this);
|
this.handleScroll = this.handleScroll.bind(this);
|
||||||
this.isAtBottom = this.isAtBottom.bind(this);
|
this.isAtBottom = this.isAtBottom.bind(this);
|
||||||
this.loadMorePostsTop = this.loadMorePostsTop.bind(this);
|
this.loadMorePostsTop = this.loadMorePostsTop.bind(this);
|
||||||
|
this.loadMorePostsBottom = this.loadMorePostsBottom.bind(this);
|
||||||
this.createPosts = this.createPosts.bind(this);
|
this.createPosts = this.createPosts.bind(this);
|
||||||
this.updateScrolling = this.updateScrolling.bind(this);
|
this.updateScrolling = this.updateScrolling.bind(this);
|
||||||
this.handleResize = this.handleResize.bind(this);
|
this.handleResize = this.handleResize.bind(this);
|
||||||
@@ -27,12 +29,15 @@ export default class PostsView extends React.Component {
|
|||||||
static get SCROLL_TYPE_BOTTOM() {
|
static get SCROLL_TYPE_BOTTOM() {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
static get SIDEBAR_OPEN() {
|
static get SCROLL_TYPE_SIDEBAR_OPEN() {
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
static get SCROLL_TYPE_NEW_MESSAGE() {
|
static get SCROLL_TYPE_NEW_MESSAGE() {
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
|
static get SCROLL_TYPE_POST() {
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
isAtBottom() {
|
isAtBottom() {
|
||||||
return ((this.refs.postlist.scrollHeight - this.refs.postlist.scrollTop) === this.refs.postlist.clientHeight);
|
return ((this.refs.postlist.scrollHeight - this.refs.postlist.scrollTop) === this.refs.postlist.clientHeight);
|
||||||
}
|
}
|
||||||
@@ -47,15 +52,22 @@ export default class PostsView extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.wasAtBottom = this.isAtBottom();
|
this.wasAtBottom = this.isAtBottom();
|
||||||
|
if (!this.jumpToPostNode && childNodes.length > 0) {
|
||||||
|
this.jumpToPostNode = childNodes[childNodes.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
// --- --------
|
// --- --------
|
||||||
|
|
||||||
this.props.postViewScrolled(this.isAtBottom());
|
this.props.postViewScrolled(this.isAtBottom());
|
||||||
this.prevScrollHeight = this.refs.postlist.scrollHeight;
|
this.prevScrollHeight = this.refs.postlist.scrollHeight;
|
||||||
|
this.prevOffsetTop = this.jumpToPostNode.offsetTop;
|
||||||
}
|
}
|
||||||
loadMorePostsTop() {
|
loadMorePostsTop() {
|
||||||
this.props.loadMorePostsTopClicked();
|
this.props.loadMorePostsTopClicked();
|
||||||
}
|
}
|
||||||
|
loadMorePostsBottom() {
|
||||||
|
this.props.loadMorePostsBottomClicked();
|
||||||
|
}
|
||||||
createPosts(posts, order) {
|
createPosts(posts, order) {
|
||||||
const postCtls = [];
|
const postCtls = [];
|
||||||
let previousPostDay = new Date(0);
|
let previousPostDay = new Date(0);
|
||||||
@@ -63,12 +75,7 @@ export default class PostsView extends React.Component {
|
|||||||
|
|
||||||
let renderedLastViewed = false;
|
let renderedLastViewed = false;
|
||||||
|
|
||||||
let numToDisplay = this.props.numPostsToDisplay;
|
for (let i = order.length - 1; i >= 0; i--) {
|
||||||
if (order.length - 1 < numToDisplay) {
|
|
||||||
numToDisplay = order.length - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = numToDisplay; i >= 0; i--) {
|
|
||||||
const post = posts[order[i]];
|
const post = posts[order[i]];
|
||||||
const parentPost = posts[post.parent_id];
|
const parentPost = posts[post.parent_id];
|
||||||
const prevPost = posts[order[i + 1]];
|
const prevPost = posts[order[i + 1]];
|
||||||
@@ -113,6 +120,8 @@ export default class PostsView extends React.Component {
|
|||||||
|
|
||||||
const keyPrefix = post.id ? post.id : i;
|
const keyPrefix = post.id ? post.id : i;
|
||||||
|
|
||||||
|
const shouldHighlight = this.props.postsToHighlight && this.props.postsToHighlight.hasOwnProperty(post.id);
|
||||||
|
|
||||||
const postCtl = (
|
const postCtl = (
|
||||||
<Post
|
<Post
|
||||||
key={keyPrefix + 'postKey'}
|
key={keyPrefix + 'postKey'}
|
||||||
@@ -124,6 +133,8 @@ export default class PostsView extends React.Component {
|
|||||||
posts={posts}
|
posts={posts}
|
||||||
hideProfilePic={hideProfilePic}
|
hideProfilePic={hideProfilePic}
|
||||||
isLastComment={isLastComment}
|
isLastComment={isLastComment}
|
||||||
|
shouldHighlight={shouldHighlight}
|
||||||
|
onClick={() => EventHelpers.emitPostFocusEvent(post.id)} //eslint-disable-line no-loop-func
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -185,9 +196,12 @@ export default class PostsView extends React.Component {
|
|||||||
this.refs.postlist.scrollTop = this.refs.postlist.scrollHeight;
|
this.refs.postlist.scrollTop = this.refs.postlist.scrollHeight;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (this.props.scrollType === PostsView.SCROLL_TYPE_POST && this.props.scrollPost) {
|
} else if (this.props.scrollType === PostsView.SCROLL_TYPE_POST && this.props.scrollPostId) {
|
||||||
window.requestAnimationFrame(() => {
|
window.requestAnimationFrame(() => {
|
||||||
const postNode = ReactDOM.findDOMNode(this.refs[this.props.scrollPost]);
|
const postNode = ReactDOM.findDOMNode(this.refs[this.props.scrollPostId]);
|
||||||
|
if (postNode == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
postNode.scrollIntoView();
|
postNode.scrollIntoView();
|
||||||
if (this.refs.postlist.scrollTop === postNode.offsetTop) {
|
if (this.refs.postlist.scrollTop === postNode.offsetTop) {
|
||||||
this.refs.postlist.scrollTop -= (this.refs.postlist.offsetHeight / 3);
|
this.refs.postlist.scrollTop -= (this.refs.postlist.offsetHeight / 3);
|
||||||
@@ -195,7 +209,7 @@ export default class PostsView extends React.Component {
|
|||||||
this.refs.postlist.scrollTop -= (this.refs.postlist.offsetHeight / 3) + (this.refs.postlist.scrollTop - postNode.offsetTop);
|
this.refs.postlist.scrollTop -= (this.refs.postlist.offsetHeight / 3) + (this.refs.postlist.scrollTop - postNode.offsetTop);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (this.props.scrollType === PostsView.SIDEBAR_OPEN) {
|
} else if (this.props.scrollType === PostsView.SCROLL_TYPE_SIDEBAR_OPEN) {
|
||||||
// If we are at the bottom then stay there
|
// If we are at the bottom then stay there
|
||||||
if (this.wasAtBottom) {
|
if (this.wasAtBottom) {
|
||||||
this.refs.postlist.scrollTop = this.refs.postlist.scrollHeight;
|
this.refs.postlist.scrollTop = this.refs.postlist.scrollHeight;
|
||||||
@@ -211,7 +225,10 @@ export default class PostsView extends React.Component {
|
|||||||
}
|
}
|
||||||
} else if (this.refs.postlist.scrollHeight !== this.prevScrollHeight) {
|
} else if (this.refs.postlist.scrollHeight !== this.prevScrollHeight) {
|
||||||
window.requestAnimationFrame(() => {
|
window.requestAnimationFrame(() => {
|
||||||
this.refs.postlist.scrollTop += (this.refs.postlist.scrollHeight - this.prevScrollHeight);
|
// Only need to jump if we added posts to the top.
|
||||||
|
if (this.jumpToPostNode && (this.jumpToPostNode.offsetTop !== this.prevOffsetTop)) {
|
||||||
|
this.refs.postlist.scrollTop += (this.refs.postlist.scrollHeight - this.prevScrollHeight);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -219,14 +236,18 @@ export default class PostsView extends React.Component {
|
|||||||
this.updateScrolling();
|
this.updateScrolling();
|
||||||
}
|
}
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.updateScrolling();
|
if (this.props.postList != null) {
|
||||||
|
this.updateScrolling();
|
||||||
|
}
|
||||||
window.addEventListener('resize', this.handleResize);
|
window.addEventListener('resize', this.handleResize);
|
||||||
}
|
}
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
window.removeEventListener('resize', this.handleResize);
|
window.removeEventListener('resize', this.handleResize);
|
||||||
}
|
}
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
this.updateScrolling();
|
if (this.props.postList != null) {
|
||||||
|
this.updateScrolling();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
shouldComponentUpdate(nextProps) {
|
shouldComponentUpdate(nextProps) {
|
||||||
if (this.props.isActive !== nextProps.isActive) {
|
if (this.props.isActive !== nextProps.isActive) {
|
||||||
@@ -235,15 +256,12 @@ export default class PostsView extends React.Component {
|
|||||||
if (this.props.postList !== nextProps.postList) {
|
if (this.props.postList !== nextProps.postList) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (this.props.scrollPost !== nextProps.scrollPost) {
|
if (this.props.scrollPostId !== nextProps.scrollPostId) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (this.props.scrollType !== nextProps.scrollType && nextProps.scrollType !== PostsView.SCROLL_TYPE_FREE) {
|
if (this.props.scrollType !== nextProps.scrollType && nextProps.scrollType !== PostsView.SCROLL_TYPE_FREE) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (this.props.numPostsToDisplay !== nextProps.numPostsToDisplay) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (this.props.messageSeparatorTime !== nextProps.messageSeparatorTime) {
|
if (this.props.messageSeparatorTime !== nextProps.messageSeparatorTime) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -256,7 +274,8 @@ export default class PostsView extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
let posts = [];
|
let posts = [];
|
||||||
let order = [];
|
let order = [];
|
||||||
let moreMessages;
|
let moreMessagesTop;
|
||||||
|
let moreMessagesBottom;
|
||||||
let postElements;
|
let postElements;
|
||||||
let activeClass = 'inactive';
|
let activeClass = 'inactive';
|
||||||
if (this.props.postList != null) {
|
if (this.props.postList != null) {
|
||||||
@@ -264,10 +283,10 @@ export default class PostsView extends React.Component {
|
|||||||
order = this.props.postList.order;
|
order = this.props.postList.order;
|
||||||
|
|
||||||
// Create intro message or top loadmore link
|
// Create intro message or top loadmore link
|
||||||
if (order.length >= this.props.numPostsToDisplay) {
|
if (this.props.showMoreMessagesTop) {
|
||||||
moreMessages = (
|
moreMessagesTop = (
|
||||||
<a
|
<a
|
||||||
ref='loadmore'
|
ref='loadmoretop'
|
||||||
className='more-messages-text theme'
|
className='more-messages-text theme'
|
||||||
href='#'
|
href='#'
|
||||||
onClick={this.loadMorePostsTop}
|
onClick={this.loadMorePostsTop}
|
||||||
@@ -276,7 +295,23 @@ export default class PostsView extends React.Component {
|
|||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
moreMessages = this.props.introText;
|
moreMessagesTop = this.props.introText;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Give option to load more posts at bottom if nessisary
|
||||||
|
if (this.props.showMoreMessagesBottom) {
|
||||||
|
moreMessagesBottom = (
|
||||||
|
<a
|
||||||
|
ref='loadmorebottom'
|
||||||
|
className='more-messages-text theme'
|
||||||
|
href='#'
|
||||||
|
onClick={this.loadMorePostsBottom}
|
||||||
|
>
|
||||||
|
{'Load more messages'}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
moreMessagesBottom = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create post elements
|
// Create post elements
|
||||||
@@ -299,8 +334,9 @@ export default class PostsView extends React.Component {
|
|||||||
ref='postlistcontent'
|
ref='postlistcontent'
|
||||||
className='post-list__content'
|
className='post-list__content'
|
||||||
>
|
>
|
||||||
{moreMessages}
|
{moreMessagesTop}
|
||||||
{postElements}
|
{postElements}
|
||||||
|
{moreMessagesBottom}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -313,11 +349,14 @@ PostsView.defaultProps = {
|
|||||||
PostsView.propTypes = {
|
PostsView.propTypes = {
|
||||||
isActive: React.PropTypes.bool,
|
isActive: React.PropTypes.bool,
|
||||||
postList: React.PropTypes.object,
|
postList: React.PropTypes.object,
|
||||||
scrollPost: React.PropTypes.string,
|
scrollPostId: React.PropTypes.string,
|
||||||
scrollType: React.PropTypes.number,
|
scrollType: React.PropTypes.number,
|
||||||
postViewScrolled: React.PropTypes.func.isRequired,
|
postViewScrolled: React.PropTypes.func.isRequired,
|
||||||
loadMorePostsTopClicked: React.PropTypes.func.isRequired,
|
loadMorePostsTopClicked: React.PropTypes.func.isRequired,
|
||||||
numPostsToDisplay: React.PropTypes.number,
|
loadMorePostsBottomClicked: React.PropTypes.func.isRequired,
|
||||||
|
showMoreMessagesTop: React.PropTypes.bool,
|
||||||
|
showMoreMessagesBottom: React.PropTypes.bool,
|
||||||
introText: React.PropTypes.element,
|
introText: React.PropTypes.element,
|
||||||
messageSeparatorTime: React.PropTypes.number
|
messageSeparatorTime: React.PropTypes.number,
|
||||||
|
postsToHighlight: React.PropTypes.object
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,12 +9,9 @@ import ChannelStore from '../stores/channel_store.jsx';
|
|||||||
import PostStore from '../stores/post_store.jsx';
|
import PostStore from '../stores/post_store.jsx';
|
||||||
|
|
||||||
import * as Utils from '../utils/utils.jsx';
|
import * as Utils from '../utils/utils.jsx';
|
||||||
import * as Client from '../utils/client.jsx';
|
import * as EventHelpers from '../dispatcher/event_helpers.jsx';
|
||||||
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
|
|
||||||
import * as AsyncClient from '../utils/async_client.jsx';
|
|
||||||
|
|
||||||
import Constants from '../utils/constants.jsx';
|
import Constants from '../utils/constants.jsx';
|
||||||
const ActionTypes = Constants.ActionTypes;
|
|
||||||
|
|
||||||
import {createChannelIntroMessage} from '../utils/channel_intro_mssages.jsx';
|
import {createChannelIntroMessage} from '../utils/channel_intro_mssages.jsx';
|
||||||
|
|
||||||
@@ -27,27 +24,26 @@ export default class PostsViewContainer extends React.Component {
|
|||||||
this.onPostsChange = this.onPostsChange.bind(this);
|
this.onPostsChange = this.onPostsChange.bind(this);
|
||||||
this.handlePostsViewScroll = this.handlePostsViewScroll.bind(this);
|
this.handlePostsViewScroll = this.handlePostsViewScroll.bind(this);
|
||||||
this.loadMorePostsTop = this.loadMorePostsTop.bind(this);
|
this.loadMorePostsTop = this.loadMorePostsTop.bind(this);
|
||||||
this.postsLoaded = this.postsLoaded.bind(this);
|
|
||||||
this.postsLoadedFailure = this.postsLoadedFailure.bind(this);
|
|
||||||
this.handlePostsViewJumpRequest = this.handlePostsViewJumpRequest.bind(this);
|
this.handlePostsViewJumpRequest = this.handlePostsViewJumpRequest.bind(this);
|
||||||
|
|
||||||
const currentChannelId = ChannelStore.getCurrentId();
|
const currentChannelId = ChannelStore.getCurrentId();
|
||||||
const state = {
|
const state = {
|
||||||
scrollType: PostsView.SCROLL_TYPE_BOTTOM,
|
scrollType: PostsView.SCROLL_TYPE_BOTTOM,
|
||||||
scrollPost: null,
|
scrollPost: null
|
||||||
numPostsToDisplay: Constants.POST_CHUNK_SIZE
|
|
||||||
};
|
};
|
||||||
if (currentChannelId) {
|
if (currentChannelId) {
|
||||||
Object.assign(state, {
|
Object.assign(state, {
|
||||||
currentChannelIndex: 0,
|
currentChannelIndex: 0,
|
||||||
channels: [currentChannelId],
|
channels: [currentChannelId],
|
||||||
postLists: [this.getChannelPosts(currentChannelId)]
|
postLists: [this.getChannelPosts(currentChannelId)],
|
||||||
|
atTop: [PostStore.getVisibilityAtTop(currentChannelId)]
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Object.assign(state, {
|
Object.assign(state, {
|
||||||
currentChannelIndex: null,
|
currentChannelIndex: null,
|
||||||
channels: [],
|
channels: [],
|
||||||
postLists: []
|
postLists: [],
|
||||||
|
atTop: []
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,24 +74,21 @@ export default class PostsViewContainer extends React.Component {
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case Constants.PostsViewJumpTypes.SIDEBAR_OPEN:
|
case Constants.PostsViewJumpTypes.SIDEBAR_OPEN:
|
||||||
this.setState({scrollType: PostsView.SIDEBAR_OPEN});
|
this.setState({scrollType: PostsView.SCROLL_TYPE_SIDEBAR_OPEN});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onChannelChange() {
|
onChannelChange() {
|
||||||
const postLists = this.state.postLists.slice();
|
const postLists = this.state.postLists.slice();
|
||||||
|
const atTop = this.state.atTop.slice();
|
||||||
const channels = this.state.channels.slice();
|
const channels = this.state.channels.slice();
|
||||||
const channelId = ChannelStore.getCurrentId();
|
const channelId = ChannelStore.getCurrentId();
|
||||||
|
|
||||||
// Has the channel really changed?
|
// Has the channel really changed?
|
||||||
if (channelId === channels[this.state.currentChannelIndex]) {
|
if (channelId === channels[this.state.currentChannelIndex]) {
|
||||||
// Dirty hack
|
|
||||||
this.forceUpdate();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PostStore.clearUnseenDeletedPosts(channelId);
|
|
||||||
|
|
||||||
let lastViewed = Number.MAX_VALUE;
|
let lastViewed = Number.MAX_VALUE;
|
||||||
const member = ChannelStore.getMember(channelId);
|
const member = ChannelStore.getMember(channelId);
|
||||||
if (member != null) {
|
if (member != null) {
|
||||||
@@ -107,115 +100,45 @@ export default class PostsViewContainer extends React.Component {
|
|||||||
newIndex = channels.length;
|
newIndex = channels.length;
|
||||||
channels.push(channelId);
|
channels.push(channelId);
|
||||||
postLists[newIndex] = this.getChannelPosts(channelId);
|
postLists[newIndex] = this.getChannelPosts(channelId);
|
||||||
|
atTop[newIndex] = PostStore.getVisibilityAtTop(channelId);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
currentChannelIndex: newIndex,
|
currentChannelIndex: newIndex,
|
||||||
currentLastViewed: lastViewed,
|
currentLastViewed: lastViewed,
|
||||||
scrollType: PostsView.SCROLL_TYPE_NEW_MESSAGE,
|
scrollType: PostsView.SCROLL_TYPE_NEW_MESSAGE,
|
||||||
channels,
|
channels,
|
||||||
postLists});
|
postLists,
|
||||||
|
atTop});
|
||||||
}
|
}
|
||||||
onChannelLeave(id) {
|
onChannelLeave(id) {
|
||||||
const postLists = this.state.postLists.slice();
|
const postLists = this.state.postLists.slice();
|
||||||
const channels = this.state.channels.slice();
|
const channels = this.state.channels.slice();
|
||||||
|
const atTop = this.state.atTop.slice();
|
||||||
const index = channels.indexOf(id);
|
const index = channels.indexOf(id);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
postLists.splice(index, 1);
|
postLists.splice(index, 1);
|
||||||
channels.splice(index, 1);
|
channels.splice(index, 1);
|
||||||
|
atTop.splice(index, 1);
|
||||||
}
|
}
|
||||||
this.setState({channels, postLists});
|
this.setState({channels, postLists, atTop});
|
||||||
}
|
}
|
||||||
onPostsChange() {
|
onPostsChange() {
|
||||||
const channels = this.state.channels;
|
const channels = this.state.channels;
|
||||||
const postLists = this.state.postLists.slice();
|
const postLists = this.state.postLists.slice();
|
||||||
const newPostsView = this.getChannelPosts(channels[this.state.currentChannelIndex]);
|
const atTop = this.state.atTop.slice();
|
||||||
|
const currentChannelId = channels[this.state.currentChannelIndex];
|
||||||
|
const newPostsView = this.getChannelPosts(currentChannelId);
|
||||||
|
|
||||||
postLists[this.state.currentChannelIndex] = newPostsView;
|
postLists[this.state.currentChannelIndex] = newPostsView;
|
||||||
this.setState({postLists});
|
atTop[this.state.currentChannelIndex] = PostStore.getVisibilityAtTop(currentChannelId);
|
||||||
|
this.setState({postLists, atTop});
|
||||||
}
|
}
|
||||||
getChannelPosts(id) {
|
getChannelPosts(id) {
|
||||||
const postList = PostStore.getPosts(id);
|
return PostStore.getVisiblePosts(id);
|
||||||
|
|
||||||
if (postList != null) {
|
|
||||||
const deletedPosts = PostStore.getUnseenDeletedPosts(id);
|
|
||||||
|
|
||||||
if (deletedPosts && Object.keys(deletedPosts).length > 0) {
|
|
||||||
for (const pid in deletedPosts) {
|
|
||||||
if (deletedPosts.hasOwnProperty(pid)) {
|
|
||||||
postList.posts[pid] = deletedPosts[pid];
|
|
||||||
postList.order.unshift(pid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
postList.order.sort((a, b) => {
|
|
||||||
if (postList.posts[a].create_at > postList.posts[b].create_at) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (postList.posts[a].create_at < postList.posts[b].create_at) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const pendingPostList = PostStore.getPendingPosts(id);
|
|
||||||
|
|
||||||
if (pendingPostList) {
|
|
||||||
postList.order = pendingPostList.order.concat(postList.order);
|
|
||||||
for (const ppid in pendingPostList.posts) {
|
|
||||||
if (pendingPostList.posts.hasOwnProperty(ppid)) {
|
|
||||||
postList.posts[ppid] = pendingPostList.posts[ppid];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return postList;
|
|
||||||
}
|
}
|
||||||
loadMorePostsTop() {
|
loadMorePostsTop() {
|
||||||
const postLists = this.state.postLists;
|
EventHelpers.emitLoadMorePostsEvent();
|
||||||
const channels = this.state.channels;
|
|
||||||
const currentChannelId = channels[this.state.currentChannelIndex];
|
|
||||||
const currentPostList = postLists[this.state.currentChannelIndex];
|
|
||||||
|
|
||||||
this.setState({numPostsToDisplay: this.state.numPostsToDisplay + Constants.POST_CHUNK_SIZE});
|
|
||||||
|
|
||||||
Client.getPostsPage(
|
|
||||||
currentChannelId,
|
|
||||||
currentPostList.order.length,
|
|
||||||
Constants.POST_CHUNK_SIZE,
|
|
||||||
this.postsLoaded,
|
|
||||||
this.postsLoadedFailure
|
|
||||||
);
|
|
||||||
}
|
|
||||||
postsLoaded(data) {
|
|
||||||
if (!data) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.order.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const postLists = this.state.postLists;
|
|
||||||
const currentPostList = postLists[this.state.currentChannelIndex];
|
|
||||||
const channels = this.state.channels;
|
|
||||||
const currentChannelId = channels[this.state.currentChannelIndex];
|
|
||||||
|
|
||||||
var newPostList = {};
|
|
||||||
newPostList.posts = Object.assign(currentPostList.posts, data.posts);
|
|
||||||
newPostList.order = currentPostList.order.concat(data.order);
|
|
||||||
|
|
||||||
AppDispatcher.handleServerAction({
|
|
||||||
type: ActionTypes.RECIEVED_POSTS,
|
|
||||||
id: currentChannelId,
|
|
||||||
post_list: newPostList
|
|
||||||
});
|
|
||||||
|
|
||||||
Client.getProfiles();
|
|
||||||
}
|
|
||||||
postsLoadedFailure(err) {
|
|
||||||
AsyncClient.dispatchError(err, 'getPosts');
|
|
||||||
}
|
}
|
||||||
handlePostsViewScroll(atBottom) {
|
handlePostsViewScroll(atBottom) {
|
||||||
if (atBottom) {
|
if (atBottom) {
|
||||||
@@ -246,15 +169,17 @@ export default class PostsViewContainer extends React.Component {
|
|||||||
isActive={isActive}
|
isActive={isActive}
|
||||||
postList={postLists[i]}
|
postList={postLists[i]}
|
||||||
scrollType={this.state.scrollType}
|
scrollType={this.state.scrollType}
|
||||||
scrollPost={this.state.scrollPost}
|
scrollPostId={this.state.scrollPost}
|
||||||
postViewScrolled={this.handlePostsViewScroll}
|
postViewScrolled={this.handlePostsViewScroll}
|
||||||
loadMorePostsTopClicked={this.loadMorePostsTop}
|
loadMorePostsTopClicked={this.loadMorePostsTop}
|
||||||
numPostsToDisplay={this.state.numPostsToDisplay}
|
loadMorePostsBottomClicked={() => {}}
|
||||||
|
showMoreMessagesTop={!this.state.atTop[this.state.currentChannelIndex]}
|
||||||
|
showMoreMessagesBottom={false}
|
||||||
introText={channel ? createChannelIntroMessage(channel, () => this.setState({showInviteModal: true})) : null}
|
introText={channel ? createChannelIntroMessage(channel, () => this.setState({showInviteModal: true})) : null}
|
||||||
messageSeparatorTime={this.state.currentLastViewed}
|
messageSeparatorTime={this.state.currentLastViewed}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
if ((!postLists[i] || !channel) && isActive) {
|
if (!postLists[i] && isActive) {
|
||||||
postListCtls.push(
|
postListCtls.push(
|
||||||
<LoadingScreen
|
<LoadingScreen
|
||||||
position='absolute'
|
position='absolute'
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ export default class RhsThread extends React.Component {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentPosts = PostStore.getPosts(currentSelected.posts[currentSelected.order[0]].channel_id);
|
var currentPosts = PostStore.getVisiblePosts(currentSelected.posts[currentSelected.order[0]].channel_id);
|
||||||
|
|
||||||
if (!currentPosts || currentPosts.order.length === 0) {
|
if (!currentPosts || currentPosts.order.length === 0) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,17 +1,12 @@
|
|||||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||||
// See License.txt for license information.
|
// See License.txt for license information.
|
||||||
|
|
||||||
import SearchStore from '../stores/search_store.jsx';
|
|
||||||
import ChannelStore from '../stores/channel_store.jsx';
|
import ChannelStore from '../stores/channel_store.jsx';
|
||||||
import UserStore from '../stores/user_store.jsx';
|
import UserStore from '../stores/user_store.jsx';
|
||||||
import UserProfile from './user_profile.jsx';
|
import UserProfile from './user_profile.jsx';
|
||||||
|
import * as EventHelpers from '../dispatcher/event_helpers.jsx';
|
||||||
import * as utils from '../utils/utils.jsx';
|
import * as utils from '../utils/utils.jsx';
|
||||||
import * as client from '../utils/client.jsx';
|
|
||||||
import * as AsyncClient from '../utils/async_client.jsx';
|
|
||||||
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
|
|
||||||
import Constants from '../utils/constants.jsx';
|
|
||||||
import * as TextFormatting from '../utils/text_formatting.jsx';
|
import * as TextFormatting from '../utils/text_formatting.jsx';
|
||||||
var ActionTypes = Constants.ActionTypes;
|
|
||||||
|
|
||||||
export default class SearchResultsItem extends React.Component {
|
export default class SearchResultsItem extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@@ -23,32 +18,7 @@ export default class SearchResultsItem extends React.Component {
|
|||||||
handleClick(e) {
|
handleClick(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
var self = this;
|
EventHelpers.emitPostFocusEvent(this.props.post.id);
|
||||||
|
|
||||||
client.getPost(
|
|
||||||
this.props.post.channel_id,
|
|
||||||
this.props.post.id,
|
|
||||||
function success(data) {
|
|
||||||
AppDispatcher.handleServerAction({
|
|
||||||
type: ActionTypes.RECIEVED_POST_SELECTED,
|
|
||||||
post_list: data,
|
|
||||||
from_search: SearchStore.getSearchTerm()
|
|
||||||
});
|
|
||||||
|
|
||||||
AppDispatcher.handleServerAction({
|
|
||||||
type: ActionTypes.RECIEVED_SEARCH,
|
|
||||||
results: null,
|
|
||||||
is_mention_search: self.props.isMentionSearch
|
|
||||||
});
|
|
||||||
},
|
|
||||||
function success(err) {
|
|
||||||
AsyncClient.dispatchError(err, 'getPost');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
var postChannel = ChannelStore.get(this.props.post.channel_id);
|
|
||||||
|
|
||||||
utils.switchChannel(postChannel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|||||||
@@ -201,10 +201,6 @@ export default class Sidebar extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
updateScrollbar() {
|
updateScrollbar() {
|
||||||
if (this.state.windowWidth > 768) {
|
|
||||||
$('.nav-pills__container').perfectScrollbar();
|
|
||||||
$('.nav-pills__container').perfectScrollbar('update');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
onChange() {
|
onChange() {
|
||||||
this.setState(this.getStateFromStores());
|
this.setState(this.getStateFromStores());
|
||||||
|
|||||||
83
web/react/dispatcher/event_helpers.jsx
Обычный файл
83
web/react/dispatcher/event_helpers.jsx
Обычный файл
@@ -0,0 +1,83 @@
|
|||||||
|
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
|
||||||
|
import ChannelStore from '../stores/channel_store.jsx';
|
||||||
|
import PostStore from '../stores/post_store.jsx';
|
||||||
|
import Constants from '../utils/constants.jsx';
|
||||||
|
const ActionTypes = Constants.ActionTypes;
|
||||||
|
import * as AsyncClient from '../utils/async_client.jsx';
|
||||||
|
import * as Client from '../utils/client.jsx';
|
||||||
|
|
||||||
|
export function emitChannelClickEvent(channel) {
|
||||||
|
AsyncClient.getChannels();
|
||||||
|
AsyncClient.getChannelExtraInfo();
|
||||||
|
AsyncClient.updateLastViewedAt();
|
||||||
|
AsyncClient.getPosts(channel.id);
|
||||||
|
|
||||||
|
AppDispatcher.handleViewAction({
|
||||||
|
type: ActionTypes.CLICK_CHANNEL,
|
||||||
|
name: channel.name,
|
||||||
|
id: channel.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function emitPostFocusEvent(postId) {
|
||||||
|
Client.getPostById(
|
||||||
|
postId,
|
||||||
|
(data) => {
|
||||||
|
AppDispatcher.handleServerAction({
|
||||||
|
type: ActionTypes.RECIEVED_FOCUSED_POST,
|
||||||
|
postId,
|
||||||
|
post_list: data
|
||||||
|
});
|
||||||
|
|
||||||
|
AsyncClient.getPostsBefore(postId, 0, Constants.POST_FOCUS_CONTEXT_RADIUS);
|
||||||
|
AsyncClient.getPostsAfter(postId, 0, Constants.POST_FOCUS_CONTEXT_RADIUS);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function emitLoadMorePostsEvent() {
|
||||||
|
const id = ChannelStore.getCurrentId();
|
||||||
|
loadMorePostsTop(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function emitLoadMorePostsFocusedTopEvent() {
|
||||||
|
const id = PostStore.getFocusedPostId();
|
||||||
|
loadMorePostsTop(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loadMorePostsTop(id) {
|
||||||
|
const earliestPostId = PostStore.getEarliestPost(id).id;
|
||||||
|
if (PostStore.requestVisibilityIncrease(id, Constants.POST_CHUNK_SIZE)) {
|
||||||
|
AsyncClient.getPostsBefore(earliestPostId, 0, Constants.POST_CHUNK_SIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function emitLoadMorePostsFocusedBottomEvent() {
|
||||||
|
const id = PostStore.getFocusedPostId();
|
||||||
|
const latestPostId = PostStore.getLatestPost(id).id;
|
||||||
|
AsyncClient.getPostsAfter(latestPostId, 0, Constants.POST_CHUNK_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function emitPostRecievedEvent(post) {
|
||||||
|
AppDispatcher.handleServerAction({
|
||||||
|
type: ActionTypes.RECIEVED_POST,
|
||||||
|
post
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function emitUserPostedEvent(post) {
|
||||||
|
AppDispatcher.handleServerAction({
|
||||||
|
type: ActionTypes.CREATE_POST,
|
||||||
|
post
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function emitPostDeletedEvent(post) {
|
||||||
|
AppDispatcher.handleServerAction({
|
||||||
|
type: ActionTypes.POST_DELETED,
|
||||||
|
post
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||||
// See License.txt for license information.
|
// See License.txt for license information.
|
||||||
|
|
||||||
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
|
|
||||||
import ChannelView from '../components/channel_view.jsx';
|
import ChannelView from '../components/channel_view.jsx';
|
||||||
import ChannelLoader from '../components/channel_loader.jsx';
|
import ChannelLoader from '../components/channel_loader.jsx';
|
||||||
import ErrorBar from '../components/error_bar.jsx';
|
import ErrorBar from '../components/error_bar.jsx';
|
||||||
@@ -23,15 +22,14 @@ import ImportThemeModal from '../components/user_settings/import_theme_modal.jsx
|
|||||||
import InviteMemberModal from '../components/invite_member_modal.jsx';
|
import InviteMemberModal from '../components/invite_member_modal.jsx';
|
||||||
|
|
||||||
import * as AsyncClient from '../utils/async_client.jsx';
|
import * as AsyncClient from '../utils/async_client.jsx';
|
||||||
import Constants from '../utils/constants.jsx';
|
import * as EventHelpers from '../dispatcher/event_helpers.jsx';
|
||||||
var ActionTypes = Constants.ActionTypes;
|
|
||||||
|
|
||||||
function setupChannelPage(props) {
|
function setupChannelPage(props, team, channel) {
|
||||||
AppDispatcher.handleViewAction({
|
if (props.PostId === '') {
|
||||||
type: ActionTypes.CLICK_CHANNEL,
|
EventHelpers.emitChannelClickEvent(channel);
|
||||||
name: props.ChannelName,
|
} else {
|
||||||
id: props.ChannelId
|
EventHelpers.emitPostFocusEvent(props.PostId);
|
||||||
});
|
}
|
||||||
|
|
||||||
AsyncClient.getAllPreferences();
|
AsyncClient.getAllPreferences();
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||||
// See License.txt for license information.
|
// See License.txt for license information.
|
||||||
|
|
||||||
import ChannelStore from '../stores/channel_store.jsx';
|
|
||||||
import TeamStore from '../stores/team_store.jsx';
|
import TeamStore from '../stores/team_store.jsx';
|
||||||
import Constants from '../utils/constants.jsx';
|
import Constants from '../utils/constants.jsx';
|
||||||
|
|
||||||
function setupHomePage() {
|
function setupHomePage() {
|
||||||
var last = ChannelStore.getLastVisitedName();
|
var last = null;
|
||||||
if (last == null || last.length === 0) {
|
if (last == null || last.length === 0) {
|
||||||
window.location = TeamStore.getCurrentTeamUrl() + '/channels/' + Constants.DEFAULT_CHANNEL;
|
window.location = TeamStore.getCurrentTeamUrl() + '/channels/' + Constants.DEFAULT_CHANNEL;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ var Utils;
|
|||||||
import Constants from '../utils/constants.jsx';
|
import Constants from '../utils/constants.jsx';
|
||||||
const ActionTypes = Constants.ActionTypes;
|
const ActionTypes = Constants.ActionTypes;
|
||||||
|
|
||||||
import BrowserStore from '../stores/browser_store.jsx';
|
|
||||||
|
|
||||||
const CHANGE_EVENT = 'change';
|
const CHANGE_EVENT = 'change';
|
||||||
const LEAVE_EVENT = 'leave';
|
const LEAVE_EVENT = 'leave';
|
||||||
const MORE_CHANGE_EVENT = 'change';
|
const MORE_CHANGE_EVENT = 'change';
|
||||||
@@ -21,7 +19,38 @@ class ChannelStoreClass extends EventEmitter {
|
|||||||
|
|
||||||
this.setMaxListeners(11);
|
this.setMaxListeners(11);
|
||||||
|
|
||||||
|
this.emitChange = this.emitChange.bind(this);
|
||||||
|
this.addChangeListener = this.addChangeListener.bind(this);
|
||||||
|
this.removeChangeListener = this.removeChangeListener.bind(this);
|
||||||
|
this.emitMoreChange = this.emitMoreChange.bind(this);
|
||||||
|
this.addMoreChangeListener = this.addMoreChangeListener.bind(this);
|
||||||
|
this.removeMoreChangeListener = this.removeMoreChangeListener.bind(this);
|
||||||
|
this.emitExtraInfoChange = this.emitExtraInfoChange.bind(this);
|
||||||
|
this.addExtraInfoChangeListener = this.addExtraInfoChangeListener.bind(this);
|
||||||
|
this.removeExtraInfoChangeListener = this.removeExtraInfoChangeListener.bind(this);
|
||||||
|
this.emitLeave = this.emitLeave.bind(this);
|
||||||
|
this.addLeaveListener = this.addLeaveListener.bind(this);
|
||||||
|
this.removeLeaveListener = this.removeLeaveListener.bind(this);
|
||||||
|
this.findFirstBy = this.findFirstBy.bind(this);
|
||||||
|
this.get = this.get.bind(this);
|
||||||
|
this.getMember = this.getMember.bind(this);
|
||||||
|
this.getByName = this.getByName.bind(this);
|
||||||
|
this.pSetPostMode = this.pSetPostMode.bind(this);
|
||||||
|
this.getPostMode = this.getPostMode.bind(this);
|
||||||
|
|
||||||
this.currentId = null;
|
this.currentId = null;
|
||||||
|
this.postMode = this.POST_MODE_CHANNEL;
|
||||||
|
this.channels = [];
|
||||||
|
this.channelMembers = {};
|
||||||
|
this.moreChannels = {};
|
||||||
|
this.moreChannels.loading = true;
|
||||||
|
this.extraInfos = {};
|
||||||
|
}
|
||||||
|
get POST_MODE_CHANNEL() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
get POST_MODE_FOCUS() {
|
||||||
|
return 2;
|
||||||
}
|
}
|
||||||
emitChange() {
|
emitChange() {
|
||||||
this.emit(CHANGE_EVENT);
|
this.emit(CHANGE_EVENT);
|
||||||
@@ -90,16 +119,6 @@ class ChannelStoreClass extends EventEmitter {
|
|||||||
setCurrentId(id) {
|
setCurrentId(id) {
|
||||||
this.currentId = id;
|
this.currentId = id;
|
||||||
}
|
}
|
||||||
setLastVisitedName(name) {
|
|
||||||
if (name == null) {
|
|
||||||
BrowserStore.removeItem('last_visited_name');
|
|
||||||
} else {
|
|
||||||
BrowserStore.setItem('last_visited_name', name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
getLastVisitedName() {
|
|
||||||
return BrowserStore.getItem('last_visited_name');
|
|
||||||
}
|
|
||||||
resetCounts(id) {
|
resetCounts(id) {
|
||||||
var cm = this.pGetChannelMembers();
|
var cm = this.pGetChannelMembers();
|
||||||
for (var cmid in cm) {
|
for (var cmid in cm) {
|
||||||
@@ -192,10 +211,10 @@ class ChannelStoreClass extends EventEmitter {
|
|||||||
this.pStoreChannels(channels);
|
this.pStoreChannels(channels);
|
||||||
}
|
}
|
||||||
pStoreChannels(channels) {
|
pStoreChannels(channels) {
|
||||||
BrowserStore.setItem('channels', channels);
|
this.channels = channels;
|
||||||
}
|
}
|
||||||
pGetChannels() {
|
pGetChannels() {
|
||||||
return BrowserStore.getItem('channels', []);
|
return this.channels;
|
||||||
}
|
}
|
||||||
pStoreChannelMember(channelMember) {
|
pStoreChannelMember(channelMember) {
|
||||||
var members = this.pGetChannelMembers();
|
var members = this.pGetChannelMembers();
|
||||||
@@ -203,49 +222,58 @@ class ChannelStoreClass extends EventEmitter {
|
|||||||
this.pStoreChannelMembers(members);
|
this.pStoreChannelMembers(members);
|
||||||
}
|
}
|
||||||
pStoreChannelMembers(channelMembers) {
|
pStoreChannelMembers(channelMembers) {
|
||||||
BrowserStore.setItem('channel_members', channelMembers);
|
this.channelMembers = channelMembers;
|
||||||
}
|
}
|
||||||
pGetChannelMembers() {
|
pGetChannelMembers() {
|
||||||
return BrowserStore.getItem('channel_members', {});
|
return this.channelMembers;
|
||||||
}
|
}
|
||||||
pStoreMoreChannels(channels) {
|
pStoreMoreChannels(channels) {
|
||||||
BrowserStore.setItem('more_channels', channels);
|
this.moreChannels = channels;
|
||||||
}
|
}
|
||||||
pGetMoreChannels() {
|
pGetMoreChannels() {
|
||||||
var channels = BrowserStore.getItem('more_channels');
|
return this.moreChannels;
|
||||||
|
|
||||||
if (channels == null) {
|
|
||||||
channels = {};
|
|
||||||
channels.loading = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return channels;
|
|
||||||
}
|
}
|
||||||
pStoreExtraInfos(extraInfos) {
|
pStoreExtraInfos(extraInfos) {
|
||||||
BrowserStore.setItem('extra_infos', extraInfos);
|
this.extraInfos = extraInfos;
|
||||||
}
|
}
|
||||||
pGetExtraInfos() {
|
pGetExtraInfos() {
|
||||||
return BrowserStore.getItem('extra_infos', {});
|
return this.extraInfos;
|
||||||
}
|
}
|
||||||
isDefault(channel) {
|
isDefault(channel) {
|
||||||
return channel.name === Constants.DEFAULT_CHANNEL;
|
return channel.name === Constants.DEFAULT_CHANNEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pSetPostMode(mode) {
|
||||||
|
this.postMode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPostMode() {
|
||||||
|
return this.postMode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var ChannelStore = new ChannelStoreClass();
|
var ChannelStore = new ChannelStoreClass();
|
||||||
|
|
||||||
ChannelStore.dispatchToken = AppDispatcher.register(function handleAction(payload) {
|
ChannelStore.dispatchToken = AppDispatcher.register((payload) => {
|
||||||
var action = payload.action;
|
var action = payload.action;
|
||||||
var currentId;
|
var currentId;
|
||||||
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case ActionTypes.CLICK_CHANNEL:
|
case ActionTypes.CLICK_CHANNEL:
|
||||||
ChannelStore.setCurrentId(action.id);
|
ChannelStore.setCurrentId(action.id);
|
||||||
ChannelStore.setLastVisitedName(action.name);
|
|
||||||
ChannelStore.resetCounts(action.id);
|
ChannelStore.resetCounts(action.id);
|
||||||
|
ChannelStore.pSetPostMode(ChannelStore.POST_MODE_CHANNEL);
|
||||||
ChannelStore.emitChange();
|
ChannelStore.emitChange();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ActionTypes.RECIEVED_FOCUSED_POST: {
|
||||||
|
const post = action.post_list.posts[action.postId];
|
||||||
|
ChannelStore.setCurrentId(post.channel_id);
|
||||||
|
ChannelStore.pSetPostMode(ChannelStore.POST_MODE_FOCUS);
|
||||||
|
ChannelStore.emitChange();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case ActionTypes.RECIEVED_CHANNELS:
|
case ActionTypes.RECIEVED_CHANNELS:
|
||||||
ChannelStore.pStoreChannels(action.channels);
|
ChannelStore.pStoreChannels(action.channels);
|
||||||
ChannelStore.pStoreChannelMembers(action.members);
|
ChannelStore.pStoreChannelMembers(action.members);
|
||||||
|
|||||||
@@ -12,9 +12,10 @@ import Constants from '../utils/constants.jsx';
|
|||||||
const ActionTypes = Constants.ActionTypes;
|
const ActionTypes = Constants.ActionTypes;
|
||||||
|
|
||||||
const CHANGE_EVENT = 'change';
|
const CHANGE_EVENT = 'change';
|
||||||
const SELECTED_POST_CHANGE_EVENT = 'selected_post_change';
|
const FOCUSED_POST_CHANGE = 'focused_post_change';
|
||||||
const EDIT_POST_EVENT = 'edit_post';
|
const EDIT_POST_EVENT = 'edit_post';
|
||||||
const POSTS_VIEW_JUMP_EVENT = 'post_list_jump';
|
const POSTS_VIEW_JUMP_EVENT = 'post_list_jump';
|
||||||
|
const SELECTED_POST_CHANGE_EVENT = 'selected_post_change';
|
||||||
|
|
||||||
class PostStoreClass extends EventEmitter {
|
class PostStoreClass extends EventEmitter {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -24,10 +25,6 @@ class PostStoreClass extends EventEmitter {
|
|||||||
this.addChangeListener = this.addChangeListener.bind(this);
|
this.addChangeListener = this.addChangeListener.bind(this);
|
||||||
this.removeChangeListener = this.removeChangeListener.bind(this);
|
this.removeChangeListener = this.removeChangeListener.bind(this);
|
||||||
|
|
||||||
this.emitSelectedPostChange = this.emitSelectedPostChange.bind(this);
|
|
||||||
this.addSelectedPostChangeListener = this.addSelectedPostChangeListener.bind(this);
|
|
||||||
this.removeSelectedPostChangeListener = this.removeSelectedPostChangeListener.bind(this);
|
|
||||||
|
|
||||||
this.emitEditPost = this.emitEditPost.bind(this);
|
this.emitEditPost = this.emitEditPost.bind(this);
|
||||||
this.addEditPostListener = this.addEditPostListener.bind(this);
|
this.addEditPostListener = this.addEditPostListener.bind(this);
|
||||||
this.removeEditPostListener = this.removeEditPostListner.bind(this);
|
this.removeEditPostListener = this.removeEditPostListner.bind(this);
|
||||||
@@ -36,27 +33,49 @@ class PostStoreClass extends EventEmitter {
|
|||||||
this.addPostsViewJumpListener = this.addPostsViewJumpListener.bind(this);
|
this.addPostsViewJumpListener = this.addPostsViewJumpListener.bind(this);
|
||||||
this.removePostsViewJumpListener = this.removePostsViewJumpListener.bind(this);
|
this.removePostsViewJumpListener = this.removePostsViewJumpListener.bind(this);
|
||||||
|
|
||||||
this.getCurrentPosts = this.getCurrentPosts.bind(this);
|
this.emitPostFocused = this.emitPostFocused.bind(this);
|
||||||
|
this.addPostFocusedListener = this.addPostFocusedListener.bind(this);
|
||||||
|
this.removePostFocusedListener = this.removePostFocusedListener.bind(this);
|
||||||
|
|
||||||
|
this.makePostsInfo = this.makePostsInfo.bind(this);
|
||||||
|
|
||||||
|
this.getAllPosts = this.getAllPosts.bind(this);
|
||||||
|
this.getEarliestPost = this.getEarliestPost.bind(this);
|
||||||
|
this.getLatestPost = this.getLatestPost.bind(this);
|
||||||
|
this.getVisiblePosts = this.getVisiblePosts.bind(this);
|
||||||
|
this.getVisibilityAtTop = this.getVisibilityAtTop.bind(this);
|
||||||
|
this.getVisibilityAtBottom = this.getVisibilityAtBottom.bind(this);
|
||||||
|
this.requestVisibilityIncrease = this.requestVisibilityIncrease.bind(this);
|
||||||
|
this.getFocusedPostId = this.getFocusedPostId.bind(this);
|
||||||
|
|
||||||
this.storePosts = this.storePosts.bind(this);
|
this.storePosts = this.storePosts.bind(this);
|
||||||
this.pStorePosts = this.pStorePosts.bind(this);
|
|
||||||
this.getPosts = this.getPosts.bind(this);
|
|
||||||
this.getPost = this.getPost.bind(this);
|
|
||||||
this.storePost = this.storePost.bind(this);
|
this.storePost = this.storePost.bind(this);
|
||||||
this.pStorePost = this.pStorePost.bind(this);
|
this.storeFocusedPost = this.storeFocusedPost.bind(this);
|
||||||
|
this.checkBounds = this.checkBounds.bind(this);
|
||||||
|
|
||||||
|
this.clearFocusedPost = this.clearFocusedPost.bind(this);
|
||||||
|
this.clearChannelVisibility = this.clearChannelVisibility.bind(this);
|
||||||
|
|
||||||
this.removePost = this.removePost.bind(this);
|
this.removePost = this.removePost.bind(this);
|
||||||
this.storePendingPost = this.storePendingPost.bind(this);
|
|
||||||
this.pStorePendingPosts = this.pStorePendingPosts.bind(this);
|
|
||||||
this.getPendingPosts = this.getPendingPosts.bind(this);
|
this.getPendingPosts = this.getPendingPosts.bind(this);
|
||||||
this.storeUnseenDeletedPost = this.storeUnseenDeletedPost.bind(this);
|
this.storePendingPost = this.storePendingPost.bind(this);
|
||||||
this.storeUnseenDeletedPosts = this.storeUnseenDeletedPosts.bind(this);
|
|
||||||
this.getUnseenDeletedPosts = this.getUnseenDeletedPosts.bind(this);
|
|
||||||
this.clearUnseenDeletedPosts = this.clearUnseenDeletedPosts.bind(this);
|
|
||||||
this.removePendingPost = this.removePendingPost.bind(this);
|
this.removePendingPost = this.removePendingPost.bind(this);
|
||||||
this.pRemovePendingPost = this.pRemovePendingPost.bind(this);
|
|
||||||
this.clearPendingPosts = this.clearPendingPosts.bind(this);
|
this.clearPendingPosts = this.clearPendingPosts.bind(this);
|
||||||
this.updatePendingPost = this.updatePendingPost.bind(this);
|
this.updatePendingPost = this.updatePendingPost.bind(this);
|
||||||
|
|
||||||
|
this.storeUnseenDeletedPost = this.storeUnseenDeletedPost.bind(this);
|
||||||
|
this.getUnseenDeletedPosts = this.getUnseenDeletedPosts.bind(this);
|
||||||
|
this.clearUnseenDeletedPosts = this.clearUnseenDeletedPosts.bind(this);
|
||||||
|
|
||||||
|
// These functions are bad and work should be done to remove this system when the RHS dies
|
||||||
this.storeSelectedPost = this.storeSelectedPost.bind(this);
|
this.storeSelectedPost = this.storeSelectedPost.bind(this);
|
||||||
this.getSelectedPost = this.getSelectedPost.bind(this);
|
this.getSelectedPost = this.getSelectedPost.bind(this);
|
||||||
|
this.emitSelectedPostChange = this.emitSelectedPostChange.bind(this);
|
||||||
|
this.addSelectedPostChangeListener = this.addSelectedPostChangeListener.bind(this);
|
||||||
|
this.removeSelectedPostChangeListener = this.removeSelectedPostChangeListener.bind(this);
|
||||||
|
this.selectedPost = null;
|
||||||
|
|
||||||
this.getEmptyDraft = this.getEmptyDraft.bind(this);
|
this.getEmptyDraft = this.getEmptyDraft.bind(this);
|
||||||
this.storeCurrentDraft = this.storeCurrentDraft.bind(this);
|
this.storeCurrentDraft = this.storeCurrentDraft.bind(this);
|
||||||
this.getCurrentDraft = this.getCurrentDraft.bind(this);
|
this.getCurrentDraft = this.getCurrentDraft.bind(this);
|
||||||
@@ -70,6 +89,9 @@ class PostStoreClass extends EventEmitter {
|
|||||||
this.getLatestUpdate = this.getLatestUpdate.bind(this);
|
this.getLatestUpdate = this.getLatestUpdate.bind(this);
|
||||||
this.getCurrentUsersLatestPost = this.getCurrentUsersLatestPost.bind(this);
|
this.getCurrentUsersLatestPost = this.getCurrentUsersLatestPost.bind(this);
|
||||||
this.getCommentCount = this.getCommentCount.bind(this);
|
this.getCommentCount = this.getCommentCount.bind(this);
|
||||||
|
|
||||||
|
this.postsInfo = {};
|
||||||
|
this.currentFocusedPostId = null;
|
||||||
}
|
}
|
||||||
emitChange() {
|
emitChange() {
|
||||||
this.emit(CHANGE_EVENT);
|
this.emit(CHANGE_EVENT);
|
||||||
@@ -83,16 +105,16 @@ class PostStoreClass extends EventEmitter {
|
|||||||
this.removeListener(CHANGE_EVENT, callback);
|
this.removeListener(CHANGE_EVENT, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
emitSelectedPostChange(fromSearch) {
|
emitPostFocused() {
|
||||||
this.emit(SELECTED_POST_CHANGE_EVENT, fromSearch);
|
this.emit(FOCUSED_POST_CHANGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
addSelectedPostChangeListener(callback) {
|
addPostFocusedListener(callback) {
|
||||||
this.on(SELECTED_POST_CHANGE_EVENT, callback);
|
this.on(FOCUSED_POST_CHANGE, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeSelectedPostChangeListener(callback) {
|
removePostFocusedListener(callback) {
|
||||||
this.removeListener(SELECTED_POST_CHANGE_EVENT, callback);
|
this.removeListener(FOCUSED_POST_CHANGE, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
emitEditPost(post) {
|
emitEditPost(post) {
|
||||||
@@ -131,82 +153,336 @@ class PostStoreClass extends EventEmitter {
|
|||||||
this.emitPostsViewJump(Constants.PostsViewJumpTypes.SIDEBAR_OPEN, null);
|
this.emitPostsViewJump(Constants.PostsViewJumpTypes.SIDEBAR_OPEN, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
getCurrentPosts() {
|
// All this does is makes sure the postsInfo is not null for the specified channel
|
||||||
var currentId = ChannelStore.getCurrentId();
|
makePostsInfo(id) {
|
||||||
|
if (!this.postsInfo.hasOwnProperty(id)) {
|
||||||
if (currentId != null) {
|
this.postsInfo[id] = {};
|
||||||
return this.getPosts(currentId);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllPosts(id) {
|
||||||
|
if (this.postsInfo.hasOwnProperty(id)) {
|
||||||
|
return Object.assign({}, this.postsInfo[id].postList);
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
storePosts(channelId, newPostsView) {
|
|
||||||
if (isPostListNull(newPostsView)) {
|
getEarliestPost(id) {
|
||||||
|
if (this.postsInfo.hasOwnProperty(id)) {
|
||||||
|
return this.postsInfo[id].postList.posts[this.postsInfo[id].postList.order[this.postsInfo[id].postList.order.length - 1]];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getLatestPost(id) {
|
||||||
|
if (this.postsInfo.hasOwnProperty(id)) {
|
||||||
|
return this.postsInfo[id].postList.posts[this.postsInfo[id].postList.order[0]];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getVisiblePosts(id) {
|
||||||
|
if (this.postsInfo.hasOwnProperty(id) && this.postsInfo[id].hasOwnProperty('postList')) {
|
||||||
|
const postList = JSON.parse(JSON.stringify(this.postsInfo[id].postList));
|
||||||
|
|
||||||
|
// Only limit visibility if we are not focused on a post
|
||||||
|
if (this.currentFocusedPostId === null) {
|
||||||
|
postList.order = postList.order.slice(0, this.postsInfo[id].endVisible);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add pending posts
|
||||||
|
if (this.postsInfo[id].hasOwnProperty('pendingPosts')) {
|
||||||
|
Object.assign(postList.posts, this.postsInfo[id].pendingPosts.posts);
|
||||||
|
postList.order = this.postsInfo[id].pendingPosts.order.concat(postList.order);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add delteted posts
|
||||||
|
if (this.postsInfo[id].hasOwnProperty('deletedPosts')) {
|
||||||
|
Object.assign(postList.posts, this.postsInfo[id].deletedPosts);
|
||||||
|
|
||||||
|
for (const postID in this.postsInfo[id].deletedPosts) {
|
||||||
|
if (this.postsInfo[id].deletedPosts.hasOwnProperty(postID)) {
|
||||||
|
postList.order.push(postID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge would be faster
|
||||||
|
postList.order.sort((a, b) => {
|
||||||
|
if (postList.posts[a].create_at > postList.posts[b].create_at) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (postList.posts[a].create_at < postList.posts[b].create_at) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return postList;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getVisibilityAtTop(id) {
|
||||||
|
if (this.postsInfo.hasOwnProperty(id)) {
|
||||||
|
return this.postsInfo[id].atTop && this.postsInfo[id].endVisible >= this.postsInfo[id].postList.order.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
getVisibilityAtBottom(id) {
|
||||||
|
if (this.postsInfo.hasOwnProperty(id)) {
|
||||||
|
return this.postsInfo[id].atBottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if posts need to be fetched
|
||||||
|
requestVisibilityIncrease(id, ammount) {
|
||||||
|
const endVisible = this.postsInfo[id].endVisible;
|
||||||
|
const postList = this.postsInfo[id].postList;
|
||||||
|
if (this.getVisibilityAtTop(id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.postsInfo[id].endVisible += ammount;
|
||||||
|
this.emitChange();
|
||||||
|
return endVisible + ammount > postList.order.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
getFocusedPostId() {
|
||||||
|
return this.currentFocusedPostId;
|
||||||
|
}
|
||||||
|
|
||||||
|
storePosts(id, newPosts) {
|
||||||
|
if (isPostListNull(newPosts)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var postList = makePostListNonNull(this.getPosts(channelId));
|
const combinedPosts = makePostListNonNull(this.getAllPosts(id));
|
||||||
|
|
||||||
for (const pid in newPostsView.posts) {
|
for (const pid in newPosts.posts) {
|
||||||
if (newPostsView.posts.hasOwnProperty(pid)) {
|
if (newPosts.posts.hasOwnProperty(pid)) {
|
||||||
const np = newPostsView.posts[pid];
|
const np = newPosts.posts[pid];
|
||||||
if (np.delete_at === 0) {
|
if (np.delete_at === 0) {
|
||||||
postList.posts[pid] = np;
|
combinedPosts.posts[pid] = np;
|
||||||
if (postList.order.indexOf(pid) === -1) {
|
if (combinedPosts.order.indexOf(pid) === -1) {
|
||||||
postList.order.push(pid);
|
combinedPosts.order.push(pid);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (pid in postList.posts) {
|
if (pid in combinedPosts.posts) {
|
||||||
delete postList.posts[pid];
|
Reflect.deleteProperty(combinedPosts.posts, pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
const index = postList.order.indexOf(pid);
|
const index = combinedPosts.order.indexOf(pid);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
postList.order.splice(index, 1);
|
combinedPosts.order.splice(index, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
postList.order.sort((a, b) => {
|
combinedPosts.order.sort((a, b) => {
|
||||||
if (postList.posts[a].create_at > postList.posts[b].create_at) {
|
if (combinedPosts.posts[a].create_at > combinedPosts.posts[b].create_at) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (postList.posts[a].create_at < postList.posts[b].create_at) {
|
if (combinedPosts.posts[a].create_at < combinedPosts.posts[b].create_at) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
var latestUpdate = 0;
|
this.makePostsInfo(id);
|
||||||
for (var pid in postList.posts) {
|
this.postsInfo[id].postList = combinedPosts;
|
||||||
if (postList.posts[pid].update_at > latestUpdate) {
|
}
|
||||||
latestUpdate = postList.posts[pid].update_at;
|
|
||||||
}
|
storePost(post) {
|
||||||
|
const postList = makePostListNonNull(this.getAllPosts(post.channel_id));
|
||||||
|
|
||||||
|
if (post.pending_post_id !== '') {
|
||||||
|
this.removePendingPost(post.channel_id, post.pending_post_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.storeLatestUpdate(channelId, latestUpdate);
|
post.pending_post_id = '';
|
||||||
this.pStorePosts(channelId, postList);
|
|
||||||
|
postList.posts[post.id] = post;
|
||||||
|
if (postList.order.indexOf(post.id) === -1) {
|
||||||
|
postList.order.unshift(post.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.makePostsInfo(post.channel_id);
|
||||||
|
this.postsInfo[post.channel_id].postList = postList;
|
||||||
|
}
|
||||||
|
|
||||||
|
storeFocusedPost(postId, postList) {
|
||||||
|
const focusedPost = postList.posts[postId];
|
||||||
|
if (!focusedPost) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.currentFocusedPostId = postId;
|
||||||
|
this.storePosts(postId, postList);
|
||||||
|
}
|
||||||
|
|
||||||
|
checkBounds(id, numRequested, postList, before) {
|
||||||
|
if (numRequested > postList.order.length) {
|
||||||
|
if (before) {
|
||||||
|
this.postsInfo[id].atTop = true;
|
||||||
|
} else {
|
||||||
|
this.postsInfo[id].atBottom = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clearFocusedPost() {
|
||||||
|
if (this.currentFocusedPostId != null) {
|
||||||
|
Reflect.deleteProperty(this.postsInfo, this.currentFocusedPostId);
|
||||||
|
this.currentFocusedPostId = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clearChannelVisibility(id, atBottom) {
|
||||||
|
this.makePostsInfo(id);
|
||||||
|
this.postsInfo[id].endVisible = Constants.POST_CHUNK_SIZE;
|
||||||
|
this.postsInfo[id].atTop = false;
|
||||||
|
this.postsInfo[id].atBottom = atBottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
removePost(post) {
|
||||||
|
const channelId = post.channel_id;
|
||||||
|
this.makePostsInfo(channelId);
|
||||||
|
const postList = this.postsInfo[channelId].postList;
|
||||||
|
if (isPostListNull(postList)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (post.id in postList.posts) {
|
||||||
|
Reflect.deleteProperty(postList.posts, post.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const index = postList.order.indexOf(post.id);
|
||||||
|
if (index !== -1) {
|
||||||
|
postList.order.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.postsInfo[channelId].postList = postList;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPendingPosts(channelId) {
|
||||||
|
if (this.postsInfo.hasOwnProperty(channelId)) {
|
||||||
|
return this.postsInfo[channelId].pendingPosts;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
storePendingPost(post) {
|
||||||
|
post.state = Constants.POST_LOADING;
|
||||||
|
|
||||||
|
const postList = makePostListNonNull(this.getPendingPosts(post.channel_id));
|
||||||
|
|
||||||
|
postList.posts[post.pending_post_id] = post;
|
||||||
|
postList.order.unshift(post.pending_post_id);
|
||||||
|
|
||||||
|
this.makePostsInfo(post.channel_id);
|
||||||
|
this.postsInfo[post.channel_id].pendingPosts = postList;
|
||||||
this.emitChange();
|
this.emitChange();
|
||||||
}
|
}
|
||||||
pStorePosts(channelId, posts) {
|
|
||||||
BrowserStore.setItem('posts_' + channelId, posts);
|
removePendingPost(channelId, pendingPostId) {
|
||||||
|
const postList = makePostListNonNull(this.getPendingPosts(channelId));
|
||||||
|
|
||||||
|
Reflect.deleteProperty(postList.posts, pendingPostId);
|
||||||
|
const index = postList.order.indexOf(pendingPostId);
|
||||||
|
if (index !== -1) {
|
||||||
|
postList.order.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.postsInfo[channelId].pendingPosts = postList;
|
||||||
|
this.emitChange();
|
||||||
}
|
}
|
||||||
getPosts(channelId) {
|
|
||||||
return BrowserStore.getItem('posts_' + channelId);
|
clearPendingPosts(channelId) {
|
||||||
|
if (this.postsInfo.hasOwnProperty(channelId)) {
|
||||||
|
Reflect.deleteProperty(this.postsInfo[channelId], 'pendingPosts');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
getPost(channelId, postId) {
|
|
||||||
return this.getPosts(channelId).posts[postId];
|
updatePendingPost(post) {
|
||||||
|
const postList = makePostListNonNull(this.getPendingPosts(post.channel_id));
|
||||||
|
|
||||||
|
if (postList.order.indexOf(post.pending_post_id) === -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
postList.posts[post.pending_post_id] = post;
|
||||||
|
this.postsInfo[post.channel_id].pendingPosts = postList;
|
||||||
|
this.emitChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
storeUnseenDeletedPost(post) {
|
||||||
|
let posts = this.getUnseenDeletedPosts(post.channel_id);
|
||||||
|
|
||||||
|
if (!posts) {
|
||||||
|
posts = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
post.message = '(message deleted)';
|
||||||
|
post.state = Constants.POST_DELETED;
|
||||||
|
post.filenames = [];
|
||||||
|
|
||||||
|
posts[post.id] = post;
|
||||||
|
this.postsInfo[post.channel_id].deletedPosts = posts;
|
||||||
|
}
|
||||||
|
|
||||||
|
getUnseenDeletedPosts(channelId) {
|
||||||
|
if (this.postsInfo.hasOwnProperty(channelId)) {
|
||||||
|
return this.postsInfo[channelId].deletedPosts;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
clearUnseenDeletedPosts(channelId) {
|
||||||
|
if (this.postsInfo.hasOwnProperty(channelId)) {
|
||||||
|
Reflect.deleteProperty(this.postsInfo[channelId], 'deletedPosts');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
storeSelectedPost(postList) {
|
||||||
|
this.selectedPost = postList;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSelectedPost() {
|
||||||
|
return this.selectedPost;
|
||||||
|
}
|
||||||
|
|
||||||
|
emitSelectedPostChange(fromSearch) {
|
||||||
|
this.emit(SELECTED_POST_CHANGE_EVENT, fromSearch);
|
||||||
|
}
|
||||||
|
|
||||||
|
addSelectedPostChangeListener(callback) {
|
||||||
|
this.on(SELECTED_POST_CHANGE_EVENT, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeSelectedPostChangeListener(callback) {
|
||||||
|
this.removeListener(SELECTED_POST_CHANGE_EVENT, callback);
|
||||||
|
}
|
||||||
|
|
||||||
getCurrentUsersLatestPost(channelId, rootId) {
|
getCurrentUsersLatestPost(channelId, rootId) {
|
||||||
const userId = UserStore.getCurrentId();
|
const userId = UserStore.getCurrentId();
|
||||||
var postList = makePostListNonNull(this.getPosts(channelId));
|
var postList = makePostListNonNull(this.getAllPosts(channelId));
|
||||||
var i = 0;
|
var i = 0;
|
||||||
var len = postList.order.length;
|
var len = postList.order.length;
|
||||||
var lastPost = null;
|
var lastPost = null;
|
||||||
|
|
||||||
for (i; i < len; i++) {
|
for (i; i < len; i++) {
|
||||||
let post = postList.posts[postList.order[i]];
|
const post = postList.posts[postList.order[i]];
|
||||||
if (post.user_id === userId && (post.props && !post.props.from_webhook || !post.props)) {
|
if (post.user_id === userId && (post.props && !post.props.from_webhook || !post.props)) {
|
||||||
if (rootId) {
|
if (rootId) {
|
||||||
if (post.root_id === rootId || post.id === rootId) {
|
if (post.root_id === rootId || post.id === rootId) {
|
||||||
@@ -222,146 +498,7 @@ class PostStoreClass extends EventEmitter {
|
|||||||
|
|
||||||
return lastPost;
|
return lastPost;
|
||||||
}
|
}
|
||||||
storePost(post) {
|
|
||||||
this.pStorePost(post);
|
|
||||||
this.emitChange();
|
|
||||||
}
|
|
||||||
pStorePost(post) {
|
|
||||||
var postList = this.getPosts(post.channel_id);
|
|
||||||
postList = makePostListNonNull(postList);
|
|
||||||
|
|
||||||
if (post.pending_post_id !== '') {
|
|
||||||
this.removePendingPost(post.channel_id, post.pending_post_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
post.pending_post_id = '';
|
|
||||||
|
|
||||||
postList.posts[post.id] = post;
|
|
||||||
if (postList.order.indexOf(post.id) === -1) {
|
|
||||||
postList.order.unshift(post.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.pStorePosts(post.channel_id, postList);
|
|
||||||
}
|
|
||||||
removePost(postId, channelId) {
|
|
||||||
var postList = this.getPosts(channelId);
|
|
||||||
if (isPostListNull(postList)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (postId in postList.posts) {
|
|
||||||
delete postList.posts[postId];
|
|
||||||
}
|
|
||||||
|
|
||||||
var index = postList.order.indexOf(postId);
|
|
||||||
if (index !== -1) {
|
|
||||||
postList.order.splice(index, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.pStorePosts(channelId, postList);
|
|
||||||
}
|
|
||||||
storePendingPost(post) {
|
|
||||||
post.state = Constants.POST_LOADING;
|
|
||||||
|
|
||||||
var postList = this.getPendingPosts(post.channel_id);
|
|
||||||
postList = makePostListNonNull(postList);
|
|
||||||
|
|
||||||
postList.posts[post.pending_post_id] = post;
|
|
||||||
postList.order.unshift(post.pending_post_id);
|
|
||||||
this.pStorePendingPosts(post.channel_id, postList);
|
|
||||||
this.emitChange();
|
|
||||||
}
|
|
||||||
pStorePendingPosts(channelId, postList) {
|
|
||||||
var posts = postList.posts;
|
|
||||||
|
|
||||||
// sort failed posts to the bottom
|
|
||||||
postList.order.sort((a, b) => {
|
|
||||||
if (posts[a].state === Constants.POST_LOADING && posts[b].state === Constants.POST_FAILED) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (posts[a].state === Constants.POST_FAILED && posts[b].state === Constants.POST_LOADING) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (posts[a].create_at > posts[b].create_at) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (posts[a].create_at < posts[b].create_at) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
BrowserStore.setGlobalItem('pending_posts_' + channelId, postList);
|
|
||||||
}
|
|
||||||
getPendingPosts(channelId) {
|
|
||||||
return BrowserStore.getGlobalItem('pending_posts_' + channelId);
|
|
||||||
}
|
|
||||||
storeUnseenDeletedPost(post) {
|
|
||||||
var posts = this.getUnseenDeletedPosts(post.channel_id);
|
|
||||||
|
|
||||||
if (!posts) {
|
|
||||||
posts = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
post.message = '(message deleted)';
|
|
||||||
post.state = Constants.POST_DELETED;
|
|
||||||
post.filenames = [];
|
|
||||||
|
|
||||||
posts[post.id] = post;
|
|
||||||
this.storeUnseenDeletedPosts(post.channel_id, posts);
|
|
||||||
}
|
|
||||||
storeUnseenDeletedPosts(channelId, posts) {
|
|
||||||
BrowserStore.setItem('deleted_posts_' + channelId, posts);
|
|
||||||
}
|
|
||||||
getUnseenDeletedPosts(channelId) {
|
|
||||||
return BrowserStore.getItem('deleted_posts_' + channelId);
|
|
||||||
}
|
|
||||||
clearUnseenDeletedPosts(channelId) {
|
|
||||||
BrowserStore.setItem('deleted_posts_' + channelId, {});
|
|
||||||
}
|
|
||||||
removePendingPost(channelId, pendingPostId) {
|
|
||||||
this.pRemovePendingPost(channelId, pendingPostId);
|
|
||||||
this.emitChange();
|
|
||||||
}
|
|
||||||
pRemovePendingPost(channelId, pendingPostId) {
|
|
||||||
var postList = this.getPendingPosts(channelId);
|
|
||||||
postList = makePostListNonNull(postList);
|
|
||||||
|
|
||||||
if (pendingPostId in postList.posts) {
|
|
||||||
delete postList.posts[pendingPostId];
|
|
||||||
}
|
|
||||||
var index = postList.order.indexOf(pendingPostId);
|
|
||||||
if (index !== -1) {
|
|
||||||
postList.order.splice(index, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.pStorePendingPosts(channelId, postList);
|
|
||||||
}
|
|
||||||
clearPendingPosts() {
|
|
||||||
BrowserStore.actionOnGlobalItemsWithPrefix('pending_posts_', (key) => {
|
|
||||||
BrowserStore.removeItem(key);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
updatePendingPost(post) {
|
|
||||||
var postList = this.getPendingPosts(post.channel_id);
|
|
||||||
postList = makePostListNonNull(postList);
|
|
||||||
|
|
||||||
if (postList.order.indexOf(post.pending_post_id) === -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
postList.posts[post.pending_post_id] = post;
|
|
||||||
this.pStorePendingPosts(post.channel_id, postList);
|
|
||||||
this.emitChange();
|
|
||||||
}
|
|
||||||
storeSelectedPost(postList) {
|
|
||||||
BrowserStore.setItem('select_post', postList);
|
|
||||||
}
|
|
||||||
getSelectedPost() {
|
|
||||||
return BrowserStore.getItem('select_post');
|
|
||||||
}
|
|
||||||
getEmptyDraft() {
|
getEmptyDraft() {
|
||||||
return {message: '', uploadsInProgress: [], previews: []};
|
return {message: '', uploadsInProgress: [], previews: []};
|
||||||
}
|
}
|
||||||
@@ -402,16 +539,23 @@ class PostStoreClass extends EventEmitter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
storeLatestUpdate(channelId, time) {
|
storeLatestUpdate(channelId, time) {
|
||||||
BrowserStore.setItem('latest_post_' + channelId, time);
|
if (!this.postsInfo.hasOwnProperty(channelId)) {
|
||||||
|
this.postsInfo[channelId] = {};
|
||||||
|
}
|
||||||
|
this.postsInfo[channelId].latestPost = time;
|
||||||
}
|
}
|
||||||
getLatestUpdate(channelId) {
|
getLatestUpdate(channelId) {
|
||||||
return BrowserStore.getItem('latest_post_' + channelId, 0);
|
if (this.postsInfo.hasOwnProperty(channelId) && this.postsInfo[channelId].hasOwnProperty('latestPost')) {
|
||||||
|
return this.postsInfo[channelId].latestPost;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
getCommentCount(post) {
|
getCommentCount(post) {
|
||||||
const posts = this.getPosts(post.channel_id).posts;
|
const posts = this.getPosts(post.channel_id).posts;
|
||||||
|
|
||||||
let commentCount = 0;
|
let commentCount = 0;
|
||||||
for (let id in posts) {
|
for (const id in posts) {
|
||||||
if (posts.hasOwnProperty(id)) {
|
if (posts.hasOwnProperty(id)) {
|
||||||
if (posts[id].root_id === post.id) {
|
if (posts[id].root_id === post.id) {
|
||||||
commentCount += 1;
|
commentCount += 1;
|
||||||
@@ -429,20 +573,45 @@ PostStore.dispatchToken = AppDispatcher.register((payload) => {
|
|||||||
var action = payload.action;
|
var action = payload.action;
|
||||||
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case ActionTypes.RECIEVED_POSTS:
|
case ActionTypes.RECIEVED_POSTS: {
|
||||||
PostStore.storePosts(action.id, makePostListNonNull(action.post_list));
|
const id = PostStore.currentFocusedPostId == null ? action.id : PostStore.currentFocusedPostId;
|
||||||
|
PostStore.checkBounds(id, action.numRequested, makePostListNonNull(action.post_list), action.before);
|
||||||
|
PostStore.storePosts(id, makePostListNonNull(action.post_list));
|
||||||
|
PostStore.emitChange();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ActionTypes.RECIEVED_FOCUSED_POST:
|
||||||
|
PostStore.clearChannelVisibility(action.postId, false);
|
||||||
|
PostStore.storeFocusedPost(action.postId, makePostListNonNull(action.post_list));
|
||||||
|
PostStore.emitChange();
|
||||||
break;
|
break;
|
||||||
case ActionTypes.RECIEVED_POST:
|
case ActionTypes.RECIEVED_POST:
|
||||||
PostStore.pStorePost(action.post);
|
PostStore.storePost(action.post);
|
||||||
|
PostStore.emitChange();
|
||||||
|
break;
|
||||||
|
case ActionTypes.RECIEVED_EDIT_POST:
|
||||||
|
PostStore.emitEditPost(action);
|
||||||
|
PostStore.emitChange();
|
||||||
|
break;
|
||||||
|
case ActionTypes.CLICK_CHANNEL:
|
||||||
|
PostStore.clearFocusedPost();
|
||||||
|
PostStore.clearChannelVisibility(action.id, true);
|
||||||
|
PostStore.clearUnseenDeletedPosts(action.id);
|
||||||
|
break;
|
||||||
|
case ActionTypes.CREATE_POST:
|
||||||
|
PostStore.storePendingPost(action.post);
|
||||||
|
PostStore.storeDraft(action.post.channel_id, null);
|
||||||
|
PostStore.jumpPostsViewToBottom();
|
||||||
|
break;
|
||||||
|
case ActionTypes.POST_DELETED:
|
||||||
|
PostStore.storeUnseenDeletedPost(action.post);
|
||||||
|
PostStore.removePost(action.post);
|
||||||
PostStore.emitChange();
|
PostStore.emitChange();
|
||||||
break;
|
break;
|
||||||
case ActionTypes.RECIEVED_POST_SELECTED:
|
case ActionTypes.RECIEVED_POST_SELECTED:
|
||||||
PostStore.storeSelectedPost(action.post_list);
|
PostStore.storeSelectedPost(action.post_list);
|
||||||
PostStore.emitSelectedPostChange(action.from_search);
|
PostStore.emitSelectedPostChange(action.from_search);
|
||||||
break;
|
break;
|
||||||
case ActionTypes.RECIEVED_EDIT_POST:
|
|
||||||
PostStore.emitEditPost(action);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||||
// See License.txt for license information.
|
// See License.txt for license information.
|
||||||
|
|
||||||
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
|
|
||||||
import UserStore from './user_store.jsx';
|
import UserStore from './user_store.jsx';
|
||||||
import PostStore from './post_store.jsx';
|
import PostStore from './post_store.jsx';
|
||||||
import ChannelStore from './channel_store.jsx';
|
import ChannelStore from './channel_store.jsx';
|
||||||
@@ -11,9 +10,9 @@ import EventEmitter from 'events';
|
|||||||
|
|
||||||
import * as Utils from '../utils/utils.jsx';
|
import * as Utils from '../utils/utils.jsx';
|
||||||
import * as AsyncClient from '../utils/async_client.jsx';
|
import * as AsyncClient from '../utils/async_client.jsx';
|
||||||
|
import * as EventHelpers from '../dispatcher/event_helpers.jsx';
|
||||||
|
|
||||||
import Constants from '../utils/constants.jsx';
|
import Constants from '../utils/constants.jsx';
|
||||||
const ActionTypes = Constants.ActionTypes;
|
|
||||||
const SocketEvents = Constants.SocketEvents;
|
const SocketEvents = Constants.SocketEvents;
|
||||||
|
|
||||||
const CHANGE_EVENT = 'change';
|
const CHANGE_EVENT = 'change';
|
||||||
@@ -91,10 +90,9 @@ class SocketStoreClass extends EventEmitter {
|
|||||||
};
|
};
|
||||||
|
|
||||||
conn.onmessage = (evt) => {
|
conn.onmessage = (evt) => {
|
||||||
AppDispatcher.handleServerAction({
|
const msg = JSON.parse(evt.data);
|
||||||
type: ActionTypes.RECIEVED_MSG,
|
this.handleMessage(msg);
|
||||||
msg: JSON.parse(evt.data)
|
this.emitChange(msg);
|
||||||
});
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -153,12 +151,12 @@ class SocketStoreClass extends EventEmitter {
|
|||||||
function handleNewPostEvent(msg) {
|
function handleNewPostEvent(msg) {
|
||||||
// Store post
|
// Store post
|
||||||
const post = JSON.parse(msg.props.post);
|
const post = JSON.parse(msg.props.post);
|
||||||
PostStore.storePost(post);
|
EventHelpers.emitPostRecievedEvent(post);
|
||||||
|
|
||||||
// Update channel state
|
// Update channel state
|
||||||
if (ChannelStore.getCurrentId() === msg.channel_id) {
|
if (ChannelStore.getCurrentId() === msg.channel_id) {
|
||||||
if (window.isActive) {
|
if (window.isActive) {
|
||||||
AsyncClient.updateLastViewedAt(true);
|
AsyncClient.updateLastViewedAt();
|
||||||
}
|
}
|
||||||
} else if (UserStore.getCurrentId() !== msg.user_id || post.type !== Constants.POST_TYPE_JOIN_LEAVE) {
|
} else if (UserStore.getCurrentId() !== msg.user_id || post.type !== Constants.POST_TYPE_JOIN_LEAVE) {
|
||||||
AsyncClient.getChannel(msg.channel_id);
|
AsyncClient.getChannel(msg.channel_id);
|
||||||
@@ -237,20 +235,17 @@ function handlePostEditEvent(msg) {
|
|||||||
|
|
||||||
function handlePostDeleteEvent(msg) {
|
function handlePostDeleteEvent(msg) {
|
||||||
const post = JSON.parse(msg.props.post);
|
const post = JSON.parse(msg.props.post);
|
||||||
|
EventHelpers.emitPostDeletedEvent(post);
|
||||||
PostStore.storeUnseenDeletedPost(post);
|
|
||||||
PostStore.removePost(post, true);
|
|
||||||
PostStore.emitChange();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleNewUserEvent() {
|
function handleNewUserEvent() {
|
||||||
AsyncClient.getProfiles();
|
AsyncClient.getProfiles();
|
||||||
AsyncClient.getChannelExtraInfo(true);
|
AsyncClient.getChannelExtraInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleUserAddedEvent(msg) {
|
function handleUserAddedEvent(msg) {
|
||||||
if (ChannelStore.getCurrentId() === msg.channel_id) {
|
if (ChannelStore.getCurrentId() === msg.channel_id) {
|
||||||
AsyncClient.getChannelExtraInfo(true);
|
AsyncClient.getChannelExtraInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UserStore.getCurrentId() === msg.user_id) {
|
if (UserStore.getCurrentId() === msg.user_id) {
|
||||||
@@ -273,7 +268,7 @@ function handleUserRemovedEvent(msg) {
|
|||||||
$('#removed_from_channel').modal('show');
|
$('#removed_from_channel').modal('show');
|
||||||
}
|
}
|
||||||
} else if (ChannelStore.getCurrentId() === msg.channel_id) {
|
} else if (ChannelStore.getCurrentId() === msg.channel_id) {
|
||||||
AsyncClient.getChannelExtraInfo(true);
|
AsyncClient.getChannelExtraInfo();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,17 +281,12 @@ function handleChannelViewedEvent(msg) {
|
|||||||
|
|
||||||
var SocketStore = new SocketStoreClass();
|
var SocketStore = new SocketStoreClass();
|
||||||
|
|
||||||
SocketStore.dispatchToken = AppDispatcher.register((payload) => {
|
/*SocketStore.dispatchToken = AppDispatcher.register((payload) => {
|
||||||
var action = payload.action;
|
var action = payload.action;
|
||||||
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case ActionTypes.RECIEVED_MSG:
|
|
||||||
SocketStore.handleMessage(action.msg);
|
|
||||||
SocketStore.emitChange(action.msg);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
});
|
});*/
|
||||||
|
|
||||||
export default SocketStore;
|
export default SocketStore;
|
||||||
|
|||||||
@@ -40,88 +40,42 @@ function isCallInProgress(callName) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getChannels(force, updateLastViewed, checkVersion) {
|
export function getChannels(checkVersion) {
|
||||||
var channels = ChannelStore.getAll();
|
if (isCallInProgress('getChannels')) {
|
||||||
|
return;
|
||||||
if (channels.length === 0 || force) {
|
|
||||||
if (isCallInProgress('getChannels')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
callTracker.getChannels = utils.getTimestamp();
|
|
||||||
|
|
||||||
client.getChannels(
|
|
||||||
(data, textStatus, xhr) => {
|
|
||||||
callTracker.getChannels = 0;
|
|
||||||
|
|
||||||
if (checkVersion) {
|
|
||||||
var serverVersion = xhr.getResponseHeader('X-Version-ID');
|
|
||||||
|
|
||||||
if (!BrowserStore.getLastServerVersion()) {
|
|
||||||
BrowserStore.setLastServerVersion(serverVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (serverVersion !== BrowserStore.getLastServerVersion()) {
|
|
||||||
BrowserStore.setLastServerVersion(serverVersion);
|
|
||||||
window.location.reload(true);
|
|
||||||
console.log('Detected version update refreshing the page'); //eslint-disable-line no-console
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (xhr.status === 304 || !data) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
AppDispatcher.handleServerAction({
|
|
||||||
type: ActionTypes.RECIEVED_CHANNELS,
|
|
||||||
channels: data.channels,
|
|
||||||
members: data.members
|
|
||||||
});
|
|
||||||
},
|
|
||||||
(err) => {
|
|
||||||
callTracker.getChannels = 0;
|
|
||||||
dispatchError(err, 'getChannels');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
if (isCallInProgress('getChannelCounts')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
callTracker.getChannelCounts = utils.getTimestamp();
|
|
||||||
|
|
||||||
client.getChannelCounts(
|
|
||||||
function getChannelCountsSuccess(data, textStatus, xhr) {
|
|
||||||
callTracker.getChannelCounts = 0;
|
|
||||||
|
|
||||||
if (xhr.status === 304 || !data) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var countMap = data.counts;
|
|
||||||
var updateAtMap = data.update_times;
|
|
||||||
|
|
||||||
for (var id in countMap) {
|
|
||||||
if ({}.hasOwnProperty.call(countMap, id)) {
|
|
||||||
var c = ChannelStore.get(id);
|
|
||||||
var count = countMap[id];
|
|
||||||
var updateAt = updateAtMap[id];
|
|
||||||
if (!c || c.total_msg_count !== count || updateAt > c.update_at) {
|
|
||||||
getChannel(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
function getChannelCountsFailure(err) {
|
|
||||||
callTracker.getChannelCounts = 0;
|
|
||||||
dispatchError(err, 'getChannelCounts');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updateLastViewed && ChannelStore.getCurrentId() != null) {
|
callTracker.getChannels = utils.getTimestamp();
|
||||||
updateLastViewedAt();
|
|
||||||
}
|
client.getChannels(
|
||||||
|
(data, textStatus, xhr) => {
|
||||||
|
callTracker.getChannels = 0;
|
||||||
|
|
||||||
|
if (checkVersion) {
|
||||||
|
var serverVersion = xhr.getResponseHeader('X-Version-ID');
|
||||||
|
|
||||||
|
if (serverVersion !== BrowserStore.getLastServerVersion()) {
|
||||||
|
BrowserStore.setLastServerVersion(serverVersion);
|
||||||
|
window.location.reload(true);
|
||||||
|
console.log('Detected version update refreshing the page'); //eslint-disable-line no-console
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xhr.status === 304 || !data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppDispatcher.handleServerAction({
|
||||||
|
type: ActionTypes.RECIEVED_CHANNELS,
|
||||||
|
channels: data.channels,
|
||||||
|
members: data.members
|
||||||
|
});
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
callTracker.getChannels = 0;
|
||||||
|
dispatchError(err, 'getChannels');
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getChannel(id) {
|
export function getChannel(id) {
|
||||||
@@ -152,14 +106,14 @@ export function getChannel(id) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateLastViewedAt(force) {
|
export function updateLastViewedAt() {
|
||||||
const channelId = ChannelStore.getCurrentId();
|
const channelId = ChannelStore.getCurrentId();
|
||||||
|
|
||||||
if (channelId === null) {
|
if (channelId === null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isCallInProgress(`updateLastViewed${channelId}`) && !force) {
|
if (isCallInProgress(`updateLastViewed${channelId}`)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,40 +159,35 @@ export function getMoreChannels(force) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getChannelExtraInfo(force) {
|
export function getChannelExtraInfo() {
|
||||||
var channelId = ChannelStore.getCurrentId();
|
const channelId = ChannelStore.getCurrentId();
|
||||||
|
|
||||||
if (channelId != null) {
|
if (channelId != null) {
|
||||||
if (isCallInProgress('getChannelExtraInfo_' + channelId)) {
|
if (isCallInProgress('getChannelExtraInfo_' + channelId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var minMembers = 0;
|
|
||||||
if (ChannelStore.getCurrent() && ChannelStore.getCurrent().type === 'D') {
|
|
||||||
minMembers = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ChannelStore.getCurrentExtraInfo().members.length <= minMembers || force) {
|
callTracker['getChannelExtraInfo_' + channelId] = utils.getTimestamp();
|
||||||
callTracker['getChannelExtraInfo_' + channelId] = utils.getTimestamp();
|
|
||||||
client.getChannelExtraInfo(
|
|
||||||
channelId,
|
|
||||||
function getChannelExtraInfoSuccess(data, textStatus, xhr) {
|
|
||||||
callTracker['getChannelExtraInfo_' + channelId] = 0;
|
|
||||||
|
|
||||||
if (xhr.status === 304 || !data) {
|
client.getChannelExtraInfo(
|
||||||
return;
|
channelId,
|
||||||
}
|
(data, textStatus, xhr) => {
|
||||||
|
callTracker['getChannelExtraInfo_' + channelId] = 0;
|
||||||
|
|
||||||
AppDispatcher.handleServerAction({
|
if (xhr.status === 304 || !data) {
|
||||||
type: ActionTypes.RECIEVED_CHANNEL_EXTRA_INFO,
|
return;
|
||||||
extra_info: data
|
|
||||||
});
|
|
||||||
},
|
|
||||||
function getChannelExtraInfoFailure(err) {
|
|
||||||
callTracker['getChannelExtraInfo_' + channelId] = 0;
|
|
||||||
dispatchError(err, 'getChannelExtraInfo');
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
}
|
AppDispatcher.handleServerAction({
|
||||||
|
type: ActionTypes.RECIEVED_CHANNEL_EXTRA_INFO,
|
||||||
|
extra_info: data
|
||||||
|
});
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
callTracker['getChannelExtraInfo_' + channelId] = 0;
|
||||||
|
dispatchError(err, 'getChannelExtraInfo');
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,89 +406,92 @@ export function search(terms) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPostsPage(force, id, maxPosts) {
|
export function getPostsPage(id, maxPosts) {
|
||||||
if (PostStore.getCurrentPosts() == null || force) {
|
let channelId = id;
|
||||||
var channelId = id;
|
if (channelId == null) {
|
||||||
|
channelId = ChannelStore.getCurrentId();
|
||||||
if (channelId == null) {
|
if (channelId == null) {
|
||||||
channelId = ChannelStore.getCurrentId();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isCallInProgress('getPostsPage_' + channelId)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var postList = PostStore.getCurrentPosts();
|
if (isCallInProgress('getPostsPage_' + channelId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var max = maxPosts;
|
var postList = PostStore.getAllPosts(id);
|
||||||
if (max == null) {
|
|
||||||
max = Constants.POST_CHUNK_SIZE * Constants.MAX_POST_CHUNKS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we already have more than POST_CHUNK_SIZE posts,
|
var max = maxPosts;
|
||||||
// let's get the amount we have but rounded up to next multiple of POST_CHUNK_SIZE,
|
if (max == null) {
|
||||||
// with a max at maxPosts
|
max = Constants.POST_CHUNK_SIZE * Constants.MAX_POST_CHUNKS;
|
||||||
var numPosts = Math.min(max, Constants.POST_CHUNK_SIZE);
|
}
|
||||||
if (postList && postList.order.length > 0) {
|
|
||||||
numPosts = Math.min(max, Constants.POST_CHUNK_SIZE * Math.ceil(postList.order.length / Constants.POST_CHUNK_SIZE));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (channelId != null) {
|
// if we already have more than POST_CHUNK_SIZE posts,
|
||||||
callTracker['getPostsPage_' + channelId] = utils.getTimestamp();
|
// let's get the amount we have but rounded up to next multiple of POST_CHUNK_SIZE,
|
||||||
|
// with a max at maxPosts
|
||||||
|
var numPosts = Math.min(max, Constants.POST_CHUNK_SIZE);
|
||||||
|
if (postList && postList.order.length > 0) {
|
||||||
|
numPosts = Math.min(max, Constants.POST_CHUNK_SIZE * Math.ceil(postList.order.length / Constants.POST_CHUNK_SIZE));
|
||||||
|
}
|
||||||
|
|
||||||
client.getPostsPage(
|
if (channelId != null) {
|
||||||
channelId,
|
callTracker['getPostsPage_' + channelId] = utils.getTimestamp();
|
||||||
0,
|
|
||||||
numPosts,
|
|
||||||
function getPostsPageSuccess(data, textStatus, xhr) {
|
|
||||||
if (xhr.status === 304 || !data) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
AppDispatcher.handleServerAction({
|
client.getPostsPage(
|
||||||
type: ActionTypes.RECIEVED_POSTS,
|
channelId,
|
||||||
id: channelId,
|
0,
|
||||||
post_list: data
|
numPosts,
|
||||||
});
|
(data, textStatus, xhr) => {
|
||||||
|
if (xhr.status === 304 || !data) {
|
||||||
getProfiles();
|
return;
|
||||||
},
|
|
||||||
function getPostsPageFailure(err) {
|
|
||||||
dispatchError(err, 'getPostsPage');
|
|
||||||
},
|
|
||||||
function getPostsPageComplete() {
|
|
||||||
callTracker['getPostsPage_' + channelId] = 0;
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
}
|
AppDispatcher.handleServerAction({
|
||||||
|
type: ActionTypes.RECIEVED_POSTS,
|
||||||
|
id: channelId,
|
||||||
|
before: true,
|
||||||
|
numRequested: numPosts,
|
||||||
|
post_list: data
|
||||||
|
});
|
||||||
|
|
||||||
|
getProfiles();
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
dispatchError(err, 'getPostsPage');
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
callTracker['getPostsPage_' + channelId] = 0;
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPosts(id) {
|
export function getPosts(id) {
|
||||||
var channelId = id;
|
let channelId = id;
|
||||||
if (channelId == null) {
|
if (channelId == null) {
|
||||||
if (ChannelStore.getCurrentId() == null) {
|
channelId = ChannelStore.getCurrentId();
|
||||||
|
if (channelId == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
channelId = ChannelStore.getCurrentId();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isCallInProgress('getPosts_' + channelId)) {
|
if (isCallInProgress('getPosts_' + channelId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PostStore.getCurrentPosts() == null) {
|
if (PostStore.getAllPosts(channelId) == null) {
|
||||||
getPostsPage(true, id, Constants.POST_CHUNK_SIZE);
|
getPostsPage(channelId, Constants.POST_CHUNK_SIZE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var latestUpdate = PostStore.getLatestUpdate(channelId);
|
const latestUpdate = PostStore.getLatestUpdate(channelId);
|
||||||
|
|
||||||
callTracker['getPosts_' + channelId] = utils.getTimestamp();
|
callTracker['getPosts_' + channelId] = utils.getTimestamp();
|
||||||
|
|
||||||
client.getPosts(
|
client.getPosts(
|
||||||
channelId,
|
channelId,
|
||||||
latestUpdate,
|
latestUpdate,
|
||||||
function success(data, textStatus, xhr) {
|
(data, textStatus, xhr) => {
|
||||||
if (xhr.status === 304 || !data) {
|
if (xhr.status === 304 || !data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -547,20 +499,100 @@ export function getPosts(id) {
|
|||||||
AppDispatcher.handleServerAction({
|
AppDispatcher.handleServerAction({
|
||||||
type: ActionTypes.RECIEVED_POSTS,
|
type: ActionTypes.RECIEVED_POSTS,
|
||||||
id: channelId,
|
id: channelId,
|
||||||
|
before: true,
|
||||||
|
numRequested: Constants.POST_CHUNK_SIZE,
|
||||||
post_list: data
|
post_list: data
|
||||||
});
|
});
|
||||||
|
|
||||||
getProfiles();
|
getProfiles();
|
||||||
},
|
},
|
||||||
function fail(err) {
|
(err) => {
|
||||||
dispatchError(err, 'getPosts');
|
dispatchError(err, 'getPosts');
|
||||||
},
|
},
|
||||||
function complete() {
|
() => {
|
||||||
callTracker['getPosts_' + channelId] = 0;
|
callTracker['getPosts_' + channelId] = 0;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getPostsBefore(postId, offset, numPost) {
|
||||||
|
const channelId = ChannelStore.getCurrentId();
|
||||||
|
if (channelId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCallInProgress('getPostsBefore_' + channelId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
client.getPostsBefore(
|
||||||
|
channelId,
|
||||||
|
postId,
|
||||||
|
offset,
|
||||||
|
numPost,
|
||||||
|
(data, textStatus, xhr) => {
|
||||||
|
if (xhr.status === 304 || !data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppDispatcher.handleServerAction({
|
||||||
|
type: ActionTypes.RECIEVED_POSTS,
|
||||||
|
id: channelId,
|
||||||
|
before: true,
|
||||||
|
numRequested: numPost,
|
||||||
|
post_list: data
|
||||||
|
});
|
||||||
|
|
||||||
|
getProfiles();
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
dispatchError(err, 'getPostsBefore');
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
callTracker['getPostsBefore_' + channelId] = 0;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPostsAfter(postId, offset, numPost) {
|
||||||
|
const channelId = ChannelStore.getCurrentId();
|
||||||
|
if (channelId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCallInProgress('getPostsAfter_' + channelId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
client.getPostsAfter(
|
||||||
|
channelId,
|
||||||
|
postId,
|
||||||
|
offset,
|
||||||
|
numPost,
|
||||||
|
(data, textStatus, xhr) => {
|
||||||
|
if (xhr.status === 304 || !data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppDispatcher.handleServerAction({
|
||||||
|
type: ActionTypes.RECIEVED_POSTS,
|
||||||
|
id: channelId,
|
||||||
|
before: false,
|
||||||
|
numRequested: numPost,
|
||||||
|
post_list: data
|
||||||
|
});
|
||||||
|
|
||||||
|
getProfiles();
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
dispatchError(err, 'getPostsAfter');
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
callTracker['getPostsAfter_' + channelId] = 0;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function getMe() {
|
export function getMe() {
|
||||||
if (isCallInProgress('getMe')) {
|
if (isCallInProgress('getMe')) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -820,7 +820,37 @@ export function getPosts(channelId, since, success, error, complete) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPost(channelId, postId, success, error) {
|
export function getPostsBefore(channelId, post, offset, numPost, success, error, complete) {
|
||||||
|
$.ajax({
|
||||||
|
url: '/api/v1/channels/' + channelId + '/post/' + post + '/before/' + offset + '/' + numPost,
|
||||||
|
dataType: 'json',
|
||||||
|
type: 'GET',
|
||||||
|
ifModified: false,
|
||||||
|
success,
|
||||||
|
error: function onError(xhr, status, err) {
|
||||||
|
var e = handleError('getPostsBefore', xhr, status, err);
|
||||||
|
error(e);
|
||||||
|
},
|
||||||
|
complete: complete
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPostsAfter(channelId, post, offset, numPost, success, error, complete) {
|
||||||
|
$.ajax({
|
||||||
|
url: '/api/v1/channels/' + channelId + '/post/' + post + '/after/' + offset + '/' + numPost,
|
||||||
|
dataType: 'json',
|
||||||
|
type: 'GET',
|
||||||
|
ifModified: false,
|
||||||
|
success,
|
||||||
|
error: function onError(xhr, status, err) {
|
||||||
|
var e = handleError('getPostsAfter', xhr, status, err);
|
||||||
|
error(e);
|
||||||
|
},
|
||||||
|
complete: complete
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPost(channelId, postId, success, error, complete) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
cache: false,
|
cache: false,
|
||||||
url: '/api/v1/channels/' + channelId + '/post/' + postId,
|
url: '/api/v1/channels/' + channelId + '/post/' + postId,
|
||||||
@@ -831,7 +861,24 @@ export function getPost(channelId, postId, success, error) {
|
|||||||
error: function onError(xhr, status, err) {
|
error: function onError(xhr, status, err) {
|
||||||
var e = handleError('getPost', xhr, status, err);
|
var e = handleError('getPost', xhr, status, err);
|
||||||
error(e);
|
error(e);
|
||||||
}
|
},
|
||||||
|
complete
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPostById(postId, success, error, complete) {
|
||||||
|
$.ajax({
|
||||||
|
cache: false,
|
||||||
|
url: '/api/v1/posts/' + postId,
|
||||||
|
dataType: 'json',
|
||||||
|
type: 'GET',
|
||||||
|
ifModified: false,
|
||||||
|
success,
|
||||||
|
error: function onError(xhr, status, err) {
|
||||||
|
var e = handleError('getPostById', xhr, status, err);
|
||||||
|
error(e);
|
||||||
|
},
|
||||||
|
complete
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,12 +10,17 @@ export default {
|
|||||||
CLICK_CHANNEL: null,
|
CLICK_CHANNEL: null,
|
||||||
CREATE_CHANNEL: null,
|
CREATE_CHANNEL: null,
|
||||||
LEAVE_CHANNEL: null,
|
LEAVE_CHANNEL: null,
|
||||||
|
CREATE_POST: null,
|
||||||
|
POST_DELETED: null,
|
||||||
|
|
||||||
RECIEVED_CHANNELS: null,
|
RECIEVED_CHANNELS: null,
|
||||||
RECIEVED_CHANNEL: null,
|
RECIEVED_CHANNEL: null,
|
||||||
RECIEVED_MORE_CHANNELS: null,
|
RECIEVED_MORE_CHANNELS: null,
|
||||||
RECIEVED_CHANNEL_EXTRA_INFO: null,
|
RECIEVED_CHANNEL_EXTRA_INFO: null,
|
||||||
|
|
||||||
|
FOCUS_POST: null,
|
||||||
RECIEVED_POSTS: null,
|
RECIEVED_POSTS: null,
|
||||||
|
RECIEVED_FOCUSED_POST: null,
|
||||||
RECIEVED_POST: null,
|
RECIEVED_POST: null,
|
||||||
RECIEVED_EDIT_POST: null,
|
RECIEVED_EDIT_POST: null,
|
||||||
RECIEVED_SEARCH: null,
|
RECIEVED_SEARCH: null,
|
||||||
@@ -99,6 +104,7 @@ export default {
|
|||||||
EMAIL_SERVICE: 'email',
|
EMAIL_SERVICE: 'email',
|
||||||
POST_CHUNK_SIZE: 60,
|
POST_CHUNK_SIZE: 60,
|
||||||
MAX_POST_CHUNKS: 3,
|
MAX_POST_CHUNKS: 3,
|
||||||
|
POST_FOCUS_CONTEXT_RADIUS: 10,
|
||||||
POST_LOADING: 'loading',
|
POST_LOADING: 'loading',
|
||||||
POST_FAILED: 'failed',
|
POST_FAILED: 'failed',
|
||||||
POST_DELETED: 'deleted',
|
POST_DELETED: 'deleted',
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// See License.txt for license information.
|
// See License.txt for license information.
|
||||||
|
|
||||||
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
|
import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
|
||||||
|
import * as EventHelpers from '../dispatcher/event_helpers.jsx';
|
||||||
import ChannelStore from '../stores/channel_store.jsx';
|
import ChannelStore from '../stores/channel_store.jsx';
|
||||||
import UserStore from '../stores/user_store.jsx';
|
import UserStore from '../stores/user_store.jsx';
|
||||||
import PreferenceStore from '../stores/preference_store.jsx';
|
import PreferenceStore from '../stores/preference_store.jsx';
|
||||||
@@ -839,23 +840,15 @@ export function isValidUsername(name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function updateAddressBar(channelName) {
|
export function updateAddressBar(channelName) {
|
||||||
var teamURL = window.location.href.split('/channels')[0];
|
const teamURL = TeamStore.getCurrentTeamUrl();
|
||||||
history.replaceState('data', '', teamURL + '/channels/' + channelName);
|
history.replaceState('data', '', teamURL + '/channels/' + channelName);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function switchChannel(channel) {
|
export function switchChannel(channel) {
|
||||||
AppDispatcher.handleViewAction({
|
EventHelpers.emitChannelClickEvent(channel);
|
||||||
type: ActionTypes.CLICK_CHANNEL,
|
|
||||||
name: channel.name,
|
|
||||||
id: channel.id
|
|
||||||
});
|
|
||||||
|
|
||||||
updateAddressBar(channel.name);
|
updateAddressBar(channel.name);
|
||||||
|
|
||||||
AsyncClient.getChannels(true, true, true);
|
|
||||||
AsyncClient.getChannelExtraInfo(true);
|
|
||||||
AsyncClient.getPosts(channel.id);
|
|
||||||
|
|
||||||
$('.inner__wrap').removeClass('move--right');
|
$('.inner__wrap').removeClass('move--right');
|
||||||
$('.sidebar--left').removeClass('move--right');
|
$('.sidebar--left').removeClass('move--right');
|
||||||
|
|
||||||
|
|||||||
@@ -361,6 +361,19 @@ body.ios {
|
|||||||
.dropdown, .comment-icon__container {
|
.dropdown, .comment-icon__container {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
.permalink-icon {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.post--highlight {
|
||||||
|
background-color: beige;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
@@ -618,6 +631,12 @@ body.ios {
|
|||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.permalink-icon {
|
||||||
|
display: inline-block;
|
||||||
|
color: $primary-color;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.comment-icon__container {
|
.comment-icon__container {
|
||||||
fill: $primary-color;
|
fill: $primary-color;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -757,3 +776,11 @@ body.ios {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.permalink-text {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.permalink-popover {
|
||||||
|
min-width: 320px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
<div id="removed_from_channel_modal"></div>
|
<div id="removed_from_channel_modal"></div>
|
||||||
<div id="register_app_modal"></div>
|
<div id="register_app_modal"></div>
|
||||||
<script>
|
<script>
|
||||||
window.setup_channel_page({{ .Props }});
|
window.setup_channel_page({{ .Props }}, {{ .Team }}, {{ .Channel }}, {{ .User }});
|
||||||
$('body').tooltip( {selector: '[data-toggle=tooltip]'} );
|
$('body').tooltip( {selector: '[data-toggle=tooltip]'} );
|
||||||
if($(window).height() > 1200){
|
if($(window).height() > 1200){
|
||||||
$('.modal-body').css('max-height', 1000);
|
$('.modal-body').css('max-height', 1000);
|
||||||
|
|||||||
@@ -47,6 +47,7 @@
|
|||||||
window.mm_config = {{ .ClientCfg }};
|
window.mm_config = {{ .ClientCfg }};
|
||||||
window.mm_team = {{ .Team }};
|
window.mm_team = {{ .Team }};
|
||||||
window.mm_user = {{ .User }};
|
window.mm_user = {{ .User }};
|
||||||
|
window.mm_channel = {{ .Channel }};
|
||||||
|
|
||||||
if ({{.SessionTokenIndex}} >= 0) {
|
if ({{.SessionTokenIndex}} >= 0) {
|
||||||
window.mm_session_token_index = {{.SessionTokenIndex}};
|
window.mm_session_token_index = {{.SessionTokenIndex}};
|
||||||
|
|||||||
191
web/web.go
191
web/web.go
@@ -91,6 +91,7 @@ func InitWeb() {
|
|||||||
mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/login", api.AppHandler(login)).Methods("GET")
|
mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/login", api.AppHandler(login)).Methods("GET")
|
||||||
mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/logout", api.AppHandler(logout)).Methods("GET")
|
mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/logout", api.AppHandler(logout)).Methods("GET")
|
||||||
mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/reset_password", api.AppHandler(resetPassword)).Methods("GET")
|
mainrouter.Handle("/{team:[A-Za-z0-9-]+(__)?[A-Za-z0-9-]+}/reset_password", api.AppHandler(resetPassword)).Methods("GET")
|
||||||
|
mainrouter.Handle("/{team}/pl/{postid}", api.AppHandler(postPermalink)).Methods("GET") // Bug in gorilla.mux prevents us from using regex here.
|
||||||
mainrouter.Handle("/{team}/login/{service}", api.AppHandler(loginWithOAuth)).Methods("GET") // Bug in gorilla.mux prevents us from using regex here.
|
mainrouter.Handle("/{team}/login/{service}", api.AppHandler(loginWithOAuth)).Methods("GET") // Bug in gorilla.mux prevents us from using regex here.
|
||||||
mainrouter.Handle("/{team}/channels/{channelname}", api.AppHandler(getChannel)).Methods("GET") // Bug in gorilla.mux prevents us from using regex here.
|
mainrouter.Handle("/{team}/channels/{channelname}", api.AppHandler(getChannel)).Methods("GET") // Bug in gorilla.mux prevents us from using regex here.
|
||||||
mainrouter.Handle("/{team}/signup/{service}", api.AppHandler(signupWithOAuth)).Methods("GET") // Bug in gorilla.mux prevents us from using regex here.
|
mainrouter.Handle("/{team}/signup/{service}", api.AppHandler(signupWithOAuth)).Methods("GET") // Bug in gorilla.mux prevents us from using regex here.
|
||||||
@@ -342,15 +343,142 @@ func logout(c *api.Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Redirect(w, r, c.GetTeamURL(), http.StatusTemporaryRedirect)
|
http.Redirect(w, r, c.GetTeamURL(), http.StatusTemporaryRedirect)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func postPermalink(c *api.Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
params := mux.Vars(r)
|
||||||
|
teamName := params["team"]
|
||||||
|
postId := params["postid"]
|
||||||
|
|
||||||
|
if len(postId) != 26 {
|
||||||
|
c.Err = model.NewAppError("postPermalink", "Invalid Post ID", "id="+postId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
team := checkSessionSwitch(c, w, r, teamName)
|
||||||
|
if team == nil {
|
||||||
|
// Error already set by getTeam
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var post *model.Post
|
||||||
|
if result := <-api.Srv.Store.Post().Get(postId); result.Err != nil {
|
||||||
|
c.Err = result.Err
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
postlist := result.Data.(*model.PostList)
|
||||||
|
post = postlist.Posts[postlist.Order[0]]
|
||||||
|
}
|
||||||
|
|
||||||
|
var channel *model.Channel
|
||||||
|
if result := <-api.Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, post.ChannelId, c.Session.UserId); result.Err != nil {
|
||||||
|
c.Err = result.Err
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
if result.Data.(int64) == 0 {
|
||||||
|
if channel = autoJoinChannelId(c, w, r, post.ChannelId); channel == nil {
|
||||||
|
http.Redirect(w, r, c.GetTeamURL()+"/channels/town-square", http.StatusFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if result := <-api.Srv.Store.Channel().Get(post.ChannelId); result.Err != nil {
|
||||||
|
c.Err = result.Err
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
channel = result.Data.(*model.Channel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doLoadChannel(c, w, r, team, channel, post.Id)
|
||||||
|
}
|
||||||
|
|
||||||
func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) {
|
func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) {
|
||||||
params := mux.Vars(r)
|
params := mux.Vars(r)
|
||||||
name := params["channelname"]
|
name := params["channelname"]
|
||||||
teamName := params["team"]
|
teamName := params["team"]
|
||||||
|
|
||||||
|
team := checkSessionSwitch(c, w, r, teamName)
|
||||||
|
if team == nil {
|
||||||
|
// Error already set by getTeam
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var channel *model.Channel
|
||||||
|
if result := <-api.Srv.Store.Channel().CheckPermissionsToByName(c.Session.TeamId, name, c.Session.UserId); result.Err != nil {
|
||||||
|
c.Err = result.Err
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
channelId := result.Data.(string)
|
||||||
|
if len(channelId) == 0 {
|
||||||
|
if channel = autoJoinChannelName(c, w, r, name); channel == nil {
|
||||||
|
http.Redirect(w, r, c.GetTeamURL()+"/channels/town-square", http.StatusFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if result := <-api.Srv.Store.Channel().Get(channelId); result.Err != nil {
|
||||||
|
c.Err = result.Err
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
channel = result.Data.(*model.Channel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doLoadChannel(c, w, r, team, channel, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func autoJoinChannelName(c *api.Context, w http.ResponseWriter, r *http.Request, channelName string) *model.Channel {
|
||||||
|
if strings.Index(channelName, "__") > 0 {
|
||||||
|
// It's a direct message channel that doesn't exist yet so let's create it
|
||||||
|
ids := strings.Split(channelName, "__")
|
||||||
|
otherUserId := ""
|
||||||
|
if ids[0] == c.Session.UserId {
|
||||||
|
otherUserId = ids[1]
|
||||||
|
} else {
|
||||||
|
otherUserId = ids[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
if sc, err := api.CreateDirectChannel(c, otherUserId); err != nil {
|
||||||
|
api.Handle404(w, r)
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return sc
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We will attempt to auto-join open channels
|
||||||
|
return joinOpenChannel(c, w, r, api.Srv.Store.Channel().GetByName(c.Session.TeamId, channelName))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func autoJoinChannelId(c *api.Context, w http.ResponseWriter, r *http.Request, channelId string) *model.Channel {
|
||||||
|
return joinOpenChannel(c, w, r, api.Srv.Store.Channel().Get(channelId))
|
||||||
|
}
|
||||||
|
|
||||||
|
func joinOpenChannel(c *api.Context, w http.ResponseWriter, r *http.Request, channel store.StoreChannel) *model.Channel {
|
||||||
|
if cr := <-channel; cr.Err != nil {
|
||||||
|
http.Redirect(w, r, c.GetTeamURL()+"/channels/town-square", http.StatusFound)
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
channel := cr.Data.(*model.Channel)
|
||||||
|
if channel.Type == model.CHANNEL_OPEN {
|
||||||
|
api.JoinChannel(c, channel.Id, "")
|
||||||
|
if c.Err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
http.Redirect(w, r, c.GetTeamURL()+"/channels/town-square", http.StatusFound)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return channel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkSessionSwitch(c *api.Context, w http.ResponseWriter, r *http.Request, teamName string) *model.Team {
|
||||||
var team *model.Team
|
var team *model.Team
|
||||||
if result := <-api.Srv.Store.Team().GetByName(teamName); result.Err != nil {
|
if result := <-api.Srv.Store.Team().GetByName(teamName); result.Err != nil {
|
||||||
c.Err = result.Err
|
c.Err = result.Err
|
||||||
return
|
return nil
|
||||||
} else {
|
} else {
|
||||||
team = result.Data.(*model.Team)
|
team = result.Data.(*model.Team)
|
||||||
}
|
}
|
||||||
@@ -368,15 +496,11 @@ func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
userChan := api.Srv.Store.User().Get(c.Session.UserId)
|
return team
|
||||||
|
}
|
||||||
|
|
||||||
var channelId string
|
func doLoadChannel(c *api.Context, w http.ResponseWriter, r *http.Request, team *model.Team, channel *model.Channel, postid string) {
|
||||||
if result := <-api.Srv.Store.Channel().CheckPermissionsToByName(c.Session.TeamId, name, c.Session.UserId); result.Err != nil {
|
userChan := api.Srv.Store.User().Get(c.Session.UserId)
|
||||||
c.Err = result.Err
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
channelId = result.Data.(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
var user *model.User
|
var user *model.User
|
||||||
if ur := <-userChan; ur.Err != nil {
|
if ur := <-userChan; ur.Err != nil {
|
||||||
@@ -388,54 +512,15 @@ func getChannel(c *api.Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
user = ur.Data.(*model.User)
|
user = ur.Data.(*model.User)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(channelId) == 0 {
|
|
||||||
if strings.Index(name, "__") > 0 {
|
|
||||||
// It's a direct message channel that doesn't exist yet so let's create it
|
|
||||||
ids := strings.Split(name, "__")
|
|
||||||
otherUserId := ""
|
|
||||||
if ids[0] == c.Session.UserId {
|
|
||||||
otherUserId = ids[1]
|
|
||||||
} else {
|
|
||||||
otherUserId = ids[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
if sc, err := api.CreateDirectChannel(c, otherUserId); err != nil {
|
|
||||||
api.Handle404(w, r)
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
channelId = sc.Id
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// We will attempt to auto-join open channels
|
|
||||||
if cr := <-api.Srv.Store.Channel().GetByName(c.Session.TeamId, name); cr.Err != nil {
|
|
||||||
http.Redirect(w, r, c.GetTeamURL()+"/channels/town-square", http.StatusFound)
|
|
||||||
} else {
|
|
||||||
channel := cr.Data.(*model.Channel)
|
|
||||||
if channel.Type == model.CHANNEL_OPEN {
|
|
||||||
api.JoinChannel(c, channel.Id, "")
|
|
||||||
if c.Err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
channelId = channel.Id
|
|
||||||
} else {
|
|
||||||
http.Redirect(w, r, c.GetTeamURL()+"/channels/town-square", http.StatusFound)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
page := NewHtmlTemplatePage("channel", "")
|
page := NewHtmlTemplatePage("channel", "")
|
||||||
page.Props["Title"] = name + " - " + team.DisplayName + " " + page.ClientCfg["SiteName"]
|
page.Props["Title"] = channel.DisplayName + " - " + team.DisplayName + " " + page.ClientCfg["SiteName"]
|
||||||
page.Props["TeamDisplayName"] = team.DisplayName
|
page.Props["TeamDisplayName"] = team.DisplayName
|
||||||
page.Props["TeamName"] = team.Name
|
page.Props["ChannelName"] = channel.Name
|
||||||
page.Props["TeamType"] = team.Type
|
page.Props["ChannelId"] = channel.Id
|
||||||
page.Props["TeamId"] = team.Id
|
page.Props["PostId"] = postid
|
||||||
page.Props["ChannelName"] = name
|
|
||||||
page.Props["ChannelId"] = channelId
|
|
||||||
page.Props["UserId"] = c.Session.UserId
|
|
||||||
page.Team = team
|
page.Team = team
|
||||||
page.User = user
|
page.User = user
|
||||||
|
page.Channel = channel
|
||||||
page.Render(c, w)
|
page.Render(c, w)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user