Add post interaction settings to dropdown for all users
This commit is contained in:
@@ -16,10 +16,11 @@ import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
|||||||
import * as Dialog from '#/components/Dialog'
|
import * as Dialog from '#/components/Dialog'
|
||||||
import {Divider} from '#/components/Divider'
|
import {Divider} from '#/components/Divider'
|
||||||
import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check'
|
import {Check_Stroke2_Corner0_Rounded as Check} from '#/components/icons/Check'
|
||||||
|
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
|
||||||
import {Loader} from '#/components/Loader'
|
import {Loader} from '#/components/Loader'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
|
|
||||||
type Props = {
|
export type Props = {
|
||||||
onSave: () => void
|
onSave: () => void
|
||||||
isSaving: boolean
|
isSaving: boolean
|
||||||
|
|
||||||
@@ -28,6 +29,8 @@ type Props = {
|
|||||||
|
|
||||||
threadgateAllowUISettings: ThreadgateAllowUISetting[]
|
threadgateAllowUISettings: ThreadgateAllowUISetting[]
|
||||||
onChangeThreadgateAllowUISettings: (v: ThreadgateAllowUISetting[]) => void
|
onChangeThreadgateAllowUISettings: (v: ThreadgateAllowUISetting[]) => void
|
||||||
|
|
||||||
|
replySettingsDisabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PostInteractionSettingsDialog({
|
export function PostInteractionSettingsDialog({
|
||||||
@@ -36,21 +39,28 @@ export function PostInteractionSettingsDialog({
|
|||||||
}: Props & {
|
}: Props & {
|
||||||
control: Dialog.DialogControlProps
|
control: Dialog.DialogControlProps
|
||||||
}) {
|
}) {
|
||||||
|
const {_} = useLingui()
|
||||||
return (
|
return (
|
||||||
<Dialog.Outer control={control}>
|
<Dialog.Outer control={control}>
|
||||||
<Dialog.Handle />
|
<Dialog.Handle />
|
||||||
<DialogContent {...rest} />
|
<Dialog.ScrollableInner
|
||||||
|
label={_(msg`Edit post interaction settings`)}
|
||||||
|
style={[{maxWidth: 500}, a.w_full]}>
|
||||||
|
<PostInteractionSettingsDialogInner {...rest} />
|
||||||
|
<Dialog.Close />
|
||||||
|
</Dialog.ScrollableInner>
|
||||||
</Dialog.Outer>
|
</Dialog.Outer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function DialogContent({
|
export function PostInteractionSettingsDialogInner({
|
||||||
onSave,
|
onSave,
|
||||||
isSaving,
|
isSaving,
|
||||||
postgate,
|
postgate,
|
||||||
onChangePostgate,
|
onChangePostgate,
|
||||||
threadgateAllowUISettings,
|
threadgateAllowUISettings,
|
||||||
onChangeThreadgateAllowUISettings,
|
onChangeThreadgateAllowUISettings,
|
||||||
|
replySettingsDisabled,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
@@ -91,9 +101,7 @@ function DialogContent({
|
|||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.ScrollableInner
|
<View>
|
||||||
label={_(msg`Edit post interaction settings`)}
|
|
||||||
style={[{maxWidth: 500}, a.w_full]}>
|
|
||||||
<View style={[a.flex_1, a.gap_md]}>
|
<View style={[a.flex_1, a.gap_md]}>
|
||||||
<Text style={[a.text_2xl, a.font_bold]}>
|
<Text style={[a.text_2xl, a.font_bold]}>
|
||||||
<Trans>Post interaction settings</Trans>
|
<Trans>Post interaction settings</Trans>
|
||||||
@@ -143,7 +151,31 @@ function DialogContent({
|
|||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<View style={[a.gap_sm]}>
|
{replySettingsDisabled && (
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.px_md,
|
||||||
|
a.py_sm,
|
||||||
|
a.rounded_sm,
|
||||||
|
a.flex_row,
|
||||||
|
a.align_center,
|
||||||
|
a.gap_sm,
|
||||||
|
t.atoms.bg_contrast_25,
|
||||||
|
]}>
|
||||||
|
<CircleInfo fill={t.atoms.text_contrast_low.color} />
|
||||||
|
<Text style={[a.leading_snug, t.atoms.text_contrast_medium]}>
|
||||||
|
<Trans>Reply settings are inherited from the root post</Trans>
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.gap_sm,
|
||||||
|
{
|
||||||
|
opacity: replySettingsDisabled ? 0.3 : 1,
|
||||||
|
},
|
||||||
|
]}>
|
||||||
<Text style={[a.font_bold, a.text_lg]}>
|
<Text style={[a.font_bold, a.text_lg]}>
|
||||||
<Trans>Reply settings</Trans>
|
<Trans>Reply settings</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
@@ -162,6 +194,7 @@ function DialogContent({
|
|||||||
onChangeThreadgateAllowUISettings([{type: 'everybody'}])
|
onChangeThreadgateAllowUISettings([{type: 'everybody'}])
|
||||||
}
|
}
|
||||||
style={{flex: 1}}
|
style={{flex: 1}}
|
||||||
|
disabled={replySettingsDisabled}
|
||||||
/>
|
/>
|
||||||
<Selectable
|
<Selectable
|
||||||
label={_(msg`Nobody`)}
|
label={_(msg`Nobody`)}
|
||||||
@@ -170,6 +203,7 @@ function DialogContent({
|
|||||||
onChangeThreadgateAllowUISettings([{type: 'nobody'}])
|
onChangeThreadgateAllowUISettings([{type: 'nobody'}])
|
||||||
}
|
}
|
||||||
style={{flex: 1}}
|
style={{flex: 1}}
|
||||||
|
disabled={replySettingsDisabled}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
@@ -188,6 +222,7 @@ function DialogContent({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
onPress={() => onPressAudience({type: 'mention'})}
|
onPress={() => onPressAudience({type: 'mention'})}
|
||||||
|
disabled={replySettingsDisabled}
|
||||||
/>
|
/>
|
||||||
<Selectable
|
<Selectable
|
||||||
label={_(msg`Followed users`)}
|
label={_(msg`Followed users`)}
|
||||||
@@ -197,6 +232,7 @@ function DialogContent({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
onPress={() => onPressAudience({type: 'following'})}
|
onPress={() => onPressAudience({type: 'following'})}
|
||||||
|
disabled={replySettingsDisabled}
|
||||||
/>
|
/>
|
||||||
{lists && lists.length > 0
|
{lists && lists.length > 0
|
||||||
? lists.map(list => (
|
? lists.map(list => (
|
||||||
@@ -211,6 +247,7 @@ function DialogContent({
|
|||||||
onPress={() =>
|
onPress={() =>
|
||||||
onPressAudience({type: 'list', list: list.uri})
|
onPressAudience({type: 'list', list: list.uri})
|
||||||
}
|
}
|
||||||
|
disabled={replySettingsDisabled}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
: // No loading states to avoid jumps for the common case (no lists)
|
: // No loading states to avoid jumps for the common case (no lists)
|
||||||
@@ -233,9 +270,7 @@ function DialogContent({
|
|||||||
<ButtonText>{_(msg`Save`)}</ButtonText>
|
<ButtonText>{_(msg`Save`)}</ButtonText>
|
||||||
{isSaving && <ButtonIcon icon={Loader} position="right" />}
|
{isSaving && <ButtonIcon icon={Loader} position="right" />}
|
||||||
</Button>
|
</Button>
|
||||||
|
</View>
|
||||||
<Dialog.Close />
|
|
||||||
</Dialog.ScrollableInner>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,15 +279,18 @@ function Selectable({
|
|||||||
isSelected,
|
isSelected,
|
||||||
onPress,
|
onPress,
|
||||||
style,
|
style,
|
||||||
|
disabled,
|
||||||
}: {
|
}: {
|
||||||
label: string
|
label: string
|
||||||
isSelected: boolean
|
isSelected: boolean
|
||||||
onPress: () => void
|
onPress: () => void
|
||||||
style?: StyleProp<ViewStyle>
|
style?: StyleProp<ViewStyle>
|
||||||
|
disabled?: boolean
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
|
disabled={disabled}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
label={label}
|
label={label}
|
||||||
accessibilityHint="Select this option"
|
accessibilityHint="Select this option"
|
||||||
|
|||||||
@@ -2,35 +2,41 @@ import {AppBskyFeedDefs, AppBskyFeedThreadgate} from '@atproto/api'
|
|||||||
|
|
||||||
import {ThreadgateAllowUISetting} from '#/state/queries/threadgate/types'
|
import {ThreadgateAllowUISetting} from '#/state/queries/threadgate/types'
|
||||||
|
|
||||||
|
export function threadgateViewToAllowUISetting(
|
||||||
|
threadgateView: AppBskyFeedDefs.ThreadgateView | undefined,
|
||||||
|
): ThreadgateAllowUISetting[] {
|
||||||
|
const threadgate =
|
||||||
|
threadgateView &&
|
||||||
|
AppBskyFeedThreadgate.isRecord(threadgateView.record) &&
|
||||||
|
AppBskyFeedThreadgate.validateRecord(threadgateView.record).success
|
||||||
|
? threadgateView.record
|
||||||
|
: undefined
|
||||||
|
return threadgateRecordToAllowUISetting(threadgate)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a full {@link AppBskyFeedThreadgate.Record} to a list of
|
* Converts a full {@link AppBskyFeedThreadgate.Record} to a list of
|
||||||
* {@link ThreadgateAllowUISetting}, for use by app UI.
|
* {@link ThreadgateAllowUISetting}, for use by app UI.
|
||||||
*/
|
*/
|
||||||
export function threadgateViewToAllowUISetting(
|
export function threadgateRecordToAllowUISetting(
|
||||||
threadgate: AppBskyFeedDefs.ThreadgateView | undefined,
|
threadgate: AppBskyFeedThreadgate.Record | undefined,
|
||||||
): ThreadgateAllowUISetting[] {
|
): ThreadgateAllowUISetting[] {
|
||||||
const record =
|
|
||||||
threadgate &&
|
|
||||||
AppBskyFeedThreadgate.isRecord(threadgate.record) &&
|
|
||||||
AppBskyFeedThreadgate.validateRecord(threadgate.record).success
|
|
||||||
? threadgate.record
|
|
||||||
: null
|
|
||||||
/*
|
/*
|
||||||
* If record doesn't exist (default), or if `record.allow === undefined`, it means
|
* If `threadgate` doesn't exist (default), or if `threadgate.allow === undefined`, it means
|
||||||
* anyone can reply.
|
* anyone can reply.
|
||||||
*
|
*
|
||||||
* If `record.allow === []` it means no one can reply, and we translate to UI code
|
* If `threadgate.allow === []` it means no one can reply, and we translate to UI code
|
||||||
* here. This was a historical choice, and we have no lexicon representation
|
* here. This was a historical choice, and we have no lexicon representation
|
||||||
* for 'replies disabled' other than an empty array.
|
* for 'replies disabled' other than an empty array.
|
||||||
*/
|
*/
|
||||||
if (!record || record.allow === undefined) {
|
if (!threadgate || threadgate.allow === undefined) {
|
||||||
return [{type: 'everybody'}]
|
return [{type: 'everybody'}]
|
||||||
}
|
}
|
||||||
if (record.allow.length === 0) {
|
if (threadgate.allow.length === 0) {
|
||||||
return [{type: 'nobody'}]
|
return [{type: 'nobody'}]
|
||||||
}
|
}
|
||||||
|
|
||||||
const settings: ThreadgateAllowUISetting[] = record.allow
|
const settings: ThreadgateAllowUISetting[] = threadgate.allow
|
||||||
.map(allow => {
|
.map(allow => {
|
||||||
let setting: ThreadgateAllowUISetting | undefined
|
let setting: ThreadgateAllowUISetting | undefined
|
||||||
if (allow.$type === 'app.bsky.feed.threadgate#mentionRule') {
|
if (allow.$type === 'app.bsky.feed.threadgate#mentionRule') {
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ export function PostThreadItem({
|
|||||||
onPostReply: (postUri: string | undefined) => void
|
onPostReply: (postUri: string | undefined) => void
|
||||||
hideTopBorder?: boolean
|
hideTopBorder?: boolean
|
||||||
}) {
|
}) {
|
||||||
|
// TODO should load this for every post, ugh
|
||||||
const {data: postgate, isLoading} = usePostgateQuery({postUri: post.uri})
|
const {data: postgate, isLoading} = usePostgateQuery({postUri: post.uri})
|
||||||
const postShadowed = usePostShadow(post)
|
const postShadowed = usePostShadow(post)
|
||||||
const richText = useMemo(
|
const richText = useMemo(
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import * as Clipboard from 'expo-clipboard'
|
|||||||
import {
|
import {
|
||||||
AppBskyFeedDefs,
|
AppBskyFeedDefs,
|
||||||
AppBskyFeedPost,
|
AppBskyFeedPost,
|
||||||
|
AppBskyFeedPostgate,
|
||||||
AppBskyFeedThreadgate,
|
AppBskyFeedThreadgate,
|
||||||
AtUri,
|
AtUri,
|
||||||
RichText as RichTextAPI,
|
RichText as RichTextAPI,
|
||||||
@@ -32,9 +33,17 @@ import {
|
|||||||
usePostDeleteMutation,
|
usePostDeleteMutation,
|
||||||
useThreadMuteMutationQueue,
|
useThreadMuteMutationQueue,
|
||||||
} from '#/state/queries/post'
|
} from '#/state/queries/post'
|
||||||
import {useToggleQuoteDetachmentMutation} from '#/state/queries/postgate'
|
import {
|
||||||
import {getMaybeDetachedQuoteEmbed} from '#/state/queries/postgate/util'
|
usePostgateQuery,
|
||||||
|
useToggleQuoteDetachmentMutation,
|
||||||
|
useWritePostgateMutation,
|
||||||
|
} from '#/state/queries/postgate'
|
||||||
|
import {
|
||||||
|
createPostgateRecord,
|
||||||
|
getMaybeDetachedQuoteEmbed,
|
||||||
|
} from '#/state/queries/postgate/util'
|
||||||
import {useToggleReplyVisibilityMutation} from '#/state/queries/threadgate'
|
import {useToggleReplyVisibilityMutation} from '#/state/queries/threadgate'
|
||||||
|
import {threadgateRecordToAllowUISetting} from '#/state/queries/threadgate/util'
|
||||||
import {useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
import {getCurrentRoute} from 'lib/routes/helpers'
|
import {getCurrentRoute} from 'lib/routes/helpers'
|
||||||
import {shareUrl} from 'lib/sharing'
|
import {shareUrl} from 'lib/sharing'
|
||||||
@@ -42,8 +51,10 @@ import {toShareUrl} from 'lib/strings/url-helpers'
|
|||||||
import {useTheme} from 'lib/ThemeContext'
|
import {useTheme} from 'lib/ThemeContext'
|
||||||
import {atoms as a, useBreakpoints, useTheme as useAlf} from '#/alf'
|
import {atoms as a, useBreakpoints, useTheme as useAlf} from '#/alf'
|
||||||
import {useDialogControl} from '#/components/Dialog'
|
import {useDialogControl} from '#/components/Dialog'
|
||||||
|
import * as Dialog from '#/components/Dialog'
|
||||||
import {useGlobalDialogsControlContext} from '#/components/dialogs/Context'
|
import {useGlobalDialogsControlContext} from '#/components/dialogs/Context'
|
||||||
import {EmbedDialog} from '#/components/dialogs/Embed'
|
import {EmbedDialog} from '#/components/dialogs/Embed'
|
||||||
|
import {PostInteractionSettingsDialogInner} from '#/components/dialogs/PostInteractionSettingsDialog'
|
||||||
import {SendViaChatDialog} from '#/components/dms/dialogs/ShareViaChatDialog'
|
import {SendViaChatDialog} from '#/components/dms/dialogs/ShareViaChatDialog'
|
||||||
import {ArrowOutOfBox_Stroke2_Corner0_Rounded as Share} from '#/components/icons/ArrowOutOfBox'
|
import {ArrowOutOfBox_Stroke2_Corner0_Rounded as Share} from '#/components/icons/ArrowOutOfBox'
|
||||||
import {BubbleQuestion_Stroke2_Corner0_Rounded as Translate} from '#/components/icons/Bubble'
|
import {BubbleQuestion_Stroke2_Corner0_Rounded as Translate} from '#/components/icons/Bubble'
|
||||||
@@ -59,6 +70,7 @@ import {EyeSlash_Stroke2_Corner0_Rounded as EyeSlash} from '#/components/icons/E
|
|||||||
import {Filter_Stroke2_Corner0_Rounded as Filter} from '#/components/icons/Filter'
|
import {Filter_Stroke2_Corner0_Rounded as Filter} from '#/components/icons/Filter'
|
||||||
import {Mute_Stroke2_Corner0_Rounded as Mute} from '#/components/icons/Mute'
|
import {Mute_Stroke2_Corner0_Rounded as Mute} from '#/components/icons/Mute'
|
||||||
import {PaperPlane_Stroke2_Corner0_Rounded as Send} from '#/components/icons/PaperPlane'
|
import {PaperPlane_Stroke2_Corner0_Rounded as Send} from '#/components/icons/PaperPlane'
|
||||||
|
import {SettingsGear2_Stroke2_Corner0_Rounded as Gear} from '#/components/icons/SettingsGear2'
|
||||||
import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as Unmute} from '#/components/icons/Speaker'
|
import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as Unmute} from '#/components/icons/Speaker'
|
||||||
import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash'
|
import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash'
|
||||||
import {Warning_Stroke2_Corner0_Rounded as Warning} from '#/components/icons/Warning'
|
import {Warning_Stroke2_Corner0_Rounded as Warning} from '#/components/icons/Warning'
|
||||||
@@ -114,6 +126,7 @@ let PostDropdownBtn = ({
|
|||||||
const loggedOutWarningPromptControl = useDialogControl()
|
const loggedOutWarningPromptControl = useDialogControl()
|
||||||
const embedPostControl = useDialogControl()
|
const embedPostControl = useDialogControl()
|
||||||
const sendViaChatControl = useDialogControl()
|
const sendViaChatControl = useDialogControl()
|
||||||
|
const postInteractionSettingsDialogControl = useDialogControl()
|
||||||
const {mutateAsync: toggleReplyVisibility} =
|
const {mutateAsync: toggleReplyVisibility} =
|
||||||
useToggleReplyVisibilityMutation()
|
useToggleReplyVisibilityMutation()
|
||||||
|
|
||||||
@@ -136,6 +149,8 @@ let PostDropdownBtn = ({
|
|||||||
)
|
)
|
||||||
const isPostHidden = hiddenPosts && hiddenPosts.includes(postUri)
|
const isPostHidden = hiddenPosts && hiddenPosts.includes(postUri)
|
||||||
const isAuthor = postAuthor.did === currentAccount?.did
|
const isAuthor = postAuthor.did === currentAccount?.did
|
||||||
|
const isRootPostAuthor =
|
||||||
|
rootPostUri && new AtUri(rootPostUri).host === currentAccount?.did
|
||||||
const isReplyHiddenByThreadgate =
|
const isReplyHiddenByThreadgate =
|
||||||
threadgateRecord?.hiddenReplies?.includes(postUri)
|
threadgateRecord?.hiddenReplies?.includes(postUri)
|
||||||
|
|
||||||
@@ -322,7 +337,8 @@ let PostDropdownBtn = ({
|
|||||||
}, [_, quoteEmbed, post, toggleQuoteDetachment])
|
}, [_, quoteEmbed, post, toggleQuoteDetachment])
|
||||||
|
|
||||||
const canEmbed = isWeb && gtMobile && !hideInPWI
|
const canEmbed = isWeb && gtMobile && !hideInPWI
|
||||||
const canHideReply = !isAuthor && !isPostHidden && rootPostUri && isReply
|
const canHideReply =
|
||||||
|
!isAuthor && isRootPostAuthor && !isPostHidden && rootPostUri && isReply
|
||||||
const canDetachQuote = quoteEmbed && quoteEmbed.isOwnedByViewer
|
const canDetachQuote = quoteEmbed && quoteEmbed.isOwnedByViewer
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -549,6 +565,16 @@ let PostDropdownBtn = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{isAuthor && (
|
{isAuthor && (
|
||||||
|
<>
|
||||||
|
<Menu.Item
|
||||||
|
testID="postDropdownEditPostInteractions"
|
||||||
|
label={_(msg`Edit interaction settings`)}
|
||||||
|
onPress={postInteractionSettingsDialogControl.open}>
|
||||||
|
<Menu.ItemText>
|
||||||
|
{_(msg`Edit interaction settings`)}
|
||||||
|
</Menu.ItemText>
|
||||||
|
<Menu.ItemIcon icon={Gear} position="right" />
|
||||||
|
</Menu.Item>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
testID="postDropdownDeleteBtn"
|
testID="postDropdownDeleteBtn"
|
||||||
label={_(msg`Delete post`)}
|
label={_(msg`Delete post`)}
|
||||||
@@ -556,6 +582,7 @@ let PostDropdownBtn = ({
|
|||||||
<Menu.ItemText>{_(msg`Delete post`)}</Menu.ItemText>
|
<Menu.ItemText>{_(msg`Delete post`)}</Menu.ItemText>
|
||||||
<Menu.ItemIcon icon={Trash} position="right" />
|
<Menu.ItemIcon icon={Trash} position="right" />
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</Menu.Group>
|
</Menu.Group>
|
||||||
</>
|
</>
|
||||||
@@ -616,9 +643,112 @@ let PostDropdownBtn = ({
|
|||||||
control={sendViaChatControl}
|
control={sendViaChatControl}
|
||||||
onSelectChat={onSelectChatToShareTo}
|
onSelectChat={onSelectChatToShareTo}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<PostInteractionSettingsDialogControlled
|
||||||
|
control={postInteractionSettingsDialogControl}
|
||||||
|
postUri={post.uri}
|
||||||
|
threadgateRecord={threadgateRecord}
|
||||||
|
/>
|
||||||
</EventStopper>
|
</EventStopper>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function PostInteractionSettingsDialogControlled(props: {
|
||||||
|
control: Dialog.DialogControlProps
|
||||||
|
postUri: string
|
||||||
|
threadgateRecord?: AppBskyFeedThreadgate.Record
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Dialog.Outer control={props.control}>
|
||||||
|
<Dialog.Handle />
|
||||||
|
<PostInteractionSettingsDialogControlledInner {...props} />
|
||||||
|
</Dialog.Outer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function PostInteractionSettingsDialogControlledInner(props: {
|
||||||
|
control: Dialog.DialogControlProps
|
||||||
|
postUri: string
|
||||||
|
threadgateRecord?: AppBskyFeedThreadgate.Record
|
||||||
|
}) {
|
||||||
|
const {_} = useLingui()
|
||||||
|
const threadgateAllowUISettings = React.useMemo(() => {
|
||||||
|
return threadgateRecordToAllowUISetting(props.threadgateRecord)
|
||||||
|
}, [props.threadgateRecord])
|
||||||
|
const [isSaving, setIsSaving] = React.useState(false)
|
||||||
|
const {mutateAsync: writePostgateRecord} = useWritePostgateMutation()
|
||||||
|
const {data: postgate, isLoading} = usePostgateQuery({postUri: props.postUri})
|
||||||
|
const [editedPostgate, setEditedPostgate] =
|
||||||
|
React.useState<AppBskyFeedPostgate.Record>()
|
||||||
|
|
||||||
|
const onChangePostgate = React.useCallback(
|
||||||
|
(next: AppBskyFeedPostgate.Record) => {
|
||||||
|
setEditedPostgate(next)
|
||||||
|
},
|
||||||
|
[setEditedPostgate],
|
||||||
|
)
|
||||||
|
|
||||||
|
const onSave = React.useCallback(async () => {
|
||||||
|
if (!editedPostgate) {
|
||||||
|
props.control.close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsSaving(true)
|
||||||
|
|
||||||
|
try {
|
||||||
|
await writePostgateRecord({
|
||||||
|
postUri: props.postUri,
|
||||||
|
postgate: editedPostgate,
|
||||||
|
})
|
||||||
|
props.control.close()
|
||||||
|
} catch (e: any) {
|
||||||
|
logger.error(`Failed to save post interaction settings`, {
|
||||||
|
context: 'PostInteractionSettingsDialogControlledInner',
|
||||||
|
safeMessage: e.message,
|
||||||
|
})
|
||||||
|
Toast.show(
|
||||||
|
_(
|
||||||
|
msg`There was an issue. Please check your internet connection and try again.`,
|
||||||
|
),
|
||||||
|
'xmark',
|
||||||
|
)
|
||||||
|
} finally {
|
||||||
|
setIsSaving(false)
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
_,
|
||||||
|
props.postUri,
|
||||||
|
props.control,
|
||||||
|
editedPostgate,
|
||||||
|
setIsSaving,
|
||||||
|
writePostgateRecord,
|
||||||
|
])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog.ScrollableInner
|
||||||
|
label={_(msg`Edit post interaction settings`)}
|
||||||
|
style={[{maxWidth: 500}, a.w_full]}>
|
||||||
|
{isLoading ? (
|
||||||
|
<Loader size="xl" />
|
||||||
|
) : (
|
||||||
|
<PostInteractionSettingsDialogInner
|
||||||
|
replySettingsDisabled
|
||||||
|
isSaving={isSaving}
|
||||||
|
onSave={onSave}
|
||||||
|
postgate={
|
||||||
|
editedPostgate ||
|
||||||
|
postgate ||
|
||||||
|
createPostgateRecord({post: props.postUri})
|
||||||
|
}
|
||||||
|
onChangePostgate={onChangePostgate}
|
||||||
|
threadgateAllowUISettings={threadgateAllowUISettings}
|
||||||
|
onChangeThreadgateAllowUISettings={() => {}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Dialog.ScrollableInner>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
PostDropdownBtn = memo(PostDropdownBtn)
|
PostDropdownBtn = memo(PostDropdownBtn)
|
||||||
export {PostDropdownBtn}
|
export {PostDropdownBtn}
|
||||||
|
|||||||
Reference in New Issue
Block a user