Skip to content

Commit

Permalink
feat(points): Support filtering points changelog
Browse files Browse the repository at this point in the history
  • Loading branch information
realth000 committed Feb 9, 2024
1 parent 053954f commit feb959b
Show file tree
Hide file tree
Showing 15 changed files with 586 additions and 63 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

## [Unreleased]

### Added

- 支持筛选积分变更历史。

### Fixed

- 修复积分统计页面我的积分标签页中缺少空隙的问题。
Expand Down
80 changes: 78 additions & 2 deletions lib/features/points/bloc/points_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:equatable/equatable.dart';
import 'package:tsdm_client/exceptions/exceptions.dart';
import 'package:tsdm_client/extensions/universal_html.dart';
import 'package:tsdm_client/features/points/models/points_change.dart';
import 'package:tsdm_client/features/points/repository/model/changelog_all_parameters.dart';
import 'package:tsdm_client/features/points/repository/model/changelog_parameter.dart';
import 'package:tsdm_client/features/points/repository/points_repository.dart';
import 'package:tsdm_client/utils/debug.dart';
Expand Down Expand Up @@ -88,6 +89,7 @@ final class PointsChangelogBloc
super(const PointsChangelogState()) {
on<PointsChangelogRefreshRequested>(_onPointsChangelogRefreshRequested);
on<PointsChangelogLoadMoreRequested>(_onPointsChangelogLoadMoreRequested);
on<PointsChangelogQueryRequested>(_onPointsChangelogQueryRequested);
}

/// Repository of changelog.
Expand All @@ -97,11 +99,16 @@ final class PointsChangelogBloc
PointsChangelogRefreshRequested event,
PointsChangelogEmitter emit,
) async {
emit(state.copyWith(status: PointsStatus.loading, fullChangelog: []));
emit(state.copyWith(
status: PointsStatus.loading,
fullChangelog: [],
));
try {
final document = await _pointsRepository
.fetchChangelogPage(state.parameter.copyWith(pageNumber: 1));
emit(_parseDocument(document, state.currentPage));
final s = _parseDocument(document, state.currentPage);
final allParameters = _parseAllParameters(document);
emit(s.copyWith(allParameters: allParameters));
} on HttpRequestFailedException catch (e) {
debug('failed to refresh changelog tab: $e');
emit(state.copyWith(status: PointsStatus.failed));
Expand All @@ -123,6 +130,75 @@ final class PointsChangelogBloc
}
}

Future<void> _onPointsChangelogQueryRequested(
PointsChangelogQueryRequested event,
PointsChangelogEmitter emit,
) async {
emit(
state.copyWith(
status: PointsStatus.loading,
fullChangelog: [],
parameter: event.parameter,
),
);
try {
final document = await _pointsRepository
.fetchChangelogPage(state.parameter.copyWith(pageNumber: 1));
final s = _parseDocument(document, state.currentPage);
final allParameters = _parseAllParameters(document);
emit(s.copyWith(allParameters: allParameters));
} on HttpRequestFailedException catch (e) {
debug('failed to refresh changelog tab: $e');
emit(state.copyWith(status: PointsStatus.failed));
}
}

ChangelogAllParameters _parseAllParameters(uh.Document document) {
// These options seem invisible in browser but exist.
// <select id="optype" name="optype">
// <option value="">Choose</option>
// <option value="TRC">Task</option>
// ...
// </select>
final extTypeList = document
.querySelectorAll('select#exttype > option')
.where((e) => e.attributes['value'] != null)
.map(
(e) => ChangelogPointsType(
name: e.innerText.trim(),
extType: e.attributes['value']!,
),
)
.toList();
final optTypeList = document
.querySelectorAll('select#optype > option')
.where((e) => e.attributes['value'] != null)
.map(
(e) => ChangelogOperationType(
name: e.innerText.trim(),
operation: e.attributes['value']!,
),
)
.toList();

final changeTypeList = document
.querySelectorAll('select#income > option')
.where((e) => e.attributes['value'] != null)
.map(
(e) => ChangelogChangeType(
name: e.innerText.trim(),
changeType: e.attributes['value']!,
),
)
.toList();

return ChangelogAllParameters(
extTypeList: extTypeList,
operationTypeList: optTypeList,
changeTypeList: changeTypeList,
);
}

/// parse [document] into state.
///
/// * [pageNumber] is the current recorded current page number. Used as a
Expand Down
9 changes: 9 additions & 0 deletions lib/features/points/bloc/points_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,12 @@ final class PointsChangelogJumpPageRequested extends PointsChangelogEvent {
/// Page number to jump to.
final int pageNumber;
}

/// User requested to do a query action with given [parameter].
final class PointsChangelogQueryRequested extends PointsChangelogEvent {
/// Constructor.
const PointsChangelogQueryRequested(this.parameter);

/// Parameter to use in query.
final ChangelogParameter parameter;
}
16 changes: 16 additions & 0 deletions lib/features/points/bloc/points_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ final class PointsChangelogState extends Equatable {
this.status = PointsStatus.initial,
this.parameter = const ChangelogParameter.empty(),
this.fullChangelog = const [],
this.allParameters = const ChangelogAllParameters.empty(),
this.currentPage = 1,
this.totalPages = 1,
});
Expand All @@ -97,17 +98,31 @@ final class PointsChangelogState extends Equatable {
/// Parameters used to do the changelog query.
final ChangelogParameter parameter;

/// All available parameters that can fill in the query filter.
///
/// Some parameters in query filters are choices provided by the server side.
/// This parameter holds those parameters.
///
/// e.g. points type, operation type, points change type.
///
/// Also there are some parameters that not came from the server side.
///
/// e.g. start time, end time.
final ChangelogAllParameters allParameters;

/// Copy with.
PointsChangelogState copyWith({
PointsStatus? status,
ChangelogParameter? parameter,
ChangelogAllParameters? allParameters,
List<PointsChange>? fullChangelog,
int? currentPage,
int? totalPages,
}) {
return PointsChangelogState(
status: status ?? this.status,
parameter: parameter ?? this.parameter,
allParameters: allParameters ?? this.allParameters,
fullChangelog: fullChangelog ?? this.fullChangelog,
currentPage: currentPage ?? this.currentPage,
totalPages: totalPages ?? this.totalPages,
Expand All @@ -118,6 +133,7 @@ final class PointsChangelogState extends Equatable {
List<Object?> get props => [
status,
parameter,
allParameters,
fullChangelog,
currentPage,
totalPages,
Expand Down
4 changes: 2 additions & 2 deletions lib/features/points/models/points_change.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ enum PointsChangeType {
/// We do not know the points become more or less.
///
/// Use as a fallback type.
unknown,
unlimited,
}

/// A single change on the user's points.
Expand Down Expand Up @@ -134,7 +134,7 @@ class PointsChange extends Equatable {
'xg1' => PointsChangeType.less,
// Fallback to unknown type.
// This might not happen but we should consider it.
String() || null => PointsChangeType.unknown,
String() || null => PointsChangeType.unlimited,
};
final changeMap = <String, String>{};
for (var i = 0; i < attrNameList.length; i++) {
Expand Down
101 changes: 101 additions & 0 deletions lib/features/points/repository/model/changelog_all_parameters.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import 'package:equatable/equatable.dart';

/// Points type.
///
/// Contains type readable [name] and parameter [extType] used in parameters.
class ChangelogPointsType extends Equatable {
/// Constructor.
const ChangelogPointsType({
required this.name,
required this.extType,
});

/// Human readable name.
///
/// e.g. 威望
final String name;

/// Parameter used in filter query.
///
/// e.g. 1
final String extType;

@override
List<Object?> get props => [name, extType];
}

/// Changelog event operation type.
///
/// Contains a human readable [name] and an [operation] name used in query
/// filter parameters.
final class ChangelogOperationType extends Equatable {
/// Constructor.
const ChangelogOperationType({required this.name, required this.operation});

/// Human readable name.
///
/// e.g. 转账接收
final String name;

/// Operation name used in query filter parameters.
///
/// e.g. RCV
final String operation;

@override
List<Object?> get props => [name, operation];
}

/// Changelog event points change type.
///
/// Contains a human readable [name] and an [changeType] name used in query
/// filter parameters.
final class ChangelogChangeType extends Equatable {
/// Constructor.
const ChangelogChangeType({required this.name, required this.changeType});

/// Human readable name.
///
/// e.g. 收入
final String name;

/// Operation name used in query filter parameters.
///
/// e.g. 1
final String changeType;

@override
List<Object?> get props => [name, changeType];
}

/// Represent all parameters that user can use to make a query.
class ChangelogAllParameters extends Equatable {
/// Constructor.
const ChangelogAllParameters({
required this.extTypeList,
required this.operationTypeList,
required this.changeTypeList,
});

/// Construct an empty parameter.
const ChangelogAllParameters.empty()
: extTypeList = const [],
operationTypeList = const [],
changeTypeList = const [];

/// All available points type to filter.
final List<ChangelogPointsType> extTypeList;

/// All available operation type to filter.
final List<ChangelogOperationType> operationTypeList;

/// All available change types.
final List<ChangelogChangeType> changeTypeList;

@override
List<Object?> get props => [
extTypeList,
operationTypeList,
changeTypeList,
];
}
33 changes: 8 additions & 25 deletions lib/features/points/repository/model/changelog_parameter.dart
Original file line number Diff line number Diff line change
@@ -1,31 +1,14 @@
import 'package:equatable/equatable.dart';

/// Points value change types.
enum IncomeType {
/// Outcome, points decreased.
outcome(-1),

/// Do not limit the search this type.
all(0),

/// Income, points increased.
income(1);

const IncomeType(this.value);

/// Value when used in [ChangelogParameter].
final int value;
}

/// Query parameters used in log request.
final class ChangelogParameter extends Equatable {
/// Constructor.
const ChangelogParameter({
required this.extType,
required this.operation,
required this.changeType,
required this.startTime,
required this.endTime,
required this.incomeType,
required this.operation,
required this.pageNumber,
});

Expand All @@ -34,7 +17,7 @@ final class ChangelogParameter extends Equatable {
: extType = '',
startTime = '',
endTime = '',
incomeType = IncomeType.all,
changeType = '',
operation = '',
pageNumber = 1;

Expand All @@ -58,7 +41,7 @@ final class ChangelogParameter extends Equatable {
final String endTime;

/// Points increase/decrease.
final IncomeType incomeType;
final String changeType;

/// Operation type.
final String operation;
Expand All @@ -71,23 +54,23 @@ final class ChangelogParameter extends Equatable {
String? extType,
String? startTime,
String? endTime,
IncomeType? incomeType,
String? incomeType,
String? operation,
int? pageNumber,
}) {
return ChangelogParameter(
extType: extType ?? this.extType,
startTime: startTime ?? this.startTime,
endTime: endTime ?? this.endTime,
incomeType: incomeType ?? this.incomeType,
changeType: incomeType ?? this.changeType,
operation: operation ?? this.operation,
pageNumber: pageNumber ?? this.pageNumber,
);
}

@override
String toString() {
return '&exttype=$extType&income=${incomeType.value}&optype=$operation&'
return '&exttype=$extType&income=$changeType&optype=$operation&'
'starttime=$startTime&endtime=$endTime&page=$pageNumber';
}

Expand All @@ -96,7 +79,7 @@ final class ChangelogParameter extends Equatable {
extType,
startTime,
endTime,
incomeType,
changeType,
operation,
pageNumber,
];
Expand Down
Loading

0 comments on commit feb959b

Please sign in to comment.