Skip to content

Commit

Permalink
feat(thread): Support viewing posts in reversed order
Browse files Browse the repository at this point in the history
Support viewing posts in reversed order:
* Note that the "reversed order" seems conflict with "only
  view specified user" on the server side on UI, but here
  do implement it by reserving both query parameters in thread
  url so there is no conflict any more. Different from the
  behavior in browser but it's ok, even better.
  • Loading branch information
realth000 committed Feb 2, 2024
1 parent 165ad31 commit 815b441
Show file tree
Hide file tree
Showing 11 changed files with 125 additions and 6 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
- 新增提示帖子内的红包已经领完了。
- 新增支持看帖时只看某个作者的楼层。
* 楼层右下角菜单 -> 只看该作者/看所有作者。
- 新增支持看帖时倒序浏览。
* 帖子右上角菜单 -> 倒序浏览/正序浏览。
- 缓存首页轮播图。

### Fixed
Expand Down
2 changes: 2 additions & 0 deletions lib/features/forum/view/forum_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,8 @@ class _ForumPageState extends State<ForumPage>
curve: Curves.ease,
duration: const Duration(milliseconds: 500),
);
case MenuActions.reverseOrder:
;
}
},
),
Expand Down
49 changes: 48 additions & 1 deletion lib/features/thread/bloc/thread_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class ThreadBloc extends Bloc<ThreadEvent, ThreadState> {
on<ThreadClosedStateUpdated>(_onThreadUpdateClosedState);
on<ThreadOnlyViewAuthorRequested>(_onThreadOnlyViewAuthorRequested);
on<ThreadViewAllAuthorsRequested>(_onThreadViewAllAuthorsRequested);
on<ThreadChangeViewOrderRequested>(_onThreadChangeViewOrderRequested);
}

final ThreadRepository _threadRepository;
Expand All @@ -41,6 +42,7 @@ class ThreadBloc extends Bloc<ThreadEvent, ThreadState> {
tid: state.tid,
pageNumber: event.pageNumber,
onlyVisibleUid: state.onlyVisibleUid,
reverseOrder: state.reverseOrder,
);
emit(await _parseFromDocument(document, event.pageNumber));
} on HttpRequestFailedException catch (e) {
Expand All @@ -58,6 +60,7 @@ class ThreadBloc extends Bloc<ThreadEvent, ThreadState> {
final document = await _threadRepository.fetchThread(
tid: state.tid,
onlyVisibleUid: state.onlyVisibleUid,
reverseOrder: state.reverseOrder,
);
emit(await _parseFromDocument(document, 1));
} on HttpRequestFailedException catch (e) {
Expand All @@ -77,6 +80,7 @@ class ThreadBloc extends Bloc<ThreadEvent, ThreadState> {
tid: state.tid,
pageNumber: event.pageNumber,
onlyVisibleUid: state.onlyVisibleUid,
reverseOrder: state.reverseOrder,
);
emit(await _parseFromDocument(document, event.pageNumber));
} on HttpRequestFailedException catch (e) {
Expand All @@ -103,13 +107,19 @@ class ThreadBloc extends Bloc<ThreadEvent, ThreadState> {
tid: state.tid,
pageNumber: state.currentPage,
onlyVisibleUid: event.uid,
reverseOrder: state.reverseOrder,
);
// Use "1" as current page number to prevent page number overflow.
final s = await _parseFromDocument(document, 1);
emit(s.copyWith(onlyVisibleUid: event.uid));
} on HttpRequestFailedException catch (e) {
debug('failed to load thread page: fid=${state.tid}, pageNumber=1 : $e');
emit(state.copyWith(status: ThreadStatus.failed));
emit(
state.copyWith(
status: ThreadStatus.failed,
onlyVisibleUid: event.uid,
),
);
}
}

