Skip to content

Introduce Dependency Injection for Better Development Experience #3175

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

Merged
merged 38 commits into from
Feb 24, 2025

Conversation

Jack251970
Copy link
Contributor

@Jack251970 Jack251970 commented Jan 9, 2025

Introduce dependency injection for accessing the API property in anywhere inside the project.

This can improve development experience. E.g. We can put the MessgaeBoxEx to Flow.Launcher project and we can access the design page like this:

image

Copy link
Contributor

coderabbitai bot commented Jan 9, 2025

📝 Walkthrough

Walkthrough

This pull request introduces a comprehensive refactoring of message display and dependency management across the Flow Launcher application. The primary changes involve replacing direct MessageBoxEx.Show calls with a centralized API.ShowMsgBox method and implementing dependency injection using Microsoft's hosting and dependency injection frameworks. The modifications span multiple files and classes, focusing on improving code modularity, testability, and consistent message handling.

Changes

File/Path Change Summary
Flow.Launcher.Core/Configuration/Portable.cs Replaced MessageBoxEx.Show with API.ShowMsgBox for message display. Added private readonly IPublicAPI API.
Flow.Launcher.Core/ExternalPlugins/Environments/* Updated message box display in environment-related classes, added protected readonly IPublicAPI API.
Flow.Launcher.Core/Plugin/PluginManager.cs Modified file copying message display method to use API.ShowMsgBox. Changed API property to be initialized directly.
Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj Added CommunityToolkit.Mvvm package reference.
Flow.Launcher/App.xaml.cs Refactored initialization with dependency injection, changed API property type to IPublicAPI.
Flow.Launcher/PublicAPIInstance.cs Updated constructor to parameterless and changed _alphabet field type to IAlphabet.
Multiple UI-related files Replaced MessageBoxEx.Show with App.API.ShowMsgBox in various methods.
Flow.Launcher/MessageBoxEx.xaml Updated class and namespace declaration to remove Core subnamespace.
Flow.Launcher/MessageBoxEx.xaml.cs Changed namespace from Flow.Launcher.Core to Flow.Launcher.

Possibly related PRs

  • Fix Custom Query Hotkey / Shortcuts Preview Issue #3248: The changes in the main PR and the retrieved PR are related as both involve modifications to how message boxes are displayed, specifically transitioning from MessageBoxEx.Show to API.ShowMsgBox, indicating a shared focus on improving user interaction through a centralized API.

Suggested labels

dependencies, .NET

Suggested reviewers

  • JohnTheGr8
  • jjw24

Poem

🐰 A Rabbit's Dependency Dance

With APIs aligned and injections so neat,
Our code now dances to a modular beat.
MessageBoxes transformed, no longer alone,
Dependency magic has found its new home!

Hop, hop, hooray! 🎉

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ 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.

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. (Beta)
  • @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.

This comment has been minimized.

@taooceros
Copy link
Member

I was thinking about unifying the access to IPublicAPI. Currently this is quite messy (some uses PluginManager.API some uses other technique).

Very earlier I was thinking about integrating dependency injection in flow, but that seems to be pretty complicated. Maybe we can do that gradually specifically for the PublicAPI?

I am not sure. What do others think about global state access?

@Jack251970
Copy link
Contributor Author

Jack251970 commented Jan 11, 2025

@taooceros I agree that dependency injection is the most elegant way to access API from anywhere. But considering the current architecture of the FL codes , App.API should not be deprecated (so many codes rely on this). So this PR exposes App.API for other projects to call it so that ProgressBoxEx can be placed into the main project which allows design page to work.

@Jack251970
Copy link
Contributor Author

Jack251970 commented Jan 12, 2025

I mean it can serve as a transitional solution for unifying the call of api functions. (here I just do for functions related to ProgressBoxEx)

This comment has been minimized.

@Jack251970 Jack251970 changed the title Move MessageBoxEx to main project for better development experience Introduce Dependency Injection for Better Development Experience Jan 12, 2025
Copy link

gitstream-cm bot commented Jan 12, 2025

🥷 Code experts: Yusyuriv, jjw24

Yusyuriv, jjw24 have most 🧠 knowledge in the files.

See details

Flow.Launcher.Core/Configuration/Portable.cs

Knowledge based on git-blame:
jjw24: 80%

Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs

Knowledge based on git-blame:
jjw24: 55%
Yusyuriv: 4%

Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs

Knowledge based on git-blame:
jjw24: 73%

Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs

Knowledge based on git-blame:
jjw24: 70%

Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs

Knowledge based on git-blame:

Flow.Launcher.Core/Plugin/PluginManager.cs

Knowledge based on git-blame:
jjw24: 4%
Yusyuriv: 4%

Flow.Launcher.Core/Plugin/PluginsLoader.cs

Knowledge based on git-blame:
jjw24: 16%

Flow.Launcher.Core/Resource/Internationalization.cs

Knowledge based on git-blame:
jjw24: 11%
Yusyuriv: 4%

Flow.Launcher.Core/Resource/Theme.cs

Knowledge based on git-blame:
jjw24: 19%
Yusyuriv: 15%

Flow.Launcher.Core/Updater.cs

Knowledge based on git-blame:
jjw24: 12%

Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj

Knowledge based on git-blame:
jjw24: 12%

Flow.Launcher.Infrastructure/PinyinAlphabet.cs

Knowledge based on git-blame:
jjw24: 5%

Flow.Launcher.Infrastructure/StringMatcher.cs

Knowledge based on git-blame:
jjw24: 65%

Flow.Launcher.Infrastructure/UserSettings/Settings.cs

Knowledge based on git-blame:
Yusyuriv: 15%
jjw24: 3%

Flow.Launcher/ActionKeywords.xaml.cs

Knowledge based on git-blame:
jjw24: 25%

Flow.Launcher/App.xaml.cs

Knowledge based on git-blame:
jjw24: 15%
Yusyuriv: 3%

Flow.Launcher/CustomQueryHotkeySetting.xaml.cs

Knowledge based on git-blame:
Yusyuriv: 19%
jjw24: 17%

Flow.Launcher/CustomShortcutSetting.xaml.cs

Knowledge based on git-blame:
Yusyuriv: 14%

Flow.Launcher/Flow.Launcher.csproj

Knowledge based on git-blame:
jjw24: 12%

Flow.Launcher/Helper/HotKeyMapper.cs

Knowledge based on git-blame:
Yusyuriv: 64%

Flow.Launcher/PriorityChangeWindow.xaml.cs

Knowledge based on git-blame:

Flow.Launcher/PublicAPIInstance.cs

Knowledge based on git-blame:
jjw24: 9%

Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs

Knowledge based on git-blame:
Yusyuriv: 92%

Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs

Knowledge based on git-blame:
Yusyuriv: 94%

Flow.Launcher/SettingPages/ViewModels/SettingsPaneProxyViewModel.cs

Knowledge based on git-blame:
Yusyuriv: 98%

Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs

Knowledge based on git-blame:
Yusyuriv: 73%

Flow.Launcher/ViewModel/MainViewModel.cs

Knowledge based on git-blame:
Yusyuriv: 3%
jjw24: 2%

Flow.Launcher/ViewModel/SettingWindowViewModel.cs

Knowledge based on git-blame:
Yusyuriv: 65%
jjw24: 8%

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

@Jack251970
Copy link
Contributor Author

I was thinking about unifying the access to IPublicAPI. Currently this is quite messy (some uses PluginManager.API some uses other technique).

Very earlier I was thinking about integrating dependency injection in flow, but that seems to be pretty complicated. Maybe we can do that gradually specifically for the PublicAPI?

I am not sure. What do others think about global state access?

Now I implement dependency injection model for this, and can you please take a look of this?

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: 8

🔭 Outside diff range comments (1)
Flow.Launcher.Core/Plugin/PluginsLoader.cs (1)

Line range hint 123-127: Consider using constructor injection instead of service locator pattern.

The use of Ioc.Default.GetRequiredService is a service locator anti-pattern that makes the code harder to test and maintain. Consider:

  1. Making the class non-static
  2. Injecting IPublicAPI through the constructor
 public static class PluginsLoader
 {
+    private readonly IPublicAPI _api;
+
+    public PluginsLoader(IPublicAPI api)
+    {
+        _api = api;
+    }

     public static IEnumerable<PluginPair> DotNetPlugins(...)
     {
         // ...
-        Ioc.Default.GetRequiredService<IPublicAPI>().ShowMsgBox(...);
+        _api.ShowMsgBox(...);
🧹 Nitpick comments (19)
Flow.Launcher.Core/Resource/Internationalization.cs (2)

128-128: Consider constructor injection instead of service locator pattern.

While the change aligns with the PR's objective to unify API access, using Ioc.Default directly is a service locator anti-pattern. Consider injecting IPublicAPI through the constructor for better testability and maintainability.

Example refactor:

 public class Internationalization
 {
     public Settings Settings { get; set; }
+    private readonly IPublicAPI _api;
     private const string Folder = "Languages";
     // ... other fields ...

-    public Internationalization()
+    public Internationalization(IPublicAPI api)
     {
+        _api = api;
         AddFlowLauncherLanguageDirectory();
     }

     // ... other methods ...

     public bool PromptShouldUsePinyin(string languageCodeToSet)
     {
         // ... existing code ...
-        if (Ioc.Default.GetRequiredService<IPublicAPI>().ShowMsgBox(text, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No)
+        if (_api.ShowMsgBox(text, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No)
             return false;
         // ... existing code ...
     }
 }

Line range hint 114-131: Consider separating UI interaction from business logic.

The method currently mixes business logic (determining if Pinyin should be used) with UI concerns (showing dialog). Consider extracting the UI interaction to improve testability and maintainability.

Example approach:

+public interface IUserPrompt
+{
+    bool ConfirmPinyinUsage(Language language);
+}

 public class Internationalization
 {
     private readonly IPublicAPI _api;
+    private readonly IUserPrompt _userPrompt;

-    public Internationalization(IPublicAPI api)
+    public Internationalization(IPublicAPI api, IUserPrompt userPrompt)
     {
         _api = api;
+        _userPrompt = userPrompt;
     }

     public bool PromptShouldUsePinyin(string languageCodeToSet)
     {
         var languageToSet = GetLanguageByLanguageCode(languageCodeToSet);

         if (Settings.ShouldUsePinyin)
             return false;

         if (languageToSet != AvailableLanguages.Chinese && languageToSet != AvailableLanguages.Chinese_TW)
             return false;

-        string text = languageToSet == AvailableLanguages.Chinese ? "是否启用拼音搜索?" : "是否啓用拼音搜索?";
-        if (_api.ShowMsgBox(text, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No)
-            return false;
-
-        return true;
+        return _userPrompt.ConfirmPinyinUsage(languageToSet);
     }
 }
Flow.Launcher.Infrastructure/UserSettings/Settings.cs (2)

16-26: Consider constructor injection for better dependency management.

While the current implementation works, it adds another initialization requirement that needs to be managed. Given the discussion about unifying API access and potentially moving towards dependency injection, consider:

  1. Using constructor injection instead of manual initialization
  2. Making the class immutable where possible
  3. Using an interface for the storage dependency

This would better align with the goal of improving the development experience and make future DI adoption easier.

Example approach:

-    public class Settings : BaseModel, IHotkeySettings
+    public class Settings : BaseModel, IHotkeySettings, ISettings
     {
-        private FlowLauncherJsonStorage<Settings> _storage;
+        private readonly ISettingsStorage<Settings> _storage;
 
-        public void Initialize(FlowLauncherJsonStorage<Settings> storage)
-        {
-            _storage = storage;
-        }
+        public Settings(ISettingsStorage<Settings> storage)
+        {
+            _storage = storage ?? throw new ArgumentNullException(nameof(storage));
+        }

Line range hint 1-590: Consider breaking down the Settings class into smaller, focused components.

The Settings class has grown to handle multiple concerns, making it harder to maintain and test. Consider splitting it into smaller, focused classes:

  1. HotkeySettings (hotkey-related properties)
  2. UISettings (window size, fonts, themes)
  3. BrowserSettings (browser configurations)
  4. ExplorerSettings (explorer configurations)
  5. PluginSettings (already partially separated)

This would:

  • Improve maintainability
  • Make the code more testable
  • Reduce the cognitive load when making changes
  • Make it easier to implement proper dependency injection
Flow.Launcher/ViewModel/MainViewModel.cs (1)

62-68: Consider future refactoring to constructor injection.

While using Ioc.Default.GetRequiredService works as a transitional solution, it introduces service location which can make the code harder to test and maintain. Consider gradually moving towards constructor injection in future refactors.

A more maintainable approach would be:

-public MainViewModel()
+public MainViewModel(Settings settings)
{
    _queryTextBeforeLeaveResults = "";
    _queryText = "";
    _lastQuery = new Query();
-   Settings = Ioc.Default.GetRequiredService<Settings>();
+   Settings = settings;
Flow.Launcher/App.xaml.cs (6)

89-90: Avoid using static instances when employing dependency injection

In lines 89-90, you assign the StringMatcher.Instance static property to the instance retrieved from the IoC container:

StringMatcher.Instance = stringMatcher;

This practice introduces a global state, which undermines the benefits of dependency injection and can lead to harder-to-test code. Consider refactoring the code to inject StringMatcher where needed rather than relying on the static instance.


98-98: Refactor to eliminate the static App.API property

While transitioning to dependency injection, retaining the static App.API property may lead to inconsistencies and reduced testability. Instead, inject IPublicAPI into classes that require it, promoting better modularity and adherence to dependency injection principles.


106-107: Inject dependencies into MainWindow instead of passing _settings directly

In lines 106-107, _settings is passed directly to MainWindow's constructor:

var window = new MainWindow(_settings, mainVM);

To maintain consistency with the dependency injection approach, consider injecting Settings into MainWindow via the IoC container. This enhances testability and aligns with best practices.


114-114: Refactor HotKeyMapper to use dependency injection

Currently, HotKeyMapper is initialized statically:

HotKeyMapper.Initialize(mainVM);

Consider refactoring HotKeyMapper to accept dependencies through constructor injection. This change would improve modularity and make the codebase more maintainable.


163-167: Optimize Updater by injecting IPublicAPI

In the AutoUpdates method, API is passed explicitly to UpdateAppAsync:

await Ioc.Default.GetRequiredService<Updater>().UpdateAppAsync(API);

Since Updater is registered in the IoC container, consider injecting IPublicAPI into Updater directly. This reduces dependencies between components and enhances code readability.


210-210: Avoid overusing Ioc.Default for resolving services

In line 210, MainViewModel is resolved directly from the service locator:

Ioc.Default.GetRequiredService<MainViewModel>().Show();

Frequent use of the service locator pattern can lead to code that's harder to maintain and test. Consider injecting MainViewModel where needed instead of resolving it directly.

Flow.Launcher/ActionKeywords.xaml.cs (1)

47-47: Inject IPublicAPI instead of accessing App.API directly

In line 47, App.API.ShowMsgBox(msg); is used to display a message box:

App.API.ShowMsgBox(msg);

To adhere to dependency injection principles and improve testability, consider injecting IPublicAPI into the ActionKeywords class. This approach reduces coupling to the App class and enhances modularity.

Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs (1)

31-31: LGTM! Consider adding error handling.

The change from MessageBoxEx.Show to API.ShowMsgBox aligns with the PR objectives. The lambda syntax is appropriate for the callback scenario.

Consider wrapping the folder removal in a try-catch block to handle potential IO exceptions:

-            FilesFolders.RemoveFolderIfExists(InstallPath, (s) => API.ShowMsgBox(s));
+            try {
+                FilesFolders.RemoveFolderIfExists(InstallPath, (s) => API.ShowMsgBox(s));
+            } catch (System.IO.IOException ex) {
+                API.ShowMsgBox($"Failed to remove folder: {ex.Message}");
+            }
Flow.Launcher/PriorityChangeWindow.xaml.cs (1)

47-47: LGTM! Consider extracting the message preparation.

The change aligns with the PR objectives.

Consider extracting the message preparation to improve readability:

-                string msg = translater.GetTranslation("invalidPriority");
-                App.API.ShowMsgBox(msg);
+                App.API.ShowMsgBox(translater.GetTranslation("invalidPriority"));
Flow.Launcher/CustomShortcutSetting.xaml.cs (1)

52-52: LGTM! Consider consolidating validation logic.

The change aligns with the PR objectives.

Consider consolidating the validation logic into a separate method:

+        private bool ValidateShortcut(string key, string value)
+        {
+            if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(value))
+            {
+                App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("emptyShortcut"));
+                return false;
+            }
+            if (((update && originalKey != key) || !update) && _hotkeyVm.DoesShortcutExist(key))
+            {
+                App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("duplicateShortcut"));
+                return false;
+            }
+            return true;
+        }
Flow.Launcher/CustomQueryHotkeySetting.xaml.cs (1)

66-66: LGTM! Error handling is maintained.

The change from MessageBoxEx.Show to App.API.ShowMsgBox maintains the existing error handling while aligning with the PR objectives.

Consider adding a descriptive error message that includes the invalid hotkey details:

-                App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("invalidPluginHotkey"));
+                var message = $"{InternationalizationManager.Instance.GetTranslation("invalidPluginHotkey")} (Action: {item.ActionKeyword}, Hotkey: {item.Hotkey})";
+                App.API.ShowMsgBox(message);
Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs (1)

Line range hint 65-72: LGTM! Consider extracting message strings to constants.

The migration from MessageBoxEx.Show to App.API.ShowMsgBox is implemented correctly. The internationalization and message box parameters are properly maintained.

Consider extracting the translation keys to constants at the class level for better maintainability and reusability:

+ private const string ClearLogFolderMessageKey = "clearlogfolderMessage";
+ private const string ClearLogFolderTitleKey = "clearlogfolder";

  private void AskClearLogFolderConfirmation()
  {
      var confirmResult = App.API.ShowMsgBox(
-         InternationalizationManager.Instance.GetTranslation("clearlogfolderMessage"),
-         InternationalizationManager.Instance.GetTranslation("clearlogfolder"),
+         InternationalizationManager.Instance.GetTranslation(ClearLogFolderMessageKey),
+         InternationalizationManager.Instance.GetTranslation(ClearLogFolderTitleKey),
          MessageBoxButton.YesNo
      );
Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs (2)

45-47: Extract common message to reduce duplication.

The "pleaseSelectAnItem" message is used multiple times. Consider extracting it to a constant or helper method.

+ private const string PleaseSelectItemKey = "pleaseSelectAnItem";
+ private void ShowPleaseSelectItemMessage() => 
+     App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation(PleaseSelectItemKey));

  private void CustomHotkeyDelete()
  {
      var item = SelectedCustomPluginHotkey;
      if (item is null)
      {
-         App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("pleaseSelectAnItem"));
+         ShowPleaseSelectItemMessage();
          return;
      }

Also applies to: 70-72, 91-93, 115-117


Line range hint 49-56: Consider extracting confirmation dialog pattern.

Both delete operations follow the same confirmation pattern. Consider extracting this to a reusable method.

+ private bool ConfirmDeletion<T>(T item, string warningKey, string itemDisplay)
+ {
+     var result = App.API.ShowMsgBox(
+         string.Format(
+             InternationalizationManager.Instance.GetTranslation(warningKey),
+             itemDisplay
+         ),
+         InternationalizationManager.Instance.GetTranslation("delete"),
+         MessageBoxButton.YesNo
+     );
+     return result == MessageBoxResult.Yes;
+ }

  private void CustomHotkeyDelete()
  {
      var item = SelectedCustomPluginHotkey;
      if (item is null)
      {
          ShowPleaseSelectItemMessage();
          return;
      }

-     var result = App.API.ShowMsgBox(/*...*/);
-     if (result is MessageBoxResult.Yes)
+     if (ConfirmDeletion(item, "deleteCustomHotkeyWarning", item.Hotkey))
      {
          Settings.CustomPluginHotkeys.Remove(item);
          HotKeyMapper.RemoveHotkey(item.Hotkey);
      }
  }

