Skip to content

usePopup

提供 Telegram 原生弹窗功能的 Hook,让你在 Mini App 中显示原生样式的对话框。

导入

typescript
import { usePopup } from '@xcloud/ui-telegram';

示例

API

返回值

属性类型描述
show(options: PopupOptions) => Promise<string | undefined>显示自定义弹窗
confirm(options: Omit<PopupOptions, 'buttons'>) => Promise<boolean>显示确认对话框
alert(options: Omit<PopupOptions, 'buttons'>) => Promise<void>显示提示对话框
isAvailable() => boolean检查 Popup 是否可用

PopupOptions

属性类型必填描述
titlestring弹窗标题
messagestring弹窗消息内容
buttonsPopupButton[]自定义按钮列表(最多3个)

PopupButton

属性类型必填描述
idstring按钮的唯一标识
type'default' | 'destructive' | 'cancel'按钮类型
textstring按钮文本

使用示例

提示对话框

显示简单的提示信息:

tsx
import { usePopup } from '@xcloud/ui-telegram';

function SuccessMessage() {
  const { alert } = usePopup();

  const handleSuccess = async () => {
    await alert({
      title: '成功',
      message: '你的操作已成功完成!',
    });
  };

  return <button onClick={handleSuccess}>显示成功消息</button>;
}

确认对话框

用户确认操作:

tsx
import { usePopup } from '@xcloud/ui-telegram';

function DeleteButton() {
  const { confirm } = usePopup();

  const handleDelete = async () => {
    const confirmed = await confirm({
      title: '确认删除',
      message: '确定要删除这个项目吗?此操作无法撤销。',
    });

    if (confirmed) {
      // 执行删除操作
      await deleteItem();
    }
  };

  return <button onClick={handleDelete}>删除</button>;
}

自定义按钮

显示最多3个自定义按钮:

tsx
import { usePopup } from '@xcloud/ui-telegram';

function DocumentActions() {
  const { show } = usePopup();

  const handleActions = async () => {
    const buttonId = await show({
      title: '文档操作',
      message: '请选择要执行的操作',
      buttons: [
        { id: 'download', type: 'default', text: '下载' },
        { id: 'share', type: 'default', text: '分享' },
        { id: 'delete', type: 'destructive', text: '删除' },
      ],
    });

    switch (buttonId) {
      case 'download':
        downloadDocument();
        break;
      case 'share':
        shareDocument();
        break;
      case 'delete':
        deleteDocument();
        break;
    }
  };

  return <button onClick={handleActions}>文档操作</button>;
}

处理用户取消

用户可能关闭弹窗而不点击任何按钮:

tsx
import { usePopup } from '@xcloud/ui-telegram';

function SaveChanges() {
  const { show } = usePopup();

  const handleSave = async () => {
    const buttonId = await show({
      title: '保存更改',
      message: '是否保存你的更改?',
      buttons: [
        { id: 'save', type: 'default', text: '保存' },
        { id: 'discard', type: 'destructive', text: '放弃' },
        { id: 'cancel', type: 'cancel', text: '取消' },
      ],
    });

    if (!buttonId) {
      // 用户关闭了弹窗
      console.log('User closed popup without selecting');
      return;
    }

    if (buttonId === 'save') {
      await saveChanges();
    } else if (buttonId === 'discard') {
      discardChanges();
    }
    // cancel 不做任何操作
  };

  return <button onClick={handleSave}>保存</button>;
}

错误提示

显示错误信息并记录日志:

tsx
import { usePopup } from '@xcloud/ui-telegram';

function FormSubmit() {
  const { alert } = usePopup();

  const handleSubmit = async (data) => {
    try {
      await submitForm(data);
    } catch (error) {
      await alert({
        title: '提交失败',
        message: error.message || '请稍后重试',
      });
      console.error('Form submission failed:', error);
    }
  };

  return <form onSubmit={handleSubmit}>...</form>;
}

长文本消息

显示较长的文本内容:

tsx
import { usePopup } from '@xcloud/ui-telegram';

function TermsOfService() {
  const { show } = usePopup();

  const showTerms = async () => {
    const accepted = await show({
      title: '使用条款',
      message: `
欢迎使用我们的服务。使用本服务即表示您同意以下条款:

1. 您必须遵守所有适用的法律法规
2. 不得滥用服务或干扰其他用户
3. 我们保留随时修改服务的权利
4. 您的数据将根据隐私政策处理

请仔细阅读并理解这些条款。
      `.trim(),
      buttons: [
        { id: 'accept', type: 'default', text: '接受' },
        { id: 'decline', type: 'destructive', text: '拒绝' },
      ],
    });

    return accepted === 'accept';
  };

  return <button onClick={showTerms}>查看条款</button>;
}

支付确认

在支付前确认订单:

tsx
import { usePopup } from '@xcloud/ui-telegram';

