Skip to content

feat: flow mode for tasks#230

Merged
hmbanan666 merged 2 commits into
mainfrom
flow-mode-on-tasks
Oct 17, 2025
Merged

feat: flow mode for tasks#230
hmbanan666 merged 2 commits into
mainfrom
flow-mode-on-tasks

Conversation

@hmbanan666
Copy link
Copy Markdown
Collaborator

@hmbanan666 hmbanan666 commented Oct 17, 2025

Summary by CodeRabbit

  • New Features

    • Flow Mode toggle: shows active tasks in a prioritized, date-ordered list and hides regular lists when enabled
    • Haptic feedback when toggling Flow Mode
    • Shows "Активных задач нет" when no active tasks are present
  • Improvements

    • Active task list now updates separately to keep not-yet-completed tasks current
    • Slight spacing adjustment in task detail timestamps

@hmbanan666 hmbanan666 self-assigned this Oct 17, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Oct 17, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Adds a "flow mode" to the tasks page with a UI switch and vibration feedback, a store flag and ordered active-task list, a new API endpoint to fetch not-completed tasks, and a DB repository method to query tasks where completedAt is null.

Changes

Cohort / File(s) Summary
UI / Task page
apps/atrium-telegram/app/pages/task/my.vue
Replaces simple toggle with a USwitch bound to isInFlowMode, calls vibrate() on change, and conditionally renders: normal lists + CreateCard when off; ordered active TaskActiveCard entries (or empty message) when on.
State / Store
apps/atrium-telegram/app/stores/task.ts
Adds notCompletedTasks (ref), isInFlowMode (ref), myTasksOrderedByDate (computed ordering dated tasks first ascending, undated last), updateNotCompleted() to fetch not-completed tasks, and invokes it from update(). Exposes the new values in the store return.
API endpoint
apps/atrium-telegram/server/api/task/list/not-completed.get.ts
New route handler returning db.task.findAllNotCompleted() (list of tasks not yet completed).
Database repository
packages/database/src/repository/task.ts
Adds static async findAllNotCompleted() to query tasks with completedAt IS NULL, ordered by updatedAt descending.
UI minor spacing
apps/atrium-telegram/app/pages/task/[taskId]/index.vue
Small UI spacing adjustment: increased gap in created/updated/closed section from 0.5 to 1.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant User
    participant UI as Task Page (my.vue)
    participant Store as Task Store
    participant API as /api/task/list/not-completed
    participant DB as Task repository

    User->>UI: Toggle Flow Mode Switch
    UI->>UI: call vibrate()
    UI->>Store: set isInFlowMode
    Store->>Store: updateNotCompleted() (async)
    Store->>API: GET /task/list/not-completed
    API->>DB: findAllNotCompleted()
    DB-->>API: Task[] (notCompleted)
    API-->>Store: Task[]
    Store->>Store: compute myTasksOrderedByDate
    Store-->>UI: notCompletedTasks / myTasksOrderedByDate
    UI->>UI: render TaskActiveCard list (or empty message) when flow mode
    UI-->>User: Updated UI
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • chore: feedback rework #99 — Related vibration/feedback hook changes that expose and parameterize vibrate() used by the new switch handler.
  • chore: inputs rework #100 — Related task-view toggling and isTodayOnly behavior adjustments that interact with isInFlowMode and CreateCard rendering.

Poem

🐰 In flow mode I hop through tasks one by one,
Dates in a row until the daylight is done.
I toggle, I buzz, then I nudge them in line —
Active chores march, a tidy design! ✨

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "feat: flow mode for tasks" directly and clearly describes the main objective of the changeset. The PR introduces a new flow mode feature across the task management system, including UI changes in the task page component, store state management, API endpoint, and database repository layer. The title is concise, specific, and uses terminology that matches the actual feature implementation (isInFlowMode flag, conditional rendering based on flow mode state). A teammate reviewing the repository history would immediately understand that this PR adds flow mode functionality for tasks.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a656112 and c9b9b26.

📒 Files selected for processing (1)
  • apps/atrium-telegram/app/pages/task/[taskId]/index.vue (1 hunks)

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.

@hmbanan666 hmbanan666 merged commit dff39c1 into main Oct 17, 2025
5 checks passed
@hmbanan666 hmbanan666 deleted the flow-mode-on-tasks branch October 17, 2025 12:57
@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown

@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: 3

🧹 Nitpick comments (9)
packages/database/src/repository/task.ts (1)

20-25: Add pagination/limit parity and plan an index for performance.

Mirror findAll()’s limit to prevent unbounded scans and responses. Also consider a partial index to serve this query efficiently.

  • Code change (if you keep the generic method):
