Progress
This commit is contained in:
@@ -253,9 +253,26 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
|
||||
<div className={[_s.default, _s.flexGrow1].join(' ')}>
|
||||
<div className={[_s.default, _s.ml5].join(' ')}>
|
||||
|
||||
<Composer
|
||||
{/*<Composer
|
||||
/>*/}
|
||||
|
||||
<Textarea
|
||||
className={_s.default}
|
||||
inputRef={this.setTextbox}
|
||||
disabled={disabled}
|
||||
placeholder={placeholder}
|
||||
autoFocus={autoFocus}
|
||||
value={value}
|
||||
onChange={this.onChange}
|
||||
onKeyDown={this.onKeyDown}
|
||||
onKeyUp={onKeyUp}
|
||||
onFocus={this.onFocus}
|
||||
onBlur={this.onBlur}
|
||||
onPaste={this.onPaste}
|
||||
aria-autocomplete='list'
|
||||
/>
|
||||
|
||||
|
||||
{ /*
|
||||
<Textarea
|
||||
className={_s.default}
|
||||
|
||||
41
app/javascript/gabsocial/components/character_counter.js
Normal file
41
app/javascript/gabsocial/components/character_counter.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import { length } from 'stringz'
|
||||
|
||||
export default class CharacterCounter extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
text: PropTypes.string.isRequired,
|
||||
max: PropTypes.number.isRequired,
|
||||
small: PropTypes.bool,
|
||||
}
|
||||
|
||||
render () {
|
||||
const { text, max, small } = this.props
|
||||
const actualRadius = small ? '10' : '16'
|
||||
const radius = small ? 8 : 12
|
||||
const circumference = 2 * Math.PI * radius
|
||||
const diff = length(text) / max
|
||||
const dashoffset = circumference * (1 - diff)
|
||||
|
||||
return (
|
||||
<div className={[_s.default, _s.mr10, _s.justifyContentCenter, _s.alignItemsCenter].join(' ')}>
|
||||
<svg width={actualRadius * 2} height={actualRadius * 2} viewBox={`0 0 ${actualRadius * 2} ${actualRadius * 2}`}>
|
||||
<circle fill='none' cx={actualRadius} cy={actualRadius} r={radius} fill="none" stroke="#e6e6e6" strokeWidth="2" />
|
||||
<circle style={{
|
||||
// transform: 'rotate(-90deg)',
|
||||
strokeDashoffset: dashoffset,
|
||||
strokeDasharray: circumference,
|
||||
}}
|
||||
fill='none'
|
||||
cx={actualRadius}
|
||||
cy={actualRadius}
|
||||
r={radius}
|
||||
strokeWidth="2"
|
||||
strokeLinecap='round'
|
||||
stroke='#21cf7a'
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -40,6 +40,7 @@ export default class ColumnHeader extends PureComponent {
|
||||
{
|
||||
showBackBtn &&
|
||||
<Button
|
||||
color='primary'
|
||||
backgroundColor='none'
|
||||
className={[_s.alignItemsCenter, _s.pl0, _s.justifyContentCenter].join(' ')}
|
||||
icon='back'
|
||||
|
||||
@@ -1,4 +1,96 @@
|
||||
import * as I from '../assets'
|
||||
import AddIcon from '../assets/add_icon'
|
||||
import AngleRightIcon from '../assets/angle_right_icon'
|
||||
import AppsIcon from '../assets/apps_icon'
|
||||
import AudioIcon from '../assets/audio_icon'
|
||||
import AudioMuteIcon from '../assets/audio_mute_icon'
|
||||
import BackIcon from '../assets/back_icon'
|
||||
import CalendarIcon from '../assets/calendar_icon'
|
||||
import ChatIcon from '../assets/chat_icon'
|
||||
import CircleIcon from '../assets/circle_icon'
|
||||
import CloseIcon from '../assets/close_icon'
|
||||
import CommentIcon from '../assets/comment_icon'
|
||||
import DissenterIcon from '../assets/dissenter_icon'
|
||||
import EllipsisIcon from '../assets/ellipsis_icon'
|
||||
import ErrorIcon from '../assets/error_icon'
|
||||
import FullscreenIcon from '../assets/fullscreen_icon'
|
||||
import GifIcon from '../assets/gif_icon'
|
||||
import GlobeIcon from '../assets/globe_icon'
|
||||
import GroupIcon from '../assets/group_icon'
|
||||
import GroupAddIcon from '../assets/group_add_icon'
|
||||
import HappyIcon from '../assets/happy_icon'
|
||||
import HomeIcon from '../assets/home_icon'
|
||||
import LikeIcon from '../assets/like_icon'
|
||||
import LinkIcon from '../assets/link_icon'
|
||||
import ListIcon from '../assets/list_icon'
|
||||
import ListAddIcon from '../assets/list_add_icon'
|
||||
import LoadingIcon from '../assets/loading_icon'
|
||||
import MediaIcon from '../assets/media_icon'
|
||||
import MinimizeFullscreenIcon from '../assets/minimize_fullscreen_icon'
|
||||
import MissingIcon from '../assets/missing_icon'
|
||||
import MoreIcon from '../assets/more_icon'
|
||||
import NotificationsIcon from '../assets/notifications_icon'
|
||||
import PauseIcon from '../assets/pause_icon'
|
||||
import PinIcon from '../assets/pin_icon'
|
||||
import PlayIcon from '../assets/play_icon'
|
||||
import PollIcon from '../assets/poll_icon'
|
||||
import RepostIcon from '../assets/repost_icon'
|
||||
import RichTextIcon from '../assets/rich_text_icon'
|
||||
import SearchIcon from '../assets/search_icon'
|
||||
import SearchAltIcon from '../assets/search_alt_icon'
|
||||
import ShareIcon from '../assets/share_icon'
|
||||
import ShopIcon from '../assets/shop_icon'
|
||||
import SubtractIcon from '../assets/subtract_icon'
|
||||
import TrendsIcon from '../assets/trends_icon'
|
||||
import VerifiedIcon from '../assets/verified_icon'
|
||||
import WarningIcon from '../assets/warning_icon'
|
||||
|
||||
const ICONS = {
|
||||
'add': AddIcon,
|
||||
'angle-right': AngleRightIcon,
|
||||
'apps': AppsIcon,
|
||||
'audio': AudioIcon,
|
||||
'audio-mute': AudioMuteIcon,
|
||||
'back': BackIcon,
|
||||
'calendar': CalendarIcon,
|
||||
'chat': ChatIcon,
|
||||
'close': CloseIcon,
|
||||
'comment': CommentIcon,
|
||||
'dissenter': DissenterIcon,
|
||||
'ellipsis': EllipsisIcon,
|
||||
'error': ErrorIcon,
|
||||
'fullscreen': FullscreenIcon,
|
||||
'gif': GifIcon,
|
||||
'globe': GlobeIcon,
|
||||
'group': GroupIcon,
|
||||
'group-add': GroupAddIcon,
|
||||
'happy': HappyIcon,
|
||||
'home': HomeIcon,
|
||||
'like': LikeIcon,
|
||||
'link': LinkIcon,
|
||||
'list': ListIcon,
|
||||
'list-add': ListAddIcon,
|
||||
'loading': LoadingIcon,
|
||||
'media': MediaIcon,
|
||||
'minimize-fullscreen': MinimizeFullscreenIcon,
|
||||
'missing': MissingIcon,
|
||||
'more': MoreIcon,
|
||||
'notifications': NotificationsIcon,
|
||||
'pause': PauseIcon,
|
||||
'pin': PinIcon,
|
||||
'play': PlayIcon,
|
||||
'poll': PollIcon,
|
||||
'repost': RepostIcon,
|
||||
'rich-text': RichTextIcon,
|
||||
'search': SearchIcon,
|
||||
'search-alt': SearchAltIcon,
|
||||
'share': ShareIcon,
|
||||
'shop': ShopIcon,
|
||||
'subtract': SubtractIcon,
|
||||
'trends': TrendsIcon,
|
||||
'verified': VerifiedIcon,
|
||||
'warning': WarningIcon,
|
||||
'': CircleIcon,
|
||||
}
|
||||
|
||||
export default class Icon extends PureComponent {
|
||||
|
||||
@@ -12,94 +104,10 @@ export default class Icon extends PureComponent {
|
||||
render() {
|
||||
const { id, ...options } = this.props
|
||||
|
||||
switch (id) {
|
||||
case 'add':
|
||||
return <I.AddIcon {...options} />
|
||||
case 'angle-right':
|
||||
return <I.AngleRightIcon {...options} />
|
||||
case 'apps':
|
||||
return <I.AppsIcon {...options} />
|
||||
case 'audio':
|
||||
return <I.AudioIcon {...options} />
|
||||
case 'audio-mute':
|
||||
return <I.AudioMuteIcon {...options} />
|
||||
case 'back':
|
||||
return <I.BackIcon {...options} />
|
||||
case 'calendar':
|
||||
return <I.CalendarIcon {...options} />
|
||||
case 'chat':
|
||||
return <I.ChatIcon {...options} />
|
||||
case 'close':
|
||||
return <I.CloseIcon {...options} />
|
||||
case 'comment':
|
||||
return <I.CommentIcon {...options} />
|
||||
case 'dissenter':
|
||||
return <I.DissenterIcon {...options} />
|
||||
case 'ellipsis':
|
||||
return <I.EllipsisIcon {...options} />
|
||||
case 'error':
|
||||
return <I.ErrorIcon {...options} />
|
||||
case 'fullscreen':
|
||||
return <I.FullscreenIcon {...options} />
|
||||
case 'globe':
|
||||
return <I.GlobeIcon {...options} />
|
||||
case 'group':
|
||||
return <I.GroupIcon {...options} />
|
||||
case 'group-add':
|
||||
return <I.GroupAddIcon {...options} />
|
||||
case 'happy':
|
||||
return <I.HappyIcon {...options} />
|
||||
case 'home':
|
||||
return <I.HomeIcon {...options} />
|
||||
case 'like':
|
||||
return <I.LikeIcon {...options} />
|
||||
case 'link':
|
||||
return <I.LinkIcon {...options} />
|
||||
case 'list':
|
||||
return <I.ListIcon {...options} />
|
||||
case 'list-add':
|
||||
return <I.ListAddIcon {...options} />
|
||||
case 'loading':
|
||||
return <I.LoadingIcon {...options} />
|
||||
case 'media':
|
||||
return <I.MediaIcon {...options} />
|
||||
case 'minimize-fullscreen':
|
||||
return <I.MinimizeFullscreenIcon {...options} />
|
||||
case 'missing':
|
||||
return <I.MissingIcon {...options} />
|
||||
case 'more':
|
||||
return <I.MoreIcon {...options} />
|
||||
case 'notifications':
|
||||
return <I.NotificationsIcon {...options} />
|
||||
case 'pause':
|
||||
return <I.PauseIcon {...options} />
|
||||
case 'pin':
|
||||
return <I.PinIcon {...options} />
|
||||
case 'play':
|
||||
return <I.PlayIcon {...options} />
|
||||
case 'poll':
|
||||
return <I.PollIcon {...options} />
|
||||
case 'repost':
|
||||
return <I.RepostIcon {...options} />
|
||||
case 'search':
|
||||
return <I.SearchIcon {...options} />
|
||||
case 'search-alt':
|
||||
return <I.SearchAltIcon {...options} />
|
||||
case 'share':
|
||||
return <I.ShareIcon {...options} />
|
||||
case 'shop':
|
||||
return <I.ShopIcon {...options} />
|
||||
case 'subtract':
|
||||
return <I.SubtractIcon {...options} />
|
||||
case 'trends':
|
||||
return <I.TrendsIcon {...options} />
|
||||
case 'verified':
|
||||
return <I.VerifiedIcon {...options} />
|
||||
case 'warning':
|
||||
return <I.WarningIcon {...options} />
|
||||
default:
|
||||
return <I.CircleIcon {...options} />
|
||||
}
|
||||
// : todo : add all required icons
|
||||
const Asset = ICONS[id] || CircleIcon
|
||||
|
||||
return <Asset {...options} />
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,12 +115,19 @@ class IntersectionObserverArticle extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { children, id, index, listLength, cachedHeight } = this.props
|
||||
const {
|
||||
children,
|
||||
id,
|
||||
index,
|
||||
listLength,
|
||||
cachedHeight
|
||||
} = this.props
|
||||
const { isIntersecting, isHidden } = this.state
|
||||
|
||||
if (!isIntersecting && (isHidden || cachedHeight)) {
|
||||
return (
|
||||
<article
|
||||
className={[_s.outlineNone].join(' ')}
|
||||
ref={this.handleRef}
|
||||
aria-posinset={index + 1}
|
||||
aria-setsize={listLength}
|
||||
@@ -128,7 +135,10 @@ class IntersectionObserverArticle extends Component {
|
||||
data-id={id}
|
||||
tabIndex='0'
|
||||
>
|
||||
{children && React.cloneElement(children, { hidden: true })}
|
||||
{
|
||||
children &&
|
||||
React.cloneElement(children, { hidden: true })
|
||||
}
|
||||
</article>
|
||||
)
|
||||
}
|
||||
@@ -141,7 +151,10 @@ class IntersectionObserverArticle extends Component {
|
||||
data-id={id}
|
||||
tabIndex='0'
|
||||
>
|
||||
{children && React.cloneElement(children, { hidden: false })}
|
||||
{
|
||||
children &&
|
||||
React.cloneElement(children, { hidden: false })
|
||||
}
|
||||
</article>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { defineMessages, injectIntl } from 'react-intl'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import Button from '../button'
|
||||
import Text from '../text'
|
||||
import ModalLayout from './modal_layout'
|
||||
|
||||
const messages = defineMessages({
|
||||
|
||||
})
|
||||
|
||||
export default
|
||||
@injectIntl
|
||||
class EditProfileModal extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
render() {
|
||||
const { intl } = this.props
|
||||
|
||||
return (
|
||||
<ModalLayout>
|
||||
</ModalLayout>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,20 @@
|
||||
import classNames from 'classnames';
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import ReactSwipeableViews from 'react-swipeable-views';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import Video from '../../features/video';
|
||||
import ExtendedVideoPlayer from '../extended_video_player';
|
||||
import Button from '../button';
|
||||
import ImageLoader from '../image_loader';
|
||||
import Icon from '../icon';
|
||||
import classNames from 'classnames'
|
||||
import { defineMessages, injectIntl } from 'react-intl'
|
||||
import ReactSwipeableViews from 'react-swipeable-views'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import Video from '../video'
|
||||
import ExtendedVideoPlayer from '../extended_video_player'
|
||||
import Button from '../button'
|
||||
import ImageLoader from '../image_loader'
|
||||
import Icon from '../icon'
|
||||
|
||||
const messages = defineMessages({
|
||||
close: { id: 'lightbox.close', defaultMessage: 'Close' },
|
||||
previous: { id: 'lightbox.previous', defaultMessage: 'Previous' },
|
||||
next: { id: 'lightbox.next', defaultMessage: 'Next' },
|
||||
viewContext: { id: 'lightbox.view_context', defaultMessage: 'View context' },
|
||||
});
|
||||
})
|
||||
|
||||
export const previewState = 'previewMediaModal';
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
EmbedModal,
|
||||
// ListEditor,
|
||||
// ListAdder,
|
||||
StatusRevisionModal,
|
||||
StatusRevisionsModal,
|
||||
} from '../../features/ui/util/async_components'
|
||||
|
||||
import ModalBase from './modal_base'
|
||||
@@ -57,10 +57,10 @@ const MODAL_COMPONENTS = {
|
||||
LIST_EDITOR: () => Promise.resolve({ default: ListEditorModal }),
|
||||
LIST_TIMELINE_SETTINGS: () => Promise.resolve({ default: ListTimelineSettingsModal }),
|
||||
MEDIA: () => Promise.resolve({ default: MediaModal }),
|
||||
'MUTE': MuteModal,
|
||||
MUTE: MuteModal,
|
||||
PRO_UPGRADE: () => Promise.resolve({ default: ProUpgradeModal }),
|
||||
REPORT: ReportModal,
|
||||
STATUS_REVISION: () => Promise.resolve({ default: StatusRevisionModal }),
|
||||
STATUS_REVISIONS: StatusRevisionsModal,
|
||||
UNAUTHORIZED: () => Promise.resolve({ default: UnauthorizedModal }),
|
||||
UNFOLLOW: () => Promise.resolve({ default: UnfollowModal }),
|
||||
VIDEO: () => Promise.resolve({ default: VideoModal }),
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import StatusRevisionListContainer from '../../containers/status_revision_list_container'
|
||||
import Button from '../button'
|
||||
|
||||
const messages = defineMessages({
|
||||
close: { id: 'lightbox.close', defaultMessage: 'Close' },
|
||||
})
|
||||
|
||||
export default
|
||||
@injectIntl
|
||||
class StatusRevisionModal extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
status: ImmutablePropTypes.map.isRequired
|
||||
}
|
||||
|
||||
render() {
|
||||
const { intl, onClose, status } = this.props
|
||||
|
||||
return (
|
||||
<div className='modal-root__modal status-revisions-root'>
|
||||
<div className='status-revisions'>
|
||||
<div className='status-revisions__header'>
|
||||
<h3 className='status-revisions__header__title'>
|
||||
<FormattedMessage id='status_revisions.heading' defaultMessage='Revision History' />
|
||||
</h3>
|
||||
<Button className='status-revisions__close' title={intl.formatMessage(messages.close)} icon='times' onClick={onClose} size={20} />
|
||||
</div>
|
||||
<div className='status-revisions__content'>
|
||||
<StatusRevisionListContainer id={status.get('id')} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
import { defineMessages, injectIntl } from 'react-intl'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import classNames from 'classnames/bind'
|
||||
// import StatusRevisionListContainer from '../../containers/status_revisions_list_container'
|
||||
import { loadStatusRevisions } from '../../actions/status_revisions'
|
||||
import ModalLayout from './modal_layout'
|
||||
import RelativeTimestamp from '../relative_timestamp'
|
||||
import ScrollableList from '../scrollable_list'
|
||||
import Text from '../text'
|
||||
|
||||
const cx = classNames.bind(_s)
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'status_revisions.heading', defaultMessage: 'Revision History' },
|
||||
})
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
loading: state.getIn(['status_revisions', 'loading']),
|
||||
error: state.getIn(['status_revisions', 'error']),
|
||||
revisions: state.getIn(['status_revisions', 'revisions']),
|
||||
})
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onLoadStatusRevisions(statusId) {
|
||||
dispatch(loadStatusRevisions(statusId))
|
||||
},
|
||||
})
|
||||
|
||||
export default
|
||||
@injectIntl
|
||||
@connect(mapStateToProps, mapDispatchToProps)
|
||||
class StatusRevisionsModal extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
status: ImmutablePropTypes.map.isRequired,
|
||||
onLoadStatusRevisions: PropTypes.func.isRequired,
|
||||
loading: PropTypes.bool.isRequired,
|
||||
error: PropTypes.bool,
|
||||
revisions: ImmutablePropTypes.list.isRequired,
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props.onLoadStatusRevisions(this.props.status.get('id'))
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
intl,
|
||||
status,
|
||||
revisions
|
||||
} = this.props
|
||||
|
||||
console.log("revisions:", revisions)
|
||||
console.log("revisions.size:", revisions.size)
|
||||
|
||||
return (
|
||||
<ModalLayout title={intl.formatMessage(messages.title)} width='480'>
|
||||
<div className={[_s.default]}>
|
||||
<ScrollableList>
|
||||
{
|
||||
revisions.map((revision, i) => {
|
||||
const isFirst = i === 0
|
||||
const isLast = i === revisions.size - 1
|
||||
|
||||
const containerClasses = cx({
|
||||
default: 1,
|
||||
pt5: 1,
|
||||
pb10: 1,
|
||||
mt5: !isFirst,
|
||||
borderColorSecondary: !isLast,
|
||||
borderBottom1PX: !isLast,
|
||||
})
|
||||
|
||||
return (
|
||||
<div key={`status-revision-${i}`} className={containerClasses}>
|
||||
<div className={[_s.default, _s.pb5].join(' ')}>
|
||||
<Text size='medium'>
|
||||
{revision.get('text')}
|
||||
</Text>
|
||||
</div>
|
||||
<div className={[_s.default]}>
|
||||
<Text size='small' color='secondary'>
|
||||
Edited on <RelativeTimestamp timestamp={revision.get('created_at')} />
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ScrollableList>
|
||||
</div>
|
||||
</ModalLayout>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import Video from '../../features/video';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import { FormattedMessage } from 'react-intl'
|
||||
import Video from '../video'
|
||||
|
||||
export const previewState = 'previewVideoModal';
|
||||
export const previewState = 'previewVideoModal'
|
||||
|
||||
export default class VideoModal extends ImmutablePureComponent {
|
||||
|
||||
@@ -12,45 +12,45 @@ export default class VideoModal extends ImmutablePureComponent {
|
||||
status: ImmutablePropTypes.map,
|
||||
time: PropTypes.number,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
if (this.context.router) {
|
||||
const history = this.context.router.history;
|
||||
const history = this.context.router.history
|
||||
|
||||
history.push(history.location.pathname, previewState);
|
||||
history.push(history.location.pathname, previewState)
|
||||
|
||||
this.unlistenHistory = history.listen(() => {
|
||||
this.props.onClose();
|
||||
});
|
||||
this.props.onClose()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
if (this.context.router) {
|
||||
this.unlistenHistory();
|
||||
this.unlistenHistory()
|
||||
|
||||
if (this.context.router.history.location.state === previewState) {
|
||||
this.context.router.history.goBack();
|
||||
this.context.router.history.goBack()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleStatusClick = e => {
|
||||
if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||
e.preventDefault();
|
||||
this.context.router.history.push(`/${this.props.status.getIn(['account', 'acct'])}/posts/${this.props.status.get('id')}`);
|
||||
e.preventDefault()
|
||||
this.context.router.history.push(`/${this.props.status.getIn(['account', 'acct'])}/posts/${this.props.status.get('id')}`)
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { media, status, time, onClose } = this.props;
|
||||
const { media, status, time, onClose } = this.props
|
||||
|
||||
const link = status && <a href={status.get('url')} onClick={this.handleStatusClick}><FormattedMessage id='lightbox.view_context' defaultMessage='View context' /></a>;
|
||||
const link = status && <a href={status.get('url')} onClick={this.handleStatusClick}><FormattedMessage id='lightbox.view_context' defaultMessage='View context' /></a>
|
||||
|
||||
return (
|
||||
<div className='modal-root__modal video-modal'>
|
||||
@@ -67,7 +67,7 @@ export default class VideoModal extends ImmutablePureComponent {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import DisplayName from './display_name';
|
||||
import Icon from './icon';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import { FormattedMessage } from 'react-intl'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import { NavLink } from 'react-router-dom'
|
||||
import DisplayName from './display_name'
|
||||
import Icon from './icon'
|
||||
|
||||
export default class MovedNote extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
};
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
to: ImmutablePropTypes.map.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
render () {
|
||||
const { to } = this.props;
|
||||
const displayNameHtml = { __html: from.get('display_name_html') };
|
||||
const displayNameHtml = { __html: from.get('display_name_html') }
|
||||
|
||||
return (
|
||||
<div className='moved-note'>
|
||||
|
||||
@@ -1,9 +1,82 @@
|
||||
export default class DatePickerPopover extends PureComponent {
|
||||
import DatePicker from 'react-datepicker'
|
||||
import { changeScheduledAt } from '../../actions/compose'
|
||||
import { openModal } from '../../actions/modal'
|
||||
import { me } from '../../initial_state'
|
||||
import { isMobile } from '../../utils/is_mobile'
|
||||
import PopoverLayout from './popover_layout'
|
||||
|
||||
// import 'react-datepicker/dist/react-datepicker.css'
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
date: state.getIn(['compose', 'scheduled_at']),
|
||||
isPro: state.getIn(['accounts', me, 'is_pro']),
|
||||
})
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
setScheduledAt (date) {
|
||||
dispatch(changeScheduledAt(date))
|
||||
},
|
||||
|
||||
onOpenProUpgradeModal() {
|
||||
dispatch(openModal('PRO_UPGRADE'))
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
export default
|
||||
@connect(mapStateToProps, mapDispatchToProps)
|
||||
class DatePickerPopover extends PureComponent {
|
||||
static propTypes = {
|
||||
date: PropTypes.instanceOf(Date),
|
||||
setScheduledAt: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
isPro: PropTypes.bool,
|
||||
onOpenProUpgradeModal: PropTypes.func.isRequired,
|
||||
position: PropTypes.string,
|
||||
small: PropTypes.bool,
|
||||
}
|
||||
|
||||
handleSetDate = (date) => {
|
||||
this.props.setScheduledAt(date)
|
||||
}
|
||||
|
||||
render() {
|
||||
const { date, isPro, position } = this.props
|
||||
|
||||
const open = !!date
|
||||
const datePickerDisabled = !isPro
|
||||
const withPortal = isMobile(window.innerWidth)
|
||||
const popperPlacement = position || undefined
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ /* */ }
|
||||
</div>
|
||||
<PopoverLayout>
|
||||
<DatePicker
|
||||
target={this}
|
||||
className='schedule-post-dropdown__datepicker'
|
||||
minDate={new Date()}
|
||||
selected={date}
|
||||
onChange={date => this.handleSetDate(date)}
|
||||
timeFormat='p'
|
||||
timeIntervals={15}
|
||||
timeCaption='Time'
|
||||
dateFormat='MMM d, yyyy h:mm aa'
|
||||
disabled={datePickerDisabled}
|
||||
showTimeSelect
|
||||
withPortal={withPortal}
|
||||
popperPlacement={popperPlacement}
|
||||
popperModifiers={{
|
||||
offset: {
|
||||
enabled: true,
|
||||
offset: '0px, 5px'
|
||||
},
|
||||
preventOverflow: {
|
||||
enabled: true,
|
||||
escapeWithReference: false,
|
||||
boundariesElement: 'viewport'
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</PopoverLayout>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import { changeSetting } from '../../actions/settings'
|
||||
import { useEmoji } from '../../actions/emojis'
|
||||
import { EmojiPicker as EmojiPickerAsync } from '../../features/ui/util/async_components'
|
||||
import { buildCustomEmojis } from '../emoji/emoji'
|
||||
import PopoverLayout from './popover_layout'
|
||||
|
||||
const messages = defineMessages({
|
||||
emoji: { id: 'emoji_button.label', defaultMessage: 'Insert emoji' },
|
||||
@@ -430,21 +431,23 @@ class EmojiPickerPopover extends ImmutablePureComponent {
|
||||
frequentlyUsedEmojis
|
||||
} = this.props
|
||||
|
||||
const { active, loading } = this.state;
|
||||
const { active, loading } = this.state
|
||||
|
||||
return (
|
||||
<div className='emoji-picker-dropdown' onKeyDown={this.handleKeyDown}>
|
||||
<EmojiPickerMenu
|
||||
custom_emojis={this.props.custom_emojis}
|
||||
loading={loading}
|
||||
onClose={this.onHideDropdown}
|
||||
onPick={onPickEmoji}
|
||||
onSkinTone={onSkinTone}
|
||||
skinTone={skinTone}
|
||||
frequentlyUsedEmojis={frequentlyUsedEmojis}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
<PopoverLayout>
|
||||
<div className={_s.emojiMart} onKeyDown={this.handleKeyDown}>
|
||||
<EmojiPickerMenu
|
||||
custom_emojis={this.props.custom_emojis}
|
||||
loading={loading}
|
||||
onClose={this.onHideDropdown}
|
||||
onPick={onPickEmoji}
|
||||
onSkinTone={onSkinTone}
|
||||
skinTone={skinTone}
|
||||
frequentlyUsedEmojis={frequentlyUsedEmojis}
|
||||
/>
|
||||
</div>
|
||||
</PopoverLayout>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { HotKeys } from 'react-hotkeys';
|
||||
import classNames from 'classnames/bind'
|
||||
import { displayMedia } from '../../initial_state';
|
||||
import Card from '../../features/status/components/card';
|
||||
import StatusCard from '../status_card'
|
||||
import { MediaGallery, Video } from '../../features/ui/util/async_components';
|
||||
import ComposeFormContainer from '../../features/compose/containers/compose_form_container'
|
||||
import Avatar from '../avatar';
|
||||
@@ -425,7 +425,7 @@ class Status extends ImmutablePureComponent {
|
||||
} else if (status.get('spoiler_text').length === 0 && status.get('card')) {
|
||||
// console.log("card:", status.get('card'))
|
||||
media = (
|
||||
<Card
|
||||
<StatusCard
|
||||
onOpenMedia={this.props.onOpenMedia}
|
||||
card={status.get('card')}
|
||||
cacheWidth={this.props.cacheMediaWidth}
|
||||
|
||||
@@ -1,57 +1,56 @@
|
||||
import Immutable from 'immutable';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import punycode from 'punycode';
|
||||
import classnames from 'classnames';
|
||||
import Icon from './icon';
|
||||
import Immutable from 'immutable'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import punycode from 'punycode'
|
||||
import Icon from './icon'
|
||||
|
||||
const IDNA_PREFIX = 'xn--';
|
||||
const IDNA_PREFIX = 'xn--'
|
||||
|
||||
const decodeIDNA = domain => {
|
||||
return domain
|
||||
.split('.')
|
||||
.map(part => part.indexOf(IDNA_PREFIX) === 0 ? punycode.decode(part.slice(IDNA_PREFIX.length)) : part)
|
||||
.join('.');
|
||||
};
|
||||
.join('.')
|
||||
}
|
||||
|
||||
const getHostname = url => {
|
||||
const parser = document.createElement('a');
|
||||
parser.href = url;
|
||||
return parser.hostname;
|
||||
};
|
||||
const parser = document.createElement('a')
|
||||
parser.href = url
|
||||
return parser.hostname
|
||||
}
|
||||
|
||||
const trim = (text, len) => {
|
||||
const cut = text.indexOf(' ', len);
|
||||
const cut = text.indexOf(' ', len)
|
||||
|
||||
if (cut === -1) {
|
||||
return text;
|
||||
return text
|
||||
}
|
||||
|
||||
return text.substring(0, cut) + (text.length > len ? '…' : '');
|
||||
};
|
||||
return text.substring(0, cut) + (text.length > len ? '…' : '')
|
||||
}
|
||||
|
||||
const domParser = new DOMParser();
|
||||
const domParser = new DOMParser()
|
||||
|
||||
const addAutoPlay = html => {
|
||||
const document = domParser.parseFromString(html, 'text/html').documentElement;
|
||||
const iframe = document.querySelector('iframe');
|
||||
const document = domParser.parseFromString(html, 'text/html').documentElement
|
||||
const iframe = document.querySelector('iframe')
|
||||
|
||||
if (iframe) {
|
||||
if (iframe.src.indexOf('?') !== -1) {
|
||||
iframe.src += '&';
|
||||
iframe.src += '&'
|
||||
} else {
|
||||
iframe.src += '?';
|
||||
iframe.src += '?'
|
||||
}
|
||||
|
||||
iframe.src += 'autoplay=1&auto_play=1';
|
||||
iframe.src += 'autoplay=1&auto_play=1'
|
||||
|
||||
// DOM parser creates html/body elements around original HTML fragment,
|
||||
// so we need to get innerHTML out of the body and not the entire document
|
||||
return document.querySelector('body').innerHTML;
|
||||
return document.querySelector('body').innerHTML
|
||||
}
|
||||
|
||||
return html;
|
||||
};
|
||||
return html
|
||||
}
|
||||
|
||||
export default class Card extends ImmutablePureComponent {
|
||||
|
||||
@@ -60,24 +59,21 @@ export default class Card extends ImmutablePureComponent {
|
||||
onOpenMedia: PropTypes.func.isRequired,
|
||||
defaultWidth: PropTypes.number,
|
||||
cacheWidth: PropTypes.func,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
};
|
||||
}
|
||||
|
||||
state = {
|
||||
width: this.props.defaultWidth || 280,
|
||||
embedded: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (!Immutable.is(this.props.card, nextProps.card)) {
|
||||
this.setState({ embedded: false });
|
||||
this.setState({ embedded: false })
|
||||
}
|
||||
}
|
||||
|
||||
handlePhotoClick = () => {
|
||||
const { card, onOpenMedia } = this.props;
|
||||
const { card, onOpenMedia } = this.props
|
||||
|
||||
onOpenMedia(
|
||||
Immutable.fromJS([
|
||||
@@ -94,23 +90,23 @@ export default class Card extends ImmutablePureComponent {
|
||||
},
|
||||
]),
|
||||
0
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
handleEmbedClick = () => {
|
||||
const { card } = this.props;
|
||||
const { card } = this.props
|
||||
|
||||
if (card.get('type') === 'photo') {
|
||||
this.handlePhotoClick();
|
||||
this.handlePhotoClick()
|
||||
} else {
|
||||
this.setState({ embedded: true });
|
||||
this.setState({ embedded: true })
|
||||
}
|
||||
}
|
||||
|
||||
setRef = c => {
|
||||
if (c) {
|
||||
if (this.props.cacheWidth) this.props.cacheWidth(c.offsetWidth);
|
||||
this.setState({ width: c.offsetWidth });
|
||||
if (this.props.cacheWidth) this.props.cacheWidth(c.offsetWidth)
|
||||
this.setState({ width: c.offsetWidth })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,12 @@ import Button from './button'
|
||||
import Avatar from './avatar'
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
onOpenStatusRevisionsPopover(status) {
|
||||
dispatch(openModal('STATUS_REVISIONS', {
|
||||
status,
|
||||
}))
|
||||
},
|
||||
|
||||
onOpenStatusOptionsPopover(targetRef, status) {
|
||||
dispatch(openPopover('STATUS_OPTIONS', {
|
||||
targetRef,
|
||||
@@ -28,6 +34,7 @@ class StatusHeader extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
status: ImmutablePropTypes.map,
|
||||
onOpenStatusRevisionsPopover: PropTypes.func.isRequired,
|
||||
onOpenStatusOptionsPopover: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
@@ -36,12 +43,7 @@ class StatusHeader extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
handleOpenStatusEdits = () => {
|
||||
// : todo :
|
||||
this.props.dispatch(openPopover('REPOST', {
|
||||
targetRef: this.statusOptionsButton,
|
||||
position: 'top',
|
||||
status: this.props.status,
|
||||
}))
|
||||
this.props.onOpenStatusRevisionsPopover(this.props.status)
|
||||
}
|
||||
|
||||
handleDeleteClick = () => {
|
||||
|
||||
@@ -5,7 +5,7 @@ import RelativeTimestamp from './relative_timestamp';
|
||||
|
||||
export default
|
||||
@injectIntl
|
||||
class StatusRevisionsList extends ImmutablePureComponent {
|
||||
class StatusRevisionItem extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
loading: PropTypes.bool.isRequired,
|
||||
@@ -3,11 +3,11 @@ import { is } from 'immutable'
|
||||
import { throttle } from 'lodash'
|
||||
import classNames from 'classnames/bind'
|
||||
import { decode } from 'blurhash'
|
||||
import { isFullscreen, requestFullscreen, exitFullscreen } from '../../utils/fullscreen'
|
||||
import { isPanoramic, isPortrait, minimumAspectRatio, maximumAspectRatio } from '../../utils/media_aspect_ratio'
|
||||
import { displayMedia } from '../../initial_state'
|
||||
import Button from '../../components/button'
|
||||
import Text from '../../components/text'
|
||||
import { isFullscreen, requestFullscreen, exitFullscreen } from '../utils/fullscreen'
|
||||
import { isPanoramic, isPortrait, minimumAspectRatio, maximumAspectRatio } from '../utils/media_aspect_ratio'
|
||||
import { displayMedia } from '../initial_state'
|
||||
import Button from './button'
|
||||
import Text from './text'
|
||||
|
||||
const cx = classNames.bind(_s)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user