This commit is contained in:
mgabdev 2020-03-06 10:38:22 -05:00
parent 5109276331
commit da3d0c3462
39 changed files with 512 additions and 564 deletions

View File

@ -0,0 +1,27 @@
const AudioIcon = ({
className = '',
width = '24px',
height = '24px',
viewBox = '0 0 24 24',
title = '',
}) => (
<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='M12.253 22.75c-.166 0-.33-.055-.466-.162L4.74 17H2.254c-1.24 0-2.25-1.01-2.25-2.25v-5.5C.003 8.01 1.013 7 2.253 7H4.74l7.047-5.588c.225-.18.533-.215.792-.087.258.125.423.387.423.675v20c0 .288-.165.55-.424.675-.104.05-.216.075-.327.075zm-10-14.25c-.413 0-.75.337-.75.75v5.5c0 .413.337.75.75.75h2.75c.17 0 .333.058.466.162l6.033 4.786V3.552L5.47 8.338c-.134.104-.298.162-.467.162h-2.75zm14.33 8.226c-.308 0-.596-.19-.705-.495-.14-.39.06-.818.45-.96 1.373-.495 2.296-1.81 2.296-3.27s-.923-2.774-2.296-3.27c-.39-.14-.592-.57-.45-.96.14-.39.57-.59.958-.45 1.967.707 3.288 2.59 3.288 4.68s-1.32 3.972-3.286 4.68c-.084.03-.17.046-.255.046z' />
<path d='M17.82 20.135c-.306 0-.594-.19-.704-.495-.14-.39.06-.82.45-.96 2.802-1.014 4.684-3.698 4.684-6.68 0-2.98-1.883-5.665-4.684-6.68-.39-.14-.592-.57-.45-.96.14-.39.573-.59.96-.45C21.47 5.14 23.75 8.39 23.75 12c0 3.61-2.28 6.862-5.674 8.09-.084.03-.17.045-.255.045z' />
</g>
</svg>
)
export default AudioIcon

View File

@ -0,0 +1,26 @@
const AudioMuteIcon = ({
className = '',
width = '24px',
height = '24px',
viewBox = '0 0 24 24',
title = 'Error',
}) => (
<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='M20.654 12l3.032-3.032c.293-.293.293-.768 0-1.06s-.768-.294-1.06 0l-3.033 3.03-3.032-3.03c-.292-.294-.767-.294-1.06 0s-.293.767 0 1.06L18.533 12 15.5 15.032c-.292.293-.292.768 0 1.06.147.147.34.22.53.22s.385-.072.53-.22l3.033-3.03 3.032 3.03c.146.147.338.22.53.22s.384-.072.53-.22c.293-.292.293-.767 0-1.06L20.655 12zm-8.401 10.75c-.166 0-.33-.055-.466-.162L4.74 17H2.254c-1.24 0-2.25-1.01-2.25-2.25v-5.5C.003 8.01 1.013 7 2.253 7H4.74l7.047-5.588c.225-.18.533-.215.792-.087.258.125.423.387.423.675v20c0 .288-.165.55-.424.675-.104.05-.216.075-.327.075zm-10-14.25c-.413 0-.75.337-.75.75v5.5c0 .413.337.75.75.75h2.75c.17 0 .333.058.466.162l6.033 4.786V3.552L5.47 8.338c-.134.104-.298.162-.467.162h-2.75z' />
</g>
</svg>
)
export default AudioMuteIcon

View File

@ -0,0 +1,29 @@
const FullscreenIcon = ({
className = '',
width = '24px',
height = '24px',
viewBox = '0 0 32 32',
title = 'Fullscreen',
}) => (
<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 10.910156 0 L 0.726562 0 C 0.324219 0 0 0.324219 0 0.726562 L 0 10.910156 C 0 11.3125 0.324219 11.636719 0.726562 11.636719 L 2.183594 11.636719 C 2.582031 11.636719 2.910156 11.3125 2.910156 10.910156 L 2.910156 2.910156 L 10.910156 2.910156 C 11.3125 2.910156 11.636719 2.582031 11.636719 2.183594 L 11.636719 0.726562 C 11.636719 0.324219 11.3125 0 10.910156 0 Z M 10.910156 0' />
<path d='M 31.273438 0 L 21.089844 0 C 20.6875 0 20.363281 0.324219 20.363281 0.726562 L 20.363281 2.183594 C 20.363281 2.582031 20.6875 2.910156 21.089844 2.910156 L 29.089844 2.910156 L 29.089844 10.910156 C 29.089844 11.3125 29.417969 11.636719 29.816406 11.636719 L 31.273438 11.636719 C 31.675781 11.636719 32 11.3125 32 10.910156 L 32 0.726562 C 32 0.324219 31.675781 0 31.273438 0 Z M 31.273438 0' />
<path d='M 31.273438 20.363281 L 29.816406 20.363281 C 29.417969 20.363281 29.089844 20.6875 29.089844 21.089844 L 29.089844 29.089844 L 21.089844 29.089844 C 20.6875 29.089844 20.363281 29.417969 20.363281 29.816406 L 20.363281 31.273438 C 20.363281 31.675781 20.6875 32 21.089844 32 L 31.273438 32 C 31.675781 32 32 31.675781 32 31.273438 L 32 21.089844 C 32 20.6875 31.675781 20.363281 31.273438 20.363281 Z M 31.273438 20.363281' />
<path d='M 10.910156 29.089844 L 2.910156 29.089844 L 2.910156 21.089844 C 2.910156 20.6875 2.582031 20.363281 2.183594 20.363281 L 0.726562 20.363281 C 0.324219 20.363281 0 20.6875 0 21.089844 L 0 31.273438 C 0 31.675781 0.324219 32 0.726562 32 L 10.910156 32 C 11.3125 32 11.636719 31.675781 11.636719 31.273438 L 11.636719 29.816406 C 11.636719 29.417969 11.3125 29.089844 10.910156 29.089844 Z M 10.910156 29.089844' />
</g>
</svg>
)
export default FullscreenIcon

