Merge pull request #1174 from florianorben/member-popover
Add styling to channel member popover
Этот коммит содержится в:
@@ -1,13 +1,7 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
const AsyncClient = require('../utils/async_client.jsx');
|
|
||||||
const ChannelStore = require('../stores/channel_store.jsx');
|
|
||||||
const Constants = require('../utils/constants.jsx');
|
|
||||||
const Client = require('../utils/client.jsx');
|
|
||||||
const Modal = ReactBootstrap.Modal;
|
const Modal = ReactBootstrap.Modal;
|
||||||
const PreferenceStore = require('../stores/preference_store.jsx');
|
|
||||||
const TeamStore = require('../stores/team_store.jsx');
|
|
||||||
const UserStore = require('../stores/user_store.jsx');
|
const UserStore = require('../stores/user_store.jsx');
|
||||||
const Utils = require('../utils/utils.jsx');
|
const Utils = require('../utils/utils.jsx');
|
||||||
|
|
||||||
@@ -70,53 +64,25 @@ export default class MoreDirectChannels extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleShowDirectChannel(teammate, e) {
|
handleShowDirectChannel(teammate, e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
if (this.state.loadingDMChannel !== -1) {
|
if (this.state.loadingDMChannel !== -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
const channelName = Utils.getDirectChannelName(UserStore.getCurrentId(), teammate.id);
|
|
||||||
let channel = ChannelStore.getByName(channelName);
|
|
||||||
|
|
||||||
const preference = PreferenceStore.setPreference(Constants.Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, teammate.id, 'true');
|
|
||||||
AsyncClient.savePreferences([preference]);
|
|
||||||
|
|
||||||
if (channel) {
|
|
||||||
Utils.switchChannel(channel);
|
|
||||||
|
|
||||||
this.handleHide();
|
|
||||||
} else {
|
|
||||||
this.setState({loadingDMChannel: teammate.id});
|
this.setState({loadingDMChannel: teammate.id});
|
||||||
|
Utils.openDirectChannelToUser(
|
||||||
channel = {
|
teammate,
|
||||||
name: channelName,
|
(channel) => {
|
||||||
last_post_at: 0,
|
Utils.switchChannel(channel);
|
||||||
total_msg_count: 0,
|
|
||||||
type: 'D',
|
|
||||||
display_name: teammate.username,
|
|
||||||
teammate_id: teammate.id,
|
|
||||||
status: UserStore.getStatus(teammate.id)
|
|
||||||
};
|
|
||||||
|
|
||||||
Client.createDirectChannel(
|
|
||||||
channel,
|
|
||||||
teammate.id,
|
|
||||||
(data) => {
|
|
||||||
this.setState({loadingDMChannel: -1});
|
this.setState({loadingDMChannel: -1});
|
||||||
|
|
||||||
AsyncClient.getChannel(data.id);
|
|
||||||
Utils.switchChannel(data);
|
|
||||||
|
|
||||||
this.handleHide();
|
this.handleHide();
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
this.setState({loadingDMChannel: -1});
|
this.setState({loadingDMChannel: -1});
|
||||||
window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/' + channelName;
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
handleUserChange() {
|
handleUserChange() {
|
||||||
this.setState({users: this.getUsersFromStore()});
|
this.setState({users: this.getUsersFromStore()});
|
||||||
|
|||||||
@@ -3,9 +3,23 @@
|
|||||||
|
|
||||||
var UserStore = require('../stores/user_store.jsx');
|
var UserStore = require('../stores/user_store.jsx');
|
||||||
var Popover = ReactBootstrap.Popover;
|
var Popover = ReactBootstrap.Popover;
|
||||||
var OverlayTrigger = ReactBootstrap.OverlayTrigger;
|
var Overlay = ReactBootstrap.Overlay;
|
||||||
|
const Utils = require('../utils/utils.jsx');
|
||||||
|
|
||||||
|
const ChannelStore = require('../stores/channel_store.jsx');
|
||||||
|
|
||||||
export default class PopoverListMembers extends React.Component {
|
export default class PopoverListMembers extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.handleShowDirectChannel = this.handleShowDirectChannel.bind(this);
|
||||||
|
this.closePopover = this.closePopover.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
this.setState({showPopover: false});
|
||||||
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const originalLeave = $.fn.popover.Constructor.prototype.leave;
|
const originalLeave = $.fn.popover.Constructor.prototype.leave;
|
||||||
$.fn.popover.Constructor.prototype.leave = function onLeave(obj) {
|
$.fn.popover.Constructor.prototype.leave = function onLeave(obj) {
|
||||||
@@ -27,12 +41,36 @@ export default class PopoverListMembers extends React.Component {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleShowDirectChannel(teammate, e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
Utils.openDirectChannelToUser(
|
||||||
|
teammate,
|
||||||
|
(channel, channelAlreadyExisted) => {
|
||||||
|
Utils.switchChannel(channel);
|
||||||
|
if (channelAlreadyExisted) {
|
||||||
|
this.closePopover();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
this.closePopover();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
closePopover() {
|
||||||
|
this.setState({showPopover: false});
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let popoverHtml = [];
|
let popoverHtml = [];
|
||||||
let count = 0;
|
let count = 0;
|
||||||
let countText = '-';
|
let countText = '-';
|
||||||
const members = this.props.members;
|
const members = this.props.members;
|
||||||
const teamMembers = UserStore.getProfilesUsernameMap();
|
const teamMembers = UserStore.getProfilesUsernameMap();
|
||||||
|
const currentUserId = UserStore.getCurrentId();
|
||||||
|
const ch = ChannelStore.getCurrent();
|
||||||
|
|
||||||
if (members && teamMembers) {
|
if (members && teamMembers) {
|
||||||
members.sort((a, b) => {
|
members.sort((a, b) => {
|
||||||
@@ -40,14 +78,75 @@ export default class PopoverListMembers extends React.Component {
|
|||||||
});
|
});
|
||||||
|
|
||||||
members.forEach((m, i) => {
|
members.forEach((m, i) => {
|
||||||
|
const details = [];
|
||||||
|
|
||||||
|
const fullName = Utils.getFullName(m);
|
||||||
|
if (fullName) {
|
||||||
|
details.push(
|
||||||
|
<span
|
||||||
|
key={`${m.id}__full-name`}
|
||||||
|
className='full-name'
|
||||||
|
>
|
||||||
|
{fullName}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m.nickname) {
|
||||||
|
const separator = fullName ? ' - ' : '';
|
||||||
|
details.push(
|
||||||
|
<span
|
||||||
|
key={`${m.nickname}__nickname`}
|
||||||
|
>
|
||||||
|
{separator + m.nickname}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let button = '';
|
||||||
|
if (currentUserId !== m.id && ch.type !== 'D') {
|
||||||
|
button = (
|
||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
className='btn btn-primary btn-message'
|
||||||
|
onClick={(e) => this.handleShowDirectChannel(m, e)}
|
||||||
|
>
|
||||||
|
{'Message'}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (teamMembers[m.username] && teamMembers[m.username].delete_at <= 0) {
|
if (teamMembers[m.username] && teamMembers[m.username].delete_at <= 0) {
|
||||||
popoverHtml.push(
|
popoverHtml.push(
|
||||||
<div
|
<div
|
||||||
className='text--nowrap'
|
className='text--nowrap'
|
||||||
key={'popover-member-' + i}
|
key={'popover-member-' + i}
|
||||||
>
|
>
|
||||||
|
|
||||||
|
<img
|
||||||
|
className='profile-img pull-left'
|
||||||
|
width='38'
|
||||||
|
height='38'
|
||||||
|
src={`/api/v1/users/${m.id}/image?time=${m.update_at}&${Utils.getSessionIndex()}`}
|
||||||
|
/>
|
||||||
|
<div className='pull-left'>
|
||||||
|
<div
|
||||||
|
className='more-name'
|
||||||
|
>
|
||||||
{m.username}
|
{m.username}
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
className='more-description'
|
||||||
|
>
|
||||||
|
{details}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className='pull-right profile-action'
|
||||||
|
>
|
||||||
|
{button}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
@@ -61,20 +160,12 @@ export default class PopoverListMembers extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<OverlayTrigger
|
<div>
|
||||||
trigger='click'
|
<div
|
||||||
placement='bottom'
|
id='member_popover'
|
||||||
rootClose={true}
|
ref='member_popover_target'
|
||||||
overlay={
|
onClick={(e) => this.setState({popoverTarget: e.target, showPopover: !this.state.showPopover})}
|
||||||
<Popover
|
|
||||||
title='Members'
|
|
||||||
id='member-list-popover'
|
|
||||||
>
|
>
|
||||||
{popoverHtml}
|
|
||||||
</Popover>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div id='member_popover'>
|
|
||||||
<div>
|
<div>
|
||||||
{countText}
|
{countText}
|
||||||
<span
|
<span
|
||||||
@@ -83,7 +174,23 @@ export default class PopoverListMembers extends React.Component {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</OverlayTrigger>
|
<Overlay
|
||||||
|
rootClose={true}
|
||||||
|
onHide={this.closePopover}
|
||||||
|
show={this.state.showPopover}
|
||||||
|
target={() => this.state.popoverTarget}
|
||||||
|
placement='bottom'
|
||||||
|
>
|
||||||
|
<Popover
|
||||||
|
title='Members'
|
||||||
|
id='member-list-popover'
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
{popoverHtml}
|
||||||
|
</div>
|
||||||
|
</Popover>
|
||||||
|
</Overlay>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ var PreferenceStore = require('../stores/preference_store.jsx');
|
|||||||
var TeamStore = require('../stores/team_store.jsx');
|
var TeamStore = require('../stores/team_store.jsx');
|
||||||
var Constants = require('../utils/constants.jsx');
|
var Constants = require('../utils/constants.jsx');
|
||||||
var ActionTypes = Constants.ActionTypes;
|
var ActionTypes = Constants.ActionTypes;
|
||||||
|
var Client = require('./client.jsx');
|
||||||
var AsyncClient = require('./async_client.jsx');
|
var AsyncClient = require('./async_client.jsx');
|
||||||
var client = require('./client.jsx');
|
var client = require('./client.jsx');
|
||||||
var Autolinker = require('autolinker');
|
var Autolinker = require('autolinker');
|
||||||
@@ -1009,3 +1010,44 @@ export function windowWidth() {
|
|||||||
export function windowHeight() {
|
export function windowHeight() {
|
||||||
return $(window).height();
|
return $(window).height();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function openDirectChannelToUser(user, successCb, errorCb) {
|
||||||
|
const channelName = this.getDirectChannelName(UserStore.getCurrentId(), user.id);
|
||||||
|
let channel = ChannelStore.getByName(channelName);
|
||||||
|
|
||||||
|
const preference = PreferenceStore.setPreference(Constants.Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, user.id, 'true');
|
||||||
|
AsyncClient.savePreferences([preference]);
|
||||||
|
|
||||||
|
if (channel) {
|
||||||
|
if ($.isFunction(successCb)) {
|
||||||
|
successCb(channel, true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
channel = {
|
||||||
|
name: channelName,
|
||||||
|
last_post_at: 0,
|
||||||
|
total_msg_count: 0,
|
||||||
|
type: 'D',
|
||||||
|
display_name: user.username,
|
||||||
|
teammate_id: user.id,
|
||||||
|
status: UserStore.getStatus(user.id)
|
||||||
|
};
|
||||||
|
|
||||||
|
Client.createDirectChannel(
|
||||||
|
channel,
|
||||||
|
user.id,
|
||||||
|
(data) => {
|
||||||
|
AsyncClient.getChannel(data.id);
|
||||||
|
if ($.isFunction(successCb)) {
|
||||||
|
successCb(data, false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/' + channelName;
|
||||||
|
if ($.isFunction(errorCb)) {
|
||||||
|
errorCb();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -61,3 +61,36 @@
|
|||||||
@include opacity(1);
|
@include opacity(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#member-list-popover {
|
||||||
|
max-width: initial;
|
||||||
|
.popover-content > div {
|
||||||
|
max-height: 350px;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
> div {
|
||||||
|
border-bottom: 1px solid rgba(51,51,51,0.1);
|
||||||
|
padding: 8px 8px 8px 15px;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: content-box;
|
||||||
|
@include clearfix;
|
||||||
|
.profile-img {
|
||||||
|
border-radius: 50px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
.more-name {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.95em;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.more-description {
|
||||||
|
@include opacity(0.7);
|
||||||
|
}
|
||||||
|
.profile-action {
|
||||||
|
margin-left: 8px;
|
||||||
|
margin-right: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user