dmUtils.ts
1 import type { NostrEvent } from '@nostrify/nostrify'; 2 3 /** 4 * Validate that an event is a proper DM event 5 */ 6 export function validateDMEvent(event: NostrEvent): boolean { 7 // Must be kind 4 (NIP-04 DM) 8 if (event.kind !== 4) return false; 9 10 // Must have a 'p' tag 11 const hasRecipient = event.tags?.some(([name]) => name === 'p'); 12 if (!hasRecipient) return false; 13 14 // Must have content (even if encrypted) 15 if (!event.content) return false; 16 17 return true; 18 } 19 20 /** 21 * Get the recipient pubkey from a DM event 22 */ 23 export function getRecipientPubkey(event: NostrEvent): string | undefined { 24 return event.tags?.find(([name]) => name === 'p')?.[1]; 25 } 26 27 /** 28 * Get the conversation partner pubkey from a DM event 29 * (the other person in the conversation, not the current user) 30 */ 31 export function getConversationPartner(event: NostrEvent, userPubkey: string): string | undefined { 32 const isFromUser = event.pubkey === userPubkey; 33 34 if (isFromUser) { 35 // If we sent it, the partner is the recipient 36 return getRecipientPubkey(event); 37 } else { 38 // If they sent it, the partner is the author 39 return event.pubkey; 40 } 41 } 42 43 /** 44 * Format timestamp for display (matches Signal/WhatsApp/Telegram pattern) 45 * Today: Show time (e.g., "2:45 PM") 46 * Yesterday: "Yesterday" 47 * This week: Day name (e.g., "Mon") 48 * This year: Month and day (e.g., "Jan 15") 49 * Older: Full date (e.g., "Jan 15, 2024") 50 */ 51 export function formatConversationTime(timestamp: number): string { 52 const date = new Date(timestamp * 1000); 53 const now = new Date(); 54 55 // Start of today (midnight) 56 const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate()); 57 58 // Start of yesterday 59 const yesterdayStart = new Date(todayStart); 60 yesterdayStart.setDate(yesterdayStart.getDate() - 1); 61 62 // Start of this week (assuming week starts on Sunday, adjust if needed) 63 const weekStart = new Date(todayStart); 64 weekStart.setDate(weekStart.getDate() - weekStart.getDay()); 65 66 if (date >= todayStart) { 67 // Today: Show time (e.g., "2:45 PM") 68 return date.toLocaleTimeString(undefined, { hour: 'numeric', minute: '2-digit' }); 69 } else if (date >= yesterdayStart) { 70 // Yesterday 71 return 'Yesterday'; 72 } else if (date >= weekStart) { 73 // This week: Show day name (e.g., "Monday") 74 return date.toLocaleDateString(undefined, { weekday: 'short' }); 75 } else if (date.getFullYear() === now.getFullYear()) { 76 // This year: Show month and day (e.g., "Jan 15") 77 return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric' }); 78 } else { 79 // Older: Show full date (e.g., "Jan 15, 2024") 80 return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' }); 81 } 82 } 83 84 /** 85 * Format timestamp as full date and time for tooltips 86 * e.g., "Mon, Jan 15, 2024, 2:45 PM" 87 */ 88 export function formatFullDateTime(timestamp: number): string { 89 const date = new Date(timestamp * 1000); 90 return date.toLocaleString(undefined, { 91 weekday: 'short', 92 year: 'numeric', 93 month: 'short', 94 day: 'numeric', 95 hour: 'numeric', 96 minute: '2-digit' 97 }); 98 }