Skip to content

Commit

Permalink
Merge pull request #106 from Unshut-Labs/ar/invalidate-members
Browse files Browse the repository at this point in the history
feat: Invalidate Queries when changes happen to the group
  • Loading branch information
alexrisch authored Jun 20, 2024
2 parents eb423db + 8b494a0 commit 61cc068
Show file tree
Hide file tree
Showing 13 changed files with 237 additions and 162 deletions.
4 changes: 2 additions & 2 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
"expo": {
"ios": {
"version": "1.4.8",
"buildNumber": "204"
"buildNumber": "205"
},
"android": {
"version": "1.4.8",
"versionCode": 134
"versionCode": 137
}
}
}
17 changes: 17 additions & 0 deletions components/Chat/ChatGroupUpdatedMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ export default function ChatGroupUpdatedMessage({
const textMessages: string[] = [];
const profiles = getProfilesStore(currentAccount()).getState().profiles;
const byInboxId = getInboxIdStore(currentAccount()).getState().byInboxId;
// TODO: Feat: handle multiple members
const initiatedByAddress = byInboxId[content.initiatedByInboxId]?.[0];
const initiatedByReadableName = getPreferredName(
profiles[initiatedByAddress]?.socials,
initiatedByAddress
);
content.membersAdded.forEach((m) => {
// TODO: Feat: handle multiple members
const firstAddress = byInboxId[m.inboxId]?.[0];
Expand All @@ -40,6 +46,17 @@ export default function ChatGroupUpdatedMessage({
);
textMessages.push(`${readableName} left the conversation`);
});
content.metadataFieldsChanged.forEach((f) => {
if (f.fieldName === "group_name") {
textMessages.push(
`The group name was changed to ${f.newValue} by ${initiatedByReadableName}`
);
} else if (f.fieldName === "group_image_url_square") {
textMessages.push(
`The group photo was changed by ${initiatedByReadableName}`
);
}
});
return textMessages;
}, [message.content]);
return (
Expand Down
118 changes: 118 additions & 0 deletions data/helpers/messages/handleGroupUpdatedMessage.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { invalidateGroupMembersQuery } from "../../../queries/useGroupMembersQuery";
import { invalidateGroupNameQuery } from "../../../queries/useGroupNameQuery";
import { invalidateGroupPhotoQuery } from "../../../queries/useGroupPhotoQuery";
import { DecodedMessageWithCodecsType } from "../../../utils/xmtpRN/client";
import { handleGroupUpdatedMessage } from "./handleGroupUpdatedMessage";

jest.mock("../../../queries/useGroupMembersQuery", () => ({
invalidateGroupMembersQuery: jest.fn(),
}));

jest.mock("../../../queries/useGroupNameQuery", () => ({
invalidateGroupNameQuery: jest.fn(),
}));

jest.mock("../../../queries/useGroupPhotoQuery", () => ({
invalidateGroupPhotoQuery: jest.fn(),
}));

describe("handleGroupUpdatedMessage", () => {
const account = "testAccount";
const topic = "testTopic";

const createMessage = (contentTypeId: string, content: any) =>
({
contentTypeId,
content: () => content,
}) as unknown as DecodedMessageWithCodecsType;

afterEach(() => {
jest.clearAllMocks();
});

it('should not proceed if contentTypeId does not include "group_updated"', () => {
const message = createMessage("text", {});

handleGroupUpdatedMessage(account, topic, message);

expect(invalidateGroupMembersQuery).not.toHaveBeenCalled();
expect(invalidateGroupNameQuery).not.toHaveBeenCalled();
expect(invalidateGroupPhotoQuery).not.toHaveBeenCalled();
});

it("should invalidate group members query if members are added or removed", () => {
const content = {
membersAdded: ["member1"],
membersRemoved: [],
metadataFieldsChanged: [],
};
const message = createMessage("group_updated", content);

handleGroupUpdatedMessage(account, topic, message);

expect(invalidateGroupMembersQuery).toHaveBeenCalledWith(account, topic);
});

it("should invalidate group name query if group name is changed", () => {
const content = {
membersAdded: [],
membersRemoved: [],
metadataFieldsChanged: [
{ fieldName: "group_name", newValue: "New Group Name" },
],
};
const message = createMessage("group_updated", content);

handleGroupUpdatedMessage(account, topic, message);

expect(invalidateGroupNameQuery).toHaveBeenCalledWith(account, topic);
});

it("should invalidate group photo query if group photo is changed", () => {
const content = {
membersAdded: [],
membersRemoved: [],
metadataFieldsChanged: [
{ fieldName: "group_image_url_square", newValue: "New Photo URL" },
],
};
const message = createMessage("group_updated", content);

handleGroupUpdatedMessage(account, topic, message);

expect(invalidateGroupPhotoQuery).toHaveBeenCalledWith(account, topic);
});

it("should invalidate all relevant queries if multiple changes occur", () => {
const content = {
membersAdded: ["member1"],
membersRemoved: ["member2"],
metadataFieldsChanged: [
{ fieldName: "group_name", newValue: "New Group Name" },
{ fieldName: "group_image_url_square", newValue: "New Photo URL" },
],
};
const message = createMessage("group_updated", content);

handleGroupUpdatedMessage(account, topic, message);

expect(invalidateGroupMembersQuery).toHaveBeenCalledWith(account, topic);
expect(invalidateGroupNameQuery).toHaveBeenCalledWith(account, topic);
expect(invalidateGroupPhotoQuery).toHaveBeenCalledWith(account, topic);
});

it("should handle empty metadataFieldsChanged array", () => {
const content = {
membersAdded: [],
membersRemoved: [],
metadataFieldsChanged: [],
};
const message = createMessage("group_updated", content);

handleGroupUpdatedMessage(account, topic, message);

expect(invalidateGroupMembersQuery).toHaveBeenCalled();
expect(invalidateGroupNameQuery).not.toHaveBeenCalled();
expect(invalidateGroupPhotoQuery).not.toHaveBeenCalled();
});
});
43 changes: 43 additions & 0 deletions data/helpers/messages/handleGroupUpdatedMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { GroupUpdatedContent } from "@xmtp/react-native-sdk";

import { invalidateGroupMembersQuery } from "../../../queries/useGroupMembersQuery";
import { invalidateGroupNameQuery } from "../../../queries/useGroupNameQuery";
import { invalidateGroupPhotoQuery } from "../../../queries/useGroupPhotoQuery";
import { DecodedMessageWithCodecsType } from "../../../utils/xmtpRN/client";

export const handleGroupUpdatedMessage = (
account: string,
topic: string,
message: DecodedMessageWithCodecsType
) => {
if (!message.contentTypeId.includes("group_updated")) return;
const content = message.content() as GroupUpdatedContent;
if (content.membersAdded.length > 0 || content.membersRemoved.length > 0) {
invalidateGroupMembersQuery(account, topic);
}
if (content.metadataFieldsChanged.length > 0) {
let groupNameChanged = false;
let groupPhotoChanged = false;
for (const field of content.metadataFieldsChanged) {
if (field.fieldName === "group_name") {
groupNameChanged = true;
} else if (field.fieldName === "group_image_url_square") {
groupPhotoChanged = true;
}
}
if (groupNameChanged) {
invalidateGroupNameQuery(account, topic);
}
if (groupPhotoChanged) {
invalidateGroupPhotoQuery(account, topic);
}
}
// Admin Update
if (
content.membersAdded.length === 0 &&
content.membersRemoved.length === 0 &&
content.metadataFieldsChanged.length === 0
) {
invalidateGroupMembersQuery(account, topic);
}
};
2 changes: 2 additions & 0 deletions data/helpers/messages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { xmtpMessageFromDb, xmtpMessageToDb } from "../../mappers";
import { getChatStore } from "../../store/accountsStore";
import { XmtpMessage } from "../../store/chatStore";

export { handleGroupUpdatedMessage } from "./handleGroupUpdatedMessage";

export const saveMessages = async (
account: string,
messages: XmtpMessage[]
Expand Down
4 changes: 4 additions & 0 deletions hooks/useGroupMembers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { currentAccount } from "../data/store/accountsStore";
import { useAddToGroupMutation } from "../queries/useAddToGroupMutation";
import { useGroupMembersQuery } from "../queries/useGroupMembersQuery";
import { usePromoteToAdminMutation } from "../queries/usePromoteToAdminMutation";
import { usePromoteToSuperAdminMutation } from "../queries/usePromoteToSuperAdminMutation";
Expand Down Expand Up @@ -34,6 +35,8 @@ export const useGroupMembers = (topic: string) => {
topic
);

const { mutateAsync: addMembers } = useAddToGroupMutation(account, topic);

return {
members,
isLoading,
Expand All @@ -43,5 +46,6 @@ export const useGroupMembers = (topic: string) => {
revokeSuperAdmin,
revokeAdmin,
removeMember,
addMembers,
};
};
6 changes: 5 additions & 1 deletion queries/useAddToGroupMutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import { useMutation } from "@tanstack/react-query";

import { refreshGroup } from "../utils/xmtpRN/conversations";
import { addMemberMutationKey } from "./MutationKeys";
import { cancelGroupMembersQuery } from "./useGroupMembersQuery";
import {
cancelGroupMembersQuery,
invalidateGroupMembersQuery,
} from "./useGroupMembersQuery";
import { useGroupQuery } from "./useGroupQuery";

export const useAddToGroupMutation = (account: string, topic: string) => {
Expand All @@ -25,6 +28,7 @@ export const useAddToGroupMutation = (account: string, topic: string) => {
},
onSuccess: (_data, _variables, _context) => {
console.log("onSuccess useAddToGroupMutation");
invalidateGroupMembersQuery(account, topic);
refreshGroup(account, topic);
},
});
Expand Down
6 changes: 6 additions & 0 deletions queries/useGroupMembersQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,9 @@ export const cancelGroupMembersQuery = async (
queryKey: groupMembersQueryKey(account, topic),
});
};

export const invalidateGroupMembersQuery = (account: string, topic: string) => {
return queryClient.invalidateQueries({
queryKey: groupMembersQueryKey(account, topic),
});
};
9 changes: 9 additions & 0 deletions queries/useGroupNameQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,12 @@ export const cancelGroupNameQuery = async (account: string, topic: string) => {
queryKey: groupNameQueryKey(account, topic),
});
};

export const invalidateGroupNameQuery = async (
account: string,
topic: string
) => {
return queryClient.invalidateQueries({
queryKey: groupNameQueryKey(account, topic),
});
};
9 changes: 9 additions & 0 deletions queries/useGroupPhotoQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,12 @@ export const cancelGroupPhotoQuery = async (account: string, topic: string) => {
queryKey: groupPhotoQueryKey(account, topic),
});
};

export const invalidateGroupPhotoQuery = async (
account: string,
topic: string
) => {
return queryClient.invalidateQueries({
queryKey: groupPhotoQueryKey(account, topic),
});
};
23 changes: 14 additions & 9 deletions screens/NewConversation/NewConversation.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { NativeStackScreenProps } from "@react-navigation/native-stack";
import React, { useEffect, useRef, useState } from "react";
import {
Alert,
Button,
Platform,
ScrollView,
StyleSheet,
Text,
TextInput,
View,
useColorScheme,
Platform,
Alert,
} from "react-native";

import ActivityIndicator from "../../components/ActivityIndicator/ActivityIndicator";
Expand All @@ -28,6 +28,7 @@ import {
} from "../../data/store/accountsStore";
import { ProfileSocials } from "../../data/store/profilesStore";
import { useSelect } from "../../data/store/storeHelpers";
import { useGroupMembers } from "../../hooks/useGroupMembers";
import { searchProfiles } from "../../utils/api";
import {
backgroundColor,
Expand All @@ -45,7 +46,6 @@ import { navigate } from "../../utils/navigation";
import { isEmptyObject } from "../../utils/objects";
import { getPreferredName } from "../../utils/profile";
import { isOnXmtp } from "../../utils/xmtpRN/client";
import { addMembersToGroup } from "../../utils/xmtpRN/conversations";
import { NewConversationModalParams } from "./NewConversationModal";

export default function NewConversation({
Expand All @@ -60,6 +60,9 @@ export default function NewConversation({
enabled: !!route.params?.addingToGroupTopic,
members: [] as (ProfileSocials & { address: string })[],
});
const { addMembers } = useGroupMembers(
route.params?.addingToGroupTopic ?? ""
);
const existingGroup = useChatStore((s) =>
route.params?.addingToGroupTopic
? s.conversations[route.params.addingToGroupTopic]
Expand Down Expand Up @@ -97,11 +100,7 @@ export default function NewConversation({
if (route.params?.addingToGroupTopic) {
setLoading(true);
try {
await addMembersToGroup(
currentAccount(),
route.params?.addingToGroupTopic,
group.members.map((m) => m.address)
);
await addMembers(group.members.map((m) => m.address));
navigation.goBack();
} catch (e) {
setLoading(false);
Expand All @@ -121,7 +120,13 @@ export default function NewConversation({
return undefined;
},
});
}, [group, loading, navigation, route.params?.addingToGroupTopic]);
}, [
group,
loading,
navigation,
route.params?.addingToGroupTopic,
addMembers,
]);

const [value, setValue] = useState(route.params?.peer || "");
const searchingForValue = useRef("");
Expand Down
Loading

0 comments on commit 61cc068

Please sign in to comment.