Browse Source

fix tabs

imwald
Silberengel 2 weeks ago
parent
commit
e3fb460f20
  1. 4
      package-lock.json
  2. 2
      package.json
  3. 11
      src/components/KindFilter/index.tsx
  4. 2
      src/components/NormalFeed/index.tsx
  5. 11
      src/components/Tabs/index.tsx
  6. 26
      src/providers/DeepBrowsingProvider.tsx

4
package-lock.json generated

@ -1,12 +1,12 @@ @@ -1,12 +1,12 @@
{
"name": "imwald",
"version": "23.17.4",
"version": "23.17.5",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "imwald",
"version": "23.17.4",
"version": "23.17.5",
"license": "MIT",
"dependencies": {
"@asciidoctor/core": "^3.0.4",

2
package.json

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
{
"name": "imwald",
"version": "23.17.4",
"version": "23.17.5",
"description": "Imwald — a user-friendly Nostr client focused on relay feed browsing, publications, and relay discovery",
"private": true,
"type": "module",

11
src/components/KindFilter/index.tsx

@ -18,6 +18,7 @@ import { @@ -18,6 +18,7 @@ import {
} from '@/lib/feed-kind-filter'
import { LIVE_ACTIVITY_KINDS } from '@/lib/live-activities'
import { cn } from '@/lib/utils'
import { useFontSize } from '@/providers/FontSizeProvider'
import { useKindFilterOrDefaults } from '@/providers/KindFilterProvider'
import { useScreenSize } from '@/providers/ScreenSizeProvider'
import { ListFilter } from 'lucide-react'
@ -59,6 +60,7 @@ export default function KindFilter({ @@ -59,6 +60,7 @@ export default function KindFilter({
}) {
const { t } = useTranslation()
const { isSmallScreen } = useScreenSize()
const { fontSize } = useFontSize()
const {
showKinds: savedShowKinds,
showKind1OPs: savedShowKind1OPs,
@ -179,7 +181,14 @@ export default function KindFilter({ @@ -179,7 +181,14 @@ export default function KindFilter({
}}
>
<ListFilter className="size-3.5 shrink-0" />
<span className="ml-1 hidden min-[352px]:inline">{t('Filter')}</span>
<span
className={cn(
'ml-1 hidden',
isSmallScreen && fontSize === 'large' ? 'min-[400px]:inline' : 'min-[352px]:inline'
)}
>
{t('Filter')}
</span>
{isDifferentFromSaved && (
<div className="absolute size-1.5 rounded-full bg-primary left-6 top-1.5 ring-1 ring-background" />
)}

2
src/components/NormalFeed/index.tsx

@ -306,7 +306,7 @@ const NormalFeed = forwardRef<TNoteListRef, { @@ -306,7 +306,7 @@ const NormalFeed = forwardRef<TNoteListRef, {
/** Notes / Replies / Gallery switch, plus refresh + kind filter — on Wisp trending only the tool row (no mode tabs). */
const tabsElement = useMemo(() => {
const kindRowOptions = (
<div className="flex items-center gap-0">
<div className="flex shrink-0 flex-nowrap items-center gap-0">
{onSubHeaderRefresh != null && <RefreshButton onClick={onSubHeaderRefresh} />}
<KindFilter showKinds={showKinds} onShowKindsChange={handleShowKindsChange} />
{mergeFilterWithTabsRow ? (

11
src/components/Tabs/index.tsx

@ -130,18 +130,19 @@ export default function Tabs({ @@ -130,18 +130,19 @@ export default function Tabs({
<div
ref={containerRef}
className={cn(
'flex w-full min-w-0 items-end justify-between border-b bg-background px-1',
// Single row: flex-nowrap (Firefox grid + w-max/min-w-full wrapped the tool column). Tabs scroll in flex-1.
'flex w-full min-w-0 flex-nowrap items-end gap-0.5 border-b bg-background px-1 sm:gap-1',
pinnedToLayout
? 'z-10'
: 'sticky top-12 z-30 transition-transform',
collapseOnDeepBrowse ? '-translate-y-[calc(100%+12rem)]' : ''
)}
>
<div className="min-w-0 w-0 flex-1">
<div className="min-h-0 min-w-0 flex-1 basis-0 overflow-x-auto overscroll-x-contain scrollbar-hide">
<div
ref={tabsContainerRef}
role="tablist"
className="relative flex gap-0.5 overflow-x-auto overscroll-x-contain scrollbar-hide sm:gap-1"
className="relative inline-flex w-max max-w-none gap-0.5 sm:gap-1"
>
{tabs.map((tab, index) => (
<button
@ -155,7 +156,7 @@ export default function Tabs({ @@ -155,7 +156,7 @@ export default function Tabs({
className={cn(
'flex shrink-0 items-center justify-center gap-1.5 whitespace-nowrap rounded-lg border-0 bg-transparent px-1.5 py-1.5 text-center text-xs font-semibold shadow-none transition-colors sm:gap-2 sm:px-3 sm:py-2 sm:text-sm md:px-5 md:text-base',
'cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background',
value === tab.value ? '' : 'text-muted-foreground'
value === tab.value ? 'text-foreground' : 'text-muted-foreground'
)}
onClick={() => {
onTabChange?.(tab.value)
@ -176,7 +177,7 @@ export default function Tabs({ @@ -176,7 +177,7 @@ export default function Tabs({
</div>
</div>
{options ? (
<div className="flex shrink-0 items-center gap-0 py-1 pl-0.5">{options}</div>
<div className="flex shrink-0 flex-nowrap items-center gap-0 py-1 pl-0.5">{options}</div>
) : null}
</div>
)

26
src/providers/DeepBrowsingProvider.tsx

@ -29,10 +29,29 @@ export function DeepBrowsingProvider({ @@ -29,10 +29,29 @@ export function DeepBrowsingProvider({
(!scrollAreaRef ? window.scrollY : scrollAreaRef.current?.scrollTop) || 0
)
const [lastScrollTop, setLastScrollTop] = useState(lastScrollTopRef.current)
/**
* Chrome (especially installed PWA) fires scroll when we restore `scrollTop` programmatically.
* That one-shot jump looks like "deep browse" and hid sticky tab rows via translate. Firefox often
* does not surface the same scroll event pattern on restore.
*/
const ignoreScrollForDeepBrowseRef = useRef(true)
useEffect(() => {
if (!active) return
ignoreScrollForDeepBrowseRef.current = true
setDeepBrowsing(false)
const syncScrollTop = () => {
const scrollTop = (!scrollAreaRef ? window.scrollY : scrollAreaRef.current?.scrollTop) || 0
lastScrollTopRef.current = scrollTop
setLastScrollTop(scrollTop)
}
syncScrollTop()
const graceTimer = window.setTimeout(() => {
ignoreScrollForDeepBrowseRef.current = false
syncScrollTop()
}, 150)
let rafId: number | null = null
const handleScroll = () => {
// Use requestAnimationFrame to throttle scroll updates and prevent scroll-linked positioning warnings
@ -43,6 +62,12 @@ export function DeepBrowsingProvider({ @@ -43,6 +62,12 @@ export function DeepBrowsingProvider({
const diff = scrollTop - lastScrollTopRef.current
lastScrollTopRef.current = scrollTop
setLastScrollTop(scrollTop)
if (ignoreScrollForDeepBrowseRef.current) {
rafId = null
return
}
if (scrollTop <= 800) {
setDeepBrowsing(false)
rafId = null
@ -62,6 +87,7 @@ export function DeepBrowsingProvider({ @@ -62,6 +87,7 @@ export function DeepBrowsingProvider({
target?.addEventListener('scroll', handleScroll, { passive: true })
return () => {
window.clearTimeout(graceTimer)
target?.removeEventListener('scroll', handleScroll)
if (rafId !== null) {
cancelAnimationFrame(rafId)

Loading…
Cancel
Save