Update LabelsOnMe dialog

This commit is contained in:
Eric Bailey
2024-02-26 18:23:49 -06:00
parent 304b6f4f00
commit 0dc1cdfdcf
8 changed files with 104 additions and 227 deletions
+94
View File
@@ -0,0 +1,94 @@
import React from 'react'
import {View} from 'react-native'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {ComAtprotoLabelDefs} from '@atproto/api'
import {atoms as a, useBreakpoints} from '#/alf'
import {Text} from '#/components/Typography'
import * as Dialog from '#/components/Dialog'
import {Button} from '#/components/Button'
export {useDialogControl as useLabelsOnMeDialogControl} from '#/components/Dialog'
type Subject =
| {
uri: string
cid: string
}
| {
did: string
}
export interface LabelsOnMeDialogProps {
control: Dialog.DialogOuterProps['control']
subject: Subject
labels: ComAtprotoLabelDefs.Label[]
}
export function LabelsOnMeDialogInner(props: LabelsOnMeDialogProps) {
const {_} = useLingui()
const control = Dialog.useDialogControl()
const {gtMobile} = useBreakpoints()
const {subject, labels} = props
const isAccount = 'did' in subject
// TODO
// const submit = async () => {
// try {
// const $type = !isAccountReport
// ? 'com.atproto.repo.strongRef'
// : 'com.atproto.admin.defs#repoRef'
// await getAgent().createModerationReport({
// reasonType: ComAtprotoModerationDefs.REASONAPPEAL,
// subject: {
// $type,
// ...props,
// },
// reason: details,
// })
// Toast.show(_(msg`We'll look into your appeal promptly.`))
// } finally {
// closeModal()
// }
// }
return (
<Dialog.ScrollableInner
accessibilityDescribedBy="dialog-description"
accessibilityLabelledBy="dialog-title">
<Text nativeID="dialog-title" style={[a.text_2xl, a.font_bold]}>
<Trans>Labels on my {isAccount ? 'account' : 'content'}</Trans>
</Text>
<Text nativeID="dialog-description" style={[a.text_sm]}>
<Trans>
You may appeal these labels if you feel they were placed in error.
</Trans>
</Text>
{labels.map(label => (
<Text key={`${label.src}-${label.val}`}>{label.val} TODO</Text>
))}
<View style={gtMobile && [a.flex_row, a.justify_end]}>
<Button
testID="doneBtn"
variant="outline"
color="primary"
size="small"
onPress={() => control.close()}
label={_(msg`Done`)}>
{_(msg`Done`)}
</Button>
</View>
</Dialog.ScrollableInner>
)
}
export function LabelsOnMeDialog(props: LabelsOnMeDialogProps) {
return (
<Dialog.Outer control={props.control}>
<Dialog.Handle />
<LabelsOnMeDialogInner {...props} />
</Dialog.Outer>
)
}
@@ -1,92 +0,0 @@
import React from 'react'
import {View} from 'react-native'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {ComAtprotoLabelDefs} from '@atproto/api'
import {atoms as a, useBreakpoints} from '#/alf'
import {Text} from '#/components/Typography'
import * as Dialog from '#/components/Dialog'
import {GlobalDialogProps} from '#/components/dialogs'
import {Button} from '#/components/Button'
type Subject =
| {
uri: string
cid: string
}
| {
did: string
}
export interface LabelsOnMeDialogProps {
subject: Subject
labels: ComAtprotoLabelDefs.Label[]
}
export function LabelsOnMeDialog({
params,
cleanup,
}: GlobalDialogProps<LabelsOnMeDialogProps>) {
const {_} = useLingui()
const control = Dialog.useDialogControl()
const {gtMobile} = useBreakpoints()
const {subject, labels} = params
const isAccount = 'did' in subject
// REQUIRED CLEANUP
const onClose = React.useCallback(() => cleanup(), [cleanup])
// TODO
// const submit = async () => {
// try {
// const $type = !isAccountReport
// ? 'com.atproto.repo.strongRef'
// : 'com.atproto.admin.defs#repoRef'
// await getAgent().createModerationReport({
// reasonType: ComAtprotoModerationDefs.REASONAPPEAL,
// subject: {
// $type,
// ...props,
// },
// reason: details,
// })
// Toast.show(_(msg`We'll look into your appeal promptly.`))
// } finally {
// closeModal()
// }
// }
return (
<Dialog.Outer defaultOpen control={control} onClose={onClose}>
<Dialog.Handle />
<Dialog.ScrollableInner
accessibilityDescribedBy="dialog-description"
accessibilityLabelledBy="dialog-title">
<Text nativeID="dialog-title" style={[a.text_2xl, a.font_bold]}>
<Trans>Labels on my {isAccount ? 'account' : 'content'}</Trans>
</Text>
<Text nativeID="dialog-description" style={[a.text_sm]}>
<Trans>
You may appeal these labels if you feel they were placed in error.
</Trans>
</Text>
{labels.map(label => (
<Text key={`${label.src}-${label.val}`}>{label.val} TODO</Text>
))}
<View style={gtMobile && [a.flex_row, a.justify_end]}>
<Button
testID="doneBtn"
variant="outline"
color="primary"
size="small"
onPress={() => control.close()}
label={_(msg`Done`)}>
{_(msg`Done`)}
</Button>
</View>
</Dialog.ScrollableInner>
</Dialog.Outer>
)
}
-16
View File
@@ -1,16 +0,0 @@
# Global Dialogs
The dialogs and utils contained in this directory are intended to be "global" in
the sense that they are very common.
A good example: the report dialog. We need to be able to open one from every
post, but if every post had its own `Dialog`, performance would suffer.
Previously the solution was to use the same modal/sheet via `Modal`, and just
swap out the content.
The solution here is to only render the dialog when it's opened, and to enable
programmatic opening of said dialog e.g. from a context menu.
**For other dialogs that can be rendered _in situ_, use `Dialog` directly.**
Only use this abstraction for instances that either aren't possible or would be
performance intensive when defining a `Dialog` in situ.
-101
View File
@@ -1,101 +0,0 @@
import React from 'react'
import * as Dialog from '#/components/Dialog'
/**
* Type util for global dialog components. Wrap individual dialog component
* types with this to get types for the additional properties applied by the
* the global dialon controller.
*/
export type GlobalDialogProps<T = any> = {
params: T
cleanup(): void
options?: Dialog.DialogControlOpenOptions
}
type ActiveDialog<T extends React.ComponentType<GlobalDialogProps>> = {
component: T
props: React.ComponentProps<T>
options?: Dialog.DialogControlOpenOptions
}
type ContextProps = {
activeDialogs: ActiveDialog<any>[]
open<T extends React.ComponentType<GlobalDialogProps>>(
component: T,
props: React.ComponentProps<T>['params'],
options?: Dialog.DialogControlOpenOptions,
): void
popTopDialog(): void
}
export const Context = React.createContext<ContextProps>({
activeDialogs: [],
open() {},
popTopDialog() {},
})
/**
* Hook to open a "global" dialog.
*
* @example
* ```tsx
* const openGlobalDialog = useOpenGlobalDialog()
* openGlobalDialog(
* MyDialog,
* // component props
* {foo: 'bar'},
* // base Dialog options
* { index: 1 }
* )
* ```
*/
export function useOpenGlobalDialog() {
return React.useContext(Context).open
}
/**
* Provider for "global" dialogs _only_. ALL dialogs are still registered by
* `#/state/dialogs` as well.
*/
export function Provider({children}: React.PropsWithChildren<{}>) {
const [activeDialogs, setActiveDialogs] = React.useState<ActiveDialog<any>[]>(
[],
)
const ctx = React.useMemo<ContextProps>(
() => ({
activeDialogs,
open(component, props, options) {
setActiveDialogs(s => [...s, {component, props, options}])
},
popTopDialog() {
setActiveDialogs(s => s.slice(0, -1))
},
}),
[activeDialogs, setActiveDialogs],
)
return <Context.Provider value={ctx}>{children}</Context.Provider>
}
/**
* Outlet for any active "global" dialogs. Since these are rendered into a
* portal in the root, this is technically not their final resting place.
*/
export function GlobalDialog() {
const {activeDialogs, popTopDialog} = React.useContext(Context)
const cleanup = React.useCallback(() => {
popTopDialog()
}, [popTopDialog])
return React.useMemo(
() =>
activeDialogs.map(({component: Comp, props, options}, i) => {
return (
<Comp key={i} params={props} cleanup={cleanup} options={options} />
)
}),
[activeDialogs, cleanup],
)
}