Expand All @@ -126,6 +136,7 @@ class ThreadBloc extends Bloc<ThreadEvent, ThreadState> {
final document = await _threadRepository.fetchThread(
tid: state.tid,
pageNumber: state.currentPage,
reverseOrder: state.reverseOrder,
);
// Use "1" as current page number to prevent page number overflow.
final s = await _parseFromDocument(
Expand All @@ -140,6 +151,41 @@ class ThreadBloc extends Bloc<ThreadEvent, ThreadState> {
}
}

Future<void> _onThreadChangeViewOrderRequested(
ThreadChangeViewOrderRequested event,
ThreadEmitter emit,
) async {
emit(
state.copyWith(
status: ThreadStatus.loading,
postList: [],
reverseOrder: !state.reverseOrder,
),
);
try {
final document = await _threadRepository.fetchThread(
tid: state.tid,
pageNumber: state.currentPage,
onlyVisibleUid: state.onlyVisibleUid,
reverseOrder: state.reverseOrder,
);
final s = await _parseFromDocument(
document,
state.currentPage,
clearOnlyVisibleUid: true,
);
emit(s.copyWith(reverseOrder: state.reverseOrder));
} on HttpRequestFailedException catch (e) {
debug('failed to load thread page: fid=${state.tid}, pageNumber=1 : $e');
emit(
state.copyWith(
status: ThreadStatus.failed,
reverseOrder: state.reverseOrder,
),
);
}
}

Future<ThreadState> _parseFromDocument(
uh.Document document,
int pageNumber, {
Expand Down Expand Up @@ -242,6 +288,7 @@ class ThreadBloc extends Bloc<ThreadEvent, ThreadState> {
threadType: threadType,
onlyVisibleUid:
(clearOnlyVisibleUid ?? false) ? null : state.onlyVisibleUid,
reverseOrder: state.reverseOrder,
);
}
}
13 changes: 13 additions & 0 deletions lib/features/thread/bloc/thread_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,16 @@ final class ThreadOnlyViewAuthorRequested extends ThreadEvent {
/// Note that triggering this event **will not change the current page number**.
/// We behave like what it acts in browser.
final class ThreadViewAllAuthorsRequested extends ThreadEvent {}

/// User requested to change the order when viewing posts in current thread.
///
/// The default behavior is forward order.
///
/// Note that the "reversed order" seems conflict with "only view specified
/// user" on the server side on UI, but here do implement it by reserving both
/// query parameters in thread url so there is no conflict any more. Different
/// from the behavior in browser but it's ok, even better.
final class ThreadChangeViewOrderRequested extends ThreadEvent {
/// Constructor.
const ThreadChangeViewOrderRequested();
}
7 changes: 7 additions & 0 deletions lib/features/thread/bloc/thread_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class ThreadState extends Equatable {
this.replyParameters,
this.threadType,
this.onlyVisibleUid,
this.reverseOrder = false,
});

/// Status.
Expand Down Expand Up @@ -87,6 +88,9 @@ class ThreadState extends Equatable {
/// Show all posts if value is null;
final String? onlyVisibleUid;

/// View posts in current thread in forward order or reverse order.
final bool reverseOrder;

/// Copy with.
ThreadState copyWith({
ThreadStatus? status,
Expand All @@ -102,6 +106,7 @@ class ThreadState extends Equatable {
List<Post>? postList,
ReplyParameters? replyParameters,
String? onlyVisibleUid,
bool? reverseOrder,
}) {
return ThreadState(
status: status ?? this.status,
Expand All @@ -118,6 +123,7 @@ class ThreadState extends Equatable {
postList: postList ?? this.postList,
replyParameters: replyParameters ?? this.replyParameters,
onlyVisibleUid: onlyVisibleUid ?? this.onlyVisibleUid,
reverseOrder: reverseOrder ?? this.reverseOrder,
);
}

Expand All @@ -136,5 +142,6 @@ class ThreadState extends Equatable {
postList,
replyParameters,
onlyVisibleUid,
reverseOrder,
];
}
7 changes: 5 additions & 2 deletions lib/features/thread/repository/thread_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,16 @@ class ThreadRepository {
required String tid,
int pageNumber = 1,
String? onlyVisibleUid,
bool reverseOrder = false,
}) async {
/// Only visible uid.
final visibleUid =
onlyVisibleUid == null ? '' : '&authorid=$onlyVisibleUid';
final orderType = reverseOrder ? '&ordertype=1' : '';
_pageNumber = pageNumber;
_threadUrl =
'$baseUrl/forum.php?mod=viewthread&tid=$tid&extra=page%3D1$visibleUid&page=$pageNumber';
_threadUrl = '$baseUrl/forum.php?mod=viewthread&tid=$tid&extra=page%3D1'
'$orderType$visibleUid'
'&page=$pageNumber';

final resp = await getIt.get<NetClientProvider>().get(_threadUrl!);
if (resp.statusCode != HttpStatus.ok) {
Expand Down
5 changes: 5 additions & 0 deletions lib/features/thread/view/thread_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ class _ThreadPageState extends State<ThreadPage>
return Scaffold(
appBar: ListAppBar(
title: title,
showReverseOrderAction: true,
onSearch: () async {
await context.pushNamed(ScreenPaths.search);
},
Expand Down Expand Up @@ -264,6 +265,10 @@ class _ThreadPageState extends State<ThreadPage>
curve: Curves.ease,
duration: const Duration(milliseconds: 500),
);
case MenuActions.reverseOrder:
context
.readOrNull<ThreadBloc>()
?.add(const ThreadChangeViewOrderRequested());
}
},
),
Expand Down
4 changes: 3 additions & 1 deletion lib/i18n/strings.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,9 @@
"actionRefresh": "Refresh",
"actionCopyUrl": "Copy Url",
"actionOpenInBrowser": "Open In Browser",
"actionBackToTop": "Back To Top"
"actionBackToTop": "Back To Top",
"actionForwardOrder": "Forward order",
"actionReverseOrder": "Reverse order"
},
"checkinForm": {
"shouldMoreThan3": "Should more than 3",
Expand Down
4 changes: 3 additions & 1 deletion lib/i18n/strings_zh-CN.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,9 @@
"actionRefresh": "刷新",
"actionCopyUrl": "复制链接",
"actionOpenInBrowser": "在浏览器中打开",
"actionBackToTop": "回到顶部"
"actionBackToTop": "回到顶部",
"actionForwardOrder": "正序浏览",
"actionReverseOrder": "倒序浏览"
},
"checkinForm": {
"shouldMoreThan3": "必须多于三个字",
Expand Down
4 changes: 3 additions & 1 deletion lib/i18n/strings_zh-TW.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,9 @@
"actionRefresh": "刷新",
"actionCopyUrl": "複製連結",
"actionOpenInBrowser": "在瀏覽器中開啟",
"actionBackToTop": "回到頂部"
"actionBackToTop": "回到頂部",
"actionForwardOrder": "正序瀏覽",
"actionReverseOrder": "倒序瀏覽"
},
"checkinForm": {
"shouldMoreThan3": "必須多於三個字",
Expand Down
34 changes: 34 additions & 0 deletions lib/widgets/list_app_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:tsdm_client/extensions/build_context.dart';
import 'package:tsdm_client/features/jump_page/cubit/jump_page_cubit.dart';
import 'package:tsdm_client/features/jump_page/widgets/jump_page_dialog.dart';
import 'package:tsdm_client/features/thread/bloc/thread_bloc.dart';
import 'package:tsdm_client/generated/i18n/strings.g.dart';

/// App bar actions.
Expand All @@ -19,6 +21,11 @@ enum MenuActions {

/// Go back to top of the page.
backToTop,

/// Change the order when viewing current page.
///
/// Only available to thread pages.
reverseOrder,
}

/// A app bar contains list and provides features including:
Expand All @@ -34,6 +41,7 @@ class ListAppBar extends StatelessWidget implements PreferredSizeWidget {
this.bottom,
this.onSelected,
this.onJumpPage,
this.showReverseOrderAction = false,
super.key,
});

Expand All @@ -54,6 +62,10 @@ class ListAppBar extends StatelessWidget implements PreferredSizeWidget {
/// Extra bottom widget.
final PreferredSizeWidget? bottom;

/// Show the action to change "view order" between forward order and reverse
/// order.
final bool showReverseOrderAction;

Future<void> _jumpPage(
BuildContext context,
int currentPage,
Expand Down Expand Up @@ -87,6 +99,10 @@ class ListAppBar extends StatelessWidget implements PreferredSizeWidget {
totalPages = jumpPageState.totalPages;
canJumpPage = jumpPageState.canJumpPage;
}

final threadBloc = context.readOrNull<ThreadBloc>();
final reverseOrder = threadBloc?.state.reverseOrder;

return AppBar(
title: title == null ? null : Text(title!),
bottom: bottom,
Expand Down Expand Up @@ -140,6 +156,24 @@ class ListAppBar extends StatelessWidget implements PreferredSizeWidget {
],
),
),
if (showReverseOrderAction && reverseOrder != null)
PopupMenuItem(
value: MenuActions.reverseOrder,
child: Row(
children: [
Icon(
reverseOrder
? Icons.align_vertical_bottom_outlined
: Icons.align_vertical_top_outlined,
),
Text(
reverseOrder
? context.t.networkList.actionForwardOrder
: context.t.networkList.actionReverseOrder,
),
],
),
),
],
onSelected: onSelected,
),
Expand Down

0 comments on commit 815b441

Please sign in to comment.