[Threadgate] Tweak threadgate buttons (#9173)

* tweak in-post threadgate button

* tweak composer threadgate button

* reduce date length slightly

* pressed styles

* make date length depend on breakpoint

* add chevron to label btn

* add tiny chevron, special-case button icon width

* [Threadgate] Add hint (#9350)

* get tooltip working on web

* add compatibility layer for working in iOS sheets

* add timeout to profile tooltip now that it appears instantly

* rm debug code

* Update ThreadgateBtn.tsx

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>

* remeasure when keyboard changes

---------

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>

* fix types

* [Threadgate] Refresh dialog (#9342)

* wip new ui

* update threadgate dialog with new designs

* restore nobody option

* relayout android sheet when ratio changes

* fix ratio changing case in bottom sheet

* timebox reached, use setTimeout

* update panel styles

* missing imports

* extract out Panel

* tweak layout animation

* fix icon color

* use same color mechamism for icon as text

* restore the header

* refreshed toggle styles (#9343)

* [Threadgate] Persist settings (#9341)

* add persist toggle to threadgate dialog

* move state back down

* sort out spacing

* wire up query

* @surfdude29 tweaks

* use tiny chevron in WhoCanReply

* wait for prefetch before opening

* move Panel into the Toggle namespace

* default -> pref

* use medium date length

* rm hover state from web selects, fix border radius

* fix key issue in Selects

---------

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>
This commit is contained in:
Samuel Newman
2025-11-14 16:21:37 +02:00
committed by GitHub
parent b56ee74cdb
commit b422765aed
27 changed files with 1057 additions and 468 deletions
+43 -16
View File
@@ -1,4 +1,4 @@
import {Fragment, useMemo} from 'react'
import {Fragment, useMemo, useRef} from 'react'
import {
Keyboard,
Platform,
@@ -22,7 +22,7 @@ import {
type ThreadgateAllowUISetting,
threadgateViewToAllowUISetting,
} from '#/state/queries/threadgate'
import {atoms as a, useTheme, web} from '#/alf'
import {atoms as a, native, useTheme, web} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
import {useDialogControl} from '#/components/Dialog'
@@ -30,13 +30,13 @@ import {
PostInteractionSettingsDialog,
usePrefetchPostInteractionSettings,
} from '#/components/dialogs/PostInteractionSettingsDialog'
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 {TinyChevronBottom_Stroke2_Corner0_Rounded as TinyChevronDownIcon} from '#/components/icons/Chevron'
import {CircleBanSign_Stroke2_Corner0_Rounded as CircleBanSignIcon} from '#/components/icons/CircleBanSign'
import {Earth_Stroke2_Corner0_Rounded as EarthIcon} from '#/components/icons/Globe'
import {Group3_Stroke2_Corner0_Rounded as GroupIcon} from '#/components/icons/Group'
import {InlineLinkText} from '#/components/Link'
import {Text} from '#/components/Typography'
import * as bsky from '#/types/bsky'
import {PencilLine_Stroke2_Corner0_Rounded as PencilLine} from './icons/Pencil'
interface WhoCanReplyProps {
post: AppBskyFeedDefs.PostView
@@ -69,6 +69,11 @@ export function WhoCanReply({post, isThreadAuthor, style}: WhoCanReplyProps) {
postUri: post.uri,
rootPostUri: rootUri,
})
const prefetchPromise = useRef<Promise<void>>(Promise.resolve())
const prefetch = () => {
prefetchPromise.current = prefetchPostInteractionSettings()
}
const anyoneCanReply =
settings.length === 1 && settings[0].type === 'everybody'
@@ -84,7 +89,14 @@ export function WhoCanReply({post, isThreadAuthor, style}: WhoCanReplyProps) {
Keyboard.dismiss()
}
if (isThreadAuthor) {
editDialogControl.open()
// wait on prefetch if it manages to resolve in under 200ms
// otherwise, proceed immediately and show the spinner -sfn
Promise.race([
prefetchPromise.current,
new Promise(res => setTimeout(res, 200)),
]).finally(() => {
editDialogControl.open()
})
} else {
infoDialogControl.open()
}
@@ -100,18 +112,27 @@ export function WhoCanReply({post, isThreadAuthor, style}: WhoCanReplyProps) {
{...(isThreadAuthor
? Platform.select({
web: {
onHoverIn: prefetchPostInteractionSettings,
onHoverIn: prefetch,
},
native: {
onPressIn: prefetchPostInteractionSettings,
onPressIn: prefetch,
},
})
: {})}
hitSlop={HITSLOP_10}>
{({hovered}) => (
<View style={[a.flex_row, a.align_center, a.gap_xs, style]}>
{({hovered, focused, pressed}) => (
<View
style={[
a.flex_row,
a.align_center,
a.gap_xs,
(hovered || focused || pressed) && native({opacity: 0.5}),
style,
]}>
<Icon
color={t.palette.contrast_400}
color={
isThreadAuthor ? t.palette.primary_500 : t.palette.contrast_400
}
width={16}
settings={settings}
/>
@@ -119,14 +140,16 @@ export function WhoCanReply({post, isThreadAuthor, style}: WhoCanReplyProps) {
style={[
a.text_sm,
a.leading_tight,
t.atoms.text_contrast_medium,
hovered && a.underline,
isThreadAuthor
? {color: t.palette.primary_500}
: t.atoms.text_contrast_medium,
(hovered || focused || pressed) && web(a.underline),
]}>
{description}
</Text>
{isThreadAuthor && (
<PencilLine width={12} fill={t.palette.primary_500} />
<TinyChevronDownIcon width={8} fill={t.palette.primary_500} />
)}
</View>
)}
@@ -164,7 +187,11 @@ function Icon({
settings.length === 0 ||
settings.every(setting => setting.type === 'everybody')
const isNobody = !!settings.find(gate => gate.type === 'nobody')
const IconComponent = isEverybody ? Earth : isNobody ? CircleBanSign : Group
const IconComponent = isEverybody
? EarthIcon
: isNobody
? CircleBanSignIcon
: GroupIcon
return <IconComponent fill={color} width={width} />
}