Skip to content

Implement calendar features, improve offline sync, and enhance UI#76

Merged
ohmzi merged 47 commits into
masterfrom
develop
May 22, 2026
Merged

Implement calendar features, improve offline sync, and enhance UI#76
ohmzi merged 47 commits into
masterfrom
develop

Conversation

@ohmzi
Copy link
Copy Markdown
Owner

@ohmzi ohmzi commented May 22, 2026

No description provided.

ohmzi and others added 30 commits May 20, 2026 19:49
# Conflicts:
#	android-compose/app/src/main/java/com/ohmz/tday/compose/feature/home/HomeScreen.kt
Replace the standard `DatePicker` with a custom-built `CalendarMonthGrid` in the iOS version to provide a more integrated and styled calendar experience.

- **iOS Improvements**:
    - Introduce `CalendarMonthGrid` and `CalendarMonthDayCell` components featuring month navigation, current day highlighting, and task count indicators.
    - Add `visibleMonth` state to manage calendar navigation independently of the selected date.
    - Switch `CalendarScreen` list style to `.plain` and adjust spacing/insets for a custom card-based layout.
    - Update the top bar "today" action to reset both selected date and visible month.
- **Android Adjustments**:
    - Refactor `CalendarTodoRow` items into a single `item` block containing a `Column` to optimize list rendering and layout consistency.
- **Shared Logic**:
    - Improve task grouping by day to support calendar badge indicators.
Introduces session user caching to allow app bootstrapping while offline. Adds a dedicated server connection probe during user-initiated refreshes with configurable timeouts to improve responsiveness. Refines the offline banner UI with auto-dismiss timing adjustments and unique notice IDs to ensure it triggers correctly on connectivity failures.

- **Sync**: Adds `probeConfiguredServer` to API services and a `verifyServerConnection` step to the sync process.
- **Auth**: Implements cached session user persistence in `SecureStore` (iOS) and `SecureConfigStore` (Android) to support offline startups.
- **UI**: Updates the `OfflineBanner` on both platforms with a 2-second auto-dismiss and improved messaging.
- **iOS**: Customizes `PullToRefreshContainer` to allow better control over refreshing states and animations.
- **Android**: Enhances `TdayPullRefresh` with scale and translation animations and visual styling refinements.

Signed-off-by: ohmzi <6551272+ohmzi@users.noreply.github.com>
Introduces the ability to "uncomplete" or restore completed tasks on both Android and iOS, featuring a multi-phase animation sequence (unchecking, unstrikethrough, and fading) to provide visual feedback before the item is moved back to the active list. Additionally, refactors segmented controls across both platforms into reusable components to ensure consistent styling and behavior.

- **Completed Tasks**:
    - Implements `uncomplete` logic in `CompletedViewModel` for both platforms, including a mandatory re-scheduling of task reminders.
    - Adds a multi-stage restoration animation in `CompletedScreen` (Android/iOS) with haptic feedback.
    - Updates `CompletedTimelineRow` to support interactive toggle icons and animated state transitions.
- **UI Components**:
    - **Android**: Introduces `TdaySegmentedSlider` to replace manual implementations in `SettingsScreen` and `CalendarScreen`.
    - **iOS**: Introduces `TdayNativeSegmentedControl` (based on `UISegmentedControl`) to unify the appearance of mode selectors in `SettingsScreen` and `CalendarScreen`.
- **Shared Logic**:
    - Ensures server sync and local cache hydration occur after a task is restored.
Introduces a custom-styled segmented slider component with smooth animations and haptic feedback.

- **UI**: Implements `TdaySegmentedSlider` with support for custom accent colors, dynamic labels, and interactive states.
- **Animations**: Adds spring-based animations for selector movement, press scaling, and alpha transitions.
- **Interactions**: Integrates `MutableInteractionSource` to handle per-segment press states and provides haptic feedback on selection changes.
- **Styling**: Uses `MaterialTheme` color schemes with custom overlays and shadows for a polished, modern look.
Update `TodoListScreen` to use theme-aware background colors across various list elements and views, replacing transparent backgrounds for better visual consistency.

- **UI**: Replace `.listRowBackground(Color.clear)` with `.listRowBackground(colors.background)` for dividers, rows, error views, and scroll tracking elements.
- **Layout**: Explicitly set the background color of the main list containers and apply `listRowSpacing(0)` to the plain list styles.
- **Styling**: Refactor section header padding and inset logic to ensure consistent spacing when pinned.
Refactor home screen tiles to use a shared shape constant for background layers and apply explicit clipping and content shapes. This ensures visual consistency and improves the accuracy of touch interactions.

- **iOS**:
    - Extract `RoundedRectangle` into a local `shape` constant within the home list button.
    - Apply `.clipShape(shape)` and `.contentShape(shape)` to ensure the button's interactive area and content perfectly match its rounded visual style.
…cing `TdayTodayBlue` and refining the UI components in the Settings screen.

This update replaces hardcoded purple and varying blue shades with a unified `tdayTodayBlue` (RGB: 110, 168, 225). It also refines the visual styling of segmented controls, switches, and labels to ensure a more cohesive and polished look across both platforms.

- **Color System**:
    - Introduced `TdayTodayBlue` as a shared constant in both Swift and Kotlin.
    - Updated `Secondary` theme colors in light and dark modes to use `TdayTodayBlue`.
    - Replaced the purple accent (`0xFF7D67B6`) in `SettingsScreen` and `TdaySegmentedSlider` with the new brand blue.

- **Android (Compose)**:
    - **TdaySegmentedSlider**: Refactored styling for better contrast and depth.
        - Implemented dynamic track and border colors based on theme luminance.
        - Updated selector logic to use a layered background approach with specific alphas for press states and theme modes.
        - Adjusted text weights and colors for better legibility (switched from primary accent to `onSurface` for selected items).
    - **SettingsScreen**:
        - Customized `Switch` colors to use a white thumb and secondary track color when checked.
        - Updated version info and reminder labels to use `colorScheme.secondary`.

