Skip to content

Commit

Permalink
feat(ui): Support scored notice in notice page
Browse files Browse the repository at this point in the history
Support scored notice in notice page:
* Need to use sealed class to refactor notice types.
* Do not allow to redirect to scored post page currently.
  • Loading branch information
realth000 committed Dec 6, 2023
1 parent 050ca9b commit 17444b6
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 27 deletions.
3 changes: 2 additions & 1 deletion lib/i18n/strings.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@
"title": "Notifications",
"noticeTab": {
"title": "Notice",
"replyBody": "replied in $threadTitle"
"replyBody": "replied in $threadTitle",
"scoreBody": "scored your thread in $threadTitle $score"
},
"messageTab": {
"title": "Messages"
Expand Down
3 changes: 2 additions & 1 deletion lib/i18n/strings_zh-CN.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@
"title": "通知",
"noticeTab": {
"title": "提醒",
"replyBody": "在 $threadTitle 中回复了"
"replyBody": "在 $threadTitle 中回复了",
"scoreBody": "在 $threadTitle 中给您的帖子评分 $score"
},
"messageTab": {
"title": "消息"
Expand Down
3 changes: 2 additions & 1 deletion lib/i18n/strings_zh-TW.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@
"title": "通知",
"noticeTab": {
"title": "提醒",
"replyBody": "在 $threadTitle 中回覆了"
"replyBody": "在 $threadTitle 中回覆了",
"scoreBody": "在 $threadTitle 中給您的帖子評分 $score"
},
"messageTab": {
"title": "訊息"
Expand Down
73 changes: 62 additions & 11 deletions lib/models/notice.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ import 'package:tsdm_client/extensions/universal_html.dart';
import 'package:tsdm_client/utils/debug.dart';
import 'package:universal_html/html.dart' as uh;

// TODO: Refactor with sealed class.
enum NoticeType {
reply,
score,
}

class _NoticeInfo {
_NoticeInfo({
required this.userAvatarUrl,
Expand All @@ -14,6 +20,9 @@ class _NoticeInfo {
required this.noticeThreadTitle,
required this.redirectUrl,
required this.ignoreCount,
required this.noticeType,
required this.score,
required this.scoreComment,
});

/// User avatar.
Expand Down Expand Up @@ -55,6 +64,15 @@ class _NoticeInfo {
///
/// Will be null when there is no ignored notice.
final int? ignoreCount;

/// Type of current notice.
final NoticeType noticeType;

/// Score received.
final String? score;

/// Comment when scoring.
final String? scoreComment;
}

class Notice {
Expand Down Expand Up @@ -84,6 +102,12 @@ class Notice {

int? get ignoreCount => _info.ignoreCount;

NoticeType get noticeType => _info.noticeType;

String? get score => _info.score;

String? get scoreComment => _info.scoreComment;

/// [element] :
/// div#ct > div.mn > div.bm.bw0 > div.xld.xlda > div.nts > div.cl (notice=xxx)
static _NoticeInfo _buildPostFromClNode(uh.Element element) {
Expand All @@ -93,18 +117,43 @@ class Notice {
final noticeTime = noticeNode?.attributes['title']?.parseToDateTimeUtc8();
final noticeTimeString = noticeNode?.firstEndDeepText();

final a1Node = element.querySelector('dd.ntc_body > a:nth-child(1)');
final userSpaceUrl = a1Node?.firstHref();
final username = a1Node?.firstEndDeepText();
String? score;
String? scoreComment;

final quoteNode = element.querySelector('dd.ntc_body > div.quote');
late final NoticeType noticeType;
if (quoteNode == null) {
noticeType = NoticeType.reply;
} else {
final n = element.querySelector('dd.ntc_body');
noticeType = NoticeType.score;
score = n?.nodes[n.nodes.length - 2].text?.trim().replaceFirst('评分 ', '');
scoreComment = quoteNode.innerText;
}

final a2Node = element.querySelector('dd.ntc_body > a:nth-child(2)');
final noticeThreadUrl = a2Node?.firstHref();
final noticeThreadTitle = a2Node?.firstEndDeepText();
String? username;
String? userSpaceUrl;
String? noticeThreadUrl;
String? noticeThreadTitle;
String? redirectUrl;

final redirectUrl = element
.querySelector('dd.ntc_body > a:nth-child(3)')
?.firstHref()
?.prependHost();
final a1Node = element.querySelector('dd.ntc_body > a:nth-child(1)');
final a2Node = element.querySelector('dd.ntc_body > a:nth-child(2)');
if (noticeType == NoticeType.reply) {
username = a1Node?.firstEndDeepText();
userSpaceUrl = a1Node?.firstHref();
noticeThreadUrl = a2Node?.firstHref();
noticeThreadTitle = a2Node?.firstEndDeepText();
redirectUrl = element
.querySelector('dd.ntc_body > a:nth-child(3)')
?.firstHref()
?.prependHost();
} else {
noticeThreadTitle = a1Node?.firstEndDeepText();
redirectUrl = a1Node?.firstHref()?.prependHost();
userSpaceUrl = a2Node?.firstHref();
username = a2Node?.firstEndDeepText();
}

final ignoreCount = element
.querySelector('dd.xg1.xw0')
Expand All @@ -123,14 +172,16 @@ class Notice {
noticeThreadTitle: noticeThreadTitle,
redirectUrl: redirectUrl,
ignoreCount: ignoreCount,
noticeType: noticeType,
score: score,
scoreComment: scoreComment,
);
}

bool isValid() {
if (username == null ||
userSpaceUrl == null ||
noticeTime == null ||
noticeThreadUrl == null ||
noticeThreadTitle == null ||
redirectUrl == null) {
debug(
Expand Down
50 changes: 37 additions & 13 deletions lib/widgets/notice_card.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,45 @@ class NoticeCard extends ConsumerWidget {
userAvatar = CircleAvatar(child: Text(notice.username?[0] ?? ''));
}

final noticeBody = switch (notice.noticeType) {
NoticeType.reply => Text(
context.t.noticePage.noticeTab
.replyBody(threadTitle: notice.noticeThreadTitle ?? '-'),
),
NoticeType.score => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
context.t.noticePage.noticeTab.scoreBody(
threadTitle: notice.noticeThreadTitle ?? '-',
score: notice.score ?? '-',
),
),
Card(
elevation: 2,
child: Padding(
padding: edgeInsetsL15T15R15B15,
child: Text(notice.scoreComment ?? ''),
),
)
],
),
};

return Card(
margin: EdgeInsets.zero,
clipBehavior: Clip.antiAlias,
child: InkWell(
onTap: notice.redirectUrl != null
? () async {
await context.pushNamed(ScreenPaths.reply,
pathParameters: <String, String>{
'target': notice.redirectUrl!,
});
}
: null,
onTap:
// TODO: Allow redirect to related post.
notice.redirectUrl != null && notice.noticeType == NoticeType.reply
? () async {
await context.pushNamed(ScreenPaths.reply,
pathParameters: <String, String>{
'target': notice.redirectUrl!,
});
}
: null,
child: Column(
children: [
ListTile(
Expand All @@ -56,14 +83,11 @@ class NoticeCard extends ConsumerWidget {
child: Row(
children: [
Expanded(
child: Text(
context.t.noticePage.noticeTab.replyBody(
threadTitle: notice.noticeThreadTitle ?? '-'),
),
child: noticeBody,
),
],
),
)
),
],
),
),
Expand Down

0 comments on commit 17444b6

Please sign in to comment.