View File

@ -1,6 +1,8 @@
import AddIcon from './add_icon'
import AppsIcon from './apps_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'
@ -10,6 +12,7 @@ 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 HomeIcon from './home_icon'
@ -18,9 +21,11 @@ import LinkIcon from './link_icon'
import ListIcon from './list_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'
@ -36,8 +41,10 @@ import WarningIcon from './warning_icon'
export {
AddIcon,
AppsIcon,
AngleRightIcon,
AppsIcon,
AudioIcon,
AudioMuteIcon,
BackIcon,
CalendarIcon,
ChatIcon,
@ -47,6 +54,7 @@ export {
DissenterIcon,
EllipsisIcon,
ErrorIcon,
FullscreenIcon,
GlobeIcon,
GroupIcon,
HomeIcon,
@ -55,9 +63,11 @@ export {
ListIcon,
LoadingIcon,
MediaIcon,
MinimizeFullscreenIcon,
MissingIcon,
MoreIcon,
NotificationsIcon,
PauseIcon,
PinIcon,
PlayIcon,
PollIcon,

View File

@ -0,0 +1,29 @@
const MinimizeFullscreenIcon = ({
className = '',
width = '24px',
height = '24px',
viewBox = '0 0 32 32',
title = 'Minimize Fullscreen',
}) => (
<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 30.527344 13.082031 L 23.257812 13.082031 C 20.855469 13.082031 18.898438 11.125 18.898438 8.722656 L 18.898438 1.453125 C 18.898438 0.652344 19.546875 0 20.351562 0 C 21.15625 0 21.804688 0.652344 21.804688 1.453125 L 21.804688 8.722656 C 21.804688 9.523438 22.457031 10.175781 23.257812 10.175781 L 30.527344 10.175781 C 31.332031 10.175781 31.980469 10.828125 31.980469 11.628906 C 31.980469 12.433594 31.332031 13.082031 30.527344 13.082031 Z M 30.527344 13.082031' />
<path d='M 20.351562 31.980469 C 19.546875 31.980469 18.898438 31.332031 18.898438 30.527344 L 18.898438 23.257812 C 18.898438 20.855469 20.855469 18.898438 23.257812 18.898438 L 30.527344 18.898438 C 31.332031 18.898438 31.980469 19.550781 31.980469 20.351562 C 31.980469 21.15625 31.332031 21.804688 30.527344 21.804688 L 23.257812 21.804688 C 22.457031 21.804688 21.804688 22.457031 21.804688 23.257812 L 21.804688 30.527344 C 21.804688 31.332031 21.15625 31.980469 20.351562 31.980469 Z M 20.351562 31.980469' />
<path d='M 8.722656 13.082031 L 1.453125 13.082031 C 0.648438 13.082031 0 12.433594 0 11.628906 C 0 10.828125 0.648438 10.175781 1.453125 10.175781 L 8.722656 10.175781 C 9.523438 10.175781 10.175781 9.523438 10.175781 8.722656 L 10.175781 1.453125 C 10.175781 0.652344 10.824219 0 11.628906 0 C 12.433594 0 13.082031 0.652344 13.082031 1.453125 L 13.082031 8.722656 C 13.082031 11.125 11.125 13.082031 8.722656 13.082031 Z M 8.722656 13.082031' />
<path d='M 11.628906 31.980469 C 10.824219 31.980469 10.175781 31.332031 10.175781 30.527344 L 10.175781 23.257812 C 10.175781 22.457031 9.523438 21.804688 8.722656 21.804688 L 1.453125 21.804688 C 0.648438 21.804688 0 21.15625 0 20.351562 C 0 19.550781 0.648438 18.898438 1.453125 18.898438 L 8.722656 18.898438 C 11.125 18.898438 13.082031 20.855469 13.082031 23.257812 L 13.082031 30.527344 C 13.082031 31.332031 12.433594 31.980469 11.628906 31.980469 Z M 11.628906 31.980469' />
</g>
</svg>
)
export default MinimizeFullscreenIcon

View File

@ -0,0 +1,28 @@
const PauseIcon = ({
className = '',
width = '16px',
height = '16px',
viewBox = '0 0 64 64',
title = 'Pause',
}) => (
<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>
<rect x='6' y='0' width='18' height='64' rx='4' />
<rect x='40' y='0' width='18' height='64' rx='4' />
</g>
</svg>
)
export default PauseIcon

View File

@ -92,6 +92,7 @@ export default class Button extends PureComponent {
font: 1,
cursorPointer: 1,
textAlignCenter: 1,
outlineNone: 1,
backgroundColorPrimary: backgroundColor === COLORS.white,
backgroundColorBrand: backgroundColor === COLORS.brand,

View File

@ -1,54 +0,0 @@
import Button from '../button';
export default class ColumnInlineForm extends PureComponent {
static propTypes = {
value: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired,
label: PropTypes.string.isRequired,
btnTitle: PropTypes.string.isRequired,
disabled: PropTypes.bool,
};
handleChange = e => {
this.props.onChange(e.target.value);
}
handleSubmit = e => {
e.preventDefault();
this.props.onSubmit();
}
handleClick = () => {
this.props.onSubmit();
}
render() {
const { value, label, btnTitle, disabled } = this.props;
return (
<form className='column-inline-form' onSubmit={this.handleSubmit}>
<label className='column-inline-form__block'>
<span className='column-inline-form__title'>{label}</span>
<input
className='column-inline-form__input'
value={value}
disabled={disabled}
onChange={this.handleChange}
placeholder={label}
/>
</label>
<Button
className='column-inline-form__btn'
disabled={disabled}
onClick={this.handleClick}
>
{btnTitle}
</Button>
</form>
);
}
}

View File

@ -1,29 +0,0 @@
.column-inline-form {
padding: 7px 5px 7px 15px;
background: lighten($ui-base-color, 4%);
@include flex(flex-start, center);
&__block {
flex: 1 1 auto;
}
&__title {}
&__input {
margin-bottom: 6px;
@include size(100%, 36px);
&:focus {
outline: 0;
}
}
&__btn {
flex: 0 0 auto;
margin: 0 5px;
margin-left: 6px;
width: 112px;
}
}

View File

@ -1 +0,0 @@
export { default } from './column_inline_form'

View File

@ -1,5 +1,5 @@
import { defineMessages, injectIntl } from 'react-intl';
import IconButton from '../icon_button';
import IconButton from './icon_button';
const messages = defineMessages({
unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unhide {domain}' },

View File

@ -1 +0,0 @@
export { default } from './domain'

View File

@ -15,10 +15,14 @@ export default class Icon extends PureComponent {
switch (id) {
case 'add':
return <I.AddIcon {...options} />
case 'apps':
return <I.AppsIcon {...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':
@ -35,6 +39,8 @@ export default class Icon extends PureComponent {
return <I.EllipsisIcon {...options} />
case 'error':
return <I.ErrorIcon {...options} />
case 'fullscreen':
return <I.FullscreenIcon {...options} />
case 'globe':
return <I.GlobeIcon {...options} />
case 'group':
@ -51,12 +57,16 @@ export default class Icon extends PureComponent {
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':

View File

@ -27,17 +27,17 @@ class Item extends ImmutablePureComponent {
displayWidth: PropTypes.number,
visible: PropTypes.bool.isRequired,
dimensions: PropTypes.object,
};
}
static defaultProps = {
standalone: false,
index: 0,
size: 1,
};
}
state = {
loaded: false,
};
}
handleMouseEnter = (e) => {
if (this.hoverToPlay()) {

View File

@ -1,8 +1,43 @@
export default class SearchPopover extends PureComponent {
render() {
// <div className='search-popout-container' style={{ ...style, position: 'absolute', zIndex: 1000 }}>
// <Motion defaultStyle={{ opacity: 0, scaleX: 1, scaleY: 1 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
// {({ opacity, scaleX, scaleY }) => (
// <div className='search-popout' style={{ opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }}>
// <h4>
// <FormattedMessage id='search_popout.search_format' defaultMessage='Advanced search format' />
// </h4>
// <ul>
// <li>
// <em>#example</em>
// <FormattedMessage id='search_popout.tips.hashtag' defaultMessage='hashtag' />
// </li>
// <li>
// <em>@username</em>
// <FormattedMessage id='search_popout.tips.user' defaultMessage='user' />
// </li>
// <li>
// <em>URL</em>
// <FormattedMessage id='search_popout.tips.user' defaultMessage='user' />
// </li>
// <li>
// <em>URL</em>
// <FormattedMessage id='search_popout.tips.status' defaultMessage='status' />
// </li>
// </ul>
// {
// searchEnabled
// ? <FormattedMessage id='search_popout.tips.full_text' defaultMessage='Simple text returns statuses you have written, favorited, reposted, or have been mentioned in, as well as matching usernames, display names, and hashtags.' />
// : <FormattedMessage id='search_popout.tips.text' defaultMessage='Simple text returns matching display names, usernames and hashtags' />
// }
// </div>
// )}
// </Motion>
// </div>
return (
<div>
{ /* */ }
{ /* */}
</div>
)
}

View File

@ -1,12 +1,9 @@
import classNames from 'classnames/bind'
import Overlay from 'react-overlays/lib/Overlay'
import {
changeSearch,
clearSearch,
submitSearch,
showSearch,
} from '../actions/search'
import SearchPopout from './search_popout'
import Input from './input'
const mapStateToProps = state => ({
@ -93,9 +90,9 @@ class Search extends PureComponent {
{
withOverlay &&
<Overlay show={expanded && !hasValue} placement='bottom' target={this}>
{/*<Overlay show={expanded && !hasValue} placement='bottom' target={this}>
<SearchPopout />
</Overlay>
</Overlay>*/}
}
</div>

View File

@ -1 +0,0 @@
export { default } from './search_popout'

View File

@ -1,53 +0,0 @@
import { FormattedMessage } from 'react-intl';
import spring from 'react-motion/lib/spring';
import Motion from '../../features/ui/util/optional_motion';
import { searchEnabled } from '../../initial_state';
export default class SearchPopout extends PureComponent {
static propTypes = {
style: PropTypes.object,
};
render() {
const { style } = this.props;
return (
<div className='search-popout-container' style={{ ...style, position: 'absolute', zIndex: 1000 }}>
<Motion defaultStyle={{ opacity: 0, scaleX: 1, scaleY: 1 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
{({ opacity, scaleX, scaleY }) => (
<div className='search-popout' style={{ opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }}>
<h4>
<FormattedMessage id='search_popout.search_format' defaultMessage='Advanced search format' />
</h4>
<ul>
<li>
<em>#example</em>
<FormattedMessage id='search_popout.tips.hashtag' defaultMessage='hashtag' />
</li>
<li>
<em>@username</em>
<FormattedMessage id='search_popout.tips.user' defaultMessage='user' />
</li>
<li>
<em>URL</em>
<FormattedMessage id='search_popout.tips.user' defaultMessage='user' />
</li>
<li>
<em>URL</em>
<FormattedMessage id='search_popout.tips.status' defaultMessage='status' />
</li>
</ul>
{
searchEnabled
? <FormattedMessage id='search_popout.tips.full_text' defaultMessage='Simple text returns statuses you have written, favorited, reposted, or have been mentioned in, as well as matching usernames, display names, and hashtags.' />
: <FormattedMessage id='search_popout.tips.text' defaultMessage='Simple text returns matching display names, usernames and hashtags' />
}
</div>
)}
</Motion>
</div>
);
}
}

View File

@ -1,36 +0,0 @@
.search-popout-container {
width: 251px;
@media screen and (max-width: $nav-breakpoint-2) {
width: 100%;
}
}
.search-popout {
background: $gab-background-container;
padding: 8px 10px 17px 10px;
margin: 4px 0 0 0;
color: $gab-secondary-text;
box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.5);
@include text-sizing(12px, 400, 14px);
@include border-design($gab-placeholder-accent, 1px, 4px);
h4 {
color: #fff;
@include text-sizing(14px, 600, 16px);
}
ul {
margin: 6px 0 6px;
li {
margin: 0 0 2px 0;
em {
color: $gab-text-highlight;
}
}
}
}

View File

@ -371,6 +371,8 @@ class Status extends ImmutablePureComponent {
if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
const video = status.getIn(['media_attachments', 0]);
console.log("VIDEO HERE")
media = (
<Bundle fetchComponent={Video} loading={this.renderLoadingVideoPlayer}>
{Component => (
@ -460,9 +462,7 @@ class Status extends ImmutablePureComponent {
<div
className={classNames('status', `status-${status.get('visibility')}`, {
'status-reply': !!status.get('in_reply_to_id'),
muted: this.props.muted,
read: unread === false,
})}
data-id={status.get('id')}
>

View File

@ -28,11 +28,6 @@
margin-top: 8px;
}
&.status-direct:not(.read) {
background: lighten($ui-base-color, 8%);
border-bottom-color: lighten($ui-base-color, 12%);
}
&.light {
.status__relative-time {
color: $light-text-color;

View File

@ -2,11 +2,11 @@ import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { defineMessages, injectIntl } from 'react-intl'
import classNames from 'classnames/bind'
import { openModal } from '../../actions/modal'
import { me, isStaff } from '../../initial_state'
import ComposeFormContainer from '../../features/compose/containers/compose_form_container'
import Icon from '../icon'
import StatusActionBarItem from '../status_action_bar_item'
import { openModal } from '../actions/modal'
import { me, isStaff } from '../initial_state'
import ComposeFormContainer from '../features/compose/containers/compose_form_container'
import Icon from './icon'
import StatusActionBarItem from './status_action_bar_item'
const messages = defineMessages({
delete: { id: 'status.delete', defaultMessage: 'Delete' },

View File

@ -1 +1 @@
export { default } from './status_action_bar'
export { default } from '../status_action_bar'

View File

@ -1,59 +0,0 @@
.status-action-bar {
display: block;
margin-top: 10px;
z-index: 4;
@include horizontal-padding(10px);
&__dropdown {
@include size(23px);
}
&__items {
display: flex;
align-items: center;
border-top: 1px solid #161616;
@include size(100%, 42px);
}
&__comment {
}
}
.status-action-bar-item {
display: inline-flex;
align-items: center;
justify-content: center;
height: 42px;
width: 33%;
padding: 5px;
box-sizing: border-box;
&__btn {
border-radius: 4px;
margin-right: 4px;
font-size: 14px;
font-weight: 500;
@include size(100%);
&:hover {
background-color: #000;
}
i {
margin-right: 4px;
}
}
&__link {
display: inline-block;
color: $action-button-color;
cursor: pointer;
text-decoration: none;
@include text-sizing(14px, 500);
}
}

View File

@ -2,10 +2,10 @@ import { Fragment } from 'react';
import { debounce } from 'lodash';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import StatusContainer from '../../containers/status_container';
import ScrollableList from '../scrollable_list';
import TimelineQueueButtonHeader from '../timeline_queue_button_header';
import ColumnIndicator from '../column_indicator';
import StatusContainer from '../containers/status_container';
import ScrollableList from './scrollable_list';
import TimelineQueueButtonHeader from './timeline_queue_button_header';
import ColumnIndicator from './column_indicator';
export default class StatusList extends ImmutablePureComponent {

View File

@ -1 +1 @@
export { default } from './status_list'
export { default } from '../status_list'

View File

@ -1,6 +1,6 @@
import ImmutablePropTypes from 'react-immutable-proptypes';
import StatusContent from '../status_content';
import DisplayName from '../display_name';
import StatusContent from './status_content';
import DisplayName from './display_name';
import { NavLink } from 'react-router-dom';
const mapStateToProps = (state, { id }) => ({

View File

@ -1,7 +1,6 @@
import { injectIntl } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import ModalLoading from './modal_loading';
import RelativeTimestamp from './relative_timestamp';
export default
@ -17,13 +16,15 @@ class StatusRevisionsList extends ImmutablePureComponent {
render() {
const { loading, error, data } = this.props;
if (loading || !data) return <ModalLoading />;
if (loading || !data) return null
if (error) return (
if (error) {
return (
<div className='status-revisions-list'>
<div className='status-revisions-list__error'>An error occured</div>
</div>
);
)
}
return (
<div className='status-revisions-list'>

View File

@ -4,7 +4,7 @@ import { List as ImmutableList } from 'immutable'
import { injectIntl, defineMessages } from 'react-intl'
import { expandAccountFeaturedTimeline, expandAccountTimeline } from '../../actions/timelines'
import { fetchAccountIdentityProofs } from '../../actions/identity_proofs'
import StatusList from '../../components/status_list/status_list'
import StatusList from '../../components/status_list'
const messages = defineMessages({
empty: { id: 'empty_column.account_timeline', defaultMessage: 'No gabs here!' },

View File

@ -1,5 +1,9 @@
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { defineMessages, injectIntl } from 'react-intl'
import { changeValue, submit, reset } from '../actions/group_editor'
import isObject from 'lodash.isobject'
import { changeValue, submit, setUp, reset } from '../actions/group_editor'
import ColumnIndicator from '../components/column_indicator';
import Button from '../components/button'
import Divider from '../components/divider'
import Input from '../components/input'
@ -13,14 +17,25 @@ const messages = defineMessages({
coverImage: { id: 'groups.form.coverImage', defaultMessage: 'Upload a banner image' },
coverImageChange: { id: 'groups.form.coverImageChange', defaultMessage: 'Banner image selected' },
create: { id: 'groups.form.create', defaultMessage: 'Create group' },
update: { id: 'groups.form.update', defaultMessage: 'Update group' },
})
const mapStateToProps = state => ({
const mapStateToProps = (state, { params }) => {
console.log('params:', params)
const groupId = isObject(params) ? params['id'] : null
const group = state.getIn(['groups', groupId])
return {
group,
error: groupId && !group,
title: state.getIn(['group_editor', 'title']),
description: state.getIn(['group_editor', 'description']),
coverImage: state.getIn(['group_editor', 'coverImage']),
disabled: state.getIn(['group_editor', 'isSubmitting']),
})
}
}
const mapDispatchToProps = dispatch => ({
onTitleChange: value => dispatch(changeValue('title', value)),
@ -28,18 +43,20 @@ const mapDispatchToProps = dispatch => ({
onCoverImageChange: value => dispatch(changeValue('coverImage', value)),
onSubmit: routerHistory => dispatch(submit(routerHistory)),
reset: () => dispatch(reset()),
setUp: group => dispatch(setUp(group)),
})
export default
@connect(mapStateToProps, mapDispatchToProps)
@injectIntl
class Create extends PureComponent {
class Create extends ImmutablePureComponent {
static contextTypes = {
router: PropTypes.object
}
static propTypes = {
group: ImmutablePropTypes.map,
title: PropTypes.string.isRequired,
description: PropTypes.string.isRequired,
coverImage: PropTypes.object,
@ -50,7 +67,17 @@ class Create extends PureComponent {
}
componentWillMount() {
if (!this.props.group) {
this.props.reset()
} else {
this.props.setUp(this.props.group)
}
}
componentWillReceiveProps(nextProps) {
if (this.props.group !== nextProps.group && !!nextProps.group) {
this.props.setUp(nextProps.group)
}
}
handleTitleChange = e => {
@ -72,6 +99,8 @@ class Create extends PureComponent {
render() {
const {
group,
error,
title,
description,
coverImage,
@ -79,6 +108,10 @@ class Create extends PureComponent {
intl
} = this.props
if (!group && error) {
return <ColumnIndicator type='missing' />
}
return (
<form onSubmit={this.handleSubmit}>
<Input
@ -115,7 +148,7 @@ class Create extends PureComponent {
className={_s.marginLeft10PX}
>
<Text color='white'>
{intl.formatMessage(messages.create)}
{intl.formatMessage(!!group ? messages.update : messages.create)}
</Text>
</Button>

View File

@ -1,137 +0,0 @@
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { changeValue, submit, setUp } from '../../actions/group_editor';
import { defineMessages, injectIntl } from 'react-intl';
import ColumnIndicator from '../../components/column_indicator';
import classNames from 'classnames';
const messages = defineMessages({
title: { id: 'groups.form.title', defaultMessage: 'Title' },
description: { id: 'groups.form.description', defaultMessage: 'Description' },
coverImage: { id: 'groups.form.coverImage', defaultMessage: 'Upload new banner image (optional)' },
coverImageChange: { id: 'groups.form.coverImageChange', defaultMessage: 'Banner image selected' },
update: { id: 'groups.form.update', defaultMessage: 'Update group' },
});
const mapStateToProps = (state, props) => ({
group: state.getIn(['groups', props.params.id]),
title: state.getIn(['group_editor', 'title']),
description: state.getIn(['group_editor', 'description']),
coverImage: state.getIn(['group_editor', 'coverImage']),
disabled: state.getIn(['group_editor', 'isSubmitting']),
});
const mapDispatchToProps = dispatch => ({
onTitleChange: value => dispatch(changeValue('title', value)),
onDescriptionChange: value => dispatch(changeValue('description', value)),
onCoverImageChange: value => dispatch(changeValue('coverImage', value)),
onSubmit: routerHistory => dispatch(submit(routerHistory)),
setUp: group => dispatch(setUp(group)),
});
export default
@connect(mapStateToProps, mapDispatchToProps)
@injectIntl
class Edit extends ImmutablePureComponent {
static contextTypes = {
router: PropTypes.object,
}
static propTypes = {
group: ImmutablePropTypes.map,
title: PropTypes.string.isRequired,
description: PropTypes.string.isRequired,
coverImage: PropTypes.object,
disabled: PropTypes.bool,
intl: PropTypes.object.isRequired,
onTitleChange: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired,
};
componentWillMount(nextProps) {
if (this.props.group) {
this.props.setUp(this.props.group);
}
}
componentWillReceiveProps(nextProps) {
if (!this.props.group && nextProps.group) {
this.props.setUp(nextProps.group);
}
}
handleTitleChange = e => {
this.props.onTitleChange(e.target.value);
}
handleDescriptionChange = e => {
this.props.onDescriptionChange(e.target.value);
}
handleCoverImageChange = e => {
this.props.onCoverImageChange(e.target.files[0]);
}
handleSubmit = e => {
e.preventDefault();
this.props.onSubmit(this.context.router.history);
}
handleClick = () => {
this.props.onSubmit(this.context.router.history);
}
render() {
const { group, title, description, coverImage, disabled, intl } = this.props;
if (typeof group === 'undefined') {
return <ColumnIndicator type='loading' />
} else if (group === false) {
return <ColumnIndicator type='missing' />
}
return (
<form className='group-form' onSubmit={this.handleSubmit}>
<div>
<input
className='standard'
type='text'
value={title}
disabled={disabled}
onChange={this.handleTitleChange}
placeholder={intl.formatMessage(messages.title)}
/>
</div>
<div>
<textarea
className='standard'
type='text'
value={description}
disabled={disabled}
onChange={this.handleDescriptionChange}
placeholder={intl.formatMessage(messages.description)}
/>
</div>
<div>
<label htmlFor='group_cover_image' className={classNames('group-form__file-label', { 'group-form__file-label--selected': coverImage !== null })}>
{intl.formatMessage(coverImage === null ? messages.coverImage : messages.coverImageChange)}
</label>
<input
type='file'
className='group-form__file'
id='group_cover_image'
disabled={disabled}
onChange={this.handleCoverImageChange}
/>
<button>{intl.formatMessage(messages.update)}</button>
</div>
</form>
);
}
}

View File

@ -1,6 +1,5 @@
import { defineMessages, injectIntl } from 'react-intl';
import { changeListEditorTitle, submitListEditor } from '../../../../actions/lists';
import ColumnInlineForm from '../../../../components/column_inline_form';
const messages = defineMessages({
title: { id: 'lists.edit.submit', defaultMessage: 'Change title' },
@ -33,16 +32,18 @@ class ListForm extends PureComponent {
render () {
const { value, disabled, intl, onSubmit, onChange } = this.props;
return (
<ColumnInlineForm
value={value}
onChange={onChange}
onSubmit={onSubmit}
label={intl.formatMessage(messages.title)}
btnTitle={intl.formatMessage(messages.save)}
disabled={disabled}
/>
);
return null;
// return (
// <ColumnInlineForm
// value={value}
// onChange={onChange}
// onSubmit={onSubmit}
// label={intl.formatMessage(messages.title)}
// btnTitle={intl.formatMessage(messages.save)}
// disabled={disabled}
// />
// );
}
}

View File

@ -46,7 +46,6 @@ import {
GettingStarted,
GroupsCollection,
GroupCreate,
GroupEdit,
GroupMembers,
GroupRemovedAccounts,
GroupTimeline,
@ -151,7 +150,7 @@ class SwitchingArea extends PureComponent {
<WrappedRoute path='/groups/create' page={ModalPage} component={GroupCreate} content={children} componentParams={{ title: 'Create Group' }} />
<WrappedRoute path='/groups/:id/members' page={GroupPage} component={GroupMembers} content={children} />
<WrappedRoute path='/groups/:id/removed_accounts' page={GroupPage} component={GroupRemovedAccounts} content={children} />
<WrappedRoute path='/groups/:id/edit' page={ModalPage} component={GroupEdit} content={children} componentParams={{ title: 'Edit Group' }} />
<WrappedRoute path='/groups/:id/edit' page={ModalPage} component={GroupCreate} content={children} componentParams={{ title: 'Edit Group' }} />
<WrappedRoute path='/groups/:id' page={GroupPage} component={GroupTimeline} content={children} />
<WrappedRoute path='/tags/:id' publicRoute page={BasicPage} component={HashtagTimeline} content={children} componentParams={{ title: 'Hashtag' }} />

View File

@ -58,10 +58,6 @@ export function GroupCreate() {
return import(/* webpackChunkName: "features/group_create" */'../../group_create')
}
export function GroupEdit() {
return import(/* webpackChunkName: "features/group_edit" */'../../group_edit')
}
export function GroupMembers() {
return import(/* webpackChunkName: "features/group_members" */'../../group_members')
}

View File

@ -1,12 +1,16 @@
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { fromJS, is } from 'immutable';
import { throttle } from 'lodash';
import classNames from 'classnames';
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 Icon from '../../components/icon';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'
import { fromJS, 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 Icon from '../../components/icon'
import Text from '../../components/text'
const cx = classNames.bind(_s)
const messages = defineMessages({
play: { id: 'video.play', defaultMessage: 'Play' },
@ -14,8 +18,6 @@ const messages = defineMessages({
mute: { id: 'video.mute', defaultMessage: 'Mute sound' },
unmute: { id: 'video.unmute', defaultMessage: 'Unmute sound' },
hide: { id: 'video.hide', defaultMessage: 'Hide video' },
expand: { id: 'video.expand', defaultMessage: 'Expand video' },
close: { id: 'video.close', defaultMessage: 'Close video' },
fullscreen: { id: 'video.fullscreen', defaultMessage: 'Full screen' },
exit_fullscreen: { id: 'video.exit_fullscreen', defaultMessage: 'Exit full screen' },
});
@ -106,7 +108,6 @@ class Video extends PureComponent {
onToggleVisibility: PropTypes.func,
intl: PropTypes.object.isRequired,
blurhash: PropTypes.string,
link: PropTypes.node,
aspectRatio: PropTypes.number,
};
@ -302,7 +303,7 @@ class Video extends PureComponent {
const hash = this.props.blurhash;
const pixels = decode(hash, 32, 32);
if (pixels) {
if (pixels && this.canvas) {
const ctx = this.canvas.getContext('2d');
const imageData = new ImageData(pixels, 32, 32);
@ -353,30 +354,35 @@ class Video extends PureComponent {
this.setState({ volume: this.video.volume, muted: this.video.muted });
}
handleOpenVideo = () => {
const { src, preview, width, height, alt } = this.props;
const media = fromJS({
type: 'video',
url: src,
preview_url: preview,
description: alt,
width,
height,
});
this.video.pause();
this.props.onOpenVideo(media, this.video.currentTime);
}
handleCloseVideo = () => {
this.video.pause();
this.props.onCloseVideo();
}
render() {
const { preview, src, inline, startTime, onOpenVideo, onCloseVideo, intl, alt, detailed, sensitive, link, aspectRatio } = this.props;
const { containerWidth, currentTime, duration, volume, buffer, dragging, paused, fullscreen, hovered, muted, revealed } = this.state;
const {
preview,
src,
inline,
startTime,
onOpenVideo,
onCloseVideo,
intl,
alt,
detailed,
sensitive,
aspectRatio
} = this.props
const {
containerWidth,
currentTime,
duration,
volume,
buffer,
dragging,
paused,
fullscreen,
hovered,
muted,
revealed
} = this.state
const progress = (currentTime / duration) * 100;
const volumeWidth = (muted) ? 0 : volume * this.volWidth;
@ -385,6 +391,8 @@ class Video extends PureComponent {
let { width, height } = this.props;
console.log("buffer, progress:", buffer, progress)
if (inline && containerWidth) {
width = containerWidth;
const minSize = containerWidth / (16 / 9);
@ -418,10 +426,40 @@ class Video extends PureComponent {
warning = <FormattedMessage id='status.media_hidden' defaultMessage='Media hidden' />;
}
// console.log("width, height:", width, height)
// className={classNames('video-player', {
// inactive: !revealed,
// detailed,
// inline: inline && !fullscreen,
// fullscreen
// })}
const seekHandleClasses = cx({
default: 1,
positionAbsolute: 1,
circle: 1,
paddingHorizontal10PX: 1,
paddingVertical10PX: 1,
backgroundColorBrand: 1,
marginLeftNeg5PX: 1,
z3: 1,
opacity0: !dragging,
opacity1: dragging || hovered,
})
const progressClasses = cx({
default: 1,
radiusSmall: 1,
marginTop10PX: 1,
positionAbsolute: 1,
height4PX: 1,
})
return (
<div
role='menuitem'
className={classNames('video-player', { inactive: !revealed, detailed, inline: inline && !fullscreen, fullscreen })}
className={[_s.default].join(' ')}
style={playerStyle}
ref={this.setPlayerRef}
onMouseEnter={this.handleMouseEnter}
@ -429,9 +467,22 @@ class Video extends PureComponent {
onClick={this.handleClickRoot}
tabIndex={0}
>
<canvas width={32} height={32} ref={this.setCanvasRef} className={classNames('media-gallery__preview', { 'media-gallery__preview--hidden': revealed })} />
{revealed && <video
{
!revealed &&
<canvas
width={32}
height={32}
ref={this.setCanvasRef}
className={[_s.default, _s.positionAbsolute, _s.height100PC, _s.width100PC, _s.top0, _s.left0].join(' ')}
/>
}
{
revealed &&
<video
className={[_s.default, _s.height100PC, _s.width100PC, _s.outlineNone].join(' ')}
playsInline
ref={this.setVideoRef}
src={src}
@ -452,33 +503,17 @@ class Video extends PureComponent {
onLoadedData={this.handleLoadedData}
onProgress={this.handleProgress}
onVolumeChange={this.handleVolumeChange}
/>}
/>
}
<div className={classNames('spoiler-button', { 'spoiler-button--hidden': revealed })}>
{ /* <div className={classNames('spoiler-button', { 'spoiler-button--hidden': revealed })}>
<button type='button' className='spoiler-button__overlay' onClick={this.toggleReveal}>
<span className='spoiler-button__overlay__label'>{warning}</span>
</button>
</div>
<div className={classNames('video-player__controls', { active: paused || hovered })}>
<div className='video-player__seek' onMouseDown={this.handleMouseDown} ref={this.setSeekRef}>
<div className='video-player__seek__buffer' style={{ width: `${buffer}%` }} />
<div className='video-player__seek__progress' style={{ width: `${progress}%` }} />
<span
className={classNames('video-player__seek__handle', { active: dragging })}
tabIndex='0'
style={{ left: `${progress}%` }}
/>
</div>
<div className='video-player__buttons-bar'>
<div className='video-player__buttons left'>
<button type='button' aria-label={intl.formatMessage(paused ? messages.play : messages.pause)} onClick={this.togglePlay}><Icon id={paused ? 'play' : 'pause'} fixedWidth /></button>
<button type='button' aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)} onClick={this.toggleMute}><Icon id={muted ? 'volume-off' : 'volume-up'} fixedWidth /></button>
</div> */ }
<div className='video-player__volume' onMouseDown={this.handleVolumeMouseDown} ref={this.setVolumeRef}>
<div className='video-player__volume__current' style={{ width: `${volumeWidth}px` }} />
<div className='video-player__volume__current' style={{ height: `${volumeWidth}px` }} />
<span
className={classNames('video-player__volume__handle')}
tabIndex='0'
@ -486,24 +521,75 @@ class Video extends PureComponent {
/>
</div>
{(detailed || fullscreen) && (
<span>
<span className='video-player__time-current'>{formatTime(currentTime)}</span>
<span className='video-player__time-sep'>/</span>
<span className='video-player__time-total'>{formatTime(duration)}</span>
</span>
)}
<div className={[_s.default, _s.z2, _s.paddingHorizontal15PX, _s.videoPlayerControlsBackground, _s.positionAbsolute, _s.bottom0, _s.right0, _s.left0].join(' ')}>
{link && <span className='video-player__link'>{link}</span>}
<div
className={[_s.default, _s.cursorPointer, _s.height22PX, _s.videoPlayerSeek].join(' ')}
onMouseDown={this.handleMouseDown}
ref={this.setSeekRef}
>
<div className={[progressClasses, _s.backgroundPanel, _s.width100PC].join(' ')} />
<div className={[progressClasses, _s.backgroundSubtle].join(' ')} style={{ width: `${buffer}%` }} />
<div className={[progressClasses, _s.backgroundColorBrand].join(' ')} style={{ width: `${progress}%` }} />
<span
className={seekHandleClasses}
tabIndex='0'
style={{
left: `${progress}%`
}}
/>
</div>
<div className='video-player__buttons right'>
{!onCloseVideo && <button type='button' aria-label={intl.formatMessage(messages.hide)} onClick={this.toggleReveal}><Icon id='eye-slash' fixedWidth /></button>}
{(!fullscreen && onOpenVideo) && <button type='button' aria-label={intl.formatMessage(messages.expand)} onClick={this.handleOpenVideo}><Icon id='expand' fixedWidth /></button>}
{onCloseVideo && <button type='button' aria-label={intl.formatMessage(messages.close)} onClick={this.handleCloseVideo}><Icon id='compress' fixedWidth /></button>}
<button type='button' aria-label={intl.formatMessage(fullscreen ? messages.exit_fullscreen : messages.fullscreen)} onClick={this.toggleFullscreen}><Icon id={fullscreen ? 'compress' : 'arrows-alt'} fixedWidth /></button>
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter, _s.paddingBottom5PX, _s.noSelect].join(' ')}>
<Button
narrow
backgroundColor='none'
aria-label={intl.formatMessage(paused ? messages.play : messages.pause)}
onClick={this.togglePlay}
icon={paused ? 'play' : 'pause'}
iconWidth='16px'
iconHeight='16px'
iconClassName={_s.fillColorWhite}
className={_s.paddingLeft0}
/>
<div className={[_s.default, _s.marginLeftAuto, _s.flexRow, _s.alignItemsCenter].join(' ')}>
<Text color='white' size='small'>
{formatTime(currentTime)}
&nbsp;/&nbsp;
{formatTime(duration)}
</Text>
<Button
narrow
backgroundColor='none'
type='button'
aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)}
onClick={this.toggleMute}
icon={muted ? 'audio-mute' : 'audio'}
iconWidth='24px'
iconHeight='24px'
iconClassName={_s.fillColorWhite}
className={[_s.paddingHorizontal10PX, _s.marginLeft10PX].join(' ')}
/>
<Button
narrow
backgroundColor='none'
aria-label={intl.formatMessage(fullscreen ? messages.exit_fullscreen : messages.fullscreen)}
onClick={this.toggleFullscreen}
icon={fullscreen ? 'minimize-fullscreen' : 'fullscreen'}
iconWidth='20px'
iconHeight='20px'
iconClassName={_s.fillColorWhite}
className={[_s.paddingHorizontal10PX, _s.paddingRight0].join(' ')}
/>
</div>
</div>
</div>
</div>
);

View File

@ -218,37 +218,6 @@
@include size(100%, 40x);
}
&__progress,
&__buffer {
display: block;
height: 4px;
border-radius: 4px;
background: lighten($ui-highlight-color, 8%);
@include abs-position(10px);
}
&__buffer {
background: rgba($white, 0.2);
}
&__handle {
z-index: 3;
opacity: 0;
margin-left: -6px;
transition: opacity .1s ease;
background: lighten($ui-highlight-color, 8%);
box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.2);
pointer-events: none;
@include circle(12px);
@include abs-position(6px);
&.active {
opacity: 1;
}
}
&:hover {
.video-player__seek__handle {
opacity: 1;

View File

@ -281,6 +281,10 @@ body {
background-color: #fff;
}
.backgroundColorWhite {
background-color: #fff;
}
.backgroundColorPrimaryOpaque {
background-color: rgba(255, 255, 255, 0.8);
}
@ -433,6 +437,10 @@ body {
height: 22px;
}
.height4PX {
height: 4px;
}
.height50PX {
height: 50px;
}
@ -702,6 +710,10 @@ body {
margin-left: 5px;
}
.marginLeftNeg5PX {
margin-left: -5px;
}
.marginRight2PX {
margin-right: 2px;
}
@ -775,6 +787,10 @@ body {
padding-top: 10px;
}
.paddingBottom5PX {
padding-bottom: 5px;
}
.paddingHorizontal15PX {
padding-left: 15px;
padding-right: 15px;
@ -784,6 +800,14 @@ body {
padding-left: 15px;
}
.paddingLeft0 {
padding-left: 0;
}
.paddingRight0 {
padding-right: 0;
}
.paddingRight15PX {
padding-right: 15px;
}
@ -826,3 +850,26 @@ body {
padding-left: 20px;
padding-right: 20px;
}
.videoPlayerControlsBackground {
background: linear-gradient(0deg,rgba(0,0,0,.85),rgba(0,0,0,.45) 60%,transparent);
}
.videoPlayerSeek:before {
content: '';
display: block;
position: absolute;
/* background: rgba(255, 255, 255, 0.35); */
border-radius: 4px;
top: 10px;
width: 100%;
height: 40px;
}
.opacity0 {
opacity: 0;
}
.opacity1 {
opacity: 1;
}