Skip to content

Add strategy and folder pinning#75

Open
njiedev wants to merge 8 commits into
SunkenInTime:mainfrom
njiedev:feature/strategy-pinning
Open

Add strategy and folder pinning#75
njiedev wants to merge 8 commits into
SunkenInTime:mainfrom
njiedev:feature/strategy-pinning

Conversation

@njiedev
Copy link
Copy Markdown

@njiedev njiedev commented May 23, 2026

What

Adds the ability to pin strategies and folders. Pinned items float to the top of the home-screen grid, rendered as normal tiles/pills (no separate section or visual indicator — per discussion, the owner will style the indicator himself).

  • Pin/Unpin added to the right-click and "⋮" menus on both strategy tiles and folder pills.
  • Pins are global: they collect at the home/root screen regardless of which folder the item lives in.
  • Pinned items are ordered most-recently-pinned first.
  • Pins are removed automatically when the underlying strategy or folder is deleted (no ghost pins).
  • Pinning is suppressed while searching, so search shows pure results.

Storage approach

Rather than adding an isPinned field to the existing StrategyData / Folder models (which would need a migration), pins live in a separate Hive box as a registry of itemId -> pinnedTimestamp. One registry handles both strategies and folders uniformly, and the existing data models are untouched.

Tests

Added test/pinned_items_provider_test.dart covering pin/toggle, recency ordering, and remove-when-absent. flutter analyze is clean on all touched files.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 23, 2026

Greptile Summary

Adds pin/unpin support for strategy tiles and folder pills, backed by a standalone Hive Box<int> registry (itemId → pinnedTimestamp). Pinned items are hoisted to the top of the root screen in recency order; pinning is suppressed during search, and pins are auto-removed on deletion.

  • PinnedItemsProvider is a clean, self-contained Riverpod Notifier with correct Hive read/write ordering and three unit tests covering toggle, recency sort, and no-op remove.
  • FolderContent adds the root-screen hoisting logic using maps built from the full Hive boxes, correctly implementing the "global pins appear at root regardless of source folder" requirement.
  • A leftover // ... your existing imports development comment remains in folder_content.dart and should be removed.

Confidence Score: 4/5

The change is well-scoped and the core persistence and deletion cleanup paths are correct. The two open items are cosmetic and a minor Riverpod style point — no data-correctness risk.

The provider, Hive wiring, and deletion cleanup are all implemented correctly. The only rough edge is using ref.read to call pinnedIdsByRecency() inside build() when the full state is already available from ref.watch on the line above — a style inconsistency that poses no practical risk but is worth cleaning up. A stale scaffolding comment also remains.

lib/widgets/folder_content.dart — minor ref.read-inside-build pattern and a leftover comment.

Important Files Changed

Filename Overview
lib/providers/pinned_items_provider.dart New Riverpod Notifier backed by a Hive box; state management, ordering, and persistence are all correct. removePin early-exits cleanly, and the box is read once at build time.
lib/widgets/folder_content.dart Pinned-items sorting logic is functionally correct for the stated design (global pins at root). Two minor issues: a leftover // ... your existing imports comment and a ref.read for pinnedIdsByRecency() that could use the already-watched pinned map directly.
lib/providers/strategy_provider.dart Adds a single removePin call before strategy deletion; correct and minimal.
lib/providers/folder_provider.dart Adds removePin(folderID) as the first step in deleteFolder; consistent with the strategy pattern. Direct-child strategy pins are also cleaned up via deleteStrategyremovePin chain.
lib/widgets/folder_pill.dart Pin/unpin menu item added correctly; isDemo guard is in place, and ref.watch drives the label correctly.
lib/widgets/strategy_tile/strategy_tile.dart Pin/unpin menu item added correctly; ref.watch drives the label, and togglePin is the right call from onPressed.
test/pinned_items_provider_test.dart Three unit tests covering toggle round-trip, recency ordering, and no-op remove; Hive is properly initialized and torn down per test.

Sequence Diagram

sequenceDiagram
    participant User
    participant StrategyTile/FolderPill
    participant PinnedItemsProvider
    participant HiveBox
    participant FolderContent

    User->>StrategyTile/FolderPill: Right-click / ⋮ → Pin
    StrategyTile/FolderPill->>PinnedItemsProvider: togglePin(id)
    PinnedItemsProvider->>HiveBox: put(id, nowMs)
    PinnedItemsProvider->>PinnedItemsProvider: "state = {...state, id: nowMs}"
    PinnedItemsProvider-->>FolderContent: rebuild (ref.watch)
    FolderContent->>PinnedItemsProvider: pinnedIdsByRecency()
    FolderContent->>FolderContent: hoist pinned items to front of lists

    User->>StrategyTile/FolderPill: Delete
    StrategyTile/FolderPill->>PinnedItemsProvider: removePin(id)
    PinnedItemsProvider->>HiveBox: delete(id)
    PinnedItemsProvider->>PinnedItemsProvider: "state = {...state}..remove(id)"
Loading

Reviews (1): Last reviewed commit: "Float pinned items to top of grid instea..." | Re-trigger Greptile

Comment on lines +182 to +184
final pinnedIds = ref
.read(pinnedItemsProvider.notifier)
.pinnedIdsByRecency();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 ref.read inside build() for state access

pinnedIdsByRecency() is called via ref.read(pinnedItemsProvider.notifier) on line 183 inside build(). Riverpod's ref.read takes a snapshot at call time and does not subscribe, so in the narrow window between the ref.watch on line 180 and this ref.read, the notifier state could theoretically change (e.g. a concurrent async pin toggle completing). In practice this means pinned (line 180) and the snapshot used by pinnedIdsByRecency() could differ. Since pinned is already the full Map<String, int> state, you can derive the sorted order directly from it — no ref.read needed.

Comment thread lib/widgets/folder_content.dart Outdated
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
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