-      orderBy: (tasks, { desc }) => desc(tasks.updatedAt),
+      orderBy: (tasks, { desc }) => desc(tasks.updatedAt),
+      limit: 1500,
  • DB index (PostgreSQL):
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_tasks_active_updated_at
  ON tasks (updated_at DESC)
  WHERE completed_at IS NULL;
apps/atrium-telegram/app/pages/task/my.vue (3)

32-42: Load data when enabling flow mode to avoid empty flicker.

If flow mode is toggled before not-completed data arrives, the page shows “Активных задач нет”. Trigger a refresh on toggle-on and add an aria label.

-        <USwitch
-          v-model="taskStore.isInFlowMode"
+        <USwitch
+          v-model="taskStore.isInFlowMode"
           size="xl"
           color="secondary"
           :label="taskStore.isInFlowMode ? `Режим &quot;Поток&quot;` : 'Режим &quot;Списки&quot;'"
           :ui="{
             root: 'items-center',
             label: 'ml-1.5 text-base/5 font-semibold',
           }"
-          @change="vibrate()"
+          aria-label="Переключить режим задач"
+          @change="(val: boolean) => { vibrate(); if (val && !taskStore.notCompletedTasks.length) taskStore.updateNotCompleted?.() }"
         />

Note: expose updateNotCompleted from the store (see store comment).


61-77: Show a loading state for flow mode.

Consider a small loader/skeleton while not-completed tasks are being fetched to prevent the “no tasks” flash.


15-18: Move inline toggle logic into a store action.

Keep UI markup clean and centralize state transitions (e.g., taskStore.toggleTodayOnly() that also disables flow mode).

apps/atrium-telegram/app/stores/task.ts (5)

30-39: Date sorting: avoid timezone pitfalls from new Date('YYYY-MM-DD').

new Date('2025-10-17') parses as UTC; ordering can be off vs local. Use string compare or @internationalized/date for day-precision.

-  const myTasksOrderedByDate = computed(() => {
-    const filterByMe = (task: Task) => task.performerId === userStore.id
-    const sortByDateAsc = (a: Task, b: Task) => a.date && b.date ? new Date(a.date).getTime() - new Date(b.date).getTime() : 0
-    const myTasks = notCompletedTasks.value.filter(filterByMe)
-    const tasksWithDate = myTasks.filter((task) => task.date).sort(sortByDateAsc)
-    const tasksWithoutDate = myTasks.filter((task) => !task.date)
-    return [...tasksWithDate, ...tasksWithoutDate]
-  })
+  const myTasksOrderedByDate = computed(() => {
+    const mine = notCompletedTasks.value.filter((t) => t.performerId === userStore.id)
+    const withDate = mine.filter((t) => !!t.date).sort((a, b) => a.date!.localeCompare(b.date!))
+    const withoutDate = mine.filter((t) => !t.date)
+    return [...withDate, ...withoutDate]
+  })

44-60: Initialize after data is ready to prevent flow-mode flicker.

isInitialized is set before updateCompleted()/updateNotCompleted(). Users can switch to flow mode and briefly see empty state. Set the flag after both calls (or add a separate “isNotCompletedLoaded”).

-      lists.value = data
-
-      isInitialized.value = true
-
-      await updateCompleted()
-      await updateNotCompleted()
+      lists.value = data
+      await Promise.all([updateCompleted(), updateNotCompleted()])
+      isInitialized.value = true

If independent failure handling is desired, use Promise.allSettled and still flip isInitialized once at least lists + one of the datasets is ready.


96-118: Expose updater for on-demand refresh.

Export updateNotCompleted so the page can refresh when toggling flow mode.

   return {
     lists,
     tasks,
     notCompletedTasks,
     isTodayOnly,
     isInFlowMode,
     isInitialized,
@@
     myTasksOrderedByDate,
 
     update,
+    updateNotCompleted,
     setAsFocused,
     setAsUnfocused,
   }

96-118: Headers duplication: factor out auth headers.

Minor: DRY the repeated Authorization header creation to a small helper.


17-20: State naming clarity.

Consider activeTasks instead of notCompletedTasks for positive naming in UI-facing code.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 217b76e and a656112.

📒 Files selected for processing (4)
  • apps/atrium-telegram/app/pages/task/my.vue (3 hunks)
  • apps/atrium-telegram/app/stores/task.ts (5 hunks)
  • apps/atrium-telegram/server/api/task/list/not-completed.get.ts (1 hunks)
  • packages/database/src/repository/task.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
packages/database/src/repository/task.ts (1)
packages/database/src/tables.ts (1)
  • tasks (266-278)
apps/atrium-telegram/server/api/task/list/not-completed.get.ts (1)
apps/web-app/server/services/db.ts (1)
  • db (164-164)
apps/atrium-telegram/app/stores/task.ts (1)
packages/database/src/repository/task.ts (1)
  • Task (6-150)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build

Comment on lines +1 to +5
import { db } from '@roll-stack/database'

export default defineEventHandler(async () => {
return db.task.findAllNotCompleted()
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Route returns all users’ active tasks; enforce auth and scope.

The handler exposes every not-completed task. Read the user from the TMA Authorization header/context and query only their tasks (or tasks in lists they can access). Also prefer return await for better stack traces.

Example:

-import { db } from '@roll-stack/database'
-
-export default defineEventHandler(async () => {
-  return db.task.findAllNotCompleted()
-})
+import { db } from '@roll-stack/database'
+// import your existing auth util; adjust to your codebase
+// e.g., const user = await requireUser(event) or readTelegramUser(event)
+export default defineEventHandler(async (event) => {
+  const user = await requireUser(event) // adapt to your auth helper
+  return await db.task.findAllNotCompletedByPerformer(user.id)
+})

If you already attach the user to event.context, use it directly. Consider projecting only needed fields to reduce payload.

Committable suggestion skipped: line range outside the PR's diff.


⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify no other routes expose unscoped task listings.

Search for unscoped usages to avoid similar leaks.


🏁 Script executed:

#!/bin/bash
# Find unscoped task list endpoints/usages
rg -nP -C2 'findAllNotCompleted\(|completedAt\s*[,)]\s*=>\s*isNull' apps/ packages/ | sed -n '1,200p'
# Look for auth helpers to wire here
rg -nP -C2 'requireUser\(|getUser.*event|telegram|initData|tma ' --type=ts -- apps/ | sed -n '1,200p'

Length of output: 15076



🏁 Script executed:

#!/bin/bash
# Check atrium-telegram app structure and auth
fd '(middleware|auth)' apps/atrium-telegram/server/ -type f
# Look for any existing auth in atrium-telegram
rg -l 'getUserSession|validateTelegramData|requireUser|getHeader' apps/atrium-telegram/server/ --type=ts

Length of output: 220



🏁 Script executed:

#!/bin/bash
# Read atrium-telegram auth middleware to identify auth pattern
cat apps/atrium-telegram/server/middleware/01.auth.ts

Length of output: 1894


Scoped task listing is protected by auth middleware but still returns unscoped results—specify userId scope.

The endpoint is covered by atrium-telegram's auth middleware (which enforces valid Telegram user), but findAllNotCompleted() returns all incomplete tasks regardless of the authenticated user. Scope results to event.context.user.id or add a userId parameter to the database method.

No other routes expose findAllNotCompleted() (grep confirmed single usage).

🤖 Prompt for AI Agents
In apps/atrium-telegram/server/api/task/list/not-completed.get.ts lines 1-5, the
handler calls db.task.findAllNotCompleted() and returns all users' incomplete
tasks despite the route being protected by auth; change the handler to scope
results to the authenticated user by reading the id from event.context.user.id
and passing it into the DB call (e.g., db.task.findAllNotCompleted({ userId:
event.context.user.id })), or if the DB API lacks that param, add a userId
argument to findAllNotCompleted and update its query to filter by userId before
returning results.

Comment on lines +20 to +25
static async findAllNotCompleted() {
return useDatabase().query.tasks.findMany({
where: (tasks, { isNull }) => isNull(tasks.completedAt),
orderBy: (tasks, { desc }) => desc(tasks.updatedAt),
})
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Security: scope the query to the requesting user (avoid leaking all tasks).

This returns every not-completed task across the system. The new API exposes it directly. Scope by performerId (and/or accessible lists) and add a limit to avoid large responses.

Apply something like:

-  static async findAllNotCompleted() {
-    return useDatabase().query.tasks.findMany({
-      where: (tasks, { isNull }) => isNull(tasks.completedAt),
-      orderBy: (tasks, { desc }) => desc(tasks.updatedAt),
-    })
-  }
+  static async findAllNotCompletedByPerformer(userId: string, limit = 1500) {
+    return useDatabase().query.tasks.findMany({
+      where: (tasks, { and, isNull, eq }) =>
+        and(isNull(tasks.completedAt), eq(tasks.performerId, userId)),
+      orderBy: (tasks, { desc }) => desc(tasks.updatedAt),
+      limit,
+    })
+  }

Then update the API route to call the scoped method. If list membership is the authority boundary, replace the performer filter with an existence check against lists the user can access.

🤖 Prompt for AI Agents
In packages/database/src/repository/task.ts around lines 20–25, the current
findAllNotCompleted() returns all not-completed tasks and may leak tasks across
users; change it to accept the calling user context (e.g., performerId and/or
accessibleListIds) and a safe limit, then scope the query to either
tasks.performerId === performerId or tasks that belong to any of the provided
accessibleListIds (use an existence check if list membership is the authority),
include a default max limit (e.g., 100) and allow callers to pass a smaller
limit, and update the API route to call this new scoped method instead of the
unscoped one.

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.

1 participant