Create moderation inbox reports list (#11570)
This commit is contained in:
@@ -0,0 +1,66 @@
|
|||||||
|
import {Pressable} from 'react-native'
|
||||||
|
import {useLingui} from '@lingui/react/macro'
|
||||||
|
|
||||||
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
|
import {ChevronBottom_Stroke2_Corner0_Rounded as ChevronDownIcon} from '#/components/icons/Chevron'
|
||||||
|
import * as Menu from '#/components/Menu'
|
||||||
|
import {Text} from '#/components/Typography'
|
||||||
|
|
||||||
|
type ReportFilter = 'all' | 'pending' | 'resolved' | 'unread'
|
||||||
|
|
||||||
|
export function FilterMenu({
|
||||||
|
filter,
|
||||||
|
setFilter,
|
||||||
|
}: {
|
||||||
|
filter: ReportFilter
|
||||||
|
setFilter: React.Dispatch<React.SetStateAction<ReportFilter>>
|
||||||
|
}) {
|
||||||
|
const t = useTheme()
|
||||||
|
const {t: l} = useLingui()
|
||||||
|
|
||||||
|
const labels: Record<ReportFilter, string> = {
|
||||||
|
all: l({context: 'moderation-report-filter', message: 'All'}),
|
||||||
|
pending: l({
|
||||||
|
context: 'moderation-report-filter',
|
||||||
|
message: 'Under review',
|
||||||
|
}),
|
||||||
|
resolved: l({context: 'moderation-report-filter', message: 'Resolved'}),
|
||||||
|
unread: l({context: 'moderation-report-filter', message: 'Unread'}),
|
||||||
|
}
|
||||||
|
const options: {value: ReportFilter; label: string}[] = [
|
||||||
|
{value: 'all', label: labels.all},
|
||||||
|
{value: 'pending', label: labels.pending},
|
||||||
|
{value: 'resolved', label: labels.resolved},
|
||||||
|
{value: 'unread', label: labels.unread},
|
||||||
|
]
|
||||||
|
const currentLabel = labels[filter]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Menu.Root>
|
||||||
|
<Menu.Trigger
|
||||||
|
label={l`Filter moderation reports (currently: ${currentLabel})`}>
|
||||||
|
{({props}) => (
|
||||||
|
<Pressable
|
||||||
|
{...props}
|
||||||
|
style={[a.flex_row, a.align_center, a.justify_center, a.gap_xs]}>
|
||||||
|
<Text style={[a.text_md, a.font_medium]}>{currentLabel}</Text>
|
||||||
|
<ChevronDownIcon size="xs" style={[t.atoms.text]} />
|
||||||
|
</Pressable>
|
||||||
|
)}
|
||||||
|
</Menu.Trigger>
|
||||||
|
<Menu.Outer>
|
||||||
|
<Menu.Group>
|
||||||
|
{options.map(option => (
|
||||||
|
<Menu.Item
|
||||||
|
key={option.value}
|
||||||
|
label={option.label}
|
||||||
|
onPress={() => setFilter(option.value)}>
|
||||||
|
<Menu.ItemText>{option.label}</Menu.ItemText>
|
||||||
|
<Menu.ItemRadio selected={filter === option.value} />
|
||||||
|
</Menu.Item>
|
||||||
|
))}
|
||||||
|
</Menu.Group>
|
||||||
|
</Menu.Outer>
|
||||||
|
</Menu.Root>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import {View} from 'react-native'
|
||||||
|
import {useLingui} from '@lingui/react/macro'
|
||||||
|
import {isBefore, subYears} from 'date-fns'
|
||||||
|
|
||||||
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
|
import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRightIcon} from '#/components/icons/Chevron'
|
||||||
|
import {Text} from '#/components/Typography'
|
||||||
|
|
||||||
|
export function ReportRow({
|
||||||
|
subject,
|
||||||
|
action,
|
||||||
|
date,
|
||||||
|
unread,
|
||||||
|
}: {
|
||||||
|
subject: string
|
||||||
|
action: string
|
||||||
|
date: Date
|
||||||
|
unread?: boolean
|
||||||
|
}) {
|
||||||
|
const t = useTheme()
|
||||||
|
const {i18n} = useLingui()
|
||||||
|
|
||||||
|
const shouldShowYear = isBefore(date, subYears(new Date(), 1))
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_row,
|
||||||
|
a.w_full,
|
||||||
|
a.align_center,
|
||||||
|
a.justify_between,
|
||||||
|
a.gap_sm,
|
||||||
|
a.p_lg,
|
||||||
|
{
|
||||||
|
backgroundColor: unread ? t.palette.primary_25 : undefined,
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
<View style={[a.flex_1, a.gap_2xs, {minWidth: 0}]}>
|
||||||
|
<Text
|
||||||
|
emoji
|
||||||
|
style={[a.text_md, unread ? a.font_semi_bold : a.font_medium]}>
|
||||||
|
{subject}
|
||||||
|
</Text>
|
||||||
|
<Text style={[a.text_sm, t.atoms.text_contrast_high]}>{action}</Text>
|
||||||
|
<Text style={[a.text_sm, t.atoms.text_contrast_medium]}>
|
||||||
|
{i18n.date(date, {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: shouldShowYear ? 'numeric' : undefined,
|
||||||
|
})}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_row,
|
||||||
|
a.flex_shrink_0,
|
||||||
|
a.align_center,
|
||||||
|
a.justify_center,
|
||||||
|
a.gap_sm,
|
||||||
|
]}>
|
||||||
|
{unread ? (
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.rounded_full,
|
||||||
|
{height: 8, width: 8, backgroundColor: t.palette.primary_500},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
) : undefined}
|
||||||
|
<ChevronRightIcon size="md" style={[t.atoms.text_contrast_medium]} />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,11 +1,24 @@
|
|||||||
import {Trans} from '@lingui/react/macro'
|
import {useState} from 'react'
|
||||||
|
import {View} from 'react-native'
|
||||||
|
import {plural} from '@lingui/core/macro'
|
||||||
|
import {Trans, useLingui} from '@lingui/react/macro'
|
||||||
|
|
||||||
|
import {Pager} from '#/view/com/pager/Pager'
|
||||||
|
import {TabBar} from '#/view/com/pager/TabBar'
|
||||||
import {NotFoundScreen} from '#/view/screens/NotFound'
|
import {NotFoundScreen} from '#/view/screens/NotFound'
|
||||||
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
import * as Layout from '#/components/Layout'
|
import * as Layout from '#/components/Layout'
|
||||||
|
import {createStaticClick, SimpleInlineLinkText} from '#/components/Link'
|
||||||
import {useAnalytics} from '#/analytics'
|
import {useAnalytics} from '#/analytics'
|
||||||
|
import {FilterMenu} from './components/FilterMenu'
|
||||||
|
import {ReportRow} from './components/ReportRow'
|
||||||
|
|
||||||
|
type ReportFilter = 'all' | 'pending' | 'resolved' | 'unread'
|
||||||
|
|
||||||
export function ModerationInboxScreen() {
|
export function ModerationInboxScreen() {
|
||||||
|
const {t: l} = useLingui()
|
||||||
const ax = useAnalytics()
|
const ax = useAnalytics()
|
||||||
|
|
||||||
const isEnabled = ax.features.enabled(ax.features.ModerationInboxEnable)
|
const isEnabled = ax.features.enabled(ax.features.ModerationInboxEnable)
|
||||||
|
|
||||||
if (!isEnabled) {
|
if (!isEnabled) {
|
||||||
@@ -14,15 +27,175 @@ export function ModerationInboxScreen() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout.Screen testID="moderationInboxScreen">
|
<Layout.Screen testID="moderationInboxScreen">
|
||||||
<Layout.Header.Outer>
|
<Pager
|
||||||
<Layout.Header.BackButton />
|
testID="moderationInboxPager"
|
||||||
<Layout.Header.Content>
|
renderTabBar={props => (
|
||||||
<Layout.Header.TitleText>
|
<Layout.Center>
|
||||||
<Trans>Moderation inbox</Trans>
|
<Layout.Header.Outer noBottomBorder>
|
||||||
</Layout.Header.TitleText>
|
<Layout.Header.BackButton />
|
||||||
</Layout.Header.Content>
|
<Layout.Header.Content align="left">
|
||||||
<Layout.Header.Slot />
|
<Layout.Header.TitleText>
|
||||||
</Layout.Header.Outer>
|
<Trans>Moderation inbox</Trans>
|
||||||
|
</Layout.Header.TitleText>
|
||||||
|
</Layout.Header.Content>
|
||||||
|
<Layout.Header.Slot />
|
||||||
|
</Layout.Header.Outer>
|
||||||
|
<TabBar
|
||||||
|
testID="moderationInboxTabs"
|
||||||
|
items={[l`Your reports`, l`Your account`]}
|
||||||
|
align="left"
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</Layout.Center>
|
||||||
|
)}>
|
||||||
|
<YourReports />
|
||||||
|
<YourAccount />
|
||||||
|
</Pager>
|
||||||
</Layout.Screen>
|
</Layout.Screen>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function YourReports() {
|
||||||
|
const t = useTheme()
|
||||||
|
const {t: l} = useLingui()
|
||||||
|
|
||||||
|
const [filter, setFilter] = useState<ReportFilter>('all')
|
||||||
|
|
||||||
|
const hasUnread = true // TODO This is hard-coded atm. -dsb
|
||||||
|
|
||||||
|
// TODO Placeholders. - dsb
|
||||||
|
const account = '@deleteme01.bsky.social'
|
||||||
|
const list = 'The Worst Posters'
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout.Center>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_row,
|
||||||
|
a.align_center,
|
||||||
|
a.justify_between,
|
||||||
|
a.gap_lg,
|
||||||
|
a.px_lg,
|
||||||
|
a.py_sm,
|
||||||
|
a.border_b,
|
||||||
|
t.atoms.border_contrast_low,
|
||||||
|
{minHeight: 48},
|
||||||
|
]}>
|
||||||
|
<FilterMenu filter={filter} setFilter={setFilter} />
|
||||||
|
{hasUnread ? (
|
||||||
|
<SimpleInlineLinkText
|
||||||
|
label={l`Mark all reports as read`}
|
||||||
|
style={[a.text_md, t.atoms.text]}
|
||||||
|
{...createStaticClick(() => {
|
||||||
|
// TODO Handle this action. -dsb
|
||||||
|
})}>
|
||||||
|
<Trans>Mark all as read</Trans>
|
||||||
|
</SimpleInlineLinkText>
|
||||||
|
) : undefined}
|
||||||
|
</View>
|
||||||
|
<ReportRow
|
||||||
|
subject={l({
|
||||||
|
context: 'moderation-report-subject',
|
||||||
|
message: `Post by ${account}`,
|
||||||
|
})}
|
||||||
|
action={l`Awaiting review`}
|
||||||
|
date={new Date()}
|
||||||
|
unread
|
||||||
|
/>
|
||||||
|
<ReportRow
|
||||||
|
subject={l({
|
||||||
|
context: 'moderation-report-subject',
|
||||||
|
message: `List “${list}”`,
|
||||||
|
})}
|
||||||
|
action={l`No action taken`}
|
||||||
|
date={new Date()}
|
||||||
|
unread
|
||||||
|
/>
|
||||||
|
<ReportRow
|
||||||
|
subject={l({
|
||||||
|
context: 'moderation-report-subject',
|
||||||
|
message: `Direct message from ${account}`,
|
||||||
|
})}
|
||||||
|
action={l`Message deleted`}
|
||||||
|
date={new Date()}
|
||||||
|
/>
|
||||||
|
</Layout.Center>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function YourAccount() {
|
||||||
|
const t = useTheme()
|
||||||
|
const {t: l} = useLingui()
|
||||||
|
|
||||||
|
const [filter, setFilter] = useState<ReportFilter>('all')
|
||||||
|
|
||||||
|
const hasUnread = true // TODO This is hard-coded atm. -dsb
|
||||||
|
|
||||||
|
// TODO Placeholders. - dsb
|
||||||
|
const guideline = l({
|
||||||
|
context: 'moderation-report-guideline',
|
||||||
|
message: 'Harassment',
|
||||||
|
})
|
||||||
|
const label = l({
|
||||||
|
context: 'moderation-report-label',
|
||||||
|
message: 'Graphic media',
|
||||||
|
})
|
||||||
|
const duration = plural(72, {
|
||||||
|
one: '# hour',
|
||||||
|
other: '# hours',
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout.Center>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.flex_row,
|
||||||
|
a.align_center,
|
||||||
|
a.justify_between,
|
||||||
|
a.gap_lg,
|
||||||
|
a.px_lg,
|
||||||
|
a.py_sm,
|
||||||
|
a.border_b,
|
||||||
|
t.atoms.border_contrast_low,
|
||||||
|
{minHeight: 48},
|
||||||
|
]}>
|
||||||
|
<FilterMenu filter={filter} setFilter={setFilter} />
|
||||||
|
{hasUnread ? (
|
||||||
|
<SimpleInlineLinkText
|
||||||
|
label={l`Mark all actions as read`}
|
||||||
|
style={[a.text_md, t.atoms.text]}
|
||||||
|
{...createStaticClick(() => {
|
||||||
|
// TODO Handle this action. -dsb
|
||||||
|
})}>
|
||||||
|
<Trans>Mark all as read</Trans>
|
||||||
|
</SimpleInlineLinkText>
|
||||||
|
) : undefined}
|
||||||
|
</View>
|
||||||
|
<ReportRow
|
||||||
|
subject={l({
|
||||||
|
context: 'moderation-report-action',
|
||||||
|
message: `Your post was removed`,
|
||||||
|
})}
|
||||||
|
action={l`Violates community guideline: ${guideline}`}
|
||||||
|
date={new Date()}
|
||||||
|
unread
|
||||||
|
/>
|
||||||
|
<ReportRow
|
||||||
|
subject={l({
|
||||||
|
context: 'moderation-report-action',
|
||||||
|
message: `A label was added to your post`,
|
||||||
|
})}
|
||||||
|
action={l`“${label}” – shown behind a warning`}
|
||||||
|
date={new Date()}
|
||||||
|
/>
|
||||||
|
<ReportRow
|
||||||
|
subject={l({
|
||||||
|
context: 'moderation-report-action',
|
||||||
|
message: `A label was added to your post`,
|
||||||
|
})}
|
||||||
|
action={l`Ban evasion – ${duration}, now expired`}
|
||||||
|
date={new Date()}
|
||||||
|
/>
|
||||||
|
</Layout.Center>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export interface TabBarProps {
|
|||||||
testID?: string
|
testID?: string
|
||||||
selectedPage: number
|
selectedPage: number
|
||||||
items: string[]
|
items: string[]
|
||||||
|
align?: 'center' | 'left'
|
||||||
onSelect?: (index: number) => void
|
onSelect?: (index: number) => void
|
||||||
onPressSelected?: (index: number) => void
|
onPressSelected?: (index: number) => void
|
||||||
dragProgress: SharedValue<number>
|
dragProgress: SharedValue<number>
|
||||||
@@ -42,6 +43,7 @@ export function TabBar({
|
|||||||
testID,
|
testID,
|
||||||
selectedPage,
|
selectedPage,
|
||||||
items,
|
items,
|
||||||
|
align = 'center',
|
||||||
onSelect,
|
onSelect,
|
||||||
onPressSelected,
|
onPressSelected,
|
||||||
dragProgress,
|
dragProgress,
|
||||||
@@ -77,10 +79,13 @@ export function TabBar({
|
|||||||
'worklet'
|
'worklet'
|
||||||
const layout = layouts.get()[index]
|
const layout = layouts.get()[index]
|
||||||
const availableSize = containerSize.get() - 2 * CONTENT_PADDING
|
const availableSize = containerSize.get() - 2 * CONTENT_PADDING
|
||||||
|
const maxOffset = Math.max(0, contentSize.get() - availableSize)
|
||||||
if (!layout) {
|
if (!layout) {
|
||||||
// Should not happen, but fall back to equal sizes.
|
// Should not happen, but fall back to equal sizes.
|
||||||
const offsetPerPage = contentSize.get() - availableSize
|
return itemsLength > 1 ? (index / (itemsLength - 1)) * maxOffset : 0
|
||||||
return (index / (itemsLength - 1)) * offsetPerPage
|
}
|
||||||
|
if (maxOffset === 0) {
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
const freeSpace = availableSize - layout.width
|
const freeSpace = availableSize - layout.width
|
||||||
const accumulatingOffset = interpolate(
|
const accumulatingOffset = interpolate(
|
||||||
@@ -91,7 +96,7 @@ export function TabBar({
|
|||||||
[0, freeSpace],
|
[0, freeSpace],
|
||||||
'clamp',
|
'clamp',
|
||||||
)
|
)
|
||||||
return layout.x - accumulatingOffset
|
return Math.min(Math.max(layout.x - accumulatingOffset, 0), maxOffset)
|
||||||
},
|
},
|
||||||
[itemsLength, contentSize, containerSize, layouts],
|
[itemsLength, contentSize, containerSize, layouts],
|
||||||
)
|
)
|
||||||
@@ -338,7 +343,7 @@ export function TabBar({
|
|||||||
onLayout={e => {
|
onLayout={e => {
|
||||||
contentSize.set(e.nativeEvent.layout.width)
|
contentSize.set(e.nativeEvent.layout.width)
|
||||||
}}
|
}}
|
||||||
style={{flexDirection: 'row', flexGrow: 1}}>
|
style={[styles.items, align === 'center' && styles.itemsCentered]}>
|
||||||
{items.map((item, i) => {
|
{items.map((item, i) => {
|
||||||
return (
|
return (
|
||||||
<TabBarItem
|
<TabBarItem
|
||||||
@@ -350,6 +355,7 @@ export function TabBar({
|
|||||||
onPressItem={onPressItem}
|
onPressItem={onPressItem}
|
||||||
onItemLayout={onItemLayout}
|
onItemLayout={onItemLayout}
|
||||||
onTextLayout={onTextLayout}
|
onTextLayout={onTextLayout}
|
||||||
|
align={align}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
@@ -382,6 +388,7 @@ function TabBarItem({
|
|||||||
onPressItem,
|
onPressItem,
|
||||||
onItemLayout,
|
onItemLayout,
|
||||||
onTextLayout,
|
onTextLayout,
|
||||||
|
align,
|
||||||
}: {
|
}: {
|
||||||
index: number
|
index: number
|
||||||
testID: string | undefined
|
testID: string | undefined
|
||||||
@@ -390,6 +397,7 @@ function TabBarItem({
|
|||||||
onPressItem: (index: number) => void
|
onPressItem: (index: number) => void
|
||||||
onItemLayout: (index: number, layout: {x: number; width: number}) => void
|
onItemLayout: (index: number, layout: {x: number; width: number}) => void
|
||||||
onTextLayout: (index: number, layout: {width: number}) => void
|
onTextLayout: (index: number, layout: {width: number}) => void
|
||||||
|
align: 'center' | 'left'
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const style = useAnimatedStyle(() => {
|
const style = useAnimatedStyle(() => {
|
||||||
@@ -421,10 +429,15 @@ function TabBarItem({
|
|||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View onLayout={handleLayout} style={{flexGrow: 1}}>
|
<View
|
||||||
|
onLayout={handleLayout}
|
||||||
|
style={[
|
||||||
|
styles.itemContainer,
|
||||||
|
align === 'center' && styles.itemContainerCentered,
|
||||||
|
]}>
|
||||||
<PressableWithHover
|
<PressableWithHover
|
||||||
testID={`${testID}-selector-${index}`}
|
testID={`${testID}-selector-${index}`}
|
||||||
style={styles.item}
|
style={[styles.item, align === 'center' && styles.itemCentered]}
|
||||||
hoverStyle={t.atoms.bg_contrast_25}
|
hoverStyle={t.atoms.bg_contrast_25}
|
||||||
onPress={() => onPressItem(index)}
|
onPress={() => onPressItem(index)}
|
||||||
accessibilityRole="tab">
|
accessibilityRole="tab">
|
||||||
@@ -448,12 +461,26 @@ const styles = StyleSheet.create({
|
|||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
paddingHorizontal: CONTENT_PADDING,
|
paddingHorizontal: CONTENT_PADDING,
|
||||||
},
|
},
|
||||||
item: {
|
items: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
},
|
||||||
|
itemsCentered: {
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
|
},
|
||||||
|
itemContainer: {
|
||||||
|
flexShrink: 0,
|
||||||
|
},
|
||||||
|
itemContainerCentered: {
|
||||||
|
flexGrow: 1,
|
||||||
|
},
|
||||||
|
item: {
|
||||||
paddingTop: 10,
|
paddingTop: 10,
|
||||||
paddingHorizontal: ITEM_PADDING,
|
paddingHorizontal: ITEM_PADDING,
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
},
|
},
|
||||||
|
itemCentered: {
|
||||||
|
flexGrow: 1,
|
||||||
|
},
|
||||||
itemInner: {
|
itemInner: {
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export interface TabBarProps {
|
|||||||
testID?: string
|
testID?: string
|
||||||
selectedPage: number
|
selectedPage: number
|
||||||
items: string[]
|
items: string[]
|
||||||
|
align?: 'center' | 'left'
|
||||||
indicatorColor?: string
|
indicatorColor?: string
|
||||||
backgroundColor?: string
|
backgroundColor?: string
|
||||||
|
|
||||||
@@ -34,6 +35,7 @@ export function TabBar({
|
|||||||
testID,
|
testID,
|
||||||
selectedPage,
|
selectedPage,
|
||||||
items,
|
items,
|
||||||
|
align = 'center',
|
||||||
onSelect,
|
onSelect,
|
||||||
onPressSelected,
|
onPressSelected,
|
||||||
}: TabBarProps) {
|
}: TabBarProps) {
|
||||||
@@ -87,7 +89,7 @@ export function TabBar({
|
|||||||
behavior: 'smooth',
|
behavior: 'smooth',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, [scrollElRef, selectedPage, styles])
|
}, [scrollElRef, selectedPage, styles, align])
|
||||||
|
|
||||||
const onPressItem = useCallback(
|
const onPressItem = useCallback(
|
||||||
(index: number) => {
|
(index: number) => {
|
||||||
@@ -119,7 +121,7 @@ export function TabBar({
|
|||||||
ref={node => {
|
ref={node => {
|
||||||
itemRefs.current[i] = node as any
|
itemRefs.current[i] = node as any
|
||||||
}}
|
}}
|
||||||
style={styles.item}
|
style={[styles.item, align === 'left' && leftAlignedStyles.item]}
|
||||||
hoverStyle={t.atoms.bg_contrast_25}
|
hoverStyle={t.atoms.bg_contrast_25}
|
||||||
onPress={() => onPressItem(i)}
|
onPress={() => onPressItem(i)}
|
||||||
accessibilityRole="tab">
|
accessibilityRole="tab">
|
||||||
@@ -239,3 +241,10 @@ const mobileStyles = StyleSheet.create({
|
|||||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const leftAlignedStyles = StyleSheet.create({
|
||||||
|
item: {
|
||||||
|
flexGrow: 0,
|
||||||
|
flexShrink: 0,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user