feat: refactor watcher for sync friend request

This commit is contained in:
Zamitto
2025-03-15 13:48:07 -03:00
parent 72cde368f5
commit ec9db3a48f
9 changed files with 68 additions and 49 deletions

View File

@@ -31,3 +31,5 @@ export const achievementSoundPath = app.isPackaged
export const backupsPath = path.join(app.getPath("userData"), "Backups"); export const backupsPath = path.join(app.getPath("userData"), "Backups");
export const appVersion = app.getVersion() + (isStaging ? "-staging" : ""); export const appVersion = app.getVersion() + (isStaging ? "-staging" : "");
export const MAIN_LOOP_INTERVAL = 1500;

View File

@@ -1,18 +1,23 @@
import { MAIN_LOOP_INTERVAL } from "@main/constants";
import { registerEvent } from "../register-event"; import { registerEvent } from "../register-event";
import { HydraApi } from "@main/services"; import { HydraApi, WindowManager } from "@main/services";
import { publishNewFriendRequestNotification } from "@main/services/notifications"; import { publishNewFriendRequestNotification } from "@main/services/notifications";
import { UserNotLoggedInError } from "@shared"; import { UserNotLoggedInError } from "@shared";
import type { FriendRequestSync } from "@types"; import type { FriendRequestSync } from "@types";
interface SyncState { interface SyncState {
friendRequestCount: number | null; friendRequestCount: number | null;
tick: number;
} }
const ticksToUpdate = (2 * 60 * 1000) / MAIN_LOOP_INTERVAL; // 2 minutes
const syncState: SyncState = { const syncState: SyncState = {
friendRequestCount: null, friendRequestCount: null,
tick: 0,
}; };
const syncFriendRequests = async (_event: Electron.IpcMainInvokeEvent) => { const syncFriendRequests = async () => {
return HydraApi.get<FriendRequestSync>(`/profile/friend-requests/sync`) return HydraApi.get<FriendRequestSync>(`/profile/friend-requests/sync`)
.then((res) => { .then((res) => {
if ( if (
@@ -24,6 +29,11 @@ const syncFriendRequests = async (_event: Electron.IpcMainInvokeEvent) => {
syncState.friendRequestCount = res.friendRequestCount; syncState.friendRequestCount = res.friendRequestCount;
WindowManager.mainWindow?.webContents.send(
"on-sync-friend-requests",
res
);
return res; return res;
}) })
.catch((err) => { .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);

View File

@@ -3,18 +3,21 @@ import { DownloadManager } from "./download";
import { watchProcesses } from "./process-watcher"; import { watchProcesses } from "./process-watcher";
import { AchievementWatcherManager } from "./achievements/achievement-watcher-manager"; import { AchievementWatcherManager } from "./achievements/achievement-watcher-manager";
import { UpdateManager } from "./update-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 () => { export const startMainLoop = async () => {
// eslint-disable-next-line no-constant-condition // eslint-disable-next-line no-constant-condition
while (true) { while (true) {
await Promise.allSettled([ await Promise.allSettled([
watchProcesses(), watchProcesses(),
watchFriendRequests(),
DownloadManager.watchDownloads(), DownloadManager.watchDownloads(),
AchievementWatcherManager.watchAchievements(), AchievementWatcherManager.watchAchievements(),
DownloadManager.getSeedStatus(), DownloadManager.getSeedStatus(),
UpdateManager.checkForUpdatePeriodically(), UpdateManager.checkForUpdatePeriodically(),
]); ]);
await sleep(1500); await sleep(MAIN_LOOP_INTERVAL);
} }
}; };

View File

@@ -4,9 +4,11 @@ import { AppUpdaterEvent, UserPreferences } from "@types";
import { app } from "electron"; import { app } from "electron";
import { publishNotificationUpdateReadyToInstall } from "@main/services/notifications"; import { publishNotificationUpdateReadyToInstall } from "@main/services/notifications";
import { db, levelKeys } from "@main/level"; import { db, levelKeys } from "@main/level";
import { MAIN_LOOP_INTERVAL } from "@main/constants";
const { autoUpdater } = updater; const { autoUpdater } = updater;
const sendEventsForDebug = false; const sendEventsForDebug = false;
const ticksToUpdate = (50 * 60 * 1000) / MAIN_LOOP_INTERVAL; // 50 minutes
export class UpdateManager { export class UpdateManager {
private static hasNotified = false; private static hasNotified = false;
@@ -72,7 +74,7 @@ export class UpdateManager {
} }
public static checkForUpdatePeriodically() { public static checkForUpdatePeriodically() {
if (this.checkTick % 2000 == 0) { if (this.checkTick % ticksToUpdate == 0) {
this.checkForUpdates(); this.checkForUpdates();
} }
this.checkTick++; this.checkTick++;

View File

@@ -15,6 +15,7 @@ import type {
SeedingStatus, SeedingStatus,
GameAchievement, GameAchievement,
Theme, Theme,
FriendRequestSync,
} from "@types"; } from "@types";
import type { AuthPage, CatalogueCategory } from "@shared"; import type { AuthPage, CatalogueCategory } from "@shared";
import type { AxiosProgressEvent } from "axios"; import type { AxiosProgressEvent } from "axios";
@@ -306,6 +307,15 @@ contextBridge.exposeInMainWorld("electron", {
ipcRenderer.invoke("processProfileImage", imagePath), ipcRenderer.invoke("processProfileImage", imagePath),
getFriendRequests: () => ipcRenderer.invoke("getFriendRequests"), getFriendRequests: () => ipcRenderer.invoke("getFriendRequests"),
syncFriendRequests: () => ipcRenderer.invoke("syncFriendRequests"), 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) => updateFriendRequest: (userId: string, action: FriendRequestAction) =>
ipcRenderer.invoke("updateFriendRequest", userId, action), ipcRenderer.invoke("updateFriendRequest", userId, action),
sendFriendRequest: (userId: string) => sendFriendRequest: (userId: string) =>

View File

@@ -20,6 +20,7 @@ import {
setUserDetails, setUserDetails,
setProfileBackground, setProfileBackground,
setGameRunning, setGameRunning,
setFriendRequestCount,
} from "@renderer/features"; } from "@renderer/features";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { UserFriendModal } from "./pages/shared-modals/user-friend-modal"; import { UserFriendModal } from "./pages/shared-modals/user-friend-modal";
@@ -51,7 +52,6 @@ export function App() {
isFriendsModalVisible, isFriendsModalVisible,
friendRequetsModalTab, friendRequetsModalTab,
friendModalUserId, friendModalUserId,
syncFriendRequests,
hideFriendsModal, hideFriendsModal,
fetchUserDetails, fetchUserDetails,
updateUserDetails, updateUserDetails,
@@ -123,7 +123,7 @@ export function App() {
.then((response) => { .then((response) => {
if (response) { if (response) {
updateUserDetails(response); updateUserDetails(response);
syncFriendRequests(); window.electron.syncFriendRequests();
} }
}) })
.finally(() => { .finally(() => {
@@ -134,23 +134,27 @@ export function App() {
$script.src = `${import.meta.env.RENDERER_VITE_EXTERNAL_RESOURCES_URL}/bundle.js?t=${Date.now()}`; $script.src = `${import.meta.env.RENDERER_VITE_EXTERNAL_RESOURCES_URL}/bundle.js?t=${Date.now()}`;
document.head.appendChild($script); document.head.appendChild($script);
}); });
}, [fetchUserDetails, syncFriendRequests, updateUserDetails, dispatch]); }, [fetchUserDetails, updateUserDetails, dispatch]);
const onSignIn = useCallback(() => { const onSignIn = useCallback(() => {
fetchUserDetails().then((response) => { fetchUserDetails().then((response) => {
if (response) { if (response) {
updateUserDetails(response); updateUserDetails(response);
syncFriendRequests(); window.electron.syncFriendRequests();
showSuccessToast(t("successfully_signed_in")); showSuccessToast(t("successfully_signed_in"));
} }
}); });
}, [ }, [fetchUserDetails, t, showSuccessToast, updateUserDetails]);
fetchUserDetails,
syncFriendRequests, useEffect(() => {
t, const unsubscribe = window.electron.onSyncFriendRequests((result) => {
showSuccessToast, dispatch(setFriendRequestCount(result.friendRequestCount));
updateUserDetails, });
]);
return () => {
unsubscribe();
};
}, [dispatch]);
useEffect(() => { useEffect(() => {
const unsubscribe = window.electron.onGamesRunning((gamesRunning) => { const unsubscribe = window.electron.onGamesRunning((gamesRunning) => {

View File

@@ -1,7 +1,7 @@
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { PeopleIcon } from "@primer/octicons-react"; import { PeopleIcon } from "@primer/octicons-react";
import { useAppSelector, useUserDetails } from "@renderer/hooks"; import { useAppSelector, useUserDetails } from "@renderer/hooks";
import { useEffect, useMemo } from "react"; import { useMemo } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { UserFriendModalTab } from "@renderer/pages/shared-modals/user-friend-modal"; import { UserFriendModalTab } from "@renderer/pages/shared-modals/user-friend-modal";
import SteamLogo from "@renderer/assets/steam-logo.svg?react"; import SteamLogo from "@renderer/assets/steam-logo.svg?react";
@@ -9,19 +9,13 @@ import { Avatar } from "../avatar/avatar";
import { AuthPage } from "@shared"; import { AuthPage } from "@shared";
import "./sidebar-profile.scss"; import "./sidebar-profile.scss";
const LONG_POLLING_INTERVAL = 120_000;
export function SidebarProfile() { export function SidebarProfile() {
const navigate = useNavigate(); const navigate = useNavigate();
const { t } = useTranslation("sidebar"); const { t } = useTranslation("sidebar");
const { const { userDetails, friendRequestCount, showFriendsModal } =
userDetails, useUserDetails();
friendRequestCount,
showFriendsModal,
syncFriendRequests,
} = useUserDetails();
const { gameRunning } = useAppSelector((state) => state.gameRunning); const { gameRunning } = useAppSelector((state) => state.gameRunning);
@@ -34,16 +28,6 @@ export function SidebarProfile() {
navigate(`/profile/${userDetails.id}`); navigate(`/profile/${userDetails.id}`);
}; };
useEffect(() => {
const pollingInterval = setInterval(() => {
syncFriendRequests();
}, LONG_POLLING_INTERVAL);
return () => {
clearInterval(pollingInterval);
};
}, [syncFriendRequests]);
const friendsButton = useMemo(() => { const friendsButton = useMemo(() => {
if (!userDetails) return null; if (!userDetails) return null;

View File

@@ -280,7 +280,10 @@ declare global {
path: string path: string
) => Promise<{ imagePath: string; mimeType: string }>; ) => Promise<{ imagePath: string; mimeType: string }>;
getFriendRequests: () => Promise<FriendRequest[]>; getFriendRequests: () => Promise<FriendRequest[]>;
syncFriendRequests: () => Promise<FriendRequestSync>; syncFriendRequests: () => Promise<void>;
onSyncFriendRequests: (
cb: (friendRequests: FriendRequestSync) => void
) => () => Electron.IpcRenderer;
updateFriendRequest: ( updateFriendRequest: (
userId: string, userId: string,
action: FriendRequestAction action: FriendRequestAction

View File

@@ -6,7 +6,6 @@ import {
setFriendRequests, setFriendRequests,
setFriendsModalVisible, setFriendsModalVisible,
setFriendsModalHidden, setFriendsModalHidden,
setFriendRequestCount,
} from "@renderer/features"; } from "@renderer/features";
import type { import type {
FriendRequestAction, 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 () => { const fetchFriendRequests = useCallback(async () => {
return window.electron return window.electron
.getFriendRequests() .getFriendRequests()
.then((friendRequests) => { .then((friendRequests) => {
syncFriendRequests(); window.electron.syncFriendRequests();
dispatch(setFriendRequests(friendRequests)); dispatch(setFriendRequests(friendRequests));
}) })
.catch(() => {}); .catch(() => {});
}, [dispatch, syncFriendRequests]); }, [dispatch]);
const showFriendsModal = useCallback( const showFriendsModal = useCallback(
(initialTab: UserFriendModalTab, userId: string) => { (initialTab: UserFriendModalTab, userId: string) => {
@@ -167,7 +157,6 @@ export function useUserDetails() {
patchUser, patchUser,
sendFriendRequest, sendFriendRequest,
fetchFriendRequests, fetchFriendRequests,
syncFriendRequests,
updateFriendRequestState, updateFriendRequestState,
blockUser, blockUser,
unblockUser, unblockUser,