Skip to content

Commit

Permalink
Merge pull request #3 from thomassth/keyboard-shortcuts
Browse files Browse the repository at this point in the history
Keyboard shortcuts
  • Loading branch information
Zen-cronic authored Nov 16, 2024
2 parents 7352b25 + dad01d6 commit 4390d5d
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@ import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { Flex, Box, Text, Button, Card, Link } from 'theme-ui'

@connect((state) => {
return {
conversation: state.zid_metadata.zid_metadata
}
})
class Comment extends React.Component {
onAcceptClicked() {
this.props.acceptClickHandler(this.props.comment)
Expand All @@ -25,7 +20,15 @@ class Comment extends React.Component {

render() {
return (
<Card sx={{ mb: [3], minWidth: '35em' }}>
<Card
sx={{
mb: [3],
p: [1],
width: '35em',
maxWidth: '100%',
boxShadow: this.props.currentItem ? 'lightseagreen 0px 0px 0px 2px;' : ''
}}
className={this.props.currentItem ? 'current-item' : ''}>
<Box>
<Text sx={{ mb: [3], color: 'red', fontSize: 12 }}>{this.props.comment.active ? null : 'Comment flagged as toxic by Jigsaw Perspective API. Comment not shown to participants. Accept to override.'}</Text>
<Text sx={{ mb: [3] }}>{this.props.comment.txt}</Text>
Expand All @@ -35,40 +38,41 @@ class Comment extends React.Component {
alignItems: 'center',
width: '100%'
}}>
<Box>
<Flex
sx={{
flexWrap: 'wrap',
gap: [2, 3]
}}>
{this.props.acceptButton ? (
<Button
sx={{ mr: [3] }}
onClick={this.onAcceptClicked.bind(this)}>
{this.props.acceptButtonText}
{this.props.acceptButtonText} (A)
</Button>
) : null}
{this.props.rejectButton ? (
<Button onClick={this.onRejectClicked.bind(this)}>
{this.props.rejectButtonText}
{this.props.rejectButtonText} (R)
</Button>
) : null}
</Box>
<Flex sx={{ alignItems: 'center' }}>
<Link
target="_blank"
sx={{ mr: [2] }}
href="https://compdemocracy.org/metadata">
{this.props.isMetaCheckbox ? 'metadata' : null}
</Link>
{this.props.isMetaCheckbox ? (
<input
type="checkbox"
label="metadata"
ref={(c) => (this.is_meta = c)}
checked={this.props.comment.is_meta}
onChange={this.onIsMetaClicked.bind(this)}
/>
) : null}
</Flex>
<Link
target="_blank"
sx={{ mr: [2] }}
href="https://compdemocracy.org/metadata">
{this.props.isMetaCheckbox ? 'metadata' : null}
</Link>
{this.props.isMetaCheckbox ? (
<input
type="checkbox"
label="metadata"
ref={(c) => (this.is_meta = c)}
checked={this.props.comment.is_meta}
onChange={this.onIsMetaClicked.bind(this)}
/>
) : null}
</Flex>
</Box>
</Card>
</Card >
)
}
}
Expand All @@ -87,7 +91,12 @@ Comment.propTypes = {
active: PropTypes.bool,
txt: PropTypes.string,
is_meta: PropTypes.bool
})
}),
currentItem: PropTypes.bool
}

export default Comment
export default connect((state) => {
return {
conversation: state.zid_metadata.zid_metadata
}
})(Comment)
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright (C) 2012-present, The Authors. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.

