Skip to content

Commit

Permalink
Merge pull request #887 from hydralauncher/hyd-332-friendsblocks-list…
Browse files Browse the repository at this point in the history
…-pagination-and-set-privacy-settings

set profile visibility and list blocked users
  • Loading branch information
thegrannychaseroperation authored Aug 17, 2024
2 parents d098887 + 3febe9b commit 3a3b7b9
Show file tree
Hide file tree
Showing 24 changed files with 805 additions and 420 deletions.
14 changes: 13 additions & 1 deletion src/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,18 @@
"undo_friendship": "Undo friendship",
"request_accepted": "Request accepted",
"user_blocked_successfully": "User blocked successfully",
"user_block_modal_text": "This will block {{displayName}}"
"user_block_modal_text": "This will block {{displayName}}",
"settings": "Settings",
"public": "Public",
"private": "Private",
"friends_only": "Friends only",
"privacy": "Privacy",
"blocked_users": "Blocked users",
"unblock": "Unblock",
"no_friends_added": "You still don't have added friends",
"pending": "Pending",
"no_pending_invites": "You have no pending invites",
"no_blocked_users": "You have no blocked users",
"friend_code_copied": "Friend code copied"
}
}
14 changes: 13 additions & 1 deletion src/locales/pt/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,18 @@
"undo_friendship": "Desfazer amizade",
"request_accepted": "Pedido de amizade aceito",
"user_blocked_successfully": "Usuário bloqueado com sucesso",
"user_block_modal_text": "Bloquear {{displayName}}"
"user_block_modal_text": "Bloquear {{displayName}}",
"settings": "Configurações",
"privacy": "Privacidade",
"private": "Privado",
"friends_only": "Apenas amigos",
"public": "Público",
"blocked_users": "Usuários bloqueados",
"unblock": "Desbloquear",
"no_friends_added": "Você ainda não possui amigos adicionados",
"pending": "Pendentes",
"no_pending_invites": "Você não possui convites de amizade pendentes",
"no_blocked_users": "Você não tem nenhum usuário bloqueado",
"friend_code_copied": "Código de amigo copiado"
}
}
7 changes: 3 additions & 4 deletions src/main/events/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import "./auth/sign-out";
import "./auth/open-auth-window";
import "./auth/get-session-hash";
import "./user/get-user";
import "./user/get-user-blocks";
import "./user/block-user";
import "./user/unblock-user";
import "./user/get-user-friends";
Expand All @@ -52,11 +53,9 @@ import "./profile/undo-friendship";
import "./profile/update-friend-request";
import "./profile/update-profile";
import "./profile/send-friend-request";
import { isPortableVersion } from "@main/helpers";

ipcMain.handle("ping", () => "pong");
ipcMain.handle("getVersion", () => app.getVersion());
ipcMain.handle(
"isPortableVersion",
() => process.env.PORTABLE_EXECUTABLE_FILE != null
);
ipcMain.handle("isPortableVersion", () => isPortableVersion());
ipcMain.handle("getDefaultDownloadsPath", () => defaultDownloadsPath);
31 changes: 10 additions & 21 deletions src/main/events/profile/update-profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,22 @@ import axios from "axios";
import fs from "node:fs";
import path from "node:path";
import { fileTypeFromFile } from "file-type";
import { UserProfile } from "@types";