Also applies to: 95-102

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 7cccbb0 and 3cb9d1d.

📒 Files selected for processing (30)
  • Flow.Launcher.Core/Configuration/Portable.cs (7 hunks)
  • Flow.Launcher.Core/ExternalPlugins/Environments/AbstractPluginEnvironment.cs (5 hunks)
  • Flow.Launcher.Core/ExternalPlugins/Environments/PythonEnvironment.cs (1 hunks)
  • Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs (1 hunks)
  • Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs (1 hunks)
  • Flow.Launcher.Core/Plugin/PluginManager.cs (1 hunks)
  • Flow.Launcher.Core/Plugin/PluginsLoader.cs (2 hunks)
  • Flow.Launcher.Core/Resource/Internationalization.cs (2 hunks)
  • Flow.Launcher.Core/Resource/Theme.cs (4 hunks)
  • Flow.Launcher.Core/Updater.cs (4 hunks)
  • Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj (1 hunks)
  • Flow.Launcher.Infrastructure/PinyinAlphabet.cs (2 hunks)
  • Flow.Launcher.Infrastructure/StringMatcher.cs (2 hunks)
  • Flow.Launcher.Infrastructure/UserSettings/Settings.cs (2 hunks)
  • Flow.Launcher/ActionKeywords.xaml.cs (1 hunks)
  • Flow.Launcher/App.xaml.cs (5 hunks)
  • Flow.Launcher/CustomQueryHotkeySetting.xaml.cs (1 hunks)
  • Flow.Launcher/CustomShortcutSetting.xaml.cs (1 hunks)
  • Flow.Launcher/Flow.Launcher.csproj (2 hunks)
  • Flow.Launcher/Helper/HotKeyMapper.cs (1 hunks)
  • Flow.Launcher/MessageBoxEx.xaml (1 hunks)
  • Flow.Launcher/MessageBoxEx.xaml.cs (1 hunks)
  • Flow.Launcher/PriorityChangeWindow.xaml.cs (2 hunks)
  • Flow.Launcher/PublicAPIInstance.cs (1 hunks)
  • Flow.Launcher/SettingPages/ViewModels/SettingsPaneAboutViewModel.cs (1 hunks)
  • Flow.Launcher/SettingPages/ViewModels/SettingsPaneHotkeyViewModel.cs (4 hunks)
  • Flow.Launcher/SettingPages/ViewModels/SettingsPaneProxyViewModel.cs (1 hunks)
  • Flow.Launcher/SettingPages/ViewModels/SettingsPaneThemeViewModel.cs (1 hunks)
  • Flow.Launcher/ViewModel/MainViewModel.cs (2 hunks)
  • Flow.Launcher/ViewModel/SettingWindowViewModel.cs (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • Flow.Launcher/MessageBoxEx.xaml.cs
🧰 Additional context used
📓 Learnings (1)
Flow.Launcher/Flow.Launcher.csproj (1)
Learnt from: taooceros
PR: Flow-Launcher/Flow.Launcher#2616
File: Flow.Launcher/Flow.Launcher.csproj:7-7
Timestamp: 2024-11-12T03:58:56.325Z
Learning: In the Flow Launcher project, the version number in the `Flow.Launcher.csproj` file is dynamically updated during the CI/CD process.
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: gitStream.cm
  • GitHub Check: gitStream.cm
🔇 Additional comments (20)
Flow.Launcher.Core/Resource/Internationalization.cs (1)

14-14: LGTM! Using CommunityToolkit.Mvvm for dependency injection.

The addition of CommunityToolkit.Mvvm's dependency injection aligns well with the PR's objective to improve API access patterns.

Flow.Launcher.Infrastructure/UserSettings/Settings.cs (1)

7-7: LGTM!

The addition of the Storage namespace import is appropriate for the new storage functionality.

Flow.Launcher/PublicAPIInstance.cs (1)

36-36: Good use of abstraction by changing _alphabet to IAlphabet

Switching the type from PinyinAlphabet to the IAlphabet interface enhances flexibility and allows for different implementations, promoting a more extensible design.

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

28-28: LGTM! Using statement aligns with DI changes.

The addition of CommunityToolkit.Mvvm.DependencyInjection enables proper dependency injection support.


68-68: Verify DI container configuration.

Ensure that Settings is properly registered in the DI container before MainViewModel is instantiated.

Run this script to verify the DI setup:

✅ Verification successful

DI container configuration for Settings is properly set up

The Settings class is correctly registered as a singleton in the DI container before MainViewModel is instantiated, ensuring proper dependency management.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for proper DI registration of Settings
# Look for container configuration and Settings registration
rg -A 5 'AddSingleton<Settings>|services\.Add.*Settings'

Length of output: 58


Script:

#!/bin/bash
# First, find Settings class definition
ast-grep --pattern 'class Settings'

# Then look for service collection setup and registration patterns
rg -l "IServiceCollection|ServiceCollection|BuildServiceProvider"

# Also search for any Settings usage in startup/configuration files
rg -l "Settings" "*Program.cs" "*Startup.cs"

Length of output: 34619


Script:

#!/bin/bash
# Look for DI configuration in App.xaml.cs and service registration patterns
rg -A 10 "CreateHost|Host\.CreateDefault|ConfigureServices|AddSingleton|\.Services\." "Flow.Launcher/App.xaml.cs"

# Search for service registration using ast-grep to find method calls
ast-grep --pattern 'AddSingleton<$_>($_)'

# Look for Settings usage in App initialization
rg -A 5 "new Settings\(\)|GetService<Settings>|GetRequiredService<Settings>" "Flow.Launcher/App.xaml.cs"

Length of output: 1522

Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptV2Environment.cs (1)

28-28: Confirmed refactoring to centralized message handling

The update to use API.ShowMsgBox(s) aligns with the project's shift towards centralized message handling via IPublicAPI. This enhances consistency across the codebase.

Flow.Launcher/PriorityChangeWindow.xaml.cs (1)

27-27: LGTM! Error handling is maintained.

The change from MessageBoxEx.Show to App.API.ShowMsgBox maintains the existing error handling while aligning with the PR objectives.

Flow.Launcher/CustomShortcutSetting.xaml.cs (1)

46-46: LGTM! Validation is maintained.

The change from MessageBoxEx.Show to App.API.ShowMsgBox maintains the existing validation while aligning with the PR objectives.

Flow.Launcher/MessageBoxEx.xaml (1)

2-2: LGTM! Namespace changes align with project reorganization.

The MessageBoxEx class has been correctly moved from Flow.Launcher.Core to Flow.Launcher namespace, aligning with the PR objectives.

Let's verify that all references to MessageBoxEx have been updated:

Also applies to: 6-6

✅ Verification successful

✅ Namespace changes are properly implemented

All references to MessageBoxEx have been updated to use the new Flow.Launcher namespace. No remaining references to the old Flow.Launcher.Core namespace were found.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining references to Flow.Launcher.Core.MessageBoxEx
rg "Flow\.Launcher\.Core\.MessageBoxEx"

Length of output: 41


Script:

#!/bin/bash
# Search for MessageBoxEx references in all files
rg "MessageBoxEx" -A 2

# Specifically look for XAML references
fd -e xaml -x grep -l "MessageBoxEx" {} \;

Length of output: 1685

Flow.Launcher.Core/Updater.cs (2)

59-59: LGTM! Consistent API usage for message boxes.

The changes to use API.ShowMsgBox are consistent with the project-wide changes and maintain the same functionality.

Also applies to: 74-76, 89-89


26-26: Consider thread safety implications of singleton API instance.

While using dependency injection is good, retrieving the API instance at field initialization might cause issues if the IoC container isn't set up when the class is instantiated.

Consider injecting IPublicAPI through the constructor instead:

-private readonly IPublicAPI API = Ioc.Default.GetRequiredService<IPublicAPI>();
+private readonly IPublicAPI API;
+
+public Updater(IPublicAPI api)
+{
+    API = api;
+}
Flow.Launcher.Core/Configuration/Portable.cs (1)

19-19: LGTM! Well-structured dependency injection.

The changes properly implement dependency injection by using a private readonly field initialized through the constructor.

Flow.Launcher.Core/Resource/Theme.cs (2)

14-14: LGTM: Dependency injection setup looks good.

The addition of CommunityToolkit.Mvvm.DependencyInjection and initialization of IPublicAPI through dependency injection aligns with the PR objectives of improving the development experience.

Also applies to: 27-27


114-114: LGTM: Message box calls properly migrated.

The MessageBoxEx.Show calls have been consistently replaced with API.ShowMsgBox, maintaining the same functionality while using the new centralized message handling approach.

Also applies to: 124-124

Flow.Launcher.Core/Plugin/PluginManager.cs (1)

522-522: LGTM: Consistent message handling approach.

The change to use API.ShowMsgBox via lambda expression maintains consistency with the PR's objective of centralizing message handling.

Flow.Launcher.Core/ExternalPlugins/Environments/TypeScriptEnvironment.cs (1)

28-28: LGTM: Consistent message handling approach.

The change to use API.ShowMsgBox via lambda expression maintains consistency with the PR's objective of centralizing message handling.

Flow.Launcher/SettingPages/ViewModels/SettingsPaneProxyViewModel.cs (1)

25-25: LGTM! Change aligns with unified API access pattern.

The replacement of MessageBoxEx.Show with App.API.ShowMsgBox is consistent with the PR objective of unifying API access.

Let's verify error handling in proxy testing scenarios:

✅ Verification successful

Error handling verification successful

The proxy-related error handling is comprehensive and properly maintained:

  • Input validation for server and port
  • Connection failure handling
  • All error messages are properly internationalized
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other proxy-related error handling patterns
rg -A 2 "proxyConnectFailed|serverCantBeEmpty|portCantBeEmpty" --type cs

Length of output: 1129

Flow.Launcher/Helper/HotKeyMapper.cs (1)

49-49: LGTM! Consistent error handling pattern.

The change to App.API.ShowMsgBox maintains consistent error handling across the application.

Let's verify the consistency of error handling in hotkey-related scenarios:

✅ Verification successful

Verified: Message box usage is consistent across the codebase

The change from MessageBoxEx.Show to App.API.ShowMsgBox in HotKeyMapper.cs aligns with the application-wide pattern where all UI components use the API abstraction for showing message boxes.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for similar hotkey registration error handling patterns
rg "registerHotkeyFailed|HotkeyManager.Current" --type cs -A 2

Length of output: 1387


Script:

#!/bin/bash
# Search for MessageBoxEx.Show and App.API.ShowMsgBox usage
rg "MessageBoxEx\.Show|App\.API\.ShowMsgBox" --type cs -A 2

Length of output: 5488

Flow.Launcher.Infrastructure/Flow.Launcher.Infrastructure.csproj (1)

57-57: LGTM! Addition of MVVM toolkit enhances development experience.

The CommunityToolkit.Mvvm package addition supports the MVVM pattern implementation across the project.

Flow.Launcher/Flow.Launcher.csproj (1)

91-92: Review framework and package version compatibility.

The project targets .NET 7 but uses .NET 8 packages (Microsoft.Extensions.DependencyInjection and Microsoft.Extensions.Hosting version 8.0.1). This version mismatch might cause compatibility issues.

Consider downgrading to version 7.x.x or upgrading the target framework to .NET 8.

Let's verify the impact:

✅ Verification successful

Version mismatch is safe for current DI usage patterns

The codebase uses only basic DI features (Host builder and singleton registration) that are stable between .NET 7 and 8. While aligning versions with the target framework is recommended, the current implementation doesn't use any .NET 8 specific features that would cause compatibility issues.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for DI usage patterns that might be affected
rg "IServiceCollection|IServiceProvider|IHost" --type cs -A 2

Length of output: 1080


Script:

#!/bin/bash
# Search for DI setup and usage patterns
rg "using Microsoft\.Extensions\.(DependencyInjection|Hosting)" --type cs -A 3

# Search for common DI patterns
rg "AddSingleton|AddScoped|AddTransient|BuildServiceProvider|CreateDefaultBuilder|ConfigureServices" --type cs -A 3

Length of output: 1767

This comment has been minimized.

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: 2

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3cb9d1d and 2a423f0.

📒 Files selected for processing (1)
  • Flow.Launcher.Infrastructure/StringMatcher.cs (3 hunks)
🔇 Additional comments (2)
Flow.Launcher.Infrastructure/StringMatcher.cs (2)

11-11: LGTM!

The use of target-typed new() expression improves code conciseness while maintaining readability.


Line range hint 254-268: LGTM!

The implementation of CalculateClosestSpaceIndex is correct and the change to static is appropriate as it doesn't use instance state.

This comment has been minimized.

This comment has been minimized.

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

♻️ Duplicate comments (2)
Flow.Launcher.Infrastructure/StringMatcher.cs (2)

17-26: 🛠️ Refactor suggestion

Consider using constructor injection instead of service locator pattern.

The current implementation uses the service locator pattern which is generally considered an anti-pattern. While the test constructor provides a workaround, it would be better to use constructor injection consistently.

-public StringMatcher()
-{
-    _alphabet = Ioc.Default.GetRequiredService<IAlphabet>();
-}
-
-// This is a workaround to allow unit tests to set the instance
-public StringMatcher(IAlphabet alphabet)
-{
-    _alphabet = alphabet;
-}
+public StringMatcher(IAlphabet alphabet)
+{
+    _alphabet = alphabet ?? throw new ArgumentNullException(nameof(alphabet));
+}

256-257: ⚠️ Potential issue

Fix numeric character comparison in IsAcronymNumber.

The current implementation incorrectly compares character codes with integers.

-private static bool IsAcronymNumber(string stringToCompare, int compareStringIndex)
-    => stringToCompare[compareStringIndex] >= 0 && stringToCompare[compareStringIndex] <= 9;
+private static bool IsAcronymNumber(string stringToCompare, int compareStringIndex)
+    => char.IsDigit(stringToCompare[compareStringIndex]);
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between ff110b3 and 3bebb69.

📒 Files selected for processing (2)
  • Flow.Launcher.Infrastructure/StringMatcher.cs (3 hunks)
  • Flow.Launcher.Test/FuzzyMatcherTest.cs (11 hunks)
🔇 Additional comments (2)
Flow.Launcher.Infrastructure/StringMatcher.cs (2)

251-254: LGTM! Good static method refactoring.

The change to make IsAcronymChar static is appropriate as it doesn't depend on instance state.


Line range hint 260-274: LGTM! Good static method refactoring.

The change to make CalculateClosestSpaceIndex static is appropriate as it doesn't depend on instance state.

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

♻️ Duplicate comments (1)
Flow.Launcher/App.xaml.cs (1)

163-167: 🛠️ Refactor suggestion

Make update checks cancellable.

The update check task should be cancellable when the app exits to prevent potential memory leaks or hanging operations.

Apply this diff to make update checks cancellable:

+        private readonly CancellationTokenSource _updateCancellation = new();
+
         private void AutoUpdates()
         {
             _ = Task.Run(async () =>
             {
                 if (_settings.AutoUpdates)
                 {
                     // check update every 5 hours
                     var timer = new PeriodicTimer(TimeSpan.FromHours(5));
-                    await Ioc.Default.GetRequiredService<Updater>().UpdateAppAsync();
+                    await Ioc.Default.GetRequiredService<Updater>().UpdateAppAsync(_updateCancellation.Token);

-                    while (await timer.WaitForNextTickAsync())
+                    while (await timer.WaitForNextTickAsync(_updateCancellation.Token))
                         // check updates on startup
-                        await Ioc.Default.GetRequiredService<Updater>().UpdateAppAsync();
+                        await Ioc.Default.GetRequiredService<Updater>().UpdateAppAsync(_updateCancellation.Token);
                 }
-            });
+            }, _updateCancellation.Token);
         }

         public void Dispose()
         {
             if (!_disposed)
             {
+                _updateCancellation.Cancel();
                 API.SaveAppAllSettings();
                 _disposed = true;
             }
         }
