Refactoring center panel away. Moving tutorial to a route. Fixing a
bunch of bugs.
Этот коммит содержится в:
@@ -13,23 +13,56 @@ import * as Utils from 'utils/utils.jsx';
|
|||||||
import * as Websockets from './websocket_actions.jsx';
|
import * as Websockets from './websocket_actions.jsx';
|
||||||
import * as I18n from 'i18n/i18n.jsx';
|
import * as I18n from 'i18n/i18n.jsx';
|
||||||
|
|
||||||
|
import {browserHistory} from 'react-router';
|
||||||
|
|
||||||
import en from 'i18n/en.json';
|
import en from 'i18n/en.json';
|
||||||
|
|
||||||
export function emitChannelClickEvent(channel) {
|
export function emitChannelClickEvent(channel) {
|
||||||
AsyncClient.getChannels(true);
|
function userVisitedFakeChannel(chan, success, fail) {
|
||||||
AsyncClient.getChannelExtraInfo(channel.id);
|
const otherUserId = Utils.getUserIdFromChannelName(chan);
|
||||||
AsyncClient.updateLastViewedAt(channel.id);
|
Client.createDirectChannel(
|
||||||
AsyncClient.getPosts(channel.id);
|
chan,
|
||||||
|
otherUserId,
|
||||||
|
(data) => {
|
||||||
|
success(data);
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
function switchToChannel(chan) {
|
||||||
|
AsyncClient.getChannels(true);
|
||||||
|
AsyncClient.getChannelExtraInfo(chan.id);
|
||||||
|
AsyncClient.updateLastViewedAt(chan.id);
|
||||||
|
AsyncClient.getPosts(chan.id);
|
||||||
|
Client.trackPage();
|
||||||
|
|
||||||
AppDispatcher.handleViewAction({
|
AppDispatcher.handleViewAction({
|
||||||
type: ActionTypes.CLICK_CHANNEL,
|
type: ActionTypes.CLICK_CHANNEL,
|
||||||
name: channel.name,
|
name: chan.name,
|
||||||
id: channel.id,
|
id: chan.id,
|
||||||
prev: ChannelStore.getCurrentId()
|
prev: ChannelStore.getCurrentId()
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (channel.fake) {
|
||||||
|
userVisitedFakeChannel(
|
||||||
|
channel,
|
||||||
|
(data) => {
|
||||||
|
switchToChannel(data);
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
browserHistory.push('/' + this.state.currentTeam.name);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
switchToChannel(channel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function emitPostFocusEvent(postId) {
|
export function emitPostFocusEvent(postId) {
|
||||||
|
AsyncClient.getChannels(true);
|
||||||
Client.getPostById(
|
Client.getPostById(
|
||||||
postId,
|
postId,
|
||||||
(data) => {
|
(data) => {
|
||||||
@@ -39,6 +72,8 @@ export function emitPostFocusEvent(postId) {
|
|||||||
post_list: data
|
post_list: data
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AsyncClient.getChannelExtraInfo(data.channel_id);
|
||||||
|
|
||||||
AsyncClient.getPostsBefore(postId, 0, Constants.POST_FOCUS_CONTEXT_RADIUS);
|
AsyncClient.getPostsBefore(postId, 0, Constants.POST_FOCUS_CONTEXT_RADIUS);
|
||||||
AsyncClient.getPostsAfter(postId, 0, Constants.POST_FOCUS_CONTEXT_RADIUS);
|
AsyncClient.getPostsAfter(postId, 0, Constants.POST_FOCUS_CONTEXT_RADIUS);
|
||||||
}
|
}
|
||||||
@@ -300,3 +335,4 @@ export function emitRemoteUserTypingEvent(channelId, userId, postParentId) {
|
|||||||
postParentId
|
postParentId
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,145 +0,0 @@
|
|||||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See License.txt for license information.
|
|
||||||
|
|
||||||
import TutorialIntroScreens from './tutorial/tutorial_intro_screens.jsx';
|
|
||||||
import CreatePost from './create_post.jsx';
|
|
||||||
import PostsViewContainer from './posts_view_container.jsx';
|
|
||||||
import PostFocusView from './post_focus_view.jsx';
|
|
||||||
import ChannelHeader from './channel_header.jsx';
|
|
||||||
import Navbar from './navbar.jsx';
|
|
||||||
import FileUploadOverlay from './file_upload_overlay.jsx';
|
|
||||||
|
|
||||||
import PreferenceStore from 'stores/preference_store.jsx';
|
|
||||||
import ChannelStore from 'stores/channel_store.jsx';
|
|
||||||
import UserStore from 'stores/user_store.jsx';
|
|
||||||
|
|
||||||
import * as Utils from 'utils/utils.jsx';
|
|
||||||
|
|
||||||
import {FormattedMessage} from 'react-intl';
|
|
||||||
|
|
||||||
import Constants from 'utils/constants.jsx';
|
|
||||||
const TutorialSteps = Constants.TutorialSteps;
|
|
||||||
const Preferences = Constants.Preferences;
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import {Link} from 'react-router';
|
|
||||||
|
|
||||||
export default class CenterPanel extends React.Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.getStateFromStores = this.getStateFromStores.bind(this);
|
|
||||||
this.validState = this.validState.bind(this);
|
|
||||||
this.onStoresChange = this.onStoresChange.bind(this);
|
|
||||||
|
|
||||||
this.state = this.getStateFromStores();
|
|
||||||
}
|
|
||||||
getStateFromStores() {
|
|
||||||
const tutorialStep = PreferenceStore.getInt(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), 999);
|
|
||||||
return {
|
|
||||||
showTutorialScreens: tutorialStep <= TutorialSteps.INTRO_SCREENS,
|
|
||||||
showPostFocus: ChannelStore.getPostMode() === ChannelStore.POST_MODE_FOCUS,
|
|
||||||
user: UserStore.getCurrentUser(),
|
|
||||||
channel: ChannelStore.getCurrent(),
|
|
||||||
profiles: JSON.parse(JSON.stringify(UserStore.getProfiles()))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
validState() {
|
|
||||||
return this.state.user && this.state.channel && this.state.profiles;
|
|
||||||
}
|
|
||||||
onStoresChange() {
|
|
||||||
this.setState(this.getStateFromStores());
|
|
||||||
}
|
|
||||||
componentDidMount() {
|
|
||||||
PreferenceStore.addChangeListener(this.onStoresChange);
|
|
||||||
ChannelStore.addChangeListener(this.onStoresChange);
|
|
||||||
UserStore.addChangeListener(this.onStoresChange);
|
|
||||||
}
|
|
||||||
componentWillUnmount() {
|
|
||||||
PreferenceStore.removeChangeListener(this.onStoresChange);
|
|
||||||
ChannelStore.removeChangeListener(this.onStoresChange);
|
|
||||||
UserStore.removeChangeListener(this.onStoresChange);
|
|
||||||
}
|
|
||||||
render() {
|
|
||||||
if (!this.validState()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const channel = this.state.channel;
|
|
||||||
var handleClick = null;
|
|
||||||
let postsContainer;
|
|
||||||
let createPost;
|
|
||||||
if (this.state.showTutorialScreens) {
|
|
||||||
postsContainer = <TutorialIntroScreens/>;
|
|
||||||
createPost = null;
|
|
||||||
} else if (this.state.showPostFocus) {
|
|
||||||
postsContainer = <PostFocusView profiles={this.state.profiles}/>;
|
|
||||||
|
|
||||||
handleClick = function clickHandler(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
Utils.switchChannel(channel);
|
|
||||||
};
|
|
||||||
|
|
||||||
createPost = (
|
|
||||||
<div
|
|
||||||
id='archive-link-home'
|
|
||||||
onClick={handleClick}
|
|
||||||
>
|
|
||||||
<Link to=''>
|
|
||||||
<FormattedMessage
|
|
||||||
id='center_panel.recent'
|
|
||||||
defaultMessage='Click here to jump to recent messages. '
|
|
||||||
/>
|
|
||||||
<i className='fa fa-arrow-down'></i>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
postsContainer = <PostsViewContainer profiles={this.state.profiles}/>;
|
|
||||||
createPost = (
|
|
||||||
<div
|
|
||||||
className='post-create__container'
|
|
||||||
id='post-create'
|
|
||||||
>
|
|
||||||
<CreatePost/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='inner-wrap channel__wrap'>
|
|
||||||
<div className='row header'>
|
|
||||||
<div id='navbar'>
|
|
||||||
<Navbar/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='row main'>
|
|
||||||
<FileUploadOverlay
|
|
||||||
id='file_upload_overlay'
|
|
||||||
overlayType='center'
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
id='app-content'
|
|
||||||
className='app__content'
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
id='channel-header'
|
|
||||||
className='channel-header'
|
|
||||||
>
|
|
||||||
<ChannelHeader
|
|
||||||
user={this.state.user}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{postsContainer}
|
|
||||||
{createPost}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CenterPanel.defaultProps = {
|
|
||||||
};
|
|
||||||
|
|
||||||
CenterPanel.propTypes = {
|
|
||||||
};
|
|
||||||
@@ -26,6 +26,7 @@ import * as Utils from 'utils/utils.jsx';
|
|||||||
import * as TextFormatting from 'utils/text_formatting.jsx';
|
import * as TextFormatting from 'utils/text_formatting.jsx';
|
||||||
import * as AsyncClient from 'utils/async_client.jsx';
|
import * as AsyncClient from 'utils/async_client.jsx';
|
||||||
import * as Client from 'utils/client.jsx';
|
import * as Client from 'utils/client.jsx';
|
||||||
|
import * as GlobalActions from 'action_creators/global_actions.jsx';
|
||||||
import Constants from 'utils/constants.jsx';
|
import Constants from 'utils/constants.jsx';
|
||||||
|
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {FormattedMessage} from 'react-intl';
|
||||||
@@ -53,11 +54,11 @@ export default class ChannelHeader extends React.Component {
|
|||||||
this.state = state;
|
this.state = state;
|
||||||
}
|
}
|
||||||
getStateFromStores() {
|
getStateFromStores() {
|
||||||
const extraInfo = ChannelStore.getCurrentExtraInfo();
|
const extraInfo = ChannelStore.getExtraInfo(this.props.channelId);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
channel: ChannelStore.getCurrent(),
|
channel: ChannelStore.get(this.props.channelId),
|
||||||
memberChannel: ChannelStore.getCurrentMember(),
|
memberChannel: ChannelStore.getMember(this.props.channelId),
|
||||||
users: extraInfo.members,
|
users: extraInfo.members,
|
||||||
userCount: extraInfo.member_count,
|
userCount: extraInfo.member_count,
|
||||||
searchVisible: SearchStore.getSearchResults() !== null,
|
searchVisible: SearchStore.getSearchResults() !== null,
|
||||||
@@ -105,7 +106,7 @@ export default class ChannelHeader extends React.Component {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const townsquare = ChannelStore.getByName('town-square');
|
const townsquare = ChannelStore.getByName('town-square');
|
||||||
Utils.switchChannel(townsquare);
|
GlobalActions.emitChannelClickEvent(townsquare);
|
||||||
},
|
},
|
||||||
(err) => {
|
(err) => {
|
||||||
AsyncClient.dispatchError(err, 'handleLeave');
|
AsyncClient.dispatchError(err, 'handleLeave');
|
||||||
@@ -433,7 +434,10 @@ export default class ChannelHeader extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div
|
||||||
|
id='channel-header'
|
||||||
|
className='channel-header'
|
||||||
|
>
|
||||||
<table className='channel-header alt'>
|
<table className='channel-header alt'>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -518,4 +522,5 @@ export default class ChannelHeader extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ChannelHeader.propTypes = {
|
ChannelHeader.propTypes = {
|
||||||
|
channelId: React.PropTypes.string.isRequired
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,14 +1,73 @@
|
|||||||
// 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 CenterPanel from 'components/center_panel.jsx';
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import ChannelHeader from 'components/channel_header.jsx';
|
||||||
|
import PostsViewContainer from 'components/posts_view_container.jsx';
|
||||||
|
import CreatePost from 'components/create_post.jsx';
|
||||||
|
|
||||||
|
import ChannelStore from 'stores/channel_store.jsx';
|
||||||
|
import UserStore from 'stores/user_store.jsx';
|
||||||
|
|
||||||
export default class ChannelView extends React.Component {
|
export default class ChannelView extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.getStateFromStores = this.getStateFromStores.bind(this);
|
||||||
|
this.isStateValid = this.isStateValid.bind(this);
|
||||||
|
this.updateState = this.updateState.bind(this);
|
||||||
|
|
||||||
|
this.state = this.getStateFromStores(props);
|
||||||
|
}
|
||||||
|
getStateFromStores(props) {
|
||||||
|
const channel = ChannelStore.getByName(props.params.channel);
|
||||||
|
const channelId = channel ? channel.id : '';
|
||||||
|
const profiles = JSON.parse(JSON.stringify(UserStore.getProfiles()));
|
||||||
|
return {
|
||||||
|
channelId,
|
||||||
|
profiles
|
||||||
|
};
|
||||||
|
}
|
||||||
|
isStateValid() {
|
||||||
|
return this.state.channelId !== '' && this.state.profiles;
|
||||||
|
}
|
||||||
|
updateState() {
|
||||||
|
this.setState(this.getStateFromStores(this.props));
|
||||||
|
}
|
||||||
|
componentDidMount() {
|
||||||
|
ChannelStore.addChangeListener(this.updateState);
|
||||||
|
}
|
||||||
|
componentWillUnmount() {
|
||||||
|
ChannelStore.removeChangeListener(this.updateState);
|
||||||
|
}
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
this.setState(this.getStateFromStores(nextProps));
|
||||||
|
}
|
||||||
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
|
if (nextState.channelId !== this.state.channelId) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<CenterPanel/>
|
<div
|
||||||
|
id='app-content'
|
||||||
|
className='app__content'
|
||||||
|
>
|
||||||
|
<ChannelHeader
|
||||||
|
channelId={this.state.channelId}
|
||||||
|
/>
|
||||||
|
<PostsViewContainer profiles={this.state.profiles}/>
|
||||||
|
<div
|
||||||
|
className='post-create__container'
|
||||||
|
id='post-create'
|
||||||
|
>
|
||||||
|
<CreatePost/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,5 +75,5 @@ ChannelView.defaultProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
ChannelView.propTypes = {
|
ChannelView.propTypes = {
|
||||||
params: React.PropTypes.object
|
params: React.PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import BrowserStore from 'stores/browser_store.jsx';
|
|||||||
import PreferenceStore from 'stores/preference_store.jsx';
|
import PreferenceStore from 'stores/preference_store.jsx';
|
||||||
import * as Utils from 'utils/utils.jsx';
|
import * as Utils from 'utils/utils.jsx';
|
||||||
import Constants from 'utils/constants.jsx';
|
import Constants from 'utils/constants.jsx';
|
||||||
|
const TutorialSteps = Constants.TutorialSteps;
|
||||||
|
const Preferences = Constants.Preferences;
|
||||||
import ErrorBar from 'components/error_bar.jsx';
|
import ErrorBar from 'components/error_bar.jsx';
|
||||||
import * as Websockets from 'action_creators/websocket_actions.jsx';
|
import * as Websockets from 'action_creators/websocket_actions.jsx';
|
||||||
|
|
||||||
@@ -17,6 +19,7 @@ import {browserHistory} from 'react-router';
|
|||||||
|
|
||||||
import SidebarRight from 'components/sidebar_right.jsx';
|
import SidebarRight from 'components/sidebar_right.jsx';
|
||||||
import SidebarRightMenu from 'components/sidebar_right_menu.jsx';
|
import SidebarRightMenu from 'components/sidebar_right_menu.jsx';
|
||||||
|
import Navbar from 'components/navbar.jsx';
|
||||||
|
|
||||||
// Modals
|
// Modals
|
||||||
import GetPostLinkModal from 'components/get_post_link_modal.jsx';
|
import GetPostLinkModal from 'components/get_post_link_modal.jsx';
|
||||||
@@ -66,6 +69,12 @@ export default class LoggedIn extends React.Component {
|
|||||||
Utils.applyTheme(Constants.THEMES.default);
|
Utils.applyTheme(Constants.THEMES.default);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Go to tutorial if we are first arrivign
|
||||||
|
const tutorialStep = PreferenceStore.getInt(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), 999);
|
||||||
|
if (tutorialStep <= TutorialSteps.INTRO_SCREENS) {
|
||||||
|
browserHistory.push(Utils.getTeamURLFromAddressBar() + '/tutorial');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
// Emit view action
|
// Emit view action
|
||||||
@@ -186,14 +195,36 @@ export default class LoggedIn extends React.Component {
|
|||||||
$(window).off('keydown.preventBackspace');
|
$(window).off('keydown.preventBackspace');
|
||||||
}
|
}
|
||||||
render() {
|
render() {
|
||||||
|
let content = [];
|
||||||
|
if (this.props.children) {
|
||||||
|
content = this.props.children;
|
||||||
|
} else {
|
||||||
|
content.push(
|
||||||
|
this.props.sidebar
|
||||||
|
);
|
||||||
|
content.push(
|
||||||
|
<div
|
||||||
|
key='inner-wrap'
|
||||||
|
className='inner-wrap channel__wrap'
|
||||||
|
>
|
||||||
|
<div className='row header'>
|
||||||
|
<div id='navbar'>
|
||||||
|
<Navbar/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='row main'>
|
||||||
|
{this.props.center}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className='channel-view'>
|
<div className='channel-view'>
|
||||||
<ErrorBar/>
|
<ErrorBar/>
|
||||||
<div className='container-fluid'>
|
<div className='container-fluid'>
|
||||||
<SidebarRight/>
|
<SidebarRight/>
|
||||||
<SidebarRightMenu/>
|
<SidebarRightMenu/>
|
||||||
{this.props.sidebar}
|
{content}
|
||||||
{this.props.center}
|
|
||||||
|
|
||||||
<GetPostLinkModal/>
|
<GetPostLinkModal/>
|
||||||
<GetTeamInviteLinkModal/>
|
<GetTeamInviteLinkModal/>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import * as AsyncClient from 'utils/async_client.jsx';
|
|||||||
import ChannelStore from 'stores/channel_store.jsx';
|
import ChannelStore from 'stores/channel_store.jsx';
|
||||||
import LoadingScreen from './loading_screen.jsx';
|
import LoadingScreen from './loading_screen.jsx';
|
||||||
import NewChannelFlow from './new_channel_flow.jsx';
|
import NewChannelFlow from './new_channel_flow.jsx';
|
||||||
|
import * as GlobalActions from 'action_creators/global_actions.jsx';
|
||||||
|
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {FormattedMessage} from 'react-intl';
|
||||||
|
|
||||||
@@ -64,8 +65,7 @@ export default class MoreChannels extends React.Component {
|
|||||||
client.joinChannel(channel.id,
|
client.joinChannel(channel.id,
|
||||||
() => {
|
() => {
|
||||||
$(ReactDOM.findDOMNode(this.refs.modal)).modal('hide');
|
$(ReactDOM.findDOMNode(this.refs.modal)).modal('hide');
|
||||||
AsyncClient.getChannel(channel.id);
|
GlobalActions.emitChannelClickEvent(channel);
|
||||||
Utils.switchChannel(channel);
|
|
||||||
this.setState({joiningChannel: -1});
|
this.setState({joiningChannel: -1});
|
||||||
},
|
},
|
||||||
(err) => {
|
(err) => {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {Modal} from 'react-bootstrap';
|
|||||||
import FilteredUserList from './filtered_user_list.jsx';
|
import FilteredUserList from './filtered_user_list.jsx';
|
||||||
import UserStore from 'stores/user_store.jsx';
|
import UserStore from 'stores/user_store.jsx';
|
||||||
import * as Utils from 'utils/utils.jsx';
|
import * as Utils from 'utils/utils.jsx';
|
||||||
|
import * as GlobalActions from 'action_creators/global_actions.jsx';
|
||||||
|
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {FormattedMessage} from 'react-intl';
|
||||||
import SpinnerButton from 'components/spinner_button.jsx';
|
import SpinnerButton from 'components/spinner_button.jsx';
|
||||||
@@ -68,7 +69,7 @@ export default class MoreDirectChannels extends React.Component {
|
|||||||
Utils.openDirectChannelToUser(
|
Utils.openDirectChannelToUser(
|
||||||
teammate,
|
teammate,
|
||||||
(channel) => {
|
(channel) => {
|
||||||
Utils.switchChannel(channel);
|
GlobalActions.emitChannelClickEvent(channel);
|
||||||
this.setState({loadingDMChannel: -1});
|
this.setState({loadingDMChannel: -1});
|
||||||
this.handleHide();
|
this.handleHide();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ export default class Navbar extends React.Component {
|
|||||||
this.showEditChannelHeaderModal = this.showEditChannelHeaderModal.bind(this);
|
this.showEditChannelHeaderModal = this.showEditChannelHeaderModal.bind(this);
|
||||||
this.showRenameChannelModal = this.showRenameChannelModal.bind(this);
|
this.showRenameChannelModal = this.showRenameChannelModal.bind(this);
|
||||||
this.hideRenameChannelModal = this.hideRenameChannelModal.bind(this);
|
this.hideRenameChannelModal = this.hideRenameChannelModal.bind(this);
|
||||||
|
this.isStateValid = this.isStateValid.bind(this);
|
||||||
|
|
||||||
this.createCollapseButtons = this.createCollapseButtons.bind(this);
|
this.createCollapseButtons = this.createCollapseButtons.bind(this);
|
||||||
this.createDropdown = this.createDropdown.bind(this);
|
this.createDropdown = this.createDropdown.bind(this);
|
||||||
@@ -64,7 +65,7 @@ export default class Navbar extends React.Component {
|
|||||||
currentUser: UserStore.getCurrentUser()
|
currentUser: UserStore.getCurrentUser()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
stateValid() {
|
isStateValid() {
|
||||||
return this.state.channel && this.state.member && this.state.users && this.state.currentUser;
|
return this.state.channel && this.state.member && this.state.users && this.state.currentUser;
|
||||||
}
|
}
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@@ -422,7 +423,7 @@ export default class Navbar extends React.Component {
|
|||||||
return buttons;
|
return buttons;
|
||||||
}
|
}
|
||||||
render() {
|
render() {
|
||||||
if (!this.stateValid()) {
|
if (!this.isStateValid()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
// See License.txt for license information.
|
// See License.txt for license information.
|
||||||
|
|
||||||
import * as Utils from 'utils/utils.jsx';
|
import * as Utils from 'utils/utils.jsx';
|
||||||
import * as AsyncClient from 'utils/async_client.jsx';
|
|
||||||
import * as Client from 'utils/client.jsx';
|
import * as Client from 'utils/client.jsx';
|
||||||
import UserStore from 'stores/user_store.jsx';
|
import UserStore from 'stores/user_store.jsx';
|
||||||
|
import * as GlobalActions from 'action_creators/global_actions.jsx';
|
||||||
|
|
||||||
import NewChannelModal from './new_channel_modal.jsx';
|
import NewChannelModal from './new_channel_modal.jsx';
|
||||||
import ChangeURLModal from './change_url_modal.jsx';
|
import ChangeURLModal from './change_url_modal.jsx';
|
||||||
@@ -110,8 +110,7 @@ class NewChannelFlow extends React.Component {
|
|||||||
Client.createChannel(channel,
|
Client.createChannel(channel,
|
||||||
(data) => {
|
(data) => {
|
||||||
this.props.onModalDismissed();
|
this.props.onModalDismissed();
|
||||||
AsyncClient.getChannel(data.id);
|
GlobalActions.emitChannelClickEvent(data);
|
||||||
Utils.switchChannel(data);
|
|
||||||
},
|
},
|
||||||
(err) => {
|
(err) => {
|
||||||
if (err.id === 'model.channel.is_valid.2_or_more.app_error') {
|
if (err.id === 'model.channel.is_valid.2_or_more.app_error') {
|
||||||
|
|||||||
109
webapp/components/permalink_view.jsx
Обычный файл
109
webapp/components/permalink_view.jsx
Обычный файл
@@ -0,0 +1,109 @@
|
|||||||
|
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import ChannelHeader from 'components/channel_header.jsx';
|
||||||
|
import PostFocusView from 'components/post_focus_view.jsx';
|
||||||
|
|
||||||
|
import ChannelStore from 'stores/channel_store.jsx';
|
||||||
|
import UserStore from 'stores/user_store.jsx';
|
||||||
|
import TeamStore from 'stores/team_store.jsx';
|
||||||
|
|
||||||
|
import {Link} from 'react-router';
|
||||||
|
import {FormattedMessage} from 'react-intl';
|
||||||
|
|
||||||
|
export default class PermalinkView extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.getStateFromStores = this.getStateFromStores.bind(this);
|
||||||
|
this.isStateValid = this.isStateValid.bind(this);
|
||||||
|
this.updateState = this.updateState.bind(this);
|
||||||
|
|
||||||
|
this.state = this.getStateFromStores(props);
|
||||||
|
}
|
||||||
|
getStateFromStores(props) {
|
||||||
|
const postId = props.params.postid;
|
||||||
|
const channel = ChannelStore.getCurrent();
|
||||||
|
const channelId = channel ? channel.id : '';
|
||||||
|
const channelName = channel ? channel.name : '';
|
||||||
|
const teamURL = TeamStore.getCurrentTeamUrl();
|
||||||
|
const profiles = JSON.parse(JSON.stringify(UserStore.getProfiles()));
|
||||||
|
return {
|
||||||
|
channelId,
|
||||||
|
channelName,
|
||||||
|
profiles,
|
||||||
|
teamURL,
|
||||||
|
postId
|
||||||
|
};
|
||||||
|
}
|
||||||
|
isStateValid() {
|
||||||
|
return this.state.channelId !== '' && this.state.profiles && this.state.teamURL;
|
||||||
|
}
|
||||||
|
updateState() {
|
||||||
|
this.setState(this.getStateFromStores(this.props));
|
||||||
|
}
|
||||||
|
componentDidMount() {
|
||||||
|
ChannelStore.addChangeListener(this.updateState);
|
||||||
|
TeamStore.addChangeListener(this.updateState);
|
||||||
|
}
|
||||||
|
componentWillUnmount() {
|
||||||
|
ChannelStore.removeChangeListener(this.updateState);
|
||||||
|
TeamStore.removeChangeListener(this.updateState);
|
||||||
|
}
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
this.setState(this.getStateFromStores(nextProps));
|
||||||
|
}
|
||||||
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
|
if (nextState.postId !== this.state.postId) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextState.channelId !== this.state.channelId) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextState.teamURL !== this.state.teamURL) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
if (!this.isStateValid()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
id='app-content'
|
||||||
|
className='app__content'
|
||||||
|
>
|
||||||
|
<ChannelHeader
|
||||||
|
channelId={this.state.channelId}
|
||||||
|
/>
|
||||||
|
<PostFocusView profiles={this.state.profiles}/>
|
||||||
|
<div
|
||||||
|
id='archive-link-home'
|
||||||
|
>
|
||||||
|
<Link
|
||||||
|
to={this.state.teamURL + '/channels/' + this.state.channelName}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='center_panel.recent'
|
||||||
|
defaultMessage='Click here to jump to recent messages. '
|
||||||
|
/>
|
||||||
|
<i className='fa fa-arrow-down'></i>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PermalinkView.defaultProps = {
|
||||||
|
};
|
||||||
|
|
||||||
|
PermalinkView.propTypes = {
|
||||||
|
params: React.PropTypes.object.isRequired
|
||||||
|
};
|
||||||
@@ -6,6 +6,7 @@ import $ from 'jquery';
|
|||||||
import UserStore from 'stores/user_store.jsx';
|
import UserStore from 'stores/user_store.jsx';
|
||||||
import {Popover, Overlay} from 'react-bootstrap';
|
import {Popover, Overlay} from 'react-bootstrap';
|
||||||
import * as Utils from 'utils/utils.jsx';
|
import * as Utils from 'utils/utils.jsx';
|
||||||
|
import * as GlobalActions from 'action_creators/global_actions.jsx';
|
||||||
import Constants from 'utils/constants.jsx';
|
import Constants from 'utils/constants.jsx';
|
||||||
|
|
||||||
import ChannelStore from 'stores/channel_store.jsx';
|
import ChannelStore from 'stores/channel_store.jsx';
|
||||||
@@ -36,7 +37,7 @@ export default class PopoverListMembers extends React.Component {
|
|||||||
Utils.openDirectChannelToUser(
|
Utils.openDirectChannelToUser(
|
||||||
teammate,
|
teammate,
|
||||||
(channel, channelAlreadyExisted) => {
|
(channel, channelAlreadyExisted) => {
|
||||||
Utils.switchChannel(channel);
|
GlobalActions.emitChannelClickEvent(channel);
|
||||||
if (channelAlreadyExisted) {
|
if (channelAlreadyExisted) {
|
||||||
this.closePopover();
|
this.closePopover();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import ReactDOM from 'react-dom';
|
|||||||
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 BrowserStore from 'stores/browser_store.jsx';
|
import BrowserStore from 'stores/browser_store.jsx';
|
||||||
import * as utils from 'utils/utils.jsx';
|
import * as GlobalActions from 'action_creators/global_actions.jsx';
|
||||||
|
|
||||||
import {FormattedMessage} from 'react-intl';
|
import {FormattedMessage} from 'react-intl';
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ export default class RemovedFromChannelModal extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var townSquare = ChannelStore.getByName('town-square');
|
var townSquare = ChannelStore.getByName('town-square');
|
||||||
setTimeout(() => utils.switchChannel(townSquare), 1);
|
setTimeout(() => GlobalActions.emitChannelClickEvent(townSquare), 1);
|
||||||
|
|
||||||
this.setState(newState);
|
this.setState(newState);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import * as Utils from 'utils/utils.jsx';
|
import * as Utils from 'utils/utils.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 GlobalActions from 'action_creators/global_actions.jsx';
|
||||||
import Constants from 'utils/constants.jsx';
|
import Constants from 'utils/constants.jsx';
|
||||||
|
|
||||||
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'react-intl';
|
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'react-intl';
|
||||||
@@ -165,8 +165,7 @@ export default class RenameChannelModal extends React.Component {
|
|||||||
Client.updateChannel(
|
Client.updateChannel(
|
||||||
channel,
|
channel,
|
||||||
() => {
|
() => {
|
||||||
AsyncClient.getChannel(channel.id);
|
GlobalActions.emitChannelClickEvent(channel);
|
||||||
Utils.updateAddressBar(channel.name);
|
|
||||||
|
|
||||||
this.handleHide();
|
this.handleHide();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,37 +1,25 @@
|
|||||||
// 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 $ from 'jquery';
|
|
||||||
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 GlobalActions from 'action_creators/global_actions.jsx';
|
import * as GlobalActions from 'action_creators/global_actions.jsx';
|
||||||
import * as TextFormatting from 'utils/text_formatting.jsx';
|
import * as TextFormatting from 'utils/text_formatting.jsx';
|
||||||
|
import * as Utils from 'utils/utils.jsx';
|
||||||
|
|
||||||
import Constants from 'utils/constants.jsx';
|
import Constants from 'utils/constants.jsx';
|
||||||
|
|
||||||
import {FormattedMessage, FormattedDate} from 'react-intl';
|
import {FormattedMessage, FormattedDate} from 'react-intl';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import {Link} from 'react-router';
|
||||||
|
|
||||||
export default class SearchResultsItem extends React.Component {
|
export default class SearchResultsItem extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.handleClick = this.handleClick.bind(this);
|
|
||||||
this.handleFocusRHSClick = this.handleFocusRHSClick.bind(this);
|
this.handleFocusRHSClick = this.handleFocusRHSClick.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleClick(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
GlobalActions.emitPostFocusEvent(this.props.post.id);
|
|
||||||
|
|
||||||
if ($(window).width() < 768) {
|
|
||||||
$('.sidebar--right').removeClass('move--left');
|
|
||||||
$('.inner-wrap').removeClass('move--left');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleFocusRHSClick(e) {
|
handleFocusRHSClick(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
GlobalActions.emitPostFocusRightHandSideFromSearch(this.props.post, this.props.isMentionSearch);
|
GlobalActions.emitPostFocusRightHandSideFromSearch(this.props.post, this.props.isMentionSearch);
|
||||||
@@ -99,16 +87,15 @@ export default class SearchResultsItem extends React.Component {
|
|||||||
</time>
|
</time>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a
|
<Link
|
||||||
href='#'
|
to={Utils.getTeamURLFromAddressBar() + '/pl/' + this.props.post.id}
|
||||||
className='search-item__jump'
|
className='search-item__jump'
|
||||||
onClick={this.handleClick}
|
|
||||||
>
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='search_item.jump'
|
id='search_item.jump'
|
||||||
defaultMessage='Jump'
|
defaultMessage='Jump'
|
||||||
/>
|
/>
|
||||||
</a>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import TeamStore from 'stores/team_store.jsx';
|
|||||||
import PreferenceStore from 'stores/preference_store.jsx';
|
import PreferenceStore from 'stores/preference_store.jsx';
|
||||||
|
|
||||||
import * as AsyncClient from 'utils/async_client.jsx';
|
import * as AsyncClient from 'utils/async_client.jsx';
|
||||||
import * as Client from 'utils/client.jsx';
|
|
||||||
import * as Utils from 'utils/utils.jsx';
|
import * as Utils from 'utils/utils.jsx';
|
||||||
|
|
||||||
import Constants from 'utils/constants.jsx';
|
import Constants from 'utils/constants.jsx';
|
||||||
@@ -29,7 +28,7 @@ import {Tooltip, OverlayTrigger} from 'react-bootstrap';
|
|||||||
import loadingGif from 'images/load.gif';
|
import loadingGif from 'images/load.gif';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {browserHistory} from 'react-router';
|
import {browserHistory, Link} from 'react-router';
|
||||||
|
|
||||||
import favicon from 'images/favicon/favicon-16x16.png';
|
import favicon from 'images/favicon/favicon-16x16.png';
|
||||||
import redFavicon from 'images/favicon/redfavicon-16x16.png';
|
import redFavicon from 'images/favicon/redfavicon-16x16.png';
|
||||||
@@ -259,7 +258,7 @@ export default class Sidebar extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (channel.id === this.state.activeId) {
|
if (channel.id === this.state.activeId) {
|
||||||
Utils.switchChannel(ChannelStore.getByName(Constants.DEFAULT_CHANNEL));
|
browserHistory.push(TeamStore.getCurrentTeamUrl() + '/channels/town-square');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,48 +405,6 @@ export default class Sidebar extends React.Component {
|
|||||||
icon = <div className='status'><i className='fa fa-lock'></i></div>;
|
icon = <div className='status'><i className='fa fa-lock'></i></div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set up click handler to switch channels (or create a new channel for non-existant ones)
|
|
||||||
var handleClick = null;
|
|
||||||
|
|
||||||
if (!channel.fake) {
|
|
||||||
handleClick = function clickHandler(e) {
|
|
||||||
if (e.target.attributes.getNamedItem('data-close')) {
|
|
||||||
handleClose(channel);
|
|
||||||
} else {
|
|
||||||
Utils.switchChannel(channel);
|
|
||||||
}
|
|
||||||
|
|
||||||
e.preventDefault();
|
|
||||||
};
|
|
||||||
} else if (channel.fake) {
|
|
||||||
// It's a direct message channel that doesn't exist yet so let's create it now
|
|
||||||
var otherUserId = Utils.getUserIdFromChannelName(channel);
|
|
||||||
|
|
||||||
if (this.state.loadingDMChannel === -1) {
|
|
||||||
handleClick = function clickHandler(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
if (e.target.attributes.getNamedItem('data-close')) {
|
|
||||||
handleClose(channel);
|
|
||||||
} else {
|
|
||||||
this.setState({loadingDMChannel: index});
|
|
||||||
|
|
||||||
Client.createDirectChannel(channel, otherUserId,
|
|
||||||
(data) => {
|
|
||||||
this.setState({loadingDMChannel: -1});
|
|
||||||
AsyncClient.getChannel(data.id);
|
|
||||||
Utils.switchChannel(data);
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
this.setState({loadingDMChannel: -1});
|
|
||||||
browserHistory('/' + this.state.currentTeam.name);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}.bind(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let closeButton = null;
|
let closeButton = null;
|
||||||
const removeTooltip = (
|
const removeTooltip = (
|
||||||
<Tooltip id='remove-dm-tooltip'>
|
<Tooltip id='remove-dm-tooltip'>
|
||||||
@@ -464,12 +421,12 @@ export default class Sidebar extends React.Component {
|
|||||||
placement='top'
|
placement='top'
|
||||||
overlay={removeTooltip}
|
overlay={removeTooltip}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className='btn-close'
|
onClick={() => handleClose(channel)}
|
||||||
data-close='true'
|
className='btn-close'
|
||||||
>
|
>
|
||||||
{'×'}
|
{'×'}
|
||||||
</span>
|
</span>
|
||||||
</OverlayTrigger>
|
</OverlayTrigger>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -481,23 +438,29 @@ export default class Sidebar extends React.Component {
|
|||||||
tutorialTip = this.createTutorialTip();
|
tutorialTip = this.createTutorialTip();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let link = '';
|
||||||
|
if (channel.fake) {
|
||||||
|
link = TeamStore.getCurrentTeamUrl() + '/channels/' + channel.name + '?fakechannel=' + encodeURIComponent(JSON.stringify(channel));
|
||||||
|
} else {
|
||||||
|
link = TeamStore.getCurrentTeamUrl() + '/channels/' + channel.name;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
key={channel.name}
|
key={channel.name}
|
||||||
ref={channel.name}
|
ref={channel.name}
|
||||||
className={linkClass}
|
className={linkClass}
|
||||||
>
|
>
|
||||||
<a
|
<Link
|
||||||
|
to={link}
|
||||||
className={rowClass}
|
className={rowClass}
|
||||||
href={'#'}
|
|
||||||
onClick={handleClick}
|
|
||||||
>
|
>
|
||||||
{icon}
|
{icon}
|
||||||
{status}
|
{status}
|
||||||
{channel.display_name}
|
{channel.display_name}
|
||||||
{badge}
|
{badge}
|
||||||
{closeButton}
|
{closeButton}
|
||||||
</a>
|
</Link>
|
||||||
{tutorialTip}
|
{tutorialTip}
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
@@ -600,6 +563,7 @@ export default class Sidebar extends React.Component {
|
|||||||
<div
|
<div
|
||||||
className='sidebar--left'
|
className='sidebar--left'
|
||||||
id='sidebar-left'
|
id='sidebar-left'
|
||||||
|
key='sidebar-left'
|
||||||
>
|
>
|
||||||
<NewChannelFlow
|
<NewChannelFlow
|
||||||
show={showChannelModal}
|
show={showChannelModal}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export default class SidebarRight extends React.Component {
|
|||||||
this.doStrangeThings = this.doStrangeThings.bind(this);
|
this.doStrangeThings = this.doStrangeThings.bind(this);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
searchVisible: !!SearchStore.getSearchResults(),
|
searchVisible: SearchStore.getSearchResults() !== null,
|
||||||
isMentionSearch: SearchStore.getIsMentionSearch(),
|
isMentionSearch: SearchStore.getIsMentionSearch(),
|
||||||
postRightVisible: !!PostStore.getSelectedPost(),
|
postRightVisible: !!PostStore.getSelectedPost(),
|
||||||
fromSearch: false,
|
fromSearch: false,
|
||||||
@@ -111,7 +111,7 @@ export default class SidebarRight extends React.Component {
|
|||||||
}
|
}
|
||||||
onSearchChange() {
|
onSearchChange() {
|
||||||
this.setState({
|
this.setState({
|
||||||
searchVisible: !!SearchStore.getSearchResults(),
|
searchVisible: SearchStore.getSearchResults() !== null,
|
||||||
isMentionSearch: SearchStore.getIsMentionSearch()
|
isMentionSearch: SearchStore.getIsMentionSearch()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
// 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 ChannelStore from 'stores/channel_store.jsx';
|
|
||||||
import TeamStore from 'stores/team_store.jsx';
|
import TeamStore from 'stores/team_store.jsx';
|
||||||
import PreferenceStore from 'stores/preference_store.jsx';
|
import PreferenceStore from 'stores/preference_store.jsx';
|
||||||
import * as Utils from 'utils/utils.jsx';
|
import * as Utils from 'utils/utils.jsx';
|
||||||
@@ -11,6 +10,7 @@ import * as AsyncClient from 'utils/async_client.jsx';
|
|||||||
import Constants from 'utils/constants.jsx';
|
import Constants from 'utils/constants.jsx';
|
||||||
|
|
||||||
import {FormattedMessage, FormattedHTMLMessage} from 'react-intl';
|
import {FormattedMessage, FormattedHTMLMessage} from 'react-intl';
|
||||||
|
import {browserHistory} from 'react-router';
|
||||||
|
|
||||||
const Preferences = Constants.Preferences;
|
const Preferences = Constants.Preferences;
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ export default class TutorialIntroScreens extends React.Component {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils.switchChannel(ChannelStore.getByName(Constants.DEFAULT_CHANNEL));
|
browserHistory.push(TeamStore.getCurrentTeamUrl() + '/channels/town-square');
|
||||||
|
|
||||||
const step = PreferenceStore.getInt(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), 0);
|
const step = PreferenceStore.getInt(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), 0);
|
||||||
|
|
||||||
@@ -52,6 +52,8 @@ export default class TutorialIntroScreens extends React.Component {
|
|||||||
UserStore.getCurrentId(),
|
UserStore.getCurrentId(),
|
||||||
'999'
|
'999'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
browserHistory.push(TeamStore.getCurrentTeamUrl() + '/channels/town-square');
|
||||||
}
|
}
|
||||||
createScreen() {
|
createScreen() {
|
||||||
switch (this.state.currentScreen) {
|
switch (this.state.currentScreen) {
|
||||||
|
|||||||
19
webapp/components/tutorial/tutorial_view.jsx
Обычный файл
19
webapp/components/tutorial/tutorial_view.jsx
Обычный файл
@@ -0,0 +1,19 @@
|
|||||||
|
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import TutorialIntroScreens from './tutorial_intro_screens.jsx';
|
||||||
|
|
||||||
|
export default class TutorialView extends React.Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
id='app-content'
|
||||||
|
className='app__content'
|
||||||
|
>
|
||||||
|
<TutorialIntroScreens/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ import NeedsTeam from 'components/needs_team.jsx';
|
|||||||
import PasswordResetSendLink from 'components/password_reset_send_link.jsx';
|
import PasswordResetSendLink from 'components/password_reset_send_link.jsx';
|
||||||
import PasswordResetForm from 'components/password_reset_form.jsx';
|
import PasswordResetForm from 'components/password_reset_form.jsx';
|
||||||
import ChannelView from 'components/channel_view.jsx';
|
import ChannelView from 'components/channel_view.jsx';
|
||||||
|
import PermalinkView from 'components/permalink_view.jsx';
|
||||||
import Sidebar from 'components/sidebar.jsx';
|
import Sidebar from 'components/sidebar.jsx';
|
||||||
import * as AsyncClient from 'utils/async_client.jsx';
|
import * as AsyncClient from 'utils/async_client.jsx';
|
||||||
import PreferenceStore from 'stores/preference_store.jsx';
|
import PreferenceStore from 'stores/preference_store.jsx';
|
||||||
@@ -34,6 +35,7 @@ import SignupUserComplete from 'components/signup_user_complete.jsx';
|
|||||||
import ShouldVerifyEmail from 'components/should_verify_email.jsx';
|
import ShouldVerifyEmail from 'components/should_verify_email.jsx';
|
||||||
import DoVerifyEmail from 'components/do_verify_email.jsx';
|
import DoVerifyEmail from 'components/do_verify_email.jsx';
|
||||||
import AdminConsole from 'components/admin_console/admin_controller.jsx';
|
import AdminConsole from 'components/admin_console/admin_controller.jsx';
|
||||||
|
import TutorialView from 'components/tutorial/tutorial_view.jsx';
|
||||||
|
|
||||||
import SignupTeamComplete from 'components/signup_team_complete/components/signup_team_complete.jsx';
|
import SignupTeamComplete from 'components/signup_team_complete/components/signup_team_complete.jsx';
|
||||||
import WelcomePage from 'components/signup_team_complete/components/team_signup_welcome_page.jsx';
|
import WelcomePage from 'components/signup_team_complete/components/team_signup_welcome_page.jsx';
|
||||||
@@ -133,20 +135,9 @@ function preLoggedIn(nextState, replace, callback) {
|
|||||||
|
|
||||||
const d2 = AsyncClient.getChannels();
|
const d2 = AsyncClient.getChannels();
|
||||||
|
|
||||||
$.when(d1, d2).done(() => callback());
|
$.when(d1, d2).done(() => {
|
||||||
}
|
callback();
|
||||||
|
});
|
||||||
function onChannelChange(nextState) {
|
|
||||||
const channelName = nextState.params.channel;
|
|
||||||
|
|
||||||
// Make sure we have all the channels
|
|
||||||
AsyncClient.getChannels(true);
|
|
||||||
|
|
||||||
// Get our channel's ID
|
|
||||||
const channel = ChannelStore.getByName(channelName);
|
|
||||||
|
|
||||||
// User clicked channel
|
|
||||||
GlobalActions.emitChannelClickEvent(channel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onRootEnter(nextState, replace, callback) {
|
function onRootEnter(nextState, replace, callback) {
|
||||||
@@ -172,6 +163,26 @@ function onPermalinkEnter(nextState) {
|
|||||||
GlobalActions.emitPostFocusEvent(postId);
|
GlobalActions.emitPostFocusEvent(postId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onChannelEnter(nextState) {
|
||||||
|
doChannelChange(nextState);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onChannelChange(prevState, nextState) {
|
||||||
|
if (prevState.params.channel !== nextState.params.channel) {
|
||||||
|
doChannelChange(nextState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function doChannelChange(state) {
|
||||||
|
let channel;
|
||||||
|
if (state.location.query.fakechannel) {
|
||||||
|
channel = JSON.parse(state.location.query.fakechannel);
|
||||||
|
} else {
|
||||||
|
channel = ChannelStore.getByName(state.params.channel);
|
||||||
|
}
|
||||||
|
GlobalActions.emitChannelClickEvent(channel);
|
||||||
|
}
|
||||||
|
|
||||||
function onLoggedOut(nextState) {
|
function onLoggedOut(nextState) {
|
||||||
const teamName = nextState.params.team;
|
const teamName = nextState.params.team;
|
||||||
Client.logout(
|
Client.logout(
|
||||||
@@ -204,7 +215,8 @@ function renderRootComponent() {
|
|||||||
>
|
>
|
||||||
<Route
|
<Route
|
||||||
path=':team/channels/:channel'
|
path=':team/channels/:channel'
|
||||||
onEnter={onChannelChange}
|
onEnter={onChannelEnter}
|
||||||
|
onChange={onChannelChange}
|
||||||
components={{
|
components={{
|
||||||
sidebar: Sidebar,
|
sidebar: Sidebar,
|
||||||
center: ChannelView
|
center: ChannelView
|
||||||
@@ -215,23 +227,23 @@ function renderRootComponent() {
|
|||||||
onEnter={onPermalinkEnter}
|
onEnter={onPermalinkEnter}
|
||||||
components={{
|
components={{
|
||||||
sidebar: Sidebar,
|
sidebar: Sidebar,
|
||||||
center: ChannelView
|
center: PermalinkView
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path=':team/tutorial'
|
||||||
|
components={{
|
||||||
|
sidebar: Sidebar,
|
||||||
|
center: TutorialView
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path=':team/logout'
|
path=':team/logout'
|
||||||
onEnter={onLoggedOut}
|
onEnter={onLoggedOut}
|
||||||
components={{
|
|
||||||
sidebar: null,
|
|
||||||
center: null
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path='admin_console'
|
path='admin_console'
|
||||||
components={{
|
component={AdminConsole}
|
||||||
sidebar: null,
|
|
||||||
center: AdminConsole
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</Route>
|
</Route>
|
||||||
<Route component={NotLoggedIn}>
|
<Route component={NotLoggedIn}>
|
||||||
|
|||||||
@@ -406,7 +406,7 @@ class PostStoreClass extends EventEmitter {
|
|||||||
let posts;
|
let posts;
|
||||||
let pendingPosts;
|
let pendingPosts;
|
||||||
for (const k in this.postsInfo) {
|
for (const k in this.postsInfo) {
|
||||||
if (this.postsInfo[k].postList.posts.hasOwnProperty(this.selectedPostId)) {
|
if (this.postsInfo[k].postList && this.postsInfo[k].postList.posts.hasOwnProperty(this.selectedPostId)) {
|
||||||
posts = this.postsInfo[k].postList.posts;
|
posts = this.postsInfo[k].postList.posts;
|
||||||
if (this.postsInfo[k].pendingPosts != null) {
|
if (this.postsInfo[k].pendingPosts != null) {
|
||||||
pendingPosts = this.postsInfo[k].pendingPosts.posts;
|
pendingPosts = this.postsInfo[k].pendingPosts.posts;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class SearchStoreClass extends EventEmitter {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.searchResults = {};
|
this.searchResults = null;
|
||||||
this.isMentionSearch = false;
|
this.isMentionSearch = false;
|
||||||
this.searchTerm = '';
|
this.searchTerm = '';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ export function notifyMe(title, body, channel) {
|
|||||||
notification.onclick = () => {
|
notification.onclick = () => {
|
||||||
window.focus();
|
window.focus();
|
||||||
if (channel) {
|
if (channel) {
|
||||||
switchChannel(channel);
|
GlobalActions.emitChannelClickEvent(channel);
|
||||||
} else {
|
} else {
|
||||||
browserHistory.push(TeamStore.getCurrentTeamUrl() + '/channels/town-square');
|
browserHistory.push(TeamStore.getCurrentTeamUrl() + '/channels/town-square');
|
||||||
}
|
}
|
||||||
@@ -957,24 +957,6 @@ export function isValidUsername(name) {
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateAddressBar(channelName) {
|
|
||||||
const teamURL = TeamStore.getCurrentTeamUrl();
|
|
||||||
history.replaceState('data', '', teamURL + '/channels/' + channelName);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function switchChannel(channel) {
|
|
||||||
GlobalActions.emitChannelClickEvent(channel);
|
|
||||||
|
|
||||||
updateAddressBar(channel.name);
|
|
||||||
|
|
||||||
$('.inner-wrap').removeClass('move--right');
|
|
||||||
$('.sidebar--left').removeClass('move--right');
|
|
||||||
|
|
||||||
client.trackPage();
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isMobile() {
|
export function isMobile() {
|
||||||
return screen.width <= 768;
|
return screen.width <= 768;
|
||||||
}
|
}
|
||||||
@@ -1253,7 +1235,7 @@ export function importSlack(file, success, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getTeamURLFromAddressBar() {
|
export function getTeamURLFromAddressBar() {
|
||||||
return window.location.href.split('/channels')[0];
|
return window.location.origin + '/' + window.location.pathname.split('/')[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getShortenedTeamURL() {
|
export function getShortenedTeamURL() {
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user