59 lines
2.6 KiB
TypeScript
59 lines
2.6 KiB
TypeScript
|
|
// apps/viewer/src/realtime/router.ts —— 服务端消息路由(P2 Task 10)。
|
||
|
|
// 纯函数 + 依赖注入:帧语义集中一处,单测直测(自身过滤/隐藏暂停/减少动画)。
|
||
|
|
//
|
||
|
|
// likes.burst 自身过滤(A21 客户端侧):originBatchIds 与本会话已提交批次求交
|
||
|
|
// 得己方 delta——己方贡献不播气泡(点击时已见过动画),他人贡献照常;total
|
||
|
|
// 恒为权威值,无论过滤与否都校准。页面不可见(visibilitychange)或「减少动画」
|
||
|
|
// 开启时跳过气泡(队列即弃——回台只看得到校准后的数字),计数校准不受影响。
|
||
|
|
import type { ServerMessage } from '@photo/contracts';
|
||
|
|
|
||
|
|
export interface RealtimeRouterDeps {
|
||
|
|
/** 页面当前是否不可见(document.hidden)。 */
|
||
|
|
isHidden(): boolean;
|
||
|
|
/** 「减少动画」偏好是否开启。 */
|
||
|
|
reducedMotion(): boolean;
|
||
|
|
/** originBatchIds 中属于本会话已提交批次的 delta 合计。 */
|
||
|
|
ownDeltaIn(batchIds: string[]): number;
|
||
|
|
/** 点赞计数权威校准(版本化)。 */
|
||
|
|
calibrateLike(photoId: string, total: number, countVersion: number): void;
|
||
|
|
/** 他人点赞气泡(photoId + 他人贡献 delta)。 */
|
||
|
|
showBubble(photoId: string, delta: number): void;
|
||
|
|
/** 新照片到达:亮提示条(不自动插入)。 */
|
||
|
|
notifyNewPhotos(): void;
|
||
|
|
/** 照片隐藏/删除:移出序列(调用方负责大图收口与提示)。 */
|
||
|
|
removePhoto(photoId: string): void;
|
||
|
|
/** 相册浏览数更新(stats.updated)。 */
|
||
|
|
setViewCount(viewCount: number): void;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function handleServerMessage(message: ServerMessage, deps: RealtimeRouterDeps): void {
|
||
|
|
switch (message.type) {
|
||
|
|
case 'pong':
|
||
|
|
return; // 心跳应答:无动作
|
||
|
|
case 'error':
|
||
|
|
// subscribe 校验失败等:连接保持,服务端已说明——本地备注即可。
|
||
|
|
console.info(`[viewer] realtime error frame: ${message.code}`);
|
||
|
|
return;
|
||
|
|
case 'photo.published':
|
||
|
|
deps.notifyNewPhotos();
|
||
|
|
return;
|
||
|
|
case 'photo.hidden':
|
||
|
|
case 'photo.deleted':
|
||
|
|
deps.removePhoto(message.photoId);
|
||
|
|
return;
|
||
|
|
case 'likes.burst': {
|
||
|
|
const own = deps.ownDeltaIn(message.originBatchIds);
|
||
|
|
// 校准无条件执行(含纯己方 burst——批次响应丢失时这是唯一权威回执)。
|
||
|
|
deps.calibrateLike(message.photoId, message.total, message.countVersion);
|
||
|
|
const others = message.delta - own;
|
||
|
|
if (others > 0 && !deps.isHidden() && !deps.reducedMotion()) {
|
||
|
|
deps.showBubble(message.photoId, others);
|
||
|
|
}
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
case 'stats.updated':
|
||
|
|
deps.setViewCount(message.viewCount);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|