Skip to content

feat: Enhance UI components, loading states, and Telegram authentication flow - #86

Open
rayaanr wants to merge 25 commits into
mainfrom
dev
Open

feat: Enhance UI components, loading states, and Telegram authentication flow#86
rayaanr wants to merge 25 commits into
mainfrom
dev

Conversation

@rayaanr

@rayaanr rayaanr commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features
    • Added offline support with automatic fallback and retry.
    • Added installable app branding, icons, metadata, and standalone launch behavior.
    • Added bot transaction history to dashboard activity.
    • Returning Telegram users are redirected to the wallet dashboard.
    • Added animated portfolio value updates in LKR.
  • Bug Fixes
    • Added a clear error message when sharing tasks outside Telegram.
    • Improved settlement text visibility and transfer details in dark mode.
  • UI Improvements
    • Refined plan cards, spacing, loading states, badges, and news layout.
  • Performance
    • Improved news loading and page responsiveness through intelligent caching.

rayaanr and others added 24 commits August 4, 2026 15:49
…dd background overlay to PlanHeroCard and TotalValueCard
…rmanceGrid, and TotalValueCard; remove unnecessary elements for cleaner UI
#83)

* feat: implement Telegram authentication flow with widget support for browser sign-in

* feat: refactor Telegram platform hooks to use non-throwing functions for improved error handling

* feat: implement Telegram OIDC authentication flow and update related components

* feat: implement redirection for non-Telegram users after browser authentication and remove BrowserAuthenticatedScreen component

* feat: enhance user data handling by integrating first and last name in useUser hook and updating verification components

* feat: refactor DashboardTopBar to improve layout and integrate logout functionality for non-Telegram users
* feat: refactor wallet metrics and improve UI components for better performance and user experience

* feat: update caching strategy and revalidation for news articles to enhance performance

* feat: update favicon and add metadata for improved site identity

* feat: implement PWA
@vercel

vercel Bot commented Aug 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
bitcoin-deepa-tma-bot Ready Ready Preview Aug 10, 2026 9:54am
bitcoin-deepa-tma-bot (dev) Ready Ready Preview Aug 10, 2026 9:54am

Request Review

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds PWA offline support, cache revalidation, shared wallet metrics, bot transaction activity, returning-user redirects, animated value rendering, and dashboard UI updates.

Changes

Application updates

