Skip to content

Commit

Permalink
Add todo button on team sidebar (#54)
Browse files Browse the repository at this point in the history
* Add todo button on team sidebar

Buttons are - Your todos, requested todos, assigned todos, refresh

Resolves issue #24

* Fix lint errors

* Open correct RHS state on todo button click

Change icons
Fix bugs

* Remove the unnecessary componentDidUpdate function

* remove refresh button

* add websocket reconnect handler

Co-authored-by: mattermod <[email protected]>
  • Loading branch information
Ashniu123 and mattermod authored Jun 4, 2020
1 parent 3ced534 commit 6b53adb
Show file tree
Hide file tree
Showing 11 changed files with 236 additions and 4 deletions.
1 change: 1 addition & 0 deletions webapp/src/action_types.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export const GET_ISSUES = pluginId + '_get_issues';
export const GET_OUT_ISSUES = pluginId + '_get_out_issues';
export const GET_IN_ISSUES = pluginId + '_get_in_issues';
export const RECEIVED_SHOW_RHS_ACTION = pluginId + '_show_rhs';
export const UPDATE_RHS_STATE = pluginId + '_update_rhs_state';
9 changes: 8 additions & 1 deletion webapp/src/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {Client4} from 'mattermost-redux/client';
import * as UserActions from 'mattermost-redux/actions/users';

import {id as pluginId} from './manifest';
import {OPEN_ROOT_MODAL, CLOSE_ROOT_MODAL, RECEIVED_SHOW_RHS_ACTION, GET_ISSUES, GET_IN_ISSUES, GET_OUT_ISSUES} from './action_types';
import {OPEN_ROOT_MODAL, CLOSE_ROOT_MODAL, RECEIVED_SHOW_RHS_ACTION, GET_ISSUES, GET_IN_ISSUES, GET_OUT_ISSUES, UPDATE_RHS_STATE} from './action_types';

export const openRootModal = (postID) => (dispatch) => {
dispatch({
Expand All @@ -29,6 +29,13 @@ export function setShowRHSAction(showRHSPluginAction) {
};
}

export function updateRhsState(rhsState) {
return {
type: UPDATE_RHS_STATE,
state: rhsState,
};
}

// TODO: Move this into mattermost-redux or mattermost-webapp.
export const getPluginServerRoute = (state) => {
const config = getConfig(state);
Expand Down
29 changes: 29 additions & 0 deletions webapp/src/components/sidebar_buttons/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';

import {list, updateRhsState} from '../../actions';

import SidebarButtons from './sidebar_buttons.jsx';

function mapStateToProps(state) {
return {
issues: state['plugins-com.mattermost.plugin-todo'].issues,
inIssues: state['plugins-com.mattermost.plugin-todo'].inIssues,
outIssues: state['plugins-com.mattermost.plugin-todo'].outIssues,
showRHSPlugin: state['plugins-com.mattermost.plugin-todo'].rhsPluginAction,
};
}

function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
list,
updateRhsState,
}, dispatch),
};
}

export default connect(mapStateToProps, mapDispatchToProps)(SidebarButtons);
125 changes: 125 additions & 0 deletions webapp/src/components/sidebar_buttons/sidebar_buttons.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import React from 'react';
import {Tooltip, OverlayTrigger} from 'react-bootstrap';
import PropTypes from 'prop-types';
import {makeStyleFromTheme, changeOpacity} from 'mattermost-redux/utils/theme_utils';

import {RHSStates} from '../../constants';

