mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-01-23 21:44:55 +03:00
Merge branch 'main' into feature/adding-flame-animation
This commit is contained in:
commit
39be8fdf53
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hydralauncher",
|
||||
"version": "2.1.1",
|
||||
"version": "2.1.3",
|
||||
"description": "Hydra",
|
||||
"main": "./out/main/index.js",
|
||||
"author": "Los Broxas",
|
||||
|
@ -59,6 +59,7 @@ import "./profile/update-friend-request";
|
||||
import "./profile/update-profile";
|
||||
import "./profile/process-profile-image";
|
||||
import "./profile/send-friend-request";
|
||||
import "./profile/sync-friend-requests";
|
||||
import { isPortableVersion } from "@main/helpers";
|
||||
|
||||
ipcMain.handle("ping", () => "pong");
|
||||
|
@ -1,32 +1,14 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import * as Sentry from "@sentry/electron/main";
|
||||
import { HydraApi, logger } from "@main/services";
|
||||
import { UserProfile } from "@types";
|
||||
import { HydraApi } from "@main/services";
|
||||
import { ProfileVisibility, UserDetails } from "@types";
|
||||
import { userAuthRepository } from "@main/repository";
|
||||
import { steamUrlBuilder, UserNotLoggedInError } from "@shared";
|
||||
import { steamGamesWorker } from "@main/workers";
|
||||
|
||||
const getSteamGame = async (objectId: string) => {
|
||||
try {
|
||||
const steamGame = await steamGamesWorker.run(Number(objectId), {
|
||||
name: "getById",
|
||||
});
|
||||
|
||||
return {
|
||||
title: steamGame.name,
|
||||
iconUrl: steamUrlBuilder.icon(objectId, steamGame.clientIcon),
|
||||
};
|
||||
} catch (err) {
|
||||
logger.error("Failed to get Steam game", err);
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
import { UserNotLoggedInError } from "@shared";
|
||||
|
||||
const getMe = async (
|
||||
_event: Electron.IpcMainInvokeEvent
|
||||
): Promise<UserProfile | null> => {
|
||||
return HydraApi.get(`/profile/me`)
|
||||
): Promise<UserDetails | null> => {
|
||||
return HydraApi.get<UserDetails>(`/profile/me`)
|
||||
.then(async (me) => {
|
||||
userAuthRepository.upsert(
|
||||
{
|
||||
@ -38,17 +20,6 @@ const getMe = async (
|
||||
["id"]
|
||||
);
|
||||
|
||||
if (me.currentGame) {
|
||||
const steamGame = await getSteamGame(me.currentGame.objectId);
|
||||
|
||||
if (steamGame) {
|
||||
me.currentGame = {
|
||||
...me.currentGame,
|
||||
...steamGame,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Sentry.setUser({ id: me.id, username: me.username });
|
||||
|
||||
return me;
|
||||
@ -61,7 +32,13 @@ const getMe = async (
|
||||
const loggedUser = await userAuthRepository.findOne({ where: { id: 1 } });
|
||||
|
||||
if (loggedUser) {
|
||||
return { ...loggedUser, id: loggedUser.userId };
|
||||
return {
|
||||
...loggedUser,
|
||||
id: loggedUser.userId,
|
||||
username: "",
|
||||
bio: "",
|
||||
profileVisibility: "PUBLIC" as ProfileVisibility,
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
|
9
src/main/events/profile/sync-friend-requests.ts
Normal file
9
src/main/events/profile/sync-friend-requests.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { registerEvent } from "../register-event";
|
||||
import { HydraApi } from "@main/services";
|
||||
import { FriendRequestSync } from "@types";
|
||||
|
||||
const syncFriendRequests = async (_event: Electron.IpcMainInvokeEvent) => {
|
||||
return HydraApi.get<FriendRequestSync>(`/profile/friend-requests/sync`);
|
||||
};
|
||||
|
||||
registerEvent("syncFriendRequests", syncFriendRequests);
|
@ -2,7 +2,7 @@ import { registerEvent } from "../register-event";
|
||||
|
||||
import type { StartGameDownloadPayload } from "@types";
|
||||
import { getFileBase64 } from "@main/helpers";
|
||||
import { DownloadManager } from "@main/services";
|
||||
import { DownloadManager, HydraApi, logger } from "@main/services";
|
||||
|
||||
import { Not } from "typeorm";
|
||||
import { steamGamesWorker } from "@main/workers";
|
||||
@ -101,6 +101,17 @@ const startGameDownload = async (
|
||||
|
||||
createGame(updatedGame!).catch(() => {});
|
||||
|
||||
HydraApi.post(
|
||||
"/games/download",
|
||||
{
|
||||
objectId: updatedGame!.objectID,
|
||||
shop: updatedGame!.shop,
|
||||
},
|
||||
{ needsAuth: false }
|
||||
).catch((err) => {
|
||||
logger.error("Failed to create game download", err);
|
||||
});
|
||||
|
||||
await DownloadManager.cancelDownload(updatedGame!.id);
|
||||
await DownloadManager.startDownload(updatedGame!);
|
||||
|
||||
|
@ -3,12 +3,19 @@ import { databasePath } from "./constants";
|
||||
import { Hydra2_0_3 } from "./migrations/20240830143811_Hydra_2_0_3";
|
||||
import { RepackUris } from "./migrations/20240830143906_RepackUris";
|
||||
import { UpdateUserLanguage } from "./migrations/20240913213944_update_user_language";
|
||||
import { EnsureRepackUris } from "./migrations/20240915035339_ensure_repack_uris";
|
||||
import { app } from "electron";
|
||||
|
||||
export type HydraMigration = Knex.Migration & { name: string };
|
||||
|
||||
class MigrationSource implements Knex.MigrationSource<HydraMigration> {
|
||||
getMigrations(): Promise<HydraMigration[]> {
|
||||
return Promise.resolve([Hydra2_0_3, RepackUris, UpdateUserLanguage]);
|
||||
return Promise.resolve([
|
||||
Hydra2_0_3,
|
||||
RepackUris,
|
||||
UpdateUserLanguage,
|
||||
EnsureRepackUris,
|
||||
]);
|
||||
}
|
||||
getMigrationName(migration: HydraMigration): string {
|
||||
return migration.name;
|
||||
@ -19,6 +26,7 @@ class MigrationSource implements Knex.MigrationSource<HydraMigration> {
|
||||
}
|
||||
|
||||
export const knexClient = knex({
|
||||
debug: !app.isPackaged,
|
||||
client: "better-sqlite3",
|
||||
connection: {
|
||||
filename: databasePath,
|
||||
|
@ -4,55 +4,15 @@ import type { Knex } from "knex";
|
||||
export const RepackUris: HydraMigration = {
|
||||
name: "RepackUris",
|
||||
up: async (knex: Knex) => {
|
||||
await knex.schema.createTable("temporary_repack", (table) => {
|
||||
const timestamp = new Date().getTime();
|
||||
table.increments("id").primary();
|
||||
table
|
||||
.text("title")
|
||||
.notNullable()
|
||||
.unique({ indexName: "repack_title_unique_" + timestamp });
|
||||
table
|
||||
.text("magnet")
|
||||
.notNullable()
|
||||
.unique({ indexName: "repack_magnet_unique_" + timestamp });
|
||||
table.text("repacker").notNullable();
|
||||
table.text("fileSize").notNullable();
|
||||
table.datetime("uploadDate").notNullable();
|
||||
table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
|
||||
table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
|
||||
table
|
||||
.integer("downloadSourceId")
|
||||
.references("download_source.id")
|
||||
.onDelete("CASCADE");
|
||||
await knex.schema.alterTable("repack", (table) => {
|
||||
table.text("uris").notNullable().defaultTo("[]");
|
||||
});
|
||||
await knex.raw(
|
||||
`INSERT INTO "temporary_repack"("id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "repack"`
|
||||
);
|
||||
await knex.schema.dropTable("repack");
|
||||
await knex.schema.renameTable("temporary_repack", "repack");
|
||||
},
|
||||
|
||||
down: async (knex: Knex) => {
|
||||
await knex.schema.renameTable("repack", "temporary_repack");
|
||||
await knex.schema.createTable("repack", (table) => {
|
||||
table.increments("id").primary();
|
||||
table.text("title").notNullable().unique();
|
||||
table.text("magnet").notNullable().unique();
|
||||
await knex.schema.alterTable("repack", (table) => {
|
||||
table.integer("page");
|
||||
table.text("repacker").notNullable();
|
||||
table.text("fileSize").notNullable();
|
||||
table.datetime("uploadDate").notNullable();
|
||||
table.datetime("createdAt").notNullable().defaultTo(knex.fn.now());
|
||||
table.datetime("updatedAt").notNullable().defaultTo(knex.fn.now());
|
||||
table
|
||||
.integer("downloadSourceId")
|
||||
.references("download_source.id")
|
||||
.onDelete("CASCADE");
|
||||
table.dropColumn("uris");
|
||||
});
|
||||
await knex.raw(
|
||||
`INSERT INTO "repack"("id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId") SELECT "id", "title", "magnet", "repacker", "fileSize", "uploadDate", "createdAt", "updatedAt", "downloadSourceId" FROM "temporary_repack"`
|
||||
);
|
||||
await knex.schema.dropTable("temporary_repack");
|
||||
},
|
||||
};
|
||||
|
17
src/main/migrations/20240915035339_ensure_repack_uris.ts
Normal file
17
src/main/migrations/20240915035339_ensure_repack_uris.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import type { HydraMigration } from "@main/knex-client";
|
||||
import type { Knex } from "knex";
|
||||
|
||||
export const EnsureRepackUris: HydraMigration = {
|
||||
name: "EnsureRepackUris",
|
||||
up: async (knex: Knex) => {
|
||||
await knex.schema.hasColumn("repack", "uris").then(async (exists) => {
|
||||
if (!exists) {
|
||||
await knex.schema.table("repack", (table) => {
|
||||
table.text("uris").notNullable().defaultTo("[]");
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
down: async (_knex: Knex) => {},
|
||||
};
|
@ -6,6 +6,7 @@ import { uploadGamesBatch } from "./library-sync";
|
||||
import { clearGamesRemoteIds } from "./library-sync/clear-games-remote-id";
|
||||
import { logger } from "./logger";
|
||||
import { UserNotLoggedInError } from "@shared";
|
||||
import { omit } from "lodash-es";
|
||||
|
||||
interface HydraApiOptions {
|
||||
needsAuth: boolean;
|
||||
@ -96,11 +97,14 @@ export class HydraApi {
|
||||
this.instance.interceptors.response.use(
|
||||
(response) => {
|
||||
logger.log(" ---- RESPONSE -----");
|
||||
const data = Array.isArray(response.data)
|
||||
? response.data
|
||||
: omit(response.data, ["username", "accessToken", "refreshToken"]);
|
||||
logger.log(
|
||||
response.status,
|
||||
response.config.method,
|
||||
response.config.url,
|
||||
response.data
|
||||
data
|
||||
);
|
||||
return response;
|
||||
},
|
||||
@ -166,7 +170,10 @@ export class HydraApi {
|
||||
this.userAuth.authToken = accessToken;
|
||||
this.userAuth.expirationTimestamp = tokenExpirationTimestamp;
|
||||
|
||||
logger.log("Token refreshed", this.userAuth);
|
||||
logger.log(
|
||||
"Token refreshed. New expiration:",
|
||||
this.userAuth.expirationTimestamp
|
||||
);
|
||||
|
||||
userAuthRepository.upsert(
|
||||
{
|
||||
|
@ -1,20 +1,8 @@
|
||||
import { Game } from "@main/entity";
|
||||
import { HydraApi } from "../hydra-api";
|
||||
import { gameRepository } from "@main/repository";
|
||||
import { logger } from "../logger";
|
||||
|
||||
export const createGame = async (game: Game) => {
|
||||
HydraApi.post(
|
||||
"/games/download",
|
||||
{
|
||||
objectId: game.objectID,
|
||||
shop: game.shop,
|
||||
},
|
||||
{ needsAuth: false }
|
||||
).catch((err) => {
|
||||
logger.error("Failed to create game download", err);
|
||||
});
|
||||
|
||||
return HydraApi.post(`/profile/games`, {
|
||||
objectId: game.objectID,
|
||||
playTimeInMilliseconds: Math.trunc(game.playTimeInMilliseconds),
|
||||
|
@ -119,7 +119,7 @@ const onCloseGame = (game: Game) => {
|
||||
if (game.remoteId) {
|
||||
updateGamePlaytime(
|
||||
game,
|
||||
performance.now() - gamePlaytime.firstTick,
|
||||
performance.now() - gamePlaytime.lastSyncTick,
|
||||
game.lastTimePlayed!
|
||||
).catch(() => {});
|
||||
} else {
|
||||
|
@ -150,6 +150,7 @@ contextBridge.exposeInMainWorld("electron", {
|
||||
processProfileImage: (imagePath: string) =>
|
||||
ipcRenderer.invoke("processProfileImage", imagePath),
|
||||
getFriendRequests: () => ipcRenderer.invoke("getFriendRequests"),
|
||||
syncFriendRequests: () => ipcRenderer.invoke("syncFriendRequests"),
|
||||
updateFriendRequest: (userId: string, action: FriendRequestAction) =>
|
||||
ipcRenderer.invoke("updateFriendRequest", userId, action),
|
||||
sendFriendRequest: (userId: string) =>
|
||||
|
@ -43,7 +43,7 @@ export function App() {
|
||||
isFriendsModalVisible,
|
||||
friendRequetsModalTab,
|
||||
friendModalUserId,
|
||||
fetchFriendRequests,
|
||||
syncFriendRequests,
|
||||
hideFriendsModal,
|
||||
} = useUserDetails();
|
||||
|
||||
@ -105,22 +105,22 @@ export function App() {
|
||||
fetchUserDetails().then((response) => {
|
||||
if (response) {
|
||||
updateUserDetails(response);
|
||||
fetchFriendRequests();
|
||||
syncFriendRequests();
|
||||
}
|
||||
});
|
||||
}, [fetchUserDetails, fetchFriendRequests, updateUserDetails, dispatch]);
|
||||
}, [fetchUserDetails, syncFriendRequests, updateUserDetails, dispatch]);
|
||||
|
||||
const onSignIn = useCallback(() => {
|
||||
fetchUserDetails().then((response) => {
|
||||
if (response) {
|
||||
updateUserDetails(response);
|
||||
fetchFriendRequests();
|
||||
syncFriendRequests();
|
||||
showSuccessToast(t("successfully_signed_in"));
|
||||
}
|
||||
});
|
||||
}, [
|
||||
fetchUserDetails,
|
||||
fetchFriendRequests,
|
||||
syncFriendRequests,
|
||||
t,
|
||||
showSuccessToast,
|
||||
updateUserDetails,
|
||||
|
@ -15,15 +15,15 @@ export function SidebarProfile() {
|
||||
|
||||
const { t } = useTranslation("sidebar");
|
||||
|
||||
const { userDetails, friendRequests, showFriendsModal, fetchFriendRequests } =
|
||||
useUserDetails();
|
||||
const {
|
||||
userDetails,
|
||||
friendRequestCount,
|
||||
showFriendsModal,
|
||||
syncFriendRequests,
|
||||
} = useUserDetails();
|
||||
|
||||
const { gameRunning } = useAppSelector((state) => state.gameRunning);
|
||||
|
||||
const receivedRequests = useMemo(() => {
|
||||
return friendRequests.filter((request) => request.type === "RECEIVED");
|
||||
}, [friendRequests]);
|
||||
|
||||
const handleProfileClick = () => {
|
||||
if (userDetails === null) {
|
||||
window.electron.openAuthWindow();
|
||||
@ -35,7 +35,7 @@ export function SidebarProfile() {
|
||||
|
||||
useEffect(() => {
|
||||
pollingInterval.current = setInterval(() => {
|
||||
fetchFriendRequests();
|
||||
syncFriendRequests();
|
||||
}, LONG_POLLING_INTERVAL);
|
||||
|
||||
return () => {
|
||||
@ -43,7 +43,7 @@ export function SidebarProfile() {
|
||||
clearInterval(pollingInterval.current);
|
||||
}
|
||||
};
|
||||
}, [fetchFriendRequests]);
|
||||
}, [syncFriendRequests]);
|
||||
|
||||
const friendsButton = useMemo(() => {
|
||||
if (!userDetails) return null;
|
||||
@ -57,16 +57,16 @@ export function SidebarProfile() {
|
||||
}
|
||||
title={t("friends")}
|
||||
>
|
||||
{receivedRequests.length > 0 && (
|
||||
{friendRequestCount > 0 && (
|
||||
<small className={styles.friendsButtonBadge}>
|
||||
{receivedRequests.length > 99 ? "99+" : receivedRequests.length}
|
||||
{friendRequestCount > 99 ? "99+" : friendRequestCount}
|
||||
</small>
|
||||
)}
|
||||
|
||||
<PeopleIcon size={16} />
|
||||
</button>
|
||||
);
|
||||
}, [userDetails, t, receivedRequests, showFriendsModal]);
|
||||
}, [userDetails, t, friendRequestCount, showFriendsModal]);
|
||||
|
||||
return (
|
||||
<div className={styles.profileContainer}>
|
||||
@ -100,6 +100,7 @@ export function SidebarProfile() {
|
||||
textOverflow: "ellipsis",
|
||||
whiteSpace: "nowrap",
|
||||
width: "100%",
|
||||
textAlign: "left",
|
||||
}}
|
||||
>
|
||||
<small>{gameRunning.title}</small>
|
||||
|
@ -26,7 +26,7 @@ export const sidebar = recipe({
|
||||
paddingTop: `${SPACING_UNIT * 6}px`,
|
||||
},
|
||||
false: {
|
||||
paddingTop: `${SPACING_UNIT * 2}px`,
|
||||
paddingTop: `${SPACING_UNIT}px`,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
5
src/renderer/src/declaration.d.ts
vendored
5
src/renderer/src/declaration.d.ts
vendored
@ -23,6 +23,8 @@ import type {
|
||||
GameStats,
|
||||
TrendingGame,
|
||||
UserStats,
|
||||
UserDetails,
|
||||
FriendRequestSync,
|
||||
} from "@types";
|
||||
import type { DiskSpace } from "check-disk-space";
|
||||
|
||||
@ -153,7 +155,7 @@ declare global {
|
||||
) => Promise<void>;
|
||||
|
||||
/* Profile */
|
||||
getMe: () => Promise<UserProfile | null>;
|
||||
getMe: () => Promise<UserDetails | null>;
|
||||
undoFriendship: (userId: string) => Promise<void>;
|
||||
updateProfile: (
|
||||
updateProfile: UpdateProfileRequest
|
||||
@ -163,6 +165,7 @@ declare global {
|
||||
path: string
|
||||
) => Promise<{ imagePath: string; mimeType: string }>;
|
||||
getFriendRequests: () => Promise<FriendRequest[]>;
|
||||
syncFriendRequests: () => Promise<FriendRequestSync>;
|
||||
updateFriendRequest: (
|
||||
userId: string,
|
||||
action: FriendRequestAction
|
||||
|
@ -1,11 +1,12 @@
|
||||
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
|
||||
import { UserFriendModalTab } from "@renderer/pages/shared-modals/user-friend-modal";
|
||||
import type { FriendRequest, UserProfile } from "@types";
|
||||
import type { FriendRequest, UserDetails } from "@types";
|
||||
|
||||
export interface UserDetailsState {
|
||||
userDetails: UserProfile | null;
|
||||
userDetails: UserDetails | null;
|
||||
profileBackground: null | string;
|
||||
friendRequests: FriendRequest[];
|
||||
friendRequestCount: number;
|
||||
isFriendsModalVisible: boolean;
|
||||
friendRequetsModalTab: UserFriendModalTab | null;
|
||||
friendModalUserId: string;
|
||||
@ -15,6 +16,7 @@ const initialState: UserDetailsState = {
|
||||
userDetails: null,
|
||||
profileBackground: null,
|
||||
friendRequests: [],
|
||||
friendRequestCount: 0,
|
||||
isFriendsModalVisible: false,
|
||||
friendRequetsModalTab: null,
|
||||
friendModalUserId: "",
|
||||
@ -24,7 +26,7 @@ export const userDetailsSlice = createSlice({
|
||||
name: "user-details",
|
||||
initialState,
|
||||
reducers: {
|
||||
setUserDetails: (state, action: PayloadAction<UserProfile | null>) => {
|
||||
setUserDetails: (state, action: PayloadAction<UserDetails | null>) => {
|
||||
state.userDetails = action.payload;
|
||||
},
|
||||
setProfileBackground: (state, action: PayloadAction<string | null>) => {
|
||||
@ -33,6 +35,9 @@ export const userDetailsSlice = createSlice({
|
||||
setFriendRequests: (state, action: PayloadAction<FriendRequest[]>) => {
|
||||
state.friendRequests = action.payload;
|
||||
},
|
||||
setFriendRequestCount: (state, action: PayloadAction<number>) => {
|
||||
state.friendRequestCount = action.payload;
|
||||
},
|
||||
setFriendsModalVisible: (
|
||||
state,
|
||||
action: PayloadAction<{ initialTab: UserFriendModalTab; userId: string }>
|
||||
@ -52,6 +57,7 @@ export const {
|
||||
setUserDetails,
|
||||
setProfileBackground,
|
||||
setFriendRequests,
|
||||
setFriendRequestCount,
|
||||
setFriendsModalVisible,
|
||||
setFriendsModalHidden,
|
||||
} = userDetailsSlice.actions;
|
||||
|
@ -6,11 +6,12 @@ import {
|
||||
setFriendRequests,
|
||||
setFriendsModalVisible,
|
||||
setFriendsModalHidden,
|
||||
setFriendRequestCount,
|
||||
} from "@renderer/features";
|
||||
import type {
|
||||
FriendRequestAction,
|
||||
UpdateProfileRequest,
|
||||
UserProfile,
|
||||
UserDetails,
|
||||
} from "@types";
|
||||
import { UserFriendModalTab } from "@renderer/pages/shared-modals/user-friend-modal";
|
||||
|
||||
@ -21,6 +22,7 @@ export function useUserDetails() {
|
||||
userDetails,
|
||||
profileBackground,
|
||||
friendRequests,
|
||||
friendRequestCount,
|
||||
isFriendsModalVisible,
|
||||
friendModalUserId,
|
||||
friendRequetsModalTab,
|
||||
@ -40,7 +42,7 @@ export function useUserDetails() {
|
||||
}, [clearUserDetails]);
|
||||
|
||||
const updateUserDetails = useCallback(
|
||||
async (userDetails: UserProfile) => {
|
||||
async (userDetails: UserDetails) => {
|
||||
dispatch(setUserDetails(userDetails));
|
||||
|
||||
if (userDetails.profileImageUrl) {
|
||||
@ -83,7 +85,10 @@ export function useUserDetails() {
|
||||
const patchUser = useCallback(
|
||||
async (values: UpdateProfileRequest) => {
|
||||
const response = await window.electron.updateProfile(values);
|
||||
return updateUserDetails(response);
|
||||
return updateUserDetails({
|
||||
...response,
|
||||
username: userDetails?.username || "",
|
||||
});
|
||||
},
|
||||
[updateUserDetails]
|
||||
);
|
||||
@ -92,11 +97,21 @@ export function useUserDetails() {
|
||||
return window.electron
|
||||
.getFriendRequests()
|
||||
.then((friendRequests) => {
|
||||
syncFriendRequests();
|
||||
dispatch(setFriendRequests(friendRequests));
|
||||
})
|
||||
.catch(() => {});
|
||||
}, [dispatch]);
|
||||
|
||||
const syncFriendRequests = useCallback(async () => {
|
||||
return window.electron
|
||||
.syncFriendRequests()
|
||||
.then((sync) => {
|
||||
dispatch(setFriendRequestCount(sync.friendRequestCount));
|
||||
})
|
||||
.catch(() => {});
|
||||
}, [dispatch]);
|
||||
|
||||
const showFriendsModal = useCallback(
|
||||
(initialTab: UserFriendModalTab, userId: string) => {
|
||||
dispatch(setFriendsModalVisible({ initialTab, userId }));
|
||||
@ -140,6 +155,7 @@ export function useUserDetails() {
|
||||
userDetails,
|
||||
profileBackground,
|
||||
friendRequests,
|
||||
friendRequestCount,
|
||||
friendRequetsModalTab,
|
||||
isFriendsModalVisible,
|
||||
friendModalUserId,
|
||||
@ -152,6 +168,7 @@ export function useUserDetails() {
|
||||
patchUser,
|
||||
sendFriendRequest,
|
||||
fetchFriendRequests,
|
||||
syncFriendRequests,
|
||||
updateFriendRequestState,
|
||||
blockUser,
|
||||
unblockUser,
|
||||
|
@ -160,12 +160,15 @@ export function DownloadSettingsModal({
|
||||
))}
|
||||
</div>
|
||||
|
||||
{selectedDownloader && selectedDownloader !== Downloader.Torrent && (
|
||||
<p style={{ marginTop: `${SPACING_UNIT}px` }}>
|
||||
<span style={{ color: vars.color.warning }}>{t("warning")}</span>{" "}
|
||||
{t("hydra_needs_to_remain_open")}
|
||||
</p>
|
||||
)}
|
||||
{selectedDownloader != null &&
|
||||
selectedDownloader !== Downloader.Torrent && (
|
||||
<p style={{ marginTop: `${SPACING_UNIT}px` }}>
|
||||
<span style={{ color: vars.color.warning }}>
|
||||
{t("warning")}
|
||||
</span>{" "}
|
||||
{t("hydra_needs_to_remain_open")}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import { useContext, useState } from "react";
|
||||
import type { HowLongToBeatCategory, SteamAppDetails } from "@types";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Button } from "@renderer/components";
|
||||
@ -9,7 +9,7 @@ import { useFormat } from "@renderer/hooks";
|
||||
import { DownloadIcon, PeopleIcon } from "@primer/octicons-react";
|
||||
|
||||
export function Sidebar() {
|
||||
const [_howLongToBeat, setHowLongToBeat] = useState<{
|
||||
const [_howLongToBeat, _setHowLongToBeat] = useState<{
|
||||
isLoading: boolean;
|
||||
data: HowLongToBeatCategory[] | null;
|
||||
}>({ isLoading: true, data: null });
|
||||
@ -17,27 +17,26 @@ export function Sidebar() {
|
||||
const [activeRequirement, setActiveRequirement] =
|
||||
useState<keyof SteamAppDetails["pc_requirements"]>("minimum");
|
||||
|
||||
const { gameTitle, shopDetails, objectID, stats } =
|
||||
useContext(gameDetailsContext);
|
||||
const { gameTitle, shopDetails, stats } = useContext(gameDetailsContext);
|
||||
|
||||
const { t } = useTranslation("game_details");
|
||||
|
||||
const { numberFormatter } = useFormat();
|
||||
|
||||
useEffect(() => {
|
||||
if (objectID) {
|
||||
setHowLongToBeat({ isLoading: true, data: null });
|
||||
// useEffect(() => {
|
||||
// if (objectID) {
|
||||
// setHowLongToBeat({ isLoading: true, data: null });
|
||||
|
||||
window.electron
|
||||
.getHowLongToBeat(objectID, "steam", gameTitle)
|
||||
.then((howLongToBeat) => {
|
||||
setHowLongToBeat({ isLoading: false, data: howLongToBeat });
|
||||
})
|
||||
.catch(() => {
|
||||
setHowLongToBeat({ isLoading: false, data: null });
|
||||
});
|
||||
}
|
||||
}, [objectID, gameTitle]);
|
||||
// window.electron
|
||||
// .getHowLongToBeat(objectID, "steam", gameTitle)
|
||||
// .then((howLongToBeat) => {
|
||||
// setHowLongToBeat({ isLoading: false, data: howLongToBeat });
|
||||
// })
|
||||
// .catch(() => {
|
||||
// setHowLongToBeat({ isLoading: false, data: null });
|
||||
// });
|
||||
// }
|
||||
// }, [objectID, gameTitle]);
|
||||
|
||||
return (
|
||||
<aside className={styles.contentSidebar}>
|
||||
|
@ -170,6 +170,10 @@ export interface UserBlocks {
|
||||
blocks: UserFriend[];
|
||||
}
|
||||
|
||||
export interface FriendRequestSync {
|
||||
friendRequestCount: number;
|
||||
}
|
||||
|
||||
export interface FriendRequest {
|
||||
id: string;
|
||||
displayName: string;
|
||||
@ -190,23 +194,34 @@ export interface UserProfileCurrentGame extends Omit<GameRunning, "objectID"> {
|
||||
sessionDurationInSeconds: number;
|
||||
}
|
||||
|
||||
export type ProfileVisibility = "PUBLIC" | "PRIVATE" | "FRIENDS";
|
||||
|
||||
export interface UserDetails {
|
||||
id: string;
|
||||
username: string;
|
||||
displayName: string;
|
||||
profileImageUrl: string | null;
|
||||
profileVisibility: ProfileVisibility;
|
||||
bio: string;
|
||||
}
|
||||
|
||||
export interface UserProfile {
|
||||
id: string;
|
||||
displayName: string;
|
||||
profileImageUrl: string | null;
|
||||
profileVisibility: "PUBLIC" | "PRIVATE" | "FRIENDS";
|
||||
totalPlayTimeInSeconds: number;
|
||||
profileVisibility: ProfileVisibility;
|
||||
libraryGames: UserGame[];
|
||||
recentGames: UserGame[];
|
||||
friends: UserFriend[];
|
||||
totalFriends: number;
|
||||
relation: UserRelation | null;
|
||||
currentGame: UserProfileCurrentGame | null;
|
||||
bio: string;
|
||||
}
|
||||
|
||||
export interface UpdateProfileRequest {
|
||||
displayName?: string;
|
||||
profileVisibility?: "PUBLIC" | "PRIVATE" | "FRIENDS";
|
||||
profileVisibility?: ProfileVisibility;
|
||||
profileImageUrl?: string | null;
|
||||
bio?: string;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user