Skip to content

Conversation

@kodjima33
Copy link
Collaborator

Summary

  • Rename 'Actions' to 'Tasks' in macOS menu
  • Add persistent task sorting that survives app restarts
  • Tasks keep their position when moving between categories (e.g., tomorrow → today)
  • Add drag-and-drop reordering for macOS tasks page (matching mobile behavior)
  • Store sort order per category in SharedPreferences

Status

🚧 Work in Progress - Still testing

Testing needed

  • Verify task sorting persists after app restart on iOS
  • Verify task sorting persists after app restart on macOS
  • Verify drag-and-drop works on both platforms
  • Verify tasks keep order when date changes (tomorrow → today)

- Rename 'Actions' to 'Tasks' in macOS menu
- Add persistent task sorting that survives app restarts
- Tasks keep their position when moving between categories (e.g., tomorrow to today)
- Add drag-and-drop reordering for macOS tasks page (matching mobile behavior)
- Store sort order per category in SharedPreferences
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces persistent task sorting and drag-and-drop reordering, which are great enhancements for task management. However, I've identified a few critical issues that need to be addressed. There's a potential race condition in how the sort order is persisted to SharedPreferences due to un-awaited asynchronous calls, which could lead to data loss. Additionally, the drag-and-drop implementation in both the desktop and mobile versions contains a bug in how it calculates the drop position, which will prevent reordering from working as expected. Addressing these issues will ensure the new features are robust and reliable.

Comment on lines 529 to 553
/// Set task sort order map
set taskSortOrder(Map<String, int> value) {
saveString('taskSortOrder', jsonEncode(value));
}

/// Update sort order for a single task
void updateTaskSortOrder(String taskId, int sortOrder) {
final current = taskSortOrder;
current[taskId] = sortOrder;
taskSortOrder = current;
}

/// Update sort orders for multiple tasks
void updateTaskSortOrders(Map<String, int> updates) {
final current = taskSortOrder;
current.addAll(updates);
taskSortOrder = current;
}

/// Remove sort order for a task (when task is deleted)
void removeTaskSortOrder(String taskId) {
final current = taskSortOrder;
current.remove(taskId);
taskSortOrder = current;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

The setter taskSortOrder and the update methods (updateTaskSortOrder, updateTaskSortOrders, removeTaskSortOrder) call saveString, which is an asynchronous operation, without awaiting its completion. This fire-and-forget approach can lead to race conditions and data loss, especially with rapid updates like those from drag-and-drop operations. For example, if two updates happen in quick succession, the second one might start before the first one has finished writing to storage, causing the first update to be overwritten.

  /// Set task sort order map
  Future<void> setTaskSortOrder(Map<String, int> value) async {
    await saveString('taskSortOrder', jsonEncode(value));
  }

  /// Update sort order for a single task
  Future<void> updateTaskSortOrder(String taskId, int sortOrder) async {
    final current = taskSortOrder;
    current[taskId] = sortOrder;
    await setTaskSortOrder(current);
  }

  /// Update sort orders for multiple tasks
  Future<void> updateTaskSortOrders(Map<String, int> updates) async {
    final current = taskSortOrder;
    current.addAll(updates);
    await setTaskSortOrder(current);
  }

  /// Remove sort order for a task (when task is deleted)
  Future<void> removeTaskSortOrder(String taskId) async {
    final current = taskSortOrder;
    current.remove(taskId);
    await setTaskSortOrder(current);
  }

Comment on lines 770 to 782
onMove: (details) {
final RenderBox? box = context.findRenderObject() as RenderBox?;
if (box != null) {
final localPosition = box.globalToLocal(details.offset);
final isAbove = localPosition.dy < 20;
if (_hoveredItemId != item.id || _hoverAbove != isAbove) {
setState(() {
_hoveredItemId = item.id;
_hoverAbove = isAbove;
});
}
}
},
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

The onMove callback for the DragTarget has a critical bug that will cause incorrect drag-and-drop behavior.

  1. context.findRenderObject() is fetching the RenderBox for the entire page, not the specific list item being hovered over. This means box.globalToLocal(details.offset) will calculate coordinates relative to the page, not the item, making the hover detection logic faulty.
  2. The line final isAbove = localPosition.dy < 20; uses a hardcoded magic number 20 to decide the drop position. This is unreliable as item heights can vary. It should be based on the item's actual height (e.g., box.size.height / 2).

To fix this, you need to obtain the RenderBox of the specific DragTarget item. A common approach is to use a GlobalKey for each item's DragTarget widget to get its context and RenderBox.

Here's a conceptual example of how you could adjust the onMove logic:

// In onMove callback:
// 1. Get the RenderBox of the target item (e.g., via a GlobalKey).
final RenderBox? itemBox = ...; // Get item's RenderBox
if (itemBox != null) {
  final localPosition = itemBox.globalToLocal(details.offset);
  // 2. Use half the item's height for comparison.
  final isAbove = localPosition.dy < itemBox.size.height / 2;
  if (_hoveredItemId != item.id || _hoverAbove != isAbove) {
    setState(() {
      _hoveredItemId = item.id;
      _hoverAbove = isAbove;
    });
  }
}

Keep only the 'Actions' -> 'Tasks' rename, remove sorting persistence
that was causing tasks not to render.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants