diff --git a/src/lib/constants.ts b/src/lib/constants.ts
index 05d1591f..e0b89980 100644
--- a/src/lib/constants.ts
+++ b/src/lib/constants.ts
@@ -84,6 +84,7 @@ export const createHitslop = (size: number): Insets => ({
export const HITSLOP_10 = createHitslop(10)
export const HITSLOP_20 = createHitslop(20)
export const HITSLOP_30 = createHitslop(30)
+export const POST_CTRL_HITSLOP = {top: 5, bottom: 10, left: 10, right: 10}
export const BACK_HITSLOP = HITSLOP_30
export const MAX_POST_LINES = 25
diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx
index 6d03029d..92b529db 100644
--- a/src/view/com/post-thread/PostThreadItem.tsx
+++ b/src/view/com/post-thread/PostThreadItem.tsx
@@ -25,7 +25,7 @@ import {sanitizeHandle} from 'lib/strings/handles'
import {countLines} from 'lib/strings/helpers'
import {niceDate} from 'lib/strings/time'
import {s} from 'lib/styles'
-import {isNative, isWeb} from 'platform/detection'
+import {isWeb} from 'platform/detection'
import {useSession} from 'state/session'
import {PostThreadFollowBtn} from 'view/com/post-thread/PostThreadFollowBtn'
import {atoms as a} from '#/alf'
@@ -35,7 +35,7 @@ import {LabelsOnMyPost} from '../../../components/moderation/LabelsOnMe'
import {PostAlerts} from '../../../components/moderation/PostAlerts'
import {PostHider} from '../../../components/moderation/PostHider'
import {getTranslatorLink, isPostInLanguage} from '../../../locale/helpers'
-import {WhoCanReply} from '../threadgate/WhoCanReply'
+import {WhoCanReplyBlock, WhoCanReplyInline} from '../threadgate/WhoCanReply'
import {ErrorMessage} from '../util/error/ErrorMessage'
import {Link, TextLink} from '../util/Link'
import {formatCount} from '../util/numeric/format'
@@ -340,6 +340,7 @@ let PostThreadItemLoaded = ({
@@ -396,11 +397,6 @@ let PostThreadItemLoaded = ({
-
>
)
} else {
@@ -579,14 +575,7 @@ let PostThreadItemLoaded = ({
) : undefined}
-
+
>
)
}
@@ -654,10 +643,12 @@ function PostOuterWrapper({
function ExpandedPostDetails({
post,
+ isThreadAuthor,
needsTranslation,
translatorUrl,
}: {
post: AppBskyFeedDefs.PostView
+ isThreadAuthor: boolean
needsTranslation: boolean
translatorUrl: string
}) {
@@ -670,14 +661,23 @@ function ExpandedPostDetails({
}, [openLink, translatorUrl])
return (
-
- {niceDate(post.indexedAt)}
+
+ {niceDate(post.indexedAt)}
+
{needsTranslation && (
<>
- ·
+ ·
Translate
diff --git a/src/view/com/threadgate/WhoCanReply.tsx b/src/view/com/threadgate/WhoCanReply.tsx
index 7e3528d9..3f9970f5 100644
--- a/src/view/com/threadgate/WhoCanReply.tsx
+++ b/src/view/com/threadgate/WhoCanReply.tsx
@@ -11,13 +11,10 @@ import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useQueryClient} from '@tanstack/react-query'
-import {useAnalytics} from '#/lib/analytics/analytics'
import {createThreadgate} from '#/lib/api'
import {until} from '#/lib/async/until'
-import {useColorSchemeStyle} from '#/lib/hooks/useColorSchemeStyle'
-import {usePalette} from '#/lib/hooks/usePalette'
+import {HITSLOP_10} from '#/lib/constants'
import {makeListLink, makeProfileLink} from '#/lib/routes/links'
-import {colors} from '#/lib/styles'
import {logger} from '#/logger'
import {isNative} from '#/platform/detection'
import {useModalControls} from '#/state/modals'
@@ -28,45 +25,301 @@ import {
} from '#/state/queries/threadgate'
import {useAgent} from '#/state/session'
import * as Toast from 'view/com/util/Toast'
+import {atoms as a, useTheme} from '#/alf'
import {Button} from '#/components/Button'
+import * as Dialog from '#/components/Dialog'
+import {useDialogControl} from '#/components/Dialog'
+import {CircleBanSign_Stroke2_Corner0_Rounded as CircleBanSign} from '#/components/icons/CircleBanSign'
+import {Earth_Stroke2_Corner0_Rounded as Earth} from '#/components/icons/Globe'
+import {Group3_Stroke2_Corner0_Rounded as Group} from '#/components/icons/Group'
+import {Text} from '#/components/Typography'
import {TextLink} from '../util/Link'
-import {Text} from '../util/text/Text'
-export function WhoCanReply({
- post,
- isThreadAuthor,
- style,
-}: {
+interface WhoCanReplyProps {
post: AppBskyFeedDefs.PostView
isThreadAuthor: boolean
style?: StyleProp
-}) {
- const {track} = useAnalytics()
+}
+
+export function WhoCanReplyInline({
+ post,
+ isThreadAuthor,
+ style,
+}: WhoCanReplyProps) {
const {_} = useLingui()
- const pal = usePalette('default')
+ const t = useTheme()
+ const infoDialogControl = useDialogControl()
+ const {settings, isRootPost, onPressEdit} = useWhoCanReply(post)
+
+ if (!isRootPost) {
+ return null
+ }
+ if (!settings.length && !isThreadAuthor) {
+ return null
+ }
+
+ const isEverybody = settings.length === 0
+ const isNobody = !!settings.find(gate => gate.type === 'nobody')
+ const description = isEverybody
+ ? _(msg`Everybody can reply`)
+ : isNobody
+ ? _(msg`Replies disabled`)
+ : _(msg`Some people can reply`)
+
+ return (
+ <>
+
+
+ >
+ )
+}
+
+export function WhoCanReplyBlock({
+ post,
+ isThreadAuthor,
+ style,
+}: WhoCanReplyProps) {
+ const {_} = useLingui()
+ const t = useTheme()
+ const infoDialogControl = useDialogControl()
+ const {settings, isRootPost, onPressEdit} = useWhoCanReply(post)
+
+ if (!isRootPost) {
+ return null
+ }
+ if (!settings.length && !isThreadAuthor) {
+ return null
+ }
+
+ const isEverybody = settings.length === 0
+ const isNobody = !!settings.find(gate => gate.type === 'nobody')
+ const description = isEverybody
+ ? _(msg`Everybody can reply`)
+ : isNobody
+ ? _(msg`Replies on this thread are disabled`)
+ : _(msg`Some people can reply`)
+
+ return (
+ <>
+
+
+ >
+ )
+}
+
+function Icon({
+ color,
+ width,
+ settings,
+}: {
+ color: string
+ width?: number
+ settings: ThreadgateSetting[]
+}) {
+ const isEverybody = settings.length === 0
+ const isNobody = !!settings.find(gate => gate.type === 'nobody')
+ const IconComponent = isEverybody ? Earth : isNobody ? CircleBanSign : Group
+ return
+}
+
+function InfoDialog({
+ control,
+ post,
+ settings,
+}: {
+ control: Dialog.DialogControlProps
+ post: AppBskyFeedDefs.PostView
+ settings: ThreadgateSetting[]
+}) {
+ return (
+
+
+
+
+ )
+}
+
+function InfoDialogInner({
+ post,
+ settings,
+}: {
+ post: AppBskyFeedDefs.PostView
+ settings: ThreadgateSetting[]
+}) {
+ const {_} = useLingui()
+ return (
+
+
+
+ Who can reply?
+
+
+
+
+ )
+}
+
+function Rules({
+ post,
+ settings,
+}: {
+ post: AppBskyFeedDefs.PostView
+ settings: ThreadgateSetting[]
+}) {
+ const t = useTheme()
+ return (
+
+ {!settings.length ? (
+ Everybody can reply
+ ) : settings[0].type === 'nobody' ? (
+ Replies to this thread are disabled
+ ) : (
+
+ Only{' '}
+ {settings.map((rule, i) => (
+ <>
+
+
+ >
+ ))}{' '}
+ can reply
+
+ )}
+
+ )
+}
+
+function Rule({
+ rule,
+ post,
+ lists,
+}: {
+ rule: ThreadgateSetting
+ post: AppBskyFeedDefs.PostView
+ lists: AppBskyGraphDefs.ListViewBasic[] | undefined
+}) {
+ const t = useTheme()
+ if (rule.type === 'mention') {
+ return mentioned users
+ }
+ if (rule.type === 'following') {
+ return (
+
+ users followed by{' '}
+
+
+ )
+ }
+ if (rule.type === 'list') {
+ const list = lists?.find(l => l.uri === rule.list)
+ if (list) {
+ const listUrip = new AtUri(list.uri)
+ return (
+
+ {' '}
+ members
+
+ )
+ }
+ }
+}
+
+function Separator({i, length}: {i: number; length: number}) {
+ if (length < 2 || i === length - 1) {
+ return null
+ }
+ if (i === length - 2) {
+ return (
+ <>
+ {length > 2 ? ',' : ''} and{' '}
+ >
+ )
+ }
+ return <>, >
+}
+
+function useWhoCanReply(post: AppBskyFeedDefs.PostView) {
const agent = useAgent()
const queryClient = useQueryClient()
const {openModal} = useModalControls()
- const containerStyles = useColorSchemeStyle(
- {
- backgroundColor: pal.colors.unreadNotifBg,
- },
- {
- backgroundColor: pal.colors.unreadNotifBg,
- },
- )
- const textStyles = useColorSchemeStyle(
- {color: colors.blue5},
- {color: colors.blue1},
- )
- const hoverStyles = useColorSchemeStyle(
- {
- backgroundColor: colors.white,
- },
- {
- backgroundColor: pal.colors.background,
- },
- )
+
const settings = React.useMemo(
() => threadgateViewToSettings(post.threadgate),
[post],
@@ -74,7 +327,6 @@ export function WhoCanReply({
const isRootPost = !('reply' in post.record)
const onPressEdit = () => {
- track('Post:EditThreadgateOpened')
if (isNative && Keyboard.isVisible()) {
Keyboard.dismiss()
}
@@ -108,7 +360,6 @@ export function WhoCanReply({
queryClient.invalidateQueries({
queryKey: [POST_THREAD_RQKEY_ROOT],
})
- track('Post:ThreadgateEdited')
} catch (err) {
Toast.show(
'There was an issue. Please check your internet connection and try again.',
@@ -119,131 +370,7 @@ export function WhoCanReply({
})
}
- if (!isRootPost) {
- return null
- }
- if (!settings.length && !isThreadAuthor) {
- return null
- }
-
- return (
-
-
-
- {!settings.length ? (
- Everybody can reply.
- ) : settings[0].type === 'nobody' ? (
- Replies to this thread are disabled.
- ) : (
-
- Only{' '}
- {settings.map((rule, i) => (
-
-
-
-
- ))}{' '}
- can reply.
-
- )}
-
-
- {isThreadAuthor && (
-
-
-
- )}
-
- )
-}
-
-function Rule({
- rule,
- post,
- lists,
-}: {
- rule: ThreadgateSetting
- post: AppBskyFeedDefs.PostView
- lists: AppBskyGraphDefs.ListViewBasic[] | undefined
-}) {
- const pal = usePalette('default')
- if (rule.type === 'mention') {
- return mentioned users
- }
- if (rule.type === 'following') {
- return (
-
- users followed by{' '}
-
-
- )
- }
- if (rule.type === 'list') {
- const list = lists?.find(l => l.uri === rule.list)
- if (list) {
- const listUrip = new AtUri(list.uri)
- return (
-
- {' '}
- members
-
- )
- }
- }
-}
-
-function Separator({i, length}: {i: number; length: number}) {
- if (length < 2 || i === length - 1) {
- return null
- }
- if (i === length - 2) {
- return (
- <>
- {length > 2 ? ',' : ''} and{' '}
- >
- )
- }
- return <>, >
+ return {settings, isRootPost, onPressEdit}
}
async function whenAppViewReady(
diff --git a/src/view/com/util/post-ctrls/PostCtrls.tsx b/src/view/com/util/post-ctrls/PostCtrls.tsx
index 55fb4a33..472ce404 100644
--- a/src/view/com/util/post-ctrls/PostCtrls.tsx
+++ b/src/view/com/util/post-ctrls/PostCtrls.tsx
@@ -15,7 +15,7 @@ import {
import {msg, plural} from '@lingui/macro'
import {useLingui} from '@lingui/react'
-import {HITSLOP_10, HITSLOP_20} from '#/lib/constants'
+import {POST_CTRL_HITSLOP} from '#/lib/constants'
import {useHaptics} from '#/lib/haptics'
import {makeProfileLink} from '#/lib/routes/links'
import {shareUrl} from '#/lib/sharing'
@@ -215,7 +215,7 @@ let PostCtrls = ({
other: 'Reply (# replies)',
})}
accessibilityHint=""
- hitSlop={big ? HITSLOP_20 : HITSLOP_10}>
+ hitSlop={POST_CTRL_HITSLOP}>
+ hitSlop={POST_CTRL_HITSLOP}>
{post.viewer?.like ? (
) : (
@@ -299,7 +299,7 @@ let PostCtrls = ({
}}
accessibilityLabel={_(msg`Share`)}
accessibilityHint=""
- hitSlop={big ? HITSLOP_20 : HITSLOP_10}>
+ hitSlop={POST_CTRL_HITSLOP}>
diff --git a/src/view/com/util/post-ctrls/RepostButton.tsx b/src/view/com/util/post-ctrls/RepostButton.tsx
index 10bc369b..d49cda44 100644
--- a/src/view/com/util/post-ctrls/RepostButton.tsx
+++ b/src/view/com/util/post-ctrls/RepostButton.tsx
@@ -3,7 +3,7 @@ import {View} from 'react-native'
import {msg, plural} from '@lingui/macro'
import {useLingui} from '@lingui/react'
-import {HITSLOP_10, HITSLOP_20} from '#/lib/constants'
+import {POST_CTRL_HITSLOP} from '#/lib/constants'
import {useHaptics} from '#/lib/haptics'
import {useRequireAuth} from '#/state/session'
import {atoms as a, useTheme} from '#/alf'
@@ -67,7 +67,7 @@ let RepostButton = ({
shape="round"
variant="ghost"
color="secondary"
- hitSlop={big ? HITSLOP_20 : HITSLOP_10}>
+ hitSlop={POST_CTRL_HITSLOP}>
{typeof repostCount !== 'undefined' && repostCount > 0 ? (