Browse Source

bug-fixes

imwald
Silberengel 2 months ago
parent
commit
01be297154
  1. 4
      docker-compose.prod.yml
  2. 1
      nip66-cron/index.mjs
  3. 4
      package-lock.json
  4. 2
      package.json
  5. 60
      scripts/README-deploy.md
  6. 25
      scripts/build-and-push-prod.sh
  7. 24
      src/PageManager.tsx
  8. 12
      src/components/Explore/index.tsx
  9. 2
      src/components/FollowingFavoriteRelayList/index.tsx
  10. 6
      src/components/ImageGallery/index.tsx
  11. 6
      src/components/ImageWithLightbox/index.tsx
  12. 34
      src/components/NormalFeed/index.tsx
  13. 6
      src/components/Note/AsciidocArticle/AsciidocArticle.tsx
  14. 6
      src/components/Note/MarkdownArticle/MarkdownArticle.tsx
  15. 2
      src/components/RelaySimpleInfo/index.tsx
  16. 2
      src/components/ui/scroll-area.tsx
  17. 31
      src/layouts/PrimaryPageLayout/index.tsx
  18. 3
      src/lib/draft-event.ts
  19. 33
      src/pages/primary/ExplorePage/index.tsx
  20. 13
      src/pages/primary/NoteListPage/FeedButton.tsx

4
docker-compose.prod.yml

@ -28,9 +28,7 @@ services:
# NIP-66 relay monitor cron: publishes 30166 (relay discovery) and 10166 (announcement). # NIP-66 relay monitor cron: publishes 30166 (relay discovery) and 10166 (announcement).
# Starts and stops with the app. Requires NIP66_MONITOR_NSEC to do anything. # Starts and stops with the app. Requires NIP66_MONITOR_NSEC to do anything.
jumble-nip66-monitor: jumble-nip66-monitor:
build: image: silberengel/imwald-jumble-nip66-monitor:latest
context: ./nip66-cron
dockerfile: Dockerfile
container_name: imwald-jumble-nip66-monitor container_name: imwald-jumble-nip66-monitor
restart: unless-stopped restart: unless-stopped
environment: environment:

1
nip66-cron/index.mjs

