Merge branch '3p-moderators' of github.com:bluesky-social/social-app into 3p-moderators
This commit is contained in:
+29
-18
@@ -48,25 +48,30 @@ export type VariantProps = {
|
||||
shape?: ButtonShape
|
||||
}
|
||||
|
||||
export type ButtonProps = React.PropsWithChildren<
|
||||
Pick<PressableProps, 'disabled' | 'onPress' | 'testID'> &
|
||||
AccessibilityProps &
|
||||
VariantProps & {
|
||||
testID?: string
|
||||
label: string
|
||||
style?: StyleProp<ViewStyle>
|
||||
}
|
||||
>
|
||||
export type ButtonState = {
|
||||
hovered: boolean
|
||||
focused: boolean
|
||||
pressed: boolean
|
||||
disabled: boolean
|
||||
}
|
||||
|
||||
export type ButtonProps = Pick<
|
||||
PressableProps,
|
||||
'disabled' | 'onPress' | 'testID'
|
||||
> &
|
||||
AccessibilityProps &
|
||||
VariantProps & {
|
||||
testID?: string
|
||||
label: string
|
||||
style?: StyleProp<ViewStyle>
|
||||
children:
|
||||
| React.ReactNode
|
||||
| string
|
||||
| ((state: VariantProps & ButtonState) => React.ReactNode | string)
|
||||
}
|
||||
export type ButtonTextProps = TextProps & VariantProps & {disabled?: boolean}
|
||||
|
||||
const Context = React.createContext<
|
||||
VariantProps & {
|
||||
hovered: boolean
|
||||
focused: boolean
|
||||
pressed: boolean
|
||||
disabled: boolean
|
||||
}
|
||||
>({
|
||||
const Context = React.createContext<VariantProps & ButtonState>({
|
||||
hovered: false,
|
||||
focused: false,
|
||||
pressed: false,
|
||||
@@ -402,6 +407,8 @@ export function Button({
|
||||
<Context.Provider value={context}>
|
||||
{typeof children === 'string' ? (
|
||||
<ButtonText>{children}</ButtonText>
|
||||
) : typeof children === 'function' ? (
|
||||
children(context)
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
@@ -524,9 +531,11 @@ export function ButtonText({children, style, ...rest}: ButtonTextProps) {
|
||||
export function ButtonIcon({
|
||||
icon: Comp,
|
||||
position,
|
||||
size: iconSize,
|
||||
}: {
|
||||
icon: React.ComponentType<SVGIconProps>
|
||||
position?: 'left' | 'right'
|
||||
size?: SVGIconProps['size']
|
||||
}) {
|
||||
const {size, disabled} = useButtonContext()
|
||||
const textStyles = useSharedButtonTextStyles()
|
||||
@@ -542,7 +551,9 @@ export function ButtonIcon({
|
||||
},
|
||||
]}>
|
||||
<Comp
|
||||
size={size === 'large' ? 'md' : size === 'tiny' ? 'xs' : 'sm'}
|
||||
size={
|
||||
iconSize ?? (size === 'large' ? 'md' : size === 'tiny' ? 'xs' : 'sm')
|
||||
}
|
||||
style={[{color: textStyles.color, pointerEvents: 'none'}]}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -10,6 +10,8 @@ import {Portal} from '#/components/Portal'
|
||||
|
||||
import {DialogOuterProps, DialogInnerProps} from '#/components/Dialog/types'
|
||||
import {Context} from '#/components/Dialog/context'
|
||||
import {Button, ButtonIcon} from '#/components/Button'
|
||||
import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times'
|
||||
|
||||
export {useDialogControl, useDialogContext} from '#/components/Dialog/context'
|
||||
export * from '#/components/Dialog/types'
|
||||
@@ -171,25 +173,28 @@ export function Handle() {
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO(eric) unused rn
|
||||
*/
|
||||
// export function Close() {
|
||||
// const {_} = useLingui()
|
||||
// const t = useTheme()
|
||||
// const {close} = useDialogContext()
|
||||
// return (
|
||||
// <View
|
||||
// style={[
|
||||
// a.absolute,
|
||||
// a.z_10,
|
||||
// {
|
||||
// top: a.pt_lg.paddingTop,
|
||||
// right: a.pr_lg.paddingRight,
|
||||
// },
|
||||
// ]}>
|
||||
// <Button onPress={close} label={_(msg`Close active dialog`)}>
|
||||
// </Button>
|
||||
// </View>
|
||||
// )
|
||||
// }
|
||||
export function Close() {
|
||||
const {_} = useLingui()
|
||||
const {close} = React.useContext(Context)
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
a.absolute,
|
||||
a.z_10,
|
||||
{
|
||||
top: a.pt_md.paddingTop,
|
||||
right: a.pr_md.paddingRight,
|
||||
},
|
||||
]}>
|
||||
<Button
|
||||
size="small"
|
||||
variant="ghost"
|
||||
color="primary"
|
||||
shape="round"
|
||||
onPress={close}
|
||||
label={_(msg`Close active dialog`)}>
|
||||
<ButtonIcon icon={X} size="md" />
|
||||
</Button>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
import React from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import LinearGradient from 'react-native-linear-gradient'
|
||||
import {AppBskyModerationDefs} from '@atproto/api'
|
||||
|
||||
import {atoms as a, useTheme, tokens, web} from '#/alf'
|
||||
import {Link, useLinkContext} from '#/components/Link'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {RichText} from '#/components/RichText'
|
||||
import {RaisingHande4Finger_Stroke2_Corner0_Rounded as RaisingHand} from '#/components/icons/RaisingHand'
|
||||
import {useModServiceInfoQuery} from '#/state/queries/modservice'
|
||||
|
||||
type ModerationServiceCardProps = {
|
||||
modservice: AppBskyModerationDefs.ModServiceViewDetailed
|
||||
}
|
||||
|
||||
export function ModerationServiceCard({
|
||||
modservice,
|
||||
}: ModerationServiceCardProps) {
|
||||
const {_} = useLingui()
|
||||
|
||||
return (
|
||||
<Link
|
||||
to={{
|
||||
screen: 'ProfileModservice',
|
||||
params: {
|
||||
name: modservice.creator.handle,
|
||||
},
|
||||
}}
|
||||
label={_(
|
||||
msg`View the moderation service provided by @${modservice.creator.handle}`,
|
||||
)}>
|
||||
<Inner modservice={modservice} />
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
function Inner({modservice}: ModerationServiceCardProps) {
|
||||
const t = useTheme()
|
||||
const {hovered, pressed, focused} = useLinkContext()
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
a.flex_row,
|
||||
a.align_center,
|
||||
a.justify_between,
|
||||
a.gap_sm,
|
||||
a.w_full,
|
||||
a.rounded_sm,
|
||||
a.pt_md,
|
||||
a.pb_sm,
|
||||
a.pl_lg,
|
||||
a.pr_sm,
|
||||
a.overflow_hidden,
|
||||
web({
|
||||
transition: 'transform 0.2s cubic-bezier(.02,.73,.27,.99)',
|
||||
}),
|
||||
{
|
||||
transform: [{scale: pressed || hovered || focused ? 0.992 : 1}],
|
||||
},
|
||||
]}>
|
||||
<LinearGradient
|
||||
colors={tokens.gradients.midnight.values.map(c => c[1])}
|
||||
locations={tokens.gradients.midnight.values.map(c => c[0])}
|
||||
start={{x: 0, y: 0}}
|
||||
end={{x: 1, y: 1}}
|
||||
style={[a.absolute, a.inset_0]}
|
||||
/>
|
||||
|
||||
<View style={[a.z_10]}>
|
||||
<Text
|
||||
style={[a.text_md, a.font_bold, a.pb_2xs, {color: t.palette.white}]}>
|
||||
{/* TODO */}
|
||||
{modservice.displayName || 'Mod service'}
|
||||
</Text>
|
||||
|
||||
{modservice.description ? (
|
||||
<RichText
|
||||
value={modservice.description}
|
||||
style={[{color: t.palette.white}]}
|
||||
/>
|
||||
) : (
|
||||
<Text>
|
||||
<Trans>
|
||||
Moderation service managed by @{modservice.creator.handle}
|
||||
</Trans>
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
|
||||
<RaisingHand size="xl" style={[a.z_10]} fill={t.palette.white} />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export function ModerationServiceCardSkeleton() {
|
||||
return (
|
||||
<View>
|
||||
<Text>Loading</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export function Loader({
|
||||
did,
|
||||
loading: LoadingComponent = ModerationServiceCardSkeleton,
|
||||
error: ErrorComponent,
|
||||
component: Component,
|
||||
}: {
|
||||
did: string
|
||||
loading?: React.ComponentType<{}>
|
||||
error?: React.ComponentType<{error: string}>
|
||||
component: React.ComponentType<{
|
||||
modservice: AppBskyModerationDefs.ModServiceViewDetailed
|
||||
}>
|
||||
}) {
|
||||
const {isLoading, data, error} = useModServiceInfoQuery({did})
|
||||
|
||||
return isLoading ? (
|
||||
LoadingComponent ? (
|
||||
<LoadingComponent />
|
||||
) : null
|
||||
) : error || !data ? (
|
||||
ErrorComponent ? (
|
||||
<ErrorComponent error={error?.message || 'Unknown error'} />
|
||||
) : null
|
||||
) : (
|
||||
<Component modservice={data} />
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import React from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {Trans} from '@lingui/macro'
|
||||
|
||||
import {atoms as a, useTheme, ViewStyleProp, flatten} from '#/alf'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {RichText} from '#/components/RichText'
|
||||
import {RaisingHande4Finger_Stroke2_Corner0_Rounded as RaisingHand} from '#/components/icons/RaisingHand'
|
||||
import {UserAvatar} from '#/view/com/util/UserAvatar'
|
||||
|
||||
export function Outer({
|
||||
children,
|
||||
style,
|
||||
}: React.PropsWithChildren<ViewStyleProp>) {
|
||||
const t = useTheme()
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
a.flex_row,
|
||||
a.align_center,
|
||||
a.gap_sm,
|
||||
a.w_full,
|
||||
a.rounded_sm,
|
||||
a.pt_md,
|
||||
a.pb_md,
|
||||
a.pl_md,
|
||||
a.pr_md,
|
||||
a.overflow_hidden,
|
||||
a.border,
|
||||
t.atoms.border_contrast_low,
|
||||
t.atoms.bg_contrast_25,
|
||||
flatten(style),
|
||||
]}>
|
||||
<View style={[a.flex_row, a.align_center, a.w_full, a.gap_md]}>
|
||||
{children}
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export function Avatar({avatar}: {avatar?: string}) {
|
||||
return <UserAvatar type="list" size={40} avatar={avatar} />
|
||||
}
|
||||
|
||||
export function Content({
|
||||
title,
|
||||
description,
|
||||
handle,
|
||||
}: {
|
||||
title: string
|
||||
description?: string
|
||||
handle: string
|
||||
}) {
|
||||
const t = useTheme()
|
||||
|
||||
return (
|
||||
<View style={[a.flex_1, a.flex_row, a.align_center, a.justify_between]}>
|
||||
<View>
|
||||
<Text style={[a.text_md, a.font_bold, a.pb_2xs]}>{title}</Text>
|
||||
|
||||
{description ? (
|
||||
<RichText value={description} style={[]} />
|
||||
) : (
|
||||
<Text>
|
||||
<Trans>Moderation service managed by @{handle}</Trans>
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
|
||||
<RaisingHand size="xl" style={[a.z_10]} fill={t.palette.primary_500} />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import React from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {AppBskyModerationDefs} from '@atproto/api'
|
||||
|
||||
import {Link as InternalLink, LinkProps} from '#/components/Link'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useModServiceInfoQuery} from '#/state/queries/modservice'
|
||||
|
||||
export * as Card from '#/components/ModerationServiceCard/Card'
|
||||
|
||||
type ModerationServiceProps = {
|
||||
modservice: AppBskyModerationDefs.ModServiceViewDetailed
|
||||
}
|
||||
|
||||
export function Link({
|
||||
children,
|
||||
modservice,
|
||||
}: ModerationServiceProps & Pick<LinkProps, 'children'>) {
|
||||
const {_} = useLingui()
|
||||
|
||||
return (
|
||||
<InternalLink
|
||||
to={{
|
||||
screen: 'ProfileModservice',
|
||||
params: {
|
||||
name: modservice.creator.handle,
|
||||
},
|
||||
}}
|
||||
label={_(
|
||||
msg`View the moderation service provided by @${modservice.creator.handle}`,
|
||||
)}>
|
||||
{children}
|
||||
</InternalLink>
|
||||
)
|
||||
}
|
||||
|
||||
export function ModerationServiceCardSkeleton() {
|
||||
return (
|
||||
<View>
|
||||
<Text>Loading</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export function Loader({
|
||||
did,
|
||||
loading: LoadingComponent = ModerationServiceCardSkeleton,
|
||||
error: ErrorComponent,
|
||||
component: Component,
|
||||
}: {
|
||||
did: string
|
||||
loading?: React.ComponentType<{}>
|
||||
error?: React.ComponentType<{error: string}>
|
||||
component: React.ComponentType<{
|
||||
modservice: AppBskyModerationDefs.ModServiceViewDetailed
|
||||
}>
|
||||
}) {
|
||||
const {isLoading, data, error} = useModServiceInfoQuery({did})
|
||||
|
||||
return isLoading ? (
|
||||
LoadingComponent ? (
|
||||
<LoadingComponent />
|
||||
) : null
|
||||
) : error || !data ? (
|
||||
ErrorComponent ? (
|
||||
<ErrorComponent error={error?.message || 'Unknown error'} />
|
||||
) : null
|
||||
) : (
|
||||
<Component modservice={data} />
|
||||
)
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import React from 'react'
|
||||
import {View, Dimensions} from 'react-native'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {LABEL_GROUPS, LabelGroupDefinition} from '@atproto/api'
|
||||
import {AppBskyModerationDefs, LabelGroupDefinition} from '@atproto/api'
|
||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||
|
||||
import {atoms as a, useTheme, tokens, native} from '#/alf'
|
||||
@@ -25,10 +25,16 @@ import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check'
|
||||
import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
|
||||
import * as Toggle from '#/components/forms/Toggle'
|
||||
import {GradientFill} from '#/components/GradientFill'
|
||||
import * as TextField from '#/components/forms/TextField'
|
||||
import {CharProgress} from '#/view/com/composer/char-progress/CharProgress'
|
||||
import {Loader} from '#/components/Loader'
|
||||
import * as Toast from '#/view/com/util/Toast'
|
||||
import {usePreferencesQuery} from '#/state/queries/preferences'
|
||||
import {useModServicesDetailedInfoQuery} from '#/state/queries/modservice'
|
||||
import {
|
||||
getLabelGroupsFromLabels,
|
||||
getModerationServiceTitle,
|
||||
useConfigurableLabelGroups,
|
||||
} from '#/lib/moderation'
|
||||
|
||||
export type ReportDialogProps =
|
||||
| {
|
||||
@@ -41,7 +47,11 @@ export type ReportDialogProps =
|
||||
did: string
|
||||
}
|
||||
|
||||
function LabelGroupButton({labelGroup}: {labelGroup: string}) {
|
||||
function LabelGroupButton({
|
||||
labelGroup,
|
||||
}: {
|
||||
labelGroup: LabelGroupDefinition['id']
|
||||
}) {
|
||||
const t = useTheme()
|
||||
const {hovered, focused, pressed} = useButtonContext()
|
||||
const labelGroupStrings = useLabelGroupStrings()
|
||||
@@ -143,22 +153,89 @@ function ModServiceToggle({title}: {title: string}) {
|
||||
)
|
||||
}
|
||||
|
||||
function SubmitViewLoader({
|
||||
children,
|
||||
}: {
|
||||
children: (props: {
|
||||
labelers: AppBskyModerationDefs.ModServiceViewDetailed[]
|
||||
labelGroupToModServiceMap: Record<LabelGroupDefinition['id'], string[]>
|
||||
}) => React.ReactNode
|
||||
}) {
|
||||
const {
|
||||
isLoading: isPreferencesLoading,
|
||||
error: preferencesError,
|
||||
data: preferences,
|
||||
} = usePreferencesQuery()
|
||||
const {
|
||||
isLoading: isModServicesLoading,
|
||||
data: modservices,
|
||||
error: modservicesError,
|
||||
} = useModServicesDetailedInfoQuery({
|
||||
dids: preferences ? preferences.moderationOpts.mods.map(m => m.did) : [],
|
||||
})
|
||||
const labelGroupToModServiceMap = React.useMemo(() => {
|
||||
if (!modservices) return {}
|
||||
|
||||
const groups: Partial<
|
||||
Record<
|
||||
LabelGroupDefinition['id'],
|
||||
AppBskyModerationDefs.ModServiceViewDetailed[]
|
||||
>
|
||||
> = {}
|
||||
|
||||
for (const modservice of modservices) {
|
||||
const labelGroups = getLabelGroupsFromLabels(
|
||||
modservice.policies.labelValues,
|
||||
)
|
||||
for (const group of labelGroups) {
|
||||
const g = (groups[group.id] = groups[group.id] || [])
|
||||
g.push(modservice)
|
||||
}
|
||||
}
|
||||
|
||||
return groups
|
||||
}, [modservices])
|
||||
|
||||
const isLoading = isPreferencesLoading || isModServicesLoading
|
||||
const error = preferencesError || modservicesError
|
||||
|
||||
return isLoading ? (
|
||||
<View style={[{height: 500}]}>
|
||||
<Loader />
|
||||
</View>
|
||||
) : error || !(preferences && modservices) ? null : ( // TODO
|
||||
children({
|
||||
labelers: modservices,
|
||||
// TODO mismatched types
|
||||
// @ts-ignore
|
||||
labelGroupToModServiceMap,
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
function SubmitView({
|
||||
selectedLabelGroup,
|
||||
goBack,
|
||||
onSubmitComplete,
|
||||
labelGroupToModServiceMap,
|
||||
}: {
|
||||
selectedLabelGroup: string
|
||||
selectedLabelGroup: LabelGroupDefinition['id']
|
||||
goBack: () => void
|
||||
onSubmitComplete: () => void
|
||||
labelers: AppBskyModerationDefs.ModServiceViewDetailed[]
|
||||
labelGroupToModServiceMap: Record<
|
||||
LabelGroupDefinition['id'],
|
||||
AppBskyModerationDefs.ModServiceViewDetailed[]
|
||||
>
|
||||
}) {
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
const labelGroupStrings = useLabelGroupStrings()
|
||||
const groupInfoStrings = labelGroupStrings[selectedLabelGroup]
|
||||
const [selectedServices, setSelectedServices] = React.useState<string[]>([])
|
||||
const [details, setDetails] = React.useState<string>('')
|
||||
const [submitting, setSubmitting] = React.useState<boolean>(false)
|
||||
const [selectedServices, setSelectedServices] = React.useState<string[]>([])
|
||||
const supportedLabelers = labelGroupToModServiceMap[selectedLabelGroup]
|
||||
|
||||
const submit = React.useCallback(async () => {
|
||||
setSubmitting(true)
|
||||
@@ -208,22 +285,35 @@ function SubmitView({
|
||||
Select the moderation service(s) to report to
|
||||
</Text>
|
||||
|
||||
<Toggle.Group
|
||||
label="Select mod services"
|
||||
values={selectedServices}
|
||||
onChange={setSelectedServices}>
|
||||
<View style={[a.flex_row, a.gap_sm, a.flex_wrap]}>
|
||||
<Toggle.Item name="bluesky" label="Bluesky">
|
||||
<ModServiceToggle title="Bluesky" />
|
||||
</Toggle.Item>
|
||||
<Toggle.Item name="contraption" label="The Contraption">
|
||||
<ModServiceToggle title="The Contraption" />
|
||||
</Toggle.Item>
|
||||
<Toggle.Item name="safety" label="Safety Corp">
|
||||
<ModServiceToggle title="Safety Corp" />
|
||||
</Toggle.Item>
|
||||
{supportedLabelers ? (
|
||||
<Toggle.Group
|
||||
label="Select mod services"
|
||||
values={selectedServices}
|
||||
onChange={setSelectedServices}>
|
||||
<View style={[a.flex_row, a.gap_sm, a.flex_wrap]}>
|
||||
{supportedLabelers.map(labeler => {
|
||||
const title = getModerationServiceTitle({
|
||||
displayName: labeler.creator.displayName,
|
||||
handle: labeler.creator.handle,
|
||||
})
|
||||
return (
|
||||
<Toggle.Item
|
||||
key={labeler.creator.did}
|
||||
name={labeler.creator.did}
|
||||
label={title}>
|
||||
<ModServiceToggle title={title} />
|
||||
</Toggle.Item>
|
||||
)
|
||||
})}
|
||||
</View>
|
||||
</Toggle.Group>
|
||||
) : (
|
||||
<View style={[a.p_lg, a.rounded_sm, t.atoms.bg_contrast_25]}>
|
||||
<Text style={[a.italic, t.atoms.text_contrast_medium]}>
|
||||
None of your subscribed labelers support this content type.
|
||||
</Text>
|
||||
</View>
|
||||
</Toggle.Group>
|
||||
)}
|
||||
</View>
|
||||
<View style={[a.gap_md]}>
|
||||
<Text style={[t.atoms.text_contrast_medium]}>
|
||||
@@ -231,13 +321,14 @@ function SubmitView({
|
||||
</Text>
|
||||
|
||||
<View style={[a.relative, a.w_full]}>
|
||||
<TextField.Input
|
||||
<Dialog.Input
|
||||
multiline
|
||||
value={details}
|
||||
onChangeText={setDetails}
|
||||
label="Text field"
|
||||
style={{paddingRight: 60}}
|
||||
numberOfLines={6}
|
||||
disabled={!supportedLabelers}
|
||||
/>
|
||||
|
||||
<View
|
||||
@@ -260,10 +351,11 @@ function SubmitView({
|
||||
<View style={[a.align_end]}>
|
||||
<Button
|
||||
size="large"
|
||||
variant="gradient"
|
||||
color="gradient_midnight"
|
||||
variant="solid"
|
||||
color="primary"
|
||||
label={_(msg`Submit`)}
|
||||
onPress={submit}>
|
||||
onPress={submit}
|
||||
disabled={!supportedLabelers}>
|
||||
<ButtonText>Submit</ButtonText>
|
||||
{submitting && <ButtonIcon icon={Loader} />}
|
||||
</Button>
|
||||
@@ -284,7 +376,9 @@ export function ReportDialog({
|
||||
const {_} = useLingui()
|
||||
const insets = useSafeAreaInsets()
|
||||
const control = Dialog.useDialogControl()
|
||||
const [selectedLabelGroup, setSelectedLabelGroup] = React.useState<string>('')
|
||||
const [selectedLabelGroup, setSelectedLabelGroup] = React.useState<
|
||||
LabelGroupDefinition['id'] | undefined
|
||||
>()
|
||||
const labelGroupStrings = useLabelGroupStrings()
|
||||
|
||||
// REQUIRED CLEANUP
|
||||
@@ -305,11 +399,7 @@ export function ReportDialog({
|
||||
}
|
||||
}, [_, params.type])
|
||||
|
||||
const groups = React.useMemo<
|
||||
[keyof typeof LABEL_GROUPS, LabelGroupDefinition][]
|
||||
>(() => {
|
||||
return Object.entries(LABEL_GROUPS).filter(([, def]) => def.configurable)
|
||||
}, [])
|
||||
const groups = useConfigurableLabelGroups()
|
||||
|
||||
return (
|
||||
<Dialog.Outer
|
||||
@@ -325,11 +415,18 @@ export function ReportDialog({
|
||||
|
||||
<Dialog.ScrollableInner label="Report Dialog">
|
||||
{selectedLabelGroup ? (
|
||||
<SubmitView
|
||||
selectedLabelGroup={selectedLabelGroup}
|
||||
goBack={() => setSelectedLabelGroup('')}
|
||||
onSubmitComplete={control.close}
|
||||
/>
|
||||
<SubmitViewLoader>
|
||||
{props => (
|
||||
// TODO same types mismatch
|
||||
// @ts-ignore
|
||||
<SubmitView
|
||||
{...props}
|
||||
selectedLabelGroup={selectedLabelGroup}
|
||||
goBack={() => setSelectedLabelGroup(undefined)}
|
||||
onSubmitComplete={control.close}
|
||||
/>
|
||||
)}
|
||||
</SubmitViewLoader>
|
||||
) : (
|
||||
<View style={[a.gap_lg]}>
|
||||
<View style={[a.justify_center, a.gap_sm]}>
|
||||
@@ -342,14 +439,14 @@ export function ReportDialog({
|
||||
<Divider />
|
||||
|
||||
<View style={[a.gap_sm, {marginHorizontal: a.p_md.padding * -1}]}>
|
||||
{groups.map(([name, def]) => {
|
||||
const groupStrings = labelGroupStrings[name]
|
||||
{groups.map(def => {
|
||||
const groupStrings = labelGroupStrings[def.id]
|
||||
return (
|
||||
<Button
|
||||
key={def.id}
|
||||
label={_(msg`Create report for ${groupStrings.name}`)}
|
||||
onPress={() => setSelectedLabelGroup(name)}>
|
||||
<LabelGroupButton labelGroup={name} />
|
||||
onPress={() => setSelectedLabelGroup(def.id)}>
|
||||
<LabelGroupButton labelGroup={def.id} />
|
||||
</Button>
|
||||
)
|
||||
})}
|
||||
|
||||
@@ -149,6 +149,7 @@ export type InputProps = Omit<TextInputProps, 'value' | 'onChangeText'> & {
|
||||
value: string
|
||||
onChangeText: (value: string) => void
|
||||
isInvalid?: boolean
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
export function createInput(Component: typeof TextInput) {
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import {createSinglePathSVG} from './TEMPLATE'
|
||||
|
||||
export const TimesLarge_Stroke2_Corner0_Rounded = createSinglePathSVG({
|
||||
path: 'M4.293 4.293a1 1 0 0 1 1.414 0L12 10.586l6.293-6.293a1 1 0 1 1 1.414 1.414L13.414 12l6.293 6.293a1 1 0 0 1-1.414 1.414L12 13.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L10.586 12 4.293 5.707a1 1 0 0 1 0-1.414Z',
|
||||
})
|
||||
Reference in New Issue
Block a user