fix: remove `useSingleton`
Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
parent
b05ff50f14
commit
d1d3fcf493
|
|
@ -4,7 +4,6 @@ import { useRefValue, useVideo } from "@follow/hooks"
|
|||
import { nextFrame, stopPropagation } from "@follow/utils/dom"
|
||||
import { cn } from "@follow/utils/utils"
|
||||
import * as Slider from "@radix-ui/react-slider"
|
||||
import { useSingleton } from "foxact/use-singleton"
|
||||
import { m, useDragControls, useSpring } from "framer-motion"
|
||||
import type { PropsWithChildren } from "react"
|
||||
import {
|
||||
|
|
@ -164,15 +163,14 @@ export const VideoPlayer = forwardRef<VideoPlayerRef, VideoPlayerProps>(
|
|||
},
|
||||
)
|
||||
const BizControlOutsideMedia = () => {
|
||||
const currentAudioPlayerIsPlayRef = useSingleton(() => AudioPlayer.get().status === "playing")
|
||||
const currentAudioPlayerIsPlayRef = useMemo(() => AudioPlayer.get().status === "playing", [])
|
||||
useEffect(() => {
|
||||
const { current } = currentAudioPlayerIsPlayRef
|
||||
if (current) {
|
||||
if (currentAudioPlayerIsPlayRef) {
|
||||
AudioPlayer.pause()
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (current) {
|
||||
if (currentAudioPlayerIsPlayRef) {
|
||||
AudioPlayer.play()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,42 +1,20 @@
|
|||
import { getStorageNS } from "@follow/utils/ns"
|
||||
import { repository } from "@pkg"
|
||||
import { createElement } from "react"
|
||||
import { toast } from "sonner"
|
||||
|
||||
import { appLog } from "~/lib/log"
|
||||
|
||||
import { waitAppReady } from "../queue"
|
||||
|
||||
const appVersionKey = getStorageNS("app_version")
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
__app_is_upgraded__: boolean
|
||||
}
|
||||
}
|
||||
export const doMigration = () => {
|
||||
const lastVersion = localStorage.getItem(appVersionKey)
|
||||
|
||||
if (lastVersion && lastVersion !== APP_VERSION) {
|
||||
appLog(`Upgrade from ${lastVersion} to ${APP_VERSION}`)
|
||||
|
||||
waitAppReady(() => {
|
||||
toast.success(
|
||||
// `App is upgraded to ${APP_VERSION}, enjoy the new features! 🎉`,
|
||||
createElement("div", {
|
||||
children: [
|
||||
"App is upgraded to ",
|
||||
createElement(
|
||||
"a",
|
||||
{
|
||||
href: `${repository.url}/releases/tag/${APP_VERSION}`,
|
||||
target: "_blank",
|
||||
className: "underline",
|
||||
},
|
||||
createElement("strong", {
|
||||
children: APP_VERSION,
|
||||
}),
|
||||
),
|
||||
", enjoy the new features! 🎉",
|
||||
],
|
||||
}),
|
||||
)
|
||||
}, 1000)
|
||||
window.__app_is_upgraded__ = true
|
||||
|
||||
// NOTE: Add migration logic here
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,13 +9,11 @@ import { Chain } from "@follow/utils/chain"
|
|||
import { cn } from "@follow/utils/utils"
|
||||
import { DotLottieReact } from "@lottiefiles/dotlottie-react"
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
|
||||
import type { SingletonRefObject } from "foxact/use-singleton"
|
||||
import { useSingleton } from "foxact/use-singleton"
|
||||
import type { PrimitiveAtom } from "jotai"
|
||||
import { atom, useStore } from "jotai"
|
||||
import { nanoid } from "nanoid"
|
||||
import type { FC, ReactNode } from "react"
|
||||
import { useEffect, useId, useMemo, useRef } from "react"
|
||||
import { useEffect, useId, useMemo, useRef, useState } from "react"
|
||||
import { Trans, useTranslation } from "react-i18next"
|
||||
|
||||
import { useServerConfigs } from "~/atoms/server-configs"
|
||||
|
|
@ -79,7 +77,7 @@ type Achievement = ExtractBizResponse<typeof apiClient.achievement.$get>["data"]
|
|||
export const AchievementModalContent: FC = () => {
|
||||
const jotaiStore = useStore()
|
||||
|
||||
const defaultAchievements = useSingleton(buildDefaultAchievements)
|
||||
const defaultAchievements = useState(buildDefaultAchievements)[0]
|
||||
|
||||
const {
|
||||
data: achievements,
|
||||
|
|
@ -97,10 +95,10 @@ export const AchievementModalContent: FC = () => {
|
|||
},
|
||||
})
|
||||
|
||||
jotaiStore.set(achievementsDataAtom.current, res.data)
|
||||
jotaiStore.set(achievementsDataAtom, res.data)
|
||||
return res.data
|
||||
},
|
||||
initialData: defaultAchievements.current as Achievement,
|
||||
initialData: defaultAchievements as Achievement,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -126,7 +124,7 @@ export const AchievementModalContent: FC = () => {
|
|||
}
|
||||
}, [achievements, isLoading, refetch])
|
||||
|
||||
const achievementsDataAtom = useSingleton(() => atom<typeof achievements>())
|
||||
const achievementsDataAtom = useState(() => atom<typeof achievements>())[0]
|
||||
|
||||
const t = useI18n()
|
||||
|
||||
|
|
@ -257,7 +255,7 @@ const buildDefaultAchievements = () => {
|
|||
}
|
||||
|
||||
const MintButton: FC<{
|
||||
achievementsDataAtom: SingletonRefObject<PrimitiveAtom<Achievement | undefined>>
|
||||
achievementsDataAtom: PrimitiveAtom<Achievement | undefined>
|
||||
achievement: Achievement[number]
|
||||
}> = ({ achievementsDataAtom, achievement }) => {
|
||||
const { mutateAsync: mintAchievement, isPending: isMinting } = useMutation({
|
||||
|
|
@ -280,7 +278,7 @@ const MintButton: FC<{
|
|||
onClick={async () => {
|
||||
const res = await mintAchievement(achievement.actionId)
|
||||
|
||||
const currentData = jotaiStore.get(achievementsDataAtom.current)
|
||||
const currentData = jotaiStore.get(achievementsDataAtom)
|
||||
if (!currentData) return
|
||||
let shouldInvalidate = false
|
||||
const newData = currentData.map((item) => {
|
||||
|
|
@ -295,7 +293,7 @@ const MintButton: FC<{
|
|||
}
|
||||
return item
|
||||
})
|
||||
jotaiStore.set(achievementsDataAtom.current, newData)
|
||||
jotaiStore.set(achievementsDataAtom, newData)
|
||||
|
||||
if (shouldInvalidate) {
|
||||
queryClient.invalidateQueries({ queryKey: ["achievements"] })
|
||||
|
|
|
|||
|
|
@ -14,11 +14,10 @@ import { RadioGroup } from "@follow/components/ui/radio-group/RadioGroup.jsx"
|
|||
import type { FeedViewType } from "@follow/constants"
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { useMutation } from "@tanstack/react-query"
|
||||
import { useSingleton } from "foxact/use-singleton"
|
||||
import { produce } from "immer"
|
||||
import { atom, useAtomValue, useStore } from "jotai"
|
||||
import type { FC } from "react"
|
||||
import { memo, useCallback, useEffect } from "react"
|
||||
import { memo, useCallback, useEffect, useState } from "react"
|
||||
import { useForm } from "react-hook-form"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { z } from "zod"
|
||||
|
|
@ -90,7 +89,7 @@ export function DiscoverForm({ type = "search" }: { type?: string }) {
|
|||
return data
|
||||
},
|
||||
})
|
||||
const discoverSearchDataAtom = useSingleton(() => atom<DiscoverSearchData>()).current
|
||||
const discoverSearchDataAtom = useState(() => atom<DiscoverSearchData>())[0]
|
||||
|
||||
const discoverSearchData = useAtomValue(discoverSearchDataAtom)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import { useScrollViewElement } from "@follow/components/ui/scroll-area/hooks.js
|
|||
import { Skeleton } from "@follow/components/ui/skeleton/index.jsx"
|
||||
import { useRefValue } from "@follow/hooks"
|
||||
import { nextFrame } from "@follow/utils/dom"
|
||||
import { useSingleton } from "foxact/use-singleton"
|
||||
import { throttle } from "lodash-es"
|
||||
import type { RenderComponentProps } from "masonic"
|
||||
import { useInfiniteLoader } from "masonic"
|
||||
|
|
@ -60,7 +59,7 @@ const gutter = 24
|
|||
|
||||
export const PictureMasonry: FC<MasonryProps> = (props) => {
|
||||
const { data } = props
|
||||
const cacheMap = useSingleton(() => new Map<string, object>()).current
|
||||
const cacheMap = useState(() => new Map<string, object>())[0]
|
||||
const [isInitDim, setIsInitDim] = useState(false)
|
||||
const [isInitLayout, setIsInitLayout] = useState(false)
|
||||
const [currentItemWidth, setCurrentItemWidth] = useState(0)
|
||||
|
|
@ -277,7 +276,7 @@ interface MasonryProps {
|
|||
|
||||
const LoadingSkeletonItem = () => {
|
||||
// random height, between 100-400px
|
||||
const randomHeight = useSingleton(() => Math.random() * 300 + 100).current
|
||||
const randomHeight = useState(() => Math.random() * 300 + 100)[0]
|
||||
return (
|
||||
<div className="relative flex gap-2 overflow-x-auto">
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { useViewport } from "@follow/components/hooks/useViewport.js"
|
||||
import { PanelSplitter } from "@follow/components/ui/divider/index.js"
|
||||
import { RootPortal } from "@follow/components/ui/portal/index.jsx"
|
||||
import { useOnce } from "@follow/hooks"
|
||||
import { IN_ELECTRON } from "@follow/shared/constants"
|
||||
import { preventDefault } from "@follow/utils/dom"
|
||||
import { cn } from "@follow/utils/utils"
|
||||
|
|
@ -14,6 +15,7 @@ import { useHotkeys } from "react-hotkeys-hook"
|
|||
import { Trans, useTranslation } from "react-i18next"
|
||||
import { useResizable } from "react-resizable-layout"
|
||||
import { Outlet } from "react-router-dom"
|
||||
import { toast } from "sonner"
|
||||
|
||||
import { setMainContainerElement } from "~/atoms/dom"
|
||||
import { getUISettings, setUISetting, useUISettingKey } from "~/atoms/settings/ui"
|
||||
|
|
@ -85,6 +87,22 @@ const errorTypes = [
|
|||
ErrorComponentType.FeedNotFound,
|
||||
] as ErrorComponentType[]
|
||||
|
||||
const useAppUpgraded = () => {
|
||||
useOnce(() => {
|
||||
if (!window.__app_is_upgraded__) {
|
||||
setTimeout(() => {
|
||||
toast.success(
|
||||
<div>
|
||||
App is upgraded to{" "}
|
||||
<a href={`${repository.url}/releases/tag/${APP_VERSION}`}>{APP_VERSION}</a>, enjoy the
|
||||
new features! 🎉
|
||||
</div>,
|
||||
)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const supportMinWidth = 1024
|
||||
export function Component() {
|
||||
const isAuthFail = useLoginModalShow()
|
||||
|
|
@ -93,6 +111,7 @@ export function Component() {
|
|||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useDailyTask()
|
||||
useAppUpgraded()
|
||||
|
||||
const isNotSupportWidth = useViewport((v) => v.w < supportMinWidth && v.w !== 0) && !IN_ELECTRON
|
||||
|
||||
|
|
|
|||
|
|
@ -50,8 +50,13 @@ const InvalidateQueryProviderElectron = () => {
|
|||
`Window switch to visible, but skip invalidation, ${currentTimeRef.current ? now - currentTimeRef.current : 0}`,
|
||||
)
|
||||
} else {
|
||||
appLog("Window switch to visible, invalidate all queries")
|
||||
queryClient.invalidateQueries()
|
||||
appLog("Window switch to visible, invalidate all queries except entries")
|
||||
queryClient.invalidateQueries({
|
||||
predicate(query) {
|
||||
// Ignore entries queries
|
||||
return query.queryKey[0] !== "entries"
|
||||
},
|
||||
})
|
||||
}
|
||||
currentTimeRef.current = 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import { useSingleton } from "foxact/use-singleton"
|
||||
import { useEffect, useRef } from "react"
|
||||
|
||||
export const useOnce = (fn: () => any) => {
|
||||
useSingleton(async () => {
|
||||
await fn()
|
||||
return true
|
||||
})
|
||||
const isDone = useRef(false)
|
||||
useEffect(() => {
|
||||
if (isDone.current) return
|
||||
fn()
|
||||
isDone.current = true
|
||||
}, [])
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue