This commit is contained in:
mgabdev 2020-03-27 18:57:03 -04:00
parent 0532890c0e
commit bc631b65db
40 changed files with 689 additions and 607 deletions

View File

@ -1,16 +0,0 @@
import api from '../api';
export const STATUS_REVISION_LIST_LOAD = 'STATUS_REVISION_LIST';
export const STATUS_REVISION_LIST_LOAD_SUCCESS = 'STATUS_REVISION_LIST_SUCCESS';
export const STATUS_REVISION_LIST_LOAD_FAIL = 'STATUS_REVISION_LIST_FAIL';
const loadSuccess = data => ({ type: STATUS_REVISION_LIST_LOAD_SUCCESS, payload: data });
const loadFail = e => ({ type: STATUS_REVISION_LIST_LOAD_FAIL, payload: e });
export function load(statusId) {
return (dispatch, getState) => {
api(getState).get(`/api/v1/statuses/${statusId}/revisions`)
.then(res => dispatch(loadSuccess(res.data)))
.catch(e => dispatch(loadFail(e)));
};
}

View File

@ -0,0 +1,23 @@
import api from '../api'
export const STATUS_REVISIONS_LOAD = 'STATUS_REVISIONS_LOAD'
export const STATUS_REVISIONS_LOAD_SUCCESS = 'STATUS_REVISIONS_SUCCESS'
export const STATUS_REVISIONS_LOAD_FAIL = 'STATUS_REVISIONS_FAIL'
const loadStatusRevisionsSuccess = (data) => ({
type: STATUS_REVISIONS_LOAD_SUCCESS,
revisions: data,
})
const loadStatusRevisionsFail = () => ({
type: STATUS_REVISIONS_LOAD_FAIL,
error: true,
})
export function loadStatusRevisions(statusId) {
return (dispatch, getState) => {
api(getState).get(`/api/v1/statuses/${statusId}/revisions`)
.then(res => dispatch(loadStatusRevisionsSuccess(res.data)))
.catch(() => dispatch(loadStatusRevisionsFail()))
}
}

View File

@ -0,0 +1,26 @@
const GifIcon = ({
className = '',
width = '26px',
height = '26px',
viewBox = '0 0 64 64',
title = 'Gif',
}) => (
<svg
className={className}
version='1.1'
xmlns='http://www.w3.org/2000/svg'
x='0px'
y='0px'
width={width}
height={height}
viewBox={viewBox}
xmlSpace='preserve'
aria-label={title}
>
<g>
<path d="M 14.0625 0.117188 C 9.851562 1.078125 7.410156 2.339844 4.867188 4.867188 C 2.746094 7.007812 1.28125 9.53125 0.472656 12.464844 L 0.0859375 13.894531 L 0.0859375 50.105469 L 0.472656 51.535156 C 1.28125 54.46875 2.746094 56.992188 4.867188 59.132812 C 7.007812 61.253906 9.53125 62.71875 12.464844 63.527344 L 13.894531 63.914062 L 50.105469 63.914062 L 51.535156 63.527344 C 54.46875 62.71875 56.992188 61.253906 59.132812 59.132812 C 61.253906 56.992188 62.71875 54.46875 63.527344 51.535156 L 63.914062 50.105469 L 63.914062 13.894531 L 63.527344 12.464844 C 62.71875 9.53125 61.253906 7.007812 59.132812 4.867188 C 57.027344 2.777344 54.550781 1.332031 51.621094 0.472656 L 50.273438 0.0859375 L 32.335938 0.0664062 C 22.46875 0.0507812 14.25 0.0664062 14.0625 0.117188 Z M 22.316406 22.148438 C 23.898438 22.550781 25.683594 23.511719 26.746094 24.523438 L 27.707031 25.429688 L 25.953125 27.082031 L 24.203125 28.734375 L 23.613281 28.09375 C 23.308594 27.753906 22.617188 27.25 22.097656 26.996094 C 21.253906 26.59375 20.933594 26.542969 19.535156 26.527344 C 18.257812 26.527344 17.785156 26.59375 17.160156 26.878906 C 16.035156 27.402344 15.074219 28.34375 14.535156 29.441406 C 14.128906 30.28125 14.0625 30.601562 14.0625 31.914062 C 14.0625 33.113281 14.148438 33.601562 14.433594 34.222656 C 14.886719 35.234375 16.015625 36.363281 17.09375 36.882812 C 17.820312 37.238281 18.171875 37.289062 19.707031 37.304688 C 22.347656 37.304688 22.230469 37.441406 22.230469 34.191406 L 22.230469 31.496094 L 27.621094 31.496094 L 27.621094 39.746094 L 26.410156 40.355469 C 23.074219 42.039062 19.46875 42.644531 16.625 42.003906 C 10.898438 40.707031 7.34375 35.417969 8.503906 29.894531 C 9.296875 26.121094 12.546875 22.839844 16.335938 21.980469 C 17.683594 21.675781 20.832031 21.761719 22.316406 22.148438 Z M 36.714844 32.085938 L 36.714844 42.105469 L 30.652344 42.105469 L 30.652344 22.0625 L 36.714844 22.0625 Z M 55.914062 24.421875 L 55.914062 26.777344 L 46.316406 26.777344 L 46.316406 30.652344 L 54.738281 30.652344 L 54.738281 35.367188 L 46.316406 35.367188 L 46.316406 42.105469 L 40.253906 42.105469 L 40.253906 22.0625 L 55.914062 22.0625 Z M 55.914062 24.421875 "/>
</g>
</svg>
)
export default GifIcon

View File

@ -1,89 +0,0 @@
import AddIcon from './add_icon'
import AngleRightIcon from './angle_right_icon'
import AppsIcon from './apps_icon'
import AudioIcon from './audio_icon'
import AudioMuteIcon from './audio_mute_icon'
import BackIcon from './back_icon'
import CalendarIcon from './calendar_icon'
import ChatIcon from './chat_icon'
import CircleIcon from './circle_icon'
import CloseIcon from './close_icon'
import CommentIcon from './comment_icon'
import DissenterIcon from './dissenter_icon'
import EllipsisIcon from './ellipsis_icon'
import ErrorIcon from './error_icon'
import FullscreenIcon from './fullscreen_icon'
import GlobeIcon from './globe_icon'
import GroupIcon from './group_icon'
import GroupAddIcon from './group_add_icon'
import HappyIcon from './happy_icon'
import HomeIcon from './home_icon'
import LikeIcon from './like_icon'
import LinkIcon from './link_icon'
import ListIcon from './list_icon'
import ListAddIcon from './list_add_icon'
import LoadingIcon from './loading_icon'
import MediaIcon from './media_icon'
import MinimizeFullscreenIcon from './minimize_fullscreen_icon'
import MissingIcon from './missing_icon'
import MoreIcon from './more_icon'
import NotificationsIcon from './notifications_icon'
import PauseIcon from './pause_icon'
import PinIcon from './pin_icon'
import PlayIcon from './play_icon'
import PollIcon from './poll_icon'
import RepostIcon from './repost_icon'
import SearchIcon from './search_icon'
import SearchAltIcon from './search_alt_icon'
import ShareIcon from './share_icon'
import ShopIcon from './shop_icon'
import SubtractIcon from './subtract_icon'
import TrendsIcon from './trends_icon'
import VerifiedIcon from './verified_icon'
import WarningIcon from './warning_icon'
export {
AddIcon,
AngleRightIcon,
AppsIcon,
AudioIcon,
AudioMuteIcon,
BackIcon,
CalendarIcon,
ChatIcon,
CircleIcon,
CloseIcon,
CommentIcon,
DissenterIcon,
EllipsisIcon,
ErrorIcon,
FullscreenIcon,
GlobeIcon,
GroupIcon,
GroupAddIcon,
HappyIcon,
HomeIcon,
LikeIcon,
LinkIcon,
ListIcon,
ListAddIcon,
LoadingIcon,
MediaIcon,
MinimizeFullscreenIcon,
MissingIcon,
MoreIcon,
NotificationsIcon,
PauseIcon,
PinIcon,
PlayIcon,
PollIcon,
RepostIcon,
SearchIcon,
SearchAltIcon,
ShareIcon,
ShopIcon,
SubtractIcon,
TrendsIcon,
VerifiedIcon,
WarningIcon,
}

