Update LabelsOnMe dialog
This commit is contained in:
@@ -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>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -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.
|
|
||||||
@@ -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],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {DialogControlProps} from '#/components/Dialog'
|
import {DialogControlProps} from '#/components/Dialog'
|
||||||
import {Provider as GlobalDialogsProvider} from '#/components/dialogs'
|
import {Provider as GlobalDialogsProvider} from '#/components/dialogs/Context'
|
||||||
import {Provider as GlobalDialogsProviderV2} from '#/components/dialogs/Context'
|
|
||||||
|
|
||||||
const DialogContext = React.createContext<{
|
const DialogContext = React.createContext<{
|
||||||
activeDialogs: React.MutableRefObject<
|
activeDialogs: React.MutableRefObject<
|
||||||
@@ -39,9 +38,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
|||||||
return (
|
return (
|
||||||
<DialogContext.Provider value={context}>
|
<DialogContext.Provider value={context}>
|
||||||
<DialogControlContext.Provider value={controls}>
|
<DialogControlContext.Provider value={controls}>
|
||||||
<GlobalDialogsProvider>
|
<GlobalDialogsProvider>{children}</GlobalDialogsProvider>
|
||||||
<GlobalDialogsProviderV2>{children}</GlobalDialogsProviderV2>
|
|
||||||
</GlobalDialogsProvider>
|
|
||||||
</DialogControlContext.Provider>
|
</DialogControlContext.Provider>
|
||||||
</DialogContext.Provider>
|
</DialogContext.Provider>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,8 +8,10 @@ import {useSession} from '#/state/session'
|
|||||||
import {atoms as a} from '#/alf'
|
import {atoms as a} from '#/alf'
|
||||||
import {Button, ButtonText, ButtonIcon, ButtonSize} from '#/components/Button'
|
import {Button, ButtonText, ButtonIcon, ButtonSize} from '#/components/Button'
|
||||||
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
|
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
|
||||||
import {useOpenGlobalDialog} from '#/components/dialogs'
|
import {
|
||||||
import {LabelsOnMeDialog} from '#/components/dialogs/LabelsOnMe'
|
LabelsOnMeDialog,
|
||||||
|
useLabelsOnMeDialogControl,
|
||||||
|
} from '#/components/LabelsOnMe'
|
||||||
|
|
||||||
export function LabelsOnMe({
|
export function LabelsOnMe({
|
||||||
details,
|
details,
|
||||||
@@ -25,7 +27,7 @@ export function LabelsOnMe({
|
|||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
const isAccount = 'did' in details
|
const isAccount = 'did' in details
|
||||||
const openDialog = useOpenGlobalDialog()
|
const control = useLabelsOnMeDialogControl()
|
||||||
|
|
||||||
if (!labels || !currentAccount) {
|
if (!labels || !currentAccount) {
|
||||||
return null
|
return null
|
||||||
@@ -39,16 +41,15 @@ export function LabelsOnMe({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[a.flex_row, style]}>
|
<View style={[a.flex_row, style]}>
|
||||||
|
<LabelsOnMeDialog control={control} subject={details} labels={labels} />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="solid"
|
variant="solid"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
size={size || 'small'}
|
size={size || 'small'}
|
||||||
label={_(msg`View information about these labels`)}
|
label={_(msg`View information about these labels`)}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
openDialog(LabelsOnMeDialog, {
|
control.open()
|
||||||
subject: details,
|
|
||||||
labels: labels!,
|
|
||||||
})
|
|
||||||
}}>
|
}}>
|
||||||
<ButtonIcon position="left" icon={CircleInfo} />
|
<ButtonIcon position="left" icon={CircleInfo} />
|
||||||
<ButtonText>
|
<ButtonText>
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import {useSession} from '#/state/session'
|
|||||||
import {useCloseAnyActiveElement} from '#/state/util'
|
import {useCloseAnyActiveElement} from '#/state/util'
|
||||||
import * as notifications from 'lib/notifications/notifications'
|
import * as notifications from 'lib/notifications/notifications'
|
||||||
import {Outlet as PortalOutlet} from '#/components/Portal'
|
import {Outlet as PortalOutlet} from '#/components/Portal'
|
||||||
import {GlobalDialog} from '#/components/dialogs'
|
|
||||||
|
|
||||||
function ShellInner() {
|
function ShellInner() {
|
||||||
const isDrawerOpen = useIsDrawerOpen()
|
const isDrawerOpen = useIsDrawerOpen()
|
||||||
@@ -96,8 +95,6 @@ function ShellInner() {
|
|||||||
<Composer winHeight={winDim.height} />
|
<Composer winHeight={winDim.height} />
|
||||||
<ModalsContainer />
|
<ModalsContainer />
|
||||||
<Lightbox />
|
<Lightbox />
|
||||||
|
|
||||||
<GlobalDialog />
|
|
||||||
<PortalOutlet />
|
<PortalOutlet />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import {useIsDrawerOpen, useSetDrawerOpen} from '#/state/shell'
|
|||||||
import {useCloseAllActiveElements} from '#/state/util'
|
import {useCloseAllActiveElements} from '#/state/util'
|
||||||
import {useWebBodyScrollLock} from '#/lib/hooks/useWebBodyScrollLock'
|
import {useWebBodyScrollLock} from '#/lib/hooks/useWebBodyScrollLock'
|
||||||
import {Outlet as PortalOutlet} from '#/components/Portal'
|
import {Outlet as PortalOutlet} from '#/components/Portal'
|
||||||
import {GlobalDialog} from '#/components/dialogs'
|
|
||||||
|
|
||||||
function ShellInner() {
|
function ShellInner() {
|
||||||
const isDrawerOpen = useIsDrawerOpen()
|
const isDrawerOpen = useIsDrawerOpen()
|
||||||
@@ -42,8 +41,6 @@ function ShellInner() {
|
|||||||
<Composer winHeight={0} />
|
<Composer winHeight={0} />
|
||||||
<ModalsContainer />
|
<ModalsContainer />
|
||||||
<Lightbox />
|
<Lightbox />
|
||||||
|
|
||||||
<GlobalDialog />
|
|
||||||
<PortalOutlet />
|
<PortalOutlet />
|
||||||
|
|
||||||
{!isDesktop && isDrawerOpen && (
|
{!isDesktop && isDrawerOpen && (
|
||||||
|
|||||||
Reference in New Issue
Block a user