Albums almost done, group, chat moderation, photo, video page updates
This commit is contained in:
mgabdev
2020-12-21 13:25:05 -05:00
parent a2ffbadedb
commit ee91809e8d
45 changed files with 1013 additions and 509 deletions

View File

@@ -1,8 +1,13 @@
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { CX } from '../constants'
import { openModal } from '../actions/modal'
import {
CX,
MODAL_ALBUM_CREATE,
} from '../constants'
import Button from './button'
import Icon from './icon'
import Image from './image'
@@ -10,19 +15,14 @@ import Text from './text'
class Album extends React.PureComponent {
handleOnClick = (e) => {
//
}
handleOnOpenAlbumCreation = () => {
handleOnOpenAlbumCreate = () => {
this.props.openAlbumCreate()
}
render() {
const {
album,
isAddable,
isDummy,
} = this.props
const title = isAddable ? 'New album' : 'Album title'
@@ -31,29 +31,26 @@ class Album extends React.PureComponent {
return (
<div className={[_s.d, _s.minW162PX, _s.px5, _s.flex1].join(' ')}>
{
!isDummy &&
<Button
noClasses
className={[_s.d, _s.noUnderline, _s.noOutline, _s.bgTransparent].join(' ')}
to={to}
onClick={isAddable ? this.handleOnOpenAlbumCreation : undefined}
>
<div className={[_s.d, _s.w100PC, _s.mt5, _s.mb10].join(' ')}>
<div className={[_s.d, _s.w100PC, _s.pt100PC].join(' ')}>
<div className={[_s.d, _s.posAbs, _s.top0, _s.w100PC, _s.right0, _s.bottom0, _s.left0].join(' ')}>
<div className={[_s.d, _s.w100PC, _s.h100PC, _s.aiCenter, _s.jcCenter, _s.radiusSmall, _s.bgTertiary, _s.border1PX, _s.borderColorSecondary].join(' ')}>
{ isAddable && <Icon id='add' size='20px' /> }
</div>
<Button
noClasses
className={[_s.d, _s.noUnderline, _s.cursorPointer, _s.outlineNone, _s.bgTransparent].join(' ')}
to={to}
onClick={isAddable ? this.handleOnOpenAlbumCreate : undefined}
>
<div className={[_s.d, _s.w100PC, _s.mt5, _s.mb10].join(' ')}>
<div className={[_s.d, _s.w100PC, _s.pt100PC].join(' ')}>
<div className={[_s.d, _s.posAbs, _s.top0, _s.w100PC, _s.right0, _s.bottom0, _s.left0].join(' ')}>
<div className={[_s.d, _s.w100PC, _s.h100PC, _s.aiCenter, _s.jcCenter, _s.radiusSmall, _s.bgTertiary, _s.border1PX, _s.borderColorSecondary].join(' ')}>
{ isAddable && <Icon id='add' size='20px' /> }
</div>
</div>
</div>
<div className={[_s.d, _s.w100PC, _s.pt7, _s.mb15].join(' ')}>
<Text weight='bold'>{title}</Text>
{ !isAddable && <Text color='secondary' size='small' className={_s.mt5}>{subtitle}</Text> }
</div>
</Button>
}
</div>
<div className={[_s.d, _s.w100PC, _s.pt7, _s.mb15].join(' ')}>
<Text weight='bold'>{title}</Text>
{ !isAddable && <Text color='secondary' size='small' className={_s.mt5}>{subtitle}</Text> }
</div>
</Button>
</div>
)
}
@@ -63,7 +60,12 @@ class Album extends React.PureComponent {
Album.propTypes = {
album: ImmutablePropTypes.map,
isAddable: PropTypes.bool,
isDummy: PropTypes.bool,
}
export default Album
const mapDispatchToProps = (dispatch) => ({
openAlbumCreate() {
dispatch(openModal(MODAL_ALBUM_CREATE))
}
})
export default connect(null, mapDispatchToProps)(Album)

View File

@@ -28,7 +28,7 @@ class DeckColumnHeader extends React.PureComponent {
} = this.props
return (
<div data-sort-header className={[_s.d, _s.w100PC, _s.flexRow, _s.aiCenter, _s.h60PX, _s.px15, _s.py10, _s.borderBottom1PX, _s.borderColorSecondary, _s.bgPrimary].join(' ')}>
<div data-sort-header className={[_s.d, _s.w100PC, _s.overflowHidden, _s.flexRow, _s.aiCenter, _s.h60PX, _s.px15, _s.py10, _s.borderBottom1PX, _s.borderColorSecondary, _s.bgPrimary].join(' ')}>
<div data-sort-header className={[_s.d, _s.flexRow, _s.mr15, _s.cursorEWResize].join(' ')}>
<span className={[_s.d, _s.w1PX, _s.h24PX, _s.mr2, _s.bgSecondary].join(' ')} />
<span className={[_s.d, _s.w1PX, _s.h24PX, _s.mr2, _s.bgSecondary].join(' ')} />
@@ -36,7 +36,7 @@ class DeckColumnHeader extends React.PureComponent {
</div>
{ !!icon && <Icon id={icon} className={[_s.cPrimary, _s.mr15].join(' ')} size='18px' /> }
<div className={[_s.d, _s.flexRow, _s.aiEnd].join(' ')}>
<div className={[_s.d, _s.flexRow, _s.aiEnd, _s.flexShrink1, _s.overflowHidden, _s.textOverflowEllipsis2].join(' ')}>
{ !!title && <Text size='extraLarge' weight='medium'>{title}</Text> }
{ !!subtitle && <Text className={_s.ml5} color='secondary'>{subtitle}</Text> }
</div>

View File

@@ -99,9 +99,6 @@ class GroupHeader extends ImmutablePureComponent {
})
}
// : todo :
// {group.get('archived') && <Icon id='lock' title={intl.formatMessage(messages.group_archived)} />}
return (
<div className={[_s.d, _s.z1, _s.w100PC, _s.mb15].join(' ')}>
<Responsive max={BREAKPOINT_EXTRA_SMALL}>

View File

@@ -14,23 +14,41 @@ class MediaItem extends ImmutablePureComponent {
state = {
loaded: false,
visible: displayMedia !== 'hide_all' && !this.props.attachment.getIn(['status', 'sensitive']) || displayMedia === 'show_all',
visible: true,
}
componentDidMount() {
if (this.props.attachment.get('blurhash')) {
const { attachment } = this.props
if (!attachment) return
if (attachment.get('blurhash')) {
this._decode()
}
this.setState({
visible: displayMedia !== 'hide_all' && !this.props.attachment.getIn(['status', 'sensitive']) || displayMedia === 'show_all',
})
}
componentDidUpdate(prevProps) {
if (prevProps.attachment.get('blurhash') !== this.props.attachment.get('blurhash') && this.props.attachment.get('blurhash')) {
const { attachment } = this.props
const { prevAttachment } = prevProps
if (prevAttachment !== attachment) {
this._decode()
return
}
if (prevAttachment.get('blurhash') !== attachment.get('blurhash') && attachment.get('blurhash')) {
this._decode()
}
}
_decode() {
const hash = this.props.attachment.get('blurhash')
_decode = () => {
const { attachment } = this.props
if (!attachment) return
const hash = attachment.get('blurhash')
const pixels = decode(hash, 160, 160)
if (pixels && this.canvas) {
@@ -41,7 +59,7 @@ class MediaItem extends ImmutablePureComponent {
}
}
setCanvasRef = c => {
setCanvasRef = (c) => {
this.canvas = c
}
@@ -50,7 +68,10 @@ class MediaItem extends ImmutablePureComponent {
}
hoverToPlay() {
return !autoPlayGif && ['gifv', 'video'].indexOf(this.props.attachment.get('type')) !== -1
const { attachment } = this.props
if (!attachment) return
return !autoPlayGif && ['gifv', 'video'].indexOf(attachment.get('type')) !== -1
}
render() {
@@ -60,104 +81,152 @@ class MediaItem extends ImmutablePureComponent {
isSmall,
} = this.props
const { visible, loaded } = this.state
if (!attachment || !account) return null
const status = attachment.get('status')
const title = status.get('spoiler_text') || attachment.get('description')
const attachmentType = attachment.get('type')
const aspectRatio = attachment.getIn(['meta', 'aspect'])
const isVideo = attachmentType === 'video'
let badge = null
if (attachmentType === 'video') {
if (isVideo) {
const duration = attachment.getIn(['meta', 'duration'])
badge = (duration / 60).toFixed(2)
} else if (attachmentType === 'gifv') {
badge = 'GIF'
}
const statusUrl = `/${account.getIn(['acct'])}/posts/${status.get('id')}`
const isSmallRatio = aspectRatio < 1
const isSquare = aspectRatio === 1
const containerClasses = CX({
d: 1,
posAbs: 1,
top0: 1,
h100PC: 1,
// w100PC: 1,
py2: !isSmall,
px2: !isSmall,
px5: 1,
flex1: !isSmallRatio && !isSquare,
minW198PX: !isVideo && !isSmallRatio && !isSquare,
minW232PX: isVideo && !isSmallRatio && !isSquare,
minW120PX: isSmallRatio,
minW162PX: isSquare,
})
const linkClasses = CX({
const paddedContainerClasses = CX({
d: 1,
w100PC: 1,
// h100PC: 1,
overflowHidden: 1,
border1PX: 1,
borderColorPrimary: 1,
h100PC: isSmallRatio || isSquare,
pt100PC: isSmallRatio || isSquare || !isVideo,
pt5625PC: isVideo && !isSmallRatio && !isSquare,
})
const statusUrl = `/${account.getIn(['acct'])}/posts/${status.get('id')}`;
// : todo : fix dimensions to be like albums
return (
<div className={[_s.d, _s.pt25PC].join(' ')}>
<div className={containerClasses}>
<NavLink
to={statusUrl}
title={title}
className={linkClasses}
>
{
(!loaded || !visible) &&
<canvas
height='100%'
width='100%'
ref={this.setCanvasRef}
className={[_s.d, _s.w100PC, _s.h100PC, _s.z2].join(' ')}
/>
}
<div className={containerClasses}>
<NavLink
className={[_s.d, _s.noUnderline, _s.outlineNone, _s.bgTransparent, _s.flexGrow1].join(' ')}
to={statusUrl}
title={title}
>
<div className={[_s.d, _s.mt5, _s.mb10, _s.flexGrow1].join(' ')}>
<div className={paddedContainerClasses}>
<div className={[_s.d, _s.posAbs, _s.top0, _s.right0, _s.left0, _s.bottom0].join(' ')}>
<div className={[_s.d, _s.h100PC, _s.aiCenter, _s.jcCenter, _s.radiusSmall, _s.overflowHidden].join(' ')}>
{
(!loaded || !visible) &&
<canvas
height='100%'
width='100%'
ref={this.setCanvasRef}
className={[_s.d, _s.w100PC, _s.h100PC, _s.z2].join(' ')}
/>
}
{
visible &&
<Image
height='100%'
width=''
src={attachment.get('preview_url')}
alt={attachment.get('description')}
title={attachment.get('description')}
onLoad={this.handleImageLoad}
className={_s.z1}
/>
}
{
visible &&
<Image
height='100%'
width=''
src={attachment.get('preview_url')}
alt={attachment.get('description')}
title={attachment.get('description')}
onLoad={this.handleImageLoad}
className={_s.z1}
/>
}
<div className={[_s.d, _s.aiCenter, _s.jcCenter, _s.h100PC, _s.w100PC, _s.z3, _s.posAbs].join(' ')}>
{
!visible &&
<Icon
id='hidden'
size='22px'
className={[_s.cWhite].join('')}
/>
}
{
!!badge &&
<div className={[_s.d, _s.posAbs, _s.radiusSmall, _s.bgBlackOpaque, _s.px5, _s.py5, _s.mr5, _s.mt5, _s.mb5, _s.bottom0, _s.right0].join(' ')}>
<Text size='extraSmall' color='white'>
{badge}
</Text>
{
(!visible || !!badge) &&
<div className={[_s.d, _s.aiCenter, _s.jcCenter, _s.h100PC, _s.w100PC, _s.z3, _s.posAbs].join(' ')}>
{
!visible &&
<Icon
id='hidden'
size='22px'
className={[_s.cWhite].join('')}
/>
}
{
!!badge &&
<div className={[_s.d, _s.posAbs, _s.radiusSmall, _s.bgBlackOpaque, _s.px5, _s.py5, _s.mr5, _s.mt5, _s.mb5, _s.bottom0, _s.right0].join(' ')}>
<Text size='extraSmall' color='white'>
{badge}
</Text>
</div>
}
</div>
}
</div>
}
</div>
</div>
</NavLink>
</div>
</div>
</NavLink>
</div>
)
// return (
// <div className={[_s.d, _s.pt25PC].join(' ')}>
// <div className={containerClasses}>
// <NavLink
// to={statusUrl}
// title={title}
// className={linkClasses}
// >
// {
// (!loaded || !visible) &&
// <canvas
// height='100%'
// width='100%'
// ref={this.setCanvasRef}
// className={[_s.d, _s.w100PC, _s.h100PC, _s.z2].join(' ')}
// />
// }
// {
// visible &&
// <Image
// height='100%'
// width=''
// src={attachment.get('preview_url')}
// alt={attachment.get('description')}
// title={attachment.get('description')}
// onLoad={this.handleImageLoad}
// className={_s.z1}
// />
// }
// </NavLink>
// </div>
// </div>
// )
}
}
MediaItem.propTypes = {
isDummy: PropTypes.bool.isRequired,
account: ImmutablePropTypes.map.isRequired,
attachment: ImmutablePropTypes.map.isRequired,
isSmall: PropTypes.bool,

View File

@@ -1 +1,29 @@
// : todo :
import React from 'react'
import PropTypes from 'prop-types'
import { defineMessages, injectIntl } from 'react-intl'
import ModalLayout from './modal_layout'
import AlbumCreate from '../../features/album_create'
class AlbumCreateModal extends React.PureComponent {
render() {
const { onClose } = this.props
return (
<ModalLayout
title='Create Album'
width={500}
onClose={onClose}
>
<AlbumCreate isModal />
</ModalLayout>
)
}
}
AlbumCreateModal.propTypes = {
onClose: PropTypes.func.isRequired,
}
export default AlbumCreateModal

View File

@@ -1,29 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import { defineMessages, injectIntl } from 'react-intl'
import ModalLayout from './modal_layout'
import BookmarkCollectionEdit from '../../features/bookmark_collection_edit'
class BookmarkCollectionEditModal extends React.PureComponent {
render() {
const { onClose } = this.props
return (
<ModalLayout
title='Edit Bookmark Collection'
width={500}
onClose={onClose}
>
<BookmarkCollectionEdit isModal />
</ModalLayout>
)
}
}
BookmarkCollectionEditModal.propTypes = {
onClose: PropTypes.func.isRequired,
}
export default BookmarkCollectionEditModal

View File

@@ -39,12 +39,6 @@ class MediaGalleryPanel extends ImmutablePureComponent {
}
}
// componentWillReceiveProps(nextProps) {
// if (nextProps.accountId && nextProps.accountId !== this.props.accountId) {
// this.props.dispatch(expandAccountMediaTimeline(nextProps.accountId, { limit: 8 }))
// }
// }
render() {
const {
account,

View File

@@ -81,8 +81,6 @@ class StatusOptionsPopover extends ImmutablePureComponent {
handleGroupRemoveAccount = () => {
const { status } = this.props
// : todo : check
this.props.onGroupRemoveAccount(status.getIn(['group', 'id']), status.getIn(['account', 'id']))
}

View File

@@ -65,6 +65,19 @@ class DeckSidebar extends ImmutablePureComponent {
<div className={[_s.d].join(' ')}>
<Button
to='/'
isText
title='Go home'
aria-label='Go home'
color='none'
backgroundColor='none'
className={[_s.d, _s.jcCenter, _s.noSelect, _s.noUnderline, _s.mt15, _s.mb15, _s.cursorPointer, _s.px10, _s.mr5].join(' ')}
icon='back'
iconSize='20px'
iconClassName={_s.fillNavigationBrand}
/>
<h1 className={[_s.d].join(' ')}>
<Button
to='/'