🧹 Nitpick comments (1)
Flow.Launcher/App.xaml.cs (1)

96-97: Track technical debt for static instance cleanup.

The TODOs indicate a plan to remove static instances of InternationalizationManager and ThemeManager. Consider creating issues to track this technical debt.

Would you like me to create issues to track the removal of these static instances? This will help ensure the cleanup tasks aren't forgotten.

Also applies to: 117-118

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between e989b5a and ad63b0e.

📒 Files selected for processing (5)
  • Flow.Launcher.Core/Resource/Theme.cs (12 hunks)
  • Flow.Launcher.Core/Updater.cs (5 hunks)
  • Flow.Launcher/App.xaml.cs (5 hunks)
  • Flow.Launcher/Helper/HotKeyMapper.cs (3 hunks)
  • Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • Plugins/Flow.Launcher.Plugin.Explorer/Views/ActionKeywordSetting.xaml.cs
🚧 Files skipped from review as they are similar to previous changes (2)
  • Flow.Launcher/Helper/HotKeyMapper.cs
  • Flow.Launcher.Core/Resource/Theme.cs
🔇 Additional comments (5)
Flow.Launcher/App.xaml.cs (3)

30-30: LGTM! Good use of interface abstraction.

The change from PublicAPIInstance to IPublicAPI improves loose coupling and aligns with dependency injection principles.


