import 'package:flutter/foundation.dart'; import 'package:fluffychat/pangea/activity_planner/activity_plan_request.dart'; import 'package:fluffychat/pangea/common/constants/model_keys.dart'; class ActivityPlanModel { final String activityId; final ActivityPlanRequest req; final String title; final String description; final String learningObjective; final String instructions; final List vocab; final String? imageURL; final DateTime? endAt; final Duration? duration; final Map? _roles; ActivityPlanModel({ required this.req, required this.title, // TODO: when we bring back user's being able to make their own activity, // then this should be required String? description, required this.learningObjective, required this.instructions, required this.vocab, required this.activityId, Map? roles, this.imageURL, this.endAt, this.duration, }) : description = (description == null || description.isEmpty) ? learningObjective : description, _roles = roles; Map get roles { if (_roles != null) return _roles!; final defaultRoles = {}; for (int i = 0; i < req.numberOfParticipants; i++) { defaultRoles['role_$i'] = ActivityRole( id: 'role_$i', name: 'Participant', goal: learningObjective, avatarUrl: null, ); } return defaultRoles; } factory ActivityPlanModel.fromJson(Map json) { final req = ActivityPlanRequest.fromJson(json[ModelKey.activityPlanRequest]); Map? roles; final roleContent = json['roles']; if (roleContent is Map) { roles = Map.from( json['roles'].map( (key, value) => MapEntry( key, ActivityRole.fromJson(value), ), ), ); } return ActivityPlanModel( imageURL: json[ModelKey.activityPlanImageURL], instructions: json[ModelKey.activityPlanInstructions], req: req, title: json[ModelKey.activityPlanTitle], description: json[ModelKey.activityPlanDescription] ?? json[ModelKey.activityPlanLearningObjective], learningObjective: json[ModelKey.activityPlanLearningObjective], vocab: List.from( json[ModelKey.activityPlanVocab].map((vocab) => Vocab.fromJson(vocab)), ), endAt: json[ModelKey.activityPlanEndAt] != null ? DateTime.parse(json[ModelKey.activityPlanEndAt]) : null, duration: json[ModelKey.activityPlanDuration] != null ? Duration( days: json[ModelKey.activityPlanDuration]['days'] ?? 0, hours: json[ModelKey.activityPlanDuration]['hours'] ?? 0, minutes: json[ModelKey.activityPlanDuration]['minutes'] ?? 0, ) : null, roles: roles, activityId: json[ModelKey.activityId] ?? json["bookmark_id"], ); } Map toJson() { return { ModelKey.activityId: activityId, ModelKey.activityPlanImageURL: imageURL, ModelKey.activityPlanInstructions: instructions, ModelKey.activityPlanRequest: req.toJson(), ModelKey.activityPlanTitle: title, ModelKey.activityPlanDescription: description, ModelKey.activityPlanLearningObjective: learningObjective, ModelKey.activityPlanVocab: vocab.map((vocab) => vocab.toJson()).toList(), ModelKey.activityPlanEndAt: endAt?.toIso8601String(), ModelKey.activityPlanDuration: { 'days': duration?.inDays ?? 0, 'hours': duration?.inHours.remainder(24) ?? 0, 'minutes': duration?.inMinutes.remainder(60) ?? 0, }, 'roles': _roles?.map( (key, value) => MapEntry(key, value.toJson()), ), }; } /// activity content displayed nicely in markdown /// use target emoji for learning objective /// use step emoji for instructions String get markdown { final String markdown = '''🎯 $learningObjective \n🪜 $instructions \n\n📖 $vocabString'''; return markdown; } String get vocabString { final List vocabList = []; String vocabString = ""; // cycle through vocab with index for (var i = 0; i < vocab.length; i++) { // if the lemma appears more than once in the vocab list, show the pos // vocab is a wrapped list of string, separated by commas final v = vocab[i]; final bool showPos = vocab.where((vocab) => vocab.lemma == v.lemma).length > 1; vocabString += '${v.lemma}${showPos ? ' (${v.pos})' : ''}${i + 1 < vocab.length ? ', ' : ''}'; vocabList.add("${v.lemma}${showPos ? ' (${v.pos})' : ''}"); } return vocabString; } List get vocabList { final List vocabList = []; // cycle through vocab with index for (var i = 0; i < vocab.length; i++) { // if the lemma appears more than once in the vocab list, show the pos // vocab is a wrapped list of string, separated by commas final v = vocab[i]; final bool showPos = vocab.where((vocab) => vocab.lemma == v.lemma).length > 1; vocabList.add("${v.lemma}${showPos ? ' (${v.pos})' : ''}"); } return vocabList; } @override bool operator ==(Object other) { if (identical(this, other)) return true; return other is ActivityPlanModel && other.req == req && other.title == title && other.learningObjective == learningObjective && other.instructions == instructions && other.description == description && listEquals(other.vocab, vocab) && other.imageURL == imageURL; } @override int get hashCode => req.hashCode ^ title.hashCode ^ learningObjective.hashCode ^ description.hashCode ^ instructions.hashCode ^ Object.hashAll(vocab) ^ imageURL.hashCode; } class Vocab { final String lemma; final String pos; Vocab({ required this.lemma, required this.pos, }); factory Vocab.fromJson(Map json) { return Vocab( lemma: json['lemma'], pos: json['pos'], ); } Map toJson() { return { 'lemma': lemma, 'pos': pos, }; } @override bool operator ==(Object other) { if (identical(this, other)) return true; return other is Vocab && other.lemma == lemma && other.pos == pos; } @override int get hashCode => lemma.hashCode ^ pos.hashCode; } class ActivityRole { final String id; final String name; final String? goal; final String? avatarUrl; ActivityRole({ required this.id, required this.name, required this.goal, this.avatarUrl, }); factory ActivityRole.fromJson(Map json) { final urlContent = json['avatar_url'] as String?; String? avatarUrl; if (urlContent != null && urlContent.isNotEmpty) { avatarUrl = urlContent; } return ActivityRole( id: json['id'] as String, name: json['name'] as String, goal: json['goal'] as String?, avatarUrl: avatarUrl, ); } Map toJson() { return { 'id': id, 'name': name, 'goal': goal, 'avatar_url': avatarUrl, }; } }