3890 activity start page changes (#3901)
* comment out unreferenced files * decouple courses, topics, and activities * update start page * disable 'join open session' button
This commit is contained in:
parent
e4626dc2c0
commit
5ce2a787b4
64 changed files with 5249 additions and 4847 deletions
|
|
@ -28,8 +28,7 @@ import 'package:fluffychat/pages/settings_notifications/settings_notifications.d
|
|||
import 'package:fluffychat/pages/settings_password/settings_password.dart';
|
||||
import 'package:fluffychat/pages/settings_security/settings_security.dart';
|
||||
import 'package:fluffychat/pages/settings_style/settings_style.dart';
|
||||
import 'package:fluffychat/pangea/activity_generator/activity_generator.dart';
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_planner_page.dart';
|
||||
import 'package:fluffychat/pangea/activity_sessions/activity_session_start/activity_session_start_page.dart';
|
||||
import 'package:fluffychat/pangea/analytics_page/analytics_page.dart';
|
||||
import 'package:fluffychat/pangea/analytics_summary/progress_indicators_enum.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/pages/pangea_invitation_selection.dart';
|
||||
|
|
@ -597,6 +596,19 @@ abstract class AppRoutes {
|
|||
routes: roomDetailsRoutes('spaceid'),
|
||||
),
|
||||
...roomDetailsRoutes('spaceid'),
|
||||
GoRoute(
|
||||
path: 'activity/:activityid',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
ActivitySessionStartPage(
|
||||
activityId: state.pathParameters['activityid']!,
|
||||
isNew: state.uri.queryParameters['new'] == 'true',
|
||||
parentId: state.pathParameters['spaceid']!,
|
||||
),
|
||||
),
|
||||
redirect: loggedOutRedirect,
|
||||
),
|
||||
GoRoute(
|
||||
path: ':roomid',
|
||||
pageBuilder: (context, state) {
|
||||
|
|
@ -911,30 +923,6 @@ abstract class AppRoutes {
|
|||
),
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: 'planner',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
ActivityPlannerPage(
|
||||
roomID: state.pathParameters[roomKey]!,
|
||||
),
|
||||
),
|
||||
redirect: loggedOutRedirect,
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: '/generator',
|
||||
redirect: loggedOutRedirect,
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
ActivityGenerator(
|
||||
roomID: state.pathParameters[roomKey]!,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
GoRoute(
|
||||
path: 'access',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
|
|
|
|||
|
|
@ -5196,5 +5196,9 @@
|
|||
"@goToCourse": {
|
||||
"type": "String",
|
||||
"course": {}
|
||||
}
|
||||
},
|
||||
"startNewSession": "Start new session",
|
||||
"joinOpenSession": "Join open session",
|
||||
"less": "less",
|
||||
"activityNotFound": "Activity not found"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2242,8 +2242,14 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
);
|
||||
}
|
||||
|
||||
if (room.isActivitySession == true && !room.activityHasStarted) {
|
||||
return ActivitySessionStartPage(room: room);
|
||||
if (room.isActivitySession == true &&
|
||||
!room.activityHasStarted &&
|
||||
room.courseParent != null) {
|
||||
return ActivitySessionStartPage(
|
||||
activityId: room.activityId!,
|
||||
room: room,
|
||||
parentId: room.courseParent!.id,
|
||||
);
|
||||
}
|
||||
// Pangea#
|
||||
final theme = Theme.of(context);
|
||||
|
|
|
|||
|
|
@ -8,9 +8,7 @@ import 'package:fluffychat/pages/chat/chat.dart';
|
|||
import 'package:fluffychat/pages/chat/events/message.dart';
|
||||
import 'package:fluffychat/pages/chat/seen_by_row.dart';
|
||||
import 'package:fluffychat/pages/chat/typing_indicators.dart';
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_plan_message.dart';
|
||||
import 'package:fluffychat/pangea/activity_sessions/activity_user_summaries_widget.dart';
|
||||
import 'package:fluffychat/pangea/events/extensions/pangea_event_extension.dart';
|
||||
import 'package:fluffychat/utils/account_config.dart';
|
||||
import 'package:fluffychat/utils/matrix_sdk_extensions/filtered_timeline_extension.dart';
|
||||
import 'package:fluffychat/utils/platform_infos.dart';
|
||||
|
|
@ -144,67 +142,48 @@ class ChatEventList extends StatelessWidget {
|
|||
key: ValueKey(event.eventId),
|
||||
index: i,
|
||||
controller: controller.scrollController,
|
||||
child:
|
||||
// #Pangea
|
||||
event.isActivityMessage
|
||||
? ActivityPlanMessage(
|
||||
event,
|
||||
controller: controller,
|
||||
timeline: timeline,
|
||||
animateIn: animateIn,
|
||||
resetAnimateIn: () {
|
||||
controller.animateInEventIndex = null;
|
||||
},
|
||||
highlightMarker:
|
||||
controller.scrollToEventIdMarker == event.eventId,
|
||||
selected: controller.selectedEvents
|
||||
.any((e) => e.eventId == event.eventId),
|
||||
)
|
||||
:
|
||||
// Pangea#
|
||||
Message(
|
||||
event,
|
||||
animateIn: animateIn,
|
||||
resetAnimateIn: () {
|
||||
controller.animateInEventIndex = null;
|
||||
},
|
||||
onSwipe: () => controller.replyAction(replyTo: event),
|
||||
// #Pangea
|
||||
onInfoTab: (_) => {},
|
||||
// onInfoTab: controller.showEventInfo,
|
||||
// Pangea#
|
||||
onMention: () => controller.sendController.text +=
|
||||
'${event.senderFromMemoryOrFallback.mention} ',
|
||||
highlightMarker:
|
||||
controller.scrollToEventIdMarker == event.eventId,
|
||||
// #Pangea
|
||||
// onSelect: controller.onSelectMessage,
|
||||
onSelect: (_) {},
|
||||
// Pangea#
|
||||
scrollToEventId: (String eventId) =>
|
||||
controller.scrollToEventId(eventId),
|
||||
longPressSelect: controller.selectedEvents.isNotEmpty,
|
||||
// #Pangea
|
||||
immersionMode: controller.choreographer.immersionMode,
|
||||
controller: controller,
|
||||
isButton: event.eventId == controller.buttonEventID,
|
||||
// Pangea#
|
||||
selected: controller.selectedEvents
|
||||
.any((e) => e.eventId == event.eventId),
|
||||
singleSelected:
|
||||
controller.selectedEvents.singleOrNull?.eventId ==
|
||||
event.eventId,
|
||||
onEdit: () => controller.editSelectedEventAction(),
|
||||
timeline: timeline,
|
||||
displayReadMarker: i > 0 &&
|
||||
controller.readMarkerEventId == event.eventId,
|
||||
nextEvent:
|
||||
i + 1 < events.length ? events[i + 1] : null,
|
||||
previousEvent: i > 0 ? events[i - 1] : null,
|
||||
wallpaperMode: hasWallpaper,
|
||||
scrollController: controller.scrollController,
|
||||
colors: colors,
|
||||
),
|
||||
child: Message(
|
||||
event,
|
||||
animateIn: animateIn,
|
||||
resetAnimateIn: () {
|
||||
controller.animateInEventIndex = null;
|
||||
},
|
||||
onSwipe: () => controller.replyAction(replyTo: event),
|
||||
// #Pangea
|
||||
onInfoTab: (_) => {},
|
||||
// onInfoTab: controller.showEventInfo,
|
||||
// Pangea#
|
||||
onMention: () => controller.sendController.text +=
|
||||
'${event.senderFromMemoryOrFallback.mention} ',
|
||||
highlightMarker:
|
||||
controller.scrollToEventIdMarker == event.eventId,
|
||||
// #Pangea
|
||||
// onSelect: controller.onSelectMessage,
|
||||
onSelect: (_) {},
|
||||
// Pangea#
|
||||
scrollToEventId: (String eventId) =>
|
||||
controller.scrollToEventId(eventId),
|
||||
longPressSelect: controller.selectedEvents.isNotEmpty,
|
||||
// #Pangea
|
||||
immersionMode: controller.choreographer.immersionMode,
|
||||
controller: controller,
|
||||
isButton: event.eventId == controller.buttonEventID,
|
||||
// Pangea#
|
||||
selected: controller.selectedEvents
|
||||
.any((e) => e.eventId == event.eventId),
|
||||
singleSelected:
|
||||
controller.selectedEvents.singleOrNull?.eventId ==
|
||||
event.eventId,
|
||||
onEdit: () => controller.editSelectedEventAction(),
|
||||
timeline: timeline,
|
||||
displayReadMarker:
|
||||
i > 0 && controller.readMarkerEventId == event.eventId,
|
||||
nextEvent: i + 1 < events.length ? events[i + 1] : null,
|
||||
previousEvent: i > 0 ? events[i - 1] : null,
|
||||
wallpaperMode: hasWallpaper,
|
||||
scrollController: controller.scrollController,
|
||||
colors: colors,
|
||||
),
|
||||
);
|
||||
},
|
||||
// #Pangea
|
||||
|
|
|
|||
|
|
@ -137,8 +137,10 @@ class Message extends StatelessWidget {
|
|||
}
|
||||
|
||||
// #Pangea
|
||||
if (event.type == PangeaEventTypes.activityPlan) {
|
||||
if (event.type == PangeaEventTypes.activityPlan &&
|
||||
event.room.activityPlan != null) {
|
||||
return ActivitySummary(
|
||||
activity: event.room.activityPlan!,
|
||||
room: event.room,
|
||||
showInstructions: controller.showInstructions,
|
||||
toggleInstructions: controller.toggleShowInstructions,
|
||||
|
|
|
|||
|
|
@ -1,267 +1,267 @@
|
|||
import 'package:flutter/material.dart';
|
||||
// import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
// import 'package:collection/collection.dart';
|
||||
// import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/activity_generator/activity_generator_view.dart';
|
||||
import 'package:fluffychat/pangea/activity_generator/activity_mode_list_repo.dart';
|
||||
import 'package:fluffychat/pangea/activity_generator/activity_plan_generation_repo.dart';
|
||||
import 'package:fluffychat/pangea/activity_generator/learning_objective_list_repo.dart';
|
||||
import 'package:fluffychat/pangea/activity_generator/list_request_schema.dart';
|
||||
import 'package:fluffychat/pangea/activity_generator/media_enum.dart';
|
||||
import 'package:fluffychat/pangea/activity_generator/topic_list_repo.dart';
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_plan_request.dart';
|
||||
import 'package:fluffychat/pangea/activity_suggestions/activity_suggestions_constants.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
// import 'package:fluffychat/config/app_config.dart';
|
||||
// import 'package:fluffychat/l10n/l10n.dart';
|
||||
// import 'package:fluffychat/pangea/activity_generator/activity_generator_view.dart';
|
||||
// import 'package:fluffychat/pangea/activity_generator/activity_mode_list_repo.dart';
|
||||
// import 'package:fluffychat/pangea/activity_generator/activity_plan_generation_repo.dart';
|
||||
// import 'package:fluffychat/pangea/activity_generator/learning_objective_list_repo.dart';
|
||||
// import 'package:fluffychat/pangea/activity_generator/list_request_schema.dart';
|
||||
// import 'package:fluffychat/pangea/activity_generator/media_enum.dart';
|
||||
// import 'package:fluffychat/pangea/activity_generator/topic_list_repo.dart';
|
||||
// import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
|
||||
// import 'package:fluffychat/pangea/activity_planner/activity_plan_request.dart';
|
||||
// import 'package:fluffychat/pangea/activity_suggestions/activity_suggestions_constants.dart';
|
||||
// import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
// import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart';
|
||||
// import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart';
|
||||
// import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class ActivityGenerator extends StatefulWidget {
|
||||
final String roomID;
|
||||
const ActivityGenerator({
|
||||
required this.roomID,
|
||||
super.key,
|
||||
});
|
||||
// class ActivityGenerator extends StatefulWidget {
|
||||
// final String roomID;
|
||||
// const ActivityGenerator({
|
||||
// required this.roomID,
|
||||
// super.key,
|
||||
// });
|
||||
|
||||
@override
|
||||
ActivityGeneratorState createState() => ActivityGeneratorState();
|
||||
}
|
||||
// @override
|
||||
// ActivityGeneratorState createState() => ActivityGeneratorState();
|
||||
// }
|
||||
|
||||
class ActivityGeneratorState extends State<ActivityGenerator> {
|
||||
bool loading = false;
|
||||
Object? error;
|
||||
List<ActivityPlanModel>? activities;
|
||||
// class ActivityGeneratorState extends State<ActivityGenerator> {
|
||||
// bool loading = false;
|
||||
// Object? error;
|
||||
// List<ActivityPlanModel>? activities;
|
||||
|
||||
final formKey = GlobalKey<FormState>();
|
||||
// final formKey = GlobalKey<FormState>();
|
||||
|
||||
final topicController = TextEditingController();
|
||||
final objectiveController = TextEditingController();
|
||||
final modeController = TextEditingController();
|
||||
// final topicController = TextEditingController();
|
||||
// final objectiveController = TextEditingController();
|
||||
// final modeController = TextEditingController();
|
||||
|
||||
MediaEnum selectedMedia = MediaEnum.nan;
|
||||
String? selectedLanguageOfInstructions;
|
||||
String? selectedTargetLanguage;
|
||||
LanguageLevelTypeEnum? selectedCefrLevel;
|
||||
int? selectedNumberOfParticipants;
|
||||
// MediaEnum selectedMedia = MediaEnum.nan;
|
||||
// String? selectedLanguageOfInstructions;
|
||||
// String? selectedTargetLanguage;
|
||||
// LanguageLevelTypeEnum? selectedCefrLevel;
|
||||
// int? selectedNumberOfParticipants;
|
||||
|
||||
String? filename;
|
||||
// String? filename;
|
||||
|
||||
List<ActivitySettingResponseSchema>? topicItems;
|
||||
List<ActivitySettingResponseSchema>? modeItems;
|
||||
List<ActivitySettingResponseSchema>? objectiveItems;
|
||||
// List<ActivitySettingResponseSchema>? topicItems;
|
||||
// List<ActivitySettingResponseSchema>? modeItems;
|
||||
// List<ActivitySettingResponseSchema>? objectiveItems;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// @override
|
||||
// void initState() {
|
||||
// super.initState();
|
||||
|
||||
selectedLanguageOfInstructions =
|
||||
MatrixState.pangeaController.languageController.userL1?.langCode;
|
||||
selectedTargetLanguage =
|
||||
MatrixState.pangeaController.languageController.userL2?.langCode;
|
||||
selectedCefrLevel = LanguageLevelTypeEnum.a1;
|
||||
selectedNumberOfParticipants = 3;
|
||||
_setMode();
|
||||
_setTopic();
|
||||
_setObjective();
|
||||
}
|
||||
// selectedLanguageOfInstructions =
|
||||
// MatrixState.pangeaController.languageController.userL1?.langCode;
|
||||
// selectedTargetLanguage =
|
||||
// MatrixState.pangeaController.languageController.userL2?.langCode;
|
||||
// selectedCefrLevel = LanguageLevelTypeEnum.a1;
|
||||
// selectedNumberOfParticipants = 3;
|
||||
// _setMode();
|
||||
// _setTopic();
|
||||
// _setObjective();
|
||||
// }
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
topicController.dispose();
|
||||
objectiveController.dispose();
|
||||
modeController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
// @override
|
||||
// void dispose() {
|
||||
// topicController.dispose();
|
||||
// objectiveController.dispose();
|
||||
// modeController.dispose();
|
||||
// super.dispose();
|
||||
// }
|
||||
|
||||
ActivitySettingRequestSchema get req => ActivitySettingRequestSchema(
|
||||
langCode:
|
||||
MatrixState.pangeaController.languageController.userL1?.langCode ??
|
||||
LanguageKeys.defaultLanguage,
|
||||
);
|
||||
// ActivitySettingRequestSchema get req => ActivitySettingRequestSchema(
|
||||
// langCode:
|
||||
// MatrixState.pangeaController.languageController.userL1?.langCode ??
|
||||
// LanguageKeys.defaultLanguage,
|
||||
// );
|
||||
|
||||
ActivityPlanRequest get planRequest => ActivityPlanRequest(
|
||||
topic: topicController.text,
|
||||
mode: modeController.text,
|
||||
objective: objectiveController.text,
|
||||
media: selectedMedia,
|
||||
languageOfInstructions: selectedLanguageOfInstructions!,
|
||||
targetLanguage: selectedTargetLanguage!,
|
||||
cefrLevel: selectedCefrLevel!,
|
||||
numberOfParticipants: selectedNumberOfParticipants!,
|
||||
);
|
||||
// ActivityPlanRequest get planRequest => ActivityPlanRequest(
|
||||
// topic: topicController.text,
|
||||
// mode: modeController.text,
|
||||
// objective: objectiveController.text,
|
||||
// media: selectedMedia,
|
||||
// languageOfInstructions: selectedLanguageOfInstructions!,
|
||||
// targetLanguage: selectedTargetLanguage!,
|
||||
// cefrLevel: selectedCefrLevel!,
|
||||
// numberOfParticipants: selectedNumberOfParticipants!,
|
||||
// );
|
||||
|
||||
Room? get room => Matrix.of(context).client.getRoomById(widget.roomID);
|
||||
// Room? get room => Matrix.of(context).client.getRoomById(widget.roomID);
|
||||
|
||||
String? validateNotNull(String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return L10n.of(context).interactiveTranslatorRequired;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
// String? validateNotNull(String? value) {
|
||||
// if (value == null || value.isEmpty) {
|
||||
// return L10n.of(context).interactiveTranslatorRequired;
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
|
||||
String? get _randomTopic => (topicItems?..shuffle())?.first.name;
|
||||
// String? get _randomTopic => (topicItems?..shuffle())?.first.name;
|
||||
|
||||
String? get _randomObjective => (objectiveItems?..shuffle())?.first.name;
|
||||
// String? get _randomObjective => (objectiveItems?..shuffle())?.first.name;
|
||||
|
||||
String? get _randomMode => (modeItems?..shuffle())?.first.name;
|
||||
// String? get _randomMode => (modeItems?..shuffle())?.first.name;
|
||||
|
||||
bool get randomizeEnabled =>
|
||||
topicItems != null && objectiveItems != null && modeItems != null;
|
||||
// bool get randomizeEnabled =>
|
||||
// topicItems != null && objectiveItems != null && modeItems != null;
|
||||
|
||||
void randomizeSelections() {
|
||||
final selectedTopic = _randomTopic;
|
||||
final selectedObjective = _randomObjective;
|
||||
final selectedMode = _randomMode;
|
||||
// void randomizeSelections() {
|
||||
// final selectedTopic = _randomTopic;
|
||||
// final selectedObjective = _randomObjective;
|
||||
// final selectedMode = _randomMode;
|
||||
|
||||
if (selectedTopic == null ||
|
||||
selectedObjective == null ||
|
||||
selectedMode == null) {
|
||||
return;
|
||||
}
|
||||
// if (selectedTopic == null ||
|
||||
// selectedObjective == null ||
|
||||
// selectedMode == null) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
topicController.text = selectedTopic;
|
||||
objectiveController.text = selectedObjective;
|
||||
modeController.text = selectedMode;
|
||||
});
|
||||
}
|
||||
}
|
||||
// if (mounted) {
|
||||
// setState(() {
|
||||
// topicController.text = selectedTopic;
|
||||
// objectiveController.text = selectedObjective;
|
||||
// modeController.text = selectedMode;
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
void clearSelections() async {
|
||||
setState(() {
|
||||
topicController.clear();
|
||||
objectiveController.clear();
|
||||
modeController.clear();
|
||||
selectedMedia = MediaEnum.nan;
|
||||
selectedLanguageOfInstructions =
|
||||
MatrixState.pangeaController.languageController.userL1?.langCode;
|
||||
selectedTargetLanguage =
|
||||
MatrixState.pangeaController.languageController.userL2?.langCode;
|
||||
selectedCefrLevel = LanguageLevelTypeEnum.a1;
|
||||
selectedNumberOfParticipants = 3;
|
||||
});
|
||||
}
|
||||
// void clearSelections() async {
|
||||
// setState(() {
|
||||
// topicController.clear();
|
||||
// objectiveController.clear();
|
||||
// modeController.clear();
|
||||
// selectedMedia = MediaEnum.nan;
|
||||
// selectedLanguageOfInstructions =
|
||||
// MatrixState.pangeaController.languageController.userL1?.langCode;
|
||||
// selectedTargetLanguage =
|
||||
// MatrixState.pangeaController.languageController.userL2?.langCode;
|
||||
// selectedCefrLevel = LanguageLevelTypeEnum.a1;
|
||||
// selectedNumberOfParticipants = 3;
|
||||
// });
|
||||
// }
|
||||
|
||||
void setSelectedNumberOfParticipants(int? value) {
|
||||
setState(() => selectedNumberOfParticipants = value);
|
||||
}
|
||||
// void setSelectedNumberOfParticipants(int? value) {
|
||||
// setState(() => selectedNumberOfParticipants = value);
|
||||
// }
|
||||
|
||||
void setSelectedTargetLanguage(String? value) {
|
||||
setState(() => selectedTargetLanguage = value);
|
||||
}
|
||||
// void setSelectedTargetLanguage(String? value) {
|
||||
// setState(() => selectedTargetLanguage = value);
|
||||
// }
|
||||
|
||||
void setSelectedLanguageOfInstructions(String? value) {
|
||||
setState(() => selectedLanguageOfInstructions = value);
|
||||
}
|
||||
// void setSelectedLanguageOfInstructions(String? value) {
|
||||
// setState(() => selectedLanguageOfInstructions = value);
|
||||
// }
|
||||
|
||||
void setSelectedCefrLevel(LanguageLevelTypeEnum? value) {
|
||||
setState(() => selectedCefrLevel = value);
|
||||
}
|
||||
// void setSelectedCefrLevel(LanguageLevelTypeEnum? value) {
|
||||
// setState(() => selectedCefrLevel = value);
|
||||
// }
|
||||
|
||||
ActivitySettingResponseSchema? get _selectedMode {
|
||||
return modeItems?.firstWhereOrNull(
|
||||
(element) => element.name.toLowerCase() == planRequest.mode.toLowerCase(),
|
||||
);
|
||||
}
|
||||
// ActivitySettingResponseSchema? get _selectedMode {
|
||||
// return modeItems?.firstWhereOrNull(
|
||||
// (element) => element.name.toLowerCase() == planRequest.mode.toLowerCase(),
|
||||
// );
|
||||
// }
|
||||
|
||||
Future<void> _setTopic() async {
|
||||
final topic = await TopicListRepo.get(req);
|
||||
// Future<void> _setTopic() async {
|
||||
// final topic = await TopicListRepo.get(req);
|
||||
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
topicItems = topic;
|
||||
});
|
||||
}
|
||||
}
|
||||
// if (mounted) {
|
||||
// setState(() {
|
||||
// topicItems = topic;
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
Future<void> _setMode() async {
|
||||
final mode = await ActivityModeListRepo.get(req);
|
||||
// Future<void> _setMode() async {
|
||||
// final mode = await ActivityModeListRepo.get(req);
|
||||
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
modeItems = mode;
|
||||
});
|
||||
_setModeImageURL();
|
||||
}
|
||||
}
|
||||
// if (mounted) {
|
||||
// setState(() {
|
||||
// modeItems = mode;
|
||||
// });
|
||||
// _setModeImageURL();
|
||||
// }
|
||||
// }
|
||||
|
||||
Future<void> _setObjective() async {
|
||||
final objective = await LearningObjectiveListRepo.get(req);
|
||||
// Future<void> _setObjective() async {
|
||||
// final objective = await LearningObjectiveListRepo.get(req);
|
||||
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
objectiveItems = objective;
|
||||
});
|
||||
}
|
||||
}
|
||||
// if (mounted) {
|
||||
// setState(() {
|
||||
// objectiveItems = objective;
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
Future<void> _setModeImageURL() async {
|
||||
final mode = _selectedMode;
|
||||
if (mode == null) return;
|
||||
// Future<void> _setModeImageURL() async {
|
||||
// final mode = _selectedMode;
|
||||
// if (mode == null) return;
|
||||
|
||||
final modeName =
|
||||
mode.defaultName.toLowerCase().replaceAll(RegExp(r'\s+'), '');
|
||||
// final modeName =
|
||||
// mode.defaultName.toLowerCase().replaceAll(RegExp(r'\s+'), '');
|
||||
|
||||
if (!mounted || activities == null) return;
|
||||
final imageUrl =
|
||||
"${AppConfig.assetsBaseURL}/${ActivitySuggestionsConstants.modeImageFileStart}$modeName.jpg";
|
||||
setState(() {
|
||||
filename = imageUrl;
|
||||
for (ActivityPlanModel activity in activities!) {
|
||||
activity = ActivityPlanModel(
|
||||
req: activity.req,
|
||||
title: activity.title,
|
||||
learningObjective: activity.learningObjective,
|
||||
instructions: activity.instructions,
|
||||
vocab: activity.vocab,
|
||||
imageURL: imageUrl,
|
||||
roles: activity.roles,
|
||||
activityId: activity.activityId,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
// if (!mounted || activities == null) return;
|
||||
// final imageUrl =
|
||||
// "${AppConfig.assetsBaseURL}/${ActivitySuggestionsConstants.modeImageFileStart}$modeName.jpg";
|
||||
// setState(() {
|
||||
// filename = imageUrl;
|
||||
// for (ActivityPlanModel activity in activities!) {
|
||||
// activity = ActivityPlanModel(
|
||||
// req: activity.req,
|
||||
// title: activity.title,
|
||||
// learningObjective: activity.learningObjective,
|
||||
// instructions: activity.instructions,
|
||||
// vocab: activity.vocab,
|
||||
// imageURL: imageUrl,
|
||||
// roles: activity.roles,
|
||||
// activityId: activity.activityId,
|
||||
// );
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
void clearActivities() {
|
||||
setState(() {
|
||||
activities = null;
|
||||
filename = null;
|
||||
});
|
||||
}
|
||||
// void clearActivities() {
|
||||
// setState(() {
|
||||
// activities = null;
|
||||
// filename = null;
|
||||
// });
|
||||
// }
|
||||
|
||||
Future<void> generate({bool force = false}) async {
|
||||
setState(() {
|
||||
loading = true;
|
||||
error = null;
|
||||
activities = null;
|
||||
});
|
||||
// Future<void> generate({bool force = false}) async {
|
||||
// setState(() {
|
||||
// loading = true;
|
||||
// error = null;
|
||||
// activities = null;
|
||||
// });
|
||||
|
||||
try {
|
||||
final resp = await ActivityPlanGenerationRepo.get(
|
||||
planRequest,
|
||||
force: force,
|
||||
);
|
||||
activities = resp.activityPlans;
|
||||
await _setModeImageURL();
|
||||
} catch (e, s) {
|
||||
error = e;
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {
|
||||
'activityPlanRequest': planRequest,
|
||||
},
|
||||
);
|
||||
} finally {
|
||||
if (mounted) setState(() => loading = false);
|
||||
}
|
||||
}
|
||||
// try {
|
||||
// final resp = await ActivityPlanGenerationRepo.get(
|
||||
// planRequest,
|
||||
// force: force,
|
||||
// );
|
||||
// activities = resp.activityPlans;
|
||||
// await _setModeImageURL();
|
||||
// } catch (e, s) {
|
||||
// error = e;
|
||||
// ErrorHandler.logError(
|
||||
// e: e,
|
||||
// s: s,
|
||||
// data: {
|
||||
// 'activityPlanRequest': planRequest,
|
||||
// },
|
||||
// );
|
||||
// } finally {
|
||||
// if (mounted) setState(() => loading = false);
|
||||
// }
|
||||
// }
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => ActivityGeneratorView(this);
|
||||
}
|
||||
// @override
|
||||
// Widget build(BuildContext context) => ActivityGeneratorView(this);
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,309 +1,309 @@
|
|||
import 'package:flutter/material.dart';
|
||||
// import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
// import 'package:cached_network_image/cached_network_image.dart';
|
||||
// import 'package:material_symbols_icons/symbols.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/activity_generator/activity_generator.dart';
|
||||
import 'package:fluffychat/pangea/activity_generator/activity_plan_card.dart';
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_planner_builder.dart';
|
||||
import 'package:fluffychat/pangea/activity_planner/suggestion_form_field.dart';
|
||||
import 'package:fluffychat/pangea/activity_suggestions/activity_suggestions_constants.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/widgets/language_level_dropdown.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
|
||||
import 'package:fluffychat/pangea/instructions/instructions_enum.dart';
|
||||
import 'package:fluffychat/pangea/instructions/instructions_inline_tooltip.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/widgets/p_language_dropdown.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
// import 'package:fluffychat/config/app_config.dart';
|
||||
// import 'package:fluffychat/l10n/l10n.dart';
|
||||
// import 'package:fluffychat/pangea/activity_generator/activity_generator.dart';
|
||||
// import 'package:fluffychat/pangea/activity_generator/activity_plan_card.dart';
|
||||
// import 'package:fluffychat/pangea/activity_planner/activity_planner_builder.dart';
|
||||
// import 'package:fluffychat/pangea/activity_planner/suggestion_form_field.dart';
|
||||
// import 'package:fluffychat/pangea/activity_suggestions/activity_suggestions_constants.dart';
|
||||
// import 'package:fluffychat/pangea/chat_settings/widgets/language_level_dropdown.dart';
|
||||
// import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
|
||||
// import 'package:fluffychat/pangea/instructions/instructions_enum.dart';
|
||||
// import 'package:fluffychat/pangea/instructions/instructions_inline_tooltip.dart';
|
||||
// import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart';
|
||||
// import 'package:fluffychat/pangea/learning_settings/widgets/p_language_dropdown.dart';
|
||||
// import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class ActivityGeneratorView extends StatelessWidget {
|
||||
final ActivityGeneratorState controller;
|
||||
// class ActivityGeneratorView extends StatelessWidget {
|
||||
// final ActivityGeneratorState controller;
|
||||
|
||||
const ActivityGeneratorView(
|
||||
this.controller, {
|
||||
super.key,
|
||||
});
|
||||
// const ActivityGeneratorView(
|
||||
// this.controller, {
|
||||
// super.key,
|
||||
// });
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = L10n.of(context);
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// final l10n = L10n.of(context);
|
||||
|
||||
if (controller.loading) {
|
||||
return SafeArea(
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(L10n.of(context).makeYourOwnActivity),
|
||||
),
|
||||
body: const Padding(
|
||||
padding: EdgeInsets.all(32.0),
|
||||
child: Center(child: CircularProgressIndicator()),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else if (controller.error != null || controller.room == null) {
|
||||
return SafeArea(
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(L10n.of(context).makeYourOwnActivity),
|
||||
),
|
||||
body: Center(
|
||||
child: Column(
|
||||
spacing: 16.0,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
ErrorIndicator(
|
||||
message: l10n.errorGenerateActivityMessage,
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: controller.generate,
|
||||
child: Text(l10n.tryAgain),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else if (controller.activities != null &&
|
||||
controller.activities!.isNotEmpty) {
|
||||
return SafeArea(
|
||||
child: ActivityPlannerBuilder(
|
||||
initialActivity: controller.activities!.first,
|
||||
initialFilename: controller.filename,
|
||||
room: controller.room!,
|
||||
builder: (c) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(L10n.of(context).makeYourOwnActivity),
|
||||
leading: BackButton(
|
||||
onPressed: () {
|
||||
c.isLaunching
|
||||
? c.setLaunchState(ActivityLaunchState.base)
|
||||
: controller.clearActivities();
|
||||
},
|
||||
),
|
||||
),
|
||||
body: ListView.builder(
|
||||
padding: const EdgeInsets.all(16),
|
||||
itemCount: controller.activities!.length,
|
||||
itemBuilder: (context, index) {
|
||||
return ActivityPlanCard(
|
||||
regenerate: () => controller.generate(force: true),
|
||||
controller: c,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
// if (controller.loading) {
|
||||
// return SafeArea(
|
||||
// child: Scaffold(
|
||||
// appBar: AppBar(
|
||||
// title: Text(L10n.of(context).makeYourOwnActivity),
|
||||
// ),
|
||||
// body: const Padding(
|
||||
// padding: EdgeInsets.all(32.0),
|
||||
// child: Center(child: CircularProgressIndicator()),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// } else if (controller.error != null || controller.room == null) {
|
||||
// return SafeArea(
|
||||
// child: Scaffold(
|
||||
// appBar: AppBar(
|
||||
// title: Text(L10n.of(context).makeYourOwnActivity),
|
||||
// ),
|
||||
// body: Center(
|
||||
// child: Column(
|
||||
// spacing: 16.0,
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
// children: [
|
||||
// ErrorIndicator(
|
||||
// message: l10n.errorGenerateActivityMessage,
|
||||
// ),
|
||||
// ElevatedButton(
|
||||
// onPressed: controller.generate,
|
||||
// child: Text(l10n.tryAgain),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// } else if (controller.activities != null &&
|
||||
// controller.activities!.isNotEmpty) {
|
||||
// return SafeArea(
|
||||
// child: ActivityPlannerBuilder(
|
||||
// initialActivity: controller.activities!.first,
|
||||
// initialFilename: controller.filename,
|
||||
// room: controller.room!,
|
||||
// builder: (c) {
|
||||
// return Scaffold(
|
||||
// appBar: AppBar(
|
||||
// title: Text(L10n.of(context).makeYourOwnActivity),
|
||||
// leading: BackButton(
|
||||
// onPressed: () {
|
||||
// c.isLaunching
|
||||
// ? c.setLaunchState(ActivityLaunchState.base)
|
||||
// : controller.clearActivities();
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
// body: ListView.builder(
|
||||
// padding: const EdgeInsets.all(16),
|
||||
// itemCount: controller.activities!.length,
|
||||
// itemBuilder: (context, index) {
|
||||
// return ActivityPlanCard(
|
||||
// regenerate: () => controller.generate(force: true),
|
||||
// controller: c,
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
|
||||
return SafeArea(
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(L10n.of(context).makeYourOwnActivity),
|
||||
leading: BackButton(
|
||||
onPressed: () {
|
||||
if (controller.activities != null &&
|
||||
controller.activities!.isNotEmpty) {
|
||||
controller.clearActivities();
|
||||
} else {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
body: Center(
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 600),
|
||||
child: Form(
|
||||
key: controller.formKey,
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
const InstructionsInlineTooltip(
|
||||
instructionsEnum: InstructionsEnum.activityPlannerOverview,
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
alignment: Alignment.center,
|
||||
child: ClipRRect(
|
||||
child: CachedNetworkImage(
|
||||
fit: BoxFit.cover,
|
||||
imageUrl:
|
||||
"${AppConfig.assetsBaseURL}/${ActivitySuggestionsConstants.makeActivityAssetPath}",
|
||||
placeholder: (context, url) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
},
|
||||
errorWidget: (context, url, error) => const SizedBox(),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16.0),
|
||||
PLanguageDropdown(
|
||||
languages:
|
||||
MatrixState.pangeaController.pLanguageStore.baseOptions,
|
||||
onChange: (val) => controller
|
||||
.setSelectedLanguageOfInstructions(val.langCode),
|
||||
initialLanguage:
|
||||
controller.selectedLanguageOfInstructions != null
|
||||
? PLanguageStore.byLangCode(
|
||||
controller.selectedLanguageOfInstructions!,
|
||||
)
|
||||
: MatrixState
|
||||
.pangeaController.languageController.userL1,
|
||||
isL2List: false,
|
||||
decorationText:
|
||||
L10n.of(context).languageOfInstructionsLabel,
|
||||
),
|
||||
const SizedBox(height: 16.0),
|
||||
PLanguageDropdown(
|
||||
languages: MatrixState
|
||||
.pangeaController.pLanguageStore.targetOptions,
|
||||
onChange: (val) =>
|
||||
controller.setSelectedTargetLanguage(val.langCode),
|
||||
initialLanguage: controller.selectedTargetLanguage != null
|
||||
? PLanguageStore.byLangCode(
|
||||
controller.selectedTargetLanguage!,
|
||||
)
|
||||
: MatrixState
|
||||
.pangeaController.languageController.userL2,
|
||||
decorationText: L10n.of(context).targetLanguageLabel,
|
||||
isL2List: true,
|
||||
),
|
||||
const SizedBox(height: 16.0),
|
||||
SuggestionFormField(
|
||||
suggestions: controller.topicItems,
|
||||
validator: controller.validateNotNull,
|
||||
maxLength: 50,
|
||||
label: l10n.topicLabel,
|
||||
placeholder: l10n.topicPlaceholder,
|
||||
controller: controller.topicController,
|
||||
),
|
||||
const SizedBox(height: 16.0),
|
||||
SuggestionFormField(
|
||||
suggestions: controller.objectiveItems,
|
||||
validator: controller.validateNotNull,
|
||||
maxLength: 140,
|
||||
label: l10n.learningObjectiveLabel,
|
||||
placeholder: l10n.learningObjectivePlaceholder,
|
||||
controller: controller.objectiveController,
|
||||
),
|
||||
const SizedBox(height: 16.0),
|
||||
SuggestionFormField(
|
||||
suggestions: controller.modeItems,
|
||||
validator: controller.validateNotNull,
|
||||
maxLength: 50,
|
||||
label: l10n.modeLabel,
|
||||
placeholder: l10n.modePlaceholder,
|
||||
controller: controller.modeController,
|
||||
),
|
||||
const SizedBox(height: 16.0),
|
||||
TextFormField(
|
||||
decoration: InputDecoration(
|
||||
labelText: l10n.numberOfLearners,
|
||||
),
|
||||
textInputAction: TextInputAction.done,
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return l10n.mustBeInteger;
|
||||
}
|
||||
final n = int.tryParse(value);
|
||||
if (n == null || n <= 0) {
|
||||
return l10n.mustBeInteger;
|
||||
}
|
||||
if (n > 50) {
|
||||
return l10n.maxFifty;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
onChanged: (val) => controller
|
||||
.setSelectedNumberOfParticipants(int.tryParse(val)),
|
||||
initialValue:
|
||||
controller.selectedNumberOfParticipants?.toString(),
|
||||
onTapOutside: (_) =>
|
||||
FocusManager.instance.primaryFocus?.unfocus(),
|
||||
onFieldSubmitted: (_) {
|
||||
if (controller.formKey.currentState?.validate() ??
|
||||
false) {
|
||||
controller.generate();
|
||||
}
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16.0),
|
||||
LanguageLevelDropdown(
|
||||
onChanged: controller.setSelectedCefrLevel,
|
||||
initialLevel: controller.selectedCefrLevel,
|
||||
),
|
||||
const SizedBox(height: 16.0),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: ElevatedButton(
|
||||
onPressed: controller.clearSelections,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Symbols.reset_focus),
|
||||
const SizedBox(width: 8),
|
||||
Text(L10n.of(context).clear),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: ElevatedButton(
|
||||
onPressed: controller.randomizeEnabled
|
||||
? controller.randomizeSelections
|
||||
: null,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.shuffle),
|
||||
const SizedBox(width: 8),
|
||||
Text(L10n.of(context).randomize),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 24.0),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
if (controller.formKey.currentState?.validate() ??
|
||||
false) {
|
||||
controller.generate();
|
||||
}
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.lightbulb_outline),
|
||||
const SizedBox(width: 8),
|
||||
Text(l10n.generateActivitiesButton),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
// return SafeArea(
|
||||
// child: Scaffold(
|
||||
// appBar: AppBar(
|
||||
// title: Text(L10n.of(context).makeYourOwnActivity),
|
||||
// leading: BackButton(
|
||||
// onPressed: () {
|
||||
// if (controller.activities != null &&
|
||||
// controller.activities!.isNotEmpty) {
|
||||
// controller.clearActivities();
|
||||
// } else {
|
||||
// Navigator.of(context).pop();
|
||||
// }
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
// body: Center(
|
||||
// child: ConstrainedBox(
|
||||
// constraints: const BoxConstraints(maxWidth: 600),
|
||||
// child: Form(
|
||||
// key: controller.formKey,
|
||||
// child: ListView(
|
||||
// padding: const EdgeInsets.all(16),
|
||||
// children: [
|
||||
// const InstructionsInlineTooltip(
|
||||
// instructionsEnum: InstructionsEnum.activityPlannerOverview,
|
||||
// ),
|
||||
// Container(
|
||||
// decoration: BoxDecoration(
|
||||
// borderRadius: BorderRadius.circular(12.0),
|
||||
// ),
|
||||
// clipBehavior: Clip.hardEdge,
|
||||
// alignment: Alignment.center,
|
||||
// child: ClipRRect(
|
||||
// child: CachedNetworkImage(
|
||||
// fit: BoxFit.cover,
|
||||
// imageUrl:
|
||||
// "${AppConfig.assetsBaseURL}/${ActivitySuggestionsConstants.makeActivityAssetPath}",
|
||||
// placeholder: (context, url) {
|
||||
// return const Center(
|
||||
// child: CircularProgressIndicator(),
|
||||
// );
|
||||
// },
|
||||
// errorWidget: (context, url, error) => const SizedBox(),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 16.0),
|
||||
// PLanguageDropdown(
|
||||
// languages:
|
||||
// MatrixState.pangeaController.pLanguageStore.baseOptions,
|
||||
// onChange: (val) => controller
|
||||
// .setSelectedLanguageOfInstructions(val.langCode),
|
||||
// initialLanguage:
|
||||
// controller.selectedLanguageOfInstructions != null
|
||||
// ? PLanguageStore.byLangCode(
|
||||
// controller.selectedLanguageOfInstructions!,
|
||||
// )
|
||||
// : MatrixState
|
||||
// .pangeaController.languageController.userL1,
|
||||
// isL2List: false,
|
||||
// decorationText:
|
||||
// L10n.of(context).languageOfInstructionsLabel,
|
||||
// ),
|
||||
// const SizedBox(height: 16.0),
|
||||
// PLanguageDropdown(
|
||||
// languages: MatrixState
|
||||
// .pangeaController.pLanguageStore.targetOptions,
|
||||
// onChange: (val) =>
|
||||
// controller.setSelectedTargetLanguage(val.langCode),
|
||||
// initialLanguage: controller.selectedTargetLanguage != null
|
||||
// ? PLanguageStore.byLangCode(
|
||||
// controller.selectedTargetLanguage!,
|
||||
// )
|
||||
// : MatrixState
|
||||
// .pangeaController.languageController.userL2,
|
||||
// decorationText: L10n.of(context).targetLanguageLabel,
|
||||
// isL2List: true,
|
||||
// ),
|
||||
// const SizedBox(height: 16.0),
|
||||
// SuggestionFormField(
|
||||
// suggestions: controller.topicItems,
|
||||
// validator: controller.validateNotNull,
|
||||
// maxLength: 50,
|
||||
// label: l10n.topicLabel,
|
||||
// placeholder: l10n.topicPlaceholder,
|
||||
// controller: controller.topicController,
|
||||
// ),
|
||||
// const SizedBox(height: 16.0),
|
||||
// SuggestionFormField(
|
||||
// suggestions: controller.objectiveItems,
|
||||
// validator: controller.validateNotNull,
|
||||
// maxLength: 140,
|
||||
// label: l10n.learningObjectiveLabel,
|
||||
// placeholder: l10n.learningObjectivePlaceholder,
|
||||
// controller: controller.objectiveController,
|
||||
// ),
|
||||
// const SizedBox(height: 16.0),
|
||||
// SuggestionFormField(
|
||||
// suggestions: controller.modeItems,
|
||||
// validator: controller.validateNotNull,
|
||||
// maxLength: 50,
|
||||
// label: l10n.modeLabel,
|
||||
// placeholder: l10n.modePlaceholder,
|
||||
// controller: controller.modeController,
|
||||
// ),
|
||||
// const SizedBox(height: 16.0),
|
||||
// TextFormField(
|
||||
// decoration: InputDecoration(
|
||||
// labelText: l10n.numberOfLearners,
|
||||
// ),
|
||||
// textInputAction: TextInputAction.done,
|
||||
// validator: (value) {
|
||||
// if (value == null || value.isEmpty) {
|
||||
// return l10n.mustBeInteger;
|
||||
// }
|
||||
// final n = int.tryParse(value);
|
||||
// if (n == null || n <= 0) {
|
||||
// return l10n.mustBeInteger;
|
||||
// }
|
||||
// if (n > 50) {
|
||||
// return l10n.maxFifty;
|
||||
// }
|
||||
// return null;
|
||||
// },
|
||||
// onChanged: (val) => controller
|
||||
// .setSelectedNumberOfParticipants(int.tryParse(val)),
|
||||
// initialValue:
|
||||
// controller.selectedNumberOfParticipants?.toString(),
|
||||
// onTapOutside: (_) =>
|
||||
// FocusManager.instance.primaryFocus?.unfocus(),
|
||||
// onFieldSubmitted: (_) {
|
||||
// if (controller.formKey.currentState?.validate() ??
|
||||
// false) {
|
||||
// controller.generate();
|
||||
// }
|
||||
// },
|
||||
// ),
|
||||
// const SizedBox(height: 16.0),
|
||||
// LanguageLevelDropdown(
|
||||
// onChanged: controller.setSelectedCefrLevel,
|
||||
// initialLevel: controller.selectedCefrLevel,
|
||||
// ),
|
||||
// const SizedBox(height: 16.0),
|
||||
// Row(
|
||||
// children: [
|
||||
// Expanded(
|
||||
// child: Padding(
|
||||
// padding: const EdgeInsets.only(right: 8.0),
|
||||
// child: ElevatedButton(
|
||||
// onPressed: controller.clearSelections,
|
||||
// child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
// children: [
|
||||
// const Icon(Symbols.reset_focus),
|
||||
// const SizedBox(width: 8),
|
||||
// Text(L10n.of(context).clear),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// Expanded(
|
||||
// child: Padding(
|
||||
// padding: const EdgeInsets.only(left: 8.0),
|
||||
// child: ElevatedButton(
|
||||
// onPressed: controller.randomizeEnabled
|
||||
// ? controller.randomizeSelections
|
||||
// : null,
|
||||
// child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
// children: [
|
||||
// const Icon(Icons.shuffle),
|
||||
// const SizedBox(width: 8),
|
||||
// Text(L10n.of(context).randomize),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// const SizedBox(height: 24.0),
|
||||
// ElevatedButton(
|
||||
// onPressed: () {
|
||||
// if (controller.formKey.currentState?.validate() ??
|
||||
// false) {
|
||||
// controller.generate();
|
||||
// }
|
||||
// },
|
||||
// child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
// children: [
|
||||
// const Icon(Icons.lightbulb_outline),
|
||||
// const SizedBox(width: 8),
|
||||
// Text(l10n.generateActivitiesButton),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,56 +1,56 @@
|
|||
import 'dart:convert';
|
||||
// import 'dart:convert';
|
||||
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:http/http.dart';
|
||||
// import 'package:get_storage/get_storage.dart';
|
||||
// import 'package:http/http.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/activity_generator/list_request_schema.dart';
|
||||
import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
import 'package:fluffychat/pangea/common/network/urls.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import '../common/network/requests.dart';
|
||||
// import 'package:fluffychat/pangea/activity_generator/list_request_schema.dart';
|
||||
// import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
// import 'package:fluffychat/pangea/common/network/urls.dart';
|
||||
// import 'package:fluffychat/widgets/matrix.dart';
|
||||
// import '../common/network/requests.dart';
|
||||
|
||||
class ActivityModeListRepo {
|
||||
static final GetStorage _modeListStorage = GetStorage('mode_list_storage');
|
||||
// class ActivityModeListRepo {
|
||||
// static final GetStorage _modeListStorage = GetStorage('mode_list_storage');
|
||||
|
||||
static void set(
|
||||
ActivitySettingRequestSchema request,
|
||||
List<ActivitySettingResponseSchema> response,
|
||||
) {
|
||||
_modeListStorage.write(
|
||||
request.storageKey,
|
||||
response.map((e) => e.toJson()).toList(),
|
||||
);
|
||||
}
|
||||
// static void set(
|
||||
// ActivitySettingRequestSchema request,
|
||||
// List<ActivitySettingResponseSchema> response,
|
||||
// ) {
|
||||
// _modeListStorage.write(
|
||||
// request.storageKey,
|
||||
// response.map((e) => e.toJson()).toList(),
|
||||
// );
|
||||
// }
|
||||
|
||||
static List<ActivitySettingResponseSchema> fromJson(Iterable json) {
|
||||
return List<ActivitySettingResponseSchema>.from(
|
||||
json.map((x) => ActivitySettingResponseSchema.fromJson(x)),
|
||||
);
|
||||
}
|
||||
// static List<ActivitySettingResponseSchema> fromJson(Iterable json) {
|
||||
// return List<ActivitySettingResponseSchema>.from(
|
||||
// json.map((x) => ActivitySettingResponseSchema.fromJson(x)),
|
||||
// );
|
||||
// }
|
||||
|
||||
static Future<List<ActivitySettingResponseSchema>> get(
|
||||
ActivitySettingRequestSchema request,
|
||||
) async {
|
||||
final cachedJson = _modeListStorage.read(request.storageKey);
|
||||
if (cachedJson != null) {
|
||||
return ActivityModeListRepo.fromJson(cachedJson);
|
||||
}
|
||||
// static Future<List<ActivitySettingResponseSchema>> get(
|
||||
// ActivitySettingRequestSchema request,
|
||||
// ) async {
|
||||
// final cachedJson = _modeListStorage.read(request.storageKey);
|
||||
// if (cachedJson != null) {
|
||||
// return ActivityModeListRepo.fromJson(cachedJson);
|
||||
// }
|
||||
|
||||
final Requests req = Requests(
|
||||
choreoApiKey: Environment.choreoApiKey,
|
||||
accessToken: MatrixState.pangeaController.userController.accessToken,
|
||||
);
|
||||
// final Requests req = Requests(
|
||||
// choreoApiKey: Environment.choreoApiKey,
|
||||
// accessToken: MatrixState.pangeaController.userController.accessToken,
|
||||
// );
|
||||
|
||||
final Response res = await req.post(
|
||||
url: PApiUrls.activityModeList,
|
||||
body: request.toJson(),
|
||||
);
|
||||
// final Response res = await req.post(
|
||||
// url: PApiUrls.activityModeList,
|
||||
// body: request.toJson(),
|
||||
// );
|
||||
|
||||
final decodedBody = jsonDecode(utf8.decode(res.bodyBytes));
|
||||
final response = ActivityModeListRepo.fromJson(decodedBody);
|
||||
// final decodedBody = jsonDecode(utf8.decode(res.bodyBytes));
|
||||
// final response = ActivityModeListRepo.fromJson(decodedBody);
|
||||
|
||||
set(request, response);
|
||||
// set(request, response);
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
// return response;
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,49 +1,49 @@
|
|||
import 'dart:convert';
|
||||
// import 'dart:convert';
|
||||
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:http/http.dart';
|
||||
// import 'package:get_storage/get_storage.dart';
|
||||
// import 'package:http/http.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_plan_request.dart';
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_plan_response.dart';
|
||||
import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
import 'package:fluffychat/pangea/common/network/urls.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import '../common/network/requests.dart';
|
||||
// import 'package:fluffychat/pangea/activity_planner/activity_plan_request.dart';
|
||||
// import 'package:fluffychat/pangea/activity_planner/activity_plan_response.dart';
|
||||
// import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
// import 'package:fluffychat/pangea/common/network/urls.dart';
|
||||
// import 'package:fluffychat/widgets/matrix.dart';
|
||||
// import '../common/network/requests.dart';
|
||||
|
||||
class ActivityPlanGenerationRepo {
|
||||
static final GetStorage _activityPlanStorage =
|
||||
GetStorage('activity_plan_storage');
|
||||
// class ActivityPlanGenerationRepo {
|
||||
// static final GetStorage _activityPlanStorage =
|
||||
// GetStorage('activity_plan_storage');
|
||||
|
||||
static void set(ActivityPlanRequest request, ActivityPlanResponse response) {
|
||||
_activityPlanStorage.write(request.storageKey, response.toJson());
|
||||
}
|
||||
// static void set(ActivityPlanRequest request, ActivityPlanResponse response) {
|
||||
// _activityPlanStorage.write(request.storageKey, response.toJson());
|
||||
// }
|
||||
|
||||
static Future<ActivityPlanResponse> get(
|
||||
ActivityPlanRequest request, {
|
||||
bool force = false,
|
||||
}) async {
|
||||
final cachedJson = _activityPlanStorage.read(request.storageKey);
|
||||
if (cachedJson != null && !force) {
|
||||
final cached = ActivityPlanResponse.fromJson(cachedJson);
|
||||
// static Future<ActivityPlanResponse> get(
|
||||
// ActivityPlanRequest request, {
|
||||
// bool force = false,
|
||||
// }) async {
|
||||
// final cachedJson = _activityPlanStorage.read(request.storageKey);
|
||||
// if (cachedJson != null && !force) {
|
||||
// final cached = ActivityPlanResponse.fromJson(cachedJson);
|
||||
|
||||
return cached;
|
||||
}
|
||||
// return cached;
|
||||
// }
|
||||
|
||||
final Requests req = Requests(
|
||||
choreoApiKey: Environment.choreoApiKey,
|
||||
accessToken: MatrixState.pangeaController.userController.accessToken,
|
||||
);
|
||||
// final Requests req = Requests(
|
||||
// choreoApiKey: Environment.choreoApiKey,
|
||||
// accessToken: MatrixState.pangeaController.userController.accessToken,
|
||||
// );
|
||||
|
||||
final Response res = await req.post(
|
||||
url: PApiUrls.activityPlanGeneration,
|
||||
body: request.toJson(),
|
||||
);
|
||||
// final Response res = await req.post(
|
||||
// url: PApiUrls.activityPlanGeneration,
|
||||
// body: request.toJson(),
|
||||
// );
|
||||
|
||||
final decodedBody = jsonDecode(utf8.decode(res.bodyBytes));
|
||||
final response = ActivityPlanResponse.fromJson(decodedBody);
|
||||
// final decodedBody = jsonDecode(utf8.decode(res.bodyBytes));
|
||||
// final response = ActivityPlanResponse.fromJson(decodedBody);
|
||||
|
||||
set(request, response);
|
||||
// set(request, response);
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
// return response;
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,57 +1,57 @@
|
|||
import 'dart:convert';
|
||||
// import 'dart:convert';
|
||||
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:http/http.dart';
|
||||
// import 'package:get_storage/get_storage.dart';
|
||||
// import 'package:http/http.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/activity_generator/list_request_schema.dart';
|
||||
import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
import 'package:fluffychat/pangea/common/network/urls.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import '../common/network/requests.dart';
|
||||
// import 'package:fluffychat/pangea/activity_generator/list_request_schema.dart';
|
||||
// import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
// import 'package:fluffychat/pangea/common/network/urls.dart';
|
||||
// import 'package:fluffychat/widgets/matrix.dart';
|
||||
// import '../common/network/requests.dart';
|
||||
|
||||
class LearningObjectiveListRepo {
|
||||
static final GetStorage _objectiveListStorage =
|
||||
GetStorage('objective_list_storage');
|
||||
// class LearningObjectiveListRepo {
|
||||
// static final GetStorage _objectiveListStorage =
|
||||
// GetStorage('objective_list_storage');
|
||||
|
||||
static void set(
|
||||
ActivitySettingRequestSchema request,
|
||||
List<ActivitySettingResponseSchema> response,
|
||||
) {
|
||||
_objectiveListStorage.write(
|
||||
request.storageKey,
|
||||
response.map((e) => e.toJson()).toList(),
|
||||
);
|
||||
}
|
||||
// static void set(
|
||||
// ActivitySettingRequestSchema request,
|
||||
// List<ActivitySettingResponseSchema> response,
|
||||
// ) {
|
||||
// _objectiveListStorage.write(
|
||||
// request.storageKey,
|
||||
// response.map((e) => e.toJson()).toList(),
|
||||
// );
|
||||
// }
|
||||
|
||||
static List<ActivitySettingResponseSchema> fromJson(Iterable json) {
|
||||
return List<ActivitySettingResponseSchema>.from(
|
||||
json.map((x) => ActivitySettingResponseSchema.fromJson(x)),
|
||||
);
|
||||
}
|
||||
// static List<ActivitySettingResponseSchema> fromJson(Iterable json) {
|
||||
// return List<ActivitySettingResponseSchema>.from(
|
||||
// json.map((x) => ActivitySettingResponseSchema.fromJson(x)),
|
||||
// );
|
||||
// }
|
||||
|
||||
static Future<List<ActivitySettingResponseSchema>> get(
|
||||
ActivitySettingRequestSchema request,
|
||||
) async {
|
||||
final cachedJson = _objectiveListStorage.read(request.storageKey);
|
||||
if (cachedJson != null) {
|
||||
return LearningObjectiveListRepo.fromJson(cachedJson);
|
||||
}
|
||||
// static Future<List<ActivitySettingResponseSchema>> get(
|
||||
// ActivitySettingRequestSchema request,
|
||||
// ) async {
|
||||
// final cachedJson = _objectiveListStorage.read(request.storageKey);
|
||||
// if (cachedJson != null) {
|
||||
// return LearningObjectiveListRepo.fromJson(cachedJson);
|
||||
// }
|
||||
|
||||
final Requests req = Requests(
|
||||
choreoApiKey: Environment.choreoApiKey,
|
||||
accessToken: MatrixState.pangeaController.userController.accessToken,
|
||||
);
|
||||
// final Requests req = Requests(
|
||||
// choreoApiKey: Environment.choreoApiKey,
|
||||
// accessToken: MatrixState.pangeaController.userController.accessToken,
|
||||
// );
|
||||
|
||||
final Response res = await req.post(
|
||||
url: PApiUrls.objectiveList,
|
||||
body: request.toJson(),
|
||||
);
|
||||
// final Response res = await req.post(
|
||||
// url: PApiUrls.objectiveList,
|
||||
// body: request.toJson(),
|
||||
// );
|
||||
|
||||
final decodedBody = jsonDecode(utf8.decode(res.bodyBytes));
|
||||
final response = LearningObjectiveListRepo.fromJson(decodedBody);
|
||||
// final decodedBody = jsonDecode(utf8.decode(res.bodyBytes));
|
||||
// final response = LearningObjectiveListRepo.fromJson(decodedBody);
|
||||
|
||||
set(request, response);
|
||||
// set(request, response);
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
// return response;
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,37 +1,37 @@
|
|||
class ActivitySettingRequestSchema {
|
||||
final String langCode;
|
||||
// class ActivitySettingRequestSchema {
|
||||
// final String langCode;
|
||||
|
||||
ActivitySettingRequestSchema({required this.langCode});
|
||||
// ActivitySettingRequestSchema({required this.langCode});
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'lang_code': langCode,
|
||||
};
|
||||
}
|
||||
// Map<String, dynamic> toJson() {
|
||||
// return {
|
||||
// 'lang_code': langCode,
|
||||
// };
|
||||
// }
|
||||
|
||||
String get storageKey => 'topic_list-$langCode';
|
||||
}
|
||||
// String get storageKey => 'topic_list-$langCode';
|
||||
// }
|
||||
|
||||
class ActivitySettingResponseSchema {
|
||||
final String defaultName;
|
||||
final String name;
|
||||
// class ActivitySettingResponseSchema {
|
||||
// final String defaultName;
|
||||
// final String name;
|
||||
|
||||
ActivitySettingResponseSchema({
|
||||
required this.defaultName,
|
||||
required this.name,
|
||||
});
|
||||
// ActivitySettingResponseSchema({
|
||||
// required this.defaultName,
|
||||
// required this.name,
|
||||
// });
|
||||
|
||||
factory ActivitySettingResponseSchema.fromJson(Map<String, dynamic> json) {
|
||||
return ActivitySettingResponseSchema(
|
||||
defaultName: json['default_name'],
|
||||
name: json['name'],
|
||||
);
|
||||
}
|
||||
// factory ActivitySettingResponseSchema.fromJson(Map<String, dynamic> json) {
|
||||
// return ActivitySettingResponseSchema(
|
||||
// defaultName: json['default_name'],
|
||||
// name: json['name'],
|
||||
// );
|
||||
// }
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'default_name': defaultName,
|
||||
'name': name,
|
||||
};
|
||||
}
|
||||
}
|
||||
// Map<String, dynamic> toJson() {
|
||||
// return {
|
||||
// 'default_name': defaultName,
|
||||
// 'name': name,
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,56 +1,56 @@
|
|||
import 'dart:convert';
|
||||
// import 'dart:convert';
|
||||
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:http/http.dart';
|
||||
// import 'package:get_storage/get_storage.dart';
|
||||
// import 'package:http/http.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/activity_generator/list_request_schema.dart';
|
||||
import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
import 'package:fluffychat/pangea/common/network/urls.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import '../common/network/requests.dart';
|
||||
// import 'package:fluffychat/pangea/activity_generator/list_request_schema.dart';
|
||||
// import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
// import 'package:fluffychat/pangea/common/network/urls.dart';
|
||||
// import 'package:fluffychat/widgets/matrix.dart';
|
||||
// import '../common/network/requests.dart';
|
||||
|
||||
class TopicListRepo {
|
||||
static final GetStorage _topicListStorage = GetStorage('topic_list_storage');
|
||||
// class TopicListRepo {
|
||||
// static final GetStorage _topicListStorage = GetStorage('topic_list_storage');
|
||||
|
||||
static void set(
|
||||
ActivitySettingRequestSchema request,
|
||||
List<ActivitySettingResponseSchema> response,
|
||||
) {
|
||||
_topicListStorage.write(
|
||||
request.storageKey,
|
||||
response.map((e) => e.toJson()).toList(),
|
||||
);
|
||||
}
|
||||
// static void set(
|
||||
// ActivitySettingRequestSchema request,
|
||||
// List<ActivitySettingResponseSchema> response,
|
||||
// ) {
|
||||
// _topicListStorage.write(
|
||||
// request.storageKey,
|
||||
// response.map((e) => e.toJson()).toList(),
|
||||
// );
|
||||
// }
|
||||
|
||||
static List<ActivitySettingResponseSchema> fromJson(Iterable json) {
|
||||
return List<ActivitySettingResponseSchema>.from(
|
||||
json.map((x) => ActivitySettingResponseSchema.fromJson(x)),
|
||||
);
|
||||
}
|
||||
// static List<ActivitySettingResponseSchema> fromJson(Iterable json) {
|
||||
// return List<ActivitySettingResponseSchema>.from(
|
||||
// json.map((x) => ActivitySettingResponseSchema.fromJson(x)),
|
||||
// );
|
||||
// }
|
||||
|
||||
static Future<List<ActivitySettingResponseSchema>> get(
|
||||
ActivitySettingRequestSchema request,
|
||||
) async {
|
||||
final cachedJson = _topicListStorage.read(request.storageKey);
|
||||
if (cachedJson != null) {
|
||||
return TopicListRepo.fromJson(cachedJson);
|
||||
}
|
||||
// static Future<List<ActivitySettingResponseSchema>> get(
|
||||
// ActivitySettingRequestSchema request,
|
||||
// ) async {
|
||||
// final cachedJson = _topicListStorage.read(request.storageKey);
|
||||
// if (cachedJson != null) {
|
||||
// return TopicListRepo.fromJson(cachedJson);
|
||||
// }
|
||||
|
||||
final Requests req = Requests(
|
||||
choreoApiKey: Environment.choreoApiKey,
|
||||
accessToken: MatrixState.pangeaController.userController.accessToken,
|
||||
);
|
||||
// final Requests req = Requests(
|
||||
// choreoApiKey: Environment.choreoApiKey,
|
||||
// accessToken: MatrixState.pangeaController.userController.accessToken,
|
||||
// );
|
||||
|
||||
final Response res = await req.post(
|
||||
url: PApiUrls.topicList,
|
||||
body: request.toJson(),
|
||||
);
|
||||
// final Response res = await req.post(
|
||||
// url: PApiUrls.topicList,
|
||||
// body: request.toJson(),
|
||||
// );
|
||||
|
||||
final decodedBody = jsonDecode(utf8.decode(res.bodyBytes));
|
||||
final response = TopicListRepo.fromJson(decodedBody);
|
||||
// final decodedBody = jsonDecode(utf8.decode(res.bodyBytes));
|
||||
// final response = TopicListRepo.fromJson(decodedBody);
|
||||
|
||||
set(request, response);
|
||||
// set(request, response);
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
// return response;
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,286 +1,286 @@
|
|||
import 'package:flutter/material.dart';
|
||||
// import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:swipe_to_action/swipe_to_action.dart';
|
||||
// import 'package:matrix/matrix.dart';
|
||||
// import 'package:swipe_to_action/swipe_to_action.dart';
|
||||
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pages/chat/chat.dart';
|
||||
import 'package:fluffychat/pages/chat/events/message_content.dart';
|
||||
import 'package:fluffychat/pages/chat/events/pangea_message_reactions.dart';
|
||||
import 'package:fluffychat/utils/date_time_extension.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import '../../../config/app_config.dart';
|
||||
// import 'package:fluffychat/config/themes.dart';
|
||||
// import 'package:fluffychat/pages/chat/chat.dart';
|
||||
// import 'package:fluffychat/pages/chat/events/message_content.dart';
|
||||
// import 'package:fluffychat/pages/chat/events/pangea_message_reactions.dart';
|
||||
// import 'package:fluffychat/utils/date_time_extension.dart';
|
||||
// import 'package:fluffychat/widgets/matrix.dart';
|
||||
// import '../../../config/app_config.dart';
|
||||
|
||||
class ActivityPlanMessage extends StatelessWidget {
|
||||
final Event event;
|
||||
final Timeline timeline;
|
||||
final bool animateIn;
|
||||
final void Function()? resetAnimateIn;
|
||||
final ChatController controller;
|
||||
final bool highlightMarker;
|
||||
final bool selected;
|
||||
// class ActivityPlanMessage extends StatelessWidget {
|
||||
// final Event event;
|
||||
// final Timeline timeline;
|
||||
// final bool animateIn;
|
||||
// final void Function()? resetAnimateIn;
|
||||
// final ChatController controller;
|
||||
// final bool highlightMarker;
|
||||
// final bool selected;
|
||||
|
||||
const ActivityPlanMessage(
|
||||
this.event, {
|
||||
required this.timeline,
|
||||
required this.controller,
|
||||
required this.selected,
|
||||
this.animateIn = false,
|
||||
this.resetAnimateIn,
|
||||
this.highlightMarker = false,
|
||||
super.key,
|
||||
});
|
||||
// const ActivityPlanMessage(
|
||||
// this.event, {
|
||||
// required this.timeline,
|
||||
// required this.controller,
|
||||
// required this.selected,
|
||||
// this.animateIn = false,
|
||||
// this.resetAnimateIn,
|
||||
// this.highlightMarker = false,
|
||||
// super.key,
|
||||
// });
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (controller.pangeaEditingEvent?.eventId == event.eventId) {
|
||||
controller.clearEditingEvent();
|
||||
}
|
||||
});
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// if (controller.pangeaEditingEvent?.eventId == event.eventId) {
|
||||
// controller.clearEditingEvent();
|
||||
// }
|
||||
// });
|
||||
|
||||
final theme = Theme.of(context);
|
||||
final color = theme.brightness == Brightness.dark
|
||||
? theme.colorScheme.onSecondary
|
||||
: theme.colorScheme.primary;
|
||||
final textColor = ThemeData.light().colorScheme.onPrimary;
|
||||
// final theme = Theme.of(context);
|
||||
// final color = theme.brightness == Brightness.dark
|
||||
// ? theme.colorScheme.onSecondary
|
||||
// : theme.colorScheme.primary;
|
||||
// final textColor = ThemeData.light().colorScheme.onPrimary;
|
||||
|
||||
final displayEvent = event.getDisplayEvent(timeline);
|
||||
const roundedCorner = Radius.circular(AppConfig.borderRadius);
|
||||
const borderRadius = BorderRadius.all(roundedCorner);
|
||||
// final displayEvent = event.getDisplayEvent(timeline);
|
||||
// const roundedCorner = Radius.circular(AppConfig.borderRadius);
|
||||
// const borderRadius = BorderRadius.all(roundedCorner);
|
||||
|
||||
final resetAnimateIn = this.resetAnimateIn;
|
||||
var animateIn = this.animateIn;
|
||||
// final resetAnimateIn = this.resetAnimateIn;
|
||||
// var animateIn = this.animateIn;
|
||||
|
||||
final row = StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
if (animateIn && resetAnimateIn != null) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
animateIn = false;
|
||||
if (context.mounted) {
|
||||
setState(resetAnimateIn);
|
||||
}
|
||||
});
|
||||
}
|
||||
return AnimatedSize(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
clipBehavior: Clip.none,
|
||||
alignment: Alignment.bottomLeft,
|
||||
child: animateIn
|
||||
? const SizedBox(height: 0, width: double.infinity)
|
||||
: Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: InkWell(
|
||||
onTap: () => controller.showToolbar(event),
|
||||
onLongPress: () => controller.showToolbar(event),
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius / 2),
|
||||
child: Material(
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius / 2),
|
||||
color: highlightMarker
|
||||
? theme.colorScheme.secondaryContainer
|
||||
.withAlpha(128)
|
||||
: Colors.transparent,
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
child: GestureDetector(
|
||||
onTap: () => controller.showToolbar(event),
|
||||
onLongPress: () => controller.showToolbar(event),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
AnimatedOpacity(
|
||||
opacity: animateIn
|
||||
? 0
|
||||
: event.messageType ==
|
||||
MessageTypes.BadEncrypted ||
|
||||
event.status.isSending
|
||||
? 0.5
|
||||
: 1,
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
borderRadius: borderRadius,
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: CompositedTransformTarget(
|
||||
link: MatrixState.pAnyState
|
||||
.layerLinkAndKey(
|
||||
event.eventId,
|
||||
)
|
||||
.link,
|
||||
child: Container(
|
||||
key: MatrixState.pAnyState
|
||||
.layerLinkAndKey(
|
||||
event.eventId,
|
||||
)
|
||||
.key,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(
|
||||
AppConfig.borderRadius,
|
||||
),
|
||||
),
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: FluffyThemes.columnWidth * 1.5,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
MessageContent(
|
||||
displayEvent,
|
||||
textColor: textColor,
|
||||
borderRadius: borderRadius,
|
||||
controller: controller,
|
||||
immersionMode: false,
|
||||
timeline: timeline,
|
||||
linkColor: theme.brightness ==
|
||||
Brightness.light
|
||||
? theme.colorScheme.primary
|
||||
: theme.colorScheme.onPrimary,
|
||||
selected: selected,
|
||||
),
|
||||
if (event.hasAggregatedEvents(
|
||||
timeline,
|
||||
RelationshipTypes.edit,
|
||||
))
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 4.0,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (event.hasAggregatedEvents(
|
||||
timeline,
|
||||
RelationshipTypes.edit,
|
||||
)) ...[
|
||||
Icon(
|
||||
Icons.edit_outlined,
|
||||
color: textColor
|
||||
.withAlpha(164),
|
||||
size: 14,
|
||||
),
|
||||
Text(
|
||||
' - ${displayEvent.originServerTs.localizedTimeShort(context)}',
|
||||
style: TextStyle(
|
||||
color:
|
||||
textColor.withAlpha(
|
||||
164,
|
||||
),
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 4.0,
|
||||
right: 4.0,
|
||||
),
|
||||
child: PangeaMessageReactions(
|
||||
event,
|
||||
timeline,
|
||||
controller,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
// final row = StatefulBuilder(
|
||||
// builder: (context, setState) {
|
||||
// if (animateIn && resetAnimateIn != null) {
|
||||
// WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
// animateIn = false;
|
||||
// if (context.mounted) {
|
||||
// setState(resetAnimateIn);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// return AnimatedSize(
|
||||
// duration: FluffyThemes.animationDuration,
|
||||
// curve: FluffyThemes.animationCurve,
|
||||
// clipBehavior: Clip.none,
|
||||
// alignment: Alignment.bottomLeft,
|
||||
// child: animateIn
|
||||
// ? const SizedBox(height: 0, width: double.infinity)
|
||||
// : Stack(
|
||||
// children: [
|
||||
// Positioned(
|
||||
// top: 0,
|
||||
// bottom: 0,
|
||||
// left: 0,
|
||||
// right: 0,
|
||||
// child: InkWell(
|
||||
// onTap: () => controller.showToolbar(event),
|
||||
// onLongPress: () => controller.showToolbar(event),
|
||||
// borderRadius:
|
||||
// BorderRadius.circular(AppConfig.borderRadius / 2),
|
||||
// child: Material(
|
||||
// borderRadius:
|
||||
// BorderRadius.circular(AppConfig.borderRadius / 2),
|
||||
// color: highlightMarker
|
||||
// ? theme.colorScheme.secondaryContainer
|
||||
// .withAlpha(128)
|
||||
// : Colors.transparent,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// Container(
|
||||
// alignment: Alignment.center,
|
||||
// child: GestureDetector(
|
||||
// onTap: () => controller.showToolbar(event),
|
||||
// onLongPress: () => controller.showToolbar(event),
|
||||
// child: Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.end,
|
||||
// children: [
|
||||
// AnimatedOpacity(
|
||||
// opacity: animateIn
|
||||
// ? 0
|
||||
// : event.messageType ==
|
||||
// MessageTypes.BadEncrypted ||
|
||||
// event.status.isSending
|
||||
// ? 0.5
|
||||
// : 1,
|
||||
// duration: FluffyThemes.animationDuration,
|
||||
// curve: FluffyThemes.animationCurve,
|
||||
// child: Container(
|
||||
// decoration: BoxDecoration(
|
||||
// color: color,
|
||||
// borderRadius: borderRadius,
|
||||
// ),
|
||||
// clipBehavior: Clip.antiAlias,
|
||||
// child: CompositedTransformTarget(
|
||||
// link: MatrixState.pAnyState
|
||||
// .layerLinkAndKey(
|
||||
// event.eventId,
|
||||
// )
|
||||
// .link,
|
||||
// child: Container(
|
||||
// key: MatrixState.pAnyState
|
||||
// .layerLinkAndKey(
|
||||
// event.eventId,
|
||||
// )
|
||||
// .key,
|
||||
// decoration: BoxDecoration(
|
||||
// borderRadius: BorderRadius.circular(
|
||||
// AppConfig.borderRadius,
|
||||
// ),
|
||||
// ),
|
||||
// constraints: const BoxConstraints(
|
||||
// maxWidth: FluffyThemes.columnWidth * 1.5,
|
||||
// ),
|
||||
// child: Column(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// crossAxisAlignment:
|
||||
// CrossAxisAlignment.start,
|
||||
// children: <Widget>[
|
||||
// MessageContent(
|
||||
// displayEvent,
|
||||
// textColor: textColor,
|
||||
// borderRadius: borderRadius,
|
||||
// controller: controller,
|
||||
// immersionMode: false,
|
||||
// timeline: timeline,
|
||||
// linkColor: theme.brightness ==
|
||||
// Brightness.light
|
||||
// ? theme.colorScheme.primary
|
||||
// : theme.colorScheme.onPrimary,
|
||||
// selected: selected,
|
||||
// ),
|
||||
// if (event.hasAggregatedEvents(
|
||||
// timeline,
|
||||
// RelationshipTypes.edit,
|
||||
// ))
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.only(
|
||||
// top: 4.0,
|
||||
// ),
|
||||
// child: Row(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// if (event.hasAggregatedEvents(
|
||||
// timeline,
|
||||
// RelationshipTypes.edit,
|
||||
// )) ...[
|
||||
// Icon(
|
||||
// Icons.edit_outlined,
|
||||
// color: textColor
|
||||
// .withAlpha(164),
|
||||
// size: 14,
|
||||
// ),
|
||||
// Text(
|
||||
// ' - ${displayEvent.originServerTs.localizedTimeShort(context)}',
|
||||
// style: TextStyle(
|
||||
// color:
|
||||
// textColor.withAlpha(
|
||||
// 164,
|
||||
// ),
|
||||
// fontSize: 12,
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.only(
|
||||
// top: 4.0,
|
||||
// right: 4.0,
|
||||
// ),
|
||||
// child: PangeaMessageReactions(
|
||||
// event,
|
||||
// timeline,
|
||||
// controller,
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
// );
|
||||
|
||||
Widget container;
|
||||
// Widget container;
|
||||
|
||||
container = Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
if (event.messageType == MessageTypes.Text)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 4.0),
|
||||
child: Material(
|
||||
borderRadius:
|
||||
BorderRadius.circular(AppConfig.borderRadius * 2),
|
||||
color: theme.colorScheme.surface.withAlpha(128),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8.0,
|
||||
vertical: 2.0,
|
||||
),
|
||||
child: Text(
|
||||
event.originServerTs.localizedTime(context),
|
||||
style: TextStyle(
|
||||
fontSize: 12 * AppConfig.fontSizeFactor,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: theme.colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
row,
|
||||
],
|
||||
);
|
||||
// container = Column(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: <Widget>[
|
||||
// if (event.messageType == MessageTypes.Text)
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
// child: Center(
|
||||
// child: Padding(
|
||||
// padding: const EdgeInsets.only(top: 4.0),
|
||||
// child: Material(
|
||||
// borderRadius:
|
||||
// BorderRadius.circular(AppConfig.borderRadius * 2),
|
||||
// color: theme.colorScheme.surface.withAlpha(128),
|
||||
// child: Padding(
|
||||
// padding: const EdgeInsets.symmetric(
|
||||
// horizontal: 8.0,
|
||||
// vertical: 2.0,
|
||||
// ),
|
||||
// child: Text(
|
||||
// event.originServerTs.localizedTime(context),
|
||||
// style: TextStyle(
|
||||
// fontSize: 12 * AppConfig.fontSizeFactor,
|
||||
// fontWeight: FontWeight.bold,
|
||||
// color: theme.colorScheme.secondary,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// row,
|
||||
// ],
|
||||
// );
|
||||
|
||||
container = Material(type: MaterialType.transparency, child: container);
|
||||
// container = Material(type: MaterialType.transparency, child: container);
|
||||
|
||||
return Center(
|
||||
child: Swipeable(
|
||||
key: ValueKey(event.eventId),
|
||||
background: const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12.0),
|
||||
child: Center(
|
||||
child: Icon(Icons.check_outlined),
|
||||
),
|
||||
),
|
||||
direction: AppConfig.swipeRightToLeftToReply
|
||||
? SwipeDirection.endToStart
|
||||
: SwipeDirection.startToEnd,
|
||||
onSwipe: (_) {},
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: FluffyThemes.maxTimelineWidth,
|
||||
),
|
||||
padding: const EdgeInsets.only(
|
||||
left: 8.0,
|
||||
right: 8.0,
|
||||
top: 4.0,
|
||||
bottom: 4.0,
|
||||
),
|
||||
child: container,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
// return Center(
|
||||
// child: Swipeable(
|
||||
// key: ValueKey(event.eventId),
|
||||
// background: const Padding(
|
||||
// padding: EdgeInsets.symmetric(horizontal: 12.0),
|
||||
// child: Center(
|
||||
// child: Icon(Icons.check_outlined),
|
||||
// ),
|
||||
// ),
|
||||
// direction: AppConfig.swipeRightToLeftToReply
|
||||
// ? SwipeDirection.endToStart
|
||||
// : SwipeDirection.startToEnd,
|
||||
// onSwipe: (_) {},
|
||||
// child: Container(
|
||||
// constraints: const BoxConstraints(
|
||||
// maxWidth: FluffyThemes.maxTimelineWidth,
|
||||
// ),
|
||||
// padding: const EdgeInsets.only(
|
||||
// left: 8.0,
|
||||
// right: 8.0,
|
||||
// top: 4.0,
|
||||
// bottom: 4.0,
|
||||
// ),
|
||||
// child: container,
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -49,32 +49,6 @@ class ActivityPlanModel {
|
|||
return defaultRoles;
|
||||
}
|
||||
|
||||
ActivityPlanModel copyWith({
|
||||
String? title,
|
||||
String? description,
|
||||
String? learningObjective,
|
||||
String? instructions,
|
||||
List<Vocab>? vocab,
|
||||
String? imageURL,
|
||||
DateTime? endAt,
|
||||
Duration? duration,
|
||||
Map<String, ActivityRole>? roles,
|
||||
}) {
|
||||
return ActivityPlanModel(
|
||||
req: req,
|
||||
title: title ?? this.title,
|
||||
description: description ?? this.description,
|
||||
learningObjective: learningObjective ?? this.learningObjective,
|
||||
instructions: instructions ?? this.instructions,
|
||||
vocab: vocab ?? this.vocab,
|
||||
imageURL: imageURL ?? this.imageURL,
|
||||
endAt: endAt ?? this.endAt,
|
||||
duration: duration ?? this.duration,
|
||||
roles: roles ?? _roles,
|
||||
activityId: activityId,
|
||||
);
|
||||
}
|
||||
|
||||
factory ActivityPlanModel.fromJson(Map<String, dynamic> json) {
|
||||
final req =
|
||||
ActivityPlanRequest.fromJson(json[ModelKey.activityPlanRequest]);
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
|
||||
// import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
|
||||
|
||||
class ActivityPlanResponse {
|
||||
final List<ActivityPlanModel> activityPlans;
|
||||
// class ActivityPlanResponse {
|
||||
// final List<ActivityPlanModel> activityPlans;
|
||||
|
||||
ActivityPlanResponse({required this.activityPlans});
|
||||
// ActivityPlanResponse({required this.activityPlans});
|
||||
|
||||
factory ActivityPlanResponse.fromJson(Map<String, dynamic> json) {
|
||||
return ActivityPlanResponse(
|
||||
activityPlans: (json['activity_plans'] as List)
|
||||
.map((e) => ActivityPlanModel.fromJson(e))
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
// factory ActivityPlanResponse.fromJson(Map<String, dynamic> json) {
|
||||
// return ActivityPlanResponse(
|
||||
// activityPlans: (json['activity_plans'] as List)
|
||||
// .map((e) => ActivityPlanModel.fromJson(e))
|
||||
// .toList(),
|
||||
// );
|
||||
// }
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'activity_plans': activityPlans.map((e) => e.toJson()).toList(),
|
||||
};
|
||||
}
|
||||
}
|
||||
// Map<String, dynamic> toJson() {
|
||||
// return {
|
||||
// 'activity_plans': activityPlans.map((e) => e.toJson()).toList(),
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,420 +1,420 @@
|
|||
import 'dart:async';
|
||||
// import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart' hide Visibility;
|
||||
// import 'package:flutter/foundation.dart';
|
||||
// import 'package:flutter/material.dart' hide Visibility;
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:http/http.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
// import 'package:collection/collection.dart';
|
||||
// import 'package:http/http.dart' as http;
|
||||
// import 'package:http/http.dart';
|
||||
// import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_plan_request.dart';
|
||||
import 'package:fluffychat/pangea/activity_suggestions/activity_plan_repo.dart';
|
||||
import 'package:fluffychat/pangea/chat/constants/default_power_level.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/constants/pangea_room_types.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart';
|
||||
import 'package:fluffychat/pangea/extensions/join_rule_extension.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/user/controllers/user_controller.dart';
|
||||
import 'package:fluffychat/utils/client_download_content_extension.dart';
|
||||
import 'package:fluffychat/utils/file_selector.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
// import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
|
||||
// import 'package:fluffychat/pangea/activity_planner/activity_plan_request.dart';
|
||||
// import 'package:fluffychat/pangea/activity_suggestions/activity_plan_repo.dart';
|
||||
// import 'package:fluffychat/pangea/chat/constants/default_power_level.dart';
|
||||
// import 'package:fluffychat/pangea/chat_settings/constants/pangea_room_types.dart';
|
||||
// import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
// import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart';
|
||||
// import 'package:fluffychat/pangea/extensions/join_rule_extension.dart';
|
||||
// import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||
// import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart';
|
||||
// import 'package:fluffychat/pangea/user/controllers/user_controller.dart';
|
||||
// import 'package:fluffychat/utils/client_download_content_extension.dart';
|
||||
// import 'package:fluffychat/utils/file_selector.dart';
|
||||
// import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
enum ActivityLaunchState {
|
||||
base,
|
||||
editing,
|
||||
launching,
|
||||
}
|
||||
// enum ActivityLaunchState {
|
||||
// base,
|
||||
// editing,
|
||||
// launching,
|
||||
// }
|
||||
|
||||
class ActivityPlannerBuilder extends StatefulWidget {
|
||||
final ActivityPlanModel initialActivity;
|
||||
final String? initialFilename;
|
||||
final Room room;
|
||||
// class ActivityPlannerBuilder extends StatefulWidget {
|
||||
// final ActivityPlanModel initialActivity;
|
||||
// final String? initialFilename;
|
||||
// final Room room;
|
||||
|
||||
final bool enabledEdits;
|
||||
final bool enableMultiLaunch;
|
||||
// final bool enabledEdits;
|
||||
// final bool enableMultiLaunch;
|
||||
|
||||
final Widget Function(ActivityPlannerBuilderState) builder;
|
||||
// final Widget Function(ActivityPlannerBuilderState) builder;
|
||||
|
||||
const ActivityPlannerBuilder({
|
||||
super.key,
|
||||
required this.initialActivity,
|
||||
this.initialFilename,
|
||||
required this.room,
|
||||
required this.builder,
|
||||
this.enabledEdits = false,
|
||||
this.enableMultiLaunch = false,
|
||||
});
|
||||
// const ActivityPlannerBuilder({
|
||||
// super.key,
|
||||
// required this.initialActivity,
|
||||
// this.initialFilename,
|
||||
// required this.room,
|
||||
// required this.builder,
|
||||
// this.enabledEdits = false,
|
||||
// this.enableMultiLaunch = false,
|
||||
// });
|
||||
|
||||
@override
|
||||
State<ActivityPlannerBuilder> createState() => ActivityPlannerBuilderState();
|
||||
}
|
||||
// @override
|
||||
// State<ActivityPlannerBuilder> createState() => ActivityPlannerBuilderState();
|
||||
// }
|
||||
|
||||
class ActivityPlannerBuilderState extends State<ActivityPlannerBuilder> {
|
||||
ActivityLaunchState launchState = ActivityLaunchState.base;
|
||||
Uint8List? avatar;
|
||||
String? imageURL;
|
||||
String? filename;
|
||||
// class ActivityPlannerBuilderState extends State<ActivityPlannerBuilder> {
|
||||
// ActivityLaunchState launchState = ActivityLaunchState.base;
|
||||
// Uint8List? avatar;
|
||||
// String? imageURL;
|
||||
// String? filename;
|
||||
|
||||
int numActivities = 1;
|
||||
// int numActivities = 1;
|
||||
|
||||
final TextEditingController titleController = TextEditingController();
|
||||
final TextEditingController instructionsController = TextEditingController();
|
||||
final TextEditingController vocabController = TextEditingController();
|
||||
final TextEditingController participantsController = TextEditingController();
|
||||
final TextEditingController learningObjectivesController =
|
||||
TextEditingController();
|
||||
// final TextEditingController titleController = TextEditingController();
|
||||
// final TextEditingController instructionsController = TextEditingController();
|
||||
// final TextEditingController vocabController = TextEditingController();
|
||||
// final TextEditingController participantsController = TextEditingController();
|
||||
// final TextEditingController learningObjectivesController =
|
||||
// TextEditingController();
|
||||
|
||||
final List<Vocab> vocab = [];
|
||||
late LanguageLevelTypeEnum languageLevel;
|
||||
// final List<Vocab> vocab = [];
|
||||
// late LanguageLevelTypeEnum languageLevel;
|
||||
|
||||
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
||||
// final GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
||||
|
||||
final StreamController stateStream = StreamController.broadcast();
|
||||
// final StreamController stateStream = StreamController.broadcast();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
resetActivity();
|
||||
}
|
||||
// @override
|
||||
// void initState() {
|
||||
// super.initState();
|
||||
// resetActivity();
|
||||
// }
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
titleController.dispose();
|
||||
learningObjectivesController.dispose();
|
||||
instructionsController.dispose();
|
||||
vocabController.dispose();
|
||||
participantsController.dispose();
|
||||
stateStream.close();
|
||||
super.dispose();
|
||||
}
|
||||
// @override
|
||||
// void dispose() {
|
||||
// titleController.dispose();
|
||||
// learningObjectivesController.dispose();
|
||||
// instructionsController.dispose();
|
||||
// vocabController.dispose();
|
||||
// participantsController.dispose();
|
||||
// stateStream.close();
|
||||
// super.dispose();
|
||||
// }
|
||||
|
||||
void update() {
|
||||
if (mounted) setState(() {});
|
||||
if (!stateStream.isClosed) {
|
||||
stateStream.add(null);
|
||||
}
|
||||
}
|
||||
// void update() {
|
||||
// if (mounted) setState(() {});
|
||||
// if (!stateStream.isClosed) {
|
||||
// stateStream.add(null);
|
||||
// }
|
||||
// }
|
||||
|
||||
Room get room => widget.room;
|
||||
// Room get room => widget.room;
|
||||
|
||||
bool get isEditing => launchState == ActivityLaunchState.editing;
|
||||
bool get isLaunching => launchState == ActivityLaunchState.launching;
|
||||
// bool get isEditing => launchState == ActivityLaunchState.editing;
|
||||
// bool get isLaunching => launchState == ActivityLaunchState.launching;
|
||||
|
||||
ActivityPlanRequest get updatedRequest {
|
||||
final int participants = int.tryParse(participantsController.text.trim()) ??
|
||||
widget.initialActivity.req.numberOfParticipants;
|
||||
final updatedReq = widget.initialActivity.req;
|
||||
updatedReq.numberOfParticipants = participants;
|
||||
updatedReq.cefrLevel = languageLevel;
|
||||
return updatedReq;
|
||||
}
|
||||
// ActivityPlanRequest get updatedRequest {
|
||||
// final int participants = int.tryParse(participantsController.text.trim()) ??
|
||||
// widget.initialActivity.req.numberOfParticipants;
|
||||
// final updatedReq = widget.initialActivity.req;
|
||||
// updatedReq.numberOfParticipants = participants;
|
||||
// updatedReq.cefrLevel = languageLevel;
|
||||
// return updatedReq;
|
||||
// }
|
||||
|
||||
ActivityPlanModel get updatedActivity {
|
||||
return ActivityPlanModel(
|
||||
req: updatedRequest,
|
||||
title: titleController.text,
|
||||
learningObjective: learningObjectivesController.text,
|
||||
instructions: instructionsController.text,
|
||||
vocab: vocab,
|
||||
imageURL: imageURL,
|
||||
roles: widget.initialActivity.roles,
|
||||
activityId: widget.initialActivity.activityId,
|
||||
);
|
||||
}
|
||||
// ActivityPlanModel get updatedActivity {
|
||||
// return ActivityPlanModel(
|
||||
// req: updatedRequest,
|
||||
// title: titleController.text,
|
||||
// learningObjective: learningObjectivesController.text,
|
||||
// instructions: instructionsController.text,
|
||||
// vocab: vocab,
|
||||
// imageURL: imageURL,
|
||||
// roles: widget.initialActivity.roles,
|
||||
// activityId: widget.initialActivity.activityId,
|
||||
// );
|
||||
// }
|
||||
|
||||
Future<void> resetActivity() async {
|
||||
avatar = null;
|
||||
filename = null;
|
||||
imageURL = null;
|
||||
// Future<void> resetActivity() async {
|
||||
// avatar = null;
|
||||
// filename = null;
|
||||
// imageURL = null;
|
||||
|
||||
titleController.text = widget.initialActivity.title;
|
||||
learningObjectivesController.text =
|
||||
widget.initialActivity.learningObjective;
|
||||
instructionsController.text = widget.initialActivity.instructions;
|
||||
participantsController.text =
|
||||
widget.initialActivity.req.numberOfParticipants.toString();
|
||||
// titleController.text = widget.initialActivity.title;
|
||||
// learningObjectivesController.text =
|
||||
// widget.initialActivity.learningObjective;
|
||||
// instructionsController.text = widget.initialActivity.instructions;
|
||||
// participantsController.text =
|
||||
// widget.initialActivity.req.numberOfParticipants.toString();
|
||||
|
||||
vocab.clear();
|
||||
vocab.addAll(widget.initialActivity.vocab);
|
||||
// vocab.clear();
|
||||
// vocab.addAll(widget.initialActivity.vocab);
|
||||
|
||||
languageLevel = widget.initialActivity.req.cefrLevel;
|
||||
// languageLevel = widget.initialActivity.req.cefrLevel;
|
||||
|
||||
imageURL = widget.initialActivity.imageURL;
|
||||
filename = widget.initialFilename;
|
||||
if (widget.initialActivity.imageURL != null) {
|
||||
await _setAvatarByURL(widget.initialActivity.imageURL!);
|
||||
}
|
||||
// imageURL = widget.initialActivity.imageURL;
|
||||
// filename = widget.initialFilename;
|
||||
// if (widget.initialActivity.imageURL != null) {
|
||||
// await _setAvatarByURL(widget.initialActivity.imageURL!);
|
||||
// }
|
||||
|
||||
update();
|
||||
}
|
||||
// update();
|
||||
// }
|
||||
|
||||
Future<void> overrideActivity(ActivityPlanModel override) async {
|
||||
avatar = null;
|
||||
filename = null;
|
||||
imageURL = null;
|
||||
// Future<void> overrideActivity(ActivityPlanModel override) async {
|
||||
// avatar = null;
|
||||
// filename = null;
|
||||
// imageURL = null;
|
||||
|
||||
titleController.text = override.title;
|
||||
learningObjectivesController.text = override.learningObjective;
|
||||
instructionsController.text = override.instructions;
|
||||
participantsController.text = override.req.numberOfParticipants.toString();
|
||||
vocab.clear();
|
||||
vocab.addAll(override.vocab);
|
||||
languageLevel = override.req.cefrLevel;
|
||||
// titleController.text = override.title;
|
||||
// learningObjectivesController.text = override.learningObjective;
|
||||
// instructionsController.text = override.instructions;
|
||||
// participantsController.text = override.req.numberOfParticipants.toString();
|
||||
// vocab.clear();
|
||||
// vocab.addAll(override.vocab);
|
||||
// languageLevel = override.req.cefrLevel;
|
||||
|
||||
if (override.imageURL != null) {
|
||||
await _setAvatarByURL(override.imageURL!);
|
||||
}
|
||||
// if (override.imageURL != null) {
|
||||
// await _setAvatarByURL(override.imageURL!);
|
||||
// }
|
||||
|
||||
update();
|
||||
}
|
||||
// update();
|
||||
// }
|
||||
|
||||
void startEditing() {
|
||||
setLaunchState(ActivityLaunchState.editing);
|
||||
}
|
||||
// void startEditing() {
|
||||
// setLaunchState(ActivityLaunchState.editing);
|
||||
// }
|
||||
|
||||
void setLaunchState(ActivityLaunchState state) {
|
||||
if (state == ActivityLaunchState.launching) {
|
||||
_addBookmarkedActivity();
|
||||
}
|
||||
// void setLaunchState(ActivityLaunchState state) {
|
||||
// if (state == ActivityLaunchState.launching) {
|
||||
// _addBookmarkedActivity();
|
||||
// }
|
||||
|
||||
launchState = state;
|
||||
update();
|
||||
}
|
||||
// launchState = state;
|
||||
// update();
|
||||
// }
|
||||
|
||||
void addVocab() {
|
||||
vocab.insert(
|
||||
0,
|
||||
Vocab(
|
||||
lemma: vocabController.text.trim(),
|
||||
pos: "",
|
||||
),
|
||||
);
|
||||
vocabController.clear();
|
||||
update();
|
||||
}
|
||||
// void addVocab() {
|
||||
// vocab.insert(
|
||||
// 0,
|
||||
// Vocab(
|
||||
// lemma: vocabController.text.trim(),
|
||||
// pos: "",
|
||||
// ),
|
||||
// );
|
||||
// vocabController.clear();
|
||||
// update();
|
||||
// }
|
||||
|
||||
void removeVocab(int index) {
|
||||
vocab.removeAt(index);
|
||||
update();
|
||||
}
|
||||
// void removeVocab(int index) {
|
||||
// vocab.removeAt(index);
|
||||
// update();
|
||||
// }
|
||||
|
||||
void setLanguageLevel(LanguageLevelTypeEnum level) {
|
||||
languageLevel = level;
|
||||
update();
|
||||
}
|
||||
// void setLanguageLevel(LanguageLevelTypeEnum level) {
|
||||
// languageLevel = level;
|
||||
// update();
|
||||
// }
|
||||
|
||||
Future<void> selectAvatar() async {
|
||||
final photo = await selectFiles(
|
||||
context,
|
||||
type: FileSelectorType.images,
|
||||
allowMultiple: false,
|
||||
);
|
||||
final bytes = await photo.singleOrNull?.readAsBytes();
|
||||
avatar = bytes;
|
||||
imageURL = null;
|
||||
filename = photo.singleOrNull?.name;
|
||||
update();
|
||||
}
|
||||
// Future<void> selectAvatar() async {
|
||||
// final photo = await selectFiles(
|
||||
// context,
|
||||
// type: FileSelectorType.images,
|
||||
// allowMultiple: false,
|
||||
// );
|
||||
// final bytes = await photo.singleOrNull?.readAsBytes();
|
||||
// avatar = bytes;
|
||||
// imageURL = null;
|
||||
// filename = photo.singleOrNull?.name;
|
||||
// update();
|
||||
// }
|
||||
|
||||
void setNumActivities(int count) {
|
||||
numActivities = count;
|
||||
update();
|
||||
}
|
||||
// void setNumActivities(int count) {
|
||||
// numActivities = count;
|
||||
// update();
|
||||
// }
|
||||
|
||||
Future<void> _setAvatarByURL(String url) async {
|
||||
try {
|
||||
if (avatar == null) {
|
||||
if (url.startsWith("mxc")) {
|
||||
final client = Matrix.of(context).client;
|
||||
final mxcUri = Uri.parse(url);
|
||||
final data = await client.downloadMxcCached(mxcUri);
|
||||
avatar = data;
|
||||
filename = Uri.encodeComponent(
|
||||
mxcUri.pathSegments.last,
|
||||
);
|
||||
} else {
|
||||
final Response response = await http.get(
|
||||
Uri.parse(url),
|
||||
headers: {
|
||||
'Authorization':
|
||||
'Bearer ${MatrixState.pangeaController.userController.accessToken}',
|
||||
},
|
||||
);
|
||||
if (response.statusCode != 200) {
|
||||
throw Exception(
|
||||
"Failed to download image from URL: ${response.statusCode}",
|
||||
);
|
||||
}
|
||||
avatar = response.bodyBytes;
|
||||
filename = Uri.encodeComponent(
|
||||
Uri.parse(url).pathSegments.last,
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (err, s) {
|
||||
ErrorHandler.logError(
|
||||
e: err,
|
||||
s: s,
|
||||
data: {
|
||||
"imageURL": widget.initialActivity.imageURL,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
// Future<void> _setAvatarByURL(String url) async {
|
||||
// try {
|
||||
// if (avatar == null) {
|
||||
// if (url.startsWith("mxc")) {
|
||||
// final client = Matrix.of(context).client;
|
||||
// final mxcUri = Uri.parse(url);
|
||||
// final data = await client.downloadMxcCached(mxcUri);
|
||||
// avatar = data;
|
||||
// filename = Uri.encodeComponent(
|
||||
// mxcUri.pathSegments.last,
|
||||
// );
|
||||
// } else {
|
||||
// final Response response = await http.get(
|
||||
// Uri.parse(url),
|
||||
// headers: {
|
||||
// 'Authorization':
|
||||
// 'Bearer ${MatrixState.pangeaController.userController.accessToken}',
|
||||
// },
|
||||
// );
|
||||
// if (response.statusCode != 200) {
|
||||
// throw Exception(
|
||||
// "Failed to download image from URL: ${response.statusCode}",
|
||||
// );
|
||||
// }
|
||||
// avatar = response.bodyBytes;
|
||||
// filename = Uri.encodeComponent(
|
||||
// Uri.parse(url).pathSegments.last,
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// } catch (err, s) {
|
||||
// ErrorHandler.logError(
|
||||
// e: err,
|
||||
// s: s,
|
||||
// data: {
|
||||
// "imageURL": widget.initialActivity.imageURL,
|
||||
// },
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
Future<void> updateImageURL() async {
|
||||
if (avatar == null) return;
|
||||
final url = await Matrix.of(context).client.uploadContent(
|
||||
avatar!,
|
||||
filename: filename,
|
||||
);
|
||||
imageURL = url.toString();
|
||||
update();
|
||||
}
|
||||
// Future<void> updateImageURL() async {
|
||||
// if (avatar == null) return;
|
||||
// final url = await Matrix.of(context).client.uploadContent(
|
||||
// avatar!,
|
||||
// filename: filename,
|
||||
// );
|
||||
// imageURL = url.toString();
|
||||
// update();
|
||||
// }
|
||||
|
||||
Future<void> saveEdits() async {
|
||||
if (!formKey.currentState!.validate()) return;
|
||||
await updateImageURL();
|
||||
setLaunchState(ActivityLaunchState.base);
|
||||
// Future<void> saveEdits() async {
|
||||
// if (!formKey.currentState!.validate()) return;
|
||||
// await updateImageURL();
|
||||
// setLaunchState(ActivityLaunchState.base);
|
||||
|
||||
await _updateBookmarkedActivity();
|
||||
update();
|
||||
}
|
||||
// await _updateBookmarkedActivity();
|
||||
// update();
|
||||
// }
|
||||
|
||||
Future<void> clearEdits() async {
|
||||
await resetActivity();
|
||||
setLaunchState(ActivityLaunchState.base);
|
||||
}
|
||||
// Future<void> clearEdits() async {
|
||||
// await resetActivity();
|
||||
// setLaunchState(ActivityLaunchState.base);
|
||||
// }
|
||||
|
||||
UserController get _userController =>
|
||||
MatrixState.pangeaController.userController;
|
||||
// UserController get _userController =>
|
||||
// MatrixState.pangeaController.userController;
|
||||
|
||||
bool get isBookmarked =>
|
||||
_userController.isBookmarked(updatedActivity.activityId);
|
||||
// bool get isBookmarked =>
|
||||
// _userController.isBookmarked(updatedActivity.activityId);
|
||||
|
||||
Future<void> toggleBookmarkedActivity() async {
|
||||
isBookmarked
|
||||
? await _removeBookmarkedActivity()
|
||||
: await _addBookmarkedActivity();
|
||||
update();
|
||||
}
|
||||
// Future<void> toggleBookmarkedActivity() async {
|
||||
// isBookmarked
|
||||
// ? await _removeBookmarkedActivity()
|
||||
// : await _addBookmarkedActivity();
|
||||
// update();
|
||||
// }
|
||||
|
||||
Future<void> _addBookmarkedActivity() async {
|
||||
await _userController.addBookmarkedActivity(
|
||||
activityId: updatedActivity.activityId,
|
||||
);
|
||||
await ActivityPlanRepo.set(updatedActivity);
|
||||
}
|
||||
// Future<void> _addBookmarkedActivity() async {
|
||||
// await _userController.addBookmarkedActivity(
|
||||
// activityId: updatedActivity.activityId,
|
||||
// );
|
||||
// await ActivityPlanRepo.set(updatedActivity);
|
||||
// }
|
||||
|
||||
Future<void> _updateBookmarkedActivity() async {
|
||||
// save updates locally, in case choreo results in error
|
||||
await ActivityPlanRepo.set(updatedActivity);
|
||||
// Future<void> _updateBookmarkedActivity() async {
|
||||
// // save updates locally, in case choreo results in error
|
||||
// await ActivityPlanRepo.set(updatedActivity);
|
||||
|
||||
// prevent an error or delay from the choreo endpoint bubbling up
|
||||
// in the UI, since the changes are still stored locally
|
||||
ActivityPlanRepo.update(
|
||||
updatedActivity,
|
||||
).then((resp) {
|
||||
_userController.updateBookmarkedActivity(
|
||||
activityId: widget.initialActivity.activityId,
|
||||
newActivityId: resp.activityId,
|
||||
);
|
||||
});
|
||||
}
|
||||
// // prevent an error or delay from the choreo endpoint bubbling up
|
||||
// // in the UI, since the changes are still stored locally
|
||||
// ActivityPlanRepo.update(
|
||||
// updatedActivity,
|
||||
// ).then((resp) {
|
||||
// _userController.updateBookmarkedActivity(
|
||||
// activityId: widget.initialActivity.activityId,
|
||||
// newActivityId: resp.activityId,
|
||||
// );
|
||||
// });
|
||||
// }
|
||||
|
||||
Future<void> _removeBookmarkedActivity() async {
|
||||
await _userController.removeBookmarkedActivity(
|
||||
activityId: updatedActivity.activityId,
|
||||
);
|
||||
await ActivityPlanRepo.remove(updatedActivity.activityId);
|
||||
}
|
||||
// Future<void> _removeBookmarkedActivity() async {
|
||||
// await _userController.removeBookmarkedActivity(
|
||||
// activityId: updatedActivity.activityId,
|
||||
// );
|
||||
// await ActivityPlanRepo.remove(updatedActivity.activityId);
|
||||
// }
|
||||
|
||||
Future<List<String>> launchToSpace() async {
|
||||
final List<String> activityRoomIDs = [];
|
||||
try {
|
||||
return Future.wait(
|
||||
List.generate(numActivities, (i) async {
|
||||
final id = await _launchActivityRoom(i);
|
||||
activityRoomIDs.add(id);
|
||||
return id;
|
||||
}),
|
||||
);
|
||||
} catch (e) {
|
||||
_cleanupFailedLaunch(activityRoomIDs);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
// Future<List<String>> launchToSpace() async {
|
||||
// final List<String> activityRoomIDs = [];
|
||||
// try {
|
||||
// return Future.wait(
|
||||
// List.generate(numActivities, (i) async {
|
||||
// final id = await _launchActivityRoom(i);
|
||||
// activityRoomIDs.add(id);
|
||||
// return id;
|
||||
// }),
|
||||
// );
|
||||
// } catch (e) {
|
||||
// _cleanupFailedLaunch(activityRoomIDs);
|
||||
// rethrow;
|
||||
// }
|
||||
// }
|
||||
|
||||
Future<String> _launchActivityRoom(int index) async {
|
||||
await updateImageURL();
|
||||
final roomID = await Matrix.of(context).client.createRoom(
|
||||
creationContent: {
|
||||
'type':
|
||||
"${PangeaRoomTypes.activitySession}:${updatedActivity.activityId}",
|
||||
},
|
||||
visibility: Visibility.private,
|
||||
name: "${updatedActivity.title} ${index + 1}",
|
||||
initialState: [
|
||||
StateEvent(
|
||||
type: PangeaEventTypes.activityPlan,
|
||||
content: updatedActivity.toJson(),
|
||||
),
|
||||
if (imageURL != null)
|
||||
StateEvent(
|
||||
type: EventTypes.RoomAvatar,
|
||||
content: {'url': imageURL},
|
||||
),
|
||||
RoomDefaults.defaultPowerLevels(
|
||||
Matrix.of(context).client.userID!,
|
||||
),
|
||||
await Matrix.of(context).client.pangeaJoinRules(
|
||||
'knock_restricted',
|
||||
allow: [
|
||||
{
|
||||
"type": "m.room_membership",
|
||||
"room_id": room.id,
|
||||
}
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
// Future<String> _launchActivityRoom(int index) async {
|
||||
// await updateImageURL();
|
||||
// final roomID = await Matrix.of(context).client.createRoom(
|
||||
// creationContent: {
|
||||
// 'type':
|
||||
// "${PangeaRoomTypes.activitySession}:${updatedActivity.activityId}",
|
||||
// },
|
||||
// visibility: Visibility.private,
|
||||
// name: "${updatedActivity.title} ${index + 1}",
|
||||
// initialState: [
|
||||
// StateEvent(
|
||||
// type: PangeaEventTypes.activityPlan,
|
||||
// content: updatedActivity.toJson(),
|
||||
// ),
|
||||
// if (imageURL != null)
|
||||
// StateEvent(
|
||||
// type: EventTypes.RoomAvatar,
|
||||
// content: {'url': imageURL},
|
||||
// ),
|
||||
// RoomDefaults.defaultPowerLevels(
|
||||
// Matrix.of(context).client.userID!,
|
||||
// ),
|
||||
// await Matrix.of(context).client.pangeaJoinRules(
|
||||
// 'knock_restricted',
|
||||
// allow: [
|
||||
// {
|
||||
// "type": "m.room_membership",
|
||||
// "room_id": room.id,
|
||||
// }
|
||||
// ],
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
|
||||
Room? activityRoom = room.client.getRoomById(roomID);
|
||||
if (activityRoom == null) {
|
||||
await room.client.waitForRoomInSync(roomID);
|
||||
activityRoom = room.client.getRoomById(roomID);
|
||||
if (activityRoom == null) {
|
||||
throw Exception("Failed to create activity room");
|
||||
}
|
||||
}
|
||||
// Room? activityRoom = room.client.getRoomById(roomID);
|
||||
// if (activityRoom == null) {
|
||||
// await room.client.waitForRoomInSync(roomID);
|
||||
// activityRoom = room.client.getRoomById(roomID);
|
||||
// if (activityRoom == null) {
|
||||
// throw Exception("Failed to create activity room");
|
||||
// }
|
||||
// }
|
||||
|
||||
await room.addToSpace(activityRoom.id);
|
||||
if (activityRoom.pangeaSpaceParents.isEmpty) {
|
||||
await room.client.waitForRoomInSync(activityRoom.id);
|
||||
}
|
||||
// await room.addToSpace(activityRoom.id);
|
||||
// if (activityRoom.pangeaSpaceParents.isEmpty) {
|
||||
// await room.client.waitForRoomInSync(activityRoom.id);
|
||||
// }
|
||||
|
||||
return activityRoom.id;
|
||||
}
|
||||
// return activityRoom.id;
|
||||
// }
|
||||
|
||||
Future<void> _cleanupFailedLaunch(List<String> roomIds) async {
|
||||
final futures = roomIds.map((id) async {
|
||||
final room = Matrix.of(context).client.getRoomById(id);
|
||||
if (room == null) return;
|
||||
// Future<void> _cleanupFailedLaunch(List<String> roomIds) async {
|
||||
// final futures = roomIds.map((id) async {
|
||||
// final room = Matrix.of(context).client.getRoomById(id);
|
||||
// if (room == null) return;
|
||||
|
||||
try {
|
||||
await room.leave();
|
||||
} catch (e) {
|
||||
debugPrint("Failed to leave room $id: $e");
|
||||
}
|
||||
});
|
||||
// try {
|
||||
// await room.leave();
|
||||
// } catch (e) {
|
||||
// debugPrint("Failed to leave room $id: $e");
|
||||
// }
|
||||
// });
|
||||
|
||||
await Future.wait(futures);
|
||||
}
|
||||
// await Future.wait(futures);
|
||||
// }
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => widget.builder(this);
|
||||
}
|
||||
// @override
|
||||
// Widget build(BuildContext context) => widget.builder(this);
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,158 +1,158 @@
|
|||
import 'package:flutter/material.dart';
|
||||
// import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
// import 'package:go_router/go_router.dart';
|
||||
// import 'package:material_symbols_icons/symbols.dart';
|
||||
// import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_planner_page_appbar.dart';
|
||||
import 'package:fluffychat/pangea/activity_planner/bookmarked_activity_list.dart';
|
||||
import 'package:fluffychat/pangea/activity_suggestions/activity_suggestions_area.dart';
|
||||
import 'package:fluffychat/pangea/activity_suggestions/activity_suggestions_constants.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/customized_svg.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
// import 'package:fluffychat/config/app_config.dart';
|
||||
// import 'package:fluffychat/l10n/l10n.dart';
|
||||
// import 'package:fluffychat/pangea/activity_planner/activity_planner_page_appbar.dart';
|
||||
// import 'package:fluffychat/pangea/activity_planner/bookmarked_activity_list.dart';
|
||||
// import 'package:fluffychat/pangea/activity_suggestions/activity_suggestions_area.dart';
|
||||
// import 'package:fluffychat/pangea/activity_suggestions/activity_suggestions_constants.dart';
|
||||
// import 'package:fluffychat/pangea/common/widgets/customized_svg.dart';
|
||||
// import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
|
||||
// import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
enum PageMode {
|
||||
featuredActivities,
|
||||
savedActivities,
|
||||
}
|
||||
// enum PageMode {
|
||||
// featuredActivities,
|
||||
// savedActivities,
|
||||
// }
|
||||
|
||||
class ActivityPlannerPage extends StatefulWidget {
|
||||
final String roomID;
|
||||
const ActivityPlannerPage({super.key, required this.roomID});
|
||||
// class ActivityPlannerPage extends StatefulWidget {
|
||||
// final String roomID;
|
||||
// const ActivityPlannerPage({super.key, required this.roomID});
|
||||
|
||||
@override
|
||||
ActivityPlannerPageState createState() => ActivityPlannerPageState();
|
||||
}
|
||||
// @override
|
||||
// ActivityPlannerPageState createState() => ActivityPlannerPageState();
|
||||
// }
|
||||
|
||||
class ActivityPlannerPageState extends State<ActivityPlannerPage> {
|
||||
PageMode pageMode = PageMode.featuredActivities;
|
||||
Room? get room => Matrix.of(context).client.getRoomById(widget.roomID);
|
||||
// class ActivityPlannerPageState extends State<ActivityPlannerPage> {
|
||||
// PageMode pageMode = PageMode.featuredActivities;
|
||||
// Room? get room => Matrix.of(context).client.getRoomById(widget.roomID);
|
||||
|
||||
void _setPageMode(PageMode? mode) {
|
||||
if (mode == null) return;
|
||||
setState(() => pageMode = mode);
|
||||
}
|
||||
// void _setPageMode(PageMode? mode) {
|
||||
// if (mode == null) return;
|
||||
// setState(() => pageMode = mode);
|
||||
// }
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
Widget? body;
|
||||
switch (pageMode) {
|
||||
case PageMode.savedActivities:
|
||||
if (room != null) {
|
||||
body = BookmarkedActivitiesList(room: room!);
|
||||
}
|
||||
break;
|
||||
case PageMode.featuredActivities:
|
||||
if (room != null) {
|
||||
body = Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: ActivitySuggestionsArea(
|
||||
scrollDirection: Axis.vertical,
|
||||
room: room!,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// final theme = Theme.of(context);
|
||||
// Widget? body;
|
||||
// switch (pageMode) {
|
||||
// case PageMode.savedActivities:
|
||||
// if (room != null) {
|
||||
// body = BookmarkedActivitiesList(room: room!);
|
||||
// }
|
||||
// break;
|
||||
// case PageMode.featuredActivities:
|
||||
// if (room != null) {
|
||||
// body = Expanded(
|
||||
// child: SingleChildScrollView(
|
||||
// child: ActivitySuggestionsArea(
|
||||
// scrollDirection: Axis.vertical,
|
||||
// room: room!,
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
|
||||
return SafeArea(
|
||||
child: Scaffold(
|
||||
appBar: ActivityPlannerPageAppBar(
|
||||
pageMode: pageMode,
|
||||
roomID: widget.roomID,
|
||||
),
|
||||
body: Center(
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 800.0),
|
||||
child: Column(
|
||||
children: [
|
||||
if ([PageMode.featuredActivities, PageMode.savedActivities]
|
||||
.contains(pageMode))
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Wrap(
|
||||
spacing: 12.0,
|
||||
runSpacing: 12.0,
|
||||
alignment: WrapAlignment.center,
|
||||
children: [
|
||||
FilterChip(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(32),
|
||||
),
|
||||
label: Row(
|
||||
spacing: 8.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Symbols.star_shine, size: 24.0),
|
||||
Text(L10n.of(context).featuredActivities),
|
||||
],
|
||||
),
|
||||
selected: pageMode == PageMode.featuredActivities,
|
||||
onSelected: (_) => _setPageMode(
|
||||
PageMode.featuredActivities,
|
||||
),
|
||||
),
|
||||
FilterChip(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(32),
|
||||
),
|
||||
label: Row(
|
||||
spacing: 8.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.save_outlined, size: 24.0),
|
||||
Text(L10n.of(context).saved),
|
||||
],
|
||||
),
|
||||
selected: pageMode == PageMode.savedActivities,
|
||||
onSelected: (_) => _setPageMode(
|
||||
PageMode.savedActivities,
|
||||
),
|
||||
),
|
||||
FilterChip(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(32),
|
||||
),
|
||||
label: Row(
|
||||
spacing: 8.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
CustomizedSvg(
|
||||
svgUrl:
|
||||
"${AppConfig.assetsBaseURL}/${ActivitySuggestionsConstants.crayonIconPath}",
|
||||
colorReplacements: {
|
||||
"#CDBEF9": colorToHex(
|
||||
theme.colorScheme.secondary,
|
||||
),
|
||||
},
|
||||
height: 24.0,
|
||||
width: 24.0,
|
||||
),
|
||||
Text(L10n.of(context).createActivityPlan),
|
||||
],
|
||||
),
|
||||
selected: false,
|
||||
onSelected: (_) => context.go(
|
||||
'/rooms/spaces/${widget.roomID}/details/planner/generator',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
body ??
|
||||
ErrorIndicator(
|
||||
message: L10n.of(context).oopsSomethingWentWrong,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
// return SafeArea(
|
||||
// child: Scaffold(
|
||||
// appBar: ActivityPlannerPageAppBar(
|
||||
// pageMode: pageMode,
|
||||
// roomID: widget.roomID,
|
||||
// ),
|
||||
// body: Center(
|
||||
// child: ConstrainedBox(
|
||||
// constraints: const BoxConstraints(maxWidth: 800.0),
|
||||
// child: Column(
|
||||
// children: [
|
||||
// if ([PageMode.featuredActivities, PageMode.savedActivities]
|
||||
// .contains(pageMode))
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.all(16.0),
|
||||
// child: Wrap(
|
||||
// spacing: 12.0,
|
||||
// runSpacing: 12.0,
|
||||
// alignment: WrapAlignment.center,
|
||||
// children: [
|
||||
// FilterChip(
|
||||
// shape: RoundedRectangleBorder(
|
||||
// borderRadius: BorderRadius.circular(32),
|
||||
// ),
|
||||
// label: Row(
|
||||
// spacing: 8.0,
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// const Icon(Symbols.star_shine, size: 24.0),
|
||||
// Text(L10n.of(context).featuredActivities),
|
||||
// ],
|
||||
// ),
|
||||
// selected: pageMode == PageMode.featuredActivities,
|
||||
// onSelected: (_) => _setPageMode(
|
||||
// PageMode.featuredActivities,
|
||||
// ),
|
||||
// ),
|
||||
// FilterChip(
|
||||
// shape: RoundedRectangleBorder(
|
||||
// borderRadius: BorderRadius.circular(32),
|
||||
// ),
|
||||
// label: Row(
|
||||
// spacing: 8.0,
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// const Icon(Icons.save_outlined, size: 24.0),
|
||||
// Text(L10n.of(context).saved),
|
||||
// ],
|
||||
// ),
|
||||
// selected: pageMode == PageMode.savedActivities,
|
||||
// onSelected: (_) => _setPageMode(
|
||||
// PageMode.savedActivities,
|
||||
// ),
|
||||
// ),
|
||||
// FilterChip(
|
||||
// shape: RoundedRectangleBorder(
|
||||
// borderRadius: BorderRadius.circular(32),
|
||||
// ),
|
||||
// label: Row(
|
||||
// spacing: 8.0,
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// CustomizedSvg(
|
||||
// svgUrl:
|
||||
// "${AppConfig.assetsBaseURL}/${ActivitySuggestionsConstants.crayonIconPath}",
|
||||
// colorReplacements: {
|
||||
// "#CDBEF9": colorToHex(
|
||||
// theme.colorScheme.secondary,
|
||||
// ),
|
||||
// },
|
||||
// height: 24.0,
|
||||
// width: 24.0,
|
||||
// ),
|
||||
// Text(L10n.of(context).createActivityPlan),
|
||||
// ],
|
||||
// ),
|
||||
// selected: false,
|
||||
// onSelected: (_) => context.go(
|
||||
// '/rooms/spaces/${widget.roomID}/details/planner/generator',
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// body ??
|
||||
// ErrorIndicator(
|
||||
// message: L10n.of(context).oopsSomethingWentWrong,
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,64 +1,64 @@
|
|||
import 'package:flutter/material.dart';
|
||||
// import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_planner_page.dart';
|
||||
// import 'package:fluffychat/config/themes.dart';
|
||||
// import 'package:fluffychat/l10n/l10n.dart';
|
||||
// import 'package:fluffychat/pangea/activity_planner/activity_planner_page.dart';
|
||||
|
||||
class ActivityPlannerPageAppBar extends StatelessWidget
|
||||
implements PreferredSizeWidget {
|
||||
final PageMode pageMode;
|
||||
final String roomID;
|
||||
// class ActivityPlannerPageAppBar extends StatelessWidget
|
||||
// implements PreferredSizeWidget {
|
||||
// final PageMode pageMode;
|
||||
// final String roomID;
|
||||
|
||||
const ActivityPlannerPageAppBar({
|
||||
required this.pageMode,
|
||||
required this.roomID,
|
||||
super.key,
|
||||
});
|
||||
// const ActivityPlannerPageAppBar({
|
||||
// required this.pageMode,
|
||||
// required this.roomID,
|
||||
// super.key,
|
||||
// });
|
||||
|
||||
@override
|
||||
Size get preferredSize => const Size.fromHeight(72);
|
||||
// @override
|
||||
// Size get preferredSize => const Size.fromHeight(72);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = L10n.of(context);
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// final l10n = L10n.of(context);
|
||||
|
||||
return AppBar(
|
||||
leading: FluffyThemes.isColumnMode(context)
|
||||
? Row(
|
||||
children: [
|
||||
const SizedBox(width: 8.0),
|
||||
BackButton(
|
||||
onPressed: Navigator.of(context).pop,
|
||||
),
|
||||
],
|
||||
)
|
||||
: null,
|
||||
title: pageMode == PageMode.savedActivities
|
||||
? Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.save),
|
||||
const SizedBox(width: 8),
|
||||
Flexible(
|
||||
child: Text(l10n.mySavedActivities),
|
||||
),
|
||||
],
|
||||
)
|
||||
: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.event_note_outlined),
|
||||
const SizedBox(width: 8),
|
||||
Flexible(
|
||||
child: Text(
|
||||
l10n.activityPlannerTitle,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
// return AppBar(
|
||||
// leading: FluffyThemes.isColumnMode(context)
|
||||
// ? Row(
|
||||
// children: [
|
||||
// const SizedBox(width: 8.0),
|
||||
// BackButton(
|
||||
// onPressed: Navigator.of(context).pop,
|
||||
// ),
|
||||
// ],
|
||||
// )
|
||||
// : null,
|
||||
// title: pageMode == PageMode.savedActivities
|
||||
// ? Row(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
// children: [
|
||||
// const Icon(Icons.save),
|
||||
// const SizedBox(width: 8),
|
||||
// Flexible(
|
||||
// child: Text(l10n.mySavedActivities),
|
||||
// ),
|
||||
// ],
|
||||
// )
|
||||
// : Row(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
// children: [
|
||||
// const Icon(Icons.event_note_outlined),
|
||||
// const SizedBox(width: 8),
|
||||
// Flexible(
|
||||
// child: Text(
|
||||
// l10n.activityPlannerTitle,
|
||||
// overflow: TextOverflow.ellipsis,
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,122 +1,122 @@
|
|||
import 'package:flutter/material.dart';
|
||||
// import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
// import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_planner_builder.dart';
|
||||
import 'package:fluffychat/pangea/activity_suggestions/activity_suggestion_card.dart';
|
||||
import 'package:fluffychat/pangea/activity_suggestions/activity_suggestion_dialog.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/user/controllers/user_controller.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
// import 'package:fluffychat/config/themes.dart';
|
||||
// import 'package:fluffychat/l10n/l10n.dart';
|
||||
// import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
|
||||
// import 'package:fluffychat/pangea/activity_planner/activity_planner_builder.dart';
|
||||
// import 'package:fluffychat/pangea/activity_suggestions/activity_suggestion_card.dart';
|
||||
// import 'package:fluffychat/pangea/activity_suggestions/activity_suggestion_dialog.dart';
|
||||
// import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
// import 'package:fluffychat/pangea/user/controllers/user_controller.dart';
|
||||
// import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class BookmarkedActivitiesList extends StatefulWidget {
|
||||
final Room room;
|
||||
const BookmarkedActivitiesList({
|
||||
super.key,
|
||||
required this.room,
|
||||
});
|
||||
// class BookmarkedActivitiesList extends StatefulWidget {
|
||||
// final Room room;
|
||||
// const BookmarkedActivitiesList({
|
||||
// super.key,
|
||||
// required this.room,
|
||||
// });
|
||||
|
||||
@override
|
||||
BookmarkedActivitiesListState createState() =>
|
||||
BookmarkedActivitiesListState();
|
||||
}
|
||||
// @override
|
||||
// BookmarkedActivitiesListState createState() =>
|
||||
// BookmarkedActivitiesListState();
|
||||
// }
|
||||
|
||||
class BookmarkedActivitiesListState extends State<BookmarkedActivitiesList> {
|
||||
bool _loading = true;
|
||||
// class BookmarkedActivitiesListState extends State<BookmarkedActivitiesList> {
|
||||
// bool _loading = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadBookmarkedActivities();
|
||||
}
|
||||
// @override
|
||||
// void initState() {
|
||||
// super.initState();
|
||||
// _loadBookmarkedActivities();
|
||||
// }
|
||||
|
||||
List<ActivityPlanModel> get _bookmarkedActivities =>
|
||||
_userController.getBookmarkedActivitiesSync();
|
||||
// List<ActivityPlanModel> get _bookmarkedActivities =>
|
||||
// _userController.getBookmarkedActivitiesSync();
|
||||
|
||||
bool get _isColumnMode => FluffyThemes.isColumnMode(context);
|
||||
// bool get _isColumnMode => FluffyThemes.isColumnMode(context);
|
||||
|
||||
double get cardHeight => _isColumnMode ? 325.0 : 250.0;
|
||||
double get cardWidth => _isColumnMode ? 225.0 : 150.0;
|
||||
// double get cardHeight => _isColumnMode ? 325.0 : 250.0;
|
||||
// double get cardWidth => _isColumnMode ? 225.0 : 150.0;
|
||||
|
||||
UserController get _userController =>
|
||||
MatrixState.pangeaController.userController;
|
||||
// UserController get _userController =>
|
||||
// MatrixState.pangeaController.userController;
|
||||
|
||||
Future<void> _loadBookmarkedActivities() async {
|
||||
try {
|
||||
setState(() => _loading = true);
|
||||
await _userController.getBookmarkedActivities();
|
||||
} catch (e, s) {
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {
|
||||
'roomId': widget.room.id,
|
||||
},
|
||||
);
|
||||
} finally {
|
||||
if (mounted) setState(() => _loading = false);
|
||||
}
|
||||
}
|
||||
// Future<void> _loadBookmarkedActivities() async {
|
||||
// try {
|
||||
// setState(() => _loading = true);
|
||||
// await _userController.getBookmarkedActivities();
|
||||
// } catch (e, s) {
|
||||
// ErrorHandler.logError(
|
||||
// e: e,
|
||||
// s: s,
|
||||
// data: {
|
||||
// 'roomId': widget.room.id,
|
||||
// },
|
||||
// );
|
||||
// } finally {
|
||||
// if (mounted) setState(() => _loading = false);
|
||||
// }
|
||||
// }
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = L10n.of(context);
|
||||
if (_loading) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator.adaptive(),
|
||||
);
|
||||
}
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// final l10n = L10n.of(context);
|
||||
// if (_loading) {
|
||||
// return const Center(
|
||||
// child: CircularProgressIndicator.adaptive(),
|
||||
// );
|
||||
// }
|
||||
|
||||
if (_bookmarkedActivities.isEmpty) {
|
||||
return Center(
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(maxWidth: 200),
|
||||
child: Text(
|
||||
l10n.noSavedActivities,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
// if (_bookmarkedActivities.isEmpty) {
|
||||
// return Center(
|
||||
// child: Container(
|
||||
// constraints: const BoxConstraints(maxWidth: 200),
|
||||
// child: Text(
|
||||
// l10n.noSavedActivities,
|
||||
// textAlign: TextAlign.center,
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
|
||||
return Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: SizedBox(
|
||||
width: 800.0,
|
||||
child: Wrap(
|
||||
alignment: WrapAlignment.spaceEvenly,
|
||||
runSpacing: 16.0,
|
||||
spacing: 4.0,
|
||||
children: _bookmarkedActivities.map((activity) {
|
||||
return ActivityPlannerBuilder(
|
||||
initialActivity: activity,
|
||||
room: widget.room,
|
||||
builder: (controller) {
|
||||
return ActivitySuggestionCard(
|
||||
controller: controller,
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return ActivitySuggestionDialog(
|
||||
controller: controller,
|
||||
buttonText: l10n.launchActivityButton,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
width: cardWidth,
|
||||
height: cardHeight,
|
||||
);
|
||||
},
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
// return Expanded(
|
||||
// child: SingleChildScrollView(
|
||||
// child: SizedBox(
|
||||
// width: 800.0,
|
||||
// child: Wrap(
|
||||
// alignment: WrapAlignment.spaceEvenly,
|
||||
// runSpacing: 16.0,
|
||||
// spacing: 4.0,
|
||||
// children: _bookmarkedActivities.map((activity) {
|
||||
// return ActivityPlannerBuilder(
|
||||
// initialActivity: activity,
|
||||
// room: widget.room,
|
||||
// builder: (controller) {
|
||||
// return ActivitySuggestionCard(
|
||||
// controller: controller,
|
||||
// onPressed: () {
|
||||
// showDialog(
|
||||
// context: context,
|
||||
// builder: (context) {
|
||||
// return ActivitySuggestionDialog(
|
||||
// controller: controller,
|
||||
// buttonText: l10n.launchActivityButton,
|
||||
// );
|
||||
// },
|
||||
// );
|
||||
// },
|
||||
// width: cardWidth,
|
||||
// height: cardHeight,
|
||||
// );
|
||||
// },
|
||||
// );
|
||||
// }).toList(),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,60 +1,60 @@
|
|||
import 'package:flutter/material.dart';
|
||||
// import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/activity_generator/list_request_schema.dart';
|
||||
// import 'package:fluffychat/pangea/activity_generator/list_request_schema.dart';
|
||||
|
||||
class SuggestionFormField extends StatelessWidget {
|
||||
final List<ActivitySettingResponseSchema>? suggestions;
|
||||
final String? Function(String?)? validator;
|
||||
final int? maxLength;
|
||||
final String label;
|
||||
final String placeholder;
|
||||
final TextEditingController controller;
|
||||
// class SuggestionFormField extends StatelessWidget {
|
||||
// final List<ActivitySettingResponseSchema>? suggestions;
|
||||
// final String? Function(String?)? validator;
|
||||
// final int? maxLength;
|
||||
// final String label;
|
||||
// final String placeholder;
|
||||
// final TextEditingController controller;
|
||||
|
||||
const SuggestionFormField({
|
||||
super.key,
|
||||
this.suggestions,
|
||||
required this.placeholder,
|
||||
this.validator,
|
||||
this.maxLength,
|
||||
required this.label,
|
||||
required this.controller,
|
||||
});
|
||||
// const SuggestionFormField({
|
||||
// super.key,
|
||||
// this.suggestions,
|
||||
// required this.placeholder,
|
||||
// this.validator,
|
||||
// this.maxLength,
|
||||
// required this.label,
|
||||
// required this.controller,
|
||||
// });
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Autocomplete<String>(
|
||||
initialValue: TextEditingValue(text: controller.text),
|
||||
optionsBuilder: (TextEditingValue textEditingValue) {
|
||||
return (suggestions ?? [])
|
||||
.where((ActivitySettingResponseSchema option) {
|
||||
return option.name
|
||||
.toLowerCase()
|
||||
.contains(textEditingValue.text.toLowerCase());
|
||||
}).map((ActivitySettingResponseSchema e) => e.name);
|
||||
},
|
||||
onSelected: (val) => controller.text = val,
|
||||
fieldViewBuilder: (
|
||||
BuildContext context,
|
||||
TextEditingController textEditingController,
|
||||
FocusNode focusNode,
|
||||
VoidCallback onFieldSubmitted,
|
||||
) {
|
||||
textEditingController.value = controller.value;
|
||||
textEditingController.addListener(() {
|
||||
controller.value = textEditingController.value;
|
||||
});
|
||||
return TextFormField(
|
||||
controller: textEditingController,
|
||||
focusNode: focusNode,
|
||||
decoration: InputDecoration(
|
||||
labelText: label,
|
||||
hintText: placeholder,
|
||||
),
|
||||
validator: validator,
|
||||
maxLength: maxLength,
|
||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return Autocomplete<String>(
|
||||
// initialValue: TextEditingValue(text: controller.text),
|
||||
// optionsBuilder: (TextEditingValue textEditingValue) {
|
||||
// return (suggestions ?? [])
|
||||
// .where((ActivitySettingResponseSchema option) {
|
||||
// return option.name
|
||||
// .toLowerCase()
|
||||
// .contains(textEditingValue.text.toLowerCase());
|
||||
// }).map((ActivitySettingResponseSchema e) => e.name);
|
||||
// },
|
||||
// onSelected: (val) => controller.text = val,
|
||||
// fieldViewBuilder: (
|
||||
// BuildContext context,
|
||||
// TextEditingController textEditingController,
|
||||
// FocusNode focusNode,
|
||||
// VoidCallback onFieldSubmitted,
|
||||
// ) {
|
||||
// textEditingController.value = controller.value;
|
||||
// textEditingController.addListener(() {
|
||||
// controller.value = textEditingController.value;
|
||||
// });
|
||||
// return TextFormField(
|
||||
// controller: textEditingController,
|
||||
// focusNode: focusNode,
|
||||
// decoration: InputDecoration(
|
||||
// labelText: label,
|
||||
// hintText: placeholder,
|
||||
// ),
|
||||
// validator: validator,
|
||||
// maxLength: maxLength,
|
||||
// onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||
// );
|
||||
// },
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:collection/collection.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
|
||||
import 'package:fluffychat/pangea/activity_sessions/activity_participant_indicator.dart';
|
||||
import 'package:fluffychat/pangea/activity_sessions/activity_role_model.dart';
|
||||
import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart';
|
||||
|
|
@ -10,7 +11,8 @@ import 'package:fluffychat/pangea/spaces/utils/load_participants_util.dart';
|
|||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
|
||||
class ActivityParticipantList extends StatelessWidget {
|
||||
final Room room;
|
||||
final ActivityPlanModel activity;
|
||||
final Room? room;
|
||||
final Function(String)? onTap;
|
||||
|
||||
final bool Function(String)? canSelect;
|
||||
|
|
@ -19,7 +21,8 @@ class ActivityParticipantList extends StatelessWidget {
|
|||
|
||||
const ActivityParticipantList({
|
||||
super.key,
|
||||
required this.room,
|
||||
required this.activity,
|
||||
this.room,
|
||||
this.onTap,
|
||||
this.canSelect,
|
||||
this.isSelected,
|
||||
|
|
@ -32,8 +35,8 @@ class ActivityParticipantList extends StatelessWidget {
|
|||
room: room,
|
||||
builder: (context, participants) {
|
||||
final theme = Theme.of(context);
|
||||
final availableRoles = room.activityPlan!.roles;
|
||||
final assignedRoles = room.assignedRoles ?? {};
|
||||
final availableRoles = activity.roles;
|
||||
final assignedRoles = room?.assignedRoles ?? {};
|
||||
|
||||
final remainingMembers = participants.participants.where(
|
||||
(p) => !assignedRoles.values.any((r) => r.userId == p.id),
|
||||
|
|
|
|||
|
|
@ -335,6 +335,14 @@ extension ActivityRoomExtension on Room {
|
|||
bool get showActivityFinished =>
|
||||
showActivityChatUI && ownRole != null && hasCompletedActivity;
|
||||
|
||||
String? get activityId {
|
||||
if (!isActivitySession) return null;
|
||||
if (isActivityRoomType) {
|
||||
return roomType!.split(":").last;
|
||||
}
|
||||
return activityPlan?.activityId;
|
||||
}
|
||||
|
||||
Future<ActivitySummaryAnalyticsModel> getActivityAnalytics() async {
|
||||
// wait for local storage box to init in getAnalytics initialization
|
||||
if (!MatrixState.pangeaController.getAnalytics.initCompleter.isCompleted) {
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ class ActivityFinishedStatusMessage extends StatelessWidget {
|
|||
throw L10n.of(context).noCourseFound;
|
||||
}
|
||||
|
||||
await coursePlan.init();
|
||||
final activityId = controller.room.activityPlan!.activityId;
|
||||
final topicId = coursePlan.topicID(activityId);
|
||||
if (topicId == null) {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import 'package:fluffychat/l10n/l10n.dart';
|
|||
import 'package:fluffychat/pages/chat/chat.dart';
|
||||
import 'package:fluffychat/pages/chat/chat_app_bar_list_tile.dart';
|
||||
import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/activity_suggestions/activity_suggestions_constants.dart';
|
||||
import 'package:fluffychat/pangea/activity_sessions/activity_session_constants.dart';
|
||||
import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
||||
|
|
@ -144,7 +144,7 @@ class ActivityPinnedMessageState extends State<ActivityPinnedMessage> {
|
|||
),
|
||||
CachedNetworkImage(
|
||||
imageUrl:
|
||||
"${AppConfig.assetsBaseURL}/${ActivitySuggestionsConstants.endActivityAssetPath}",
|
||||
"${AppConfig.assetsBaseURL}/${ActivitySessionConstants.endActivityAssetPath}",
|
||||
width: isColumnMode ? 240.0 : 120.0,
|
||||
),
|
||||
Row(
|
||||
|
|
|
|||
|
|
@ -1,94 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pangea/activity_sessions/activity_role_model.dart';
|
||||
import 'package:fluffychat/pangea/activity_sessions/activity_session_chat/activity_analytics_chip.dart';
|
||||
import 'package:fluffychat/pangea/activity_summary/activity_summary_analytics_model.dart';
|
||||
import 'package:fluffychat/pangea/activity_summary/activity_summary_response_model.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/analytics_summary/progress_indicators_enum.dart';
|
||||
|
||||
class ActivityResultsCarousel extends StatelessWidget {
|
||||
final String userId;
|
||||
final ActivityRoleModel selectedRole;
|
||||
final ParticipantSummaryModel summary;
|
||||
final ActivitySummaryAnalyticsModel? analytics;
|
||||
|
||||
final User? user;
|
||||
|
||||
const ActivityResultsCarousel({
|
||||
super.key,
|
||||
required this.userId,
|
||||
required this.selectedRole,
|
||||
required this.summary,
|
||||
required this.analytics,
|
||||
this.user,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final isColumnMode = FluffyThemes.isColumnMode(context);
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.surfaceContainerHighest,
|
||||
),
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: isColumnMode ? 80.0 : 125.0,
|
||||
child: SingleChildScrollView(
|
||||
child: Text(
|
||||
summary.feedback,
|
||||
style: const TextStyle(fontSize: 12.0),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10.0),
|
||||
Wrap(
|
||||
spacing: 8.0,
|
||||
runSpacing: 8.0,
|
||||
children: [
|
||||
if (analytics != null)
|
||||
ActivityAnalyticsChip(
|
||||
ConstructTypeEnum.vocab.indicator.icon,
|
||||
"${analytics!.uniqueConstructCountForUser(userId, ConstructTypeEnum.vocab)}",
|
||||
),
|
||||
if (analytics != null)
|
||||
ActivityAnalyticsChip(
|
||||
ConstructTypeEnum.morph.indicator.icon,
|
||||
"${analytics!.uniqueConstructCountForUser(userId, ConstructTypeEnum.morph)}",
|
||||
),
|
||||
ActivityAnalyticsChip(
|
||||
Icons.school,
|
||||
summary.cefrLevel,
|
||||
),
|
||||
...summary.superlatives.map(
|
||||
(sup) => Container(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.primaryContainer,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Text(
|
||||
sup,
|
||||
style: const TextStyle(
|
||||
fontSize: 12.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
class ActivitySuggestionsConstants {
|
||||
class ActivitySessionConstants {
|
||||
static const String plusIconPath = "add_icon.svg";
|
||||
static const String crayonIconPath = "make_your_own_icon.svg";
|
||||
static const String modeImageFileStart = "activityplanner_mode_";
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class ActivitySuggestionCardRow extends StatelessWidget {
|
||||
class ActivitySessionDetailsRow extends StatelessWidget {
|
||||
final IconData? icon;
|
||||
final Widget? leading;
|
||||
final Widget child;
|
||||
final double? iconSize;
|
||||
|
||||
const ActivitySuggestionCardRow({
|
||||
const ActivitySessionDetailsRow({
|
||||
required this.child,
|
||||
this.icon,
|
||||
this.leading,
|
||||
|
|
@ -2,15 +2,20 @@ import 'dart:async';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
|
||||
import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/activity_sessions/activity_session_start/activity_sessions_start_view.dart';
|
||||
import 'package:fluffychat/pangea/bot/utils/bot_name.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/course_activity_repo.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/course_plan_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
|
||||
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
enum SessionState {
|
||||
notStarted,
|
||||
|
|
@ -20,10 +25,16 @@ enum SessionState {
|
|||
}
|
||||
|
||||
class ActivitySessionStartPage extends StatefulWidget {
|
||||
final Room room;
|
||||
final String activityId;
|
||||
final bool isNew;
|
||||
final Room? room;
|
||||
final String parentId;
|
||||
const ActivitySessionStartPage({
|
||||
super.key,
|
||||
required this.room,
|
||||
required this.activityId,
|
||||
this.isNew = false,
|
||||
this.room,
|
||||
required this.parentId,
|
||||
});
|
||||
|
||||
@override
|
||||
|
|
@ -32,69 +43,86 @@ class ActivitySessionStartPage extends StatefulWidget {
|
|||
}
|
||||
|
||||
class ActivitySessionStartController extends State<ActivitySessionStartPage> {
|
||||
bool _started = false;
|
||||
ActivityPlanModel? activity;
|
||||
bool loading = true;
|
||||
Object? error;
|
||||
|
||||
bool showInstructions = false;
|
||||
String? _selectedRoleId;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadActivity();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant ActivitySessionStartPage oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.room.id != widget.room.id) {
|
||||
if (oldWidget.room?.id != widget.room?.id) {
|
||||
setState(() {
|
||||
_started = false;
|
||||
_selectedRoleId = null;
|
||||
showInstructions = false;
|
||||
});
|
||||
}
|
||||
|
||||
if (oldWidget.activityId != widget.activityId) {
|
||||
_loadActivity();
|
||||
}
|
||||
}
|
||||
|
||||
Room get room => widget.room;
|
||||
Room? get room => widget.room;
|
||||
|
||||
bool get isBotRoomMember => room.getParticipants().any(
|
||||
(p) => p.id == BotName.byEnvironment,
|
||||
Room? get parent => Matrix.of(context).client.getRoomById(
|
||||
widget.parentId,
|
||||
);
|
||||
|
||||
String get displayname => room.getLocalizedDisplayname(
|
||||
MatrixLocals(L10n.of(context)),
|
||||
);
|
||||
bool get isBotRoomMember =>
|
||||
room?.getParticipants().any(
|
||||
(p) => p.id == BotName.byEnvironment,
|
||||
) ??
|
||||
false;
|
||||
|
||||
SessionState get state {
|
||||
if (room.ownRole != null) return SessionState.confirmedRole;
|
||||
if (room?.ownRole != null) return SessionState.confirmedRole;
|
||||
if (_selectedRoleId != null) return SessionState.selectedRole;
|
||||
if (room.isRoomAdmin && !_started) return SessionState.notStarted;
|
||||
if (room == null) {
|
||||
return widget.isNew
|
||||
? SessionState.notSelectedRole
|
||||
: SessionState.notStarted;
|
||||
}
|
||||
return SessionState.notSelectedRole;
|
||||
}
|
||||
|
||||
String get descriptionText {
|
||||
String? get descriptionText {
|
||||
switch (state) {
|
||||
case SessionState.confirmedRole:
|
||||
return L10n.of(context).waitingToFillRole(room.remainingRoles);
|
||||
return L10n.of(context).waitingToFillRole(room!.remainingRoles);
|
||||
case SessionState.selectedRole:
|
||||
return room.activityPlan!.learningObjective;
|
||||
return activity!.description;
|
||||
case SessionState.notStarted:
|
||||
return L10n.of(context).letsGo;
|
||||
return null;
|
||||
|
||||
case SessionState.notSelectedRole:
|
||||
return room.isRoomAdmin
|
||||
return room?.isRoomAdmin ?? false
|
||||
? L10n.of(context).chooseRole
|
||||
: L10n.of(context).chooseRoleToParticipate;
|
||||
}
|
||||
}
|
||||
|
||||
String get buttonText => state == SessionState.notStarted
|
||||
? L10n.of(context).start
|
||||
: L10n.of(context).confirm;
|
||||
|
||||
bool get enableButtons => [
|
||||
SessionState.notStarted,
|
||||
SessionState.selectedRole,
|
||||
].contains(state);
|
||||
|
||||
bool canSelectParticipant(String id) {
|
||||
if (state == SessionState.confirmedRole) return false;
|
||||
if (state == SessionState.confirmedRole ||
|
||||
state == SessionState.notStarted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final availableRoles = room.activityPlan!.roles;
|
||||
final assignedRoles = room.assignedRoles ?? {};
|
||||
final availableRoles = activity!.roles;
|
||||
final assignedRoles = room?.assignedRoles ?? {};
|
||||
final unassignedIds = availableRoles.keys
|
||||
.where((id) => !assignedRoles.containsKey(id))
|
||||
.toList();
|
||||
|
|
@ -103,7 +131,7 @@ class ActivitySessionStartController extends State<ActivitySessionStartPage> {
|
|||
|
||||
bool isParticipantSelected(String id) {
|
||||
if (state == SessionState.confirmedRole) {
|
||||
return room.ownRole?.id == id;
|
||||
return room?.ownRole?.id == id;
|
||||
}
|
||||
return _selectedRoleId == id;
|
||||
}
|
||||
|
|
@ -120,33 +148,63 @@ class ActivitySessionStartController extends State<ActivitySessionStartPage> {
|
|||
if (mounted) setState(() => _selectedRoleId = id);
|
||||
}
|
||||
|
||||
Future<void> _loadActivity() async {
|
||||
try {
|
||||
setState(() {
|
||||
loading = true;
|
||||
error = null;
|
||||
});
|
||||
|
||||
final activities = await CourseActivityRepo.get(
|
||||
widget.activityId,
|
||||
[widget.activityId],
|
||||
);
|
||||
|
||||
if (activities.isEmpty) {
|
||||
throw Exception("Activity not found");
|
||||
}
|
||||
|
||||
if (mounted) setState(() => activity = activities.first);
|
||||
} catch (e) {
|
||||
if (mounted) setState(() => error = e);
|
||||
} finally {
|
||||
if (mounted) setState(() => loading = false);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> onTap() async {
|
||||
switch (state) {
|
||||
case SessionState.notStarted:
|
||||
if (mounted) setState(() => _started = true);
|
||||
case SessionState.selectedRole:
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => room.joinActivity(
|
||||
room.activityPlan!.roles[_selectedRoleId!]!,
|
||||
),
|
||||
);
|
||||
if (mounted) setState(() {});
|
||||
case SessionState.notSelectedRole:
|
||||
case SessionState.confirmedRole:
|
||||
break;
|
||||
if (state != SessionState.selectedRole) return;
|
||||
if (room != null) {
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => room!.joinActivity(
|
||||
activity!.roles[_selectedRoleId!]!,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
final resp = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => parent!.launchActivityRoom(
|
||||
activity!,
|
||||
activity!.roles[_selectedRoleId!],
|
||||
),
|
||||
);
|
||||
|
||||
if (!resp.isError) {
|
||||
context.go("/rooms/spaces/${widget.parentId}/${resp.result}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> pingCourse() async {
|
||||
if (room.courseParent == null) {
|
||||
if (room?.courseParent == null) {
|
||||
throw Exception("Activity is not part of a course");
|
||||
}
|
||||
|
||||
await room.courseParent!.sendTextEvent(
|
||||
await room!.courseParent!.sendTextEvent(
|
||||
L10n.of(context).pingParticipantsNotification(
|
||||
room.client.userID!.localpart ?? room.client.userID!,
|
||||
room.getLocalizedDisplayname(MatrixLocals(L10n.of(context))),
|
||||
room!.client.userID!.localpart ?? room!.client.userID!,
|
||||
room!.getLocalizedDisplayname(MatrixLocals(L10n.of(context))),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,10 +8,12 @@ import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart
|
|||
import 'package:fluffychat/pangea/activity_sessions/activity_session_start/activity_session_start_page.dart';
|
||||
import 'package:fluffychat/pangea/activity_sessions/activity_summary_widget.dart';
|
||||
import 'package:fluffychat/pangea/bot/utils/bot_name.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/share_room_button.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/utils/stream_extension.dart';
|
||||
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class ActivitySessionStartView extends StatelessWidget {
|
||||
final ActivitySessionStartController controller;
|
||||
|
|
@ -23,14 +25,28 @@ class ActivitySessionStartView extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final buttonStyle = ElevatedButton.styleFrom(
|
||||
backgroundColor: theme.colorScheme.primaryContainer,
|
||||
foregroundColor: theme.colorScheme.onPrimaryContainer,
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20.0),
|
||||
),
|
||||
);
|
||||
|
||||
return StreamBuilder(
|
||||
stream: controller.room.client.onRoomState.stream
|
||||
stream: Matrix.of(context)
|
||||
.client
|
||||
.onRoomState
|
||||
.stream
|
||||
.rateLimit(const Duration(seconds: 1)),
|
||||
builder: (context, snapshot) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leadingWidth: 52.0,
|
||||
title: Text(controller.displayname),
|
||||
title: controller.activity == null
|
||||
? null
|
||||
: Text(controller.activity!.title),
|
||||
leading: Padding(
|
||||
padding: const EdgeInsets.only(left: 12.0),
|
||||
child: Center(
|
||||
|
|
@ -41,168 +57,190 @@ class ActivitySessionStartView extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
actions: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 12.0),
|
||||
child: SizedBox(
|
||||
width: 40.0,
|
||||
height: 40.0,
|
||||
child: Center(
|
||||
child: ShareRoomButton(room: controller.room),
|
||||
if (controller.room != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 12.0),
|
||||
child: SizedBox(
|
||||
width: 40.0,
|
||||
height: 40.0,
|
||||
child: Center(
|
||||
child: ShareRoomButton(room: controller.room!),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 600.0,
|
||||
),
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
spacing: 12.0,
|
||||
children: [
|
||||
ActivitySummary(
|
||||
room: controller.room,
|
||||
showInstructions: controller.showInstructions,
|
||||
toggleInstructions: controller.toggleInstructions,
|
||||
onTapParticipant: controller.selectRole,
|
||||
isParticipantSelected:
|
||||
controller.isParticipantSelected,
|
||||
canSelectParticipant:
|
||||
controller.canSelectParticipant,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
AnimatedSize(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
top: BorderSide(color: theme.dividerColor),
|
||||
),
|
||||
color: theme.colorScheme.surface,
|
||||
),
|
||||
padding: const EdgeInsets.all(24.0),
|
||||
child: Column(
|
||||
spacing: 16.0,
|
||||
children: [
|
||||
Text(
|
||||
controller.descriptionText,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
child: controller.loading
|
||||
? const Center(child: CircularProgressIndicator.adaptive())
|
||||
: controller.error != null
|
||||
? Center(
|
||||
child: ErrorIndicator(
|
||||
message: L10n.of(context).activityNotFound,
|
||||
),
|
||||
if (controller.state == SessionState.confirmedRole) ...[
|
||||
if (controller.room.courseParent != null)
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor:
|
||||
theme.colorScheme.primaryContainer,
|
||||
foregroundColor:
|
||||
theme.colorScheme.onPrimaryContainer,
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20.0),
|
||||
)
|
||||
: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 600.0,
|
||||
),
|
||||
),
|
||||
onPressed: () => showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: controller.pingCourse,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
L10n.of(context).pingParticipants,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (controller.room.isRoomAdmin) ...[
|
||||
if (!controller.isBotRoomMember)
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor:
|
||||
theme.colorScheme.primaryContainer,
|
||||
foregroundColor:
|
||||
theme.colorScheme.onPrimaryContainer,
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20.0),
|
||||
),
|
||||
),
|
||||
onPressed: () => showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => controller.room
|
||||
.invite(BotName.byEnvironment),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
spacing: 12.0,
|
||||
children: [
|
||||
Text(L10n.of(context).playWithBot),
|
||||
ActivitySummary(
|
||||
activity: controller.activity!,
|
||||
room: controller.room,
|
||||
showInstructions:
|
||||
controller.showInstructions,
|
||||
toggleInstructions:
|
||||
controller.toggleInstructions,
|
||||
onTapParticipant: controller.selectRole,
|
||||
isParticipantSelected:
|
||||
controller.isParticipantSelected,
|
||||
canSelectParticipant:
|
||||
controller.canSelectParticipant,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor:
|
||||
theme.colorScheme.primaryContainer,
|
||||
foregroundColor:
|
||||
theme.colorScheme.onPrimaryContainer,
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20.0),
|
||||
),
|
||||
),
|
||||
AnimatedSize(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
top: BorderSide(color: theme.dividerColor),
|
||||
),
|
||||
color: theme.colorScheme.surface,
|
||||
),
|
||||
onPressed: () => context.go(
|
||||
"/rooms/${controller.room.id}/invite",
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
padding: const EdgeInsets.all(24.0),
|
||||
child: Column(
|
||||
spacing: 16.0,
|
||||
children: [
|
||||
Text(L10n.of(context).inviteFriends),
|
||||
if (controller.descriptionText != null)
|
||||
Text(
|
||||
controller.descriptionText!,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
if (controller.state ==
|
||||
SessionState.notStarted) ...[
|
||||
ElevatedButton(
|
||||
style: buttonStyle,
|
||||
onPressed: () => context.go(
|
||||
"/rooms/spaces/${controller.widget.parentId}/activity/${controller.widget.activityId}?new=true",
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
L10n.of(context).startNewSession,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
ElevatedButton(
|
||||
style: buttonStyle,
|
||||
onPressed: null,
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
L10n.of(context).joinOpenSession,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
] else if (controller.state ==
|
||||
SessionState.confirmedRole) ...[
|
||||
if (controller.room!.courseParent != null)
|
||||
ElevatedButton(
|
||||
style: buttonStyle,
|
||||
onPressed: () =>
|
||||
showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: controller.pingCourse,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
L10n.of(context).pingParticipants,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (controller.room!.isRoomAdmin) ...[
|
||||
if (!controller.isBotRoomMember)
|
||||
ElevatedButton(
|
||||
style: buttonStyle,
|
||||
onPressed: () =>
|
||||
showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => controller.room!
|
||||
.invite(BotName.byEnvironment),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
L10n.of(context).playWithBot,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
ElevatedButton(
|
||||
style: buttonStyle,
|
||||
onPressed: () => context.go(
|
||||
"/rooms/${controller.room!.id}/invite",
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
L10n.of(context).inviteFriends,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
] else
|
||||
ElevatedButton(
|
||||
style: buttonStyle,
|
||||
onPressed: controller.enableButtons
|
||||
? controller.onTap
|
||||
: null,
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
controller.room?.isRoomAdmin ?? true
|
||||
? L10n.of(context).start
|
||||
: L10n.of(context).confirm,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
] else
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor:
|
||||
theme.colorScheme.primaryContainer,
|
||||
foregroundColor:
|
||||
theme.colorScheme.onPrimaryContainer,
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20.0),
|
||||
),
|
||||
),
|
||||
onPressed: controller.enableButtons
|
||||
? controller.onTap
|
||||
: null,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(controller.buttonText),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -5,15 +5,16 @@ import 'package:matrix/matrix.dart';
|
|||
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
|
||||
import 'package:fluffychat/pangea/activity_sessions/activity_participant_list.dart';
|
||||
import 'package:fluffychat/pangea/activity_sessions/activity_role_model.dart';
|
||||
import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/activity_suggestions/activity_suggestion_card_row.dart';
|
||||
import 'package:fluffychat/pangea/activity_sessions/activity_session_details_row.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/url_image_widget.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart';
|
||||
|
||||
class ActivitySummary extends StatelessWidget {
|
||||
final Room room;
|
||||
final ActivityPlanModel activity;
|
||||
final Room? room;
|
||||
|
||||
final bool showInstructions;
|
||||
final VoidCallback toggleInstructions;
|
||||
|
|
@ -25,22 +26,18 @@ class ActivitySummary extends StatelessWidget {
|
|||
|
||||
const ActivitySummary({
|
||||
super.key,
|
||||
required this.room,
|
||||
required this.activity,
|
||||
required this.showInstructions,
|
||||
required this.toggleInstructions,
|
||||
this.onTapParticipant,
|
||||
this.canSelectParticipant,
|
||||
this.isParticipantSelected,
|
||||
this.getParticipantOpacity,
|
||||
this.room,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final activity = room.activityPlan;
|
||||
if (activity == null) {
|
||||
return const SizedBox();
|
||||
}
|
||||
|
||||
final theme = Theme.of(context);
|
||||
return Center(
|
||||
child: Container(
|
||||
|
|
@ -49,21 +46,15 @@ class ActivitySummary extends StatelessWidget {
|
|||
maxWidth: FluffyThemes.columnWidth * 1.5,
|
||||
),
|
||||
child: Column(
|
||||
spacing: 12.0,
|
||||
spacing: 4.0,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: ImageByUrl(
|
||||
imageUrl: activity.imageURL,
|
||||
width: 80.0,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
activity.learningObjective,
|
||||
style: const TextStyle(fontSize: 12.0),
|
||||
ImageByUrl(
|
||||
imageUrl: activity.imageURL,
|
||||
width: 80.0,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
ActivityParticipantList(
|
||||
activity: activity,
|
||||
room: room,
|
||||
onTap: onTapParticipant,
|
||||
canSelect: canSelectParticipant,
|
||||
|
|
@ -78,60 +69,61 @@ class ActivitySummary extends StatelessWidget {
|
|||
spacing: 4.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 6.0,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
Stack(
|
||||
alignment: Alignment.bottomRight,
|
||||
children: [
|
||||
Text(
|
||||
activity.description,
|
||||
style: const TextStyle(
|
||||
fontSize: 12.0,
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: toggleInstructions,
|
||||
style: TextButton.styleFrom(
|
||||
minimumSize: Size.zero,
|
||||
padding: EdgeInsets.zero,
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(0.0),
|
||||
),
|
||||
backgroundColor: theme.colorScheme.surface,
|
||||
),
|
||||
child: Text(
|
||||
showInstructions
|
||||
? L10n.of(context).less
|
||||
: L10n.of(context).more,
|
||||
style: TextStyle(
|
||||
fontSize: 12.0,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (showInstructions) ...[
|
||||
Row(
|
||||
spacing: 8.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
activity.req.mode,
|
||||
style: const TextStyle(fontSize: 12.0),
|
||||
),
|
||||
Row(
|
||||
spacing: 8.0,
|
||||
spacing: 4.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.school, size: 12.0),
|
||||
Text(
|
||||
activity.req.mode,
|
||||
activity.req.cefrLevel.string,
|
||||
style: const TextStyle(fontSize: 12.0),
|
||||
),
|
||||
Row(
|
||||
spacing: 4.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.school, size: 12.0),
|
||||
Text(
|
||||
activity.req.cefrLevel.string,
|
||||
style: const TextStyle(fontSize: 12.0),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: toggleInstructions,
|
||||
child: Row(
|
||||
spacing: 4.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
showInstructions
|
||||
? L10n.of(context).hideInstructions
|
||||
: L10n.of(context).seeInstructions,
|
||||
style: const TextStyle(fontSize: 12.0),
|
||||
),
|
||||
Icon(
|
||||
showInstructions
|
||||
? Icons.arrow_drop_up
|
||||
: Icons.arrow_drop_down,
|
||||
size: 12.0,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (showInstructions) ...[
|
||||
ActivitySuggestionCardRow(
|
||||
ActivitySessionDetailsRow(
|
||||
icon: Symbols.target,
|
||||
iconSize: 16.0,
|
||||
child: Text(
|
||||
|
|
@ -139,7 +131,7 @@ class ActivitySummary extends StatelessWidget {
|
|||
style: const TextStyle(fontSize: 12.0),
|
||||
),
|
||||
),
|
||||
ActivitySuggestionCardRow(
|
||||
ActivitySessionDetailsRow(
|
||||
icon: Symbols.steps,
|
||||
iconSize: 16.0,
|
||||
child: Text(
|
||||
|
|
@ -147,7 +139,7 @@ class ActivitySummary extends StatelessWidget {
|
|||
style: const TextStyle(fontSize: 12.0),
|
||||
),
|
||||
),
|
||||
ActivitySuggestionCardRow(
|
||||
ActivitySessionDetailsRow(
|
||||
icon: Symbols.dictionary,
|
||||
iconSize: 16.0,
|
||||
child: Wrap(
|
||||
|
|
|
|||
|
|
@ -1,79 +1,79 @@
|
|||
import 'dart:convert';
|
||||
// import 'dart:convert';
|
||||
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:http/http.dart';
|
||||
// import 'package:get_storage/get_storage.dart';
|
||||
// import 'package:http/http.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
|
||||
import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
import 'package:fluffychat/pangea/common/network/requests.dart';
|
||||
import 'package:fluffychat/pangea/common/network/urls.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
// import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
|
||||
// import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
// import 'package:fluffychat/pangea/common/network/requests.dart';
|
||||
// import 'package:fluffychat/pangea/common/network/urls.dart';
|
||||
// import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class ActivityPlanRepo {
|
||||
static final GetStorage _activityPlanStorage =
|
||||
GetStorage('activity_plan_by_id_storage');
|
||||
// class ActivityPlanRepo {
|
||||
// static final GetStorage _activityPlanStorage =
|
||||
// GetStorage('activity_plan_by_id_storage');
|
||||
|
||||
static ActivityPlanModel? getCached(String id) {
|
||||
final cachedJson = _activityPlanStorage.read(id);
|
||||
if (cachedJson == null) return null;
|
||||
// static ActivityPlanModel? getCached(String id) {
|
||||
// final cachedJson = _activityPlanStorage.read(id);
|
||||
// if (cachedJson == null) return null;
|
||||
|
||||
try {
|
||||
return ActivityPlanModel.fromJson(cachedJson);
|
||||
} catch (e) {
|
||||
_removeCached(id);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// try {
|
||||
// return ActivityPlanModel.fromJson(cachedJson);
|
||||
// } catch (e) {
|
||||
// _removeCached(id);
|
||||
// return null;
|
||||
// }
|
||||
// }
|
||||
|
||||
static Future<void> _setCached(ActivityPlanModel response) =>
|
||||
_activityPlanStorage.write(response.activityId, response.toJson());
|
||||
// static Future<void> _setCached(ActivityPlanModel response) =>
|
||||
// _activityPlanStorage.write(response.activityId, response.toJson());
|
||||
|
||||
static Future<void> _removeCached(String id) =>
|
||||
_activityPlanStorage.remove(id);
|
||||
// static Future<void> _removeCached(String id) =>
|
||||
// _activityPlanStorage.remove(id);
|
||||
|
||||
static Future<void> set(ActivityPlanModel activity) => _setCached(activity);
|
||||
// static Future<void> set(ActivityPlanModel activity) => _setCached(activity);
|
||||
|
||||
static Future<ActivityPlanModel> get(String id) async {
|
||||
final cached = getCached(id);
|
||||
if (cached != null) return cached;
|
||||
// static Future<ActivityPlanModel> get(String id) async {
|
||||
// final cached = getCached(id);
|
||||
// if (cached != null) return cached;
|
||||
|
||||
final Requests req = Requests(
|
||||
choreoApiKey: Environment.choreoApiKey,
|
||||
accessToken: MatrixState.pangeaController.userController.accessToken,
|
||||
);
|
||||
// final Requests req = Requests(
|
||||
// choreoApiKey: Environment.choreoApiKey,
|
||||
// accessToken: MatrixState.pangeaController.userController.accessToken,
|
||||
// );
|
||||
|
||||
final Response res = await req.get(
|
||||
url: "${PApiUrls.activityPlan}/$id",
|
||||
);
|
||||
// final Response res = await req.get(
|
||||
// url: "${PApiUrls.activityPlan}/$id",
|
||||
// );
|
||||
|
||||
final decodedBody = jsonDecode(utf8.decode(res.bodyBytes));
|
||||
final response = ActivityPlanModel.fromJson(decodedBody["plan"]);
|
||||
// final decodedBody = jsonDecode(utf8.decode(res.bodyBytes));
|
||||
// final response = ActivityPlanModel.fromJson(decodedBody["plan"]);
|
||||
|
||||
_setCached(response);
|
||||
return response;
|
||||
}
|
||||
// _setCached(response);
|
||||
// return response;
|
||||
// }
|
||||
|
||||
static Future<ActivityPlanModel> update(
|
||||
ActivityPlanModel update,
|
||||
) async {
|
||||
final Requests req = Requests(
|
||||
choreoApiKey: Environment.choreoApiKey,
|
||||
accessToken: MatrixState.pangeaController.userController.accessToken,
|
||||
);
|
||||
// static Future<ActivityPlanModel> update(
|
||||
// ActivityPlanModel update,
|
||||
// ) async {
|
||||
// final Requests req = Requests(
|
||||
// choreoApiKey: Environment.choreoApiKey,
|
||||
// accessToken: MatrixState.pangeaController.userController.accessToken,
|
||||
// );
|
||||
|
||||
final Response res = await req.patch(
|
||||
url: "${PApiUrls.activityPlan}/${update.activityId}",
|
||||
body: update.toJson(),
|
||||
);
|
||||
// final Response res = await req.patch(
|
||||
// url: "${PApiUrls.activityPlan}/${update.activityId}",
|
||||
// body: update.toJson(),
|
||||
// );
|
||||
|
||||
final decodedBody = jsonDecode(utf8.decode(res.bodyBytes));
|
||||
final response = ActivityPlanModel.fromJson(decodedBody["plan"]);
|
||||
// final decodedBody = jsonDecode(utf8.decode(res.bodyBytes));
|
||||
// final response = ActivityPlanModel.fromJson(decodedBody["plan"]);
|
||||
|
||||
_removeCached(update.activityId);
|
||||
_setCached(response);
|
||||
// _removeCached(update.activityId);
|
||||
// _setCached(response);
|
||||
|
||||
return response;
|
||||
}
|
||||
// return response;
|
||||
// }
|
||||
|
||||
static Future<void> remove(String id) => _removeCached(id);
|
||||
}
|
||||
// static Future<void> remove(String id) => _removeCached(id);
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,102 +1,102 @@
|
|||
import 'dart:convert';
|
||||
// import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
// import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:http/http.dart';
|
||||
// import 'package:get_storage/get_storage.dart';
|
||||
// import 'package:http/http.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_plan_request.dart';
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_plan_response.dart';
|
||||
import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
import 'package:fluffychat/pangea/common/network/requests.dart';
|
||||
import 'package:fluffychat/pangea/common/network/urls.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
// import 'package:fluffychat/pangea/activity_planner/activity_plan_request.dart';
|
||||
// import 'package:fluffychat/pangea/activity_planner/activity_plan_response.dart';
|
||||
// import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
// import 'package:fluffychat/pangea/common/network/requests.dart';
|
||||
// import 'package:fluffychat/pangea/common/network/urls.dart';
|
||||
// import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class ActivitySearchRepo {
|
||||
static final GetStorage _activityPlanStorage =
|
||||
GetStorage('activity_plan_search_storage');
|
||||
// class ActivitySearchRepo {
|
||||
// static final GetStorage _activityPlanStorage =
|
||||
// GetStorage('activity_plan_search_storage');
|
||||
|
||||
static void set(
|
||||
String storageKey,
|
||||
ActivityPlanResponseWrapper wrappedResponse,
|
||||
) {
|
||||
_activityPlanStorage.write(storageKey, wrappedResponse.toJson());
|
||||
}
|
||||
// static void set(
|
||||
// String storageKey,
|
||||
// ActivityPlanResponseWrapper wrappedResponse,
|
||||
// ) {
|
||||
// _activityPlanStorage.write(storageKey, wrappedResponse.toJson());
|
||||
// }
|
||||
|
||||
static Future<ActivityPlanResponseWrapper> get(
|
||||
ActivityPlanRequest request,
|
||||
) async {
|
||||
final storageKey = "${request.storageKey}_wrapper";
|
||||
final cachedJson = _activityPlanStorage.read(storageKey);
|
||||
if (cachedJson != null) {
|
||||
ActivityPlanResponseWrapper? cached;
|
||||
try {
|
||||
cached = ActivityPlanResponseWrapper.fromJson(cachedJson);
|
||||
} catch (e) {
|
||||
_activityPlanStorage.remove(storageKey);
|
||||
}
|
||||
// static Future<ActivityPlanResponseWrapper> get(
|
||||
// ActivityPlanRequest request,
|
||||
// ) async {
|
||||
// final storageKey = "${request.storageKey}_wrapper";
|
||||
// final cachedJson = _activityPlanStorage.read(storageKey);
|
||||
// if (cachedJson != null) {
|
||||
// ActivityPlanResponseWrapper? cached;
|
||||
// try {
|
||||
// cached = ActivityPlanResponseWrapper.fromJson(cachedJson);
|
||||
// } catch (e) {
|
||||
// _activityPlanStorage.remove(storageKey);
|
||||
// }
|
||||
|
||||
if (cached is ActivityPlanResponseWrapper) {
|
||||
return cached;
|
||||
}
|
||||
}
|
||||
// if (cached is ActivityPlanResponseWrapper) {
|
||||
// return cached;
|
||||
// }
|
||||
// }
|
||||
|
||||
final Requests req = Requests(
|
||||
choreoApiKey: Environment.choreoApiKey,
|
||||
accessToken: MatrixState.pangeaController.userController.accessToken,
|
||||
);
|
||||
// final Requests req = Requests(
|
||||
// choreoApiKey: Environment.choreoApiKey,
|
||||
// accessToken: MatrixState.pangeaController.userController.accessToken,
|
||||
// );
|
||||
|
||||
Response? res;
|
||||
try {
|
||||
res = await req.post(
|
||||
url: PApiUrls.activityPlanSearch,
|
||||
body: request.toJson(),
|
||||
);
|
||||
} catch (err) {
|
||||
debugPrint("err: $err, err is http response: ${err is Response}");
|
||||
if (err is Response) {
|
||||
return ActivityPlanResponseWrapper(
|
||||
response: ActivityPlanResponse(activityPlans: []),
|
||||
statusCode: err.statusCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
// Response? res;
|
||||
// try {
|
||||
// res = await req.post(
|
||||
// url: PApiUrls.activityPlanSearch,
|
||||
// body: request.toJson(),
|
||||
// );
|
||||
// } catch (err) {
|
||||
// debugPrint("err: $err, err is http response: ${err is Response}");
|
||||
// if (err is Response) {
|
||||
// return ActivityPlanResponseWrapper(
|
||||
// response: ActivityPlanResponse(activityPlans: []),
|
||||
// statusCode: err.statusCode,
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
final decodedBody = jsonDecode(utf8.decode(res!.bodyBytes));
|
||||
final response = ActivityPlanResponse.fromJson(decodedBody);
|
||||
final wrappedResponse = ActivityPlanResponseWrapper(
|
||||
response: response,
|
||||
statusCode: res.statusCode,
|
||||
);
|
||||
// final decodedBody = jsonDecode(utf8.decode(res!.bodyBytes));
|
||||
// final response = ActivityPlanResponse.fromJson(decodedBody);
|
||||
// final wrappedResponse = ActivityPlanResponseWrapper(
|
||||
// response: response,
|
||||
// statusCode: res.statusCode,
|
||||
// );
|
||||
|
||||
if (res.statusCode == 200) {
|
||||
set(storageKey, wrappedResponse);
|
||||
}
|
||||
// if (res.statusCode == 200) {
|
||||
// set(storageKey, wrappedResponse);
|
||||
// }
|
||||
|
||||
return wrappedResponse;
|
||||
}
|
||||
}
|
||||
// return wrappedResponse;
|
||||
// }
|
||||
// }
|
||||
|
||||
class ActivityPlanResponseWrapper {
|
||||
final ActivityPlanResponse response;
|
||||
final int statusCode;
|
||||
// class ActivityPlanResponseWrapper {
|
||||
// final ActivityPlanResponse response;
|
||||
// final int statusCode;
|
||||
|
||||
ActivityPlanResponseWrapper({
|
||||
required this.response,
|
||||
required this.statusCode,
|
||||
});
|
||||
// ActivityPlanResponseWrapper({
|
||||
// required this.response,
|
||||
// required this.statusCode,
|
||||
// });
|
||||
|
||||
factory ActivityPlanResponseWrapper.fromJson(Map<String, dynamic> json) {
|
||||
return ActivityPlanResponseWrapper(
|
||||
response: json['activity_plan_response'].fromJson,
|
||||
statusCode: json['activity_response_status'],
|
||||
);
|
||||
}
|
||||
// factory ActivityPlanResponseWrapper.fromJson(Map<String, dynamic> json) {
|
||||
// return ActivityPlanResponseWrapper(
|
||||
// response: json['activity_plan_response'].fromJson,
|
||||
// statusCode: json['activity_response_status'],
|
||||
// );
|
||||
// }
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'activity_plan_response': response.toJson(),
|
||||
'activity_response_status': statusCode,
|
||||
};
|
||||
}
|
||||
}
|
||||
// Map<String, dynamic> toJson() {
|
||||
// return {
|
||||
// 'activity_plan_response': response.toJson(),
|
||||
// 'activity_response_status': statusCode,
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,51 +1,51 @@
|
|||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
// import 'package:fluffychat/l10n/l10n.dart';
|
||||
|
||||
/// 200: All activities successfully retrieved
|
||||
/// 202: Waiting for activities to load
|
||||
/// 504: Timeout
|
||||
/// Other: Error
|
||||
enum ActivitySearchEnum {
|
||||
complete,
|
||||
waiting,
|
||||
timeout,
|
||||
error,
|
||||
}
|
||||
// /// 200: All activities successfully retrieved
|
||||
// /// 202: Waiting for activities to load
|
||||
// /// 504: Timeout
|
||||
// /// Other: Error
|
||||
// enum ActivitySearchEnum {
|
||||
// complete,
|
||||
// waiting,
|
||||
// timeout,
|
||||
// error,
|
||||
// }
|
||||
|
||||
extension ActivitySearchExtension on ActivitySearchEnum {
|
||||
ActivitySearchEnum fromCode(int statusCode) {
|
||||
switch (statusCode) {
|
||||
case 200:
|
||||
return ActivitySearchEnum.complete;
|
||||
case 202:
|
||||
return ActivitySearchEnum.waiting;
|
||||
case 504:
|
||||
return ActivitySearchEnum.timeout;
|
||||
default:
|
||||
return ActivitySearchEnum.error;
|
||||
}
|
||||
}
|
||||
// extension ActivitySearchExtension on ActivitySearchEnum {
|
||||
// ActivitySearchEnum fromCode(int statusCode) {
|
||||
// switch (statusCode) {
|
||||
// case 200:
|
||||
// return ActivitySearchEnum.complete;
|
||||
// case 202:
|
||||
// return ActivitySearchEnum.waiting;
|
||||
// case 504:
|
||||
// return ActivitySearchEnum.timeout;
|
||||
// default:
|
||||
// return ActivitySearchEnum.error;
|
||||
// }
|
||||
// }
|
||||
|
||||
bool get hideCards {
|
||||
switch (this) {
|
||||
case ActivitySearchEnum.complete:
|
||||
case ActivitySearchEnum.waiting:
|
||||
return false;
|
||||
case ActivitySearchEnum.timeout:
|
||||
case ActivitySearchEnum.error:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// bool get hideCards {
|
||||
// switch (this) {
|
||||
// case ActivitySearchEnum.complete:
|
||||
// case ActivitySearchEnum.waiting:
|
||||
// return false;
|
||||
// case ActivitySearchEnum.timeout:
|
||||
// case ActivitySearchEnum.error:
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
|
||||
String message(L10n l10n) {
|
||||
switch (this) {
|
||||
case ActivitySearchEnum.waiting:
|
||||
return l10n.activitySuggestionTimeoutMessage;
|
||||
case ActivitySearchEnum.timeout:
|
||||
return l10n.generatingNewActivities;
|
||||
case ActivitySearchEnum.error:
|
||||
return l10n.errorFetchingActivitiesMessage;
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
// String message(L10n l10n) {
|
||||
// switch (this) {
|
||||
// case ActivitySearchEnum.waiting:
|
||||
// return l10n.activitySuggestionTimeoutMessage;
|
||||
// case ActivitySearchEnum.timeout:
|
||||
// return l10n.generatingNewActivities;
|
||||
// case ActivitySearchEnum.error:
|
||||
// return l10n.errorFetchingActivitiesMessage;
|
||||
// default:
|
||||
// return '';
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,38 +1,38 @@
|
|||
import 'package:fluffychat/pangea/activity_generator/media_enum.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart';
|
||||
// import 'package:fluffychat/pangea/activity_generator/media_enum.dart';
|
||||
// import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart';
|
||||
|
||||
class ActivitySearchRequest {
|
||||
final String targetLanguage;
|
||||
final String languageOfInstructions;
|
||||
final LanguageLevelTypeEnum? languageLevel;
|
||||
// class ActivitySearchRequest {
|
||||
// final String targetLanguage;
|
||||
// final String languageOfInstructions;
|
||||
// final LanguageLevelTypeEnum? languageLevel;
|
||||
|
||||
final String? mode;
|
||||
final String? learningObjective;
|
||||
final String? topic;
|
||||
final MediaEnum? media;
|
||||
final int? numberOfParticipants;
|
||||
// final String? mode;
|
||||
// final String? learningObjective;
|
||||
// final String? topic;
|
||||
// final MediaEnum? media;
|
||||
// final int? numberOfParticipants;
|
||||
|
||||
ActivitySearchRequest({
|
||||
required this.targetLanguage,
|
||||
required this.languageOfInstructions,
|
||||
this.mode,
|
||||
this.learningObjective,
|
||||
this.topic,
|
||||
this.media,
|
||||
this.numberOfParticipants = 2,
|
||||
this.languageLevel = LanguageLevelTypeEnum.preA1,
|
||||
});
|
||||
// ActivitySearchRequest({
|
||||
// required this.targetLanguage,
|
||||
// required this.languageOfInstructions,
|
||||
// this.mode,
|
||||
// this.learningObjective,
|
||||
// this.topic,
|
||||
// this.media,
|
||||
// this.numberOfParticipants = 2,
|
||||
// this.languageLevel = LanguageLevelTypeEnum.preA1,
|
||||
// });
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'target_language': targetLanguage,
|
||||
'language_of_instructions': languageOfInstructions,
|
||||
'language_level': languageLevel,
|
||||
'mode': mode,
|
||||
'objective': learningObjective,
|
||||
'topic': topic,
|
||||
'media': media,
|
||||
'number_of_participants': numberOfParticipants,
|
||||
};
|
||||
}
|
||||
}
|
||||
// Map<String, dynamic> toJson() {
|
||||
// return {
|
||||
// 'target_language': targetLanguage,
|
||||
// 'language_of_instructions': languageOfInstructions,
|
||||
// 'language_level': languageLevel,
|
||||
// 'mode': mode,
|
||||
// 'objective': learningObjective,
|
||||
// 'topic': topic,
|
||||
// 'media': media,
|
||||
// 'number_of_participants': numberOfParticipants,
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -3,11 +3,10 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_planner_builder.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/url_image_widget.dart';
|
||||
|
||||
class ActivitySuggestionCard extends StatelessWidget {
|
||||
final ActivityPlannerBuilderState controller;
|
||||
final ActivityPlanModel activity;
|
||||
final VoidCallback onPressed;
|
||||
final double width;
|
||||
final double height;
|
||||
|
|
@ -18,7 +17,7 @@ class ActivitySuggestionCard extends StatelessWidget {
|
|||
|
||||
const ActivitySuggestionCard({
|
||||
super.key,
|
||||
required this.controller,
|
||||
required this.activity,
|
||||
required this.onPressed,
|
||||
required this.width,
|
||||
required this.height,
|
||||
|
|
@ -27,8 +26,6 @@ class ActivitySuggestionCard extends StatelessWidget {
|
|||
this.iconSize,
|
||||
});
|
||||
|
||||
ActivityPlanModel get activity => controller.updatedActivity;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
|
|
|||
|
|
@ -1,312 +1,312 @@
|
|||
import 'dart:async';
|
||||
// import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
// import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
// import 'package:collection/collection.dart';
|
||||
// import 'package:go_router/go_router.dart';
|
||||
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/activity_generator/activity_plan_generation_repo.dart';
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_planner_builder.dart';
|
||||
import 'package:fluffychat/pangea/activity_suggestions/activity_suggestion_dialog_content.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/full_width_dialog.dart';
|
||||
// import 'package:fluffychat/config/themes.dart';
|
||||
// import 'package:fluffychat/l10n/l10n.dart';
|
||||
// import 'package:fluffychat/pangea/activity_generator/activity_plan_generation_repo.dart';
|
||||
// import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
|
||||
// import 'package:fluffychat/pangea/activity_planner/activity_planner_builder.dart';
|
||||
// import 'package:fluffychat/pangea/activity_suggestions/activity_suggestion_dialog_content.dart';
|
||||
// import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
// import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
|
||||
// import 'package:fluffychat/pangea/common/widgets/full_width_dialog.dart';
|
||||
|
||||
class ActivitySuggestionDialog extends StatefulWidget {
|
||||
final ActivityPlannerBuilderState controller;
|
||||
final String buttonText;
|
||||
// class ActivitySuggestionDialog extends StatefulWidget {
|
||||
// final ActivityPlannerBuilderState controller;
|
||||
// final String buttonText;
|
||||
|
||||
final Function(ActivityPlanModel)? replaceActivity;
|
||||
// final Function(ActivityPlanModel)? replaceActivity;
|
||||
|
||||
const ActivitySuggestionDialog({
|
||||
required this.controller,
|
||||
required this.buttonText,
|
||||
this.replaceActivity,
|
||||
super.key,
|
||||
});
|
||||
// const ActivitySuggestionDialog({
|
||||
// required this.controller,
|
||||
// required this.buttonText,
|
||||
// this.replaceActivity,
|
||||
// super.key,
|
||||
// });
|
||||
|
||||
@override
|
||||
ActivitySuggestionDialogState createState() =>
|
||||
ActivitySuggestionDialogState();
|
||||
}
|
||||
// @override
|
||||
// ActivitySuggestionDialogState createState() =>
|
||||
// ActivitySuggestionDialogState();
|
||||
// }
|
||||
|
||||
class ActivitySuggestionDialogState extends State<ActivitySuggestionDialog> {
|
||||
bool _loading = false;
|
||||
String? _regenerateError;
|
||||
String? _launchError;
|
||||
// class ActivitySuggestionDialogState extends State<ActivitySuggestionDialog> {
|
||||
// bool _loading = false;
|
||||
// String? _regenerateError;
|
||||
// String? _launchError;
|
||||
|
||||
double get _width => FluffyThemes.isColumnMode(context)
|
||||
? 400.0
|
||||
: MediaQuery.of(context).size.width;
|
||||
// double get _width => FluffyThemes.isColumnMode(context)
|
||||
// ? 400.0
|
||||
// : MediaQuery.of(context).size.width;
|
||||
|
||||
StreamSubscription? _stateSubscription;
|
||||
// StreamSubscription? _stateSubscription;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_stateSubscription = widget.controller.stateStream.stream.listen((state) {
|
||||
if (mounted) setState(() {});
|
||||
});
|
||||
}
|
||||
// @override
|
||||
// void initState() {
|
||||
// super.initState();
|
||||
// _stateSubscription = widget.controller.stateStream.stream.listen((state) {
|
||||
// if (mounted) setState(() {});
|
||||
// });
|
||||
// }
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_stateSubscription?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
// @override
|
||||
// void dispose() {
|
||||
// _stateSubscription?.cancel();
|
||||
// super.dispose();
|
||||
// }
|
||||
|
||||
Future<void> launchActivity() async {
|
||||
try {
|
||||
if (!widget.controller.room.isSpace) {
|
||||
throw Exception(
|
||||
"Cannot launch activity in a non-space room",
|
||||
);
|
||||
}
|
||||
// Future<void> launchActivity() async {
|
||||
// try {
|
||||
// if (!widget.controller.room.isSpace) {
|
||||
// throw Exception(
|
||||
// "Cannot launch activity in a non-space room",
|
||||
// );
|
||||
// }
|
||||
|
||||
setState(() {
|
||||
_loading = true;
|
||||
_regenerateError = null;
|
||||
_launchError = null;
|
||||
});
|
||||
// setState(() {
|
||||
// _loading = true;
|
||||
// _regenerateError = null;
|
||||
// _launchError = null;
|
||||
// });
|
||||
|
||||
final ids = await widget.controller.launchToSpace();
|
||||
ids.length == 1
|
||||
? context
|
||||
.go("/rooms/spaces/${widget.controller.room.id}/${ids.first}")
|
||||
: context.go("/rooms/spaces/${widget.controller.room.id}/details");
|
||||
Navigator.of(context).pop();
|
||||
} catch (e, s) {
|
||||
_launchError = L10n.of(context).errorLaunchActivityMessage;
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {
|
||||
"request": widget.controller.updatedRequest.toJson(),
|
||||
},
|
||||
);
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_loading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// final ids = await widget.controller.launchToSpace();
|
||||
// ids.length == 1
|
||||
// ? context
|
||||
// .go("/rooms/spaces/${widget.controller.room.id}/${ids.first}")
|
||||
// : context.go("/rooms/spaces/${widget.controller.room.id}/details");
|
||||
// Navigator.of(context).pop();
|
||||
// } catch (e, s) {
|
||||
// _launchError = L10n.of(context).errorLaunchActivityMessage;
|
||||
// ErrorHandler.logError(
|
||||
// e: e,
|
||||
// s: s,
|
||||
// data: {
|
||||
// "request": widget.controller.updatedRequest.toJson(),
|
||||
// },
|
||||
// );
|
||||
// } finally {
|
||||
// if (mounted) {
|
||||
// setState(() {
|
||||
// _loading = false;
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
Future<void> onRegenerate() async {
|
||||
setState(() {
|
||||
_loading = true;
|
||||
_regenerateError = null;
|
||||
_launchError = null;
|
||||
});
|
||||
// Future<void> onRegenerate() async {
|
||||
// setState(() {
|
||||
// _loading = true;
|
||||
// _regenerateError = null;
|
||||
// _launchError = null;
|
||||
// });
|
||||
|
||||
try {
|
||||
final resp = await ActivityPlanGenerationRepo.get(
|
||||
widget.controller.updatedRequest,
|
||||
force: true,
|
||||
);
|
||||
final plan = resp.activityPlans.firstOrNull;
|
||||
if (plan == null) {
|
||||
throw Exception("No activity plan generated");
|
||||
}
|
||||
// try {
|
||||
// final resp = await ActivityPlanGenerationRepo.get(
|
||||
// widget.controller.updatedRequest,
|
||||
// force: true,
|
||||
// );
|
||||
// final plan = resp.activityPlans.firstOrNull;
|
||||
// if (plan == null) {
|
||||
// throw Exception("No activity plan generated");
|
||||
// }
|
||||
|
||||
widget.replaceActivity?.call(plan);
|
||||
await widget.controller.overrideActivity(plan);
|
||||
} catch (e, s) {
|
||||
_regenerateError = L10n.of(context).errorRegenerateActivityMessage;
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {
|
||||
"request": widget.controller.updatedRequest.toJson(),
|
||||
},
|
||||
);
|
||||
return;
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_loading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// widget.replaceActivity?.call(plan);
|
||||
// await widget.controller.overrideActivity(plan);
|
||||
// } catch (e, s) {
|
||||
// _regenerateError = L10n.of(context).errorRegenerateActivityMessage;
|
||||
// ErrorHandler.logError(
|
||||
// e: e,
|
||||
// s: s,
|
||||
// data: {
|
||||
// "request": widget.controller.updatedRequest.toJson(),
|
||||
// },
|
||||
// );
|
||||
// return;
|
||||
// } finally {
|
||||
// if (mounted) {
|
||||
// setState(() {
|
||||
// _loading = false;
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
void _resetActivity() {
|
||||
widget.controller.resetActivity();
|
||||
setState(() {
|
||||
_loading = false;
|
||||
_regenerateError = null;
|
||||
_launchError = null;
|
||||
});
|
||||
}
|
||||
// void _resetActivity() {
|
||||
// widget.controller.resetActivity();
|
||||
// setState(() {
|
||||
// _loading = false;
|
||||
// _regenerateError = null;
|
||||
// _launchError = null;
|
||||
// });
|
||||
// }
|
||||
|
||||
ButtonStyle get buttonStyle => ElevatedButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
|
||||
foregroundColor: Theme.of(context).colorScheme.onPrimaryContainer,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12.0,
|
||||
),
|
||||
);
|
||||
// ButtonStyle get buttonStyle => ElevatedButton.styleFrom(
|
||||
// backgroundColor: Theme.of(context).colorScheme.primaryContainer,
|
||||
// foregroundColor: Theme.of(context).colorScheme.onPrimaryContainer,
|
||||
// padding: const EdgeInsets.symmetric(
|
||||
// horizontal: 12.0,
|
||||
// ),
|
||||
// );
|
||||
|
||||
double get width => FluffyThemes.isColumnMode(context)
|
||||
? 400.0
|
||||
: MediaQuery.of(context).size.width;
|
||||
// double get width => FluffyThemes.isColumnMode(context)
|
||||
// ? 400.0
|
||||
// : MediaQuery.of(context).size.width;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final buttonStyle = this.buttonStyle;
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// final theme = Theme.of(context);
|
||||
// final buttonStyle = this.buttonStyle;
|
||||
|
||||
final body = Stack(
|
||||
alignment: Alignment.topCenter,
|
||||
children: [
|
||||
DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.surface,
|
||||
),
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
if (_regenerateError != null || _launchError != null) {
|
||||
return Center(
|
||||
child: Column(
|
||||
spacing: 16.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ErrorIndicator(
|
||||
message: _regenerateError ?? _launchError!,
|
||||
),
|
||||
if (_regenerateError != null)
|
||||
Row(
|
||||
spacing: 8.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: onRegenerate,
|
||||
style: buttonStyle,
|
||||
child: Text(L10n.of(context).tryAgain),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: _resetActivity,
|
||||
style: buttonStyle,
|
||||
child: Text(L10n.of(context).reset),
|
||||
),
|
||||
],
|
||||
)
|
||||
else
|
||||
ElevatedButton(
|
||||
onPressed: launchActivity,
|
||||
style: buttonStyle,
|
||||
child: Text(L10n.of(context).tryAgain),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
// final body = Stack(
|
||||
// alignment: Alignment.topCenter,
|
||||
// children: [
|
||||
// DecoratedBox(
|
||||
// decoration: BoxDecoration(
|
||||
// color: theme.colorScheme.surface,
|
||||
// ),
|
||||
// child: Builder(
|
||||
// builder: (context) {
|
||||
// if (_regenerateError != null || _launchError != null) {
|
||||
// return Center(
|
||||
// child: Column(
|
||||
// spacing: 16.0,
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// ErrorIndicator(
|
||||
// message: _regenerateError ?? _launchError!,
|
||||
// ),
|
||||
// if (_regenerateError != null)
|
||||
// Row(
|
||||
// spacing: 8.0,
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// ElevatedButton(
|
||||
// onPressed: onRegenerate,
|
||||
// style: buttonStyle,
|
||||
// child: Text(L10n.of(context).tryAgain),
|
||||
// ),
|
||||
// ElevatedButton(
|
||||
// onPressed: _resetActivity,
|
||||
// style: buttonStyle,
|
||||
// child: Text(L10n.of(context).reset),
|
||||
// ),
|
||||
// ],
|
||||
// )
|
||||
// else
|
||||
// ElevatedButton(
|
||||
// onPressed: launchActivity,
|
||||
// style: buttonStyle,
|
||||
// child: Text(L10n.of(context).tryAgain),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
|
||||
if (_loading) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator.adaptive(),
|
||||
);
|
||||
}
|
||||
// if (_loading) {
|
||||
// return const Center(
|
||||
// child: CircularProgressIndicator.adaptive(),
|
||||
// );
|
||||
// }
|
||||
|
||||
return Form(
|
||||
key: widget.controller.formKey,
|
||||
child: ActivitySuggestionDialogContent(controller: this),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 4.0,
|
||||
left: 4.0,
|
||||
child: IconButton.filled(
|
||||
style: IconButton.styleFrom(
|
||||
backgroundColor: theme.colorScheme.surface.withAlpha(170),
|
||||
),
|
||||
icon: Icon(
|
||||
Icons.close_outlined,
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
onPressed: Navigator.of(context).pop,
|
||||
tooltip: L10n.of(context).close,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
// return Form(
|
||||
// key: widget.controller.formKey,
|
||||
// child: ActivitySuggestionDialogContent(controller: this),
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
// Positioned(
|
||||
// top: 4.0,
|
||||
// left: 4.0,
|
||||
// child: IconButton.filled(
|
||||
// style: IconButton.styleFrom(
|
||||
// backgroundColor: theme.colorScheme.surface.withAlpha(170),
|
||||
// ),
|
||||
// icon: Icon(
|
||||
// Icons.close_outlined,
|
||||
// color: theme.colorScheme.onSurface,
|
||||
// ),
|
||||
// onPressed: Navigator.of(context).pop,
|
||||
// tooltip: L10n.of(context).close,
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
|
||||
return FullWidthDialog(
|
||||
dialogContent: body,
|
||||
maxWidth: _width,
|
||||
maxHeight: 650.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
// return FullWidthDialog(
|
||||
// dialogContent: body,
|
||||
// maxWidth: _width,
|
||||
// maxHeight: 650.0,
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
class NumberCounter extends StatelessWidget {
|
||||
final int count;
|
||||
final Function(int) update;
|
||||
// class NumberCounter extends StatelessWidget {
|
||||
// final int count;
|
||||
// final Function(int) update;
|
||||
|
||||
final int? min;
|
||||
final int? max;
|
||||
// final int? min;
|
||||
// final int? max;
|
||||
|
||||
const NumberCounter({
|
||||
super.key,
|
||||
required this.count,
|
||||
required this.update,
|
||||
this.min,
|
||||
this.max,
|
||||
});
|
||||
// const NumberCounter({
|
||||
// super.key,
|
||||
// required this.count,
|
||||
// required this.update,
|
||||
// this.min,
|
||||
// this.max,
|
||||
// });
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
),
|
||||
child: Row(
|
||||
spacing: 4.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.remove),
|
||||
iconSize: 24.0,
|
||||
style: IconButton.styleFrom(
|
||||
padding: const EdgeInsets.all(0.0),
|
||||
),
|
||||
onPressed: min == null || count - 1 >= min!
|
||||
? () {
|
||||
if (count > 0) {
|
||||
update(count - 1);
|
||||
}
|
||||
}
|
||||
: null,
|
||||
),
|
||||
Text(
|
||||
count.toString(),
|
||||
style: const TextStyle(
|
||||
fontSize: 16.0,
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.add),
|
||||
iconSize: 24.0,
|
||||
style: max == null || count + 1 <= max!
|
||||
? IconButton.styleFrom(
|
||||
padding: const EdgeInsets.all(0.0),
|
||||
)
|
||||
: null,
|
||||
onPressed: max == null || count + 1 <= max!
|
||||
? () {
|
||||
update(count + 1);
|
||||
}
|
||||
: null,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return Container(
|
||||
// decoration: BoxDecoration(
|
||||
// borderRadius: BorderRadius.circular(12.0),
|
||||
// color: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
// ),
|
||||
// child: Row(
|
||||
// spacing: 4.0,
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// IconButton(
|
||||
// icon: const Icon(Icons.remove),
|
||||
// iconSize: 24.0,
|
||||
// style: IconButton.styleFrom(
|
||||
// padding: const EdgeInsets.all(0.0),
|
||||
// ),
|
||||
// onPressed: min == null || count - 1 >= min!
|
||||
// ? () {
|
||||
// if (count > 0) {
|
||||
// update(count - 1);
|
||||
// }
|
||||
// }
|
||||
// : null,
|
||||
// ),
|
||||
// Text(
|
||||
// count.toString(),
|
||||
// style: const TextStyle(
|
||||
// fontSize: 16.0,
|
||||
// ),
|
||||
// ),
|
||||
// IconButton(
|
||||
// icon: const Icon(Icons.add),
|
||||
// iconSize: 24.0,
|
||||
// style: max == null || count + 1 <= max!
|
||||
// ? IconButton.styleFrom(
|
||||
// padding: const EdgeInsets.all(0.0),
|
||||
// )
|
||||
// : null,
|
||||
// onPressed: max == null || count + 1 <= max!
|
||||
// ? () {
|
||||
// update(count + 1);
|
||||
// }
|
||||
// : null,
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,317 +1,317 @@
|
|||
// shows n rows of activity suggestions vertically, where n is the number of rows
|
||||
// as the user tries to scroll horizontally to the right, the client will fetch more activity suggestions
|
||||
// // shows n rows of activity suggestions vertically, where n is the number of rows
|
||||
// // as the user tries to scroll horizontally to the right, the client will fetch more activity suggestions
|
||||
|
||||
import 'dart:async';
|
||||
// import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
// import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
import 'package:shimmer/shimmer.dart';
|
||||
// import 'package:collection/collection.dart';
|
||||
// import 'package:matrix/matrix.dart';
|
||||
// import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
// import 'package:shimmer/shimmer.dart';
|
||||
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/activity_generator/media_enum.dart';
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_plan_request.dart';
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_planner_builder.dart';
|
||||
import 'package:fluffychat/pangea/activity_suggestions/activity_plan_search_repo.dart';
|
||||
import 'package:fluffychat/pangea/activity_suggestions/activity_search_enum.dart';
|
||||
import 'package:fluffychat/pangea/activity_suggestions/activity_suggestion_card.dart';
|
||||
import 'package:fluffychat/pangea/activity_suggestions/activity_suggestion_dialog.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
// import 'package:fluffychat/config/themes.dart';
|
||||
// import 'package:fluffychat/l10n/l10n.dart';
|
||||
// import 'package:fluffychat/pangea/activity_generator/media_enum.dart';
|
||||
// import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
|
||||
// import 'package:fluffychat/pangea/activity_planner/activity_plan_request.dart';
|
||||
// import 'package:fluffychat/pangea/activity_planner/activity_planner_builder.dart';
|
||||
// import 'package:fluffychat/pangea/activity_suggestions/activity_plan_search_repo.dart';
|
||||
// import 'package:fluffychat/pangea/activity_suggestions/activity_search_enum.dart';
|
||||
// import 'package:fluffychat/pangea/activity_suggestions/activity_suggestion_card.dart';
|
||||
// import 'package:fluffychat/pangea/activity_suggestions/activity_suggestion_dialog.dart';
|
||||
// import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
// import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
|
||||
// import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart';
|
||||
// import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart';
|
||||
// import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class ActivitySuggestionsArea extends StatefulWidget {
|
||||
final Axis? scrollDirection;
|
||||
final Room room;
|
||||
// class ActivitySuggestionsArea extends StatefulWidget {
|
||||
// final Axis? scrollDirection;
|
||||
// final Room room;
|
||||
|
||||
const ActivitySuggestionsArea({
|
||||
super.key,
|
||||
this.scrollDirection,
|
||||
required this.room,
|
||||
});
|
||||
@override
|
||||
ActivitySuggestionsAreaState createState() => ActivitySuggestionsAreaState();
|
||||
}
|
||||
// const ActivitySuggestionsArea({
|
||||
// super.key,
|
||||
// this.scrollDirection,
|
||||
// required this.room,
|
||||
// });
|
||||
// @override
|
||||
// ActivitySuggestionsAreaState createState() => ActivitySuggestionsAreaState();
|
||||
// }
|
||||
|
||||
class ActivitySuggestionsAreaState extends State<ActivitySuggestionsArea> {
|
||||
StreamSubscription? _languageStream;
|
||||
// class ActivitySuggestionsAreaState extends State<ActivitySuggestionsArea> {
|
||||
// StreamSubscription? _languageStream;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_setActivityItems();
|
||||
// @override
|
||||
// void initState() {
|
||||
// super.initState();
|
||||
// _setActivityItems();
|
||||
|
||||
_languageStream ??= MatrixState
|
||||
.pangeaController.userController.languageStream.stream
|
||||
.listen((update) {
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) => _setActivityItems(),
|
||||
);
|
||||
});
|
||||
}
|
||||
// _languageStream ??= MatrixState
|
||||
// .pangeaController.userController.languageStream.stream
|
||||
// .listen((update) {
|
||||
// WidgetsBinding.instance.addPostFrameCallback(
|
||||
// (_) => _setActivityItems(),
|
||||
// );
|
||||
// });
|
||||
// }
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_scrollController.dispose();
|
||||
_languageStream?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
// @override
|
||||
// void dispose() {
|
||||
// _scrollController.dispose();
|
||||
// _languageStream?.cancel();
|
||||
// super.dispose();
|
||||
// }
|
||||
|
||||
// _loading is true when _setActivityItems is currently requesting activities
|
||||
bool _loading = true;
|
||||
ActivitySearchEnum _status = ActivitySearchEnum.waiting;
|
||||
// // _loading is true when _setActivityItems is currently requesting activities
|
||||
// bool _loading = true;
|
||||
// ActivitySearchEnum _status = ActivitySearchEnum.waiting;
|
||||
|
||||
bool get _isColumnMode => FluffyThemes.isColumnMode(context);
|
||||
// bool get _isColumnMode => FluffyThemes.isColumnMode(context);
|
||||
|
||||
final List<ActivityPlanModel> _activityItems = [];
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
double get cardHeight => _isColumnMode ? 325.0 : 250.0;
|
||||
double get cardWidth => _isColumnMode ? 225.0 : 150.0;
|
||||
// final List<ActivityPlanModel> _activityItems = [];
|
||||
// final ScrollController _scrollController = ScrollController();
|
||||
// double get cardHeight => _isColumnMode ? 325.0 : 250.0;
|
||||
// double get cardWidth => _isColumnMode ? 225.0 : 150.0;
|
||||
|
||||
String get instructionLanguage =>
|
||||
MatrixState.pangeaController.languageController.userL1?.langCode ??
|
||||
LanguageKeys.defaultLanguage;
|
||||
String get targetLanguage =>
|
||||
MatrixState.pangeaController.languageController.userL2?.langCode ??
|
||||
LanguageKeys.defaultLanguage;
|
||||
// String get instructionLanguage =>
|
||||
// MatrixState.pangeaController.languageController.userL1?.langCode ??
|
||||
// LanguageKeys.defaultLanguage;
|
||||
// String get targetLanguage =>
|
||||
// MatrixState.pangeaController.languageController.userL2?.langCode ??
|
||||
// LanguageKeys.defaultLanguage;
|
||||
|
||||
ActivityPlanRequest get _request {
|
||||
return ActivityPlanRequest(
|
||||
topic: "",
|
||||
mode: "",
|
||||
objective: "",
|
||||
media: MediaEnum.nan,
|
||||
cefrLevel: LanguageLevelTypeEnum.a1,
|
||||
languageOfInstructions: instructionLanguage,
|
||||
targetLanguage: targetLanguage,
|
||||
numberOfParticipants: 3,
|
||||
count: 5,
|
||||
);
|
||||
}
|
||||
// ActivityPlanRequest get _request {
|
||||
// return ActivityPlanRequest(
|
||||
// topic: "",
|
||||
// mode: "",
|
||||
// objective: "",
|
||||
// media: MediaEnum.nan,
|
||||
// cefrLevel: LanguageLevelTypeEnum.a1,
|
||||
// languageOfInstructions: instructionLanguage,
|
||||
// targetLanguage: targetLanguage,
|
||||
// numberOfParticipants: 3,
|
||||
// count: 5,
|
||||
// );
|
||||
// }
|
||||
|
||||
Future<void> _setActivityItems({int retries = 0}) async {
|
||||
if (retries > 3) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_status = ActivitySearchEnum.timeout;
|
||||
_loading = false;
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Future<void> _setActivityItems({int retries = 0}) async {
|
||||
// if (retries > 3) {
|
||||
// if (mounted) {
|
||||
// setState(() {
|
||||
// _status = ActivitySearchEnum.timeout;
|
||||
// _loading = false;
|
||||
// });
|
||||
// }
|
||||
// return;
|
||||
// }
|
||||
|
||||
try {
|
||||
if (retries == 0 && mounted) {
|
||||
setState(() {
|
||||
_activityItems.clear();
|
||||
_loading = true;
|
||||
_status = ActivitySearchEnum.waiting;
|
||||
});
|
||||
}
|
||||
// try {
|
||||
// if (retries == 0 && mounted) {
|
||||
// setState(() {
|
||||
// _activityItems.clear();
|
||||
// _loading = true;
|
||||
// _status = ActivitySearchEnum.waiting;
|
||||
// });
|
||||
// }
|
||||
|
||||
final resp = await ActivitySearchRepo.get(_request).timeout(
|
||||
const Duration(seconds: 5),
|
||||
onTimeout: () {
|
||||
if (mounted) setState(() => _status = ActivitySearchEnum.timeout);
|
||||
// final resp = await ActivitySearchRepo.get(_request).timeout(
|
||||
// const Duration(seconds: 5),
|
||||
// onTimeout: () {
|
||||
// if (mounted) setState(() => _status = ActivitySearchEnum.timeout);
|
||||
|
||||
Future.delayed(const Duration(seconds: 5), () {
|
||||
if (mounted) _setActivityItems(retries: retries + 1);
|
||||
});
|
||||
// Future.delayed(const Duration(seconds: 5), () {
|
||||
// if (mounted) _setActivityItems(retries: retries + 1);
|
||||
// });
|
||||
|
||||
return Future<ActivityPlanResponseWrapper>.error(
|
||||
TimeoutException(
|
||||
L10n.of(context).activitySuggestionTimeoutMessage,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
_activityItems.addAll(resp.response.activityPlans);
|
||||
_status = _status.fromCode(resp.statusCode);
|
||||
if (_status != ActivitySearchEnum.error) {
|
||||
if (_activityItems.isEmpty) {
|
||||
if (mounted && retries != 0) {
|
||||
setState(() => _status = ActivitySearchEnum.timeout);
|
||||
}
|
||||
// return Future<ActivityPlanResponseWrapper>.error(
|
||||
// TimeoutException(
|
||||
// L10n.of(context).activitySuggestionTimeoutMessage,
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
// );
|
||||
// _activityItems.addAll(resp.response.activityPlans);
|
||||
// _status = _status.fromCode(resp.statusCode);
|
||||
// if (_status != ActivitySearchEnum.error) {
|
||||
// if (_activityItems.isEmpty) {
|
||||
// if (mounted && retries != 0) {
|
||||
// setState(() => _status = ActivitySearchEnum.timeout);
|
||||
// }
|
||||
|
||||
Future.delayed(const Duration(seconds: 5), () {
|
||||
if (mounted) _setActivityItems(retries: retries + 1);
|
||||
});
|
||||
} else {
|
||||
if (mounted) setState(() => _loading = false);
|
||||
}
|
||||
}
|
||||
} on TimeoutException {
|
||||
rethrow;
|
||||
} catch (e, s) {
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {
|
||||
'retries': retries,
|
||||
'request': _request.toJson(),
|
||||
},
|
||||
level: SentryLevel.warning,
|
||||
);
|
||||
}
|
||||
}
|
||||
// Future.delayed(const Duration(seconds: 5), () {
|
||||
// if (mounted) _setActivityItems(retries: retries + 1);
|
||||
// });
|
||||
// } else {
|
||||
// if (mounted) setState(() => _loading = false);
|
||||
// }
|
||||
// }
|
||||
// } on TimeoutException {
|
||||
// rethrow;
|
||||
// } catch (e, s) {
|
||||
// ErrorHandler.logError(
|
||||
// e: e,
|
||||
// s: s,
|
||||
// data: {
|
||||
// 'retries': retries,
|
||||
// 'request': _request.toJson(),
|
||||
// },
|
||||
// level: SentryLevel.warning,
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
void _onReplaceActivity(int index, ActivityPlanModel a) {
|
||||
setState(() => _activityItems[index] = a);
|
||||
}
|
||||
// void _onReplaceActivity(int index, ActivityPlanModel a) {
|
||||
// setState(() => _activityItems[index] = a);
|
||||
// }
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// final theme = Theme.of(context);
|
||||
|
||||
// Show all loaded activities, or a loading view if there are none
|
||||
final List<Widget> cards = _activityItems.isEmpty
|
||||
? List.generate(5, (i) {
|
||||
return Shimmer.fromColors(
|
||||
baseColor: theme.colorScheme.primary.withAlpha(20),
|
||||
highlightColor: theme.colorScheme.primary.withAlpha(50),
|
||||
child: Container(
|
||||
height: cardHeight,
|
||||
width: cardWidth,
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(24.0),
|
||||
),
|
||||
),
|
||||
);
|
||||
})
|
||||
: _activityItems
|
||||
.mapIndexed((index, activity) {
|
||||
return ActivityPlannerBuilder(
|
||||
initialActivity: activity,
|
||||
room: widget.room,
|
||||
builder: (controller) {
|
||||
return ActivitySuggestionCard(
|
||||
controller: controller,
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return ActivitySuggestionDialog(
|
||||
controller: controller,
|
||||
buttonText: L10n.of(context).saveAndLaunch,
|
||||
replaceActivity: (a) =>
|
||||
_onReplaceActivity(index, a),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
width: cardWidth,
|
||||
height: cardHeight,
|
||||
);
|
||||
},
|
||||
);
|
||||
})
|
||||
.cast<Widget>()
|
||||
.toList();
|
||||
// // Show all loaded activities, or a loading view if there are none
|
||||
// final List<Widget> cards = _activityItems.isEmpty
|
||||
// ? List.generate(5, (i) {
|
||||
// return Shimmer.fromColors(
|
||||
// baseColor: theme.colorScheme.primary.withAlpha(20),
|
||||
// highlightColor: theme.colorScheme.primary.withAlpha(50),
|
||||
// child: Container(
|
||||
// height: cardHeight,
|
||||
// width: cardWidth,
|
||||
// decoration: BoxDecoration(
|
||||
// color: theme.colorScheme.surfaceContainer,
|
||||
// borderRadius: BorderRadius.circular(24.0),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// })
|
||||
// : _activityItems
|
||||
// .mapIndexed((index, activity) {
|
||||
// return ActivityPlannerBuilder(
|
||||
// initialActivity: activity,
|
||||
// room: widget.room,
|
||||
// builder: (controller) {
|
||||
// return ActivitySuggestionCard(
|
||||
// controller: controller,
|
||||
// onPressed: () {
|
||||
// showDialog(
|
||||
// context: context,
|
||||
// builder: (context) {
|
||||
// return ActivitySuggestionDialog(
|
||||
// controller: controller,
|
||||
// buttonText: L10n.of(context).saveAndLaunch,
|
||||
// replaceActivity: (a) =>
|
||||
// _onReplaceActivity(index, a),
|
||||
// );
|
||||
// },
|
||||
// );
|
||||
// },
|
||||
// width: cardWidth,
|
||||
// height: cardHeight,
|
||||
// );
|
||||
// },
|
||||
// );
|
||||
// })
|
||||
// .cast<Widget>()
|
||||
// .toList();
|
||||
|
||||
final scrollDirection = widget.scrollDirection ??
|
||||
(_isColumnMode ? Axis.horizontal : Axis.vertical);
|
||||
// final scrollDirection = widget.scrollDirection ??
|
||||
// (_isColumnMode ? Axis.horizontal : Axis.vertical);
|
||||
|
||||
return Column(
|
||||
spacing: 8.0,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
AnimatedSize(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
child: _status.hideCards
|
||||
? Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
spacing: 16.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ErrorIndicator(
|
||||
message: _status.message(L10n.of(context)),
|
||||
),
|
||||
if (_loading && _status == ActivitySearchEnum.timeout)
|
||||
const CircularProgressIndicator(),
|
||||
if (!_loading && _status == ActivitySearchEnum.timeout)
|
||||
ElevatedButton(
|
||||
onPressed: _setActivityItems,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: theme.colorScheme.primaryContainer,
|
||||
foregroundColor:
|
||||
theme.colorScheme.onPrimaryContainer,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12.0,
|
||||
),
|
||||
),
|
||||
child: Text(L10n.of(context).tryAgain),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Container(
|
||||
decoration: const BoxDecoration(),
|
||||
child: Column(
|
||||
children: [
|
||||
scrollDirection == Axis.horizontal
|
||||
? Scrollbar(
|
||||
thumbVisibility: true,
|
||||
controller: _scrollController,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16.0),
|
||||
child: SingleChildScrollView(
|
||||
controller: _scrollController,
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
spacing: 8.0,
|
||||
children: cards,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: SizedBox(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: Wrap(
|
||||
alignment: WrapAlignment.spaceEvenly,
|
||||
runSpacing: 16.0,
|
||||
spacing: 4.0,
|
||||
children: cards,
|
||||
),
|
||||
),
|
||||
if (cards.length < 5)
|
||||
Padding(
|
||||
padding: const EdgeInsetsGeometry.all(16.0),
|
||||
child: ErrorIndicator(
|
||||
message: _status.message(L10n.of(context)),
|
||||
),
|
||||
),
|
||||
if (cards.length < 5 && _loading)
|
||||
const CircularProgressIndicator(),
|
||||
if (cards.length < 5 && !_loading)
|
||||
Padding(
|
||||
padding: const EdgeInsetsGeometry.only(bottom: 16),
|
||||
child: ElevatedButton(
|
||||
onPressed: _setActivityItems,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor:
|
||||
theme.colorScheme.primaryContainer,
|
||||
foregroundColor:
|
||||
theme.colorScheme.onPrimaryContainer,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12.0,
|
||||
),
|
||||
),
|
||||
child: Text(L10n.of(context).tryAgain),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
// return Column(
|
||||
// spacing: 8.0,
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// AnimatedSize(
|
||||
// duration: FluffyThemes.animationDuration,
|
||||
// child: _status.hideCards
|
||||
// ? Padding(
|
||||
// padding: const EdgeInsets.all(8.0),
|
||||
// child: Column(
|
||||
// spacing: 16.0,
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// ErrorIndicator(
|
||||
// message: _status.message(L10n.of(context)),
|
||||
// ),
|
||||
// if (_loading && _status == ActivitySearchEnum.timeout)
|
||||
// const CircularProgressIndicator(),
|
||||
// if (!_loading && _status == ActivitySearchEnum.timeout)
|
||||
// ElevatedButton(
|
||||
// onPressed: _setActivityItems,
|
||||
// style: ElevatedButton.styleFrom(
|
||||
// backgroundColor: theme.colorScheme.primaryContainer,
|
||||
// foregroundColor:
|
||||
// theme.colorScheme.onPrimaryContainer,
|
||||
// padding: const EdgeInsets.symmetric(
|
||||
// horizontal: 12.0,
|
||||
// ),
|
||||
// ),
|
||||
// child: Text(L10n.of(context).tryAgain),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// )
|
||||
// : Container(
|
||||
// decoration: const BoxDecoration(),
|
||||
// child: Column(
|
||||
// children: [
|
||||
// scrollDirection == Axis.horizontal
|
||||
// ? Scrollbar(
|
||||
// thumbVisibility: true,
|
||||
// controller: _scrollController,
|
||||
// child: Padding(
|
||||
// padding: const EdgeInsets.only(bottom: 16.0),
|
||||
// child: SingleChildScrollView(
|
||||
// controller: _scrollController,
|
||||
// scrollDirection: Axis.horizontal,
|
||||
// child: Row(
|
||||
// spacing: 8.0,
|
||||
// children: cards,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// )
|
||||
// : SizedBox(
|
||||
// width: MediaQuery.of(context).size.width,
|
||||
// child: Wrap(
|
||||
// alignment: WrapAlignment.spaceEvenly,
|
||||
// runSpacing: 16.0,
|
||||
// spacing: 4.0,
|
||||
// children: cards,
|
||||
// ),
|
||||
// ),
|
||||
// if (cards.length < 5)
|
||||
// Padding(
|
||||
// padding: const EdgeInsetsGeometry.all(16.0),
|
||||
// child: ErrorIndicator(
|
||||
// message: _status.message(L10n.of(context)),
|
||||
// ),
|
||||
// ),
|
||||
// if (cards.length < 5 && _loading)
|
||||
// const CircularProgressIndicator(),
|
||||
// if (cards.length < 5 && !_loading)
|
||||
// Padding(
|
||||
// padding: const EdgeInsetsGeometry.only(bottom: 16),
|
||||
// child: ElevatedButton(
|
||||
// onPressed: _setActivityItems,
|
||||
// style: ElevatedButton.styleFrom(
|
||||
// backgroundColor:
|
||||
// theme.colorScheme.primaryContainer,
|
||||
// foregroundColor:
|
||||
// theme.colorScheme.onPrimaryContainer,
|
||||
// padding: const EdgeInsets.symmetric(
|
||||
// horizontal: 12.0,
|
||||
// ),
|
||||
// ),
|
||||
// child: Text(L10n.of(context).tryAgain),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -111,7 +111,6 @@ class PangeaController {
|
|||
static final List<String> _storageKeys = [
|
||||
'mode_list_storage',
|
||||
'activity_plan_storage',
|
||||
'activity_plan_by_id_storage',
|
||||
'bookmarked_activities',
|
||||
'objective_list_storage',
|
||||
'topic_list_storage',
|
||||
|
|
@ -130,6 +129,11 @@ class PangeaController {
|
|||
'onboarding_storage',
|
||||
'analytics_request_storage',
|
||||
'activity_analytics_storage',
|
||||
'course_topic_storage',
|
||||
'course_media_storage',
|
||||
'course_location_storage',
|
||||
'course_activity_storage',
|
||||
'course_location_media_storage',
|
||||
];
|
||||
|
||||
Future<void> clearCache({List<String> exclude = const []}) async {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
|||
import 'package:fluffychat/pangea/course_chats/course_chats_view.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/course_plan_model.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/course_plan_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/course_topic_model.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/public_spaces/public_room_bottom_sheet.dart';
|
||||
import 'package:fluffychat/pangea/spaces/constants/space_constants.dart';
|
||||
|
|
@ -100,29 +101,29 @@ class CourseChatsController extends State<CourseChats> {
|
|||
}
|
||||
|
||||
int get _selectedTopicIndex =>
|
||||
course?.topics.indexWhere((t) => t.uuid == selectedTopicId) ?? -1;
|
||||
course?.loadedTopics.indexWhere((t) => t.uuid == selectedTopicId) ?? -1;
|
||||
|
||||
bool get canMoveLeft => _selectedTopicIndex > 0;
|
||||
bool get canMoveRight {
|
||||
if (course == null) return false;
|
||||
final endIndex =
|
||||
room?.ownCurrentTopicIndex(course!) ?? (course!.topics.length - 1);
|
||||
final endIndex = room?.ownCurrentTopicIndex(course!) ??
|
||||
(course!.loadedTopics.length - 1);
|
||||
return _selectedTopicIndex < endIndex;
|
||||
}
|
||||
|
||||
void moveLeft() {
|
||||
if (canMoveLeft) {
|
||||
setSelectedTopicId(course!.topics[_selectedTopicIndex - 1].uuid);
|
||||
setSelectedTopicId(course!.loadedTopics[_selectedTopicIndex - 1].uuid);
|
||||
}
|
||||
}
|
||||
|
||||
void moveRight() {
|
||||
if (canMoveRight) {
|
||||
setSelectedTopicId(course!.topics[_selectedTopicIndex + 1].uuid);
|
||||
setSelectedTopicId(course!.loadedTopics[_selectedTopicIndex + 1].uuid);
|
||||
}
|
||||
}
|
||||
|
||||
Topic? get selectedTopic => course?.topics.firstWhereOrNull(
|
||||
CourseTopicModel? get selectedTopic => course?.loadedTopics.firstWhereOrNull(
|
||||
(topic) => topic.uuid == selectedTopicId,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ import 'package:fluffychat/pangea/chat_settings/constants/pangea_room_types.dart
|
|||
import 'package:fluffychat/pangea/course_chats/course_chats_page.dart';
|
||||
import 'package:fluffychat/pangea/course_chats/unjoined_chat_list_item.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/course_plan_builder.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/course_plan_model.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/course_plan_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/course_topic_model.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/space_analytics/analytics_request_indicator.dart';
|
||||
import 'package:fluffychat/pangea/spaces/widgets/knocking_users_indicator.dart';
|
||||
|
|
@ -41,13 +41,13 @@ class CourseChatsView extends StatelessWidget {
|
|||
|
||||
return CoursePlanBuilder(
|
||||
courseId: room.coursePlan?.uuid,
|
||||
onFound: (course) {
|
||||
onLoaded: (course) {
|
||||
controller.setCourse(course);
|
||||
final topic = room.ownCurrentTopic(course);
|
||||
if (topic != null) controller.setSelectedTopicId(topic.uuid);
|
||||
},
|
||||
builder: (context, courseController) {
|
||||
final Topic? topic = controller.selectedTopic;
|
||||
final CourseTopicModel? topic = controller.selectedTopic;
|
||||
final List<String> activityIds = topic?.activityIds ?? [];
|
||||
|
||||
return StreamBuilder(
|
||||
|
|
@ -220,23 +220,24 @@ class CourseChatsView extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
spacing: 6.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.location_on,
|
||||
size: 24.0,
|
||||
),
|
||||
Text(
|
||||
topic.location,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
if (topic.location != null)
|
||||
Row(
|
||||
spacing: 6.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.location_on,
|
||||
size: 24.0,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
topic.location!,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
MouseRegion(
|
||||
cursor: controller.canMoveRight
|
||||
? SystemMouseCursors.click
|
||||
|
|
|
|||
|
|
@ -83,14 +83,14 @@ class CourseInfoChips extends StatelessWidget {
|
|||
),
|
||||
CourseInfoChip(
|
||||
icon: Icons.location_on,
|
||||
text: L10n.of(context).numModules(course.topics.length),
|
||||
text: L10n.of(context).numModules(course.topicIds.length),
|
||||
fontSize: fontSize,
|
||||
iconSize: iconSize,
|
||||
padding: padding,
|
||||
),
|
||||
CourseInfoChip(
|
||||
icon: Icons.event_note_outlined,
|
||||
text: L10n.of(context).numActivityPlans(course.activities),
|
||||
text: L10n.of(context).numActivityPlans(course.totalActivities),
|
||||
fontSize: fontSize,
|
||||
iconSize: iconSize,
|
||||
padding: padding,
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ class SelectedCourseView extends StatelessWidget {
|
|||
Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: ListView.builder(
|
||||
itemCount: course.topics.length + 2,
|
||||
itemCount: course.loadedTopics.length + 2,
|
||||
itemBuilder: (context, index) {
|
||||
if (index == 0) {
|
||||
return Column(
|
||||
|
|
@ -112,11 +112,11 @@ class SelectedCourseView extends StatelessWidget {
|
|||
|
||||
index--;
|
||||
|
||||
if (index == course.topics.length) {
|
||||
if (index == course.loadedTopics.length) {
|
||||
return const SizedBox(height: 150.0);
|
||||
}
|
||||
|
||||
final topic = course.topics[index];
|
||||
final topic = course.loadedTopics[index];
|
||||
return Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(vertical: 4.0),
|
||||
|
|
@ -186,17 +186,18 @@ class SelectedCourseView extends StatelessWidget {
|
|||
spacing: 8.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
CourseInfoChip(
|
||||
icon: Icons.location_on,
|
||||
text: topic.location,
|
||||
fontSize: descFontSize,
|
||||
iconSize: smallIconSize,
|
||||
),
|
||||
if (topic.location != null)
|
||||
CourseInfoChip(
|
||||
icon: Icons.location_on,
|
||||
text: topic.location!,
|
||||
fontSize: descFontSize,
|
||||
iconSize: smallIconSize,
|
||||
),
|
||||
CourseInfoChip(
|
||||
icon: Icons.event_note_outlined,
|
||||
text: L10n.of(context)
|
||||
.numActivityPlans(
|
||||
topic.activities.length,
|
||||
topic.loadedActivities.length,
|
||||
),
|
||||
fontSize: descFontSize,
|
||||
iconSize: smallIconSize,
|
||||
|
|
|
|||
127
lib/pangea/course_plans/course_activity_repo.dart
Normal file
127
lib/pangea/course_plans/course_activity_repo.dart
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/models/course_plan/cms_course_plan_activity.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/models/course_plan/cms_course_plan_activity_media.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/payload_repo.dart';
|
||||
|
||||
class CourseActivityRepo {
|
||||
static final Map<String, Completer<List<ActivityPlanModel>>> _cache = {};
|
||||
static final GetStorage _storage = GetStorage('course_activity_storage');
|
||||
|
||||
static ActivityPlanModel? _getCached(String uuid) {
|
||||
final json = _storage.read<Map<String, dynamic>>(uuid);
|
||||
if (json != null) {
|
||||
try {
|
||||
return ActivityPlanModel.fromJson(json);
|
||||
} catch (e) {
|
||||
// ignore invalid cached data
|
||||
_storage.remove(uuid);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static Future<void> _setCached(ActivityPlanModel activity) =>
|
||||
_storage.write(activity.activityId, activity.toJson());
|
||||
|
||||
static List<ActivityPlanModel> getSync(List<String> uuids) {
|
||||
return uuids
|
||||
.map((uuid) => _getCached(uuid))
|
||||
.whereType<ActivityPlanModel>()
|
||||
.toList();
|
||||
}
|
||||
|
||||
static Future<List<ActivityPlanModel>> get(
|
||||
String topicId,
|
||||
List<String> uuids,
|
||||
) async {
|
||||
final activities = <ActivityPlanModel>[];
|
||||
final toFetch = <String>[];
|
||||
|
||||
for (final uuid in uuids) {
|
||||
final cached = _getCached(uuid);
|
||||
if (cached != null) {
|
||||
activities.add(cached);
|
||||
} else {
|
||||
toFetch.add(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
if (toFetch.isNotEmpty) {
|
||||
final fetchedActivities = await _fetch(topicId, toFetch);
|
||||
activities.addAll(fetchedActivities);
|
||||
for (final activity in fetchedActivities) {
|
||||
await _setCached(activity);
|
||||
}
|
||||
}
|
||||
|
||||
return activities;
|
||||
}
|
||||
|
||||
static Future<List<ActivityPlanModel>> _fetch(
|
||||
String topicId,
|
||||
List<String> uuids,
|
||||
) async {
|
||||
if (_cache.containsKey(topicId)) {
|
||||
return _cache[topicId]!.future;
|
||||
}
|
||||
|
||||
final completer = Completer<List<ActivityPlanModel>>();
|
||||
_cache[topicId] = completer;
|
||||
|
||||
final where = {
|
||||
"id": {"in": uuids.join(",")},
|
||||
};
|
||||
final limit = uuids.length;
|
||||
|
||||
try {
|
||||
final cmsCoursePlanActivitiesResult = await PayloadRepo.payload.find(
|
||||
CmsCoursePlanActivity.slug,
|
||||
CmsCoursePlanActivity.fromJson,
|
||||
where: where,
|
||||
limit: limit,
|
||||
page: 1,
|
||||
sort: "createdAt",
|
||||
);
|
||||
|
||||
final imageUrls = await _fetchImageUrls(
|
||||
cmsCoursePlanActivitiesResult.docs.map((a) => a.id).toList(),
|
||||
);
|
||||
|
||||
final activities = cmsCoursePlanActivitiesResult.docs
|
||||
.map((a) => a.toActivityPlanModel(imageUrls[a.id]))
|
||||
.toList();
|
||||
|
||||
completer.complete(activities);
|
||||
return activities;
|
||||
} catch (e) {
|
||||
completer.completeError(e);
|
||||
rethrow;
|
||||
} finally {
|
||||
_cache.remove(topicId);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<Map<String, String>> _fetchImageUrls(
|
||||
List<String> activityIds,
|
||||
) async {
|
||||
final where = {
|
||||
"id": {"in": activityIds.join(",")},
|
||||
};
|
||||
final limit = activityIds.length;
|
||||
final cmsCoursePlanActivityMediasResult = await PayloadRepo.payload.find(
|
||||
CmsCoursePlanActivityMedia.slug,
|
||||
CmsCoursePlanActivityMedia.fromJson,
|
||||
where: where,
|
||||
limit: limit,
|
||||
page: 1,
|
||||
sort: "createdAt",
|
||||
);
|
||||
return Map.fromEntries(
|
||||
cmsCoursePlanActivityMediasResult.docs.map((e) => MapEntry(e.id, e.url!)),
|
||||
);
|
||||
}
|
||||
}
|
||||
102
lib/pangea/course_plans/course_location_media_repo.dart
Normal file
102
lib/pangea/course_plans/course_location_media_repo.dart
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/payload_client/models/course_plan/cms_course_plan_topic_location_media.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/payload_repo.dart';
|
||||
|
||||
class CourseLocationMediaRepo {
|
||||
static final Map<String, Completer<Map<String, String>>> _cache = {};
|
||||
static final GetStorage _storage =
|
||||
GetStorage('course_location_media_storage');
|
||||
|
||||
static String? _getCached(String uuid) {
|
||||
try {
|
||||
return _storage.read(uuid) as String?;
|
||||
} catch (e) {
|
||||
// If parsing fails, remove the corrupted cache entry
|
||||
_storage.remove(uuid);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static Future<void> _setCached(String uuid, String url) =>
|
||||
_storage.write(uuid, url);
|
||||
|
||||
static List<String> getSync(List<String> uuids) {
|
||||
final urls = <String>[];
|
||||
for (final uuid in uuids) {
|
||||
final cached = _getCached(uuid);
|
||||
if (cached != null) {
|
||||
urls.add(cached);
|
||||
}
|
||||
}
|
||||
|
||||
return urls;
|
||||
}
|
||||
|
||||
static Future<List<String>> get(String topicId, List<String> uuids) async {
|
||||
final urls = <String>[];
|
||||
final toFetch = <String>[];
|
||||
|
||||
for (final uuid in uuids) {
|
||||
final cached = _getCached(uuid);
|
||||
if (cached != null) {
|
||||
urls.add(cached);
|
||||
} else {
|
||||
toFetch.add(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
if (toFetch.isNotEmpty) {
|
||||
final fetched = await _fetch(topicId, toFetch);
|
||||
urls.addAll(fetched.values);
|
||||
for (final entry in fetched.entries) {
|
||||
await _setCached(entry.key, entry.value);
|
||||
}
|
||||
}
|
||||
|
||||
return urls;
|
||||
}
|
||||
|
||||
static Future<Map<String, String>> _fetch(
|
||||
String topicId,
|
||||
List<String> uuids,
|
||||
) async {
|
||||
if (_cache.containsKey(topicId)) {
|
||||
return _cache[topicId]!.future;
|
||||
}
|
||||
|
||||
final completer = Completer<Map<String, String>>();
|
||||
_cache[topicId] = completer;
|
||||
|
||||
final where = {
|
||||
"id": {"in": uuids.join(",")},
|
||||
};
|
||||
final limit = uuids.length;
|
||||
|
||||
try {
|
||||
final cmsCoursePlanTopicLocationMediasResult =
|
||||
await PayloadRepo.payload.find(
|
||||
CmsCoursePlanTopicLocationMedia.slug,
|
||||
CmsCoursePlanTopicLocationMedia.fromJson,
|
||||
where: where,
|
||||
limit: limit,
|
||||
page: 1,
|
||||
sort: "createdAt",
|
||||
);
|
||||
|
||||
final media = Map.fromEntries(
|
||||
cmsCoursePlanTopicLocationMediasResult.docs
|
||||
.map((e) => MapEntry(e.id, e.url!)),
|
||||
);
|
||||
completer.complete(media);
|
||||
return media;
|
||||
} catch (e) {
|
||||
completer.completeError(e);
|
||||
rethrow;
|
||||
} finally {
|
||||
_cache.remove(topicId);
|
||||
}
|
||||
}
|
||||
}
|
||||
30
lib/pangea/course_plans/course_location_model.dart
Normal file
30
lib/pangea/course_plans/course_location_model.dart
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
class CourseLocationModel {
|
||||
String uuid;
|
||||
String name;
|
||||
List<String> mediaIds;
|
||||
|
||||
CourseLocationModel({
|
||||
required this.uuid,
|
||||
required this.name,
|
||||
required this.mediaIds,
|
||||
});
|
||||
|
||||
factory CourseLocationModel.fromJson(Map<String, dynamic> json) {
|
||||
return CourseLocationModel(
|
||||
uuid: json['uuid'] as String,
|
||||
name: json['name'] as String,
|
||||
mediaIds: (json['media_ids'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'uuid': uuid,
|
||||
'name': name,
|
||||
'media_ids': mediaIds,
|
||||
};
|
||||
}
|
||||
}
|
||||
107
lib/pangea/course_plans/course_location_repo.dart
Normal file
107
lib/pangea/course_plans/course_location_repo.dart
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/course_plans/course_location_model.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/models/course_plan/cms_course_plan_topic_location.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/payload_repo.dart';
|
||||
|
||||
class CourseLocationRepo {
|
||||
static final Map<String, Completer<List<CourseLocationModel>>> _cache = {};
|
||||
static final GetStorage _storage = GetStorage('course_location_storage');
|
||||
|
||||
static CourseLocationModel? _getCached(String uuid) {
|
||||
final json = _storage.read(uuid);
|
||||
if (json != null) {
|
||||
try {
|
||||
return CourseLocationModel.fromJson(Map<String, dynamic>.from(json));
|
||||
} catch (e) {
|
||||
// If parsing fails, remove the corrupted cache entry
|
||||
_storage.remove(uuid);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static Future<void> _setCached(String uuid, CourseLocationModel location) =>
|
||||
_storage.write(uuid, location.toJson());
|
||||
|
||||
static List<CourseLocationModel> getSync(List<String> uuids) {
|
||||
final locations = <CourseLocationModel>[];
|
||||
for (final uuid in uuids) {
|
||||
final cached = _getCached(uuid);
|
||||
if (cached != null) {
|
||||
locations.add(cached);
|
||||
}
|
||||
}
|
||||
|
||||
return locations;
|
||||
}
|
||||
|
||||
static Future<List<CourseLocationModel>> get(
|
||||
String courseId,
|
||||
List<String> uuids,
|
||||
) async {
|
||||
final locations = <CourseLocationModel>[];
|
||||
final toFetch = <String>[];
|
||||
|
||||
for (final uuid in uuids) {
|
||||
final cached = _getCached(uuid);
|
||||
if (cached != null) {
|
||||
locations.add(cached);
|
||||
} else {
|
||||
toFetch.add(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
if (toFetch.isNotEmpty) {
|
||||
final fetchedLocations = await _fetch(courseId, toFetch);
|
||||
locations.addAll(fetchedLocations);
|
||||
for (int i = 0; i < toFetch.length; i++) {
|
||||
await _setCached(toFetch[i], fetchedLocations[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return locations;
|
||||
}
|
||||
|
||||
static Future<List<CourseLocationModel>> _fetch(
|
||||
String topicId,
|
||||
List<String> uuids,
|
||||
) async {
|
||||
if (_cache.containsKey(topicId)) {
|
||||
return _cache[topicId]!.future;
|
||||
}
|
||||
|
||||
final completer = Completer<List<CourseLocationModel>>();
|
||||
_cache[topicId] = completer;
|
||||
|
||||
final where = {
|
||||
"id": {"in": uuids.join(",")},
|
||||
};
|
||||
final limit = uuids.length;
|
||||
|
||||
try {
|
||||
final cmsCoursePlanTopicLocationsResult = await PayloadRepo.payload.find(
|
||||
CmsCoursePlanTopicLocation.slug,
|
||||
CmsCoursePlanTopicLocation.fromJson,
|
||||
where: where,
|
||||
limit: limit,
|
||||
page: 1,
|
||||
sort: "createdAt",
|
||||
);
|
||||
|
||||
final locations = cmsCoursePlanTopicLocationsResult.docs
|
||||
.map((location) => location.toCourseLocationModel())
|
||||
.toList();
|
||||
|
||||
completer.complete(locations);
|
||||
return locations;
|
||||
} catch (e) {
|
||||
completer.completeError(e);
|
||||
rethrow;
|
||||
} finally {
|
||||
_cache.remove(topicId);
|
||||
}
|
||||
}
|
||||
}
|
||||
98
lib/pangea/course_plans/course_media_repo.dart
Normal file
98
lib/pangea/course_plans/course_media_repo.dart
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/payload_client/models/course_plan/cms_course_plan_media.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/payload_repo.dart';
|
||||
|
||||
class CourseMediaRepo {
|
||||
static final Map<String, Completer<Map<String, String>>> _cache = {};
|
||||
static final GetStorage _storage = GetStorage('course_media_storage');
|
||||
|
||||
static List<String> getSync(List<String> uuids) {
|
||||
final urls = <String>[];
|
||||
for (final uuid in uuids) {
|
||||
final cached = _getCached(uuid);
|
||||
if (cached != null) {
|
||||
urls.add(cached);
|
||||
}
|
||||
}
|
||||
|
||||
return urls;
|
||||
}
|
||||
|
||||
static Future<List<String>> get(String courseId, List<String> uuids) async {
|
||||
final urls = <String>[];
|
||||
final toFetch = <String>[];
|
||||
|
||||
for (final uuid in uuids) {
|
||||
final cached = _getCached(uuid);
|
||||
if (cached != null) {
|
||||
urls.add(cached);
|
||||
} else {
|
||||
toFetch.add(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
if (toFetch.isNotEmpty) {
|
||||
final fetchedUrls = await _fetch(courseId, toFetch);
|
||||
urls.addAll(fetchedUrls.values.toList());
|
||||
for (final entry in fetchedUrls.entries) {
|
||||
await _setCached(entry.key, entry.value);
|
||||
}
|
||||
}
|
||||
|
||||
return urls;
|
||||
}
|
||||
|
||||
static String? _getCached(String uuid) => _storage.read(uuid);
|
||||
|
||||
static Future<void> _setCached(String uuid, String url) =>
|
||||
_storage.write(uuid, url);
|
||||
|
||||
static Future<Map<String, String>> _fetch(
|
||||
String courseId,
|
||||
List<String> uuids,
|
||||
) async {
|
||||
if (_cache.containsKey(courseId)) {
|
||||
return _cache[courseId]!.future;
|
||||
}
|
||||
|
||||
final completer = Completer<Map<String, String>>();
|
||||
_cache[courseId] = completer;
|
||||
|
||||
final where = {
|
||||
"id": {"in": uuids.join(",")},
|
||||
};
|
||||
final limit = uuids.length;
|
||||
|
||||
try {
|
||||
final cmsCoursePlanMediaResult = await PayloadRepo.payload.find(
|
||||
CmsCoursePlanMedia.slug,
|
||||
CmsCoursePlanMedia.fromJson,
|
||||
where: where,
|
||||
limit: limit,
|
||||
page: 1,
|
||||
sort: "createdAt",
|
||||
);
|
||||
|
||||
if (cmsCoursePlanMediaResult.docs.isEmpty) {
|
||||
return {};
|
||||
}
|
||||
|
||||
final media = Map.fromEntries(
|
||||
cmsCoursePlanMediaResult.docs
|
||||
.where((media) => media.url != null)
|
||||
.map((media) => MapEntry(media.id, media.url!)),
|
||||
);
|
||||
|
||||
completer.complete(media);
|
||||
return media;
|
||||
} catch (e) {
|
||||
completer.completeError(e);
|
||||
rethrow;
|
||||
} finally {
|
||||
_cache.remove(courseId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ import 'package:fluffychat/pangea/course_plans/course_plans_repo.dart';
|
|||
class CoursePlanBuilder extends StatefulWidget {
|
||||
final String? courseId;
|
||||
final VoidCallback? onNotFound;
|
||||
final Function(CoursePlanModel course)? onFound;
|
||||
final Function(CoursePlanModel course)? onLoaded;
|
||||
final Widget Function(
|
||||
BuildContext context,
|
||||
CoursePlanController controller,
|
||||
|
|
@ -17,7 +17,7 @@ class CoursePlanBuilder extends StatefulWidget {
|
|||
required this.courseId,
|
||||
required this.builder,
|
||||
this.onNotFound,
|
||||
this.onFound,
|
||||
this.onLoaded,
|
||||
});
|
||||
|
||||
@override
|
||||
|
|
@ -61,9 +61,13 @@ class CoursePlanController extends State<CoursePlanBuilder> {
|
|||
});
|
||||
|
||||
course = await CoursePlansRepo.get(widget.courseId!);
|
||||
course == null
|
||||
? widget.onNotFound?.call()
|
||||
: widget.onFound?.call(course!);
|
||||
if (course == null) {
|
||||
widget.onNotFound?.call();
|
||||
} else {
|
||||
await course!.init();
|
||||
}
|
||||
|
||||
widget.onLoaded?.call(course!);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
} finally {
|
||||
|
|
|
|||
|
|
@ -1,71 +1,15 @@
|
|||
import 'package:fluffychat/pangea/activity_generator/media_enum.dart';
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_plan_request.dart';
|
||||
import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/course_media_repo.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/course_topic_model.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/course_topic_repo.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/models/language_model.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/models/course_plan/cms_course_plan.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/models/course_plan/cms_course_plan_activity.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/models/course_plan/cms_course_plan_activity_media.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/models/course_plan/cms_course_plan_media.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/models/course_plan/cms_course_plan_topic.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/models/course_plan/cms_course_plan_topic_location.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/models/course_plan/cms_course_plan_topic_location_media.dart';
|
||||
|
||||
/// Represents a topic in the course planner response.
|
||||
class Topic {
|
||||
final String title;
|
||||
final String description;
|
||||
final String location;
|
||||
final String uuid;
|
||||
final String? imageUrl;
|
||||
|
||||
final List<ActivityPlanModel> activities;
|
||||
|
||||
Topic({
|
||||
required this.title,
|
||||
required this.description,
|
||||
this.location = "Unknown",
|
||||
required this.uuid,
|
||||
List<ActivityPlanModel>? activities,
|
||||
this.imageUrl,
|
||||
}) : activities = activities ?? [];
|
||||
|
||||
/// Deserialize from JSON
|
||||
factory Topic.fromJson(Map<String, dynamic> json) {
|
||||
return Topic(
|
||||
title: json['title'] as String,
|
||||
description: json['description'] as String,
|
||||
location: json['location'] as String? ?? "Unknown",
|
||||
uuid: json['uuid'] as String,
|
||||
activities: (json['activities'] as List<dynamic>?)
|
||||
?.map(
|
||||
(e) => ActivityPlanModel.fromJson(e as Map<String, dynamic>),
|
||||
)
|
||||
.toList() ??
|
||||
[],
|
||||
imageUrl: json['image_url'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
/// Serialize to JSON
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'title': title,
|
||||
'description': description,
|
||||
'location': location,
|
||||
'uuid': uuid,
|
||||
'activities': activities.map((e) => e.toJson()).toList(),
|
||||
'image_url': imageUrl,
|
||||
};
|
||||
}
|
||||
|
||||
List<String> get activityIds => activities.map((e) => e.activityId).toList();
|
||||
}
|
||||
|
||||
/// Represents a course plan in the course planner response.
|
||||
class CoursePlanModel {
|
||||
final String uuid;
|
||||
|
||||
final String targetLanguage;
|
||||
final String languageOfInstructions;
|
||||
final LanguageLevelTypeEnum cefrLevel;
|
||||
|
|
@ -73,10 +17,8 @@ class CoursePlanModel {
|
|||
final String title;
|
||||
final String description;
|
||||
|
||||
final String uuid;
|
||||
|
||||
final List<Topic> topics;
|
||||
final String? imageUrl;
|
||||
final List<String> topicIds;
|
||||
final List<String> mediaIds;
|
||||
|
||||
CoursePlanModel({
|
||||
required this.targetLanguage,
|
||||
|
|
@ -85,12 +27,9 @@ class CoursePlanModel {
|
|||
required this.title,
|
||||
required this.description,
|
||||
required this.uuid,
|
||||
List<Topic>? topics,
|
||||
this.imageUrl,
|
||||
}) : topics = topics ?? [];
|
||||
|
||||
int get activities =>
|
||||
topics.map((t) => t.activities.length).reduce((a, b) => a + b);
|
||||
required this.topicIds,
|
||||
required this.mediaIds,
|
||||
});
|
||||
|
||||
LanguageModel? get targetLanguageModel =>
|
||||
PLanguageStore.byLangCode(targetLanguage);
|
||||
|
|
@ -107,16 +46,17 @@ class CoursePlanModel {
|
|||
languageOfInstructions.toUpperCase();
|
||||
|
||||
String? topicID(String activityID) {
|
||||
for (final topic in topics) {
|
||||
for (final activity in topic.activities) {
|
||||
if (activity.activityId == activityID) {
|
||||
return topic.uuid;
|
||||
}
|
||||
for (final topic in loadedTopics) {
|
||||
if (topic.activityIds.any((id) => id == activityID)) {
|
||||
return topic.uuid;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
int get totalActivities =>
|
||||
loadedTopics.fold(0, (sum, topic) => sum + topic.activityIds.length);
|
||||
|
||||
/// Deserialize from JSON
|
||||
factory CoursePlanModel.fromJson(Map<String, dynamic> json) {
|
||||
return CoursePlanModel(
|
||||
|
|
@ -126,11 +66,14 @@ class CoursePlanModel {
|
|||
title: json['title'] as String,
|
||||
description: json['description'] as String,
|
||||
uuid: json['uuid'] as String,
|
||||
topics: (json['topics'] as List<dynamic>?)
|
||||
?.map((e) => Topic.fromJson(e as Map<String, dynamic>))
|
||||
topicIds: (json['topic_ids'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
[],
|
||||
mediaIds: (json['media_ids'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
[],
|
||||
imageUrl: json['image_url'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -143,144 +86,41 @@ class CoursePlanModel {
|
|||
'title': title,
|
||||
'description': description,
|
||||
'uuid': uuid,
|
||||
'topics': topics.map((e) => e.toJson()).toList(),
|
||||
'image_url': imageUrl,
|
||||
'topic_ids': topicIds,
|
||||
'media_ids': mediaIds,
|
||||
};
|
||||
}
|
||||
|
||||
factory CoursePlanModel.fromCmsDocs(
|
||||
CmsCoursePlan cmsCoursePlan,
|
||||
List<CmsCoursePlanMedia>? cmsCoursePlanMedias,
|
||||
List<CmsCoursePlanTopic>? cmsCoursePlanTopics,
|
||||
List<CmsCoursePlanTopicLocation>? cmsCoursePlanTopicLocations,
|
||||
List<CmsCoursePlanTopicLocationMedia>? cmsCoursePlanTopicLocationMedias,
|
||||
List<CmsCoursePlanActivity>? cmsCoursePlanActivities,
|
||||
List<CmsCoursePlanActivityMedia>? cmsCoursePlanActivityMedias,
|
||||
) {
|
||||
// fetch topics
|
||||
List<Topic>? topics;
|
||||
if (cmsCoursePlanTopics != null) {
|
||||
for (final topic in cmsCoursePlanTopics) {
|
||||
// select locations of current topic
|
||||
List<CmsCoursePlanTopicLocation>? topicLocations;
|
||||
if (cmsCoursePlanTopicLocations != null) {
|
||||
for (final location in cmsCoursePlanTopicLocations) {
|
||||
if (location.coursePlanTopics.contains(topic.id)) {
|
||||
topicLocations ??= [];
|
||||
topicLocations.add(location);
|
||||
}
|
||||
}
|
||||
}
|
||||
bool get topicListComplete => topicIds.length == loadedTopics.length;
|
||||
List<CourseTopicModel> get loadedTopics => CourseTopicRepo.getSync(topicIds);
|
||||
Future<List<CourseTopicModel>> fetchTopics() =>
|
||||
CourseTopicRepo.get(uuid, topicIds);
|
||||
|
||||
// select activities of current topic
|
||||
List<CmsCoursePlanActivity>? topicActivities;
|
||||
if (cmsCoursePlanActivities != null) {
|
||||
for (final activity in cmsCoursePlanActivities) {
|
||||
if (activity.coursePlanTopics.contains(topic.id)) {
|
||||
topicActivities ??= [];
|
||||
topicActivities.add(activity);
|
||||
}
|
||||
}
|
||||
}
|
||||
bool get mediaListComplete => mediaIds.length == loadedMediaUrls.length;
|
||||
List<String> get loadedMediaUrls => CourseMediaRepo.getSync(mediaIds);
|
||||
Future<List<String>> fetchMediaUrls() => CourseMediaRepo.get(uuid, mediaIds);
|
||||
String? get imageUrl => loadedMediaUrls.isEmpty
|
||||
? null
|
||||
: "${Environment.cmsApi}${loadedMediaUrls.first}";
|
||||
|
||||
List<ActivityPlanModel>? activityPlans;
|
||||
if (topicActivities != null) {
|
||||
for (final activity in topicActivities) {
|
||||
// select media of current activity
|
||||
List<CmsCoursePlanActivityMedia>? activityMedias;
|
||||
if (cmsCoursePlanActivityMedias != null) {
|
||||
for (final media in cmsCoursePlanActivityMedias) {
|
||||
if (media.coursePlanActivities.contains(activity.id)) {
|
||||
activityMedias ??= [];
|
||||
activityMedias.add(media);
|
||||
}
|
||||
}
|
||||
}
|
||||
Future<void> init() async {
|
||||
final courseFutures = <Future>[
|
||||
fetchMediaUrls(),
|
||||
fetchTopics(),
|
||||
];
|
||||
await Future.wait(courseFutures);
|
||||
|
||||
final Map<String, ActivityRole> roles = {};
|
||||
for (final role in activity.roles) {
|
||||
roles[role.id] = ActivityRole(
|
||||
id: role.id,
|
||||
name: role.name,
|
||||
avatarUrl: role.avatarUrl,
|
||||
goal: role.goal,
|
||||
);
|
||||
}
|
||||
|
||||
activityPlans ??= [];
|
||||
activityPlans.add(
|
||||
ActivityPlanModel(
|
||||
req: ActivityPlanRequest(
|
||||
topic: "",
|
||||
mode: "",
|
||||
objective: "",
|
||||
media: MediaEnum.nan,
|
||||
cefrLevel: activity.cefrLevel,
|
||||
languageOfInstructions: activity.l1,
|
||||
targetLanguage: activity.l2,
|
||||
numberOfParticipants: activity.roles.length,
|
||||
),
|
||||
activityId: activity.id,
|
||||
title: activity.title,
|
||||
description: activity.description,
|
||||
learningObjective: activity.learningObjective,
|
||||
instructions: activity.instructions,
|
||||
vocab: activity.vocabs
|
||||
.map((v) => Vocab(lemma: v.lemma, pos: v.pos))
|
||||
.toList(),
|
||||
roles: roles,
|
||||
imageURL: activityMedias != null && activityMedias.isNotEmpty
|
||||
? '${Environment.cmsApi}${activityMedias.first.url}'
|
||||
: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
List<CmsCoursePlanTopicLocationMedia>? topicLocationMedias;
|
||||
if (cmsCoursePlanTopicLocationMedias != null &&
|
||||
topicLocations != null &&
|
||||
topicLocations.isNotEmpty) {
|
||||
final location = topicLocations.first;
|
||||
for (final media in cmsCoursePlanTopicLocationMedias) {
|
||||
if (media.coursePlanTopicLocations.contains(location.id)) {
|
||||
topicLocationMedias ??= [];
|
||||
topicLocationMedias.add(media);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
topics ??= [];
|
||||
topics.add(
|
||||
Topic(
|
||||
uuid: topic.id,
|
||||
title: topic.title,
|
||||
description: topic.description,
|
||||
location: topicLocations != null && topicLocations.isNotEmpty
|
||||
? topicLocations.first.name
|
||||
: "Any",
|
||||
activities: activityPlans,
|
||||
imageUrl:
|
||||
topicLocationMedias != null && topicLocationMedias.isNotEmpty
|
||||
? '${Environment.cmsApi}${topicLocationMedias.last.url}'
|
||||
: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return CoursePlanModel(
|
||||
uuid: cmsCoursePlan.id,
|
||||
title: cmsCoursePlan.title,
|
||||
description: cmsCoursePlan.description,
|
||||
cefrLevel:
|
||||
LanguageLevelTypeEnumExtension.fromString(cmsCoursePlan.cefrLevel),
|
||||
languageOfInstructions: cmsCoursePlan.l1,
|
||||
targetLanguage: cmsCoursePlan.l2,
|
||||
topics: topics,
|
||||
imageUrl: cmsCoursePlanMedias != null && cmsCoursePlanMedias.isNotEmpty
|
||||
? '${Environment.cmsApi}${cmsCoursePlanMedias.first.url}'
|
||||
: null,
|
||||
final topicFutures = <Future>[];
|
||||
topicFutures.addAll(
|
||||
loadedTopics.map(
|
||||
(topic) => topic.fetchActivities(),
|
||||
),
|
||||
);
|
||||
topicFutures.addAll(
|
||||
loadedTopics.map(
|
||||
(topic) => topic.fetchLocationMedia(),
|
||||
),
|
||||
);
|
||||
await Future.wait(topicFutures);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,18 @@
|
|||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
|
||||
import 'package:fluffychat/pangea/activity_sessions/activity_role_model.dart';
|
||||
import 'package:fluffychat/pangea/activity_sessions/activity_roles_model.dart';
|
||||
import 'package:fluffychat/pangea/bot/utils/bot_name.dart';
|
||||
import 'package:fluffychat/pangea/chat/constants/default_power_level.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/constants/pangea_room_types.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/course_plan_event.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/course_plan_model.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/course_topic_model.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/course_user_event.dart';
|
||||
import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart';
|
||||
import 'package:fluffychat/pangea/extensions/join_rule_extension.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||
|
||||
extension CoursePlanRoomExtension on Room {
|
||||
CoursePlanEvent? get coursePlan {
|
||||
|
|
@ -32,7 +40,7 @@ extension CoursePlanRoomExtension on Room {
|
|||
final state = _courseUserState(userID);
|
||||
if (state == null) return false;
|
||||
|
||||
final topicIndex = course.topics.indexWhere(
|
||||
final topicIndex = course.loadedTopics.indexWhere(
|
||||
(t) => t.uuid == topicID,
|
||||
);
|
||||
|
||||
|
|
@ -40,27 +48,28 @@ extension CoursePlanRoomExtension on Room {
|
|||
throw Exception('Topic not found');
|
||||
}
|
||||
|
||||
final activityIds =
|
||||
course.topics[topicIndex].activities.map((a) => a.activityId).toList();
|
||||
final activityIds = course.loadedTopics[topicIndex].loadedActivities
|
||||
.map((a) => a.activityId)
|
||||
.toList();
|
||||
return state.completedActivities(topicID).toSet().containsAll(activityIds);
|
||||
}
|
||||
|
||||
Topic? currentTopic(
|
||||
CourseTopicModel? currentTopic(
|
||||
String userID,
|
||||
CoursePlanModel course,
|
||||
) {
|
||||
if (coursePlan == null) return null;
|
||||
final topicIDs = course.topics.map((t) => t.uuid).toList();
|
||||
final topicIDs = course.loadedTopics.map((t) => t.uuid).toList();
|
||||
if (topicIDs.isEmpty) return null;
|
||||
|
||||
final index = topicIDs.indexWhere(
|
||||
(t) => !_hasCompletedTopic(userID, t, course),
|
||||
);
|
||||
|
||||
return index == -1 ? null : course.topics[index];
|
||||
return index == -1 ? null : course.loadedTopics[index];
|
||||
}
|
||||
|
||||
Topic? ownCurrentTopic(CoursePlanModel course) =>
|
||||
CourseTopicModel? ownCurrentTopic(CoursePlanModel course) =>
|
||||
currentTopic(client.userID!, course);
|
||||
|
||||
int currentTopicIndex(
|
||||
|
|
@ -68,7 +77,7 @@ extension CoursePlanRoomExtension on Room {
|
|||
CoursePlanModel course,
|
||||
) {
|
||||
if (coursePlan == null) return -1;
|
||||
final topicIDs = course.topics.map((t) => t.uuid).toList();
|
||||
final topicIDs = course.loadedTopics.map((t) => t.uuid).toList();
|
||||
if (topicIDs.isEmpty) return -1;
|
||||
|
||||
final index = topicIDs.indexWhere(
|
||||
|
|
@ -93,7 +102,7 @@ extension CoursePlanRoomExtension on Room {
|
|||
if (user.id == BotName.byEnvironment) continue;
|
||||
final topicIndex = currentTopicIndex(user.id, course);
|
||||
if (topicIndex != -1) {
|
||||
final topicID = course.topics[topicIndex].uuid;
|
||||
final topicID = course.loadedTopics[topicIndex].uuid;
|
||||
topicUserMap.putIfAbsent(topicID, () => []).add(user);
|
||||
}
|
||||
}
|
||||
|
|
@ -117,4 +126,57 @@ extension CoursePlanRoomExtension on Room {
|
|||
state.toJson(),
|
||||
);
|
||||
}
|
||||
|
||||
Future<String> launchActivityRoom(
|
||||
ActivityPlanModel activity,
|
||||
ActivityRole? role,
|
||||
) async {
|
||||
final roomID = await client.createRoom(
|
||||
creationContent: {
|
||||
'type': "${PangeaRoomTypes.activitySession}:${activity.activityId}",
|
||||
},
|
||||
visibility: Visibility.private,
|
||||
name: activity.title,
|
||||
initialState: [
|
||||
StateEvent(
|
||||
type: PangeaEventTypes.activityPlan,
|
||||
content: activity.toJson(),
|
||||
),
|
||||
if (activity.imageURL != null)
|
||||
StateEvent(
|
||||
type: EventTypes.RoomAvatar,
|
||||
content: {'url': activity.imageURL},
|
||||
),
|
||||
if (role != null)
|
||||
StateEvent(
|
||||
type: PangeaEventTypes.activityRole,
|
||||
content: ActivityRolesModel({
|
||||
role.id: ActivityRoleModel(
|
||||
id: role.id,
|
||||
userId: client.userID!,
|
||||
role: role.name,
|
||||
),
|
||||
}).toJson(),
|
||||
),
|
||||
RoomDefaults.defaultPowerLevels(
|
||||
client.userID!,
|
||||
),
|
||||
await client.pangeaJoinRules(
|
||||
'knock_restricted',
|
||||
allow: [
|
||||
{
|
||||
"type": "m.room_membership",
|
||||
"room_id": id,
|
||||
}
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
await addToSpace(roomID);
|
||||
if (pangeaSpaceParents.isEmpty) {
|
||||
await client.waitForRoomInSync(roomID);
|
||||
}
|
||||
return roomID;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,12 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/course_plan_model.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/models/language_model.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/models/course_plan/cms_course_plan.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/models/course_plan/cms_course_plan_activity.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/models/course_plan/cms_course_plan_activity_media.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/models/course_plan/cms_course_plan_media.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/models/course_plan/cms_course_plan_topic.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/models/course_plan/cms_course_plan_topic_location.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/models/course_plan/cms_course_plan_topic_location_media.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/payload_client.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/payload_repo.dart';
|
||||
|
||||
class CourseFilter {
|
||||
final LanguageModel? targetLanguage;
|
||||
|
|
@ -43,13 +37,9 @@ class CourseFilter {
|
|||
}
|
||||
|
||||
class CoursePlansRepo {
|
||||
static final Map<String, Completer<CoursePlanModel>> cache = {};
|
||||
static final GetStorage _courseStorage = GetStorage("course_storage");
|
||||
|
||||
static final PayloadClient payload = PayloadClient(
|
||||
baseUrl: Environment.cmsApi,
|
||||
accessToken: MatrixState.pangeaController.userController.accessToken,
|
||||
);
|
||||
|
||||
static CoursePlanModel? _getCached(String id) {
|
||||
final json = _courseStorage.read(id);
|
||||
if (json != null) {
|
||||
|
|
@ -107,15 +97,30 @@ class CoursePlansRepo {
|
|||
return cached;
|
||||
}
|
||||
|
||||
final cmsCoursePlan = await payload.findById(
|
||||
"course-plans",
|
||||
id,
|
||||
CmsCoursePlan.fromJson,
|
||||
);
|
||||
if (cache.containsKey(id)) {
|
||||
return cache[id]!.future;
|
||||
}
|
||||
|
||||
final coursePlan = await _fromCmsCoursePlan(cmsCoursePlan);
|
||||
await _setCached(coursePlan);
|
||||
return coursePlan;
|
||||
final completer = Completer<CoursePlanModel>();
|
||||
cache[id] = completer;
|
||||
|
||||
try {
|
||||
final cmsCoursePlan = await PayloadRepo.payload.findById(
|
||||
"course-plans",
|
||||
id,
|
||||
CmsCoursePlan.fromJson,
|
||||
);
|
||||
|
||||
final coursePlan = cmsCoursePlan.toCoursePlanModel();
|
||||
await _setCached(coursePlan);
|
||||
completer.complete(coursePlan);
|
||||
return coursePlan;
|
||||
} catch (e) {
|
||||
completer.completeError(e);
|
||||
rethrow;
|
||||
} finally {
|
||||
cache.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<List<CoursePlanModel>> search({CourseFilter? filter}) async {
|
||||
|
|
@ -170,7 +175,7 @@ class CoursePlansRepo {
|
|||
}
|
||||
}
|
||||
|
||||
final result = await payload.find(
|
||||
final result = await PayloadRepo.payload.find(
|
||||
CmsCoursePlan.slug,
|
||||
CmsCoursePlan.fromJson,
|
||||
page: 1,
|
||||
|
|
@ -178,13 +183,11 @@ class CoursePlansRepo {
|
|||
where: where,
|
||||
);
|
||||
|
||||
final coursePlans = await Future.wait(
|
||||
result.docs.map(
|
||||
(cmsCoursePlan) => _fromCmsCoursePlan(
|
||||
cmsCoursePlan,
|
||||
),
|
||||
),
|
||||
);
|
||||
final coursePlans = result.docs
|
||||
.map(
|
||||
(cmsCoursePlan) => cmsCoursePlan.toCoursePlanModel(),
|
||||
)
|
||||
.toList();
|
||||
|
||||
await _setCachedSearchResults(
|
||||
filter ?? CourseFilter(),
|
||||
|
|
@ -193,170 +196,4 @@ class CoursePlansRepo {
|
|||
|
||||
return coursePlans;
|
||||
}
|
||||
|
||||
static Future<CoursePlanModel> _fromCmsCoursePlan(
|
||||
CmsCoursePlan cmsCoursePlan,
|
||||
) async {
|
||||
final medias = await _getMedia(cmsCoursePlan);
|
||||
final topics = await _getTopics(cmsCoursePlan);
|
||||
final locations = await _getTopicLocations(topics ?? []);
|
||||
final locationMedias = await _getTopicLocationMedia(locations ?? []);
|
||||
final activities = await _getTopicActivities(topics ?? []);
|
||||
final activityMedias = await _getActivityMedia(activities ?? []);
|
||||
return CoursePlanModel.fromCmsDocs(
|
||||
cmsCoursePlan,
|
||||
medias,
|
||||
topics,
|
||||
locations,
|
||||
locationMedias,
|
||||
activities,
|
||||
activityMedias,
|
||||
);
|
||||
}
|
||||
|
||||
static Future<List<CmsCoursePlanMedia>?> _getMedia(
|
||||
CmsCoursePlan cmsCoursePlan,
|
||||
) async {
|
||||
final docs = cmsCoursePlan.coursePlanMedia?.docs;
|
||||
if (docs == null || docs.isEmpty) return null;
|
||||
|
||||
final where = {
|
||||
"id": {"in": docs.join(",")},
|
||||
};
|
||||
final limit = docs.length;
|
||||
final cmsCoursePlanMediaResult = await payload.find(
|
||||
CmsCoursePlanMedia.slug,
|
||||
CmsCoursePlanMedia.fromJson,
|
||||
where: where,
|
||||
limit: limit,
|
||||
page: 1,
|
||||
sort: "createdAt",
|
||||
);
|
||||
return cmsCoursePlanMediaResult.docs;
|
||||
}
|
||||
|
||||
static Future<List<CmsCoursePlanTopic>?> _getTopics(
|
||||
CmsCoursePlan cmsCoursePlan,
|
||||
) async {
|
||||
final docs = cmsCoursePlan.coursePlanTopics?.docs;
|
||||
if (docs == null || docs.isEmpty) return null;
|
||||
|
||||
final where = {
|
||||
"id": {"in": docs.join(",")},
|
||||
};
|
||||
final limit = docs.length;
|
||||
final cmsCourseTopicsResult = await payload.find(
|
||||
CmsCoursePlanTopic.slug,
|
||||
CmsCoursePlanTopic.fromJson,
|
||||
where: where,
|
||||
limit: limit,
|
||||
page: 1,
|
||||
sort: "createdAt",
|
||||
);
|
||||
return cmsCourseTopicsResult.docs;
|
||||
}
|
||||
|
||||
static Future<List<CmsCoursePlanTopicLocation>?> _getTopicLocations(
|
||||
List<CmsCoursePlanTopic> topics,
|
||||
) async {
|
||||
final List<String> locations = [];
|
||||
for (final top in topics) {
|
||||
if (top.coursePlanTopicLocations?.docs != null) {
|
||||
locations.addAll(top.coursePlanTopicLocations!.docs!);
|
||||
}
|
||||
}
|
||||
if (locations.isEmpty) return null;
|
||||
|
||||
final where = {
|
||||
"id": {"in": locations.join(",")},
|
||||
};
|
||||
final limit = locations.length;
|
||||
final cmsCoursePlanTopicLocationsResult = await payload.find(
|
||||
CmsCoursePlanTopicLocation.slug,
|
||||
CmsCoursePlanTopicLocation.fromJson,
|
||||
where: where,
|
||||
limit: limit,
|
||||
page: 1,
|
||||
sort: "createdAt",
|
||||
);
|
||||
return cmsCoursePlanTopicLocationsResult.docs;
|
||||
}
|
||||
|
||||
static Future<List<CmsCoursePlanTopicLocationMedia>?> _getTopicLocationMedia(
|
||||
List<CmsCoursePlanTopicLocation> locations,
|
||||
) async {
|
||||
final List<String> mediaIds = [];
|
||||
for (final loc in locations) {
|
||||
if (loc.coursePlanTopicLocationMedia?.docs != null) {
|
||||
mediaIds.addAll(loc.coursePlanTopicLocationMedia!.docs!);
|
||||
}
|
||||
}
|
||||
if (mediaIds.isEmpty) return null;
|
||||
|
||||
final where = {
|
||||
"id": {"in": mediaIds.join(",")},
|
||||
};
|
||||
final limit = mediaIds.length;
|
||||
final cmsCoursePlanTopicLocationMediasResult = await payload.find(
|
||||
CmsCoursePlanTopicLocationMedia.slug,
|
||||
CmsCoursePlanTopicLocationMedia.fromJson,
|
||||
where: where,
|
||||
limit: limit,
|
||||
page: 1,
|
||||
sort: "createdAt",
|
||||
);
|
||||
return cmsCoursePlanTopicLocationMediasResult.docs;
|
||||
}
|
||||
|
||||
static Future<List<CmsCoursePlanActivity>?> _getTopicActivities(
|
||||
List<CmsCoursePlanTopic> topics,
|
||||
) async {
|
||||
final List<String> activities = [];
|
||||
for (final top in topics) {
|
||||
if (top.coursePlanActivities?.docs != null) {
|
||||
activities.addAll(top.coursePlanActivities!.docs!);
|
||||
}
|
||||
}
|
||||
if (activities.isEmpty) return null;
|
||||
|
||||
final where = {
|
||||
"id": {"in": activities.join(",")},
|
||||
};
|
||||
final limit = activities.length;
|
||||
final cmsCoursePlanActivitiesResult = await payload.find(
|
||||
CmsCoursePlanActivity.slug,
|
||||
CmsCoursePlanActivity.fromJson,
|
||||
where: where,
|
||||
limit: limit,
|
||||
page: 1,
|
||||
sort: "createdAt",
|
||||
);
|
||||
return cmsCoursePlanActivitiesResult.docs;
|
||||
}
|
||||
|
||||
static Future<List<CmsCoursePlanActivityMedia>?> _getActivityMedia(
|
||||
List<CmsCoursePlanActivity> activity,
|
||||
) async {
|
||||
final List<String> mediaIds = [];
|
||||
for (final act in activity) {
|
||||
if (act.coursePlanActivityMedia?.docs != null) {
|
||||
mediaIds.addAll(act.coursePlanActivityMedia!.docs!);
|
||||
}
|
||||
}
|
||||
if (mediaIds.isEmpty) return null;
|
||||
|
||||
final where = {
|
||||
"id": {"in": mediaIds.join(",")},
|
||||
};
|
||||
final limit = mediaIds.length;
|
||||
final cmsCoursePlanActivityMediasResult = await payload.find(
|
||||
CmsCoursePlanActivityMedia.slug,
|
||||
CmsCoursePlanActivityMedia.fromJson,
|
||||
where: where,
|
||||
limit: limit,
|
||||
page: 1,
|
||||
sort: "createdAt",
|
||||
);
|
||||
return cmsCoursePlanActivityMediasResult.docs;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
86
lib/pangea/course_plans/course_topic_model.dart
Normal file
86
lib/pangea/course_plans/course_topic_model.dart
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
|
||||
import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/course_activity_repo.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/course_location_media_repo.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/course_location_model.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/course_location_repo.dart';
|
||||
|
||||
/// Represents a topic in the course planner response.
|
||||
class CourseTopicModel {
|
||||
final String title;
|
||||
final String description;
|
||||
final String uuid;
|
||||
final List<String> locationIds;
|
||||
final List<String> activityIds;
|
||||
|
||||
CourseTopicModel({
|
||||
required this.title,
|
||||
required this.description,
|
||||
required this.uuid,
|
||||
required this.activityIds,
|
||||
required this.locationIds,
|
||||
});
|
||||
|
||||
bool get locationListComplete => locationIds.length == loadedLocations.length;
|
||||
List<CourseLocationModel> get loadedLocations =>
|
||||
CourseLocationRepo.getSync(locationIds);
|
||||
Future<List<CourseLocationModel>> fetchLocations() =>
|
||||
CourseLocationRepo.get(uuid, locationIds);
|
||||
String? get location => loadedLocations.firstOrNull?.name;
|
||||
|
||||
bool get locationMediaListComplete =>
|
||||
loadedLocationMediaIds.length ==
|
||||
loadedLocations.map((e) => e.mediaIds.length).fold(0, (a, b) => a + b);
|
||||
List<String> get loadedLocationMediaIds => loadedLocations
|
||||
.map((location) => CourseLocationMediaRepo.getSync(location.mediaIds))
|
||||
.expand((e) => e)
|
||||
.toList();
|
||||
Future<List<String>> fetchLocationMedia() async {
|
||||
final allLocationMedia = <String>[];
|
||||
for (final location in await fetchLocations()) {
|
||||
allLocationMedia.addAll(
|
||||
await CourseLocationMediaRepo.get(uuid, location.mediaIds),
|
||||
);
|
||||
}
|
||||
return allLocationMedia;
|
||||
}
|
||||
|
||||
String? get imageUrl => loadedLocationMediaIds.isEmpty
|
||||
? null
|
||||
: "${Environment.cmsApi}${loadedLocationMediaIds.first}";
|
||||
|
||||
bool get activityListComplete =>
|
||||
activityIds.length == loadedActivities.length;
|
||||
List<ActivityPlanModel> get loadedActivities =>
|
||||
CourseActivityRepo.getSync(activityIds);
|
||||
Future<List<ActivityPlanModel>> fetchActivities() =>
|
||||
CourseActivityRepo.get(uuid, activityIds);
|
||||
|
||||
/// Deserialize from JSON
|
||||
factory CourseTopicModel.fromJson(Map<String, dynamic> json) {
|
||||
return CourseTopicModel(
|
||||
title: json['title'] as String,
|
||||
description: json['description'] as String,
|
||||
uuid: json['uuid'] as String,
|
||||
activityIds: (json['activity_ids'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
[],
|
||||
locationIds: (json['location_ids'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList() ??
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
/// Serialize to JSON
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'title': title,
|
||||
'description': description,
|
||||
'uuid': uuid,
|
||||
'activity_ids': activityIds,
|
||||
'location_ids': locationIds,
|
||||
};
|
||||
}
|
||||
}
|
||||
109
lib/pangea/course_plans/course_topic_repo.dart
Normal file
109
lib/pangea/course_plans/course_topic_repo.dart
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/course_plans/course_topic_model.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/models/course_plan/cms_course_plan_topic.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/payload_repo.dart';
|
||||
|
||||
class CourseTopicRepo {
|
||||
static final Map<String, Completer<List<CourseTopicModel>>> _cache = {};
|
||||
static final GetStorage _storage = GetStorage('course_topic_storage');
|
||||
|
||||
static List<CourseTopicModel> getSync(List<String> uuids) {
|
||||
final topics = <CourseTopicModel>[];
|
||||
|
||||
for (final uuid in uuids) {
|
||||
final cached = _getCached(uuid);
|
||||
if (cached != null) {
|
||||
topics.add(cached);
|
||||
}
|
||||
}
|
||||
|
||||
return topics;
|
||||
}
|
||||
|
||||
static Future<List<CourseTopicModel>> get(
|
||||
String courseId,
|
||||
List<String> uuids,
|
||||
) async {
|
||||
final topics = <CourseTopicModel>[];
|
||||
final toFetch = <String>[];
|
||||
|
||||
for (final uuid in uuids) {
|
||||
final cached = _getCached(uuid);
|
||||
if (cached != null) {
|
||||
topics.add(cached);
|
||||
} else {
|
||||
toFetch.add(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
if (toFetch.isNotEmpty) {
|
||||
final fetchedTopics = await _fetch(courseId, toFetch);
|
||||
topics.addAll(fetchedTopics);
|
||||
await _setCached(fetchedTopics);
|
||||
}
|
||||
|
||||
return topics;
|
||||
}
|
||||
|
||||
static CourseTopicModel? _getCached(String uuid) {
|
||||
final json = _storage.read(uuid);
|
||||
if (json != null) {
|
||||
try {
|
||||
return CourseTopicModel.fromJson(Map<String, dynamic>.from(json));
|
||||
} catch (e) {
|
||||
_storage.remove(uuid);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static Future<void> _setCached(List<CourseTopicModel> topics) async {
|
||||
for (final topic in topics) {
|
||||
await _storage.write(topic.uuid, topic.toJson());
|
||||
}
|
||||
}
|
||||
|
||||
static Future<List<CourseTopicModel>> _fetch(
|
||||
String courseId,
|
||||
List<String> uuids,
|
||||
) async {
|
||||
if (_cache.containsKey(courseId)) {
|
||||
return _cache[courseId]!.future;
|
||||
}
|
||||
|
||||
final completer = Completer<List<CourseTopicModel>>();
|
||||
_cache[courseId] = completer;
|
||||
|
||||
final where = {
|
||||
"id": {"in": uuids.join(",")},
|
||||
};
|
||||
|
||||
final limit = uuids.length;
|
||||
|
||||
try {
|
||||
final cmsCourseTopicsResult = await PayloadRepo.payload.find(
|
||||
CmsCoursePlanTopic.slug,
|
||||
CmsCoursePlanTopic.fromJson,
|
||||
where: where,
|
||||
limit: limit,
|
||||
page: 1,
|
||||
sort: "createdAt",
|
||||
);
|
||||
|
||||
final topics = cmsCourseTopicsResult.docs.map((topic) {
|
||||
return topic.toCourseTopicModel();
|
||||
}).toList();
|
||||
|
||||
completer.complete(topics);
|
||||
return topics;
|
||||
} catch (e) {
|
||||
completer.completeError(e);
|
||||
rethrow;
|
||||
} finally {
|
||||
_cache.remove(courseId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,23 +2,20 @@
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:cached_network_image_platform_interface/cached_network_image_platform_interface.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_planner_builder.dart';
|
||||
import 'package:fluffychat/pangea/activity_suggestions/activity_suggestion_card.dart';
|
||||
import 'package:fluffychat/pangea/activity_suggestions/activity_suggestion_dialog.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/url_image_widget.dart';
|
||||
import 'package:fluffychat/pangea/course_creation/course_info_chip_widget.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/course_plan_builder.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/course_plan_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/course_settings/pin_clipper.dart';
|
||||
import 'package:fluffychat/pangea/course_settings/topic_participant_list.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class CourseSettings extends StatelessWidget {
|
||||
final Room room;
|
||||
|
|
@ -64,7 +61,7 @@ class CourseSettings extends StatelessWidget {
|
|||
return Column(
|
||||
spacing: isColumnMode ? 40.0 : 36.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: course.topics.mapIndexed((index, topic) {
|
||||
children: course.loadedTopics.mapIndexed((index, topic) {
|
||||
final unlocked = index <= currentTopicIndex;
|
||||
final usersInTopic = topicsToUsers[topic.uuid] ?? [];
|
||||
return AbsorbPointer(
|
||||
|
|
@ -91,45 +88,18 @@ class CourseSettings extends StatelessWidget {
|
|||
children: [
|
||||
ClipPath(
|
||||
clipper: PinClipper(),
|
||||
child: topic.imageUrl != null
|
||||
? CachedNetworkImage(
|
||||
imageRenderMethodForWeb:
|
||||
ImageRenderMethodForWeb
|
||||
.HttpGet,
|
||||
httpHeaders: {
|
||||
'Authorization':
|
||||
'Bearer ${MatrixState.pangeaController.userController.accessToken}',
|
||||
},
|
||||
width: 54.0,
|
||||
height: 54.0,
|
||||
fit: BoxFit.cover,
|
||||
imageUrl: topic.imageUrl!,
|
||||
placeholder: (context, url) {
|
||||
return const Center(
|
||||
child:
|
||||
CircularProgressIndicator(),
|
||||
);
|
||||
},
|
||||
errorWidget:
|
||||
(context, url, error) {
|
||||
return Container(
|
||||
width: 54.0,
|
||||
height: 54.0,
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme
|
||||
.secondary,
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
: Container(
|
||||
width: 54.0,
|
||||
height: 54.0,
|
||||
decoration: BoxDecoration(
|
||||
color: theme
|
||||
.colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
child: ImageByUrl(
|
||||
imageUrl: topic.imageUrl,
|
||||
width: 54.0,
|
||||
replacement: Container(
|
||||
width: 54.0,
|
||||
height: 54.0,
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
theme.colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (!unlocked)
|
||||
const Positioned(
|
||||
|
|
@ -153,12 +123,13 @@ class CourseSettings extends StatelessWidget {
|
|||
fontSize: titleFontSize,
|
||||
),
|
||||
),
|
||||
CourseInfoChip(
|
||||
icon: Icons.location_on,
|
||||
text: topic.location,
|
||||
fontSize: descFontSize,
|
||||
iconSize: iconSize,
|
||||
),
|
||||
if (topic.location != null)
|
||||
CourseInfoChip(
|
||||
icon: Icons.location_on,
|
||||
text: topic.location!,
|
||||
fontSize: descFontSize,
|
||||
iconSize: iconSize,
|
||||
),
|
||||
if (constraints.maxWidth < 700.0)
|
||||
Padding(
|
||||
padding: const EdgeInsetsGeometry
|
||||
|
|
@ -196,36 +167,21 @@ class CourseSettings extends StatelessWidget {
|
|||
height: 210.0,
|
||||
child: ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: topic.activities.length,
|
||||
itemCount: topic.loadedActivities.length,
|
||||
itemBuilder: (context, index) {
|
||||
final activity = topic.activities[index];
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 24.0),
|
||||
child: ActivityPlannerBuilder(
|
||||
initialActivity: activity,
|
||||
room: room,
|
||||
builder: (activityController) {
|
||||
return ActivitySuggestionCard(
|
||||
controller: activityController,
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return ActivitySuggestionDialog(
|
||||
controller: activityController,
|
||||
buttonText:
|
||||
L10n.of(context).launchToSpace,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
width: 120.0,
|
||||
height: 200.0,
|
||||
fontSize: 12.0,
|
||||
fontSizeSmall: 8.0,
|
||||
iconSize: 8.0,
|
||||
);
|
||||
},
|
||||
child: ActivitySuggestionCard(
|
||||
activity: topic.loadedActivities[index],
|
||||
// TODO: go to activity start page
|
||||
onPressed: () => context.go(
|
||||
"/rooms/spaces/${room.id}/activity/${topic.loadedActivities[index].activityId}",
|
||||
),
|
||||
width: 120.0,
|
||||
height: 200.0,
|
||||
fontSize: 12.0,
|
||||
fontSizeSmall: 8.0,
|
||||
iconSize: 8.0,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import 'package:fluffychat/pangea/course_plans/course_plan_model.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/join_field.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/polymorphic_relationship.dart';
|
||||
|
||||
|
|
@ -65,4 +67,17 @@ class CmsCoursePlan {
|
|||
'createdAt': createdAt,
|
||||
};
|
||||
}
|
||||
|
||||
CoursePlanModel toCoursePlanModel() {
|
||||
return CoursePlanModel(
|
||||
uuid: id,
|
||||
targetLanguage: l2,
|
||||
languageOfInstructions: l1,
|
||||
cefrLevel: LanguageLevelTypeEnumExtension.fromString(cefrLevel),
|
||||
title: title,
|
||||
description: description,
|
||||
mediaIds: coursePlanMedia?.docs ?? [],
|
||||
topicIds: coursePlanTopics?.docs ?? [],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
import 'package:fluffychat/pangea/activity_generator/media_enum.dart';
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_plan_request.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/join_field.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/polymorphic_relationship.dart';
|
||||
|
|
@ -33,6 +36,15 @@ class CmsCoursePlanActivityRole {
|
|||
'avatarUrl': avatarUrl,
|
||||
};
|
||||
}
|
||||
|
||||
ActivityRole toActivityRole() {
|
||||
return ActivityRole(
|
||||
id: id,
|
||||
name: name,
|
||||
goal: goal,
|
||||
avatarUrl: avatarUrl,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents vocabulary in a course plan activity
|
||||
|
|
@ -75,6 +87,7 @@ class CmsCoursePlanActivity {
|
|||
final String instructions;
|
||||
final String l1; // Language of instruction
|
||||
final String l2; // Target language
|
||||
final String mode;
|
||||
final LanguageLevelTypeEnum cefrLevel;
|
||||
final List<CmsCoursePlanActivityRole> roles;
|
||||
final List<CmsCoursePlanVocab> vocabs;
|
||||
|
|
@ -93,6 +106,7 @@ class CmsCoursePlanActivity {
|
|||
required this.instructions,
|
||||
required this.l1,
|
||||
required this.l2,
|
||||
required this.mode,
|
||||
required this.cefrLevel,
|
||||
required this.roles,
|
||||
required this.vocabs,
|
||||
|
|
@ -113,6 +127,7 @@ class CmsCoursePlanActivity {
|
|||
instructions: json['instructions'] as String,
|
||||
l1: json['l1'] as String,
|
||||
l2: json['l2'] as String,
|
||||
mode: json['mode'] as String,
|
||||
cefrLevel: LanguageLevelTypeEnumExtension.fromString(
|
||||
json['cefrLevel'] as String,
|
||||
),
|
||||
|
|
@ -152,6 +167,7 @@ class CmsCoursePlanActivity {
|
|||
'instructions': instructions,
|
||||
'l1': l1,
|
||||
'l2': l2,
|
||||
'mode': mode,
|
||||
'cefrLevel': cefrLevel.string,
|
||||
'roles': roles.map((role) => role.toJson()).toList(),
|
||||
'vocabs': vocabs.map((vocab) => vocab.toJson()).toList(),
|
||||
|
|
@ -163,4 +179,29 @@ class CmsCoursePlanActivity {
|
|||
'createdAt': createdAt,
|
||||
};
|
||||
}
|
||||
|
||||
ActivityPlanModel toActivityPlanModel(String? imageUrl) {
|
||||
return ActivityPlanModel(
|
||||
req: ActivityPlanRequest(
|
||||
topic: "",
|
||||
mode: mode,
|
||||
objective: "",
|
||||
media: MediaEnum.nan,
|
||||
cefrLevel: cefrLevel,
|
||||
languageOfInstructions: l1,
|
||||
targetLanguage: l2,
|
||||
numberOfParticipants: roles.length,
|
||||
),
|
||||
activityId: id,
|
||||
title: title,
|
||||
description: description,
|
||||
learningObjective: learningObjective,
|
||||
instructions: instructions,
|
||||
vocab: vocabs.map((v) => Vocab(lemma: v.lemma, pos: v.pos)).toList(),
|
||||
roles: Map.fromEntries(
|
||||
roles.map((role) => MapEntry(role.id, role.toActivityRole())),
|
||||
),
|
||||
imageURL: imageUrl,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:fluffychat/pangea/course_plans/course_topic_model.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/join_field.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/polymorphic_relationship.dart';
|
||||
|
||||
|
|
@ -65,4 +66,14 @@ class CmsCoursePlanTopic {
|
|||
'createdAt': createdAt,
|
||||
};
|
||||
}
|
||||
|
||||
CourseTopicModel toCourseTopicModel() {
|
||||
return CourseTopicModel(
|
||||
title: title,
|
||||
description: description,
|
||||
uuid: id,
|
||||
activityIds: coursePlanActivities?.docs ?? [],
|
||||
locationIds: coursePlanTopicLocations?.docs ?? [],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:fluffychat/pangea/course_plans/course_location_model.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/join_field.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/polymorphic_relationship.dart';
|
||||
|
||||
|
|
@ -62,4 +63,12 @@ class CmsCoursePlanTopicLocation {
|
|||
'createdAt': createdAt,
|
||||
};
|
||||
}
|
||||
|
||||
CourseLocationModel toCourseLocationModel() {
|
||||
return CourseLocationModel(
|
||||
uuid: id,
|
||||
name: name,
|
||||
mediaIds: coursePlanTopicLocationMedia?.docs ?? [],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
lib/pangea/payload_client/payload_repo.dart
Normal file
10
lib/pangea/payload_client/payload_repo.dart
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
import 'package:fluffychat/pangea/payload_client/payload_client.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class PayloadRepo {
|
||||
static final PayloadClient payload = PayloadClient(
|
||||
baseUrl: Environment.cmsApi,
|
||||
accessToken: MatrixState.pangeaController.userController.accessToken,
|
||||
);
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ import 'package:fluffychat/pangea/user/models/analytics_profile_model.dart';
|
|||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class LoadParticipantsBuilder extends StatefulWidget {
|
||||
final Room room;
|
||||
final Room? room;
|
||||
final bool loadProfiles;
|
||||
final Widget Function(
|
||||
BuildContext context,
|
||||
|
|
@ -34,7 +34,7 @@ class LoadParticipantsBuilderState extends State<LoadParticipantsBuilder> {
|
|||
|
||||
final Map<String, AnalyticsProfileModel> _levelsCache = {};
|
||||
|
||||
List<User> get participants => widget.room.getParticipants();
|
||||
List<User> get participants => widget.room?.getParticipants() ?? [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
|
@ -45,7 +45,7 @@ class LoadParticipantsBuilderState extends State<LoadParticipantsBuilder> {
|
|||
@override
|
||||
void didUpdateWidget(LoadParticipantsBuilder oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.room.id != widget.room.id) {
|
||||
if (oldWidget.room?.id != widget.room?.id) {
|
||||
_loadParticipants();
|
||||
}
|
||||
}
|
||||
|
|
@ -57,7 +57,7 @@ class LoadParticipantsBuilderState extends State<LoadParticipantsBuilder> {
|
|||
error = null;
|
||||
});
|
||||
|
||||
await widget.room.requestParticipants(
|
||||
await widget.room?.requestParticipants(
|
||||
[Membership.join, Membership.invite, Membership.knock],
|
||||
false,
|
||||
true,
|
||||
|
|
@ -70,7 +70,7 @@ class LoadParticipantsBuilderState extends State<LoadParticipantsBuilder> {
|
|||
e: err,
|
||||
s: s,
|
||||
data: {
|
||||
'roomId': widget.room.id,
|
||||
'roomId': widget.room?.id,
|
||||
},
|
||||
);
|
||||
} finally {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:jwt_decode/jwt_decode.dart';
|
||||
import 'package:matrix/matrix.dart' as matrix;
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/client_analytics_extension.dart';
|
||||
import 'package:fluffychat/pangea/bot/utils/bot_name.dart';
|
||||
import 'package:fluffychat/pangea/common/constants/model_keys.dart';
|
||||
|
|
@ -150,7 +148,6 @@ class UserController {
|
|||
_initializing = true;
|
||||
|
||||
try {
|
||||
await GetStorage.init('activity_plan_by_id_storage');
|
||||
await _initialize();
|
||||
_addProfileListener();
|
||||
_addAnalyticsRoomIdsToPublicProfile();
|
||||
|
|
@ -458,21 +455,21 @@ class UserController {
|
|||
);
|
||||
}
|
||||
|
||||
Future<List<ActivityPlanModel>> getBookmarkedActivities() async {
|
||||
if (activitiesProfile == null) {
|
||||
throw Exception("Activities profile is not initialized");
|
||||
}
|
||||
// Future<List<ActivityPlanModel>> getBookmarkedActivities() async {
|
||||
// if (activitiesProfile == null) {
|
||||
// throw Exception("Activities profile is not initialized");
|
||||
// }
|
||||
|
||||
return activitiesProfile!.getBookmarkedActivities();
|
||||
}
|
||||
// return activitiesProfile!.getBookmarkedActivities();
|
||||
// }
|
||||
|
||||
List<ActivityPlanModel> getBookmarkedActivitiesSync() {
|
||||
if (activitiesProfile == null) {
|
||||
throw Exception("Activities profile is not initialized");
|
||||
}
|
||||
// List<ActivityPlanModel> getBookmarkedActivitiesSync() {
|
||||
// if (activitiesProfile == null) {
|
||||
// throw Exception("Activities profile is not initialized");
|
||||
// }
|
||||
|
||||
return activitiesProfile!.getBookmarkedActivitiesSync();
|
||||
}
|
||||
// return activitiesProfile!.getBookmarkedActivitiesSync();
|
||||
// }
|
||||
|
||||
Future<void> updateBookmarkedActivity({
|
||||
required String activityId,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
|
||||
import 'package:fluffychat/pangea/activity_suggestions/activity_plan_repo.dart';
|
||||
import 'package:fluffychat/pangea/common/constants/model_keys.dart';
|
||||
import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart';
|
||||
|
||||
|
|
@ -26,14 +24,14 @@ class ActivitiesProfileModel {
|
|||
_bookmarkedActivities.remove(activityId);
|
||||
}
|
||||
|
||||
Future<List<ActivityPlanModel>> getBookmarkedActivities() => Future.wait(
|
||||
_bookmarkedActivities.map((id) => ActivityPlanRepo.get(id)).toList(),
|
||||
);
|
||||
// Future<List<ActivityPlanModel>> getBookmarkedActivities() => Future.wait(
|
||||
// _bookmarkedActivities.map((id) => ActivityPlanRepo.get(id)).toList(),
|
||||
// );
|
||||
|
||||
List<ActivityPlanModel> getBookmarkedActivitiesSync() => _bookmarkedActivities
|
||||
.map((id) => ActivityPlanRepo.getCached(id))
|
||||
.whereType<ActivityPlanModel>()
|
||||
.toList();
|
||||
// List<ActivityPlanModel> getBookmarkedActivitiesSync() => _bookmarkedActivities
|
||||
// .map((id) => ActivityPlanRepo.getCached(id))
|
||||
// .whereType<ActivityPlanModel>()
|
||||
// .toList();
|
||||
|
||||
static ActivitiesProfileModel fromJson(Map<String, dynamic> json) {
|
||||
if (!json.containsKey(PangeaEventTypes.profileActivities)) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue