Skip to content

feat(web): show login wall on duplicated chat for askgh#939

Open
msukkari wants to merge 4 commits intomainfrom
michael/askgh-duplicate-login-wall-SOU-568
Open

feat(web): show login wall on duplicated chat for askgh#939
msukkari wants to merge 4 commits intomainfrom
michael/askgh-duplicate-login-wall-SOU-568

Conversation

@msukkari
Copy link
Contributor

@msukkari msukkari commented Feb 25, 2026

Summary

  • When EXPERIMENT_ASK_GH_ENABLED=true, shows a login modal when an anonymous user tries to send a message on a duplicated chat
  • After OAuth redirect, the pending message is automatically restored and submitted on the correct chat
  • Moves LoginModal component to a shared location (app/components/loginModal.tsx) for reuse across askgh landing page and chat thread
  • Fixes vi.Mock type errors in listCommitsApi.test.ts

Test plan

  • With EXPERIMENT_ASK_GH_ENABLED=true, open a public chat as anonymous user → duplicate → try to send message → login modal should appear
  • After logging in via the modal, verify the pending message is auto-submitted on the same chat
  • Dismiss the modal, navigate to a different chat, verify the pending message is NOT auto-submitted there
  • Without the flag, verify behavior is unchanged (no login wall)
  • Verify askgh landing page login wall still works correctly

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Show a login wall when anonymous users try to send messages in the AskGH experiment; pending messages are preserved and automatically restored after successful sign-in.
  • Chores

    • Internal updates to imports and test setup (no user-facing behavior changes).

…cated chat

When EXPERIMENT_ASK_GH_ENABLED is true, anonymous users who duplicate a
public chat and try to send a follow-up message are now shown a login
modal instead of sending the message. After OAuth redirect, the pending
message is automatically restored and submitted.

Also fixes vi.Mock type errors in listCommitsApi.test.ts by importing
Mock type directly from vitest.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions

This comment has been minimized.

msukkari added a commit that referenced this pull request Feb 25, 2026
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 25, 2026

Walkthrough

Adds an AskGH "login wall" flow: unauthenticated send attempts store a pending message, fetch identity-provider info, open a LoginModal for OAuth, and restore/resend the pending message after authentication. Also updates hook and component signatures and test mocks; adds a changelog entry.

Changes

Cohort / File(s) Summary
AskGH login-wall core
packages/web/src/features/chat/useCreateNewChatThread.ts, packages/web/src/features/chat/actions.ts
Introduces getAskGhLoginWallData and authentication-aware createNewChatThread flow; stores/restores pending new-chat data in sessionStorage and exposes loginWall state from the hook.
Chat UI integration
packages/web/src/features/chat/components/chatThread/chatThread.tsx, packages/web/src/app/[domain]/chat/components/landingPageChatBox.tsx, packages/web/src/app/[domain]/chat/page.tsx
Wires login-wall into ChatThread and landing chat box: opens LoginModal when needed, preserves pending messages across OAuth redirect, adds isAuthenticated prop propagation and loginWall handling.
AskGH landing pages
packages/web/src/app/[domain]/askgh/[owner]/[repo]/components/landingPage.tsx, packages/web/src/app/[domain]/askgh/[owner]/[repo]/page.tsx
Removes provider plumbing from page-level code, updates LandingPage to use loginWall state and simplified auth handling; switches some imports to alias paths.
Tests / mocks
packages/web/src/features/git/listCommitsApi.test.ts, packages/web/src/features/git/utils.test.ts
Adjusts vitest mock typing (use Mock) and adds global logger mock for test setup.
Changelog
CHANGELOG.md
Adds Unreleased entry describing login wall shown to anonymous users in duplicated AskGH chats.
Minor formatting
packages/web/src/app/[domain]/chat/[id]/page.tsx
Whitespace/import-line edit with no runtime change.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ChatThread
    participant Server
    participant SessionStorage
    participant LoginModal
    participant OAuthProvider

    User->>ChatThread: Submit message (unauthenticated)
    ChatThread->>Server: getAskGhLoginWallData()
    Server-->>ChatThread: { isEnabled, providers }
    ChatThread->>SessionStorage: store pending message
    ChatThread->>LoginModal: open(with providers)
    LoginModal->>OAuthProvider: redirect user for auth
    OAuthProvider-->>ChatThread: redirect back (authenticated)
    ChatThread->>SessionStorage: retrieve pending message
    ChatThread->>ChatThread: restore UI message
    ChatThread->>ChatThread: createNewChatThread (authenticated) / send
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

sourcebot-team

Suggested reviewers

  • brendan-kellam
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(web): show login wall on duplicated chat for askgh' accurately describes the main objective: displaying a login modal when anonymous users attempt to send messages in duplicated public chats within the AskGH experiment.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch michael/askgh-duplicate-login-wall-SOU-568

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@brendan-kellam brendan-kellam Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can remove this from the landing page now, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

asked claude to refactor the guard logic to be down in the actual chat component

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/web/src/features/chat/components/chatThread/chatThread.tsx`:
- Around line 219-226: The restore logic currently marks
hasRestoredPendingMessage and removes PENDING_MESSAGE_STORAGE_KEY before
validating the stored payload and chatId, and it rehydrates search scopes from
current selectedSearchScopes instead of the saved draft; change the flow in the
restore functions that reference PENDING_MESSAGE_STORAGE_KEY (including the
blocks around hasRestoredPendingMessage and the later restore paths at the
228-238 and 335-337 areas) to: read and JSON.parse the stored payload, validate
stored.chatId matches the currently selected chat id (or the chat being opened),
and only when the chatId matches apply the draft into your draft state
(including restoring saved selectedSearchScopes from the payload), then set
hasRestoredPendingMessage.current = true and finally call
sessionStorage.removeItem(PENDING_MESSAGE_STORAGE_KEY); if it does not match, do
not clear storage or mark restored so the draft survives for the correct chat.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f9421ed and 314b573.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • packages/web/src/app/[domain]/askgh/[owner]/[repo]/components/landingPage.tsx
  • packages/web/src/app/[domain]/chat/[id]/components/chatThreadPanel.tsx
  • packages/web/src/app/[domain]/chat/[id]/page.tsx
  • packages/web/src/app/components/loginModal.tsx
  • packages/web/src/features/chat/components/chatThread/chatThread.tsx
  • packages/web/src/features/git/listCommitsApi.test.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@msukkari msukkari force-pushed the michael/askgh-duplicate-login-wall-SOU-568 branch from 314b573 to 825781f Compare February 25, 2026 19:07
…rror

The test was failing because importing utils.ts transitively pulled in
server-side environment variables via createLogger, which crashes in
the jsdom test environment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
packages/web/src/features/chat/components/chatThread/chatThread.tsx (1)

211-241: Pending message is lost if user lands on a different chat after OAuth redirect.

Lines 222–223 clear sessionStorage and mark restore as done before the storedChatId !== chatId check on line 229. If the redirect lands on a different chat first, the draft is irreversibly deleted. Move the cleanup to after a successful match and send.

Additionally, selectedSearchScopes on line 235 uses the current runtime value, not the scopes active when the message was drafted. Consider persisting and restoring them alongside children.

Proposed fix
-        hasRestoredPendingMessage.current = true;
-        sessionStorage.removeItem(PENDING_MESSAGE_STORAGE_KEY);
-
         try {
-            const { chatId: storedChatId, children } = JSON.parse(stored) as { chatId: string; children: Descendant[] };
+            const { chatId: storedChatId, children, selectedSearchScopes: storedScopes } =
+                JSON.parse(stored) as { chatId: string; children: Descendant[]; selectedSearchScopes?: SearchScope[] };
 
             // Only restore if we're on the same chat that stored the pending message
             if (storedChatId !== chatId) {
                 return;
             }
 
             const text = slateContentToString(children);
             const mentions = getAllMentionElements(children);
-            const message = createUIMessage(text, mentions.map(({ data }) => data), selectedSearchScopes);
+            const message = createUIMessage(text, mentions.map(({ data }) => data), storedScopes ?? selectedSearchScopes);
             sendMessage(message);
             setIsAutoScrollEnabled(true);
+            hasRestoredPendingMessage.current = true;
+            sessionStorage.removeItem(PENDING_MESSAGE_STORAGE_KEY);
         } catch (error) {
+            hasRestoredPendingMessage.current = true;
+            sessionStorage.removeItem(PENDING_MESSAGE_STORAGE_KEY);
             console.error('Failed to restore pending message:', error);
         }

And in the storage call (line 335):

-sessionStorage.setItem(PENDING_MESSAGE_STORAGE_KEY, JSON.stringify({ chatId, children }));
+sessionStorage.setItem(PENDING_MESSAGE_STORAGE_KEY, JSON.stringify({ chatId, children, selectedSearchScopes }));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/web/src/features/chat/components/chatThread/chatThread.tsx` around
lines 211 - 241, The restore logic clears sessionStorage and marks
hasRestoredPendingMessage.current=true before verifying the stored chat matches
the current chat and before sending, which causes drafts to be lost if the user
lands on a different chat; move
sessionStorage.removeItem(PENDING_MESSAGE_STORAGE_KEY) and setting
hasRestoredPendingMessage.current=true to after the storedChatId === chatId
check and after sendMessage succeeds. Also persist the search scopes with the
stored payload and, when restoring, read the saved scopes instead of using the
current selectedSearchScopes (update the JSON shape saved to
PENDING_MESSAGE_STORAGE_KEY and use those restored scopes when calling
createUIMessage); adjust slateContentToString, getAllMentionElements,
createUIMessage, and sendMessage usage accordingly.
🧹 Nitpick comments (2)
packages/web/src/features/chat/components/chatThread/chatThread.tsx (2)

330-351: Server call on every unauthenticated submit — consider caching the result.

getAskGhLoginWallData() is a server action called on every submit attempt by an unauthenticated user. Since the result (feature flag + providers) is unlikely to change during a session, consider fetching it once (e.g., on mount or on first submit) and caching in state, rather than making a round-trip on every keystroke→submit.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/web/src/features/chat/components/chatThread/chatThread.tsx` around
lines 330 - 351, The onSubmit handler calls getAskGhLoginWallData() on every
unauthenticated submit; cache that result in component state instead (e.g., add
askGhLoginWallData / askGhLoginLoaded state) and populate it either on mount
(useEffect) or on first unauthenticated submit, then read from that cached state
in onSubmit (replace direct getAskGhLoginWallData() call). Update onSubmit to
only call the server when cached data is missing, setLoginWallProviders from the
cached response, and ensure dependencies (sendMessage, selectedSearchScopes,
isAuthenticated, captureEvent, chatId, askGhLoginWallData) are kept correct to
avoid stale closures.

503-509: callbackUrl fallback is fine for a client component, but the empty-string fallback is a silent no-op.

Since this is a 'use client' component and LoginModal only renders when isLoginModalOpen is true (which requires user interaction), window will always be defined here. The typeof window !== 'undefined' guard is unnecessary but harmless. However, if it ever did fall through to '', the OAuth redirect would break silently. A minor nit — you could simplify to just window.location.href.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/web/src/features/chat/components/chatThread/chatThread.tsx` around
lines 503 - 509, The callbackUrl currently uses a typeof window check that can
fall back to an empty string; update the LoginModal invocation in chatThread.tsx
so callbackUrl is set directly to window.location.href (since this is a 'use
client' component and LoginModal only opens after user interaction), i.e.,
replace the ternary expression passed to the callbackUrl prop for LoginModal
(the code referencing isLoginModalOpen/loginWallProviders) with
window.location.href to avoid a silent empty-string fallback.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@packages/web/src/features/chat/components/chatThread/chatThread.tsx`:
- Around line 211-241: The restore logic clears sessionStorage and marks
hasRestoredPendingMessage.current=true before verifying the stored chat matches
the current chat and before sending, which causes drafts to be lost if the user
lands on a different chat; move
sessionStorage.removeItem(PENDING_MESSAGE_STORAGE_KEY) and setting
hasRestoredPendingMessage.current=true to after the storedChatId === chatId
check and after sendMessage succeeds. Also persist the search scopes with the
stored payload and, when restoring, read the saved scopes instead of using the
current selectedSearchScopes (update the JSON shape saved to
PENDING_MESSAGE_STORAGE_KEY and use those restored scopes when calling
createUIMessage); adjust slateContentToString, getAllMentionElements,
createUIMessage, and sendMessage usage accordingly.

---

Nitpick comments:
In `@packages/web/src/features/chat/components/chatThread/chatThread.tsx`:
- Around line 330-351: The onSubmit handler calls getAskGhLoginWallData() on
every unauthenticated submit; cache that result in component state instead
(e.g., add askGhLoginWallData / askGhLoginLoaded state) and populate it either
on mount (useEffect) or on first unauthenticated submit, then read from that
cached state in onSubmit (replace direct getAskGhLoginWallData() call). Update
onSubmit to only call the server when cached data is missing,
setLoginWallProviders from the cached response, and ensure dependencies
(sendMessage, selectedSearchScopes, isAuthenticated, captureEvent, chatId,
askGhLoginWallData) are kept correct to avoid stale closures.
- Around line 503-509: The callbackUrl currently uses a typeof window check that
can fall back to an empty string; update the LoginModal invocation in
chatThread.tsx so callbackUrl is set directly to window.location.href (since
this is a 'use client' component and LoginModal only opens after user
interaction), i.e., replace the ternary expression passed to the callbackUrl
prop for LoginModal (the code referencing isLoginModalOpen/loginWallProviders)
with window.location.href to avoid a silent empty-string fallback.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 314b573 and 11184ea.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • packages/web/src/app/[domain]/chat/[id]/page.tsx
  • packages/web/src/features/chat/actions.ts
  • packages/web/src/features/chat/components/chatThread/chatThread.tsx
  • packages/web/src/features/git/utils.test.ts
💤 Files with no reviewable changes (1)
  • packages/web/src/app/[domain]/chat/[id]/page.tsx

Centralizes the login wall logic in the hook so all chat creation entry
points are guarded automatically. This adds the login wall to the
general /chat landing page and removes the duplicate manual logic from
the askgh landing page.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/web/src/features/chat/useCreateNewChatThread.ts (1)

33-47: ⚠️ Potential issue | 🟠 Major

Guard async failures to avoid unhandled rejections and stuck UX state.

Line 40 (createChat) and Line 62 (getAskGhLoginWallData) can throw; they are not wrapped. Also Line 71 and Line 93 invoke doCreateChat without controlled error propagation. This can cause unhandled rejections and inconsistent loading/error behavior.

💡 Proposed fix
@@
-    const doCreateChat = useCallback(async (children: Descendant[], selectedSearchScopes: SearchScope[]) => {
+    const doCreateChat = useCallback(async (children: Descendant[], selectedSearchScopes: SearchScope[]) => {
         const text = slateContentToString(children);
         const mentions = getAllMentionElements(children);
@@
-        setIsLoading(true);
-        const response = await createChat();
+        setIsLoading(true);
+        let response;
+        try {
+            response = await createChat();
+        } catch (error) {
+            toast({
+                description: "❌ Failed to create chat. Reason: unexpected error"
+            });
+            setIsLoading(false);
+            return;
+        }
@@
-    const createNewChatThread = useCallback(async (children: Descendant[], selectedSearchScopes: SearchScope[]) => {
+    const createNewChatThread = useCallback(async (children: Descendant[], selectedSearchScopes: SearchScope[]) => {
         if (!isAuthenticated) {
-            const result = await getAskGhLoginWallData();
+            const result = await getAskGhLoginWallData();
             if (!isServiceError(result) && result.isEnabled) {
@@
-        doCreateChat(children, selectedSearchScopes);
+        await doCreateChat(children, selectedSearchScopes);
     }, [isAuthenticated, captureEvent, doCreateChat]);
@@
-            doCreateChat(children, selectedSearchScopes);
+            void doCreateChat(children, selectedSearchScopes);
         } catch (error) {
             console.error('Failed to restore pending message:', error);
         }
#!/bin/bash
# Inspect async calls and call-sites that should be handled explicitly.
rg -n -C3 'await createChat\(|await getAskGhLoginWallData\(|doCreateChat\(' packages/web/src/features/chat/useCreateNewChatThread.ts

Also applies to: 60-72, 88-95

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/web/src/features/chat/useCreateNewChatThread.ts` around lines 33 -
47, The createChat and getAskGhLoginWallData calls (and the doCreateChat
callers) can throw and are not guarded; wrap the async body of doCreateChat in
try/catch/finally so any thrown error is caught, always call setIsLoading(false)
in finally, and surface a controlled error to callers (either return a Result or
rethrow after cleanup). Update call sites that invoke doCreateChat to await it
and handle rejections (or inspect the returned Result) instead of letting
unhandled promises propagate. Specifically modify the doCreateChat function
around the createChat call and the code that calls getAskGhLoginWallData, ensure
toast shows errors with the caught error message, and adjust callers that call
doCreateChat so they handle the returned error state.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/web/src/features/chat/useCreateNewChatThread.ts`:
- Around line 65-67: The pending payload saved with
sessionStorage.setItem(PENDING_NEW_CHAT_KEY, ...) should be removed when the
login modal is dismissed; update the login modal dismiss path (where
setLoginWallState is called with isOpen: false / the modal close handler) to
call sessionStorage.removeItem(PENDING_NEW_CHAT_KEY) so the stale payload can’t
be restored later, and ensure any local flags used for restoration (e.g.,
hasRestoredPendingMessage) remain consistent with this removal.

---

Outside diff comments:
In `@packages/web/src/features/chat/useCreateNewChatThread.ts`:
- Around line 33-47: The createChat and getAskGhLoginWallData calls (and the
doCreateChat callers) can throw and are not guarded; wrap the async body of
doCreateChat in try/catch/finally so any thrown error is caught, always call
setIsLoading(false) in finally, and surface a controlled error to callers
(either return a Result or rethrow after cleanup). Update call sites that invoke
doCreateChat to await it and handle rejections (or inspect the returned Result)
instead of letting unhandled promises propagate. Specifically modify the
doCreateChat function around the createChat call and the code that calls
getAskGhLoginWallData, ensure toast shows errors with the caught error message,
and adjust callers that call doCreateChat so they handle the returned error
state.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 11184ea and 7a0103b.

📒 Files selected for processing (5)
  • packages/web/src/app/[domain]/askgh/[owner]/[repo]/components/landingPage.tsx
  • packages/web/src/app/[domain]/askgh/[owner]/[repo]/page.tsx
  • packages/web/src/app/[domain]/chat/components/landingPageChatBox.tsx
  • packages/web/src/app/[domain]/chat/page.tsx
  • packages/web/src/features/chat/useCreateNewChatThread.ts
💤 Files with no reviewable changes (1)
  • packages/web/src/app/[domain]/askgh/[owner]/[repo]/page.tsx

Comment on lines +65 to +67
sessionStorage.setItem(PENDING_NEW_CHAT_KEY, JSON.stringify({ children, selectedSearchScopes }));
setLoginWallState({ isOpen: true, providers: result.providers });
return;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cd packages/web/src/features/chat && wc -l useCreateNewChatThread.ts

Repository: sourcebot-dev/sourcebot

Length of output: 96


🏁 Script executed:

cat -n packages/web/src/features/chat/useCreateNewChatThread.ts

Repository: sourcebot-dev/sourcebot

Length of output: 5108


Clear the pending payload when users dismiss the login modal to prevent unintended restoration after later authentication.

When a user dismisses the login modal at line 105, the pending message stored at line 65 is not cleared. If they authenticate from another tab and return to the app, the restoration effect at lines 80–93 will replay the stale pending message. While the code does remove the stored message immediately after restoration (line 86) and uses hasRestoredPendingMessage to prevent repeated restorations within the same component lifecycle, a single unintended restoration can still occur if users dismiss the modal without logging in and then authenticate elsewhere.

💡 Proposed fix
-            onOpenChange: (open: boolean) => setLoginWallState(prev => ({ ...prev, isOpen: open })),
+            onOpenChange: (open: boolean) => {
+                if (!open) {
+                    sessionStorage.removeItem(PENDING_NEW_CHAT_KEY);
+                }
+                setLoginWallState(prev => ({ ...prev, isOpen: open }));
+            },

Also applies to: 80–87, 89–93, 102–106

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/web/src/features/chat/useCreateNewChatThread.ts` around lines 65 -
67, The pending payload saved with sessionStorage.setItem(PENDING_NEW_CHAT_KEY,
...) should be removed when the login modal is dismissed; update the login modal
dismiss path (where setLoginWallState is called with isOpen: false / the modal
close handler) to call sessionStorage.removeItem(PENDING_NEW_CHAT_KEY) so the
stale payload can’t be restored later, and ensure any local flags used for
restoration (e.g., hasRestoredPendingMessage) remain consistent with this
removal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants