Files
impulse-jail/src/hooks/useCountdown.ts
T
kurihada 5bf98753f1 feat: 完整 MVP — 剁手小黑屋反冲动消费 Web App
- 数据层: Prisma 7 + SQLite (better-sqlite3 adapter), Item 模型
- API: POST/GET/PATCH /api/items, 商品 CRUD 与状态流转
- 关押表单: 图片拖拽上传 + Canvas 压缩, Base64 存储
- 首页看守所: 72h 倒计时 Hook, 省钱看板, 冷却/释放卡片交互
- PWA: manifest.ts, apple-icon, viewport 沉浸配置, iOS 引导组件
- 部署: Dockerfile 多阶段构建, docker-compose, Jenkinsfile CI/CD
- 图标: 全套专属像素风锁头+购物车图标 (favicon/96/180/192/512/OG)
2026-02-25 18:28:17 +08:00

44 lines
1.1 KiB
TypeScript

"use client";
import { useState, useEffect } from "react";
const COOLING_HOURS = 72;
const COOLING_MS = COOLING_HOURS * 60 * 60 * 1000;
export interface CountdownResult {
hours: number;
minutes: number;
seconds: number;
isExpired: boolean;
display: string;
}
export function useCountdown(createdAt: string): CountdownResult {
const [now, setNow] = useState(Date.now());
useEffect(() => {
const timer = setInterval(() => setNow(Date.now()), 1000);
return () => clearInterval(timer);
}, []);
const created = new Date(createdAt).getTime();
const deadline = created + COOLING_MS;
const remaining = Math.max(0, deadline - now);
const totalSeconds = Math.floor(remaining / 1000);
const hours = Math.floor(totalSeconds / 3600);
const minutes = Math.floor((totalSeconds % 3600) / 60);
const seconds = totalSeconds % 60;
const pad = (n: number) => n.toString().padStart(2, "0");
const display = `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
return {
hours,
minutes,
seconds,
isExpired: remaining === 0,
display,
};
}