mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
fix: prioritize owned agents in mention autocomplete
Co-authored-by: npub1shglkdhngx3hrnhf4gf8vhpqdrmeludctechdvpwd3988zzs7ncq2cmtxu <85d1fb36f341a371cee9aa12765c2068f79ff1b85e7176b02e6c4a738850f4f0@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1shglkdhngx3hrnhf4gf8vhpqdrmeludctechdvpwd3988zzs7ncq2cmtxu <85d1fb36f341a371cee9aa12765c2068f79ff1b85e7176b02e6c4a738850f4f0@sprout-oss.stage.blox.sqprod.co>
This commit is contained in:
parent
5d77fa5749
commit
9b211ae4d0
@@ -62,6 +62,7 @@ export function flushMentionDebounce<T extends MentionCandidateWithUI>(opts: {
|
||||
opts.candidates,
|
||||
mention.query,
|
||||
opts.activePersonaIds,
|
||||
opts.currentPubkey,
|
||||
);
|
||||
|
||||
if (ranked.length === 0) {
|
||||
|
||||
@@ -21,12 +21,49 @@ function rankedPubkeys(
|
||||
candidates,
|
||||
query = "brain",
|
||||
activePersonaIds = new Set(),
|
||||
currentPubkey,
|
||||
) {
|
||||
return rankMentionCandidates(candidates, query, activePersonaIds).map(
|
||||
return rankMentionCandidates(
|
||||
candidates,
|
||||
query,
|
||||
activePersonaIds,
|
||||
currentPubkey,
|
||||
).map(
|
||||
(item) => item.candidate.pubkey ?? `persona:${item.candidate.personaId}`,
|
||||
);
|
||||
}
|
||||
|
||||
test("rankMentionCandidates: current user's agents rank before all other candidates", () => {
|
||||
const currentPubkey = "a".repeat(64);
|
||||
const ownedAgent = candidate({
|
||||
displayName: "Brain",
|
||||
isAgent: true,
|
||||
ownerPubkey: currentPubkey.toUpperCase(),
|
||||
pubkey: "7".repeat(64),
|
||||
});
|
||||
const channelMember = candidate({
|
||||
displayName: "Brain",
|
||||
isMember: true,
|
||||
pubkey: CHANNEL_BRAIN_PUBKEY,
|
||||
});
|
||||
const remoteAgent = candidate({
|
||||
displayName: "Brain",
|
||||
isAgent: true,
|
||||
ownerPubkey: "b".repeat(64),
|
||||
pubkey: OTHER_BRAIN_PUBKEY,
|
||||
});
|
||||
|
||||
assert.deepEqual(
|
||||
rankedPubkeys(
|
||||
[channelMember, remoteAgent, ownedAgent],
|
||||
"brain",
|
||||
new Set(),
|
||||
currentPubkey,
|
||||
),
|
||||
["7".repeat(64), CHANNEL_BRAIN_PUBKEY, OTHER_BRAIN_PUBKEY],
|
||||
);
|
||||
});
|
||||
|
||||
test("rankMentionCandidates: channel members outrank runnable personas, people, and other agents", () => {
|
||||
const persona = candidate({
|
||||
kind: "persona",
|
||||
|
||||
@@ -9,11 +9,13 @@ export type MentionCandidateForRanking = {
|
||||
personaName?: string | null;
|
||||
pubkey?: string;
|
||||
secondaryLabel?: string | null;
|
||||
ownerPubkey?: string | null;
|
||||
};
|
||||
|
||||
export type RankedMentionCandidate<T extends MentionCandidateForRanking> = {
|
||||
candidate: T;
|
||||
groupRank: number;
|
||||
isOwnedByCurrentUser: boolean;
|
||||
label: string;
|
||||
order: number;
|
||||
score: number;
|
||||
@@ -55,8 +57,12 @@ export function rankMentionCandidates<T extends MentionCandidateForRanking>(
|
||||
candidates: readonly T[],
|
||||
query: string,
|
||||
activePersonaIds: ReadonlySet<string> = new Set(),
|
||||
currentPubkey?: string | null,
|
||||
): RankedMentionCandidate<T>[] {
|
||||
const lowerQuery = query.toLowerCase();
|
||||
const normalizedCurrentPubkey = currentPubkey
|
||||
? normalizePubkey(currentPubkey)
|
||||
: null;
|
||||
|
||||
return candidates
|
||||
.map((candidate, order) => {
|
||||
@@ -91,12 +97,27 @@ export function rankMentionCandidates<T extends MentionCandidateForRanking>(
|
||||
: null
|
||||
: null;
|
||||
const score = labelScore !== null ? labelScore : pubkeyScore;
|
||||
const isOwnedByCurrentUser =
|
||||
candidate.isAgent &&
|
||||
normalizedCurrentPubkey !== null &&
|
||||
candidate.ownerPubkey != null &&
|
||||
normalizePubkey(candidate.ownerPubkey) === normalizedCurrentPubkey;
|
||||
|
||||
return { candidate, groupRank, label, order, score };
|
||||
return {
|
||||
candidate,
|
||||
groupRank,
|
||||
isOwnedByCurrentUser,
|
||||
label,
|
||||
order,
|
||||
score,
|
||||
};
|
||||
})
|
||||
.filter((item): item is RankedMentionCandidate<T> => item.score !== null)
|
||||
.sort(
|
||||
(a, b) =>
|
||||
a.groupRank - b.groupRank || a.score - b.score || a.order - b.order,
|
||||
Number(b.isOwnedByCurrentUser) - Number(a.isOwnedByCurrentUser) ||
|
||||
a.groupRank - b.groupRank ||
|
||||
a.score - b.score ||
|
||||
a.order - b.order,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -519,7 +519,6 @@ export function useMentions(
|
||||
() => searchableNames.map((n) => n.toLowerCase()),
|
||||
[searchableNames],
|
||||
);
|
||||
|
||||
// --- Debounce infrastructure for updateMentionQuery ---
|
||||
const debounceTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(
|
||||
null,
|
||||
@@ -552,6 +551,7 @@ export function useMentions(
|
||||
mentionCandidatesWithTeams,
|
||||
mentionQuery,
|
||||
activePersonaIds,
|
||||
currentPubkey,
|
||||
)
|
||||
.slice(
|
||||
0,
|
||||
|
||||
@@ -133,5 +133,9 @@ final mentionCandidatesProvider = Provider.family
|
||||
currentPubkey: currentPubkey,
|
||||
);
|
||||
|
||||
return rankMentionCandidates(candidates, args.query);
|
||||
return rankMentionCandidates(
|
||||
candidates,
|
||||
args.query,
|
||||
currentPubkey: currentPubkey,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -60,11 +60,13 @@ int? _scoreLabel(String label, String lowerQuery) {
|
||||
/// stable original order.
|
||||
List<MentionCandidate> rankMentionCandidates(
|
||||
List<MentionCandidate> candidates,
|
||||
String query,
|
||||
) {
|
||||
String query, {
|
||||
String? currentPubkey,
|
||||
}) {
|
||||
final lowerQuery = query.toLowerCase();
|
||||
final currentLower = currentPubkey?.toLowerCase();
|
||||
|
||||
final ranked = <(MentionCandidate, int, int, int)>[];
|
||||
final ranked = <(MentionCandidate, bool, int, int, int)>[];
|
||||
for (var order = 0; order < candidates.length; order++) {
|
||||
final candidate = candidates[order];
|
||||
|
||||
@@ -89,15 +91,27 @@ List<MentionCandidate> rankMentionCandidates(
|
||||
}
|
||||
|
||||
if (score == null) continue;
|
||||
ranked.add((candidate, _groupRank(candidate), score, order));
|
||||
final isOwnedByCurrentUser =
|
||||
candidate.isAgent &&
|
||||
currentLower != null &&
|
||||
candidate.ownerPubkey?.toLowerCase() == currentLower;
|
||||
ranked.add((
|
||||
candidate,
|
||||
isOwnedByCurrentUser,
|
||||
_groupRank(candidate),
|
||||
score,
|
||||
order,
|
||||
));
|
||||
}
|
||||
|
||||
ranked.sort((a, b) {
|
||||
final group = a.$2.compareTo(b.$2);
|
||||
final owned = (b.$2 ? 1 : 0).compareTo(a.$2 ? 1 : 0);
|
||||
if (owned != 0) return owned;
|
||||
final group = a.$3.compareTo(b.$3);
|
||||
if (group != 0) return group;
|
||||
final score = a.$3.compareTo(b.$3);
|
||||
final score = a.$4.compareTo(b.$4);
|
||||
if (score != 0) return score;
|
||||
return a.$4.compareTo(b.$4);
|
||||
return a.$5.compareTo(b.$5);
|
||||
});
|
||||
|
||||
return [for (final item in ranked) item.$1];
|
||||
|
||||
@@ -12,6 +12,7 @@ MentionCandidate candidate({
|
||||
String? secondaryLabel,
|
||||
bool isAgent = false,
|
||||
bool isMember = false,
|
||||
String? ownerPubkey,
|
||||
String? pubkey,
|
||||
}) {
|
||||
return MentionCandidate(
|
||||
@@ -20,20 +21,50 @@ MentionCandidate candidate({
|
||||
secondaryLabel: secondaryLabel,
|
||||
isAgent: isAgent,
|
||||
isMember: isMember,
|
||||
ownerPubkey: ownerPubkey,
|
||||
);
|
||||
}
|
||||
|
||||
List<String> rankedPubkeys(
|
||||
List<MentionCandidate> candidates, [
|
||||
String query = 'brain',
|
||||
String? currentPubkey,
|
||||
]) {
|
||||
return [
|
||||
for (final ranked in rankMentionCandidates(candidates, query))
|
||||
for (final ranked in rankMentionCandidates(
|
||||
candidates,
|
||||
query,
|
||||
currentPubkey: currentPubkey,
|
||||
))
|
||||
ranked.pubkey,
|
||||
];
|
||||
}
|
||||
|
||||
void main() {
|
||||
test("current user's agents rank before all other candidates", () {
|
||||
final currentPubkey = 'a' * 64;
|
||||
final ownedAgent = candidate(
|
||||
isAgent: true,
|
||||
ownerPubkey: currentPubkey.toUpperCase(),
|
||||
pubkey: '7' * 64,
|
||||
);
|
||||
final channelMember = candidate(isMember: true, pubkey: channelBrainPubkey);
|
||||
final remoteAgent = candidate(
|
||||
isAgent: true,
|
||||
ownerPubkey: 'b' * 64,
|
||||
pubkey: otherBrainPubkey,
|
||||
);
|
||||
|
||||
expect(
|
||||
rankedPubkeys(
|
||||
[channelMember, remoteAgent, ownedAgent],
|
||||
'brain',
|
||||
currentPubkey,
|
||||
),
|
||||
['7' * 64, channelBrainPubkey, otherBrainPubkey],
|
||||
);
|
||||
});
|
||||
|
||||
test('channel members outrank people and other agents', () {
|
||||
final remoteAgent = candidate(isAgent: true, pubkey: otherBrainPubkey);
|
||||
final person = candidate(pubkey: '6' * 64);
|
||||
|
||||
Reference in New Issue
Block a user