- **iOS (SwiftUI)**:
    - **SettingsScreen**:
        - Updated the segmented control accent color to `tdayTodayBlue`.
        - Shifted foreground colors for version availability and default reminder values from `primary` to `secondary`.
        - Added `.tint(colors.secondary)` to the admin AI summary toggle for visual consistency.
…nd iOS.

This update enhances the transition when selecting a task from search results by introducing coordinated delays and smoother scrolling animations to ensure the target task is properly centered and highlighted in the list.

- **Android (Compose)**:
    - **HomeScreen**: Added `openTaskFromSearch` to handle keyboard dismissal and focus clearing before navigating. Introduced a `140ms` delay to allow search UI animations to complete before transitioning.
    - **TodoListScreen**: Refactored scroll logic to use `animateScrollToItem` with a calculated offset to center the target task. Added a follow-up `animateScrollBy` for fine-grained centering after the initial layout.
    - Introduced constants for scroll delays (`260ms`, `70ms`) and fine-tuning durations (`320ms`).

- **iOS (SwiftUI)**:
    - **HomeScreen**: Implemented `openSearchResult` with a `140ms` delay and state-based debounce to prevent multiple simultaneous navigations. Replaced search result `Button` with `onTapGesture` and `contentShape` for better interaction handling.
    - **TodoListScreen**: Updated `scrollToHighlightedTodo` to target specific task IDs instead of section headers. Added `searchResultScrollDelay` (`0.30s`) and `searchResultScrollDuration` (`0.52s`) to improve the visual flow when deep-linking to a task.
    - Improved thread safety for concurrent scroll requests using a `highlightedScrollRequestID`.

- **UX Refinements**:
    - Standardized the "flash" highlight timing across both platforms to trigger only after the scroll animation has settled.
    - Ensured the software keyboard is explicitly dismissed on both platforms when a search result is selected.
…rch results on Android and iOS.

This update optimizes the transition from search results to the task list by reducing navigation delays and improving the precision and smoothness of the "scroll to highlighted item" behavior.

- **Search Navigation**:
    - **Android**: Removed `SEARCH_RESULT_NAVIGATION_DELAY_MS` (140ms) and the `searchResultOpening` guard state to allow immediate navigation upon selecting a search result.
    - **iOS**: Removed the explicit search motion delay, triggering navigation immediately while maintaining a brief cooldown to reset the opening state.

- **Scrolling Logic & Precision**:
    - Introduced a "pre-scroll" mechanism on both platforms that jumps to an item a few positions before the target before performing the final animated centering. This ensures the target item is within the layout viewport for more reliable animation.
    - **Android (Compose)**:
        - Increased scroll durations and delays (`SEARCH_RESULT_CENTER_SCROLL_DURATION_MS` to 820ms) for a more deliberate feel.
        - Added logic to expand collapsed sections with a 80ms delay before initiating the scroll.
    - **iOS (SwiftUI)**:
        - Refined `highlightedTodoTarget` to return both the target and a pre-scroll anchor.
        - Increased scroll and flash delays to synchronize better with the list expansion and navigation transitions.

- **Constants & Cleanup**:
    - Standardized `searchResultPreScrollItemCount` to 5 items across both platforms.
    - Updated various motion constants (delays and durations) to align the visual experience across Android and iOS.
…in `HomeScreen`.

This update replaces the fixed maximum height for search results with a calculated height based on the number of items. This ensures the dropdown matches its content size for small result sets while still respecting the maximum height limit for longer lists.

- **Dynamic Height Logic**:
    - Add constants for row height (`66`), vertical padding (`8`), and separator height (`1`).
    - Implement a `resultsHeight` computed property that calculates the total height based on the number of tasks and separators.
    - Constrain the final height using `min(contentHeight, maxResultsHeight)` to maintain the 320pt cap.
- **UI Integration**:
    - Update the search results container in `HomeScreen` to use the dynamic `resultsHeight` instead of a static `maxHeight`.
…Android and iOS.

This update standardizes the pull-to-refresh visual dimensions across platforms, improves the reliability of refresh triggers in SwiftUI, and streamlines the highlighted item scrolling logic in the Android Compose client.

- **Pull-to-Refresh Enhancements**:
    - **Visuals (Android & iOS)**: Increased dimensions for the refresh indicator container, dots, and spacing. Updated corner radius and offsets for a bolder appearance.
    - **iOS (SwiftUI)**: Replaced the standard `.refreshable` modifier with a custom manual trigger logic using `PullProgressChange`. This ensures more reliable execution and state synchronization between `isRefreshing` and local flight states.
    - **iOS (SwiftUI)**: Improved `UIScrollView` detection logic to better locate the nearest scrollable area (searching both ancestors and descendants).
    - **iOS (SwiftUI)**: Set `scrollBounceBehavior` to `.always` on the Home screen to ensure pull-to-refresh is accessible even with short content.

- **Android (Compose) List Improvements**:
    - **Scroll Logic**: Simplified the highlighted todo navigation in `TodoListScreen`. Replaced the multi-step delayed animation (pre-scroll, delay, center scroll) with a single `scrollToItem` call using a calculated centered offset.
    - **Section State**: Updated `collapsedSectionKeys` to remain expanded when a `highlightedTodoId` is present, ensuring search results or deep-linked items are visible.
    - **Clean-up**: Removed several unused constants related to the legacy multi-stage scroll animation and layout delays.

- **iOS App State & Sync**:
    - **Offline Handling**: Refined `confirmOfflineSyncFailure` in `AppViewModel` to perform a forced sync with a connection probe before marking the app as offline. This reduces false-positive offline notifications.
    - **UI**: Removed the redundant `ProgressView` from the Home screen when loading the initial summary.
