Skip to content

feat(HikVision): add IsOpenSound parameter#7412

Merged
ArgoZhang merged 2 commits intomainfrom
fix-hik
Dec 25, 2025
Merged

feat(HikVision): add IsOpenSound parameter#7412
ArgoZhang merged 2 commits intomainfrom
fix-hik

Conversation

@ArgoZhang
Copy link
Copy Markdown
Member

@ArgoZhang ArgoZhang commented Dec 25, 2025

Link issues

fixes #7411

Summary By Copilot

Regression?

  • Yes
  • No

Risk

  • High
  • Medium
  • Low

Verification

  • Manual (required)
  • Automated

Packaging changes reviewed?

  • Yes
  • No
  • N/A

☑️ Self Check before Merge

⚠️ Please check all items below before review. ⚠️

  • Doc is updated/provided or not needed
  • Demo is updated/provided or not needed
  • Merge the latest code from the main branch

Summary by Sourcery

Update the HikVision sample to derive UI control states from the HikVision component’s runtime properties, including new sound and recording state flags, and adjust the button layout for better responsiveness.

New Features:

  • Expose and use the HikVision sound open state (IsOpenSound) to control related UI actions.

Enhancements:

  • Bind login, preview, sound, and recording button enabled states directly to HikVision login, real-play, sound, and recording status instead of maintaining separate local flags.
  • Improve the HikVision sample action buttons layout with a responsive grid container.

Copilot AI review requested due to automatic review settings December 25, 2025 05:29
@bb-auto bb-auto Bot added the enhancement New feature or request label Dec 25, 2025
@bb-auto bb-auto Bot added this to the v10.1.0 milestone Dec 25, 2025
@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Dec 25, 2025

Reviewer's Guide

Refactors the HikVision sample component to derive button enabled/disabled states directly from the HikVision service state (including new IsOpenSound and IsStartRecord flags), simplifies event handlers, and updates the UI layout for the control buttons while removing the now-unneeded CSS file.

Sequence diagram for OpenSound using IsOpenSound state

sequenceDiagram
    actor User
    participant HikVisions as HikVisionsComponent
    participant HikVisionService
    participant ToastService

    User->>HikVisions: click OpenSound button
    HikVisions->>HikVisionService: OpenSound()
    HikVisionService-->>HikVisions: result (bool)
    alt result is true
        HikVisions->>ToastService: Success(消息通知, 打开声音成功)
        HikVisions->>HikVisions: recompute _openSoundStatus, _closeSoundStatus
    else result is false
        HikVisions->>ToastService: Error(消息通知, 打开声音失败)
        HikVisions->>HikVisions: recompute _openSoundStatus, _closeSoundStatus
    end

    note over HikVisions,HikVisionService: OpenSound button disabled when\n_hikVision is not { IsLogin: true, IsRealPlaying: true, IsOpenSound: false }\nCloseSound button disabled when\n_hikVision is not { IsLogin: true, IsRealPlaying: true, IsOpenSound: true }
Loading

Class diagram for the refactored HikVisions component state handling

classDiagram
    class HikVisions {
        - string _ip
        - int _port
        - string _userName
        - string _password
        - bool _inited
        - HikVisionService _hikVision
        - List~SelectedItem~ _analogChannels
        - int _channelId
        bool _loginStatus
        bool _logoutStatus
        bool _startRealPlayStatus
        bool _stopRealPlayStatus
        bool _openSoundStatus
        bool _closeSoundStatus
        bool _startRecordStatus
        bool _stopRecordStatus
        + Task OnLogin()
        + Task OnLogout()
        + Task OnStartRealPlay()
        + Task OnStopRealPlay()
        + Task OnOpenSound()
        + Task OnCloseSound()
        + Task OnStartRecord()
        + Task OnStopRecord()
        + Task OnCapture()
        + Task OnInitedAsync(bool initialized)
        + Task OnLoginAsync()
        + Task OnGetChannelsAsync(HikVisionChannel channel)
        + Task OnLogoutAsync()
        + Task OnStartRealPlayedAsync()
        + Task OnStopRealPlayedAsync()
    }

    note for HikVisions "Computed button states now derived from HikVisionService state:\n_loginStatus => _hikVision.IsLogin\n_logoutStatus => !_hikVision.IsLogin\n_startRealPlayStatus => _hikVision is not { IsLogin: true, IsRealPlaying: false }\n_stopRealPlayStatus => _hikVision is not { IsLogin: true, IsRealPlaying: true }\n_openSoundStatus => _hikVision is not { IsLogin: true, IsRealPlaying: true, IsOpenSound: false }\n_closeSoundStatus => _hikVision is not { IsLogin: true, IsRealPlaying: true, IsOpenSound: true }\n_startRecordStatus => _hikVision is not { IsLogin: true, IsRealPlaying: true, IsStartRecord: false }\n_stopRecordStatus => _hikVision is not { IsLogin: true, IsRealPlaying: true, IsStartRecord: true }"

    class HikVisionService {
        bool IsLogin
        bool IsRealPlaying
        bool IsOpenSound
        bool IsStartRecord
        + Task~bool~ Login(string ip, int port, string userName, string password, HikVisionLoginType loginType)
        + Task Logout()
        + Task StartRealPlay(int streamType, int channelId)
        + Task StopRealPlay()
        + Task~bool~ OpenSound()
        + Task~bool~ CloseSound()
        + Task~bool~ StartRecord()
        + Task~bool~ StopRecord()
    }

    HikVisions --> HikVisionService : uses
Loading

File-Level Changes

Change Details Files
Drive button enabled/disabled state directly from HikVision instance properties instead of manually tracking status flags.
  • Replace mutable boolean fields for login, real play, sound, and recording states with expression-bodied properties that read from _hikVision (IsLogin, IsRealPlaying, IsOpenSound, IsStartRecord).
  • Update the logic for enabling/disabling login/logout, real play, sound, and record buttons to depend on HikVision state patterns (e.g., require IsLogin and IsRealPlaying for sound/record actions).
  • Remove all manual updates of these status fields from event handlers and lifecycle callbacks, relying on _hikVision’s state plus StateHasChanged instead.
src/BootstrapBlazor.Server/Components/Samples/HikVisions.razor.cs
Simplify HikVision control event handlers to delegate state management to the HikVision service and UI binding.
  • Stop resetting UI state booleans in OnLogin, OnLogout, OnStartRealPlay, OnStopRealPlay, OnOpenSound, OnCloseSound, OnStartRecord, OnStopRecord, and async callback methods; keep only service calls, toast notifications, channel list clearing, and StateHasChanged.
  • Remove now-obsolete initialization logic that toggled status flags when the component or playback started/stopped.
src/BootstrapBlazor.Server/Components/Samples/HikVisions.razor.cs
Improve the layout of HikVision control buttons using a responsive CSS grid and remove the dedicated CSS file.
  • Wrap the control buttons in a div using CSS grid with auto-fit columns and gaps for better responsive layout.
  • Delete the component-specific HikVisions.razor.css stylesheet as it is no longer needed for layout.
  • Keep the per-button bindings and click handlers intact while only changing the surrounding markup structure.
src/BootstrapBlazor.Server/Components/Samples/HikVisions.razor
src/BootstrapBlazor.Server/Components/Samples/HikVisions.razor.css

Assessment against linked issues

Issue Objective Addressed Explanation
#7411 Add and use an IsOpenSound parameter/property in the HikVision-related code so that sound-open state is tracked and used to control UI behavior (e.g., button enabled/disabled states).

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@ArgoZhang ArgoZhang merged commit d39741f into main Dec 25, 2025
6 of 8 checks passed
@ArgoZhang ArgoZhang deleted the fix-hik branch December 25, 2025 05:29
Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • _loginStatus now directly accesses _hikVision.IsLogin without a null check, unlike the other status properties that use pattern matching; consider making this consistent (e.g., _hikVision?.IsLogin == true) to avoid a potential null reference during initialization.
  • The new inline grid styling for the button group (style="width: 100%; display: grid; ...") would be easier to maintain and more consistent with the rest of the codebase if moved into a CSS class instead of using a large inline style block.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- _loginStatus now directly accesses `_hikVision.IsLogin` without a null check, unlike the other status properties that use pattern matching; consider making this consistent (e.g., `_hikVision?.IsLogin == true`) to avoid a potential null reference during initialization.
- The new inline grid styling for the button group (`style="width: 100%; display: grid; ..."`) would be easier to maintain and more consistent with the rest of the codebase if moved into a CSS class instead of using a large inline style block.

## Individual Comments

