Skip to content

Commit 27c641e

Browse files
authored
Merge branch 'main' into 218-improve-interactive-translator
2 parents 7828f88 + 5b4c268 commit 27c641e

File tree

6 files changed

+178
-58
lines changed

6 files changed

+178
-58
lines changed

lib/pangea/controllers/my_analytics_controller.dart

+45
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'dart:developer';
33

44
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
55
import 'package:fluffychat/pangea/enum/construct_type_enum.dart';
6+
import 'package:fluffychat/pangea/matrix_event_wrappers/construct_analytics_event.dart';
67
import 'package:fluffychat/pangea/models/student_analytics_summary_model.dart';
78
import 'package:fluffychat/pangea/utils/error_handler.dart';
89
import 'package:flutter/foundation.dart';
@@ -111,4 +112,48 @@ class MyAnalyticsController {
111112
ErrorHandler.logError(e: err, s: s);
112113
}
113114
}
115+
116+
// used to aggregate ConstructEvents, from multiple senders (students) with the same lemma
117+
List<AggregateConstructUses> aggregateConstructData(
118+
List<ConstructEvent> constructs,
119+
) {
120+
final Map<String, List<ConstructEvent>> lemmasToConstructs = {};
121+
for (final construct in constructs) {
122+
lemmasToConstructs[construct.content.lemma] ??= [];
123+
lemmasToConstructs[construct.content.lemma]!.add(construct);
124+
}
125+
126+
final List<AggregateConstructUses> aggregatedConstructs = [];
127+
for (final lemmaToConstructs in lemmasToConstructs.entries) {
128+
final List<ConstructEvent> lemmaConstructs = lemmaToConstructs.value;
129+
final AggregateConstructUses aggregatedData = AggregateConstructUses(
130+
constructs: lemmaConstructs,
131+
);
132+
aggregatedConstructs.add(aggregatedData);
133+
}
134+
return aggregatedConstructs;
135+
}
136+
}
137+
138+
class AggregateConstructUses {
139+
final List<ConstructEvent> _constructs;
140+
141+
AggregateConstructUses({required List<ConstructEvent> constructs})
142+
: _constructs = constructs;
143+
144+
String get lemma {
145+
assert(
146+
_constructs.isNotEmpty &&
147+
_constructs.every(
148+
(construct) =>
149+
construct.content.lemma == _constructs.first.content.lemma,
150+
),
151+
);
152+
return _constructs.first.content.lemma;
153+
}
154+
155+
List<OneConstructUse> get uses => _constructs
156+
.map((construct) => construct.content.uses)
157+
.expand((element) => element)
158+
.toList();
114159
}

lib/pangea/pages/analytics/analytics_list_tile.dart