35-63: Improve error handling and code organization in constructor.

While the DI setup is well-structured, consider:

  1. Adding error handling for settings and DI container initialization
  2. Moving DI container setup to a separate method for better organization

Apply this diff to improve the constructor:

 public App()
 {
     try
     {
         // Initialize settings
         InitializeSettings();
         
         // Configure and build DI container
         var host = ConfigureDependencyInjection();
         Ioc.Default.ConfigureServices(host.Services);
 
         // Initialize the public API and Settings first
         API = Ioc.Default.GetRequiredService<IPublicAPI>();
         _settings.Initialize();
     }
     catch (Exception ex)
     {
         MessageBox.Show($"Failed to initialize application: {ex.Message}", "Critical Error",
             MessageBoxButton.OK, MessageBoxImage.Error);
         throw;
     }
 }

 private void InitializeSettings()
 {
     var storage = new FlowLauncherJsonStorage<Settings>();
     _settings = storage.Load();
     _settings.SetStorage(storage);
     _settings.WMPInstalled = WindowsMediaPlayerHelper.IsWindowsMediaPlayerInstalled();
 }

 private IHost ConfigureDependencyInjection()
 {
     return Host.CreateDefaultBuilder()
         .UseContentRoot(AppContext.BaseDirectory)
         .ConfigureServices(services => services
             .AddSingleton(_ => _settings)
             .AddSingleton(sp => new Updater(sp.GetRequiredService<IPublicAPI>(), Launcher.Properties.Settings.Default.GithubRepo))
             .AddSingleton<Portable>()
             .AddSingleton<SettingWindowViewModel>()
             .AddSingleton<IAlphabet, PinyinAlphabet>()
             .AddSingleton<StringMatcher>()
             .AddSingleton<Internationalization>()
             .AddSingleton<IPublicAPI, PublicAPIInstance>()
             .AddSingleton<MainViewModel>()
             .AddSingleton<Theme>()
         ).Build();
 }

210-210: LGTM! Good use of dependency injection.

The change correctly uses DI to get the MainViewModel instance.

Flow.Launcher.Core/Updater.cs (2)

25-28: LGTM! Thread-safe property implementation.

The init-only property ensures thread safety by preventing modifications after initialization, and the private field follows C# naming conventions.


37-104: LGTM! Consistent use of dependency injection.

The changes effectively utilize the injected _api instance throughout the method, maintaining consistent error handling and translation support while removing the need for passing the API as a parameter.

This comment has been minimized.

@taooceros
Copy link
Member

taooceros commented Feb 23, 2025

@Jack251970 I make a few more service to be injected via constructor when possible. Please take a brief look.

I think the status of this pr is fine to merge. It's hard to get such kind of refactor to work, very nice work!

taooceros
taooceros previously approved these changes Feb 23, 2025
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: 3

♻️ Duplicate comments (7)
Flow.Launcher.Infrastructure/StringMatcher.cs (3)

1-23: 🛠️ Refactor suggestion

Consider using constructor injection instead of service locator pattern.

While the changes introduce dependency injection, using Ioc.Default (service locator pattern) is generally considered an anti-pattern. The constructor injection for Settings is a step in the right direction, but the service locator is still used elsewhere.

Apply this diff to improve the dependency injection approach:

