Skip to content

Implement Anytime tasks support and Floater navigation across platforms#80

Merged
ohmzi merged 11 commits into
masterfrom
develop
May 29, 2026
Merged

Implement Anytime tasks support and Floater navigation across platforms#80
ohmzi merged 11 commits into
masterfrom
develop

Conversation

@ohmzi
Copy link
Copy Markdown
Owner

@ohmzi ohmzi commented May 29, 2026

No description provided.

ohmzi added 9 commits May 28, 2026 10:17
…s all platforms

This update introduces the ability to create and manage tasks without a due date ("Anytime" tasks), affecting the backend, mobile apps (Android/iOS), and web interface. It includes database schema migrations, UI refinements for task creation/editing, and a new "Anytime" feed.

### Core Logic & Backend
- **Database Schema**: Updated `Todos` and `CompletedTodos` tables to make the `due` column nullable. Added Android Room migration (v4 to v5) to support nullable `dueEpochMs`.
- **Backend Services**: Modified `TodoService` and `CompletedTodoService` to handle optional due dates. Recurring tasks now strictly require a due date.
- **Validation**: Updated `ContractValidators` to enforce that recurring tasks (`rrule`) must have a `due` date, while standard tasks do not.

### Android (Compose)
- **Anytime Feed**: Introduced `TodoListMode.ANYTIME` and integrated it into `TodoListScreen` and `HomeViewModel`.
- **UX Refinements**:
    - Added a "Schedule" toggle in `CreateTaskBottomSheet` to switch between scheduled and "Anytime" modes.
    - Implemented `RootFeedDock` and `RootCreateTaskButton` in `TdayApp` for centralized navigation between Home and Anytime feeds.
- **Widget**: Updated `TodayTasksWidget` to gracefully handle tasks without due dates.

### iOS (SwiftUI)
- **UX Refinements**:
    - Updated `CreateTaskSheet` with a "Schedule" toggle.
    - Introduced `RootFeedDock` for quick switching between Home and Anytime views in `AppRootView`.
    - Enhanced `HomeScreen` and `TodoListScreen` to display "Anytime" instead of a time string when a due date is missing.
- **Calendar**: Filtered out tasks without due dates from the `CalendarScreen` to prevent layout issues.

### Web & Common
- **API Integration**: Updated TypeScript types and API clients to support optional `due` dates.
- **Data Mapping**: Updated cache mappers on both mobile platforms to handle `null` due dates during synchronization and persistence.
- **UI Consistency**: Standardized the display of "Anytime" labels across task rows and detail views when no due date is present.
Introduce a persistent docking navigation component ("RootFeedDock") to toggle between "Home" and "Anytime" views, featuring a spring-animated sliding selector and scroll-responsive collapsing behavior.

- **Navigation & UI**:
    - Implement `RootFeedDock` in both Android (Compose) and iOS (SwiftUI) with interactive state management, including a "tap-to-expand" behavior when collapsed.
    - Unified the "Anytime" view to use a new hero header style with dynamic daytime/nighttime icons (Sun/Moon) based on the local hour.
    - Standardized dock collapse thresholds to `44.dp` / `44pt` across platforms to improve consistency during list scrolling.

- **Android (Compose)**:
    - Created `RootFeedDock.kt` and `RootCreateTaskButton` for the main navigation interface.
    - Refactored `HomeScreen` and `TodoListScreen` to support the new dock logic and remove legacy cascade animations for list items to improve performance.
    - Updated `TdayApp.kt` navigation graph to handle the `Anytime` route via the root dock state rather than independent screen transitions.
    - Updated `TdaySegmentedSlider` to handle text overflow with ellipses.

- **iOS (SwiftUI)**:
    - Created `RootFeedDock.swift` using `ultraThinMaterial` and `GeometryReader` for a native, fluid feel.
    - Refactored `AppRootView` and `AppViewModel` to manage a synchronized navigation path that treats "Home" and "Anytime" as top-level root states.
    - Updated `TodoListScreen` to support `usesRootFeedHeader` logic, hiding standard navigation bars when the hero header is present.

- **Backend & Tooling**:
    - **Database**: Added a migration to allow unscheduled todos by dropping `NOT NULL` constraints on `due` dates in `todos` and `completedtodo` tables.
    - **Web**: Added a memory-based storage mock for `localStorage` and `sessionStorage` in test environments.
