fix(ui): relabel agent owner attribution from "owned by" to "managed by" (#2133)

Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
Co-authored-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
This commit is contained in:
Tyler
2026-07-19 14:39:59 -04:00
committed by GitHub
parent 3881bff619
commit a1e977cd1b
12 changed files with 33 additions and 31 deletions

View File

@@ -895,7 +895,7 @@ function AddMemberSearchResultRow({
</div>
{ownerLabel ? (
<span className="block truncate text-xs text-muted-foreground">
owned by {ownerLabel}
managed by {ownerLabel}
</span>
) : null}
</div>

View File

@@ -184,16 +184,16 @@ export const MentionAutocomplete = React.memo(function MentionAutocomplete({
className="min-w-0 truncate"
title={
suggestion.ownerLabel && suggestion.notInChannel
? `owned by ${suggestion.ownerLabel} · not in channel`
? `managed by ${suggestion.ownerLabel} · not in channel`
: suggestion.ownerLabel
? `owned by ${suggestion.ownerLabel}`
? `managed by ${suggestion.ownerLabel}`
: "not in channel"
}
>
{suggestion.ownerLabel && suggestion.notInChannel
? `owned by ${suggestion.ownerLabel} · not in channel`
? `managed by ${suggestion.ownerLabel} · not in channel`
: suggestion.ownerLabel
? `owned by ${suggestion.ownerLabel}`
? `managed by ${suggestion.ownerLabel}`
: "not in channel"}
</span>
) : null}

View File

@@ -15,7 +15,7 @@ export function MessageAgentOwner({
data-testid="message-agent-owner"
>
<span className="sr-only">
{ownerLabel ? "Agent owned by" : "Agent; owner unavailable"}
{ownerLabel ? "Agent managed by" : "Agent; owner unavailable"}
</span>
{ownerPubkey && ownerLabel ? (
<>
@@ -24,7 +24,7 @@ export function MessageAgentOwner({
className="inline-flex shrink-0 items-baseline gap-1 leading-4"
>
<Bot className="relative -top-px h-3.5 w-3.5 self-center" />
<span>owned by</span>
<span>managed by</span>
</span>
<UserProfilePopover
pubkey={ownerPubkey}

View File

@@ -135,7 +135,7 @@ export function NewMessageResultRow({
</div>
{ownerLabel ? (
<span className="block truncate text-xs text-muted-foreground">
owned by {ownerLabel}
managed by {ownerLabel}
</span>
) : null}
</div>

View File

@@ -47,7 +47,7 @@ export type ProfileField = {
const AGENT_INFO_LABELS = new Set([
"Public key",
"Owned by",
"Managed by",
"NIP-05",
"Agent type",
"Capabilities",
@@ -277,12 +277,12 @@ export function buildOwnerFields({
</span>
),
icon: UserRound,
label: "Owned by",
label: "Managed by",
onClick:
ownerClickable && ownerProfilePubkey
? () => onOpenProfile?.(ownerProfilePubkey)
: undefined,
testId: "user-profile-owned-by",
testId: "user-profile-managed-by",
});
}
@@ -407,17 +407,17 @@ export function buildOwnerFields({
function orderProfileFields(fields: ProfileField[]) {
const visibilityLabel = "Visibility";
const publicKeyLabel = "Public key";
const ownedByLabel = "Owned by";
const managedByLabel = "Managed by";
const statusLabel = "Status";
return [
...fields.filter((field) => field.label === visibilityLabel),
...fields.filter((field) => field.label === publicKeyLabel),
...fields.filter((field) => field.label === ownedByLabel),
...fields.filter((field) => field.label === managedByLabel),
...fields.filter(
(field) =>
field.label !== visibilityLabel &&
field.label !== publicKeyLabel &&
field.label !== ownedByLabel &&
field.label !== managedByLabel &&
field.copyValue,
),
...fields.filter((field) => field.label === statusLabel),
@@ -425,7 +425,7 @@ function orderProfileFields(fields: ProfileField[]) {
if (
field.label === visibilityLabel ||
field.label === publicKeyLabel ||
field.label === ownedByLabel ||
field.label === managedByLabel ||
field.label === statusLabel
) {
return false;

View File

@@ -534,7 +534,7 @@ export function UserProfilePopover({
className="mt-0.5 truncate text-xs leading-4 text-muted-foreground"
data-testid={`user-profile-popover-owner-${pubkey}`}
>
owned by {ownerLabel}
managed by {ownerLabel}
</p>
) : null}
{profileSubheader ? (

View File

@@ -300,7 +300,7 @@ test("thread autocomplete keeps multiple long names readable in a narrow panel",
row.getByTestId("mention-suggestion-avatar-fallback"),
).toBeVisible();
await expect(row.getByText("agent")).toBeVisible();
await expect(row.getByText("owned by you")).toBeVisible();
await expect(row.getByText("managed by you")).toBeVisible();
await expect(row.getByText(name)).not.toHaveCSS(
"text-overflow",
@@ -1738,7 +1738,7 @@ test("agent profile popover shows its owner", async ({ page }) => {
profilePopover.getByTestId(
`user-profile-popover-owner-${OWNED_AGENT_PROFILE_PUBKEY}`,
),
).toHaveText("owned by bob");
).toHaveText("managed by bob");
});
test("agent profile popover labels an agent owned by the viewer as you", async ({
@@ -1778,7 +1778,7 @@ test("agent profile popover labels an agent owned by the viewer as you", async (
profilePopover.getByTestId(
`user-profile-popover-owner-${OWNED_AGENT_PROFILE_PUBKEY}`,
),
).toHaveText("owned by you");
).toHaveText("managed by you");
});
test("agent profile popover falls back to the owner's pubkey", async ({
@@ -1818,7 +1818,7 @@ test("agent profile popover falls back to the owner's pubkey", async ({
profilePopover.getByTestId(
`user-profile-popover-owner-${OWNED_AGENT_PROFILE_PUBKEY}`,
),
).toHaveText("owned by 11111111…1111");
).toHaveText("managed by 11111111…1111");
});
test("human profile popover does not show an owner", async ({ page }) => {

View File

@@ -123,18 +123,20 @@ test("agent owner label identifies the agent and owner", async ({ page }) => {
await expect(ownerTreatment.locator("svg")).toBeVisible();
await expect(
ownerTreatment.getByText("owned by", { exact: true }),
ownerTreatment.getByText("managed by", { exact: true }),
).toBeVisible();
await expect(ownerTreatment.locator(".font-semibold")).toHaveText("bob");
await expect(ownerTreatment.getByRole("button")).toHaveAccessibleName("bob");
await expect(ownerTreatment.locator(".sr-only")).toHaveText("Agent owned by");
await expect(ownerTreatment.locator(".sr-only")).toHaveText(
"Agent managed by",
);
const joinedRow = page
.getByTestId("system-message-row")
.filter({ hasText: "alice" })
.filter({ hasText: "joined the channel" });
await expect(joinedRow.getByTestId("message-agent-owner")).toContainText(
"owned bybob",
"managed bybob",
);
});

View File

@@ -91,7 +91,7 @@ test("new-DM agent name swaps to its public key on name hover", async ({
const agentName = agentResult.getByTestId(`new-dm-name-${AGENT_PUBKEY}`);
const agentNpub = agentResult.getByTestId(`new-dm-npub-${AGENT_PUBKEY}`);
await expect(agentResult).toContainText("owned by you");
await expect(agentResult).toContainText("managed by you");
await expect(agentName).toContainText("Pinky");
await expect(agentNpub).toHaveCSS("opacity", "0");
@@ -127,7 +127,7 @@ test("new-DM agent name swaps to its public key on name hover", async ({
await expect(
settledAgentName.getByText("Pinky", { exact: true }),
).not.toHaveCSS("opacity", "1");
await expect(settledAgentResult).toContainText("owned by you");
await expect(settledAgentResult).toContainText("managed by you");
await waitForAnimations(page);
await page.getByTestId("new-message-page").screenshot({
path: `${SHOTS}/new-dm-agent-name-hover.png`,

View File

@@ -135,7 +135,7 @@ class ComposeBar extends HookConsumerWidget {
false;
// Preload profiles for channel members, mentionable agents, and their
// owners so @mention suggestions show names ("owned by …" included).
// owners so @mention suggestions show names ("managed by …" included).
final relayAgents = ref.watch(agentDirectoryProvider).asData?.value;
final agentOwners = ref.watch(agentOwnersProvider).asData?.value;
useEffect(
@@ -240,7 +240,7 @@ class ComposeBar extends HookConsumerWidget {
.take(_mentionSuggestionLimit)
.toList();
// Resolve owner names for the visible "owned by …" subtitles.
// Resolve owner names for the visible "managed by …" subtitles.
useEffect(() {
final ownerPubkeys = [for (final s in suggestions) ?s.ownerPubkey];
if (ownerPubkeys.isNotEmpty) {

View File

@@ -76,7 +76,7 @@ class _MentionSuggestions extends StatelessWidget {
/// The secondary info line under a mention suggestion — mirrors desktop's
/// `MentionAutocomplete` subtitle: bot icon + "agent" (or an "admin" badge
/// for human admins), then "owned by …" / "not in channel".
/// for human admins), then "managed by …" / "not in channel".
abstract final class _MentionSuggestionInfo {
static Widget? build(
BuildContext context, {
@@ -93,9 +93,9 @@ abstract final class _MentionSuggestionInfo {
final String? detail;
if (ownerLabel != null && notInChannel) {
detail = 'owned by $ownerLabel \u00b7 not in channel';
detail = 'managed by $ownerLabel \u00b7 not in channel';
} else if (ownerLabel != null) {
detail = 'owned by $ownerLabel';
detail = 'managed by $ownerLabel';
} else if (notInChannel) {
detail = 'not in channel';
} else {

View File

@@ -70,7 +70,7 @@ bool agentIsSharedWithUser(
agent.channelIds.any(sharedChannelIds.contains);
}
/// Format the "owned by …" label. Mirrors desktop's `formatOwnerLabel`.
/// Format the "managed by …" label. Mirrors desktop's `formatOwnerLabel`.
String? formatOwnerLabel(
String? ownerPubkey,
String? currentPubkey,