cc98-cli 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +47 -0
- package/README.md +4 -1
- package/dist/api/client.d.ts +1 -0
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +3 -0
- package/dist/api/client.js.map +1 -1
- package/dist/api/endpoints.d.ts +1 -0
- package/dist/api/endpoints.d.ts.map +1 -1
- package/dist/api/endpoints.js +1 -0
- package/dist/api/endpoints.js.map +1 -1
- package/dist/cli/commands/topic.d.ts.map +1 -1
- package/dist/cli/commands/topic.js +7 -0
- package/dist/cli/commands/topic.js.map +1 -1
- package/dist/cli/router.js +3 -0
- package/dist/cli/router.js.map +1 -1
- package/dist/tui/cached-client.d.ts +1 -0
- package/dist/tui/cached-client.d.ts.map +1 -1
- package/dist/tui/cached-client.js +3 -0
- package/dist/tui/cached-client.js.map +1 -1
- package/dist/tui/components/content.d.ts.map +1 -1
- package/dist/tui/components/content.js +5 -6
- package/dist/tui/components/content.js.map +1 -1
- package/dist/tui/components/sidebar.d.ts.map +1 -1
- package/dist/tui/components/sidebar.js +1 -11
- package/dist/tui/components/sidebar.js.map +1 -1
- package/dist/tui/components/status.d.ts.map +1 -1
- package/dist/tui/components/status.js +3 -0
- package/dist/tui/components/status.js.map +1 -1
- package/dist/tui/controller.d.ts +19 -0
- package/dist/tui/controller.d.ts.map +1 -1
- package/dist/tui/controller.js +415 -76
- package/dist/tui/controller.js.map +1 -1
- package/dist/tui/navigation.d.ts.map +1 -1
- package/dist/tui/navigation.js +2 -3
- package/dist/tui/navigation.js.map +1 -1
- package/dist/tui/renderer.js +11 -27
- package/dist/tui/renderer.js.map +1 -1
- package/dist/tui/state/store.d.ts.map +1 -1
- package/dist/tui/state/store.js +2 -1
- package/dist/tui/state/store.js.map +1 -1
- package/dist/tui/state/types.d.ts +27 -2
- package/dist/tui/state/types.d.ts.map +1 -1
- package/dist/tui/topic-reader.d.ts +2 -1
- package/dist/tui/topic-reader.d.ts.map +1 -1
- package/dist/tui/topic-reader.js +17 -5
- package/dist/tui/topic-reader.js.map +1 -1
- package/package.json +1 -1
package/dist/tui/controller.js
CHANGED
|
@@ -8,7 +8,7 @@ import { EMOJI_CATEGORIES, getEmojiArt, renderCc98Logo, renderEmojiCode } from "
|
|
|
8
8
|
import { navItems, settingsItems } from "./navigation.js";
|
|
9
9
|
import { getStatus } from "./state/store.js";
|
|
10
10
|
import { asArray, asNumber, asObject, chatItem, chatMessageItems, flattenBoards, genericItem, historyItem, isAbortError, jsonPreviewLines, loadChatUserNames, mapLimit, noticeItem, overviewStats, topicItem, unreadStats, userItem } from "./helpers.js";
|
|
11
|
-
import { appendTopicPosts, buildTopicReader, currentTopicPost, findTopicPostByFloor, getTopicPageInfo, jumpToPage, FLOORS_PER_PAGE } from "./topic-reader.js";
|
|
11
|
+
import { appendTopicPosts, buildTopicReader, currentTopicLine, currentTopicPost, findTopicPostByFloor, getTopicPageInfo, jumpToPage, replaceTopicPosts, FLOORS_PER_PAGE } from "./topic-reader.js";
|
|
12
12
|
export class TuiController {
|
|
13
13
|
state;
|
|
14
14
|
client;
|
|
@@ -23,6 +23,7 @@ export class TuiController {
|
|
|
23
23
|
settingsStore = new SettingsStore();
|
|
24
24
|
updateChecked = false;
|
|
25
25
|
autoSigninChecked = false;
|
|
26
|
+
listReturnState;
|
|
26
27
|
constructor(state, client, tokenStore, render, close, nextSignal, abortCurrent, webVpnOptions) {
|
|
27
28
|
this.state = state;
|
|
28
29
|
this.client = client;
|
|
@@ -54,6 +55,7 @@ export class TuiController {
|
|
|
54
55
|
this.state.parentList = undefined;
|
|
55
56
|
this.state.currentBoard = undefined;
|
|
56
57
|
this.state.currentChat = undefined;
|
|
58
|
+
this.state.listPaging = undefined;
|
|
57
59
|
this.render();
|
|
58
60
|
try {
|
|
59
61
|
// 加载快捷键配置
|
|
@@ -74,6 +76,7 @@ export class TuiController {
|
|
|
74
76
|
this.state.viewTitle = next.title;
|
|
75
77
|
this.state.items = next.items;
|
|
76
78
|
this.state.stats = next.stats;
|
|
79
|
+
this.state.listPaging = next.paging;
|
|
77
80
|
if (next.overview) {
|
|
78
81
|
this.state.overview = next.overview;
|
|
79
82
|
}
|
|
@@ -90,6 +93,9 @@ export class TuiController {
|
|
|
90
93
|
if (version === this.loadVersion) {
|
|
91
94
|
this.state.loading = false;
|
|
92
95
|
this.render();
|
|
96
|
+
if (this.state.listPaging?.hasMore) {
|
|
97
|
+
void this.ensureListWindowFilled(this.nextSignal());
|
|
98
|
+
}
|
|
93
99
|
if (shouldAutoSignin) {
|
|
94
100
|
void this.runAutoSignin();
|
|
95
101
|
}
|
|
@@ -189,9 +195,7 @@ export class TuiController {
|
|
|
189
195
|
}
|
|
190
196
|
handleSearchKey(key) {
|
|
191
197
|
if (this.keybindings.matches(key, "searchClose")) {
|
|
192
|
-
this.
|
|
193
|
-
this.state.searchQuery = "";
|
|
194
|
-
this.render();
|
|
198
|
+
this.closeSearch();
|
|
195
199
|
return;
|
|
196
200
|
}
|
|
197
201
|
if (this.keybindings.matches(key, "searchToggleMode")) {
|
|
@@ -215,7 +219,7 @@ export class TuiController {
|
|
|
215
219
|
const selected = this.state.searchResults[this.state.itemIndex];
|
|
216
220
|
if (selected) {
|
|
217
221
|
// 有选中项:打开
|
|
218
|
-
this.
|
|
222
|
+
this.restoreSearchOriginForActivation();
|
|
219
223
|
void this.activateContentItem(selected, this.nextSignal());
|
|
220
224
|
}
|
|
221
225
|
else if (this.state.searchQuery.trim()) {
|
|
@@ -402,7 +406,7 @@ export class TuiController {
|
|
|
402
406
|
}
|
|
403
407
|
// r:刷新
|
|
404
408
|
if (this.keybindings.matches(key, "topicRefresh") && this.state.topic) {
|
|
405
|
-
void this.
|
|
409
|
+
void this.refreshCurrentTopic(this.nextSignal());
|
|
406
410
|
return;
|
|
407
411
|
}
|
|
408
412
|
// s:收藏
|
|
@@ -493,9 +497,20 @@ export class TuiController {
|
|
|
493
497
|
void this.load(true);
|
|
494
498
|
}
|
|
495
499
|
handleContentKey(key) {
|
|
500
|
+
if ((key === "\t" || key === "\x1b[Z") && this.state.tabs.length > 1) {
|
|
501
|
+
this.switchTab(key === "\x1b[Z" ? -1 : 1);
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
if (/^[1-9]$/.test(key) && this.state.tabs.length > 1) {
|
|
505
|
+
this.switchTabToIndex(Number(key) - 1);
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
496
508
|
if (this.keybindings.matches(key, "listNext")) {
|
|
509
|
+
const previousIndex = this.state.itemIndex;
|
|
510
|
+
const shouldAdvanceAfterLoad = previousIndex >= this.state.items.length - 1;
|
|
497
511
|
this.state.itemIndex = Math.min(Math.max(0, this.state.items.length - 1), this.state.itemIndex + 1);
|
|
498
512
|
this.render();
|
|
513
|
+
void this.checkListAutoLoad(shouldAdvanceAfterLoad ? previousIndex + 1 : undefined);
|
|
499
514
|
return;
|
|
500
515
|
}
|
|
501
516
|
if (this.keybindings.matches(key, "listPrev")) {
|
|
@@ -525,13 +540,22 @@ export class TuiController {
|
|
|
525
540
|
if (this.keybindings.matches(key, "menu"))
|
|
526
541
|
this.openMenu();
|
|
527
542
|
}
|
|
543
|
+
switchTab(delta) {
|
|
544
|
+
const current = Math.max(0, this.state.tabs.findIndex((tab) => tab.id === this.state.tabId));
|
|
545
|
+
const next = (current + delta + this.state.tabs.length) % this.state.tabs.length;
|
|
546
|
+
this.switchTabToIndex(next);
|
|
547
|
+
}
|
|
548
|
+
switchTabToIndex(index) {
|
|
549
|
+
const tab = this.state.tabs[index];
|
|
550
|
+
if (!tab || tab.id === this.state.tabId)
|
|
551
|
+
return;
|
|
552
|
+
this.state.tabId = tab.id;
|
|
553
|
+
void this.load(true);
|
|
554
|
+
}
|
|
528
555
|
leave() {
|
|
529
556
|
this.abortCurrent();
|
|
530
557
|
if (this.state.mode === "topic") {
|
|
531
|
-
this.
|
|
532
|
-
this.state.focus = "content";
|
|
533
|
-
this.state.status = "";
|
|
534
|
-
this.render();
|
|
558
|
+
void this.leaveTopic();
|
|
535
559
|
return;
|
|
536
560
|
}
|
|
537
561
|
if (this.state.parentList) {
|
|
@@ -543,8 +567,139 @@ export class TuiController {
|
|
|
543
567
|
this.state.status = "";
|
|
544
568
|
this.render();
|
|
545
569
|
}
|
|
570
|
+
rememberListReturnState() {
|
|
571
|
+
if (this.state.mode !== "list")
|
|
572
|
+
return;
|
|
573
|
+
this.listReturnState = {
|
|
574
|
+
itemIndex: this.state.itemIndex,
|
|
575
|
+
scroll: this.state.scroll,
|
|
576
|
+
paging: this.cloneListPaging(this.state.listPaging)
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
async leaveTopic() {
|
|
580
|
+
const listReturn = this.listReturnState;
|
|
581
|
+
this.state.mode = "list";
|
|
582
|
+
this.state.focus = "content";
|
|
583
|
+
this.state.status = "";
|
|
584
|
+
this.state.topic = undefined;
|
|
585
|
+
if (listReturn) {
|
|
586
|
+
this.state.itemIndex = Math.min(Math.max(0, this.state.items.length - 1), listReturn.itemIndex);
|
|
587
|
+
this.state.listPaging = this.cloneListPaging(listReturn.paging);
|
|
588
|
+
this.state.scroll = listReturn.paging?.anchorOnReturn ? this.state.itemIndex : listReturn.scroll;
|
|
589
|
+
this.listReturnState = undefined;
|
|
590
|
+
}
|
|
591
|
+
this.render();
|
|
592
|
+
if (this.state.listPaging?.anchorOnReturn) {
|
|
593
|
+
await this.ensureListWindowFilled(this.nextSignal());
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
async ensureListWindowFilled(signal) {
|
|
597
|
+
const paging = this.state.listPaging;
|
|
598
|
+
if (!paging?.hasMore || this.state.loadingMore)
|
|
599
|
+
return;
|
|
600
|
+
const capacity = Math.max(1, this.state.listViewportCapacity || 10);
|
|
601
|
+
const targetLength = this.state.scroll + capacity;
|
|
602
|
+
while (this.state.items.length < targetLength && paging.hasMore && !this.state.loadingMore) {
|
|
603
|
+
const previousLength = this.state.items.length;
|
|
604
|
+
await this.loadNextListPage(signal);
|
|
605
|
+
if (this.state.items.length === previousLength)
|
|
606
|
+
break;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
async checkListAutoLoad(advanceToIndex) {
|
|
610
|
+
const paging = this.state.listPaging;
|
|
611
|
+
if (!paging?.hasMore || this.state.loadingMore)
|
|
612
|
+
return;
|
|
613
|
+
const capacity = Math.max(1, this.state.listViewportCapacity || 10);
|
|
614
|
+
const visibleEnd = this.state.scroll + capacity;
|
|
615
|
+
if (this.state.items.length <= visibleEnd || this.state.itemIndex >= this.state.items.length - 2) {
|
|
616
|
+
await this.loadNextListPage(this.nextSignal());
|
|
617
|
+
if (advanceToIndex !== undefined && advanceToIndex < this.state.items.length) {
|
|
618
|
+
this.state.itemIndex = advanceToIndex;
|
|
619
|
+
this.state.status = "";
|
|
620
|
+
this.render();
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
async loadNextListPage(signal) {
|
|
625
|
+
const paging = this.state.listPaging;
|
|
626
|
+
if (!paging?.hasMore || this.state.loadingMore)
|
|
627
|
+
return;
|
|
628
|
+
this.state.loadingMore = true;
|
|
629
|
+
this.render();
|
|
630
|
+
try {
|
|
631
|
+
const nextItems = await this.fetchNextListPage(paging, signal);
|
|
632
|
+
this.state.items.push(...nextItems);
|
|
633
|
+
paging.loaded += nextItems.length;
|
|
634
|
+
if (paging.kind !== "favorite-board-topics") {
|
|
635
|
+
paging.hasMore = nextItems.length >= paging.size;
|
|
636
|
+
}
|
|
637
|
+
this.state.stats = this.state.stats.map((item) => {
|
|
638
|
+
if (item.title === "版面主题" && paging.kind === "favorite-board-topics") {
|
|
639
|
+
return { ...item, detail: `${paging.loaded} 条` };
|
|
640
|
+
}
|
|
641
|
+
if (["主题", "新帖流", "最新新帖", "关注用户", "收藏更新"].includes(item.title)) {
|
|
642
|
+
return { ...item, detail: `${this.state.items.length} 条` };
|
|
643
|
+
}
|
|
644
|
+
return item;
|
|
645
|
+
});
|
|
646
|
+
this.state.status = paging.hasMore ? "已加载更多" : "已到底";
|
|
647
|
+
}
|
|
648
|
+
catch (error) {
|
|
649
|
+
if (!isAbortError(error))
|
|
650
|
+
this.state.status = error instanceof Error ? error.message : "加载失败";
|
|
651
|
+
}
|
|
652
|
+
finally {
|
|
653
|
+
this.state.loadingMore = false;
|
|
654
|
+
this.render();
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
async fetchNextListPage(paging, signal) {
|
|
658
|
+
if (paging.kind === "new-topics") {
|
|
659
|
+
const topics = asArray(await this.client.getNewTopics(paging.loaded, paging.size, false, signal));
|
|
660
|
+
return topics.map((topic) => topicItem(topic));
|
|
661
|
+
}
|
|
662
|
+
if (paging.kind === "followee-topics") {
|
|
663
|
+
const topics = asArray(await this.client.getFolloweeTopics(paging.loaded, paging.size, false, signal));
|
|
664
|
+
return topics.map((topic) => topicItem(topic));
|
|
665
|
+
}
|
|
666
|
+
if (paging.kind === "favorite-board-topics") {
|
|
667
|
+
return this.fetchNextFavoriteBoardTopics(paging, signal);
|
|
668
|
+
}
|
|
669
|
+
const order = paging.kind === "favorite-updates" ? 1 : 0;
|
|
670
|
+
const topics = asArray(await this.client.getFavoriteTopics(paging.loaded, paging.size, order, 0, false, signal));
|
|
671
|
+
return topics.map((topic) => topicItem(topic));
|
|
672
|
+
}
|
|
673
|
+
async fetchNextFavoriteBoardTopics(paging, signal) {
|
|
674
|
+
const take = Math.max(1, Math.min(20, paging.size));
|
|
675
|
+
if ((paging.buffer?.length ?? 0) >= take) {
|
|
676
|
+
const items = paging.buffer?.splice(0, take) ?? [];
|
|
677
|
+
paging.hasMore = (paging.buffer?.length ?? 0) > 0 || (paging.boardCursors?.some((cursor) => cursor.hasMore) ?? false);
|
|
678
|
+
return items;
|
|
679
|
+
}
|
|
680
|
+
const cursors = paging.boardCursors?.filter((cursor) => cursor.hasMore) ?? [];
|
|
681
|
+
if (cursors.length === 0) {
|
|
682
|
+
const items = paging.buffer?.splice(0, take) ?? [];
|
|
683
|
+
paging.hasMore = (paging.buffer?.length ?? 0) > 0;
|
|
684
|
+
return items;
|
|
685
|
+
}
|
|
686
|
+
const batches = await mapLimit(cursors, 3, async (cursor) => {
|
|
687
|
+
const topics = asArray(await this.client.getBoardTopics(cursor.boardId, cursor.loaded, cursor.size, false, false, signal));
|
|
688
|
+
cursor.loaded += topics.length;
|
|
689
|
+
cursor.hasMore = topics.length >= cursor.size;
|
|
690
|
+
return topics.map((topic) => topicItem(topic, { title: cursor.title, boardId: cursor.boardId }));
|
|
691
|
+
});
|
|
692
|
+
const merged = [...(paging.buffer ?? []), ...batches.flat()]
|
|
693
|
+
.sort((left, right) => (right.sortTime ?? 0) - (left.sortTime ?? 0));
|
|
694
|
+
paging.buffer = merged;
|
|
695
|
+
paging.hasMore = paging.buffer.length > 0 || cursors.some((cursor) => cursor.hasMore);
|
|
696
|
+
return paging.buffer.splice(0, take);
|
|
697
|
+
}
|
|
546
698
|
refresh() {
|
|
547
|
-
if (this.state.
|
|
699
|
+
if (this.state.mode === "topic" && this.state.topic) {
|
|
700
|
+
void this.refreshCurrentTopic(this.nextSignal());
|
|
701
|
+
}
|
|
702
|
+
else if (this.state.currentBoard) {
|
|
548
703
|
void this.openBoard(this.state.currentBoard.boardId, this.state.currentBoard.title, true, this.nextSignal(), false);
|
|
549
704
|
}
|
|
550
705
|
else if (this.state.currentChat) {
|
|
@@ -556,6 +711,7 @@ export class TuiController {
|
|
|
556
711
|
}
|
|
557
712
|
openSearch() {
|
|
558
713
|
this.state.modal = "search";
|
|
714
|
+
this.state.searchOrigin = { itemIndex: this.state.itemIndex, scroll: this.state.scroll };
|
|
559
715
|
this.state.searchQuery = "";
|
|
560
716
|
this.state.searchResults = [];
|
|
561
717
|
this.state.searchMode = "topics";
|
|
@@ -563,6 +719,29 @@ export class TuiController {
|
|
|
563
719
|
this.state.itemIndex = 0;
|
|
564
720
|
this.render();
|
|
565
721
|
}
|
|
722
|
+
closeSearch() {
|
|
723
|
+
const origin = this.state.searchOrigin;
|
|
724
|
+
this.state.modal = null;
|
|
725
|
+
this.state.searchOrigin = undefined;
|
|
726
|
+
this.state.searchQuery = "";
|
|
727
|
+
this.state.searchResults = [];
|
|
728
|
+
if (origin) {
|
|
729
|
+
this.state.itemIndex = origin.itemIndex;
|
|
730
|
+
this.state.scroll = origin.scroll;
|
|
731
|
+
}
|
|
732
|
+
this.render();
|
|
733
|
+
}
|
|
734
|
+
restoreSearchOriginForActivation() {
|
|
735
|
+
const origin = this.state.searchOrigin;
|
|
736
|
+
this.state.modal = null;
|
|
737
|
+
this.state.searchOrigin = undefined;
|
|
738
|
+
this.state.searchQuery = "";
|
|
739
|
+
this.state.searchResults = [];
|
|
740
|
+
if (origin) {
|
|
741
|
+
this.state.itemIndex = origin.itemIndex;
|
|
742
|
+
this.state.scroll = origin.scroll;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
566
745
|
getSearchScope() {
|
|
567
746
|
// 根据当前位置确定搜索范围
|
|
568
747
|
if (this.state.currentBoard) {
|
|
@@ -598,9 +777,19 @@ export class TuiController {
|
|
|
598
777
|
this.state.userDetail = undefined;
|
|
599
778
|
this.render();
|
|
600
779
|
}
|
|
780
|
+
setTabs(tabs, defaultId) {
|
|
781
|
+
this.state.tabs = tabs;
|
|
782
|
+
if (!tabs.some((tab) => tab.id === this.state.tabId)) {
|
|
783
|
+
this.state.tabId = defaultId;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
601
786
|
async loadView(view, force, signal) {
|
|
787
|
+
if (view !== "new" && view !== "following") {
|
|
788
|
+
this.setTabs([{ id: "default", label: "" }], "default");
|
|
789
|
+
}
|
|
602
790
|
switch (view) {
|
|
603
791
|
case "hot": {
|
|
792
|
+
this.setTabs([{ id: "default", label: "" }], "default");
|
|
604
793
|
const [index, unread] = await Promise.all([
|
|
605
794
|
this.client.getForumIndex(force, signal),
|
|
606
795
|
this.client.getUnreadCount(force, signal)
|
|
@@ -612,14 +801,46 @@ export class TuiController {
|
|
|
612
801
|
title: "十大",
|
|
613
802
|
items: hotTopics.map((topic) => topicItem(topic)),
|
|
614
803
|
stats: unreadStats(unreadObject),
|
|
615
|
-
overview: overviewStats(indexObject, unreadObject)
|
|
804
|
+
overview: overviewStats(indexObject, unreadObject),
|
|
805
|
+
status: "十大:j/k 选择 Enter 打开 r 刷新"
|
|
616
806
|
};
|
|
617
807
|
}
|
|
618
808
|
case "new": {
|
|
619
|
-
|
|
620
|
-
|
|
809
|
+
this.setTabs([
|
|
810
|
+
{ id: "new-latest", label: "最新" },
|
|
811
|
+
{ id: "new-random", label: "随机" },
|
|
812
|
+
{ id: "new-recommendation", label: "推荐" }
|
|
813
|
+
], "new-latest");
|
|
814
|
+
if (this.state.tabId === "new-recommendation") {
|
|
815
|
+
const topics = asArray(await this.client.getRandomRecommendations(10, true, signal));
|
|
816
|
+
return {
|
|
817
|
+
title: "新帖 · 推荐",
|
|
818
|
+
items: topics.map((topic) => topicItem(topic)),
|
|
819
|
+
stats: [{ title: "推荐", detail: `${topics.length} 条` }],
|
|
820
|
+
status: "新帖:Tab 切换 j/k 选择 Enter 打开 r 换一批"
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
if (this.state.tabId === "new-random") {
|
|
824
|
+
const topics = asArray(await this.client.getRandomTopics(20, true, signal));
|
|
825
|
+
return {
|
|
826
|
+
title: "新帖 · 随机",
|
|
827
|
+
items: topics.map((topic) => topicItem(topic)),
|
|
828
|
+
stats: [{ title: "随机新帖", detail: `${topics.length} 条` }],
|
|
829
|
+
status: "新帖:Tab 切换 j/k 选择 Enter 打开 r 换一批"
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
const size = 20;
|
|
833
|
+
const topics = asArray(await this.client.getNewTopics(0, size, force, signal));
|
|
834
|
+
return {
|
|
835
|
+
title: "新帖 · 最新",
|
|
836
|
+
items: topics.map((topic) => topicItem(topic)),
|
|
837
|
+
stats: [{ title: "最新新帖", detail: `${topics.length} 条` }],
|
|
838
|
+
status: "新帖:Tab 切换 j/k 选择 Enter 打开 r 刷新",
|
|
839
|
+
paging: { kind: "new-topics", loaded: topics.length, size, hasMore: topics.length >= size, anchorOnReturn: true }
|
|
840
|
+
};
|
|
621
841
|
}
|
|
622
842
|
case "boards": {
|
|
843
|
+
this.setTabs([{ id: "default", label: "" }], "default");
|
|
623
844
|
const sections = asArray(await this.client.getAllBoards(force, signal));
|
|
624
845
|
const boards = flattenBoards(sections);
|
|
625
846
|
return {
|
|
@@ -630,15 +851,37 @@ export class TuiController {
|
|
|
630
851
|
};
|
|
631
852
|
}
|
|
632
853
|
case "following": {
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
854
|
+
this.setTabs([
|
|
855
|
+
{ id: "follow-boards", label: "版面" },
|
|
856
|
+
{ id: "follow-users", label: "用户" },
|
|
857
|
+
{ id: "follow-favorites", label: "追踪" }
|
|
858
|
+
], "follow-boards");
|
|
859
|
+
if (this.state.tabId === "follow-users") {
|
|
860
|
+
const size = 20;
|
|
861
|
+
const topics = asArray(await this.client.getFolloweeTopics(0, size, force, signal));
|
|
862
|
+
return {
|
|
863
|
+
title: "关注 · 用户",
|
|
864
|
+
items: topics.map((topic) => topicItem(topic)),
|
|
865
|
+
stats: [{ title: "关注用户", detail: `${topics.length} 条` }],
|
|
866
|
+
status: "关注:Tab 切换 j/k 选择 Enter 打开 r 刷新",
|
|
867
|
+
paging: { kind: "followee-topics", loaded: topics.length, size, hasMore: topics.length >= size, anchorOnReturn: true }
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
if (this.state.tabId === "follow-favorites") {
|
|
871
|
+
const size = 20;
|
|
872
|
+
const topics = asArray(await this.client.getFavoriteTopics(0, size, 1, 0, force, signal));
|
|
873
|
+
return {
|
|
874
|
+
title: "关注 · 追踪",
|
|
875
|
+
items: topics.map((topic) => topicItem(topic)),
|
|
876
|
+
stats: [{ title: "收藏更新", detail: `${topics.length} 条` }],
|
|
877
|
+
status: "关注:Tab 切换 j/k 选择 Enter 打开 r 刷新",
|
|
878
|
+
paging: { kind: "favorite-updates", loaded: topics.length, size, hasMore: topics.length >= size, anchorOnReturn: true }
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
return this.loadFavoriteBoardTopics(force, signal);
|
|
640
882
|
}
|
|
641
883
|
case "favorite": {
|
|
884
|
+
this.setTabs([{ id: "default", label: "" }], "default");
|
|
642
885
|
const [meRaw, sectionsRaw, topicFavorites] = await Promise.all([
|
|
643
886
|
this.client.getMe(force, signal),
|
|
644
887
|
this.client.getAllBoards(false, signal),
|
|
@@ -647,12 +890,20 @@ export class TuiController {
|
|
|
647
890
|
const customBoards = asArray(asObject(meRaw).customBoards).filter((id) => typeof id === "number");
|
|
648
891
|
const allBoards = flattenBoards(asArray(sectionsRaw));
|
|
649
892
|
const boardById = new Map(allBoards.filter((board) => board.boardId !== undefined).map((board) => [board.boardId, board]));
|
|
893
|
+
const boardPageSize = 5;
|
|
650
894
|
const topicGroups = await mapLimit(customBoards, 3, async (boardId) => {
|
|
651
895
|
const board = boardById.get(boardId);
|
|
652
|
-
const topics = asArray(await this.client.getBoardTopics(boardId, 0,
|
|
653
|
-
return topics.map((topic) => topicItem(topic, board));
|
|
896
|
+
const topics = asArray(await this.client.getBoardTopics(boardId, 0, boardPageSize, false, force, signal));
|
|
897
|
+
return { boardId, board, topics: topics.map((topic) => topicItem(topic, board)), hasMore: topics.length >= boardPageSize };
|
|
654
898
|
});
|
|
655
|
-
const boardTopics = topicGroups.
|
|
899
|
+
const boardTopics = topicGroups.flatMap((group) => group.topics).sort((left, right) => (right.sortTime ?? 0) - (left.sortTime ?? 0));
|
|
900
|
+
const boardCursors = topicGroups.map((group) => ({
|
|
901
|
+
boardId: group.boardId,
|
|
902
|
+
title: group.board?.title ?? `#${group.boardId}`,
|
|
903
|
+
loaded: group.topics.length,
|
|
904
|
+
size: boardPageSize,
|
|
905
|
+
hasMore: group.hasMore
|
|
906
|
+
}));
|
|
656
907
|
return {
|
|
657
908
|
title: "收藏",
|
|
658
909
|
items: [
|
|
@@ -667,7 +918,15 @@ export class TuiController {
|
|
|
667
918
|
{ title: "收藏主题", detail: `${asArray(topicFavorites).length} 条` },
|
|
668
919
|
{ title: "版面主题", detail: `${boardTopics.length} 条` }
|
|
669
920
|
],
|
|
670
|
-
status: "收藏:j/k 选择 Enter 打开 h 返回 r 刷新"
|
|
921
|
+
status: "收藏:j/k 选择 Enter 打开 h 返回 r 刷新",
|
|
922
|
+
paging: {
|
|
923
|
+
kind: "favorite-board-topics",
|
|
924
|
+
loaded: boardTopics.length,
|
|
925
|
+
size: Math.max(12, customBoards.length * boardPageSize),
|
|
926
|
+
hasMore: boardCursors.some((cursor) => cursor.hasMore),
|
|
927
|
+
anchorOnReturn: true,
|
|
928
|
+
boardCursors
|
|
929
|
+
}
|
|
671
930
|
};
|
|
672
931
|
}
|
|
673
932
|
case "messages": {
|
|
@@ -736,6 +995,46 @@ export class TuiController {
|
|
|
736
995
|
}
|
|
737
996
|
}
|
|
738
997
|
}
|
|
998
|
+
async loadFavoriteBoardTopics(force, signal) {
|
|
999
|
+
const [meRaw, sectionsRaw] = await Promise.all([
|
|
1000
|
+
this.client.getMe(force, signal),
|
|
1001
|
+
this.client.getAllBoards(false, signal)
|
|
1002
|
+
]);
|
|
1003
|
+
const customBoards = asArray(asObject(meRaw).customBoards).filter((id) => typeof id === "number");
|
|
1004
|
+
const allBoards = flattenBoards(asArray(sectionsRaw));
|
|
1005
|
+
const boardById = new Map(allBoards.filter((board) => board.boardId !== undefined).map((board) => [board.boardId, board]));
|
|
1006
|
+
const boardPageSize = 5;
|
|
1007
|
+
const topicGroups = await mapLimit(customBoards, 3, async (boardId) => {
|
|
1008
|
+
const board = boardById.get(boardId);
|
|
1009
|
+
const topics = asArray(await this.client.getBoardTopics(boardId, 0, boardPageSize, false, force, signal));
|
|
1010
|
+
return { boardId, board, topics: topics.map((topic) => topicItem(topic, board)), hasMore: topics.length >= boardPageSize };
|
|
1011
|
+
});
|
|
1012
|
+
const boardTopics = topicGroups.flatMap((group) => group.topics).sort((left, right) => (right.sortTime ?? 0) - (left.sortTime ?? 0));
|
|
1013
|
+
const boardCursors = topicGroups.map((group) => ({
|
|
1014
|
+
boardId: group.boardId,
|
|
1015
|
+
title: group.board?.title ?? `#${group.boardId}`,
|
|
1016
|
+
loaded: group.topics.length,
|
|
1017
|
+
size: boardPageSize,
|
|
1018
|
+
hasMore: group.hasMore
|
|
1019
|
+
}));
|
|
1020
|
+
return {
|
|
1021
|
+
title: "关注 · 版面",
|
|
1022
|
+
items: boardTopics,
|
|
1023
|
+
stats: [
|
|
1024
|
+
{ title: "关注版面", detail: `${customBoards.length} 个` },
|
|
1025
|
+
{ title: "版面主题", detail: `${boardTopics.length} 条` }
|
|
1026
|
+
],
|
|
1027
|
+
status: "关注:Tab 切换 j/k 选择 Enter 打开 r 刷新",
|
|
1028
|
+
paging: {
|
|
1029
|
+
kind: "favorite-board-topics",
|
|
1030
|
+
loaded: boardTopics.length,
|
|
1031
|
+
size: Math.max(12, customBoards.length * boardPageSize),
|
|
1032
|
+
hasMore: boardCursors.some((cursor) => cursor.hasMore),
|
|
1033
|
+
anchorOnReturn: true,
|
|
1034
|
+
boardCursors
|
|
1035
|
+
}
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
739
1038
|
async activateSetting(selected) {
|
|
740
1039
|
if (!selected)
|
|
741
1040
|
return;
|
|
@@ -839,6 +1138,7 @@ export class TuiController {
|
|
|
839
1138
|
}
|
|
840
1139
|
async activateContentItem(selected, signal) {
|
|
841
1140
|
if (selected.topicId !== undefined) {
|
|
1141
|
+
this.rememberListReturnState();
|
|
842
1142
|
await this.openTopic(selected.topicId, false, signal);
|
|
843
1143
|
return;
|
|
844
1144
|
}
|
|
@@ -864,11 +1164,6 @@ export class TuiController {
|
|
|
864
1164
|
this.openEmojiCategory(selected.meta.slice("emoji-category:".length));
|
|
865
1165
|
return;
|
|
866
1166
|
}
|
|
867
|
-
if (selected.meta?.startsWith("emoji-batch:")) {
|
|
868
|
-
this.state.status = "继续向下选择具体表情,Enter 放大预览";
|
|
869
|
-
this.render();
|
|
870
|
-
return;
|
|
871
|
-
}
|
|
872
1167
|
if (selected.meta?.startsWith("emoji:")) {
|
|
873
1168
|
this.openEmojiDetail(selected.meta.slice("emoji:".length));
|
|
874
1169
|
return;
|
|
@@ -884,7 +1179,7 @@ export class TuiController {
|
|
|
884
1179
|
this.state.status = "当前条目不可进入";
|
|
885
1180
|
this.render();
|
|
886
1181
|
}
|
|
887
|
-
async openTopic(topicId, force, signal) {
|
|
1182
|
+
async openTopic(topicId, force, signal, restore) {
|
|
888
1183
|
this.state.mode = "topic";
|
|
889
1184
|
this.state.loading = true;
|
|
890
1185
|
this.state.error = undefined;
|
|
@@ -893,11 +1188,15 @@ export class TuiController {
|
|
|
893
1188
|
this.state.scroll = 0;
|
|
894
1189
|
this.render();
|
|
895
1190
|
try {
|
|
1191
|
+
const from = restore ? Math.max(0, restore.floor - 1) : 0;
|
|
1192
|
+
const size = 10;
|
|
896
1193
|
const [topicRaw, postsRaw] = await Promise.all([
|
|
897
1194
|
this.client.getTopic(topicId, force, signal),
|
|
898
|
-
this.client.getTopicPosts(topicId,
|
|
1195
|
+
this.client.getTopicPosts(topicId, from, size, force, signal)
|
|
899
1196
|
]);
|
|
900
|
-
this.state.topic = buildTopicReader(topicId, asObject(topicRaw), asArray(postsRaw),
|
|
1197
|
+
this.state.topic = buildTopicReader(topicId, asObject(topicRaw), asArray(postsRaw), size, from);
|
|
1198
|
+
if (restore)
|
|
1199
|
+
this.restoreTopicPosition(restore);
|
|
901
1200
|
this.state.loading = false;
|
|
902
1201
|
this.state.status = "";
|
|
903
1202
|
}
|
|
@@ -913,6 +1212,31 @@ export class TuiController {
|
|
|
913
1212
|
void this.preloadTopicImages(this.state.topic);
|
|
914
1213
|
}
|
|
915
1214
|
}
|
|
1215
|
+
async refreshCurrentTopic(signal) {
|
|
1216
|
+
const topic = this.state.topic;
|
|
1217
|
+
if (!topic)
|
|
1218
|
+
return;
|
|
1219
|
+
const restore = this.getTopicRestoreTarget(topic);
|
|
1220
|
+
await this.openTopic(topic.topicId, true, signal, restore);
|
|
1221
|
+
}
|
|
1222
|
+
getTopicRestoreTarget(topic) {
|
|
1223
|
+
const post = currentTopicPost(topic, topic.cursorLine);
|
|
1224
|
+
return {
|
|
1225
|
+
floor: post?.floor ?? 1,
|
|
1226
|
+
lineOffset: post ? Math.max(0, topic.cursorLine - post.lineStart) : 0,
|
|
1227
|
+
loaded: topic.loaded
|
|
1228
|
+
};
|
|
1229
|
+
}
|
|
1230
|
+
restoreTopicPosition(target) {
|
|
1231
|
+
const topic = this.state.topic;
|
|
1232
|
+
if (!topic)
|
|
1233
|
+
return;
|
|
1234
|
+
const post = findTopicPostByFloor(topic, target.floor);
|
|
1235
|
+
if (!post)
|
|
1236
|
+
return;
|
|
1237
|
+
topic.cursorLine = Math.min(post.lineEnd, post.lineStart + target.lineOffset);
|
|
1238
|
+
this.state.scroll = topic.cursorLine;
|
|
1239
|
+
}
|
|
916
1240
|
async openBoard(boardId, title, force, signal, pushParent = true) {
|
|
917
1241
|
if (pushParent)
|
|
918
1242
|
this.snapshotParent();
|
|
@@ -921,6 +1245,7 @@ export class TuiController {
|
|
|
921
1245
|
this.state.viewTitle = title;
|
|
922
1246
|
this.state.focus = "content";
|
|
923
1247
|
this.state.currentBoard = { boardId, title };
|
|
1248
|
+
this.state.listPaging = undefined;
|
|
924
1249
|
this.state.itemIndex = 0;
|
|
925
1250
|
this.state.scroll = 0;
|
|
926
1251
|
this.render();
|
|
@@ -946,6 +1271,7 @@ export class TuiController {
|
|
|
946
1271
|
this.state.error = undefined;
|
|
947
1272
|
this.state.viewTitle = `私信: ${title}`;
|
|
948
1273
|
this.state.focus = "content";
|
|
1274
|
+
this.state.listPaging = undefined;
|
|
949
1275
|
this.state.itemIndex = 0;
|
|
950
1276
|
this.state.scroll = 0;
|
|
951
1277
|
this.render();
|
|
@@ -1042,9 +1368,7 @@ export class TuiController {
|
|
|
1042
1368
|
return;
|
|
1043
1369
|
const pageInfo = getTopicPageInfo(topic, topic.cursorLine);
|
|
1044
1370
|
if (pageInfo.currentPage > 1) {
|
|
1045
|
-
|
|
1046
|
-
this.state.status = "";
|
|
1047
|
-
this.render();
|
|
1371
|
+
await this.jumpToTopicPage(pageInfo.currentPage - 1, this.nextSignal());
|
|
1048
1372
|
}
|
|
1049
1373
|
}
|
|
1050
1374
|
async jumpToTopicPage(page, signal) {
|
|
@@ -1057,13 +1381,9 @@ export class TuiController {
|
|
|
1057
1381
|
this.render();
|
|
1058
1382
|
return;
|
|
1059
1383
|
}
|
|
1060
|
-
// 如果目标页未加载,需要加载到该页
|
|
1061
1384
|
const targetFloor = (page - 1) * FLOORS_PER_PAGE + 1;
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
await this.loadNextTopicPage(signal, true);
|
|
1065
|
-
if (topic.loaded === previousLoaded)
|
|
1066
|
-
break;
|
|
1385
|
+
if (!findTopicPostByFloor(topic, targetFloor)) {
|
|
1386
|
+
await this.loadTopicWindow(targetFloor, signal);
|
|
1067
1387
|
}
|
|
1068
1388
|
const post = findTopicPostByFloor(topic, targetFloor);
|
|
1069
1389
|
if (post) {
|
|
@@ -1075,6 +1395,30 @@ export class TuiController {
|
|
|
1075
1395
|
}
|
|
1076
1396
|
this.render();
|
|
1077
1397
|
}
|
|
1398
|
+
async loadTopicWindow(startFloor, signal) {
|
|
1399
|
+
const topic = this.state.topic;
|
|
1400
|
+
if (!topic || this.state.loadingMore)
|
|
1401
|
+
return;
|
|
1402
|
+
const from = Math.max(0, startFloor - 1);
|
|
1403
|
+
this.state.loadingMore = true;
|
|
1404
|
+
this.render();
|
|
1405
|
+
try {
|
|
1406
|
+
const posts = asArray(await this.client.getTopicPosts(topic.topicId, from, topic.size, false, signal));
|
|
1407
|
+
replaceTopicPosts(topic, posts, from);
|
|
1408
|
+
topic.hasMore = from + posts.length < topic.totalFloors;
|
|
1409
|
+
this.state.scroll = 0;
|
|
1410
|
+
this.state.status = "";
|
|
1411
|
+
void this.preloadTopicImages(topic);
|
|
1412
|
+
}
|
|
1413
|
+
catch (error) {
|
|
1414
|
+
if (!isAbortError(error))
|
|
1415
|
+
this.state.status = error instanceof Error ? error.message : "加载失败";
|
|
1416
|
+
}
|
|
1417
|
+
finally {
|
|
1418
|
+
this.state.loadingMore = false;
|
|
1419
|
+
this.render();
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1078
1422
|
async loadNextTopicPage(signal, quiet = false) {
|
|
1079
1423
|
const topic = this.state.topic;
|
|
1080
1424
|
if (!topic?.hasMore || this.state.loadingMore)
|
|
@@ -1108,11 +1452,8 @@ export class TuiController {
|
|
|
1108
1452
|
this.render();
|
|
1109
1453
|
return;
|
|
1110
1454
|
}
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
await this.loadNextTopicPage(signal, true);
|
|
1114
|
-
if (topic.loaded === previousLoaded)
|
|
1115
|
-
break;
|
|
1455
|
+
if (!findTopicPostByFloor(topic, floor)) {
|
|
1456
|
+
await this.loadTopicWindow(floor, signal);
|
|
1116
1457
|
}
|
|
1117
1458
|
const post = findTopicPostByFloor(topic, floor);
|
|
1118
1459
|
topic.cursorLine = post?.lineStart ?? topic.cursorLine;
|
|
@@ -1139,8 +1480,9 @@ export class TuiController {
|
|
|
1139
1480
|
case "favorite-topics":
|
|
1140
1481
|
case "favorite-updates": {
|
|
1141
1482
|
const order = action === "favorite-updates" ? 1 : 0;
|
|
1142
|
-
const
|
|
1143
|
-
|
|
1483
|
+
const size = 20;
|
|
1484
|
+
const topics = asArray(await this.client.getFavoriteTopics(0, size, order, 0, false, signal));
|
|
1485
|
+
this.openReadOnlyList(action === "favorite-updates" ? "收藏更新" : "收藏主题", topics.map((topic) => topicItem(topic)), [{ title: "主题", detail: `${topics.length}` }], { kind: action === "favorite-updates" ? "favorite-updates" : "favorite-topics", loaded: topics.length, size, hasMore: topics.length >= size, anchorOnReturn: true });
|
|
1144
1486
|
return;
|
|
1145
1487
|
}
|
|
1146
1488
|
case "favorite-groups": {
|
|
@@ -1359,12 +1701,7 @@ export class TuiController {
|
|
|
1359
1701
|
const topic = this.state.topic;
|
|
1360
1702
|
if (!topic)
|
|
1361
1703
|
return undefined;
|
|
1362
|
-
|
|
1363
|
-
const line = post.lines.find((entry) => entry.line === topic.cursorLine);
|
|
1364
|
-
if (line)
|
|
1365
|
-
return line;
|
|
1366
|
-
}
|
|
1367
|
-
return undefined;
|
|
1704
|
+
return currentTopicLine(topic, topic.cursorLine);
|
|
1368
1705
|
}
|
|
1369
1706
|
/**
|
|
1370
1707
|
* Preload images for topic in background
|
|
@@ -1589,24 +1926,14 @@ export class TuiController {
|
|
|
1589
1926
|
this.render();
|
|
1590
1927
|
return;
|
|
1591
1928
|
}
|
|
1592
|
-
const items =
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
});
|
|
1601
|
-
for (const code of batch) {
|
|
1602
|
-
const art = getEmojiArt(code);
|
|
1603
|
-
items.push({
|
|
1604
|
-
title: `[${code}]`,
|
|
1605
|
-
meta: `emoji:${code}`,
|
|
1606
|
-
detail: art ? `${category.label} · ${art.width}x${art.height}px` : category.label
|
|
1607
|
-
});
|
|
1608
|
-
}
|
|
1609
|
-
}
|
|
1929
|
+
const items = category.codes.map((code) => {
|
|
1930
|
+
const art = getEmojiArt(code);
|
|
1931
|
+
return {
|
|
1932
|
+
title: `[${code}]`,
|
|
1933
|
+
meta: `emoji:${code}`,
|
|
1934
|
+
detail: art ? `${category.label} · ${art.width}x${art.height}px` : category.label
|
|
1935
|
+
};
|
|
1936
|
+
});
|
|
1610
1937
|
this.openReadOnlyList(category.label, items, [
|
|
1611
1938
|
{ title: "分类", detail: category.label },
|
|
1612
1939
|
{ title: "数量", detail: `${category.codes.length} 个` },
|
|
@@ -1635,7 +1962,7 @@ export class TuiController {
|
|
|
1635
1962
|
}
|
|
1636
1963
|
async openAccountSwitcher() {
|
|
1637
1964
|
try {
|
|
1638
|
-
const accounts = await this.tokenStore.listAccounts();
|
|
1965
|
+
const accounts = (await this.tokenStore.listAccounts()).filter((account) => account.account !== "default");
|
|
1639
1966
|
const currentAccount = await this.tokenStore.getCurrentAccountName();
|
|
1640
1967
|
if (accounts.length === 0) {
|
|
1641
1968
|
this.state.modal = "info";
|
|
@@ -1654,7 +1981,7 @@ export class TuiController {
|
|
|
1654
1981
|
this.state.viewTitle = "切换账号";
|
|
1655
1982
|
this.state.items = items;
|
|
1656
1983
|
this.state.stats = [{ title: "账号数", detail: `${accounts.length}` }];
|
|
1657
|
-
this.state.itemIndex = accounts.findIndex(a => a.account === currentAccount);
|
|
1984
|
+
this.state.itemIndex = Math.max(0, accounts.findIndex(a => a.account === currentAccount));
|
|
1658
1985
|
this.state.scroll = 0;
|
|
1659
1986
|
this.state.focus = "content";
|
|
1660
1987
|
this.state.mode = "list";
|
|
@@ -1752,11 +2079,12 @@ export class TuiController {
|
|
|
1752
2079
|
const users = asArray(await this.client.getUsers(ids, false, signal));
|
|
1753
2080
|
this.openReadOnlyList(type === "follower" ? "粉丝列表" : "关注列表", users.map((user) => userItem(user)), [{ title: "用户", detail: `${users.length}` }]);
|
|
1754
2081
|
}
|
|
1755
|
-
openReadOnlyList(title, items, stats) {
|
|
2082
|
+
openReadOnlyList(title, items, stats, paging) {
|
|
1756
2083
|
this.snapshotParent();
|
|
1757
2084
|
this.state.viewTitle = title;
|
|
1758
2085
|
this.state.items = items;
|
|
1759
2086
|
this.state.stats = stats;
|
|
2087
|
+
this.state.listPaging = paging;
|
|
1760
2088
|
this.state.itemIndex = 0;
|
|
1761
2089
|
this.state.scroll = 0;
|
|
1762
2090
|
this.state.focus = "content";
|
|
@@ -1775,6 +2103,7 @@ export class TuiController {
|
|
|
1775
2103
|
itemIndex: this.state.itemIndex,
|
|
1776
2104
|
scroll: this.state.scroll,
|
|
1777
2105
|
status: this.state.status,
|
|
2106
|
+
paging: this.cloneListPaging(this.state.listPaging),
|
|
1778
2107
|
parent: this.state.parentList
|
|
1779
2108
|
};
|
|
1780
2109
|
}
|
|
@@ -1788,6 +2117,7 @@ export class TuiController {
|
|
|
1788
2117
|
this.state.itemIndex = parent.itemIndex;
|
|
1789
2118
|
this.state.scroll = parent.scroll;
|
|
1790
2119
|
this.state.status = parent.status;
|
|
2120
|
+
this.state.listPaging = this.cloneListPaging(parent.paging);
|
|
1791
2121
|
this.state.parentList = parent.parent;
|
|
1792
2122
|
this.state.currentBoard = undefined;
|
|
1793
2123
|
this.state.currentChat = undefined;
|
|
@@ -1796,6 +2126,15 @@ export class TuiController {
|
|
|
1796
2126
|
this.state.focus = "content";
|
|
1797
2127
|
this.render();
|
|
1798
2128
|
}
|
|
2129
|
+
cloneListPaging(paging) {
|
|
2130
|
+
if (!paging)
|
|
2131
|
+
return undefined;
|
|
2132
|
+
return {
|
|
2133
|
+
...paging,
|
|
2134
|
+
boardCursors: paging.boardCursors?.map((cursor) => ({ ...cursor })),
|
|
2135
|
+
buffer: paging.buffer?.map((itemValue) => ({ ...itemValue }))
|
|
2136
|
+
};
|
|
2137
|
+
}
|
|
1799
2138
|
async checkUpdate(forceShow = false) {
|
|
1800
2139
|
if (forceShow) {
|
|
1801
2140
|
this.state.status = "正在检查 GitHub Release...";
|