@ -27,7 +27,6 @@ const DEFAULT_RELAYS_TO_MONITOR = [
] ]
const DEFAULT_PUBLISH_RELAYS = [ const DEFAULT_PUBLISH_RELAYS = [
'wss://theforest.nostr1.com',
'wss://thecitadel.nostr1.com', 'wss://thecitadel.nostr1.com',
'wss://relay.damus.io', 'wss://relay.damus.io',
'wss://relay.nostr.watch' 'wss://relay.nostr.watch'

4
package-lock.json generated

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

2
package.json

@ -1,6 +1,6 @@
{ {
"name": "jumble-imwald", "name": "jumble-imwald",
"version": "16.1.3", "version": "17.0.0",
"description": "A user-friendly Nostr client focused on relay feed browsing and relay discovery, forked from Jumble", "description": "A user-friendly Nostr client focused on relay feed browsing and relay discovery, forked from Jumble",
"private": true, "private": true,
"type": "module", "type": "module",

60
scripts/README-deploy.md

@ -0,0 +1,60 @@
# Deploy Jumble with docker-compose.prod.yml (remote server)
Workflow: **build and push locally****pull and run on the server**.
## Local: build and push
From the **repo root** on your machine:
```bash
docker login # once, if needed
./scripts/build-and-push-prod.sh
```
This builds both images and pushes two tags each (`latest` and the version from `package.json`, e.g. `17.0.0`):
- **Main app:** `silberengel/imwald-jumble`
- **NIP-66 monitor:** `silberengel/imwald-jumble-nip66-monitor`
## Remote server: one-time setup
1. **Docker**
Install Docker and Docker Compose (v2).
2. **Clone the repo** (so you have `docker-compose.prod.yml`):
```bash
git clone <your-repo-url> jumble
cd jumble
```
3. **Optional env file** (e.g. for NIP-66 monitor):
```bash
# .env next to docker-compose.prod.yml
NIP66_MONITOR_NSEC=nsec1...
NIP66_MONITOR_NPUB=npub1...
```
## Remote server: pull and run
After you’ve pushed from local:
```bash
cd jumble
docker compose -f docker-compose.prod.yml pull
docker compose -f docker-compose.prod.yml up -d
```
The app is on **port 8089**. Both services use `:latest`; to pin a version, set the image in `docker-compose.prod.yml` to e.g. `silberengel/imwald-jumble:17.0.0` and `silberengel/imwald-jumble-nip66-monitor:17.0.0`.
## Useful commands (server)
```bash
# Status
docker compose -f docker-compose.prod.yml ps
# Logs
docker compose -f docker-compose.prod.yml logs -f
# Stop
docker compose -f docker-compose.prod.yml down
```

25
scripts/build-and-push-prod.sh

@ -0,0 +1,25 @@
#!/usr/bin/env bash
# Build main app and NIP-66 monitor images locally; push to silberengel/imwald-jumble and silberengel/imwald-jumble-nip66-monitor as :latest and :<version from package.json>.
# Run from repo root. Requires: docker, docker login. On the server you then pull and run docker-compose.prod.yml.
set -e
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$REPO_ROOT"
VERSION="$(node -p "require('./package.json').version")"
IMAGE_APP="silberengel/imwald-jumble"
IMAGE_MONITOR="silberengel/imwald-jumble-nip66-monitor"
echo "Building main app (version: $VERSION)"
docker build -t "$IMAGE_APP:latest" -t "$IMAGE_APP:$VERSION" .
echo "Building NIP-66 monitor (version: $VERSION)"
docker build -t "$IMAGE_MONITOR:latest" -t "$IMAGE_MONITOR:$VERSION" ./nip66-cron
echo "Pushing $IMAGE_APP and $IMAGE_MONITOR"
docker push "$IMAGE_APP:latest"
docker push "$IMAGE_APP:$VERSION"
docker push "$IMAGE_MONITOR:latest"
docker push "$IMAGE_MONITOR:$VERSION"
echo "Done. On the server: docker compose -f docker-compose.prod.yml pull && docker compose -f docker-compose.prod.yml up -d"

24
src/PageManager.tsx

@ -473,10 +473,10 @@ function MainContentArea({
primaryNoteView: !!primaryNoteView primaryNoteView: !!primaryNoteView
}) })
// Always use single column layout since double-panel is disabled // Always use single column layout since double-panel is disabled. flex + min-h-0 so primary page ScrollArea gets a height and can scroll.
return ( return (
<div className="grid grid-cols-1 gap-2 w-full pr-2 py-2"> <div className="flex-1 flex flex-col min-h-0 w-full pr-2 py-2">
<div className="rounded-lg shadow-lg bg-background overflow-hidden"> <div className="flex-1 flex flex-col min-h-0 rounded-lg shadow-lg bg-background overflow-hidden">
{primaryNoteView ? ( {primaryNoteView ? (
// Show note view with back button // Show note view with back button
<div className="flex flex-col h-full w-full"> <div className="flex flex-col h-full w-full">
@ -519,7 +519,7 @@ function MainContentArea({
return ( return (
<div <div
key={name} key={name}
className="flex flex-col h-full w-full" className="flex flex-col h-full min-h-0 w-full"
style={{ style={{
display: isCurrentPage ? 'block' : 'none' display: isCurrentPage ? 'block' : 'none'
}} }}
@ -1579,13 +1579,15 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) {
} else { } else {
// Single-pane mode: show feed only, drawer overlay for notes // Single-pane mode: show feed only, drawer overlay for notes
return ( return (
<MainContentArea <div className="flex-1 flex flex-col min-h-0 min-w-0">
primaryPages={primaryPages} <MainContentArea
currentPrimaryPage={currentPrimaryPage} primaryPages={primaryPages}
primaryNoteView={primaryNoteView} currentPrimaryPage={currentPrimaryPage}
primaryViewType={primaryViewType} primaryNoteView={primaryNoteView}
goBack={goBack} primaryViewType={primaryViewType}
/> goBack={goBack}
/>
</div>
) )
} }
})()} })()}

12
src/components/Explore/index.tsx

@ -6,8 +6,6 @@ import relayInfoService from '@/services/relay-info.service'
import { TAwesomeRelayCollection } from '@/types' import { TAwesomeRelayCollection } from '@/types'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import RelaySimpleInfo, { RelaySimpleInfoSkeleton } from '../RelaySimpleInfo' import RelaySimpleInfo, { RelaySimpleInfoSkeleton } from '../RelaySimpleInfo'
import { useDeepBrowsing } from '@/providers/DeepBrowsingProvider'
import { cn } from '@/lib/utils'
export default function Explore() { export default function Explore() {
const [collections, setCollections] = useState<TAwesomeRelayCollection[] | null>(null) const [collections, setCollections] = useState<TAwesomeRelayCollection[] | null>(null)
@ -30,7 +28,7 @@ export default function Explore() {
} }
return ( return (
<div className="min-w-0 w-full overflow-x-hidden space-y-6"> <div className="min-w-0 w-full overflow-x-hidden space-y-6 pb-8">
{collections.map((collection) => ( {collections.map((collection) => (
<RelayCollection key={collection.id} collection={collection} /> <RelayCollection key={collection.id} collection={collection} />
))} ))}
@ -39,15 +37,9 @@ export default function Explore() {
} }
function RelayCollection({ collection }: { collection: TAwesomeRelayCollection }) { function RelayCollection({ collection }: { collection: TAwesomeRelayCollection }) {
const { deepBrowsing } = useDeepBrowsing()
return ( return (
<div className="min-w-0"> <div className="min-w-0">
<div <div className="px-4 pt-3 pb-3.5 text-2xl font-semibold max-md:border-b min-w-0 break-words">
className={cn(
'sticky bg-background z-20 px-4 py-3 text-2xl font-semibold max-md:border-b min-w-0 break-words',
deepBrowsing ? 'top-12' : 'top-24'
)}
>
{collection.name} {collection.name}
</div> </div>
<div className="grid min-w-0 md:px-4 md:grid-cols-2 md:gap-3"> <div className="grid min-w-0 md:px-4 md:grid-cols-2 md:gap-3">

2
src/components/FollowingFavoriteRelayList/index.tsx

@ -57,7 +57,7 @@ export default function FollowingFavoriteRelayList() {
}, [showCount, relays]) }, [showCount, relays])
return ( return (
<div> <div className="pb-8">
{relays.slice(0, showCount).map(([url, users]) => ( {relays.slice(0, showCount).map(([url, users]) => (
<RelayItem key={url} url={url} users={users} /> <RelayItem key={url} url={url} users={users} />
))} ))}

6
src/components/ImageGallery/index.tsx

@ -124,10 +124,14 @@ export default function ImageGallery({
open={index >= 0} open={index >= 0}
close={() => setIndex(-1)} close={() => setIndex(-1)}
controller={{ controller={{
closeOnBackdropClick: true, closeOnBackdropClick: false,
closeOnPullUp: true, closeOnPullUp: true,
closeOnPullDown: true closeOnPullDown: true
}} }}
render={{
buttonPrev: images.length <= 1 ? () => null : undefined,
buttonNext: images.length <= 1 ? () => null : undefined
}}
styles={{ styles={{
toolbar: { paddingTop: '2.25rem' } toolbar: { paddingTop: '2.25rem' }
}} }}

6
src/components/ImageWithLightbox/index.tsx

@ -84,10 +84,14 @@ export default function ImageWithLightbox({
open={index >= 0} open={index >= 0}
close={() => setIndex(-1)} close={() => setIndex(-1)}
controller={{ controller={{
closeOnBackdropClick: true, closeOnBackdropClick: false,
closeOnPullUp: true, closeOnPullUp: true,
closeOnPullDown: true closeOnPullDown: true
}} }}
render={{
buttonPrev: () => null,
buttonNext: () => null
}}
styles={{ styles={{
toolbar: { paddingTop: '2.25rem' } toolbar: { paddingTop: '2.25rem' }
}} }}

34
src/components/NormalFeed/index.tsx

@ -261,22 +261,24 @@ const NormalFeed = forwardRef<TNoteListRef, {
</> </>
} }
/> />
{activeTab === 'rss' ? ( <div className="pt-2 min-w-0">
<RssFeedList key={rssRefreshKey} /> {activeTab === 'rss' ? (
) : ( <RssFeedList key={rssRefreshKey} />
<NoteList ) : (
ref={noteListRef} <NoteList
showKinds={temporaryShowKinds} ref={noteListRef}
showKind1OPs={showKind1OPs} showKinds={temporaryShowKinds}
showKind1Replies={showKind1Replies} showKind1OPs={showKind1OPs}
showKind1111={showKind1111} showKind1Replies={showKind1Replies}
subRequests={subRequests} showKind1111={showKind1111}
hideReplies={listMode === 'posts'} subRequests={subRequests}
hideUntrustedNotes={hideUntrustedNotes} hideReplies={listMode === 'posts'}
areAlgoRelays={areAlgoRelays} hideUntrustedNotes={hideUntrustedNotes}
showRelayCloseReason={showRelayCloseReason} areAlgoRelays={areAlgoRelays}
/> showRelayCloseReason={showRelayCloseReason}
)} />
)}
</div>
</> </>
) )
}) })

6
src/components/Note/AsciidocArticle/AsciidocArticle.tsx

@ -2090,10 +2090,14 @@ export default function AsciidocArticle({
open={lightboxIndex >= 0} open={lightboxIndex >= 0}
close={() => setLightboxIndex(-1)} close={() => setLightboxIndex(-1)}
controller={{ controller={{
closeOnBackdropClick: true, closeOnBackdropClick: false,
closeOnPullUp: true, closeOnPullUp: true,
closeOnPullDown: true closeOnPullDown: true
}} }}
render={{
buttonPrev: allImages.length <= 1 ? () => null : undefined,
buttonNext: allImages.length <= 1 ? () => null : undefined
}}
styles={{ styles={{
toolbar: { paddingTop: '2.25rem' } toolbar: { paddingTop: '2.25rem' }
}} }}

6
src/components/Note/MarkdownArticle/MarkdownArticle.tsx

@ -3737,10 +3737,14 @@ export default function MarkdownArticle({
open={lightboxIndex >= 0} open={lightboxIndex >= 0}
close={() => setLightboxIndex(-1)} close={() => setLightboxIndex(-1)}
controller={{ controller={{
closeOnBackdropClick: true, closeOnBackdropClick: false,
closeOnPullUp: true, closeOnPullUp: true,
closeOnPullDown: true closeOnPullDown: true
}} }}
render={{
buttonPrev: allImages.length <= 1 ? () => null : undefined,
buttonNext: allImages.length <= 1 ? () => null : undefined
}}
styles={{ styles={{
toolbar: { paddingTop: '2.25rem' } toolbar: { paddingTop: '2.25rem' }
}} }}

2
src/components/RelaySimpleInfo/index.tsx

@ -19,7 +19,7 @@ export default function RelaySimpleInfo({
const { t } = useTranslation() const { t } = useTranslation()
return ( return (
<div className={cn('min-w-0 space-y-1', className)} {...props}> <div className={cn('min-w-0 min-h-[4.5rem] space-y-1', className)} {...props}>
<div className="flex items-start justify-between gap-2 w-full min-w-0"> <div className="flex items-start justify-between gap-2 w-full min-w-0">
<div className="flex flex-1 w-0 items-center gap-2"> <div className="flex flex-1 w-0 items-center gap-2">
<RelayIcon url={relayInfo?.url} className="h-9 w-9" /> <RelayIcon url={relayInfo?.url} className="h-9 w-9" />

2
src/components/ui/scroll-area.tsx

@ -8,7 +8,7 @@ const ScrollArea = React.forwardRef<
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root> & { scrollBarClassName?: string } React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root> & { scrollBarClassName?: string }
>(({ className, scrollBarClassName, children, ...props }, ref) => ( >(({ className, scrollBarClassName, children, ...props }, ref) => (
<ScrollAreaPrimitive.Root className={cn('relative overflow-hidden', className)} {...props}> <ScrollAreaPrimitive.Root className={cn('relative overflow-hidden', className)} {...props}>
<ScrollAreaPrimitive.Viewport ref={ref} className="h-full w-full rounded-[inherit]"> <ScrollAreaPrimitive.Viewport ref={ref} className="h-full min-h-0 w-full rounded-[inherit]">
{children} {children}
</ScrollAreaPrimitive.Viewport> </ScrollAreaPrimitive.Viewport>
<ScrollBar className={scrollBarClassName} /> <ScrollBar className={scrollBarClassName} />

31
src/layouts/PrimaryPageLayout/index.tsx

@ -1,6 +1,5 @@
import ScrollToTopButton from '@/components/ScrollToTopButton' import ScrollToTopButton from '@/components/ScrollToTopButton'
import { Titlebar } from '@/components/Titlebar' import { Titlebar } from '@/components/Titlebar'
import { ScrollArea } from '@/components/ui/scroll-area'
import { TPrimaryPageName, usePrimaryPage } from '@/PageManager' import { TPrimaryPageName, usePrimaryPage } from '@/PageManager'
import { DeepBrowsingProvider } from '@/providers/DeepBrowsingProvider' import { DeepBrowsingProvider } from '@/providers/DeepBrowsingProvider'
import { useScreenSize } from '@/providers/ScreenSizeProvider' import { useScreenSize } from '@/providers/ScreenSizeProvider'
@ -13,13 +12,16 @@ const PrimaryPageLayout = forwardRef(
titlebar, titlebar,
pageName, pageName,
displayScrollToTopButton = false, displayScrollToTopButton = false,
hideTitlebarBottomBorder = false hideTitlebarBottomBorder = false,
subHeader
}: { }: {
children?: React.ReactNode children?: React.ReactNode
titlebar: React.ReactNode titlebar: React.ReactNode
pageName: TPrimaryPageName pageName: TPrimaryPageName
displayScrollToTopButton?: boolean displayScrollToTopButton?: boolean
hideTitlebarBottomBorder?: boolean hideTitlebarBottomBorder?: boolean
/** Rendered between titlebar and scroll area; not in scroll flow so it never overlaps content */
subHeader?: React.ReactNode
}, },
ref ref
) => { ) => {
@ -72,7 +74,7 @@ const PrimaryPageLayout = forwardRef(
<DeepBrowsingProvider active={current === pageName && display}> <DeepBrowsingProvider active={current === pageName && display}>
<div <div
ref={smallScreenScrollAreaRef} ref={smallScreenScrollAreaRef}
className="min-w-0 overflow-x-hidden" className="min-w-0 w-full overflow-x-hidden"
style={{ style={{
paddingBottom: 'calc(env(safe-area-inset-bottom) + 3rem)' paddingBottom: 'calc(env(safe-area-inset-bottom) + 3rem)'
}} }}
@ -80,7 +82,10 @@ const PrimaryPageLayout = forwardRef(
<PrimaryPageTitlebar hideBottomBorder={hideTitlebarBottomBorder}> <PrimaryPageTitlebar hideBottomBorder={hideTitlebarBottomBorder}>
{titlebar} {titlebar}
</PrimaryPageTitlebar> </PrimaryPageTitlebar>
{children} {subHeader && <div className="shrink-0 w-full min-w-0 bg-background">{subHeader}</div>}
<div className="min-w-0 w-full">
{children}
</div>
</div> </div>
{displayScrollToTopButton && <ScrollToTopButton />} {displayScrollToTopButton && <ScrollToTopButton />}
</DeepBrowsingProvider> </DeepBrowsingProvider>
@ -89,17 +94,19 @@ const PrimaryPageLayout = forwardRef(
return ( return (
<DeepBrowsingProvider active={current === pageName && display} scrollAreaRef={scrollAreaRef}> <DeepBrowsingProvider active={current === pageName && display} scrollAreaRef={scrollAreaRef}>
<ScrollArea <div className="relative h-full min-h-0 flex flex-col">
className="h-full overflow-auto"
scrollBarClassName="z-50 pt-12"
ref={scrollAreaRef}
>
<PrimaryPageTitlebar hideBottomBorder={hideTitlebarBottomBorder}> <PrimaryPageTitlebar hideBottomBorder={hideTitlebarBottomBorder}>
{titlebar} {titlebar}
</PrimaryPageTitlebar> </PrimaryPageTitlebar>
{children} {subHeader && <div className="shrink-0 bg-background">{subHeader}</div>}
<div className="h-4" /> <div
</ScrollArea> ref={scrollAreaRef}
className={subHeader ? 'flex-1 min-h-0 overflow-y-auto overflow-x-hidden' : 'absolute top-12 left-0 right-0 bottom-0 overflow-y-auto overflow-x-hidden'}
>
{children}
<div className="h-4" />
</div>
</div>
{displayScrollToTopButton && <ScrollToTopButton scrollAreaRef={scrollAreaRef} />} {displayScrollToTopButton && <ScrollToTopButton scrollAreaRef={scrollAreaRef} />}
</DeepBrowsingProvider> </DeepBrowsingProvider>
) )

3
src/lib/draft-event.ts

@ -571,14 +571,13 @@ export async function createPollDraftEvent(
mentions: string[], mentions: string[],
{ isMultipleChoice, relays, options, endsAt }: TPollCreateData, { isMultipleChoice, relays, options, endsAt }: TPollCreateData,
{ {
addClientTag,
isNsfw, isNsfw,
addExpirationTag, addExpirationTag,
expirationMonths, expirationMonths,
addQuietTag, addQuietTag,
quietDays quietDays
}: { }: {
addClientTag?: boolean addClientTag?: boolean // accepted for API compat; client tag is added in publish()
isNsfw?: boolean isNsfw?: boolean
addExpirationTag?: boolean addExpirationTag?: boolean
expirationMonths?: number expirationMonths?: number

33
src/pages/primary/ExplorePage/index.tsx

@ -28,23 +28,26 @@ const ExplorePage = forwardRef((_, ref) => {
ref={ref} ref={ref}
pageName="explore" pageName="explore"
titlebar={<ExplorePageTitlebar />} titlebar={<ExplorePageTitlebar />}
subHeader={
<Tabs
value={tab}
tabs={[
{ value: 'explore', label: 'Explore' },
{ value: 'following', label: "Following's Favorites" }
]}
onTabChange={(tab) => {
setTab(tab as TExploreTabs)
window.dispatchEvent(new CustomEvent('pageTabChanged', {
detail: { page: 'explore', tab: tab }
}))
}}
/>
}
displayScrollToTopButton displayScrollToTopButton
> >
<Tabs <div className="min-w-0 pt-2">
value={tab} {tab === 'following' ? <FollowingFavoriteRelayList /> : <Explore />}
tabs={[ </div>
{ value: 'explore', label: 'Explore' },
{ value: 'following', label: "Following's Favorites" }
]}
onTabChange={(tab) => {
setTab(tab as TExploreTabs)
// Dispatch tab change event for PageManager
window.dispatchEvent(new CustomEvent('pageTabChanged', {
detail: { page: 'explore', tab: tab }
}))
}}
/>
{tab === 'following' ? <FollowingFavoriteRelayList /> : <Explore />}
</PrimaryPageLayout> </PrimaryPageLayout>
) )
}) })

13
src/pages/primary/NoteListPage/FeedButton.tsx

@ -7,7 +7,7 @@ import { useFavoriteRelays } from '@/providers/FavoriteRelaysProvider'
import { useFeed } from '@/providers/FeedProvider' import { useFeed } from '@/providers/FeedProvider'
import { useScreenSize } from '@/providers/ScreenSizeProvider' import { useScreenSize } from '@/providers/ScreenSizeProvider'
import { BookmarkIcon, ChevronDown, Server, UsersRound } from 'lucide-react' import { BookmarkIcon, ChevronDown, Server, UsersRound } from 'lucide-react'
import { forwardRef, HTMLAttributes, useMemo, useState } from 'react' import { forwardRef, ButtonHTMLAttributes, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
export default function FeedButton({ className }: { className?: string }) { export default function FeedButton({ className }: { className?: string }) {
@ -52,7 +52,7 @@ export default function FeedButton({ className }: { className?: string }) {
) )
} }
const FeedSwitcherTrigger = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>( const FeedSwitcherTrigger = forwardRef<HTMLButtonElement, ButtonHTMLAttributes<HTMLButtonElement>>(
({ className, ...props }, ref) => { ({ className, ...props }, ref) => {
const { t } = useTranslation() const { t } = useTranslation()
const { feedInfo, relayUrls } = useFeed() const { feedInfo, relayUrls } = useFeed()
@ -84,8 +84,9 @@ const FeedSwitcherTrigger = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivEle
}, [feedInfo, activeRelaySet]) }, [feedInfo, activeRelaySet])
return ( return (
<div <button
className={cn('flex items-center gap-2 clickable px-3 h-full rounded-lg', className)} type="button"
className={cn('flex items-center gap-2 clickable px-3 h-full rounded-lg bg-transparent border-0 text-left', className)}
ref={ref} ref={ref}
{...props} {...props}
> >
@ -98,9 +99,9 @@ const FeedSwitcherTrigger = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivEle
) : ( ) : (
<Server /> <Server />
)} )}
<div className="text-lg font-semibold truncate">{title}</div> <span className="text-lg font-semibold truncate">{title}</span>
<ChevronDown /> <ChevronDown />
</div> </button>
) )
} }
) )

Loading…
Cancel
Save