补上好友申请前后关系状态聊天页的入口,并把验证留言渲染成真实消息气泡

第3章要求pending_inbound/pending_outbound/none/blocked几种关系状态都能在真实聊天
窗口里看到(参考图 个人对话框(对方的状态)/(我的状态).png、开启沟通页面.png)。
messages-view.jsx里对应的横幅逻辑("以上是打招呼的消息"/"好友申请正在等待处理"等)
本身早就是对的,但排查发现两个问题:

1. 全项目没有任何入口能导航进这个视图——"新朋友"列表(FriendRequests)每一行没有
   onClick;好友个人主页(Profile)非好友分支只显示一段cannotAddReason纯文字,没有
   任何可点击进聊天的按钮。发起方和接收方在成为好友之前都完全看不到这个视图。
2. 验证留言原文(发好友申请时填的那段话,存在im_friend_requests表的message字段,
   不是真实Tinode消息,没有走topic.publish())没有被当作消息渲染出来,参考图里
   明确是作为聊天页第一条消息气泡展示的。

修复:
- contact-management-view.jsx: FriendRequests每一行加onClick,用(onCreateTopic||
  onTopicSelected)跳进对应聊天(同一套Profile.openChat已经验证过的冷启动安全写法,
  接受/拒绝按钮补stopPropagation不受影响);Profile非好友分支(blocked_by_peer/
  blocked_by_me/pending_inbound/pending_outbound)补一个"查看聊天"按钮,跟
  cannotAddReason文字并排展示(不复用people-profile-actions的两列grid,那是给
  发消息+音视频通话两个平级按钮设计的)。ContactManagementView/sidepanel-view.jsx
  早前就已经把onCreateTopic透传到位,这次只是多接一层。
- messages-view.jsx: loadRelationshipContext新增loadPendingRequestMessage,
  relationship是pending_inbound/pending_outbound时,用relationshipContext自带的
  request_id去GET /friend-requests换真实留言文本(没有留言用参考图默认文案"请求
  添加你为好友"兜底),渲染成一条合成的消息气泡插在消息列表最前面(不进
  messageNodes、不调用发消息接口)——pending_inbound左对齐、pending_outbound右
  对齐,气泡样式直接复用chat-message.jsx真实消息已经在用的.bubble/.left/.right
  这套CSS(app里"发出的"气泡实际是这套已实现的浅色而非参考图的蓝色,保持跟同一
  会话里其它真实消息一致,不単独改这一条的颜色去凑旧版参考图)。

