mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-03-09 15:40:26 +00:00
feat: adding wine prefix
This commit is contained in:
parent
a498f9dd80
commit
0e5d37a3a0
26 changed files with 423 additions and 130 deletions
|
@ -26,7 +26,7 @@ export interface CloudSyncContext {
|
|||
backupState: CloudSyncState;
|
||||
setShowCloudSyncModal: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
downloadGameArtifact: (gameArtifactId: string) => Promise<void>;
|
||||
uploadSaveGame: () => Promise<void>;
|
||||
uploadSaveGame: (downloadOptionTitle: string | null) => Promise<void>;
|
||||
deleteGameArtifact: (gameArtifactId: string) => Promise<void>;
|
||||
setShowCloudSyncFilesModal: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
getGameBackupPreview: () => Promise<void>;
|
||||
|
@ -108,10 +108,13 @@ export function CloudSyncContextProvider({
|
|||
]);
|
||||
}, [objectId, shop]);
|
||||
|
||||
const uploadSaveGame = useCallback(async () => {
|
||||
setUploadingBackup(true);
|
||||
window.electron.uploadSaveGame(objectId, shop);
|
||||
}, [objectId, shop]);
|
||||
const uploadSaveGame = useCallback(
|
||||
async (downloadOptionTitle: string | null) => {
|
||||
setUploadingBackup(true);
|
||||
window.electron.uploadSaveGame(objectId, shop, downloadOptionTitle);
|
||||
},
|
||||
[objectId, shop]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const removeUploadCompleteListener = window.electron.onUploadComplete(
|
||||
|
|
|
@ -3,6 +3,7 @@ import {
|
|||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
|
@ -45,6 +46,7 @@ export const gameDetailsContext = createContext<GameDetailsContext>({
|
|||
stats: null,
|
||||
achievements: null,
|
||||
hasNSFWContentBlocked: false,
|
||||
lastDownloadedOption: null,
|
||||
setGameColor: () => {},
|
||||
selectGameExecutable: async () => null,
|
||||
updateGame: async () => {},
|
||||
|
@ -199,6 +201,19 @@ export function GameDetailsContextProvider({
|
|||
};
|
||||
}, [game?.id, isGameRunning, updateGame]);
|
||||
|
||||
const lastDownloadedOption = useMemo(() => {
|
||||
if (game?.uri) {
|
||||
const repack = repacks.find((repack) =>
|
||||
repack.uris.some((uri) => uri.includes(game.uri!))
|
||||
);
|
||||
|
||||
if (!repack) return null;
|
||||
return repack;
|
||||
}
|
||||
|
||||
return null;
|
||||
}, [game?.uri, repacks]);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = window.electron.onUpdateAchievements(
|
||||
objectId,
|
||||
|
@ -259,6 +274,7 @@ export function GameDetailsContextProvider({
|
|||
stats,
|
||||
achievements,
|
||||
hasNSFWContentBlocked,
|
||||
lastDownloadedOption,
|
||||
setHasNSFWContentBlocked,
|
||||
setGameColor,
|
||||
selectGameExecutable,
|
||||
|
|
|
@ -22,6 +22,7 @@ export interface GameDetailsContext {
|
|||
stats: GameStats | null;
|
||||
achievements: UserAchievement[] | null;
|
||||
hasNSFWContentBlocked: boolean;
|
||||
lastDownloadedOption: GameRepack | null;
|
||||
setGameColor: React.Dispatch<React.SetStateAction<string>>;
|
||||
selectGameExecutable: () => Promise<string | null>;
|
||||
updateGame: () => Promise<void>;
|
||||
|
|
7
src/renderer/src/declaration.d.ts
vendored
7
src/renderer/src/declaration.d.ts
vendored
|
@ -91,6 +91,7 @@ declare global {
|
|||
) => Promise<void>;
|
||||
createGameShortcut: (id: number) => Promise<boolean>;
|
||||
updateExecutablePath: (id: number, executablePath: string) => Promise<void>;
|
||||
selectGameWinePrefix: (id: number, winePrefixPath: string) => Promise<void>;
|
||||
verifyExecutablePathInUse: (executablePath: string) => Promise<Game>;
|
||||
getLibrary: () => Promise<LibraryGame[]>;
|
||||
openGameInstaller: (gameId: number) => Promise<boolean>;
|
||||
|
@ -125,7 +126,11 @@ declare global {
|
|||
getDiskFreeSpace: (path: string) => Promise<DiskSpace>;
|
||||
|
||||
/* Cloud save */
|
||||
uploadSaveGame: (objectId: string, shop: GameShop) => Promise<void>;
|
||||
uploadSaveGame: (
|
||||
objectId: string,
|
||||
shop: GameShop,
|
||||
downloadOptionTitle: string | null
|
||||
) => Promise<void>;
|
||||
downloadGameArtifact: (
|
||||
objectId: string,
|
||||
shop: GameShop,
|
||||
|
|
|
@ -134,9 +134,11 @@ export const achievementsProgressBar = style({
|
|||
transition: "all ease 0.2s",
|
||||
"::-webkit-progress-bar": {
|
||||
backgroundColor: "rgba(255, 255, 255, 0.15)",
|
||||
borderRadius: "4px",
|
||||
},
|
||||
"::-webkit-progress-value": {
|
||||
backgroundColor: vars.color.muted,
|
||||
borderRadius: "4px",
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Modal, ModalProps } from "@renderer/components";
|
||||
import { Button, Modal, ModalProps, TextField } from "@renderer/components";
|
||||
import { useContext, useMemo } from "react";
|
||||
import { cloudSyncContext } from "@renderer/context";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export interface CloudSyncFilesModalProps
|
||||
extends Omit<ModalProps, "children" | "title"> {}
|
||||
|
@ -11,6 +12,8 @@ export function CloudSyncFilesModal({
|
|||
}: CloudSyncFilesModalProps) {
|
||||
const { backupPreview } = useContext(cloudSyncContext);
|
||||
|
||||
const { t } = useTranslation("game_details");
|
||||
|
||||
const files = useMemo(() => {
|
||||
if (!backupPreview) {
|
||||
return [];
|
||||
|
@ -24,6 +27,24 @@ export function CloudSyncFilesModal({
|
|||
});
|
||||
}, [backupPreview]);
|
||||
|
||||
const handleChangeExecutableLocation = async () => {
|
||||
const path = await selectGameExecutable();
|
||||
|
||||
if (path) {
|
||||
const gameUsingPath =
|
||||
await window.electron.verifyExecutablePathInUse(path);
|
||||
|
||||
if (gameUsingPath) {
|
||||
showErrorToast(
|
||||
t("executable_path_in_use", { game: gameUsingPath.title })
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
window.electron.updateExecutablePath(game.id, path).then(updateGame);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
visible={visible}
|
||||
|
@ -31,11 +52,11 @@ export function CloudSyncFilesModal({
|
|||
description="Escolha quais diretórios serão sincronizados"
|
||||
onClose={onClose}
|
||||
>
|
||||
{/* <div className={styles.downloaders}>
|
||||
{/* <div>
|
||||
{["AUTOMATIC", "CUSTOM"].map((downloader) => (
|
||||
<Button
|
||||
key={downloader}
|
||||
className={styles.downloaderOption}
|
||||
// className={styles.downloaderOption}
|
||||
theme={selectedDownloader === downloader ? "primary" : "outline"}
|
||||
disabled={
|
||||
downloader === Downloader.RealDebrid &&
|
||||
|
@ -51,6 +72,23 @@ export function CloudSyncFilesModal({
|
|||
))}
|
||||
</div> */}
|
||||
|
||||
<TextField
|
||||
// value={game.executablePath || ""}
|
||||
readOnly
|
||||
theme="dark"
|
||||
disabled
|
||||
placeholder={t("no_directory_selected")}
|
||||
rightContent={
|
||||
<Button
|
||||
type="button"
|
||||
theme="outline"
|
||||
onClick={handleChangeExecutableLocation}
|
||||
>
|
||||
{t("select_directory")}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
|
|
@ -38,3 +38,14 @@ export const syncIcon = style({
|
|||
animationIterationCount: "infinite",
|
||||
animationTimingFunction: "linear",
|
||||
});
|
||||
|
||||
export const progress = style({
|
||||
width: "100%",
|
||||
height: "5px",
|
||||
"::-webkit-progress-bar": {
|
||||
backgroundColor: vars.color.darkBackground,
|
||||
},
|
||||
"::-webkit-progress-value": {
|
||||
backgroundColor: vars.color.muted,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
import { Button, Modal, ModalProps } from "@renderer/components";
|
||||
import {
|
||||
Button,
|
||||
ConfirmationModal,
|
||||
Modal,
|
||||
ModalProps,
|
||||
} from "@renderer/components";
|
||||
import { useContext, useEffect, useMemo, useState } from "react";
|
||||
import { cloudSyncContext, gameDetailsContext } from "@renderer/context";
|
||||
|
||||
|
@ -10,6 +15,7 @@ import {
|
|||
ClockIcon,
|
||||
DeviceDesktopIcon,
|
||||
HistoryIcon,
|
||||
InfoIcon,
|
||||
SyncIcon,
|
||||
TrashIcon,
|
||||
UploadIcon,
|
||||
|
@ -43,7 +49,8 @@ export function CloudSyncModal({ visible, onClose }: CloudSyncModalProps) {
|
|||
setShowCloudSyncFilesModal,
|
||||
} = useContext(cloudSyncContext);
|
||||
|
||||
const { objectId, shop, gameTitle } = useContext(gameDetailsContext);
|
||||
const { objectId, shop, gameTitle, lastDownloadedOption } =
|
||||
useContext(gameDetailsContext);
|
||||
|
||||
const { showSuccessToast, showErrorToast } = useToast();
|
||||
|
||||
|
@ -140,112 +147,137 @@ export function CloudSyncModal({ visible, onClose }: CloudSyncModalProps) {
|
|||
const disableActions = uploadingBackup || restoringBackup || deletingArtifact;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
visible={visible}
|
||||
title={t("cloud_save")}
|
||||
description={t("cloud_save_description")}
|
||||
onClose={onClose}
|
||||
large
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
marginBottom: 24,
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex", gap: 4, flexDirection: "column" }}>
|
||||
<h2>{gameTitle}</h2>
|
||||
<p>{backupStateLabel}</p>
|
||||
<>
|
||||
{/* <ConfirmationModal
|
||||
confirmButtonLabel="confirm"
|
||||
cancelButtonLabel="cancel"
|
||||
descriptionText="description"
|
||||
title="title"
|
||||
onConfirm={() => {}}
|
||||
onClose={() => {}}
|
||||
visible
|
||||
/> */}
|
||||
|
||||
<button
|
||||
<Modal
|
||||
visible={visible}
|
||||
title={t("cloud_save")}
|
||||
description={t("cloud_save_description")}
|
||||
onClose={onClose}
|
||||
large
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
marginBottom: 24,
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex", gap: 4, flexDirection: "column" }}>
|
||||
<h2>{gameTitle}</h2>
|
||||
<p>{backupStateLabel}</p>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
style={{
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
alignSelf: "flex-start",
|
||||
fontSize: 14,
|
||||
cursor: "pointer",
|
||||
textDecoration: "underline",
|
||||
color: vars.color.body,
|
||||
}}
|
||||
onClick={() => setShowCloudSyncFilesModal(true)}
|
||||
>
|
||||
Gerenciar arquivos
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
type="button"
|
||||
style={{
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
alignSelf: "flex-start",
|
||||
fontSize: 14,
|
||||
cursor: "pointer",
|
||||
textDecoration: "underline",
|
||||
color: vars.color.body,
|
||||
}}
|
||||
onClick={() => setShowCloudSyncFilesModal(true)}
|
||||
onClick={() => uploadSaveGame(lastDownloadedOption?.title ?? null)}
|
||||
disabled={disableActions || !backupPreview}
|
||||
>
|
||||
Gerenciar arquivos
|
||||
</button>
|
||||
<UploadIcon />
|
||||
{t("create_backup")}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
type="button"
|
||||
onClick={uploadSaveGame}
|
||||
disabled={disableActions || !backupPreview}
|
||||
>
|
||||
<UploadIcon />
|
||||
{t("create_backup")}
|
||||
</Button>
|
||||
</div>
|
||||
<div style={{ display: "flex", justifyContent: "space-between" }}>
|
||||
<div
|
||||
style={{
|
||||
marginBottom: 16,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: SPACING_UNIT,
|
||||
}}
|
||||
>
|
||||
<h2>{t("backups")}</h2>
|
||||
<small>{artifacts.length} / 2</small>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
marginBottom: 16,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: SPACING_UNIT,
|
||||
}}
|
||||
>
|
||||
<h2>{t("backups")}</h2>
|
||||
<small>{artifacts.length} / 2</small>
|
||||
</div>
|
||||
<div style={{ display: "flex", flexDirection: "column" }}>
|
||||
<small>Espaço usado</small>
|
||||
<progress className={styles.progress} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul className={styles.artifacts}>
|
||||
{artifacts.map((artifact) => (
|
||||
<li key={artifact.id} className={styles.artifactButton}>
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 8,
|
||||
}}
|
||||
>
|
||||
<h3>Backup do dia {format(artifact.createdAt, "dd/MM")}</h3>
|
||||
<small>{formatBytes(artifact.artifactLengthInBytes)}</small>
|
||||
<ul className={styles.artifacts}>
|
||||
{artifacts.map((artifact) => (
|
||||
<li key={artifact.id} className={styles.artifactButton}>
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 8,
|
||||
marginBottom: 4,
|
||||
}}
|
||||
>
|
||||
<h3>Backup do dia {format(artifact.createdAt, "dd/MM")}</h3>
|
||||
<small>{formatBytes(artifact.artifactLengthInBytes)}</small>
|
||||
</div>
|
||||
|
||||
<span style={{ display: "flex", alignItems: "center", gap: 8 }}>
|
||||
<DeviceDesktopIcon size={14} />
|
||||
{artifact.hostname}
|
||||
</span>
|
||||
|
||||
<span style={{ display: "flex", alignItems: "center", gap: 8 }}>
|
||||
<InfoIcon size={14} />
|
||||
{artifact.downloadOptionTitle ?? t("no_download_option_info")}
|
||||
</span>
|
||||
|
||||
<span style={{ display: "flex", alignItems: "center", gap: 8 }}>
|
||||
<ClockIcon size={14} />
|
||||
{format(artifact.createdAt, "dd/MM/yyyy HH:mm:ss")}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<span style={{ display: "flex", alignItems: "center", gap: 8 }}>
|
||||
<DeviceDesktopIcon size={14} />
|
||||
{artifact.hostname}
|
||||
</span>
|
||||
|
||||
<span style={{ display: "flex", alignItems: "center", gap: 8 }}>
|
||||
<ClockIcon size={14} />
|
||||
{format(artifact.createdAt, "dd/MM/yyyy HH:mm:ss")}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div style={{ display: "flex", gap: 8, alignItems: "center" }}>
|
||||
<Button
|
||||
type="button"
|
||||
onClick={() => handleBackupInstallClick(artifact.id)}
|
||||
disabled={disableActions}
|
||||
>
|
||||
<HistoryIcon />
|
||||
{t("install_backup")}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
onClick={() => handleDeleteArtifactClick(artifact.id)}
|
||||
theme="danger"
|
||||
disabled={disableActions}
|
||||
>
|
||||
<TrashIcon />
|
||||
{t("delete_backup")}
|
||||
</Button>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Modal>
|
||||
<div style={{ display: "flex", gap: 8, alignItems: "center" }}>
|
||||
<Button
|
||||
type="button"
|
||||
onClick={() => handleBackupInstallClick(artifact.id)}
|
||||
disabled={disableActions}
|
||||
>
|
||||
<HistoryIcon />
|
||||
{t("install_backup")}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
onClick={() => handleDeleteArtifactClick(artifact.id)}
|
||||
theme="danger"
|
||||
disabled={disableActions}
|
||||
>
|
||||
<TrashIcon />
|
||||
{t("delete_backup")}
|
||||
</Button>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import { gameDetailsContext } from "@renderer/context";
|
|||
import { DeleteGameModal } from "@renderer/pages/downloads/delete-game-modal";
|
||||
import { useDownload, useToast } from "@renderer/hooks";
|
||||
import { RemoveGameFromLibraryModal } from "./remove-from-library-modal";
|
||||
import { FileDirectoryIcon, FileIcon } from "@primer/octicons-react";
|
||||
|
||||
export interface GameOptionsModalProps {
|
||||
visible: boolean;
|
||||
|
@ -94,6 +95,20 @@ export function GameOptionsModal({
|
|||
await window.electron.openGameExecutablePath(game.id);
|
||||
};
|
||||
|
||||
const handleChangeWinePrefixPath = async () => {
|
||||
const { filePaths } = await window.electron.showOpenDialog({
|
||||
properties: ["openDirectory"],
|
||||
});
|
||||
|
||||
if (filePaths && filePaths.length > 0) {
|
||||
await window.electron.selectGameWinePrefix(game.id, filePaths[0]);
|
||||
await updateGame();
|
||||
}
|
||||
};
|
||||
|
||||
const shouldShowWinePrefixConfiguration =
|
||||
window.electron.platform === "darwin";
|
||||
|
||||
return (
|
||||
<>
|
||||
<DeleteGameModal
|
||||
|
@ -135,6 +150,7 @@ export function GameOptionsModal({
|
|||
theme="outline"
|
||||
onClick={handleChangeExecutableLocation}
|
||||
>
|
||||
<FileIcon />
|
||||
{t("select_executable")}
|
||||
</Button>
|
||||
}
|
||||
|
@ -155,12 +171,41 @@ export function GameOptionsModal({
|
|||
</div>
|
||||
)}
|
||||
|
||||
{shouldShowWinePrefixConfiguration && (
|
||||
<div className={styles.optionsContainer}>
|
||||
<div className={styles.gameOptionHeader}>
|
||||
<h2>{t("wine_prefix")}</h2>
|
||||
<h4 className={styles.gameOptionHeaderDescription}>
|
||||
{t("wine_prefix_description")}
|
||||
</h4>
|
||||
</div>
|
||||
<TextField
|
||||
value={game.winePrefixPath || ""}
|
||||
readOnly
|
||||
theme="dark"
|
||||
disabled
|
||||
placeholder={t("no_directory_selected")}
|
||||
rightContent={
|
||||
<Button
|
||||
type="button"
|
||||
theme="outline"
|
||||
onClick={handleChangeWinePrefixPath}
|
||||
>
|
||||
<FileDirectoryIcon />
|
||||
{t("select_executable")}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={styles.gameOptionHeader}>
|
||||
<h2>{t("downloads_secion_title")}</h2>
|
||||
<h4 className={styles.gameOptionHeaderDescription}>
|
||||
{t("downloads_section_description")}
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<div className={styles.gameOptionRow}>
|
||||
<Button
|
||||
onClick={() => setShowRepacksModal(true)}
|
||||
|
@ -179,12 +224,14 @@ export function GameOptionsModal({
|
|||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={styles.gameOptionHeader}>
|
||||
<h2>{t("danger_zone_section_title")}</h2>
|
||||
<h4 className={styles.gameOptionHeaderDescription}>
|
||||
{t("danger_zone_section_description")}
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<div className={styles.gameOptionRow}>
|
||||
<Button
|
||||
onClick={() => setShowRemoveGameModal(true)}
|
||||
|
|
|
@ -196,8 +196,10 @@ export const achievementsProgressBar = style({
|
|||
transition: "all ease 0.2s",
|
||||
"::-webkit-progress-bar": {
|
||||
backgroundColor: "rgba(255, 255, 255, 0.15)",
|
||||
borderRadius: "4px",
|
||||
},
|
||||
"::-webkit-progress-value": {
|
||||
backgroundColor: vars.color.muted,
|
||||
borderRadius: "4px",
|
||||
},
|
||||
});
|
||||
|
|
|
@ -70,7 +70,7 @@ export const heroPanel = style({
|
|||
|
||||
export const userInformation = style({
|
||||
display: "flex",
|
||||
padding: `${SPACING_UNIT * 6}px ${SPACING_UNIT * 3}px`,
|
||||
padding: `${SPACING_UNIT * 7}px ${SPACING_UNIT * 3}px`,
|
||||
alignItems: "center",
|
||||
gap: `${SPACING_UNIT * 2}px`,
|
||||
});
|
||||
|
|
|
@ -33,7 +33,7 @@ type FriendAction =
|
|||
| ("BLOCK" | "UNDO_FRIENDSHIP" | "SEND");
|
||||
|
||||
const backgroundImageLayer =
|
||||
"linear-gradient(135deg, rgb(0 0 0 / 50%), rgb(0 0 0 / 60%))";
|
||||
"linear-gradient(135deg, rgb(0 0 0 / 40%), rgb(0 0 0 / 30%))";
|
||||
|
||||
export function ProfileHero() {
|
||||
const [showEditProfileModal, setShowEditProfileModal] = useState(false);
|
||||
|
|
|
@ -12,7 +12,7 @@ export function UploadBackgroundImageButton() {
|
|||
const { hasActiveSubscription } = useUserDetails();
|
||||
|
||||
const { isMe, setSelectedBackgroundImage } = useContext(userProfileContext);
|
||||
const { patchUser } = useUserDetails();
|
||||
const { patchUser, fetchUserDetails } = useUserDetails();
|
||||
|
||||
const { showSuccessToast } = useToast();
|
||||
|
||||
|
@ -37,6 +37,7 @@ export function UploadBackgroundImageButton() {
|
|||
await patchUser({ backgroundImageUrl: path });
|
||||
|
||||
showSuccessToast("Background image updated");
|
||||
await fetchUserDetails();
|
||||
}
|
||||
} finally {
|
||||
setIsUploadingBackgorundImage(false);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue