Этот коммит содержится в:
Asaad Mahmood
2015-10-19 22:59:01 +05:00
родитель 2d42aefeaf fd69910fab
Коммит 5135986a3b
20 изменённых файлов: 328 добавлений и 201 удалений

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

@@ -40,9 +40,13 @@ export default class AdminController extends React.Component {
config: AdminStore.getConfig(),
teams: AdminStore.getAllTeams(),
selectedTeams,
selected: 'service_settings',
selectedTeam: null
selected: props.tab || 'service_settings',
selectedTeam: props.teamId || null
};
if (!props.tab) {
history.replaceState(null, null, `/admin_console/${this.state.selected}`);
}
}
componentDidMount() {
@@ -142,7 +146,9 @@ export default class AdminController extends React.Component {
} else if (this.state.selected === 'service_settings') {
tab = <ServiceSettingsTab config={this.state.config} />;
} else if (this.state.selected === 'team_users') {
tab = <TeamUsersTab team={this.state.teams[this.state.selectedTeam]} />;
if (this.state.teams) {
tab = <TeamUsersTab team={this.state.teams[this.state.selectedTeam]} />;
}
}
}

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

@@ -24,6 +24,7 @@ export default class AdminSidebar extends React.Component {
handleClick(name, teamId, e) {
e.preventDefault();
this.props.selectTab(name, teamId);
history.pushState({name: name, teamId: teamId}, null, `/admin_console/${name}/${teamId || ''}`);
}
isSelected(name, teamId) {

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

@@ -4,7 +4,6 @@
const ChannelStore = require('../stores/channel_store.jsx');
const UserStore = require('../stores/user_store.jsx');
const PostStore = require('../stores/post_store.jsx');
const SocketStore = require('../stores/socket_store.jsx');
const NavbarSearchBox = require('./search_bar.jsx');
const AsyncClient = require('../utils/async_client.jsx');
const Client = require('../utils/client.jsx');
@@ -25,7 +24,6 @@ export default class ChannelHeader extends React.Component {
super(props);
this.onListenerChange = this.onListenerChange.bind(this);
this.onSocketChange = this.onSocketChange.bind(this);
this.handleLeave = this.handleLeave.bind(this);
this.searchMentions = this.searchMentions.bind(this);
@@ -45,7 +43,6 @@ export default class ChannelHeader extends React.Component {
ChannelStore.addExtraInfoChangeListener(this.onListenerChange);
PostStore.addSearchChangeListener(this.onListenerChange);
UserStore.addChangeListener(this.onListenerChange);
SocketStore.addChangeListener(this.onSocketChange);
}
componentWillUnmount() {
ChannelStore.removeChangeListener(this.onListenerChange);
@@ -60,16 +57,9 @@ export default class ChannelHeader extends React.Component {
}
$('.channel-header__info .description').popover({placement: 'bottom', trigger: 'hover', html: true, delay: {show: 500, hide: 500}});
}
onSocketChange(msg) {
if (msg.action === 'new_user' ||
msg.action === 'user_added' ||
(msg.action === 'user_removed' && msg.user_id !== UserStore.getCurrentId())) {
AsyncClient.getChannelExtraInfo(true);
}
}
handleLeave() {
Client.leaveChannel(this.state.channel.id,
function handleLeaveSuccess() {
() => {
AppDispatcher.handleViewAction({
type: ActionTypes.LEAVE_CHANNEL,
id: this.state.channel.id
@@ -77,8 +67,8 @@ export default class ChannelHeader extends React.Component {
const townsquare = ChannelStore.getByName('town-square');
Utils.switchChannel(townsquare);
}.bind(this),
function handleLeaveError(err) {
},
(err) => {
AsyncClient.dispatchError(err, 'handleLeave');
}
);

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

@@ -1,8 +1,11 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
var SocketStore = require('../stores/socket_store.jsx');
var UserStore = require('../stores/user_store.jsx');
const SocketStore = require('../stores/socket_store.jsx');
const UserStore = require('../stores/user_store.jsx');
const Constants = require('../utils/constants.jsx');
const SocketEvents = Constants.SocketEvents;
export default class MsgTyping extends React.Component {
constructor(props) {
@@ -33,9 +36,9 @@ export default class MsgTyping extends React.Component {
}
onChange(msg) {
if (msg.action === 'typing' &&
this.props.channelId === msg.channel_id &&
this.props.parentId === msg.props.parent_id) {
if (msg.action === SocketEvents.TYPING &&
this.props.channelId === msg.channel_id &&
this.props.parentId === msg.props.parent_id) {
this.lastTime = new Date().getTime();
var username = 'Someone';
@@ -52,7 +55,7 @@ export default class MsgTyping extends React.Component {
}
}.bind(this), 3000);
}
} else if (msg.action === 'posted' && msg.channel_id === this.props.channelId) {
} else if (msg.action === SocketEvents.POSTED && msg.channel_id === this.props.channelId) {
this.setState({text: ''});
}
}

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

@@ -1,20 +1,24 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
var PostStore = require('../stores/post_store.jsx');
var ChannelStore = require('../stores/channel_store.jsx');
var UserStore = require('../stores/user_store.jsx');
var PreferenceStore = require('../stores/preference_store.jsx');
var UserProfile = require('./user_profile.jsx');
var AsyncClient = require('../utils/async_client.jsx');
var Post = require('./post.jsx');
var LoadingScreen = require('./loading_screen.jsx');
var SocketStore = require('../stores/socket_store.jsx');
var utils = require('../utils/utils.jsx');
var Client = require('../utils/client.jsx');
var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
var Constants = require('../utils/constants.jsx');
var ActionTypes = Constants.ActionTypes;
const Post = require('./post.jsx');
const UserProfile = require('./user_profile.jsx');
const AsyncClient = require('../utils/async_client.jsx');
const LoadingScreen = require('./loading_screen.jsx');
const PostStore = require('../stores/post_store.jsx');
const ChannelStore = require('../stores/channel_store.jsx');
const UserStore = require('../stores/user_store.jsx');
const SocketStore = require('../stores/socket_store.jsx');
const PreferenceStore = require('../stores/preference_store.jsx');
const utils = require('../utils/utils.jsx');
const Client = require('../utils/client.jsx');
const Constants = require('../utils/constants.jsx');
const ActionTypes = Constants.ActionTypes;
const SocketEvents = Constants.SocketEvents;
const AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
export default class PostList extends React.Component {
constructor(props) {
@@ -58,7 +62,7 @@ export default class PostList extends React.Component {
}
}
postList.order.sort(function postSort(a, b) {
postList.order.sort((a, b) => {
if (postList.posts[a].create_at > postList.posts[b].create_at) {
return -1;
}
@@ -82,7 +86,7 @@ export default class PostList extends React.Component {
}
return {
postList: postList
postList
};
}
componentDidMount() {
@@ -263,14 +267,14 @@ export default class PostList extends React.Component {
Client.getPosts(
id,
PostStore.getLatestUpdate(id),
function success() {
() => {
this.loadInProgress = false;
this.setState({isFirstLoadComplete: true});
}.bind(this),
function fail() {
},
() => {
this.loadInProgress = false;
this.setState({isFirstLoadComplete: true});
}.bind(this)
}
);
}
onChange() {
@@ -281,28 +285,16 @@ export default class PostList extends React.Component {
}
}
onSocketChange(msg) {
var post;
if (msg.action === 'posted' || msg.action === 'post_edited') {
post = JSON.parse(msg.props.post);
PostStore.storePost(post);
} else if (msg.action === 'post_deleted') {
if (msg.action === SocketEvents.POST_DELETED) {
var activeRoot = $(document.activeElement).closest('.comment-create-body')[0];
var activeRootPostId = '';
if (activeRoot && activeRoot.id.length > 0) {
activeRootPostId = activeRoot.id;
}
post = JSON.parse(msg.props.post);
PostStore.storeUnseenDeletedPost(post);
PostStore.removePost(post, true);
PostStore.emitChange();
if (activeRootPostId === msg.props.post_id && UserStore.getCurrentId() !== msg.user_id) {
$('#post_deleted').modal('show');
}
} else if (msg.action === 'new_user') {
AsyncClient.getProfiles();
}
}
onTimeChange() {
@@ -352,7 +344,7 @@ export default class PostList extends React.Component {
data-title={channel.display_name}
data-channelid={channel.id}
>
<i className='fa fa-pencil'></i>Set a description
<i className='fa fa-pencil'></i>{'Set a description'}
</a>
</div>
);

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

@@ -2,7 +2,6 @@
// See License.txt for license information.
const AsyncClient = require('../utils/async_client.jsx');
const BrowserStore = require('../stores/browser_store.jsx');
const ChannelStore = require('../stores/channel_store.jsx');
const Client = require('../utils/client.jsx');
const Constants = require('../utils/constants.jsx');
@@ -11,7 +10,6 @@ const NewChannelFlow = require('./new_channel_flow.jsx');
const MoreDirectChannels = require('./more_direct_channels.jsx');
const SearchBox = require('./search_bar.jsx');
const SidebarHeader = require('./sidebar_header.jsx');
const SocketStore = require('../stores/socket_store.jsx');
const TeamStore = require('../stores/team_store.jsx');
const UnreadChannelIndicator = require('./unread_channel_indicator.jsx');
const UserStore = require('../stores/user_store.jsx');
@@ -129,7 +127,6 @@ export default class Sidebar extends React.Component {
UserStore.addChangeListener(this.onChange);
UserStore.addStatusesChangeListener(this.onChange);
TeamStore.addChangeListener(this.onChange);
SocketStore.addChangeListener(this.onSocketChange);
PreferenceStore.addChangeListener(this.onChange);
if ($(window).width() > 768) {
@@ -162,7 +159,6 @@ export default class Sidebar extends React.Component {
UserStore.removeChangeListener(this.onChange);
UserStore.removeStatusesChangeListener(this.onChange);
TeamStore.removeChangeListener(this.onChange);
SocketStore.removeChangeListener(this.onSocketChange);
PreferenceStore.removeChangeListener(this.onChange);
}
onChange() {
@@ -171,94 +167,6 @@ export default class Sidebar extends React.Component {
this.setState(newState);
}
}
onSocketChange(msg) {
if (msg.action === 'posted') {
if (ChannelStore.getCurrentId() === msg.channel_id) {
if (window.isActive) {
AsyncClient.updateLastViewedAt();
}
} else {
AsyncClient.getChannels();
}
if (UserStore.getCurrentId() !== msg.user_id) {
var mentions = [];
if (msg.props.mentions) {
mentions = JSON.parse(msg.props.mentions);
}
var channel = ChannelStore.get(msg.channel_id);
const user = UserStore.getCurrentUser();
const member = ChannelStore.getMember(msg.channel_id);
var notifyLevel = member && member.notify_props ? member.notify_props.desktop : 'default';
if (notifyLevel === 'default') {
notifyLevel = user.notify_props.desktop;
}
if (notifyLevel === 'none') {
return;
} else if (notifyLevel === 'mention' && mentions.indexOf(user.id) === -1 && channel.type !== 'D') {
return;
}
var username = 'Someone';
if (UserStore.hasProfile(msg.user_id)) {
username = UserStore.getProfile(msg.user_id).username;
}
var title = 'Posted';
if (channel) {
title = channel.display_name;
}
var repRegex = new RegExp('<br>', 'g');
var post = JSON.parse(msg.props.post);
var msgProps = msg.props;
var notifyText = post.message.replace(repRegex, '\n').replace(/\n+/g, ' ').replace('<mention>', '').replace('</mention>', '');
if (notifyText.length > 50) {
notifyText = notifyText.substring(0, 49) + '...';
}
if (notifyText.length === 0) {
if (msgProps.image) {
Utils.notifyMe(title, username + ' uploaded an image', channel);
} else if (msgProps.otherFile) {
Utils.notifyMe(title, username + ' uploaded a file', channel);
} else {
Utils.notifyMe(title, username + ' did something new', channel);
}
} else {
Utils.notifyMe(title, username + ' wrote: ' + notifyText, channel);
}
if (!user.notify_props || user.notify_props.desktop_sound === 'true') {
Utils.ding();
}
}
} else if (msg.action === 'viewed') {
if (ChannelStore.getCurrentId() !== msg.channel_id && UserStore.getCurrentId() === msg.user_id) {
AsyncClient.getChannel(msg.channel_id);
}
} else if (msg.action === 'user_added') {
if (UserStore.getCurrentId() === msg.user_id) {
AsyncClient.getChannel(msg.channel_id);
}
} else if (msg.action === 'user_removed') {
if (msg.user_id === UserStore.getCurrentId()) {
AsyncClient.getChannels(true);
if (msg.props.remover !== msg.user_id && msg.props.channel_id === ChannelStore.getCurrentId() && $('#removed_from_channel').length > 0) {
var sentState = {};
sentState.channelName = ChannelStore.getCurrent().display_name;
sentState.remover = UserStore.getProfile(msg.props.remover).username;
BrowserStore.setItem('channel-removed-state', sentState);
$('#removed_from_channel').modal('show');
}
}
}
}
updateTitle() {
const channel = ChannelStore.getCurrent();
if (channel) {

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

@@ -86,11 +86,11 @@ export default class UserProfile extends React.Component {
dataContent.push(
<div
data-toggle='tooltip'
title="' + this.state.profile.email + '"
title={this.state.profile.email}
key='user-popover-email'
>
<a
href="mailto:' + this.state.profile.email + '"
href={'mailto:' + this.state.profile.email}
className='text-nowrap text-lowercase user-popover__email'
>
{this.state.profile.email}

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

@@ -6,6 +6,7 @@ const Utils = require('../utils/utils.jsx');
const Constants = require('../utils/constants.jsx');
const ViewImagePopoverBar = require('./view_image_popover_bar.jsx');
const Modal = ReactBootstrap.Modal;
const KeyCodes = Constants.KeyCodes;
export default class ViewImageModal extends React.Component {
constructor(props) {
@@ -63,11 +64,11 @@ export default class ViewImageModal extends React.Component {
this.loadImage(id);
}
handleKeyPress(e) {
if (!e) {
if (!e || !this.props.show) {
return;
} else if (e.keyCode === 39) {
} else if (e.keyCode === KeyCodes.RIGHT) {
this.handleNext();
} else if (e.keyCode === 37) {
} else if (e.keyCode === KeyCodes.LEFT) {
this.handlePrev();
}
}

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

@@ -5,9 +5,12 @@ var ErrorBar = require('../components/error_bar.jsx');
var SelectTeamModal = require('../components/admin_console/select_team_modal.jsx');
var AdminController = require('../components/admin_console/admin_controller.jsx');
export function setupAdminConsolePage() {
export function setupAdminConsolePage(props) {
ReactDOM.render(
<AdminController />,
<AdminController
tab={props.ActiveTab}
teamId={props.TeamId}
/>,
document.getElementById('admin_controller')
);

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

@@ -1,15 +1,22 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
var UserStore = require('./user_store.jsx');
var ErrorStore = require('./error_store.jsx');
var EventEmitter = require('events').EventEmitter;
const AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
const UserStore = require('./user_store.jsx');
const PostStore = require('./post_store.jsx');
const ChannelStore = require('./channel_store.jsx');
const BrowserStore = require('./browser_store.jsx');
const ErrorStore = require('./error_store.jsx');
const EventEmitter = require('events').EventEmitter;
var Constants = require('../utils/constants.jsx');
var ActionTypes = Constants.ActionTypes;
const Utils = require('../utils/utils.jsx');
const AsyncClient = require('../utils/async_client.jsx');
var CHANGE_EVENT = 'change';
const Constants = require('../utils/constants.jsx');
const ActionTypes = Constants.ActionTypes;
const SocketEvents = Constants.SocketEvents;
const CHANGE_EVENT = 'change';
var conn;
@@ -94,6 +101,39 @@ class SocketStoreClass extends EventEmitter {
removeChangeListener(callback) {
this.removeListener(CHANGE_EVENT, callback);
}
handleMessage(msg) {
switch (msg.action) {
case SocketEvents.POSTED:
handleNewPostEvent(msg);
break;
case SocketEvents.POST_EDITED:
handlePostEditEvent(msg);
break;
case SocketEvents.POST_DELETED:
handlePostDeleteEvent(msg);
break;
case SocketEvents.NEW_USER:
handleNewUserEvent();
break;
case SocketEvents.USER_ADDED:
handleUserAddedEvent(msg);
break;
case SocketEvents.USER_REMOVED:
handleUserRemovedEvent(msg);
break;
case SocketEvents.CHANNEL_VIEWED:
handleChannelViewedEvent(msg);
break;
default:
}
}
sendMessage(msg) {
if (conn && conn.readyState === WebSocket.OPEN) {
conn.send(JSON.stringify(msg));
@@ -104,6 +144,138 @@ class SocketStoreClass extends EventEmitter {
}
}
function handleNewPostEvent(msg) {
// Store post
const post = JSON.parse(msg.props.post);
PostStore.storePost(post);
// Update channel state
if (ChannelStore.getCurrentId() === msg.channel_id) {
if (window.isActive) {
AsyncClient.updateLastViewedAt();
}
} else {
AsyncClient.getChannel(msg.channel_id);
}
// Send desktop notification
if (UserStore.getCurrentId() !== msg.user_id) {
const msgProps = msg.props;
let mentions = [];
if (msgProps.mentions) {
mentions = JSON.parse(msg.props.mentions);
}
const channel = ChannelStore.get(msg.channel_id);
const user = UserStore.getCurrentUser();
const member = ChannelStore.getMember(msg.channel_id);
let notifyLevel = member && member.notify_props ? member.notify_props.desktop : 'default';
if (notifyLevel === 'default') {
notifyLevel = user.notify_props.desktop;
}
if (notifyLevel === 'none') {
return;
} else if (notifyLevel === 'mention' && mentions.indexOf(user.id) === -1 && channel.type !== 'D') {
return;
}
let username = 'Someone';
if (UserStore.hasProfile(msg.user_id)) {
username = UserStore.getProfile(msg.user_id).username;
}
let title = 'Posted';
if (channel) {
title = channel.display_name;
}
let notifyText = post.message.replace(/\n+/g, ' ');
if (notifyText.length > 50) {
notifyText = notifyText.substring(0, 49) + '...';
}
if (notifyText.length === 0) {
if (msgProps.image) {
Utils.notifyMe(title, username + ' uploaded an image', channel);
} else if (msgProps.otherFile) {
Utils.notifyMe(title, username + ' uploaded a file', channel);
} else {
Utils.notifyMe(title, username + ' did something new', channel);
}
} else {
Utils.notifyMe(title, username + ' wrote: ' + notifyText, channel);
}
if (!user.notify_props || user.notify_props.desktop_sound === 'true') {
Utils.ding();
}
}
}
function handlePostEditEvent(msg) {
// Store post
const post = JSON.parse(msg.props.post);
PostStore.storePost(post);
// Update channel state
if (ChannelStore.getCurrentId() === msg.channel_id) {
if (window.isActive) {
AsyncClient.updateLastViewedAt();
}
}
}
function handlePostDeleteEvent(msg) {
const post = JSON.parse(msg.props.post);
PostStore.storeUnseenDeletedPost(post);
PostStore.removePost(post, true);
PostStore.emitChange();
}
function handleNewUserEvent() {
AsyncClient.getProfiles();
AsyncClient.getChannelExtraInfo(true);
}
function handleUserAddedEvent(msg) {
if (ChannelStore.getCurrentId() === msg.channel_id) {
AsyncClient.getChannelExtraInfo(true);
}
if (UserStore.getCurrentId() === msg.user_id) {
AsyncClient.getChannel(msg.channel_id);
}
}
function handleUserRemovedEvent(msg) {
if (UserStore.getCurrentId() === msg.user_id) {
AsyncClient.getChannels();
if (msg.props.remover_id !== msg.user_id &&
msg.channel_id === ChannelStore.getCurrentId() &&
$('#removed_from_channel').length > 0) {
var sentState = {};
sentState.channelName = ChannelStore.getCurrent().display_name;
sentState.remover = UserStore.getProfile(msg.props.remover_id).username;
BrowserStore.setItem('channel-removed-state', sentState);
$('#removed_from_channel').modal('show');
}
} else if (ChannelStore.getCurrentId() === msg.channel_id) {
AsyncClient.getChannelExtraInfo(true);
}
}
function handleChannelViewedEvent(msg) {
// Useful for when multiple devices have the app open to different channels
if (ChannelStore.getCurrentId() !== msg.channel_id && UserStore.getCurrentId() === msg.user_id) {
AsyncClient.getChannel(msg.channel_id);
}
}
var SocketStore = new SocketStoreClass();
SocketStore.dispatchToken = AppDispatcher.register((payload) => {
@@ -111,6 +283,7 @@ SocketStore.dispatchToken = AppDispatcher.register((payload) => {
switch (action.type) {
case ActionTypes.RECIEVED_MSG:
SocketStore.handleMessage(action.msg);
SocketStore.emitChange(action.msg);
break;

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

@@ -47,6 +47,18 @@ module.exports = {
SERVER_ACTION: null,
VIEW_ACTION: null
}),
SocketEvents: {
POSTED: 'posted',
POST_EDITED: 'post_edited',
POST_DELETED: 'post_deleted',
CHANNEL_VIEWED: 'channel_viewed',
NEW_USER: 'new_user',
USER_ADDED: 'user_added',
USER_REMOVED: 'user_removed',
TYPING: 'typing'
},
SPECIAL_MENTIONS: ['all', 'channel'],
CHARACTER_LIMIT: 4000,
IMAGE_TYPES: ['jpg', 'gif', 'bmp', 'png', 'jpeg'],

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

@@ -12,7 +12,7 @@
<div id='select_team_modal'></div>
<script>
window.setup_admin_console_page();
window.setup_admin_console_page({{ .Props }});
$(document).ready(function(){
$('[data-toggle="tooltip"]').tooltip();

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

@@ -64,6 +64,9 @@ func InitWeb() {
mainrouter.Handle("/signup/{service:[A-Za-z]+}/complete", api.AppHandlerIndependent(signupCompleteOAuth)).Methods("GET")
mainrouter.Handle("/admin_console", api.UserRequired(adminConsole)).Methods("GET")
mainrouter.Handle("/admin_console/", api.UserRequired(adminConsole)).Methods("GET")
mainrouter.Handle("/admin_console/{tab:[A-Za-z0-9-_]+}", api.UserRequired(adminConsole)).Methods("GET")
mainrouter.Handle("/admin_console/{tab:[A-Za-z0-9-_]+}/{team:[A-Za-z0-9-]*}", api.UserRequired(adminConsole)).Methods("GET")
mainrouter.Handle("/hooks/{id:[A-Za-z0-9]+}", api.ApiAppHandler(incomingWebhook)).Methods("POST")
@@ -699,7 +702,14 @@ func adminConsole(c *api.Context, w http.ResponseWriter, r *http.Request) {
return
}
params := mux.Vars(r)
activeTab := params["tab"]
teamId := params["team"]
page := NewHtmlTemplatePage("admin_console", "Admin Console")
page.Props["ActiveTab"] = activeTab
page.Props["TeamId"] = teamId
page.Render(c, w)
}