Merge branch 'feat/blobbi-eyes-feed' into 'main'

Enable Blobbi eye tracking on feed cards (feed-only, with touch support)

Closes #249

See merge request soapbox-pub/ditto!201
This commit is contained in:
Chad Curtis
2026-04-24 01:56:35 +00:00
4 changed files with 21 additions and 5 deletions
+10
View File
@@ -75,8 +75,18 @@ function attachGlobalMouseListener() {
globalMouseY = e.clientY;
};
const handleTouch = (e: TouchEvent) => {
const touch = e.touches[0];
if (touch) {
globalMouseX = touch.clientX;
globalMouseY = touch.clientY;
}
};
// Use capture phase for earliest possible update
window.addEventListener('mousemove', handleMouseMove, { capture: true, passive: true });
window.addEventListener('touchstart', handleTouch, { capture: true, passive: true });
window.addEventListener('touchmove', handleTouch, { capture: true, passive: true });
mouseListenerAttached = true;
}
+9 -3
View File
@@ -1,13 +1,19 @@
import { useMemo } from 'react';
import type { NostrEvent } from '@nostrify/nostrify';
import { BlobbiStageVisual } from '@/blobbi/ui/BlobbiStageVisual';
import { BlobbiStageVisual, type BlobbiLookMode } from '@/blobbi/ui/BlobbiStageVisual';
import { parseBlobbiEvent } from '@/blobbi/core/lib/blobbi';
import { calculateProjectedDecay } from '@/blobbi/core/hooks/useProjectedBlobbiState';
import { resolveStatusRecipe, attenuateRecipeForFeed, EMPTY_RECIPE } from '@/blobbi/ui/lib/status-reactions';
import { buildSleepingRecipe } from '@/blobbi/ui/lib/recipe';
export function BlobbiStateCard({ event }: { event: NostrEvent }) {
interface BlobbiStateCardProps {
event: NostrEvent;
/** Controls eye tracking behavior. Default: 'forward' (eyes look straight ahead). */
lookMode?: BlobbiLookMode;
}
export function BlobbiStateCard({ event, lookMode = 'forward' }: BlobbiStateCardProps) {
const companion = useMemo(() => parseBlobbiEvent(event), [event]);
const isSleeping = companion?.state === 'sleeping';
@@ -44,7 +50,7 @@ export function BlobbiStateCard({ event }: { event: NostrEvent }) {
companion={companion}
size="lg"
animated={!isSleeping}
lookMode="forward"
lookMode={lookMode}
recipe={feedRecipe}
recipeLabel={feedRecipeLabel}
className="size-48 sm:size-56"
+1 -1
View File
@@ -668,7 +668,7 @@ export const NoteCard = memo(function NoteCard({
<ProfileCardContent event={event} />
) : isBlobbiState ? (
<Suspense fallback={<Skeleton className="h-24 w-full rounded-lg" />}>
<BlobbiStateCard event={event} />
<BlobbiStateCard event={event} lookMode="follow-pointer" />
</Suspense>
) : (
<TruncatedNoteContent
+1 -1
View File
@@ -2165,7 +2165,7 @@ function PostDetailContent({ event }: { event: NostrEvent }) {
<EncryptedLetterContent event={event} />
) : isBlobbiState ? (
<Suspense fallback={<Skeleton className="h-24 w-full rounded-lg" />}>
<BlobbiStateCard event={event} />
<BlobbiStateCard event={event} lookMode="follow-pointer" />
</Suspense>
) : isBadgeAward ? (
<BadgeAwardCard event={event} />