收藏与分享

在回答下方加「收藏」按钮,在会话列表里给会话加「分享」和收藏标记。本页用插槽和会话列表自己的两个位置实现它。

recipe-favorites.tsx
import { useDemoSessions } from '../sessions';

import { Chatkit, SessionList, useCurrentSession } from '@volcengine/chatkit';
import { createMockAdapter } from '@volcengine/chatkit/adapters/mock';
import '@volcengine/chatkit/styles.css';
import { useState } from 'react';

const adapter = createMockAdapter();

// 回答操作栏里的「收藏」:用公开 hook 拿到当前会话,收藏时记下这条消息属于哪个会话
function FavoriteButton({ active, onToggle }: { active: boolean; onToggle: (sessionId: string) => void }) {
  const { session } = useCurrentSession();
  return (
    <button type="button" className="demo-chip" aria-pressed={active} onClick={() => session && onToggle(session.id)}>
      {active ? '★ 已收藏' : '☆ 收藏'}
    </button>
  );
}

// 收藏与分享只用插槽:回答下方的按钮放在 message.actions,
// 会话列表每一行的「分享会话」和收藏标记放在 SessionList 自己的两个位置 itemActions、itemBadge。
export default function RecipeFavorites() {
  const { chat: sessions, listProps } = useDemoSessions({ adapter });
  // 业务方自己的状态:消息 id → 所在的会话 id
  const [favorites, setFavorites] = useState<Record<string, string>>({});
  const toggle = (messageId: string, sessionId: string) =>
    setFavorites(({ [messageId]: existing, ...rest }) => (existing ? rest : { ...rest, [messageId]: sessionId }));
  return (
    <div className="demo-row">
      <SessionList
        {...listProps}
        style={{ width: 200 }}
        itemBadge={({ session }) => (Object.values(favorites).includes(session.id) ? <span>★</span> : null)}
        itemActions={({ session }) => (
          <button type="button" onClick={() => window.alert(`分享链接:https://example.com/s/${session.id}`)}>
            分享会话
          </button>
        )}
      />
      <div className="demo-main">
        <Chatkit
          sessions={sessions}
          slots={{
            'message.actions': ({ message }) => (
              <FavoriteButton active={message.id in favorites} onToggle={sessionId => toggle(message.id, sessionId)} />
            ),
          }}
        />
      </div>
    </div>
  );
}
点回答下方的「收藏」,左侧这个会话的标题后出现标记;每一行的菜单里有「分享会话」新窗口打开

在线示例只在 React 状态中记录收藏,刷新后丢失;「分享会话」弹出 example.com 占位地址,不会创建可访问的分享内容。下面的 useFavorites、分享按钮和存储接口由业务应用实现。

这个功能是什么

  • 每条回答的操作栏里,在内置按钮之后多出「收藏」。
  • 会话列表每一行的操作菜单里有「分享会话」;有收藏内容的会话标题后显示一个标记。

用到的入口

入口用来做什么
slots['message.actions']回答操作栏里的收藏
SessionList 的 itemActions会话列表每一行的分享
SessionList 的 itemBadge会话标题后的收藏标记
useCurrentSession收藏时知道消息属于哪个会话

代码

import { Chatkit, SessionList, useChatSessions, useCurrentSession } from '@volcengine/chatkit';

function FavoriteAction({ messageId, favorites }: { messageId: string; favorites: ReturnType<typeof useFavorites> }) {
  const { session } = useCurrentSession();
  if (!session) return null;
  return (
    <FavoriteButton
      active={favorites.has(session.id, messageId)}
      onToggle={() => favorites.toggle(session.id, messageId)}
    />
  );
}

export function App() {
  const sessions = useChatSessions({ adapter, activeSessionId });
  const favorites = useFavorites(); // 业务方自己的状态:收藏了哪些消息

  return (
    <div style={{ display: 'flex', height: '100vh' }}>
      <SessionList
        sessions={sessions}
        itemActions={({ session }) => <ShareSessionButton sessionId={session.id} />}
        itemBadge={({ session }) => (favorites.hasInSession(session.id) ? <StarIcon /> : null)}
      />
      <div style={{ flex: 1, minWidth: 0 }}>
        <Chatkit
          sessions={sessions}
          slots={{
            'message.actions': ({ message, run }) =>
              run?.status === 'completed' ? (
                <>
                  <FavoriteAction messageId={message.id} favorites={favorites} />
                  <ShareButton messageId={message.id} />
                </>
              ) : null,
          }}
        />
      </div>
    </div>
  );
}

message.actions 只对回答生效。上下文里的 run 是这条回答所属那一轮的运行信息,可以用它只在完成的回答上显示按钮。

上例有意限制为 run.status === 'completed';没有运行记录的历史回答也会被隐藏,即使正文完整。需要支持这类历史时,应按业务允许的消息状态决定是否显示,不能把缺少 run 等同于失败。收藏记录建议使用会话 id 与消息 id 共同标识,并处理编辑或重新生成后原消息失效的情况。

变化

  • 分享选中的一段文字:用 message.selection,见划词追问。
  • 收藏夹页面:在页面别处显示收藏列表,点一条用 sessions.actions.select(id) 切到对应会话。
  • 回答下方再加「分享」:在 message.actions 里多放一个按钮。
  • 按钮和内置的样子一致:使用主题 token,见主题。

为什么不内置

收藏存在哪里、分享链接怎么生成、谁能看到分享的内容,都需要业务方和自己的后端约定。 复制、赞踩不需要后端配合(或只需要一个回调),所以内置;收藏与分享不是。