Add ability to reply, repost (without quote post), and like posts using VoiceOver (#765)

Co-authored-by: Paul Frazee <pfrazee@gmail.com>
zio/stable
Ollie H 2023-05-30 17:50:56 -07:00 committed by GitHub
parent 7458b6f600
commit a9a661ab58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 138 additions and 30 deletions

View File

@ -1,4 +1,4 @@
import React from 'react' import React, {useCallback, useMemo} from 'react'
import {observer} from 'mobx-react-lite' import {observer} from 'mobx-react-lite'
import {Linking, StyleSheet, View} from 'react-native' import {Linking, StyleSheet, View} from 'react-native'
import Clipboard from '@react-native-clipboard/clipboard' import Clipboard from '@react-native-clipboard/clipboard'
@ -133,6 +133,40 @@ export const PostThreadItem = observer(function PostThreadItem({
) )
}, [item, store]) }, [item, store])
const accessibilityActions = useMemo(
() => [
{
name: 'reply',
label: 'Reply',
},
{
name: 'repost',
label: item.post.viewer?.repost ? 'Undo repost' : 'Repost',
},
{name: 'like', label: item.post.viewer?.like ? 'Unlike' : 'Like'},
],
[item.post.viewer?.like, item.post.viewer?.repost],
)
const onAccessibilityAction = useCallback(
event => {
switch (event.nativeEvent.actionName) {
case 'like':
onPressToggleLike()
break
case 'reply':
onPressReply()
break
case 'repost':
onPressToggleRepost()
break
default:
break
}
},
[onPressReply, onPressToggleLike, onPressToggleRepost],
)
if (!record) { if (!record) {
return <ErrorMessage message="Invalid or unsupported post record" /> return <ErrorMessage message="Invalid or unsupported post record" />
} }
@ -154,7 +188,9 @@ export const PostThreadItem = observer(function PostThreadItem({
<PostHider <PostHider
testID={`postThreadItem-by-${item.post.author.handle}`} testID={`postThreadItem-by-${item.post.author.handle}`}
style={[styles.outer, styles.outerHighlighted, pal.border, pal.view]} style={[styles.outer, styles.outerHighlighted, pal.border, pal.view]}
moderation={item.moderation.thread}> moderation={item.moderation.thread}
accessibilityActions={accessibilityActions}
onAccessibilityAction={onAccessibilityAction}>
<View style={styles.layout}> <View style={styles.layout}>
<View style={styles.layoutAvi}> <View style={styles.layoutAvi}>
<Link <Link
@ -324,7 +360,9 @@ export const PostThreadItem = observer(function PostThreadItem({
pal.view, pal.view,
item._showParentReplyLine && styles.noTopBorder, item._showParentReplyLine && styles.noTopBorder,
]} ]}
moderation={item.moderation.thread}> moderation={item.moderation.thread}
accessibilityActions={accessibilityActions}
onAccessibilityAction={onAccessibilityAction}>
{item._showParentReplyLine && ( {item._showParentReplyLine && (
<View <View
style={[ style={[

View File

@ -1,4 +1,4 @@
import React, {useState, useEffect} from 'react' import React, {useCallback, useEffect, useMemo, useState} from 'react'
import { import {
ActivityIndicator, ActivityIndicator,
Linking, Linking,
@ -205,11 +205,47 @@ const PostLoaded = observer(
) )
}, [item, setDeleted, store]) }, [item, setDeleted, store])
const accessibilityActions = useMemo(
() => [
{
name: 'reply',
label: 'Reply',
},
{
name: 'repost',
label: item.post.viewer?.repost ? 'Undo repost' : 'Repost',
},
{name: 'like', label: item.post.viewer?.like ? 'Unlike' : 'Like'},
],
[item.post.viewer?.like, item.post.viewer?.repost],
)
const onAccessibilityAction = useCallback(
event => {
switch (event.nativeEvent.actionName) {
case 'like':
onPressToggleLike()
break
case 'reply':
onPressReply()
break
case 'repost':
onPressToggleRepost()
break
default:
break
}
},
[onPressReply, onPressToggleLike, onPressToggleRepost],
)
return ( return (
<PostHider <PostHider
href={itemHref} href={itemHref}
style={[styles.outer, pal.view, pal.border, style]} style={[styles.outer, pal.view, pal.border, style]}
moderation={item.moderation.list}> moderation={item.moderation.list}
accessibilityActions={accessibilityActions}
onAccessibilityAction={onAccessibilityAction}>
{showReplyLine && <View style={styles.replyLine} />} {showReplyLine && <View style={styles.replyLine} />}
<View style={styles.layout}> <View style={styles.layout}>
<View style={styles.layoutAvi}> <View style={styles.layoutAvi}>

View File

@ -1,4 +1,4 @@
import React, {useMemo, useState} from 'react' import React, {useCallback, useMemo, useState} from 'react'
import {observer} from 'mobx-react-lite' import {observer} from 'mobx-react-lite'
import {Linking, StyleSheet, View} from 'react-native' import {Linking, StyleSheet, View} from 'react-native'
import Clipboard from '@react-native-clipboard/clipboard' import Clipboard from '@react-native-clipboard/clipboard'
@ -158,12 +158,48 @@ export const FeedItem = observer(function ({
moderation = {behavior: ModerationBehaviorCode.Show} moderation = {behavior: ModerationBehaviorCode.Show}
} }
const accessibilityActions = useMemo(
() => [
{
name: 'reply',
label: 'Reply',
},
{
name: 'repost',
label: item.post.viewer?.repost ? 'Undo repost' : 'Repost',
},
{name: 'like', label: item.post.viewer?.like ? 'Unlike' : 'Like'},
],
[item.post.viewer?.like, item.post.viewer?.repost],
)
const onAccessibilityAction = useCallback(
event => {
switch (event.nativeEvent.actionName) {
case 'like':
onPressToggleLike()
break
case 'reply':
onPressReply()
break
case 'repost':
onPressToggleRepost()
break
default:
break
}
},
[onPressReply, onPressToggleLike, onPressToggleRepost],
)
return ( return (
<PostHider <PostHider
testID={`feedItem-by-${item.post.author.handle}`} testID={`feedItem-by-${item.post.author.handle}`}
style={outerStyles} style={outerStyles}
href={itemHref} href={itemHref}
moderation={moderation}> moderation={moderation}
accessibilityActions={accessibilityActions}
onAccessibilityAction={onAccessibilityAction}>
{isThreadChild && ( {isThreadChild && (
<View <View
style={[styles.topReplyLine, {borderColor: pal.colors.replyLine}]} style={[styles.topReplyLine, {borderColor: pal.colors.replyLine}]}

View File

@ -1,11 +1,5 @@
import React from 'react' import React, {ComponentProps} from 'react'
import { import {StyleSheet, TouchableOpacity, View} from 'react-native'
StyleProp,
StyleSheet,
TouchableOpacity,
View,
ViewStyle,
} from 'react-native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {usePalette} from 'lib/hooks/usePalette' import {usePalette} from 'lib/hooks/usePalette'
import {Link} from '../Link' import {Link} from '../Link'
@ -13,18 +7,21 @@ import {Text} from '../text/Text'
import {addStyle} from 'lib/styles' import {addStyle} from 'lib/styles'
import {ModerationBehaviorCode, ModerationBehavior} from 'lib/labeling/types' import {ModerationBehaviorCode, ModerationBehavior} from 'lib/labeling/types'
interface Props extends ComponentProps<typeof Link> {
// testID?: string
// href?: string
// style: StyleProp<ViewStyle>
moderation: ModerationBehavior
}
export function PostHider({ export function PostHider({
testID, testID,
href, href,
moderation, moderation,
style, style,
children, children,
}: React.PropsWithChildren<{ ...props
testID?: string }: Props) {
href?: string
moderation: ModerationBehavior
style: StyleProp<ViewStyle>
}>) {
const pal = usePalette('default') const pal = usePalette('default')
const [override, setOverride] = React.useState(false) const [override, setOverride] = React.useState(false)
const bg = override ? pal.viewLight : pal.view const bg = override ? pal.viewLight : pal.view
@ -70,7 +67,14 @@ export function PostHider({
// NOTE: any further label enforcement should occur in ContentContainer // NOTE: any further label enforcement should occur in ContentContainer
return ( return (
<Link testID={testID} style={style} href={href} noFeedback> <Link
testID={testID}
style={style}
href={href}
noFeedback
accessible={true}
accessibilityRole="none"
{...props}>
{children} {children}
</Link> </Link>
) )

View File

@ -191,9 +191,7 @@ export function PostCtrls(opts: PostCtrlsOpts) {
onPress={onPressToggleLikeWrapper} onPress={onPressToggleLikeWrapper}
accessibilityRole="button" accessibilityRole="button"
accessibilityLabel={opts.isLiked ? 'Unlike' : 'Like'} accessibilityLabel={opts.isLiked ? 'Unlike' : 'Like'}
accessibilityHint={ accessibilityHint="">
opts.isReposted ? 'Removes like from the post' : 'Like the post'
}>
{opts.isLiked ? ( {opts.isLiked ? (
<HeartIconSolid <HeartIconSolid
style={styles.ctrlIconLiked} style={styles.ctrlIconLiked}

View File

@ -50,11 +50,7 @@ export const RepostButton = ({
style={styles.control} style={styles.control}
accessibilityRole="button" accessibilityRole="button"
accessibilityLabel={isReposted ? 'Undo repost' : 'Repost'} accessibilityLabel={isReposted ? 'Undo repost' : 'Repost'}
accessibilityHint={ accessibilityHint="">
isReposted
? `Remove your repost of the post`
: `Repost or quote post the post`
}>
<RepostIcon <RepostIcon
style={ style={
isReposted isReposted