function CheckoutButton({ total, items }: { total: number; items: number }) {
  const { show } = usePopup();

  const handleCheckout = async () => {
    const confirmed = await show({
      title: '确认订单',
      message: `你将支付 $${total.toFixed(2)} 购买 ${items} 件商品。`,
      buttons: [
        { id: 'pay', type: 'default', text: '确认支付' },
        { id: 'cancel', type: 'cancel', text: '取消' },
      ],
    });

    if (confirmed === 'pay') {
      // 跳转到支付页面
      proceedToPayment();
    }
  };

  return (
    <button onClick={handleCheckout}>
      结账 (${total.toFixed(2)})
    </button>
  );
}

评分请求

请求用户评分:

tsx
import { usePopup } from '@xcloud/ui-telegram';

function RatingPrompt() {
  const { show } = usePopup();

  const requestRating = async () => {
    const response = await show({
      title: '喜欢这个应用吗?',
      message: '如果你喜欢这个应用,请给我们评分!',
      buttons: [
        { id: 'rate', type: 'default', text: '去评分' },
        { id: 'later', type: 'default', text: '稍后提醒' },
        { id: 'never', type: 'cancel', text: '不再提醒' },
      ],
    });

    if (response === 'rate') {
      openRatingPage();
    } else if (response === 'later') {
      scheduleReminderLater();
    } else if (response === 'never') {
      disableRatingPrompts();
    }
  };

  return <button onClick={requestRating}>评分</button>;
}

检查可用性

在非 Telegram 环境中降级处理:

tsx
import { usePopup } from '@xcloud/ui-telegram';

function NotificationButton() {
  const { show, isAvailable } = usePopup();

  const notify = async (message: string) => {
    if (isAvailable()) {
      await show({
        title: '通知',
        message,
        buttons: [{ id: 'ok', type: 'default', text: '确定' }],
      });
    } else {
      // 降级到浏览器 alert
      alert(message);
    }
  };

  return <button onClick={() => notify('Hello!')}>通知</button>;
}

按钮类型说明

default

  • 默认样式的按钮
  • 通常用于确认、继续等正面操作
  • 蓝色外观(遵循 Telegram 主题)

destructive

  • 破坏性操作按钮
  • 用于删除、移除等不可逆操作
  • 红色外观,警示作用

cancel

  • 取消按钮
  • 用于取消操作、返回等
  • 灰色外观,较低优先级

最佳实践

1. 限制按钮数量

Telegram 原生弹窗最多支持 3 个按钮。建议:

  • 1 个按钮:确认信息
  • 2 个按钮:确认/取消
  • 3 个按钮:主操作/次要操作/取消

2. 合理使用按钮类型

tsx
// ✅ 好的做法
await show({
  title: '删除确认',
  message: '确定要删除吗?',
  buttons: [
    { id: 'delete', type: 'destructive', text: '删除' },
    { id: 'cancel', type: 'cancel', text: '取消' },
  ],
});

// ❌ 不好的做法
await show({
  title: '删除确认',
  message: '确定要删除吗?',
  buttons: [
    { id: 'delete', type: 'default', text: '删除' }, // 应该用 destructive
    { id: 'cancel', type: 'default', text: '取消' }, // 应该用 cancel
  ],
});

3. 提供清晰的消息

消息应该简洁明了,告诉用户发生了什么或需要做什么:

tsx
// ✅ 好的做法
await alert({
  title: '上传成功',
  message: '你的文件已成功上传到云端',
});

// ❌ 不好的做法
await alert({
  title: '成功',
  message: '操作完成', // 太模糊
});

4. 使用快捷方法

对于简单的确认和提示,使用 confirm()alert()

tsx
// ✅ 使用快捷方法
const confirmed = await confirm({
  title: '确认',
  message: '确定要继续吗?',
});

// ❌ 不必要的复杂代码
const buttonId = await show({
  title: '确认',
  message: '确定要继续吗?',
  buttons: [
    { id: 'ok', type: 'default', text: '确定' },
    { id: 'cancel', type: 'cancel', text: '取消' },
  ],
});
const confirmed = buttonId === 'ok';

5. 处理关闭情况

始终处理用户关闭弹窗的情况:

tsx
const buttonId = await show({
  title: '选择操作',
  message: '请选择',
  buttons: [...],
});

if (!buttonId) {
  // 用户关闭了弹窗,不做任何操作
  return;
}

// 处理用户选择

注意事项

  • 弹窗是模态的,会阻塞用户交互直到关闭
  • 在 Web 版 Telegram 中,弹窗可能降级为浏览器原生 alert/confirm
  • 按钮文本应简短,避免过长导致显示问题
  • 消息内容支持换行符 \n
  • 不要在弹窗中放置过多信息,保持简洁
  • 弹窗会自动适配 Telegram 的主题颜色

相关 Hooks

基于 MIT 许可发布