Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions client/src/components/forms/addNewProfile/AddNewProfile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { Label } from "@/components/ui/label";
import { generateRandomUsername } from "@/lib/GenerateRandomUsername";
import { useProfiles } from "./useProfiles";
import { useEffect, useState } from "react";
import SlimeArt from "../../../assets/SlimeArt.png";

interface AddNewProfileProps {
open: boolean;
Expand Down Expand Up @@ -54,7 +53,7 @@ export function AddNewProfile({ open, onOpenChange }: AddNewProfileProps) {
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [open]);
const [profilePicture, setProfilePicture] = useState(SlimeArt);
const [profilePicture, setProfilePicture] = useState<File | null>(null);

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
Expand Down Expand Up @@ -84,11 +83,15 @@ export function AddNewProfile({ open, onOpenChange }: AddNewProfileProps) {
try {
setIsSubmitting(true);
setError(null);
await addProfile({ name: normalizedUsername, avatar: profilePicture });
await addProfile({
name: normalizedUsername,
avatar: "",
avatarFile: profilePicture,
});
onOpenChange(false);
// Prepare a fresh suggestion for the next time the dialog opens.
setUsername("");
setProfilePicture(SlimeArt);
setProfilePicture(null);
} catch (err) {
console.error("Failed to add profile:", err);
setError("Failed to save profile. Please try again.");
Expand Down Expand Up @@ -141,7 +144,7 @@ export function AddNewProfile({ open, onOpenChange }: AddNewProfileProps) {
onChange={(e) => {
if (e.target.files) {
const file = e.target.files[0];
setProfilePicture(URL.createObjectURL(file));
setProfilePicture(file ?? null);
}
}}
/>
Expand Down
27 changes: 5 additions & 22 deletions client/src/components/forms/addNewProfile/ProfilesContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { AuthContext } from "@/context/AuthContext";
import {
useProfilesQuery,
useAddProfileMutation,
useUpdateProfileMutation,
useDeleteProfileMutation,
type ProfileResponse,
} from "@/hooks/useQueryHooks";
Expand All @@ -37,15 +36,14 @@ export function ProfileProvider({ children }: { children: React.ReactNode }) {

const { data: fetchedProfiles = [] } = useProfilesQuery(numUserId);
const addProfileMutation = useAddProfileMutation();
const updateProfileMutation = useUpdateProfileMutation();
const deleteProfileMutation = useDeleteProfileMutation();

const profiles = useMemo<Profile[]>(
() =>
fetchedProfiles.map((p: ProfileResponse) => ({
id: p.id,
name: p.display_name,
avatar: "",
avatar: p.avatar_url ?? `/api/profiles/${p.id}/pfp`,
coins: p.coins,
})),
[fetchedProfiles],
Expand All @@ -63,9 +61,6 @@ export function ProfileProvider({ children }: { children: React.ReactNode }) {
return profiles.find((p) => p.id === selectedProfileId) ?? profiles[0];
}, [profiles, selectedProfileId]);

console.warn("User: ", userId);
console.warn("Profiles: ", profiles);

// useEffect(() => {
// fetchProfiles();
// }, [fetchProfiles]);
Expand All @@ -79,33 +74,21 @@ export function ProfileProvider({ children }: { children: React.ReactNode }) {
await addProfileMutation.mutateAsync({
display_name: profile.name,
user_id: numUserId,
profile_picture: profile.avatarFile ?? null,
});

// No return needed - mutations handle cache invalidation
};

const removeProfile = async (name: string) => {
const profile = profiles.find((p) => p.name === name);
if (!profile || !profile.id) return;
if (!profile || !profile.id || !numUserId) return;

try {
// Rename to tombstone first (without optimistic rename in UI),
// then delete optimistically so tombstone text never flashes.
const tombstoneName =
`del_${profile.id}_${Date.now().toString(36)}`.slice(0, 20);

await updateProfileMutation.mutateAsync({
await deleteProfileMutation.mutateAsync({
profileId: profile.id,
payload: {
id: profile.id,
display_name: tombstoneName,
coins: profile.coins ?? 0,
},
optimistic: false,
invalidateAfterSuccess: false,
userId: numUserId,
});

await deleteProfileMutation.mutateAsync(profile.id);
} catch (error) {
console.error("Error deleting profile:", error);
throw error;
Expand Down
1 change: 1 addition & 0 deletions client/src/components/forms/addNewProfile/ProfilesTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export interface Profile {
id?: number;
name: string;
avatar: string;
avatarFile?: File | null;
coins?: number;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ import {
getTextColor,
} from "@/lib/utils";
import { useProfiles } from "@/components/forms/addNewProfile/useProfiles";
import { useUploadProfilePictureMutation } from "@/hooks/useQueryHooks";

export function ProfilePageContent() {
const pfpinputRef = useRef<HTMLInputElement>(null);
const { selectedProfile } = useProfiles();
const uploadProfilePictureMutation = useUploadProfilePictureMutation();
// local avatarSrc is only used when the user picks a local file (blob)
// otherwise we display the selectedProfile.avatar
const [avatarSrc, setAvatarSrc] = useState<string | null>(null);
Expand All @@ -31,14 +33,28 @@ export function ProfilePageContent() {
};
}, [avatarSrc]);

const onFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const onFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file) return;

if (!selectedProfile?.id) {
return;
}

const url = URL.createObjectURL(file);
setAvatarSrc((prev) => {
if (prev && prev.startsWith("blob:")) URL.revokeObjectURL(prev);
return url;
});

try {
await uploadProfilePictureMutation.mutateAsync({
profileId: selectedProfile.id,
file,
});
} catch (error) {
console.error("Failed to upload profile picture:", error);
}
};

const bgClass = getBackgroundClasses(
Expand Down
Loading
Loading