summaryrefslogtreecommitdiffstats
path: root/webapp/components/emoji
diff options
context:
space:
mode:
Diffstat (limited to 'webapp/components/emoji')
-rw-r--r--webapp/components/emoji/components/add_emoji.jsx316
-rw-r--r--webapp/components/emoji/components/delete_emoji_modal.jsx50
-rw-r--r--webapp/components/emoji/components/emoji_list.jsx228
-rw-r--r--webapp/components/emoji/components/emoji_list_item.jsx103
4 files changed, 0 insertions, 697 deletions
diff --git a/webapp/components/emoji/components/add_emoji.jsx b/webapp/components/emoji/components/add_emoji.jsx
deleted file mode 100644
index 936e15d6b..000000000
--- a/webapp/components/emoji/components/add_emoji.jsx
+++ /dev/null
@@ -1,316 +0,0 @@
-// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import React from 'react';
-import PropTypes from 'prop-types';
-
-import * as EmojiActions from 'actions/emoji_actions.jsx';
-import EmojiStore from 'stores/emoji_store.jsx';
-
-import BackstageHeader from 'components/backstage/components/backstage_header.jsx';
-import {FormattedMessage} from 'react-intl';
-import FormError from 'components/form_error.jsx';
-import {Link} from 'react-router';
-import SpinnerButton from 'components/spinner_button.jsx';
-
-export default class AddEmoji extends React.Component {
- static propTypes = {
- team: PropTypes.object,
- user: PropTypes.object
- };
-
- static contextTypes = {
- router: PropTypes.object.isRequired
- };
-
- constructor(props) {
- super(props);
-
- this.handleSubmit = this.handleSubmit.bind(this);
-
- this.updateName = this.updateName.bind(this);
- this.updateImage = this.updateImage.bind(this);
-
- this.state = {
- name: '',
- image: null,
- imageUrl: '',
- saving: false,
- error: null
- };
- }
-
- handleSubmit(e) {
- e.preventDefault();
-
- if (this.state.saving) {
- return;
- }
-
- this.setState({
- saving: true,
- error: null
- });
-
- const emoji = {
- creator_id: this.props.user.id,
- name: this.state.name.trim().toLowerCase()
- };
-
- // trim surrounding colons if the user accidentally included them in the name
- if (emoji.name.startsWith(':') && emoji.name.endsWith(':')) {
- emoji.name = emoji.name.substring(1, emoji.name.length - 1);
- }
-
- if (!emoji.name) {
- this.setState({
- saving: false,
- error: (
- <FormattedMessage
- id='add_emoji.nameRequired'
- defaultMessage='A name is required for the emoji'
- />
- )
- });
-
- return;
- } else if (/[^a-z0-9_-]/.test(emoji.name)) {
- this.setState({
- saving: false,
- error: (
- <FormattedMessage
- id='add_emoji.nameInvalid'
- defaultMessage="An emoji's name can only contain lowercase letters, numbers, and the symbols '-' and '_'."
- />
- )
- });
-
- return;
- } else if (EmojiStore.hasSystemEmoji(emoji.name)) {
- this.setState({
- saving: false,
- error: (
- <FormattedMessage
- id='add_emoji.nameTaken'
- defaultMessage='This name is already in use by a system emoji. Please choose another name.'
- />
- )
- });
-
- return;
- }
-
- if (!this.state.image) {
- this.setState({
- saving: false,
- error: (
- <FormattedMessage
- id='add_emoji.imageRequired'
- defaultMessage='An image is required for the emoji'
- />
- )
- });
-
- return;
- }
-
- EmojiActions.addEmoji(
- emoji,
- this.state.image,
- () => {
- // for some reason, browserHistory.push doesn't trigger a state change even though the url changes
- this.context.router.push('/' + this.props.team.name + '/emoji');
- },
- (err) => {
- this.setState({
- saving: false,
- error: err.message
- });
- }
- );
- }
-
- updateName(e) {
- this.setState({
- name: e.target.value
- });
- }
-
- updateImage(e) {
- if (e.target.files.length === 0) {
- this.setState({
- image: null,
- imageUrl: ''
- });
-
- return;
- }
-
- const image = e.target.files[0];
-
- const reader = new FileReader();
- reader.onload = () => {
- this.setState({
- image,
- imageUrl: reader.result
- });
- };
- reader.readAsDataURL(image);
- }
-
- render() {
- let filename = null;
- if (this.state.image) {
- filename = (
- <span className='add-emoji__filename'>
- {this.state.image.name}
- </span>
- );
- }
-
- let preview = null;
- if (this.state.imageUrl) {
- preview = (
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='preview'
- >
- <FormattedMessage
- id='add_emoji.preview'
- defaultMessage='Preview'
- />
- </label>
- <div className='col-md-5 col-sm-8 add-emoji__preview'>
- <FormattedMessage
- id='add_emoji.preview.sentence'
- defaultMessage='This is a sentence with {image} in it.'
- values={{
- image: (
- <span
- className='emoticon'
- style={{backgroundImage: 'url(' + this.state.imageUrl + ')'}}
- />
- )
- }}
- />
- </div>
- </div>
- );
- }
-
- return (
- <div className='backstage-content row'>
- <BackstageHeader>
- <Link to={'/' + this.props.team.name + '/emoji'}>
- <FormattedMessage
- id='emoji_list.header'
- defaultMessage='Custom Emoji'
- />
- </Link>
- <FormattedMessage
- id='add_emoji.header'
- defaultMessage='Add'
- />
- </BackstageHeader>
- <div className='backstage-form'>
- <form
- className='form-horizontal'
- onSubmit={this.handleSubmit}
- >
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='name'
- >
- <FormattedMessage
- id='add_emoji.name'
- defaultMessage='Name'
- />
- </label>
- <div className='col-md-5 col-sm-8'>
- <input
- id='name'
- type='text'
- maxLength='64'
- className='form-control'
- value={this.state.name}
- onChange={this.updateName}
- />
- <div className='form__help'>
- <FormattedMessage
- id='add_emoji.name.help'
- defaultMessage="Choose a name for your emoji made of up to 64 characters consisting of lowercase letters, numbers, and the symbols '-' and '_'."
- />
- </div>
- </div>
- </div>
- <div className='form-group'>
- <label
- className='control-label col-sm-4'
- htmlFor='image'
- >
- <FormattedMessage
- id='add_emoji.image'
- defaultMessage='Image'
- />
- </label>
- <div className='col-md-5 col-sm-8'>
- <div>
- <div className='add-emoji__upload'>
- <button className='btn btn-primary'>
- <FormattedMessage
- id='add_emoji.image.button'
- defaultMessage='Select'
- />
- </button>
- <input
- type='file'
- accept='.jpg,.png,.gif'
- multiple={false}
- onChange={this.updateImage}
- />
- </div>
- {filename}
- <div className='form__help'>
- <FormattedMessage
- id='add_emoji.image.help'
- defaultMessage='Choose the image for your emoji. The image can be a gif, png, or jpeg file with a max size of 64 KB and dimensions up to 128 by 128 pixels.'
- />
- </div>
- </div>
- </div>
- </div>
- {preview}
- <div className='backstage-form__footer'>
- <FormError
- type='backstage'
- error={this.state.error}
- />
- <Link
- className='btn btn-sm'
- to={'/' + this.props.team.name + '/emoji'}
- >
- <FormattedMessage
- id='add_emoji.cancel'
- defaultMessage='Cancel'
- />
- </Link>
- <SpinnerButton
- className='btn btn-primary'
- type='submit'
- spinning={this.state.saving}
- onClick={this.handleSubmit}
- >
- <FormattedMessage
- id='add_emoji.save'
- defaultMessage='Save'
- />
- </SpinnerButton>
- </div>
- </form>
- </div>
- </div>
- );
- }
-}
diff --git a/webapp/components/emoji/components/delete_emoji_modal.jsx b/webapp/components/emoji/components/delete_emoji_modal.jsx
deleted file mode 100644
index 8b8ad7725..000000000
--- a/webapp/components/emoji/components/delete_emoji_modal.jsx
+++ /dev/null
@@ -1,50 +0,0 @@
-import PropTypes from 'prop-types';
-import React from 'react';
-import {FormattedMessage} from 'react-intl';
-
-import DeleteModalTrigger from '../../delete_modal_trigger.jsx';
-
-export default class DeleteEmoji extends DeleteModalTrigger {
- get triggerTitle() {
- return (
- <FormattedMessage
- id='emoji_list.delete'
- defaultMessage='Delete'
- />
- );
- }
-
- get modalTitle() {
- return (
- <FormattedMessage
- id='emoji_list.delete.confirm.title'
- defaultMessage='Delete Custom Emoji'
- />
- );
- }
-
- get modalMessage() {
- return (
- <div className='alert alert-warning'>
- <i className='fa fa-warning fa-margin--right'/>
- <FormattedMessage
- id='emoji_list.delete.confirm.msg'
- defaultMessage='This action permanently deletes the custom emoji. Are you sure you want to delete it?'
- />
- </div>
- );
- }
-
- get modalConfirmButton() {
- return (
- <FormattedMessage
- id='emoji_list.delete.confirm.button'
- defaultMessage='Delete'
- />
- );
- }
-}
-
-DeleteEmoji.propTypes = {
- onDelete: PropTypes.func.isRequired
-};
diff --git a/webapp/components/emoji/components/emoji_list.jsx b/webapp/components/emoji/components/emoji_list.jsx
deleted file mode 100644
index 94d82caaa..000000000
--- a/webapp/components/emoji/components/emoji_list.jsx
+++ /dev/null
@@ -1,228 +0,0 @@
-// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import EmojiListItem from './emoji_list_item.jsx';
-import LoadingScreen from 'components/loading_screen.jsx';
-
-import EmojiStore from 'stores/emoji_store.jsx';
-import UserStore from 'stores/user_store.jsx';
-
-import * as EmojiActions from 'actions/emoji_actions.jsx';
-
-import * as Utils from 'utils/utils.jsx';
-
-import React from 'react';
-import PropTypes from 'prop-types';
-import {Link} from 'react-router';
-import {FormattedMessage} from 'react-intl';
-
-export default class EmojiList extends React.Component {
- static get propTypes() {
- return {
- team: PropTypes.object,
- user: PropTypes.object
- };
- }
-
- constructor(props) {
- super(props);
-
- this.updateTitle = this.updateTitle.bind(this);
-
- this.handleEmojiChange = this.handleEmojiChange.bind(this);
- this.handleUserChange = this.handleUserChange.bind(this);
- this.deleteEmoji = this.deleteEmoji.bind(this);
- this.updateFilter = this.updateFilter.bind(this);
-
- this.state = {
- emojis: EmojiStore.getCustomEmojiMap(),
- loading: true,
- filter: '',
- users: UserStore.getProfiles()
- };
- }
-
- componentDidMount() {
- EmojiStore.addChangeListener(this.handleEmojiChange);
- UserStore.addChangeListener(this.handleUserChange);
-
- if (window.mm_config.EnableCustomEmoji === 'true') {
- EmojiActions.loadEmoji().then(() => this.setState({loading: false}));
- }
-
- this.updateTitle();
- }
-
- updateTitle() {
- let currentSiteName = '';
- if (global.window.mm_config.SiteName != null) {
- currentSiteName = global.window.mm_config.SiteName;
- }
-
- document.title = Utils.localizeMessage('custom_emoji.header', 'Custom Emoji') + ' - ' + this.props.team.display_name + ' ' + currentSiteName;
- }
-
- componentWillUnmount() {
- EmojiStore.removeChangeListener(this.handleEmojiChange);
- UserStore.removeChangeListener(this.handleUserChange);
- }
-
- handleEmojiChange() {
- this.setState({
- emojis: EmojiStore.getCustomEmojiMap()
- });
- }
-
- handleUserChange() {
- this.setState({users: UserStore.getProfiles()});
- }
-
- updateFilter(e) {
- this.setState({
- filter: e.target.value
- });
- }
-
- deleteEmoji(emoji) {
- EmojiActions.deleteEmoji(emoji.id);
- }
-
- render() {
- const filter = this.state.filter.toLowerCase();
- const isSystemAdmin = Utils.isSystemAdmin(this.props.user.roles);
-
- const emojis = [];
- if (this.state.loading) {
- emojis.push(
- <tr
- key='loading'
- className='backstage-list__item backstage-list__empty'
- >
- <td colSpan='4'>
- <LoadingScreen key='loading'/>
- </td>
- </tr>
- );
- } else if (this.state.emojis.size === 0) {
- emojis.push(
- <tr
- key='empty'
- className='backstage-list__item backstage-list__empty'
- >
- <td colSpan='4'>
- <FormattedMessage
- id='emoji_list.empty'
- defaultMessage='No custom emoji found'
- />
- </td>
- </tr>
- );
- } else {
- for (const [, emoji] of this.state.emojis) {
- let onDelete = null;
- if (isSystemAdmin || this.props.user.id === emoji.creator_id) {
- onDelete = this.deleteEmoji;
- }
-
- emojis.push(
- <EmojiListItem
- key={emoji.id}
- emoji={emoji}
- onDelete={onDelete}
- filter={filter}
- creator={this.state.users[emoji.creator_id] || {}}
- />
- );
- }
- }
-
- return (
- <div className='backstage-content emoji-list'>
- <div className='backstage-header'>
- <h1>
- <FormattedMessage
- id='emoji_list.header'
- defaultMessage='Custom Emoji'
- />
- </h1>
- <Link
- className='add-link'
- to={'/' + this.props.team.name + '/emoji/add'}
- >
- <button
- type='button'
- className='btn btn-primary'
- >
- <FormattedMessage
- id='emoji_list.add'
- defaultMessage='Add Custom Emoji'
- />
- </button>
- </Link>
- </div>
- <div className='backstage-filters'>
- <div className='backstage-filter__search'>
- <i className='fa fa-search'/>
- <input
- type='search'
- className='form-control'
- placeholder={Utils.localizeMessage('emoji_list.search', 'Search Custom Emoji')}
- value={this.state.filter}
- onChange={this.updateFilter}
- style={{flexGrow: 0, flexShrink: 0}}
- />
- </div>
- </div>
- <span className='backstage-list__help'>
- <p>
- <FormattedMessage
- id='emoji_list.help'
- defaultMessage="Custom emoji are available to everyone on your server. Type ':' in a message box to bring up the emoji selection menu. Other users may need to refresh the page before new emojis appear."
- />
- </p>
- <p>
- <FormattedMessage
- id='emoji_list.help2'
- defaultMessage="Tip: If you add #, ##, or ### as the first character on a new line containing emoji, you can use larger sized emoji. To try it out, send a message such as: '# :smile:'."
- />
- </p>
- </span>
- <div className='backstage-list'>
- <table className='emoji-list__table'>
- <thead>
- <tr className='backstage-list__item emoji-list__table-header'>
- <th className='emoji-list__name'>
- <FormattedMessage
- id='emoji_list.name'
- defaultMessage='Name'
- />
- </th>
- <th className='emoji-list__image'>
- <FormattedMessage
- id='emoji_list.image'
- defaultMessage='Image'
- />
- </th>
- <th className='emoji-list__creator'>
- <FormattedMessage
- id='emoji_list.creator'
- defaultMessage='Creator'
- />
- </th>
- <th className='emoji-list_actions'>
- <FormattedMessage
- id='emoji_list.actions'
- defaultMessage='Actions'
- />
- </th>
- </tr>
- </thead>
- <tbody>
- {emojis}
- </tbody>
- </table>
- </div>
- </div>
- );
- }
-}
diff --git a/webapp/components/emoji/components/emoji_list_item.jsx b/webapp/components/emoji/components/emoji_list_item.jsx
deleted file mode 100644
index cb3a8aceb..000000000
--- a/webapp/components/emoji/components/emoji_list_item.jsx
+++ /dev/null
@@ -1,103 +0,0 @@
-import PropTypes from 'prop-types';
-
-// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import React from 'react';
-
-import EmojiStore from 'stores/emoji_store.jsx';
-import DeleteEmoji from './delete_emoji_modal.jsx';
-
-import * as Utils from 'utils/utils.jsx';
-
-import {FormattedMessage} from 'react-intl';
-
-export default class EmojiListItem extends React.Component {
- static get propTypes() {
- return {
- emoji: PropTypes.object.isRequired,
- onDelete: PropTypes.func.isRequired,
- filter: PropTypes.string,
- creator: PropTypes.object.isRequired
- };
- }
-
- constructor(props) {
- super(props);
-
- this.handleDelete = this.handleDelete.bind(this);
- }
-
- handleDelete() {
- this.props.onDelete(this.props.emoji);
- }
-
- matchesFilter(emoji, creator, filter) {
- if (!filter) {
- return true;
- }
-
- if (emoji.name.toLowerCase().indexOf(filter) !== -1) {
- return true;
- }
-
- if (creator && creator.username && creator.username.toLowerCase().indexOf(filter) !== -1) {
- return true;
- }
-
- return false;
- }
-
- render() {
- const emoji = this.props.emoji;
- const creator = this.props.creator;
- const filter = this.props.filter ? this.props.filter.toLowerCase() : '';
-
- if (!this.matchesFilter(emoji, creator, filter)) {
- return null;
- }
-
- let creatorName;
- if (creator) {
- creatorName = Utils.displayUsernameForUser(creator);
-
- if (creatorName !== creator.username) {
- creatorName += ' (@' + creator.username + ')';
- }
- } else {
- creatorName = (
- <FormattedMessage
- id='emoji_list.somebody'
- defaultMessage='Somebody on another team'
- />
- );
- }
-
- let deleteButton = null;
- if (this.props.onDelete) {
- deleteButton = (
- <DeleteEmoji onDelete={this.handleDelete}/>
- );
- }
-
- return (
- <tr className='backstage-list__item'>
- <td className='emoji-list__name'>
- {':' + emoji.name + ':'}
- </td>
- <td className='emoji-list__image'>
- <span
- className='emoticon'
- style={{backgroundImage: 'url(' + EmojiStore.getEmojiImageUrl(emoji) + ')'}}
- />
- </td>
- <td className='emoji-list__creator'>
- {creatorName}
- </td>
- <td className='emoji-list-item_actions'>
- {deleteButton}
- </td>
- </tr>
- );
- }
-}