-public StringMatcher(IAlphabet alphabet, Settings settings)
+public StringMatcher(IAlphabet alphabet, Settings settings, IServiceProvider serviceProvider)
 {
-    _alphabet = alphabet;
+    _alphabet = alphabet ?? throw new ArgumentNullException(nameof(alphabet));
+    _settings = settings ?? throw new ArgumentNullException(nameof(settings));
     UserSettingSearchPrecision = settings.QuerySearchPrecision;
 }

25-28: 🛠️ Refactor suggestion

Replace service locator with constructor injection.

The static FuzzySearch method uses service locator pattern which makes testing and dependency tracking harder.

Apply this diff to improve the implementation:

-public static MatchResult FuzzySearch(string query, string stringToCompare)
-{
-    return Ioc.Default.GetRequiredService<StringMatcher>().FuzzyMatch(query, stringToCompare);
-}
+public MatchResult FuzzySearch(string query, string stringToCompare)
+{
+    return FuzzyMatch(query, stringToCompare);
+}

246-252: ⚠️ Potential issue

Fix numeric character comparison in IsAcronymNumber.

The current implementation incorrectly compares characters with numeric values. The comparison stringToCompare[compareStringIndex] >= 0 && stringToCompare[compareStringIndex] <= 9 compares character codes with integers.

Apply this diff to fix the numeric check:

-private static bool IsAcronymNumber(string stringToCompare, int compareStringIndex)
-    => stringToCompare[compareStringIndex] >= 0 && stringToCompare[compareStringIndex] <= 9;
+private static bool IsAcronymNumber(string stringToCompare, int compareStringIndex)
+    => char.IsDigit(stringToCompare[compareStringIndex]);
Flow.Launcher/App.xaml.cs (1)

161-165: 🛠️ Refactor suggestion

Make update checks cancellable.

The update check task should be cancellable when the app exits to prevent potential memory leaks or hanging operations.

Flow.Launcher/PublicAPIInstance.cs (3)

40-44: 🛠️ Refactor suggestion

Consider using constructor injection over service location for better testability.

Using Ioc.Default.GetRequiredService<T>() within the constructor introduces a dependency on the service locator pattern, which can make the code harder to test and less maintainable. Prefer using constructor injection to explicitly declare dependencies.

Apply this diff to improve testability:

-public PublicAPIInstance()
+public PublicAPIInstance(
+    SettingWindowViewModel settingsVM,
+    MainViewModel mainVM)
{
-    _settingsVM = Ioc.Default.GetRequiredService<SettingWindowViewModel>();
-    _mainVM = Ioc.Default.GetRequiredService<MainViewModel>();
+    _settingsVM = settingsVM;
+    _mainVM = mainVM;
    GlobalHotkey.hookedKeyboardCallback = KListener_hookedKeyboardCallback;
    WebRequest.RegisterPrefix("data", new DataWebRequestFactory());
}