验证:真实两个新账号(vcm909641发起→hankqa14b接收)走了一遍完整流程——
从"新朋友"列表点击行正确进入聊天页,留言气泡+"以上是打招呼的消息"横幅正确显示;
从Profile页"查看聊天"进入,留言气泡右对齐+"好友申请正在等待处理"横幅正确显示;
点"通过验证"接受申请后正确留在列表页不误触发导航;接受后重新打开同一会话,
合成气泡正确消失,被真实的"你们已成为好友,现在可以开始聊天了。"系统消息取代,
无残留/无重复。
This commit is contained in:
Your Name
2026-09-06 17:24:36 +08:00
parent e4b0868036
commit ac8f0cf315
8 changed files with 189 additions and 23 deletions
+7
View File
@@ -9392,6 +9392,13 @@ div.toast.show {
border-bottom: 1px solid #eceef1; border-bottom: 1px solid #eceef1;
} }
/* .people-request-list article is now clickable (role="button", opens the real chat -- see
contact-management-view.jsx's FriendRequests) -- the others in the selector above aren't, so this
is its own rule rather than joining the block just above. */
.people-request-list article[role="button"] {
cursor: pointer;
}
.people-request-list p, .people-request-list p,
.group-request-list p { .group-request-list p {
max-width: 24rem; max-width: 24rem;
+1 -1
View File
File diff suppressed because one or more lines are too long
+21 -5
View File
@@ -172,7 +172,7 @@ function requestStatusLabel(item) {
return null; return null;
} }
function FriendRequests({onToast}) { function FriendRequests({onToast, onTopicSelected, onCreateTopic}) {
const [items, setItems] = React.useState([]); const [items, setItems] = React.useState([]);
const [loading, setLoading] = React.useState(true); const [loading, setLoading] = React.useState(true);
const [loadError, setLoadError] = React.useState(false); const [loadError, setLoadError] = React.useState(false);
@@ -189,13 +189,19 @@ function FriendRequests({onToast}) {
onToast(err.message || '操作失败'); onToast(err.message || '操作失败');
}); });
}; };
// 参考图 个人对话框(对方的状态)/(我的状态).png:申请的验证留言应该在真实聊天窗口里以消息
// 气泡展示,横幅提示("以上是打招呼的消息"/"好友申请正在等待处理")已经在 messages-view.jsx
// 里做对了,但这个列表原来每一行完全没有 onClick,点了毫无反应——点进去看这条留言/横幅的入口
// 一直不存在。跟 Profile 的 openChat 用同一个"优先走 onCreateTopic 保证冷启动也能订阅"的写法。
const openChat = item => (onCreateTopic || onTopicSelected) &&
(onCreateTopic || onTopicSelected)(item.direction === 'inbound' ? item.applicant_id : item.target_user_id);
return <div className="people-page"><Header title="新朋友" back="address-book" /><main className="people-request-list"> return <div className="people-page"><Header title="新朋友" back="address-book" /><main className="people-request-list">
{loading ? <p className="people-empty-hint">加载中…</p> : items.length === 0 ? <p className="people-empty-hint">{loadError ? '加载失败,请重试' : '暂无新朋友申请'}</p> : null} {loading ? <p className="people-empty-hint">加载中…</p> : items.length === 0 ? <p className="people-empty-hint">{loadError ? '加载失败,请重试' : '暂无新朋友申请'}</p> : null}
{items.map(item => <article key={item.request_id}> {items.map(item => <article key={item.request_id} role="button" tabIndex={0} onClick={() => openChat(item)}>
<Avatar person={item.peer} /> <Avatar person={item.peer} />
<div><strong>{(item.peer && item.peer.display_name) || item.applicant_id}</strong><p>{item.message || '请求添加您为好友'}</p><small>{new Date(item.created_at).toLocaleString()}</small></div> <div><strong>{(item.peer && item.peer.display_name) || item.applicant_id}</strong><p>{item.message || '请求添加您为好友'}</p><small>{new Date(item.created_at).toLocaleString()}</small></div>
{item.status === 'pending' && item.direction === 'inbound' ? {item.status === 'pending' && item.direction === 'inbound' ?
<span><button type="button" onClick={() => handle(item, false)}>拒绝</button><button type="button" className="primary" onClick={() => handle(item, true)}>通过验证</button></span> : <span><button type="button" onClick={e => { e.stopPropagation(); handle(item, false); }}>拒绝</button><button type="button" className="primary" onClick={e => { e.stopPropagation(); handle(item, true); }}>通过验证</button></span> :
<em>{requestStatusLabel(item)}</em>} <em>{requestStatusLabel(item)}</em>}
</article>)} </article>)}
</main></div>; </main></div>;
@@ -511,7 +517,17 @@ function Profile({uid, friend, onTopicSelected, onCreateTopic, onToast}) {
<button type="button" onClick={openChat}><Icon>call</Icon>音视频通话</button> <button type="button" onClick={openChat}><Icon>call</Icon>音视频通话</button>
</div> : </div> :
canAdd ? <button type="button" className="people-primary" onClick={() => navigate('friend-verification', uid)}><Icon>person_add_alt</Icon>添加好友</button> : canAdd ? <button type="button" className="people-primary" onClick={() => navigate('friend-verification', uid)}><Icon>person_add_alt</Icon>添加好友</button> :
<p className="people-empty-hint">{cannotAddReason}</p>} // cannotAddReason 覆盖的四种关系(blocked_by_peer/blocked_by_me/pending_inbound/
// pending_outbound)在 messages-view.jsx 里都已经有对应的、真实可用的聊天视图(对方发来的
// 留言气泡+"以上是打招呼的消息"横幅,或"好友申请正在等待处理"横幅,或拉黑后仍可输入、发送
// 失败再展示具体原因)——原来这个非好友分支只显示 cannotAddReason 这段纯文字,没有任何
// 入口能点进去看。同一套 openChat(优先 onCreateTopic 保证冷启动)。按钮不套进
// .people-profile-actions 的两列grid(那是给"发消息+音视频通话"两个平级按钮设计的),
// 单独一个满宽按钮,跟上面纯文字提示分两行。
<>
<p className="people-empty-hint">{cannotAddReason}</p>
<button type="button" className="people-primary" onClick={openChat}><Icon>chat_bubble_outline</Icon>查看聊天</button>
</>}
</main> </main>
</div>; </div>;
} }
@@ -674,7 +690,7 @@ export default function ContactManagementView({view, hashParams, myUserId, tinod
const uid = hashParams && hashParams.uid; const uid = hashParams && hashParams.uid;
let content; let content;
if (view === 'address-book') content = <AddressBook myUserId={myUserId} onTopicSelected={onTopicSelected} />; if (view === 'address-book') content = <AddressBook myUserId={myUserId} onTopicSelected={onTopicSelected} />;
else if (view === 'friend-requests') content = <FriendRequests onToast={setToast} />; else if (view === 'friend-requests') content = <FriendRequests onToast={setToast} onTopicSelected={onTopicSelected} onCreateTopic={onCreateTopic} />;
else if (view === 'add-friend' || view === 'friend-search-result') content = <AddFriend result={view === 'friend-search-result'} onToast={setToast} onOpenScanner={onOpenScanner} />; else if (view === 'add-friend' || view === 'friend-search-result') content = <AddFriend result={view === 'friend-search-result'} onToast={setToast} onOpenScanner={onOpenScanner} />;
else if (view === 'my-friend-qr') content = <QrPage onToast={setToast} />; else if (view === 'my-friend-qr') content = <QrPage onToast={setToast} />;
else if (view === 'friend-qr') content = <FriendQr uid={uid} onToast={setToast} />; else if (view === 'friend-qr') content = <FriendQr uid={uid} onToast={setToast} />;
+57 -3
View File
@@ -47,7 +47,7 @@ import { CALL_STATE_OUTGOING_INITATED, CALL_STATE_IN_PROGRESS } from '../constan
import { blobToBase64, fileToBase64, imageScaled, importVCard, makeImageUrl } from '../lib/blob-helpers.js'; import { blobToBase64, fileToBase64, imageScaled, importVCard, makeImageUrl } from '../lib/blob-helpers.js';
import { topicDisplayName } from '../lib/utils.js'; import { topicDisplayName } from '../lib/utils.js';
import { acceptGroupInvitation, checkURL, confirmRiskChallenge, createChatUploader, favoriteMessages, getBootstrap, import { acceptGroupInvitation, checkURL, confirmRiskChallenge, createChatUploader, favoriteMessages, getBootstrap,
getConversationContext, getFavorites, getGroupJoinRequests, getGroupMembers, getLatestGroupAnnouncement, getConversationContext, getFavorites, getFriendRequests, getGroupJoinRequests, getGroupMembers, getLatestGroupAnnouncement,
getTopicDraft, recallMessage, updateTopicDraft } from '../lib/im-api.js'; getTopicDraft, recallMessage, updateTopicDraft } from '../lib/im-api.js';
import HashNavigation from '../lib/navigation.js'; import HashNavigation from '../lib/navigation.js';
import { bytesToHumanSize, relativeDateFormat, shortDateFormat } from '../lib/strformat.js'; import { bytesToHumanSize, relativeDateFormat, shortDateFormat } from '../lib/strformat.js';
@@ -495,6 +495,7 @@ class MessagesView extends React.Component {
selectedPin: 0, selectedPin: 0,
subsVersion: 0, subsVersion: 0,
relationshipContext: null, relationshipContext: null,
pendingRequestMessage: null,
groupRoles: null, groupRoles: null,
myGroupRole: null, myGroupRole: null,
groupAnnouncement: null, groupAnnouncement: null,
@@ -523,6 +524,7 @@ class MessagesView extends React.Component {
dragging: false, dragging: false,
selectedPin: 0, selectedPin: 0,
relationshipContext: null, relationshipContext: null,
pendingRequestMessage: null,
groupRoles: null, groupRoles: null,
myGroupRole: null, myGroupRole: null,
groupAnnouncement: null, groupAnnouncement: null,
@@ -1251,12 +1253,48 @@ class MessagesView extends React.Component {
} }
const topicName = topic.name; const topicName = topic.name;
getConversationContext(topicName).then(data => { getConversationContext(topicName).then(data => {
if (this.state.topic == topicName) { if (this.state.topic != topicName) {
this.setState({relationshipContext: data}); return;
} }
this.setState({relationshipContext: data});
this.loadPendingRequestMessage(topicName, data);
}).catch(_ => {}); }).catch(_ => {});
} }
// 个人对话框(对方的状态).png / (我的状态).png:pending_inbound/pending_outbound 关系下,聊天
// 页顶部第一条应该是发起好友申请时留的验证留言原文,以真实消息气泡的样式渲染(对方发的居左、
// 自己发的居右),下面再跟现有的 #relationship-status-note 横幅。这条留言从来不是一条真实的
// Tinode 消息(没有走 topic.publish(),只存在 im_friend_requests 表的 message 字段里),所以
// 不会自然出现在 messageNodes 里——relationshipContextPayload 已经带了 request_id,用它去
// GET /friend-requests 换真实留言文本;找不到(比如留言本身是空的)就回退成参考图里的默认文案。
loadPendingRequestMessage(topicName, ctx) {
const relationship = ctx && ctx.relationship;
if (relationship != 'pending_inbound' && relationship != 'pending_outbound') {
this.setState({pendingRequestMessage: null});
return;
}
if (!ctx.request_id) {
this.setState({pendingRequestMessage: {text: '请求添加你为好友', response: relationship == 'pending_inbound', ts: null}});
return;
}
const direction = relationship == 'pending_inbound' ? 'inbound' : 'outbound';
getFriendRequests(direction, 'all').then(data => {
if (this.state.topic != topicName) {
return;
}
const match = (data.items || []).find(item => item.request_id == ctx.request_id);
this.setState({pendingRequestMessage: {
text: (match && match.message) || '请求添加你为好友',
response: relationship == 'pending_inbound',
ts: match ? new Date(match.created_at) : null
}});
}).catch(_ => {
if (this.state.topic == topicName) {
this.setState({pendingRequestMessage: {text: '请求添加你为好友', response: relationship == 'pending_inbound', ts: null}});
}
});
}
// Ch.6: role badges (docs/images/群标识.png) and the announcement/join-request banners at the top of a // Ch.6: role badges (docs/images/群标识.png) and the announcement/join-request banners at the top of a
// group chat (docs/images/群公告状态.png, 新邀请进群申请.png) all need data no P2P topic has -- fetch it // group chat (docs/images/群公告状态.png, 新邀请进群申请.png) all need data no P2P topic has -- fetch it
// once per group-topic-load, same best-effort pattern as loadRelationshipContext above. Every `topic` // once per group-topic-load, same best-effort pattern as loadRelationshipContext above. Every `topic`
@@ -2586,6 +2624,22 @@ class MessagesView extends React.Component {
</button> </button>
<div id="messages-panel" ref={this.handleScrollReference}> <div id="messages-panel" ref={this.handleScrollReference}>
<ul id="scroller" className={chatBoxClass}> <ul id="scroller" className={chatBoxClass}>
{this.state.pendingRequestMessage ?
<>
{this.state.pendingRequestMessage.ts ?
<MetaMessage date={relativeDateFormat(this.state.pendingRequestMessage.ts)}
locale={this.props.intl.locale} key="pending-request-date" /> : null}
<li className={'single ' + (this.state.pendingRequestMessage.response ? 'left' : 'right')}
key="pending-request-message">
<div>
<div className="bubble tip">
<div className="content-meta">
<div className="message-content">{this.state.pendingRequestMessage.text}</div>
</div>
</div>
</div>
</li>
</> : null}
{messageNodes} {messageNodes}
</ul> </ul>
</div> </div>
+31 -8
View File
@@ -7817,7 +7817,9 @@ function requestStatusLabel(item) {
return null; return null;
} }
function FriendRequests({ function FriendRequests({
onToast onToast,
onTopicSelected,
onCreateTopic
}) { }) {
const [items, setItems] = react__WEBPACK_IMPORTED_MODULE_0___default().useState([]); const [items, setItems] = react__WEBPACK_IMPORTED_MODULE_0___default().useState([]);
const [loading, setLoading] = react__WEBPACK_IMPORTED_MODULE_0___default().useState(true); const [loading, setLoading] = react__WEBPACK_IMPORTED_MODULE_0___default().useState(true);
@@ -7840,6 +7842,7 @@ function FriendRequests({
onToast(err.message || '操作失败'); onToast(err.message || '操作失败');
}); });
}; };
const openChat = item => (onCreateTopic || onTopicSelected) && (onCreateTopic || onTopicSelected)(item.direction === 'inbound' ? item.applicant_id : item.target_user_id);
return (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("div", { return (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("div", {
className: "people-page", className: "people-page",
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)(Header, { children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)(Header, {
@@ -7854,6 +7857,9 @@ function FriendRequests({
className: "people-empty-hint", className: "people-empty-hint",
children: loadError ? '加载失败,请重试' : '暂无新朋友申请' children: loadError ? '加载失败,请重试' : '暂无新朋友申请'
}, void 0, false) : null, items.map(item => (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("article", { }, void 0, false) : null, items.map(item => (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("article", {
role: "button",
tabIndex: 0,
onClick: () => openChat(item),
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)(Avatar, { children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)(Avatar, {
person: item.peer person: item.peer
}, 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_6__.jsxDEV)("div", {
@@ -7867,12 +7873,18 @@ function FriendRequests({
}, void 0, true), item.status === 'pending' && item.direction === 'inbound' ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("span", { }, void 0, true), item.status === 'pending' && item.direction === 'inbound' ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("span", {
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("button", { children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("button", {
type: "button", type: "button",
onClick: () => handle(item, false), onClick: e => {
e.stopPropagation();
handle(item, false);
},
children: "拒绝" 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_6__.jsxDEV)("button", {
type: "button", type: "button",
className: "primary", className: "primary",
onClick: () => handle(item, true), onClick: e => {
e.stopPropagation();
handle(item, true);
},
children: "通过验证" children: "通过验证"
}, void 0, false)] }, void 0, false)]
}, void 0, true) : (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("em", { }, void 0, true) : (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("em", {
@@ -8385,10 +8397,19 @@ function Profile({
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)(Icon, { children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)(Icon, {
children: "person_add_alt" children: "person_add_alt"
}, void 0, false), "添加好友"] }, void 0, false), "添加好友"]
}, void 0, true) : (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("p", { }, void 0, true) : (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)(react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.Fragment, {
className: "people-empty-hint", children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("p", {
children: cannotAddReason className: "people-empty-hint",
}, void 0, false)] children: cannotAddReason
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("button", {
type: "button",
className: "people-primary",
onClick: openChat,
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)(Icon, {
children: "chat_bubble_outline"
}, void 0, false), "查看聊天"]
}, void 0, true)]
}, void 0, true)]
}, void 0, true)] }, void 0, true)]
}, void 0, true); }, void 0, true);
} }
@@ -8738,7 +8759,9 @@ function ContactManagementView({
myUserId: myUserId, myUserId: myUserId,
onTopicSelected: onTopicSelected onTopicSelected: onTopicSelected
}, void 0, false);else if (view === 'friend-requests') content = (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)(FriendRequests, { }, void 0, false);else if (view === 'friend-requests') content = (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)(FriendRequests, {
onToast: setToast onToast: setToast,
onTopicSelected: onTopicSelected,
onCreateTopic: onCreateTopic
}, void 0, false);else if (view === 'add-friend' || view === 'friend-search-result') content = (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)(AddFriend, { }, void 0, false);else if (view === 'add-friend' || view === 'friend-search-result') content = (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)(AddFriend, {
result: view === 'friend-search-result', result: view === 'friend-search-result',
onToast: setToast, onToast: setToast,
+1 -1
View File
File diff suppressed because one or more lines are too long
+70 -4
View File
@@ -457,6 +457,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
selectedPin: 0, selectedPin: 0,
subsVersion: 0, subsVersion: 0,
relationshipContext: null, relationshipContext: null,
pendingRequestMessage: null,
groupRoles: null, groupRoles: null,
myGroupRole: null, myGroupRole: null,
groupAnnouncement: null, groupAnnouncement: null,
@@ -484,6 +485,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
dragging: false, dragging: false,
selectedPin: 0, selectedPin: 0,
relationshipContext: null, relationshipContext: null,
pendingRequestMessage: null,
groupRoles: null, groupRoles: null,
myGroupRole: null, myGroupRole: null,
groupAnnouncement: null, groupAnnouncement: null,
@@ -1079,12 +1081,57 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
} }
const topicName = topic.name; const topicName = topic.name;
(0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_19__.getConversationContext)(topicName).then(data => { (0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_19__.getConversationContext)(topicName).then(data => {
if (this.state.topic != topicName) {
return;
}
this.setState({
relationshipContext: data
});
this.loadPendingRequestMessage(topicName, data);
}).catch(_ => {});
}
loadPendingRequestMessage(topicName, ctx) {
const relationship = ctx && ctx.relationship;
if (relationship != 'pending_inbound' && relationship != 'pending_outbound') {
this.setState({
pendingRequestMessage: null
});
return;
}
if (!ctx.request_id) {
this.setState({
pendingRequestMessage: {
text: '请求添加你为好友',
response: relationship == 'pending_inbound',
ts: null
}
});
return;
}
const direction = relationship == 'pending_inbound' ? 'inbound' : 'outbound';
(0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_19__.getFriendRequests)(direction, 'all').then(data => {
if (this.state.topic != topicName) {
return;
}
const match = (data.items || []).find(item => item.request_id == ctx.request_id);
this.setState({
pendingRequestMessage: {
text: match && match.message || '请求添加你为好友',
response: relationship == 'pending_inbound',
ts: match ? new Date(match.created_at) : null
}
});
}).catch(_ => {
if (this.state.topic == topicName) { if (this.state.topic == topicName) {
this.setState({ this.setState({
relationshipContext: data pendingRequestMessage: {
text: '请求添加你为好友',
response: relationship == 'pending_inbound',
ts: null
}
}); });
} }
}).catch(_ => {}); });
} }
loadGroupContext(topic) { loadGroupContext(topic) {
if (!topic || !topic.isGroupType() || topic.isChannelType()) { if (!topic || !topic.isGroupType() || topic.isChannelType()) {
@@ -2190,8 +2237,27 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("ul", { children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("ul", {
id: "scroller", id: "scroller",
className: chatBoxClass, className: chatBoxClass,
children: messageNodes children: [this.state.pendingRequestMessage ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.Fragment, {
}, void 0, false) children: [this.state.pendingRequestMessage.ts ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)(_widgets_meta_message_jsx__WEBPACK_IMPORTED_MODULE_11__["default"], {
date: (0,_lib_strformat_js__WEBPACK_IMPORTED_MODULE_21__.relativeDateFormat)(this.state.pendingRequestMessage.ts),
locale: this.props.intl.locale
}, "pending-request-date", false) : null, (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("li", {
className: 'single ' + (this.state.pendingRequestMessage.response ? 'left' : 'right'),
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
className: "bubble tip",
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
className: "content-meta",
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
className: "message-content",
children: this.state.pendingRequestMessage.text
}, void 0, false)
}, void 0, false)
}, void 0, false)
}, void 0, false)
}, "pending-request-message", false)]
}, void 0, true) : null, messageNodes]
}, void 0, true)
}, void 0, false), !this.state.isReader ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", { }, void 0, false), !this.state.isReader ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
id: "write-only-background", id: "write-only-background",
children: this.state.readingBlocked ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", { children: this.state.readingBlocked ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_22__.jsxDEV)("div", {
File diff suppressed because one or more lines are too long