Skip to content

Support Multiple Topmost Records #3500

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: dev
Choose a base branch
from
Open

Support Multiple Topmost Records #3500

wants to merge 13 commits into from

Conversation

Jack251970
Copy link
Contributor

@Jack251970 Jack251970 commented May 1, 2025

Support Multiple Topmost Records

Currently, for one query, Flow can only support one topmost record. We should support multiple topmost records because users may want to set many records to be topmost.

The latter record is, the topper it be.

Test

  • Old data TopMostRecord.json will be converted into new data MultipleTopMostRecord.json.
  • Flow can display multiple topmost records.
  • Latter records are topper.

e.g.
MultipleTopMostRecord.json:

{
  "records": {
    "vs": [
      {
        "Title": "Visual Studio 2022",
        "SubTitle": "",
        "PluginID": "791FC278BA414111B8D1886DFE447410",
        "RecordKey": null
      },
      {
        "Title": "Visual Studio Installer",
        "SubTitle": "",
        "PluginID": "791FC278BA414111B8D1886DFE447410",
        "RecordKey": null
      },
      // This is the latest record and it should be the topmost
      {
        "Title": "Visual Studio Code",
        "SubTitle": "",
        "PluginID": "791FC278BA414111B8D1886DFE447410",
        "RecordKey": null
      }
    ]
  }
}

Screenshot:
image

Copy link

gitstream-cm bot commented May 1, 2025

🚨 gitStream Monthly Automation Limit Reached 🚨

Your organization has exceeded the number of pull requests allowed for automation with gitStream.
Monthly PRs automated: 274/250

To continue automating your PR workflows and unlock additional features, please contact LinearB.

@Jack251970 Jack251970 requested a review from Copilot May 1, 2025 05:28
@Jack251970 Jack251970 added the enhancement New feature or request label May 1, 2025
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR refactors the topmost record storage to support multiple topmost records while preserving the original order. Key changes include:

  • Replacing the single-record storage with a new FlowLauncherJsonStorageTopMostRecord class.
  • Migrating old topmost record data to a new structure supporting multiple records.
  • Extending JsonStorage functionality with Exists and Delete methods.

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
Flow.Launcher/ViewModel/MainViewModel.cs Updated to use FlowLauncherJsonStorageTopMostRecord for topmost data.
Flow.Launcher/Storage/TopMostRecord.cs Introduced a new storage class with migration logic and multiple records handling.
Flow.Launcher.Infrastructure/Storage/JsonStorage.cs Added utility methods for file existence check and deletion.

This comment has been minimized.

Copy link

gitstream-cm bot commented May 1, 2025

🥷 Code experts: no user but you matched threshold 10

Jack251970 has most 👩‍💻 activity in the files.
Jack251970 has most 🧠 knowledge in the files.

See details

Flow.Launcher.Infrastructure/Storage/JsonStorage.cs

Activity based on git-commit:

Jack251970
MAY
APR 17 additions & 6 deletions
MAR
FEB 1 additions & 1 deletions
JAN
DEC

Knowledge based on git-blame:
Jack251970: 8%

Flow.Launcher/Storage/TopMostRecord.cs

Activity based on git-commit:

Jack251970
MAY
APR
MAR
FEB
JAN 30 additions & 9 deletions
DEC 3 additions & 2 deletions

Knowledge based on git-blame:
Jack251970: 47%

Flow.Launcher/ViewModel/MainViewModel.cs

Activity based on git-commit:

Jack251970
MAY
APR 35 additions & 28 deletions
MAR 695 additions & 628 deletions
FEB 63 additions & 21 deletions
JAN 17 additions & 21 deletions
DEC 59 additions & 63 deletions

Knowledge based on git-blame:
Jack251970: 30%

To learn more about /:\ gitStream - Visit our Docs

@Jack251970 Jack251970 requested a review from taooceros May 1, 2025 05:30
Copy link
Contributor

coderabbitai bot commented May 1, 2025

📝 Walkthrough

"""

Walkthrough

This update introduces new methods to the generic JSON storage class for existence checking and deletion of storage files. It adds a new class, FlowLauncherJsonStorageTopMostRecord, to handle migration and management of top-most records, supporting multiple records per query and providing migration from the old single-record format. Serialization support for complex concurrent collections is implemented via a custom JSON converter. The main view model is updated to use the new top-most record storage class, reflecting the migration and new data structure.

Changes

File(s) Change Summary
Flow.Launcher.Infrastructure/Storage/JsonStorage.cs Added Exists() and Delete() public methods to check for file existence and delete storage/backup/temp files if present.
Flow.Launcher/Storage/TopMostRecord.cs Introduced FlowLauncherJsonStorageTopMostRecord for migration and management of top-most records, supporting multiple records per query. Added MultipleTopMostRecord and a custom JSON converter for concurrent collections. Updated Record class to use immutable properties and enhanced equality logic. Retained old TopMostRecord class as obsolete for migration.
Flow.Launcher/ViewModel/MainViewModel.cs Replaced usage of generic top-most record storage with the new specialized class. Updated field, constructor, save method, and result scoring logic accordingly.

Sequence Diagram(s)

sequenceDiagram
    participant MainViewModel
    participant TopMostRecordStorage
    participant JsonStorage

    MainViewModel->>TopMostRecordStorage: Instantiate FlowLauncherJsonStorageTopMostRecord
    TopMostRecordStorage->>JsonStorage: Check if old data exists (Exists())
    alt Old data exists
        TopMostRecordStorage->>JsonStorage: Load old TopMostRecord
        TopMostRecordStorage->>TopMostRecordStorage: Migrate to MultipleTopMostRecord
        TopMostRecordStorage->>JsonStorage: Delete old data (Delete())
    else New data exists
        TopMostRecordStorage->>JsonStorage: Load MultipleTopMostRecord
        TopMostRecordStorage->>JsonStorage: Delete old data if any (Delete())
    end
    MainViewModel->>TopMostRecordStorage: Save()
    TopMostRecordStorage->>JsonStorage: Save new MultipleTopMostRecord
Loading

Possibly related PRs

Suggested reviewers

  • taooceros
  • onesounds

Poem

🐇 In burrows deep where data hides,
New records dance in multiple strides.
Old files vanish, migration’s done,
Scores adjust, the race is won.
JSON hops with queues in tow,
A rabbit’s code, swift as it goes! 🥕✨
"""

Tip

⚡️ Faster reviews with caching
  • CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure Review - Disable Cache at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the Data Retention setting under your Organization Settings.

Enjoy the performance boost—your workflow just got faster.


📜 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 1cf264a and c64f3df.

📒 Files selected for processing (1)
  • Flow.Launcher/ViewModel/MainViewModel.cs (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Flow.Launcher/ViewModel/MainViewModel.cs
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

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

🧹 Nitpick comments (6)
Flow.Launcher.Infrastructure/Storage/JsonStorage.cs (1)

48-52: Consider broadening “existence” semantics

Exists() currently returns File.Exists(FilePath) only.
In day-to-day use we normally only care about the primary json, but the consumer of this API may interpret “storage exists” as “any artefact exists”. If a previous run crashed while writing, only the .bak or .tmp file may be present, yet Exists() will misleadingly return false.

Either (a) clarify the XML-doc comment to state that only the main file is checked, or (b) extend the check to the backup / temp paths so that migration code can make a more reliable decision.

Flow.Launcher/ViewModel/MainViewModel.cs (3)

40-44: Field naming can be misleading

_topMostRecord actually holds the storage wrapper (FlowLauncherJsonStorageTopMostRecord), not the record collection itself.
Because the same identifier is later used for model operations (AddOrUpdate, IsTopMost) it still works, but the mental model is harder to follow.
A name such as _topMostRecordStorage (old name) or _topMostRecordManager would better convey responsibility.


1613-1614: Missing await/async variant of Save()

JsonStorage.Save() is synchronous and can block the UI thread when Flow is closed via UI. You already have SaveAsync() upstream – consider exposing an async counterpart in FlowLauncherJsonStorageTopMostRecord and calling it from an async SaveAsync in MainViewModel, or invoking the sync save on a background thread.


1646-1651: IsTopMost invoked per-result may impact large result sets

UpdateResultView iterates every incoming Result and calls _topMostRecord.IsTopMost(result); under the new design this does an O(n) bag scan per call.
In the worst case (large multi-plugin query + many top-most records) this becomes quadratic.

An inexpensive optimisation is to cache _topMostRecord look-ups once per query key:

var isTopMostCache = new Dictionary<string, bool>();
...
var key = result.OriginQuery?.RawQuery;
if (key != null && isTopMostCache.TryGetValue(key, out var isTop))
{
    if (isTop) result.Score = Result.MaxScore;
}
else
{
    var top = _topMostRecord.IsTopMost(result);
    if (key != null) isTopMostCache[key] = top;
    if (top) result.Score = Result.MaxScore;
}
Flow.Launcher/Storage/TopMostRecord.cs (2)

252-277: Custom converter should null-check & reuse options

Read() assumes dictionary is non-null. A corrupted file will throw NRE instead of propagating JsonException, losing diagnostic information.
Add a guard:

if (dictionary == null)
    return new();

Also, consider passing the existing options when re-serialising in Write() to preserve caller settings (e.g., indentation).


279-299: Record.Equals lacks GetHashCode override

Equals is overridden for logical comparison but GetHashCode isn’t, breaking hash-based collections, and may lead to subtle bugs if a Record is ever placed in a HashSet/dictionary. Either add GetHashCode or seal the class and document the behaviour.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 950a7f5 and 0673d07.

📒 Files selected for processing (3)
  • Flow.Launcher.Infrastructure/Storage/JsonStorage.cs (1 hunks)
  • Flow.Launcher/Storage/TopMostRecord.cs (2 hunks)
  • Flow.Launcher/ViewModel/MainViewModel.cs (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: gitStream workflow automation
  • GitHub Check: gitStream.cm
  • GitHub Check: build
🔇 Additional comments (1)
Flow.Launcher/ViewModel/MainViewModel.cs (1)

136-138: Constructor no longer pre-loads data – confirm this is intended

Old code loaded records eagerly (_topMostRecordStorage.Load()); the new wrapper loads lazily inside its own ctor.
That is fine, but be aware it now occurs before MainViewModel’s constructor body runs only if the wrapper’s constructor does synchronous IO. If startup performance matters, consider lazy-loading on first use.

@Jack251970 Jack251970 requested a review from Copilot May 1, 2025 05:39

This comment has been minimized.

coderabbitai bot added a commit that referenced this pull request May 1, 2025
Docstrings generation was requested by @Jack251970.

* #3500 (comment)

The following files were modified:

* `Flow.Launcher.Infrastructure/Storage/JsonStorage.cs`
* `Flow.Launcher/Storage/TopMostRecord.cs`
* `Flow.Launcher/ViewModel/MainViewModel.cs`
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR introduces support for multiple topmost records by migrating from a single-record storage mechanism to a new storage system that allows multiple records per query. Key changes include refactoring the storage API in MainViewModel, implementing a new data structure (MultipleTopMostRecord) with conversion logic from the old format, and adding file existence and deletion functionality in JsonStorage.

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
Flow.Launcher/ViewModel/MainViewModel.cs Migrates the topmost record storage to the new FlowLauncherJsonStorageTopMostRecord implementation.
Flow.Launcher/Storage/TopMostRecord.cs Refactors the storage mechanism to support multiple topmost records and introduces a new data structure with migration logic.
Flow.Launcher/Infrastructure/Storage/JsonStorage.cs Adds helper methods (Exists and Delete) for file management, supporting the migration process.
Comments suppressed due to low confidence (1)

Flow.Launcher/Storage/TopMostRecord.cs:90

  • [nitpick] The internal class name 'TopMostRecord' may cause confusion with the new multi-record storage and the legacy naming in the migration logic. Consider renaming it to a name that clearly indicates its role as the legacy data structure.
internal class TopMostRecord

Copy link
Contributor

coderabbitai bot commented May 1, 2025

Note

Generated docstrings for this pull request at #3501

This comment has been minimized.

@Jack251970 Jack251970 requested a review from Copilot May 1, 2025 05:44
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR introduces support for multiple topmost records by migrating from a single-record storage design to a multiple-record model.

  • Replaces the old FlowLauncherJsonStorage with a new FlowLauncherJsonStorageTopMostRecord in MainViewModel.
  • Implements migration logic and new data structures (using ConcurrentBag) in the storage layer to accommodate multiple records.
  • Adds file existence and deletion capabilities in JsonStorage to support the migration process.

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
Flow.Launcher/ViewModel/MainViewModel.cs Updated storage field and usage to support multiple topmost records.
Flow.Launcher/Storage/TopMostRecord.cs Introduced new classes for multiple topmost records and provided migration logic from old data.
Flow.Launcher/Infrastructure/Storage/JsonStorage.cs Added methods to check file existence and delete files for storage management.

Copy link
Contributor

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

♻️ Duplicate comments (1)
Flow.Launcher/Storage/TopMostRecord.cs (1)

146-148: ConcurrentBag usage is appropriate for this implementation

Using a ConcurrentBag for storing multiple records per query fits the requirements, as the retrieved learnings confirm that sequence order preservation is not needed in this context. This implementation choice appropriately balances thread safety with performance.

🧹 Nitpick comments (1)
Flow.Launcher/Storage/TopMostRecord.cs (1)

196-199: Check for empty bag may be redundant

The check for value.IsEmpty after already rebuilding the bag seems redundant since we're replacing the entire bag with filtered elements on line 193. The bag would be empty only if it contained just the one element being removed.

-    // if the bag is empty, remove the bag from the dictionary
-    if (value.IsEmpty)
-    {
-        records.TryRemove(result.OriginQuery.RawQuery, out _);
-    }
+    // if the bag is empty, remove the bag from the dictionary
+    if (records[result.OriginQuery.RawQuery].IsEmpty)
+    {
+        records.TryRemove(result.OriginQuery.RawQuery, out _);
+    }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4ecef47 and c49b3b7.

📒 Files selected for processing (1)
  • Flow.Launcher/Storage/TopMostRecord.cs (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
Flow.Launcher/Storage/TopMostRecord.cs (2)
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3500
File: Flow.Launcher/Storage/TopMostRecord.cs:191-197
Timestamp: 2025-05-01T05:35:21.827Z
Learning: ConcurrentBag.TryTake(out T) in C# removes an arbitrary item from the bag, not necessarily the one matching the out parameter. This can lead to removing the wrong element when trying to remove a specific item. A safer approach is to reconstruct the collection using filtering or use an ordered collection with proper removal methods.
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3500
File: Flow.Launcher/Storage/TopMostRecord.cs:145-149
Timestamp: 2025-05-01T05:38:25.645Z
Learning: For the MultipleTopMostRecord implementation in Flow.Launcher, sequence order of records in the ConcurrentBag does not need to be preserved, as confirmed by the developer. The unordered nature of ConcurrentBag is acceptable for this implementation.
🧬 Code Graph Analysis (1)
Flow.Launcher/Storage/TopMostRecord.cs (2)
Flow.Launcher.Infrastructure/Storage/JsonStorage.cs (3)
  • Exists (48-51)
  • Delete (53-62)
  • Save (202-213)
Flow.Launcher/ViewModel/MainViewModel.cs (3)
  • Save (1609-1614)
  • Result (1372-1410)
  • Result (1412-1438)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (5)
Flow.Launcher/Storage/TopMostRecord.cs (5)

12-85: Good implementation of the new storage class for managing topmost records

The new FlowLauncherJsonStorageTopMostRecord class provides a clean facade that handles migration between the old single-record format and the new multiple-records format. The constructor logic properly addresses all possible scenarios (new data exists, only old data exists, neither exists) with appropriate exception handling during file deletion operations.


191-194: Good fix for the ConcurrentBag.TryTake issue

This implementation correctly addresses the issue with ConcurrentBag.TryTake() removing arbitrary items. Instead of using TryTake(), the code creates a new bag from filtered elements, ensuring the exact matching record is removed.


229-233: Good fix for record updates using filtered approach

Similar to the Remove method, the AddOrUpdate method correctly handles updating records by creating a new collection from filtered elements rather than attempting to use TryTake(), which would remove arbitrary items. This implementation ensures that the specific record is properly updated.


240-262: Well-implemented JSON converter for concurrent collections

The ConcurrentDictionaryConcurrentBagConverter properly handles the serialization and deserialization of the concurrent collections by converting them to standard collections that can be serialized. This is a good practice for working with concurrent collections that don't have built-in serialization support.


266-269: Good choice to make Record properties immutable

Using init accessors instead of set makes the Record properties immutable after initialization, which is a good practice for value objects. This prevents accidental modification of records after creation and helps maintain data integrity.

This comment has been minimized.

@Jack251970 Jack251970 requested a review from Copilot May 1, 2025 05:48
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR enables Flow to support multiple topmost records by migrating existing single-record data and updating the storage logic.

  • Refactors storage for topmost records to use a new JSON storage class and data structure.
  • Adds migration logic to convert old topmost records into a new multiple-record format.
  • Enhances JSON storage by adding existence checks and file deletion support.

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
Flow.Launcher/ViewModel/MainViewModel.cs Updates references to topmost record storage to use the new storage class for multiple topmost records.
Flow.Launcher/Storage/TopMostRecord.cs Introduces the new storage class (FlowLauncherJsonStorageTopMostRecord) and data structure (MultipleTopMostRecord) for migration.
Flow.Launcher/Infrastructure/Storage/JsonStorage.cs Adds new utility methods to check file existence and delete storage files.
Comments suppressed due to low confidence (1)

Flow.Launcher/Infrastructure/Storage/JsonStorage.cs:57

  • Wrap the File.Delete call in a try-catch block to gracefully handle possible exceptions during file deletion and enhance fault tolerance.
if (File.Exists(path))

@onesounds
Copy link
Contributor

onesounds commented May 1, 2025

Just in case, I’ll mention this now — I haven’t tested this PR and I'm not fully aware of its intent. But from a UX perspective, if there’s already a top-most item A and you add another one B, the display order should be B, then A.

When there are three top-most item — A, B, and C — the one that was activated most recently should be displayed in front. Otherwise, users may feel like their top-most action didn’t work, and they’ll be forced to deactivate other top-most item to perform their intended task.

@Jack251970
Copy link
Contributor Author

Just in case, I’ll mention this now — I haven’t tested this PR and I'm not fully aware of its intent. But from a UX perspective, if there’s already a top-most item A and you add another one B, the display order should be B, then A.

When there are three top-most item — A, B, and C — the one that was activated most recently should be displayed in front. Otherwise, users may feel like their top-most action didn’t work, and they’ll be forced to deactivate other top-most item to perform their intended task.

Yeah, that makes sense. But I think the impact may be very small. Users typically will not set so much records as topmost, so ABC, CBA really does differ too much. The interface still allows them to select the correct option easily.

@onesounds
Copy link
Contributor

onesounds commented May 1, 2025

Noooo~ ABC and CBA is different! (I actually tested this PR.) I use the Top Most feature all the time, and since our search often doesn’t show the best results first, a true “pin to top” is essential. This PR isn’t Top Most—it’s more like “add to top-group.” If there’s already a Top Most item, clicking Top Most again doesn’t move it to the first position, so you’d have to rename the menu—and it’s not intuitive.

Isn’t it just a matter of sorting by insertion order? If I pin A, then B, then C, C was last, so it should appear first—C, B, A. That’s trivial. Items with TopMost = true will still be grouped at the top, matching your PR’s intention. We can’t be sure there won’t be requests like “Please allow ordering within the top group.” If someone files a bug titled “I clicked Top Most but it sorted to second place,” replying “Oh, unpin the first item” would be ridiculous. Users won’t remember what they pinned—they have no way to know unless they try—so when they click Top Most, their only expectation is “show this at the very top.” It’s fine to group all pinned items together, but that group’s order needs to reflect the user’s intent (most recently pinned first).

Anyway, this implementation fails to meet the functional expectation of “Top Most,” so it can’t stay like this. If you’re going to add this feature, please change it so that items are displayed in the order in which “Top Most” was applied—i.e., the item most recently marked as Top Most should appear first.

@Jack251970
Copy link
Contributor Author

Noooo~ ABC and CBA is different! (I actually tested this PR.) I use the Top Most feature all the time, and since our search often doesn’t show the best results first, a true “pin to top” is essential. This PR isn’t Top Most—it’s more like “add to top-group.” If there’s already a Top Most item, clicking Top Most again doesn’t move it to the first position, so you’d have to rename the menu—and it’s not intuitive.

Isn’t it just a matter of sorting by insertion order? If I pin A, then B, then C, C was last, so it should appear first—C, B, A. That’s trivial. Items with TopMost = true will still be grouped at the top, matching your PR’s intention. We can’t be sure there won’t be requests like “Please allow ordering within the top group.” If someone files a bug titled “I clicked Top Most but it sorted to second place,” replying “Oh, unpin the first item” would be ridiculous. Users won’t remember what they pinned—they have no way to know unless they try—so when they click Top Most, their only expectation is “show this at the very top.” It’s fine to group all pinned items together, but that group’s order needs to reflect the user’s intent (most recently pinned first).

Anyway, this implementation fails to meet the functional expectation of “Top Most,” so it can’t stay like this. If you’re going to add this feature, please change it so that items are displayed in the order in which “Top Most” was applied—i.e., the item most recently marked as Top Most should appear first.

Well, I get it.

This comment has been minimized.

@Jack251970 Jack251970 requested a review from Copilot May 2, 2025 02:30
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR updates Flow to support multiple topmost records by migrating from a legacy single-record structure to a new data structure that supports multiple records and adjusts record ranking accordingly. Key changes include:

  • Replacing the single topmost record storage with a new FlowLauncherJsonStorageTopMostRecord class.
  • Updating the scoring logic in MainViewModel to factor in record positioning.
  • Implementing migration logic in the storage layer to convert old data to the new format.

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
Flow.Launcher/ViewModel/MainViewModel.cs Updates to use the new topmost record structure and scoring adjustment.
Flow.Launcher/Storage/TopMostRecord.cs Introduces the new FlowLauncherJsonStorageTopMostRecord and migration logic.
Flow.Launcher/Infrastructure/Storage/JsonStorage.cs Adds supportive file-check and deletion methods.

Copy link
Contributor

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

🧹 Nitpick comments (3)
Flow.Launcher/Storage/TopMostRecord.cs (3)

46-47: Add logging for old data migration.

Consider adding a log message here to indicate that an old data migration is being performed. This would improve maintainability by providing better visibility into the migration process.

-                // Migrate old data to new data
+                // Migrate old data to new data
+                System.Diagnostics.Debug.WriteLine("Migrating from TopMostRecord to MultipleTopMostRecord format.");

167-167: Consider if ConcurrentQueue is the right data structure for user expectations.

The PR objectives mention that users expect the most recently pinned item to appear first (i.e., at the very top). However, ConcurrentQueue is FIFO (First In, First Out), meaning the oldest items will be enumerated first.

This may create a user experience issue where the most recently pinned item appears at the bottom of the topmost group, not at the top as users would expect when they pin an item.

Do you want to reconsider using a data structure that naturally places the most recent items first, such as ConcurrentStack (LIFO), or manually reversing the collection during enumeration?


271-280: Consider preserving order for user experience.

When deserializing the records, the order within each queue is preserved. However, if order is important for user experience (especially for the multiple topmost items feature), consider adding a comment that explains how ordering works in the deserialized data.

This is particularly important since there was a discussion in the PR about the appropriate ordering of topmost items.

         public override ConcurrentDictionary<string, ConcurrentQueue<Record>> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
         {
             var dictionary = JsonSerializer.Deserialize<Dictionary<string, List<Record>>>(ref reader, options);
             var concurrentDictionary = new ConcurrentDictionary<string, ConcurrentQueue<Record>>();
             foreach (var kvp in dictionary)
             {
+                // Preserve the order of records as they were stored in the JSON
                 concurrentDictionary.TryAdd(kvp.Key, new ConcurrentQueue<Record>(kvp.Value));
             }
             return concurrentDictionary;
         }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c49b3b7 and b25777b.

📒 Files selected for processing (2)
  • Flow.Launcher/Storage/TopMostRecord.cs (2 hunks)
  • Flow.Launcher/ViewModel/MainViewModel.cs (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Flow.Launcher/ViewModel/MainViewModel.cs
🧰 Additional context used
🧠 Learnings (1)
Flow.Launcher/Storage/TopMostRecord.cs (2)
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3500
File: Flow.Launcher/Storage/TopMostRecord.cs:191-197
Timestamp: 2025-05-01T05:35:21.827Z
Learning: ConcurrentBag.TryTake(out T) in C# removes an arbitrary item from the bag, not necessarily the one matching the out parameter. This can lead to removing the wrong element when trying to remove a specific item. A safer approach is to reconstruct the collection using filtering or use an ordered collection with proper removal methods.
Learnt from: Jack251970
PR: Flow-Launcher/Flow.Launcher#3500
File: Flow.Launcher/Storage/TopMostRecord.cs:145-149
Timestamp: 2025-05-01T05:38:25.645Z
Learning: For the MultipleTopMostRecord implementation in Flow.Launcher, sequence order of records in the ConcurrentBag does not need to be preserved, as confirmed by the developer. The unordered nature of ConcurrentBag is acceptable for this implementation.
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (1)
Flow.Launcher/Storage/TopMostRecord.cs (1)

183-207: Consider clarifying the topmost index calculation logic.

The current implementation assumes newer items (at the end of the queue) should have higher priority, which conflicts with the PR objective that states "the most recently pinned item should appear first."

In the comments discussion, users expect that when clicking "Top Most" on an item, it should move to the first position, which is intuitive. The current logic might not align with this expectation.

-            // since the latter items should be more recent, we should return the smaller index for score to subtract
-            // which can make them more topmost
-            // A, B, C => 2, 1, 0 => (max - 2), (max - 1), (max - 0)
+            // Items are stored in queue order, but for display we need to consider user expectations
+            // Users expect their most recently pinned item to appear first (at the top)
+            // The topmost index affects result ordering, with smaller values appearing higher

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

This comment has been minimized.

@Jack251970 Jack251970 requested a review from onesounds May 2, 2025 04:29
@jjw24 jjw24 added this to the Future milestone May 4, 2025
Copy link

@check-spelling-bot Report

🔴 Please review

See the 📂 files view, the 📜action log, or 📝 job summary for details.

❌ Errors and Warnings Count
❌ forbidden-pattern 22
⚠️ non-alpha-in-dictionary 19

See ❌ Event descriptions for more information.

If the flagged items are 🤯 false positives

If items relate to a ...

  • binary file (or some other file you wouldn't want to check at all).

    Please add a file path to the excludes.txt file matching the containing file.

    File paths are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your files.

    ^ refers to the file's path from the root of the repository, so ^README\.md$ would exclude README.md (on whichever branch you're using).

  • well-formed pattern.

    If you can write a pattern that would match it,
    try adding it to the patterns.txt file.

    Patterns are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your lines.

    Note that patterns can't match multiline strings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants