mirror of
https://github.com/block/buzz.git
synced 2026-11-12 04:12:21 +00:00
fix(collections): surface pull requests from channels
Signed-off-by: John Tennant <jtennant@squareup.com>
This commit is contained in:
@@ -229,6 +229,52 @@ test("suppresses matching PR mention rows across sources but keeps unrelated cha
|
||||
);
|
||||
});
|
||||
|
||||
test("suppresses a PR conversation without hiding another thread from the same channel source", () => {
|
||||
const message = (eventId, threadRootId, label) => ({
|
||||
activityType: "channel-message",
|
||||
actorIdentity: "buzz:human",
|
||||
authorPubkey: "human",
|
||||
channelId: "channel-1",
|
||||
createdAt: 1,
|
||||
eventId,
|
||||
kind: "thread",
|
||||
label,
|
||||
sourceMemberId: "channel-source",
|
||||
threadRootId,
|
||||
url: "",
|
||||
});
|
||||
const prConversation = message("pr-reply", "pr-thread", "PR chatter");
|
||||
const unrelatedConversation = message(
|
||||
"design-reply",
|
||||
"design-thread",
|
||||
"Unrelated design decision",
|
||||
);
|
||||
const pullRequest = {
|
||||
...prConversation,
|
||||
activityType: "github-pr",
|
||||
kind: "pull request",
|
||||
label: "PR #7",
|
||||
sourceEventIds: ["pr-reply"],
|
||||
sourceMemberIds: ["channel-source"],
|
||||
url: "https://github.com/block/buzz/pull/7",
|
||||
};
|
||||
const stateChange = {
|
||||
...pullRequest,
|
||||
activityType: "github-pr-activity",
|
||||
createdAt: 2,
|
||||
kind: "PR review",
|
||||
url: `${pullRequest.url}#pullrequestreview-1`,
|
||||
};
|
||||
|
||||
assert.deepEqual(
|
||||
projectCollectionActivity(
|
||||
[prConversation, unrelatedConversation, pullRequest, stateChange],
|
||||
{ human: false },
|
||||
),
|
||||
[unrelatedConversation, stateChange],
|
||||
);
|
||||
});
|
||||
|
||||
test("names channel-derived live thread provenance", () => {
|
||||
assert.equal(
|
||||
collectionChannelThreadProvenance("#design"),
|
||||
|
||||
@@ -362,15 +362,22 @@ export function projectCollectionActivity(
|
||||
activity: readonly DerivedCollectionActivity[],
|
||||
actorIsAgent: Readonly<Record<string, boolean | undefined>>,
|
||||
): DerivedCollectionActivity[] {
|
||||
const conversationIdentity = (item: DerivedCollectionActivity) =>
|
||||
item.channelId
|
||||
? `${item.channelId}:${item.threadRootId ?? item.eventId ?? item.sourceMemberId}`
|
||||
: null;
|
||||
const pullRequestUrls = new Set(
|
||||
activity
|
||||
.filter((item) => item.activityType === "github-pr")
|
||||
.map((item) => item.url.toLocaleLowerCase()),
|
||||
);
|
||||
const pullRequestSources = new Set(
|
||||
const pullRequestConversations = new Set(
|
||||
activity
|
||||
.filter((item) => item.activityType === "github-pr")
|
||||
.flatMap((item) => item.sourceMemberIds ?? [item.sourceMemberId]),
|
||||
.flatMap((item) => {
|
||||
const identity = conversationIdentity(item);
|
||||
return identity ? [identity] : [];
|
||||
}),
|
||||
);
|
||||
const pullRequestEvents = new Set(
|
||||
activity
|
||||
@@ -384,7 +391,7 @@ export function projectCollectionActivity(
|
||||
]).some((mention) => pullRequestUrls.has(mention.url.toLocaleLowerCase()));
|
||||
return (
|
||||
!mentionsResolvedPullRequest &&
|
||||
!pullRequestSources.has(item.sourceMemberId) &&
|
||||
!pullRequestConversations.has(conversationIdentity(item) ?? "") &&
|
||||
(!item.eventId || !pullRequestEvents.has(item.eventId))
|
||||
);
|
||||
});
|
||||
|
||||
@@ -67,7 +67,6 @@ function sourceLabel(member: CollectionMember): string {
|
||||
function messageActivity(
|
||||
member: CollectionMember,
|
||||
events: Awaited<ReturnType<typeof getChannelWindowEvents>>,
|
||||
includePullRequests: boolean,
|
||||
): DerivedCollectionActivity[] {
|
||||
const reference = member.reference;
|
||||
if (
|
||||
@@ -102,25 +101,21 @@ function messageActivity(
|
||||
reference.type === "thread" ? reference.root_event_id : thread.rootId,
|
||||
url: "",
|
||||
};
|
||||
const pullRequests = includePullRequests
|
||||
? extractGitHubPullRequestLinks([event]).map(
|
||||
(link): DerivedCollectionActivity => ({
|
||||
...link,
|
||||
activityType: "github-pr",
|
||||
actorIdentity: `buzz:${event.pubkey.toLocaleLowerCase()}`,
|
||||
authorPubkey: event.pubkey,
|
||||
channelId,
|
||||
createdAt: event.created_at,
|
||||
eventId: event.id,
|
||||
provenanceLabel: `${sourceLabel(member)} → mentioned PR`,
|
||||
sourceMemberId: member.id,
|
||||
threadRootId:
|
||||
reference.type === "thread"
|
||||
? reference.root_event_id
|
||||
: thread.rootId,
|
||||
}),
|
||||
)
|
||||
: [];
|
||||
const pullRequests = extractGitHubPullRequestLinks([event]).map(
|
||||
(link): DerivedCollectionActivity => ({
|
||||
...link,
|
||||
activityType: "github-pr",
|
||||
actorIdentity: `buzz:${event.pubkey.toLocaleLowerCase()}`,
|
||||
authorPubkey: event.pubkey,
|
||||
channelId,
|
||||
createdAt: event.created_at,
|
||||
eventId: event.id,
|
||||
provenanceLabel: `${sourceLabel(member)} → mentioned PR`,
|
||||
sourceMemberId: member.id,
|
||||
threadRootId:
|
||||
reference.type === "thread" ? reference.root_event_id : thread.rootId,
|
||||
}),
|
||||
);
|
||||
return [message, ...pullRequests];
|
||||
});
|
||||
}
|
||||
@@ -332,7 +327,7 @@ async function discoverCollectionMemberActivity(
|
||||
},
|
||||
);
|
||||
return {
|
||||
items: [...threadActivity, ...messageActivity(member, messages, false)],
|
||||
items: [...threadActivity, ...messageActivity(member, messages)],
|
||||
warnings: [],
|
||||
};
|
||||
}
|
||||
@@ -344,14 +339,14 @@ async function discoverCollectionMemberActivity(
|
||||
{ limit: COLLECTION_CHANNEL_DISCOVERY_LIMIT },
|
||||
);
|
||||
return {
|
||||
items: messageActivity(member, [root, ...replies.events], true),
|
||||
items: messageActivity(member, [root, ...replies.events]),
|
||||
warnings: [],
|
||||
};
|
||||
}
|
||||
if (member.reference.type === "message") {
|
||||
const event = await getEventById(member.reference.event_id);
|
||||
return {
|
||||
items: messageActivity(member, [event], true),
|
||||
items: messageActivity(member, [event]),
|
||||
warnings: [],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -52,6 +52,8 @@ import {
|
||||
CollectionRowActions,
|
||||
DerivedCollectionActivityRow,
|
||||
} from "./CollectionDetailRows";
|
||||
|
||||
const PEOPLE_PREVIEW_LIMIT = 8;
|
||||
import { CollectionGlyph } from "./CollectionGlyph";
|
||||
import { CollectionIconDialog } from "./CollectionIconDialog";
|
||||
|
||||
@@ -98,6 +100,12 @@ export function CollectionDetailScreen({
|
||||
!identity.startsWith("buzz:") ||
|
||||
!profiles[identity.slice("buzz:".length)]?.isAgent,
|
||||
);
|
||||
const visiblePeopleIdentities = peopleIdentities.slice(
|
||||
0,
|
||||
PEOPLE_PREVIEW_LIMIT,
|
||||
);
|
||||
const hiddenPeopleCount =
|
||||
peopleIdentities.length - visiblePeopleIdentities.length;
|
||||
const members = (data?.members ?? []).filter((member) =>
|
||||
collectionMemberMatches(member, search, type),
|
||||
);
|
||||
@@ -454,7 +462,7 @@ export function CollectionDetailScreen({
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
{peopleIdentities.map((identity) => {
|
||||
{visiblePeopleIdentities.map((identity) => {
|
||||
const pubkey = identity.startsWith("buzz:")
|
||||
? identity.slice("buzz:".length)
|
||||
: null;
|
||||
@@ -494,6 +502,11 @@ export function CollectionDetailScreen({
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
{hiddenPeopleCount > 0 ? (
|
||||
<span className="px-1 text-sm text-muted-foreground">
|
||||
+{hiddenPeopleCount} more
|
||||
</span>
|
||||
) : null}
|
||||
{selectedPerson ? (
|
||||
<Button
|
||||
onClick={() => setSelectedPerson(null)}
|
||||
|
||||
@@ -218,10 +218,10 @@ test("creates a collection and adds supported reference types", async ({
|
||||
});
|
||||
}, githubPullRequestUrl);
|
||||
await page.evaluate(
|
||||
({ failingUrl, url }) => {
|
||||
({ failingUrl }) => {
|
||||
window.__BUZZ_E2E_EMIT_MOCK_MESSAGE__?.({
|
||||
channelName: "general",
|
||||
content: `Thread status for Bird Voice: ${url} and ${failingUrl}`,
|
||||
content: `Thread status for Bird Voice: ${failingUrl}`,
|
||||
parentEventId: "mock-general-welcome",
|
||||
extraTags: [
|
||||
["e", "mock-general-welcome", "", "root"],
|
||||
@@ -229,8 +229,14 @@ test("creates a collection and adds supported reference types", async ({
|
||||
],
|
||||
});
|
||||
},
|
||||
{ failingUrl: failingGithubPullRequestUrl, url: githubPullRequestUrl },
|
||||
{ failingUrl: failingGithubPullRequestUrl },
|
||||
);
|
||||
await page.evaluate(() => {
|
||||
window.__BUZZ_E2E_EMIT_MOCK_MESSAGE__?.({
|
||||
channelName: "general",
|
||||
content: "Unrelated channel update",
|
||||
});
|
||||
});
|
||||
await page.getByRole("button", { name: "Add to collection" }).click();
|
||||
await page
|
||||
.getByTestId("add-to-collection-dialog")
|
||||
@@ -304,6 +310,9 @@ test("creates a collection and adds supported reference types", async ({
|
||||
await expect(page.getByTestId("collection-activity")).not.toContainText(
|
||||
"Please review",
|
||||
);
|
||||
await expect(page.getByTestId("collection-activity")).toContainText(
|
||||
"Unrelated channel update",
|
||||
);
|
||||
await expect(pullRequest.getByLabel(/^Derived from /)).toBeVisible();
|
||||
await expect(pullRequest).toContainText("Live collections activity feed");
|
||||
await expect(pullRequest).toContainText("open by mock-author");
|
||||
|
||||
Reference in New Issue
Block a user