From d1d3fcf493bf26ce58cd33fedd7320cfcfe8ef5f Mon Sep 17 00:00:00 2001 From: Innei Date: Fri, 25 Oct 2024 21:35:03 +0800 Subject: [PATCH] fix: remove `useSingleton` Signed-off-by: Innei --- .../src/components/ui/media/VideoPlayer.tsx | 8 ++--- .../renderer/src/initialize/migrates/index.ts | 34 ++++--------------- .../achievement/AchievementModalContent.tsx | 18 +++++----- apps/renderer/src/modules/discover/form.tsx | 5 ++- .../entry-column/Items/picture-masonry.tsx | 5 ++- apps/renderer/src/pages/(main)/layout.tsx | 19 +++++++++++ .../providers/invalidate-query-provider.tsx | 9 +++-- packages/hooks/src/useOnce.ts | 12 ++++--- 8 files changed, 54 insertions(+), 56 deletions(-) diff --git a/apps/renderer/src/components/ui/media/VideoPlayer.tsx b/apps/renderer/src/components/ui/media/VideoPlayer.tsx index 1a20990c7..83598cb34 100644 --- a/apps/renderer/src/components/ui/media/VideoPlayer.tsx +++ b/apps/renderer/src/components/ui/media/VideoPlayer.tsx @@ -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( }, ) 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() } } diff --git a/apps/renderer/src/initialize/migrates/index.ts b/apps/renderer/src/initialize/migrates/index.ts index 85702173e..035b8e6ee 100644 --- a/apps/renderer/src/initialize/migrates/index.ts +++ b/apps/renderer/src/initialize/migrates/index.ts @@ -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 } diff --git a/apps/renderer/src/modules/achievement/AchievementModalContent.tsx b/apps/renderer/src/modules/achievement/AchievementModalContent.tsx index 31754940b..a9bf29fce 100644 --- a/apps/renderer/src/modules/achievement/AchievementModalContent.tsx +++ b/apps/renderer/src/modules/achievement/AchievementModalContent.tsx @@ -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["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()) + const achievementsDataAtom = useState(() => atom())[0] const t = useI18n() @@ -257,7 +255,7 @@ const buildDefaultAchievements = () => { } const MintButton: FC<{ - achievementsDataAtom: SingletonRefObject> + achievementsDataAtom: PrimitiveAtom 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"] }) diff --git a/apps/renderer/src/modules/discover/form.tsx b/apps/renderer/src/modules/discover/form.tsx index 0f635c19b..1768c9aa4 100644 --- a/apps/renderer/src/modules/discover/form.tsx +++ b/apps/renderer/src/modules/discover/form.tsx @@ -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()).current + const discoverSearchDataAtom = useState(() => atom())[0] const discoverSearchData = useAtomValue(discoverSearchDataAtom) diff --git a/apps/renderer/src/modules/entry-column/Items/picture-masonry.tsx b/apps/renderer/src/modules/entry-column/Items/picture-masonry.tsx index 2159602c2..a0947e0e7 100644 --- a/apps/renderer/src/modules/entry-column/Items/picture-masonry.tsx +++ b/apps/renderer/src/modules/entry-column/Items/picture-masonry.tsx @@ -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 = (props) => { const { data } = props - const cacheMap = useSingleton(() => new Map()).current + const cacheMap = useState(() => new Map())[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 (
{ + useOnce(() => { + if (!window.__app_is_upgraded__) { + setTimeout(() => { + toast.success( +
+ App is upgraded to{" "} + {APP_VERSION}, enjoy the + new features! 🎉 +
, + ) + }) + } + }) +} + const supportMinWidth = 1024 export function Component() { const isAuthFail = useLoginModalShow() @@ -93,6 +111,7 @@ export function Component() { const containerRef = useRef(null) useDailyTask() + useAppUpgraded() const isNotSupportWidth = useViewport((v) => v.w < supportMinWidth && v.w !== 0) && !IN_ELECTRON diff --git a/apps/renderer/src/providers/invalidate-query-provider.tsx b/apps/renderer/src/providers/invalidate-query-provider.tsx index 150675863..e2abf9fab 100644 --- a/apps/renderer/src/providers/invalidate-query-provider.tsx +++ b/apps/renderer/src/providers/invalidate-query-provider.tsx @@ -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 } diff --git a/packages/hooks/src/useOnce.ts b/packages/hooks/src/useOnce.ts index a4ecb245d..23ebf38e0 100644 --- a/packages/hooks/src/useOnce.ts +++ b/packages/hooks/src/useOnce.ts @@ -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 + }, []) }