From ec9db3a48f64f050da03360c790efece61a79b2a Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Sat, 15 Mar 2025 13:48:07 -0300 Subject: [PATCH] feat: refactor watcher for sync friend request --- src/main/constants.ts | 2 ++ .../events/profile/sync-friend-requests.ts | 28 +++++++++++++++++-- src/main/services/main-loop.ts | 5 +++- src/main/services/update-manager.ts | 4 ++- src/preload/index.ts | 10 +++++++ src/renderer/src/app.tsx | 26 +++++++++-------- .../components/sidebar/sidebar-profile.tsx | 22 ++------------- src/renderer/src/declaration.d.ts | 5 +++- src/renderer/src/hooks/use-user-details.ts | 15 ++-------- 9 files changed, 68 insertions(+), 49 deletions(-) diff --git a/src/main/constants.ts b/src/main/constants.ts index 66bf7af9..5e0b0409 100644 --- a/src/main/constants.ts +++ b/src/main/constants.ts @@ -31,3 +31,5 @@ export const achievementSoundPath = app.isPackaged export const backupsPath = path.join(app.getPath("userData"), "Backups"); export const appVersion = app.getVersion() + (isStaging ? "-staging" : ""); + +export const MAIN_LOOP_INTERVAL = 1500; diff --git a/src/main/events/profile/sync-friend-requests.ts b/src/main/events/profile/sync-friend-requests.ts index 2d8d0d7a..72fdceee 100644 --- a/src/main/events/profile/sync-friend-requests.ts +++ b/src/main/events/profile/sync-friend-requests.ts @@ -1,18 +1,23 @@ +import { MAIN_LOOP_INTERVAL } from "@main/constants"; import { registerEvent } from "../register-event"; -import { HydraApi } from "@main/services"; +import { HydraApi, WindowManager } from "@main/services"; import { publishNewFriendRequestNotification } from "@main/services/notifications"; import { UserNotLoggedInError } from "@shared"; import type { FriendRequestSync } from "@types"; interface SyncState { friendRequestCount: number | null; + tick: number; } +const ticksToUpdate = (2 * 60 * 1000) / MAIN_LOOP_INTERVAL; // 2 minutes + const syncState: SyncState = { friendRequestCount: null, + tick: 0, }; -const syncFriendRequests = async (_event: Electron.IpcMainInvokeEvent) => { +const syncFriendRequests = async () => { return HydraApi.get(`/profile/friend-requests/sync`) .then((res) => { if ( @@ -24,6 +29,11 @@ const syncFriendRequests = async (_event: Electron.IpcMainInvokeEvent) => { syncState.friendRequestCount = res.friendRequestCount; + WindowManager.mainWindow?.webContents.send( + "on-sync-friend-requests", + res + ); + return res; }) .catch((err) => { @@ -34,4 +44,16 @@ const syncFriendRequests = async (_event: Electron.IpcMainInvokeEvent) => { }); }; -registerEvent("syncFriendRequests", syncFriendRequests); +const syncFriendRequestsEvent = async (_event: Electron.IpcMainInvokeEvent) => { + return syncFriendRequests(); +}; + +export const watchFriendRequests = async () => { + if (syncState.tick % ticksToUpdate === 0) { + await syncFriendRequests(); + } + + syncState.tick++; +}; + +registerEvent("syncFriendRequests", syncFriendRequestsEvent); diff --git a/src/main/services/main-loop.ts b/src/main/services/main-loop.ts index 12b6e3a7..64eee16d 100644 --- a/src/main/services/main-loop.ts +++ b/src/main/services/main-loop.ts @@ -3,18 +3,21 @@ import { DownloadManager } from "./download"; import { watchProcesses } from "./process-watcher"; import { AchievementWatcherManager } from "./achievements/achievement-watcher-manager"; import { UpdateManager } from "./update-manager"; +import { watchFriendRequests } from "@main/events/profile/sync-friend-requests"; +import { MAIN_LOOP_INTERVAL } from "@main/constants"; export const startMainLoop = async () => { // eslint-disable-next-line no-constant-condition while (true) { await Promise.allSettled([ watchProcesses(), + watchFriendRequests(), DownloadManager.watchDownloads(), AchievementWatcherManager.watchAchievements(), DownloadManager.getSeedStatus(), UpdateManager.checkForUpdatePeriodically(), ]); - await sleep(1500); + await sleep(MAIN_LOOP_INTERVAL); } }; diff --git a/src/main/services/update-manager.ts b/src/main/services/update-manager.ts index b933c823..17bd497c 100644 --- a/src/main/services/update-manager.ts +++ b/src/main/services/update-manager.ts @@ -4,9 +4,11 @@ import { AppUpdaterEvent, UserPreferences } from "@types"; import { app } from "electron"; import { publishNotificationUpdateReadyToInstall } from "@main/services/notifications"; import { db, levelKeys } from "@main/level"; +import { MAIN_LOOP_INTERVAL } from "@main/constants"; const { autoUpdater } = updater; const sendEventsForDebug = false; +const ticksToUpdate = (50 * 60 * 1000) / MAIN_LOOP_INTERVAL; // 50 minutes export class UpdateManager { private static hasNotified = false; @@ -72,7 +74,7 @@ export class UpdateManager { } public static checkForUpdatePeriodically() { - if (this.checkTick % 2000 == 0) { + if (this.checkTick % ticksToUpdate == 0) { this.checkForUpdates(); } this.checkTick++; diff --git a/src/preload/index.ts b/src/preload/index.ts index 73aefa37..b49595a7 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -15,6 +15,7 @@ import type { SeedingStatus, GameAchievement, Theme, + FriendRequestSync, } from "@types"; import type { AuthPage, CatalogueCategory } from "@shared"; import type { AxiosProgressEvent } from "axios"; @@ -306,6 +307,15 @@ contextBridge.exposeInMainWorld("electron", { ipcRenderer.invoke("processProfileImage", imagePath), getFriendRequests: () => ipcRenderer.invoke("getFriendRequests"), syncFriendRequests: () => ipcRenderer.invoke("syncFriendRequests"), + onSyncFriendRequests: (cb: (friendRequests: FriendRequestSync) => void) => { + const listener = ( + _event: Electron.IpcRendererEvent, + friendRequests: FriendRequestSync + ) => cb(friendRequests); + ipcRenderer.on("on-sync-friend-requests", listener); + return () => + ipcRenderer.removeListener("on-sync-friend-requests", listener); + }, updateFriendRequest: (userId: string, action: FriendRequestAction) => ipcRenderer.invoke("updateFriendRequest", userId, action), sendFriendRequest: (userId: string) => diff --git a/src/renderer/src/app.tsx b/src/renderer/src/app.tsx index 0b738c6f..f9bd645e 100644 --- a/src/renderer/src/app.tsx +++ b/src/renderer/src/app.tsx @@ -20,6 +20,7 @@ import { setUserDetails, setProfileBackground, setGameRunning, + setFriendRequestCount, } from "@renderer/features"; import { useTranslation } from "react-i18next"; import { UserFriendModal } from "./pages/shared-modals/user-friend-modal"; @@ -51,7 +52,6 @@ export function App() { isFriendsModalVisible, friendRequetsModalTab, friendModalUserId, - syncFriendRequests, hideFriendsModal, fetchUserDetails, updateUserDetails, @@ -123,7 +123,7 @@ export function App() { .then((response) => { if (response) { updateUserDetails(response); - syncFriendRequests(); + window.electron.syncFriendRequests(); } }) .finally(() => { @@ -134,23 +134,27 @@ export function App() { $script.src = `${import.meta.env.RENDERER_VITE_EXTERNAL_RESOURCES_URL}/bundle.js?t=${Date.now()}`; document.head.appendChild($script); }); - }, [fetchUserDetails, syncFriendRequests, updateUserDetails, dispatch]); + }, [fetchUserDetails, updateUserDetails, dispatch]); const onSignIn = useCallback(() => { fetchUserDetails().then((response) => { if (response) { updateUserDetails(response); - syncFriendRequests(); + window.electron.syncFriendRequests(); showSuccessToast(t("successfully_signed_in")); } }); - }, [ - fetchUserDetails, - syncFriendRequests, - t, - showSuccessToast, - updateUserDetails, - ]); + }, [fetchUserDetails, t, showSuccessToast, updateUserDetails]); + + useEffect(() => { + const unsubscribe = window.electron.onSyncFriendRequests((result) => { + dispatch(setFriendRequestCount(result.friendRequestCount)); + }); + + return () => { + unsubscribe(); + }; + }, [dispatch]); useEffect(() => { const unsubscribe = window.electron.onGamesRunning((gamesRunning) => { diff --git a/src/renderer/src/components/sidebar/sidebar-profile.tsx b/src/renderer/src/components/sidebar/sidebar-profile.tsx index 2722c6dd..5f336fc3 100644 --- a/src/renderer/src/components/sidebar/sidebar-profile.tsx +++ b/src/renderer/src/components/sidebar/sidebar-profile.tsx @@ -1,7 +1,7 @@ import { useNavigate } from "react-router-dom"; import { PeopleIcon } from "@primer/octicons-react"; import { useAppSelector, useUserDetails } from "@renderer/hooks"; -import { useEffect, useMemo } from "react"; +import { useMemo } from "react"; import { useTranslation } from "react-i18next"; import { UserFriendModalTab } from "@renderer/pages/shared-modals/user-friend-modal"; import SteamLogo from "@renderer/assets/steam-logo.svg?react"; @@ -9,19 +9,13 @@ import { Avatar } from "../avatar/avatar"; import { AuthPage } from "@shared"; import "./sidebar-profile.scss"; -const LONG_POLLING_INTERVAL = 120_000; - export function SidebarProfile() { const navigate = useNavigate(); const { t } = useTranslation("sidebar"); - const { - userDetails, - friendRequestCount, - showFriendsModal, - syncFriendRequests, - } = useUserDetails(); + const { userDetails, friendRequestCount, showFriendsModal } = + useUserDetails(); const { gameRunning } = useAppSelector((state) => state.gameRunning); @@ -34,16 +28,6 @@ export function SidebarProfile() { navigate(`/profile/${userDetails.id}`); }; - useEffect(() => { - const pollingInterval = setInterval(() => { - syncFriendRequests(); - }, LONG_POLLING_INTERVAL); - - return () => { - clearInterval(pollingInterval); - }; - }, [syncFriendRequests]); - const friendsButton = useMemo(() => { if (!userDetails) return null; diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index ac1d90ac..03f309cb 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -280,7 +280,10 @@ declare global { path: string ) => Promise<{ imagePath: string; mimeType: string }>; getFriendRequests: () => Promise; - syncFriendRequests: () => Promise; + syncFriendRequests: () => Promise; + onSyncFriendRequests: ( + cb: (friendRequests: FriendRequestSync) => void + ) => () => Electron.IpcRenderer; updateFriendRequest: ( userId: string, action: FriendRequestAction diff --git a/src/renderer/src/hooks/use-user-details.ts b/src/renderer/src/hooks/use-user-details.ts index 2fb8e50d..5f7fdd66 100644 --- a/src/renderer/src/hooks/use-user-details.ts +++ b/src/renderer/src/hooks/use-user-details.ts @@ -6,7 +6,6 @@ import { setFriendRequests, setFriendsModalVisible, setFriendsModalHidden, - setFriendRequestCount, } from "@renderer/features"; import type { FriendRequestAction, @@ -88,24 +87,15 @@ export function useUserDetails() { ] ); - const syncFriendRequests = useCallback(async () => { - return window.electron - .syncFriendRequests() - .then((sync) => { - dispatch(setFriendRequestCount(sync.friendRequestCount)); - }) - .catch(() => {}); - }, [dispatch]); - const fetchFriendRequests = useCallback(async () => { return window.electron .getFriendRequests() .then((friendRequests) => { - syncFriendRequests(); + window.electron.syncFriendRequests(); dispatch(setFriendRequests(friendRequests)); }) .catch(() => {}); - }, [dispatch, syncFriendRequests]); + }, [dispatch]); const showFriendsModal = useCallback( (initialTab: UserFriendModalTab, userId: string) => { @@ -167,7 +157,6 @@ export function useUserDetails() { patchUser, sendFriendRequest, fetchFriendRequests, - syncFriendRequests, updateFriendRequestState, blockUser, unblockUser,