Skip to content
This repository has been archived by the owner on Oct 22, 2021. It is now read-only.

Filter assignments by course #257

Merged
merged 17 commits into from
Apr 24, 2020
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/app/app_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ class AppConfig {
overline: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 12,
color: brightness.disabledOnColor,
),
);
}
Expand Down
10 changes: 9 additions & 1 deletion lib/app/sort_filter/filtering.dart
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ class DateRangeFilterSelection {

typedef CategoryLabelBuilder<C> = Widget Function(
BuildContext context, C category);
typedef WebQueryCategoryParser<C> = C Function(String value);

class CategoryFilter<T, C> extends Filter<T, Set<C>> {
const CategoryFilter(
Expand All @@ -164,10 +165,13 @@ class CategoryFilter<T, C> extends Filter<T, Set<C>> {
@required this.categoriesController,
@required this.categoryLabelBuilder,
this.defaultSelection = const {},
this.webQueryKey,
@required this.webQueryParser,
}) : assert(selector != null),
assert(categoriesController != null),
assert(categoryLabelBuilder != null),
assert(defaultSelection != null),
assert(webQueryParser != null),
super(titleGetter);

final Selector<T, C> selector;
Expand All @@ -177,9 +181,13 @@ class CategoryFilter<T, C> extends Filter<T, Set<C>> {
@override
final Set<C> defaultSelection;

final String webQueryKey;
final WebQueryCategoryParser<C> webQueryParser;

@override
Set<C> tryParseWebQuery(Map<String, String> query, String key) {
return {};
final queryValue = query[webQueryKey ?? key];
return queryValue.split(',').map(webQueryParser).toSet();
}

@override
Expand Down
25 changes: 11 additions & 14 deletions lib/app/sort_filter/sort_filter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -124,18 +124,10 @@ class SortFilterSelection<T> {
) {
assert(config.filters[flagsKey] is FlagsFilter);

return SortFilterSelection(
config: config,
sortSelectionKey: sortSelectionKey,
sortOrder: sortOrder,
filterSelections: {
...filterSelections,
flagsKey: <String, bool>{
...filterSelections[flagsKey],
flag: selection,
}
},
);
return withFilterSelection(flagsKey, <String, bool>{
...filterSelections[flagsKey],
flag: selection,
});
}

List<T> apply(List<T> allItems) {
Expand Down Expand Up @@ -253,11 +245,11 @@ class _Section extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
padding: EdgeInsets.symmetric(vertical: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(title, style: context.textTheme.overline),
Text(title.toUpperCase(), style: context.textTheme.overline),
SizedBox(height: 4),
child,
],
Expand Down Expand Up @@ -334,6 +326,11 @@ mixin SortFilterStateMixin<W extends SortFilterWidget<T>, T> on State<W> {
setState(() => sortFilterSelection = selection);
}

void setFilter(String key, dynamic selection) {
updateSortFilterSelection(
sortFilterSelection.withFilterSelection(key, selection));
}

// ignore: avoid_positional_boolean_parameters
void setFlagFilter(String key, bool value) {
updateSortFilterSelection(
Expand Down
8 changes: 1 addition & 7 deletions lib/app/widgets/card.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import 'package:black_hole_flutter/black_hole_flutter.dart';
import 'package:flutter/material.dart';

import 'text.dart';

class FancyCard extends StatelessWidget {
const FancyCard({
Key key,
Expand Down Expand Up @@ -55,11 +53,7 @@ class FancyCard extends StatelessWidget {
if (title != null)
Padding(
padding: EdgeInsets.fromLTRB(16, 0, 16, 16),
child: FancyText(
title.toUpperCase(),
style: context.textTheme.overline,
emphasis: TextEmphasis.disabled,
),
child: Text(title.toUpperCase(), style: context.textTheme.overline),
),
Padding(
padding: omitHorizontalPadding
Expand Down
42 changes: 20 additions & 22 deletions lib/app/widgets/text.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class FancyText extends StatefulWidget {
this.maxLines,
this.textType = TextType.plain,
this.showRichText = false,
this.estimatedLines,
this.estimatedWidth,
this.style,
this.emphasis,
this.textAlign,
Expand All @@ -28,10 +28,7 @@ class FancyText extends StatefulWidget {
assert(!showRichText || maxLines == null,
"maxLines isn't supported in combination with showRichText."),
assert(maxLines == null || maxLines > 0),
assert(estimatedLines == null || estimatedLines > 0),
assert(estimatedLines == null ||
estimatedLines < 1 ||
estimatedLines % 1 == 0),
assert(estimatedWidth == null || estimatedWidth > 0),
assert(!showRichText || textAlign == null,
"textAlign isn't supported in combination with showRichText."),
super(key: key);
Expand All @@ -40,15 +37,15 @@ class FancyText extends StatefulWidget {
String data, {
Key key,
TextType textType = TextType.html,
double estimatedLines,
double estimatedWidth,
TextStyle style,
TextEmphasis emphasis,
}) : this(
data,
key: key,
textType: textType,
showRichText: true,
estimatedLines: estimatedLines,
estimatedWidth: estimatedWidth,
style: style,
emphasis: emphasis,
);
Expand All @@ -58,15 +55,15 @@ class FancyText extends StatefulWidget {
Key key,
int maxLines = 1,
TextType textType = TextType.html,
double estimatedLines,
double estimatedWidth,
TextStyle style,
}) : this(
data,
key: key,
maxLines: maxLines,
textType: textType,
showRichText: false,
estimatedLines: estimatedLines,
estimatedWidth: estimatedWidth,
style: style,
emphasis: TextEmphasis.medium,
);
Expand All @@ -75,7 +72,7 @@ class FancyText extends StatefulWidget {
final int maxLines;
final TextType textType;
final bool showRichText;
final double estimatedLines;
final double estimatedWidth;
final TextStyle style;
final TextEmphasis emphasis;
final TextAlign textAlign;
Expand All @@ -85,18 +82,14 @@ class FancyText extends StatefulWidget {
}

class _FancyTextState extends State<FancyText> {
double previewLines;
double lastLineWidthFactor;

@override
void initState() {
super.initState();

final estimatedLines = widget.estimatedLines ?? widget.maxLines ?? 1;
if (estimatedLines < 1) {
previewLines = estimatedLines;
} else {
previewLines =
estimatedLines - 1 + lerpDouble(0.2, 0.9, Random().nextDouble());
if (widget.estimatedWidth == null) {
lastLineWidthFactor = lerpDouble(0.2, 0.9, Random().nextDouble());
}
}

Expand Down Expand Up @@ -147,29 +140,34 @@ class _FancyTextState extends State<FancyText> {
final resolvedStyle = DefaultTextStyle.of(context).style.merge(style);
final color = context.theme.isDark ? theme.disabledColor : Colors.black38;

Widget buildBar(double widthFactor) {
Widget buildBar({double width, double widthFactor}) {
assert((width == null) != (widthFactor == null));
return Material(
shape: StadiumBorder(),
color: color,
child: FractionallySizedBox(
widthFactor: widthFactor,
child: SizedBox(height: resolvedStyle.fontSize),
child: SizedBox(width: width, height: resolvedStyle.fontSize),
),
);
}

final fullLines = previewLines.floor();
assert(widget.estimatedWidth == null || lastLineWidthFactor == null);
final fullLines = widget.maxLines != null ? widget.maxLines - 1 : 0;
final lineSpacing =
((resolvedStyle.height ?? 1.5) - 1) * resolvedStyle.fontSize;
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
for (var i = 0; i < fullLines; i++) ...[
buildBar(1),
buildBar(widthFactor: 1),
SizedBox(height: lineSpacing)
],
buildBar(previewLines - fullLines),
buildBar(
width: widget.estimatedWidth,
widthFactor: lastLineWidthFactor,
),
],
);
}
Expand Down
23 changes: 1 addition & 22 deletions lib/assignment/widgets/assignment_detail_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class _AssignmentDetailScreenState extends State<AssignmentDetailScreen>
initialTabIndex: initialTabIndex,
appBarBuilder: (_) => FancyAppBar(
title: Text(assignment.name),
subtitle: _buildSubtitle(context, assignment.courseId),
subtitle: CourseName.orNull(assignment.courseId),
actions: <Widget>[
if (user?.hasPermission(Permission.assignmentEdit) == true)
_buildArchiveAction(context, assignment),
Expand Down Expand Up @@ -87,27 +87,6 @@ class _AssignmentDetailScreenState extends State<AssignmentDetailScreen>
);
}

Widget _buildSubtitle(BuildContext context, Id<Course> courseId) {
if (courseId == null) {
return null;
}

return CachedRawBuilder<Course>(
controller: courseId.controller,
builder: (context, update) {
return Row(children: <Widget>[
CourseColorDot(update.data),
SizedBox(width: 8),
Text(
update.data?.name ??
update.error?.toString() ??
context.s.general_loading,
),
]);
},
);
}

Widget _buildArchiveAction(BuildContext context, Assignment assignment) {
final s = context.s;

Expand Down
57 changes: 28 additions & 29 deletions lib/assignment/widgets/assignments_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,22 +41,13 @@ class AssignmentsScreen extends SortFilterWidget<Assignment> {
selector: (assignment) => assignment.dueAt?.inLocalZone()?.calendarDate,
defaultSelection: DateRangeFilterSelection(start: LocalDate.today()),
),
'course': CategoryFilter<Assignment, Id<Course>>(
(s) => 'Course',
'courseId': CategoryFilter<Assignment, Id<Course>>(
(s) => s.assignment_assignment_property_course,
selector: (assignment) => assignment.courseId,
categoriesController: services.storage.root.courses.controller
.map((courses) => courses.map((c) => c.id)),
categoryLabelBuilder: (_, courseId) {
return CachedRawBuilder<Course>(
controller: courseId.controller,
builder: (_, update) {
return FancyText(
update.error?.toString() ?? update.data?.name,
estimatedLines: 0.2,
);
},
);
},
categoryLabelBuilder: (_, courseId) => CourseName(courseId),
webQueryParser: (value) => Id<Course>(value),
),
'more': FlagsFilter<Assignment>(
(s) => s.general_entity_property_more,
Expand Down Expand Up @@ -118,6 +109,16 @@ class _AssignmentsScreenState extends State<AssignmentsScreen>
EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: AssignmentCard(
assignment: assignments[index],
onCourseClicked: (courseId) =>
setFilter('courseId', {courseId}),
onOverdueClicked: () {
setFilter(
'dueAt',
DateRangeFilterSelection(
end: LocalDate.today() - Period(days: 1),
),
);
},
setFlagFilterCallback: setFlagFilter,
),
),
Expand All @@ -132,24 +133,28 @@ class _AssignmentsScreenState extends State<AssignmentsScreen>
}
}

typedef OnCourseClicked = void Function(Id<Course> courseId);
JonasWanke marked this conversation as resolved.
Show resolved Hide resolved

class AssignmentCard extends StatelessWidget {
const AssignmentCard({
@required this.assignment,
@required this.onCourseClicked,
@required this.onOverdueClicked,
@required this.setFlagFilterCallback,
}) : assert(assignment != null),
assert(onCourseClicked != null),
assert(onOverdueClicked != null),
assert(setFlagFilterCallback != null);

final Assignment assignment;
final OnCourseClicked onCourseClicked;
final VoidCallback onOverdueClicked;
final SetFlagFilterCallback<Assignment> setFlagFilterCallback;

void _showAssignmentDetailsScreen(BuildContext context) {
context.navigator.pushNamed('/homework/${assignment.id}');
}

@override
Widget build(BuildContext context) {
return FancyCard(
onTap: () => _showAssignmentDetailsScreen(context),
onTap: () => context.navigator.pushNamed('/homework/${assignment.id}'),
omitBottomPadding: true,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
Expand Down Expand Up @@ -190,16 +195,10 @@ class AssignmentCard extends StatelessWidget {

return <Widget>[
if (assignment.courseId != null)
CachedRawBuilder<Course>(
controller: assignment.courseId.controller,
builder: (_, update) {
return CourseChip(
update.data,
onPressed: () {
// TODO(JonasWanke): filter list by course, https://github.com/schul-cloud/schulcloud-flutter/issues/145
},
);
},
CourseChip(
assignment.courseId,
key: ValueKey(assignment.courseId),
onPressed: () => onCourseClicked(assignment.courseId),
),
if (assignment.isOverdue)
ActionChip(
Expand All @@ -208,7 +207,7 @@ class AssignmentCard extends StatelessWidget {
color: context.theme.errorColor,
),
label: Text(s.assignment_assignment_overdue),
onPressed: () {},
onPressed: onOverdueClicked,
),
if (assignment.isArchived)
FlagFilterPreviewChip(
Expand Down
1 change: 1 addition & 0 deletions lib/course/course.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export 'routes.dart';
export 'widgets/course_chip.dart';
export 'widgets/course_color_dot.dart';
export 'widgets/course_detail_screen.dart';
export 'widgets/course_name.dart';
export 'widgets/courses_screen.dart';
Loading