-
Notifications
You must be signed in to change notification settings - Fork 13.5k
feat: Sidebar draft group #38225
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
feat: Sidebar draft group #38225
Changes from all commits
1f5425e
e3bc7b8
4f1855b
3e83767
d261ca1
fb59dfe
dd3e4a6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| --- | ||
| '@rocket.chat/ui-client': minor | ||
| '@rocket.chat/i18n': minor | ||
| '@rocket.chat/meteor': minor | ||
| --- | ||
|
|
||
| Adds a new "Drafts" group to the sidebar, providing quick access to all rooms with unfinished messages. | ||
| > This feature is available under the `Drafts in sidebar` feature preview and needs to be enabled in settings to be tested. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| import { useLocalStorage } from '@rocket.chat/fuselage-hooks'; | ||
| import { useEndpoint } from '@rocket.chat/ui-contexts'; | ||
| import { useCallback, useRef } from 'react'; | ||
|
|
||
| export const useDraft = (rid: string, serverDraft?: string, tmid?: string) => { | ||
| const storageKey = `messagebox_${rid}${tmid ? `-${tmid}` : ''}`; | ||
| const [localDraft, setLocalDraft] = useLocalStorage<string>(storageKey, ''); | ||
| const saveDraft = useEndpoint('POST', '/v1/subscriptions.saveDraft'); | ||
| const initialValueRef = useRef(serverDraft || localDraft); | ||
| const draftRef = useRef<string | null>(null); | ||
|
Comment on lines
+9
to
+10
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
# 1) Locate MessageBox usage and verify whether remount is forced via a room/thread-based key.
rg -n --type=tsx '<MessageBox\b|MessageBox\(' apps/meteor -C3
rg -n --type=tsx 'key=\{[^}]*(_id|rid|tmid)' apps/meteor -C2
# 2) Inspect where useRoomSubscription is consumed to confirm whether subscription can be undefined initially then populated.
rg -n --type=tsx '\buseRoomSubscription\s*\(' apps/meteor -C3
# 3) Confirm all useDraft call sites and their argument sources.
rg -n --type=ts --type=tsx '\buseDraft\s*\(' apps/meteor packages -C3Repository: RocketChat/Rocket.Chat Length of output: 93 🏁 Script executed: # 1) Locate MessageBox usage and verify whether remount is forced via a room/thread-based key.
rg -n -t ts -t js 'MessageBox' apps/meteor/client/views/room --max-count=20 -A 3 -B 1
# 2) Find and read the useDraft.ts hook implementation
cat -n apps/meteor/client/views/room/composer/messageBox/hooks/useDraft.ts
# 3) Locate useDraft call sites
rg -n -t ts -t js 'useDraft\s*\(' apps/meteor packages -C 3
# 4) Find MessageBox component definition
find apps/meteor -name 'MessageBox*' -type fRepository: RocketChat/Rocket.Chat Length of output: 32226 🏁 Script executed: # Check if there are any useEffect dependencies on tmid that reset the composer state
rg -n -t ts -t js 'useEffect.*tmid|tmid.*useEffect' apps/meteor/client/views/room/composer --max-count=20 -A 5
# Check MessageBox component for any useEffect that resets state when tmid changes
sed -n '97,200p' apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx | head -100
# Check if ChatContext or chat.composer handles thread changes
rg -n -t ts -t js 'chat\.composer.*tmid|setComposer.*tmid' apps/meteor/client/views/room --max-count=10 -B 2 -A 2Repository: RocketChat/Rocket.Chat Length of output: 2996 🏁 Script executed: # Check what happens when tmid changes - look for any state reset logic
sed -n '97,400p' apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx | grep -n "tmid\|useEffect\|composer"
# Check createComposerAPI to see how initialValue is used
rg -n 'createComposerAPI' apps/meteor --max-count=5 -A 20 -B 2Repository: RocketChat/Rocket.Chat Length of output: 9446 🏁 Script executed: # Check if there's any cleanup or reset logic when tmid changes in chat.composer or ChatContext
rg -n 'setComposerAPI\|clear.*composer\|reset.*composer' apps/meteor/client/views/room/contexts/ChatContext.ts -A 5 -B 5
# Check if there's a useEffect that handles room or thread changes to reset composer
sed -n '1,500p' apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx | grep -n "useEffect\|cleanup\|return"
# Verify where chat.composer gets cleared/reset when navigating between threads
rg -n 'chat\.setComposerAPI\|composer.*clear' apps/meteor/client/views/room -A 3 -B 1 | head -50Repository: RocketChat/Rocket.Chat Length of output: 996 🏁 Script executed: # Look at the full ChatContext implementation to understand composer lifecycle
cat -n apps/meteor/client/views/room/contexts/ChatContext.ts | head -100
# Check if there's logic that clears composer state when switching threads
rg -n 'clearComposerAPI\|setComposerAPI\|null' apps/meteor/client/views/room/contexts/ChatContext.ts -B 2 -A 2
# Look for where initialValue is actually used in the composer
cat -n apps/meteor/app/ui-message/client/messageBox/createComposerAPI.ts | sed -n '1,80p'Repository: RocketChat/Rocket.Chat Length of output: 3343 🏁 Script executed: # Continue reading createComposerAPI to see how initialDraft is used
cat -n apps/meteor/app/ui-message/client/messageBox/createComposerAPI.ts | sed -n '80,180p'
# Search for where the initialDraft parameter is applied to the input
rg -n 'initialDraft\|input\.value\s*=' apps/meteor/app/ui-message/client/messageBox/createComposerAPI.ts -A 2 -B 2Repository: RocketChat/Rocket.Chat Length of output: 2947 🏁 Script executed: # Search for initialDraft usage - it should be set somewhere on the input
rg -n 'initialDraft' apps/meteor/app/ui-message/client/messageBox/createComposerAPI.ts
# Look at the return statement of createComposerAPI to see the full API
cat -n apps/meteor/app/ui-message/client/messageBox/createComposerAPI.ts | tail -100Repository: RocketChat/Rocket.Chat Length of output: 3241 🏁 Script executed: # Check the full callbackRef useCallback and see what happens when it's recreated
sed -n '130,160p' apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx
# Check if there's any useEffect that monitors tmid/room changes and resets composer state
sed -n '200,300p' apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx | grep -n "useEffect\|tmid"
# Search for any cleanup when MessageBox unmounts or when room/tmid changes
rg -n 'useEffect.*\[\]|useEffect.*\[room\|useEffect.*\[tmid' apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx -A 8Repository: RocketChat/Rocket.Chat Length of output: 967 🏁 Script executed: # Check ComposerMessage more carefully - is MessageBox keyed by both room AND tmid?
sed -n '80,100p' apps/meteor/client/views/room/composer/ComposerMessage.tsx
# Look for any other MessageBox usages to see if they use keys
rg -n '<MessageBox' apps/meteor/client/views/room -B 2 -A 2Repository: RocketChat/Rocket.Chat Length of output: 10716 Stale
🤖 Prompt for AI Agents |
||
|
|
||
| const persistLocal = useCallback( | ||
| (value: string) => { | ||
| draftRef.current = value; | ||
| setLocalDraft(value); | ||
| }, | ||
| [setLocalDraft], | ||
| ); | ||
|
|
||
| const flushDraft = useCallback(() => { | ||
| if (draftRef.current === null || tmid) { | ||
| return; | ||
| } | ||
|
|
||
| void saveDraft({ rid, draft: draftRef.current }); | ||
| draftRef.current = null; | ||
| }, [saveDraft, rid, tmid]); | ||
|
|
||
| return { | ||
| initialValue: initialValueRef.current, | ||
| persistLocal, | ||
| flushDraft, | ||
| }; | ||
| }; | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: The
readyguard is ineffective with debouncing: the initializationinputevent is queued whilereadyis false, but executes afterready = true, soinitialDraftis still persisted.Prompt for AI agents