fix(api): include personal notes in dm search scopes (#3486)

This commit is contained in:
Hampus authored and GitHub committed 2026-10-15 02:43:35 +02:00
1 parent 7c4180c2be
commit 9213a765b1
2 files changed
+40 -1

No files matched your search

@@ -1,6 +1,8 @@
// SPDX-License-Identifier: AGPL-3.0-or-later // SPDX-License-Identifier: AGPL-3.0-or-later
import type {UserID} from '@app/api/BrandedTypes'; import type {UserID} from '@app/api/BrandedTypes';
import {userIdToChannelId} from '@app/api/BrandedTypes';
import {isPersonalNotesChannelId} from '@app/api/channel/services/PersonalNotesChannelRepair';
import type {Channel} from '@app/api/models/Channel'; import type {Channel} from '@app/api/models/Channel';
import type {UserRepository} from '@app/api/user/repositories/UserRepository'; import type {UserRepository} from '@app/api/user/repositories/UserRepository';
import {ChannelTypes} from '@fluxer/constants/src/ChannelConstants'; import {ChannelTypes} from '@fluxer/constants/src/ChannelConstants';
@@ -15,6 +17,9 @@ interface DmScopeOptions {
} }
export function isDmScopeChannelForUser(channel: Channel, userId: UserID): boolean { export function isDmScopeChannelForUser(channel: Channel, userId: UserID): boolean {
if (channel.type === ChannelTypes.DM_PERSONAL_NOTES) {
return isPersonalNotesChannelId({userId, channelId: channel.id});
}
const isDm = channel.type === ChannelTypes.DM || channel.type === ChannelTypes.GROUP_DM; const isDm = channel.type === ChannelTypes.DM || channel.type === ChannelTypes.GROUP_DM;
return isDm && channel.recipientIds.has(userId); return isDm && channel.recipientIds.has(userId);
} }
@@ -26,7 +31,7 @@ export async function getDmChannelIdsForScope({
includeChannel, includeChannel,
}: DmScopeOptions): Promise<Array<string>> { }: DmScopeOptions): Promise<Array<string>> {
const summaryResults = await userRepository.listPrivateChannelSummaries(userId); const summaryResults = await userRepository.listPrivateChannelSummaries(userId);
const channelIdStrings = new Set<string>(); const channelIdStrings = new Set<string>([userIdToChannelId(userId).toString()]);
for (const summary of summaryResults) { for (const summary of summaryResults) {
const isDm = const isDm =
summary.channelType === ChannelTypes.DM || summary.channelType === ChannelTypes.GROUP_DM || summary.isGroupDm; summary.channelType === ChannelTypes.DM || summary.channelType === ChannelTypes.GROUP_DM || summary.isGroupDm;
@@ -174,5 +174,39 @@ describe('Message Search Endpoint', () => {
} }
expect(found).toBe(true); expect(found).toBe(true);
}); });
test.each(['all_dms', 'open_dms', 'all', 'open_dms_and_all_guilds'] as const)(
'finds personal note messages in the %s scope',
async (scope) => {
const account = await createTestAccount(harness);
await ensureSessionStarted(harness, account.token);
const personalNotesChannelId = account.userId;
await sendMessage(harness, account.token, personalNotesChannelId, `personal-notes-bootstrap-${Date.now()}`);
await markChannelAsIndexed(harness, personalNotesChannelId);
const uniqueContent = `personal-notes-${scope}-${Date.now()}`;
await sendMessage(harness, account.token, personalNotesChannelId, uniqueContent);
let foundInScope = false;
let foundInFilter = false;
for (let attempt = 0; attempt < 20 && !(foundInScope && foundInFilter); attempt++) {
const scoped = await createBuilder<MessageSearchResponse>(harness, account.token)
.post('/search/messages')
.body({content: uniqueContent, scope, context_channel_id: personalNotesChannelId})
.expect(HTTP_STATUS.OK)
.execute();
foundInScope ||= isSearchResult(scoped) && scoped.messages.some((m) => m.content === uniqueContent);
const filtered = await createBuilder<MessageSearchResponse>(harness, account.token)
.post('/search/messages')
.body({content: uniqueContent, scope, channel_ids: [personalNotesChannelId]})
.expect(HTTP_STATUS.OK)
.execute();
foundInFilter ||= isSearchResult(filtered) && filtered.messages.some((m) => m.content === uniqueContent);
if (!(foundInScope && foundInFilter)) {
await wait(TEST_TIMEOUTS.QUICK);
}
}
expect(foundInScope).toBe(true);
expect(foundInFilter).toBe(true);
},
);
}); });
}); });