A Next.js application for browsing Pebble watchfaces from the Rebble App Store and tracking your preferences. Click to like watchfaces, or scroll past to automatically mark them as "not interested".
- Infinite Scroll: Browse watchfaces endlessly with automatic loading
- One-Click Liking: Click any watchface to mark it as liked
- Automatic Dislike Tracking: Watchfaces you scroll past are automatically marked as "disliked"
- Smart Filtering: View All, Liked, or Unreviewed watchfaces
- Local SQLite Database: All preferences stored locally in
watchface-reviews.db - Intersection Observer: Tracks which watchfaces you've seen using visibility detection
- Liked (Click): When you click on a watchface card, it's saved with
liked=true - Disliked (Scroll Past): When a watchface is 80%+ visible for 1.5+ seconds and you scroll past it without clicking, it's saved with
liked=false - Visual Indicators:
- Liked: Green ring + checkmark overlay
- Disliked: Grayed out with X icon
- Unreviewed: Normal appearance
- Unreviewed: Shows only watchfaces you haven't seen yet
- Liked: Shows only watchfaces you clicked on
- All: Shows everything (useful for reviewing your choices)
- Node.js 18+
- npm
npm installnpm run devOpen http://localhost:3001 in your browser.
npm run build
npm start- Framework: Next.js 16 (App Router)
- UI: Tailwind CSS + ShadCN/UI components
- Database: Better-SQLite3 (local SQLite)
- API: Rebble App Store API
- Icons: Lucide React
- Language: TypeScript
app/
├── page.tsx # Main watchface grid with infinite scroll
├── layout.tsx # Root layout
├── globals.css # Global styles
├── actions.ts # Server actions for database operations
└── api/
└── watchfaces/
└── route.ts # API proxy to Rebble App Store
components/
└── watchface-card.tsx # Individual watchface card with Intersection Observer
lib/
├── db.ts # SQLite database setup and queries
├── types.ts # TypeScript interfaces
└── utils.ts # Utility functions (cn helper)
CREATE TABLE reviews (
app_id TEXT PRIMARY KEY,
title TEXT NOT NULL,
developer_name TEXT NOT NULL,
hearts INTEGER NOT NULL,
icon_image TEXT,
list_image TEXT,
liked INTEGER NOT NULL, -- 1 = liked, 0 = disliked
reviewed_at TEXT DEFAULT CURRENT_TIMESTAMP
)The app fetches watchfaces from the Rebble App Store API:
GET https://appstore-api.rebble.io/api/v1/apps/collection/featured-watchfaces/watchfaces
Query parameters:
limit: Number of items per page (default: 39)offset: Pagination offsetplatform: allhardware: basaltfirmware_version: 3filter_hardware: trueimage_ratio: 1
The app uses the Intersection Observer API to automatically track which watchfaces you've viewed:
- Triggers when a card is 80%+ visible
- Waits 1.5 seconds to confirm you've "seen" it
- If you scroll away without clicking, marks as disliked
- Prevents false positives from rapid scrolling
All database operations use Next.js Server Actions for type-safe, server-side data management:
toggleReview(): Click handler - adds/removes likesmarkAsDisliked(): Automatic handler for scrolled-past itemsgetReviewedAppIdsAction(): Loads reviewed IDs on mountcheckReviewStatus(): Checks if an app has been reviewed
Your preference data is stored in:
./watchface-reviews.db
This is a SQLite database file that persists between sessions. Back it up if you want to save your preferences!
- Start with Unreviewed tab: Focus on new watchfaces you haven't seen
- Review your Liked list: Switch to "Liked" tab to see your favorites
- Undo a choice: Click a liked watchface again to unlike it
- Keyboard-friendly: Just scroll and click - no complex interactions needed
Potential features to add:
- Export liked watchfaces to JSON
- Sort by hearts/popularity
- Search functionality
- Different collections (not just "featured")
- Stats dashboard (total reviewed, like ratio, etc.)
ISC
- Built for browsing the Pebble App Store via Rebble
- Watchface data provided by Rebble