Skip to content

Commit 19fd0b2

Browse files
committed
fix: unified date time method on html nodes
In many situations the date time text format is not the same, especially when in recent 7 days. Use `dateTime` extension method as a unified way to get the correct date time. Refactored callsite: * Thread last reply time in latest thread page. * Message publish time in broadcast message page. * Personal message sent time in personal message page. * Rate act time in rate log item. * Thread operate time in operation log item. * Sales history item time in locked card. * Post publish tiem in post item. * Thread latest reply time in forum page.
1 parent 7169737 commit 19fd0b2

File tree

11 files changed

+56
-44
lines changed

11 files changed

+56
-44
lines changed

lib/extensions/universal_html.dart

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -265,18 +265,52 @@ extension GrepExtension on Element {
265265

266266
/// Parse the datetime on current node or first child.
267267
///
268+
/// In many situations the date time text format is not the same, especially when in recent 7 days.
269+
///
270+
/// Use this function as a unified way to get the correct date time.
271+
///
272+
/// Example:
273+
///
268274
/// From current node `<span>`'s title attribute or first child (usually
269275
/// a span, too) 's title attribute.
270276
DateTime? dateTime() {
271-
if (tagName != 'SPAN') {
272-
return null;
277+
if (tagName == 'A') {
278+
// Use case: last reply time in threads in my thread page.
279+
if (children.firstOrNull?.tagName == 'SPAN') {
280+
// Recent 7 days.
281+
return children.first.attributes['title']?.parseToDateTimeUtc8();
282+
}
283+
284+
// More than 7 days.
285+
return nodes.firstOrNull?.text?.trim().parseToDateTimeUtc8();
273286
}
274-
if (classes.contains('xg1')) {
275-
final text1 = innerText.parseToDateTimeUtc8();
276-
if (text1 != null) {
277-
return text1;
287+
288+
if (tagName == 'SPAN') {
289+
if (attributes.containsKey('title')) {
290+
// Use case: rate log item in recent 7 days.
291+
return attributes['title']!.parseToDateTimeUtc8();
292+
}
293+
294+
// Use case: broadcast message publish time in broadcast page.
295+
if (children.firstOrNull?.tagName == 'SPAN') {
296+
// Recent 7 days.
297+
return children.firstOrNull?.attributes['title']?.parseToDateTimeUtc8();
278298
}
299+
300+
// More than 7 days.
301+
return innerText.trim().parseToDateTimeUtc8();
302+
}
303+
304+
// Try parse it.
305+
306+
if (children.firstOrNull?.tagName == 'SPAN' && children.first.attributes.containsKey('title')) {
307+
return children.first.attributes['title']!.parseToDateTimeUtc8();
308+
}
309+
310+
if (nodes.firstOrNull?.nodeType == Node.TEXT_NODE) {
311+
return nodes.first.text?.trim().parseToDateTimeUtc8();
279312
}
280-
return children.firstOrNull?.attributes['title']?.parseToDateTimeUtc8();
313+
314+
return null;
281315
}
282316
}

lib/features/my_thread/models/my_thread.dart

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,7 @@ class MyThread with MyThreadMappable {
9494
final latestReplyNode = element.querySelector('td.by');
9595
final latestReplyAuthorName = latestReplyNode?.querySelector('cite > a')?.firstEndDeepText();
9696
final latestReplyAuthorUrl = latestReplyNode?.querySelector('cite > a')?.firstHref();
97-
final latestReplyTime =
98-
// Within 7 days.
99-
latestReplyNode?.querySelector('em > a > span')?.attributes['title']?.parseToDateTimeUtc8() ??
100-
// More than 7 days ago.
101-
latestReplyNode?.querySelector('em > a')?.firstEndDeepText()?.parseToDateTimeUtc8();
97+
final latestReplyTime = latestReplyNode?.querySelector('em > a')?.dateTime();
10298
String? quotedMessage;
10399
if (element.classes.contains('bw0_all')) {
104100
quotedMessage = element.nextElementSibling?.querySelector('td.xg1')?.innerText.trim();

lib/features/notification/bloc/broadcast_message_detail_cubit.dart

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import 'package:bloc/bloc.dart';
22
import 'package:dart_mappable/dart_mappable.dart';
33
import 'package:tsdm_client/constants/url.dart';
4-
import 'package:tsdm_client/extensions/string.dart';
4+
import 'package:tsdm_client/extensions/universal_html.dart';
55
import 'package:tsdm_client/features/notification/repository/notification_repository.dart';
66
import 'package:tsdm_client/utils/logger.dart';
77
import 'package:universal_html/html.dart' as uh;
88

99
part 'broadcast_message_detail_cubit.mapper.dart';
10-
1110
part 'broadcast_message_detail_state.dart';
1211

1312
/// Cubit of broadcast message detail page.
@@ -31,10 +30,7 @@ final class BroadcastMessageDetailCubit extends Cubit<BroadcastMessageDetailStat
3130
(v) {
3231
final (document, _) = v;
3332
final infoNode = document.querySelector('div#pm_ul');
34-
final datetime =
35-
infoNode?.querySelector('dl > dd.ptm > span.xg1')?.innerText.parseToDateTimeUtc8() ??
36-
// Recent messages.
37-
infoNode?.querySelector('dl > dd.ptm > span.xg1 > span')?.title?.parseToDateTimeUtc8();
33+
final datetime = infoNode?.querySelector('dl > dd.ptm > span.xg1')?.dateTime();
3834
final messageNode = infoNode?.querySelector('dl > dd > p.pm_smry');
3935
if (datetime == null || messageNode == null) {
4036
error(

lib/features/notification/models/message.dart

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,7 @@ final class PersonalMessage with PersonalMessageMappable {
7272
}
7373

7474
final username = element.querySelector('dd:nth-child(3) > a')?.innerText;
75-
final lastMessageTime =
76-
// Old messages.
77-
element.querySelector('dd:nth-child(3) > span.xg1')?.innerText.parseToDateTimeUtc8() ??
78-
// Recent messages.
79-
element.querySelector('dd:nth-child(3) > span.xg1 > span')?.title?.parseToDateTimeUtc8();
75+
final lastMessageTime = element.querySelector('dd:nth-child(3) > span.xg1')?.dateTime();
8076
final chatUrl = element.querySelector('a#pmlist_${messageId}_a')?.attributes['href']?.unescapeHtml()?.prependHost();
8177
final message = element
8278
.querySelector('dd:nth-child(3) > span.xg1')
@@ -139,10 +135,7 @@ final class BroadcastMessage with BroadcastMessageMappable {
139135
return null;
140136
}
141137
final message = infoNode.querySelector('span')?.innerText.trim();
142-
final messageTime =
143-
infoNode.querySelector('span.xg1')?.innerText.trim().parseToDateTimeUtc8() ??
144-
// Less than 7 days
145-
infoNode.querySelector('span.xg1 > span')?.title?.parseToDateTimeUtc8();
138+
final messageTime = infoNode.querySelector('span.xg1')?.dateTime();
146139
final redirectUrl = infoNode.querySelector('a')?.attributes['href']?.unescapeHtml()?.prependHost();
147140
if (message == null || messageTime == null) {
148141
talker.error(

lib/features/rate/repository/rate_repository.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'package:tsdm_client/constants/url.dart';
55
import 'package:tsdm_client/exceptions/exceptions.dart';
66
import 'package:tsdm_client/extensions/fp.dart';
77
import 'package:tsdm_client/extensions/string.dart';
8+
import 'package:tsdm_client/extensions/universal_html.dart';
89
import 'package:tsdm_client/features/rate/models/models.dart';
910
import 'package:tsdm_client/instance.dart';
1011
import 'package:tsdm_client/shared/providers/net_client_provider/net_client_provider.dart';
@@ -55,7 +56,7 @@ final class RateRepository with LoggerMixin {
5556
final userNode = tds[1].querySelector('a');
5657
final username = userNode?.innerText.trim();
5758
final uid = userNode?.attributes['href']?.tryParseAsUri()?.queryParameters['uid'];
58-
final time = tds[2].querySelector('span')?.attributes['title']?.parseToDateTimeUtc8();
59+
final time = tds[2].dateTime();
5960
final reason = tds[3].innerText.trim();
6061

6162
if (attrValue == null || username == null || uid == null || time == null) {
Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
import 'package:dart_mappable/dart_mappable.dart';
2-
import 'package:tsdm_client/extensions/string.dart';
32
import 'package:tsdm_client/extensions/universal_html.dart';
43
import 'package:universal_html/html.dart' as uh;
54

65
part 'models.mapper.dart';
7-
8-
part 'thread_breadcrumb.dart';
9-
106
part 'operation_log_item.dart';
7+
part 'thread_breadcrumb.dart';

lib/features/thread/v1/models/operation_log_item.dart

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,7 @@ final class OperationLogItem with OperationLogItemMappable {
1414
}
1515

1616
final username = element.querySelector('a')?.innerText;
17-
final time =
18-
tds[1].querySelector('span')?.attributes['title']?.parseToDateTimeUtc8() ??
19-
tds[1].firstEndDeepText()?.parseToDateTimeUtc8();
17+
final time = tds[1].dateTime();
2018
final action = tds[2].innerText;
2119
String? duration = tds[3].innerText;
2220
if (duration.isEmpty) {

lib/shared/models/forum.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ final class Forum with ForumMappable {
144144
element.querySelector('td:nth-child(4) > div') ??
145145
// 旅行者 theme
146146
element.querySelector('td:nth-child(3) > div');
147+
// We assume that the time text style of latest thread is always in span with title attribute, say it in recent
148+
// 7 days.
147149
final latestThreadTime = latestThreadNode?.querySelector('cite > span')?.attributes['title']?.parseToDateTimeUtc8();
148150
final latestThreadTimeText = latestThreadNode?.querySelector('cite > span')?.firstEndDeepText();
149151
final latestThreadUrl = latestThreadNode?.querySelector('a')?.firstHref();

lib/shared/models/normal_thread.dart

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -292,11 +292,7 @@ class NormalThread with NormalThreadMappable {
292292
final threadLastReplyAuthorUrl = threadLastReplyNode?.querySelector('cite > a')?.attributes['href'];
293293
// We only have username here.
294294
final threadLastReplyAuthorName = threadLastReplyNode?.querySelector('cite > a')?.firstEndDeepText();
295-
final threadLastReplyTime =
296-
// Within 7 days.
297-
threadLastReplyNode?.querySelector('em > a > span')?.attributes['title']?.parseToDateTimeUtc8() ??
298-
// 7 days ago.
299-
threadLastReplyNode?.querySelector('em > a')?.firstEndDeepText()?.parseToDateTimeUtc8();
295+
final threadLastReplyTime = threadLastReplyNode?.querySelector('em > a')?.dateTime();
300296

301297
if (threadLastReplyAuthorName == null || threadLastReplyAuthorUrl == null || threadLastReplyTime == null) {
302298
talker.error(

lib/shared/models/post.dart

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,7 @@ class Post with PostMappable {
180180
// Recent post can grep [publishTime] in the the "title" attribute
181181
// in first child.
182182
// Otherwise fallback split time string.
183-
final postPublishTime =
184-
postPublishTimeNode?.querySelector('span')?.attributes['title']?.parseToDateTimeUtc8() ??
185-
postPublishTimeNode?.text?.substring(4).parseToDateTimeUtc8();
183+
final postPublishTime = postPublishTimeNode?.dateTime();
186184
// Sometimes the #postmessage_ID ID does not match postID.
187185
// e.g. tid=1184238
188186
// Use div.pcb to match it.

0 commit comments

Comments
 (0)