88-88: ⚠️ Potential issue

Add proper async/await handling for image loader save.

Similar to the update check, discarding the Task could hide potential errors.

Apply this diff to properly handle async operations:

-_ = ImageLoader.Save();
+try
+{
+    await ImageLoader.Save();
+}
+catch (Exception ex)
+{
+    LogException(nameof(PublicAPIInstance), "Failed to save image loader", ex);
+}

80-81: ⚠️ Potential issue

Add proper error handling for update check.

The current implementation discards the Task with _, which could hide potential errors. Additionally, using service locator here introduces the same testability issues as in the constructor.

Apply this diff to improve error handling and testability:

-// Must use Ioc.Default.GetRequiredService<Updater>() to avoid circular dependency
-public void CheckForNewUpdate() => _ = Ioc.Default.GetRequiredService<Updater>().UpdateAppAsync(false);
+private readonly Updater _updater;
+
+public PublicAPIInstance(
+    SettingWindowViewModel settingsVM,
+    MainViewModel mainVM,
+    Updater updater)
+{
+    _updater = updater;
+    // ... existing constructor code ...
+}
+
+public async Task CheckForNewUpdate()
+{
+    try
+    {
+        await _updater.UpdateAppAsync(false);
+    }
+    catch (Exception ex)
+    {
+        LogException(nameof(PublicAPIInstance), "Failed to check for updates", ex);
+        ShowMsgError("Update Check Failed", ex.Message);
+    }
+}
🧹 Nitpick comments (4)
Flow.Launcher.Infrastructure/StringMatcher.cs (1)

12-12: Use explicit type declaration for better readability.

The use of new() target-typed new expression can make the code less readable, especially for complex types.

Apply this diff to improve readability:

-private readonly MatchOption _defaultMatchOption = new();
+private readonly MatchOption _defaultMatchOption = new MatchOption();
Flow.Launcher/ViewModel/SettingWindowViewModel.cs (2)

1-1: Remove unused import.

After moving to constructor injection, the CommunityToolkit.Mvvm.DependencyInjection import is no longer needed.

-using CommunityToolkit.Mvvm.DependencyInjection;

7-47: Great architectural improvements!

The changes successfully:

  • Replace service locator with proper dependency injection
  • Avoid cyclic dependencies through constructor injection
  • Improve testability by making dependencies explicit
  • Maintain existing functionality while improving the architecture

This aligns well with the PR objectives of implementing dependency injection for better development experience.

Flow.Launcher/App.xaml.cs (1)

94-95: Track TODO comments for future cleanup.

The TODO comments indicate plans to remove static instances of InternationalizationManager and ThemeManager. These should be tracked to ensure they're addressed.

Would you like me to create issues to track the removal of these static instances?

Also applies to: 115-116

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between ad63b0e and f5f0986.

📒 Files selected for processing (5)
  • Flow.Launcher.Infrastructure/StringMatcher.cs (2 hunks)
  • Flow.Launcher/App.xaml.cs (5 hunks)
  • Flow.Launcher/PublicAPIInstance.cs (6 hunks)
  • Flow.Launcher/SettingWindow.xaml.cs (5 hunks)
  • Flow.Launcher/ViewModel/SettingWindowViewModel.cs (1 hunks)
🔇 Additional comments (9)
Flow.Launcher/SettingWindow.xaml.cs (5)

21-22: LGTM! Fields are properly marked as readonly.

The new fields for injected dependencies follow good practices by being marked as readonly, preventing accidental modifications after initialization.


27-35: Consider the trade-offs of service locator pattern.

While using Ioc.Default.GetRequiredService (service locator pattern) makes dependencies implicit and complicates testing, I understand from the PR objectives that this is an intentional transitional solution to maintain compatibility with App.API. However, consider planning a future refactor to:

  • Make dependencies explicit through constructor injection
  • Improve testability
  • Make class dependencies clear to consumers

134-147: LGTM! Appropriate use of static method.

The IsPositionValid method has been correctly marked as static since it doesn't access any instance members.


154-154: LGTM! Removed unnecessary this keyword.

Removing the this keyword improves code clarity as it's not needed for disambiguation here.

Also applies to: 163-163


169-169: LGTM! Correctly using injected services.

The PaneData initialization properly uses the injected services _updater and _portable.

Flow.Launcher/ViewModel/SettingWindowViewModel.cs (2)

11-14: LGTM! Good use of constructor injection.

The constructor now properly accepts dependencies through injection, which is a significant improvement over the service locator pattern.


21-21: LGTM! Clean property implementations.

The properties correctly delegate to the injected settings instance, maintaining functionality while leveraging dependency injection.

Also applies to: 26-27, 32-33, 38-39, 44-45

Flow.Launcher/App.xaml.cs (2)

30-30: Good use of interface abstraction!

Changing the API property type from PublicAPIInstance to IPublicAPI improves abstraction and testability.


206-209: Good use of dependency injection for second instance handling!

The change consistently follows the DI pattern by using Ioc.Default.GetRequiredService<MainViewModel>().

This comment has been minimized.

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

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between f5f0986 and ef68ccb.

📒 Files selected for processing (1)
  • Flow.Launcher.Infrastructure/StringMatcher.cs (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: gitStream.cm
  • GitHub Check: gitStream.cm
🔇 Additional comments (4)
Flow.Launcher.Infrastructure/StringMatcher.cs (4)

1-1: Consider using constructor injection instead of service locator pattern.

While the change introduces dependency injection, using Ioc.Default (service locator pattern) is generally considered an anti-pattern as it makes dependencies implicit and harder to track.

Apply this diff to improve the implementation:

-using CommunityToolkit.Mvvm.DependencyInjection;

 public StringMatcher(IAlphabet alphabet, Settings settings)
 {
     _alphabet = alphabet ?? throw new ArgumentNullException(nameof(alphabet));
-    UserSettingSearchPrecision = settings.QuerySearchPrecision;
+    UserSettingSearchPrecision = settings?.QuerySearchPrecision ?? throw new ArgumentNullException(nameof(settings));
 }

Also applies to: 18-21


30-33: Refactor static method to use dependency injection properly.

The static FuzzySearch method uses service locator pattern which makes testing and dependency tracking harder.

Apply this diff to improve the implementation:

-public static MatchResult FuzzySearch(string query, string stringToCompare)
-{
-    return Ioc.Default.GetRequiredService<StringMatcher>().FuzzyMatch(query, stringToCompare);
-}
+public MatchResult FuzzySearch(string query, string stringToCompare)
+{
+    return FuzzyMatch(query, stringToCompare);
+}

256-257: Fix numeric character comparison in IsAcronymNumber.

The current implementation incorrectly compares characters with numeric values.

Apply this diff to fix the numeric check:

-private static bool IsAcronymNumber(string stringToCompare, int compareStringIndex)
-    => stringToCompare[compareStringIndex] >= 0 && stringToCompare[compareStringIndex] <= 9;
+private static bool IsAcronymNumber(string stringToCompare, int compareStringIndex)
+    => char.IsDigit(stringToCompare[compareStringIndex]);

251-254: LGTM: Method visibility changes improve code organization.

The conversion of helper methods to static improves code organization by clearly indicating these methods don't depend on instance state.

Also applies to: 260-261

@Jack251970
Copy link
Contributor Author

@taooceros Hi, I fix unit test build issue and remove usage of _settingsVM._settings, could you please check if this works for you?

This comment has been minimized.

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 (3)
Flow.Launcher/PublicAPIInstance.cs (3)

42-47: 🛠️ Refactor suggestion

Consider using constructor injection over service location.

Using Ioc.Default.GetRequiredService<T>() within the constructor introduces a dependency on the service locator pattern, which can make the code harder to test and less maintainable.

Apply this diff to use constructor injection:

-public PublicAPIInstance()
+public PublicAPIInstance(
+    Settings settings,
+    SettingWindowViewModel settingsVM,
+    MainViewModel mainVM)
{
-    _settings = Ioc.Default.GetRequiredService<Settings>();
-    _settingsVM = Ioc.Default.GetRequiredService<SettingWindowViewModel>();
-    _mainVM = Ioc.Default.GetRequiredService<MainViewModel>();
+    _settings = settings;
+    _settingsVM = settingsVM;
+    _mainVM = mainVM;
    GlobalHotkey.hookedKeyboardCallback = KListener_hookedKeyboardCallback;
    WebRequest.RegisterPrefix("data", new DataWebRequestFactory());
}

83-84: ⚠️ Potential issue

Add proper error handling for update check.

The current implementation discards the Task with _, which could hide potential errors.

Apply this diff to properly handle async operations:

-public void CheckForNewUpdate() => _ = Ioc.Default.GetRequiredService<Updater>().UpdateAppAsync(false);
+public async Task CheckForNewUpdate()
+{
+    try
+    {
+        await Ioc.Default.GetRequiredService<Updater>().UpdateAppAsync(false);
+    }
+    catch (Exception ex)
+    {
+        LogException(nameof(PublicAPIInstance), "Failed to check for updates", ex);
+        ShowMsgError("Update Check Failed", ex.Message);
+    }
+}

91-91: ⚠️ Potential issue

Add proper error handling for image loader save.

Similar to the update check, discarding the Task could hide potential errors.

Apply this diff to properly handle async operations:

-_ = ImageLoader.Save();
+try
+{
+    await ImageLoader.Save();
+}
+catch (Exception ex)
+{
+    LogException(nameof(PublicAPIInstance), "Failed to save image loader", ex);
+}
🧹 Nitpick comments (1)
Flow.Launcher/PublicAPIInstance.cs (1)

253-254: Avoid direct access to internal settings field.

Accessing _settings directly bypasses any encapsulation provided by Settings. Consider using properties or methods to access settings.

Apply this diff to use proper encapsulation:

-var explorerInfo = _settings.CustomExplorer;
+var explorerInfo = _settingsVM.Settings.CustomExplorer;

-var browserInfo = _settings.CustomBrowser;
+var browserInfo = _settingsVM.Settings.CustomBrowser;

Also applies to: 274-275

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between ef68ccb and 91490c0.

📒 Files selected for processing (2)
  • Flow.Launcher/PublicAPIInstance.cs (5 hunks)
  • Flow.Launcher/ViewModel/SettingWindowViewModel.cs (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: gitStream.cm
  • GitHub Check: Report (PR)
🔇 Additional comments (5)
Flow.Launcher/ViewModel/SettingWindowViewModel.cs (4)

6-8: LGTM! Good use of encapsulation and immutability.

The changes demonstrate good practices:

  • Using partial class for potential extensions
  • Private readonly field for better encapsulation
  • Immutable field that can only be set in constructor

10-13: LGTM! Good use of constructor injection.

The constructor now properly follows dependency injection pattern, which:

  • Improves testability
  • Makes dependencies explicit
  • Avoids service locator anti-pattern

15-21: LGTM! Clear documentation and straightforward implementation.

The Save method is well-documented and follows single responsibility principle.


23-45: LGTM! Clean property delegation pattern.

The properties provide a clean abstraction over settings while avoiding state duplication.

Flow.Launcher/PublicAPIInstance.cs (1)

28-37: LGTM! Good separation of concerns with DI.

The addition of CommunityToolkit.Mvvm.DependencyInjection and the new Settings field properly separates configuration concerns.

Copy link

@check-spelling-bot Report

🔴 Please review

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

❌ Errors Count
❌ forbidden-pattern 22
⚠️ ignored-expect-variant 1
⚠️ 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.

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 (2)
Flow.Launcher/PublicAPIInstance.cs (2)

81-82: 🛠️ Refactor suggestion

Service locator pattern introduces testability concerns.

Using Ioc.Default.GetRequiredService<Updater>() makes the code harder to test and maintain. While the comment explains it's to avoid circular dependency, consider restructuring to use constructor injection.

Consider this alternative approach:

-// Must use Ioc.Default.GetRequiredService<Updater>() to avoid circular dependency
-public void CheckForNewUpdate() => _ = Ioc.Default.GetRequiredService<Updater>().UpdateAppAsync(false);
+private readonly Lazy<Updater> _updater;
+
+public PublicAPIInstance(Settings settings, MainViewModel mainVM, Func<Updater> updaterFactory)
+{
+    _updater = new Lazy<Updater>(updaterFactory);
+    // ... other initialization
+}
+
+public void CheckForNewUpdate() => _ = _updater.Value.UpdateAppAsync(false);

88-89: ⚠️ Potential issue

Improve async operation handling in SaveAppAllSettings.

The method discards the Task from ImageLoader.Save() with _, which could hide potential errors.

Apply this diff to properly handle the async operation:

 public void SaveAppAllSettings()
 {
     PluginManager.Save();
     _mainVM.Save();
     _settings.Save();
-    _ = ImageLoader.Save();
+    try
+    {
+        await ImageLoader.Save();
+    }
+    catch (Exception ex)
+    {
+        LogException(nameof(PublicAPIInstance), "Failed to save image loader", ex);
+    }
 }
🧹 Nitpick comments (2)
Flow.Launcher/PublicAPIInstance.cs (2)

28-30: Consider the implications of direct Settings dependency.

The change from SettingWindowViewModel to Settings reduces a layer of abstraction. While this aligns with the PR's objective of improving dependency injection, ensure that UI-specific settings logic isn't leaked into this class.

Also applies to: 36-36


41-47: Constructor changes look good but consider constructor injection for all dependencies.

The removal of PinyinAlphabet and switch to Settings simplifies the constructor. However, the use of Ioc.Default.GetRequiredService elsewhere in the class suggests not all dependencies are constructor-injected.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 91490c0 and 16798a0.

📒 Files selected for processing (1)
  • Flow.Launcher/PublicAPIInstance.cs (5 hunks)
🔇 Additional comments (2)
Flow.Launcher/PublicAPIInstance.cs (2)

109-109: LGTM: Settings window opening simplified.

The removal of the _settingsVM parameter in SingletonWindowOpener.Open<SettingWindow>() aligns with the dependency injection improvements.


251-251: Direct access to settings properties is appropriate here.

The direct access to _settings.CustomExplorer and _settings.CustomBrowser is cleaner than the previous approach through _settingsVM. This aligns with the PR's objective of improving the development experience.

Also applies to: 272-272

@Jack251970 Jack251970 requested a review from taooceros February 24, 2025 08:31
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.

2 participants