diff --git a/src/views/contact-management-view.jsx b/src/views/contact-management-view.jsx index 6285f29b..e82fb1fa 100644 --- a/src/views/contact-management-view.jsx +++ b/src/views/contact-management-view.jsx @@ -406,7 +406,7 @@ function Verification({uid, onToast}) { ; } -function Profile({uid, friend, onTopicSelected, onToast}) { +function Profile({uid, friend, onTopicSelected, onCreateTopic, onToast}) { const [profile, setProfile] = React.useState(() => (uid ? readCachedData('profile-seed:' + uid) : null)); // A failed fetch with no seed to fall back on previously left this stuck on "加载中…" forever -- // tracked separately from `profile` itself (which legitimately stays null while genuinely still @@ -478,7 +478,17 @@ function Profile({uid, friend, onTopicSelected, onToast}) { // Real native Tinode p2p topic name (from my side) is just the peer's own user_id ("usrXXXX") -- // NOT `profile.topic` (a separate "p2pXXXX" identifier the custom REST layer uses internally, // which Tinode.topicType() doesn't recognize, so selecting it left the message pane blank). - const openChat = () => onTopicSelected(uid); + // + // Must go through onCreateTopic (tinode-web.jsx's handleStartTopicRequest), not the plain + // onTopicSelected -- confirmed live: a freshly-accepted friend with zero prior message history has + // no locally cached p2p topic yet, and onTopicSelected (handleTopicSelected) only *selects* a topic, + // it never sends the native {sub} request that actually creates/joins it. messages-view.jsx's own + // isWriter stays stuck at its default `false` (no ACS ever arrives because nothing subscribed), + // permanently showing "消息已禁用" with no compose box and no way to send the first message -- + // 100% reproducible for every real "just became friends, say hi" case, not an edge case. + // handleStartTopicRequest already guards the case this topic *does* have history (isTopicCached), + // delegating straight to handleTopicSelected then, so this is safe to call unconditionally. + const openChat = () => (onCreateTopic || onTopicSelected)(uid); return
navigate('friend-settings', uid)} />
@@ -659,7 +669,7 @@ function ConfirmSheet({type, uid, tinode, onToast}) {
; } -export default function ContactManagementView({view, hashParams, myUserId, tinode, onTopicSelected, onOpenScanner}) { +export default function ContactManagementView({view, hashParams, myUserId, tinode, onTopicSelected, onCreateTopic, onOpenScanner}) { const [toast, setToast] = React.useState(''); const uid = hashParams && hashParams.uid; let content; @@ -669,7 +679,7 @@ export default function ContactManagementView({view, hashParams, myUserId, tinod else if (view === 'my-friend-qr') content = ; else if (view === 'friend-qr') content = ; else if (view === 'friend-verification') content = ; - else if (view === 'friend-profile' || view === 'group-member-profile') content = ; + else if (view === 'friend-profile' || view === 'group-member-profile') content = ; else if (view === 'friend-settings' || view === 'friend-settings-blocked') content = ; else if (view === 'block-list') content = ; else if (view === 'friend-delete-confirm') content = ; diff --git a/src/views/sidepanel-view.jsx b/src/views/sidepanel-view.jsx index 8e61ad05..12383e2c 100644 --- a/src/views/sidepanel-view.jsx +++ b/src/views/sidepanel-view.jsx @@ -196,7 +196,8 @@ class SidepanelView extends React.PureComponent { if (CONTACT_MANAGEMENT_VIEWS.includes(view)) { return
+ tinode={this.props.tinode} onTopicSelected={this.props.onTopicSelected} onCreateTopic={this.props.onCreateTopic} + onOpenScanner={this.props.onOpenScanner} key={view} />
; }