-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@ class AnalyticsListTileState extends State<AnalyticsListTile> {
104104
)
105105
: null,
106106
selected: widget.selected,
107-
enabled: widget.enabled,
108107
onTap: () {
109108
(room?.isSpace ?? false) && widget.allowNavigateOnSelect
110109
? context.go(

lib/pangea/pages/analytics/base_analytics.dart

+24-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'package:fluffychat/pangea/extensions/client_extension.dart';
55
import 'package:fluffychat/pangea/pages/analytics/base_analytics_view.dart';
66
import 'package:fluffychat/pangea/pages/analytics/student_analytics/student_analytics.dart';
77
import 'package:flutter/material.dart';
8+
import 'package:future_loading_dialog/future_loading_dialog.dart';
89
import 'package:matrix/matrix.dart';
910

1011
import '../../../widgets/matrix.dart';
@@ -101,18 +102,40 @@ class BaseAnalyticsController extends State<BaseAnalyticsPage> {
101102
}
102103
}
103104

104-
void toggleSelection(AnalyticsSelected selectedParam) {
105+
Future<void> toggleSelection(AnalyticsSelected selectedParam) async {
106+
final bool joinSelectedRoom =
107+
selectedParam.type == AnalyticsEntryType.room &&
108+
!enableSelection(
109+
selectedParam,
110+
);
111+
112+
if (joinSelectedRoom) {
113+
await showFutureLoadingDialog(
114+
context: context,
115+
future: () async {
116+
final waitForRoom = Matrix.of(context).client.waitForRoomInSync(
117+
selectedParam.id,
118+
join: true,
119+
);
120+
await Matrix.of(context).client.joinRoom(selectedParam.id);
121+
await waitForRoom;
122+
},
123+
);
124+
}
125+
105126
setState(() {
106127
debugPrint("selectedParam.id is ${selectedParam.id}");
107128
currentLemma = null;
108129
selected = isSelected(selectedParam.id) ? null : selectedParam;
109130
});
131+
110132
pangeaController.analytics.setConstructs(
111133
constructType: ConstructType.grammar,
112134
defaultSelected: widget.defaultSelected,
113135
selected: selected,
114136
removeIT: true,
115137
);
138+
116139
Future.delayed(Duration.zero, () => setState(() {}));
117140
}
118141

lib/pangea/pages/analytics/base_analytics_view.dart

+1
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ class BaseAnalyticsView extends StatelessWidget {
145145
) *
146146
72,
147147
child: TabBarView(
148+
physics: const NeverScrollableScrollPhysics(),
148149
children: [
149150
Column(
150151
crossAxisAlignment:

lib/pangea/pages/analytics/class_list/class_list.dart

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import 'dart:async';
22

3+
import 'package:fluffychat/pangea/enum/time_span.dart';
4+
import 'package:fluffychat/pangea/pages/analytics/class_list/class_list_view.dart';
35
import 'package:flutter/material.dart';
4-
56
import 'package:matrix/matrix.dart';
67

7-
import 'package:fluffychat/pangea/enum/time_span.dart';
8-
import 'package:fluffychat/pangea/pages/analytics/class_list/class_list_view.dart';
98
import '../../../../widgets/matrix.dart';
109
import '../../../constants/pangea_event_types.dart';
1110
import '../../../controllers/pangea_controller.dart';
@@ -42,7 +41,11 @@ class AnalyticsClassListController extends State<AnalyticsClassList> {
4241
if (!(refreshTimer[newState.room.id]?.isActive ?? false)) {
4342
refreshTimer[newState.room.id] = Timer(
4443
const Duration(seconds: 3),
45-
() => updateClassAnalytics(context, newState.room),
44+
() {
45+
if (newState.room.isSpace) {
46+
updateClassAnalytics(context, newState.room);
47+
}
48+
},
4649
);
4750
}
4851
}

lib/pangea/pages/analytics/construct_list.dart

+101-52
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import 'dart:async';
33
import 'package:collection/collection.dart';
44
import 'package:fluffychat/config/app_config.dart';
55
import 'package:fluffychat/pangea/constants/pangea_event_types.dart';
6+
import 'package:fluffychat/pangea/controllers/my_analytics_controller.dart';
67
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
78
import 'package:fluffychat/pangea/enum/construct_type_enum.dart';
8-
import 'package:fluffychat/pangea/matrix_event_wrappers/construct_analytics_event.dart';
99
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
1010
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_representation_event.dart';
1111
import 'package:fluffychat/pangea/models/constructs_analytics_model.dart';
@@ -169,7 +169,7 @@ class ConstructListViewState extends State<ConstructListView> {
169169

170170
int get lemmaIndex =>
171171
constructs?.indexWhere(
172-
(element) => element.content.lemma == widget.controller.currentLemma,
172+
(element) => element.lemma == widget.controller.currentLemma,
173173
) ??
174174
-1;
175175

@@ -217,7 +217,7 @@ class ConstructListViewState extends State<ConstructListView> {
217217

218218
setState(() => fetchingUses = true);
219219
try {
220-
final List<OneConstructUse> uses = currentConstruct!.content.uses;
220+
final List<OneConstructUse> uses = currentConstruct!.uses;
221221
_msgEvents.clear();
222222

223223
for (final OneConstructUse use in uses) {
@@ -236,16 +236,24 @@ class ConstructListViewState extends State<ConstructListView> {
236236
ErrorHandler.logError(
237237
e: err,
238238
s: s,
239-
m: "Failed to fetch uses for current construct ${currentConstruct?.content.lemma}",
239+
m: "Failed to fetch uses for current construct ${currentConstruct?.lemma}",
240240
);
241241
}
242242
}
243243

244-
List<ConstructEvent>? get constructs =>
245-
widget.pangeaController.analytics.constructs;
246-
247-
ConstructEvent? get currentConstruct => constructs?.firstWhereOrNull(
248-
(element) => element.content.lemma == widget.controller.currentLemma,
244+
List<AggregateConstructUses>? get constructs =>
245+
widget.pangeaController.analytics.constructs != null
246+
? widget.pangeaController.myAnalytics
247+
.aggregateConstructData(
248+
widget.pangeaController.analytics.constructs!,
249+
)
250+
.sorted(
251+
(a, b) => b.uses.length.compareTo(a.uses.length),
252+
)
253+
: null;
254+
255+
AggregateConstructUses? get currentConstruct => constructs?.firstWhereOrNull(
256+
(element) => element.lemma == widget.controller.currentLemma,
249257
);
250258

251259
// given the current lemma and list of message events, return a list of
@@ -280,6 +288,13 @@ class ConstructListViewState extends State<ConstructListView> {
280288
return allMsgErrorSteps;
281289
}
282290

291+
Future<void> showConstructMessagesDialog() async {
292+
await showDialog<ConstructMessagesDialog>(
293+
context: context,
294+
builder: (c) => ConstructMessagesDialog(controller: this),
295+
);
296+
}
297+
283298
@override
284299
Widget build(BuildContext context) {
285300
if (!widget.init || fetchingUses) {
@@ -294,58 +309,92 @@ class ConstructListViewState extends State<ConstructListView> {
294309
);
295310
}
296311

297-
final msgEventMatches = getMessageEventMatches();
312+
return Expanded(
313+
child: ListView.builder(
314+
itemCount: constructs!.length,
315+
itemBuilder: (context, index) {
316+
return ListTile(
317+
title: Text(
318+
constructs![index].lemma,
319+
),
320+
subtitle: Text(
321+
'${L10n.of(context)!.total} ${constructs![index].uses.length}',
322+
),
323+
onTap: () async {
324+
final String lemma = constructs![index].lemma;
325+
widget.controller.setCurrentLemma(lemma);
326+
fetchUses().then((_) => showConstructMessagesDialog());
327+
},
328+
);
329+
},
330+
),
331+
);
332+
}
333+
}
298334

299-
return widget.controller.currentLemma == null
300-
? Expanded(
301-
child: ListView.builder(
302-
itemCount: constructs!.length,
303-
itemBuilder: (context, index) {
304-
return ListTile(
305-
title: Text(
306-
constructs![index].content.lemma,
307-
),
308-
subtitle: Text(
309-
'${L10n.of(context)!.total} ${constructs![index].content.uses.length}',
310-
),
311-
onTap: () {
312-
final String lemma = constructs![index].content.lemma;
313-
widget.controller.setCurrentLemma(lemma);
314-
fetchUses();
315-
},
316-
);
317-
},
335+
class ConstructMessagesDialog extends StatelessWidget {
336+
final ConstructListViewState controller;
337+
const ConstructMessagesDialog({
338+
super.key,
339+
required this.controller,
340+
});
341+
342+
@override
343+
Widget build(BuildContext context) {
344+
if (controller.widget.controller.currentLemma == null) {
345+
return const AlertDialog(content: CircularProgressIndicator.adaptive());
346+
}
347+
348+
final msgEventMatches = controller.getMessageEventMatches();
349+
350+
return AlertDialog(
351+
title: Center(child: Text(controller.widget.controller.currentLemma!)),
352+
content: Column(
353+
mainAxisSize: MainAxisSize.min,
354+
crossAxisAlignment: CrossAxisAlignment.start,
355+
children: [
356+
if (controller.constructs![controller.lemmaIndex].uses.length >
357+
controller._msgEvents.length)
358+
Center(
359+
child: Padding(
360+
padding: const EdgeInsets.all(8.0),
361+
child: Text(L10n.of(context)!.roomDataMissing),
362+
),
318363
),
319-
)
320-
: Expanded(
364+
SingleChildScrollView(
321365
child: Column(
322-
crossAxisAlignment: CrossAxisAlignment.start,
323366
children: [
324-
if (constructs![lemmaIndex].content.uses.length >
325-
_msgEvents.length)
326-
Center(
327-
child: Padding(
328-
padding: const EdgeInsets.all(8.0),
329-
child: Text(L10n.of(context)!.roomDataMissing),
330-
),
331-
),
332-
Expanded(
333-
child: ListView.separated(
334-
separatorBuilder: (context, index) =>
367+
...msgEventMatches.mapIndexed(
368+
(index, event) => Column(
369+
children: [
370+
ConstructMessage(
371+
msgEvent: event.msgEvent,
372+
lemma: controller.widget.controller.currentLemma!,
373+
errorMessage: event.lemmaMatch,
374+
),
375+
if (index < msgEventMatches.length - 1)
335376
const Divider(height: 1),
336-
itemCount: msgEventMatches.length,
337-
itemBuilder: (context, index) {
338-
return ConstructMessage(
339-
msgEvent: msgEventMatches[index].msgEvent,
340-
lemma: widget.controller.currentLemma!,
341-
errorMessage: msgEventMatches[index].lemmaMatch,
342-
);
343-
},
377+
],
344378
),
345379
),
346380
],
347381
),
348-
);
382+
),
383+
],
384+
),
385+
actions: [
386+
TextButton(
387+
onPressed: () => Navigator.of(context, rootNavigator: false).pop(),
388+
child: Text(
389+
L10n.of(context)!.close.toUpperCase(),
390+
style: TextStyle(
391+
color:
392+
Theme.of(context).textTheme.bodyMedium?.color?.withAlpha(150),
393+
),
394+
),
395+
),
396+
],
397+
);
349398
}
350399
}
351400

0 commit comments

Comments
 (0)