Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jts/2006 improve search #94

Merged
merged 6 commits into from
Sep 20, 2024
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
39 changes: 28 additions & 11 deletions src/components/views/dialogs/InviteDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,6 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
// Verji Start - generate a list of userId's which are members in currently active space
this.spaceMembers = SpaceStore.instance.activeSpaceRoom?.getJoinedMembers() ?? ([] as RoomMember[]);
this.spaceMembers.forEach((m) => {
console.log(m);
this.spaceMemberIds.push(m.userId);
});
// Verji end
Expand Down Expand Up @@ -535,8 +534,12 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
externals.forEach((id) => excludedTargetIds.add(id));
}

// VERJI added param activeSpaceMembers - used to fileter the recents based on membership in space
// VERJI added param activeSpaceMembers - used to filter the recents based on membership in space
public static buildRecents(excludedTargetIds: Set<string>, activeSpaceMembers: string[]): Result[] {
// Verji - If we don't want to see the Recents-suggestions(featureflag), we just return an empty array
if(!SettingsStore.getValue(UIFeature.ShowRecentsInSuggestions)){
return [] as Result[]
}
const rooms = DMRoomMap.shared().getUniqueRoomsWithIndividuals(); // map of userId => js-sdk Room

// Also pull in all the rooms tagged as DefaultTagID.DM so we don't miss anything. Sometimes the
Expand Down Expand Up @@ -618,10 +621,13 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
private buildSuggestions(excludedTargetIds: Set<string>): { userId: string; user: Member }[] {
const cli = MatrixClientPeg.safeGet();
const activityScores = buildActivityScores(cli);
const memberScores = buildMemberScores(cli);

const memberComparator = compareMembers(activityScores, memberScores);
let memberScores = {} as { [userId: string]: { member: RoomMember; score: number; numRooms: number } };
if (SettingsStore.getValue(UIFeature.ShowRoomMembersInSuggestions)) {
memberScores = buildMemberScores(cli);
}

const memberComparator = compareMembers(activityScores, memberScores);
return Object.values(memberScores)
.map(({ member }) => member)
.filter((member) => !excludedTargetIds.has(member.userId))
Expand Down Expand Up @@ -657,7 +663,6 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
)
.then(async (r) => {
this.setState({ busy: false });

if (r.results.find((e) => e.user_id == this.state.filterText.trim())) {
foundUser = true;
}
Expand All @@ -674,12 +679,21 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial

if (foundUser == false) {
// Look in other stores for user if search might have failed unexpectedly
const possibleMembers = [
...this.state.recents,
...this.state.suggestions,
...this.state.serverResultsMixin,
...this.state.threepidResultsMixin,
];
// VERJI - Add feature flag ShowRoomMembersInSuggestions, if false, only show Recents
let possibleMembers = [] as Result[];
if (SettingsStore.getValue(UIFeature.ShowRoomMembersInSuggestions)) {
possibleMembers = [
...this.state.recents,
...this.state.suggestions,
...this.state.serverResultsMixin,
...this.state.threepidResultsMixin,
];
}
else if(SettingsStore.getValue(UIFeature.ShowRecentsInSuggestions)){
possibleMembers = [
...this.state.recents
];
}
const toAdd = [];
const potentialAddresses = this.state.filterText
.split(/[\s,]+/)
Expand Down Expand Up @@ -1207,6 +1221,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
e.preventDefault();

// Process it as a list of addresses to add instead
// VERJI HIDE RESULTS HER
const possibleMembers = [
// If we can avoid hitting the profile endpoint, we should.
...this.state.recents,
Expand Down Expand Up @@ -1349,6 +1364,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
!sourceMembers.some((m) => m.userId === u.userId) &&
!priorityAdditionalMembers.some((m) => m.userId === u.userId) &&
!otherAdditionalMembers.some((m) => m.userId === u.userId)
//HERE
);
};

Expand Down Expand Up @@ -1384,6 +1400,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial

// Now we mix in the additional members. Again, we presume these have already been filtered. We
// also assume they are more relevant than our suggestions and prepend them to the list.

sourceMembers = [...priorityAdditionalMembers, ...sourceMembers, ...otherAdditionalMembers];

// If we're going to hide one member behind 'show more', just use up the space of the button
Expand Down
5 changes: 3 additions & 2 deletions src/components/views/dialogs/spotlight/SpotlightDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,9 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
userResults.push(result);
}
}
addUserResults(findVisibleRoomMembers(visibleRooms, cli), false);
if (SettingsStore.getValue(UIFeature.ShowRoomMembersInSuggestions)) {
addUserResults(findVisibleRoomMembers(visibleRooms, cli), false);
}
addUserResults(userDirectorySearchResults, true);
if (profile) {
addUserResults([new DirectoryMember(profile)], true);
Expand Down Expand Up @@ -428,7 +430,6 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
if (trimmedQuery) {
const lcQuery = trimmedQuery.toLowerCase();
const normalizedQuery = normalize(trimmedQuery);

possibleResults.forEach((entry) => {
if (isRoomResult(entry)) {
// If the room is a DM with a user that is part of the user directory search results,
Expand Down
8 changes: 8 additions & 0 deletions src/settings/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1511,6 +1511,14 @@ export const SETTINGS: { [setting: string]: ISetting } = {
supportedLevels: LEVELS_UI_FEATURE,
default: true,
},
[UIFeature.ShowRoomMembersInSuggestions]: {
supportedLevels: LEVELS_UI_FEATURE,
default: true,
},
[UIFeature.ShowRecentsInSuggestions]: {
supportedLevels: LEVELS_UI_FEATURE,
default: true,
},

// Electron-specific settings, they are stored by Electron and set/read over an IPC.
// We store them over there are they are necessary to know before the renderer process launches.
Expand Down
2 changes: 2 additions & 0 deletions src/settings/UIFeature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ export const enum UIFeature {
HelpShowMatrixDisclosurePolicyAndLinks = "UIFeature.helpShowMatrixDisclosurePolicyAndLinks",
ShowInviteToSpaceFromPeoplePlus = "UIFeature.showInviteToSpaceFromPeoplePlus",
SettingShowMessageSearch = "UIFeature.settingShowMessageSearch",
ShowRoomMembersInSuggestions = "UIFeature.showRoomMembersInSuggestions",
ShowRecentsInSuggestions = "UIFeature.showRecentsInSuggestions",
}

export enum UIComponent {
Expand Down
25 changes: 22 additions & 3 deletions src/utils/SortMembers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import { KnownMembership } from "matrix-js-sdk/src/types";

import { Member } from "./direct-messages";
import DMRoomMap from "./DMRoomMap";
import SettingsStore from "../settings/SettingsStore";
import { UIFeature } from "../settings/UIFeature";

export const compareMembers =
(
Expand Down Expand Up @@ -100,12 +102,29 @@ interface IMemberScore {
}

export function buildMemberScores(cli: MatrixClient): { [userId: string]: IMemberScore } {
// VERJI - if feature flag is false, we don't need to calculate and build memberscores, and just return an empty map instead. Suggestions will be populated by searchResults instead
if (!SettingsStore.getValue(UIFeature.ShowRoomMembersInSuggestions)) {
return {} as { [userId: string]: IMemberScore };
}
const maxConsideredMembers = 200;
const consideredRooms = joinedRooms(cli).filter((room) => room.getJoinedMemberCount() < maxConsideredMembers);
const memberPeerEntries = consideredRooms.flatMap((room) =>
room.getJoinedMembers().map((member) => ({ member, roomSize: room.getJoinedMemberCount() })),
);

const memberPeerEntries = consideredRooms.flatMap((room) => {
// VERJI Log the roomId here
console.log("[SortMembers.ts] - Processing room:", room.roomId);
console.log("[SortMembers.ts] - is this a space room?:", room.isSpaceRoom());
// VERJI - A filter to exclude members from Space Rooms, not really necessary if featureflag showRoomMembersInSuggestions is false, but keeping it in case we want to "fine-tune" suggestions later
if (room.isSpaceRoom()) {
console.log("[SortMembers.ts] - skipping the room", room.roomId);
console.log("[SortMembers.ts] - number of members excluded: ", room.getJoinedMemberCount());
return [];
}

return room.getJoinedMembers().map((member) => ({ member, roomSize: room.getJoinedMemberCount() }));
});

const userMeta = groupBy(memberPeerEntries, ({ member }) => member.userId);

// If the iteratee in mapValues returns undefined that key will be removed from the resultant object
return mapValues(userMeta, (roomMemberships) => {
if (!roomMemberships.length) return;
Expand Down
74 changes: 74 additions & 0 deletions test/components/views/dialogs/InviteDialog-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ import SettingsStore from "../../../../src/settings/SettingsStore";
import Modal from "../../../../src/Modal";
import HomePage from "../../../../src/components/structures/HomePage";
import { UIFeature } from "../../../../src/settings/UIFeature";
import * as SortMembers from "../../../../src/utils/SortMembers";
import SpaceStore from "../../../../src/stores/spaces/SpaceStore";

const mockGetAccessToken = jest.fn().mockResolvedValue("getAccessToken");
jest.mock("../../../../src/IdentityAuthClient", () =>
Expand Down Expand Up @@ -189,7 +191,79 @@ describe("InviteDialog", () => {
afterAll(() => {
jest.restoreAllMocks();
});
describe("UIFeature.ShowRoomMembersInSuggestions", () => {
const testUser = {
_userId: "@suggestedMember:verji.app",
displayName: "Suggested Member",
getMxcAvatarUrl: jest.fn().mockReturnValue(aliceProfileInfo.avatar_url),
};
const mockSpaceMembers = [
{
userId: testUser._userId,
user: {
_userId: "@suggestedMember:verji.app",
displayName: "Suggested Member",
getMxcAvatarUrl: jest.fn().mockReturnValue(aliceProfileInfo.avatar_url),
},
},
];

const memberScores: { [userId: string]: any } = {
[testUser._userId]: {
member: testUser,
score: 0.92,
numRooms: 2,
},
};
beforeEach(() => {
// Mock activeSpaceRoom to be an object with getJoinedMembers method
const roomMock: Partial<Room> = {
getJoinedMembers: jest.fn().mockReturnValue([mockSpaceMembers /* mock RoomMember[] */]),
};
const mockBuildMembers = jest.spyOn(SortMembers, "buildMemberScores");
mockBuildMembers.mockImplementation(() => {
return memberScores;
});

// Mock the SpaceStore instance and activeSpaceRoom
jest.spyOn(SpaceStore.instance, "activeSpaceRoom", "get").mockReturnValue(roomMock as Room);
});

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

it("Should render suggestions when UIFeature.ShowRoomMembersInSuggestions is TRUE(default)", async () => {
jest.spyOn(SettingsStore, "getValue").mockImplementation((name: string) => {
if (name == UIFeature.ShowRoomMembersInSuggestions) return true;
});

render(<InviteDialog kind={InviteKind.Dm} onFinished={jest.fn()} initialText="" />);

expect(screen.queryAllByText("Suggestions").length).toBe(1);

// The "presentation" is used by the rendered tiles of members in the suggestion.
const suggestionTile = screen.getAllByRole("presentation");

expect(suggestionTile).toBeTruthy();
});

it("should NOT render suggestions when UIFeature.ShowRoomMembersInSuggestions is FALSE", async () => {
jest.spyOn(SettingsStore, "getValue").mockImplementation((name: string) => {
if (name == UIFeature.ShowRoomMembersInSuggestions) return false;
});

render(<InviteDialog kind={InviteKind.Dm} onFinished={jest.fn()} initialText="" />);

expect(screen.queryAllByText("Suggestions").length).toBe(0);

// The "presentation" is used by the rendered tiles of members in the suggestion.
const suggestionTile = screen.queryByRole("presentation");

expect(suggestionTile).toBeFalsy();
});
});
it("should label with space name", () => {
room.isSpaceRoom = jest.fn().mockReturnValue(true);
room.getType = jest.fn().mockReturnValue(RoomType.Space);
Expand Down
Loading