Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/BootstrapBlazor.Server/Components/Layout/BaseLayout.razor
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,14 @@
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>

@code {
RenderFragment RenderVote =>
@<div>
<p style="font-weight: bold;">我正在参加 Gitee 2025 最受欢迎的开源软件投票活动,快来给我投票吧!</p>
Copy link

Copilot AI Oct 26, 2025

Choose a reason for hiding this comment

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

[nitpick] The text '我正在参加' (I am participating) should be '我们正在参加' (We are participating) to correctly represent the project rather than an individual.

Suggested change
<p style="font-weight: bold;">我正在参加 Gitee 2025 最受欢迎的开源软件投票活动,快来给我投票吧!</p>
<p style="font-weight: bold;">我们正在参加 Gitee 2025 最受欢迎的开源软件投票活动,快来给我投票吧!</p>

Copilot uses AI. Check for mistakes.
<div style="display: flex; justify-content: space-around;" id="bb-gitee-vote">
<a href="https://gitee.com/activity/2025opensource?ident=I6MYBB" target="_blank" style="font-weight: bold; padding: 3px 12px; border: 1px solid var(--bs-primary); border-radius: var(--bs-border-radius);">必须投一票</a>
<a href="https://gitee.com/activity/2025opensource?ident=I6MYBB" target="_blank" class="text-muted" style="padding: 3px 12px;" title="老六你居然不投票">我知道了</a>
</div>
</div>;
}
56 changes: 47 additions & 9 deletions src/BootstrapBlazor.Server/Components/Layout/BaseLayout.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@

using Microsoft.Extensions.Options;
using Microsoft.JSInterop;
using System.Globalization;

namespace BootstrapBlazor.Server.Components.Layout;

/// <summary>
/// 母版页基类
/// </summary>
public partial class BaseLayout : IDisposable
public partial class BaseLayout : IAsyncDisposable
{
[Inject]
[NotNull]
Expand Down Expand Up @@ -50,6 +51,8 @@ public partial class BaseLayout : IDisposable
private string? CancelText { get; set; }

private bool _init = false;
private JSModule? _module;
private DotNetObjectReference<BaseLayout>? _interop;
Copy link

Copilot AI Oct 26, 2025

Choose a reason for hiding this comment

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

The _interop DotNetObjectReference is created but never disposed. Add _interop?.Dispose(); in the DisposeAsync method to prevent memory leaks.

Copilot uses AI. Check for mistakes.

/// <summary>
/// <inheritdoc/>
Expand All @@ -71,13 +74,18 @@ protected override void OnInitialized()
/// <inheritdoc/>
/// </summary>
/// <returns></returns>
protected override async Task OnInitializedAsync()
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnInitializedAsync();
await base.OnAfterRenderAsync(firstRender);

var module = await JSRuntime.LoadModule($"{WebsiteOption.Value.JSModuleRootPath}Layout/BaseLayout.razor.js");
await module.InvokeVoidAsync("initTheme");
_init = true;
if (firstRender)
{
_module = await JSRuntime.LoadModule($"{WebsiteOption.Value.JSModuleRootPath}Layout/BaseLayout.razor.js");
_interop = DotNetObjectReference.Create(this);
await _module.InvokeVoidAsync("doTask", _interop);
_init = true;
StateHasChanged();
}
}

private async Task NotifyCommit(DispatchEntry<GiteePostBody> payload)
Expand Down Expand Up @@ -121,25 +129,55 @@ private async Task NotifyReboot(DispatchEntry<bool> payload)
}
}

/// <summary>
/// 显示投票弹窗
/// </summary>
/// <returns></returns>
[JSInvokable]
public async Task ShowVoteToast()
{
// 英文环境不投票
if(CultureInfo.CurrentUICulture.Name == "en-US")
{
return;
}

_option = new ToastOption()
{
Category = ToastCategory.Information,
Title = "Gitee 评选活动",
IsAutoHide = false,
ChildContent = RenderVote,
PreventDuplicates = true
};
await Toast.Show(_option);
}

/// <summary>
/// 释放资源
/// </summary>
/// <param name="disposing"></param>
private void Dispose(bool disposing)
private async ValueTask DisposeAsync(bool disposing)
{
if (disposing)
{
CommitDispatchService.UnSubscribe(NotifyCommit);
RebootDispatchService.UnSubscribe(NotifyReboot);

if (_module != null)
{
await _module.InvokeVoidAsync("dispose");
await _module.DisposeAsync();
}
}
}

/// <summary>
/// 释放资源
/// </summary>
public void Dispose()
public async ValueTask DisposeAsync()
{
Dispose(true);
await DisposeAsync(true);
GC.SuppressFinalize(this);
}
}
37 changes: 36 additions & 1 deletion src/BootstrapBlazor.Server/Components/Layout/BaseLayout.razor.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,41 @@
import { getTheme, setTheme } from "../../_content/BootstrapBlazor/modules/utility.js"
import EventHandler from "../../_content/BootstrapBlazor/modules/event-handler.js"

export function initTheme() {
function initTheme() {
const currentTheme = getTheme();
setTheme(currentTheme, false);
}

export function doTask(invoke) {
initTheme();

const v = localStorage.getItem('bb-gitee-vote');
if (v) {
try {
const differ = new Date().getTime() - v;
if (differ < 86400000) {
Copy link

Copilot AI Oct 26, 2025

Choose a reason for hiding this comment

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

Magic number 86400000 (milliseconds in 24 hours) should be extracted as a named constant for better readability. Consider: const ONE_DAY_MS = 86400000;

Copilot uses AI. Check for mistakes.
return;
}
}
catch {
localStorage.removeItem('bb-gitee-vote');
}
}
const handler = setTimeout(async () => {
clearTimeout(handler);
await invoke.invokeMethodAsync("ShowVoteToast");
Comment on lines +24 to +26
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (bug_risk): Using setTimeout with async function may cause unhandled promise rejections.

Since errors in the async callback won't be caught, wrap the function body in try/catch or handle the promise to prevent unhandled rejections.

}, 10000);
Copy link

Copilot AI Oct 26, 2025

Choose a reason for hiding this comment

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

Magic number 10000 (10 seconds delay) should be extracted as a named constant for better readability. Consider: const VOTE_TOAST_DELAY_MS = 10000;

Copilot uses AI. Check for mistakes.

EventHandler.on(document, 'click', '#bb-gitee-vote', e => {
const toast = e.delegateTarget.closest('.toast');
if (toast) {
toast.classList.remove('show');

localStorage.setItem('bb-gitee-vote', new Date().getTime());
}
});
}

export function dispose() {
EventHandler.off(document, 'click', '#bb-gitee-vote');
}
Loading