Browse Source

bug-fixes

imwald
Silberengel 3 weeks ago
parent
commit
cc409a9288
  1. 24
      src/components/AdvancedEventLab/AdvancedEventLabDialog.tsx
  2. 10
      src/components/EventPowLabel/index.tsx
  3. 7
      src/components/KindFilter/index.tsx
  4. 5
      src/components/LiveActivitiesStrip.tsx
  5. 47
      src/components/NormalFeed/index.tsx
  6. 4
      src/components/Note/index.tsx
  7. 26
      src/components/NoteList/index.tsx
  8. 2
      src/components/RefreshButton/index.tsx
  9. 2
      src/components/ReplyNote/index.tsx
  10. 19
      src/components/Tabs/index.tsx
  11. 6
      src/components/ZapDialog/PostPaymentMessagePrompt.tsx
  12. 20
      src/components/ZapDialog/PublicMessageForm.tsx
  13. 22
      src/components/ZapDialog/SuperchatRequestForm.tsx
  14. 7
      src/constants.ts
  15. 3
      src/lib/console-log-buffer.ts
  16. 31
      src/lib/error-suppression.ts
  17. 12
      src/services/client-events.service.ts

24
src/components/AdvancedEventLab/AdvancedEventLabDialog.tsx

@ -705,19 +705,19 @@ export default function AdvancedEventLabDialog({ @@ -705,19 +705,19 @@ export default function AdvancedEventLabDialog({
minHeight: '12rem',
fontFamily: 'var(--font-mono, ui-monospace, monospace)'
},
// LanguageTool hits: drop default thin SVG underline, use thick wavy line (see `LT_GRAMMAR_MARK_CLASS`).
// LanguageTool hits: subtle wavy underline (default CM lint SVG is hidden).
[`.cm-lintRange.${LT_GRAMMAR_MARK_CLASS}`]: {
backgroundImage: 'none',
paddingBottom: '2px',
paddingBottom: '1px',
textDecoration: 'underline',
textDecorationSkipInk: 'none',
textDecorationStyle: 'wavy',
textDecorationColor: '#ea580c',
textDecorationThickness: '3px',
textUnderlineOffset: '3px'
textDecorationColor: 'rgba(234, 88, 12, 0.45)',
textDecorationThickness: '1.5px',
textUnderlineOffset: '2px'
},
[`.cm-lintRange-active.${LT_GRAMMAR_MARK_CLASS}`]: {
backgroundColor: 'rgba(234, 88, 12, 0.22)'
backgroundColor: 'rgba(234, 88, 12, 0.1)'
}
}),
EditorView.updateListener.of((update) => {
@ -742,16 +742,16 @@ export default function AdvancedEventLabDialog({ @@ -742,16 +742,16 @@ export default function AdvancedEventLabDialog({
EditorView.theme({
[`.cm-lintRange.${LT_GRAMMAR_MARK_CLASS}`]: {
backgroundImage: 'none',
paddingBottom: '2px',
paddingBottom: '1px',
textDecoration: 'underline',
textDecorationSkipInk: 'none',
textDecorationStyle: 'wavy',
textDecorationColor: '#fdba74',
textDecorationThickness: '3px',
textUnderlineOffset: '3px'
textDecorationColor: 'rgba(251, 146, 60, 0.5)',
textDecorationThickness: '1.5px',
textUnderlineOffset: '2px'
},
[`.cm-lintRange-active.${LT_GRAMMAR_MARK_CLASS}`]: {
backgroundColor: 'rgba(251, 146, 60, 0.28)'
backgroundColor: 'rgba(251, 146, 60, 0.12)'
}
})
)
@ -988,7 +988,7 @@ export default function AdvancedEventLabDialog({ @@ -988,7 +988,7 @@ export default function AdvancedEventLabDialog({
<AdvancedEventLabMarkupToolbar markupMode={markupMode} viewRef={markupView} sliceRef={sliceRef} />
<div
ref={markupHost}
className="min-h-[24rem] h-[min(84vh,56rem)] overflow-hidden rounded-md border bg-muted/20"
className="min-h-[16rem] h-[min(56vh,37.5rem)] overflow-hidden rounded-md border bg-muted/20"
/>
</TabsContent>

10
src/components/EventPowLabel/index.tsx

@ -20,16 +20,14 @@ export default function EventPowLabel({ @@ -20,16 +20,14 @@ export default function EventPowLabel({
return (
<span
className={cn(
'inline-flex shrink-0 items-center gap-1 rounded-md border-2 border-amber-500/90',
'bg-gradient-to-r from-amber-400/40 to-yellow-300/30 px-2 py-0.5',
'text-xs font-bold uppercase tracking-wide text-amber-950 shadow-sm',
'ring-2 ring-amber-400/35 dark:border-amber-400/80 dark:from-amber-500/30 dark:to-yellow-500/20',
'dark:text-amber-50 dark:ring-amber-300/25',
'inline-flex shrink-0 items-center gap-0.5 rounded border border-amber-500/20',
'bg-amber-500/[0.08] px-1 py-px text-[10px] font-medium leading-none text-amber-800/75',
'dark:border-amber-400/15 dark:bg-amber-500/10 dark:text-amber-200/65',
className
)}
title={t('Proof of Work')}
>
<Pickaxe className="size-3.5 shrink-0" strokeWidth={2.5} aria-hidden />
<Pickaxe className="size-2.5 shrink-0 opacity-60" strokeWidth={2} aria-hidden />
{t('POW {{difficulty}}', { difficulty })}
</span>
)

7
src/components/KindFilter/index.tsx

@ -166,8 +166,9 @@ export default function KindFilter({ @@ -166,8 +166,9 @@ export default function KindFilter({
<Button
variant="ghost"
size="titlebar-icon"
aria-label={t('Filter')}
className={cn(
'relative w-fit px-2 h-8 text-xs focus:text-foreground',
'relative h-8 w-fit shrink-0 px-1.5 text-xs focus:text-foreground',
!isDifferentFromSaved && !feedKindFilterBypass && 'text-muted-foreground',
feedKindFilterBypass && 'text-amber-600 dark:text-amber-400'
)}
@ -177,8 +178,8 @@ export default function KindFilter({ @@ -177,8 +178,8 @@ export default function KindFilter({
}
}}
>
<ListFilter className="size-2.5" />
<span className="ml-1 text-xs">{t('Filter')}</span>
<ListFilter className="size-3.5 shrink-0" />
<span className="ml-1 hidden min-[22rem]:inline">{t('Filter')}</span>
{isDifferentFromSaved && (
<div className="absolute size-1.5 rounded-full bg-primary left-6 top-1.5 ring-1 ring-background" />
)}

5
src/components/LiveActivitiesStrip.tsx

@ -65,6 +65,9 @@ export default function LiveActivitiesStrip({ placement }: { placement: TPlaceme @@ -65,6 +65,9 @@ export default function LiveActivitiesStrip({ placement }: { placement: TPlaceme
const onSwipePointerDown = useCallback(
(e: React.PointerEvent<HTMLDivElement>) => {
if (placement !== 'mobile' || items.length <= 1) return
// Taps on the note button / external link must not capture — capture retargets pointerup
// away from the button and suppresses its click (note never opens in the secondary panel).
if ((e.target as HTMLElement).closest('button, a, [role="tab"]')) return
swipeGrabRef.current = { x: e.clientX, y: e.clientY, pointerId: e.pointerId }
e.currentTarget.setPointerCapture(e.pointerId)
},
@ -157,6 +160,7 @@ export default function LiveActivitiesStrip({ placement }: { placement: TPlaceme @@ -157,6 +160,7 @@ export default function LiveActivitiesStrip({ placement }: { placement: TPlaceme
>
<button
type="button"
onPointerDown={(e) => e.stopPropagation()}
onClick={openLiveNote}
className={cn(
'flex min-w-0 flex-1 gap-2 rounded-md text-left transition-colors hover:bg-muted/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
@ -195,6 +199,7 @@ export default function LiveActivitiesStrip({ placement }: { placement: TPlaceme @@ -195,6 +199,7 @@ export default function LiveActivitiesStrip({ placement }: { placement: TPlaceme
)}
title={t('liveActivities.openJoinPageTitle')}
aria-label={t('liveActivities.openJoinPageTitle')}
onPointerDown={(e) => e.stopPropagation()}
onClick={(e) => e.stopPropagation()}
>
<ExternalLink className="size-4 shrink-0" aria-hidden />

47
src/components/NormalFeed/index.tsx

@ -4,7 +4,7 @@ import { RefreshButton } from '@/components/RefreshButton' @@ -4,7 +4,7 @@ import { RefreshButton } from '@/components/RefreshButton'
import Tabs, { TabDefinition } from '@/components/Tabs'
import { useGlobalRelayBootstrapDefaults } from '@/hooks/use-global-relay-bootstrap-defaults'
import { useKindFilterOrDefaults } from '@/providers/KindFilterProvider'
import { PROFILE_MEDIA_TAB_KINDS, FAST_READ_RELAY_URLS } from '@/constants'
import { HOME_GALLERY_TAB_KINDS, FAST_READ_RELAY_URLS } from '@/constants'
import { isWispTrendingNotesRelayUrl } from '@/lib/wisp-trending-relay'
import type { TPrimaryPageName } from '@/PageManager'
import { TFeedSubRequest, TNoteListMode } from '@/types'
@ -23,7 +23,7 @@ import { @@ -23,7 +23,7 @@ import {
import KindFilter from '../KindFilter'
/**
* Home Gallery: favorites (or chip relays) first, then {@link FAST_READ_RELAY_URLS} so NIP-71 / picture / voice
* Home Gallery: favorites (or chip relays) first, then {@link FAST_READ_RELAY_URLS} so NIP-71 / picture
* events are not starved when the users relay set is mostly text timelines. Deduped by normalized URL.
*/
function galleryRelayUrlsMergedWithReadLayer(
@ -193,7 +193,7 @@ const NormalFeed = forwardRef<TNoteListRef, { @@ -193,7 +193,7 @@ const NormalFeed = forwardRef<TNoteListRef, {
setFeedFilterTabRowHost((prev) => (Object.is(prev, node) ? prev : node))
}, [])
const MEDIA_KINDS = useMemo(() => [...PROFILE_MEDIA_TAB_KINDS], [])
const MEDIA_KINDS = useMemo(() => [...HOME_GALLERY_TAB_KINDS], [])
/** Every shard URL is a nostrarchives Wisp “trending notes” stream — replies/gallery tabs are not applicable. */
const isWispTrendingOnlyFeed = useMemo(
@ -295,17 +295,25 @@ const NormalFeed = forwardRef<TNoteListRef, { @@ -295,17 +295,25 @@ const NormalFeed = forwardRef<TNoteListRef, {
/** Include kind picker deps for single-relay chips (kindless REQ + client-side kinds). */
const subHeaderFilterDepsKey = `${allowKindlessRelayExplore ? 'kle' : 'std'}|${showKindsKey}|${feedKindFilterBypass}|${showAllKindsProp ? 'allProp' : 'k'}`
const renderTabsInFeed = !(isMainFeed && setSubHeader) && !allowKindlessRelayExplore
const mergeFilterWithTabsRow =
showFeedClientFilter && ((isMainFeed && !!setSubHeader) || renderTabsInFeed)
/** 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-1">
<div className="flex items-center gap-0">
{onSubHeaderRefresh != null && <RefreshButton onClick={onSubHeaderRefresh} />}
<KindFilter showKinds={showKinds} onShowKindsChange={handleShowKindsChange} />
{mergeFilterWithTabsRow ? (
<div ref={onFeedFilterTabRowSlotRef} className="flex items-center" />
) : null}
</div>
)
if (isMainFeed && isWispTrendingOnlyFeed) {
return (
<div className="flex w-full min-w-0 items-center justify-end gap-1 py-1">{kindRowOptions}</div>
<div className="flex w-full min-w-0 items-center justify-end gap-0 py-1">{kindRowOptions}</div>
)
}
return (
@ -324,13 +332,12 @@ const NormalFeed = forwardRef<TNoteListRef, { @@ -324,13 +332,12 @@ const NormalFeed = forwardRef<TNoteListRef, {
handleListModeChange,
showKinds,
onSubHeaderRefresh,
handleShowKindsChange
handleShowKindsChange,
mergeFilterWithTabsRow
])
const renderTabsInFeed = !(isMainFeed && setSubHeader) && !allowKindlessRelayExplore
const mergeFilterWithTabsRow =
showFeedClientFilter && ((isMainFeed && !!setSubHeader) || renderTabsInFeed)
const tabRowChromeClass =
'w-full min-w-0 border-b border-border/80 bg-background/95 pb-1.5 pt-0.5 backdrop-blur supports-[backdrop-filter]:bg-background/80'
/**
* Push the tab row into {@link PrimaryPageLayout} subHeader. Use `useEffect` (not `useLayoutEffect`) so
@ -345,15 +352,7 @@ const NormalFeed = forwardRef<TNoteListRef, { @@ -345,15 +352,7 @@ const NormalFeed = forwardRef<TNoteListRef, {
useEffect(() => {
if (!isMainFeed || !setSubHeader) return
if (mergeFilterWithTabsRow) {
setSubHeader(
<div className="flex w-full min-w-0 flex-wrap items-end gap-x-2 gap-y-1 border-b border-border/80 bg-background/95 pb-1.5 pt-0.5 backdrop-blur supports-[backdrop-filter]:bg-background/80">
<div className="min-w-0 flex-1">{tabsElement}</div>
<div
ref={onFeedFilterTabRowSlotRef}
className="flex shrink-0 flex-col items-end self-start"
/>
</div>
)
setSubHeader(<div className={tabRowChromeClass}>{tabsElement}</div>)
} else {
setSubHeader(tabsElement)
}
@ -376,15 +375,7 @@ const NormalFeed = forwardRef<TNoteListRef, { @@ -376,15 +375,7 @@ const NormalFeed = forwardRef<TNoteListRef, {
<>
{renderTabsInFeed &&
(mergeFilterWithTabsRow ? (
<div className="sticky top-0 z-20 border-b border-border/80 bg-background/95 pb-1.5 pt-0.5 backdrop-blur supports-[backdrop-filter]:bg-background/80">
<div className="flex w-full min-w-0 flex-wrap items-end gap-x-2 gap-y-1">
<div className="min-w-0 flex-1">{tabsElement}</div>
<div
ref={onFeedFilterTabRowSlotRef}
className="flex shrink-0 flex-col items-end self-start"
/>
</div>
</div>
<div className={cn('sticky top-0 z-20', tabRowChromeClass)}>{tabsElement}</div>
) : (
tabsElement
))}

4
src/components/Note/index.tsx

@ -693,6 +693,7 @@ export default function Note({ @@ -693,6 +693,7 @@ export default function Note({
className="shrink-0 text-sm text-muted-foreground"
short={isSmallScreen}
/>
<EventPowLabel event={event} />
</div>
) : isSyntheticRssParent ? (
<>
@ -746,6 +747,7 @@ export default function Note({ @@ -746,6 +747,7 @@ export default function Note({
className="shrink-0"
short={isSmallScreen}
/>
<EventPowLabel event={event} />
</div>
</div>
) : (
@ -763,6 +765,7 @@ export default function Note({ @@ -763,6 +765,7 @@ export default function Note({
className="shrink-0"
short={isSmallScreen}
/>
<EventPowLabel event={event} />
</span>
</div>
)}
@ -812,7 +815,6 @@ export default function Note({ @@ -812,7 +815,6 @@ export default function Note({
)}
</div>
</div>
<EventPowLabel event={event} className="mt-1" />
{webReactionParentUrl ? (
<div className="mt-2 not-prose max-w-full" data-parent-note-preview>
<WebPreview url={webReactionParentUrl} className="w-full" />

26
src/components/NoteList/index.tsx

@ -4,7 +4,8 @@ import { @@ -4,7 +4,8 @@ import {
ExtendedKind,
FAST_READ_RELAY_URLS,
FIRST_RELAY_RESULT_GRACE_MS,
PROFILE_MEDIA_TAB_KINDS,
HOME_GALLERY_TAB_KINDS,
HOME_GALLERY_TAB_KIND_SET,
SINGLE_RELAY_KINDLESS_EOSE_TIMEOUT_MS,
SINGLE_RELAY_KINDLESS_REQ_LIMIT
} from '@/constants'
@ -1369,6 +1370,10 @@ const NoteList = forwardRef( @@ -1369,6 +1370,10 @@ const NoteList = forwardRef(
if (extraShouldHideEvent?.(evt)) return true
if (homeFeedListMode === 'media' && !HOME_GALLERY_TAB_KIND_SET.has(evt.kind)) {
return true
}
if (
homeFeedActiveSeenOnAllowlist &&
homeFeedListMode === 'posts' &&
@ -2607,7 +2612,7 @@ const NoteList = forwardRef( @@ -2607,7 +2612,7 @@ const NoteList = forwardRef(
}
try {
const hits = client.eventService.listSessionEventsByKinds([...PROFILE_MEDIA_TAB_KINDS], {
const hits = client.eventService.listSessionEventsByKinds([...HOME_GALLERY_TAB_KINDS], {
limit: 800
})
mergeLayer(hits as Event[], 'gallery_session_local')
@ -2619,7 +2624,7 @@ const NoteList = forwardRef( @@ -2619,7 +2624,7 @@ const NoteList = forwardRef(
try {
const since = dayjs().subtract(120, 'day').unix()
const rows = await indexedDb.scanEventArchiveByKinds({
kinds: [...PROFILE_MEDIA_TAB_KINDS],
kinds: [...HOME_GALLERY_TAB_KINDS],
since,
maxRowsScanned: 28_000,
maxMatches: 220
@ -3111,7 +3116,7 @@ const NoteList = forwardRef( @@ -3111,7 +3116,7 @@ const NoteList = forwardRef(
...(runtimeSnapshot.rawCount === 0
? {
emptyHint:
'All sub-batches returned 0 events: relays may not index these kinds for this author, the query may have timed out before slow relays EOSEd, or posts are kind 1 with links (this tab uses native media kinds only: picture, NIP-71 video regular/addressable, voice).'
'All sub-batches returned 0 events: relays may not index these kinds for this author, the query may have timed out before slow relays EOSEd, or posts are kind 1 with links (Gallery uses kinds 20, 21, 22, 34235 only).'
}
: {})
})
@ -4485,7 +4490,7 @@ const NoteList = forwardRef( @@ -4485,7 +4490,7 @@ const NoteList = forwardRef(
useFeedFilterTabRowPortal && feedClientFilterTabRowHost
const feedClientFilterPanelSurfaceClass = feedClientFilterPanelPortalMode
? 'absolute top-full right-0 z-50 mt-1 w-[min(100vw-1rem,28rem)] max-w-[calc(100vw-1rem)] space-y-3 rounded-lg border border-border bg-background p-3 shadow-lg'
? 'space-y-3 border-b border-border/80 bg-background/95 px-2 py-3 backdrop-blur supports-[backdrop-filter]:bg-background/80'
: 'space-y-3 border-t border-border/60 px-2 py-3'
const feedClientFilterSectionClass = 'space-y-2 rounded-md border border-border/60 bg-muted/25 p-2.5'
@ -4685,10 +4690,7 @@ const NoteList = forwardRef( @@ -4685,10 +4690,7 @@ const NoteList = forwardRef(
) : null
const feedClientFilterChrome = feedClientFilterPanelPortalMode ? (
<div className="relative flex items-center gap-1">
{feedClientFilterToggleButton}
{feedClientFilterPanel}
</div>
feedClientFilterToggleButton
) : (
<>
<div className="flex items-center gap-1">{feedClientFilterToggleButton}</div>
@ -4696,6 +4698,10 @@ const NoteList = forwardRef( @@ -4696,6 +4698,10 @@ const NoteList = forwardRef(
</>
)
/** Tab-row portal: toggle lives in the header; panel expands in-flow above the list. */
const feedClientFilterPanelInList =
feedClientFilterPanelPortalMode ? feedClientFilterPanel : null
const feedClientFilterBarEmbedded = (
<div className="sticky top-0 z-20 border-b border-border/80 bg-background/95 px-1 py-1 backdrop-blur supports-[backdrop-filter]:bg-background/80">
{feedClientFilterChrome}
@ -4892,6 +4898,7 @@ const NoteList = forwardRef( @@ -4892,6 +4898,7 @@ const NoteList = forwardRef(
</div>
) : null}
{showFeedClientFilter ? feedClientFilterBar : null}
{feedClientFilterPanelInList}
{list}
</div>
</PullToRefresh>
@ -4906,6 +4913,7 @@ const NoteList = forwardRef( @@ -4906,6 +4913,7 @@ const NoteList = forwardRef(
</div>
) : null}
{showFeedClientFilter ? feedClientFilterBar : null}
{feedClientFilterPanelInList}
{list}
</div>
)}

2
src/components/RefreshButton/index.tsx

@ -46,7 +46,7 @@ export function RefreshButton({ @@ -46,7 +46,7 @@ export function RefreshButton({
onClick()
setTimeout(() => setRefreshing(false), 500)
}}
className="text-muted-foreground focus:text-foreground [&_svg]:size-3 h-8 px-2 text-xs"
className="h-8 shrink-0 px-1.5 text-muted-foreground focus:text-foreground [&_svg]:size-3"
>
{refreshing ? (
<Skeleton className="size-3 shrink-0 rounded-sm" aria-hidden />

2
src/components/ReplyNote/index.tsx

@ -142,12 +142,12 @@ export default function ReplyNote({ @@ -142,12 +142,12 @@ export default function ReplyNote({
className="shrink-0"
short={isSmallScreen}
/>
<EventPowLabel event={event} />
</div>
</div>
</div>
<NoteOptions event={event} className="shrink-0 [&_svg]:size-5" />
</div>
<EventPowLabel event={event} className="mt-0.5" />
{webReactionParentUrl ? (
<div className="not-prose mt-1.5 max-w-full" data-parent-note-preview>
<WebPreview url={webReactionParentUrl} className="w-full" />

19
src/components/Tabs/index.tsx

@ -40,7 +40,7 @@ export default function Tabs({ @@ -40,7 +40,7 @@ export default function Tabs({
const activeTab = tabRefs.current[activeIndex]
const tabsContainer = tabsContainerRef.current
const { offsetWidth, offsetLeft, offsetHeight } = activeTab
const padding = 24 // 12px padding on each side
const padding = Math.min(24, Math.max(8, offsetWidth * 0.12))
// Get the container's top position relative to the viewport
const containerTop = tabsContainer.getBoundingClientRect().top
@ -124,15 +124,15 @@ export default function Tabs({ @@ -124,15 +124,15 @@ export default function Tabs({
<div
ref={containerRef}
className={cn(
'sticky flex justify-between top-12 bg-background z-30 px-1 w-full transition-transform border-b',
'sticky top-12 z-30 flex w-full min-w-0 items-end justify-between border-b bg-background px-1 transition-transform',
deepBrowsing && lastScrollTop > threshold ? '-translate-y-[calc(100%+12rem)]' : ''
)}
>
<div className="flex-1 w-0 min-w-0">
<div className="min-w-0 w-0 flex-1">
<div
ref={tabsContainerRef}
role="tablist"
className="flex relative gap-1 overflow-x-auto scrollbar-hide"
className="relative flex gap-0.5 overflow-x-auto overscroll-x-contain scrollbar-hide sm:gap-1"
>
{tabs.map((tab, index) => (
<button
@ -144,9 +144,8 @@ export default function Tabs({ @@ -144,9 +144,8 @@ export default function Tabs({
tabRefs.current[index] = el
}}
className={cn(
'text-center py-2 px-2 sm:px-4 md:px-6 font-semibold whitespace-nowrap rounded-lg text-xs sm:text-sm md:text-base shrink-0 flex items-center gap-2 justify-center',
'bg-transparent border-0 shadow-none cursor-pointer transition-colors',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background',
'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'
)}
onClick={() => {
@ -158,7 +157,7 @@ export default function Tabs({ @@ -158,7 +157,7 @@ export default function Tabs({
</button>
))}
<div
className="absolute h-1 bg-primary rounded-full transition-all duration-500"
className="absolute h-1 rounded-full bg-primary transition-all duration-500"
style={{
width: `${indicatorStyle.width}px`,
left: `${indicatorStyle.left}px`,
@ -167,7 +166,9 @@ export default function Tabs({ @@ -167,7 +166,9 @@ export default function Tabs({
/>
</div>
</div>
{options && <div className="py-1 flex items-center shrink-0 gap-1">{options}</div>}
{options ? (
<div className="flex shrink-0 items-center gap-0 py-1 pl-0.5">{options}</div>
) : null}
</div>
)
}

6
src/components/ZapDialog/PostPaymentMessagePrompt.tsx

@ -140,7 +140,7 @@ export default function PostPaymentMessagePrompt({ @@ -140,7 +140,7 @@ export default function PostPaymentMessagePrompt({
<DialogDescription className="sr-only">{t('Post payment prompt label')}</DialogDescription>
) : null}
</DrawerHeader>
<div className="min-w-0 overflow-x-hidden px-0 pb-4">{body}</div>
<div className="min-w-0 px-px pb-4">{body}</div>
{step === 'choice' ? (
<DrawerFooter className={choiceFooterClass}>{choiceActions}</DrawerFooter>
) : null}
@ -152,7 +152,7 @@ export default function PostPaymentMessagePrompt({ @@ -152,7 +152,7 @@ export default function PostPaymentMessagePrompt({
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent
className="flex w-[calc(100vw-1.25rem)] max-w-lg min-w-0 flex-col gap-4 overflow-hidden sm:max-w-lg"
className="flex w-[calc(100vw-1.25rem)] max-w-lg min-w-0 flex-col gap-4 max-h-[min(92dvh,900px)] overflow-y-auto sm:max-w-lg"
onOpenAutoFocus={(e) => e.preventDefault()}
>
<DialogHeader className="min-w-0 shrink-0">
@ -161,7 +161,7 @@ export default function PostPaymentMessagePrompt({ @@ -161,7 +161,7 @@ export default function PostPaymentMessagePrompt({
<DialogDescription className="sr-only">{t('Post payment prompt label')}</DialogDescription>
) : null}
</DialogHeader>
<div className="min-w-0 overflow-x-hidden">{body}</div>
<div className="min-w-0 px-px">{body}</div>
{step === 'choice' ? (
<DialogFooter className={choiceFooterClass}>{choiceActions}</DialogFooter>
) : null}

20
src/components/ZapDialog/PublicMessageForm.tsx

@ -94,15 +94,17 @@ export default function PublicMessageForm({ @@ -94,15 +94,17 @@ export default function PublicMessageForm({
return (
<div className="min-w-0">
<p className="text-sm text-muted-foreground">{t('Tip notice prompt description')}</p>
<Textarea
ref={textareaRef}
value={message}
onChange={(e) => setMessage(e.target.value)}
disabled={sending}
rows={6}
className="mt-3 min-h-[10rem] resize-y text-sm leading-relaxed"
aria-label={t('Tip notice prompt description')}
/>
<div className="mt-3 min-w-0 max-w-full">
<Textarea
ref={textareaRef}
value={message}
onChange={(e) => setMessage(e.target.value)}
disabled={sending}
rows={6}
className="min-h-[10rem] w-full max-w-full resize-y box-border text-sm leading-relaxed focus-visible:ring-inset"
aria-label={t('Tip notice prompt description')}
/>
</div>
{previewEvent ? (
<div className="mt-4 min-w-0">
<p className="text-xs font-medium text-muted-foreground">{t('Preview')}</p>

22
src/components/ZapDialog/SuperchatRequestForm.tsx

@ -129,16 +129,18 @@ export default function SuperchatRequestForm({ @@ -129,16 +129,18 @@ export default function SuperchatRequestForm({
{t('Superchat estimated amount hint')}
</p>
</div>
<Textarea
ref={textareaRef}
value={message}
onChange={(e) => setMessage(e.target.value)}
disabled={sending}
rows={5}
className="mt-3 min-h-[8rem] resize-y text-sm leading-relaxed"
aria-label={t('Superchat message')}
placeholder={t('Superchat message placeholder')}
/>
<div className="mt-3 min-w-0 max-w-full">
<Textarea
ref={textareaRef}
value={message}
onChange={(e) => setMessage(e.target.value)}
disabled={sending}
rows={5}
className="min-h-[8rem] w-full max-w-full resize-y box-border text-sm leading-relaxed focus-visible:ring-inset"
aria-label={t('Superchat message')}
placeholder={t('Superchat message placeholder')}
/>
</div>
<div className="mt-4 grid gap-2">
<Label htmlFor="superchat-pow">{t('Proof of Work (difficulty {{minPow}})', { minPow })}</Label>
<Slider

7
src/constants.ts

@ -972,7 +972,14 @@ export const PROFILE_MEDIA_TAB_KINDS: readonly number[] = [ @@ -972,7 +972,14 @@ export const PROFILE_MEDIA_TAB_KINDS: readonly number[] = [
ExtendedKind.VOICE
]
/** Home feed Gallery tab: picture + NIP-71 video only (20, 21, 22, 34235). */
export const HOME_GALLERY_TAB_KINDS: readonly number[] = [
ExtendedKind.PICTURE,
...NIP71_VIDEO_KINDS
]
const PROFILE_MEDIA_TAB_KIND_SET = new Set<number>(PROFILE_MEDIA_TAB_KINDS)
export const HOME_GALLERY_TAB_KIND_SET = new Set<number>(HOME_GALLERY_TAB_KINDS)
/**
* Kinds subscribed on the profile Posts tab only. Omits publication kinds and native media kinds so those

3
src/lib/console-log-buffer.ts

@ -82,6 +82,9 @@ function captureLog(type: string, ...args: unknown[]) { @@ -82,6 +82,9 @@ function captureLog(type: string, ...args: unknown[]) {
if (message.includes('NOTICE from')) {
return
}
if (import.meta.env.DEV && message.includes('[feed:')) {
return
}
buffer.push({ type, message, formattedParts, timestamp: Date.now() })
if (buffer.length > MAX_ENTRIES) {
buffer.splice(0, buffer.length - MAX_ENTRIES)

31
src/lib/error-suppression.ts

@ -103,6 +103,37 @@ function isExpectedDevAppNoise(message: string): boolean { @@ -103,6 +103,37 @@ function isExpectedDevAppNoise(message: string): boolean {
if (message.includes('[vite]') && (message.includes('connected') || message.includes('connecting'))) {
return true
}
if (message.includes('[feed:')) {
return true
}
if (
message.includes('[SpellsPage] Spell feed') ||
message.includes('[NIP-42] Auth accepted') ||
message.includes('[RelayInfo] NIP-11 received') ||
message.includes('[client] Prewarm:') ||
message.includes('[RssFeedSettingsPage] Loaded RSS feed list')
) {
return true
}
if (
message.includes('localhost:4869') ||
message.includes('127.0.0.1:4869') ||
message.includes('ws://localhost:4869')
) {
if (
message.includes('[PublishEvent]') ||
message.includes('[Publish]') ||
message.includes('[RelayOp]') ||
message.includes('connection failed') ||
message.includes('connection timed out') ||
message.includes('Local relay connection timeout')
) {
return true
}
}
if (message.includes('[FetchRelayLists] Network relay-list fetch exceeded budget')) {
return true
}
return false
}

12
src/services/client-events.service.ts

@ -232,11 +232,13 @@ export class EventService { @@ -232,11 +232,13 @@ export class EventService {
const snapshot = [...waiters]
this.sessionEventWaiters.delete(hexId)
for (const cb of snapshot) {
try {
cb()
} catch (e) {
logger.warn('[EventService] sessionEventWaiter failed', { hexId: hexId.slice(0, 8), e })
}
queueMicrotask(() => {
try {
cb()
} catch (e) {
logger.warn('[EventService] sessionEventWaiter failed', { hexId: hexId.slice(0, 8), e })
}
})
}
}

Loading…
Cancel
Save