/// User preference for one question delivery pipeline / API source. class QuestionSourcePreference { const QuestionSourcePreference({ required this.id, required this.label, required this.description, this.enabled = true, this.ratio = 25, this.comingSoon = false, }); /// Pipeline key or source identifier (matches server `pipeline_key` / `source_tag`). final String id; final String label; final String description; /// Whether this source may contribute questions. final bool enabled; /// Relative weight (0–100). Effective mix uses enabled sources only. final int ratio; /// UI-only stub: shown in settings but not yet wired to a pipeline. final bool comingSoon; QuestionSourcePreference copyWith({ String? label, String? description, bool? enabled, int? ratio, bool? comingSoon, }) { return QuestionSourcePreference( id: id, label: label ?? this.label, description: description ?? this.description, enabled: enabled ?? this.enabled, ratio: ratio ?? this.ratio, comingSoon: comingSoon ?? this.comingSoon, ); } Map toJson() { return { 'id': id, 'label': label, 'description': description, 'enabled': enabled, 'ratio': ratio, 'comingSoon': comingSoon, }; } /// Payload persisted to the API (labels come from server defaults). Map toStorageJson() { return { 'id': id, 'enabled': enabled, 'ratio': ratio, }; } factory QuestionSourcePreference.fromJson(Map json) { return QuestionSourcePreference( id: json['id'] as String, label: json['label'] as String, description: json['description'] as String? ?? '', enabled: json['enabled'] as bool? ?? true, ratio: (json['ratio'] as num?)?.round().clamp(0, 100) ?? 25, comingSoon: json['comingSoon'] as bool? ?? false, ); } @override bool operator ==(Object other) { return other is QuestionSourcePreference && other.id == id && other.label == label && other.description == description && other.enabled == enabled && other.ratio == ratio && other.comingSoon == comingSoon; } @override int get hashCode => Object.hash(id, label, description, enabled, ratio, comingSoon); }