Update-sample-course (#3823)

* chore(courses): update test/sample coures

* feat(activity_planner): addition of goals, location, and description to activities

* formatting

* added activity ids and renamed bookMarkId to activityId

* formatting

---------

Co-authored-by: ggurdin <ggurdin@gmail.com>
This commit is contained in:
wcjord 2025-08-27 16:41:03 -04:00 committed by GitHub
parent 077bbb13f5
commit 4e3f82331c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 3224 additions and 5269 deletions

View file

@ -221,7 +221,7 @@ class ActivityGeneratorState extends State<ActivityGenerator> {
vocab: activity.vocab,
imageURL: imageUrl,
roles: activity.roles,
bookmarkId: activity.bookmarkId,
activityId: activity.activityId,
);
}
});

View file

@ -182,7 +182,7 @@ class ActivityPlanCard extends StatelessWidget {
width: 24.0,
height: 24.0,
cacheKey: controller
.updatedActivity.bookmarkId,
.updatedActivity.activityId,
fit: BoxFit.cover,
)
: CachedNetworkImage(

View file

@ -4,9 +4,10 @@ import 'package:fluffychat/pangea/activity_planner/activity_plan_request.dart';
import 'package:fluffychat/pangea/common/constants/model_keys.dart';
class ActivityPlanModel {
final String bookmarkId;
final String activityId;
final ActivityPlanRequest req;
final String title;
final String description;
final String learningObjective;
final String instructions;
final List<Vocab> vocab;
@ -18,15 +19,21 @@ class ActivityPlanModel {
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.bookmarkId,
required this.activityId,
Map<String, ActivityRole>? roles,
this.imageURL,
this.endAt,
this.duration,
}) : _roles = roles;
}) : description = (description == null || description.isEmpty)
? learningObjective
: description,
_roles = roles;
Map<String, ActivityRole> get roles {
if (_roles != null) return _roles!;
@ -35,6 +42,7 @@ class ActivityPlanModel {
defaultRoles['role_$i'] = ActivityRole(
id: 'role_$i',
name: 'Participant',
goal: learningObjective,
avatarUrl: null,
);
}
@ -43,6 +51,7 @@ class ActivityPlanModel {
ActivityPlanModel copyWith({
String? title,
String? description,
String? learningObjective,
String? instructions,
List<Vocab>? vocab,
@ -54,6 +63,7 @@ class ActivityPlanModel {
return ActivityPlanModel(
req: req,
title: title ?? this.title,
description: description ?? this.description,
learningObjective: learningObjective ?? this.learningObjective,
instructions: instructions ?? this.instructions,
vocab: vocab ?? this.vocab,
@ -61,7 +71,7 @@ class ActivityPlanModel {
endAt: endAt ?? this.endAt,
duration: duration ?? this.duration,
roles: roles ?? _roles,
bookmarkId: bookmarkId,
activityId: activityId,
);
}
@ -87,6 +97,8 @@ class ActivityPlanModel {
instructions: json[ModelKey.activityPlanInstructions],
req: req,
title: json[ModelKey.activityPlanTitle],
description: json[ModelKey.activityPlanDescription] ??
json[ModelKey.activityPlanLearningObjective],
learningObjective: json[ModelKey.activityPlanLearningObjective],
vocab: List<Vocab>.from(
json[ModelKey.activityPlanVocab].map((vocab) => Vocab.fromJson(vocab)),
@ -102,17 +114,18 @@ class ActivityPlanModel {
)
: null,
roles: roles,
bookmarkId: json[ModelKey.activityPlanBookmarkId] ?? json["bookmark_id"],
activityId: json[ModelKey.activityId] ?? json["bookmark_id"],
);
}
Map<String, dynamic> toJson() {
return {
ModelKey.activityPlanBookmarkId: bookmarkId,
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(),
@ -154,6 +167,7 @@ class ActivityPlanModel {
other.title == title &&
other.learningObjective == learningObjective &&
other.instructions == instructions &&
other.description == description &&
listEquals(other.vocab, vocab) &&
other.imageURL == imageURL;
}
@ -163,6 +177,7 @@ class ActivityPlanModel {
req.hashCode ^
title.hashCode ^
learningObjective.hashCode ^
description.hashCode ^
instructions.hashCode ^
Object.hashAll(vocab) ^
imageURL.hashCode;
@ -205,11 +220,13 @@ class Vocab {
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,
});
@ -223,6 +240,7 @@ class ActivityRole {
return ActivityRole(
id: json['id'],
name: json['name'],
goal: json['goal'],
avatarUrl: avatarUrl,
);
}
@ -231,6 +249,7 @@ class ActivityRole {
return {
'id': id,
'name': name,
'goal': goal,
'avatar_url': avatarUrl,
};
}

View file

@ -6,6 +6,7 @@ class ActivityPlanRequest {
final String topic;
final String mode;
final String objective;
final String location;
final MediaEnum media;
LanguageLevelTypeEnum cefrLevel;
final String languageOfInstructions;
@ -21,6 +22,7 @@ class ActivityPlanRequest {
required this.cefrLevel,
required this.languageOfInstructions,
required this.targetLanguage,
this.location = "any",
this.count = 3,
required this.numberOfParticipants,
});
@ -36,6 +38,7 @@ class ActivityPlanRequest {
ModelKey.activityRequestTargetLanguage: targetLanguage,
ModelKey.activityRequestCount: count,
ModelKey.activityRequestNumberOfParticipants: numberOfParticipants,
ModelKey.activityPlanLocation: location,
};
}
@ -56,6 +59,7 @@ class ActivityPlanRequest {
count: json[ModelKey.activityRequestCount],
numberOfParticipants:
json[ModelKey.activityRequestNumberOfParticipants],
location: json[ModelKey.activityPlanLocation] ?? "any",
);
String get storageKey =>
@ -73,6 +77,7 @@ class ActivityPlanRequest {
other.cefrLevel == cefrLevel &&
other.languageOfInstructions == languageOfInstructions &&
other.targetLanguage == targetLanguage &&
other.location == location &&
other.count == count &&
other.numberOfParticipants == numberOfParticipants;
}
@ -87,5 +92,6 @@ class ActivityPlanRequest {
languageOfInstructions.hashCode ^
targetLanguage.hashCode ^
count.hashCode ^
location.hashCode ^
numberOfParticipants.hashCode;
}

View file

@ -122,7 +122,7 @@ class ActivityPlannerBuilderState extends State<ActivityPlannerBuilder> {
vocab: vocab,
imageURL: imageURL,
roles: widget.initialActivity.roles,
bookmarkId: widget.initialActivity.bookmarkId,
activityId: widget.initialActivity.activityId,
);
}
@ -283,7 +283,7 @@ class ActivityPlannerBuilderState extends State<ActivityPlannerBuilder> {
MatrixState.pangeaController.userController;
bool get isBookmarked =>
_userController.isBookmarked(updatedActivity.bookmarkId);
_userController.isBookmarked(updatedActivity.activityId);
Future<void> toggleBookmarkedActivity() async {
isBookmarked
@ -294,7 +294,7 @@ class ActivityPlannerBuilderState extends State<ActivityPlannerBuilder> {
Future<void> _addBookmarkedActivity() async {
await _userController.addBookmarkedActivity(
activityId: updatedActivity.bookmarkId,
activityId: updatedActivity.activityId,
);
await ActivityPlanRepo.set(updatedActivity);
}
@ -309,17 +309,17 @@ class ActivityPlannerBuilderState extends State<ActivityPlannerBuilder> {
updatedActivity,
).then((resp) {
_userController.updateBookmarkedActivity(
activityId: widget.initialActivity.bookmarkId,
newActivityId: resp.bookmarkId,
activityId: widget.initialActivity.activityId,
newActivityId: resp.activityId,
);
});
}
Future<void> _removeBookmarkedActivity() async {
await _userController.removeBookmarkedActivity(
activityId: updatedActivity.bookmarkId,
activityId: updatedActivity.activityId,
);
await ActivityPlanRepo.remove(updatedActivity.bookmarkId);
await ActivityPlanRepo.remove(updatedActivity.activityId);
}
Future<List<String>> launchToSpace() async {
@ -343,7 +343,7 @@ class ActivityPlannerBuilderState extends State<ActivityPlannerBuilder> {
final roomID = await Matrix.of(context).client.createRoom(
creationContent: {
'type':
"${PangeaRoomTypes.activitySession}:${updatedActivity.bookmarkId}",
"${PangeaRoomTypes.activitySession}:${updatedActivity.activityId}",
},
visibility: Visibility.private,
name: "${updatedActivity.title} ${index + 1}",

View file

@ -46,7 +46,7 @@ class ActivityFinishedStatusMessage extends StatelessWidget {
throw L10n.of(context).noCourseFound;
}
final activityId = controller.room.activityPlan!.bookmarkId;
final activityId = controller.room.activityPlan!.activityId;
final topicId = coursePlan.topicID(activityId);
if (topicId == null) {
throw L10n.of(context).activityNotFoundForCourse;

View file

@ -26,7 +26,7 @@ class ActivityPlanRepo {
}
static Future<void> _setCached(ActivityPlanModel response) =>
_activityPlanStorage.write(response.bookmarkId, response.toJson());
_activityPlanStorage.write(response.activityId, response.toJson());
static Future<void> _removeCached(String id) =>
_activityPlanStorage.remove(id);
@ -62,14 +62,14 @@ class ActivityPlanRepo {
);
final Response res = await req.patch(
url: "${PApiUrls.activityPlan}/${update.bookmarkId}",
url: "${PApiUrls.activityPlan}/${update.activityId}",
body: update.toJson(),
);
final decodedBody = jsonDecode(utf8.decode(res.bodyBytes));
final response = ActivityPlanModel.fromJson(decodedBody["plan"]);
_removeCached(update.bookmarkId);
_removeCached(update.activityId);
_setCached(response);
return response;

View file

@ -58,7 +58,7 @@ class ActivitySuggestionCard extends StatelessWidget {
uri: Uri.parse(activity.imageURL!),
width: width,
height: width,
cacheKey: activity.bookmarkId,
cacheKey: activity.activityId,
fit: BoxFit.cover,
)
: CachedNetworkImage(

View file

@ -64,7 +64,7 @@ class _ActivitySuggestionDialogImage extends StatelessWidget {
),
width: width / 2,
height: 200,
cacheKey: activityController.updatedActivity.bookmarkId,
cacheKey: activityController.updatedActivity.activityId,
fit: BoxFit.cover,
)
: CachedNetworkImage(
@ -624,7 +624,7 @@ class _ActivitySuggestionLaunchContent extends StatelessWidget {
),
width: 24.0,
height: 24.0,
cacheKey: activityController.updatedActivity.bookmarkId,
cacheKey: activityController.updatedActivity.activityId,
fit: BoxFit.cover,
)
: CachedNetworkImage(

View file

@ -159,11 +159,13 @@ class ModelKey {
// activity plan
static const String activityPlanRequest = "req";
static const String activityPlanTitle = "title";
static const String activityPlanDescription = "description";
static const String activityPlanLocation = "location";
static const String activityPlanLearningObjective = "learning_objective";
static const String activityPlanInstructions = "instructions";
static const String activityPlanVocab = "vocab";
static const String activityPlanImageURL = "image_url";
static const String activityPlanBookmarkId = "activity_id";
static const String activityId = "activity_id";
static const String activityPlanEndAt = "end_at";
static const String activityPlanDuration = "duration";
static const String activityPlanTopicId = "topic_id";

View file

@ -63,7 +63,7 @@ class CourseChatsView extends StatelessWidget {
for (final joinedRoom in joinedRooms) {
if (joinedRoom.isActivitySession) {
if (topic == null ||
activityIds.contains(joinedRoom.activityPlan?.bookmarkId)) {
activityIds.contains(joinedRoom.activityPlan?.activityId)) {
joinedSessions.add(joinedRoom);
}
} else {

View file

@ -28,7 +28,7 @@ class Topic {
title: json['title'] as String,
description: json['description'] as String,
location: json['location'] as String? ?? "Unknown",
uuid: json['uuid'] as String,
uuid: json['id'] as String,
activities: (json['activities'] as List<dynamic>?)
?.map(
(e) => ActivityPlanModel.fromJson(e as Map<String, dynamic>),
@ -45,13 +45,13 @@ class Topic {
'title': title,
'description': description,
'location': location,
'uuid': uuid,
'id': uuid,
'activities': activities.map((e) => e.toJson()).toList(),
'image_url': imageUrl,
};
}
List<String> get activityIds => activities.map((e) => e.bookmarkId).toList();
List<String> get activityIds => activities.map((e) => e.activityId).toList();
}
/// Represents a course plan in the course planner response.
@ -99,7 +99,7 @@ class CoursePlanModel {
String? topicID(String activityID) {
for (final topic in topics) {
for (final activity in topic.activities) {
if (activity.bookmarkId == activityID) {
if (activity.activityId == activityID) {
return topic.uuid;
}
}
@ -115,7 +115,7 @@ class CoursePlanModel {
cefrLevel: LanguageLevelTypeEnumExtension.fromString(json['cefr_level']),
title: json['title'] as String,
description: json['description'] as String,
uuid: json['uuid'] as String,
uuid: json['id'] as String,
topics: (json['topics'] as List<dynamic>?)
?.map((e) => Topic.fromJson(e as Map<String, dynamic>))
.toList() ??
@ -132,7 +132,7 @@ class CoursePlanModel {
'cefr_level': cefrLevel.string,
'title': title,
'description': description,
'uuid': uuid,
'id': uuid,
'topics': topics.map((e) => e.toJson()).toList(),
'image_url': imageUrl,
};

View file

@ -41,7 +41,7 @@ extension CoursePlanRoomExtension on Room {
}
final activityIds =
course.topics[topicIndex].activities.map((a) => a.bookmarkId).toList();
course.topics[topicIndex].activities.map((a) => a.activityId).toList();
return state.completedActivities(topicID).toSet().containsAll(activityIds);
}

File diff suppressed because it is too large Load diff