… search navigation and pull-to-refresh UI.

This update introduces logic to automatically re-sync data when the app returns to the foreground, improves the reliability of navigating to specific tasks from search results, and refines the pull-to-refresh animation across Android and iOS.

- **App Lifecycle & Sync**:
    - **Android & iOS**: Added `reconnectAfterForeground()` to `AppViewModel`. This triggers a background sync and refreshes the realtime connection when the app is resumed from a paused or background state.
    - Added a `LifecycleEventObserver` on Android and `scenePhase` observation on iOS to detect foreground transitions.
    - Centralized server reconnection logic to ensure consistent behavior across connectivity changes and manual refreshes.

- **Search & Navigation (Android)**:
    - **Search Result Handling**: Added a short delay (`260ms`) when opening a task from search results to allow for a smoother transition before closing the search overlay.
    - **Reliable Highlighting**: Implemented a `PENDING_SEARCH_HIGHLIGHT_TODO_ID` in the navigation `SavedStateHandle`. This ensures that when navigating to the "All Todos" screen from search, the target task is correctly identified and highlighted even if the navigation takes time to settle.
    - **Smooth Scrolling**: Replaced `scrollToItem` with `animateScrollToItem` when highlighting a task, adding a `480ms` delay to ensure the list layout has stabilized.

- **Pull-to-Refresh UI Refinement**:
    - **Visual Polish**: Increased the number of animated bars from 3 to 5 and added a horizontal "sweep" background effect that tracks pull progress and animates during refreshing.
    - **Metrics**: Standardized dimensions across platforms (e.g., container width increased from `72dp` to `116dp`, adjusted dot spacing and heights).
    - **Shadows & Transitions**: Updated shadow colors and opacities to better align with the brand theme; refined scale and offset animations for a more fluid feel.

- **Bug Fixes & Clean-up**:
    - Fixed an issue where the offline notice wouldn't consistently trigger during network-driven sync failures.
    - Standardized connectivity probe timeouts for background refreshes.
…ftUI client.

This update adjusts the dimensions, styling, and layout logic of the `TdayPullRefreshIndicator` to provide a more polished and prominent visual experience during the pull-to-refresh gesture.

- **Metrics & Dimensions**:
    - Increased `triggerDistance` from 98 to 112 and updated container dimensions to 152x58.
    - Adjusted dot sizes (width 9, max height 30) and spacing (10).
    - Updated `sweepHeight` to 40 and `cornerRadius` to 29 for a rounder appearance.

- **Styling & Effects**:
    - Added a subtle stroke to the internal sweep area for better definition.
    - Updated background opacity logic for the sweep area based on pull progress.
    - Refined shadow values (radius and offsets) for both the primary indicator and the black drop shadow to enhance depth.
    - Adjusted entrance animation offsets and scale effects for a smoother appearance.

- **Layout & Logic**:
    - Refactored the internal `ZStack` to use a `clipShape`, ensuring the background sweep effect stays contained within its track.
    - Simplified bar metric calculations by removing the manual `verticalOffset`, relying on the `HStack` alignment instead.
    - Removed the manual horizontal offset calculation for the sweep indicator in favor of a centered layout within the track.
… in the Android Compose client.

This update overhauls the pull-to-refresh component with more robust gesture handling and updated styling, refactors title collapse logic for better performance, and improves search result navigation.

- **Pull-to-Refresh Enhancements**:
    - **Visuals**: Updated `TdayPullRefresh` with new dimensions, increased container size, larger dots, and refined elevation/spacing constants in `Dimens.kt`.
    - **Interaction**: Implemented explicit pointer detection (`PointerEventPass.Initial`) to track user pull state, ensuring content offsets only apply during active manual pulls.
    - **Rendering**: Replaced `pullToRefreshIndicator` with a custom `graphicsLayer` implementation and added `clipRect` to handle drawing outside bounds during transitions.
    - **Animation**: Adjusted spin duration and added scaling effects to the refresh indicator.

- **List & Navigation Improvements**:
    - **Search Scrolling**: Introduced `animateSearchResultScrollToItem`, a multi-pass scroll logic that estimates item positions and corrects for dynamic content height to accurately center search results.
    - **Collapse Logic**: Simplified title collapse behavior across `TodoListScreen`, `CalendarScreen`, `CompletedScreen`, and `SettingsScreen`. Replaced `NestedScrollConnection` and `animateFloatAsState` with `derivedStateOf` based on list scroll offsets for a more direct, non-animated response.
    - **Performance**: Added `contentType` to `LazyColumn` items to improve composition performance and reused static `DateTimeFormatter` instances for task rows.

- **UI/UX Refinements**:
    - Standardized date and time formatters across various task row types.
    - Reduced `SEARCH_RESULT_NAV_SETTLE_DELAY_MS` for snappier navigation transitions.
    - Fixed vertical centering of refresh bar dots by zeroing out previous offsets.
…Android and iOS clients.

This update replaces standard empty state UI components with a new "watermark" style background illustration (large, faded, rotated icons) and centered text messages. This ensures a consistent, modern aesthetic when screens have no content to display.

- **Shared UI Components**:
    - **iOS**: Introduced `EmptyTaskWatermark` in `TdayTheme.swift`, featuring a large system icon with custom blending, rotation, and positioning.
    - **Android**: Integrated `EmptyTaskWatermark` and `EmptyTaskBackgroundMessage` (Compose) to handle empty state visuals.

- **Feature Integration**:
    - **Todo List & Search**: Updated `TodoListScreen` on both platforms to display mode-specific watermarks (e.g., sun for "Today," flag for "Priority," or custom list icons).
    - **Calendar**: Added a "calendar" watermark and centered empty message when no tasks are scheduled for a selected date.
    - **Completed**: Implemented a "checkmark" watermark for the completed tasks screen.

- **UI/UX Refinements**:
    - **Android**: Refactored `LazyColumn` layouts into `Box` containers to overlay watermarks behind the list area.
    - **iOS**: Simplified `TimelineCategoryEmptyState` by removing redundant icon logic, relying instead on the new background watermark overlay.
    - Updated specific iconography: Changed "Overdue" from a clock to an error outline/exclamation mark and "Scheduled" to a clock/calendar icon.
    - Ensured empty states only trigger when `isLoading` is false to prevent flickering during data fetches.

- **Code Clean-up**:
    - Removed legacy empty state composables and view modifiers (`EmptyTimelineState`, `EmptyCalendarState`) in favor of the new standardized components.
    - Optimized logic for selecting icons based on the current list mode or custom list keys.
…termark and background message.

This update refactors the "empty state" presentation across the Completed, Todo, and Calendar screens. It replaces scattered text views and legacy empty state components with a consistent `ZStack` layout that pairs the `EmptyTaskWatermark` with a new `EmptyTaskBackgroundMessage` component.

- **Theme & Components**:
    - **EmptyTaskWatermark**: Refactored to use `GeometryReader` for more precise positioning. Increased icon size to `194` and adjusted the layout to align relative to the bottom-trailing area (2/3 down the screen height).
    - **EmptyTaskBackgroundMessage**: Introduced as a reusable component for empty state text, featuring a bold rounded font, center alignment, and horizontal padding.

- **Feature Integration**:
    - **CompletedScreen**: Replaced `TimelineEmptyState` with the unified watermark and "No completed tasks" message overlay.
    - **TodoListScreen**: Removed `TimelineEmptyState` and `TimelineCategoryEmptyState`. Replaced with a `ZStack` overlay containing the appropriate watermark and dynamic category message.
    - **CalendarScreen**: Removed the inline "No pending task..." list row. Added an overlay containing the calendar watermark and background message when no tasks are present.

- **Clean-up**:
    - Removed unused metrics (`emptyStateSize`, `emptyStateOffset`) from `TodoTimelineMetrics`.
    - Deleted legacy `TimelineEmptyState` and `TimelineCategoryEmptyState` struct definitions from `TodoListScreen.swift`.
    - Applied `.allowsHitTesting(false)` and `.accessibilityHidden(true)` to background decorations to ensure they don't interfere with user interactions.
…mpty states and standardize its implementation across Android and iOS screens.

This update refactors the empty state UI by creating a dedicated `EmptyTaskWatermark` and `EmptyTaskBackgroundMessage` system. On Android, the layout logic is moved outside of scrollable containers to ensure watermarks remain fixed and correctly layered, while iOS receives corresponding cleanup.

- **New Core UI Components (Android)**:
    - **EmptyTaskWatermark**: A decorative, large-scale icon watermark positioned with a slight rotation and opacity. It supports optional accent color blending.
    - **EmptyTaskBackgroundMessage**: A bold, centered text component for display when no tasks are present.

- **Feature Integration & Refactoring**:
    - **TodoListScreen & CompletedScreen (Android)**: Refactored the layout to wrap the `LazyColumn` and empty state components in a `Box`. This ensures watermarks are rendered relative to the screen dimensions rather than as list items, preventing them from scrolling or affecting list spacing.
    - **Timeline Adjustments**: Refined spacing in `TodoListScreen` by increasing `timelineHeaderBodySpacing` to `4.dp` and adding top padding to section headers (except the first) for better visual rhythm.
    - **CalendarScreen cleanup**: Removed local implementations of empty state watermarks on both Android and iOS in favor of a more consistent architectural approach.

- **Layout Fixes**:
    - Wrapped `LazyColumn` in an additional `Box` within `Scaffold` content to properly handle system padding while allowing full-screen overlays like the watermark to be positioned accurately.
…g for Android and iOS.

This update introduces a standardized title collapse mechanism across multiple screens on Android using a new snapping utility. It also refines how task sections are ordered in the "All" todo list view and improves the robustness of scroll snapping on iOS.

- **Android (Compose)**:
    - **Title Collapse & Snapping**:
        - Introduced `snapTitleCollapsePx` utility to calculate whether a header should snap to a collapsed or expanded state based on scroll velocity and position.
        - Integrated `NestedScrollConnection` and `onPreFling` logic in `TodoListScreen`, `CalendarScreen`, `CompletedScreen`, `LatestReleaseScreen`, and `SettingsScreen` to handle manual and velocity-based header snapping.
        - Added `animateFloatAsState` for smoother transitions of the collapse progress across all top-level screens.
        - Added a `LaunchedEffect` to ensure headers snap to a valid state (fully expanded or fully collapsed) when scrolling stops.
    - **Todo List Logic**:
        - Modified `buildScheduledSections` to allow conditional placement of the "Earlier" section. In "All" mode, "Earlier" tasks now appear before "Today" to improve chronological flow.
        - Updated `TodoListScreen` to automatically collapse the "Today" header when navigating to a specific task search result.

- **iOS (SwiftUI)**:
    - **PullToRefresh Snapping**:
        - Refactored `PullToRefresh` snapping logic to use a `Timer`-based check (`scheduleSnapCheck`) ensuring the scroll view is fully idle (not dragging or decelerating) before snapping.
        - Added `maxScrollableOffset` validation to prevent snapping issues on short lists where the content doesn't exceed the collapse distance.
        - Improved memory management by invalidating the snap timer on deinitialization.

- **Clean-up**:
    - Removed `derivedStateOf` logic in favor of more performant state-driven animations for header progress.
    - Standardized naming and constants for collapse thresholds and durations.
…troduce a reusable swipe action component.

This update synchronizes timeline header logic and swipe action appearances across the iOS app while adding a dedicated UI component for swipe actions in the Android client. Key changes include consistent tinting for task interactions, improved header collapse animations, and adjustments to timeline section ordering.

- **iOS (SwiftUI)**:
    - **Swipe Actions**: Introduced `TaskSwipeActionTint` to define standard RGB colors for "edit" (blue) and "delete" (red) actions. Updated `CompletedScreen`, `TodoListScreen`, and `CalendarScreen` to use these consistent tints.
    - **Timeline Header & Metrics**:
        - Increased `expandedTitleHeight` from 42 to 56 in `TodoTimelineMetrics`.
        - Refined `CalendarHeroTitle` to handle height collapse based on scroll progress, ensuring a smoother transition to the pinned state.
    - **Timeline Logic**:
        - Modified `buildFutureTimelineSections` to support conditional positioning of the "Earlier" section. In "All Tasks" mode, "Earlier" now appears before "Today", while in "Priority" or "List" modes, it appears after "Today".
        - Simplified scroll progress calculations in `SettingsScreen` by removing the "snapped" progress and elastic top insets.
    - **Bug Fixes & Cleanup**: Removed the `.destructive` role from delete buttons to allow for custom `.tint` application while maintaining standard swipe behavior.

- **Android (Compose)**:
    - **New Component**: Added `TaskSwipeActionButton`, a reusable component for swipeable list items. It features:
        - Animated scaling and alpha transitions based on reveal progress.
        - Haptic-friendly touch feedback using `MutableInteractionSource` and `animateFloatAsState`.
        - Standardized layout with a card-based icon container and a bold label.

- **UI/UX Refinements**:
    - Standardized row heights and spacing across different timeline modes to ensure visual consistency between the calendar and task lists.
…troduce a reusable swipe action component.

This update synchronizes timeline header logic and swipe action appearances across the iOS app while adding a dedicated UI component for swipe actions in the Android client. Key changes include consistent tinting for task interactions, improved header collapse animations, and adjustments to timeline section ordering.

- **iOS (SwiftUI)**:
    - **Swipe Actions**: Introduced `TaskSwipeActionTint` to define standard RGB colors for "edit" (blue) and "delete" (red) actions. Updated `CompletedScreen`, `TodoListScreen`, and `CalendarScreen` to use these consistent tints.
    - **Timeline Header & Metrics**:
        - Increased `expandedTitleHeight` from 42 to 56 in `TodoTimelineMetrics`.
        - Refined `CalendarHeroTitle` to handle height collapse based on scroll progress, ensuring a smoother transition to the pinned state.
    - **Timeline Logic**:
        - Modified `buildFutureTimelineSections` to support conditional positioning of the "Earlier" section. In "All Tasks" mode, "Earlier" now appears before "Today", while in "Priority" or "List" modes, it appears after "Today".
        - Simplified scroll progress calculations in `SettingsScreen` by removing the "snapped" progress and elastic top insets.
    - **Bug Fixes & Cleanup**: Removed the `.destructive` role from delete buttons to allow for custom `.tint` application while maintaining standard swipe behavior.

- **Android (Compose)**:
    - **New Component**: Added `TaskSwipeActionButton`, a reusable component for swipeable list items. It features:
        - Animated scaling and alpha transitions based on reveal progress.
        - Haptic-friendly touch feedback using `MutableInteractionSource` and `animateFloatAsState`.
        - Standardized layout with a card-based icon container and a bold label.

- **UI/UX Refinements**:
    - Standardized row heights and spacing across different timeline modes to ensure visual consistency between the calendar and task lists.
…he SwiftUI client.

This update replaces the static hero title row with a dynamic `CalendarElasticTopBar` that responds to scroll gestures. The new implementation features a sophisticated handoff animation between expanded and collapsed states, including parallax offsets and alpha transitions.

- **Animation & Layout**:
    - Define `CalendarTitleHandoff` constants to manage collapse distances (180pt), fade thresholds, and snapping behaviors.
    - Implement `CalendarElasticTopBar` which transitions from a large hero title to a centered navigation bar title as the user scrolls.
    - Apply `scaleEffect` and vertical offsets to the collapsed title for a "reveal" effect from behind the top bar buttons.
    - Adjust the expanded title's opacity and position using linear progress mapping to ensure smooth fading and lifting transitions.

- **Scroll Interaction**:
    - Introduce `CalendarTitleCollapseScrollObserver`, a `UIViewRepresentable` that monitors the underlying `UIScrollView`'s pan gestures and content offsets.
    - Implement custom scroll consumption logic to prioritize title collapse/expansion before allowing the list content to scroll.
    - Add snapping logic via a timer-based check to ensure the title always settles in either a fully expanded or fully collapsed state.
    - Added `sliderPartialSnapDistance` to automatically snap the calendar view mode tabs to the top if they are partially visible after a scroll.

- **UI Refinements**:
    - Refactor top bar buttons into `CalendarTopBarButton` with support for plain, filled, and outlined chrome styles.
    - Use `TdayPressButtonStyle` for consistent haptic and visual feedback on button interactions.
    - Clean up the main `CalendarScreen` by removing the manual `calendarHeroTitleRow` in favor of the inset `CalendarElasticTopBar`.
ohmzi added 16 commits May 21, 2026 22:26
…he SwiftUI client.

This update replaces the static hero title row with a dynamic `CalendarElasticTopBar` that responds to scroll gestures. The new implementation features a sophisticated handoff animation between expanded and collapsed states, including parallax offsets and alpha transitions.

- **Animation & Layout**:
    - Define `CalendarTitleHandoff` constants to manage collapse distances (180pt), fade thresholds, and snapping behaviors.
    - Implement `CalendarElasticTopBar` which transitions from a large hero title to a centered navigation bar title as the user scrolls.
    - Apply `scaleEffect` and vertical offsets to the collapsed title for a "reveal" effect from behind the top bar buttons.
    - Adjust the expanded title's opacity and position using linear progress mapping to ensure smooth fading and lifting transitions.

- **Scroll Interaction**:
    - Introduce `CalendarTitleCollapseScrollObserver`, a `UIViewRepresentable` that monitors the underlying `UIScrollView`'s pan gestures and content offsets.
    - Implement custom scroll consumption logic to prioritize title collapse/expansion before allowing the list content to scroll.
    - Add snapping logic via a timer-based check to ensure the title always settles in either a fully expanded or fully collapsed state.
    - Added `sliderPartialSnapDistance` to automatically snap the calendar view mode tabs to the top if they are partially visible after a scroll.

- **UI Refinements**:
    - Refactor top bar buttons into `CalendarTopBarButton` with support for plain, filled, and outlined chrome styles.
    - Use `TdayPressButtonStyle` for consistent haptic and visual feedback on button interactions.
    - Clean up the main `CalendarScreen` by removing the manual `calendarHeroTitleRow` in favor of the inset `CalendarElasticTopBar`.
… Android and iOS.

This update ensures that the application splash screen remains visible for at least one second, preventing a "flickering" experience during fast app initialization or bootstrap sequences.

- **Android (Compose)**:
    - Introduce `SPLASH_MIN_DISPLAY_MS` constant set to 1,000ms.
    - Update `AppNavigationEffect` to track the splash start time and inject a `delay` if the initial loading and authentication checks complete faster than the minimum duration.
    - Define `isStartupSplashRoute()` to identify initial entry routes (Splash, Login, ServerSetup) where the delay should apply.

- **iOS (SwiftUI)**:
    - Add `hasMetLaunchSplashMinimum` state to `AppRootView` to track the splash timing requirement.
    - Implement a `.task` modifier that sleeps for 1 second before allowing the transition from `AppLaunchSplashView` to the main content.
    - Update the conditional rendering logic to ensure the splash view stays active until both the initial bootstrap is complete and the minimum time has elapsed.
…atus codes on Android and iOS.

This update refines the logic used to determine if an API error should be treated as a transient connectivity issue. By including specific HTTP status codes related to timeouts and gateway errors (e.g., 408, 502, 503, 504, and Cloudflare-style 52x errors), the application can better handle scenarios where the server is temporarily unreachable or overloaded.

- **Common Logic**:
    - Add `isLikelyServerUnavailableStatus` helper to identify connectivity-related HTTP status codes: 408 (Request Timeout), 502 (Bad Gateway), 503 (Service Unavailable), 504 (Gateway Timeout), and 520–524 (Server/Origin errors).

- **Android (Compose)**:
    - Update `isLikelyConnectivityIssue` in `ApiResponseUtils.kt` to check `ApiCallException` status codes.
    - Expand string-based error matching to include keywords like "bad gateway", "service unavailable", "gateway timeout", and "origin unreachable".
    - Add `ApiResponseUtilsTest.kt` to verify classification of various status codes.

- **iOS (SwiftUI)**:
    - Update `isLikelyConnectivityIssue` in `TdayAPIService.swift` to evaluate `APIError` status codes using the new classification logic.
    - Add `ConnectivityClassificationTests.swift` to the `TdayCoreTests` target to ensure consistent behavior across status codes.
    - Update project configuration to include the new test file.
… Android and iOS.

This update replaces the fixed 1.5-second splash screen timer with a gesture-based mechanism that allows users to keep the splash screen visible by pressing and holding the screen. The app now transitions to the main content immediately after initialization unless the splash screen is being actively held.

- **Android (Compose)**:
    - Replace `SPLASH_MIN_DISPLAY_MS` constant and `delay` logic with an `isStartupSplashHeld` state.
    - Update `SplashScreen` to use `pointerInput` and `detectTapGestures` to track press and release events.
    - Refactor `HandleNavigation` to observe `isStartupSplashHeld` before triggering initial routing.
    - Use `DisposableEffect` to ensure the hold state is cleared when the splash component is disposed.

- **iOS (SwiftUI)**:
    - Replace `hasMetLaunchSplashMinimum` and `Task.sleep` logic with an `isLaunchSplashHeld` state.
    - Add a `DragGesture` (with `minimumDistance: 0`) to `AppLaunchSplashView` to detect touch start and end.
    - Apply `contentShape(Rectangle())` to ensure the entire screen area is gesture-responsive.
    - Ensure `isHeld` is reset to `false` in `onDisappear`.
… flow.

This update introduces a visual identity to the iOS launch process by adding branding elements to the static launch storyboard and synchronizing it with a SwiftUI-based splash view during app initialization.

- **Launch Storyboard**:
    - Update `LaunchScreen.storyboard` to include a centered `StackView` containing the app title ("T'Day") and a tagline ("Your server remembers, so you don't have to").
    - Add Auto Layout constraints to ensure the branding elements remain centered and properly padded on different screen sizes.
    - Define and apply new named colors `LaunchForeground` and `LaunchMuted` for the text elements, supporting both light and dark modes.

- **App Initialization**:
    - Refactor `TdayApp` to defer `AppContainer` initialization. The `appContainer` is now an optional state variable initialized asynchronously within a `.task` modifier.
    - Introduce `isLaunchSplashHeld` state to manage the transition from the splash view to the main application interface.
    - Wrap the main window content in a `Group` that displays `AppLaunchSplashView` while the container is loading or the splash is held, ensuring a seamless transition from the system launch screen to the app UI.

- **UI Components & Assets**:
    - Export `AppLaunchSplashView` from `AppRootView.swift` to make it accessible for the top-level app transition.
    - Add color assets for `LaunchForeground` and `LaunchMuted` with specific sRGB components for light and dark appearances.
This change enhances the initial loading experience by introducing the brand's upright logo and transitioning from system fonts to the custom "Nunito-ExtraBold" typeface on the launch screen.

- **Assets & Configuration**:
    - Add `LaunchSplashLogoUpright.png` to the project resources and update the Xcode project file references.
    - Register the `Nunito.ttf` font within the `LaunchScreen.storyboard` custom fonts section.

- **Launch Screen UI**:
    - Add an `imageView` to the `LaunchScreen.storyboard` to display the new splash logo (160x160).
    - Insert a spacer view to provide 24pt of vertical separation between the logo and the application title.
    - Update the "T'Day" title and slogan labels to use the `Nunito-ExtraBold` font.
    - Refine the `stackView` layout, increasing its height to accommodate the new visual elements while maintaining center alignment.
…ets across Android and iOS.

This update standardizes error messaging for server reachability and connectivity issues across both platforms. It also refines the visual identity of the Android application by updating the launcher and splash icons and improves the pull-to-refresh animation.

- **Networking & Auth**:
    - **Unified Error Messaging**: Introduced `serverUnreachableMessage` (iOS) and `SERVER_UNREACHABLE_MESSAGE` (Android) to provide a consistent "Cannot reach server..." message for connectivity issues.
    - **Expanded Error Detection**: Updated `AuthViewModel` and `AuthRepository` on both platforms to catch a wider range of network-related strings (e.g., "timed out", "bad gateway", "service unavailable", "web server is down") and HTTP status codes (408, 502-504, etc.).
    - **Android Auth Flow**: Improved `AuthRepository.kt` to better handle connectivity issues during the multi-step sign-in process, ensuring consistent error reporting.
    - **iOS Auth Flow**: Integrated `isLikelyServerUnavailableStatusCode` checks into `AuthRepository.swift` to catch server-side failures during the callback phase.

- **Android UI & Assets**:
    - **Launcher & Splash Icons**: Redesigned `splash_icon.xml` and `ic_launcher_foreground.xml` with a more detailed calendar-style vector graphic. Added new color resources in `colors.xml` (e.g., `ic_launcher_surface`, `ic_launcher_task`, `ic_launcher_outline`) to support the new designs.
    - **Pull-to-Refresh**: Enhanced `TdayPullRefresh.kt` with an animated `refreshingBottomOffset` to ensure the loading indicator maintains its correct position during the refreshing state.
    - **Dark Mode**: Added specific color overrides in `values-night/colors.xml` for launcher backgrounds.

- **iOS Assets**:
    - Added `LaunchSplashLogoUpright.png` to the resources directory.
…ets across Android and iOS.

This update standardizes error messaging for server reachability and connectivity issues across both platforms. It also refines the visual identity of the Android application by updating the launcher and splash icons and improves the pull-to-refresh animation.

- **Networking & Auth**:
    - **Unified Error Messaging**: Introduced `serverUnreachableMessage` (iOS) and `SERVER_UNREACHABLE_MESSAGE` (Android) to provide a consistent "Cannot reach server..." message for connectivity issues.
    - **Expanded Error Detection**: Updated `AuthViewModel` and `AuthRepository` on both platforms to catch a wider range of network-related strings (e.g., "timed out", "bad gateway", "service unavailable", "web server is down") and HTTP status codes (408, 502-504, etc.).
    - **Android Auth Flow**: Improved `AuthRepository.kt` to better handle connectivity issues during the multi-step sign-in process, ensuring consistent error reporting.
    - **iOS Auth Flow**: Integrated `isLikelyServerUnavailableStatusCode` checks into `AuthRepository.swift` to catch server-side failures during the callback phase.

- **Android UI & Assets**:
    - **Launcher & Splash Icons**: Redesigned `splash_icon.xml` and `ic_launcher_foreground.xml` with a more detailed calendar-style vector graphic. Added new color resources in `colors.xml` (e.g., `ic_launcher_surface`, `ic_launcher_task`, `ic_launcher_outline`) to support the new designs.
    - **Pull-to-Refresh**: Enhanced `TdayPullRefresh.kt` with an animated `refreshingBottomOffset` to ensure the loading indicator maintains its correct position during the refreshing state.
    - **Dark Mode**: Added specific color overrides in `values-night/colors.xml` for launcher backgrounds.

- **iOS Assets**:
    - Added `LaunchSplashLogoUpright.png` to the resources directory.
…nd refactor launch assets on iOS.

This update standardizes the pull-to-refresh indicator's visual identity using a brand-specific blue accent and introduces more robust refresh state handling in the Android Compose implementation.

- **Android (Compose)**:
    - **TdayPullRefresh**: Improved state management to handle "in-flight" refresh states. Added a `RefreshHandoffHoldMillis` (1.5s) delay to keep the indicator visible during short network transitions or until an external refresh signal is confirmed.
    - Updated `TdayPullToRefreshIndicator` to use `TdayTodayBlue` for background and dot accents instead of the default primary color scheme.
    - Added `zIndex(1f)` to the indicator to ensure it remains on top of content during the refresh animation.

- **iOS (SwiftUI)**:
    - **PullToRefresh**: Updated the refresh indicator components (rounded rectangle background, stroke, and dots) to use `Color.tdayTodayBlue` instead of the primary color.
    - Adjusted shadow colors to use the new blue accent for consistent branding.

- **Assets & Resources**:
    - **iOS**: Migrated `LaunchSplashStack` from an asset catalog (`.imageset`) to a standalone PNG resource in the `Resources` directory.
    - Updated the Xcode project file to reflect the removal of the image set and the addition of `LaunchSplashStack.png` to the build resources.
This update introduces a formal splash screen theme using the Android 12+ Splash Screen API and refactors the application startup sequence to defer non-critical initialization until the first frame is drawn.

- **Splash Screen & Branding**:
    - Add `Theme.Tday.Starting` to handle the initial window background and splash icon presentation.
    - Update `splash_icon.xml` and `ic_launcher_foreground.xml` with refined vector paths for better scaling and visual consistency.
    - Introduce `window_splash.xml` as a layered drawable for the starting window background.
    - Add `ic_launcher_monochrome.xml` and update adaptive icon definitions to support themed icons.
    - Remove `splash_blank.xml` in favor of the new branded splash assets.

- **Startup Optimization**:
    - **MainActivity**: Set the main app theme immediately in `onCreate` and wrap `TdayApp` with a callback to trigger deferred tasks.
    - **TdayApp**: Implement a "startup frame" logic using `withFrameNanos` to ensure the splash screen UI is rendered before transitioning to the main navigation.
    - **TdayApplication**: Introduce `runDeferredStartup()` to initialize heavy dependencies like Sentry and notification permission checks only after the UI is responsive.
    - Pass a randomized `tagline` from the startup state to `SplashScreen` to ensure visual continuity during the transition.

- **Theme & Styles**:
    - Update `themes.xml` (including v31) to link the new starting theme and splash attributes.
    - Standardize splash background colors across light and dark modes.
…egistration.

This update integrates the Jetpack Credentials API into the Android Compose client to support saving, updating, and retrieving credentials via the system password manager. It also updates the backend to support Digital Asset Links for cross-platform credential sharing.

- **Android (Compose)**:
    - **SystemCredentialService**: Introduce a new service to interact with `CredentialManager`, supporting `GetPasswordOption` for logins and `CreatePasswordRequest` for saving new accounts.
    - **Auth Integration**: Update `AuthViewModel` to trigger credential saving after successful manual login or registration. Added `LoginCredentialCoordinator` to handle one-time auto-fill requests when the login screen appears.
    - **Autofill UI**: Add a custom `tdayAutofill` modifier to the onboarding flow, linking `OutlinedTextField` components to `AutofillType.Password`, `AutofillType.EmailAddress`, and others.
    - **Session Management**: Ensure `clearCredentialState()` is called on logout to notify the system provider.
    - **Configuration**: Added `asset_statements` to `AndroidManifest.xml` to link the app with the web domain.

- **Backend (Ktor)**:
    - **Digital Asset Links**: Implement a new route `/.well-known/assetlinks.json` that serves the required JSON payload for Android credential sharing.
    - **Configuration**: Add `ANDROID_PACKAGE_NAME` and `ANDROID_SHA256_CERT_FINGERPRINTS` environment variables to populate the Asset Links manifest.
    - **Validation**: Added unit tests to ensure `assetlinks.json` correctly reflects provided fingerprints or returns an empty array when unset.

- **Testing**:
    - Add `AuthViewModelTest` and `LoginCredentialCoordinatorTest` to verify that credentials are only saved on success and only requested under appropriate UI states (e.g., not while typing or loading).
@deepsource-io
Copy link
Copy Markdown

deepsource-io Bot commented May 22, 2026

DeepSource Code Review

We reviewed changes in 250e094...a7fee0f 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 22, 2026 7:29a.m. Review ↗
Kotlin May 22, 2026 7:29a.m. Review ↗
Secrets May 22, 2026 7:29a.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.


@Composable
fun TdayApp() {
fun TdayApp(
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

`TdayApp` has a cyclomatic complexity of 16 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.

@@ -38,20 +38,36 @@ internal fun extractApiErrorMessage(response: Response<*>, fallback: String): St
}

internal fun isLikelyConnectivityIssue(error: Throwable): 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.

`isLikelyConnectivityIssue` 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 restoreSession(): SessionUser? {
return restoreSessionFromServer()?.also(::cacheSessionUser)
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.


@Module
@InstallIn(SingletonComponent::class)
abstract class SystemCredentialModule {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

An abstract class without a concrete member can be refactored to an interface.


When an abstract class does not have any concrete members, it should be refactored into an interface. Similarly, if an abstract class does not define any abstract members, it should be refactored into a concrete class. This refactoring promotes better code organization, readability, and adherence to Kotlin language conventions.

Comment on lines +104 to +111
} catch (error: CreateCredentialException) {
Log.w(
LOG_TAG,
"Android Password Manager could not save credential: ${error.type}",
error
)
SystemCredentialSaveResult.FAILED
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This catch block is unreachable.


Unreachable catch blocks should be removed, because they serve no useful purpose and can lead to confusing and potentially incorrect behavior in exception handling.

}
}

private fun toFriendlyMessage(message: String?): String {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

`toFriendlyMessage` 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.


@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CompletedScreen(
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

`CompletedScreen` has a cyclomatic complexity of 18 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.


@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun OnboardingWizardOverlay(
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

`OnboardingWizardOverlay` has a cyclomatic complexity of 63 with "Critical" 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.

Introduces a hydration state to track the initial data load for the task list. This prevents layout jitter and unwanted entry animations when the "Today" view first populates.

- **ViewModel**: Adds `hasHydratedSnapshot` to `TodoListUiState` to track when the first data fetch completes for a specific mode or list.
- **UI Logic**: Implements `suppressInitialTodayTimeline` to hide the list and empty state watermark until the initial data snapshot is ready.
- **Animations**:
    - Introduces `timelineAnimationsEnabled` to conditionally apply `Modifier.animateItem()`.
    - Uses a `LaunchedEffect` with `withFrameNanos` to ensure animations for the "Today" view are only enabled after the initial content has been laid out, avoiding "fly-in" effects on app start.
- **Stability**: Ensures the hydration flag is reset when switching between different timeline modes.
@ohmzi ohmzi merged commit 383ee75 into master May 22, 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