import { Button , ButtonGroup , Intent , Menu , Popover , Position , Tag } from '@blueprintjs/core' ; import _ from 'lodash' ; import * as React from 'react' ; import { connect } from 'react-redux' ; import ActionTypeMenuItem from 'components/ActionTypeMenuItem' ; import FlexContent from 'components/FlexContent' ; import FlexLayout from 'components/FlexLayout' ; import Help from 'components/Help' ; import NonIdealState from 'components/NonIdealState' ; import SettingsAction from 'components/SettingsAction' ; import SettingsInput from 'components/SettingsInput' ; import SettingsTable from 'components/SettingsTable' ; import SettingsView from 'components/SettingsView' ; import Action , { ActionPlaceholder , ActionType } from 'libs/Action' ; import { addAction , moveAction , removeAction , updateAction } from 'store/ducks/settings' ; import { ApplicationState } from 'store/reducers' ; import { getActions } from 'store/selectors/settings' ; import styled from 'styled' ; /**
 * Wrapper component.
 */ const Wrapper = styled ( SettingsView ) `
  display: flex;
  flex-direction: column;
  height: 100%;

  & > div,
  & > section {
    flex-shrink: 0;
  }
` ; /**
 * Notice component.
 */ const Notice = styled ( FlexLayout ) `
  display: flex;
  font-style: italic;
  margin-bottom: 20px;

  & > span {
    font-style: normal;
  }
` ; /**
 * Inline component.
 */ const Inline = styled . div `
  display: flex;
  margin-top: 10px;
` ; /**
 * InlineInput component.
 */ const InlineInput = styled ( SettingsInput ) `
  flex: 1;
  margin-right: 10px;
` ; /**
 * Placeholder component.
 */ const Placeholder = styled ( Tag ) `
  margin-left: 8px;
` ; /**
 * React State.
 */ const initialState = { editing : false , id : undefined as Optional < string > , name : '' , recipient : '' , text : '' , type : ActionType . Say , validation : Action . validate ( false , ActionType . Say , '' , '' , '' ) , } ; type State = Readonly < typeof initialState > ; /**
 * SettingsActions Component.
 */ class SettingsActions extends React . Component < Props , State > { public state : State = initialState ; return public render ( ) { const { editing , name , recipient , text , type } = this . state ; const { actions } = this . props ; return ( < Wrapper > < Notice > < FlexContent > Available text placeholders : { _ . map ( ActionPlaceholder , ( placeholder ) => this . renderPlaceholder ( placeholder ) ) } < /FlexContent> < Help > < p > Actions provide a mechanism to trigger various behaviors on users / mesages . < /p> < p > Actions are triggered either by using the context menu on the left of each messages or in the dialog appearing when clicking a username . < /p> < p > There are multiple action types : < /p> < ul > < li > < u > Say < /u>: Send the action text as a chat message. < /li> < li > < u > Whisper < /u>: Whisper the action text to a user. < /li> < li > < u > Prepare < /u>: Paste the action text in the chat input, ready to be shared. < /li> < li > < u > Open URL < /u>: Open the action text as an URL. < /li> < /ul> < /Help> < /Notice> < SettingsInput intent = { this . getIntent ( 'text' ) } value = { text } onChange = { this . onChangeText } placeholder = "Action text…" rightElement = { this . renderActionTypeMenu ( ) } / > < Inline > { type === ActionType . Whisper && ( < InlineInput intent = { this . getIntent ( 'recipient' ) } value = { recipient } onChange = { this . onChangeRecipient } placeholder = "Whisper recipient…" / > ) } < InlineInput intent = { this . getIntent ( 'name' ) } value = { name } onChange = { this . onChangeName } placeholder = "Action name…" / > { this . renderButtons ( ) } < /Inline> < SettingsTable > { actions . length > 0 ? ( _ . map ( actions , ( action , index ) => ( < SettingsAction canMoveDown = { actions . length > 1 && index < actions . length - 1 } canMoveUp = { actions . length > 1 && index > 0 } remove = { this . props . removeAction } move = { this . props . moveAction } isEditing = { editing } edit = { this . edit } action = { action } key = { action . id } / > ) ) ) : ( < NonIdealState small title = "No action yet!" details = "Try adding some above." / > ) } < /SettingsTable> < /Wrapper>); } private renderButtons ( ) { const { editing , validation } = this . state ; if ( editing ) { return ( < ButtonGroup > < Button text = "Cancel" intent = { Intent . NONE } onClick = { this . onClickCancel } / > < Button disabled = { ! validation . valid } text = "Edit" intent = { Intent . SUCCESS } onClick = { this . onClickSubmit } / > < /ButtonGroup>); } return < Button disabled = { ! validation . valid } text = "Add" intent = { Intent . PRIMARY } onClick = { this . onClickSubmit } / > ; } private renderPlaceholder ( placeholder ) { return ( < Placeholder key = { placeholder } > { '{{' } { placeholder } { '}}' } < /Placeholder>); } private renderActionTypeMenu ( ) { return ( < Popover content = { < Menu > { _ . map ( ActionType , ( type ) => ( < ActionTypeMenuItem key = { type } type = { type } onClick = { this . onChangeType } / > ) ) } < /Menu>} position={Position.BOTTOM_RIGHT} usePortal={false}> < Button minimal rightIcon = "caret-down" > { this . state . type } < /Button> < /Popover>); } private getIntent ( key ) { const { editing , validation } = this . state ; const intent = validation [ key ] ? Intent . SUCCESS : Intent . DANGER ; if ( editing ) { return intent ; } return this . state [ key ] . length === 0 ? Intent . NONE : intent ; } private edit = ( id ) => { const action = _ . find ( this . props . actions , [ 'id' , id ] ) ; if ( ! _ . isNil ( action ) ) { const recipient = _ . get ( action , 'recipient' , '' ) as string ; this . setState ( ( ) => ( { ... action , editing : true , recipient , validation : Action . validate ( true , action . type , action . text , action . name , recipient ) , } ) ) ; } } ; private onClickCancel = ( ) => { this . setState ( initialState ) ; } ; private onClickSubmit = ( ) => { const { editing , id , type , text , name , recipient } = this . state ; const action = new Action ( type , name , text , recipient , id ) ; if ( action . validate ( ) ) { if ( editing && ! _ . isNil ( id ) ) { this . props . updateAction ( id , action . serialize ( ) ) ; } else if ( ! editing ) { this . props . addAction ( action . serialize ( ) ) ; } this . setState ( ( { type : oldType } ) => ( { ... initialState , type : oldType } ) ) ; } } ; private onChangeType = ( type ) => { this . setState ( ( { editing , name , recipient , text } ) => ( { type , validation : Action . validate ( editing , type , text , name , recipient ) , } ) ) ; } ; private onChangeText = ( event ) => { const text = event . currentTarget . value ; this . setState ( ( { editing , name , recipient , type } ) => ( { text , validation : Action . validate ( editing , type , text , name , recipient ) , } ) ) ; } ; private onChangeRecipient = ( event ) => { const recipient = event . currentTarget . value ; this . setState ( ( { editing , name , text , type } ) => ( { recipient , validation : Action . validate ( editing , type , text , name , recipient ) , } ) ) ; } ; private onChangeName = ( event ) => { const name = event . currentTarget . value ; this . setState ( ( { editing , recipient , text , type } ) => ( { name , validation : Action . validate ( editing , type , text , name , recipient ) , } ) ) ; } ; } export default connect < StateProps , DispatchProps , { } , ApplicationState > ( ( state ) => ( { actions : getActions ( state ) , } ) , { addAction , moveAction , removeAction , updateAction } ) ( SettingsActions ) ; /**
 * React Props.
 */ interface StateProps { actions : ReturnType < typeof getActions > ; } /**
 * React Props.
 */ interface DispatchProps { addAction : typeof addAction ; moveAction : typeof moveAction ; removeAction : typeof removeAction ; updateAction : typeof updateAction ; } /**
 * React Props.
 */ type Props = StateProps & DispatchProps ;