import React from 'react'
import React, { useEffect, useState } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import {
Expand All @@ -9,47 +9,72 @@ import {
} from '../../../actions'
import Comment from './comment'

@connect((state) => state.mod_comments_accepted)
class ModerateCommentsAccepted extends React.Component {
onCommentRejected(comment) {
this.props.dispatch(changeCommentStatusToRejected(comment))
}
function ModerateCommentsAccepted({ dispatch, accepted_comments = [] }) {

const [currentItem, setCurrentItem] = useState(0)
useEffect(() => {
function handleKeyDown(e) {
if (e.code === 'KeyR') {
onCommentRejected(accepted_comments[currentItem])
}
if (e.code === 'KeyW') {
setCurrentItem(Math.max(0, currentItem - 1))
}
if (accepted_comments && e.code === 'KeyS') {
setCurrentItem(Math.min(accepted_comments.length - 1, currentItem + 1))
}
}
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [currentItem, accepted_comments]);

useEffect(() => {
if (accepted_comments && currentItem > accepted_comments.length - 1) {
setCurrentItem(Math.min(accepted_comments.length - 1, currentItem))
}
}, [accepted_comments?.length ?? 0])

toggleIsMetaHandler(comment, is_meta) {
this.props.dispatch(changeCommentCommentIsMeta(comment, is_meta))
function onCommentRejected(comment) {
dispatch(changeCommentStatusToRejected(comment))
}

createCommentMarkup() {
const comments = this.props.accepted_comments.map((comment, i) => {
function toggleIsMetaHandler(comment, is_meta) {
dispatch(changeCommentCommentIsMeta(comment, is_meta))
}
function createCommentMarkup() {
const comments = accepted_comments.map((comment, i) => {
return (
<Comment
key={i}
rejectButton
rejectClickHandler={this.onCommentRejected.bind(this)}
rejectClickHandler={onCommentRejected}
rejectButtonText="reject"
isMetaCheckbox
toggleIsMetaHandler={this.toggleIsMetaHandler.bind(this)}
toggleIsMetaHandler={toggleIsMetaHandler}
comment={comment}
currentItem={currentItem === i}
/>
)
})
return comments
}

render() {
return (
<div>
{this.props.accepted_comments !== null
? this.createCommentMarkup()
: 'Loading accepted comments...'}
</div>
)
}
return (
<div>
{accepted_comments !== null
? createCommentMarkup()
: 'Loading accepted comments...'}
</div>
)
}

ModerateCommentsAccepted.propTypes = {
dispatch: PropTypes.func,
accepted_comments: PropTypes.arrayOf(PropTypes.object)
}

export default ModerateCommentsAccepted
const mapStateToProps = (state) => ({
dispatch: state.mod_comments_accepted.dispatch,
accepted_comments: state.mod_comments_accepted.accepted_comments,
});
export default connect(mapStateToProps)(ModerateCommentsAccepted)
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright (C) 2012-present, The Authors. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.

import React from 'react'
import React, { useEffect, useState } from 'react'
import PropTypes from 'prop-types'
import {
changeCommentStatusToAccepted,
Expand All @@ -9,47 +9,72 @@ import {
import { connect } from 'react-redux'
import Comment from './comment'

@connect((state) => state.mod_comments_rejected)
class ModerateCommentsRejected extends React.Component {
onCommentAccepted(comment) {
this.props.dispatch(changeCommentStatusToAccepted(comment))
function ModerateCommentsRejected({ dispatch, rejected_comments = [] }) {
const [currentItem, setCurrentItem] = useState(0)
useEffect(() => {
function handleKeyDown(e) {
if (e.code === 'KeyA') {
onCommentAccepted(rejected_comments[currentItem])
}
if (e.code === 'KeyW') {
setCurrentItem(Math.max(0, currentItem - 1))
}
if (rejected_comments && e.code === 'KeyS') {
setCurrentItem(Math.min(rejected_comments.length - 1, currentItem + 1))
}
}
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [currentItem, rejected_comments]);

useEffect(() => {
if (rejected_comments && currentItem > rejected_comments.length - 1) {
setCurrentItem(Math.min(rejected_comments.length - 1, currentItem))
}
}, [rejected_comments?.length ?? 0])


function onCommentAccepted(comment) {
dispatch(changeCommentStatusToAccepted(comment))
}

toggleIsMetaHandler(comment, is_meta) {
this.props.dispatch(changeCommentCommentIsMeta(comment, is_meta))
function toggleIsMetaHandler(comment, is_meta) {
dispatch(changeCommentCommentIsMeta(comment, is_meta))
}

createCommentMarkup() {
const comments = this.props.rejected_comments.map((comment, i) => {
function createCommentMarkup() {
const comments = rejected_comments.map((comment, i) => {
return (
<Comment
key={i}
acceptButton
acceptButtonText="accept"
acceptClickHandler={this.onCommentAccepted.bind(this)}
acceptClickHandler={onCommentAccepted}
isMetaCheckbox
toggleIsMetaHandler={this.toggleIsMetaHandler.bind(this)}
toggleIsMetaHandler={toggleIsMetaHandler}
comment={comment}
currentItem={currentItem === i}
/>
)
})
return comments
}

render() {
return (
<div>
{this.props.rejected_comments !== null
? this.createCommentMarkup()
: 'Loading rejected comments...'}
</div>
)
}
return (
<div>
{rejected_comments !== null
? createCommentMarkup()
: 'Loading rejected comments...'}
</div>
)
}

ModerateCommentsRejected.propTypes = {
dispatch: PropTypes.func,
rejected_comments: PropTypes.arrayOf(PropTypes.object)
}

export default ModerateCommentsRejected
const mapStateToProps = (state) => ({
dispatch: state.mod_comments_rejected.dispatch,
rejected_comments: state.mod_comments_rejected.rejected_comments,
});
export default connect(mapStateToProps)(ModerateCommentsRejected)
Loading

0 comments on commit 4390d5d

Please sign in to comment.