Introduce "Floater" tasks (previously "Anytime" tasks) as a dedicated feature with its own database tables, API endpoints, and UI management logic across Android, iOS, and the Backend. Floaters are distinct from scheduled todos in that they do not require a due date.

- **Backend & Shared Logic**:
    - Created `Floaters`, `FloaterLists`, `CompletedFloaters` tables and corresponding services/routes.
    - Updated `Todos` and `CompletedTodos` schema to strictly require a `due` date, effectively migrating unscheduled tasks to the new Floater system.
    - Added validation logic and DTOs for Floater CRUD operations.

- **Android (Compose)**:
    - Renamed `Anytime` mode to `Floater` across `TodoListMode`, `RootFeedTab`, and `AppRoute`.
    - Implemented `FloaterListRepository` and updated `SyncManager` to handle Floater-specific offline caching and synchronization.
    - Enhanced `TodoListScreen` with a new search header, "My Lists" section for floaters, and specialized sectioning (High/Medium/Low priority).
    - Updated `CreateTaskBottomSheet` to toggle schedule controls based on the current mode.

- **iOS (SwiftUI)**:
    - Introduced `FloaterListRepository` and updated `AppContainer` to manage Floater data.
    - Refactored `AppRootView` and `RootFeedDock` to transition from "Anytime" to "Floater" terminology and navigation.
    - Updated `SwiftData` models to include `CachedFloaterEntity`, `CachedFloaterListEntity`, and `CachedCompletedFloaterEntity`.
    - Implemented a new search interface in `TodoListScreen` specifically for the Floater root view.

- **Data Persistence**:
    - Performed destructive migrations (Android) and schema updates (iOS/Backend) to support the separated Floater tables.
    - Updated `SyncManager` on both platforms to resolve local IDs and merge remote snapshots for Floaters and Floater Lists.
…oss Android and Android

Standardize the design and behavior of bottom sheets, dialogs, and task list interactions to ensure a consistent experience across platforms.

- **Unified Sheet Framework**:
    - **Android**: Introduced `TdaySheetChrome.kt` with `TdaySheetDefaults` and shared components like `TdaySheetHeader`, `TdaySheetCard`, and `TdaySheetSectionTitle` to replace ad-hoc styling.
    - **iOS**: Introduced `TdaySheetChrome.swift` implementing the same standardized metrics, shapes, and header logic.
    - Refactored `CreateTaskSheet`, `ListSettingsBottomSheet`, and `HomeScreen` lists to use these new shared components.

- **Task Swipe & Gesture Improvements**:
    - Implemented a "single-open" swipe policy: opening a swipe action on one task row automatically closes any other open swipe actions in the same list.
    - Updated `todoTrailingSwipeActions` (iOS) and `TaskSwipeRevealState` (Android) to synchronize state via a shared `openSwipeTaskId`.
    - Improved `SwipeActions` (iOS) by allowing the pan gesture to explicitly claim the "active row" status during the initial drag phase.

- **UI & UX Refinements**:
    - **Scroll-to-Top**: Added `scrollToTopRequestID` logic to `HomeScreen` and `TodoListScreen` (Floater tab) to allow tapping the active tab icon to scroll the list to the top.
    - **Pull-to-Refresh**: Enhanced iOS `PullToRefresh` to better handle overscroll distances and added refresh support to `CompletedScreen`, `CalendarScreen`, and `TodoListScreen`.
    - **Visual Tweak**: Updated priority colors and icons for better accessibility (e.g., using `flag.fill` for high priority) and removed the "Floater" text fallback for tasks without due dates in favor of hiding the subtitle.
    - **List Management**: Fixed navigation logic to correctly redirect to the Floater tab when a list is deleted from its detail view.
…droid and iOS

Update the `RootFeedDock` component to support a morphing transition between icons and text labels based on the dock's expansion state. When collapsed, the selected tab displays an icon; when expanded, tabs transition to show text labels.

- **Shared UI Changes**:
    - Redefined dock dimensions: set collapsed width to match height (square aspect) and increased tab width to 102dp/pts to better accommodate text.
    - Added specialized icons for "Home" and "Floater" tabs.

- **Android (Compose)**:
    - Updated `RootFeedDock.kt` to include `Icon` and `Text` layers within tab items.
    - Integrated `expansionProgress` to cross-fade alpha and apply subtle scale transforms during the transition from icon to text.
    - Added `RootFeedTab.icon()` helper to map tabs to `Icons.Rounded` vectors.

