From 8d402b7d89962a649cbbacd25c4fa69daf107dbf Mon Sep 17 00:00:00 2001 From: Bekiboo Date: Mon, 15 Jun 2026 08:04:36 +0300 Subject: [PATCH 1/2] fix(blabsy): recover composer when a Blab fails to send --- .../client/src/components/input/input.tsx | 90 ++++++++++--------- 1 file changed, 48 insertions(+), 42 deletions(-) diff --git a/platforms/blabsy/client/src/components/input/input.tsx b/platforms/blabsy/client/src/components/input/input.tsx index 5e5aacfb4..b7d233f4d 100644 --- a/platforms/blabsy/client/src/components/input/input.tsx +++ b/platforms/blabsy/client/src/components/input/input.tsx @@ -81,50 +81,56 @@ export function Input({ const userId = user?.id as string; - const tweetData: WithFieldValue> = { - text: inputValue.trim() || null, - parent: isReplying && parent ? parent : null, - images: await uploadImages(userId, selectedImages), - userLikes: [], - createdBy: userId, - createdAt: serverTimestamp(), - updatedAt: null, - userReplies: 0, - userRetweets: [] - }; - - await sleep(500); - - const [tweetRef] = await Promise.all([ - addDoc(tweetsCollection, tweetData), - manageTotalTweets('increment', userId), - tweetData.images && manageTotalPhotos('increment', userId), - isReplying && manageReply('increment', parent?.id as string) - ]); - - const { id: tweetId } = await getDoc(tweetRef); - - if (!modal && !replyModal) { - discardTweet(); + try { + const tweetData: WithFieldValue> = { + text: inputValue.trim() || null, + parent: isReplying && parent ? parent : null, + images: await uploadImages(userId, selectedImages), + userLikes: [], + createdBy: userId, + createdAt: serverTimestamp(), + updatedAt: null, + userReplies: 0, + userRetweets: [] + }; + + await sleep(500); + + const [tweetRef] = await Promise.all([ + addDoc(tweetsCollection, tweetData), + manageTotalTweets('increment', userId), + tweetData.images && manageTotalPhotos('increment', userId), + isReplying && manageReply('increment', parent?.id as string) + ]); + + const { id: tweetId } = await getDoc(tweetRef); + + if (!modal && !replyModal) { + discardTweet(); + setLoading(false); + } + + if (closeModal) closeModal(); + + toast.success( + () => ( + + Your Blab was sent + + View + + + ), + { duration: 6000 } + ); + } catch (error) { + console.error('Failed to send Blab:', error); setLoading(false); + toast.error('Failed to send your Blab. Please try again.'); } - - if (closeModal) closeModal(); - - toast.success( - () => ( - - Your Blab was sent - - View - - - ), - { duration: 6000 } - ); }; const handleImageUpload = ( From 7eec7661606b8854beecf52232f8cb65bd8a7124 Mon Sep 17 00:00:00 2001 From: Bekiboo Date: Mon, 15 Jun 2026 10:27:06 +0300 Subject: [PATCH 2/2] fix(blabsy): treat Blab creation as canonical send, counters best-effort MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses review feedback: addDoc and the counter updates ran together in Promise.all, so if addDoc succeeded but manageTotalTweets/manageTotalPhotos rejected, the catch reported "Failed to send" even though the Blab was created — prompting a retry and duplicate Blabs while metrics drift. Await addDoc on its own as the canonical send, then run the counter/reply updates best-effort (logged, non-fatal). Also use tweetRef.id directly instead of an extra getDoc round-trip, removing another post-send failure point (and a read). Co-Authored-By: Claude Opus 4.8 (1M context) --- .../client/src/components/input/input.tsx | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/platforms/blabsy/client/src/components/input/input.tsx b/platforms/blabsy/client/src/components/input/input.tsx index b7d233f4d..da9a6621c 100644 --- a/platforms/blabsy/client/src/components/input/input.tsx +++ b/platforms/blabsy/client/src/components/input/input.tsx @@ -3,7 +3,7 @@ import { useState, useEffect, useRef, useId } from 'react'; import { AnimatePresence, motion } from 'motion/react'; import cn from 'clsx'; import { toast } from 'react-hot-toast'; -import { addDoc, getDoc, serverTimestamp } from 'firebase/firestore'; +import { addDoc, serverTimestamp } from 'firebase/firestore'; import { tweetsCollection } from '@lib/firebase/collections'; import { manageReply, @@ -96,14 +96,19 @@ export function Input({ await sleep(500); - const [tweetRef] = await Promise.all([ - addDoc(tweetsCollection, tweetData), + // Creating the Blab is the canonical "send" — its success defines + // success. addDoc returns a ref whose id is available immediately. + const tweetRef = await addDoc(tweetsCollection, tweetData); + + // Counter/reply updates are secondary: a failure here must NOT report + // the send as failed, or the user retries and duplicates the Blab. + await Promise.all([ manageTotalTweets('increment', userId), tweetData.images && manageTotalPhotos('increment', userId), isReplying && manageReply('increment', parent?.id as string) - ]); - - const { id: tweetId } = await getDoc(tweetRef); + ]).catch((error) => + console.error('Blab sent, but a follow-up counter update failed:', error) + ); if (!modal && !replyModal) { discardTweet(); @@ -117,7 +122,7 @@ export function Input({ Your Blab was sent View