diff --git a/README.md b/README.md
index 6df9d8a..c9cb3aa 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,5 @@
## TODOs
- [ ] replace mock wallet connection/disconnection
- [ ] replace mock Ordinal verification (API)
-- [ ]
\ No newline at end of file
+- [ ] figure out using actual icons for cells
+- [ ] store message cache in indexedDB -- make app local-first (update from/to Waku when available)
\ No newline at end of file
diff --git a/src/components/CellList.tsx b/src/components/CellList.tsx
index a37a95b..3a32510 100644
--- a/src/components/CellList.tsx
+++ b/src/components/CellList.tsx
@@ -5,6 +5,7 @@ import { Skeleton } from '@/components/ui/skeleton';
import { Layout, MessageSquare, RefreshCw } from 'lucide-react';
import { CreateCellDialog } from './CreateCellDialog';
import { Button } from '@/components/ui/button';
+import { CypherImage } from './ui/CypherImage';
const CellList = () => {
const { cells, isInitialLoading, posts, refreshData, isRefreshing } = useForum();
@@ -67,10 +68,11 @@ const CellList = () => {
cells.map((cell) => (
-
{cell.name}
diff --git a/src/components/PostDetail.tsx b/src/components/PostDetail.tsx
index 37f712d..6d526b4 100644
--- a/src/components/PostDetail.tsx
+++ b/src/components/PostDetail.tsx
@@ -8,6 +8,7 @@ import { Skeleton } from '@/components/ui/skeleton';
import { ArrowLeft, ArrowUp, ArrowDown, Clock, MessageCircle, Send, RefreshCw } from 'lucide-react';
import { formatDistanceToNow } from 'date-fns';
import { Comment } from '@/types';
+import { CypherImage } from './ui/CypherImage';
const PostDetail = () => {
const { postId } = useParams<{ postId: string }>();
diff --git a/src/components/PostList.tsx b/src/components/PostList.tsx
index 6f9314b..1b5920c 100644
--- a/src/components/PostList.tsx
+++ b/src/components/PostList.tsx
@@ -8,6 +8,7 @@ import { Textarea } from '@/components/ui/textarea';
import { Skeleton } from '@/components/ui/skeleton';
import { ArrowLeft, MessageSquare, MessageCircle, ArrowUp, ArrowDown, Clock, RefreshCw } from 'lucide-react';
import { formatDistanceToNow } from 'date-fns';
+import { CypherImage } from './ui/CypherImage';
const PostList = () => {
const { cellId } = useParams<{ cellId: string }>();
@@ -99,10 +100,11 @@ const PostList = () => {
-
diff --git a/src/components/ui/CypherImage.tsx b/src/components/ui/CypherImage.tsx
new file mode 100644
index 0000000..31104b6
--- /dev/null
+++ b/src/components/ui/CypherImage.tsx
@@ -0,0 +1,161 @@
+import React, { useState } from 'react';
+import { cn } from '@/lib/utils';
+
+type CypherImageProps = {
+ src: string;
+ alt: string;
+ className?: string;
+ fallbackClassName?: string;
+ generateUniqueFallback?: boolean;
+} & Omit
, 'src' | 'alt' | 'className'>;
+
+/**
+ * CypherImage component that renders a cypherpunk-style fallback image
+ * when the actual image cannot be loaded.
+ */
+export function CypherImage({
+ src,
+ alt,
+ className,
+ fallbackClassName,
+ generateUniqueFallback = false,
+ ...props
+}: CypherImageProps) {
+ const [imageError, setImageError] = useState(false);
+
+ // Generate a seed based on the alt text or src to create consistent fallbacks for the same resource
+ const seed = generateUniqueFallback ?
+ (alt || src).split('').reduce((acc, char) => acc + char.charCodeAt(0), 0) : 0;
+
+ // Handle image load error
+ const handleError = () => {
+ setImageError(true);
+ };
+
+ if (imageError) {
+ // Generate some values based on the seed for variety
+ const hue = (seed % 60) + 140; // Cyan-ish colors (140-200)
+ const gridSize = (seed % 8) + 8; // 8-16px
+ const noiseIntensity = (seed % 30) + 5; // 5-35%
+ const scanlineOpacity = ((seed % 4) + 1) / 10; // 0.1-0.5
+
+ return (
+
+ {/* Noise overlay */}
+
+
+ {/* Scanlines */}
+
+
+ {/* CRT glow effect */}
+
+
+
+ {/* Glitch effect lines */}
+
+
+ {/* Main content container with glitch effect */}
+
+ {/* Glitched text behind the main letter */}
+
+ {Array.from({ length: 3 }, (_, i) => {
+ const chars = '01[]{}><#$%&@!?;:+=*/\\|~^';
+ return chars[(seed + i) % chars.length];
+ }).join('')}
+
+
+ {/* First letter of alt text in center */}
+
+ {alt.charAt(0).toUpperCase()}
+
+
+ {/* Random characters that occasionally "glitch" in */}
+
+ {seed.toString(16).substring(0, 4)}
+
+
+
+
+ );
+ }
+
+ return (
+
+ );
+}
\ No newline at end of file
diff --git a/src/index.css b/src/index.css
index 412e038..f434f1c 100644
--- a/src/index.css
+++ b/src/index.css
@@ -1,4 +1,3 @@
-
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&family=Inter:wght@300;400;500;600;700&display=swap');
@tailwind base;
@@ -110,3 +109,40 @@
@apply border-l-2 border-cyber-muted pl-4 py-2 my-3 hover:border-cyber-accent transition-colors;
}
}
+
+/* Cyberpunk glow animation for CypherImage */
+@keyframes cyber-flicker {
+ 0%, 100% {
+ opacity: 1;
+ filter: drop-shadow(0 0 2px rgba(0, 255, 255, 0.8));
+ }
+ 8%, 10% {
+ opacity: 0.8;
+ filter: drop-shadow(0 0 5px rgba(0, 255, 255, 0.8));
+ }
+ 20%, 25% {
+ opacity: 1;
+ filter: drop-shadow(0 0 1px rgba(0, 255, 255, 0.5));
+ }
+ 30% {
+ opacity: 0.6;
+ filter: drop-shadow(0 0 8px rgba(0, 255, 255, 1));
+ }
+ 40%, 45% {
+ opacity: 1;
+ filter: drop-shadow(0 0 2px rgba(0, 255, 255, 0.6));
+ }
+ 50%, 55% {
+ opacity: 0.9;
+ filter: drop-shadow(0 0 3px rgba(0, 255, 255, 0.8));
+ }
+ 60%, 100% {
+ opacity: 1;
+ filter: drop-shadow(0 0 2px rgba(0, 255, 255, 0.6));
+ }
+}
+
+.cyberpunk-glow {
+ animation: cyber-flicker 8s infinite;
+ will-change: filter, opacity;
+}
diff --git a/src/lib/waku/index.ts b/src/lib/waku/index.ts
index 2bdc1dc..a341dde 100644
--- a/src/lib/waku/index.ts
+++ b/src/lib/waku/index.ts
@@ -156,6 +156,7 @@ class MessageManager {
const messages = await this.storeManager.queryStore();
for (const message of messages) {
+ console.log("message", message);
this.updateCache(message);
}