Skip to content

Commit

Permalink
feat(forum): Support filtering threads
Browse files Browse the repository at this point in the history
Support filtering threads by:
* Thread types.
* Thread special types.
* Thread active time (dateline).
* Sort thread order.
* Digested mark.
* Recommended mark.
  • Loading branch information
realth000 committed Feb 3, 2024
1 parent b550ad4 commit da0daaf
Show file tree
Hide file tree
Showing 13 changed files with 909 additions and 129 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
* 楼层右下角菜单 -> 只看该作者/看所有作者。
- 新增支持看帖时倒序浏览。
* 帖子右上角菜单 -> 倒序浏览/正序浏览。
- 新增支持筛选帖子。
- 缓存首页轮播图。

### Fixed
Expand Down
3 changes: 3 additions & 0 deletions lib/constants/layout.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ const edgeInsetsL20R20B10 = EdgeInsets.only(left: 20, right: 20, bottom: 10);
const edgeInsetsL10T5R5B5 =
EdgeInsets.only(left: 10, top: 5, right: 5, bottom: 5);

/// An [EdgeInsets] with 10 at left, 5 at top and 10 at right.
const edgeInsetsL10T5R10 = EdgeInsets.only(left: 10, top: 5, right: 10);

/// An [EdgeInsets] with 10 at left, 5 at top, 10 at right and 20 at bottom.
const edgeInsetsL10T5R10B20 =
EdgeInsets.only(left: 10, top: 5, right: 10, bottom: 20);
Expand Down
102 changes: 101 additions & 1 deletion lib/features/forum/bloc/forum_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:bloc/bloc.dart';
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/forum/models/thread_filter.dart';
import 'package:tsdm_client/features/forum/repository/forum_repository.dart';
import 'package:tsdm_client/shared/models/forum.dart';
import 'package:tsdm_client/shared/models/normal_thread.dart';
Expand All @@ -15,6 +16,11 @@ part 'forum_state.dart';
/// Emitter
typedef ForumEmitter = Emitter<ForumState>;

final _typeIDRe = RegExp(r'&typeid=(?<id>\d+)');
final _specialTypeRe = RegExp('&specialtype=(?<type>[a-z]+)');
final _orderByRe = RegExp('&orderby=(?<orderby>[a-z]+)');
final _datelineRe = RegExp(r'&dateline=(?<dateline>\d+)');

/// Bloc of forum page.
class ForumBloc extends Bloc<ForumEvent, ForumState> {
/// Constructor.
Expand All @@ -26,6 +32,9 @@ class ForumBloc extends Bloc<ForumEvent, ForumState> {
on<ForumLoadMoreRequested>(_onForumLoadMoreRequested);
on<ForumRefreshRequested>(_onForumRefreshRequested);
on<ForumJumpPageRequested>(_onForumJumpPageRequested);
on<ForumChangeThreadFilterStateRequested>(
_onForumChangeThreadFilterStateRequested,
);
}

final ForumRepository _forumRepository;
Expand All @@ -38,6 +47,7 @@ class ForumBloc extends Bloc<ForumEvent, ForumState> {
final document = await _forumRepository.fetchForum(
fid: state.fid,
pageNumber: event.pageNumber,
filterState: state.filterState,
);
emit(await _parseFromDocument(document, event.pageNumber));
} on HttpRequestFailedException catch (e) {
Expand All @@ -61,7 +71,10 @@ class ForumBloc extends Bloc<ForumEvent, ForumState> {
);

try {
final document = await _forumRepository.fetchForum(fid: state.fid);
final document = await _forumRepository.fetchForum(
fid: state.fid,
filterState: state.filterState,
);
emit(await _parseFromDocument(document, 1));
} on HttpRequestFailedException catch (e) {
debug('failed to load forum page: fid=${state.fid}, pageNumber=1 : $e');
Expand All @@ -79,6 +92,7 @@ class ForumBloc extends Bloc<ForumEvent, ForumState> {
final document = await _forumRepository.fetchForum(
fid: state.fid,
pageNumber: event.pageNumber,
filterState: state.filterState,
);
emit(await _parseFromDocument(document, event.pageNumber));
} on HttpRequestFailedException catch (e) {
Expand All @@ -87,6 +101,31 @@ class ForumBloc extends Bloc<ForumEvent, ForumState> {
}
}

Future<void> _onForumChangeThreadFilterStateRequested(
ForumChangeThreadFilterStateRequested event,
ForumEmitter emit,
) async {
emit(
state.copyWith(
status: ForumStatus.loading,
normalThreadList: [],
filterState: event.filterState,
),
);

try {
final document = await _forumRepository.fetchForum(
fid: state.fid,
filterState: state.filterState,
);

emit(await _parseFromDocument(document, 1));
} on HttpRequestFailedException catch (e) {
debug('failed to load forum page: fid=${state.fid}, pageNumber=1 : $e');
emit(state.copyWith(status: ForumStatus.failed));
}
}

Future<ForumState> _parseFromDocument(
uh.Document document,
int pageNumber,
Expand Down Expand Up @@ -137,6 +176,63 @@ class ForumBloc extends Bloc<ForumEvent, ForumState> {
final currentPage = document.currentPage();
final totalPages = document.totalPages();

// Update thread filter config.
final filterTypeList = document
.querySelector('ul#thread_types')
?.querySelectorAll('li > a')
.where((e) => e.innerText.isNotEmpty)
.map(
(e) => FilterType(
name: e.innerText.trim(),
typeID: _typeIDRe
.firstMatch(e.attributes['href'] ?? '')
?.namedGroup('id'),
),
)
.toList();

final filterSpecialTypeList = document
.querySelector('div#filter_special_menu')
?.querySelectorAll('ul > li > a')
.where((e) => e.innerText.isNotEmpty)
.map(
(e) => FilterSpecialType(
name: e.innerText.trim(),
specialType: _specialTypeRe
.firstMatch(e.attributes['href'] ?? '')
?.namedGroup('type'),
),
)
.toList();

final filterOrderList = document
.querySelector('div#filter_orderby_menu')
?.querySelectorAll('ul > li > a')
.where((e) => e.innerText.isNotEmpty)
.map(
(e) => FilterOrder(
name: e.innerText.trim(),
orderBy: _orderByRe
.firstMatch(e.attributes['href'] ?? '')
?.namedGroup('orderby'),
),
)
.toList();

final filterDatelineList = document
.querySelector('div#filter_dateline_menu')
?.querySelectorAll('ul > li > a')
.where((e) => e.innerText.isNotEmpty)
.map(
(e) => FilterDateline(
name: e.innerText.trim(),
dateline: _datelineRe
.firstMatch(e.attributes['href'] ?? '')
?.namedGroup('dateline'),
),
)
.toList();

return state.copyWith(
status: ForumStatus.success,
title: title,
Expand All @@ -150,6 +246,10 @@ class ForumBloc extends Bloc<ForumEvent, ForumState> {
permissionDeniedMessage: permissionDeniedMessage,
currentPage: currentPage ?? pageNumber,
totalPages: totalPages ?? currentPage ?? pageNumber,
filterTypeList: filterTypeList,
filterSpecialTypeList: filterSpecialTypeList,
filterOrderList: filterOrderList,
filterDatelineList: filterDatelineList,
);
}

Expand Down
9 changes: 9 additions & 0 deletions lib/features/forum/bloc/forum_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,12 @@ final class ForumJumpPageRequested extends ForumEvent {
/// Page number to jump to.
final int pageNumber;
}

/// User requested to change the current thread filter state.
final class ForumChangeThreadFilterStateRequested extends ForumEvent {
/// Constructor.
const ForumChangeThreadFilterStateRequested(this.filterState);

/// [FilterState] to change to.
final FilterState filterState;
}
36 changes: 36 additions & 0 deletions lib/features/forum/bloc/forum_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ class ForumState extends Equatable {
this.havePermission = true,
this.permissionDeniedMessage,
this.needLogin = false,
this.filterState = const FilterState(),
this.filterTypeList = const [],
this.filterSpecialTypeList = const [],
this.filterOrderList = const [],
this.filterDatelineList = const [],
});

/// Page status.
Expand Down Expand Up @@ -83,6 +88,21 @@ class ForumState extends Equatable {
/// Only works when no user logged.
final bool needLogin;

/// State of thread filter.
final FilterState filterState;

/// All available [FilterType] list.
final List<FilterType> filterTypeList;

/// All available [FilterSpecialType] list.
final List<FilterSpecialType> filterSpecialTypeList;

/// All available [FilterOrder] list.
final List<FilterOrder> filterOrderList;

/// All available [FilterDateline] list.
final List<FilterDateline> filterDatelineList;

/// Copy with
ForumState copyWith({
ForumStatus? status,
Expand All @@ -98,6 +118,11 @@ class ForumState extends Equatable {
uh.Element? permissionDeniedMessage,
int? currentPage,
int? totalPages,
FilterState? filterState,
List<FilterType>? filterTypeList,
List<FilterSpecialType>? filterSpecialTypeList,
List<FilterOrder>? filterOrderList,
List<FilterDateline>? filterDatelineList,
}) {
return ForumState(
status: status ?? this.status,
Expand All @@ -114,6 +139,12 @@ class ForumState extends Equatable {
permissionDeniedMessage ?? this.permissionDeniedMessage,
currentPage: currentPage ?? this.currentPage,
totalPages: totalPages ?? this.totalPages,
filterState: filterState ?? this.filterState,
filterTypeList: filterTypeList ?? this.filterTypeList,
filterSpecialTypeList:
filterSpecialTypeList ?? this.filterSpecialTypeList,
filterOrderList: filterOrderList ?? this.filterOrderList,
filterDatelineList: filterDatelineList ?? this.filterDatelineList,
);
}

Expand All @@ -131,5 +162,10 @@ class ForumState extends Equatable {
permissionDeniedMessage.hashCode,
currentPage,
totalPages,
filterState,
filterTypeList,
filterSpecialTypeList,
filterOrderList,
filterDatelineList,
];
}
Loading

0 comments on commit da0daaf

Please sign in to comment.