350 lines
10 KiB
React
350 lines
10 KiB
React
|
import React from 'react';
|
||
|
import axios from 'axios';
|
||
|
import '!style-loader!css-loader!emoji-mart/css/emoji-mart.css';
|
||
|
import { Picker } from 'emoji-mart';
|
||
|
import Emoji from 'emoji-mart/dist/components/emoji/emoji.js';
|
||
|
import RichCheckbox from '../common/RichCheckbox.jsx';
|
||
|
import RichMultiSelect from '../common/RichMultiSelect.jsx';
|
||
|
import RichSelect from '../common/RichSelect.jsx';
|
||
|
import Variables from '../common/Variables.jsx';
|
||
|
import Help from '../common/Help.jsx';
|
||
|
|
||
|
export default class ResponseModal extends React.Component {
|
||
|
state = {
|
||
|
channels: [],
|
||
|
formData: this.defaultFormData,
|
||
|
isPickerOpen: false,
|
||
|
contentChars: 0,
|
||
|
contentLimit: false,
|
||
|
nameChars: 0,
|
||
|
nameLimit: false,
|
||
|
}
|
||
|
|
||
|
get defaultFormData() {
|
||
|
return {
|
||
|
command: '',
|
||
|
response: '',
|
||
|
type: 'message',
|
||
|
allowedChannels: [],
|
||
|
ignoredChannels: [],
|
||
|
reactions: [],
|
||
|
wildcard: false,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
async componentWillMount() {
|
||
|
this.setState({
|
||
|
channels: this.props.channels || [],
|
||
|
emojis: this.props.emojis || [],
|
||
|
formData: this.props.command ? this.props.command : this.defaultFormData,
|
||
|
});
|
||
|
}
|
||
|
|
||
|
async componentWillReceiveProps(props) {
|
||
|
this.setState({
|
||
|
channels: props.channels || [],
|
||
|
emojis: props.emojis || [],
|
||
|
formData: props.command ? props.command : this.defaultFormData,
|
||
|
});
|
||
|
}
|
||
|
|
||
|
setFormData(key, val) {
|
||
|
const formData = this.state.formData;
|
||
|
formData[key] = val;
|
||
|
return this.setState({
|
||
|
formData,
|
||
|
contentChars: formData.response.length,
|
||
|
nameChars: formData.command.length
|
||
|
});
|
||
|
}
|
||
|
|
||
|
handleInput(type, event) {
|
||
|
if (type == 'response') {
|
||
|
var input = event.target.value;
|
||
|
this.setState({
|
||
|
contentChars: input.length
|
||
|
});
|
||
|
if (input.length >= 2000) {
|
||
|
this.setState({
|
||
|
contentLimit: true
|
||
|
});
|
||
|
}
|
||
|
else {
|
||
|
this.setState({
|
||
|
contentLimit: false
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
this.setFormData(type, event.target.value);
|
||
|
}
|
||
|
|
||
|
handleCommandName = (event) => {
|
||
|
let value = event.target.value;
|
||
|
|
||
|
if (value.length > 72) {
|
||
|
return;
|
||
|
}
|
||
|
this.setState({
|
||
|
nameChars: value.length
|
||
|
});
|
||
|
if (value.length >= 72) {
|
||
|
this.setState({
|
||
|
nameChars: 72,
|
||
|
nameLimit: true
|
||
|
});
|
||
|
}
|
||
|
else {
|
||
|
this.setState({
|
||
|
nameLimit: false
|
||
|
})
|
||
|
}
|
||
|
this.setFormData('command', event.target.value);
|
||
|
}
|
||
|
|
||
|
handleMultiSelect(type, props, selectedOptions) {
|
||
|
this.setFormData(type, selectedOptions.map(o => ({ id: o.value, name: o.label })));
|
||
|
}
|
||
|
|
||
|
handleCheckbox(type, identifier, isEnabled) {
|
||
|
this.setFormData(type, isEnabled);
|
||
|
}
|
||
|
|
||
|
addReaction = (emoji) => {
|
||
|
if (emoji.custom) {
|
||
|
const customEmoji = this.state.emojis.find(e => e.name === emoji.name);
|
||
|
emoji._id = customEmoji.id;
|
||
|
}
|
||
|
|
||
|
const { formData } = this.state;
|
||
|
formData.reactions = formData.reactions || [];
|
||
|
formData.reactions.push(emoji);
|
||
|
|
||
|
this.setState({ formData, isPickerOpen: false });
|
||
|
}
|
||
|
|
||
|
deleteReaction(reaction) {
|
||
|
const { formData } = this.state;
|
||
|
const index = formData.reactions.findIndex(r => r.id === reaction.id);
|
||
|
formData.reactions.splice(index, 1);
|
||
|
|
||
|
this.setState({ formData });
|
||
|
}
|
||
|
|
||
|
addHandler = () => {
|
||
|
const command = this.state.formData;
|
||
|
const url = `/api/server/${this.props.match.params.id}/autoresponder/create`;
|
||
|
const data = { command };
|
||
|
|
||
|
// Max length
|
||
|
if (command.command.length > 72) return _showError('Command name cannot be over 72 characters.');
|
||
|
if (command.response.length > 2000) return _showError('Respones cannot be over 2,000 characters.');
|
||
|
|
||
|
const test = command.response.replace(/ *\{[^)]*\} */g, '');
|
||
|
if (test.length > 2000) return _showError('Response cannot be over 2,000 characters.');
|
||
|
|
||
|
axios.post(url, data).then((res) => {
|
||
|
if (res.status === 200) {
|
||
|
_showSuccess(`Added Response ${command.command}`);
|
||
|
|
||
|
try {
|
||
|
this.props.onClose(command);
|
||
|
} catch (err) {
|
||
|
return;
|
||
|
}
|
||
|
} else {
|
||
|
_showError('An error occurred.');
|
||
|
}
|
||
|
}).catch((err) => {
|
||
|
_showError('Something went wrong.');
|
||
|
});
|
||
|
}
|
||
|
|
||
|
saveHandler = () => {
|
||
|
const command = this.state.formData;
|
||
|
const url = `/api/server/${this.props.match.params.id}/autoresponder/edit`;
|
||
|
const data = { command };
|
||
|
|
||
|
if (command.response.length > 2000) return _showError('Respones cannot be over 2,000 characters.');
|
||
|
|
||
|
const test = command.response.replace(/ *\{[^)]*\} */g, '');
|
||
|
if (test.length > 2000) return _showError('Response cannot be over 2,000 characters.');
|
||
|
|
||
|
axios.post(url, data).then((res) => {
|
||
|
if (res.status === 200) {
|
||
|
_showSuccess(`Updated Response ${command.command}`);
|
||
|
|
||
|
this.props.onClose(command);
|
||
|
} else {
|
||
|
_showError('An error occurred.');
|
||
|
}
|
||
|
}).catch((err) => {
|
||
|
if (err) {
|
||
|
_showError('Something went wrong.');
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
togglePicker = () => {
|
||
|
this.setState({ isPickerOpen: !this.state.isPickerOpen });
|
||
|
}
|
||
|
|
||
|
render() {
|
||
|
const { formData } = this.state;
|
||
|
const disableName = !!this.props.command;
|
||
|
|
||
|
let channels = this.state.channels.filter(c => (c.type === 0 || c.type === 4));
|
||
|
|
||
|
const channelOptions = channels.map(c => ({ value: c.id, label: c.name }));
|
||
|
|
||
|
let allowedChannels = this.state.formData.allowedChannels || [];
|
||
|
let ignoredChannels = this.state.formData.ignoredChannels || [];
|
||
|
|
||
|
allowedChannels = channels.filter(c => allowedChannels.find(i => i.id === c.id));
|
||
|
ignoredChannels = channels.filter(c => ignoredChannels.find(i => i.id === c.id));
|
||
|
|
||
|
const customs = this.state.emojis.map(emoji => {
|
||
|
return {
|
||
|
name: emoji.name,
|
||
|
short_names: [emoji.name],
|
||
|
imageUrl: `https://cdn.discordapp.com/emojis/${emoji.id}.${emoji.animated ? 'gif' : 'png'}?v=1`,
|
||
|
};
|
||
|
});
|
||
|
|
||
|
// const { reactions } = formData;
|
||
|
// reactions = reactions.map(r => {
|
||
|
// // r.imageUrl = `https://cdn.discordapp.com/emojis/${emoji.id}.${emoji.animated ? 'gif' : 'png'}?v=1`,
|
||
|
// })
|
||
|
|
||
|
const canAddReactions = !formData.reactions || formData.reactions.length < 3;
|
||
|
|
||
|
const categories = [
|
||
|
'search',
|
||
|
'recent',
|
||
|
'custom',
|
||
|
'people',
|
||
|
'nature',
|
||
|
'foods',
|
||
|
'activity',
|
||
|
'places',
|
||
|
'objects',
|
||
|
'symbols',
|
||
|
'flags',
|
||
|
];
|
||
|
|
||
|
const contentClass = this.state.contentLimit ? "over2k" : "";
|
||
|
const nameClass = this.state.nameLimit ? "over2k" : "";
|
||
|
|
||
|
return (<div className='settings-content'>
|
||
|
<div className='cc-add'>
|
||
|
<h3 className='title is-5'>{this.props.command ? 'Edit' : 'Add'} Response</h3>
|
||
|
<label className='label'>Command - <span className={nameClass}>{this.state.nameChars}</span> Character{this.state.nameChars == 1 ? '' : 's'}</label>
|
||
|
<p className='cc-name control'>
|
||
|
<input className='input' type='text' value={formData.command} disabled={disableName} onChange={this.handleCommandName} />
|
||
|
</p>
|
||
|
<p className='control'>
|
||
|
<input id='messageType' className='radio'
|
||
|
type='radio'
|
||
|
name='type'
|
||
|
value='message'
|
||
|
onChange={this.handleInput.bind(this, 'type')}
|
||
|
checked={formData.type === 'message'} />
|
||
|
<label htmlFor='messageType'>
|
||
|
Message Response
|
||
|
</label>
|
||
|
<Help text='Auto responds with a message.' />
|
||
|
<input id='reactionType' className='radio'
|
||
|
type='radio'
|
||
|
name='type'
|
||
|
value='reaction'
|
||
|
onChange={this.handleInput.bind(this, 'type')}
|
||
|
checked={formData.type === 'reaction'} />
|
||
|
<label htmlFor='reactionType'>
|
||
|
Reaction Response
|
||
|
</label>
|
||
|
<Help text='Auto responds by adding a reaction to the message. Note: Dyno requires the "Add Reactions" permission to be able to do this.' />
|
||
|
</p>
|
||
|
{formData.type === 'message' && (
|
||
|
<p className='control'>
|
||
|
<label className='label'>Response - <span className={contentClass}>{this.state.contentChars}</span> Character{this.state.contentChars == 1 ? '' : 's'}</label> {/*- */}
|
||
|
<textarea className='input cc-response' name="response" value={formData.response} onChange={this.handleInput.bind(this, 'response')}></textarea>
|
||
|
</p>
|
||
|
)}
|
||
|
{formData.type === 'reaction' && (
|
||
|
<div className='reaction-picker'>
|
||
|
{formData.reactions && formData.reactions.map(reaction => (
|
||
|
<div key={reaction.id} className='reaction'>
|
||
|
{reaction.custom ?
|
||
|
<span className='custom' style={{
|
||
|
background: `url(${reaction.imageUrl}) no-repeat center`,
|
||
|
backgroundSize: `20px`,
|
||
|
}}></span> :
|
||
|
<Emoji set='twitter' sheetSize={64} emoji={reaction.colons} />
|
||
|
}
|
||
|
<a className='icon delete-reaction' onClick={this.deleteReaction.bind(this, reaction)}>
|
||
|
<i className='fa fa-times'></i>
|
||
|
</a>
|
||
|
</div>
|
||
|
))}
|
||
|
{canAddReactions && (
|
||
|
<div className='reaction'>
|
||
|
<a className='is-info' onClick={this.togglePicker}>+</a>
|
||
|
</div>
|
||
|
)}
|
||
|
{this.state.isPickerOpen && (
|
||
|
<Picker
|
||
|
set='twitter'
|
||
|
title='Pick an emoji'
|
||
|
custom={customs}
|
||
|
include={categories}
|
||
|
showPreview={false}
|
||
|
// emojiTooltip={true}
|
||
|
onClick={this.addReaction} />
|
||
|
)}
|
||
|
</div>
|
||
|
)}
|
||
|
<RichMultiSelect
|
||
|
module={module}
|
||
|
setting='allowedChannels'
|
||
|
friendlyName='Allowed Channel'
|
||
|
label='Allowed Channels'
|
||
|
placeholder='Allowed Channels'
|
||
|
defaultValue={allowedChannels}
|
||
|
options={channelOptions}
|
||
|
helpText='Dyno will only respond in these channels.'
|
||
|
onChange={this.handleMultiSelect.bind(this, 'allowedChannels')} />
|
||
|
<RichMultiSelect
|
||
|
module={module}
|
||
|
setting='ignoredChannels'
|
||
|
friendlyName='Ignored Channel'
|
||
|
label='Ignored Channels'
|
||
|
placeholder='Ignored Channels'
|
||
|
defaultValue={ignoredChannels}
|
||
|
options={channelOptions}
|
||
|
helpText='Dyno will not respond in these channels.'
|
||
|
onChange={this.handleMultiSelect.bind(this, 'ignoredChannels')} />
|
||
|
<RichCheckbox
|
||
|
text='Wildcard'
|
||
|
onChange={this.handleCheckbox.bind(this, 'wildcard')}
|
||
|
helpText='Auto responder will search for the command anywhere in the message.'
|
||
|
defaultValue={formData.wildcard} />
|
||
|
<div className='control'>
|
||
|
<Variables />
|
||
|
</div>
|
||
|
{this.props.command ? (
|
||
|
<div className='cc-buttons'>
|
||
|
<span className='control'>
|
||
|
<a className='button edit-command is-info' onClick={this.saveHandler}>Save Response</a>
|
||
|
</span>
|
||
|
</div>
|
||
|
) : (
|
||
|
<div className='cc-buttons'>
|
||
|
<span className='control'>
|
||
|
<a className='button add-command is-info' onClick={this.addHandler}>Add Response</a>
|
||
|
</span>
|
||
|
</div>
|
||
|
)}
|
||
|
</div>
|
||
|
</div>);
|
||
|
}
|
||
|
}
|