1 Capture
2 Organize
3 Review
4 Submit
Step 1 — Capture
Camera Permission
9:41
Camera Permission Required
We need camera access to capture photos for your jobs.

useMassCapture: hasPermission === false

Active Capture
9:41
12 photos | 3 jobs
New Job
Done

CameraView component — full screen camera overlay

onCapture, onNewJob, onDone callbacks

Discard Confirmation
9:41
12 photos | 3 jobs
Done
Discard Session?
You have 12 photos. Are you sure you want to discard them?

Alert.alert when totalPhotos > 0 and close pressed


Step 2 — Organize
Organize Photos
9:41
Organize Photos
12 photos
3 jobs
Split here
Split here
Tap the scissors icon between photos to split into a new job

PhotoGrid + split/delete per photo

splitGroupAtPhoto, removePhoto callbacks

No Photos State
9:41
Organize Photos
No Photos
Start by capturing photos first.

Empty component: !state.session


Step 3 — Review & AI Detection
AI Detection Active
9:41
Review & Submit
Detecting job types... (2/5)
Jobs to Submit
5 jobs ready for review
Job 1
Kitchen Remodel 92% match
AI Insight
Detected cabinet work, countertops, and sink fixtures consistent with kitchen renovation.
Title
Description
Job 2
Detecting job type...
Title
Description

useJobTypeDetection — Gemini 3 Flash via Convex

detectForGroups with onProgress/onResult/onError

Property + Completed Cards
9:41
Review & Submit
Jobs to Submit
3 jobs ready for review
Property Location
Your Properties
Akron, OH 44301
Job 1
+2
Roof Repair 88% match
AI Insight
Detected damaged shingles and exposed underlayment consistent with storm damage.
Title
Description

AddressAutocomplete + property chips from Convex

handleSelectProperty, handleAddressSelect

New Address Entry
9:41
Review & Submit
Jobs to Submit
3 jobs ready for review
Property Location
Your Properties
Enter Address
Cleveland, OH 44114
Job 1
Deck Repair 62% match
Title
Description

AddressAutocomplete with StructuredAddress

isNewAddress state, handleAddressSelect


Modal — Job Type Selector
AI Suggestions Visible

JobTypeSelector modal with FlatList

alternatives[] from AI detection, allJobTypes from hook

Search Filtering

Filters by name, category, and keywords

searchQuery state filters allJobTypes


Step 4 — Submit States
Submitting Batch
9:41
Review & Submit
Jobs to Submit
3 jobs ready for review
123 Oak St, Akron, OH 44301
Kitchen Cabinet Replacement
92%
Storm Damage Roof Repair
88%
Deck Board Replacement
62%

createJobBatch Convex mutation

isSubmitting: true, buttons disabled

Partial Success Toast
9:41
Review & Submit
2/3 jobs created. 1 failed.
Jobs to Submit
3 jobs ready for review
Kitchen Cabinet Replacement
Posted
Storm Damage Roof Repair
Posted
Deck Board Replacement
Failed

Partial success: successCount < totalCount

showToast type: "warning", errorCount tracked

Validation Error
9:41
Review & Submit
Job 2 needs a title.
Jobs to Submit
2 jobs ready for review
Property Location
Job 2
HVAC Repair 85% match
Title
Description

validateCards checks title, description, jobTypeId

showToast type: "error" per missing field


Data Architecture
Hooks
useMassCapture — 547 lines
State machine, AsyncStorage persistence
Auto-save every 500ms for crash recovery

useJobTypeDetection — 327 lines
Gemini 3 Flash via Convex actions
Base64 photo encoding, batch detection
Types
MassPostStep: capture | organize | review
CapturedPhoto: id, localUri, timestamp, groupIndex
ReviewJobCard: detection, title, desc, photos
BatchJobInput: up to 25 jobs per batch
DragState: defined but not yet implemented
Convex Backend
createJobBatch mutation — batch creates jobs with photos, property, and optional immediate publish
posterProperties.list query — fetches saved properties for quick selection
Gemini action — AI detection with confidence, reasoning, alternatives, suggested title/description