173 lines
5.7 KiB
Dart
173 lines
5.7 KiB
Dart
import 'dart:convert';
|
|
import 'word_model.dart';
|
|
|
|
/// Single item in a divination reading (e.g. a drawn Rune, Card, Hexagram, or Sign)
|
|
class ReadingSignItem {
|
|
final String title;
|
|
final String? subtitle;
|
|
final String? symbol;
|
|
final String? lore;
|
|
final bool isReversed;
|
|
final WordItem? linkedWord;
|
|
final Map<String, dynamic>? meta;
|
|
|
|
const ReadingSignItem({
|
|
required this.title,
|
|
this.subtitle,
|
|
this.symbol,
|
|
this.lore,
|
|
this.isReversed = false,
|
|
this.linkedWord,
|
|
this.meta,
|
|
});
|
|
|
|
Map<String, dynamic> toMap() => {
|
|
'title': title,
|
|
if (subtitle != null) 'subtitle': subtitle,
|
|
if (symbol != null) 'symbol': symbol,
|
|
if (lore != null) 'lore': lore,
|
|
'isReversed': isReversed,
|
|
if (linkedWord != null) 'linkedWord': linkedWord!.toMap(),
|
|
if (meta != null) 'meta': meta,
|
|
};
|
|
|
|
factory ReadingSignItem.fromMap(Map<String, dynamic> map) => ReadingSignItem(
|
|
title: map['title'] as String? ?? '',
|
|
subtitle: map['subtitle'] as String?,
|
|
symbol: map['symbol'] as String?,
|
|
lore: map['lore'] as String?,
|
|
isReversed: map['isReversed'] as bool? ?? false,
|
|
linkedWord: map['linkedWord'] != null
|
|
? WordItem.fromMap(map['linkedWord'] as Map<String, dynamic>)
|
|
: null,
|
|
meta: map['meta'] as Map<String, dynamic>?,
|
|
);
|
|
}
|
|
|
|
/// A single completed divination reading inside a day session
|
|
class SyncreticReading {
|
|
final String id;
|
|
final String systemId; // 'cleromancy', 'matrix', 'runes', 'lenormand', 'tarot', 'iching', 'ogham', 'geomancy'
|
|
final String systemName;
|
|
final String systemIcon;
|
|
final DateTime timestamp;
|
|
final String layoutTitle; // e.g. 'Бросок 2d12', 'Ленорман d6+d12', 'Триптих Норн'
|
|
final String inputMethod; // 'dice' | 'manual'
|
|
final List<ReadingSignItem> items;
|
|
final String? note;
|
|
final String? question;
|
|
|
|
const SyncreticReading({
|
|
required this.id,
|
|
required this.systemId,
|
|
required this.systemName,
|
|
required this.systemIcon,
|
|
required this.timestamp,
|
|
required this.layoutTitle,
|
|
this.inputMethod = 'dice',
|
|
required this.items,
|
|
this.note,
|
|
this.question,
|
|
});
|
|
|
|
String get formattedTime {
|
|
final hour = timestamp.hour.toString().padLeft(2, '0');
|
|
final min = timestamp.minute.toString().padLeft(2, '0');
|
|
return '$hour:$min';
|
|
}
|
|
|
|
Map<String, dynamic> toMap() => {
|
|
'id': id,
|
|
'systemId': systemId,
|
|
'systemName': systemName,
|
|
'systemIcon': systemIcon,
|
|
'timestamp': timestamp.toIso8601String(),
|
|
'layoutTitle': layoutTitle,
|
|
'inputMethod': inputMethod,
|
|
'items': items.map((i) => i.toMap()).toList(),
|
|
if (note != null) 'note': note,
|
|
if (question != null) 'question': question,
|
|
};
|
|
|
|
factory SyncreticReading.fromMap(Map<String, dynamic> map) => SyncreticReading(
|
|
id: map['id'] as String? ?? '',
|
|
systemId: map['systemId'] as String? ?? '',
|
|
systemName: map['systemName'] as String? ?? '',
|
|
systemIcon: map['systemIcon'] as String? ?? '🔮',
|
|
timestamp: DateTime.tryParse(map['timestamp'] as String? ?? '') ?? DateTime.now(),
|
|
layoutTitle: map['layoutTitle'] as String? ?? '',
|
|
inputMethod: map['inputMethod'] as String? ?? 'dice',
|
|
items: (map['items'] as List<dynamic>?)
|
|
?.map((i) => ReadingSignItem.fromMap(i as Map<String, dynamic>))
|
|
.toList() ??
|
|
[],
|
|
note: map['note'] as String?,
|
|
question: map['question'] as String?,
|
|
);
|
|
}
|
|
|
|
/// A day-long (or question-long) Syncretic Session accumulating multiple readings across systems
|
|
class SyncreticSession {
|
|
final String id;
|
|
final DateTime date;
|
|
final String? mainQuestion;
|
|
final List<SyncreticReading> readings;
|
|
final String? comparativeAiSynthesis;
|
|
|
|
const SyncreticSession({
|
|
required this.id,
|
|
required this.date,
|
|
this.mainQuestion,
|
|
this.readings = const [],
|
|
this.comparativeAiSynthesis,
|
|
});
|
|
|
|
String get dateKey =>
|
|
'${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}';
|
|
|
|
String get formattedDate {
|
|
final day = date.day.toString().padLeft(2, '0');
|
|
final month = date.month.toString().padLeft(2, '0');
|
|
final year = date.year.toString();
|
|
return '$day.$month.$year';
|
|
}
|
|
|
|
SyncreticSession copyWith({
|
|
String? id,
|
|
DateTime? date,
|
|
String? mainQuestion,
|
|
List<SyncreticReading>? readings,
|
|
String? comparativeAiSynthesis,
|
|
}) {
|
|
return SyncreticSession(
|
|
id: id ?? this.id,
|
|
date: date ?? this.date,
|
|
mainQuestion: mainQuestion ?? this.mainQuestion,
|
|
readings: readings ?? this.readings,
|
|
comparativeAiSynthesis: comparativeAiSynthesis ?? this.comparativeAiSynthesis,
|
|
);
|
|
}
|
|
|
|
Map<String, dynamic> toMap() => {
|
|
'id': id,
|
|
'date': date.toIso8601String(),
|
|
if (mainQuestion != null) 'mainQuestion': mainQuestion,
|
|
'readings': readings.map((r) => r.toMap()).toList(),
|
|
if (comparativeAiSynthesis != null) 'comparativeAiSynthesis': comparativeAiSynthesis,
|
|
};
|
|
|
|
factory SyncreticSession.fromMap(Map<String, dynamic> map) => SyncreticSession(
|
|
id: map['id'] as String? ?? '',
|
|
date: DateTime.tryParse(map['date'] as String? ?? '') ?? DateTime.now(),
|
|
mainQuestion: map['mainQuestion'] as String?,
|
|
readings: (map['readings'] as List<dynamic>?)
|
|
?.map((r) => SyncreticReading.fromMap(r as Map<String, dynamic>))
|
|
.toList() ??
|
|
[],
|
|
comparativeAiSynthesis: map['comparativeAiSynthesis'] as String?,
|
|
);
|
|
|
|
String toJson() => jsonEncode(toMap());
|
|
factory SyncreticSession.fromJson(String str) => SyncreticSession.fromMap(jsonDecode(str));
|
|
}
|