export default class SidebarButtons extends React.PureComponent {
static propTypes = {
theme: PropTypes.object.isRequired,
isTeamSidebar: PropTypes.bool,
showRHSPlugin: PropTypes.func.isRequired,
issues: PropTypes.arrayOf(PropTypes.object),
inIssues: PropTypes.arrayOf(PropTypes.object),
outIssues: PropTypes.arrayOf(PropTypes.object),
actions: PropTypes.shape({
list: PropTypes.func.isRequired,
updateRhsState: PropTypes.func.isRequired,
}).isRequired,
};

constructor(props) {
super(props);

this.state = {
refreshing: false,
};
}

openRHS = (rhsState) => {
this.props.actions.updateRhsState(rhsState);
this.props.showRHSPlugin();
}

render() {
const style = getStyle(this.props.theme);
const isTeamSidebar = this.props.isTeamSidebar;

let container = style.containerHeader;
let button = style.buttonHeader;
let placement = 'bottom';
if (isTeamSidebar) {
placement = 'right';
button = style.buttonTeam;
container = style.containerTeam;
}

const issues = this.props.issues || [];
const inIssues = this.props.inIssues || [];
const outIssues = this.props.outIssues || [];

return (
<div style={container}>
<OverlayTrigger
key='myTodosLink'
placement={placement}
overlay={<Tooltip id='myTodosTooltip'>{'Your Todos'}</Tooltip>}
>
<a
style={button}
onClick={() => this.openRHS(RHSStates.InListName)}
>
<i className='icon icon-check'/>
{' ' + issues.length}
</a>
</OverlayTrigger>
<OverlayTrigger
key='incomingTodosLink'
placement={placement}
overlay={<Tooltip id='incomingTodosTooltip'>{'Incoming Todos'}</Tooltip>}
>
<a
onClick={() => this.openRHS(RHSStates.InListName)}
style={button}
>
<i className='icon icon-arrow-down'/>
{' ' + inIssues.length}
</a>
</OverlayTrigger>
<OverlayTrigger
key='outgoingTodosLink'
placement={placement}
overlay={<Tooltip id='outgoingTodosTooltip'>{'Outgoing Todos'}</Tooltip>}
>
<a
onClick={() => this.openRHS(RHSStates.OutListName)}
style={button}
>
<i className='icon icon-arrow-up'/>
{' ' + outIssues.length}
</a>
</OverlayTrigger>
</div>
);
}
}

const getStyle = makeStyleFromTheme((theme) => {
return {
buttonTeam: {
color: changeOpacity(theme.sidebarText, 0.6),
display: 'block',
marginBottom: '10px',
width: '100%',
},
buttonHeader: {
color: changeOpacity(theme.sidebarText, 0.6),
textAlign: 'center',
cursor: 'pointer',
},
containerHeader: {
marginTop: '10px',
marginBottom: '5px',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-around',
padding: '0 10px',
},
containerTeam: {
},
};
});
1 change: 1 addition & 0 deletions webapp/src/components/sidebar_right/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ function mapStateToProps(state) {
inTodos: getInIssues(state),
outTodos: getOutIssues(state),
siteURL: getSiteURL(),
rhsState: state['plugins-com.mattermost.plugin-todo'].rhsState,
};
}

