fix: stop rebuilding whole course settings tab on screen size change (#4368)
This commit is contained in:
parent
bb0206780c
commit
f59f72c53d
5 changed files with 125 additions and 121 deletions
|
|
@ -10,6 +10,10 @@ import 'package:fluffychat/l10n/l10n.dart';
|
|||
import 'package:fluffychat/pages/settings/settings.dart';
|
||||
import 'package:fluffychat/pangea/chat/constants/default_power_level.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/pages/pangea_room_details.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/course_activities/activity_summaries_provider.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/courses/course_plan_builder.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/courses/course_plan_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/download/download_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/download/download_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/extensions/join_rule_extension.dart';
|
||||
|
|
@ -44,7 +48,28 @@ class ChatDetails extends StatefulWidget {
|
|||
ChatDetailsController createState() => ChatDetailsController();
|
||||
}
|
||||
|
||||
class ChatDetailsController extends State<ChatDetails> {
|
||||
// #Pangea
|
||||
// class ChatDetailsController extends State<ChatDetails> {
|
||||
class ChatDetailsController extends State<ChatDetails>
|
||||
with ActivitySummariesProvider, CoursePlanProvider {
|
||||
bool loadingActivities = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadSummaries();
|
||||
_loadCourseInfo();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant ChatDetails oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.roomId != widget.roomId) {
|
||||
_loadCourseInfo();
|
||||
}
|
||||
}
|
||||
|
||||
// Pangea#
|
||||
bool displaySettings = false;
|
||||
|
||||
void toggleDisplaySettings() =>
|
||||
|
|
@ -52,15 +77,6 @@ class ChatDetailsController extends State<ChatDetails> {
|
|||
|
||||
String? get roomId => widget.roomId;
|
||||
|
||||
// #Pangea
|
||||
final GlobalKey<ChatDetailsController> addConversationBotKey =
|
||||
GlobalKey<ChatDetailsController>();
|
||||
|
||||
bool displayAddStudentOptions = false;
|
||||
void toggleAddStudentOptions() =>
|
||||
setState(() => displayAddStudentOptions = !displayAddStudentOptions);
|
||||
// Pangea#
|
||||
|
||||
void setDisplaynameAction() async {
|
||||
final room = Matrix.of(context).client.getRoomById(roomId!)!;
|
||||
final input = await showTextInputDialog(
|
||||
|
|
@ -208,14 +224,6 @@ class ChatDetailsController extends State<ChatDetails> {
|
|||
);
|
||||
}
|
||||
|
||||
static const fixedWidth = 360.0;
|
||||
|
||||
@override
|
||||
// #Pangea
|
||||
Widget build(BuildContext context) => PangeaRoomDetailsView(this);
|
||||
// Widget build(BuildContext context) => ChatDetailsView(this);
|
||||
// Pangea#
|
||||
|
||||
// #Pangea
|
||||
void downloadChatAction() async {
|
||||
if (roomId == null) return;
|
||||
|
|
@ -373,5 +381,53 @@ class ChatDetailsController extends State<ChatDetails> {
|
|||
if (resp.isError || resp.result == null || !mounted) return;
|
||||
context.go('/rooms/${resp.result}/invite');
|
||||
}
|
||||
|
||||
Future<void> _loadCourseInfo() async {
|
||||
final room = Matrix.of(context).client.getRoomById(roomId!);
|
||||
if (room == null || !room.isSpace || room.coursePlan == null) {
|
||||
setState(() {
|
||||
course = null;
|
||||
loadingCourse = false;
|
||||
loadingTopics = false;
|
||||
loadingActivities = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() => loadingActivities = true);
|
||||
await loadCourse(room.coursePlan!.uuid);
|
||||
if (course != null) {
|
||||
await loadTopics();
|
||||
await loadAllActivities();
|
||||
}
|
||||
if (mounted) setState(() => loadingActivities = false);
|
||||
}
|
||||
|
||||
Future<void> _loadSummaries() async {
|
||||
try {
|
||||
final room = Matrix.of(context).client.getRoomById(roomId!);
|
||||
if (room == null || !room.isSpace) return;
|
||||
await loadRoomSummaries(
|
||||
room.spaceChildren.map((c) => c.roomId).whereType<String>().toList(),
|
||||
);
|
||||
} catch (e, s) {
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {
|
||||
"message": "Failed to load activity summaries",
|
||||
"roomId": roomId,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
// Pangea#
|
||||
|
||||
static const fixedWidth = 360.0;
|
||||
|
||||
@override
|
||||
// #Pangea
|
||||
Widget build(BuildContext context) => PangeaRoomDetailsView(this);
|
||||
// Widget build(BuildContext context) => ChatDetailsView(this);
|
||||
// Pangea#
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ class ActivityFinishedStatusMessage extends StatelessWidget {
|
|||
);
|
||||
|
||||
if (navigate == true && controller.room.courseParent != null) {
|
||||
context.go(
|
||||
context.push(
|
||||
"/rooms/spaces/${controller.room.courseParent!.id}/details?tab=course",
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -139,7 +139,8 @@ class VocabAnalyticsListView extends StatelessWidget {
|
|||
key: const PageStorageKey("vocab-analytics-list-view-page-key"),
|
||||
slivers: [
|
||||
// Full-width tooltip
|
||||
if (!controller.isSearching && controller.selectedConstructLevel == null)
|
||||
if (!controller.isSearching &&
|
||||
controller.selectedConstructLevel == null)
|
||||
const SliverToBoxAdapter(
|
||||
child: InstructionsInlineTooltip(
|
||||
instructionsEnum: InstructionsEnum.analyticsVocabList,
|
||||
|
|
|
|||
|
|
@ -287,12 +287,7 @@ class SpaceDetailsContent extends StatelessWidget {
|
|||
case SpaceSettingsTabs.course:
|
||||
return SingleChildScrollView(
|
||||
child: CourseSettings(
|
||||
// on redirect back to chat settings after completing activity,
|
||||
// course settings doesn't refresh activity details by default
|
||||
// the key forces a rebuild on this redirect
|
||||
key: ValueKey(controller.widget.activeTab),
|
||||
room: room,
|
||||
courseId: room.coursePlan?.uuid,
|
||||
controller: controller,
|
||||
),
|
||||
);
|
||||
case SpaceSettingsTabs.participants:
|
||||
|
|
|
|||
|
|
@ -9,101 +9,50 @@ import 'package:matrix/matrix.dart';
|
|||
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pages/chat_details/chat_details.dart';
|
||||
import 'package:fluffychat/pangea/activity_planner/activity_plan_model.dart';
|
||||
import 'package:fluffychat/pangea/activity_suggestions/activity_suggestion_card.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/url_image_widget.dart';
|
||||
import 'package:fluffychat/pangea/course_creation/course_info_chip_widget.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/course_activities/activity_summaries_provider.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/courses/course_plan_builder.dart';
|
||||
import 'package:fluffychat/pangea/course_plans/courses/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/pangea/events/constants/pangea_event_types.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class CourseSettings extends StatefulWidget {
|
||||
final Room room;
|
||||
class CourseSettings extends StatelessWidget {
|
||||
// final Room room;
|
||||
|
||||
/// The course ID to load, from the course plan event in the room.
|
||||
/// Separate from the room to trigger didUpdateWidget on change
|
||||
final String? courseId;
|
||||
// /// The course ID to load, from the course plan event in the room.
|
||||
// /// Separate from the room to trigger didUpdateWidget on change
|
||||
// final String? courseId;
|
||||
final ChatDetailsController controller;
|
||||
|
||||
const CourseSettings({
|
||||
super.key,
|
||||
required this.room,
|
||||
required this.courseId,
|
||||
required this.controller,
|
||||
});
|
||||
|
||||
@override
|
||||
State<CourseSettings> createState() => CourseSettingsState();
|
||||
}
|
||||
|
||||
class CourseSettingsState extends State<CourseSettings>
|
||||
with ActivitySummariesProvider, CoursePlanProvider {
|
||||
Room get room => widget.room;
|
||||
bool _loadingActivities = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadSummaries();
|
||||
_loadCourseInfo();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant CourseSettings oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.room.id != widget.room.id ||
|
||||
oldWidget.courseId != widget.courseId) {
|
||||
_loadCourseInfo();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadCourseInfo() async {
|
||||
if (widget.courseId == null) {
|
||||
setState(() {
|
||||
course = null;
|
||||
loadingCourse = false;
|
||||
loadingTopics = false;
|
||||
_loadingActivities = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() => _loadingActivities = true);
|
||||
await loadCourse(widget.courseId!);
|
||||
if (course != null) {
|
||||
await loadTopics();
|
||||
await loadAllActivities();
|
||||
}
|
||||
if (mounted) setState(() => _loadingActivities = false);
|
||||
}
|
||||
|
||||
Future<void> _loadSummaries() async {
|
||||
try {
|
||||
await loadRoomSummaries(
|
||||
room.spaceChildren.map((c) => c.roomId).whereType<String>().toList(),
|
||||
);
|
||||
} catch (e, s) {
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {
|
||||
"message": "Failed to load activity summaries",
|
||||
"roomId": room.id,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (loadingCourse) {
|
||||
if (controller.loadingCourse) {
|
||||
return const Center(child: CircularProgressIndicator.adaptive());
|
||||
}
|
||||
|
||||
if (course == null || courseError != null) {
|
||||
final room =
|
||||
Matrix.of(context).client.getRoomById(controller.widget.roomId);
|
||||
if (room == null || !room.isSpace) {
|
||||
return Center(
|
||||
child: Text(
|
||||
L10n.of(context).noCourseFound,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (controller.course == null || controller.courseError != null) {
|
||||
return room.canChangeStateEvent(PangeaEventTypes.coursePlan)
|
||||
? Column(
|
||||
spacing: 50.0,
|
||||
|
|
@ -121,8 +70,8 @@ class CourseSettingsState extends State<CourseSettings>
|
|||
foregroundColor:
|
||||
Theme.of(context).colorScheme.onPrimaryContainer,
|
||||
),
|
||||
onPressed: () =>
|
||||
context.go("/rooms/spaces/${room.id}/addcourse"),
|
||||
onPressed: () => context
|
||||
.go("/rooms/spaces/${controller.roomId}/addcourse"),
|
||||
child: Row(
|
||||
spacing: 8.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
|
@ -149,39 +98,41 @@ class CourseSettingsState extends State<CourseSettings>
|
|||
final double descFontSize = isColumnMode ? 12.0 : 8.0;
|
||||
final double iconSize = isColumnMode ? 16.0 : 12.0;
|
||||
|
||||
if (loadingTopics) {
|
||||
if (controller.loadingTopics) {
|
||||
return const Center(child: CircularProgressIndicator.adaptive());
|
||||
}
|
||||
|
||||
final activeTopicId = currentTopicId(
|
||||
room.client.userID!,
|
||||
course!,
|
||||
final activeTopicId = controller.currentTopicId(
|
||||
Matrix.of(context).client.userID!,
|
||||
controller.course!,
|
||||
);
|
||||
|
||||
final int? topicIndex =
|
||||
activeTopicId == null ? null : course!.topicIds.indexOf(activeTopicId);
|
||||
final int? topicIndex = activeTopicId == null
|
||||
? null
|
||||
: controller.course!.topicIds.indexOf(activeTopicId);
|
||||
|
||||
final Map<String, List<User>> userTopics = _loadingActivities
|
||||
final Map<String, List<User>> userTopics = controller.loadingActivities
|
||||
? {}
|
||||
: topicsToUsers(
|
||||
: controller.topicsToUsers(
|
||||
room,
|
||||
course!,
|
||||
controller.course!,
|
||||
);
|
||||
|
||||
return Column(
|
||||
spacing: isColumnMode ? 40.0 : 36.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: course!.topicIds.mapIndexed((index, topicId) {
|
||||
final topic = course!.loadedTopics[topicId];
|
||||
children: controller.course!.topicIds.mapIndexed((index, topicId) {
|
||||
final topic = controller.course!.loadedTopics[topicId];
|
||||
if (topic == null) {
|
||||
return const SizedBox();
|
||||
}
|
||||
|
||||
final usersInTopic = userTopics[topicId] ?? [];
|
||||
final activityError = activityErrors[topicId];
|
||||
final activityError = controller.activityErrors[topicId];
|
||||
|
||||
final bool locked = topicIndex == null ? false : index > topicIndex;
|
||||
final disabled = locked || _loadingActivities || activityError != null;
|
||||
final disabled =
|
||||
locked || controller.loadingActivities || activityError != null;
|
||||
return AbsorbPointer(
|
||||
absorbing: disabled,
|
||||
child: Opacity(
|
||||
|
|
@ -224,7 +175,7 @@ class CourseSettingsState extends State<CourseSettings>
|
|||
right: 0,
|
||||
child: Icon(Icons.lock, size: 24.0),
|
||||
)
|
||||
else if (_loadingActivities)
|
||||
else if (controller.loadingActivities)
|
||||
const SizedBox(
|
||||
width: 54.0,
|
||||
height: 54.0,
|
||||
|
|
@ -285,7 +236,7 @@ class CourseSettingsState extends State<CourseSettings>
|
|||
},
|
||||
),
|
||||
if (!locked)
|
||||
_loadingActivities
|
||||
controller.loadingActivities
|
||||
? const Center(
|
||||
child: CircularProgressIndicator.adaptive(),
|
||||
)
|
||||
|
|
@ -299,7 +250,8 @@ class CourseSettingsState extends State<CourseSettings>
|
|||
child: TopicActivitiesList(
|
||||
room: room,
|
||||
activities: topic.loadedActivities,
|
||||
controller: this,
|
||||
hasCompletedActivity:
|
||||
controller.hasCompletedActivity,
|
||||
),
|
||||
)
|
||||
: const SizedBox(),
|
||||
|
|
@ -315,13 +267,13 @@ class CourseSettingsState extends State<CourseSettings>
|
|||
class TopicActivitiesList extends StatefulWidget {
|
||||
final Room room;
|
||||
final Map<String, ActivityPlanModel> activities;
|
||||
final CourseSettingsState controller;
|
||||
final bool Function(String userId, String activityId) hasCompletedActivity;
|
||||
|
||||
const TopicActivitiesList({
|
||||
super.key,
|
||||
required this.room,
|
||||
required this.activities,
|
||||
required this.controller,
|
||||
required this.hasCompletedActivity,
|
||||
});
|
||||
@override
|
||||
State<TopicActivitiesList> createState() => TopicActivitiesListState();
|
||||
|
|
@ -357,7 +309,7 @@ class TopicActivitiesListState extends State<TopicActivitiesList> {
|
|||
itemCount: activityEntries.length,
|
||||
itemBuilder: (context, index) {
|
||||
final activityEntry = activityEntries[index];
|
||||
final complete = widget.controller.hasCompletedActivity(
|
||||
final complete = widget.hasCompletedActivity(
|
||||
widget.room.client.userID!,
|
||||
activityEntry.key,
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue