两个人以上的对话在 Korat 里发生在群组,因为聊天本身只有一对一。它有两种形态。大部分是帖子:群组帖子就是 posts 表里一行带着社区 id 的普通记录,所以评论、提及、通知和搜索都原封不动地成立,不必并行再建一遍。此外,群组还可以有子房间——列在群组内部的 Discord 式文字频道,有自己的权限和自己的已读状态。子房间是刻意做薄的,下面的提示框写明了薄到什么程度。
群组帖子就是带地址的信息流帖子
这是理解 Korat 群组最要紧的一件事。你往群里发帖时,创建的和你发在自己信息流上的是完全同一个对象:同样的表情回应、同样的评论、同样能解析到真实账号的 @提及、同样的编辑与删除、同样的通知管线。不存在一套缺胳膊少腿的二等群评论系统。
群 id 唯一改变的是帖子出现在哪里。群组帖子在代码里恰好一个位置被过滤出主信息流,因此留在群内,不会渗进每一个恰好和某个成员是好友的人的时间线。一个位置、一条过滤规则,也是行为一致的原因——不存在第二条排除规则在别处慢慢与第一条产生分歧。
实际的回报是:Korat 的群组一上来就和信息流一样强,改进同时落在两边。代价写在下面,也很直白:群组的子房间不是消息模块,群组耐久的那一半是它的帖子板。
群组的子房间不是消息模块。私聊只有一对一,也无法变成群组。群组的子房间只承载纯文字,能发、能删自己发的——不能编辑消息、不能针对某条消息回复、不能用表情回应、不能发图片、文件或语音消息,也不能通话。
所以五个人实时你来我往是可以的,在子房间里,用纯文字。做不到的是让这种你来我往带上私聊才有的附件、表情回应和通话。群组耐久、可搜索、可评论的那一半是它的帖子板,带真实角色和真实入群流程。
公开与封闭,以及封闭到底意味着什么
创建群组时你要设定名称、简介、分类、公开或封闭的隐私设置,以及一段群规。群规字段不是装饰:一个没有写下规则的群最终还是会有规则,由当天负责管理的那个人临时发明,而成员是通过违反它才知道有这么一条。
封闭群使用入群申请:有人提出申请,群主或管理员批准或拒绝;申请人也可以撤回待处理的申请,而不是卡在一个不会到来的决定上。两侧都会收到通知——管理员收到申请到达,申请人收到批准结果。申请石沉大海是所有群组系统最常见的失败,而批准时发一条通知是最便宜的修法。
群的状态在你点之前就摆出来,而不是点完才知道:封闭群的卡片上带一把锁,所以“加入”和“申请加入”的差别在你还在犹豫时就看得见——按钮绝不能承诺下一屏会拒绝的事。
四个分区,而不是一个列表加徽章
群组页被拆成带计数的标题分区,而不是用一个小徽章去表达你和这个群的关系:我管理的群、我加入的群、待处理申请和发现,另加一个横跨全部的搜索。
它最初只是一个“我的群组”列表,后来重做了,因为这四行意思完全不同。你拥有的群有设置,还有一个等着你的申请队列;你只是提交过申请的群什么都还没有。把它们分进带计数的标题分区,一眼就能回答“有没有需要我处理的事”,而埋在第三十行的一个徽章回答不了。
角色,以及把群交出去
有三种角色——群主、管理员和成员——以徽章形式显示在名字旁边。群主或管理员可以改变成员角色、移除成员、编辑群设置或删除群。还有群主转让,它比听上去重要:一个群主已经不再用这个应用、又交不出钥匙的群,会带着一屏锁死的设置慢慢死掉。
Korat 的规矩是:后端会拒绝的动作,界面上就不该提供。群主看不到“退出群组”这一项,因为退出会让群变成孤儿;想脱身的群主的路径是先转让。破坏性操作——删除群、移除成员——都在确认框后面,而确认框会同时明说什么*不会*发生,因为人们犹豫,多半是因为默认设想了最坏的结果。
对应决定的通知
群组发四种通知,每一种都对应一个有人需要知道点什么的时刻:你所在群里的新帖子、送到管理员那里的入群申请、你的申请被批准,以及你的角色变更。
第一种早期是缺失的,结果是群看起来像被废弃了——成员根本无从得知有人发过东西。同样的失败模式后来又在店铺订单上出现了一次。一个产出内容却不告诉任何人的功能不是安静,是坏的。
把群分享出去
群可以通过 Korat 其余部分共用的那套不透明链接分享出去:一条 koratland.com/s/<token> 形式的 URL,令牌是 14 位随机字符,在应用内解析。解析器只返回令牌指向的对象类型和具体是哪一个;它从不透露是谁创建的分享,也不透露被打开过多少次。所以一条公开张贴的招募链接,不携带任何关于张贴者的信息。
群里发的帖子本身就是动态里的帖子,只是多带一个群 id;要带成员排行榜的跑团或球队,用运动俱乐部——两者的子房间是同一套,差别在排行榜。