Permalink
Browse files

add classic view post controls

  • Loading branch information...
grantcodes committed Dec 8, 2018
1 parent c23fb6a commit bb5acc83438ae4297de2f7e727f16ed44fa38d59
@@ -203,6 +203,7 @@ class ClassicView extends Component {
{selectedPostId && (
<div ref={this.articleRef} className={classes.postColumn}>
<Post
focus
post={post}
expandableContent={false}
style={{
@@ -211,6 +212,8 @@ class ClassicView extends Component {
maxWidth: 700,
boxShadow: 'none',
}}
shortcutOnNext={this.handleNextPost}
scrollElement={this.articleRef.current}
/>
<AppBar
position="sticky"
@@ -36,6 +36,7 @@ class LayoutShortcuts extends Component {
onPrevious()
break
case 'SELECT_POST':
focusComponent('post')
onSelectPost()
break
case 'FOCUS_CHANNEL_LIST':
@@ -0,0 +1,122 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { withStyles } from '@material-ui/core/styles'
import { Shortcuts } from 'react-shortcuts'
import { focusComponent } from '../../actions'

const styles = theme => {
const color =
theme.palette.type === 'dark'
? theme.palette.secondary.main
: theme.palette.primary.main
return {
main: {
display: 'block',
outline: 'none',
},
focused: {
boxShadow: `inset -2px -2px 0 ${color}, inset 2px 2px 0 ${color}`,
},
}
}

class PostShortcuts extends Component {
constructor(props) {
super(props)
this.handleShortcuts = this.handleShortcuts.bind(this)
this.ref = React.createRef()
}

componentDidUpdate() {
const { focus, postsAreFocused } = this.props
const el = this.ref.current._domNode
if (focus && postsAreFocused && el !== document.activeElement) {
el.focus()
}
}

handleShortcuts(action) {
const { onNext, focusComponent, scrollElement, post } = this.props

switch (action) {
case 'SCROLL_DOWN':
if (scrollElement) {
scrollElement.scrollBy(0, 50)
}
break
case 'SCROLL_UP':
if (scrollElement) {
scrollElement.scrollBy(0, -50)
}
break
case 'TO_TIMELINE':
focusComponent('timeline')
break
case 'NEXT':
onNext()
break
case 'OPEN':
if (post.url) {
window.open(post.url, '_blank')
}
break
case 'TOGGLE_READ':
console.log('Mark me as read please')
break
default:
// Nothing to handle
break
}
}

render() {
const { children, classes, focus, postsAreFocused, ...props } = this.props
const classNames = [classes.main]
if (postsAreFocused && focus) {
classNames.push(classes.focused)
}

return (
<Shortcuts
{...props}
name="POST"
handler={this.handleShortcuts}
ref={this.ref}
className={classNames.join(' ')}
>
{children}
</Shortcuts>
)
}
}

PostShortcuts.defaultProps = {
focus: false,
onNext: () => {},
}

PostShortcuts.propTypes = {
post: PropTypes.object.isRequired,
focus: PropTypes.bool.isRequired,
postsAreFocused: PropTypes.bool.isRequired,
onNext: PropTypes.func.isRequired,
}

const mapStateToProps = state => ({
postsAreFocused: state.app.get('focusedComponent') === 'post',
})

const mapDispatchToProps = dispatch =>
bindActionCreators(
{
focusComponent,
},
dispatch
)

export default connect(
mapStateToProps,
mapDispatchToProps
)(withStyles(styles)(PostShortcuts))
@@ -5,6 +5,7 @@ import { withStyles } from '@material-ui/core/styles'
import Card from '@material-ui/core/Card'
import CardHeader from '@material-ui/core/CardHeader'
import CardMedia from '@material-ui/core/CardMedia'
import Shortcuts from './Shortcuts'
import AuthorAvatar from '../AuthorAvatar'
import TogetherCardContent from './Content'
import TogetherCardPhotos from './Photos'
@@ -23,6 +24,9 @@ const TogetherCard = ({
style,
classes,
selectedChannel,
scrollElement,
shortcutOnNext,
focus,
}) => {
// Parse author data
const avatarData = authorToAvatarData(item.author)
@@ -69,102 +73,110 @@ const TogetherCard = ({
data-isread={item._is_read}
data-id={item._id}
>
{item.featured && <TogetherCardPhotos photos={item.featured} />}
<CardHeader
title={authorNameLink}
subheader={date}
avatar={<AuthorAvatar author={item.author || '?'} />}
/>

{property('in-reply-to', ({ value: url }) => (
<TogetherCardReplyContext
type="reply"
url={url}
reference={item.refs ? item.refs[url] : null}
/>
))}

{property('repost-of', ({ value: url }) => (
<TogetherCardReplyContext
type="repost"
url={url}
reference={item.refs ? item.refs[url] : null}
<Shortcuts
post={item}
scrollElement={scrollElement}
onNext={shortcutOnNext}
focus={focus}
>
{item.featured && <TogetherCardPhotos photos={item.featured} />}
<CardHeader
title={authorNameLink}
subheader={date}
avatar={<AuthorAvatar author={item.author || '?'} />}
/>
))}

{property('like-of', ({ value: url }) => (
<TogetherCardReplyContext
type="like"
url={url}
reference={item.refs ? item.refs[url] : null}
/>
))}
{property('in-reply-to', ({ value: url }) => (
<TogetherCardReplyContext
type="reply"
url={url}
reference={item.refs ? item.refs[url] : null}
/>
))}

{property('bookmark-of', ({ value: url }) => (
<TogetherCardReplyContext
type="bookmark"
url={url}
reference={item.refs ? item.refs[url] : null}
/>
))}
{property('repost-of', ({ value: url }) => (
<TogetherCardReplyContext
type="repost"
url={url}
reference={item.refs ? item.refs[url] : null}
/>
))}

{property('quotation-of', ({ value: url }) => (
<TogetherCardReplyContext
type="quotation"
url={url}
reference={item.refs ? item.refs[url] : null}
/>
))}

{property('video', ({ value: video }) =>
typeof video == 'string' ? (
<CardMedia
component="video"
src={video}
controls={true}
poster={
item.photo && item.photo.length === 1 ? item.photo[0] : null
}
{property('like-of', ({ value: url }) => (
<TogetherCardReplyContext
type="like"
url={url}
reference={item.refs ? item.refs[url] : null}
/>
) : null
)}
))}

{property('audio', ({ value: audio }) =>
typeof audio == 'string' ? (
<CardMedia component="audio" src={audio} controls={true} />
) : null
)}
{property('bookmark-of', ({ value: url }) => (
<TogetherCardReplyContext
type="bookmark"
url={url}
reference={item.refs ? item.refs[url] : null}
/>
))}

{/* TODO: This hides the single photo if there is a single video but I am not sure that is correct */}
{property('quotation-of', ({ value: url }) => (
<TogetherCardReplyContext
type="quotation"
url={url}
reference={item.refs ? item.refs[url] : null}
/>
))}

{property('video', ({ value: video }) =>
typeof video == 'string' ? (
<CardMedia
component="video"
src={video}
controls={true}
poster={
item.photo && item.photo.length === 1 ? item.photo[0] : null
}
/>
) : null
)}

{item.photo &&
!hideProperties.includes('photo') &&
(!item.video || item.video.length !== 1) && (
<TogetherCardPhotos photos={item.photo} />
{property('audio', ({ value: audio }) =>
typeof audio == 'string' ? (
<CardMedia component="audio" src={audio} controls={true} />
) : null
)}

{!item['repost-of'] && (
<TogetherCardContent expandable={expandableContent} post={item} />
)}
{/* TODO: This hides the single photo if there is a single video but I am not sure that is correct */}

{property('checkin', ({ value: location }) => (
<TogetherCardLocation location={location} author={item.author} />
))}
{property('location', ({ value: location }) => (
<TogetherCardLocation location={location} author={item.author} />
))}
{item.photo &&
!hideProperties.includes('photo') &&
(!item.video || item.video.length !== 1) && (
<TogetherCardPhotos photos={item.photo} />
)}

<TogetherCardActions
post={item}
channel={selectedChannel}
shownActions={shownActions}
/>
{!item['repost-of'] && (
<TogetherCardContent expandable={expandableContent} post={item} />
)}

{property('checkin', ({ value: location }) => (
<TogetherCardLocation location={location} author={item.author} />
))}
{property('location', ({ value: location }) => (
<TogetherCardLocation location={location} author={item.author} />
))}

<TogetherCardActions
post={item}
channel={selectedChannel}
shownActions={shownActions}
/>
</Shortcuts>
</Card>
)
}

TogetherCard.defaultProps = {
post: {},
focus: false,
hideProperties: [],
expandableContent: true,
}
@@ -174,6 +186,9 @@ TogetherCard.propTypes = {
shownActions: PropTypes.array,
hideProperties: PropTypes.array.isRequired,
expandableContent: PropTypes.bool.isRequired,
focus: PropTypes.bool.isRequired,
scrollElement: PropTypes.element,
shortcutOnNext: PropTypes.func,
}

const mapStateToProps = (state, props) => ({
@@ -18,6 +18,13 @@ export default {
NEXT: ['space'],
OPEN: ['v'],
TOGGLE_READ: ['m'],
// TODO:
// Load full content
// Previous
// Like
// Repost
// Reply
//
},
GLOBAL: {
KONAMI: ['up up down down left right left right b a'],
@@ -33,14 +40,9 @@ export default {
NEW_POST: ['ctrl+n', 'meta+n', 'alt+n'],
FOCUS_CHANNEL_LIST: ['c'],
HELP: ['?'],
// TODO:
// Open notifications
// Mark channel read
// Jump to timeline
},
}

/**
* TODO: Classic view needs to mark stuff as read
* TODO: Global keymapping for:
* Open notifications
* Mark channel read
* Jump to channel list
* Jump to timeline
*/

0 comments on commit bb5acc8

Please sign in to comment.