All projects

Field photography client

Manzoni

A mobile image-intake client with a persistent upload queue and delivery receipts from Colombo.

I designed and built this project independently, using my own resources.

Explore the project

Public repository

Technical documentation

Mobile client. The repository contains Expo setup and the retained Flutter implementation.

Technologies

  • React Native
  • Expo
  • SQLite
  • Flutter

01 / Context

The problem behind the project

A photograph captured in the field should remain queued after an application relaunch, and an accepted upload should not be mistaken for completed delivery.

Manzoni is the field client for Colombo’s upload contract. A captured image must survive an application restart and a bad network before the server has accepted it; after acceptance, the client must avoid sending a duplicate simply because it cannot currently read the receipt.

The interface therefore needs more than an upload progress bar. Accepted, delivered, confirmed by the newsroom, failed, expired, and unknown delivery are different states with different safe actions.

02 / Implementation

Design and implementation

I build the mobile intake and queue workflow, including the current Expo migration and its integration with Colombo.

I built the mobile capture and queue workflow with app-owned media copies, SQLite persistence, secure credential storage, and the current React Native/Expo implementation. The earlier Flutter application remains a separate rollback/reference path.

After an HTTP acceptance, the required operation ID becomes the reconciliation identity. The client polls Colombo on launch, foreground return, a real offline-to-online transition, a slow foreground interval, or an explicit Check delivery action. Automatic triggers respect backoff; an operator refresh can ask immediately.

The domain reconciliation rules prevent a local timeout, an unavailable status endpoint, or an unrecognized receipt from becoming a false failure or success. Existing queue schemas migrate in place so pending rows and media copies survive an application update.

03 / Architecture

Architecture and workflow

  1. Capture locally

    Copy media into application-owned storage and persist the queue entry before attempting the upload.

  2. Obtain durable acceptance

    Send the authenticated upload and require HTTP 202 plus Colombo’s operation ID.

  3. Reconcile delivery

    Advance only on authoritative receipt states, preserving unknown state when the server cannot answer.

  4. Retry safely

    Allow a new upload only after a terminal failed/expired outcome; retain accepted work that may still be processing.

04 / Approach

Engineering decisions

Own the queued media

Camera captures and selected images are copied into application storage and recorded in SQLite before upload.

Separate credentials from queue data

SecureStore holds credentials; the queue contains media metadata and lifecycle state.

Follow the delivery receipt

The client records Colombo’s accepted operation and follows it through to a terminal outcome. The original Flutter client remains as a rollback/reference implementation.

05 / Evidence

Evidence and outcomes

The repository checks receipt mapping, reconciliation triggers, schema migration, and mobile build/export contracts. The important failure case is an accepted upload followed by a status outage: the client must keep the existing operation rather than create a duplicate.

The internal-distribution guard requires an exact published Thompson package and registry integrity. The retained documentation records that this guard can block a candidate until the package is actually available; a successful development build is not substituted for distribution acceptance.

Source and technical notes

06 / Current scope

Where it stands

The public branch now contains the Expo/TypeScript migration. Physical camera behavior and internal distribution have their own release requirements.

The Flutter path is not the durable Expo queue described here. Migration, package publication, signed internal builds, and physical-device checks remain distinct tasks.

Delivery unknown is intentional. It is safer and more accurate than guessing that an accepted file failed because the network cannot return its receipt.