### Comment 1
<location> `src/BootstrapBlazor.Server/Components/Samples/HikVisions.razor.cs:27-39` </location>
<code_context>
-    private bool _closeSoundStatus = true;
-    private bool _startRecordStatus = true;
-    private bool _stopRecordStatus = true;
+    private bool _loginStatus => _hikVision.IsLogin;
+    private bool _logoutStatus => !_hikVision.IsLogin;
+    private bool _startRealPlayStatus => _hikVision is not { IsLogin: true, IsRealPlaying: false };
+    private bool _stopRealPlayStatus => _hikVision is not { IsLogin: true, IsRealPlaying: true };
+    private bool _openSoundStatus => _hikVision is not { IsLogin: true, IsRealPlaying: true, IsOpenSound: false };
+    private bool _closeSoundStatus => _hikVision is not { IsLogin: true, IsRealPlaying: true, IsOpenSound: true };
+    private bool _startRecordStatus => _hikVision is not { IsLogin: true, IsRealPlaying: true, IsStartRecord: false };
+    private bool _stopRecordStatus => _hikVision is not { IsLogin: true, IsRealPlaying: true, IsStartRecord: true };

     private List<SelectedItem> _analogChannels = [];
</code_context>

<issue_to_address>
**suggestion:** The new predicate-based disable flags are compact but fairly hard to parse; consider refactoring for clarity.

These predicates for real play, sound, and record use inverted logic (`true` = disabled), which makes them harder to reason about. Consider renaming them to reflect the disabled state (e.g. `_startRealPlayDisabled`) or extracting the conditions into clearly named helpers (e.g. `CanStartRealPlay`) to improve readability and future maintenance.

```suggestion
    private bool _inited;

    // Capability predicates (positive logic) for readability
    private bool CanLogin => !_hikVision.IsLogin;
    private bool CanLogout => _hikVision.IsLogin;
    private bool CanStartRealPlay => _hikVision is { IsLogin: true, IsRealPlaying: false };
    private bool CanStopRealPlay => _hikVision is { IsLogin: true, IsRealPlaying: true };
    private bool CanOpenSound => _hikVision is { IsLogin: true, IsRealPlaying: true, IsOpenSound: false };
    private bool CanCloseSound => _hikVision is { IsLogin: true, IsRealPlaying: true, IsOpenSound: true };
    private bool CanStartRecord => _hikVision is { IsLogin: true, IsRealPlaying: true, IsStartRecord: false };
    private bool CanStopRecord => _hikVision is { IsLogin: true, IsRealPlaying: true, IsStartRecord: true };

    // Status flags are used as disabled predicates in the UI (true => disabled)
    private bool _loginStatus => !CanLogin;
    private bool _logoutStatus => !CanLogout;
    private bool _startRealPlayStatus => !CanStartRealPlay;
    private bool _stopRealPlayStatus => !CanStopRealPlay;
    private bool _openSoundStatus => !CanOpenSound;
    private bool _closeSoundStatus => !CanCloseSound;
    private bool _startRecordStatus => !CanStartRecord;
    private bool _stopRecordStatus => !CanStopRecord;

    private List<SelectedItem> _analogChannels = [];
    private int _channelId = 1;
```
</issue_to_address>

### Comment 2
<location> `src/BootstrapBlazor.Server/Components/Samples/HikVisions.razor:72` </location>
<code_context>
-                    <Button OnClick="OnStopRecord" IsDisabled="_stopRecordStatus">
-                        <span>停止录像</span>
-                    </Button>
+                    <div style="width: 100%; display: grid; gap: .5rem; grid-template-columns: repeat(auto-fit, minmax(80px, 1fr));">
+                        <Button OnClick="OnLogin" IsDisabled="_loginStatus">
+                            <span>登录</span>
</code_context>

<issue_to_address>
**suggestion:** Inline grid styling for the button layout might be better expressed via a reusable CSS class.

Now that `HikVisions.razor.css` is removed, this fairly complex layout is defined inline via `style`. If you anticipate adjusting spacing or reusing this pattern, consider moving it into a scoped CSS class to keep styling separate from markup and simplify future changes.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines 27 to 39
private bool _inited;

private bool _loginStatus = true;
private bool _logoutStatus = true;
private bool _startRealPlayStatus = true;
private bool _stopRealPlayStatus = true;
private bool _openSoundStatus = true;
private bool _closeSoundStatus = true;
private bool _startRecordStatus = true;
private bool _stopRecordStatus = true;
private bool _loginStatus => _hikVision.IsLogin;
private bool _logoutStatus => !_hikVision.IsLogin;
private bool _startRealPlayStatus => _hikVision is not { IsLogin: true, IsRealPlaying: false };
private bool _stopRealPlayStatus => _hikVision is not { IsLogin: true, IsRealPlaying: true };
private bool _openSoundStatus => _hikVision is not { IsLogin: true, IsRealPlaying: true, IsOpenSound: false };
private bool _closeSoundStatus => _hikVision is not { IsLogin: true, IsRealPlaying: true, IsOpenSound: true };
private bool _startRecordStatus => _hikVision is not { IsLogin: true, IsRealPlaying: true, IsStartRecord: false };
private bool _stopRecordStatus => _hikVision is not { IsLogin: true, IsRealPlaying: true, IsStartRecord: true };

private List<SelectedItem> _analogChannels = [];
private int _channelId = 1;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion: The new predicate-based disable flags are compact but fairly hard to parse; consider refactoring for clarity.

These predicates for real play, sound, and record use inverted logic (true = disabled), which makes them harder to reason about. Consider renaming them to reflect the disabled state (e.g. _startRealPlayDisabled) or extracting the conditions into clearly named helpers (e.g. CanStartRealPlay) to improve readability and future maintenance.

Suggested change
private bool _inited;
private bool _loginStatus = true;
private bool _logoutStatus = true;
private bool _startRealPlayStatus = true;
private bool _stopRealPlayStatus = true;
private bool _openSoundStatus = true;
private bool _closeSoundStatus = true;
private bool _startRecordStatus = true;
private bool _stopRecordStatus = true;
private bool _loginStatus => _hikVision.IsLogin;
private bool _logoutStatus => !_hikVision.IsLogin;
private bool _startRealPlayStatus => _hikVision is not { IsLogin: true, IsRealPlaying: false };
private bool _stopRealPlayStatus => _hikVision is not { IsLogin: true, IsRealPlaying: true };
private bool _openSoundStatus => _hikVision is not { IsLogin: true, IsRealPlaying: true, IsOpenSound: false };
private bool _closeSoundStatus => _hikVision is not { IsLogin: true, IsRealPlaying: true, IsOpenSound: true };
private bool _startRecordStatus => _hikVision is not { IsLogin: true, IsRealPlaying: true, IsStartRecord: false };
private bool _stopRecordStatus => _hikVision is not { IsLogin: true, IsRealPlaying: true, IsStartRecord: true };
private List<SelectedItem> _analogChannels = [];
private int _channelId = 1;
private bool _inited;
// Capability predicates (positive logic) for readability
private bool CanLogin => !_hikVision.IsLogin;
private bool CanLogout => _hikVision.IsLogin;
private bool CanStartRealPlay => _hikVision is { IsLogin: true, IsRealPlaying: false };
private bool CanStopRealPlay => _hikVision is { IsLogin: true, IsRealPlaying: true };
private bool CanOpenSound => _hikVision is { IsLogin: true, IsRealPlaying: true, IsOpenSound: false };
private bool CanCloseSound => _hikVision is { IsLogin: true, IsRealPlaying: true, IsOpenSound: true };
private bool CanStartRecord => _hikVision is { IsLogin: true, IsRealPlaying: true, IsStartRecord: false };
private bool CanStopRecord => _hikVision is { IsLogin: true, IsRealPlaying: true, IsStartRecord: true };
// Status flags are used as disabled predicates in the UI (true => disabled)
private bool _loginStatus => !CanLogin;
private bool _logoutStatus => !CanLogout;
private bool _startRealPlayStatus => !CanStartRealPlay;
private bool _stopRealPlayStatus => !CanStopRealPlay;
private bool _openSoundStatus => !CanOpenSound;
private bool _closeSoundStatus => !CanCloseSound;
private bool _startRecordStatus => !CanStartRecord;
private bool _stopRecordStatus => !CanStopRecord;
private List<SelectedItem> _analogChannels = [];
private int _channelId = 1;

@codecov
Copy link
Copy Markdown

codecov Bot commented Dec 25, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (bed84ac) to head (2693a33).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main     #7412   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files          748       748           
  Lines        32793     32793           
  Branches      4551      4551           
=========================================
  Hits         32793     32793           
Flag Coverage Δ
BB 100.00% <ø> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

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 adds the IsOpenSound parameter to the HikVision component and refactors the sample page to use computed button states instead of manual state management. The package version is updated from 10.0.7 to 10.0.8 to support the new functionality.

  • Refactored button disabled states from manually tracked booleans to computed properties based on component state
  • Replaced CSS file with inline grid layout for better button arrangement
  • Updated HikVision package version to 10.0.8

Reviewed changes

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

File Description
src/BootstrapBlazor.Server/Components/Samples/HikVisions.razor.css Removed CSS file containing button margin styles
src/BootstrapBlazor.Server/Components/Samples/HikVisions.razor.cs Refactored button state management to use computed properties based on _hikVision component state (IsLogin, IsRealPlaying, IsOpenSound, IsStartRecord)
src/BootstrapBlazor.Server/Components/Samples/HikVisions.razor Replaced flat button layout with CSS grid layout using inline styles
src/BootstrapBlazor.Server/BootstrapBlazor.Server.csproj Updated BootstrapBlazor.HikVision package from version 10.0.7 to 10.0.8
Comments suppressed due to low confidence (4)

src/BootstrapBlazor.Server/Components/Samples/HikVisions.razor.cs:80

  • After successfully opening the sound, consider calling StateHasChanged() to ensure the button states (_openSoundStatus and _closeSoundStatus) are updated immediately. Without this, the UI may not reflect the new state until the next automatic render cycle.
    private async Task OnOpenSound()
    {
        var result = await _hikVision.OpenSound();
        if (result)
        {
            await ToastService.Success("消息通知", "打开声音成功");
        }
        else
        {
            await ToastService.Error("消息通知", "打开声音失败");
        }

src/BootstrapBlazor.Server/Components/Samples/HikVisions.razor.cs:93

  • After successfully closing the sound, consider calling StateHasChanged() to ensure the button states (_openSoundStatus and _closeSoundStatus) are updated immediately. Without this, the UI may not reflect the new state until the next automatic render cycle.
    private async Task OnCloseSound()
    {
        var result = await _hikVision.CloseSound();
        if (result)
        {
            await ToastService.Success("消息通知", "关闭声音成功");
        }
        else
        {
            await ToastService.Error("消息通知", "关闭声音失败");
        }

src/BootstrapBlazor.Server/Components/Samples/HikVisions.razor.cs:111

  • After successfully starting the record, consider calling StateHasChanged() to ensure the button states (_startRecordStatus and _stopRecordStatus) are updated immediately. Without this, the UI may not reflect the new state until the next automatic render cycle.
    private async Task OnStartRecord()
    {
        var result = await _hikVision.StartRecord();
        if (result)
        {
            await ToastService.Success("消息通知", "开始录像成功");
        }
        else
        {
            await ToastService.Error("消息通知", "开始录像失败");
        }

src/BootstrapBlazor.Server/Components/Samples/HikVisions.razor.cs:124

  • After successfully stopping the record, consider calling StateHasChanged() to ensure the button states (_startRecordStatus and _stopRecordStatus) are updated immediately. Without this, the UI may not reflect the new state until the next automatic render cycle.
    private async Task OnStopRecord()
    {
        var result = await _hikVision.StopRecord();
        if (result)
        {
            await ToastService.Success("消息通知", "结束录像成功");
        }
        else
        {
            await ToastService.Error("消息通知", "结束录像失败");
        }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

<Button OnClick="OnStopRecord" IsDisabled="_stopRecordStatus">
<span>停止录像</span>
</Button>
<div style="width: 100%; display: grid; gap: .5rem; grid-template-columns: repeat(auto-fit, minmax(80px, 1fr));">
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

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

The inline style uses .5rem which may not be valid CSS. While some browsers accept leading-zero-less decimal numbers, it's better to use 0.5rem for better compatibility and clarity.

Suggested change
<div style="width: 100%; display: grid; gap: .5rem; grid-template-columns: repeat(auto-fit, minmax(80px, 1fr));">
<div style="width: 100%; display: grid; gap: 0.5rem; grid-template-columns: repeat(auto-fit, minmax(80px, 1fr));">

Copilot uses AI. Check for mistakes.
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.

feat(HikVision): add IsOpenSound parameter

2 participants