diff --git a/css/base.css b/css/base.css
index 3c188cad..2e58493f 100644
--- a/css/base.css
+++ b/css/base.css
@@ -4647,6 +4647,17 @@ video {
user-select: none;
}
+/* 群系统事件(成员加入/角色变更/公告发布等),docs/images/内容沟通对话3-开启进群验证与拉人状态.png
+ 显示为不带背景的纯居中灰字,跟需要背景色块区分"这是一条独立提示"的 date/deleted 两种 .meta
+ 变体不一样——公告/进群这类事件本身已经在句子里说明了是系统提示,不需要额外的气泡背景。 */
+.meta .bubble.system {
+ max-width: 90%;
+ padding: 0.3rem 0.5rem;
+ background-color: transparent;
+ font-size: 0.8rem;
+ text-align: center;
+}
+
/* Left triangle placed bottom left flush. */
.left .bubble.tip::before {
content: '';
diff --git a/src/views/content-library-view.jsx b/src/views/content-library-view.jsx
index 835ca80f..a020c34a 100644
--- a/src/views/content-library-view.jsx
+++ b/src/views/content-library-view.jsx
@@ -266,10 +266,17 @@ function FavoriteItem({item, onOpen, selecting, selected, onSelect, query, onToa
const TYPE_ROUTE = {all: 'favorites-mixed', file: 'favorites-files', text: 'favorites-text', voice: 'favorites-voice', link: 'favorites-links', image: 'favorites-image', video: 'favorites-video'};
const TYPE_LABELS = [['all', '全部'], ['image', '图片'], ['video', '视频'], ['file', '文件'], ['voice', '语音'], ['text', '文本'], ['link', '链接']];
-function Favorites({view, tinode, onToast}) {
+function Favorites({view, hashParams, tinode, onToast}) {
const initialType = view === 'favorites-files' ? 'file' : view === 'favorites-text' ? 'text' : view === 'favorites-voice' ? 'voice' : view === 'favorites-links' ? 'link' : view === 'favorites-image' ? 'image' : view === 'favorites-video' ? 'video' : 'all';
const selecting = view === 'favorites-select';
- const [type, setType] = React.useState(selecting ? 'file' : initialType);
+ // 'favorites-select' 是所有类型 tab 共用的同一个路由,本身不带任何类型信息——原来不管进入
+ // 多选前停在哪个 tab,一律硬编码回退成 'file'。实测复现:在"文本"tab 点"多选",列表和头部
+ // 的"已选择 N 项"计数都静默换成了"文件"tab 的数据(tab 高亮也跟着跳到"文件"),用户在视觉上
+ // 完全看不出发生了什么,勾选/删除会作用在自己压根没看到、以为还是文本收藏的文件项上——测试
+ // 过程中真的因为这个把一条本不该删的文件收藏删掉了。现在"多选"按钮把当前 type 作为 hash 参数
+ // 带过去,这里读回来,保留进入多选前用户实际所在的 tab;只有直接深链到 'favorites-select'
+ // 且没带 type 参数时才落回原来的 'file' 默认值。
+ const [type, setType] = React.useState(selecting ? ((hashParams && hashParams.type) || 'file') : initialType);
// 输入值与实际发请求用的值分开:原来 query 直接进 load 的依赖数组,输入 6 个字的关键词就打
// 6 次接口,而且返回顺序没有保证——后发的短前缀请求可能后到、把正确结果覆盖掉。
const [queryInput, setQueryInput] = React.useState('');
@@ -359,11 +366,19 @@ function Favorites({view, tinode, onToast}) {
};
const list = items || [];
return
-
navigate(selecting ? 'favorites-files' : 'favorites-select')} />
+ {/* "返回"/"取消"都要回到多选前那个类型 tab,而不是硬编码的"文件"——TYPE_ROUTE 查不到时
+ (比如 type 还是初始的 'all') 才退回 'favorites-files',跟原来的兜底保持一致。 */}
+ navigate(selecting ? (TYPE_ROUTE[type] || 'favorites-files') : 'favorites-select', selecting ? undefined : {type})} />
+ // 多选模式下切 tab 之前,selected 存的是"收藏 id 的集合",不带类型信息——原来只切 type、
+ // 不清 selected,导致在"文件"tab 选中 2 项后切到"图片"tab,头部仍显示"已选择 2 项"、点
+ // 删除/转发会真的作用在两个已经从当前列表消失、用户此刻根本看不到也无法勾选/取消的文件上,
+ // 且没有任何提示告诉用户这两项还算在内。多选场景下天然只该管"当前这个列表里勾了什么",
+ // 跨 tab 静默累加选择集不是这里的预期行为(对照参考图"收藏—文件(多选)"也只画了单一
+ // 类型列表内的勾选,没有跨 tab 累加的设计)。
+ onClick={() => { setType(id); setSelected([]); if (TYPE_ROUTE[id] && !selecting) navigateReplace(TYPE_ROUTE[id]); }}>{label})}
{items === null ? 加载中…
:
list.length ? <>{list.map(item => 0 && forTarget.every(r => r.status === 'sent');
}).length;
onToast(successTargets === selected.length ? `已分享给 ${selected.length} 个会话` : `${successTargets}/${selected.length} 个会话分享成功`);
- window.setTimeout(() => navigate('favorites-files'), 700);
+ // 发完之后回到分享前那个类型 tab,而不是硬编码的"文件"——preview 是这批收藏里第一条的详情,
+ // 拿它的 type 就够找回对应 tab;preview 还没加载完时退回原来的 'favorites-files' 兜底。
+ window.setTimeout(() => navigate(TYPE_ROUTE[preview && preview.type] || 'favorites-files'), 700);
}).catch(err => onToast(err.message || '分享失败')).finally(() => setSending(false));
};
const previewAtt = preview && firstAttachment(preview.snapshot && preview.snapshot.content);
@@ -418,7 +435,10 @@ function FavoriteShare({hashParams, myUserId, chatList, onToast}) {
(!query || topicDisplayName(c.name, c.public, c.name || '').toLowerCase().includes(query.toLowerCase())));
const nameOf = uid => { const t = contacts.find(c => c.name === uid); return (t && topicDisplayName(t.name, t.public, uid)) || uid; };
return
-
+ {/* 同上一条注释——"favorites-select" 不带类型信息,点这里的返回箭头原来会静默把多选列表
+ 换成硬编码的"文件"tab。preview.type 是这批收藏第一条的真实类型,带回去能让返回箭头落回
+ 分享前实际所在的 tab。 */}
+
{!confirming ? <>
最近聊天
@@ -718,7 +738,7 @@ function History({view, hashParams, myUserId, onToast}) {
export default function ContentLibraryView({view, hashParams, tinode, myUserId, onTopicSelected, chatList}) {
const [toast, setToast] = React.useState(''); let content;
- if (view.startsWith('favorites')) content =
;
+ if (view.startsWith('favorites')) content =
;
else if (view === 'favorite-share') content =
;
else if (view.startsWith('file-download')) content =
;
else content =
;