From e3ce5f7b8d92876811475ffe9c52babed3058e64 Mon Sep 17 00:00:00 2001 From: Silberengel Date: Sat, 15 Nov 2025 11:54:27 +0100 Subject: [PATCH] more bug-fixes --- src/components/CacheRelaysSetting/index.tsx | 137 +++++++++++++++--- src/components/PostEditor/PollEditor.tsx | 30 ++-- src/components/PostEditor/PostContent.tsx | 1 + .../CacheRelayOnlySetting.tsx | 79 ++++++---- 4 files changed, 178 insertions(+), 69 deletions(-) diff --git a/src/components/CacheRelaysSetting/index.tsx b/src/components/CacheRelaysSetting/index.tsx index d5d43b0..31491eb 100644 --- a/src/components/CacheRelaysSetting/index.tsx +++ b/src/components/CacheRelaysSetting/index.tsx @@ -57,11 +57,11 @@ export default function CacheRelaysSetting() { const [loadingItems, setLoadingItems] = useState(false) const [wordWrapEnabled, setWordWrapEnabled] = useState(true) const [searchQuery, setSearchQuery] = useState('') - const [consoleLogs, setConsoleLogs] = useState>([]) + const [consoleLogs, setConsoleLogs] = useState; timestamp: number }>>([]) const [showConsoleLogs, setShowConsoleLogs] = useState(false) const [consoleLogSearch, setConsoleLogSearch] = useState('') const [consoleLogLevel, setConsoleLogLevel] = useState<'error' | 'warn' | 'info' | 'log' | 'all'>('error') - const consoleLogRef = useRef>([]) + const consoleLogRef = useRef; timestamp: number }>>([]) const sensors = useSensors( useSensor(PointerSensor, { @@ -401,7 +401,9 @@ export default function CacheRelaysSetting() { } } - // Capture console logs - start capturing immediately when component mounts + // Capture console logs and logger output - start capturing immediately when component mounts + // Note: The logger uses console.log/error/warn/info internally, so intercepting console methods + // will automatically capture all logger output (debug, info, warn, error, perf, component, etc.) useEffect(() => { const originalLog = console.log const originalError = console.error @@ -409,20 +411,64 @@ export default function CacheRelaysSetting() { const originalInfo = console.info const captureLog = (type: string, ...args: any[]) => { - const message = args.map(arg => { - if (typeof arg === 'object') { - try { - return JSON.stringify(arg, null, 2) - } catch { + // Handle console formatting with %c placeholders for CSS styling + // Console.log supports %c for CSS styling: console.log('%cText', 'color: red') + let message = '' + let formattedParts: Array<{ text: string; style?: string }> = [] + + if (args.length > 0 && typeof args[0] === 'string' && args[0].includes('%c')) { + // Handle %c formatting + const formatString = args[0] + const parts = formatString.split(/%c/g) + formattedParts = [] + + for (let i = 0; i < parts.length; i++) { + const text = parts[i] + const style = i < args.length - 1 && typeof args[i + 1] === 'string' ? args[i + 1] : undefined + formattedParts.push({ text, style }) + } + + // Also include remaining args + const remainingArgs = args.slice(parts.length) + if (remainingArgs.length > 0) { + const remainingText = remainingArgs.map(arg => { + if (typeof arg === 'object') { + try { + return JSON.stringify(arg, null, 2) + } catch { + return String(arg) + } + } return String(arg) + }).join(' ') + if (formattedParts.length > 0) { + formattedParts[formattedParts.length - 1].text += ' ' + remainingText + } else { + formattedParts.push({ text: remainingText }) } } - return String(arg) - }).join(' ') + + // Create a plain text version for search/filtering + message = formattedParts.map(p => p.text).join('') + } else { + // Normal formatting - convert all args to strings + message = args.map(arg => { + if (typeof arg === 'object') { + try { + return JSON.stringify(arg, null, 2) + } catch { + return String(arg) + } + } + return String(arg) + }).join(' ') + formattedParts = [{ text: message }] + } const logEntry = { type, message, + formattedParts, timestamp: Date.now() } @@ -438,6 +484,7 @@ export default function CacheRelaysSetting() { } } + // Intercept console methods - this will capture all logger output since logger uses console internally console.log = (...args: any[]) => { captureLog('log', ...args) originalLog.apply(console, args) @@ -836,16 +883,6 @@ export default function CacheRelaysSetting() { {t('View Console Logs')} ({consoleLogRef.current.length}) - {Object.keys(cacheInfo).length > 0 && ( -
-
{t('Cache Statistics:')}
- {Object.entries(cacheInfo).map(([storeName, count]) => ( -
- {storeName}: {count} {t('items')} -
- ))} -
- )} {isSmallScreen ? ( @@ -1290,7 +1327,35 @@ export default function CacheRelaysSetting() { [{log.type}]
-                          {log.message}
+                          {log.formattedParts ? (
+                            log.formattedParts.map((part, i) => {
+                              if (part.style) {
+                                // Parse CSS string like "color:#f1b912" or "color: #f1b912; font-weight: bold"
+                                const styleObj: Record = {}
+                                part.style.split(';').forEach(rule => {
+                                  const trimmed = rule.trim()
+                                  if (trimmed) {
+                                    const colonIndex = trimmed.indexOf(':')
+                                    if (colonIndex > 0) {
+                                      const key = trimmed.substring(0, colonIndex).trim()
+                                      const value = trimmed.substring(colonIndex + 1).trim()
+                                      // Convert kebab-case to camelCase
+                                      const camelKey = key.replace(/-([a-z])/g, (_, c) => c.toUpperCase())
+                                      styleObj[camelKey] = value
+                                    }
+                                  }
+                                })
+                                return (
+                                  
+                                    {part.text}
+                                  
+                                )
+                              }
+                              return {part.text}
+                            })
+                          ) : (
+                            log.message
+                          )}
                         
@@ -1379,7 +1444,35 @@ export default function CacheRelaysSetting() { [{log.type}]
-                          {log.message}
+                          {log.formattedParts ? (
+                            log.formattedParts.map((part, i) => {
+                              if (part.style) {
+                                // Parse CSS string like "color:#f1b912" or "color: #f1b912; font-weight: bold"
+                                const styleObj: Record = {}
+                                part.style.split(';').forEach(rule => {
+                                  const trimmed = rule.trim()
+                                  if (trimmed) {
+                                    const colonIndex = trimmed.indexOf(':')
+                                    if (colonIndex > 0) {
+                                      const key = trimmed.substring(0, colonIndex).trim()
+                                      const value = trimmed.substring(colonIndex + 1).trim()
+                                      // Convert kebab-case to camelCase
+                                      const camelKey = key.replace(/-([a-z])/g, (_, c) => c.toUpperCase())
+                                      styleObj[camelKey] = value
+                                    }
+                                  }
+                                })
+                                return (
+                                  
+                                    {part.text}
+                                  
+                                )
+                              }
+                              return {part.text}
+                            })
+                          ) : (
+                            log.message
+                          )}
                         
diff --git a/src/components/PostEditor/PollEditor.tsx b/src/components/PostEditor/PollEditor.tsx index 3637839..6216f5f 100644 --- a/src/components/PostEditor/PollEditor.tsx +++ b/src/components/PostEditor/PollEditor.tsx @@ -2,21 +2,23 @@ import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Switch } from '@/components/ui/switch' -import { normalizeUrl } from '@/lib/url' import { TPollCreateData } from '@/types' import dayjs from 'dayjs' import { Eraser, X } from 'lucide-react' import { Dispatch, SetStateAction, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' +import PostRelaySelector from './PostRelaySelector' export default function PollEditor({ pollCreateData, setPollCreateData, - setIsPoll: _setIsPoll + setIsPoll: _setIsPoll, + content = '' }: { pollCreateData: TPollCreateData setPollCreateData: Dispatch> setIsPoll: Dispatch> + content?: string }) { const { t } = useTranslation() const [isMultipleChoice, setIsMultipleChoice] = useState(pollCreateData.isMultipleChoice) @@ -24,21 +26,17 @@ export default function PollEditor({ const [endsAt, setEndsAt] = useState( pollCreateData.endsAt ? dayjs(pollCreateData.endsAt * 1000).format('YYYY-MM-DDTHH:mm') : '' ) - const [relayUrls, setRelayUrls] = useState(pollCreateData.relays.join(', ')) + const [additionalRelayUrls, setAdditionalRelayUrls] = useState(pollCreateData.relays) + const [_isProtectedEvent, setIsProtectedEvent] = useState(false) useEffect(() => { setPollCreateData({ isMultipleChoice, options, endsAt: endsAt ? dayjs(endsAt).startOf('minute').unix() : undefined, - relays: relayUrls - ? relayUrls - .split(',') - .map((url) => normalizeUrl(url.trim())) - .filter(Boolean) - : [] + relays: additionalRelayUrls }) - }, [isMultipleChoice, options, endsAt, relayUrls]) + }, [isMultipleChoice, options, endsAt, additionalRelayUrls, setPollCreateData]) const handleAddOption = () => { setOptions([...options, '']) @@ -113,13 +111,11 @@ export default function PollEditor({ -
- - setRelayUrls(e.target.value)} - placeholder="wss://relay1.com, wss://relay2.com" +
+
diff --git a/src/components/PostEditor/PostContent.tsx b/src/components/PostEditor/PostContent.tsx index 4dd06fc..5805e50 100644 --- a/src/components/PostEditor/PostContent.tsx +++ b/src/components/PostEditor/PostContent.tsx @@ -1617,6 +1617,7 @@ export default function PostContent({ pollCreateData={pollCreateData} setPollCreateData={setPollCreateData} setIsPoll={setIsPoll} + content={text} /> )} {isHighlight && ( diff --git a/src/pages/secondary/PostSettingsPage/CacheRelayOnlySetting.tsx b/src/pages/secondary/PostSettingsPage/CacheRelayOnlySetting.tsx index 6d0abcc..2645f15 100644 --- a/src/pages/secondary/PostSettingsPage/CacheRelayOnlySetting.tsx +++ b/src/pages/secondary/PostSettingsPage/CacheRelayOnlySetting.tsx @@ -1,46 +1,65 @@ import { Label } from '@/components/ui/label' import { Switch } from '@/components/ui/switch' -import { StorageKey } from '@/constants' -import { hasCacheRelays } from '@/lib/private-relays' +import { StorageKey, ExtendedKind } from '@/constants' import { useNostr } from '@/providers/NostrProvider' +import indexedDb from '@/services/indexed-db.service' import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' export default function CacheRelayOnlySetting() { const { t } = useTranslation() - const { pubkey } = useNostr() - const [enabled, setEnabled] = useState(true) // Default ON when cache exists + const { cacheRelayListEvent, pubkey } = useNostr() const [hasCacheRelaysAvailable, setHasCacheRelaysAvailable] = useState(false) - + const [enabled, setEnabled] = useState(false) // Start as OFF, will be updated based on cache availability + + // Check if user has cache relays - check both provider state and IndexedDB as fallback + // Note: Cache relay events use 'r' tags, not 'relay' tags useEffect(() => { - // Check if user has cache relays first - if (pubkey) { - hasCacheRelays(pubkey) - .then((hasCache) => { - setHasCacheRelaysAvailable(hasCache) - - if (hasCache) { - // If cache exists, load from localStorage or default to true (ON) - const stored = window.localStorage.getItem(StorageKey.USE_CACHE_ONLY_FOR_PRIVATE_NOTES) - setEnabled(stored === null ? true : stored === 'true') - } else { - // If no cache, set to false (OFF) and save it - setEnabled(false) - window.localStorage.setItem(StorageKey.USE_CACHE_ONLY_FOR_PRIVATE_NOTES, 'false') + const checkCacheRelays = async () => { + let hasRelays = false + + // First check provider state + if (cacheRelayListEvent) { + hasRelays = cacheRelayListEvent.tags.some(tag => tag[0] === 'r' && tag[1]) + } else if (pubkey) { + // Fallback: check IndexedDB directly if provider state isn't loaded yet + try { + const storedEvent = await indexedDb.getReplaceableEvent(pubkey, ExtendedKind.CACHE_RELAYS) + if (storedEvent) { + hasRelays = storedEvent.tags.some(tag => tag[0] === 'r' && tag[1]) } - }) - .catch(() => { - setHasCacheRelaysAvailable(false) - // If check fails, assume no cache and set to false + } catch (error) { + // Ignore errors + } + } + + setHasCacheRelaysAvailable(hasRelays) + + // Set enabled state based on cache availability + if (hasRelays) { + // If cache exists, default to true (ON) + // Only respect localStorage if it's explicitly set to 'false' by the user + const stored = window.localStorage.getItem(StorageKey.USE_CACHE_ONLY_FOR_PRIVATE_NOTES) + // Default to ON when cache exists - only set to OFF if user explicitly set it to 'false' + if (stored === 'false') { setEnabled(false) - window.localStorage.setItem(StorageKey.USE_CACHE_ONLY_FOR_PRIVATE_NOTES, 'false') - }) - } else { - setHasCacheRelaysAvailable(false) - setEnabled(false) - window.localStorage.setItem(StorageKey.USE_CACHE_ONLY_FOR_PRIVATE_NOTES, 'false') + } else { + // Default to ON (either null or 'true') + setEnabled(true) + // Save the default ON state if not already set + if (stored === null) { + window.localStorage.setItem(StorageKey.USE_CACHE_ONLY_FOR_PRIVATE_NOTES, 'true') + } + } + } else { + // If no cache, set to false (OFF) and save it + setEnabled(false) + window.localStorage.setItem(StorageKey.USE_CACHE_ONLY_FOR_PRIVATE_NOTES, 'false') + } } - }, [pubkey]) + + checkCacheRelays() + }, [cacheRelayListEvent, pubkey]) const handleEnabledChange = (checked: boolean) => { setEnabled(checked)