- **iOS (SwiftUI)**:
    - Refactored `RootFeedDock.swift` to use a `ZStack` for toggling between `Image`/`RootFeedFloaterIcon` and `Text` based on `isExpanded`.
    - Created `RootFeedFloaterIcon`, a custom drawing of a bubble-style icon using `GeometryReader` and `Circle` shapes.
    - Improved accessibility by adding explicit labels to dock tab buttons.
    - Swapped the "Floater" system image from `tray.full.fill` to `circle.dotted`.
Introduce a standardized `TdayToolbarButtonStyle` and `tdayToolbarButtonEffect` to unify button interactions, replacing manual shadow and animation configurations across the app.

- **Core UI Components**:
    - Implement `TdayToolbarButtonStyle` and `TdayToolbarButtonEffectModifier` to handle ripple effects, scaling, and double-layered shadow animations (ambient and key shadows).
    - Add a `shadowsEnabled` toggle to the new effect modifier to support flat styles in compact layouts.
- **Screen Refactors**:
    - **TodoListScreen**: Replace hardcoded `TdayPressButtonStyle` instances with the new `TdayToolbarButtonStyle` for search and filter buttons.
    - **HomeScreen**: Migrate `HomeToolbarButtonStyle` to use the centralized `.tdayToolbarButtonEffect()`, simplifying the shadow management logic.
Centralize and promote internal selector components to shared UI utilities and tune several visual metrics across the iOS application.

- **Component Refactoring**:
    - Extract `CreateTaskSheetSelectorCard` and `CreateTaskSheetSelectorRow` from `CreateTaskSheet.swift` to `TdaySheetChrome.swift`, renaming them to `TdayCenteredSelectorCard` and `TdayCenteredSelectorRow` for broader reuse.
    - Update `CreateTaskSheet` to utilize these new shared components for list, priority, and repeat rule selection.

- **UI & Layout Refinements**:
    - **TodoList**: Adjust `rootFeedTitleBottomInset` from 0 to 12 in `TodoListScreen` for better spacing.
    - **RootFeedDock**: Update background materials and opacities to improve layering and translucency in both light and dark modes.
    - **FAB Shadows**: Soften the shadow effects on `TaskFloatingActionButton` by reducing radii, offsets, and opacities for a more subtle elevation.

- **Android (Compose)**:
    - Add missing imports to `TdaySheetChrome.kt` (no functional changes in the provided diff beyond setup for future expansion).
…oss platforms

This update standardizes the reminder selection experience by replacing platform-native menus with a custom centered selector overlay/dialog on both iOS and Android. It also refactors the calendar view logic to improve layout stability during transitions.

- **Settings & Reminders (iOS & Android)**:
    - Replaced the standard `Menu` (iOS) and `DropdownMenu` (Android) for default reminder selection with a custom `TdayCenteredSelectorDialog` / `SettingsReminderSelectorOverlay`.
    - Implemented a unified color swatch logic for reminder options to provide consistent visual cues.
    - Added a scale and opacity transition for the new selector overlay on iOS.

- **iOS Calendar UI**:
    - Refactored `CalendarScreen` to group the calendar mode card, error views, and task rows into a single `calendarModeAndTaskSection`.
    - Moved the task list logic into a dedicated `pendingTaskRows` ViewBuilder to simplify the main body.
    - Optimized layout transitions by applying the `calendarModeResizeAnimation` to the entire container rather than individual list rows, reducing visual jitter when switching display modes.

- **Shared Components (Android)**:
    - Renamed and promoted `CenteredSelectorDialog` to `TdayCenteredSelectorDialog` within `CreateTaskBottomSheet.kt` to facilitate reuse across the application.
…droid

This update introduces a "Local Mode" (or "This device" mode) that allows users to use the app without a server connection or account. It refines the onboarding process, disables synchronization logic when in local mode, and updates the UI to reflect the available feature set.

- **Onboarding & Authentication**:
    - **UI/UX**: Redesigned the onboarding wizard with a new "Mode" step to choose between "Self-hosted" and "This device" setups. Improved visual styling with hero tiles, updated chips, and themed iconography.
    - **Logic**: Added `AppDataMode` (`unset`, `server`, `local`) to track the workspace type. Implemented `useLocalMode` in view models to initialize a local-only environment by clearing server config and halting sync loops.

- **Data & Sync Management**:
    - **Local State**: Updated `SyncManager`, `TodoRepository`, `ListRepository`, and `CompletedRepository` to bypass API calls and synchronization logic when `isLocalMode` is active.
    - **Persistence**: Enhanced `SecureStore` (iOS) and `SecureConfigStore` (Android) to persist the selected data mode.
    - **Cache**: Modified `OfflineCacheManager` to normalize state in local mode by stripping pending mutations and sync timestamps.

- **UI Refinements & Feature Gating**:
    - **Gating**: Disabled server-dependent features in Local Mode, including AI summaries, NLP title parsing, admin settings, and the sign-out option.
    - **UX**:
        - Disabled pull-to-refresh across all screens (Home, TodoList, Calendar, Completed) when in local mode.
        - Suppressed the `OfflineBanner` and version check alerts for local workspaces.
        - Updated `SettingsScreen` to hide profile cards and server version info in local mode.

- **Platform Specifics**:
    - **Android**: Updated `TdayApp` navigation logic and `OnboardingWizardOverlay` with Compose-based UI enhancements.
    - **iOS**: Updated `AppRootView` navigation logic and `OnboardingWizardOverlay` with SwiftUI-based UI enhancements; updated project files to include new `AppDataMode` files.
@deepsource-io
Copy link
Copy Markdown

deepsource-io Bot commented May 29, 2026

DeepSource Code Review

We reviewed changes in 32b79b8...910273f on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

Code Review Summary

Analyzer Status Updated (UTC) Details
JavaScript May 29, 2026 7:25p.m. Review ↗
Kotlin May 29, 2026 7:25p.m. Review ↗
Secrets May 29, 2026 7:25p.m. Review ↗

Important

AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.

…ks, and mobile parity

Comprehensive update of the repository documentation to align with recent architectural shifts, specifically focusing on the introduction of Local Mode, Floater/Anytime tasks, and synchronized mobile data models across Android (Room) and iOS (SwiftData).

- **New Documentation**:
    - Created `docs/PRODUCT_DIRECTION.md`: Defines the "north star" for T'Day, including the separation of scheduled tasks vs. floaters and the priority of mobile parity.
    - Created `docs/DATA_MODEL.md`: Centralizes the schema for backend tables, shared KMP DTOs, and mirrored mobile cache entities (Room/SwiftData).
    - Created `docs/REPO_HOUSEKEEPING.md`: Establishes maintenance expectations, markdown audit history, and rules for generated files.

- **Mobile Alignment**:
    - Updated `android-compose/README.md` and `ios-swiftUI/README.md` to reflect identical feature surfaces: Local Mode, Server Mode, `RootFeedDock` navigation, and persistent cache logic.
    - Standardized "Mobile Parity" rules in `CONTRIBUTING.md` and `AGENTS.md`, requiring behavior synchronization between platforms even if implementations differ.

- **Architecture & API**:
    - Updated `docs/ARCHITECTURE.md` to detail the local-first sync flow and the transition from encrypted JSON snapshots to Room/SwiftData persistence.
    - Updated `docs/API_GUIDELINES.md` with new endpoints for Floaters, Floater Lists, and mobile server discovery (`/api/mobile/probe`).
    - Refined `docs/CODING_STANDARDS.md` and `docs/TESTING.md` to include Swift/iOS patterns and enforcement of data contract consistency.

- **Security & Telemetry**:
    - Updated `SECURITY.md` and `docs/TELEMETRY.md` to explicitly state that Local Mode data remains on-device and is excluded from server sync and crash reporting.
@deepsource-io
Copy link
Copy Markdown

deepsource-io Bot commented May 29, 2026

DeepSource Code Review

We reviewed changes in 32b79b8...1c5effd on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

Important

Some issues found as part of this review are outside of the diff in this pull request and aren't shown in the inline review comments due to GitHub's API limitations. You can see those issues on the DeepSource dashboard.

PR Report Card

Overall Grade   Security  

Reliability  

Complexity  

Hygiene  

Code Review Summary

Analyzer Status Updated (UTC) Details
JavaScript May 29, 2026 7:26p.m. Review ↗
Kotlin May 29, 2026 7:26p.m. Review ↗
Secrets May 29, 2026 7:26p.m. Review ↗

Important

AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.

…ks, and mobile parity

Comprehensive update of the repository documentation to align with recent architectural shifts, specifically focusing on the introduction of Local Mode, Floater/Anytime tasks, and synchronized mobile data models across Android (Room) and iOS (SwiftData).