const patchUserProfile = async (
displayName: string,
profileImageUrl?: string
) => {
if (profileImageUrl) {
return HydraApi.patch("/profile", {
displayName,
profileImageUrl,
});
} else {
return HydraApi.patch("/profile", {
displayName,
});
}
import { UpdateProfileProps, UserProfile } from "@types";

const patchUserProfile = async (updateProfile: UpdateProfileProps) => {
return HydraApi.patch("/profile", updateProfile);
};

const updateProfile = async (
_event: Electron.IpcMainInvokeEvent,
displayName: string,
newProfileImagePath: string | null
updateProfile: UpdateProfileProps
): Promise<UserProfile> => {
if (!newProfileImagePath) {
return patchUserProfile(displayName);
if (!updateProfile.profileImageUrl) {
return patchUserProfile(updateProfile);
}

const newProfileImagePath = updateProfile.profileImageUrl;

const stats = fs.statSync(newProfileImagePath);
const fileBuffer = fs.readFileSync(newProfileImagePath);
const fileSizeInBytes = stats.size;
Expand All @@ -53,7 +42,7 @@ const updateProfile = async (
})
.catch(() => undefined);

return patchUserProfile(displayName, profileImageUrl);
return patchUserProfile({ ...updateProfile, profileImageUrl });
};

registerEvent("updateProfile", updateProfile);
13 changes: 13 additions & 0 deletions src/main/events/user/get-user-blocks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { registerEvent } from "../register-event";
import { HydraApi } from "@main/services";
import { UserBlocks } from "@types";

export const getUserBlocks = async (
_event: Electron.IpcMainInvokeEvent,
take: number,
skip: number
): Promise<UserBlocks> => {
return HydraApi.get(`/profile/blocks`, { take, skip });
};

registerEvent("getUserBlocks", getUserBlocks);
3 changes: 3 additions & 0 deletions src/main/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,7 @@ export const requestWebPage = async (url: string) => {
.then((response) => response.data);
};

export const isPortableVersion = () =>
process.env.PORTABLE_EXECUTABLE_FILE != null;

export * from "./download-source";
7 changes: 5 additions & 2 deletions src/preload/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type {
StartGameDownloadPayload,
GameRunning,
FriendRequestAction,
UpdateProfileProps,
} from "@types";

contextBridge.exposeInMainWorld("electron", {
Expand Down Expand Up @@ -137,8 +138,8 @@ contextBridge.exposeInMainWorld("electron", {
getMe: () => ipcRenderer.invoke("getMe"),
undoFriendship: (userId: string) =>
ipcRenderer.invoke("undoFriendship", userId),
updateProfile: (displayName: string, newProfileImagePath: string | null) =>
ipcRenderer.invoke("updateProfile", displayName, newProfileImagePath),
updateProfile: (updateProfile: UpdateProfileProps) =>
ipcRenderer.invoke("updateProfile", updateProfile),
getFriendRequests: () => ipcRenderer.invoke("getFriendRequests"),
updateFriendRequest: (userId: string, action: FriendRequestAction) =>
ipcRenderer.invoke("updateFriendRequest", userId, action),
Expand All @@ -151,6 +152,8 @@ contextBridge.exposeInMainWorld("electron", {
unblockUser: (userId: string) => ipcRenderer.invoke("unblockUser", userId),
getUserFriends: (userId: string, take: number, skip: number) =>
ipcRenderer.invoke("getUserFriends", userId, take, skip),
getUserBlocks: (take: number, skip: number) =>
ipcRenderer.invoke("getUserBlocks", take, skip),

/* Auth */
signOut: () => ipcRenderer.invoke("signOut"),
Expand Down
10 changes: 8 additions & 2 deletions src/renderer/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export function App() {
fetchFriendRequests();
}
});
}, [fetchUserDetails, updateUserDetails, dispatch]);
}, [fetchUserDetails, fetchFriendRequests, updateUserDetails, dispatch]);

const onSignIn = useCallback(() => {
fetchUserDetails().then((response) => {
Expand All @@ -118,7 +118,13 @@ export function App() {
showSuccessToast(t("successfully_signed_in"));
}
});
}, [fetchUserDetails, t, showSuccessToast, updateUserDetails]);
}, [
fetchUserDetails,
fetchFriendRequests,
t,
showSuccessToast,
updateUserDetails,
]);

