mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 21:15:32 +09:00
fix(threads): announce threads above the latest five messages (#3307)
This commit is contained in:
1 parent
c486c2d3d0
commit
aa42bf13ce
2 files changed
+22
-11
No files matched your search
@@ -76,10 +76,9 @@ import type {
|
|||||||
} from '@fluxer/schema/src/domains/channel/ForumRequestSchemas';
|
} from '@fluxer/schema/src/domains/channel/ForumRequestSchemas';
|
||||||
import type {ThreadChannelResponse} from '@fluxer/schema/src/domains/channel/ThreadRequestSchemas';
|
import type {ThreadChannelResponse} from '@fluxer/schema/src/domains/channel/ThreadRequestSchemas';
|
||||||
import type {MessageResponse} from '@fluxer/schema/src/domains/message/MessageResponseSchemas';
|
import type {MessageResponse} from '@fluxer/schema/src/domains/message/MessageResponseSchemas';
|
||||||
import {snowflakeToDate} from '@fluxer/snowflake/src/Snowflake';
|
|
||||||
import * as BucketUtils from '@fluxer/snowflake/src/SnowflakeBuckets';
|
import * as BucketUtils from '@fluxer/snowflake/src/SnowflakeBuckets';
|
||||||
|
|
||||||
const THREAD_CREATED_MESSAGE_MIN_SOURCE_AGE_MS = 5 * 60 * 1000;
|
const THREAD_CREATED_MESSAGE_RECENT_WINDOW = 5;
|
||||||
|
|
||||||
function slowmodeThreadKey(parentId: ChannelID, userId: UserID): string {
|
function slowmodeThreadKey(parentId: ChannelID, userId: UserID): string {
|
||||||
return `slowmode-thread:${parentId}:${userId}`;
|
return `slowmode-thread:${parentId}:${userId}`;
|
||||||
@@ -159,13 +158,22 @@ export class ThreadCreationService {
|
|||||||
}
|
}
|
||||||
const starter = await this.buildStarterEvent(params.viewer, params.user.id, view, params.requestCache);
|
const starter = await this.buildStarterEvent(params.viewer, params.user.id, view, params.requestCache);
|
||||||
if (starter) events.push(starter);
|
if (starter) events.push(starter);
|
||||||
if (Date.now() - snowflakeToDate(source.id).getTime() >= THREAD_CREATED_MESSAGE_MIN_SOURCE_AGE_MS) {
|
if (!(await this.isAmongRecentMessages(parentAuth.channel.id, source.id))) {
|
||||||
events.push(await this.createThreadCreatedMessage(parentAuth.channel, view, params.user.id));
|
events.push(await this.createThreadCreatedMessage(parentAuth.channel, view, params.user.id));
|
||||||
}
|
}
|
||||||
await dispatchThreadEvents(this.ctx.gatewayService, view.state.guildId, events);
|
await dispatchThreadEvents(this.ctx.gatewayService, view.state.guildId, events);
|
||||||
return mapThreadToResponse(view);
|
return mapThreadToResponse(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async isAmongRecentMessages(channelId: ChannelID, messageId: MessageID): Promise<boolean> {
|
||||||
|
const recent = await this.ctx.channelRepository.messages.listMessages(
|
||||||
|
channelId,
|
||||||
|
undefined,
|
||||||
|
THREAD_CREATED_MESSAGE_RECENT_WINDOW,
|
||||||
|
);
|
||||||
|
return recent.some((message) => message.id === messageId);
|
||||||
|
}
|
||||||
|
|
||||||
async authenticateParent(viewer: ThreadViewer, userId: UserID, channelId: ChannelID): Promise<AuthenticatedChannel> {
|
async authenticateParent(viewer: ThreadViewer, userId: UserID, channelId: ChannelID): Promise<AuthenticatedChannel> {
|
||||||
return this.ctx.channelAuth.getChannelAuthenticated({userId, channelId, viewer});
|
return this.ctx.channelAuth.getChannelAuthenticated({userId, channelId, viewer});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -268,25 +268,28 @@ describe('thread routes', () => {
|
|||||||
expect(member?.flags).toBe(ThreadMemberFlags.HAS_INTERACTED);
|
expect(member?.flags).toBe(ThreadMemberFlags.HAS_INTERACTED);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('announces threads started from older messages in the parent without moving its last message', async () => {
|
it('announces threads started from messages above the latest five without moving its last message', async () => {
|
||||||
const s = await setup();
|
const s = await setup();
|
||||||
const message = await sendMessage(harness, s.owner.token, s.channelId, 'old news');
|
const pushedUp = await sendMessage(harness, s.owner.token, s.channelId, 'old news');
|
||||||
const now = Date.now();
|
const nearBottom = await sendMessage(harness, s.owner.token, s.channelId, 'recent news');
|
||||||
vi.spyOn(Date, 'now').mockReturnValue(now + 6 * 60 * 1000);
|
let latest = nearBottom;
|
||||||
|
for (let i = 0; i < 4; i++) latest = await sendMessage(harness, s.owner.token, s.channelId, `filler ${i}`);
|
||||||
const dispatch = vi.spyOn(NoopGatewayService.prototype, 'dispatchGuild');
|
const dispatch = vi.spyOn(NoopGatewayService.prototype, 'dispatchGuild');
|
||||||
|
for (const message of [nearBottom, pushedUp]) {
|
||||||
await threadsRequest(harness, s.owner.token)
|
await threadsRequest(harness, s.owner.token)
|
||||||
.post(`/channels/${s.channelId}/messages/${message.id}/threads`)
|
.post(`/channels/${s.channelId}/messages/${message.id}/threads`)
|
||||||
.body({name: 'Later'})
|
.body({name: message.content})
|
||||||
.expect(201)
|
.expect(201)
|
||||||
.execute();
|
.execute();
|
||||||
|
}
|
||||||
const announced = dispatch.mock.calls
|
const announced = dispatch.mock.calls
|
||||||
.map(([params]) => params.data as MessageResponse)
|
.map(([params]) => params.data as MessageResponse)
|
||||||
.filter((data) => data.type === MessageTypes.THREAD_CREATED);
|
.filter((data) => data.type === MessageTypes.THREAD_CREATED);
|
||||||
expect(announced).toHaveLength(1);
|
expect(announced).toHaveLength(1);
|
||||||
expect(announced[0]).toMatchObject({channel_id: s.channelId, content: 'Later'});
|
expect(announced[0]).toMatchObject({channel_id: s.channelId, content: 'old news'});
|
||||||
expect(announced[0]!.message_reference?.channel_id).toBe(message.id);
|
expect(announced[0]!.message_reference?.channel_id).toBe(pushedUp.id);
|
||||||
const parent = await repository.findUnique(createChannelID(BigInt(s.channelId)));
|
const parent = await repository.findUnique(createChannelID(BigInt(s.channelId)));
|
||||||
expect(parent?.lastMessageId?.toString()).toBe(message.id);
|
expect(parent?.lastMessageId?.toString()).toBe(latest.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('refuses a second thread on the same message', async () => {
|
it('refuses a second thread on the same message', async () => {
|
||||||
|
|||||||
Reference in new issue
Block a user