Expand Down
9 changes: 8 additions & 1 deletion webapp/src/components/sidebar_right/sidebar_right.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export default class SidebarRight extends React.PureComponent {
outTodos: PropTypes.arrayOf(PropTypes.object),
theme: PropTypes.object.isRequired,
siteURL: PropTypes.string.isRequired,
rhsState: PropTypes.string,
actions: PropTypes.shape({
remove: PropTypes.func.isRequired,
complete: PropTypes.func.isRequired,
Expand All @@ -58,7 +59,7 @@ export default class SidebarRight extends React.PureComponent {
super(props);

this.state = {
list: MyListName,
list: props.rhsState || MyListName,
showInbox: true,
showMy: true,
};
Expand All @@ -84,6 +85,12 @@ export default class SidebarRight extends React.PureComponent {
this.props.actions.list(false, 'out');
}

componentDidUpdate(prevProps) {
if (prevProps.rhsState !== this.props.rhsState) {
this.openList(this.props.rhsState);
}
}

getInIssues() {
return this.props.inTodos.length;
}
Expand Down
15 changes: 15 additions & 0 deletions webapp/src/components/team_sidebar/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import {connect} from 'react-redux';

import TeamSidebar from './team_sidebar.jsx';

function mapStateToProps(state) {
const members = state.entities.teams.myMembers || {};
return {
show: Object.keys(members).length > 1,
};
}

export default connect(mapStateToProps)(TeamSidebar);
27 changes: 27 additions & 0 deletions webapp/src/components/team_sidebar/team_sidebar.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import React from 'react';
import PropTypes from 'prop-types';

import SidebarButtons from '../sidebar_buttons';

export default class TeamSidebar extends React.PureComponent {
static propTypes = {
show: PropTypes.bool.isRequired,
theme: PropTypes.object.isRequired,
};

render() {
if (!this.props.show) {
return null;
}

return (
<SidebarButtons
theme={this.props.theme}
isTeamSidebar={true}
/>
);
}
}
8 changes: 7 additions & 1 deletion webapp/src/constants.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,18 @@ export const Constants = {
DASH: ['-', 189],
PERIOD: ['.', 190],
FORWARD_SLASH: ['/', 191],
TILDE: ['~', 192], // coudnt find the key or even get code from browser - no reference in code as of now
TILDE: ['~', 192], // couldn't find the key or even get code from browser - no reference in code as of now
OPEN_BRACKET: ['[', 219],
BACK_SLASH: ['\\', 220],
CLOSE_BRACKET: [']', 221],
COMPOSING: ['Composing', 229],
},
SETTING_BUTTONS_TEAM: 'team',
};

export const RHSStates = {
InListName: 'my',
OutListName: 'out',
};

export default Constants;
4 changes: 4 additions & 0 deletions webapp/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import SidebarRight from './components/sidebar_right';
import { openRootModal, list, setShowRHSAction } from './actions';
import reducer from './reducer';
import PostTypeTodo from './components/post_type_todo';
import TeamSidebar from './components/team_sidebar';

let activityFunc;
let lastActivityTime = Number.MAX_SAFE_INTEGER;
Expand All @@ -18,6 +19,8 @@ export default class Plugin {
registry.registerRootComponent(Root);
registry.registerReducer(reducer);

registry.registerBottomTeamSidebarComponent(TeamSidebar);

registry.registerPostDropdownMenuAction(
'Add Todo',
(postID) => store.dispatch(openRootModal(postID)),
Expand All @@ -35,6 +38,7 @@ export default class Plugin {
};

registry.registerWebSocketEventHandler(`custom_${pluginId}_refresh`, refresh);
registry.registerReconnectHandler(refresh);

store.dispatch(list(true));
store.dispatch(list(false, 'in'));
Expand Down
12 changes: 11 additions & 1 deletion webapp/src/reducer.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {combineReducers} from 'redux';

import {OPEN_ROOT_MODAL, CLOSE_ROOT_MODAL, GET_ISSUES, GET_IN_ISSUES, GET_OUT_ISSUES, RECEIVED_SHOW_RHS_ACTION} from './action_types';
import {OPEN_ROOT_MODAL, CLOSE_ROOT_MODAL, GET_ISSUES, GET_IN_ISSUES, GET_OUT_ISSUES, RECEIVED_SHOW_RHS_ACTION, UPDATE_RHS_STATE} from './action_types';

const rootModalVisible = (state = false, action) => {
switch (action.type) {
Expand Down Expand Up @@ -60,11 +60,21 @@ function rhsPluginAction(state = null, action) {
}
}

function rhsState(state = '', action) {
switch (action.type) {
case UPDATE_RHS_STATE:
return action.state;
default:
return state;
}
}

export default combineReducers({
rootModalVisible,
postID,
issues,
inIssues,
outIssues,
rhsState,
rhsPluginAction,
});

0 comments on commit 6b53adb

Please sign in to comment.