修复全页面走查发现的问题(第三批:转发、i18n、自聊天显示名)
第4章 批量转发
- 确认弹层里那栏留言是个只读 <div>,需求 4.19 与对接文档要求的"附加留言"根本没法填,
comment 永远是空串。改成真实输入框(方括号那段是占位文案不是内容),并补 input 的样式。
- 无条数上限:服务端硬卡 sources ≤ 100(im_http.go:3560),超了直接 400 MALFORMED_REQUEST,
用户只看到一句无从下手的"转发失败"。本地先拦并说清超了多少。
- 无目标数上限:bootstrap 下发 limits.max_forward_targets=9、服务端硬限 20,前端两个都不读。
读各视图已在写的同一份 bootstrap 缓存,不额外发请求;选择视图也补上错误显示。
i18n(这几条影响真实页面,不只是演示页)
- menu_item_forward "重定向" → "转发",forward_to "重定向消息" → "转发给"。Forward 被当网络
术语直译了,第4章通篇和参考图用的都是"转发"。
- last_seen_timestamp 中英两侧都丢了 {timestamp} 占位符,离线时头部只显示"最后可见"没有时间。
- peers_messaging_disabled "成员间消息已禁用" → "对方已关闭消息"(原文是 Peer's)。
- search_no_results "搜索返回任何结果" 缺"无";icon_title_send "发信息" → "发送消息"。
- 已跑 build:i18n 同步 src/i18n.min/(那份才是打包进产物的)。
自聊天显示名
- topicDisplayName 不处理 slf,消息首页把"已保存的消息"显示成"未命名会话"。该显示名靠
Tinode.isSelfTopicName 特判得出、不在 public 里。集中到这个共享函数,所有调用点都受益。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
02b98cceae
commit
c9b8125013
+74
-29
@@ -4762,6 +4762,7 @@ function topicDisplayName(topicName, publicData, fallback) {
|
||||
const fn = publicData && publicData.fn;
|
||||
if (fn) return fn;
|
||||
if (publicData && tinode_sdk__WEBPACK_IMPORTED_MODULE_0__.Tinode.isGroupTopicName(topicName) && publicData.name) return publicData.name;
|
||||
if (tinode_sdk__WEBPACK_IMPORTED_MODULE_0__.Tinode.isSelfTopicName(topicName)) return '已保存的消息';
|
||||
return fallback;
|
||||
}
|
||||
function updateFavicon(count) {
|
||||
@@ -22186,7 +22187,8 @@ __webpack_require__.r(__webpack_exports__);
|
||||
/* harmony import */ var _search_contacts_jsx__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./search-contacts.jsx */ "./src/widgets/search-contacts.jsx");
|
||||
/* harmony import */ var _lib_im_api_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../lib/im-api.js */ "./src/lib/im-api.js");
|
||||
/* harmony import */ var _lib_utils_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../lib/utils.js */ "./src/lib/utils.js");
|
||||
/* harmony import */ var react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! react/jsx-dev-runtime */ "./node_modules/react/jsx-dev-runtime.js");
|
||||
/* harmony import */ var _lib_use_cached_data_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../lib/use-cached-data.js */ "./src/lib/use-cached-data.js");
|
||||
/* harmony import */ var react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! react/jsx-dev-runtime */ "./node_modules/react/jsx-dev-runtime.js");
|
||||
|
||||
|
||||
|
||||
@@ -22194,6 +22196,9 @@ __webpack_require__.r(__webpack_exports__);
|
||||
|
||||
|
||||
|
||||
|
||||
const MAX_FORWARD_SOURCES = 100;
|
||||
const MAX_FORWARD_TARGETS = 20;
|
||||
class BatchForwardDialog extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component) {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -22202,6 +22207,7 @@ class BatchForwardDialog extends (react__WEBPACK_IMPORTED_MODULE_0___default().C
|
||||
multiSelect: false,
|
||||
selectedTargets: [],
|
||||
confirmTargets: null,
|
||||
comment: '',
|
||||
sending: false,
|
||||
error: null
|
||||
};
|
||||
@@ -22213,6 +22219,7 @@ class BatchForwardDialog extends (react__WEBPACK_IMPORTED_MODULE_0___default().C
|
||||
this.handleConfirmTargets = this.handleConfirmTargets.bind(this);
|
||||
this.handleCancelConfirm = this.handleCancelConfirm.bind(this);
|
||||
this.handleSend = this.handleSend.bind(this);
|
||||
this.handleCommentChange = this.handleCommentChange.bind(this);
|
||||
}
|
||||
componentDidMount() {
|
||||
this.props.onInitFind();
|
||||
@@ -22250,9 +22257,29 @@ class BatchForwardDialog extends (react__WEBPACK_IMPORTED_MODULE_0___default().C
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.setState(s => ({
|
||||
selectedTargets: s.selectedTargets.includes(uid) ? s.selectedTargets.filter(t => t != uid) : s.selectedTargets.concat(uid)
|
||||
}));
|
||||
this.setState(s => {
|
||||
if (s.selectedTargets.includes(uid)) {
|
||||
return {
|
||||
selectedTargets: s.selectedTargets.filter(t => t != uid),
|
||||
error: null
|
||||
};
|
||||
}
|
||||
const max = this.forwardTargetLimit();
|
||||
if (s.selectedTargets.length >= max) {
|
||||
return {
|
||||
error: `最多同时转发给 ${max} 个会话`
|
||||
};
|
||||
}
|
||||
return {
|
||||
selectedTargets: s.selectedTargets.concat(uid),
|
||||
error: null
|
||||
};
|
||||
});
|
||||
}
|
||||
forwardTargetLimit() {
|
||||
const boot = this.props.myUserId ? (0,_lib_use_cached_data_js__WEBPACK_IMPORTED_MODULE_6__.readCachedData)('bootstrap:' + this.props.myUserId) : null;
|
||||
const configured = boot && boot.limits && Number(boot.limits.max_forward_targets);
|
||||
return configured > 0 ? Math.min(configured, MAX_FORWARD_TARGETS) : MAX_FORWARD_TARGETS;
|
||||
}
|
||||
handleConfirmTargets(e) {
|
||||
e.preventDefault();
|
||||
@@ -22268,16 +22295,27 @@ class BatchForwardDialog extends (react__WEBPACK_IMPORTED_MODULE_0___default().C
|
||||
error: null
|
||||
});
|
||||
}
|
||||
handleCommentChange(e) {
|
||||
this.setState({
|
||||
comment: e.target.value
|
||||
});
|
||||
}
|
||||
handleSend() {
|
||||
const sources = this.props.seqs.map(seq => ({
|
||||
topic: this.props.topicName,
|
||||
seq: String(seq)
|
||||
}));
|
||||
if (sources.length > MAX_FORWARD_SOURCES) {
|
||||
this.setState({
|
||||
error: `一次最多转发 ${MAX_FORWARD_SOURCES} 条消息,当前已选 ${sources.length} 条`
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
sending: true,
|
||||
error: null
|
||||
});
|
||||
(0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_4__.forwardMessages)('separate', sources, this.state.confirmTargets).then(result => {
|
||||
(0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_4__.forwardMessages)('separate', sources, this.state.confirmTargets, this.state.comment.trim()).then(result => {
|
||||
const failed = result && result.failed || [];
|
||||
if (failed.length) {
|
||||
this.setState({
|
||||
@@ -22300,38 +22338,42 @@ class BatchForwardDialog extends (react__WEBPACK_IMPORTED_MODULE_0___default().C
|
||||
name: uid,
|
||||
public: null
|
||||
});
|
||||
return (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("div", {
|
||||
return (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||||
className: "alert-container",
|
||||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("div", {
|
||||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||||
className: "forward-dialog batch-forward-confirm",
|
||||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("div", {
|
||||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||||
className: "title",
|
||||
children: "发送给"
|
||||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("ul", {
|
||||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("ul", {
|
||||
className: "contact-box",
|
||||
children: targets.map(t => (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("li", {
|
||||
children: targets.map(t => (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("li", {
|
||||
className: "batch-forward-target",
|
||||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("span", {
|
||||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("span", {
|
||||
children: tinode_sdk__WEBPACK_IMPORTED_MODULE_1__.Tinode.isSelfTopicName(t.name) ? '已保存的消息' : (0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_5__.topicDisplayName)(t.name, t.public, t.name)
|
||||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("i", {
|
||||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("i", {
|
||||
className: "material-icons",
|
||||
children: "chevron_right"
|
||||
}, void 0, false)]
|
||||
}, t.name, true))
|
||||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("div", {
|
||||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("input", {
|
||||
className: "batch-forward-comment",
|
||||
children: '[逐条转发]共' + this.props.seqs.length + '条消息'
|
||||
}, void 0, false), this.state.error ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("div", {
|
||||
type: "text",
|
||||
value: this.state.comment,
|
||||
onChange: this.handleCommentChange,
|
||||
disabled: this.state.sending,
|
||||
placeholder: '[逐条转发]共' + this.props.seqs.length + '条消息'
|
||||
}, void 0, false), this.state.error ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||||
className: "auth-field-error",
|
||||
children: this.state.error
|
||||
}, void 0, false) : null, (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("div", {
|
||||
}, void 0, false) : null, (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||||
className: "batch-forward-actions",
|
||||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("button", {
|
||||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("button", {
|
||||
type: "button",
|
||||
onClick: this.handleCancelConfirm,
|
||||
disabled: this.state.sending,
|
||||
children: "取消"
|
||||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("button", {
|
||||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("button", {
|
||||
type: "button",
|
||||
className: "primary",
|
||||
onClick: this.handleSend,
|
||||
@@ -22344,37 +22386,40 @@ class BatchForwardDialog extends (react__WEBPACK_IMPORTED_MODULE_0___default().C
|
||||
}
|
||||
let contacts = this.state.query != null ? this.props.searchResults : this.props.contacts;
|
||||
contacts = contacts.filter(c => c.name != this.props.topicSelected && c.acs.isJoiner() && c.acs.isWriter());
|
||||
return (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("div", {
|
||||
return (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||||
className: "alert-container",
|
||||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("div", {
|
||||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||||
className: "forward-dialog",
|
||||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("div", {
|
||||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||||
className: "title with-control",
|
||||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("div", {
|
||||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||||
children: "转发"
|
||||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("div", {
|
||||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("a", {
|
||||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("a", {
|
||||
href: "#",
|
||||
onClick: this.handleToggleMultiSelect,
|
||||
children: this.state.multiSelect ? '' : '多选'
|
||||
}, void 0, false), this.state.multiSelect ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("a", {
|
||||
}, void 0, false), this.state.multiSelect ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("a", {
|
||||
href: "#",
|
||||
onClick: this.handleConfirmTargets,
|
||||
className: this.state.selectedTargets.length ? '' : 'disabled',
|
||||
children: "确定"
|
||||
}, void 0, false) : null, (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("a", {
|
||||
}, void 0, false) : null, (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("a", {
|
||||
href: "#",
|
||||
onClick: this.handleClose,
|
||||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("i", {
|
||||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("i", {
|
||||
className: "material-icons",
|
||||
children: "close"
|
||||
}, void 0, false)
|
||||
}, void 0, false)]
|
||||
}, void 0, true)]
|
||||
}, void 0, true), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)(_search_contacts_jsx__WEBPACK_IMPORTED_MODULE_3__["default"], {
|
||||
}, void 0, true), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)(_search_contacts_jsx__WEBPACK_IMPORTED_MODULE_3__["default"], {
|
||||
placeholder: "搜索联系人",
|
||||
onSearchContacts: this.handleSearchContacts
|
||||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)(_contact_list_jsx__WEBPACK_IMPORTED_MODULE_2__["default"], {
|
||||
}, void 0, false), this.state.error ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)("div", {
|
||||
className: "auth-field-error",
|
||||
children: this.state.error
|
||||
}, void 0, false) : null, (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_7__.jsxDEV)(_contact_list_jsx__WEBPACK_IMPORTED_MODULE_2__["default"], {
|
||||
tinode: this.props.tinode,
|
||||
contacts: contacts,
|
||||
myUserId: this.props.myUserId,
|
||||
|
||||
Reference in New Issue
Block a user