Layer / File(s) Summary
Offline app and PWA setup
public/*, src/app/layout.tsx, src/app/manifest.ts, src/app/register-sw.tsx, next.config.mjs
Registers the service worker, defines application metadata, precaches static assets, and serves an offline fallback.
Caching and shared wallet metrics
src/lib/news.ts, src/app/dashboard/news/..., src/hooks/query/useWalletMetrics.ts, src/app/dashboard/{page.tsx,plans/page.tsx}, src/app/dashboard/layout.tsx
Caches parsed news for 900 seconds and centralizes wallet and transaction-derived metrics. Dashboard layout queries preload shared data.
Bot transaction activity flow
src/app/api/transaction/bot-history/route.ts, src/hooks/query/useBotTransactionHistory.ts, src/lib/activity.ts, src/app/dashboard/activity/page.tsx, src/lib/types.ts, src/components/dashboard/activity/ActivityRow.tsx
Adds authenticated bot-history pagination, maps bot transactions into activity items, and combines bot and DCA activity histories.
Returning-user navigation
src/app/page.tsx, src/hooks/query/useRegisterTelegramUser.ts, src/lib/auth.ts
Stores registration status and redirects returning Telegram users to the wallet dashboard.
Dashboard presentation and motion
src/components/motion/number-ticker.tsx, src/components/dashboard/wallet/TotalValueCard.tsx, src/lib/ease.ts, src/app/plans/choose/page.tsx, src/components/ui/plan-card.tsx, src/components/dashboard/tasks/ManageTasksSection.tsx, src/components/dashboard/news/NewsListSection.tsx, src/components/dashboard/plans/PlanHeroCard.tsx
Adds animated LKR totals and updates plan, news, settlement, and Telegram sharing interfaces.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ActivityPage
  participant BotHistoryHook
  participant BotHistoryRoute
  participant BotHistoryAPI
  participant ActivityMapper
  ActivityPage->>BotHistoryHook: Request bot transaction pages
  BotHistoryHook->>BotHistoryRoute: Send bearer token and pagination
  BotHistoryRoute->>BotHistoryAPI: Forward authenticated request
  BotHistoryAPI-->>BotHistoryRoute: Return transaction data
  BotHistoryRoute-->>BotHistoryHook: Return JSON response
  BotHistoryHook->>ActivityMapper: Map transactions
  ActivityMapper-->>ActivityPage: Return activity items
Loading

Possibly related PRs

Suggested labels: Review effort 4/5

Suggested reviewers: helloscoopa

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.39% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes UI and Telegram authentication changes, but it omits major caching, offline support, activity, and wallet metric changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (4)
public/sw.js (1)

1-5: 🎯 Functional Correctness | 🔵 Trivial | 🏗️ Heavy lift

Version precache entries for each release.

A deployment that changes offline.html or an icon without changing /sw.js does not install a new service worker. Existing clients then keep the old bd-static-v1 asset indefinitely.

Generate precache revisions from the build output, or update CACHE_VERSION whenever a precached or unversioned icon asset changes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/sw.js` around lines 1 - 5, Update CACHE_VERSION in the service worker
whenever any PRECACHE_URLS asset changes, including offline.html or the icon
files, so each release installs a new cache and refreshes existing clients;
alternatively, generate CACHE_VERSION from the build output while preserving the
existing static cache naming flow.
src/lib/ease.ts (1)

5-10: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Derive EASE_OUT_CSS from EASE_OUT.

The control points appear twice. If one changes, the other can drift. Build the CSS string from the tuple.

♻️ Proposed refactor
-export const EASE_OUT_CSS = "cubic-bezier(0.16, 1, 0.3, 1)";
+export const EASE_OUT_CSS = `cubic-bezier(${EASE_OUT.join(", ")})`;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/ease.ts` around lines 5 - 10, Update EASE_OUT_CSS to derive its
cubic-bezier values from the EASE_OUT tuple instead of duplicating the control
points, keeping the resulting CSS string unchanged.
src/components/motion/number-ticker.tsx (2)

168-172: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Bind the digit row height to DIGIT_HEIGHT_EM.

Line 169 hardcodes h-[1.1em], but the container height (Line 159) and the translate offset (Line 164) use DIGIT_HEIGHT_EM. If DIGIT_HEIGHT_EM changes, the row height no longer matches the offset step, and the column shows the wrong digit. Tailwind cannot read the TypeScript constant, so use an inline style.

♻️ Proposed refactor
         {DIGITS.map((n) => (
-          <span key={n} className="flex h-[1.1em] items-center justify-center leading-none">
+          <span
+            key={n}
+            className="flex items-center justify-center leading-none"
+            style={{ height: `${DIGIT_HEIGHT_EM}em` }}
+          >
             {n}
           </span>
         ))}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/motion/number-ticker.tsx` around lines 168 - 172, Update the
digit row span in the DIGITS.map rendering to use an inline height style derived
from DIGIT_HEIGHT_EM instead of the hardcoded h-[1.1em] class. Keep the existing
flex, alignment, and leading classes unchanged so the row height stays
synchronized with the container and translate offset.

48-54: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Derive armed instead of setting state in an effect.

The effect only copies inView into state. It causes an extra render and triggers the react-hooks/set-state-in-effect error reported by ESLint. useInView already returns a render-safe value, and once: true keeps it latched.

♻️ Proposed refactor
   const containerRef = useRef<HTMLSpanElement>(null);
   const inView = useInView(containerRef, { once: true, amount: 0.6 });
-  const [armed, setArmed] = useState(!startOnView);
-
-  useEffect(() => {
-    if (startOnView && inView) setArmed(true);
-  }, [startOnView, inView]);
+  const armed = !startOnView || inView;

Remove useState from the import if it becomes unused after both effects are reviewed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/motion/number-ticker.tsx` around lines 48 - 54, Update the
armed value in the number ticker component to derive directly from startOnView
and the latched inView result instead of storing it with useState or updating it
in an effect. Remove the now-unnecessary armed state effect and remove useState
from the import if no other state usage remains; preserve immediate activation
when startOnView is false.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/dashboard/tasks/ManageTasksSection.tsx`:
- Around line 20-23: Update the action logic in ManageTasksSection around
isTelegramEnv and shareStory to treat null as a pending state: disable or show a
pending state until detection resolves, show the unavailable snackbar only when
isTelegramEnv === false, and do not attempt sharing while pending. Wrap the
shareStory invocation in error handling so failures from an unavailable WebApp
handle are caught and surfaced through the existing error state.

In `@src/lib/ease.ts`:
- Around line 51-52: Update the documentation comment for the dragged handles
and fills slider spring configuration to replace “butterily” with clear wording
and describe the configuration as overdamped rather than critically damped,
while retaining that the value follows the pointer and does not rebound at an
end.

---

Nitpick comments:
In `@public/sw.js`:
- Around line 1-5: Update CACHE_VERSION in the service worker whenever any
PRECACHE_URLS asset changes, including offline.html or the icon files, so each
release installs a new cache and refreshes existing clients; alternatively,
generate CACHE_VERSION from the build output while preserving the existing
static cache naming flow.

In `@src/components/motion/number-ticker.tsx`:
- Around line 168-172: Update the digit row span in the DIGITS.map rendering to
use an inline height style derived from DIGIT_HEIGHT_EM instead of the hardcoded
h-[1.1em] class. Keep the existing flex, alignment, and leading classes
unchanged so the row height stays synchronized with the container and translate
offset.
- Around line 48-54: Update the armed value in the number ticker component to
derive directly from startOnView and the latched inView result instead of
storing it with useState or updating it in an effect. Remove the now-unnecessary
armed state effect and remove useState from the import if no other state usage
remains; preserve immediate activation when startOnView is false.

In `@src/lib/ease.ts`:
- Around line 5-10: Update EASE_OUT_CSS to derive its cubic-bezier values from
the EASE_OUT tuple instead of duplicating the control points, keeping the
resulting CSS string unchanged.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ae7b367e-1556-4694-a6d4-cfd5fdd90771

📥 Commits

Reviewing files that changed from the base of the PR and between 3b24741 and 51fe9ed.

⛔ Files ignored due to path filters (6)
  • public/apple-touch-icon.png is excluded by !**/*.png
  • public/icons/icon-192.png is excluded by !**/*.png
  • public/icons/icon-512.png is excluded by !**/*.png
  • public/icons/icon-maskable-192.png is excluded by !**/*.png
  • public/icons/icon-maskable-512.png is excluded by !**/*.png
  • src/app/favicon.ico is excluded by !**/*.ico
📒 Files selected for processing (19)
  • next.config.mjs
  • public/offline.html
  • public/sw.js
  • src/app/dashboard/news/[id]/page.tsx
  • src/app/dashboard/news/page.tsx
  • src/app/dashboard/page.tsx
  • src/app/dashboard/plans/page.tsx
  • src/app/layout.tsx
  • src/app/manifest.ts
  • src/app/plans/choose/page.tsx
  • src/app/register-sw.tsx
  • src/components/dashboard/activity/ActivityRow.tsx
  • src/components/dashboard/tasks/ManageTasksSection.tsx
  • src/components/dashboard/wallet/TotalValueCard.tsx
  • src/components/motion/number-ticker.tsx
  • src/components/ui/plan-card.tsx
  • src/hooks/query/useWalletMetrics.ts
  • src/lib/ease.ts
  • src/lib/news.ts

Comment on lines +20 to +23
const [snackbarError, setSnackbarError] = useState<string | null>(null);
const { openTelegramLink, shareStory } = useTMA();
const { userID, count } = useStore();
const isTelegramEnv = useIsTelegramEnv();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Handle the pending Telegram-environment state separately.

useIsTelegramEnv() starts with null and resolves asynchronously in src/hooks/useIsTelegramEnv.ts, Lines 20-46. The !isTelegramEnv check treats the pending state as unavailable. A Telegram user can tap before detection completes and receive the error instead of sharing.

Show a pending state or disable the action until detection completes. Only show the unavailable snackbar when isTelegramEnv === false. Also catch shareStory, because useTMA can throw while its WebApp handle is still unavailable.

Suggested condition fix
-        if (!isTelegramEnv) {
+        if (isTelegramEnv === null) {
+          setSnackbarError("Checking Telegram availability. Try again.");
+          return;
+        }
+        if (isTelegramEnv === false) {
           setSnackbarError("Open this app inside Telegram to share a story.");
           return;
         }

Also applies to: 41-44

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/dashboard/tasks/ManageTasksSection.tsx` around lines 20 - 23,
Update the action logic in ManageTasksSection around isTelegramEnv and
shareStory to treat null as a pending state: disable or show a pending state
until detection resolves, show the unavailable snackbar only when isTelegramEnv
=== false, and do not attempt sharing while pending. Wrap the shareStory
invocation in error handling so failures from an unavailable WebApp handle are
caught and surfaced through the existing error state.

Comment thread src/lib/ease.ts
Comment on lines +51 to +52
/** Dragged handles and fills (sliders) — critically damped `useSpring` config,
* so the value follows the pointer butterily and never rebounds off an end. */

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the typo and the damping description.

"butterily" is not a word. Also, the config is overdamped, not critically damped: the damping ratio is 50 / (2 * sqrt(700 * 0.5)) ≈ 1.34. The no-rebound behavior still holds.

📝 Proposed wording
-/** Dragged handles and fills (sliders) — critically damped `useSpring` config,
- * so the value follows the pointer butterily and never rebounds off an end. */
+/** Dragged handles and fills (sliders) — overdamped `useSpring` config,
+ * so the value follows the pointer smoothly and never rebounds off an end. */
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/** Dragged handles and fills (sliders) critically damped `useSpring` config,
* so the value follows the pointer butterily and never rebounds off an end. */
/** Dragged handles and fills (sliders) overdamped `useSpring` config,
* so the value follows the pointer smoothly and never rebounds off an end. */
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/ease.ts` around lines 51 - 52, Update the documentation comment for
the dragged handles and fills slider spring configuration to replace “butterily”
with clear wording and describe the configuration as overdamped rather than
critically damped, while retaining that the value follows the pointer and does
not rebound at an end.

…87)

* feat: add bot transaction history API and integrate into activity dashboard

* refactor: streamline user registration flow and improve returning user handling

* feat: add KYC status and subscription current hooks to dashboard layout

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/app/api/transaction/bot-history/route.ts`:
- Around line 34-41: Replace the direct fetch call in the bot-history route with
the repository fetchy wrapper imported from `@/lib/fetchy`, preserving the GET
method, Authorization and content-type headers, pagination query parameters in
apiUrl, and no-cache behavior.

