Skip to content

Remove the old avatar JSON file#315

Open
lyx2-2 wants to merge 4 commits into
mainfrom
azure-avatars-upload-support
Open

Remove the old avatar JSON file#315
lyx2-2 wants to merge 4 commits into
mainfrom
azure-avatars-upload-support

Conversation

@lyx2-2
Copy link
Copy Markdown
Collaborator

@lyx2-2 lyx2-2 commented Apr 14, 2026

No description provided.

@lyx2-2 lyx2-2 requested a review from nxn-nicole April 14, 2026 01:17
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 14, 2026

Test Results

24 tests   24 ✅  2s ⏱️
 1 suites   0 💤
 1 files     0 ❌

Results for commit 196bd4a.

♻️ This comment has been updated with latest results.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 14, 2026

Package Line Rate Branch Rate Complexity Health
BlotzTask 3% 14% 1525
BlotzTask 3% 14% 1525
Summary 3% (1778 / 60624) 14% (196 / 1400) 3050

@Phenomenax Phenomenax self-requested a review April 18, 2026 06:27
Comment thread blotztask-mobile/src/feature/settings/hooks/useAvatarListQuery.ts Outdated
Comment thread blotztask-mobile/src/feature/settings/hooks/useAvatarListQuery.ts Outdated
Comment thread blotztask-mobile/src/shared/constants/query-key-factory.ts Outdated
@lyx2-2 lyx2-2 force-pushed the azure-avatars-upload-support branch from d881d1e to 66aa6d3 Compare April 23, 2026 00:37
export function useAvatarListQuery() {
const avatarListQuery = useQuery<AvatarListDTO>({
queryKey: avatarKeys.all,
queryFn: () => fetchAvatarList(),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the data you fetched from Azure is consistent, so usually you don't need to fetch again. You can add
staleTime: Infinity,
gcTime: Infinity,

const isSelected = avatar.url === selectedAvatarUrl;
{isAvatarListError ? (
<Text className="w-full text-center text-base font-baloo text-secondary">
Failed to load avatars.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should not hard code the string here add Chinese language support.

if (avatars.some((avatar) => avatar.url === pictureUrl)) {
setSelectedAvatarUrl(pictureUrl);
}
}, [avatars, userProfile?.pictureUrl]);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this useEffect doing here? If it's just to fetch the default value for avatar URL, I think you can just add it inside of useState instead of using another useEffect here.

const { userProfile } = useUserProfile();
const { avatars, isAvatarListLoading, isAvatarListError } = useAvatarListQuery();

const [selectedAvatarUrl, setSelectedAvatarUrl] = useState<string | null>(null);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we already have the avatar URL set in user profile, so you don't need to add another local state of avatar URL here.If you need the URL, just read it from the user profile.

const handleAvatarSelect = async (avatar: AvatarDTO) => {
if (isUserUpdating) return;

const previousAvatarUrl = selectedAvatarUrl;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think it's not very useful to set a previous avatar URL here because you already use updateStateAvatar. Since you're using the useMutation function, if it fails on error, it will roll back itself. The user profile won't be updated, so you still can read the old cache value.

isOnBoarded: userProfile?.isOnBoarded ?? false,
});
} catch {
setSelectedAvatarUrl(previousAvatarUrl);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import { Pressable, Text, View } from "react-native";
import { ReturnButton } from "@/shared/components/return-button";
import { useUserProfileMutation } from "@/feature/settings/hooks/useUserProfileMutation";
import { useUserProfile } from "@/shared/hooks/useUserProfile";
import { AvatarDTO } from "@/feature/settings/modals/avatar-dto";
import { useAvatarListQuery } from "@/feature/settings/hooks/useAvatarListQuery";
import LoadingScreen from "@/shared/components/loading-screen";
import { SafeAreaView } from "react-native-safe-area-context";
import { Image } from "expo-image";

export default function SettingsAvatarScreen() {
const { userProfile } = useUserProfile();
const { avatars, isAvatarListLoading, isAvatarListError } = useAvatarListQuery();
const { updateUserProfile, isUserUpdating } = useUserProfileMutation();

if (isAvatarListLoading) {
return ;
}

const handleAvatarSelect = async (avatar: AvatarDTO) => {
if (isUserUpdating) return;

try {
  await updateUserProfile({
    displayName: userProfile?.displayName ?? "",
    pictureUrl: avatar.url,
    isOnBoarded: userProfile?.isOnBoarded ?? false,
  });
} catch {
  console.log("Failed to update avatar.");
}

};

return (




Avatar

  <View className="flex-row flex-wrap justify-between m-6">
    {isAvatarListError ? (
      <Text className="w-full text-center text-base font-baloo text-secondary">
        Failed to load avatars.
      </Text>
    ) : (
      avatars.map((avatar) => {
        const isSelected = avatar.url === userProfile?.pictureUrl;

        return (
          <Pressable
            key={avatar.url}
            onPress={() => handleAvatarSelect(avatar)}
            className={`mb-6 items-center ${isUserUpdating ? "opacity-70" : ""}`}
            disabled={isUserUpdating}
          >
            <View
              className="rounded-full border-4"
              style={{
                borderColor: isSelected ? "#8B86B3" : "transparent",
              }}
            >
              <Image
                source={{ uri: avatar.url }}
                style={{ width: 80, height: 80, borderRadius: 48 }}
                contentFit="cover"
              />
            </View>
          </Pressable>
        );
      })
    )}
  </View>
</SafeAreaView>

);
}

I generated this part from claude to see if this logic will make the code look better. It feels like everything's still working in this.

);
}