useEffect(() => {
const unsubscribe = window.electron.onGamesRunning((gamesRunning) => {
Expand Down
7 changes: 3 additions & 4 deletions src/renderer/src/declaration.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type {
FriendRequest,
FriendRequestAction,
UserFriends,
UserBlocks,
} from "@types";
import type { DiskSpace } from "check-disk-space";

Expand Down Expand Up @@ -135,14 +136,12 @@ declare global {
take: number,
skip: number
) => Promise<UserFriends>;
getUserBlocks: (take: number, skip: number) => Promise<UserBlocks>;

/* Profile */
getMe: () => Promise<UserProfile | null>;
undoFriendship: (userId: string) => Promise<void>;
updateProfile: (
displayName: string,
newProfileImagePath: string | null
) => Promise<UserProfile>;
updateProfile: (updateProfile: UpdateProfileProps) => Promise<UserProfile>;
getFriendRequests: () => Promise<FriendRequest[]>;
updateFriendRequest: (
userId: string,
Expand Down
18 changes: 9 additions & 9 deletions src/renderer/src/hooks/use-user-details.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import {
setFriendsModalHidden,
} from "@renderer/features";
import { profileBackgroundFromProfileImage } from "@renderer/helpers";
import { FriendRequestAction, UserDetails } from "@types";
import { FriendRequestAction, UpdateProfileProps, UserDetails } from "@types";
import { UserFriendModalTab } from "@renderer/pages/shared-modals/user-friend-modal";
import { logger } from "@renderer/logger";

export function useUserDetails() {
const dispatch = useAppDispatch();
Expand Down Expand Up @@ -43,7 +44,10 @@ export function useUserDetails() {
if (userDetails.profileImageUrl) {
const profileBackground = await profileBackgroundFromProfileImage(
userDetails.profileImageUrl
);
).catch((err) => {
logger.error("profileBackgroundFromProfileImage", err);
return `#151515B3`;
});
dispatch(setProfileBackground(profileBackground));

window.localStorage.setItem(
Expand Down Expand Up @@ -74,12 +78,8 @@ export function useUserDetails() {
}, [clearUserDetails]);

const patchUser = useCallback(
async (displayName: string, imageProfileUrl: string | null) => {
const response = await window.electron.updateProfile(
displayName,
imageProfileUrl
);

async (props: UpdateProfileProps) => {
const response = await window.electron.updateProfile(props);
return updateUserDetails(response);
},
[updateUserDetails]
Expand All @@ -99,7 +99,7 @@ export function useUserDetails() {
dispatch(setFriendsModalVisible({ initialTab, userId }));
fetchFriendRequests();
},
[dispatch]
[dispatch, fetchFriendRequests]
);

const hideFriendsModal = useCallback(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,33 @@ import {
XCircleIcon,
} from "@primer/octicons-react";
import * as styles from "./user-friend-modal.css";
import cn from "classnames";
import { SPACING_UNIT } from "@renderer/theme.css";
import { useTranslation } from "react-i18next";

export type UserFriendItemProps = {
userId: string;
profileImageUrl: string | null;
displayName: string;
onClickItem: (userId: string) => void;
} & (
| { type: "ACCEPTED"; onClickUndoFriendship: (userId: string) => void }
| {
type: "ACCEPTED";
onClickUndoFriendship: (userId: string) => void;
onClickItem: (userId: string) => void;
}
| { type: "BLOCKED"; onClickUnblock: (userId: string) => void }
| {
type: "SENT" | "RECEIVED";
onClickCancelRequest: (userId: string) => void;
onClickAcceptRequest: (userId: string) => void;
onClickRefuseRequest: (userId: string) => void;
onClickItem: (userId: string) => void;
}
| { type: null }
| { type: null; onClickItem: (userId: string) => void }
);

export const UserFriendItem = (props: UserFriendItemProps) => {
const { t } = useTranslation("user_profile");
const { userId, profileImageUrl, displayName, type, onClickItem } = props;
const { userId, profileImageUrl, displayName, type } = props;

const getRequestDescription = () => {
if (type === "ACCEPTED" || type === null) return null;
Expand Down Expand Up @@ -86,15 +90,69 @@ export const UserFriendItem = (props: UserFriendItemProps) => {
);
}

if (type === "BLOCKED") {
return (
<button
className={styles.cancelRequestButton}
onClick={() => props.onClickUnblock(userId)}
title={t("unblock")}
>
<XCircleIcon size={28} />
</button>
);
}

return null;
};

if (type === "BLOCKED") {
return (
<div className={styles.friendListContainer}>
<div className={styles.friendListButton} style={{ cursor: "inherit" }}>
<div className={styles.friendAvatarContainer}>
{profileImageUrl ? (
<img
className={styles.profileAvatar}
alt={displayName}
src={profileImageUrl}
/>
) : (
<PersonIcon size={24} />
)}
</div>
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
flex: "1",
minWidth: 0,
}}
>
<p className={styles.friendListDisplayName}>{displayName}</p>
</div>
</div>

<div
style={{
position: "absolute",
right: "8px",
display: "flex",
gap: `${SPACING_UNIT}px`,
}}
>
{getRequestActions()}
</div>
</div>
);
}

return (
<div className={cn(styles.friendListContainer, styles.profileContentBox)}>
<div className={styles.friendListContainer}>
<button
type="button"
className={styles.friendListButton}
onClick={() => onClickItem(userId)}
onClick={() => props.onClickItem(userId)}
>
<div className={styles.friendAvatarContainer}>
{profileImageUrl ? (
Expand Down
Loading

0 comments on commit 3a3b7b9

Please sign in to comment.