同步草稿未读原生角标

平台壳按可见作品架未读完成草稿同步 app.setBadgeCount

新增草稿未读更新计数模型并覆盖多恢复ID去重

更新原生壳方案、HostBridge 协议和共享决策记录
This commit is contained in:
2026-06-18 07:18:42 +08:00
parent cdcbaf4cee
commit 20e4e88bd4
6 changed files with 170 additions and 30 deletions
@@ -190,7 +190,10 @@ import {
saveBabyObjectMatchDraft,
} from '../../services/edutainment-baby-object';
import { getExternalGenerationQueueOverview } from '../../services/external-generation';
import { showHostLocalNotification } from '../../services/host-bridge/hostBridge';
import {
setHostAppBadgeCount,
showHostLocalNotification,
} from '../../services/host-bridge/hostBridge';
import {
jumpHopClient,
type JumpHopGalleryCardResponse,
@@ -481,13 +484,13 @@ import {
buildPendingBigFishWorks,
buildPendingJumpHopWorks,
buildPendingMatch3DWorks,
buildPendingPuzzleClearWorks,
buildPendingPuzzleWorks,
buildPendingSquareHoleWorks,
buildPendingVisualNovelWorks,
buildPendingWoodenFishWorks,
collectDraftNoticeKeys,
collectVisibleDraftNoticeKeys,
countUnreadDraftGenerationUpdates,
createPendingDraftShelfState,
type DraftGenerationNoticeMap,
type DraftGenerationNoticeStatus,
@@ -3222,16 +3225,6 @@ export function PlatformEntryFlowShellImpl({
],
[jumpHopWorks, pendingDraftShelfItems],
);
const puzzleClearShelfItems = useMemo(
() => [
...buildPendingPuzzleClearWorks(
pendingDraftShelfItems['puzzle-clear'],
puzzleClearWorks,
),
...puzzleClearWorks,
],
[pendingDraftShelfItems, puzzleClearWorks],
);
const woodenFishShelfItems = useMemo(
() => [
...buildPendingWoodenFishWorks(
@@ -3298,28 +3291,24 @@ export function PlatformEntryFlowShellImpl({
}),
[draftGenerationNotices, pendingDraftShelfItems],
);
const visibleDraftNoticeKeys = useMemo(
() =>
collectVisibleDraftNoticeKeys({
rpgItems: creationHubItems,
bigFishItems: bigFishShelfItems,
jumpHopItems: jumpHopShelfItems,
woodenFishItems: woodenFishShelfItems,
match3dItems: match3dShelfItems,
squareHoleItems: isSquareHoleCreationVisible
? squareHoleShelfItems
: [],
puzzleItems: puzzleShelfItems,
visualNovelItems: visualNovelShelfItems,
barkBattleItems: barkBattleShelfItems,
babyObjectMatchItems: babyObjectMatchDrafts,
}),
const visibleDraftNoticeSources = useMemo(
() => ({
rpgItems: creationHubItems,
bigFishItems: bigFishShelfItems,
jumpHopItems: jumpHopShelfItems,
woodenFishItems: woodenFishShelfItems,
match3dItems: match3dShelfItems,
squareHoleItems: isSquareHoleCreationVisible ? squareHoleShelfItems : [],
puzzleItems: puzzleShelfItems,
visualNovelItems: visualNovelShelfItems,
barkBattleItems: barkBattleShelfItems,
babyObjectMatchItems: babyObjectMatchDrafts,
}),
[
babyObjectMatchDrafts,
barkBattleShelfItems,
bigFishShelfItems,
jumpHopShelfItems,
puzzleClearShelfItems,
woodenFishShelfItems,
creationHubItems,
isSquareHoleCreationVisible,
@@ -3329,6 +3318,18 @@ export function PlatformEntryFlowShellImpl({
visualNovelShelfItems,
],
);
const visibleDraftNoticeKeys = useMemo(
() => collectVisibleDraftNoticeKeys(visibleDraftNoticeSources),
[visibleDraftNoticeSources],
);
const unreadDraftGenerationUpdateCount = useMemo(
() =>
countUnreadDraftGenerationUpdates(
draftGenerationNotices,
visibleDraftNoticeSources,
),
[draftGenerationNotices, visibleDraftNoticeSources],
);
const hasUnreadDraftUpdates = useMemo(
() =>
hasUnreadDraftGenerationUpdates(
@@ -3337,6 +3338,9 @@ export function PlatformEntryFlowShellImpl({
),
[draftGenerationNotices, visibleDraftNoticeKeys],
);
useEffect(() => {
void setHostAppBadgeCount({ count: unreadDraftGenerationUpdateCount });
}, [unreadDraftGenerationUpdateCount]);
const resultViewError =
autosaveCoordinator.customWorldAutoSaveError ??
sessionController.customWorldError;
@@ -14,6 +14,7 @@ import {
buildPuzzleResultProfileId,
buildPuzzleResultWorkId,
collectVisibleDraftNoticeKeys,
countUnreadDraftGenerationUpdates,
createPendingDraftShelfState,
type DraftGenerationNoticeMap,
getGenerationNoticeShelfKeys,
@@ -595,6 +596,63 @@ describe('platformDraftGenerationShelfModel', () => {
visibleKeys,
),
).toBe(false);
expect(
countUnreadDraftGenerationUpdates(
{
'puzzle:puzzle-work-ocean': {
status: 'ready',
seen: false,
},
'puzzle:puzzle-profile-ocean': {
status: 'ready',
seen: false,
},
'puzzle:puzzle-session-ocean': {
status: 'ready',
seen: false,
},
},
{
rpgItems: [],
bigFishItems: [],
jumpHopItems: [],
woodenFishItems: [],
match3dItems: [],
squareHoleItems: [],
puzzleItems: [puzzle],
visualNovelItems: [],
barkBattleItems: [],
babyObjectMatchItems: [],
},
),
).toBe(1);
expect(
countUnreadDraftGenerationUpdates(
{
'puzzle:puzzle-profile-ocean': {
status: 'failed',
seen: false,
},
'puzzle:puzzle-session-other': {
status: 'ready',
seen: false,
},
},
{
rpgItems: [],
bigFishItems: [],
jumpHopItems: [],
woodenFishItems: [],
match3dItems: [],
squareHoleItems: [],
puzzleItems: [puzzle],
visualNovelItems: [],
barkBattleItems: [],
babyObjectMatchItems: [],
},
),
).toBe(0);
});
});
@@ -1111,6 +1111,79 @@ export function hasUnreadDraftGenerationUpdates(
});
}
export function countUnreadDraftGenerationUpdates(
notices: DraftGenerationNoticeMap,
sources: PlatformDraftGenerationVisibleShelfSources,
) {
const hasUnreadNotice = (keys: readonly string[]) =>
keys.some((key) => {
const notice = notices[key];
return notice?.status === 'ready' && !notice.seen;
});
return [
...sources.rpgItems.map((item) =>
collectDraftNoticeKeys('rpg', [
item.workId,
item.sessionId,
item.profileId,
]),
),
...sources.bigFishItems.map((item) =>
collectDraftNoticeKeys('big-fish', [item.workId, item.sourceSessionId]),
),
...sources.jumpHopItems.map((item) =>
collectDraftNoticeKeys('jump-hop', [
item.workId,
item.profileId,
item.sourceSessionId,
]),
),
...sources.woodenFishItems.map((item) =>
collectDraftNoticeKeys('wooden-fish', [
item.workId,
item.profileId,
item.sourceSessionId,
]),
),
...sources.match3dItems.map((item) =>
collectDraftNoticeKeys('match3d', [
item.workId,
item.profileId,
item.sourceSessionId,
]),
),
...sources.squareHoleItems.map((item) =>
collectDraftNoticeKeys('square-hole', [
item.workId,
item.profileId,
item.sourceSessionId,
]),
),
...sources.puzzleItems.map((item) =>
collectDraftNoticeKeys('puzzle', [
item.workId,
item.profileId,
item.sourceSessionId,
buildPuzzleResultWorkId(item.sourceSessionId),
buildPuzzleResultProfileId(item.sourceSessionId),
]),
),
...sources.visualNovelItems.map((item) =>
collectDraftNoticeKeys('visual-novel', [item.profileId]),
),
...sources.barkBattleItems.map((item) =>
collectDraftNoticeKeys('bark-battle', [item.workId, item.draftId]),
),
...sources.babyObjectMatchItems.map((item) =>
collectDraftNoticeKeys('baby-object-match', [
item.profileId,
item.draftId,
]),
),
].filter(hasUnreadNotice).length;
}
export function mergeBigFishWorkSummary(
current: BigFishWorkSummary,
updated: BigFishWorkSummary,