function isAvatarListDTO(value: unknown): value is AvatarListDTO {
Copy link
Copy Markdown
Collaborator

@nxn-nicole nxn-nicole Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure whether we need a very complex type check here, because we maintain this JSON file ourselves, so usually it shouldn't have any type error.
If you want to do type validation, please use Zod instead of a manual validate function.
export const AvatarDTOSchema = z.object({
id: z.string(),
label: z.string(),
url: z.string(),
});
const AvatarListDTOSchema = z.object({
version: z.number(),
avatars: z.array(AvatarDTOSchema),
});

@lyx2-2 lyx2-2 force-pushed the azure-avatars-upload-support branch from 66aa6d3 to ae3a883 Compare May 2, 2026 03:56
@lyx2-2 lyx2-2 force-pushed the azure-avatars-upload-support branch from ae3a883 to 196bd4a Compare May 8, 2026 04:08
import { AvatarDTO } from "@/feature/settings/modals/avatar-dto";
import { LOCAL_AVATARS } from "@/feature/settings/constants/local-avatar-catalog";

export function useAvatarListQuery() {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don‘t think you need this. if you store avatr locally, there won't be loading and error state

: avatarDataDevelopment;
const avatars = avatarData.avatars;
const { userProfile } = useUserProfile();
const { avatars, isAvatarListLoading, isAvatarListError } = useAvatarListQuery();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you don't use query, this can also be removed.


const { updateUserProfile, isUserUpdating } = useUserProfileMutation();

if (isAvatarListLoading) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't feel like there will be an avatar loading state. You can delete this.

const { userProfile } = useUserProfile();
const { avatars, isAvatarListLoading, isAvatarListError } = useAvatarListQuery();

const [selectedAvatarId, setSelectedAvatarId] = useState<string | null>(null);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, there is no need to hold a local state for the selected avatar ID. When a user clicks a new avatar, just upload it and always use userProfile.avatar as the single source of truth.

<View className="flex-row flex-wrap justify-between m-6">
{avatars.map((avatar) => {
const isSelected = avatar.url === selectedAvatarUrl;
{isAvatarListError ? (
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't feel like there will be an error state. I can't delete this.

: PNGIMAGES.blotzIcon;
const pictureValue = userProfile?.pictureUrl;
const SelectedAvatarComponent = getLocalAvatarComponent(pictureValue);
const avatarSource = isRemoteAvatarUrl(pictureValue) ? { uri: pictureValue } : PNGIMAGES.blotzIcon;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const SelectedAvatarComponent = getLocalAvatarComponent(pictureValue);
const avatarSource = pictureValue && !SelectedAvatarComponent
? { uri: pictureValue }
: PNGIMAGES.blotzIcon;
I feel like this works the same, and you can remove the function isRemoteAvatarUrl

/>
)}
<Pressable
onPress={handleProfileEdit}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, if you didn't add any new package, you shouldn't have changes in package-lock.json.
You can copy the package-lock.json from main to cover your local package-lock.json to avoid changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants