feat: improve mobile experience
This commit is contained in:
23
src/providers/FeedProvider.tsx
Normal file
23
src/providers/FeedProvider.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { TFeedType } from '@/types'
|
||||
import { createContext, useContext, useState } from 'react'
|
||||
|
||||
type TFeedContext = {
|
||||
feedType: TFeedType
|
||||
setFeedType: (feedType: TFeedType) => void
|
||||
}
|
||||
|
||||
const FeedContext = createContext<TFeedContext | undefined>(undefined)
|
||||
|
||||
export const useFeed = () => {
|
||||
const context = useContext(FeedContext)
|
||||
if (!context) {
|
||||
throw new Error('useFeed must be used within a FeedProvider')
|
||||
}
|
||||
return context
|
||||
}
|
||||
|
||||
export function FeedProvider({ children }: { children: React.ReactNode }) {
|
||||
const [feedType, setFeedType] = useState<TFeedType>('relays')
|
||||
|
||||
return <FeedContext.Provider value={{ feedType, setFeedType }}>{children}</FeedContext.Provider>
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import { useNostr } from './NostrProvider'
|
||||
type TFollowListContext = {
|
||||
followListEvent: Event | undefined
|
||||
followings: string[]
|
||||
isReady: boolean
|
||||
isFetching: boolean
|
||||
follow: (pubkey: string) => Promise<void>
|
||||
unfollow: (pubkey: string) => Promise<void>
|
||||
}
|
||||
@@ -27,33 +27,35 @@ export const useFollowList = () => {
|
||||
export function FollowListProvider({ children }: { children: React.ReactNode }) {
|
||||
const { pubkey: accountPubkey, publish } = useNostr()
|
||||
const [followListEvent, setFollowListEvent] = useState<Event | undefined>(undefined)
|
||||
const [isReady, setIsReady] = useState(false)
|
||||
const followings = useMemo(
|
||||
() =>
|
||||
followListEvent?.tags
|
||||
.filter(tagNameEquals('p'))
|
||||
.map(([, pubkey]) => pubkey)
|
||||
.filter(Boolean)
|
||||
.reverse() ?? [],
|
||||
[followListEvent]
|
||||
)
|
||||
const [isFetching, setIsFetching] = useState(true)
|
||||
const followings = useMemo(() => {
|
||||
return Array.from(
|
||||
new Set(
|
||||
followListEvent?.tags
|
||||
.filter(tagNameEquals('p'))
|
||||
.map(([, pubkey]) => pubkey)
|
||||
.filter(Boolean)
|
||||
.reverse() ?? []
|
||||
)
|
||||
)
|
||||
}, [followListEvent])
|
||||
|
||||
useEffect(() => {
|
||||
if (!accountPubkey) return
|
||||
|
||||
const init = async () => {
|
||||
setIsReady(false)
|
||||
setIsFetching(true)
|
||||
setFollowListEvent(undefined)
|
||||
const event = await client.fetchFollowListEvent(accountPubkey)
|
||||
setFollowListEvent(event)
|
||||
setIsReady(true)
|
||||
setIsFetching(false)
|
||||
}
|
||||
|
||||
init()
|
||||
}, [accountPubkey])
|
||||
|
||||
const follow = async (pubkey: string) => {
|
||||
if (!isReady || !accountPubkey) return
|
||||
if (isFetching || !accountPubkey) return
|
||||
|
||||
const newFollowListDraftEvent: TDraftEvent = {
|
||||
kind: kinds.Contacts,
|
||||
@@ -67,7 +69,7 @@ export function FollowListProvider({ children }: { children: React.ReactNode })
|
||||
}
|
||||
|
||||
const unfollow = async (pubkey: string) => {
|
||||
if (!isReady || !accountPubkey || !followListEvent) return
|
||||
if (isFetching || !accountPubkey || !followListEvent) return
|
||||
|
||||
const newFollowListDraftEvent: TDraftEvent = {
|
||||
kind: kinds.Contacts,
|
||||
@@ -87,7 +89,7 @@ export function FollowListProvider({ children }: { children: React.ReactNode })
|
||||
value={{
|
||||
followListEvent,
|
||||
followings,
|
||||
isReady,
|
||||
isFetching,
|
||||
follow,
|
||||
unfollow
|
||||
}}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import LoginDialog from '@/components/LoginDialog'
|
||||
import { useToast } from '@/hooks'
|
||||
import { useFetchFollowings, useToast } from '@/hooks'
|
||||
import { useFetchRelayList } from '@/hooks/useFetchRelayList'
|
||||
import client from '@/services/client.service'
|
||||
import storage from '@/services/storage.service'
|
||||
import { ISigner, TAccount, TAccountPointer, TDraftEvent } from '@/types'
|
||||
import { ISigner, TAccount, TAccountPointer, TDraftEvent, TRelayList } from '@/types'
|
||||
import dayjs from 'dayjs'
|
||||
import { Event, kinds } from 'nostr-tools'
|
||||
import { createContext, useContext, useEffect, useState } from 'react'
|
||||
@@ -14,6 +14,8 @@ import { NsecSigner } from './nsec.signer'
|
||||
|
||||
type TNostrContext = {
|
||||
pubkey: string | null
|
||||
relayList: TRelayList | null
|
||||
followings: string[] | null
|
||||
account: TAccountPointer | null
|
||||
accounts: TAccountPointer[]
|
||||
switchAccount: (account: TAccountPointer | null) => Promise<void>
|
||||
@@ -46,7 +48,8 @@ export function NostrProvider({ children }: { children: React.ReactNode }) {
|
||||
const [signer, setSigner] = useState<ISigner | null>(null)
|
||||
const [openLoginDialog, setOpenLoginDialog] = useState(false)
|
||||
const { relayUrls: currentRelayUrls } = useRelaySettings()
|
||||
const { relayList } = useFetchRelayList(account?.pubkey)
|
||||
const { relayList, isFetching: isFetchingRelayList } = useFetchRelayList(account?.pubkey)
|
||||
const { followings, isFetching: isFetchingFollowings } = useFetchFollowings(account?.pubkey)
|
||||
|
||||
useEffect(() => {
|
||||
const init = async () => {
|
||||
@@ -224,6 +227,8 @@ export function NostrProvider({ children }: { children: React.ReactNode }) {
|
||||
<NostrContext.Provider
|
||||
value={{
|
||||
pubkey: account?.pubkey ?? null,
|
||||
relayList: isFetchingRelayList ? null : relayList,
|
||||
followings: isFetchingFollowings ? null : followings,
|
||||
account,
|
||||
accounts: storage
|
||||
.getAccounts()
|
||||
|
||||
@@ -5,6 +5,7 @@ import client from '@/services/client.service'
|
||||
import storage from '@/services/storage.service'
|
||||
import { TRelayGroup } from '@/types'
|
||||
import { createContext, Dispatch, useContext, useEffect, useState } from 'react'
|
||||
import { useFeed } from './FeedProvider'
|
||||
|
||||
type TRelaySettingsContext = {
|
||||
relayGroups: TRelayGroup[]
|
||||
@@ -31,6 +32,7 @@ export const useRelaySettings = () => {
|
||||
}
|
||||
|
||||
export function RelaySettingsProvider({ children }: { children: React.ReactNode }) {
|
||||
const { setFeedType } = useFeed()
|
||||
const [relayGroups, setRelayGroups] = useState<TRelayGroup[]>([])
|
||||
const [temporaryRelayUrls, setTemporaryRelayUrls] = useState<string[]>([])
|
||||
const [relayUrls, setRelayUrls] = useState<string[]>(
|
||||
@@ -49,6 +51,7 @@ export function RelaySettingsProvider({ children }: { children: React.ReactNode
|
||||
.map((url) => normalizeUrl(url))
|
||||
if (tempRelays.length) {
|
||||
setTemporaryRelayUrls(tempRelays)
|
||||
setFeedType('relays')
|
||||
}
|
||||
const storedGroups = storage.getRelayGroups()
|
||||
setRelayGroups(storedGroups)
|
||||
@@ -93,6 +96,7 @@ export function RelaySettingsProvider({ children }: { children: React.ReactNode
|
||||
isActive: group.groupName === groupName
|
||||
}))
|
||||
)
|
||||
setFeedType('relays')
|
||||
setTemporaryRelayUrls([])
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ type ThemeProviderState = {
|
||||
setThemeSetting: (themeSetting: TThemeSetting) => Promise<void>
|
||||
}
|
||||
|
||||
async function getSystemTheme() {
|
||||
function getSystemTheme() {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
||||
}
|
||||
|
||||
@@ -26,9 +26,9 @@ export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
|
||||
|
||||
useEffect(() => {
|
||||
const init = async () => {
|
||||
const themeSetting = await storage.getThemeSetting()
|
||||
const themeSetting = storage.getThemeSetting()
|
||||
if (themeSetting === 'system') {
|
||||
setTheme(await getSystemTheme())
|
||||
setTheme(getSystemTheme())
|
||||
return
|
||||
}
|
||||
setTheme(themeSetting)
|
||||
@@ -65,10 +65,10 @@ export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
|
||||
const value = {
|
||||
themeSetting: themeSetting,
|
||||
setThemeSetting: async (themeSetting: TThemeSetting) => {
|
||||
await storage.setThemeSetting(themeSetting)
|
||||
storage.setThemeSetting(themeSetting)
|
||||
setThemeSetting(themeSetting)
|
||||
if (themeSetting === 'system') {
|
||||
setTheme(await getSystemTheme())
|
||||
setTheme(getSystemTheme())
|
||||
return
|
||||
}
|
||||
setTheme(themeSetting)
|
||||
|
||||
Reference in New Issue
Block a user