665. PotatoChat手机版通知分组

你是想实现/优化 PotatoChat 手机版的“通知分组”功能吧?先确认一下你需要哪一部分内容:

665. PotatoChat手机版通知分组

  • 哪个平台:Android、iOS 还是两者都要?
  • 想要实现的粒度:按会话分组、按联系人分组,还是按群组/频道分组?
  • 需要服务端推送示例、客户端代码示例,还是产品/交互设计建议?

下面先给一个精炼的端到端实现思路(包含关键要点、示例和注意事项),你可以按需要让我展开某个平台的完整代码。

目标与设计原则(建议)

  • 按会话(conversation)分组:每个会话一组,便于用户快速定位未读消息来源。
  • 组内合并:当有多条同一会话消息时只显示展开后的最新几条或一个摘要。
  • 组摘要:当有很多会话未读时首页显示会话摘要(未读数/最新会话发送者与内容)。
  • 支持快速回复、标记已读、免打扰会话设置。
  • 与系统通知兼容(Android Notification Group / iOS thread-id),并兼顾消息样式(短文本预览或 MessagingStyle)。

Android 实现要点(Android 8+ 优先)

  • 使用 NotificationCompat.MessagingStyle 或 NotificationCompat.Style 来渲染消息历史。
  • 对每个会话使用相同的 group key,例如 "potato_chat_conv_{conversationId}",调用 NotificationCompat.Builder.setGroup(groupKey)。
  • 生成组汇总(group summary)通知:单独构建一条 summary 通知,调用 setGroup(groupKey) 和 setGroupSummary(true)。
  • notificationId:单个会话可用固定 id(例如 hash(conversationId))覆盖相同会话的通知;summary 用另一个固定 id(如 -1 或特定范围)。
  • 通知渠道(NotificationChannel)按重要性分级(消息、系统通知等)。
  • 支持 inline reply:添加 RemoteInput 和相应 PendingIntent。
  • FCM 推送:通常把会话 id 等放在 data 负载里,由应用收到后根据 payload 构建通知并设置 setGroup。避免仅依赖 FCM 的 notification 部分(notification 放在 payload 时系统直接构建,难以自定义 group)。

简化 Kotlin 示例(伪码)

  • 单个会话通知(每次收到新消息,应用端构建/更新):
    • groupKey = "potato_chat_conv_$conversationId"
    • builder = NotificationCompat.Builder(context, channelId)
      .setSmallIcon(…)
      .setContentTitle(senderName)
      .setContentText(messageText)
      .setGroup(groupKey)
      .setStyle(MessagingStyle(…).addMessage(…))
    • notify(notificationIdForConversation, builder.build())
  • 构建 summary:
    • summaryBuilder = NotificationCompat.Builder(…).setContentTitle("X 条新消息").setGroup(groupKey).setGroupSummary(true)
    • notify(summaryIdForGroup, summaryBuilder.build())

iOS 实现要点(UNUserNotificationCenter / APNs)

  • 使用 aps.thread-id(或 UNNotificationContent.threadIdentifier)按会话分组:所有来自同一会话的通知设置相同 thread-id,系统会把它们放在同一组里。
  • 使用 category(UNNotificationCategory)以支持交互(快速回复、动作)。
  • APNs 的 collapse-id(apns-collapse-id)用于覆盖或折叠相同会话的旧通知(减少重复);thread-id 用来分组。
  • iOS 12+ 支持“Group Notifications”(在通知中心折叠显示),thread-id 是关键。
  • 如果需要展示富文本或多条消息,可以在 app 内用 UNNotificationServiceExtension 修改通知内容。

APNs payload 示例(伪 JSON)

  • 分组(thread-id)与折叠:
    {
    "aps": {
    "alert": {
    "title": "张三",
    "body": "你好,今天… "
    },
    "sound": "default",
    "badge": 3,
    "thread-id": "conversation_123"
    },
    "conversation_id": "123",
    "sender_id": "u456",
    "custom": { … }
    }
  • 可在 header 使用 apns-collapse-id 来覆盖旧通知。

服务端推送策略与 payload 建议

  • 把会话 id、消息 id、发送者、时间放在 data(FCM)/自定义字段(APNs)中,客户端根据这些字段决定如何合成/更新通知并分组。
  • 避免在服务器端发送大量独立系统 notification(会导致用户收到过多通知)。可以:
    • 合并推送:短时间内把多条消息合并成一条 summary 推送;或
    • 发送每条消息的 data 推送,客户端本地去做 group/更新逻辑(更灵活)。
  • 对于 Android FCM,尽量使用 data-only 消息("data")并让应用在 onMessageReceived 中处理,这样可完全控制 setGroup 等;使用 notification 消息时系统直接展示,分组受限。
  • 对 iOS,APNs 的 thread-id 与 collapse-id 能帮助系统在通知中心分组/折叠。

UX 与边界场景

  • 免打扰(静音)会话:仍记录通知,但不展示或降低优先级/频道。
  • 群组合并策略:群聊可显示群名 + 最近发言人 + 消息摘要。
  • 未读计数:在 summary 或 app 图标上显示未读总数(注意 iOS 用 badge)。
  • 离线/网络中断:客户端收到推送后应可靠保存通知和消息数据,确保用户打开 app 后状态一致。
  • 权限与设置:允许用户在设置里选择按会话、按联系人、或关闭分组显示(或关闭通知)。

测试要点

  • 多设备同时登录:避免重复通知,或在服务端维护最近推送策略(例如只给非活动设备推送)。
  • 不同 Android 版本行为差异(pre-Oreo、Oreo+),测试 NotificationChannel 行为。
  • iOS 不同系统版本的 thread-id 行为(测试 iOS 12+)。
  • 条件测试:免打扰、锁屏、横幅、通知中心、交互操作(回复/标为已读)。

如果你希望我给出:

  • 具体 Android Kotlin 或 Java 的完整通知构建代码示例,或
  • FCM HTTP v1 的 payload 示例(具体到字段),或
  • iOS Swift 的 UNNotification/UNNotificationServiceExtension 示例

请告诉我你需要的平台和更具体的实现目标(例如“按会话分组并支持内联回复,使用 FCM data only 推送”),我把相应完整示例发给你。