View File

@ -2,7 +2,7 @@ const MediaIcon = ({
className = '',
width = '16px',
height = '16px',
viewBox = '0 0 48 48',
viewBox = '0 0 64 64',
title = 'Media',
}) => (
<svg
@ -18,10 +18,8 @@ const MediaIcon = ({
aria-label={title}
>
<g>
<path d='M 24.87 37.95 C 23.47 37.93 22.13 37.43 21 36.54 L 14 30.33 C 12.91 29.3 11.21 29.21 10 30.14 L 0 37.5 L 0 42.28 C -0 43.56 0.91 44.66 2.2 44.73 C 2.26 44.74 2.33 44.74 2.39 44.73 L 37.85 44.73 C 39.27 44.73 40.69 43.7 40.69 42.28 L 40.69 29.75 L 27.97 37.11 C 27 37.68 25.96 37.97 24.87 37.95 Z M 24.87 37.95' />
<path d='M 27.19 22.58 C 27.19 24.15 25.92 25.43 24.35 25.43 C 22.78 25.43 21.51 24.15 21.51 22.58 C 21.51 21 22.78 19.74 24.35 19.74 C 25.92 19.74 27.19 21 27.19 22.58 Z M 27.19 22.58' />
<path d='M 47.41 8.31 C 47 7.75 46.36 7.39 45.66 7.34 L 10.46 3.27 C 9.78 3.21 9 3.4 8.53 3.79 C 8 4.23 7.69 4.82 7.56 5.47 L 6.98 10.51 L 37.85 10.51 C 40.79 10.57 43.17 12.93 43.27 15.87 L 43.27 38.66 C 43.27 38.54 43.86 38.41 44.11 38.15 C 44.65 37.73 44.96 37 44.95 36.41 L 47.99 10.18 C 48 9.51 47.84 8.83 47.41 8.31 Z M 47.41 8.31' />
<path d='M 37.85 13 L 2.39 13 C 0.97 13 0 14.45 0 15.87 L 0 34.27 L 8.53 28 C 10.69 26.47 13.69 26.6 15.7 28.39 L 22.8 34.6 C 23.87 35.5 25.4 35.64 26.61 34.92 L 40.69 26.72 L 40.69 15.87 C 40.59 14.35 39.37 13.15 37.85 13 Z M 24.35 28 C 21.36 28 18.93 25.58 18.93 22.58 C 18.93 19.59 21.36 17.16 24.35 17.16 C 27.35 17.16 29.78 19.59 29.78 22.58 C 29.78 25.58 27.35 28 24.35 28 Z M 24.35 28' />
<path d='M 58 0 L 6 0 C 2.6875 0 0 2.6875 0 6 L 0 58 C 0 61.3125 2.6875 64 6 64 L 58 64 C 61.3125 64 64 61.3125 64 58 L 64 6 C 64 2.6875 61.3125 0 58 0 Z M 60 39.179688 L 51.421875 30.601562 C 50.640625 29.824219 49.378906 29.824219 48.601562 30.601562 L 38 41.179688 L 25.421875 28.601562 C 24.640625 27.824219 23.378906 27.824219 22.601562 28.601562 L 4 47.179688 L 4 6 C 4 4.894531 4.894531 4 6 4 L 58 4 C 59.105469 4 60 4.894531 60 6 Z M 60 39.179688' />
<circle cx='42' cy='16' r='6' />
</g>
</svg>
)

View File

@ -0,0 +1,26 @@
const RichTextIcon = ({
className = '',
width = '26px',
height = '26px',
viewBox = '0 0 64 64',
title = 'Rich Text',
}) => (
<svg
className={className}
version='1.1'
xmlns='http://www.w3.org/2000/svg'
x='0px'
y='0px'
width={width}
height={height}
viewBox={viewBox}
xmlSpace='preserve'
aria-label={title}
>
<g>
<path d='M 47.558594 64 L 16.441406 64 L 16.441406 61.351562 C 17.808594 60.863281 19.191406 60.4375 20.589844 60.070312 C 21.988281 59.703125 23.496094 59.402344 25.113281 59.152344 L 25.113281 4.753906 L 13.3125 4.753906 L 8.371094 17.832031 L 6.042969 17.832031 C 5.855469 16.613281 5.699219 15.222656 5.574219 13.667969 C 5.449219 12.121094 5.34375 10.535156 5.246094 8.914062 C 5.152344 7.308594 5.078125 5.714844 5.019531 4.164062 C 4.953125 2.617188 4.921875 1.222656 4.921875 0 L 59.078125 0 C 59.078125 1.222656 59.042969 2.597656 58.980469 4.117188 C 58.917969 5.640625 58.839844 7.210938 58.742188 8.824219 C 58.644531 10.4375 58.558594 12.023438 58.460938 13.582031 C 58.367188 15.132812 58.226562 16.554688 58.039062 17.832031 L 55.585938 17.832031 L 50.6875 4.753906 L 38.980469 4.753906 L 38.980469 59.15625 C 40.59375 59.464844 42.105469 59.769531 43.5 60.078125 C 44.902344 60.378906 46.25 60.800781 47.558594 61.355469 Z M 47.558594 64' />
</g>
</svg>
)
export default RichTextIcon

View File

@ -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}

View File

@ -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'

View File

@ -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} />
}
}

View File

@ -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>
)
}

View File

@ -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>
)
}
}

View File

@ -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';

View File

@ -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 }),

View File

@ -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>
)
}
}

View File

@ -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>
)
}
}

View File

@ -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>
);
)
}
}

View File

@ -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'>

View File

@ -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>
)
}
}

View File

@ -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>
)
}
}

View File

@ -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}

View File

@ -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 })
}
}

View File

@ -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 = () => {

View File

@ -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,

View File

@ -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)

View File

@ -1,19 +1,19 @@
import { Fragment } from 'react';
import ReactDOM from 'react-dom';
import { List as ImmutableList, fromJS } from 'immutable';
import { IntlProvider, addLocaleData } from 'react-intl';
import { getLocale } from '../locales';
import Video from '../features/video';
import Card from '../features/status/components/card';
import Poll from '../components/poll';
import MediaGallery from '../components/media_gallery';
import ModalRoot from '../components/modal/modal_root';
import MediaModal from '../components/modal/media_modal';
import { Fragment } from 'react'
import ReactDOM from 'react-dom'
import { List as ImmutableList, fromJS } from 'immutable'
import { IntlProvider, addLocaleData } from 'react-intl'
import { getLocale } from '../locales'
import Video from '../components/video'
import StatusCard from '../components/status_card'
import Poll from '../components/poll'
import MediaGallery from '../components/media_gallery'
import ModalRoot from '../components/modal/modal_root'
import MediaModal from '../components/modal/media_modal'
const { localeData, messages } = getLocale();
addLocaleData(localeData);
const MEDIA_COMPONENTS = { MediaGallery, Video, Card, Poll };
const MEDIA_COMPONENTS = { MediaGallery, Video, StatusCard, Poll };
export default class MediaContainer extends PureComponent {

View File

@ -1,25 +0,0 @@
import ImmutablePureComponent from 'react-immutable-pure-component'
import { load } from '../actions/status_revision_list'
import StatusRevisionList from '../components/status_revision_list'
class StatusRevisionListContainer extends ImmutablePureComponent {
componentDidMount() {
this.props.load(this.props.id)
}
render() {
return <StatusRevisionList {...this.props} />
}
}
const mapStateToProps = state => ({
loading: state.getIn(['status_revision_list', 'loading']),
error: state.getIn(['status_revision_list', 'error']),
data: state.getIn(['status_revision_list', 'data']),
})
const mapDispatchToProps = {
load
}
export default connect(mapStateToProps, mapDispatchToProps)(StatusRevisionListContainer)

View File

@ -3,19 +3,19 @@ import ImmutablePureComponent from 'react-immutable-pure-component'
import { length } from 'stringz'
import ImmutablePropTypes from 'react-immutable-proptypes'
import classNames from 'classnames/bind'
import CharacterCounter from '../character_counter'
import CharacterCounter from '../../../../components/character_counter'
import UploadForm from '../upload_form'
import ReplyIndicatorContainer from '../../containers/reply_indicator_container'
import AutosuggestTextbox from '../../../../components/autosuggest_textbox'
import PollButton from '../../components/poll_button'
import UploadButton from '../../components/upload_button'
import UploadButton from '../media_upload_button'
import SpoilerButton from '../../components/spoiler_button'
import RichTextEditorButton from '../../components/rich_text_editor_button'
import GifSelectorButton from '../../components/gif_selector_button'
import StatusVisibilityButton from '../../components/status_visibility_button'
import EmojiPickerButton from '../../components/emoji_picker_button'
import PollFormContainer from '../../containers/poll_form_container'
import SchedulePostDropdown from '../../components/schedule_post_dropdown'
import SchedulePostButton from '../schedule_post_button'
import QuotedStatusPreviewContainer from '../../containers/quoted_status_preview_container'
import Icon from '../../../../components/icon'
import Button from '../../../../components/button'
@ -347,11 +347,13 @@ class ComposeForm extends ImmutablePureComponent {
<StatusVisibilityButton small={shouldCondense} />
}
<SpoilerButton small={shouldCondense} />
<SchedulePostDropdown small={shouldCondense} position={isModalOpen ? 'top' : undefined} />
<SchedulePostButton small={shouldCondense} />
<GifSelectorButton small={shouldCondense} />
<EmojiPickerButton small={shouldCondense} />
</div>
<CharacterCounter max={maxPostCharacterCount} text={text} small={shouldCondense} />
{
!shouldCondense &&
<Button

View File

@ -42,7 +42,7 @@ class SpoilerButton extends PureComponent {
return (
<ComposeExtraButton
title={intl.formatMessage(messages.title)}
icon='letter-t'
icon='rich-text'
onClick={this.handleClick}
small={small}
active={active}

View File

@ -0,0 +1,68 @@
import { injectIntl, defineMessages } from 'react-intl'
import { openModal } from '../../../actions/modal'
import { openPopover } from '../../../actions/popover'
import { me } from '../../../initial_state'
import ComposeExtraButton from './compose_extra_button'
// import 'react-datepicker/dist/react-datepicker.css'
const messages = defineMessages({
schedule_status: { id: 'schedule_status.title', defaultMessage: 'Schedule' },
})
const mapStateToProps = state => ({
isPro: state.getIn(['accounts', me, 'is_pro']),
})
const mapDispatchToProps = dispatch => ({
onOpenDatePickerPopover(targetRef) {
dispatch(openPopover('DATE_PICKER', {
targetRef,
}))
},
onOpenProUpgradeModal() {
dispatch(openModal('PRO_UPGRADE'))
},
})
export default
@injectIntl
@connect(mapStateToProps, mapDispatchToProps)
class SchedulePostDropdown extends PureComponent {
static propTypes = {
intl: PropTypes.object.isRequired,
isPro: PropTypes.bool,
onOpenProUpgradeModal: PropTypes.func.isRequired,
onOpenDatePickerPopover: PropTypes.func.isRequired,
small: PropTypes.bool,
}
handleToggle = () => {
if (!this.props.isPro) {
this.props.onOpenProUpgradeModal()
} else {
this.props.onOpenDatePickerPopover(this.button)
}
}
setButton = (n) => {
this.button = n
}
render () {
const { intl, small } = this.props
return (
<ComposeExtraButton
icon='calendar'
title={intl.formatMessage(messages.schedule_status)}
onClick={this.handleToggle}
small={small}
buttonRef={this.setButton}
/>
)
}
}

View File

@ -1,128 +0,0 @@
import { injectIntl, defineMessages } from 'react-intl'
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 ComposeExtraButton from './compose_extra_button'
// import 'react-datepicker/dist/react-datepicker.css'
const messages = defineMessages({
schedule_status: { id: 'schedule_status.title', defaultMessage: 'Schedule' },
})
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'))
},
})
class DatePickerWrapper extends PureComponent {
static propTypes = {
value: PropTypes.string,
onClick: PropTypes.func,
}
render() {
const { value, onClick } = this.props
return (
<button className='schedule-post-dropdown-wrapper' onClick={onClick}>
{value}
</button>
)
}
}
export default
@injectIntl
@connect(mapStateToProps, mapDispatchToProps)
class SchedulePostDropdown 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,
}
handleToggle = () => {
if (!this.props.isPro) {
return this.props.onOpenProUpgradeModal()
}
const { date } = this.props
const value = date ? null : new Date()
this.handleSetDate(value)
}
handleSetDate = (date) => {
this.props.setScheduledAt(date)
}
render () {
const { intl, date, isPro, position, small } = this.props
const open = !!date
const datePickerDisabled = !isPro
const withPortal = isMobile(window.innerWidth)
const popperPlacement = position || undefined
return (
<div className={[].join(' ')}>
<div className={[].join(' ')}>
<ComposeExtraButton
icon='calendar'
title={intl.formatMessage(messages.schedule_status)}
onClick={this.handleToggle}
small={small}
/>
</div>
{
open &&
<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
customInput={<DatePickerWrapper />}
withPortal={withPortal}
popperPlacement={popperPlacement}
popperModifiers={{
offset: {
enabled: true,
offset: '0px, 5px'
},
preventOverflow: {
enabled: true,
escapeWithReference: false,
boundariesElement: 'viewport'
}
}}
/>
}
</div>
)
}
}

