Progress
added composer query string functionality for url, text. fixed issue with no user in base_controller, cleaned up video component, albums starting
This commit is contained in:
parent
47d57960a4
commit
47cd60f851
|
@ -98,6 +98,7 @@ class Api::BaseController < ApplicationController
|
|||
end
|
||||
|
||||
def superapp?
|
||||
return true if doorkeeper_token.nil?
|
||||
doorkeeper_token && doorkeeper_token.application.superapp? || false
|
||||
end
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { NavLink } from 'react-router-dom'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import { CX } from '../constants'
|
||||
|
@ -15,17 +16,30 @@ class Album extends React.PureComponent {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { album } = this.props
|
||||
const { album, isDummy } = this.props
|
||||
|
||||
return (
|
||||
<Button
|
||||
to={to}
|
||||
href={href}
|
||||
onClick={this.handleOnClick}
|
||||
noClasses
|
||||
>
|
||||
|
||||
</Button>
|
||||
<div className={[_s.d, _s.minW162PX, _s.px5, _s.flex1].join(' ')}>
|
||||
{
|
||||
!isDummy &&
|
||||
<NavLink
|
||||
className={[_s.d, _s.noUnderline].join(' ')}
|
||||
to='/'
|
||||
>
|
||||
<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.radiusSmall, _s.bgTertiary, _s.border1PX, _s.borderColorSecondary].join(' ')} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={[_s.d, _s.w100PC, _s.pt7, _s.mb15].join(' ')}>
|
||||
<Text weight='bold'>Profile Photos</Text>
|
||||
<Text color='secondary' size='small' className={_s.mt5}>1 Item</Text>
|
||||
</div>
|
||||
</NavLink>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -34,6 +48,7 @@ class Album extends React.PureComponent {
|
|||
Album.propTypes = {
|
||||
album: ImmutablePropTypes.map,
|
||||
isAddable: PropTypes.bool,
|
||||
isDummy: PropTypes.bool,
|
||||
}
|
||||
|
||||
export default Album
|
|
@ -3,7 +3,10 @@ import PropTypes from 'prop-types'
|
|||
import { connect } from 'react-redux'
|
||||
import { length } from 'stringz'
|
||||
import { openPopover } from '../../actions/popover'
|
||||
import { MAX_POST_CHARACTER_COUNT } from '../../constants'
|
||||
import {
|
||||
CX,
|
||||
MAX_POST_CHARACTER_COUNT,
|
||||
} from '../../constants'
|
||||
import Heading from '../heading'
|
||||
import Button from '../button'
|
||||
import BackButton from '../back_button'
|
||||
|
@ -23,15 +26,27 @@ class ComposeNavigationBar extends React.PureComponent {
|
|||
isSubmitting,
|
||||
anyMedia,
|
||||
text,
|
||||
isXS,
|
||||
} = this.props
|
||||
|
||||
const disabledButton = isSubmitting || isUploading || isChangingUpload || length(text) > MAX_POST_CHARACTER_COUNT || (length(text.trim()) === 0 && !anyMedia)
|
||||
|
||||
const innerClasses = CX({
|
||||
d: 1,
|
||||
flexRow: 1,
|
||||
saveAreaInsetPT: 1,
|
||||
saveAreaInsetPL: 1,
|
||||
saveAreaInsetPR: 1,
|
||||
w100PC: 1,
|
||||
maxW640PX: !isXS,
|
||||
mlAuto: !isXS,
|
||||
mrAuto: !isXS,
|
||||
})
|
||||
|
||||
return (
|
||||
<div className={[_s.d, _s.z4, _s.h53PX, _s.w100PC].join(' ')}>
|
||||
<div className={[_s.d, _s.h53PX, _s.bgNavigation, _s.aiCenter, _s.z3, _s.top0, _s.right0, _s.left0, _s.posFixed].join(' ')} >
|
||||
|
||||
<div className={[_s.d, _s.flexRow, _s.saveAreaInsetPT, _s.saveAreaInsetPL, _s.saveAreaInsetPR, _s.w100PC].join(' ')}>
|
||||
<div className={innerClasses}>
|
||||
|
||||
<BackButton
|
||||
toHome
|
||||
|
@ -81,6 +96,7 @@ ComposeNavigationBar.propTypes = {
|
|||
isSubmitting: PropTypes.bool,
|
||||
anyMedia: PropTypes.bool,
|
||||
text: PropTypes.string,
|
||||
isXS: PropTypes.bool,
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ComposeNavigationBar)
|
|
@ -1,11 +1,26 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { connect } from 'react-redux'
|
||||
import { POPOVER_PROFILE_OPTIONS } from '../../constants'
|
||||
import { openPopover } from '../../actions/popover'
|
||||
import BackButton from '../back_button'
|
||||
import Button from '../button'
|
||||
import Heading from '../heading'
|
||||
|
||||
class ProfileNavigationBar extends React.PureComponent {
|
||||
|
||||
handleOpenMore = () => {
|
||||
this.props.openProfileOptionsPopover({
|
||||
account: this.props.account,
|
||||
targetRef: this.openMoreNode,
|
||||
position: 'bottom',
|
||||
})
|
||||
}
|
||||
|
||||
setOpenMoreNodeRef = (n) => {
|
||||
this.openMoreNode = n
|
||||
}
|
||||
|
||||
render() {
|
||||
const { titleHTML } = this.props
|
||||
|
||||
|
@ -51,9 +66,15 @@ class ProfileNavigationBar extends React.PureComponent {
|
|||
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
openProfileOptionsPopover(props) {
|
||||
dispatch(openPopover(POPOVER_PROFILE_OPTIONS, props))
|
||||
},
|
||||
})
|
||||
|
||||
ProfileNavigationBar.propTypes = {
|
||||
titleHTML: PropTypes.string,
|
||||
showBackBtn: PropTypes.bool,
|
||||
}
|
||||
|
||||
export default ProfileNavigationBar
|
||||
export default connect(null, mapDispatchToProps)(ProfileNavigationBar)
|
|
@ -226,6 +226,7 @@ class ProfileHeader extends ImmutablePureComponent {
|
|||
iconClassName={_s.inheritFill}
|
||||
color='brand'
|
||||
backgroundColor='none'
|
||||
// : TODO :
|
||||
className={[_s.jcCenter, _s.aiCenter, _s.mr10, _s.px10].join(' ')}
|
||||
onClick={this.handleOpenMore}
|
||||
buttonRef={this.setOpenMoreNodeRef}
|
||||
|
@ -365,6 +366,18 @@ class ProfileHeader extends ImmutablePureComponent {
|
|||
onClick={this.handleOpenMore}
|
||||
buttonRef={this.setOpenMoreNodeRef}
|
||||
/>
|
||||
<Button
|
||||
isOutline
|
||||
icon='chat'
|
||||
iconSize='18px'
|
||||
iconClassName={_s.inheritFill}
|
||||
color='brand'
|
||||
backgroundColor='none'
|
||||
// : TODO :
|
||||
className={[_s.jcCenter, _s.aiCenter, _s.mr10, _s.px10].join(' ')}
|
||||
onClick={this.handleOpenMore}
|
||||
buttonRef={this.setOpenMoreNodeRef}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
|
|
@ -5,21 +5,10 @@ import ImmutablePropTypes from 'react-immutable-proptypes'
|
|||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import { defineMessages, injectIntl } from 'react-intl'
|
||||
import { is } from 'immutable'
|
||||
import throttle from 'lodash.throttle'
|
||||
import { decode } from 'blurhash'
|
||||
import videojs from 'video.js'
|
||||
import { isFullscreen, requestFullscreen, exitFullscreen } from '../utils/fullscreen'
|
||||
import { isPanoramic, isPortrait, minimumAspectRatio, maximumAspectRatio } from '../utils/media_aspect_ratio'
|
||||
import {
|
||||
openPopover,
|
||||
} from '../actions/popover'
|
||||
import { displayMedia } from '../initial_state'
|
||||
import {
|
||||
CX,
|
||||
POPOVER_VIDEO_STATS,
|
||||
BREAKPOINT_EXTRA_SMALL,
|
||||
} from '../constants'
|
||||
import Responsive from '../features/ui/util/responsive_component'
|
||||
import Button from './button'
|
||||
import Icon from './icon'
|
||||
import SensitiveMediaItem from './sensitive_media_item'
|
||||
|
@ -27,10 +16,6 @@ import Text from './text'
|
|||
|
||||
import '!style-loader!css-loader!video.js/dist/video-js.min.css'
|
||||
|
||||
// check every 100 ms for buffer
|
||||
const checkInterval = 100
|
||||
const FIXED_VAR = 6
|
||||
|
||||
const videoJsOptions = {
|
||||
autoplay: false,
|
||||
playbackRates: [0.5, 1, 1.5, 2],
|
||||
|
@ -38,130 +23,19 @@ const videoJsOptions = {
|
|||
sources: [{}],
|
||||
}
|
||||
|
||||
const formatTime = (secondsNum) => {
|
||||
if (isNaN(secondsNum)) secondsNum = 0
|
||||
|
||||
let hours = Math.floor(secondsNum / 3600)
|
||||
let minutes = Math.floor((secondsNum - (hours * 3600)) / 60)
|
||||
let seconds = Math.floor(secondsNum) - (hours * 3600) - (minutes * 60)
|
||||
|
||||
if (hours < 10) hours = '0' + hours
|
||||
if (minutes < 10) minutes = '0' + minutes
|
||||
if (seconds < 10) seconds = '0' + seconds
|
||||
|
||||
return (hours === '00' ? '' : `${hours}:`) + `${minutes}:${seconds}`
|
||||
}
|
||||
|
||||
export const findElementPosition = (el) => {
|
||||
let box
|
||||
|
||||
if (el.getBoundingClientRect && el.parentNode) {
|
||||
box = el.getBoundingClientRect()
|
||||
}
|
||||
|
||||
if (!box) {
|
||||
return {
|
||||
left: 0,
|
||||
top: 0,
|
||||
}
|
||||
}
|
||||
|
||||
const docEl = document.documentElement
|
||||
const body = document.body
|
||||
|
||||
const clientLeft = docEl.clientLeft || body.clientLeft || 0
|
||||
const scrollLeft = window.pageXOffset || body.scrollLeft
|
||||
const left = (box.left + scrollLeft) - clientLeft
|
||||
|
||||
const clientTop = docEl.clientTop || body.clientTop || 0
|
||||
const scrollTop = window.pageYOffset || body.scrollTop
|
||||
const top = (box.top + scrollTop) - clientTop
|
||||
|
||||
return {
|
||||
left: Math.round(left),
|
||||
top: Math.round(top),
|
||||
}
|
||||
}
|
||||
|
||||
export const getPointerPosition = (el, event) => {
|
||||
const position = {}
|
||||
const box = findElementPosition(el)
|
||||
const boxW = el.offsetWidth
|
||||
const boxH = el.offsetHeight
|
||||
const boxY = box.top
|
||||
const boxX = box.left
|
||||
|
||||
let pageY = event.pageY
|
||||
let pageX = event.pageX
|
||||
|
||||
if (event.changedTouches) {
|
||||
pageX = event.changedTouches[0].pageX
|
||||
pageY = event.changedTouches[0].pageY
|
||||
}
|
||||
|
||||
position.y = Math.max(0, Math.min(1, (pageY - boxY) / boxH))
|
||||
position.x = Math.max(0, Math.min(1, (pageX - boxX) / boxW))
|
||||
|
||||
return position
|
||||
}
|
||||
|
||||
class Video extends ImmutablePureComponent {
|
||||
|
||||
state = {
|
||||
currentTime: 0,
|
||||
duration: 0,
|
||||
volume: 0.5,
|
||||
paused: true,
|
||||
dragging: false,
|
||||
draggingVolume: false,
|
||||
containerWidth: this.props.width,
|
||||
fullscreen: false,
|
||||
hovered: false,
|
||||
muted: false,
|
||||
hoveringVolumeButton: false,
|
||||
hoveringVolumeControl: false,
|
||||
revealed: this.props.visible !== undefined ? this.props.visible : (displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all'),
|
||||
pipAvailable: true,
|
||||
isBuffering: false,
|
||||
}
|
||||
|
||||
bufferCheckInterval = null
|
||||
lastPlayPos = 0
|
||||
volHeight = 100
|
||||
volOffset = 13
|
||||
|
||||
componentDidMount() {
|
||||
const { meta, blurhash } = this.props
|
||||
|
||||
document.addEventListener('fullscreenchange', this.handleFullscreenChange, true)
|
||||
document.addEventListener('webkitfullscreenchange', this.handleFullscreenChange, true)
|
||||
document.addEventListener('mozfullscreenchange', this.handleFullscreenChange, true)
|
||||
document.addEventListener('MSFullscreenChange', this.handleFullscreenChange, true)
|
||||
|
||||
if (meta) {
|
||||
this.setState({ duration: parseInt(meta.get('duration')) })
|
||||
}
|
||||
|
||||
if ('pictureInPictureEnabled' in document) {
|
||||
this.setState({ pipAvailable: true })
|
||||
}
|
||||
|
||||
videoJsOptions.sources = [{ src: this.props.src }]
|
||||
console.log("videoJsOptions:", videoJsOptions)
|
||||
// instantiate video.js
|
||||
this.videoPlayer = videojs(this.videoNode, videoJsOptions, function onPlayerReady() {
|
||||
console.log('onPlayerReady', this)
|
||||
})
|
||||
this.videoPlayer = videojs(this.video, videoJsOptions)
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener('fullscreenchange', this.handleFullscreenChange, true)
|
||||
document.removeEventListener('webkitfullscreenchange', this.handleFullscreenChange, true)
|
||||
document.removeEventListener('mozfullscreenchange', this.handleFullscreenChange, true)
|
||||
document.removeEventListener('MSFullscreenChange', this.handleFullscreenChange, true)
|
||||
|
||||
clearInterval(this.bufferCheckInterval)
|
||||
|
||||
if (this.videoPlayer) {
|
||||
this.videoPlayer.dispose()
|
||||
}
|
||||
|
@ -179,34 +53,6 @@ class Video extends ImmutablePureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
checkBuffering = () => {
|
||||
const { isBuffering, paused } = this.state
|
||||
if (!this.video) {
|
||||
this.handlePause()
|
||||
return
|
||||
}
|
||||
const { currentTime } = this.video
|
||||
|
||||
// Checking offset should be at most the check interval but allow for some margin
|
||||
let offset = (checkInterval - 30) / 1000
|
||||
|
||||
if (!isBuffering && currentTime < (this.lastPlayPos + offset) && !paused) {
|
||||
// If no buffering is currently detected, and the position does not seem to increase
|
||||
// and the player isn't manually paused...
|
||||
this.setState({ isBuffering: true })
|
||||
} else if (isBuffering && currentTime > (this.lastPlayPos + offset) && !paused) {
|
||||
// If we were buffering but the player has advanced, then there is no buffering
|
||||
this.setState({ isBuffering: false })
|
||||
}
|
||||
|
||||
this.lastPlayPos = currentTime
|
||||
}
|
||||
|
||||
volHandleOffset = (v) => {
|
||||
const offset = v * this.volHeight + this.volOffset
|
||||
return (offset > 110) ? 110 : offset
|
||||
}
|
||||
|
||||
setPlayerRef = (n) => {
|
||||
this.player = n
|
||||
|
||||
|
@ -220,197 +66,10 @@ class Video extends ImmutablePureComponent {
|
|||
|
||||
setVideoRef = (n) => {
|
||||
this.video = n
|
||||
this.videoNode = n
|
||||
|
||||
if (this.video) {
|
||||
const { volume, muted } = this.video
|
||||
this.setState({
|
||||
volume,
|
||||
muted,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
setSeekRef = (n) => {
|
||||
this.seek = n
|
||||
}
|
||||
|
||||
setVolumeRef = (n) => {
|
||||
this.volume = n
|
||||
}
|
||||
|
||||
setSettingsBtnRef = (n) => {
|
||||
this.settingsBtn = n
|
||||
}
|
||||
|
||||
handleClickRoot = (e) => e.stopPropagation()
|
||||
|
||||
handlePlay = () => {
|
||||
this.setState({ paused: false })
|
||||
|
||||
this.bufferCheckInterval = setInterval(this.checkBuffering, checkInterval)
|
||||
}
|
||||
|
||||
handlePause = () => {
|
||||
this.setState({
|
||||
paused: true,
|
||||
isBuffering: false,
|
||||
})
|
||||
|
||||
clearInterval(this.bufferCheckInterval)
|
||||
}
|
||||
|
||||
handleTimeUpdate = () => {
|
||||
const { currentTime, duration } = this.video
|
||||
this.setState({
|
||||
currentTime: currentTime.toFixed(FIXED_VAR),
|
||||
duration: duration.toFixed(FIXED_VAR),
|
||||
})
|
||||
}
|
||||
|
||||
handleVolumeMouseDown = (e) => {
|
||||
document.addEventListener('mousemove', this.handleMouseVolSlide, true)
|
||||
document.addEventListener('mouseup', this.handleVolumeMouseUp, true)
|
||||
document.addEventListener('touchmove', this.handleMouseVolSlide, true)
|
||||
document.addEventListener('touchend', this.handleVolumeMouseUp, true)
|
||||
|
||||
this.handleMouseVolSlide(e)
|
||||
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
this.setState({ draggingVolume: true })
|
||||
}
|
||||
|
||||
handleVolumeMouseUp = () => {
|
||||
this.handleMouseLeaveVolumeControl()
|
||||
|
||||
document.removeEventListener('mousemove', this.handleMouseVolSlide, true)
|
||||
document.removeEventListener('mouseup', this.handleVolumeMouseUp, true)
|
||||
document.removeEventListener('touchmove', this.handleMouseVolSlide, true)
|
||||
document.removeEventListener('touchend', this.handleVolumeMouseUp, true)
|
||||
|
||||
this.setState({ draggingVolume: false })
|
||||
}
|
||||
|
||||
handleMouseVolSlide = throttle((e) => {
|
||||
const rect = this.volume.getBoundingClientRect()
|
||||
const y = 1 - ((e.clientY - rect.top) / this.volHeight)
|
||||
|
||||
if (!isNaN(y)) {
|
||||
const slideamt = y
|
||||
if (y > 1) {
|
||||
slideamt = 1
|
||||
} else if (y < 0) {
|
||||
slideamt = 0
|
||||
}
|
||||
this.video.volume = slideamt
|
||||
this.setState({ volume: slideamt })
|
||||
}
|
||||
}, 60)
|
||||
|
||||
handleMouseDown = (e) => {
|
||||
document.addEventListener('mousemove', this.handleMouseMove, true)
|
||||
document.addEventListener('mouseup', this.handleMouseUp, true)
|
||||
document.addEventListener('touchmove', this.handleMouseMove, true)
|
||||
document.addEventListener('touchend', this.handleMouseUp, true)
|
||||
|
||||
this.setState({ dragging: true })
|
||||
this.video.pause()
|
||||
this.handleMouseMove(e)
|
||||
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
}
|
||||
|
||||
handleMouseUp = () => {
|
||||
document.removeEventListener('mousemove', this.handleMouseMove, true)
|
||||
document.removeEventListener('mouseup', this.handleMouseUp, true)
|
||||
document.removeEventListener('touchmove', this.handleMouseMove, true)
|
||||
document.removeEventListener('touchend', this.handleMouseUp, true)
|
||||
|
||||
this.setState({ dragging: false })
|
||||
this.video.play()
|
||||
}
|
||||
|
||||
handleMouseMove = throttle(e => {
|
||||
const { x } = getPointerPosition(this.seek, e)
|
||||
const currentTime = parseFloat(this.video.duration * x).toFixed(FIXED_VAR)
|
||||
|
||||
if (!isNaN(currentTime)) {
|
||||
this.video.currentTime = currentTime
|
||||
this.setState({ currentTime })
|
||||
}
|
||||
}, 60)
|
||||
|
||||
togglePlay = () => {
|
||||
if (this.state.paused) {
|
||||
this.video.play()
|
||||
} else {
|
||||
this.video.pause()
|
||||
}
|
||||
}
|
||||
|
||||
toggleFullscreen = () => {
|
||||
if (isFullscreen()) {
|
||||
exitFullscreen()
|
||||
} else {
|
||||
requestFullscreen(this.player)
|
||||
}
|
||||
}
|
||||
|
||||
togglePip = () => {
|
||||
try {
|
||||
if (this.video !== document.pictureInPictureElement) {
|
||||
if (this.state.paused) {
|
||||
this.video.play()
|
||||
}
|
||||
setTimeout(() => { // : hack :
|
||||
this.video.requestPictureInPicture()
|
||||
}, 500)
|
||||
} else {
|
||||
document.exitPictureInPicture()
|
||||
}
|
||||
} catch(e) {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
handleFullscreenChange = () => {
|
||||
this.setState({ fullscreen: isFullscreen() })
|
||||
}
|
||||
|
||||
handleMouseEnter = () => {
|
||||
this.setState({ hovered: true })
|
||||
}
|
||||
|
||||
handleMouseLeave = () => {
|
||||
this.setState({ hovered: false })
|
||||
}
|
||||
|
||||
handleMouseEnterAudio = () => {
|
||||
this.setState({ hoveringVolumeButton: true })
|
||||
}
|
||||
|
||||
handleMouseLeaveAudio = throttle(() => {
|
||||
this.setState({ hoveringVolumeButton: false })
|
||||
}, 2500)
|
||||
|
||||
handleMouseEnterVolumeControl = () => {
|
||||
this.setState({ hoveringVolumeControl: true })
|
||||
}
|
||||
|
||||
handleMouseLeaveVolumeControl = throttle(() => {
|
||||
if (!this.state.draggingVolume) {
|
||||
this.setState({ hoveringVolumeControl: false })
|
||||
}
|
||||
}, 2500)
|
||||
|
||||
toggleMute = () => {
|
||||
this.video.muted = !this.video.muted
|
||||
this.setState({ muted: this.video.muted })
|
||||
}
|
||||
|
||||
toggleReveal = () => {
|
||||
if (this.props.onToggleVisibility) {
|
||||
this.props.onToggleVisibility()
|
||||
|
@ -419,36 +78,6 @@ class Video extends ImmutablePureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
handleLoadedData = () => {
|
||||
if (this.props.startTime) {
|
||||
this.video.currentTime = this.props.startTime
|
||||
this.video.play()
|
||||
}
|
||||
}
|
||||
|
||||
handleProgress = () => {
|
||||
const { buffered, duration } = this.video
|
||||
|
||||
if (!buffered) return
|
||||
if (buffered.length > 0) {
|
||||
this.setState({
|
||||
buffer: buffered.end(0) / duration * 100,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
handleVolumeChange = () => {
|
||||
const { volume, muted } = this.video
|
||||
this.setState({
|
||||
volume,
|
||||
muted,
|
||||
})
|
||||
}
|
||||
|
||||
handleOnClickSettings = () => {
|
||||
this.props.onOpenVideoStatsPopover(this.settingsBtn, this.props.meta)
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
preview,
|
||||
|
@ -464,26 +93,9 @@ class Video extends ImmutablePureComponent {
|
|||
|
||||
const {
|
||||
containerWidth,
|
||||
currentTime,
|
||||
duration,
|
||||
volume,
|
||||
buffer,
|
||||
dragging,
|
||||
paused,
|
||||
fullscreen,
|
||||
hovered,
|
||||
muted,
|
||||
revealed,
|
||||
hoveringVolumeButton,
|
||||
hoveringVolumeControl,
|
||||
pipAvailable,
|
||||
isBuffering,
|
||||
} = this.state
|
||||
|
||||
const progress = (currentTime / duration) * 100
|
||||
|
||||
const volumeHeight = (muted) ? 0 : volume * this.volHeight
|
||||
const volumeHandleLoc = (muted) ? this.volHandleOffset(0) : this.volHandleOffset(volume)
|
||||
const playerStyle = {}
|
||||
|
||||
let { width, height } = this.props
|
||||
|
@ -505,7 +117,7 @@ class Video extends ImmutablePureComponent {
|
|||
|
||||
let preload
|
||||
|
||||
if (startTime || fullscreen || dragging) {
|
||||
if (startTime) {
|
||||
preload = 'auto'
|
||||
} else if (detailed) {
|
||||
preload = 'metadata'
|
||||
|
@ -513,87 +125,13 @@ class Video extends ImmutablePureComponent {
|
|||
preload = 'none'
|
||||
}
|
||||
|
||||
const mainContainerClasses = CX({
|
||||
d: 1,
|
||||
mt10: 1,
|
||||
outlineNone: 1,
|
||||
})
|
||||
|
||||
const seekHandleClasses = CX({
|
||||
d: 1,
|
||||
posAbs: 1,
|
||||
circle: 1,
|
||||
h20PX: 1,
|
||||
w20PX: 1,
|
||||
bgTransparent: 1,
|
||||
mlNeg5PX: 1,
|
||||
mr5: 1,
|
||||
z3: 1,
|
||||
aiCenter: 1,
|
||||
jcCenter: 1,
|
||||
videoEase: 1,
|
||||
opacity0: !dragging,
|
||||
opacity1: dragging || hovered,
|
||||
})
|
||||
|
||||
const seekInnerHandleClasses = CX({
|
||||
d: 1,
|
||||
circle: 1,
|
||||
h14PX: 1,
|
||||
w14PX: 1,
|
||||
bgBrand: 1,
|
||||
boxShadow1: 1,
|
||||
})
|
||||
|
||||
const progressClasses = CX({
|
||||
d: 1,
|
||||
radiusSmall: 1,
|
||||
mt10: 1,
|
||||
posAbs: 1,
|
||||
h4PX: 1,
|
||||
videoEase: 1,
|
||||
})
|
||||
|
||||
const volumeControlClasses = CX({
|
||||
d: 1,
|
||||
posAbs: 1,
|
||||
bgBlackOpaque: 1,
|
||||
videoPlayerVolume: 1,
|
||||
h122PX: 1,
|
||||
circle: 1,
|
||||
displayNone: !hoveringVolumeButton && !hoveringVolumeControl || !hovered,
|
||||
})
|
||||
|
||||
const videoControlsBackgroundClasses = CX({
|
||||
d: 1,
|
||||
z2: 1,
|
||||
px15: 1,
|
||||
videoPlayerControlsBackground: 1,
|
||||
posAbs: 1,
|
||||
bottom0: 1,
|
||||
right0: 1,
|
||||
left0: 1,
|
||||
displayNone: !hovered && !paused,
|
||||
})
|
||||
|
||||
const overlayClasses = CX({
|
||||
d: 1,
|
||||
top50PC: 1,
|
||||
left50PC: 1,
|
||||
posAbs: 1,
|
||||
z2: 1,
|
||||
aiCenter: 1,
|
||||
jcCenter: 1,
|
||||
displayNone: !paused && !isBuffering,
|
||||
})
|
||||
|
||||
if (!revealed && sensitive) {
|
||||
return <SensitiveMediaItem onClick={this.toggleReveal} />
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={mainContainerClasses}
|
||||
className={[_s.d, _s.mt10, _s.outlineNone].join(' ')}
|
||||
style={playerStyle}
|
||||
ref={this.setPlayerRef}
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
|
@ -601,35 +139,19 @@ class Video extends ImmutablePureComponent {
|
|||
onClick={this.handleClickRoot}
|
||||
tabIndex={0}
|
||||
>
|
||||
|
||||
<div className={overlayClasses} id='overlay'>
|
||||
{
|
||||
!paused && true &&
|
||||
<Icon id='loading' size='60px' className={[_s.d, _s.posAbs].join(' ')} />
|
||||
}
|
||||
</div>
|
||||
|
||||
<div data-vjs-player>
|
||||
<video
|
||||
className={[_s.d, _s.h100PC, _s.w100PC, _s.outlineNone, 'video-js'].join(' ')}
|
||||
ref={this.setVideoRef}
|
||||
playsInline
|
||||
// poster={preview}
|
||||
// preload={preload}
|
||||
// role='button'
|
||||
// tabIndex='0'
|
||||
// aria-label={alt}
|
||||
// title={alt}
|
||||
// width={width}
|
||||
// height={height}
|
||||
// volume={volume}
|
||||
// onClick={this.togglePlay}
|
||||
// onPlay={this.handlePlay}
|
||||
// onPause={this.handlePause}
|
||||
// onTimeUpdate={this.handleTimeUpdate}
|
||||
// onLoadedData={this.handleLoadedData}
|
||||
// onProgress={this.handleProgress}
|
||||
// onVolumeChange={this.handleVolumeChange}
|
||||
poster={preview}
|
||||
preload={preload}
|
||||
role='button'
|
||||
tabIndex='0'
|
||||
aria-label={alt}
|
||||
title={alt}
|
||||
width={width}
|
||||
height={height}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
@ -651,30 +173,9 @@ class Video extends ImmutablePureComponent {
|
|||
}
|
||||
|
||||
const messages = defineMessages({
|
||||
play: { id: 'video.play', defaultMessage: 'Play' },
|
||||
pause: { id: 'video.pause', defaultMessage: 'Pause' },
|
||||
mute: { id: 'video.mute', defaultMessage: 'Mute sound' },
|
||||
unmute: { id: 'video.unmute', defaultMessage: 'Unmute sound' },
|
||||
hide: { id: 'video.hide', defaultMessage: 'Hide video' },
|
||||
fullscreen: { id: 'video.fullscreen', defaultMessage: 'Full screen' },
|
||||
exit_fullscreen: { id: 'video.exit_fullscreen', defaultMessage: 'Exit full screen' },
|
||||
sensitive: { id: 'status.sensitive_warning', defaultMessage: 'Sensitive content' },
|
||||
hidden: { id: 'status.media_hidden', defaultMessage: 'Media hidden' },
|
||||
video_stats: { id: 'video.stats_label', defaultMessage: 'Video meta stats' },
|
||||
toggle_visible: { id: 'media_gallery.toggle_visible', defaultMessage: 'Hide media' },
|
||||
})
|
||||
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
onOpenVideoStatsPopover(targetRef, meta) {
|
||||
dispatch(openPopover(POPOVER_VIDEO_STATS, {
|
||||
targetRef,
|
||||
meta,
|
||||
position: 'top',
|
||||
}))
|
||||
}
|
||||
})
|
||||
|
||||
Video.propTypes = {
|
||||
preview: PropTypes.string,
|
||||
src: PropTypes.string.isRequired,
|
||||
|
@ -692,7 +193,6 @@ Video.propTypes = {
|
|||
blurhash: PropTypes.string,
|
||||
aspectRatio: PropTypes.number,
|
||||
meta: ImmutablePropTypes.map,
|
||||
onOpenVideoStatsPopover: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
export default injectIntl(connect(null, mapDispatchToProps)(Video))
|
||||
export default injectIntl(Video)
|
|
@ -7,109 +7,149 @@ import { injectIntl, defineMessages } from 'react-intl'
|
|||
import { expandAccountMediaTimeline } from '../actions/timelines'
|
||||
import { getAccountGallery } from '../selectors'
|
||||
import ColumnIndicator from '../components/column_indicator'
|
||||
import Heading from '../components/heading'
|
||||
import TabBar from '../components/tab_bar'
|
||||
import MediaItem from '../components/media_item'
|
||||
import LoadMore from '../components/load_more'
|
||||
import Block from '../components/block'
|
||||
import Image from '../components/image'
|
||||
import Album from '../components/album'
|
||||
import MediaGalleryPlaceholder from '../components/placeholder/media_gallery_placeholder'
|
||||
|
||||
class AccountAlbums extends ImmutablePureComponent {
|
||||
|
||||
componentDidMount() {
|
||||
const { accountId, mediaType } = this.props
|
||||
// componentDidMount() {
|
||||
// const { accountId, mediaType } = this.props
|
||||
|
||||
if (accountId && accountId !== -1) {
|
||||
this.props.dispatch(expandAccountMediaTimeline(accountId, { mediaType }))
|
||||
}
|
||||
}
|
||||
// if (accountId && accountId !== -1) {
|
||||
// this.props.dispatch(expandAccountMediaTimeline(accountId, { mediaType }))
|
||||
// }
|
||||
// }
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (
|
||||
(nextProps.accountId && nextProps.accountId !== this.props.accountId) ||
|
||||
(nextProps.accountId && nextProps.mediaType !== this.props.mediaType)
|
||||
) {
|
||||
this.props.dispatch(expandAccountMediaTimeline(nextProps.accountId, {
|
||||
mediaType: nextProps.mediaType,
|
||||
}))
|
||||
}
|
||||
}
|
||||
// componentWillReceiveProps(nextProps) {
|
||||
// if (
|
||||
// (nextProps.accountId && nextProps.accountId !== this.props.accountId) ||
|
||||
// (nextProps.accountId && nextProps.mediaType !== this.props.mediaType)
|
||||
// ) {
|
||||
// this.props.dispatch(expandAccountMediaTimeline(nextProps.accountId, {
|
||||
// mediaType: nextProps.mediaType,
|
||||
// }))
|
||||
// }
|
||||
// }
|
||||
|
||||
handleScrollToBottom = () => {
|
||||
if (this.props.hasMore) {
|
||||
this.handleLoadMore(this.props.attachments.size > 0 ? this.props.attachments.last().getIn(['status', 'id']) : undefined)
|
||||
}
|
||||
}
|
||||
// handleScrollToBottom = () => {
|
||||
// if (this.props.hasMore) {
|
||||
// this.handleLoadMore(this.props.attachments.size > 0 ? this.props.attachments.last().getIn(['status', 'id']) : undefined)
|
||||
// }
|
||||
// }
|
||||
|
||||
handleScroll = (e) => {
|
||||
const { scrollTop, scrollHeight, clientHeight } = e.target
|
||||
const offset = scrollHeight - scrollTop - clientHeight
|
||||
// handleScroll = (e) => {
|
||||
// const { scrollTop, scrollHeight, clientHeight } = e.target
|
||||
// const offset = scrollHeight - scrollTop - clientHeight
|
||||
|
||||
if (150 > offset && !this.props.isLoading) {
|
||||
this.handleScrollToBottom()
|
||||
}
|
||||
}
|
||||
// if (150 > offset && !this.props.isLoading) {
|
||||
// this.handleScrollToBottom()
|
||||
// }
|
||||
// }
|
||||
|
||||
handleLoadMore = (maxId) => {
|
||||
if (this.props.accountId && this.props.accountId !== -1) {
|
||||
this.props.dispatch(expandAccountMediaTimeline(this.props.accountId, {
|
||||
maxId,
|
||||
mediaType: this.props.mediaType,
|
||||
}))
|
||||
}
|
||||
}
|
||||
// handleLoadMore = (maxId) => {
|
||||
// if (this.props.accountId && this.props.accountId !== -1) {
|
||||
// this.props.dispatch(expandAccountMediaTimeline(this.props.accountId, {
|
||||
// maxId,
|
||||
// mediaType: this.props.mediaType,
|
||||
// }))
|
||||
// }
|
||||
// }
|
||||
|
||||
handleLoadOlder = (e) => {
|
||||
e.preventDefault()
|
||||
this.handleScrollToBottom()
|
||||
}
|
||||
// handleLoadOlder = (e) => {
|
||||
// e.preventDefault()
|
||||
// this.handleScrollToBottom()
|
||||
// }
|
||||
|
||||
render() {
|
||||
const {
|
||||
attachments,
|
||||
isLoading,
|
||||
hasMore,
|
||||
intl,
|
||||
account,
|
||||
} = this.props
|
||||
|
||||
if (!account) return null
|
||||
|
||||
return (
|
||||
<Block>
|
||||
<div
|
||||
role='feed'
|
||||
onScroll={this.handleScroll}
|
||||
className={[_s.d, _s.flexRow, _s.flexWrap, _s.py5, _s.px5].join(' ')}
|
||||
>
|
||||
|
||||
{
|
||||
attachments.map((attachment, i) => (
|
||||
<MediaItem
|
||||
key={attachment.get('id')}
|
||||
attachment={attachment}
|
||||
account={account}
|
||||
/>
|
||||
))
|
||||
}
|
||||
|
||||
{
|
||||
isLoading && attachments.size === 0 &&
|
||||
<div className={[_s.d, _s.w100PC].join(' ')}>
|
||||
<MediaGalleryPlaceholder />
|
||||
</div>
|
||||
}
|
||||
|
||||
{
|
||||
!isLoading && attachments.size === 0 &&
|
||||
<ColumnIndicator type='error' message={intl.formatMessage(messages.none)} />
|
||||
}
|
||||
<div className={[_s.d, _s.px10, _s.py10].join(' ')}>
|
||||
<div className={[_s.d, _s.px5, _s.py5, _s.mb10].join(' ')}>
|
||||
<Heading size='h2'>Photos</Heading>
|
||||
</div>
|
||||
<TabBar tabs={[
|
||||
{
|
||||
title: 'All Photos',
|
||||
to: '/'
|
||||
},
|
||||
{
|
||||
title: 'Albums',
|
||||
isActive: true,
|
||||
to: '/'
|
||||
},
|
||||
]}/>
|
||||
</div>
|
||||
|
||||
{
|
||||
hasMore && !(isLoading && attachments.size === 0) &&
|
||||
<LoadMore visible={!isLoading} onClick={this.handleLoadOlder} />
|
||||
}
|
||||
<div className={[_s.d, _s.w100PC, _s.flexRow, _s.flexWrap, _s.px10, _s.mb15, _s.pb10].join(' ')}>
|
||||
<Album />
|
||||
<Album />
|
||||
<Album />
|
||||
<Album />
|
||||
<Album />
|
||||
<Album />
|
||||
|
||||
<Album isDummy />
|
||||
<Album isDummy />
|
||||
<Album isDummy />
|
||||
<Album isDummy />
|
||||
<Album isDummy />
|
||||
<Album isDummy />
|
||||
</div>
|
||||
</Block>
|
||||
)
|
||||
// const {
|
||||
// attachments,
|
||||
// isLoading,
|
||||
// hasMore,
|
||||
// intl,
|
||||
// account,
|
||||
// } = this.props
|
||||
|
||||
// if (!account) return null
|
||||
|
||||
// return (
|
||||
// <Block>
|
||||
// <div
|
||||
// role='feed'
|
||||
// onScroll={this.handleScroll}
|
||||
// className={[_s.d, _s.flexRow, _s.flexWrap, _s.py5, _s.px5].join(' ')}
|
||||
// >
|
||||
|
||||
// {
|
||||
// attachments.map((attachment, i) => (
|
||||
// <MediaItem
|
||||
// key={attachment.get('id')}
|
||||
// attachment={attachment}
|
||||
// account={account}
|
||||
// />
|
||||
// ))
|
||||
// }
|
||||
|
||||
// {
|
||||
// isLoading && attachments.size === 0 &&
|
||||
// <div className={[_s.d, _s.w100PC].join(' ')}>
|
||||
// <MediaGalleryPlaceholder />
|
||||
// </div>
|
||||
// }
|
||||
|
||||
// {
|
||||
// !isLoading && attachments.size === 0 &&
|
||||
// <ColumnIndicator type='error' message={intl.formatMessage(messages.none)} />
|
||||
// }
|
||||
// </div>
|
||||
|
||||
// {
|
||||
// hasMore && !(isLoading && attachments.size === 0) &&
|
||||
// <LoadMore visible={!isLoading} onClick={this.handleLoadOlder} />
|
||||
// }
|
||||
// </Block>
|
||||
// )
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,11 +1,31 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { connect } from 'react-redux'
|
||||
import { clearCompose } from '../../actions/compose'
|
||||
import { withRouter } from 'react-router-dom'
|
||||
import queryString from 'query-string'
|
||||
import { clearCompose, changeCompose } from '../../actions/compose'
|
||||
import ComposeFormContainer from './containers/compose_form_container'
|
||||
|
||||
class Compose extends React.PureComponent {
|
||||
|
||||
componentDidMount() {
|
||||
const search = this.context.router.route.location.search
|
||||
try {
|
||||
const qp = queryString.parse(search)
|
||||
const url = `${qp.url || ''}`
|
||||
const text = `${qp.text || ''}`
|
||||
|
||||
if (url.length > 0 || text.length > 0) {
|
||||
let value = ""
|
||||
if (text.length > 0) value += `${text} `
|
||||
if (url.length > 0) value += url
|
||||
this.props.dispatch(changeCompose(value))
|
||||
}
|
||||
} catch (error) {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.dispatch(clearCompose())
|
||||
}
|
||||
|
@ -16,4 +36,8 @@ class Compose extends React.PureComponent {
|
|||
|
||||
}
|
||||
|
||||
export default connect()(Compose)
|
||||
Compose.contextTypes = {
|
||||
router: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
export default withRouter(connect()(Compose))
|
|
@ -2,6 +2,7 @@ export function About() { return import(/* webpackChunkName: "features/about/abo
|
|||
export function AboutSidebar() { return import(/* webpackChunkName: "components/about_sidebar" */'../../../components/sidebar/about_sidebar') }
|
||||
export function AccountTimeline() { return import(/* webpackChunkName: "features/account_timeline" */'../../account_timeline') }
|
||||
export function AccountCommentsTimeline() { return import(/* webpackChunkName: "features/account_comments_timeline" */'../../account_comments_timeline') }
|
||||
export function AccountAlbums() { return import(/* webpackChunkName: "features/account_albums" */'../../account_albums') }
|
||||
export function AccountGallery() { return import(/* webpackChunkName: "features/account_gallery" */'../../account_gallery') }
|
||||
export function AlbumCreate() { return import(/* webpackChunkName: "features/album_create" */'../../album_create') }
|
||||
export function AlbumCreateModal() { return import(/* webpackChunkName: "components/album_create_modal" */'../../../components/modal/album_create_modal') }
|
||||
|
|
|
@ -1,28 +1,27 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { me } from '../initial_state'
|
||||
import DefaultSidebar from '../components/sidebar/default_sidebar'
|
||||
import { CX } from '../constants'
|
||||
import ComposeNavigationBar from '../components/navigation_bar/compose_navigation_bar_xs'
|
||||
import Responsive from '../features/ui/util/responsive_component'
|
||||
import WrappedBundle from '../features/ui/util/wrapped_bundle'
|
||||
import {
|
||||
SidebarXS,
|
||||
} from '../features/ui/util/async_components'
|
||||
|
||||
class ComposeLayout extends React.PureComponent {
|
||||
|
||||
render() {
|
||||
const { children, isXS } = this.props
|
||||
|
||||
if (!isXS) return null
|
||||
|
||||
const mainClasses = CX({
|
||||
d: 1,
|
||||
w100PC: 1,
|
||||
flexGrow1: 1,
|
||||
borderRight1PX: !isXS,
|
||||
borderLeft1PX: !isXS,
|
||||
borderColorSecondary: !isXS,
|
||||
})
|
||||
|
||||
return (
|
||||
<div className={[_s.d, _s.w100PC, _s.minH100VH, _s.bgTertiary].join(' ')}>
|
||||
<WrappedBundle component={SidebarXS} />
|
||||
|
||||
<ComposeNavigationBar />
|
||||
|
||||
<main role='main' className={[_s.d, _s.w100PC, _s.flexGrow1].join(' ')}>
|
||||
<div className={[_s.d, _s.w100PC, _s.maxW640PX, _s.mlAuto, _s.mrAuto, _s.minH100VH, _s.bgTertiary].join(' ')}>
|
||||
<ComposeNavigationBar isXS={isXS} />
|
||||
<main role='main' className={mainClasses}>
|
||||
{ children }
|
||||
</main>
|
||||
</div>
|
||||
|
|
|
@ -88,7 +88,7 @@ class ProfileLayout extends ImmutablePureComponent {
|
|||
<Responsive max={BREAKPOINT_EXTRA_SMALL}>
|
||||
{
|
||||
!!me &&
|
||||
<ProfileNavigationBar titleHTML={titleHTML} />
|
||||
<ProfileNavigationBar titleHTML={titleHTML} account={account} />
|
||||
}
|
||||
{
|
||||
!me &&
|
||||
|
|
|
@ -35,7 +35,6 @@ class ComposePage extends React.PureComponent {
|
|||
const { width } = this.state
|
||||
|
||||
const isXS = width <= BREAKPOINT_EXTRA_SMALL
|
||||
if (!isXS) throw 'This page does not exist'
|
||||
|
||||
return (
|
||||
<ComposeLayout title='Compose' isXS={isXS}>
|
||||
|
|
|
@ -4,7 +4,7 @@ import {
|
|||
ALBUMS_FETCH_FAIL,
|
||||
ALBUMS_CREATE_SUCCESS,
|
||||
ALBUMS_REMOVE_REQUEST,
|
||||
} from '../actions/bookmarks'
|
||||
} from '../actions/albums'
|
||||
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'
|
||||
|
||||
const initialState = ImmutableMap({
|
||||
|
@ -24,7 +24,7 @@ export default function albums(state = initialState, action) {
|
|||
})
|
||||
case ALBUMS_FETCH_SUCCESS:
|
||||
return state.withMutations((map) => {
|
||||
map.set('items', fromJS(action.bookmarkCollections))
|
||||
map.set('items', fromJS(action.albums))
|
||||
map.set('isLoading', false)
|
||||
map.set('isFetched', true)
|
||||
map.set('isError', false)
|
||||
|
@ -36,10 +36,10 @@ export default function albums(state = initialState, action) {
|
|||
map.set('isError', true)
|
||||
})
|
||||
case ALBUMS_CREATE_SUCCESS:
|
||||
return state.update('items', list => list.push(fromJS(action.bookmarkCollection)))
|
||||
return state.update('items', list => list.push(fromJS(action.albums)))
|
||||
case ALBUMS_REMOVE_REQUEST:
|
||||
return state.update('items', list => list.filterNot((item) => {
|
||||
return item.get('id') === action.bookmarkCollectionId
|
||||
return item.get('id') === action.albumId
|
||||
}))
|
||||
default:
|
||||
return state
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
// APIs for normalizing fullscreen operations. Note that Edge uses
|
||||
// the WebKit-prefixed APIs currently (as of Edge 16).
|
||||
|
||||
export const isFullscreen = () => document.fullscreenElement ||
|
||||
document.webkitFullscreenElement ||
|
||||
document.mozFullScreenElement;
|
||||
|
||||
export const exitFullscreen = () => {
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
} else if (document.webkitExitFullscreen) {
|
||||
document.webkitExitFullscreen();
|
||||
} else if (document.mozCancelFullScreen) {
|
||||
document.mozCancelFullScreen();
|
||||
}
|
||||
};
|
||||
|
||||
export const requestFullscreen = el => {
|
||||
if (el.requestFullscreen) {
|
||||
el.requestFullscreen();
|
||||
} else if (el.webkitRequestFullscreen) {
|
||||
el.webkitRequestFullscreen();
|
||||
} else if (el.mozRequestFullScreen) {
|
||||
el.mozRequestFullScreen();
|
||||
}
|
||||
};
|
||||
|
||||
export const attachFullscreenListener = (listener) => {
|
||||
if ('onfullscreenchange' in document) {
|
||||
document.addEventListener('fullscreenchange', listener);
|
||||
} else if ('onwebkitfullscreenchange' in document) {
|
||||
document.addEventListener('webkitfullscreenchange', listener);
|
||||
} else if ('onmozfullscreenchange' in document) {
|
||||
document.addEventListener('mozfullscreenchange', listener);
|
||||
}
|
||||
};
|
||||
|
||||
export const detachFullscreenListener = (listener) => {
|
||||
if ('onfullscreenchange' in document) {
|
||||
document.removeEventListener('fullscreenchange', listener);
|
||||
} else if ('onwebkitfullscreenchange' in document) {
|
||||
document.removeEventListener('webkitfullscreenchange', listener);
|
||||
} else if ('onmozfullscreenchange' in document) {
|
||||
document.removeEventListener('mozfullscreenchange', listener);
|
||||
}
|
||||
};
|
|
@ -600,6 +600,7 @@ pre {
|
|||
.maxW212PX { max-width: 212px; }
|
||||
|
||||
.minW330PX { min-width: 330px; }
|
||||
.minW162PX { min-width: 162px; }
|
||||
.minW120PX { min-width: 120px; }
|
||||
.minW84PX { min-width: 84px; }
|
||||
.minW76PX { min-width: 76px; }
|
||||
|
@ -849,6 +850,7 @@ pre {
|
|||
.mtNeg50PX { margin-top: -50px; }
|
||||
.mtNeg75PX { margin-top: -75px; }
|
||||
|
||||
.pt100PC { padding-top: 100%; }
|
||||
.pt5625PC { padding-top: 56.25%; }
|
||||
.pt25PC { padding-top: 25%; }
|
||||
.pt53PX { padding-top: 53px; }
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# cover_id :bigint(8)
|
||||
# count :integer default(0), not null
|
||||
#
|
||||
|
||||
class MediaAttachmentAlbum < ApplicationRecord
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
# account_id :integer not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# visibility :string
|
||||
#
|
||||
|
||||
class StatusBookmarkCollection < ApplicationRecord
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
class AddCountToMediaAttachmentAlbums < ActiveRecord::Migration[5.2]
|
||||
disable_ddl_transaction!
|
||||
|
||||
def change
|
||||
safety_assured { add_column :media_attachment_albums, :count, :integer, null: false, default: 0 }
|
||||
end
|
||||
end
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2020_12_17_003945) do
|
||||
ActiveRecord::Schema.define(version: 2020_12_18_012018) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "pg_stat_statements"
|
||||
|
@ -420,6 +420,7 @@ ActiveRecord::Schema.define(version: 2020_12_17_003945) do
|
|||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.bigint "cover_id"
|
||||
t.integer "count", default: 0, null: false
|
||||
t.index ["cover_id"], name: "index_media_attachment_albums_on_cover_id"
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue