diff --git a/src/PageManager.tsx b/src/PageManager.tsx
index 7af1aa4..417a775 100644
--- a/src/PageManager.tsx
+++ b/src/PageManager.tsx
@@ -4,6 +4,7 @@ import { cn } from '@/lib/utils'
import logger from '@/lib/logger'
import { ChevronLeft } from 'lucide-react'
import NoteListPage from '@/pages/primary/NoteListPage'
+import SecondaryNoteListPage from '@/pages/secondary/NoteListPage'
// Page imports needed for primary note view
import SettingsPage from '@/pages/secondary/SettingsPage'
import RelaySettingsPage from '@/pages/secondary/RelaySettingsPage'
@@ -131,7 +132,7 @@ export function useSmartNoteNavigation() {
// Use primary note view to show notes since secondary panel is disabled
// Extract note ID from URL (e.g., "/notes/note1..." -> "note1...")
const noteId = url.replace('/notes/', '')
- window.history.replaceState(null, '', url)
+ window.history.pushState(null, '', url)
setPrimaryNoteView(, 'note')
}
@@ -145,8 +146,8 @@ export function useSmartRelayNavigation() {
const navigateToRelay = (url: string) => {
// Use primary note view to show relay pages since secondary panel is disabled
// Extract relay URL from the URL (e.g., "/relays/wss://..." -> "wss://...")
- const relayUrl = url.replace('/relays/', '')
- window.history.replaceState(null, '', url)
+ const relayUrl = decodeURIComponent(url.replace('/relays/', ''))
+ window.history.pushState(null, '', url)
setPrimaryNoteView(, 'relay')
}
@@ -160,7 +161,7 @@ export function useSmartProfileNavigation() {
const navigateToProfile = (url: string) => {
// Use primary note view to show profiles since secondary panel is disabled
const profileId = url.replace('/users/', '')
- window.history.replaceState(null, '', url)
+ window.history.pushState(null, '', url)
setPrimaryNoteView(, 'profile')
}
@@ -173,8 +174,8 @@ export function useSmartHashtagNavigation() {
const navigateToHashtag = (url: string) => {
// Use primary note view to show hashtag feed since secondary panel is disabled
- window.history.replaceState(null, '', url)
- setPrimaryNoteView(, 'hashtag')
+ window.history.pushState(null, '', url)
+ setPrimaryNoteView(, 'hashtag')
}
return { navigateToHashtag }
@@ -187,7 +188,7 @@ export function useSmartFollowingListNavigation() {
const navigateToFollowingList = (url: string) => {
// Use primary note view to show following list since secondary panel is disabled
const profileId = url.replace('/users/', '').replace('/following', '')
- window.history.replaceState(null, '', url)
+ window.history.pushState(null, '', url)
setPrimaryNoteView(, 'profile')
}
@@ -200,7 +201,7 @@ export function useSmartMuteListNavigation() {
const navigateToMuteList = (url: string) => {
// Use primary note view to show mute list since secondary panel is disabled
- window.history.replaceState(null, '', url)
+ window.history.pushState(null, '', url)
setPrimaryNoteView(, 'settings')
}
@@ -214,7 +215,7 @@ export function useSmartOthersRelaySettingsNavigation() {
const navigateToOthersRelaySettings = (url: string) => {
// Use primary note view to show others relay settings since secondary panel is disabled
const profileId = url.replace('/users/', '').replace('/relays', '')
- window.history.replaceState(null, '', url)
+ window.history.pushState(null, '', url)
setPrimaryNoteView(, 'profile')
}
@@ -228,22 +229,22 @@ export function useSmartSettingsNavigation() {
const navigateToSettings = (url: string) => {
// Use primary note view to show settings since secondary panel is disabled
if (url === '/settings') {
- window.history.replaceState(null, '', url)
+ window.history.pushState(null, '', url)
setPrimaryNoteView(, 'settings')
} else if (url === '/settings/relays') {
- window.history.replaceState(null, '', url)
+ window.history.pushState(null, '', url)
setPrimaryNoteView(, 'settings-sub')
} else if (url === '/settings/wallet') {
- window.history.replaceState(null, '', url)
+ window.history.pushState(null, '', url)
setPrimaryNoteView(, 'settings-sub')
} else if (url === '/settings/posts') {
- window.history.replaceState(null, '', url)
+ window.history.pushState(null, '', url)
setPrimaryNoteView(, 'settings-sub')
} else if (url === '/settings/general') {
- window.history.replaceState(null, '', url)
+ window.history.pushState(null, '', url)
setPrimaryNoteView(, 'settings-sub')
} else if (url === '/settings/translation') {
- window.history.replaceState(null, '', url)
+ window.history.pushState(null, '', url)
setPrimaryNoteView(, 'settings-sub')
}
}
@@ -285,13 +286,13 @@ function MainContentArea({
currentPrimaryPage,
primaryNoteView,
primaryViewType,
- setPrimaryNoteView
+ goBack
}: {
primaryPages: { name: TPrimaryPageName; element: ReactNode; props?: any }[]
currentPrimaryPage: TPrimaryPageName
primaryNoteView: ReactNode | null
primaryViewType: 'note' | 'settings' | 'settings-sub' | 'profile' | 'hashtag' | 'relay' | null
- setPrimaryNoteView: (view: ReactNode | null, type?: 'note' | 'settings' | 'settings-sub' | 'profile' | 'hashtag' | 'relay') => void
+ goBack: () => void
}) {
logger.debug('MainContentArea rendering:', {
currentPrimaryPage,
@@ -313,16 +314,7 @@ function MainContentArea({
variant="ghost"
size="titlebar-icon"
title="Back"
- onClick={() => {
- if (primaryViewType === 'settings-sub') {
- // For settings sub-pages, navigate back to main settings page
- window.history.replaceState(null, '', '/settings')
- setPrimaryNoteView(, 'settings')
- } else {
- // For other pages, go back to feed
- setPrimaryNoteView(null)
- }
- }}
+ onClick={goBack}
>
@@ -405,8 +397,37 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) {
window.history.replaceState(null, '', newUrl)
}
}
+
+ const goBack = () => {
+ // Special handling for settings sub-pages - go back to main settings page
+ if (primaryViewType === 'settings-sub') {
+ window.history.pushState(null, '', '/settings')
+ setPrimaryNoteView(, 'settings')
+ } else {
+ // Use browser's back functionality for other pages
+ window.history.back()
+ }
+ }
const ignorePopStateRef = useRef(false)
+ // Handle browser back button
+ useEffect(() => {
+ const handlePopState = () => {
+ if (ignorePopStateRef.current) {
+ ignorePopStateRef.current = false
+ return
+ }
+
+ // If we have a primary note view open, close it and go back to the main page
+ if (primaryNoteView) {
+ setPrimaryNoteView(null)
+ }
+ }
+
+ window.addEventListener('popstate', handlePopState)
+ return () => window.removeEventListener('popstate', handlePopState)
+ }, [primaryNoteView])
+
useEffect(() => {
if (['/npub1', '/nprofile1'].some((prefix) => window.location.pathname.startsWith(prefix))) {
window.history.replaceState(
@@ -709,7 +730,7 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) {
currentPrimaryPage={currentPrimaryPage}
primaryNoteView={primaryNoteView}
primaryViewType={primaryViewType}
- setPrimaryNoteView={setPrimaryNoteView}
+ goBack={goBack}
/>
diff --git a/src/components/Profile/ProfileBookmarksAndHashtags.tsx b/src/components/Profile/ProfileBookmarksAndHashtags.tsx
index c46a1fc..c3eebe8 100644
--- a/src/components/Profile/ProfileBookmarksAndHashtags.tsx
+++ b/src/components/Profile/ProfileBookmarksAndHashtags.tsx
@@ -317,15 +317,17 @@ export default function ProfileBookmarksAndHashtags({
}
return (
-
- {pinEvents.map((event) => (
-
- ))}
+
+
+ {pinEvents.map((event) => (
+
+ ))}
+
)
}
@@ -350,15 +352,17 @@ export default function ProfileBookmarksAndHashtags({
}
return (
-
- {bookmarkEvents.map((event) => (
-
- ))}
+
+
+ {bookmarkEvents.map((event) => (
+
+ ))}
+
)
}
@@ -383,15 +387,17 @@ export default function ProfileBookmarksAndHashtags({
}
return (
-
- {hashtagEvents.map((event) => (
-
- ))}
+
+
+ {hashtagEvents.map((event) => (
+
+ ))}
+
)
}
diff --git a/src/components/SearchBar/index.tsx b/src/components/SearchBar/index.tsx
index 7b15ed3..e564a02 100644
--- a/src/components/SearchBar/index.tsx
+++ b/src/components/SearchBar/index.tsx
@@ -1,10 +1,10 @@
import SearchInput from '@/components/SearchInput'
import { useSearchProfiles } from '@/hooks'
-import { toNote } from '@/lib/link'
+import { toNote, toNoteList } from '@/lib/link'
import { randomString } from '@/lib/random'
import { normalizeUrl } from '@/lib/url'
import { cn } from '@/lib/utils'
-import { useSmartNoteNavigation } from '@/PageManager'
+import { useSmartNoteNavigation, useSmartHashtagNavigation } from '@/PageManager'
import { useScreenSize } from '@/providers/ScreenSizeProvider'
import modalManager from '@/services/modal-manager.service'
import { TSearchParams } from '@/types'
@@ -33,6 +33,7 @@ const SearchBar = forwardRef<
>(({ input, setInput, onSearch }, ref) => {
const { t } = useTranslation()
const { navigateToNote } = useSmartNoteNavigation()
+ const { navigateToHashtag } = useSmartHashtagNavigation()
const { isSmallScreen } = useScreenSize()
const [debouncedInput, setDebouncedInput] = useState(input)
const { profiles, isFetching: isFetchingProfiles } = useSearchProfiles(debouncedInput, 5)
@@ -89,6 +90,8 @@ const SearchBar = forwardRef<
if (params.type === 'note') {
navigateToNote(toNote(params.search))
+ } else if (params.type === 'hashtag') {
+ navigateToHashtag(toNoteList({ hashtag: params.search }))
} else {
onSearch(params)
}
diff --git a/src/pages/primary/SearchPage/index.tsx b/src/pages/primary/SearchPage/index.tsx
index 521ac56..1c83e74 100644
--- a/src/pages/primary/SearchPage/index.tsx
+++ b/src/pages/primary/SearchPage/index.tsx
@@ -39,6 +39,7 @@ const SearchPage = forwardRef((_, ref) => {
diff --git a/src/pages/secondary/GeneralSettingsPage/index.tsx b/src/pages/secondary/GeneralSettingsPage/index.tsx
index 6449816..b6e88ce 100644
--- a/src/pages/secondary/GeneralSettingsPage/index.tsx
+++ b/src/pages/secondary/GeneralSettingsPage/index.tsx
@@ -6,7 +6,6 @@ import { LocalizedLanguageNames, TLanguage } from '@/i18n'
import SecondaryPageLayout from '@/layouts/SecondaryPageLayout'
import { cn, isSupportCheckConnectionType } from '@/lib/utils'
import { useContentPolicy } from '@/providers/ContentPolicyProvider'
-import { useScreenSize } from '@/providers/ScreenSizeProvider'
import { useTheme } from '@/providers/ThemeProvider'
import { useUserPreferences } from '@/providers/UserPreferencesProvider'
import { useUserTrust } from '@/providers/UserTrustProvider'
@@ -20,7 +19,6 @@ const GeneralSettingsPage = forwardRef(({ index, hideTitlebar = false }: { index
const { t, i18n } = useTranslation()
const [language, setLanguage] = useState
(i18n.language as TLanguage)
const { themeSetting, setThemeSetting } = useTheme()
- const { isSmallScreen } = useScreenSize()
const {
autoplay,
setAutoplay,
diff --git a/src/pages/secondary/HomePage/index.tsx b/src/pages/secondary/HomePage/index.tsx
index 8dfdee3..bc4fb70 100644
--- a/src/pages/secondary/HomePage/index.tsx
+++ b/src/pages/secondary/HomePage/index.tsx
@@ -4,10 +4,9 @@ import { Button } from '@/components/ui/button'
import { RECOMMENDED_RELAYS } from '@/constants'
import SecondaryPageLayout from '@/layouts/SecondaryPageLayout'
import { toRelay } from '@/lib/link'
-import { useUserPreferences } from '@/providers/UserPreferencesProvider'
import relayInfoService from '@/services/relay-info.service'
import { TRelayInfo } from '@/types'
-import { ArrowRight, Server, X } from 'lucide-react'
+import { ArrowRight, Server } from 'lucide-react'
import { forwardRef, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
diff --git a/src/pages/secondary/NoteListPage/index.tsx b/src/pages/secondary/NoteListPage/index.tsx
index 5b095bf..181c7ce 100644
--- a/src/pages/secondary/NoteListPage/index.tsx
+++ b/src/pages/secondary/NoteListPage/index.tsx
@@ -15,7 +15,12 @@ import { UserRound, Plus } from 'lucide-react'
import React, { forwardRef, useEffect, useState, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
-const NoteListPage = forwardRef(({ index, hideTitlebar = false }: { index?: number; hideTitlebar?: boolean }, ref) => {
+interface NoteListPageProps {
+ index?: number
+ hideTitlebar?: boolean
+}
+
+const NoteListPage = forwardRef(({ index, hideTitlebar = false }, ref) => {
const { t } = useTranslation()
const { push } = useSecondaryPage()
const { relayList, pubkey } = useNostr()
@@ -149,6 +154,40 @@ const NoteListPage = forwardRef(({ index, hideTitlebar = false }: { index?: numb
init()
}, [])
+ // Listen for URL changes to re-initialize the page
+ useEffect(() => {
+ const handlePopState = () => {
+ const searchParams = new URLSearchParams(window.location.search)
+ const hashtag = searchParams.get('t')
+ if (hashtag) {
+ setData({ type: 'hashtag' })
+ setTitle(`# ${hashtag}`)
+ setSubRequests([
+ {
+ filter: { '#t': [hashtag] },
+ urls: BIG_RELAY_URLS
+ }
+ ])
+ // Set controls for hashtag subscribe button
+ if (pubkey) {
+ setControls(
+
+ )
+ }
+ }
+ }
+
+ window.addEventListener('popstate', handlePopState)
+ return () => window.removeEventListener('popstate', handlePopState)
+ }, [pubkey, isHashtagSubscribed, t])
+
// Update controls when subscription status changes
useEffect(() => {
if (data?.type === 'hashtag' && pubkey) {
@@ -183,9 +222,17 @@ const NoteListPage = forwardRef(({ index, hideTitlebar = false }: { index?: numb
ref={ref}
index={index}
title={hideTitlebar ? undefined : title}
- controls={controls}
+ controls={hideTitlebar ? undefined : controls}
displayScrollToTopButton
>
+ {hideTitlebar && data?.type === 'hashtag' && (
+
+ )}
{content}
)
diff --git a/src/pages/secondary/SearchPage/index.tsx b/src/pages/secondary/SearchPage/index.tsx
index b5951dc..db3751f 100644
--- a/src/pages/secondary/SearchPage/index.tsx
+++ b/src/pages/secondary/SearchPage/index.tsx
@@ -1,15 +1,13 @@
import SearchBar, { TSearchBarRef } from '@/components/SearchBar'
import SearchResult from '@/components/SearchResult'
-import { Button } from '@/components/ui/button'
import SecondaryPageLayout from '@/layouts/SecondaryPageLayout'
import { toSearch } from '@/lib/link'
import { useSecondaryPage } from '@/PageManager'
import { TSearchParams } from '@/types'
-import { ChevronLeft } from 'lucide-react'
import { forwardRef, useEffect, useMemo, useRef, useState } from 'react'
const SearchPage = forwardRef(({ index, hideTitlebar = false }: { index?: number; hideTitlebar?: boolean }, ref) => {
- const { push, pop } = useSecondaryPage()
+ const { push } = useSecondaryPage()
const [input, setInput] = useState('')
const searchBarRef = useRef(null)
const searchParams = useMemo(() => {