Commiting
This commit is contained in:
@@ -1,82 +0,0 @@
|
||||
import emojify from '../emoji';
|
||||
|
||||
describe('emoji', () => {
|
||||
describe('.emojify', () => {
|
||||
it('ignores unknown shortcodes', () => {
|
||||
expect(emojify(':foobarbazfake:')).toEqual(':foobarbazfake:');
|
||||
});
|
||||
|
||||
it('ignores shortcodes inside of tags', () => {
|
||||
expect(emojify('<p data-foo=":smile:"></p>')).toEqual('<p data-foo=":smile:"></p>');
|
||||
});
|
||||
|
||||
it('works with unclosed tags', () => {
|
||||
expect(emojify('hello>')).toEqual('hello>');
|
||||
expect(emojify('<hello')).toEqual('<hello');
|
||||
});
|
||||
|
||||
it('works with unclosed shortcodes', () => {
|
||||
expect(emojify('smile:')).toEqual('smile:');
|
||||
expect(emojify(':smile')).toEqual(':smile');
|
||||
});
|
||||
|
||||
it('does unicode', () => {
|
||||
expect(emojify('\uD83D\uDC69\u200D\uD83D\uDC69\u200D\uD83D\uDC66\u200D\uD83D\uDC66')).toEqual(
|
||||
'<img draggable="false" class="emojione" alt="👩👩👦👦" title=":woman-woman-boy-boy:" src="/emoji/1f469-200d-1f469-200d-1f466-200d-1f466.svg" />');
|
||||
expect(emojify('👨👩👧👧')).toEqual(
|
||||
'<img draggable="false" class="emojione" alt="👨👩👧👧" title=":man-woman-girl-girl:" src="/emoji/1f468-200d-1f469-200d-1f467-200d-1f467.svg" />');
|
||||
expect(emojify('👩👩👦')).toEqual('<img draggable="false" class="emojione" alt="👩👩👦" title=":woman-woman-boy:" src="/emoji/1f469-200d-1f469-200d-1f466.svg" />');
|
||||
expect(emojify('\u2757')).toEqual(
|
||||
'<img draggable="false" class="emojione" alt="❗" title=":exclamation:" src="/emoji/2757.svg" />');
|
||||
});
|
||||
|
||||
it('does multiple unicode', () => {
|
||||
expect(emojify('\u2757 #\uFE0F\u20E3')).toEqual(
|
||||
'<img draggable="false" class="emojione" alt="❗" title=":exclamation:" src="/emoji/2757.svg" /> <img draggable="false" class="emojione" alt="#️⃣" title=":hash:" src="/emoji/23-20e3.svg" />');
|
||||
expect(emojify('\u2757#\uFE0F\u20E3')).toEqual(
|
||||
'<img draggable="false" class="emojione" alt="❗" title=":exclamation:" src="/emoji/2757.svg" /><img draggable="false" class="emojione" alt="#️⃣" title=":hash:" src="/emoji/23-20e3.svg" />');
|
||||
expect(emojify('\u2757 #\uFE0F\u20E3 \u2757')).toEqual(
|
||||
'<img draggable="false" class="emojione" alt="❗" title=":exclamation:" src="/emoji/2757.svg" /> <img draggable="false" class="emojione" alt="#️⃣" title=":hash:" src="/emoji/23-20e3.svg" /> <img draggable="false" class="emojione" alt="❗" title=":exclamation:" src="/emoji/2757.svg" />');
|
||||
expect(emojify('foo \u2757 #\uFE0F\u20E3 bar')).toEqual(
|
||||
'foo <img draggable="false" class="emojione" alt="❗" title=":exclamation:" src="/emoji/2757.svg" /> <img draggable="false" class="emojione" alt="#️⃣" title=":hash:" src="/emoji/23-20e3.svg" /> bar');
|
||||
});
|
||||
|
||||
it('ignores unicode inside of tags', () => {
|
||||
expect(emojify('<p data-foo="\uD83D\uDC69\uD83D\uDC69\uD83D\uDC66"></p>')).toEqual('<p data-foo="\uD83D\uDC69\uD83D\uDC69\uD83D\uDC66"></p>');
|
||||
});
|
||||
|
||||
it('does multiple emoji properly (issue 5188)', () => {
|
||||
expect(emojify('👌🌈💕')).toEqual('<img draggable="false" class="emojione" alt="👌" title=":ok_hand:" src="/emoji/1f44c.svg" /><img draggable="false" class="emojione" alt="🌈" title=":rainbow:" src="/emoji/1f308.svg" /><img draggable="false" class="emojione" alt="💕" title=":two_hearts:" src="/emoji/1f495.svg" />');
|
||||
expect(emojify('👌 🌈 💕')).toEqual('<img draggable="false" class="emojione" alt="👌" title=":ok_hand:" src="/emoji/1f44c.svg" /> <img draggable="false" class="emojione" alt="🌈" title=":rainbow:" src="/emoji/1f308.svg" /> <img draggable="false" class="emojione" alt="💕" title=":two_hearts:" src="/emoji/1f495.svg" />');
|
||||
});
|
||||
|
||||
it('does an emoji that has no shortcode', () => {
|
||||
expect(emojify('👁🗨')).toEqual('<img draggable="false" class="emojione" alt="👁🗨" title="" src="/emoji/1f441-200d-1f5e8.svg" />');
|
||||
});
|
||||
|
||||
it('does an emoji whose filename is irregular', () => {
|
||||
expect(emojify('↙️')).toEqual('<img draggable="false" class="emojione" alt="↙️" title=":arrow_lower_left:" src="/emoji/2199.svg" />');
|
||||
});
|
||||
|
||||
it('avoid emojifying on invisible text', () => {
|
||||
expect(emojify('<a href="http://example.com/test%F0%9F%98%84"><span class="invisible">http://</span><span class="ellipsis">example.com/te</span><span class="invisible">st😄</span></a>'))
|
||||
.toEqual('<a href="http://example.com/test%F0%9F%98%84"><span class="invisible">http://</span><span class="ellipsis">example.com/te</span><span class="invisible">st😄</span></a>');
|
||||
expect(emojify('<span class="invisible">:luigi:</span>', { ':luigi:': { static_url: 'luigi.exe' } }))
|
||||
.toEqual('<span class="invisible">:luigi:</span>');
|
||||
});
|
||||
|
||||
it('avoid emojifying on invisible text with nested tags', () => {
|
||||
expect(emojify('<span class="invisible">😄<span class="foo">bar</span>😴</span>😇'))
|
||||
.toEqual('<span class="invisible">😄<span class="foo">bar</span>😴</span><img draggable="false" class="emojione" alt="😇" title=":innocent:" src="/emoji/1f607.svg" />');
|
||||
expect(emojify('<span class="invisible">😄<span class="invisible">😕</span>😴</span>😇'))
|
||||
.toEqual('<span class="invisible">😄<span class="invisible">😕</span>😴</span><img draggable="false" class="emojione" alt="😇" title=":innocent:" src="/emoji/1f607.svg" />');
|
||||
expect(emojify('<span class="invisible">😄<br/>😴</span>😇'))
|
||||
.toEqual('<span class="invisible">😄<br/>😴</span><img draggable="false" class="emojione" alt="😇" title=":innocent:" src="/emoji/1f607.svg" />');
|
||||
});
|
||||
|
||||
it('skips the textual presentation VS15 character', () => {
|
||||
expect(emojify('✴︎')) // This is U+2734 EIGHT POINTED BLACK STAR then U+FE0E VARIATION SELECTOR-15
|
||||
.toEqual('<img draggable="false" class="emojione" alt="✴" title=":eight_pointed_black_star:" src="/emoji/2734.svg" />');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,176 +0,0 @@
|
||||
import pick from 'lodash.pick'
|
||||
import { emojiIndex } from 'emoji-mart';
|
||||
import { search } from '../emoji_mart_search_light';
|
||||
|
||||
const trimEmojis = emoji => pick(emoji, ['id', 'unified', 'native', 'custom']);
|
||||
|
||||
describe('emoji_index', () => {
|
||||
it('should give same result for emoji_index_light and emoji-mart', () => {
|
||||
const expected = [
|
||||
{
|
||||
id: 'pineapple',
|
||||
unified: '1f34d',
|
||||
native: '🍍',
|
||||
},
|
||||
];
|
||||
expect(search('pineapple').map(trimEmojis)).toEqual(expected);
|
||||
expect(emojiIndex.search('pineapple').map(trimEmojis)).toEqual(expected);
|
||||
});
|
||||
|
||||
it('orders search results correctly', () => {
|
||||
const expected = [
|
||||
{
|
||||
id: 'apple',
|
||||
unified: '1f34e',
|
||||
native: '🍎',
|
||||
},
|
||||
{
|
||||
id: 'pineapple',
|
||||
unified: '1f34d',
|
||||
native: '🍍',
|
||||
},
|
||||
{
|
||||
id: 'green_apple',
|
||||
unified: '1f34f',
|
||||
native: '🍏',
|
||||
},
|
||||
{
|
||||
id: 'iphone',
|
||||
unified: '1f4f1',
|
||||
native: '📱',
|
||||
},
|
||||
];
|
||||
expect(search('apple').map(trimEmojis)).toEqual(expected);
|
||||
expect(emojiIndex.search('apple').map(trimEmojis)).toEqual(expected);
|
||||
});
|
||||
|
||||
it('can include/exclude categories', () => {
|
||||
expect(search('flag', { include: ['people'] })).toEqual([]);
|
||||
expect(emojiIndex.search('flag', { include: ['people'] })).toEqual([]);
|
||||
});
|
||||
|
||||
it('(different behavior from emoji-mart) do not erases custom emoji if not passed again', () => {
|
||||
const custom = [
|
||||
{
|
||||
id: 'gabsocial',
|
||||
name: 'gabsocial',
|
||||
short_names: ['gabsocial'],
|
||||
text: '',
|
||||
emoticons: [],
|
||||
keywords: ['gabsocial'],
|
||||
imageUrl: 'http://example.com',
|
||||
custom: true,
|
||||
},
|
||||
];
|
||||
search('', { custom });
|
||||
emojiIndex.search('', { custom });
|
||||
const expected = [];
|
||||
const lightExpected = [
|
||||
{
|
||||
id: 'gabsocial',
|
||||
custom: true,
|
||||
},
|
||||
];
|
||||
expect(search('masto').map(trimEmojis)).toEqual(lightExpected);
|
||||
expect(emojiIndex.search('masto').map(trimEmojis)).toEqual(expected);
|
||||
});
|
||||
|
||||
it('(different behavior from emoji-mart) erases custom emoji if another is passed', () => {
|
||||
const custom = [
|
||||
{
|
||||
id: 'gabsocial',
|
||||
name: 'gabsocial',
|
||||
short_names: ['gabsocial'],
|
||||
text: '',
|
||||
emoticons: [],
|
||||
keywords: ['gabsocial'],
|
||||
imageUrl: 'http://example.com',
|
||||
custom: true,
|
||||
},
|
||||
];
|
||||
search('', { custom });
|
||||
emojiIndex.search('', { custom });
|
||||
const expected = [];
|
||||
expect(search('masto', { custom: [] }).map(trimEmojis)).toEqual(expected);
|
||||
expect(emojiIndex.search('masto').map(trimEmojis)).toEqual(expected);
|
||||
});
|
||||
|
||||
it('handles custom emoji', () => {
|
||||
const custom = [
|
||||
{
|
||||
id: 'gabsocial',
|
||||
name: 'gabsocial',
|
||||
short_names: ['gabsocial'],
|
||||
text: '',
|
||||
emoticons: [],
|
||||
keywords: ['gabsocial'],
|
||||
imageUrl: 'http://example.com',
|
||||
custom: true,
|
||||
},
|
||||
];
|
||||
search('', { custom });
|
||||
emojiIndex.search('', { custom });
|
||||
const expected = [
|
||||
{
|
||||
id: 'gabsocial',
|
||||
custom: true,
|
||||
},
|
||||
];
|
||||
expect(search('masto', { custom }).map(trimEmojis)).toEqual(expected);
|
||||
expect(emojiIndex.search('masto', { custom }).map(trimEmojis)).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should filter only emojis we care about, exclude pineapple', () => {
|
||||
const emojisToShowFilter = emoji => emoji.unified !== '1F34D';
|
||||
expect(search('apple', { emojisToShowFilter }).map((obj) => obj.id))
|
||||
.not.toContain('pineapple');
|
||||
expect(emojiIndex.search('apple', { emojisToShowFilter }).map((obj) => obj.id))
|
||||
.not.toContain('pineapple');
|
||||
});
|
||||
|
||||
it('does an emoji whose unified name is irregular', () => {
|
||||
const expected = [
|
||||
{
|
||||
'id': 'water_polo',
|
||||
'unified': '1f93d',
|
||||
'native': '🤽',
|
||||
},
|
||||
{
|
||||
'id': 'man-playing-water-polo',
|
||||
'unified': '1f93d-200d-2642-fe0f',
|
||||
'native': '🤽♂️',
|
||||
},
|
||||
{
|
||||
'id': 'woman-playing-water-polo',
|
||||
'unified': '1f93d-200d-2640-fe0f',
|
||||
'native': '🤽♀️',
|
||||
},
|
||||
];
|
||||
expect(search('polo').map(trimEmojis)).toEqual(expected);
|
||||
expect(emojiIndex.search('polo').map(trimEmojis)).toEqual(expected);
|
||||
});
|
||||
|
||||
it('can search for thinking_face', () => {
|
||||
const expected = [
|
||||
{
|
||||
id: 'thinking_face',
|
||||
unified: '1f914',
|
||||
native: '🤔',
|
||||
},
|
||||
];
|
||||
expect(search('thinking_fac').map(trimEmojis)).toEqual(expected);
|
||||
expect(emojiIndex.search('thinking_fac').map(trimEmojis)).toEqual(expected);
|
||||
});
|
||||
|
||||
it('can search for woman-facepalming', () => {
|
||||
const expected = [
|
||||
{
|
||||
id: 'woman-facepalming',
|
||||
unified: '1f926-200d-2640-fe0f',
|
||||
native: '🤦♀️',
|
||||
},
|
||||
];
|
||||
expect(search('woman-facep').map(trimEmojis)).toEqual(expected);
|
||||
expect(emojiIndex.search('woman-facep').map(trimEmojis)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
@@ -54,7 +54,7 @@ class Image extends React.PureComponent {
|
||||
<img
|
||||
alt={alt}
|
||||
className={classes}
|
||||
{...otherProps}
|
||||
{...otherProps} // : todo : remove
|
||||
ref={imageRef}
|
||||
src={src}
|
||||
onError={this.handleOnError}
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import { defineMessages, injectIntl } from 'react-intl'
|
||||
import api from '../../api'
|
||||
import ModalLayout from './modal_layout'
|
||||
import Divider from '../divider'
|
||||
import Icon from '../icon'
|
||||
import Input from '../input'
|
||||
import Text from '../text'
|
||||
|
||||
class EmbedModal extends ImmutablePureComponent {
|
||||
|
||||
state = {
|
||||
loading: false,
|
||||
oembed: null,
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { url } = this.props
|
||||
|
||||
this.setState({ loading: true })
|
||||
|
||||
api().post('/api/web/embed', { url }).then(res => {
|
||||
this.setState({ loading: false, oembed: res.data })
|
||||
|
||||
const iframeDocument = this.iframe.contentWindow.document
|
||||
|
||||
iframeDocument.open()
|
||||
iframeDocument.write(res.data.html)
|
||||
iframeDocument.close()
|
||||
|
||||
iframeDocument.body.style.margin = 0
|
||||
this.iframe.width = iframeDocument.body.scrollWidth
|
||||
this.iframe.height = iframeDocument.body.scrollHeight
|
||||
}).catch(error => {
|
||||
this.props.onError(error)
|
||||
})
|
||||
}
|
||||
|
||||
setIframeRef = c => {
|
||||
this.iframe = c
|
||||
}
|
||||
|
||||
handleTextareaClick = (e) => {
|
||||
e.target.select()
|
||||
}
|
||||
|
||||
render() {
|
||||
const { intl, onClose } = this.props
|
||||
const { oembed } = this.state
|
||||
|
||||
return (
|
||||
<ModalLayout
|
||||
title={intl.formatMessage(messages.embed)}
|
||||
onClose={onClose}
|
||||
>
|
||||
<div className={_s.d}>
|
||||
<Text className={_s.my10}>
|
||||
{intl.formatMessage(messages.instructions)}
|
||||
</Text>
|
||||
|
||||
<div className={[_s.d, _s.mb10].join(' ')}>
|
||||
<Input
|
||||
readOnly
|
||||
type='text'
|
||||
value={oembed && oembed.html || ''}
|
||||
onClick={this.handleTextareaClick}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Text className={_s.mb10}>
|
||||
{intl.formatMessage(messages.preview)}
|
||||
</Text>
|
||||
|
||||
<div className={[_s.d, _s.w100PC, _s.bgSubtle, _s.h220PX, _s.aiCenter, _s.jcCenter].join(' ')}>
|
||||
<iframe
|
||||
className={[_s.d, _s.w100PC, _s.h100PC, _s.z2].join(' ')}
|
||||
frameBorder='0'
|
||||
ref={this.setIframeRef}
|
||||
sandbox='allow-same-origin'
|
||||
title='preview'
|
||||
/>
|
||||
{
|
||||
!oembed &&
|
||||
<Icon id='loading' size='34px' className={[_s.posAbs, _s.z3].join(' ')} />
|
||||
}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</ModalLayout>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const messages = defineMessages({
|
||||
embed: { id: 'status.embed', defaultMessage: 'Embed' },
|
||||
instructions: { id: 'embed.instructions', defaultMessage: 'Embed this status on your website by copying the code below.' },
|
||||
preview: { id: 'embed.preview', defaultMessage: 'Here is what it will look like:' },
|
||||
})
|
||||
|
||||
EmbedModal.propTypes = {
|
||||
url: PropTypes.string.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
onError: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
export default injectIntl(EmbedModal)
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
MODAL_EDIT_PROFILE,
|
||||
MODAL_EDIT_SHORTCUTS,
|
||||
MODAL_EMAIL_CONFIRMATION_REMINDER,
|
||||
MODAL_EMBED,
|
||||
MODAL_GROUP_CREATE,
|
||||
MODAL_GROUP_DELETE,
|
||||
MODAL_GROUP_PASSWORD,
|
||||
@@ -50,7 +49,6 @@ import {
|
||||
EditProfileModal,
|
||||
EditShortcutsModal,
|
||||
EmailConfirmationReminderModal,
|
||||
EmbedModal,
|
||||
GroupCreateModal,
|
||||
GroupDeleteModal,
|
||||
GroupMembersModal,
|
||||
@@ -86,7 +84,6 @@ MODAL_COMPONENTS[MODAL_DISPLAY_OPTIONS] = DisplayOptionsModal
|
||||
MODAL_COMPONENTS[MODAL_EDIT_SHORTCUTS] = EditShortcutsModal
|
||||
MODAL_COMPONENTS[MODAL_EDIT_PROFILE] = EditProfileModal
|
||||
MODAL_COMPONENTS[MODAL_EMAIL_CONFIRMATION_REMINDER] = EmailConfirmationReminderModal
|
||||
MODAL_COMPONENTS[MODAL_EMBED] = EmbedModal
|
||||
MODAL_COMPONENTS[MODAL_GROUP_CREATE] = GroupCreateModal
|
||||
MODAL_COMPONENTS[MODAL_GROUP_DELETE] = GroupDeleteModal
|
||||
MODAL_COMPONENTS[MODAL_GROUP_PASSWORD] = GroupPasswordModal
|
||||
|
||||
@@ -12,19 +12,38 @@ import MediaGalleryPanelPlaceholder from '../placeholder/media_gallery_panel_pla
|
||||
|
||||
class MediaGalleryPanel extends ImmutablePureComponent {
|
||||
|
||||
state = {
|
||||
fetched: false,
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(nextProps, prevState) {
|
||||
if (nextProps.shouldLoad && !prevState.fetched) {
|
||||
return { fetched: true }
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (!prevState.fetched && this.state.fetched && this.props.isLazy) {
|
||||
this.props.dispatch(expandAccountMediaTimeline(this.props.accountId, { limit: 8 }))
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { accountId } = this.props
|
||||
const { accountId, isLazy } = this.props
|
||||
|
||||
if (accountId && accountId !== -1) {
|
||||
if (!isLazy && !!accountId && accountId !== -1) {
|
||||
this.props.dispatch(expandAccountMediaTimeline(accountId, { limit: 8 }))
|
||||
this.setState({ fetched: true })
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.accountId && nextProps.accountId !== this.props.accountId) {
|
||||
this.props.dispatch(expandAccountMediaTimeline(nextProps.accountId, { limit: 8 }))
|
||||
}
|
||||
}
|
||||
// componentWillReceiveProps(nextProps) {
|
||||
// if (nextProps.accountId && nextProps.accountId !== this.props.accountId) {
|
||||
// this.props.dispatch(expandAccountMediaTimeline(nextProps.accountId, { limit: 8 }))
|
||||
// }
|
||||
// }
|
||||
|
||||
render() {
|
||||
const {
|
||||
@@ -33,8 +52,9 @@ class MediaGalleryPanel extends ImmutablePureComponent {
|
||||
intl,
|
||||
isLoading,
|
||||
} = this.props
|
||||
const { fetched } = this.state
|
||||
|
||||
if (!attachments) return null
|
||||
if (!attachments && fetched) return null
|
||||
|
||||
return (
|
||||
<PanelLayout
|
||||
|
||||
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types'
|
||||
import { defineMessages, injectIntl } from 'react-intl'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import { me, favouritesCount } from '../../initial_state'
|
||||
import { me } from '../../initial_state'
|
||||
import { shortNumberFormat } from '../../utils/numbers'
|
||||
import PanelLayout from './panel_layout'
|
||||
import UserStat from '../user_stat'
|
||||
@@ -56,7 +56,7 @@ class ProfileStatsPanel extends ImmutablePureComponent {
|
||||
account.get('id') === me &&
|
||||
<UserStat
|
||||
title={intl.formatMessage(messages.likes)}
|
||||
value={shortNumberFormat(favouritesCount)}
|
||||
value={shortNumberFormat(0)}
|
||||
to={`/${account.get('acct')}/likes`}
|
||||
isCentered={noPanel}
|
||||
/>
|
||||
|
||||
@@ -14,8 +14,6 @@ import {
|
||||
unbookmark,
|
||||
} from '../../actions/interactions';
|
||||
import {
|
||||
muteStatus,
|
||||
unmuteStatus,
|
||||
deleteStatus,
|
||||
editStatus,
|
||||
} from '../../actions/statuses';
|
||||
@@ -27,7 +25,6 @@ import {
|
||||
pinGroupStatus,
|
||||
unpinGroupStatus,
|
||||
} from '../../actions/groups'
|
||||
import { initMuteModal } from '../../actions/mutes'
|
||||
import { initReport } from '../../actions/reports'
|
||||
import { openModal } from '../../actions/modal'
|
||||
import {
|
||||
@@ -35,7 +32,6 @@ import {
|
||||
openPopover,
|
||||
} from '../../actions/popover'
|
||||
import {
|
||||
MODAL_EMBED,
|
||||
MODAL_PRO_UPGRADE,
|
||||
POPOVER_STATUS_SHARE,
|
||||
} from '../../constants'
|
||||
@@ -57,13 +53,12 @@ class StatusOptionsPopover extends ImmutablePureComponent {
|
||||
componentDidMount() {
|
||||
if (!this.props.groupRelationships && this.props.groupId) {
|
||||
this.props.onFetchGroupRelationships(this.props.groupId)
|
||||
// : todo :
|
||||
// check if pin
|
||||
// check if bookmark
|
||||
}
|
||||
}
|
||||
|
||||
handleConversationMuteClick = () => {
|
||||
this.props.onMuteConversation(this.props.status)
|
||||
}
|
||||
|
||||
handleGroupRemoveAccount = () => {
|
||||
const { status } = this.props
|
||||
|
||||
@@ -144,15 +139,6 @@ class StatusOptionsPopover extends ImmutablePureComponent {
|
||||
let menu = []
|
||||
|
||||
if (me) {
|
||||
if (status.getIn(['account', 'id']) === me) {
|
||||
menu.push({
|
||||
icon: 'audio-mute',
|
||||
hideArrow: true,
|
||||
title: intl.formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation),
|
||||
onClick: this.handleConversationMuteClick,
|
||||
})
|
||||
}
|
||||
|
||||
if (isReply) {
|
||||
menu.push({
|
||||
icon: 'repost',
|
||||
@@ -296,8 +282,6 @@ const messages = defineMessages({
|
||||
cannot_quote: { id: 'status.cannot_quote', defaultMessage: 'This post cannot be quoted' },
|
||||
like: { id: 'status.like', defaultMessage: 'Like' },
|
||||
report: { id: 'status.report', defaultMessage: 'Report @{name}' },
|
||||
muteConversation: { id: 'status.mute_conversation', defaultMessage: 'Mute conversation' },
|
||||
unmuteConversation: { id: 'status.unmute_conversation', defaultMessage: 'Unmute conversation' },
|
||||
pin: { id: 'status.pin', defaultMessage: 'Pin on profile' },
|
||||
unpin: { id: 'status.unpin', defaultMessage: 'Unpin from profile' },
|
||||
groupPin: { id: 'status.group_pin', defaultMessage: 'Pin in group' },
|
||||
@@ -327,16 +311,6 @@ const mapStateToProps = (state, { status }) => {
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
|
||||
onMuteConversation(status) {
|
||||
dispatch(closePopover())
|
||||
|
||||
if (status.get('muted')) {
|
||||
dispatch(unmuteStatus(status.get('id')))
|
||||
} else {
|
||||
dispatch(muteStatus(status.get('id')))
|
||||
}
|
||||
},
|
||||
|
||||
onPin(status) {
|
||||
dispatch(closePopover())
|
||||
|
||||
@@ -411,11 +385,6 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
dispatch(editStatus(status))
|
||||
},
|
||||
|
||||
onMute(account) {
|
||||
dispatch(closePopover())
|
||||
dispatch(initMuteModal(account))
|
||||
},
|
||||
|
||||
onBlock(status) {
|
||||
dispatch(closePopover())
|
||||
const account = status.get('account')
|
||||
@@ -480,11 +449,9 @@ StatusOptionsPopover.propTypes = {
|
||||
onMute: PropTypes.func.isRequired,
|
||||
onBlock: PropTypes.func.isRequired,
|
||||
onReport: PropTypes.func.isRequired,
|
||||
onMuteConversation: PropTypes.func.isRequired,
|
||||
onPin: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
onFetchGroupRelationships: PropTypes.func.isRequired,
|
||||
onOpenEmbedModal: PropTypes.func.isRequired,
|
||||
onOpenProUpgradeModal: PropTypes.func.isRequired,
|
||||
onClosePopover: PropTypes.func.isRequired,
|
||||
isXS: PropTypes.bool,
|
||||
|
||||
@@ -82,6 +82,7 @@ class DefaultSidebar extends ImmutablePureComponent {
|
||||
</SidebarSectionTitle>
|
||||
<SidebarSectionItem title='Home' icon='home' to='/home' count={homeItemsQueueCount} />
|
||||
<SidebarSectionItem title='Notifications' icon='notifications' to='/notifications' count={notificationCount} />
|
||||
<SidebarSectionItem title='Chats' icon='chat' to='/messages' />
|
||||
<SidebarSectionItem title='Groups' icon='group' to='/groups' />
|
||||
<SidebarSectionItem title='Lists' icon='list' to='/lists' />
|
||||
<SidebarSectionItem title='Explore' icon='explore' to='/explore' />
|
||||
|
||||
@@ -52,7 +52,7 @@ class TabBarItem extends React.PureComponent {
|
||||
|
||||
// Combine state, props, location to make absolutely
|
||||
// sure of active status.
|
||||
const active = isActive || (to === location.pathname && !location.search) || isCurrent
|
||||
const active = (isActive === true || isCurrent || (to === location.pathname && !location.search))
|
||||
|
||||
const containerClasses = CX({
|
||||
d: 1,
|
||||
|
||||
125
app/javascript/gabsocial/components/toast.js
Normal file
125
app/javascript/gabsocial/components/toast.js
Normal file
@@ -0,0 +1,125 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {
|
||||
CX,
|
||||
TOAST_TYPE_ERROR,
|
||||
TOAST_TYPE_SUCCESS,
|
||||
// TOAST_TYPE_NOTIFICATION,
|
||||
} from '../constants'
|
||||
import Button from './button'
|
||||
import Image from './image'
|
||||
import RelativeTimestamp from './relative_timestamp'
|
||||
import Text from './text'
|
||||
|
||||
class Toast extends React.PureComponent {
|
||||
|
||||
componentDidMount() {
|
||||
this._timer = setTimeout(() => {
|
||||
this.handleOnDismiss()
|
||||
}, 5000)
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearTimeout(this._timer)
|
||||
}
|
||||
|
||||
handleOnDismiss = () => {
|
||||
this.props.onDismiss(this.props.id)
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
image,
|
||||
isImageAccount,
|
||||
title,
|
||||
message,
|
||||
date,
|
||||
to,
|
||||
type,
|
||||
} = this.props
|
||||
|
||||
const contentClasses = CX({
|
||||
default: 1,
|
||||
mt5: 1,
|
||||
pt2: 1,
|
||||
maxWidth240PX: 1,
|
||||
flexRow: !!image,
|
||||
})
|
||||
|
||||
const innerContentClasses = CX({
|
||||
default: 1,
|
||||
flexNormal: 1,
|
||||
pl10: !!image,
|
||||
pt2: !!image,
|
||||
displayInline: !!date && !image,
|
||||
})
|
||||
|
||||
const imageClasses = CX({
|
||||
radiusSmall: !isImageAccount,
|
||||
circle: isImageAccount,
|
||||
})
|
||||
|
||||
const dateClasses = CX({
|
||||
mr5: 1,
|
||||
mt2: !!image,
|
||||
})
|
||||
|
||||
return (
|
||||
<div className={[_s.default, _s.radiusSmall, _s.mb5, _s.px15, _s.pt10, _s.pb15, _s.bgPrimary, _s.boxShadowToast].join(' ')}>
|
||||
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter, _s.justifyContentCenter].join(' ')}>
|
||||
<Text size='media' weight='medium' className={[_s.mr15, _s.minWidth160PX].join(' ')}>
|
||||
{title}
|
||||
</Text>
|
||||
<Button
|
||||
backgroundColor='secondary'
|
||||
color='primary'
|
||||
icon='close'
|
||||
iconSize='6px'
|
||||
onClick={this.handleOnDismiss}
|
||||
className={[_s.mlAuto, _s.px10].join(' ')}
|
||||
/>
|
||||
</div>
|
||||
<div className={contentClasses}>
|
||||
{
|
||||
!!image &&
|
||||
<Image
|
||||
src={image}
|
||||
className={imageClasses}
|
||||
height='52px'
|
||||
width='52px'
|
||||
/>
|
||||
}
|
||||
<div className={innerContentClasses}>
|
||||
<Text size='small'>
|
||||
{message}
|
||||
</Text>
|
||||
{
|
||||
date &&
|
||||
<Text color='secondary' size='extraSmall' className={dateClasses}>
|
||||
<RelativeTimestamp timestamp={date} />
|
||||
</Text>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Toast.propTypes = {
|
||||
date: PropTypes.string.isRequired,
|
||||
image: PropTypes.string,
|
||||
isImageAccount: PropTypes.bool,
|
||||
id: PropTypes.string.isRequired,
|
||||
message: PropTypes.string.isRequired,
|
||||
onDismiss: PropTypes.func.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
to: PropTypes.string,
|
||||
type: PropTypes.oneOf([
|
||||
TOAST_TYPE_ERROR,
|
||||
TOAST_TYPE_SUCCESS,
|
||||
]).isRequired,
|
||||
}
|
||||
|
||||
export default Toast
|
||||
Reference in New Issue
Block a user