Save preferred contractors, organize collections, manage blocked users, and discover AI-recommended pros
FavoritesScreen — favorites.items.length === 0, empty state with CTA
FavoritesScreen — api.poster.favorites.list, enriched with contractorProfile, contractorReviews
ContractorPreviewSheet — BottomSheet modal, api.poster.favorites.list enriched, contractorProfile portfolio
CollectionsScreen — api.poster.favorites.collections.list, grouped by collectionId
CollectionDetailScreen — api.poster.favorites.collections.get(collectionId), drag-to-reorder, share deep link
BlockedScreen — api.poster.blocking.listBlocked, enriched with contractorProfile
BlockContractorSheet — api.poster.blocking.blockUser({ userId, userType: "contractor", reason })
RecommendationsScreen — api.poster.recommendations.getMatches, Gemini-powered scoring, favorite similarity engine
posterFavorites
posterId: v.id("posterUsers")
contractorId: v.id("contractorUsers")
createdAt: v.number()
idx: by_poster_id, by_contractor_id
userBlocks
blockerId: v.string()
blockerType: v.string()
blockedId: v.string()
blockedType: v.string()
reason: v.optional(v.string())
createdAt: v.number()
idx: by_blocker, by_blocked
contractorProfile
contractorReviews
api.poster.favorites.list
→ enriched: name, businessName,
avatarUrl, isVerified,
servicesOffered, yearsExperience,
avgRating, reviewCount
api.poster.favorites.checkStatus
→ { isFavorite, favoriteId }
api.poster.favorites
.getContractorFavoriteCount
api.poster.blocking.listBlocked
→ enriched: name, email, type
api.poster.blocking.isBlocked
api.poster.blocking.amIBlocked
api.poster.favorites.add
args: { contractorId }
→ { success, favoriteId,
alreadyExists }
api.poster.favorites.remove
args: { contractorId }
→ { success, wasRemoved }
api.poster.blocking.blockUser
args: { userId, userType,
reason? }
→ { success, blockId,
alreadyBlocked }
api.poster.blocking.unblockUser
args: { userId, userType }
→ { success, wasBlocked }
FavoritesScreen
app/(poster)/favorites.tsx
BlockedScreen
app/(poster)/blocked.tsx
ContractorPreviewSheet
components/poster/ContractorPreview
BlockContractorSheet
components/poster/BlockConfirm
CollectionsScreen (planned)
CollectionDetailScreen (planned)
RecommendationsScreen (planned)
* Collections & Recommendations
require new schema tables
and Gemini integration