admin
2568c32cf4
feat: notification polling 30s->10s, video thumbnails, @-mentions
...
- NotificationBell: refetchInterval 30000->10000, refetchOnWindowFocus, i18n label
- ProfileTabs: Mux thumbnail for VIDEO posts in grid, play icon overlay
- parse-caption: add mention segment type for @word parsing
- MentionTextarea: new component with @-autocomplete via search.pets
- PostCard: render @mentions as blue links to /search
- CommentSheet: parse comment bodies for mentions, use MentionTextarea
- PhotoPostForm: use MentionTextarea for caption input
2026-06-23 06:17:58 +02:00
admin
7b0baf3be8
fix(docker): lazy-init Supabase+Redis, self-host Redis, simplify build args
2026-06-21 18:51:41 +02:00
admin
5800e06cd9
feat: ad engagement, invite gate, sidebar footer, docker setup
...
Ad interactions (reactions/comments/reposts on ads):
- Schema: AdReaction, AdComment, AdRepost models with RLS enabled
- tRPC: new ads router (getFeedData, toggleReaction, addComment, listComments, toggleRepost)
- AdCard: fully interactive card with paw, comment sheet, repost — mirrors PostCard style
- getActiveAds includes _count for advertiser engagement metrics
Invite system enforcement:
- proxy.ts: /sign-up blocked without pf_invite cookie
- INVITE_REQUIRED=false env var disables gate for post-beta live launch
- Full invite flow: /join → cookie → /sign-up → consume-invite → 3 codes issued to new user
- Admin: listInviteCodes, createRootInvite, revokeInvite procedures
Sidebar mini-footer:
- Impressum + Datenschutz links always visible at sidebar bottom
- PawFeed Alpha-Test copyright line
Docker self-hosting:
- docker/Dockerfile: 3-stage build using Next.js standalone output
- docker/docker-compose.yml: env_file + build-args for NEXT_PUBLIC_ vars
- .dockerignore at project root
- next.config.ts: output standalone for minimal image
- .env.example: documented INVITE_REQUIRED
2026-06-21 18:12:13 +02:00
admin and Claude Sonnet 4.6
60366691e8
feat(05): Phase 5 — short-form video via Mux
...
Schema:
- Add VideoPost model (muxUploadId, muxAssetId, muxPlaybackId,
status, durationSecs, aspectRatio) linked 1-to-1 with Post
- Add VideoStatus enum (PROCESSING/READY/ERROR)
- Add PostType.VIDEO
Backend:
- src/lib/mux.ts — singleton Mux client + webhook secret
- videos router: createUpload (Mux direct upload URL + PROCESSING post),
getByPostId (status polling), updateCaption
- POST /api/webhooks/mux: verifies signature; handles
video.upload.asset_created (store muxAssetId),
video.asset.ready (set READY + playbackId + fan-out to feed),
video.asset.errored (set ERROR)
- feed.ts + posts.byPetId: include videoPost in all post queries
UI:
- VideoUploadForm: drop zone → XHR PUT to Mux upload URL with
progress bar; caption field; transitions to "Processing…" on success
- VideoCard: PROCESSING shows spinner; READY renders MuxPlayer
(HLS, orange accent); polls every 5s until status changes
- PostTypeSheet: "Video Post" option added (between Photo and Milestone)
- PostCard: VIDEO type renders VideoCard instead of image carousel
Env vars: MUX_TOKEN_ID, MUX_TOKEN_SECRET, MUX_WEBHOOK_SECRET,
NEXT_PUBLIC_APP_URL — placeholders added to .env.local
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-13 13:27:43 +02:00
admin and Claude Sonnet 4.6
847ac97bde
feat(03-06): Hashtag search end-to-end — Phase 3 complete
...
- hashtag-helpers.ts: extractHashtags (regex+dedup) + saveHashtags (upsert)
- parse-caption.ts: parseCaption → CaptionSegment[] for client rendering
- search.byHashtag GREEN: hashtag.findUnique → postHashtag.findMany + pagination
- posts.create + reposts.create: extract+save hashtags after post creation
- /hashtag/[tag]: infinite scroll post grid + useInView sentinel
- /search Tags tab: byHashtag query wired, 3-col post grid
- PostCard: parseCaption replaces raw caption — hashtags are orange Link to /hashtag/[tag]
- STATE.md: Phase 3 complete, 3/7 phases done
Tests: 67 passing, 0 failing (5 todo), tsc clean
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-13 12:16:03 +02:00
admin
9b02d3dc8b
feat(02-06): milestone-meta map + MilestoneCard + PostCard MILESTONE routing — MILE-02
2026-06-13 10:21:49 +02:00
admin
c435404b2d
feat(02-01): shared libs + 7 tRPC router stubs registered in _app.ts
...
- src/lib/redis.ts: @upstash/redis singleton (server-only)
- src/lib/feed-helpers.ts: feedKey, fanOutPost, backfillOnFollow, getFeedPage, trimFeed, FEED_RETENTION_MS
- src/lib/media-url.ts: getMediaUrl (mirrors getAvatarUrl signature, client-safe)
- src/lib/milestone-thresholds.ts: FOLLOWER_MILESTONE_THRESHOLDS constant (D-09, A3)
- Router stubs: posts, stories, milestones, follows, blocks, reports, feed (all NOT_IMPLEMENTED)
- All 9 routers registered in _app.ts (pets, media + 7 new)
- feed.getFeed input includes cursor: z.number().nullish() per Pitfall 6
- Rule 1 fix: @upstash/redis zrange requires {offset, count} not just count
- npx tsc --noEmit exits 0
2026-06-07 12:23:10 +02:00
admin
95e42f5474
fix(01-04): replace all R2/api-avatar URLs with Supabase Storage CDN via getAvatarUrl()
2026-06-06 11:38:37 +02:00
admin
e3c279d6a4
feat(01-04): switch media storage from Cloudflare R2 to Supabase Storage
2026-06-06 11:32:17 +02:00
admin
e0214ea0f8
feat(01-01): wire Clerk proxy.ts, Prisma 7 schema + singleton, tRPC stack, R2 client, ActivePetContext
...
- Create proxy.ts at repo root with clerkMiddleware + createRouteMatcher (public allowlist: sign-up, log-in, forgot-password, reset-password, verify-email)
- Create prisma/schema.prisma: Owner, Species, Breed, Pet models with ownership index and FK constraints
- Create prisma.config.ts with Prisma 7 defineConfig (datasource url via env)
- Create src/lib/prisma.ts: Prisma 7 singleton using @prisma/adapter-pg for local dev, withAccelerate() for Prisma Accelerate on Vercel
- Create src/lib/r2.ts (server-only): S3Client + getPresignedUploadUrl with contentType enforcement and 300s expiry
- Create src/lib/auth.ts: getOwnerId() Clerk auth wrapper
- Create src/trpc/init.ts: createTRPCContext, router, publicProcedure, protectedProcedure (throws UNAUTHORIZED)
- Create src/trpc/routers/pets.ts: pets.create (upserts Owner, creates Pet with ownerId=ctx.userId), pets.list (scoped), pets.byId (ownership-asserted)
- Create src/trpc/routers/_app.ts: appRouter + AppRouter type export
- Create src/trpc/query-client.ts, server.ts (RSC caller), client.tsx (TRPCReactProvider + useTRPC)
- Create src/app/api/trpc/[trpc]/route.ts: tRPC fetch handler (GET + POST)
- Create src/context/ActivePetContext.tsx + src/hooks/useActivePet.ts: localStorage-backed active pet context
- Create prisma/seed.ts: seeds Dog/Cat/Bird species idempotently via upsert
- Update src/app/layout.tsx: wrap children in ClerkProvider (orange primary), TRPCReactProvider, ActivePetProvider, Toaster
- Install @prisma/adapter-pg + pg for Prisma 7 local dev (Rule 2: required for Prisma 7 engine compatibility)
- [Rule 1 - Bug] Prisma 7 no longer supports url/directUrl in schema.prisma; moved to prisma.config.ts with defineConfig
2026-06-05 19:18:16 +02:00
admin
dbc074e9ee
chore(01-01): scaffold Next.js 16 + install full stack + Vitest test scaffold
...
- Create-next-app Next.js 16.2.7 with TypeScript 6.0.3, React 19, Tailwind v4, Turbopack
- Install all pinned runtime deps: Clerk 7.4.3, tRPC 11.17.0, Prisma 7.8.0, Zod 4.4.3, AWS SDK 3.1062.0, Sonner 2.0.7, Upstash Redis 1.38.0
- Configure Tailwind v4 (postcss.config.mjs uses @tailwindcss/postcss; globals.css uses @import "tailwindcss")
- Initialize shadcn/ui with default preset; add all Phase 1 components (button, input, form, label, textarea, select, avatar, card, dialog, dropdown-menu, sonner, progress, separator, badge, skeleton)
- Set orange-500 design system: --primary: oklch(0.7024 0.1967 41.45); --background: oklch(0.9882 0 0)
- Load Inter via next/font/google in root layout
- Create vitest.config.ts with jsdom environment and @ path alias
- Create Wave 0 test scaffolds (22 it.todo tests): auth, pet-profile, media-upload + prisma-mock helper
- Create .env.example listing all required service env vars
2026-06-05 19:10:46 +02:00