feat: better p and authors titles

This commit is contained in:
Alejandro Gómez
2025-12-12 10:12:57 +01:00
parent d93bbf1eec
commit 16158e7045
2 changed files with 80 additions and 45 deletions

View File

@@ -10,7 +10,8 @@ import {
} from "@/constants/command-icons";
import type { EventPointer, AddressPointer } from "nostr-tools/nip19";
import type { LucideIcon } from "lucide-react";
import { nip19 } from "nostr-tools";
import { kinds, nip19 } from "nostr-tools";
import { ProfileContent } from "applesauce-core/helpers";
export interface WindowTitleData {
title: string;
@@ -18,6 +19,51 @@ export interface WindowTitleData {
tooltip?: string;
}
/**
* Format profile names with prefix
* @param prefix - Prefix to use (e.g., 'by ', '@ ')
* @param pubkeys - Array of pubkeys to format
* @param profiles - Array of corresponding profile metadata
* @returns Formatted string like "by Alice, Bob & 3 others" or null if no pubkeys
*/
function formatProfileNames(
prefix: string,
pubkeys: string[],
profiles: (ProfileContent | undefined)[],
): string | null {
if (!pubkeys.length) return null;
const names: string[] = [];
const [pubkey1, pubkey2] = pubkeys;
const [profile1, profile2] = profiles;
// Add first profile
if (profile1) {
const name = profile1.display_name || profile1.name;
names.push(name || `${pubkey1.slice(0, 8)}...`);
} else if (pubkey1) {
names.push(`${pubkey1.slice(0, 8)}...`);
}
// Add second profile if exists
if (pubkeys.length > 1) {
if (profile2) {
const name = profile2.display_name || profile2.name;
names.push(name || `${pubkey2.slice(0, 8)}...`);
} else if (pubkey2) {
names.push(`${pubkey2.slice(0, 8)}...`);
}
}
// Add "& X other(s)" if more than 2
if (pubkeys.length > 2) {
const othersCount = pubkeys.length - 2;
names.push(`& ${othersCount} other${othersCount > 1 ? "s" : ""}`);
}
return names.length > 0 ? `${prefix}${names.join(", ")}` : null;
}
/**
* Generate raw command string from window appId and props
*/
@@ -115,10 +161,7 @@ function useDynamicTitle(window: WindowInstance): WindowTitleData {
if (appId !== "profile" || !profilePubkey) return null;
if (profile) {
const displayName = profile.display_name || profile.name;
if (displayName) {
return `@${displayName}`;
}
return profile.display_name || profile.name;
}
return `Profile ${profilePubkey.slice(0, 8)}...`;
@@ -134,13 +177,13 @@ function useDynamicTitle(window: WindowInstance): WindowTitleData {
const kindName = getKindName(event.kind);
// For text-based events, show a preview
if (event.kind === 1 && event.content) {
if (event.kind === kinds.ShortTextNote && event.content) {
const preview = event.content.slice(0, 40).trim();
return preview ? `${kindName}: ${preview}...` : kindName;
}
// For articles (kind 30023), show title tag
if (event.kind === 30023) {
if (event.kind === kinds.LongFormArticle) {
const titleTag = event.tags.find((t) => t[0] === "title")?.[1];
if (titleTag) {
return titleTag.length > 50 ? `${titleTag.slice(0, 50)}...` : titleTag;
@@ -148,7 +191,7 @@ function useDynamicTitle(window: WindowInstance): WindowTitleData {
}
// For highlights (kind 9802), show preview
if (event.kind === 9802 && event.content) {
if (event.kind === kinds.Highlights && event.content) {
const preview = event.content.slice(0, 40).trim();
return preview ? `Highlight: ${preview}...` : "Highlight";
}
@@ -174,14 +217,19 @@ function useDynamicTitle(window: WindowInstance): WindowTitleData {
}
}, [appId, props]);
// Fetch profiles for REQ authors (up to 2)
// Fetch profiles for REQ authors and tagged users (up to 2 each)
const reqAuthors =
appId === "req" && props.filter?.authors ? props.filter.authors : [];
const author1Pubkey = reqAuthors[0] || "";
const author2Pubkey = reqAuthors[1] || "";
const [author1Pubkey, author2Pubkey] = reqAuthors;
const author1Profile = useProfile(author1Pubkey);
const author2Profile = useProfile(author2Pubkey);
const reqTagged =
appId === "req" && props.filter?.["#p"] ? props.filter["#p"] : [];
const [tagged1Pubkey, tagged2Pubkey] = reqTagged;
const tagged1Profile = useProfile(tagged1Pubkey);
const tagged2Profile = useProfile(tagged2Pubkey);
// REQ titles
const reqTitle = useMemo(() => {
if (appId !== "req") return null;
@@ -202,48 +250,34 @@ function useDynamicTitle(window: WindowInstance): WindowTitleData {
}
}
// Format tagged users with @ prefix
if (filter["#p"] && filter["#p"].length > 0) {
const taggedText = formatProfileNames("@", reqTagged, [
tagged1Profile,
tagged2Profile,
]);
if (taggedText) parts.push(taggedText);
}
// Format authors with "by " prefix
if (filter.authors && filter.authors.length > 0) {
const authorNames: string[] = [];
// Add first author
if (author1Profile) {
const name = author1Profile.display_name || author1Profile.name;
authorNames.push(
name ? `@${name}` : `@${author1Pubkey.slice(0, 8)}...`,
);
} else if (author1Pubkey) {
authorNames.push(`@${author1Pubkey.slice(0, 8)}...`);
}
// Add second author if exists
if (filter.authors.length > 1) {
if (author2Profile) {
const name = author2Profile.display_name || author2Profile.name;
authorNames.push(
name ? `@${name}` : `@${author2Pubkey.slice(0, 8)}...`,
);
} else if (author2Pubkey) {
authorNames.push(`@${author2Pubkey.slice(0, 8)}...`);
}
}
// Add "& X other(s)" if more than 2
if (filter.authors.length > 2) {
const othersCount = filter.authors.length - 2;
authorNames.push(`& ${othersCount} other${othersCount > 1 ? "s" : ""}`);
}
parts.push(authorNames.join(", "));
const authorsText = formatProfileNames("by ", reqAuthors, [
author1Profile,
author2Profile,
]);
if (authorsText) parts.push(authorsText);
}
return parts.length > 0 ? parts.join(" • ") : "REQ";
}, [
appId,
props,
reqAuthors,
reqTagged,
author1Profile,
author2Profile,
author1Pubkey,
author2Pubkey,
tagged1Profile,
tagged2Profile,
]);
// Encode/Decode titles

View File

@@ -4,11 +4,12 @@ import { ProfileContent } from "applesauce-core/helpers";
import { kinds } from "nostr-tools";
import db from "@/services/db";
export function useProfile(pubkey: string): ProfileContent | undefined {
export function useProfile(pubkey?: string): ProfileContent | undefined {
const [profile, setProfile] = useState<ProfileContent | undefined>();
useEffect(() => {
let mounted = true;
if (!pubkey) return;
// Load from IndexedDB first
db.profiles.get(pubkey).then((cachedProfile) => {