View File

@ -1,7 +1,7 @@
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ProgressBar from '../../../../components/progress_bar'
import Upload from '../upload'
import Upload from '../media_upload_item'
import SensitiveMediaButton from '../sensitive_media_button'
const mapStateToProps = state => ({

View File

@ -126,10 +126,10 @@ export function Status() {
return import(/* webpackChunkName: "features/status" */'../../status')
}
export function StatusRevisionModal() {
return import(/* webpackChunkName: "modals/mute_modal" */'../../../components/modal/status_revision_modal')
export function StatusRevisionsModal() {
return import(/* webpackChunkName: "modals/status_revisions_modal" */'../../../components/modal/status_revisions_modal')
}
export function Video() {
return import(/* webpackChunkName: "features/video" */'../../video')
return import(/* webpackChunkName: "features/video" */'../../../components/video')
}

View File

@ -1,42 +1,42 @@
import { combineReducers } from 'redux-immutable';
import popover from './popover';
import timelines from './timelines';
import meta from './meta';
import { loadingBarReducer } from 'react-redux-loading-bar';
import modal from './modal';
import user_lists from './user_lists';
import domain_lists from './domain_lists';
import accounts from './accounts';
import accounts_counters from './accounts_counters';
import statuses from './statuses';
import relationships from './relationships';
import settings from './settings';
import push_notifications from './push_notifications';
import status_lists from './status_lists';
import mutes from './mutes';
import reports from './reports';
import contexts from './contexts';
import compose from './compose';
import search from './search';
import media_attachments from './media_attachments';
import notifications from './notifications';
import height_cache from './height_cache';
import custom_emojis from './custom_emojis';
import lists from './lists';
import listEditor from './list_editor';
import listAdder from './list_adder';
import filters from './filters';
import conversations from './conversations';
import suggestions from './suggestions';
import polls from './polls';
import identity_proofs from './identity_proofs';
import hashtags from './hashtags';
import groups from './groups';
import group_relationships from './group_relationships';
import group_lists from './group_lists';
import group_editor from './group_editor';
import sidebar from './sidebar';
import status_revision_list from './status_revision_list';
import { combineReducers } from 'redux-immutable'
import popover from './popover'
import timelines from './timelines'
import meta from './meta'
import { loadingBarReducer } from 'react-redux-loading-bar'
import modal from './modal'
import user_lists from './user_lists'
import domain_lists from './domain_lists'
import accounts from './accounts'
import accounts_counters from './accounts_counters'
import statuses from './statuses'
import relationships from './relationships'
import settings from './settings'
import push_notifications from './push_notifications'
import status_lists from './status_lists'
import mutes from './mutes'
import reports from './reports'
import contexts from './contexts'
import compose from './compose'
import search from './search'
import media_attachments from './media_attachments'
import notifications from './notifications'
import height_cache from './height_cache'
import custom_emojis from './custom_emojis'
import lists from './lists'
import listEditor from './list_editor'
import listAdder from './list_adder'
import filters from './filters'
import conversations from './conversations'
import suggestions from './suggestions'
import polls from './polls'
import identity_proofs from './identity_proofs'
import hashtags from './hashtags'
import groups from './groups'
import group_relationships from './group_relationships'
import group_lists from './group_lists'
import group_editor from './group_editor'
import sidebar from './sidebar'
import status_revisions from './status_revisions'
const reducers = {
popover,
@ -76,7 +76,7 @@ const reducers = {
group_lists,
group_editor,
sidebar,
status_revision_list,
};
status_revisions,
}
export default combineReducers(reducers);
export default combineReducers(reducers)

View File

@ -1,31 +0,0 @@
import { Map as ImmutableMap } from 'immutable';
import {
STATUS_REVISION_LIST_LOAD,
STATUS_REVISION_LIST_LOAD_SUCCESS,
STATUS_REVISION_LIST_LOAD_FAIL
} from '../actions/status_revision_list';
const initialState = ImmutableMap({
loading: false,
error: null,
data: null
});
export default function statusRevisionList(state = initialState, action) {
switch(action.type) {
case STATUS_REVISION_LIST_LOAD:
return initialState;
case STATUS_REVISION_LIST_LOAD_SUCCESS:
return state.withMutations(mutable => {
mutable.set('loading', false);
mutable.set('data', action.payload);
});
case STATUS_REVISION_LIST_LOAD_FAIL:
return state.withMutations(mutable => {
mutable.set('loading', false);
mutable.set('error', action.payload);
});
default:
return state;
}
};

View File

@ -0,0 +1,31 @@
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'
import {
STATUS_REVISIONS_LOAD,
STATUS_REVISIONS_LOAD_SUCCESS,
STATUS_REVISIONS_LOAD_FAIL
} from '../actions/status_revisions'
const initialState = ImmutableMap({
loading: false,
error: null,
revisions: ImmutableList(),
})
export default function statusRevisions(state = initialState, action) {
switch (action.type) {
case STATUS_REVISIONS_LOAD:
return initialState
case STATUS_REVISIONS_LOAD_SUCCESS:
return state.withMutations(mutable => {
mutable.set('loading', false)
mutable.set('revisions', fromJS(action.revisions).reverse())
})
case STATUS_REVISIONS_LOAD_FAIL:
return state.withMutations(mutable => {
mutable.set('loading', false)
mutable.set('error', action.error)
})
default:
return state
}
}

View File

@ -131,7 +131,7 @@
"rails-ujs": "^5.2.3",
"react": "^16.12.0",
"react-contenteditable": "^3.3.3",
"react-datepicker": "^2.9.6",
"react-datepicker": "^2.14.1",
"react-dom": "^16.7.0",
"react-hotkeys": "^1.1.4",
"react-immutable-proptypes": "^2.1.0",

View File

@ -8099,10 +8099,10 @@ react-contenteditable@^3.3.3:
fast-deep-equal "^2.0.1"
prop-types "^15.7.1"
react-datepicker@^2.9.6:
version "2.9.6"
resolved "https://registry.yarnpkg.com/react-datepicker/-/react-datepicker-2.9.6.tgz#26190c9f71692149d0d163398aa19e08626444b1"
integrity sha512-PLiVhyAr567gWuLMZwIH9WpTIZOZVLhEFyuUzSx3kmQdiikjrYpdNlxsfbbgaxRnee5y08KJZequaqRsNySXmw==
react-datepicker@^2.14.1:
version "2.14.1"
resolved "https://registry.yarnpkg.com/react-datepicker/-/react-datepicker-2.14.1.tgz#83463beb85235a575475955f554290a95f89c65b"
integrity sha512-8eWHvrjXfKVkt5rERXC6/c/eEdcE2stIsl+QmTO5Efgpacf8MOCyVpBisJLVLDYjVlENczhOcRlIzvraODHKxA==
dependencies:
classnames "^2.2.6"
date-fns "^2.0.1"