This commit is contained in:
Copilot 2025-09-21 15:56:01 +09:00 committed by GitHub
commit 15c52964a9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 67 additions and 4 deletions

View File

@ -267,7 +267,7 @@ export class NotificationService implements OnApplicationShutdown {
excludeTypes?: (MiNotification['type'] | string)[],
},
): Promise<MiNotification[]> {
let sinceTime = sinceId ? this.toXListId(sinceId) : null;
let sinceTime = sinceId && sinceId !== '0' ? this.toXListId(sinceId) : null;
let untilTime = untilId ? this.toXListId(untilId) : null;
let notifications: MiNotification[];
@ -275,10 +275,11 @@ export class NotificationService implements OnApplicationShutdown {
let notificationsRes: [id: string, fields: string[]][];
// sinceidのみの場合は古い順、そうでない場合は新しい順。 QueryService.makePaginationQueryも参照
if (sinceTime && !untilTime) {
// sinceId が '0' の場合は最初から古い順で取得
if ((sinceTime && !untilTime) || (sinceId === '0' && !untilTime)) {
notificationsRes = await this.redisClient.xrange(
`notificationTimeline:${userId}`,
'(' + sinceTime,
sinceTime ? '(' + sinceTime : '-',
'+',
'COUNT', limit);
} else {
@ -307,7 +308,7 @@ export class NotificationService implements OnApplicationShutdown {
}
// フィルタしたことで通知が0件になった場合、次のページを取得する
if (sinceId && !untilId) {
if ((sinceId && !untilId) || (sinceId === '0' && !untilId)) {
sinceTime = notificationsRes[notificationsRes.length - 1][0];
} else {
untilTime = notificationsRes[notificationsRes.length - 1][0];

View File

@ -0,0 +1,62 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
process.env.NODE_ENV = 'test';
import * as assert from 'assert';
import { api, post, signup } from '../utils.js';
import type * as misskey from 'misskey-js';
describe('Notification Sorting', () => {
let alice: misskey.entities.SignupResponse;
let bob: misskey.entities.SignupResponse;
beforeAll(async () => {
alice = await signup({ username: 'alice' });
bob = await signup({ username: 'bob' });
}, 1000 * 60 * 2);
test('notifications are sorted correctly when oldest first is requested', async () => {
// Create some notifications by having Bob mention Alice
const note1 = await post(bob, { text: '@alice first mention' });
await new Promise(resolve => setTimeout(resolve, 1000)); // Wait 1 second
const note2 = await post(bob, { text: '@alice second mention' });
await new Promise(resolve => setTimeout(resolve, 1000)); // Wait 1 second
const note3 = await post(bob, { text: '@alice third mention' });
// Test newest first (default)
const newestFirstRes = await api('i/notifications', {
includeTypes: ['mention'],
limit: 10,
}, alice);
assert.strictEqual(newestFirstRes.status, 200);
const newestFirst = newestFirstRes.body.filter(n => n.type === 'mention');
assert.strictEqual(newestFirst.length >= 3, true);
// Verify newest first order (note3 should be first)
const newestFirstIds = newestFirst.map(n => n.note.id);
const note3Index = newestFirstIds.indexOf(note3.id);
const note1Index = newestFirstIds.indexOf(note1.id);
assert.strictEqual(note3Index < note1Index, true);
// Test oldest first with sinceId: '0'
const oldestFirstRes = await api('i/notifications', {
includeTypes: ['mention'],
sinceId: '0',
limit: 10,
}, alice);
assert.strictEqual(oldestFirstRes.status, 200);
const oldestFirst = oldestFirstRes.body.filter(n => n.type === 'mention');
assert.strictEqual(oldestFirst.length >= 3, true);
// Verify oldest first order (note1 should be first)
const oldestFirstIds = oldestFirst.map(n => n.note.id);
const note1IndexOldest = oldestFirstIds.indexOf(note1.id);
const note3IndexOldest = oldestFirstIds.indexOf(note3.id);
assert.strictEqual(note1IndexOldest < note3IndexOldest, true);
});
});