diff --git a/src/BootstrapBlazor.Server/Components/Layout/BaseLayout.razor b/src/BootstrapBlazor.Server/Components/Layout/BaseLayout.razor index d31966d483f..dda61a1d6c7 100644 --- a/src/BootstrapBlazor.Server/Components/Layout/BaseLayout.razor +++ b/src/BootstrapBlazor.Server/Components/Layout/BaseLayout.razor @@ -15,3 +15,14 @@ Reload 🗙 + +@code { + RenderFragment RenderVote => + @
+

我正在参加 Gitee 2025 最受欢迎的开源软件投票活动,快来给我投票吧!

+
+ 必须投一票 + 我知道了 +
+
; +} diff --git a/src/BootstrapBlazor.Server/Components/Layout/BaseLayout.razor.cs b/src/BootstrapBlazor.Server/Components/Layout/BaseLayout.razor.cs index d089760ff06..d774d70ebfc 100644 --- a/src/BootstrapBlazor.Server/Components/Layout/BaseLayout.razor.cs +++ b/src/BootstrapBlazor.Server/Components/Layout/BaseLayout.razor.cs @@ -5,13 +5,14 @@ using Microsoft.Extensions.Options; using Microsoft.JSInterop; +using System.Globalization; namespace BootstrapBlazor.Server.Components.Layout; /// /// 母版页基类 /// -public partial class BaseLayout : IDisposable +public partial class BaseLayout : IAsyncDisposable { [Inject] [NotNull] @@ -50,6 +51,8 @@ public partial class BaseLayout : IDisposable private string? CancelText { get; set; } private bool _init = false; + private JSModule? _module; + private DotNetObjectReference? _interop; /// /// @@ -71,13 +74,18 @@ protected override void OnInitialized() /// /// /// - 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 payload) @@ -121,25 +129,55 @@ private async Task NotifyReboot(DispatchEntry payload) } } + /// + /// 显示投票弹窗 + /// + /// + [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); + } + /// /// 释放资源 /// /// - 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(); + } } } /// /// 释放资源 /// - public void Dispose() + public async ValueTask DisposeAsync() { - Dispose(true); + await DisposeAsync(true); GC.SuppressFinalize(this); } } diff --git a/src/BootstrapBlazor.Server/Components/Layout/BaseLayout.razor.js b/src/BootstrapBlazor.Server/Components/Layout/BaseLayout.razor.js index 6d4599d796a..f3ec8d6636e 100644 --- a/src/BootstrapBlazor.Server/Components/Layout/BaseLayout.razor.js +++ b/src/BootstrapBlazor.Server/Components/Layout/BaseLayout.razor.js @@ -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) { + return; + } + } + catch { + localStorage.removeItem('bb-gitee-vote'); + } + } + const handler = setTimeout(async () => { + clearTimeout(handler); + await invoke.invokeMethodAsync("ShowVoteToast"); + }, 10000); + + 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'); +}