fix: include room ID in message permalink for cross-room navigation
Links now use #roomId/messageHash format so the app can load the correct room before scrolling to the target message. Handles hashchange events and auto-navigates on page load. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
2e1a0ac858
commit
7210acf032
@ -190,10 +190,9 @@
|
|||||||
// Register global 401 handler so any expired-token API call triggers logout
|
// Register global 401 handler so any expired-token API call triggers logout
|
||||||
setOnUnauthorized(() => this.handleLogout())
|
setOnUnauthorized(() => this.handleLogout())
|
||||||
|
|
||||||
// Listen for hash changes to scroll to linked messages
|
// Listen for hash changes to navigate to linked messages
|
||||||
this._onHashChange = () => {
|
this._onHashChange = () => {
|
||||||
const hash = window.location.hash?.slice(1)
|
this.navigateToMessageLink()
|
||||||
if (hash) this.scrollToHash(hash)
|
|
||||||
}
|
}
|
||||||
window.addEventListener('hashchange', this._onHashChange)
|
window.addEventListener('hashchange', this._onHashChange)
|
||||||
|
|
||||||
@ -365,6 +364,9 @@
|
|||||||
|
|
||||||
// Process any pending invite token
|
// Process any pending invite token
|
||||||
await this.processInviteToken()
|
await this.processInviteToken()
|
||||||
|
|
||||||
|
// Check for a message permalink in the URL hash (e.g. #roomId/messageHash)
|
||||||
|
this.navigateToMessageLink()
|
||||||
},
|
},
|
||||||
|
|
||||||
handleLogin(data) {
|
handleLogin(data) {
|
||||||
@ -408,14 +410,7 @@
|
|||||||
typingUsers: [],
|
typingUsers: [],
|
||||||
})
|
})
|
||||||
ws.joinRoom(roomId)
|
ws.joinRoom(roomId)
|
||||||
|
this.scrollToBottom()
|
||||||
// Check for hash in URL to scroll to a specific message
|
|
||||||
const targetHash = window.location.hash?.slice(1)
|
|
||||||
if (targetHash) {
|
|
||||||
this.scrollToHash(targetHash)
|
|
||||||
} else {
|
|
||||||
this.scrollToBottom()
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Failed to load room:', e)
|
console.error('Failed to load room:', e)
|
||||||
}
|
}
|
||||||
@ -504,12 +499,35 @@
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** Parse #roomId/messageHash from URL, load the room, and scroll to the message */
|
||||||
|
async navigateToMessageLink() {
|
||||||
|
const fragment = window.location.hash?.slice(1)
|
||||||
|
if (!fragment) return
|
||||||
|
const slashIdx = fragment.indexOf('/')
|
||||||
|
if (slashIdx === -1) return // not a room/hash link
|
||||||
|
|
||||||
|
const roomId = fragment.slice(0, slashIdx)
|
||||||
|
const msgHash = fragment.slice(slashIdx + 1)
|
||||||
|
if (!roomId || !msgHash) return
|
||||||
|
|
||||||
|
// Store the target hash before selecting the room
|
||||||
|
this._pendingScrollHash = msgHash
|
||||||
|
|
||||||
|
// Select the room if not already active
|
||||||
|
if (this.state.activeRoomId !== roomId) {
|
||||||
|
await this.selectRoom(roomId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scroll to the message after render
|
||||||
|
this.scrollToHash(this._pendingScrollHash)
|
||||||
|
this._pendingScrollHash = null
|
||||||
|
},
|
||||||
|
|
||||||
scrollToHash(hash) {
|
scrollToHash(hash) {
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
const el = document.getElementById('msg-' + hash)
|
const el = document.getElementById('msg-' + hash)
|
||||||
if (el) {
|
if (el) {
|
||||||
el.scrollIntoView({ behavior: 'smooth', block: 'center' })
|
el.scrollIntoView({ behavior: 'smooth', block: 'center' })
|
||||||
// Briefly highlight the message
|
|
||||||
el.classList.add('hash-highlight')
|
el.classList.add('hash-highlight')
|
||||||
setTimeout(() => el.classList.remove('hash-highlight'), 3000)
|
setTimeout(() => el.classList.remove('hash-highlight'), 3000)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -478,7 +478,9 @@
|
|||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
const hash = this.props.message?.hash
|
const hash = this.props.message?.hash
|
||||||
if (!hash) return
|
if (!hash) return
|
||||||
const url = `${window.location.origin}${window.location.pathname}#${hash}`
|
const roomId = this.props.message?.room_id
|
||||||
|
if (!roomId) return
|
||||||
|
const url = `${window.location.origin}${window.location.pathname}#${roomId}/${hash}`
|
||||||
const btn = e.currentTarget
|
const btn = e.currentTarget
|
||||||
navigator.clipboard.writeText(url).then(() => {
|
navigator.clipboard.writeText(url).then(() => {
|
||||||
btn.classList.add('copied')
|
btn.classList.add('copied')
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user