Skip to content

Commit

Permalink
refactor(*): Page number in app bar jump page button
Browse files Browse the repository at this point in the history
  • Loading branch information
realth000 committed Aug 15, 2024
1 parent cb5fc63 commit be42e89
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 54 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:bloc/bloc.dart';
import 'package:dart_mappable/dart_mappable.dart';
import 'package:tsdm_client/exceptions/exceptions.dart';
import 'package:tsdm_client/extensions/universal_html.dart';
import 'package:tsdm_client/features/notification/repository/notification_repository.dart';
import 'package:tsdm_client/shared/models/models.dart';
import 'package:tsdm_client/utils/logger.dart';
Expand Down Expand Up @@ -48,7 +49,7 @@ class NotificationDetailCubit extends Cubit<NotificationDetailState>
emit(state.copyWith(status: NotificationDetailStatus.failed));
return;
}
final postData = Post.fromPostNode(postNode);
final postData = Post.fromPostNode(postNode, document.currentPage() ?? 1);

// Parse thread id.
final tidMatch = _tidRe.firstMatch(url);
Expand Down
5 changes: 4 additions & 1 deletion lib/features/thread/bloc/thread_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,10 @@ class ThreadBloc extends Bloc<ThreadEvent, ThreadState> with LoggerMixin {

final threadClosed = document.querySelector('form#fastpostform') == null;
final threadDataNode = document.querySelector('div#postlist');
final postList = Post.buildListFromThreadDataNode(threadDataNode);
final postList = Post.buildListFromThreadDataNode(
threadDataNode,
document.currentPage() ?? 1,
);
String? title;
// Most threads have thread type node before the title.
title =
Expand Down
83 changes: 39 additions & 44 deletions lib/features/thread/widgets/post_list.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import 'dart:core';

import 'package:collection/collection.dart';
import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:super_sliver_list/super_sliver_list.dart';
import 'package:tsdm_client/constants/layout.dart';
import 'package:tsdm_client/features/jump_page/cubit/jump_page_cubit.dart';
import 'package:tsdm_client/features/thread/bloc/thread_bloc.dart';
import 'package:tsdm_client/generated/i18n/strings.g.dart';
import 'package:tsdm_client/shared/models/models.dart';
Expand Down Expand Up @@ -75,19 +76,43 @@ class _PostListState extends State<PostList> {
/// [ScrollController] comes from outside.
///
/// Do NOT dispose it here.
late final ScrollController _listScrollController;
late ScrollController _listScrollController;

late ListController _listController;

/// Current page number
int pageNumber = 1;

void _updatePageNumber() {
final p = _listController.visibleRange?.$1;
if (p != null) {
// List forms through separator builder, divide 2 because of the separator
// widget.
final p2 = widget.postList[p ~/ 2].page;
if (p2 != pageNumber) {
pageNumber = p2;
// Current page changes.
context.read<JumpPageCubit>().setPageInfo(currentPage: p2);
}
}
}

@override
void initState() {
super.initState();
// Try use the thread type in widget which comes from routing.
_threadType = widget.threadType;
_listScrollController = widget.scrollController;
_listController = ListController();
_listController.addListener(_updatePageNumber);
}

@override
void dispose() {
_refreshController.dispose();
_listController
..removeListener(_updatePageNumber)
..dispose();
super.dispose();
}

Expand Down Expand Up @@ -143,50 +168,20 @@ class _PostListState extends State<PostList> {
);
}

List<Widget> _buildPostList(BuildContext context) {
Widget _buildPostList(BuildContext context) {
if (widget.postList.isEmpty) {
return [];
}
final ret = <Widget>[];

// Current sliver group index.
//
// All posts in the same page will be gathered in a group.
// Each page has at most 10 posts.
final postGroupList = widget.postList.slices(10);
for (final postGroup in postGroupList) {
// final pageNumber =
// ((postGroup.firstOrNull?.postFloor ?? 0) / 10).floor() + 1;
ret.add(
SliverList.separated(
itemCount: postGroup.length,
itemBuilder: (context, index) {
return widget.widgetBuilder(context, postGroup[index]);
},
separatorBuilder: (context, index) =>
widget.useDivider ? const Divider(thickness: 0.5) : sizedBoxW4H4,
),
// SliverMainAxisGroup(
// slivers: [
// SliverPersistentHeader(
// pinned: true,
// delegate: PostGroupHeaderDelegate(groupIndex: '$pageNumber'),
// ),
// SliverList.separated(
// itemCount: postGroup.length,
// itemBuilder: (context, index) {
// return widget.widgetBuilder(context, postGroup[index]);
// },
// separatorBuilder: (context, index) => widget.useDivider
// ? const Divider(thickness: 0.5)
// : sizedBoxW4H4,
// ),
// ],
// ),
);
return sizedBoxEmpty;
}

return ret;
return SuperSliverList.separated(
listController: _listController,
itemCount: widget.postList.length,
itemBuilder: (context, index) {
return widget.widgetBuilder(context, widget.postList[index]);
},
separatorBuilder: (context, index) =>
widget.useDivider ? const Divider(thickness: 0.5) : sizedBoxW4H4,
);
}

Widget _buildBody(BuildContext context, ThreadState state) {
Expand Down Expand Up @@ -255,7 +250,7 @@ class _PostListState extends State<PostList> {
),
),
),
..._buildPostList(context),
_buildPostList(context),
],
);
},
Expand Down
11 changes: 8 additions & 3 deletions lib/shared/models/post.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class Post with PostMappable {
required this.lastEditUsername,
required this.lastEditTime,
required this.shareLink,
required this.page,
this.locked = const [],
this.rate,
this.packetUrl,
Expand Down Expand Up @@ -98,8 +99,11 @@ class Post with PostMappable {
/// May be null, maybe...
final UserBriefProfile? userBriefProfile;

/// Current page number.
final int page;

/// Build [Post] from [element] that has attribute id "post_$postID".
static Post? fromPostNode(uh.Element element) {
static Post? fromPostNode(uh.Element element, int page) {
final trRootNode = element.querySelector('table > tbody > tr');
final postID = element.id.replaceFirst('post_', '');
if (postID.isEmpty) {
Expand Down Expand Up @@ -293,13 +297,14 @@ class Post with PostMappable {
lastEditTime: lastEditTime,
userBriefProfile: userBriefProfile,
shareLink: shareLink,
page: page,
);
}

/// Build a list of [Post] from the given [ThreadData] [uh.Element].
///
/// [element]'s id is "postlist".
static List<Post> buildListFromThreadDataNode(uh.Element? element) {
static List<Post> buildListFromThreadDataNode(uh.Element? element, int page) {
if (element == null) {
return [];
}
Expand All @@ -314,7 +319,7 @@ class Post with PostMappable {
// This while is a while (0), will not loop twice.
if ((currentElement.attributes['id'] ?? '').startsWith('post_')) {
// Build post here.
final post = Post.fromPostNode(currentElement);
final post = Post.fromPostNode(currentElement, page);
if (post == null) {
talker.error('warning: post is empty');
continue;
Expand Down
2 changes: 1 addition & 1 deletion lib/widgets/list_app_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ class ListAppBar extends StatelessWidget implements PreferredSizeWidget {
var totalPages = 0;
var canJumpPage = false;
if (onJumpPage != null) {
final jumpPageState = context.read<JumpPageCubit>().state;
final jumpPageState = context.watch<JumpPageCubit>().state;
currentPage = jumpPageState.currentPage;
totalPages = jumpPageState.totalPages;
canJumpPage = jumpPageState.canJumpPage;
Expand Down
8 changes: 8 additions & 0 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1445,6 +1445,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.8.18"
super_sliver_list:
dependency: "direct main"
description:
name: super_sliver_list
sha256: b1e1e64d08ce40e459b9bb5d9f8e361617c26b8c9f3bb967760b0f436b6e3f56
url: "https://pub.dev"
source: hosted
version: "0.4.1"
surf_lint_rules:
dependency: "direct main"
description:
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ dependencies:
# For drift
sqlite3_flutter_libs: ^0.5.24
stream_transform: ^2.1.0
super_sliver_list: ^0.4.1
surf_lint_rules: ^3.1.0
talker: ^4.4.0
talker_flutter: ^4.4.0
Expand Down
20 changes: 16 additions & 4 deletions test/regression/test_001_parsing_post_card_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,10 @@ void main() {
group('ParsePostBody', () {
test('with pcb and table', () {
final document = parseHtmlDocument(_postBodyWithPcbTable);
final postData = Post.fromPostNode(document.body!.querySelector('div')!);
final postData = Post.fromPostNode(
document.body!.querySelector('div')!,
1,
);
expect(postData?.data.contains('test_body_with_pcb_table'), true);
expect(postData?.author.uid, '123789');
expect(postData?.author.name, 'test_user');
Expand All @@ -178,7 +181,10 @@ void main() {
});
test('with pcb', () {
final document = parseHtmlDocument(_postBodyWithPcb);
final postData = Post.fromPostNode(document.body!.querySelector('div')!);
final postData = Post.fromPostNode(
document.body!.querySelector('div')!,
1,
);
expect(postData?.data.contains('test_body_with_pcb'), true);
expect(postData?.author.uid, '123789');
expect(postData?.author.name, 'test_user');
Expand All @@ -189,7 +195,10 @@ void main() {
});
test('with pcbs', () {
final document = parseHtmlDocument(_postBodyWithPcbs);
final postData = Post.fromPostNode(document.body!.querySelector('div')!);
final postData = Post.fromPostNode(
document.body!.querySelector('div')!,
1,
);
expect(postData?.data.contains('test_body_with_pcbs'), true);
expect(postData?.author.uid, '123789');
expect(postData?.author.name, 'test_user');
Expand All @@ -200,7 +209,10 @@ void main() {
});
test('with locked with purchase', () {
final document = parseHtmlDocument(_postBodyWithLockedWithPurchase);
final postData = Post.fromPostNode(document.body!.querySelector('div')!);
final postData = Post.fromPostNode(
document.body!.querySelector('div')!,
1,
);
expect(
postData?.data.contains('test_body_with_locked_with_purchase'),
true,
Expand Down

0 comments on commit be42e89

Please sign in to comment.