You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
fluffychat/lib/pangea/activity_planner/activity_planner_page.dart

352 lines
14 KiB
Dart

import 'dart:math';
import 'package:flutter/material.dart';
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/pangea/activity_planner/activity_list_view.dart';
import 'package:fluffychat/pangea/activity_planner/activity_mode_list_repo.dart';
import 'package:fluffychat/pangea/activity_planner/activity_plan_request.dart';
import 'package:fluffychat/pangea/activity_planner/learning_objective_list_repo.dart';
import 'package:fluffychat/pangea/activity_planner/list_request_schema.dart';
import 'package:fluffychat/pangea/activity_planner/media_enum.dart';
import 'package:fluffychat/pangea/activity_planner/suggestion_form_field.dart';
import 'package:fluffychat/pangea/activity_planner/topic_list_repo.dart';
import 'package:fluffychat/pangea/chat_settings/widgets/language_level_dropdown.dart';
import 'package:fluffychat/pangea/instructions/instructions_enum.dart';
import 'package:fluffychat/pangea/instructions/instructions_inline_tooltip.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/pangea/learning_settings/widgets/p_language_dropdown.dart';
import 'package:fluffychat/widgets/matrix.dart';
enum _PageMode {
settings,
generatedActivities,
savedActivities,
}
class ActivityPlannerPage extends StatefulWidget {
final String roomID;
const ActivityPlannerPage({super.key, required this.roomID});
@override
ActivityPlannerPageState createState() => ActivityPlannerPageState();
}
class ActivityPlannerPageState extends State<ActivityPlannerPage> {
final _formKey = GlobalKey<FormState>();
/// Index of the content to display
_PageMode _pageMode = _PageMode.settings;
/// Selected values from the form
String? _selectedTopic;
String? _selectedMode;
String? _selectedObjective;
MediaEnum _selectedMedia = MediaEnum.nan;
String? _selectedLanguageOfInstructions;
String? _selectedTargetLanguage;
LanguageLevelTypeEnum? _selectedCefrLevel;
int? _selectedNumberOfParticipants;
List<String> activities = [];
Room? get room => Matrix.of(context).client.getRoomById(widget.roomID);
@override
void initState() {
super.initState();
if (room == null) {
Navigator.of(context).pop();
return;
}
_selectedLanguageOfInstructions =
MatrixState.pangeaController.languageController.userL1?.langCode;
_selectedTargetLanguage =
MatrixState.pangeaController.languageController.userL2?.langCode;
_selectedCefrLevel = LanguageLevelTypeEnum.a1;
_selectedNumberOfParticipants = max(room?.getParticipants().length ?? 1, 1);
}
final _topicController = TextEditingController();
final _objectiveController = TextEditingController();
final _modeController = TextEditingController();
@override
void dispose() {
_topicController.dispose();
_objectiveController.dispose();
_modeController.dispose();
super.dispose();
}
ActivitySettingRequestSchema get req => ActivitySettingRequestSchema(
langCode:
MatrixState.pangeaController.languageController.userL2?.langCode ??
LanguageKeys.defaultLanguage,
);
Future<List<ActivitySettingResponseSchema>> get _topicItems =>
TopicListRepo.get(req);
Future<List<ActivitySettingResponseSchema>> get _modeItems =>
ActivityModeListRepo.get(req);
Future<List<ActivitySettingResponseSchema>> get _objectiveItems =>
LearningObjectiveListRepo.get(req);
Future<void> _generateActivities() async {
_pageMode = _PageMode.generatedActivities;
setState(() {});
}
Future<String> _randomTopic() async {
final topics = await _topicItems;
return (topics..shuffle()).first.name;
}
Future<String> _randomObjective() async {
final objectives = await _objectiveItems;
return (objectives..shuffle()).first.name;
}
Future<String> _randomMode() async {
final modes = await _modeItems;
return (modes..shuffle()).first.name;
}
void _randomizeSelections() async {
_selectedTopic = await _randomTopic();
_selectedObjective = await _randomObjective();
_selectedMode = await _randomMode();
setState(() {
_topicController.text = _selectedTopic!;
_objectiveController.text = _selectedObjective!;
_modeController.text = _selectedMode!;
});
}
// Add validation logic
String? _validateNotNull(String? value) {
if (value == null || value.isEmpty) {
return L10n.of(context).interactiveTranslatorRequired;
}
return null;
}
@override
Widget build(BuildContext context) {
final l10n = L10n.of(context);
return Scaffold(
appBar: AppBar(
leading: _pageMode == _PageMode.settings
? IconButton(
icon: const Icon(Icons.close),
onPressed: () => Navigator.of(context).pop(),
)
: IconButton(
onPressed: () => setState(() => _pageMode = _PageMode.settings),
icon: const Icon(Icons.arrow_back),
),
title: _pageMode == _PageMode.savedActivities
? Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.bookmarks),
const SizedBox(width: 8),
Text(l10n.myBookmarkedActivities),
],
),
)
: Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.event_note_outlined),
const SizedBox(width: 8),
Text(l10n.activityPlannerTitle),
],
),
),
actions: [
Tooltip(
message: l10n.myBookmarkedActivities,
child: IconButton(
onPressed: () =>
setState(() => _pageMode = _PageMode.savedActivities),
icon: const Icon(Icons.bookmarks),
),
),
],
),
body: _pageMode != _PageMode.settings
? ActivityListView(
room: room,
activityPlanRequest: _PageMode.savedActivities == _pageMode
? null
: ActivityPlanRequest(
topic: _selectedTopic!,
mode: _selectedMode!,
objective: _selectedObjective!,
media: _selectedMedia,
languageOfInstructions: _selectedLanguageOfInstructions!,
targetLanguage: _selectedTargetLanguage!,
cefrLevel: _selectedCefrLevel!,
numberOfParticipants: _selectedNumberOfParticipants!,
),
)
: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 600),
child: Form(
key: _formKey,
child: ListView(
padding: const EdgeInsets.all(16),
children: [
const InstructionsInlineTooltip(
instructionsEnum:
InstructionsEnum.activityPlannerOverview,
),
Row(
children: [
Expanded(
child: Column(
children: [
SuggestionFormField(
suggestions: _topicItems,
validator: _validateNotNull,
label: l10n.topicLabel,
placeholder: l10n.topicPlaceholder,
onSelected: (val) => _selectedTopic = val,
initialValue: _selectedTopic,
controller: _topicController,
),
const SizedBox(height: 24),
SuggestionFormField(
suggestions: _objectiveItems,
validator: _validateNotNull,
label: l10n.learningObjectiveLabel,
placeholder:
l10n.learningObjectivePlaceholder,
onSelected: (val) => _selectedObjective = val,
initialValue: _selectedObjective,
controller: _objectiveController,
),
const SizedBox(height: 24),
SuggestionFormField(
suggestions: _modeItems,
validator: _validateNotNull,
label: l10n.modeLabel,
placeholder: l10n.modePlaceholder,
onSelected: (val) => _selectedMode = val,
initialValue: _selectedMode,
controller: _modeController,
),
],
),
),
const SizedBox(width: 4),
Column(
children: [
IconButton(
icon: const Icon(Icons.shuffle),
onPressed: _randomizeSelections,
),
],
),
],
),
const SizedBox(height: 24),
DropdownButtonFormField2<MediaEnum>(
decoration: InputDecoration(labelText: l10n.mediaLabel),
items: MediaEnum.values
.map(
(e) => DropdownMenuItem(
value: e,
child: Text(e.toDisplayCopyUsingL10n(context)),
),
)
.toList(),
onChanged: (val) =>
_selectedMedia = val ?? MediaEnum.nan,
value: _selectedMedia,
),
const SizedBox(height: 24),
LanguageLevelDropdown(
initialLevel: _selectedCefrLevel,
onChanged: (val) => _selectedCefrLevel = val,
),
const SizedBox(height: 24),
PLanguageDropdown(
languages: MatrixState
.pangeaController.pLanguageStore.baseOptions,
onChange: (val) =>
_selectedLanguageOfInstructions = val.langCode,
initialLanguage: MatrixState
.pangeaController.languageController.userL1,
isL2List: false,
decorationText:
L10n.of(context).languageOfInstructionsLabel,
),
const SizedBox(height: 24),
PLanguageDropdown(
languages: MatrixState
.pangeaController.pLanguageStore.targetOptions,
onChange: (val) =>
_selectedTargetLanguage = val.langCode,
initialLanguage: MatrixState
.pangeaController.languageController.userL2,
decorationText: L10n.of(context).targetLanguageLabel,
isL2List: true,
),
const SizedBox(height: 24),
TextFormField(
decoration: InputDecoration(
labelText: l10n.numberOfLearners,
),
keyboardType: TextInputType.number,
validator: (value) {
if (value == null || value.isEmpty) {
return l10n.mustBeInteger;
}
final n = int.tryParse(value);
if (n == null || n <= 0) {
return l10n.mustBeInteger;
}
return null;
},
onChanged: (val) =>
_selectedNumberOfParticipants = int.tryParse(val),
initialValue: _selectedNumberOfParticipants?.toString(),
),
const SizedBox(height: 24),
ElevatedButton(
onPressed: () {
if (_formKey.currentState?.validate() ?? false) {
_generateActivities();
}
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.lightbulb_outline),
const SizedBox(width: 8),
Text(l10n.generateActivitiesButton),
],
),
),
],
),
),
),
),
);
}
}