前言
最近一款名为“死了么”的App迅速火爆,精准切入独居人群的安全痛点,目前已登顶苹果付费榜第一名。其核心逻辑极其简明:签到报平安,断联即发邮件。出于好奇,我查阅了相关数据。第七次全国人口普查显示,我国一人户家庭已超过1.25亿户,占全国户口总数的25%。对于独居者而言,意外发生不可怕,可怕的是无人知晓。这款应用恰恰命中了这一深层需求。趁着这波热度,我也重新梳理思路,打造了一款对标产品——“活着呢”(TO BE LIVE)。
产品哲学
首先阐述核心逻辑,简洁直观,用户一目了然:
- 生存契约:用户设定一个确认周期,默认48小时。
- 打卡续命:在倒计时结束前,用户需主动点击“我还活着呢”按钮完成签到。
- 失联预警:若超时未签到,系统自动判定“可能发生意外”,并立即通知预设的紧急联系人。
简而言之,这相当于一个24小时不间断的守护卫士,时刻关注你最根本的问题——你是否还在。
技术选型
探讨完产品逻辑,接下来介绍技术搭建方案。技术栈围绕“快速验证”这一目标进行选择:
- 前端框架:Next.js 14(App Router)。兼顾SEO友好与流畅的路由体验。
- 状态动效:Framer Motion。为倒计时和按钮注入“呼吸感”,避免界面沉闷。
- 后端支撑:Supabase(BaaS)。无需自行编写CRUD,秒级完成Auth授权与数据库存储,大幅节省开发时间。
- 通知服务:Resend。用于定时发送邮件,轻量且够用。
- 主题适配:Tailwind CSS。支持“深邃绿”和“荧光绿”两套主题——白天呈现沉静的翡翠色,夜晚则化作守护的微光,视觉质感突出。
- 部署环境:Vercel。全球加速,毕竟生命经不起等待。
核心代码实现
核心功能分为三大块:注册/登录退出、签到与紧急联系人设置、主题切换。这里重点拆解登录页和签到页的逻辑实现。
登录页
用户未注册时先创建账号,再登录。核心代码非常简洁:注册和登录共用同一表单,通过isLogin状态进行分支判断。
const handleSubmit = async (e) => {
e.preventDefault()
setLoading(true)
try {
if (isLogin) {
const { error } = await supabase.auth.signInWithPassword({ email, password })
if (error) throw error
router.push('/')
} else {
const { error } = await supabase.auth.signUp({ email, password })
if (error) throw error
alert('验证邮件已发送,请查收!')
}
} catch (err) {
alert(err.message)
} finally {
setLoading(false)
}
}
签到页
签到页是整个应用的核心,动态倒计时逻辑需在客户端实时计算,并根据紧迫程度变换颜色。当前安全周期固定为48小时,后续可改为用户自定义。按钮设计颇具巧思:点击时有缩放反馈,当倒计时剩余不足8小时时,图标变为警示三角,颜色从绿色过渡到琥珀色再到红色。该逻辑通过getAlertColorClass函数实现:
// 动态获取当前的强调色(预警系统)
const getAlertColorClass = () => {
if (timeLeft.h < 8) return "text-red-500";
if (timeLeft.h < 24) return "text-amber-500";
return "text-app-accent"; // 使用主题定义的强调色
};
倒计时更新核心逻辑通过useEffect定时器,每秒计算一次剩余时间:
const updateCountdown = useCallback(() => {
if (!profile?.last_check_in) return;
const last = new Date(profile.last_check_in).getTime();
const now = new Date().getTime();
const total = 48 * 60 * 60 * 1000;
const remaining = Math.max(0, total - (now - last));
setTimeLeft({
h: Math.floor(remaining / (1000 * 60 * 60)),
m: Math.floor((remaining % (1000 * 60 * 60)) / (1000 * 60)),
s: Math.floor((remaining % (1000 * 60)) / 1000),
percent: (remaining / total) * 100
});
}, [profile]);
useEffect(() => {
const timer = setInterval(updateCountdown, 1000);
return () => clearInterval(timer);
}, [updateCountdown]);
点击签到按钮时,将当前时间写入数据库,同时触发手机振动(若设备支持),给予用户物理层面的确认反馈:
const handleCheckIn = async () => {
if (!user) return;
const now = new Date().toISOString();
const { error } = await supabase.from("profiles").update({ last_check_in: now }).eq("id", user.id);
if (!error) {
setProfile((prev) => ({ ...prev, last_check_in: now }));
if ("vibrate" in na vigator) na vigator.vibrate([50, 30, 50]);
}
};
整体逻辑虽不复杂,但胜在简洁高效。
PWA 改造:从网页变成桌面 App
作为一款守护工具,必须做到触手可及。因此为其集成了PWA功能,用户“一键添加到桌面”后,使用体验将显著提升。通过配置 manifest.json,应用实现了:
- Standalone 模式:隐藏浏览器地址栏,提供全屏沉浸体验。
- 自定义图标:一个可爱鬼(代表用户当下状态)和一颗回答“YES”的小心脏。
该配置本身并不复杂,但效果立竿见影——用户打开时如同使用原生App,不会产生“仍在浏览器中”的突兀感。
后记
“我还活着呢”(TO BE LIVE),在每一个平凡的24小时里,温柔地问一句:“嘿,还在吗?”
项目已开源,欢迎来“续命”。