- **New Documentation**:
    - Created `docs/PRODUCT_DIRECTION.md`: Defines the "north star" for T'Day, including the separation of scheduled tasks vs. floaters and the priority of mobile parity.
    - Created `docs/DATA_MODEL.md`: Centralizes the schema for backend tables, shared KMP DTOs, and mirrored mobile cache entities (Room/SwiftData).
    - Created `docs/REPO_HOUSEKEEPING.md`: Establishes maintenance expectations, markdown audit history, and rules for generated files.

- **Mobile Alignment**:
    - Updated `android-compose/README.md` and `ios-swiftUI/README.md` to reflect identical feature surfaces: Local Mode, Server Mode, `RootFeedDock` navigation, and persistent cache logic.
    - Standardized "Mobile Parity" rules in `CONTRIBUTING.md` and `AGENTS.md`, requiring behavior synchronization between platforms even if implementations differ.

- **Architecture & API**:
    - Updated `docs/ARCHITECTURE.md` to detail the local-first sync flow and the transition from encrypted JSON snapshots to Room/SwiftData persistence.
    - Updated `docs/API_GUIDELINES.md` with new endpoints for Floaters, Floater Lists, and mobile server discovery (`/api/mobile/probe`).
    - Refined `docs/CODING_STANDARDS.md` and `docs/TESTING.md` to include Swift/iOS patterns and enforcement of data contract consistency.

- **Security & Telemetry**:
    - Updated `SECURITY.md` and `docs/TELEMETRY.md` to explicitly state that Local Mode data remains on-device and is excluded from server sync and crash reporting.
}
}

suspend fun updateList(
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

`updateList` has a cyclomatic complexity of 22 with "High" risk


Cyclomatic complexity is a software metric that measures the number of
independent paths through a function. A function with high cyclomatic
complexity can be hard to understand and maintain. A higher cyclomatic
complexity indicates that the function has more decision points and is more complex.

}.onFailure { /* pending mutation will be retried by background sync */ }
}

suspend fun updateTodo(todo: TodoItem, payload: CreateTaskPayload) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

`updateTodo` has a cyclomatic complexity of 33 with "Very High" risk


Cyclomatic complexity is a software metric that measures the number of
independent paths through a function. A function with high cyclomatic
complexity can be hard to understand and maintain. A higher cyclomatic
complexity indicates that the function has more decision points and is more complex.

}
}

suspend fun updateFloater(floater: TodoItem, payload: CreateTaskPayload) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

`updateFloater` has a cyclomatic complexity of 23 with "High" risk


Cyclomatic complexity is a software metric that measures the number of
independent paths through a function. A function with high cyclomatic
complexity can be hard to understand and maintain. A higher cyclomatic
complexity indicates that the function has more decision points and is more complex.

}
}

suspend fun moveTodo(todo: TodoItem, due: Instant) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

`moveTodo` has a cyclomatic complexity of 21 with "High" risk


Cyclomatic complexity is a software metric that measures the number of
independent paths through a function. A function with high cyclomatic
complexity can be hard to understand and maintain. A higher cyclomatic
complexity indicates that the function has more decision points and is more complex.


private fun isScheduledTodo(todo: TodoItem, now: Instant = Instant.now()): Boolean {
return !todo.due.isBefore(now)
return todo.due?.isBefore(now) == 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.

Functions with exact one statement, the return statement, can be rewritten with ExpressionBodySyntax.


Functions which only contain a return statement can be collapsed to an expression body. This shortens the code and makes it more readable.


private fun isOverdueTodo(todo: TodoItem, now: Instant = Instant.now()): Boolean {
return todo.due.isBefore(now)
return todo.due?.isBefore(now) == true
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Functions with exact one statement, the return statement, can be rewritten with ExpressionBodySyntax.


Functions which only contain a return statement can be collapsed to an expression body. This shortens the code and makes it more readable.

}
data object FloaterListTodos : AppRoute("floater/list/{listId}/{listName}") {
fun create(listId: String, listName: String): String {
return "floater/list/$listId/${Uri.encode(listName)}"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Functions with exact one statement, the return statement, can be rewritten with ExpressionBodySyntax.


Functions which only contain a return statement can be collapsed to an expression body. This shortens the code and makes it more readable.

@ohmzi ohmzi merged commit 7c5ea3b into master May 29, 2026
3 of 5 checks passed
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