Message

Message class API reference

Message

A single data change in the Talon sync system. Each Message represents one field-level change to your data.

Overview

Messages are the fundamental unit of synchronization in Talon. They capture:

  • What changed: table, row, column, value
  • Who changed it: userId, clientId
  • When it changed: localTimestamp (HLC), serverTimestamp
  • Sync status: hasBeenSynced, hasBeenApplied

Constructor

Message({
  required String id,
  required String table,
  required String row,
  required String column,
  required String dataType,
  required String value,
  int? serverTimestamp,
  required String localTimestamp,
  required String userId,
  required String clientId,
  required bool hasBeenApplied,
  required bool hasBeenSynced,
})

In most cases, you won't create messages directly. Use talon.saveChange() instead.

Properties

Identity Fields

PropertyTypeDescription
idStringUnique message identifier (UUID)
userIdStringUser who made this change
clientIdStringClient/device that made this change

Location Fields

PropertyTypeDescription
tableStringTarget table name
rowStringRow identifier (primary key)
columnStringColumn name that changed

Value Fields

PropertyTypeDescription
valueStringSerialized string value
dataTypeStringType hint for deserialization
typedValuedynamicDeserialized value (getter)

Timestamp Fields

PropertyTypeDescription
localTimestampStringHLC timestamp for conflict resolution
serverTimestampint?Server-assigned timestamp for sync tracking

Sync Status Fields

PropertyTypeDescription
hasBeenSyncedboolWhether synced to server
hasBeenAppliedboolWhether applied to local data

Methods

copyWith

Message copyWith({
  String? id,
  String? table,
  String? row,
  String? column,
  String? dataType,
  String? value,
  int? serverTimestamp,
  String? localTimestamp,
  String? userId,
  String? clientId,
  bool? hasBeenApplied,
  bool? hasBeenSynced,
})

Create a copy with some fields replaced.

final synced = message.copyWith(hasBeenSynced: true);

toMap

Map<String, dynamic> toMap()

Convert to a Map for database storage.

await db.insert('messages', message.toMap());

Note: The table field is stored as table_name to avoid SQL reserved word conflicts. Boolean fields are stored as integers (0 or 1).

fromMap

factory Message.fromMap(Map<String, dynamic> map)

Create a Message from a database row.

final rows = await db.query('messages');
final messages = rows.map((row) => Message.fromMap(row)).toList();

toJson

String toJson()

Convert to JSON string.

fromJson

factory Message.fromJson(String source)

Create from JSON string.

The typedValue Getter

Returns the deserialized value based on dataType.

dynamic get typedValue

Type Conversions

dataTypeReturnsNotes
'null'null-
'string'StringAs-is
'int'intVia int.tryParse
'double'doubleVia double.tryParse
'bool'bool'1' or 'true' = true
'datetime'DateTime?Via DateTime.tryParse
'json'Map or ListVia json.decode

Example

// Integer value
final countMsg = Message(..., dataType: 'int', value: '42');
final count = countMsg.typedValue as int; // 42

// JSON value
final metaMsg = Message(..., dataType: 'json', value: '{"key":"val"}');
final meta = metaMsg.typedValue as Map; // {'key': 'val'}

// DateTime value
final dateMsg = Message(..., dataType: 'datetime', value: '2024-01-15T10:30:00Z');
final date = dateMsg.typedValue as DateTime;

Database Column Mapping

When storing messages, use these column names:

PropertyColumn Name
idid
tabletable_name
rowrow
columncolumn
dataTypedata_type
valuevalue
serverTimestampserver_timestamp
localTimestamplocal_timestamp
userIduser_id
clientIdclient_id
hasBeenAppliedhasBeenApplied
hasBeenSyncedhasBeenSynced

Equality

Two messages are equal if all their properties match:

@override
bool operator ==(covariant Message other) {
  return other.id == id &&
      other.table == table &&
      other.row == row &&
      other.column == column &&
      other.dataType == dataType &&
      other.value == value &&
      other.serverTimestamp == serverTimestamp &&
      other.localTimestamp == localTimestamp &&
      other.userId == userId &&
      other.clientId == clientId &&
      other.hasBeenApplied == hasBeenApplied &&
      other.hasBeenSynced == hasBeenSynced;
}

Example: Complete Message Lifecycle

// 1. User saves a change
await talon.saveChange(
  table: 'todos',
  row: 'todo-123',
  column: 'name',
  value: 'Buy milk',
);

// 2. Internally, Talon creates a Message:
// Message(
//   id: 'msg-abc',
//   table: 'todos',
//   row: 'todo-123',
//   column: 'name',
//   dataType: 'string',
//   value: 'Buy milk',
//   localTimestamp: '1705123456789:0001:device-456',
//   userId: 'user-123',
//   clientId: 'device-456',
//   hasBeenApplied: true,  // Applied locally
//   hasBeenSynced: false,  // Not yet synced
// )

// 3. After sync, hasBeenSynced becomes true
// 4. Server assigns serverTimestamp (e.g., 1705123456800)