In `@src/app/dashboard/activity/page.tsx`:
- Around line 56-59: Update the filtering predicate using itemCategory in the
activity list so category === "all" bypasses category filtering and retains
every item. Only compare itemCategory against category when category !== "all",
preserving the existing exclusion behavior for specific categories.
- Around line 39-48: The activity pagination logic currently excludes bot
history for the "all" category. Update showBotHistory and the related
hasNextPage/fetchNextPage flow so category "all" includes bot history while
preserving transactions-only behavior for "transactions".

In `@src/app/page.tsx`:
- Around line 193-215: Scope the returning-user marker to the authenticated
Telegram user and require authoritative validation before redirecting. In
src/app/page.tsx lines 193-215, match the cached identity to authData.user.id
and do not let the cached boolean alone trigger router.replace or block the
registration response; in src/lib/auth.ts lines 217-236, store or migrate the
marker as a user-scoped record containing the Telegram ID and remove the legacy
global boolean; in src/hooks/query/useRegisterTelegramUser.ts lines 31-34, pass
the authenticated Telegram user ID when updating or clearing that marker.

In `@src/components/dashboard/news/NewsListSection.tsx`:
- Line 43: Update the category span in NewsListSection to include the min-w-0
utility alongside truncate, allowing long article.category values to shrink and
truncate within the metadata flex row.

In `@src/hooks/query/useBotTransactionHistory.ts`:
- Around line 9-22: Move the BotTransaction interface from
useBotTransactionHistory.ts into src/lib/types.ts as the shared domain type.
Import BotTransaction from src/lib/types.ts in both useBotTransactionHistory and
activity.ts, removing the lib-to-hook dependency while preserving the existing
fields and contract.
- Around line 40-63: Update the useInfiniteQuery configuration in
useBotTransactionHistory to include a non-secret authenticated account
identifier in queryKeys.botTransactions, never the bearer token, so caches are
isolated per user. Also invalidate or remove this query during sign-out and
sign-in authentication transitions using the existing auth lifecycle handling.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ec8a8d22-4c89-4502-b827-63204f98dfaa

📥 Commits

Reviewing files that changed from the base of the PR and between 51fe9ed and e42c8da.

📒 Files selected for processing (13)
  • src/app/api/transaction/bot-history/route.ts
  • src/app/dashboard/activity/page.tsx
  • src/app/dashboard/layout.tsx
  • src/app/page.tsx
  • src/components/dashboard/activity/ActivityRow.tsx
  • src/components/dashboard/news/NewsListSection.tsx
  • src/components/dashboard/plans/PlanHeroCard.tsx
  • src/hooks/query/useBotTransactionHistory.ts
  • src/hooks/query/useRegisterTelegramUser.ts
  • src/lib/activity.ts
  • src/lib/auth.ts
  • src/lib/query-keys.ts
  • src/lib/types.ts

Comment on lines +34 to +41
const response = await fetch(apiUrl.toString(), {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
cache: "no-cache",
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Use fetchy for the upstream request.

Line 34 bypasses the repository HTTP wrapper. Migrate this request to @/lib/fetchy and preserve the authorization header, pagination parameters, and cache behavior.

As per coding guidelines, “All HTTP requests must use the custom fetchy wrapper from @/lib/fetchy.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/api/transaction/bot-history/route.ts` around lines 34 - 41, Replace
the direct fetch call in the bot-history route with the repository fetchy
wrapper imported from `@/lib/fetchy`, preserving the GET method, Authorization and
content-type headers, pagination query parameters in apiUrl, and no-cache
behavior.

Source: Coding guidelines

Comment on lines +39 to +48
const showDca = category !== "transactions";
const showBotHistory = category === "transactions";
const isLoading =
(showDca && dca.isLoading) || (showBotHistory && botHistory.isLoading);
const hasNextPage = (showDca && dca.hasNextPage) || (showBotHistory && botHistory.hasNextPage);
const isFetchingNextPage = dca.isFetchingNextPage || botHistory.isFetchingNextPage;
const fetchNextPage = () => {
if (showDca && dca.hasNextPage) dca.fetchNextPage();
if (showBotHistory && botHistory.hasNextPage) botHistory.fetchNextPage();
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Load both histories in the all category.

showBotHistory is false for all. Therefore, hasNextPage and fetchNextPage never load bot pages after the initial page. Include bot history when category === "all".

Proposed fix
-  const showDca = category !== "transactions";
-  const showBotHistory = category === "transactions";
+  const showDca = category === "all" || category === "plans";
+  const showBotHistory = category === "all" || category === "transactions";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const showDca = category !== "transactions";
const showBotHistory = category === "transactions";
const isLoading =
(showDca && dca.isLoading) || (showBotHistory && botHistory.isLoading);
const hasNextPage = (showDca && dca.hasNextPage) || (showBotHistory && botHistory.hasNextPage);
const isFetchingNextPage = dca.isFetchingNextPage || botHistory.isFetchingNextPage;
const fetchNextPage = () => {
if (showDca && dca.hasNextPage) dca.fetchNextPage();
if (showBotHistory && botHistory.hasNextPage) botHistory.fetchNextPage();
};
const showDca = category === "all" || category === "plans";
const showBotHistory = category === "all" || category === "transactions";
const isLoading =
(showDca && dca.isLoading) || (showBotHistory && botHistory.isLoading);
const hasNextPage = (showDca && dca.hasNextPage) || (showBotHistory && botHistory.hasNextPage);
const isFetchingNextPage = dca.isFetchingNextPage || botHistory.isFetchingNextPage;
const fetchNextPage = () => {
if (showDca && dca.hasNextPage) dca.fetchNextPage();
if (showBotHistory && botHistory.hasNextPage) botHistory.fetchNextPage();
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/dashboard/activity/page.tsx` around lines 39 - 48, The activity
pagination logic currently excludes bot history for the "all" category. Update
showBotHistory and the related hasNextPage/fetchNextPage flow so category "all"
includes bot history while preserving transactions-only behavior for
"transactions".

Comment on lines +56 to +59
const itemCategory = getActivityCategory(item.type);
if (category === "all" ? itemCategory === "transactions" : itemCategory !== category) {
return false;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Correct the all category predicate.

When category === "all", Line 57 returns false for every "transactions" item. This excludes all mapped bot transfers from the all-activity view. Only apply the category comparison when category !== "all".

Proposed fix
-      if (category === "all" ? itemCategory === "transactions" : itemCategory !== category) {
+      if (category !== "all" && itemCategory !== category) {
         return false;
       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const itemCategory = getActivityCategory(item.type);
if (category === "all" ? itemCategory === "transactions" : itemCategory !== category) {
return false;
}
const itemCategory = getActivityCategory(item.type);
if (category !== "all" && itemCategory !== category) {
return false;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/dashboard/activity/page.tsx` around lines 56 - 59, Update the
filtering predicate using itemCategory in the activity list so category ===
"all" bypasses category filtering and retains every item. Only compare
itemCategory against category when category !== "all", preserving the existing
exclusion behavior for specific categories.

Comment thread src/app/page.tsx
Comment on lines +193 to +215
const [cachedReturningUser] = useState(() => getIsExistingUserFromStorage());

// /api/user (Mongo-backed) already fires on every launch — read its
// result instead of ignoring it. status 409 means this Telegram id was
// already registered, i.e. a returning user, who skips straight to the
// dashboard. It only fires when the profile has a username; without one,
// treat the visitor as new rather than waiting on a call that never runs.
const canCheckExisting = !!(authData?.user?.id && authData?.user?.username);
const isReturningUser = cachedReturningUser || registerUser.data?.status === 409;
const checkPending =
!cachedReturningUser &&
canCheckExisting &&
registerUser.data === undefined &&
!registerUser.isError;

useEffect(() => {
if (isReturningUser) {
router.replace("/dashboard?tab=wallet");
}
}, [isReturningUser, router]);

if (checkPending || isReturningUser) {
return <PageShell>{null}</PageShell>;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Do not redirect from an unscoped and unvalidated cached flag.

A flag written for one Telegram account redirects any later account on the same device. A stale flag also redirects before a 201 response can clear it. cachedReturningUser remains true for the current mount, so clearing localStorage does not stop router.replace().

  • src/app/page.tsx#L193-L215: Redirect only after the cached identity matches the current Telegram ID and an authoritative check remains valid. Do not treat the boolean alone as proof.
  • src/lib/auth.ts#L217-L236: Store the Telegram user ID with the marker, or replace the marker with a user-scoped record. Remove or migrate the legacy global boolean.
  • src/hooks/query/useRegisterTelegramUser.ts#L31-L34: Pass the authenticated Telegram user ID when updating or clearing the user-scoped marker.
📍 Affects 3 files
  • src/app/page.tsx#L193-L215 (this comment)
  • src/lib/auth.ts#L217-L236
  • src/hooks/query/useRegisterTelegramUser.ts#L31-L34
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/page.tsx` around lines 193 - 215, Scope the returning-user marker to
the authenticated Telegram user and require authoritative validation before
redirecting. In src/app/page.tsx lines 193-215, match the cached identity to
authData.user.id and do not let the cached boolean alone trigger router.replace
or block the registration response; in src/lib/auth.ts lines 217-236, store or
migrate the marker as a user-scoped record containing the Telegram ID and remove
the legacy global boolean; in src/hooks/query/useRegisterTelegramUser.ts lines
31-34, pass the authenticated Telegram user ID when updating or clearing that
marker.

<Clock size={12} className="shrink-0" />
<span className="shrink-0">{article.readTimeMinutes} min read</span>
<span className="shrink-0">·</span>
<span className="truncate">{article.category}</span>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== candidate files =="
fd -a 'NewsListSection\.tsx|news\.ts|tailwind\.config|global\.css|index\.css' . | sed 's#^\./##'

echo "== NewsListSection outline =="
ast-grep outline src/components/dashboard/news/NewsListSection.tsx --view expanded 2>/dev/null || true

echo "== NewsListSection relevant content =="
cat -n src/components/dashboard/news/NewsListSection.tsx | sed -n '1,120p'

echo "== news.ts relevant content =="
if [ -f src/lib/news.ts ]; then
  cat -n src/lib/news.ts | sed -n '1,220p'
fi

echo "== CSS flex/truncate definitions if Tailwind classes present =="
rg -n "\.truncate|flex-shrink|flex-grow|overflow-hidden|text-ellipsis|min-w-0|flex-1" -S . -g '!node_modules' -g '!dist' -g '!build' | sed -n '1,200p'

echo "== Tailwind/PostCSS config search =="
for f in tailwind.config.js tailwind.config.ts postcss.config.js postcss.config.mjs vite.config.ts next.config.js next.config.mjs; do
  [ -f "$f" ] && { echo "--- $f"; sed -n '1,220p' "$f"; }
done

Repository: CeyLabs/BitcoinDeepaBot-TMA

Length of output: 18839


🌐 Web query:

Tailwind CSS truncate class flex item min-w-0 text-overflow-ellipsis overflow-hidden documentation

💡 Result:

In Tailwind CSS, the truncate class is a utility used to truncate overflowing text with an ellipsis [1][2]. It is a shorthand that applies the following CSS properties to an element [1][3]: overflow: hidden; text-overflow: ellipsis; white-space: nowrap; When using truncate within a flex container, you may encounter an issue where the flex item does not shrink as expected, causing the layout to break or the ellipsis to fail to appear [4][5][6]. This occurs because flex items have a default min-width of auto (similar to min-content), which prevents them from shrinking smaller than their internal content size [5][6]. To resolve this and allow the element to truncate properly, you must apply the min-w-0 class to the flex item [7][5]. The min-w-0 class sets min-width: 0px;, overriding the default flex behavior and allowing the item to shrink to accommodate the truncation [7][5]. Example of correct usage:

Very long text that will now correctly truncate with an ellipsis...

Citations:


🌐 Web query:

Tailwind CSS v4 truncate flex item min-w-0 min-w-0 flex-1 truncate documentation

💡 Result:

To truncate text within a flex container in Tailwind CSS, you must override the default flex item behavior that prevents content from shrinking below its natural size. The Core Problem By default, flex items have a min-width of auto [1][2]. This forces them to be at least as wide as their content, which prevents truncation from working even when the truncate utility (which sets overflow: hidden, text-overflow: ellipsis, and white-space: nowrap) is applied [3][1][2]. The Standard Solution: min-w-0 To allow a flex item to shrink and properly truncate, you must explicitly set its minimum width to 0 [4][1][2]. Example implementation:

This very long text will now truncate properly instead of overflowing the flex container.

Why this works: 1. min-w-0: Applies min-width: 0px, overriding the default min-width: auto and allowing the element to shrink as needed [4][1][2]. 2. flex-1: Often used alongside min-w-0 to allow the element to grow and fill available space [4][2]. 3. truncate: Applies the necessary styles to hide overflow and show an ellipsis [3][5]. New Utility in Tailwind CSS v4.1+ As of Tailwind CSS v4.1, a new utility called wrap-anywhere was introduced [6]. While primarily designed for handling long strings and text wrapping, it allows for mid-word line breaks when calculating the intrinsic size of an element, which can sometimes remove the need to set min-width: 0 on a child element in specific layouts [6]. However, for standard single-line truncation, the min-w-0 approach remains the standard, robust solution [4][2].

Citations:


Make the category flex item shrinkable.

Feed categories are assigned from item.categories?.[0], so long category values can make the metadata row overflow. Add min-w-0 to the category <span> so truncate can work in the flex container.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/dashboard/news/NewsListSection.tsx` at line 43, Update the
category span in NewsListSection to include the min-w-0 utility alongside
truncate, allowing long article.category values to shrink and truncate within
the metadata flex row.

Comment on lines +9 to +22
export interface BotTransaction {
id: number;
time: string;
direction: "incoming" | "outgoing";
from_id: number;
to_id: number;
from_user?: string;
to_user?: string;
type: string;
amount: number;
amount_lkr: string;
memo?: string;
success: boolean;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Move BotTransaction to src/lib/types.ts.

BotTransaction is a domain API contract. src/lib/activity.ts now imports it from a query hook. Define it in src/lib/types.ts, then import it into both modules. This removes the lib-to-hook dependency.

As per coding guidelines, “src/lib/types.ts: Use the core domain types defined in src/lib/types.ts.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/hooks/query/useBotTransactionHistory.ts` around lines 9 - 22, Move the
BotTransaction interface from useBotTransactionHistory.ts into src/lib/types.ts
as the shared domain type. Import BotTransaction from src/lib/types.ts in both
useBotTransactionHistory and activity.ts, removing the lib-to-hook dependency
while preserving the existing fields and contract.

Source: Coding guidelines

Comment on lines +40 to +63
const query = useInfiniteQuery({
queryKey: queryKeys.botTransactions,
queryFn: async ({ pageParam }) => {
const data = await fetchy.get<BotTransactionsResponse>(
`/api/transaction/bot-history?limit=${PAGE_SIZE}&offset=${pageParam}`,
{
headers: { Authorization: `Bearer ${authToken}` },
shouldCache: false,
}
);

const transactions = data.success ? data.transactions : [];
const hasMore = data.success && data.offset + transactions.length < data.count;

return {
transactions,
nextOffset: data.offset + PAGE_SIZE,
hasMore,
};
},
initialPageParam: 0,
getNextPageParam: (lastPage) => (lastPage.hasMore ? lastPage.nextOffset : undefined),
enabled: !!authToken,
staleTime: 1000 * 60 * 5,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Scope the query cache to the authenticated user.

queryKeys.botTransactions has no user identity. If one user signs out and another user signs in, TanStack Query can return the first user’s cached transfers during the five-minute stale window. Add a non-secret account identifier to the query key and clear this query during authentication transitions. Do not use the bearer token as a query-key value.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/hooks/query/useBotTransactionHistory.ts` around lines 40 - 63, Update the
useInfiniteQuery configuration in useBotTransactionHistory to include a
non-secret authenticated account identifier in queryKeys.botTransactions, never
the bearer token, so caches are isolated per user. Also invalidate or remove
this query during sign-out and sign-in authentication transitions using the
existing auth lifecycle handling.

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