helpers.tsx raw

   1  // Helper functions and constants for the ORLY dashboard
   2  
   3  // Comprehensive kind names mapping
   4  export const KIND_NAMES = {
   5    0: "Profile Metadata",
   6    1: "Text Note",
   7    2: "Recommend Relay",
   8    3: "Contacts",
   9    4: "Encrypted DM",
  10    5: "Delete Request",
  11    6: "Repost",
  12    7: "Reaction",
  13    8: "Badge Award",
  14    16: "Generic Repost",
  15    40: "Channel Creation",
  16    41: "Channel Metadata",
  17    42: "Channel Message",
  18    43: "Channel Hide Message",
  19    44: "Channel Mute User",
  20    1063: "File Metadata",
  21    1311: "Live Chat Message",
  22    1984: "Reporting",
  23    1985: "Label",
  24    9734: "Zap Request",
  25    9735: "Zap Receipt",
  26    10000: "Mute List",
  27    10001: "Pin List",
  28    10002: "Relay List Metadata",
  29    10003: "Bookmark List",
  30    10004: "Communities List",
  31    10005: "Public Chats List",
  32    10006: "Blocked Relays List",
  33    10007: "Search Relays List",
  34    10009: "User Groups",
  35    10015: "Interests List",
  36    10030: "User Emoji List",
  37    13194: "Wallet Info",
  38    22242: "Client Auth",
  39    23194: "Wallet Request",
  40    23195: "Wallet Response",
  41    24133: "Nostr Connect",
  42    27235: "HTTP Auth",
  43    30000: "Categorized People List",
  44    30001: "Categorized Bookmarks",
  45    30002: "Categorized Relay List",
  46    30003: "Bookmark Sets",
  47    30004: "Curation Sets",
  48    30005: "Video Sets",
  49    30008: "Profile Badges",
  50    30009: "Badge Definition",
  51    30015: "Interest Sets",
  52    30017: "Create/Update Stall",
  53    30018: "Create/Update Product",
  54    30019: "Marketplace UI/UX",
  55    30020: "Product Sold As Auction",
  56    30023: "Long-form Content",
  57    30024: "Draft Long-form Content",
  58    30030: "Emoji Sets",
  59    30063: "Release Artifact Sets",
  60    30078: "Application-specific Data",
  61    30311: "Live Event",
  62    30315: "User Statuses",
  63    30388: "Slide Set",
  64    30402: "Classified Listing",
  65    30403: "Draft Classified Listing",
  66    30617: "Repository Announcement",
  67    30618: "Repository State Announcement",
  68    30818: "Wiki Article",
  69    30819: "Redirects",
  70    31922: "Date-Based Calendar Event",
  71    31923: "Time-Based Calendar Event",
  72    31924: "Calendar",
  73    31925: "Calendar Event RSVP",
  74    31989: "Handler Recommendation",
  75    31990: "Handler Information",
  76    34550: "Community Definition",
  77    34551: "Community Post Approval",
  78  };
  79  
  80  // Get human-readable kind name
  81  export function getKindName(kind) {
  82    return KIND_NAMES[kind] || `Kind ${kind}`;
  83  }
  84  
  85  // Validate hex string (for pubkeys and event IDs)
  86  export function isValidHex(str, length = null) {
  87    if (!str || typeof str !== "string") return false;
  88    const hexRegex = /^[0-9a-fA-F]+$/;
  89    if (!hexRegex.test(str)) return false;
  90    if (length && str.length !== length) return false;
  91    return true;
  92  }
  93  
  94  // Validate pubkey (64 character hex)
  95  export function isValidPubkey(pubkey) {
  96    return isValidHex(pubkey, 64);
  97  }
  98  
  99  // Validate event ID (64 character hex)
 100  export function isValidEventId(eventId) {
 101    return isValidHex(eventId, 64);
 102  }
 103  
 104  // Validate tag name (single letter a-zA-Z)
 105  export function isValidTagName(tagName) {
 106    return /^[a-zA-Z]$/.test(tagName);
 107  }
 108  
 109  // Format timestamp to localized string
 110  export function formatTimestamp(timestamp) {
 111    return new Date(timestamp * 1000).toLocaleString();
 112  }
 113  
 114  // Format timestamp for datetime-local input
 115  export function formatDateTimeLocal(timestamp) {
 116    const date = new Date(timestamp * 1000);
 117    const year = date.getFullYear();
 118    const month = String(date.getMonth() + 1).padStart(2, '0');
 119    const day = String(date.getDate()).padStart(2, '0');
 120    const hours = String(date.getHours()).padStart(2, '0');
 121    const minutes = String(date.getMinutes()).padStart(2, '0');
 122    return `${year}-${month}-${day}T${hours}:${minutes}`;
 123  }
 124  
 125  // Parse datetime-local input to unix timestamp
 126  export function parseDateTimeLocal(dateTimeString) {
 127    return Math.floor(new Date(dateTimeString).getTime() / 1000);
 128  }
 129  
 130  // Truncate pubkey for display
 131  export function truncatePubkey(pubkey) {
 132    if (!pubkey) return "";
 133    return pubkey.slice(0, 8) + "..." + pubkey.slice(-8);
 134  }
 135  
 136  // Truncate content for display
 137  export function truncateContent(content, maxLength = 100) {
 138    if (!content) return "";
 139    return content.length > maxLength ? content.slice(0, maxLength) + "..." : content;
 140  }
 141  
 142  // Build Nostr filter from form data
 143  export function buildFilter({
 144    searchText = null,
 145    kinds = [],
 146    authors = [],
 147    ids = [],
 148    tags = [],
 149    since = null,
 150    until = null,
 151    limit = null,
 152  }) {
 153    const filter = {};
 154    
 155    if (searchText && searchText.trim()) {
 156      filter.search = searchText.trim();
 157    }
 158    
 159    if (kinds && kinds.length > 0) {
 160      filter.kinds = kinds;
 161    }
 162    
 163    if (authors && authors.length > 0) {
 164      filter.authors = authors;
 165    }
 166    
 167    if (ids && ids.length > 0) {
 168      filter.ids = ids;
 169    }
 170    
 171    // Add tag filters (e.g., #e, #p, #a)
 172    if (tags && tags.length > 0) {
 173      tags.forEach(tag => {
 174        if (tag.name && tag.value) {
 175          const tagKey = `#${tag.name}`;
 176          if (!filter[tagKey]) {
 177            filter[tagKey] = [];
 178          }
 179          filter[tagKey].push(tag.value);
 180        }
 181      });
 182    }
 183    
 184    if (since) {
 185      filter.since = since;
 186    }
 187    
 188    if (until) {
 189      filter.until = until;
 190    }
 191    
 192    if (limit && limit > 0) {
 193      filter.limit = limit;
 194    }
 195    
 196    return filter;
 197  }
 198  
 199  // Pretty print JSON with word breaking for long strings
 200  export function prettyPrintFilter(filter) {
 201    return JSON.stringify(filter, null, 2);
 202  }
 203  
 204