diff --git a/src/BootstrapBlazor.Shared/Locales/en.json b/src/BootstrapBlazor.Shared/Locales/en.json index b1cc6f33a87b867d8e8985e4b554b40717799683..024dec1f79548c82592df8c602580271d2d2367e 100644 --- a/src/BootstrapBlazor.Shared/Locales/en.json +++ b/src/BootstrapBlazor.Shared/Locales/en.json @@ -2782,5 +2782,29 @@ "LastUpdateTime": "UpdateTime", "CurrentDistance": "CurrentDistance", "TotalDistance": "TotalDistance" + }, + "BootstrapBlazor.Shared.Samples.Notifications": { + "Title": "Notifications", + "BaseUsageText": "Basic usage", + "IntroText1": "Notification information through browser API.", + "IntroText2": "Click the button to notification.", + "IntroText3": "Note: For security reasons, when a web page tries to access notifications, the user is notified and asked to grant permission.", + "NotificationButtonText": "Display notification", + "NotificationSilentButtonText": "Display silent notification", + "CheckPermissionText": "Check Permission", + "PermissionText": "Permission", + "SilentText": "Silent", + "TitleText": "Title", + "MessageText": "Message", + "IconText": "Icon", + "SoundText": "The audio URL to play when the notification", + "CheckPermissionResultSuccess": "Check permission success", + "NotificationResultSuccess": "Call Notification success", + "BasicUsageTips": "Uses the static class BrowserNotification to call the Dispatch method.", + "ShowNotificationCallbackText": "Show notification callback ", + "GetPermissionCallbackText": "Get permission callback ", + "OnClickText": "Click notification callback ", + "TitleSampleText": "You have a new logistics notification", + "MessageSampleText": "Your package has arrived on Panama South Road, and there are only 3 stops left for distribution." } } diff --git a/src/BootstrapBlazor.Shared/Locales/zh.json b/src/BootstrapBlazor.Shared/Locales/zh.json index f4bf4e094ba846af83ae0381e5e2df8000a7ba43..9bbde7a49139e5cb86b2d45bc86ec44c0aca60a1 100644 --- a/src/BootstrapBlazor.Shared/Locales/zh.json +++ b/src/BootstrapBlazor.Shared/Locales/zh.json @@ -419,7 +419,8 @@ "ChartDoughnutText": "圆环图", "ChartBubbleText": "气泡图", "DispatchText": "消息分发 Dispatch", - "GeolocationText": "地理定位组件 Geolocation" + "GeolocationText": "地理定位组件 Geolocation", + "NotificationsText": "浏览器通知 Notification" }, "BootstrapBlazor.Shared.Samples.GlobalException": { "Title": "全局异常", @@ -2697,7 +2698,8 @@ "ChartDoughnut": "圆环图", "ChartBubble": "气泡图", "Transition": "过渡效果 Transition", - "Geolocation": "地理定位组件 Geolocation" + "Geolocation": "地理定位组件 Geolocation", + "Notification": "浏览器通知 Notification" }, "BootstrapBlazor.Shared.Samples.Table.TablesFooter": { "Left": "左对齐", @@ -2770,7 +2772,7 @@ "BootstrapBlazor.Shared.Samples.Geolocations": { "Title": "地理定位/移动距离追踪", "BaseUsageText": "基础用法", - "IntroText1": "通过浏览器API获取定位信息。", + "IntroText1": "通过浏览器 API 获取定位信息。", "IntroText2": "单击按钮以获取地理位置坐标。", "IntroText3": "注意: 出于安全考虑,当网页请求获取用户位置信息时,用户会被提示进行授权。注意不同浏览器在请求权限时有不同的策略和方式。Windows10 在未开启定位的情况下无法获取位置。", "GetLocationButtonText": "获取位置", @@ -2788,5 +2790,29 @@ "LastUpdateTime": "时间戳", "CurrentDistance": "移动距离", "TotalDistance": "总移动距离" + }, + "BootstrapBlazor.Shared.Samples.Notifications": { + "Title": "通知", + "BaseUsageText": "基础用法", + "IntroText1": "通过浏览器API发送通知信息。", + "IntroText2": "单击按钮以发送通知。", + "IntroText3": "注意: 出于安全考虑,当网页请求发送通知时,用户会被提示进行授权。", + "NotificationButtonText": "显示通知", + "NotificationSilentButtonText": "显示静默通知", + "CheckPermissionText": "检查权限", + "PermissionText": "允许通知", + "SilentText": "静默", + "TitleText": "标题", + "MessageText": "信息", + "IconText": "图标", + "SoundText": "通知触发时要播放的音频文件的 URL", + "CheckPermissionResultSuccess": "获取权限成功", + "NotificationResultSuccess": "调用通知成功", + "BasicUsageTips": "使用 BrowserNotification 静态方法直接调用 Dispatch 方法", + "ShowNotificationCallbackText": "发送结果回调 ", + "GetPermissionCallbackText": "检查通知权限结果回调 ", + "OnClickText": "通知点击后的回调 ", + "TitleSampleText": "你有新的物流通知", + "MessageSampleText": "您的包裹已到达亮马河南路,距离分发只剩3站." } } diff --git a/src/BootstrapBlazor.Shared/Pages/Coms.razor b/src/BootstrapBlazor.Shared/Pages/Coms.razor index 860c51ab9795375d4eef425216e492f57ce2bfc6..1844c932e8608a1b4a509a0ec259af970d55306e 100644 --- a/src/BootstrapBlazor.Shared/Pages/Coms.razor +++ b/src/BootstrapBlazor.Shared/Pages/Coms.razor @@ -90,10 +90,10 @@ + - @@ -103,7 +103,7 @@ - + @@ -124,6 +124,7 @@ + diff --git a/src/BootstrapBlazor.Shared/Samples/Geolocations.razor b/src/BootstrapBlazor.Shared/Samples/Geolocations.razor index 2ba374513725c1f3074afcd8da7b2a0906583b4f..c325c6e7adcf1cff97b37fe2f223a6a216cbcd29 100644 --- a/src/BootstrapBlazor.Shared/Samples/Geolocations.razor +++ b/src/BootstrapBlazor.Shared/Samples/Geolocations.razor @@ -10,7 +10,7 @@ @if (Model != null) { -
+
diff --git a/src/BootstrapBlazor.Shared/Samples/Geolocations.razor.cs b/src/BootstrapBlazor.Shared/Samples/Geolocations.razor.cs index f0d5cbb5a79ddb6f6d17d25585f0c4f6ae647be8..a2346b151ae4918d9bf0f3aa4fadaa4052300127 100644 --- a/src/BootstrapBlazor.Shared/Samples/Geolocations.razor.cs +++ b/src/BootstrapBlazor.Shared/Samples/Geolocations.razor.cs @@ -3,7 +3,6 @@ // Website: https://www.blazor.zone or https://argozhang.github.io/ using BootstrapBlazor.Components; -using BootstrapBlazor.Shared.Common; using BootstrapBlazor.Shared.Components; using Microsoft.AspNetCore.Components; using Microsoft.Extensions.Localization; @@ -29,16 +28,12 @@ public partial class Geolocations : IDisposable [NotNull] private IJSRuntime? JSRuntime { get; set; } - [Inject] - [NotNull] - private GeolocationService? GeolocationService { get; set; } - private GeolocationItem? Model { get; set; } private async Task GetLocation() { Interop ??= new JSInterop(JSRuntime); - var ret = await GeolocationService.GetLocaltion(Interop, this, nameof(GetLocationCallback)); + var ret = await Geolocation.GetLocaltion(Interop, this, nameof(GetLocationCallback)); Trace.Log(ret ? Localizer["GetLocationResultSuccess"] : Localizer["GetLocationResultFailed"]); } diff --git a/src/BootstrapBlazor.Shared/Samples/Notifications.razor b/src/BootstrapBlazor.Shared/Samples/Notifications.razor new file mode 100644 index 0000000000000000000000000000000000000000..cd055af1b120916d679a94d5960946d8314407cf --- /dev/null +++ b/src/BootstrapBlazor.Shared/Samples/Notifications.razor @@ -0,0 +1,51 @@ +@page "/notifications" + +

@Localizer["Title"]

+ + +

@Localizer["IntroText2"]

+ +

@Localizer["IntroText3"]

+
+
+

@((MarkupString)Localizer["BasicUsageTips"].Value)

+
private NotificationItem Model { get; set; } = new NotificationItem();
+
+private async Task Dispatch()
+{
+    Interop ??= new JSInterop<Notifications>(JSRuntime);
+    await BrowserNotification.Dispatch(Interop, this, Model, nameof(ShowNotificationCallback));
+}
+
+[JSInvokable]
+public void ShowNotificationCallback(bool result)
+{
+    // callback
+    StateHasChanged();
+}
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + +
+
+ +
+ + diff --git a/src/BootstrapBlazor.Shared/Samples/Notifications.razor.cs b/src/BootstrapBlazor.Shared/Samples/Notifications.razor.cs new file mode 100644 index 0000000000000000000000000000000000000000..02c6b9a896412b7d02ea342249371589a9f54c97 --- /dev/null +++ b/src/BootstrapBlazor.Shared/Samples/Notifications.razor.cs @@ -0,0 +1,182 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +using BootstrapBlazor.Components; +using BootstrapBlazor.Shared.Common; +using BootstrapBlazor.Shared.Components; +using Microsoft.AspNetCore.Components; +using Microsoft.Extensions.Localization; +using Microsoft.JSInterop; + +namespace BootstrapBlazor.Shared.Samples; + +/// +/// Notifications 通知 +/// +public partial class Notifications : IDisposable +{ + private JSInterop? Interop { get; set; } + + [NotNull] + private BlockLogger? Trace { get; set; } + + [Inject] + [NotNull] + private IStringLocalizer? Localizer { get; set; } + + [Inject] + [NotNull] + private IJSRuntime? JSRuntime { get; set; } + + private bool Permission { get; set; } + + private NotificationItem Model { get; set; } = new NotificationItem(); + + /// + /// OnInitialized 方法 + /// + protected override void OnInitialized() + { + base.OnInitialized(); + Model.Title ??= Localizer["TitleSampleText"]; + Model.Message ??= Localizer["MessageSampleText"]; + Model.Icon ??= "_content/BootstrapBlazor.Shared/images/Argo-C.png"; + Model.OnClick ??= nameof(OnClickNotificationCallback); + } + + /// + /// OnAfterRenderAsync 方法 + /// + /// + /// + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + Interop = new JSInterop(JSRuntime); + await BrowserNotification.CheckPermission(Interop, this, nameof(GetPermissionCallback), false); + } + } + + private async Task CheckPermission() + { + Interop ??= new JSInterop(JSRuntime); + await BrowserNotification.CheckPermission(Interop, this, nameof(GetPermissionCallback)); + } + + private async Task Dispatch() + { + Interop ??= new JSInterop(JSRuntime); + await BrowserNotification.Dispatch(Interop, this, Model, nameof(ShowNotificationCallback)); + } + + /// + /// + /// + /// + [JSInvokable] + public void GetPermissionCallback(bool result) + { + Permission = result; + Trace.Log(Localizer["GetPermissionCallbackText"] + (result ? "OK" : "No permission")); + StateHasChanged(); + } + + /// + /// + /// + /// + [JSInvokable] + public void ShowNotificationCallback(bool result) + { + Trace.Log($"{Localizer["ShowNotificationCallbackText"]}: {result}"); + StateHasChanged(); + } + + /// + /// + /// + [JSInvokable] + public void OnClickNotificationCallback() + { + Trace.Log($"{Localizer["OnClickText"]}"); + StateHasChanged(); + } + + /// + /// + /// + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + if (Interop != null) + { + Interop.Dispose(); + Interop = null; + } + } + } + + /// + /// + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private IEnumerable GetNotificationItem() => new AttributeItem[] + { + new() + { + Name = "Title", + Description = Localizer["TitleText"], + Type = "string", + ValueList = " — ", + DefaultValue = " — " + }, + new() + { + Name = "Message", + Description = Localizer["MessageText"], + Type = "string", + ValueList = " — ", + DefaultValue = " — " + }, + new() { + Name = "Icon", + Description = Localizer["IconText"], + Type = "string", + ValueList = " — ", + DefaultValue = " — " + }, + new() + { + Name = "Silent", + Description = Localizer["SilentText"], + Type = "bool", + ValueList = " — ", + DefaultValue = " — " + }, + new() + { + Name = "Sound", + Description = Localizer["SoundText"], + Type = "string", + ValueList = " — ", + DefaultValue = " — " + }, + new() + { + Name = "OnClick", + Description = Localizer["OnClickText"], + Type = "Methods", + ValueList = " — ", + DefaultValue = " — " + }, + }; +} diff --git a/src/BootstrapBlazor.Shared/Shared/NavMenu.razor.cs b/src/BootstrapBlazor.Shared/Shared/NavMenu.razor.cs index aa9a06cd4f46288e60f0e61be6fa544c2c3aaccb..2346ec815467bc6dfa869b621bad06686dc1e0aa 100644 --- a/src/BootstrapBlazor.Shared/Shared/NavMenu.razor.cs +++ b/src/BootstrapBlazor.Shared/Shared/NavMenu.razor.cs @@ -215,144 +215,144 @@ public sealed partial class NavMenu private void AddForm(DemoMenuItem item) { item.Items = new List + { + new() { - new() - { - Text = Localizer["AutoComplete"], - Url = "autocompletes" - }, - new() - { - IsNew = true, - Text = Localizer["AutoFill"], - Url = "autofills" - }, - new() - { - Text = Localizer["Button"], - Url = "buttons" - }, - new() - { - Text = Localizer["Cascader"], - Url = "cascaders" - }, - new() - { - Text = Localizer["Checkbox"], - Url = "checkboxs" - }, - new() - { - Text = Localizer["CheckboxList"], - Url = "checkboxlists" - }, - new() - { - Text = Localizer["ColorPicker"], - Url = "colorpickers" - }, - new() - { - Text = Localizer["DateTimePicker"], - Url = "datetimepickers" - }, - new() - { - Text = Localizer["DateTimeRange"], - Url = "datetimeranges" - }, - new() - { - Text = Localizer["Editor"], - Url = "editors" - }, - new() - { - Text = Localizer["EditorForm"], - Url = "editorforms" - }, - new() - { - Text = Localizer["FloatingLabel"], - Url = "floatinglabels" - }, - new() - { - Text = Localizer["Input"], - Url = "inputs" - }, - new() - { - Text = Localizer["InputNumber"], - Url = "inputnumbers" - }, - new() - { - Text = Localizer["InputGroup"], - Url = "inputgroups" - }, - new() - { - Text = Localizer["Markdown"], - Url = "markdowns" - }, - new() - { - Text = Localizer["MultiSelect"], - Url = "multiselects" - }, - new() - { - Text = Localizer["Radio"], - Url = "radios" - }, - new() - { - Text = Localizer["Rate"], - Url = "rates" - }, - new() - { - Text = Localizer["Select"], - Url = "selects" - }, - new() - { - Text = Localizer["Slider"], - Url = "sliders" - }, - new() - { - Text = Localizer["Switch"], - Url = "switchs" - }, - new() - { - Text = Localizer["Textarea"], - Url = "textareas" - }, - new() - { - Text = Localizer["Toggle"], - Url = "toggles" - }, - new() - { - Text = Localizer["Transfer"], - Url = "transfers" - }, - new() - { - Text = Localizer["Upload"], - Url = "uploads" - }, - new() - { - Text = Localizer["ValidateForm"], - Url = "validateforms" - } - }; + Text = Localizer["AutoComplete"], + Url = "autocompletes" + }, + new() + { + IsNew = true, + Text = Localizer["AutoFill"], + Url = "autofills" + }, + new() + { + Text = Localizer["Button"], + Url = "buttons" + }, + new() + { + Text = Localizer["Cascader"], + Url = "cascaders" + }, + new() + { + Text = Localizer["Checkbox"], + Url = "checkboxs" + }, + new() + { + Text = Localizer["CheckboxList"], + Url = "checkboxlists" + }, + new() + { + Text = Localizer["ColorPicker"], + Url = "colorpickers" + }, + new() + { + Text = Localizer["DateTimePicker"], + Url = "datetimepickers" + }, + new() + { + Text = Localizer["DateTimeRange"], + Url = "datetimeranges" + }, + new() + { + Text = Localizer["Editor"], + Url = "editors" + }, + new() + { + Text = Localizer["EditorForm"], + Url = "editorforms" + }, + new() + { + Text = Localizer["FloatingLabel"], + Url = "floatinglabels" + }, + new() + { + Text = Localizer["Input"], + Url = "inputs" + }, + new() + { + Text = Localizer["InputNumber"], + Url = "inputnumbers" + }, + new() + { + Text = Localizer["InputGroup"], + Url = "inputgroups" + }, + new() + { + Text = Localizer["Markdown"], + Url = "markdowns" + }, + new() + { + Text = Localizer["MultiSelect"], + Url = "multiselects" + }, + new() + { + Text = Localizer["Radio"], + Url = "radios" + }, + new() + { + Text = Localizer["Rate"], + Url = "rates" + }, + new() + { + Text = Localizer["Select"], + Url = "selects" + }, + new() + { + Text = Localizer["Slider"], + Url = "sliders" + }, + new() + { + Text = Localizer["Switch"], + Url = "switchs" + }, + new() + { + Text = Localizer["Textarea"], + Url = "textareas" + }, + new() + { + Text = Localizer["Toggle"], + Url = "toggles" + }, + new() + { + Text = Localizer["Transfer"], + Url = "transfers" + }, + new() + { + Text = Localizer["Upload"], + Url = "uploads" + }, + new() + { + Text = Localizer["ValidateForm"], + Url = "validateforms" + } + }; AddBadge(item); } @@ -466,6 +466,12 @@ public sealed partial class NavMenu Url = "handwrittenpage" }, new() + { + IsNew = true, + Text = Localizer["Ip"], + Url = "ips" + }, + new() { IsNew = true, Text = Localizer["LinkButton"], @@ -483,12 +489,6 @@ public sealed partial class NavMenu Url = "locator" }, new() - { - IsNew = true, - Text = Localizer["Ip"], - Url = "ips" - }, - new() { IsNew = true, Text = Localizer["Print"], @@ -772,6 +772,12 @@ public sealed partial class NavMenu Url = "modals" }, new() + { + IsNew= true, + Text = Localizer["Notification"], + Url = "notifications" + }, + new() { Text = Localizer["Popconfirm"], Url = "popconfirms" diff --git a/src/BootstrapBlazor.Shared/wwwroot/images/Notifications.jpg b/src/BootstrapBlazor.Shared/wwwroot/images/Notifications.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b0030e203598a5f3cc1c8c56ddefd390802c4622 Binary files /dev/null and b/src/BootstrapBlazor.Shared/wwwroot/images/Notifications.jpg differ diff --git a/src/BootstrapBlazor/BootstrapBlazor.csproj b/src/BootstrapBlazor/BootstrapBlazor.csproj index b2bc4e866b1005967f42dbd33fd41dedd2b9054d..da95e2c8d1ba07d1860862d8d43bb35b656bd5bf 100644 --- a/src/BootstrapBlazor/BootstrapBlazor.csproj +++ b/src/BootstrapBlazor/BootstrapBlazor.csproj @@ -1,7 +1,7 @@ - 6.3.1-beta01 + 6.3.1-beta02 @@ -40,4 +40,8 @@ + + + + diff --git a/src/BootstrapBlazor/Components/Geolocation/GeolocationService.cs b/src/BootstrapBlazor/Components/Geolocation/Geolocation.cs similarity index 74% rename from src/BootstrapBlazor/Components/Geolocation/GeolocationService.cs rename to src/BootstrapBlazor/Components/Geolocation/Geolocation.cs index 2d2931268acd5de57b859680160266b34863d62d..89398553fe0a7fa9acf58b90ea4d5d8e07e3650d 100644 --- a/src/BootstrapBlazor/Components/Geolocation/GeolocationService.cs +++ b/src/BootstrapBlazor/Components/Geolocation/Geolocation.cs @@ -7,13 +7,13 @@ namespace BootstrapBlazor.Components; /// /// 地理位置坐标服务 /// -public class GeolocationService +public static class Geolocation { /// /// 获取当前地理位置坐标信息 /// /// - public async Task GetLocaltion(JSInterop interop, TComponent component, string callbackMethodName) where TComponent : class + public static async Task GetLocaltion(JSInterop interop, TComponent component, string callbackMethodName) where TComponent : class { var ret = await interop.GetGeolocationItemAsync(component, callbackMethodName); return ret; diff --git a/src/BootstrapBlazor/Components/Notifications/BrowserNotification.cs b/src/BootstrapBlazor/Components/Notifications/BrowserNotification.cs new file mode 100644 index 0000000000000000000000000000000000000000..685728736f6854b6a79bb46462367176448e25d8 --- /dev/null +++ b/src/BootstrapBlazor/Components/Notifications/BrowserNotification.cs @@ -0,0 +1,37 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +namespace BootstrapBlazor.Components; + +/// +/// 浏览器通知服务 +/// +public static class BrowserNotification +{ + /// + /// 检查浏览器通知权限状态 + /// + /// + /// JSInterop 实例 + /// 当前页面 + /// 检查通知权限结果回调方法 + /// 是否请求权限 默认 true + /// + public static ValueTask CheckPermission(JSInterop interop, TComponent component, string? callbackMethodName = null, bool requestPermission = true) where TComponent : class => interop.CheckNotifyPermissionAsync(component, callbackMethodName, requestPermission); + + /// + /// 发送浏览器通知 + /// + /// + /// JSInterop 实例 + /// 当前页面 + /// NotificationItem 实例 + /// 发送结果回调方法 + /// + public static async Task Dispatch(JSInterop interop, TComponent component, NotificationItem model, string? callbackMethodName) where TComponent : class + { + var ret = await interop.Dispatch(component, model, callbackMethodName); + return ret; + } +} diff --git a/src/BootstrapBlazor/Components/Notifications/Notification.js b/src/BootstrapBlazor/Components/Notifications/Notification.js new file mode 100644 index 0000000000000000000000000000000000000000..e2b854e777d781d6f75bfcfa118e0823d7ea33a1 --- /dev/null +++ b/src/BootstrapBlazor/Components/Notifications/Notification.js @@ -0,0 +1,41 @@ +(function ($) { + $.extend({ + bb_notify_checkPermission: function (obj, method, requestPermission) { + if ((!window.Notification && !navigator.mozNotification) || !window.FileReader || !window.history.pushState) { + console.warn("Your browser does not support all features of this API"); + if (obj !== null && method !== '') obj.invokeMethodAsync(method, false); + } + else if (Notification.permission === "granted") { + if (obj !== null && method !== '') obj.invokeMethodAsync(method, true); + } + else if (requestPermission && (Notification.permission !== 'denied' || Notification.permission === "default")) { + Notification.requestPermission(function (permission) { + var granted = permission === "granted"; + if (obj !== null && method !== '') obj.invokeMethodAsync(method, granted); + }); + } + }, + bb_notify_display: function (obj, method, model) { + var ret = false; + $.bb_notify_checkPermission(null, null, true); + if (model.title !== null) { + var onClickCallback = model.onClick; + var options = {}; + if (model.message !== null) options.body = model.message.substr(0, 250); + if (model.icon !== null) options.icon = model.icon; + if (model.silent !== null) options.silent = model.silent; + if (model.sound !== null) options.sound = model.sound; + var notification = new Notification(model.title.substr(0, 100), options); + if (obj !== null && onClickCallback !== null) { + notification.onclick = function (event) { + event.preventDefault(); + obj.invokeMethodAsync(onClickCallback); + } + } + ret = true; + } + if (obj !== null && method !== null) obj.invokeMethodAsync(method, ret); + return ret; + } + }); +})(jQuery); diff --git a/src/BootstrapBlazor/Components/Notifications/NotificationItem.cs b/src/BootstrapBlazor/Components/Notifications/NotificationItem.cs new file mode 100644 index 0000000000000000000000000000000000000000..e4cc000c1710e181cbad0b287b9ed57b6c0868d0 --- /dev/null +++ b/src/BootstrapBlazor/Components/Notifications/NotificationItem.cs @@ -0,0 +1,41 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +namespace BootstrapBlazor.Components; + +/// +/// 通知信息类 +/// +public class NotificationItem +{ + /// + /// 获得/设置 标题 + /// + public string? Title { get; set; } + + /// + /// 获得/设置 信息 + /// + public string? Message { get; set; } + + /// + /// 获得/设置 图标 + /// + public string? Icon { get; set; } + + /// + /// 获得/设置 静默 + /// + public bool Silent { get; set; } + + /// + /// 获得/设置 通知触发时要播放的音频文件的 URL + /// + public string? Sound { get; set; } + + /// + /// 获得/设置 通知点击后的回调 + /// + public string? OnClick { get; set; } +} diff --git a/src/BootstrapBlazor/Extensions/BootstrapBlazorServiceCollectionExtensions.cs b/src/BootstrapBlazor/Extensions/BootstrapBlazorServiceCollectionExtensions.cs index 216fec9dd89ee055b001dd5ec03e562baaf2eb96..8c1a8c3fc938536fd3afb1e80857296d7ba67fba 100644 --- a/src/BootstrapBlazor/Extensions/BootstrapBlazorServiceCollectionExtensions.cs +++ b/src/BootstrapBlazor/Extensions/BootstrapBlazorServiceCollectionExtensions.cs @@ -53,7 +53,6 @@ public static class BootstrapBlazorServiceCollectionExtensions services.TryAddSingleton, ConfigureOptions>(); services.ConfigureBootstrapBlazorOption(configureOptions); - services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton, ConfigureOptions>(); return services; diff --git a/src/BootstrapBlazor/Utils/JSInterop.cs b/src/BootstrapBlazor/Utils/JSInterop.cs index 322649817dd87924980754dc53c1dba9170786a5..d14614acbbbc00befd937b2d79ddb2366b973957 100644 --- a/src/BootstrapBlazor/Utils/JSInterop.cs +++ b/src/BootstrapBlazor/Utils/JSInterop.cs @@ -70,6 +70,27 @@ public class JSInterop : IDisposable where TValue : class return _jsRuntime.InvokeAsync("$.bb_geo_getCurrnetPosition", _objRef, callbackMethodName); } + + /// + /// + /// + /// + internal ValueTask CheckNotifyPermissionAsync(TValue value, string? callbackMethodName = null, bool requestPermission = true) + { + _objRef = DotNetObjectReference.Create(value); + return _jsRuntime.InvokeVoidAsync("$.bb_notify_checkPermission", _objRef, callbackMethodName ?? "", requestPermission); + } + + /// + /// + /// + /// + internal ValueTask Dispatch(TValue value, NotificationItem model, string? callbackMethodName = null) + { + _objRef = DotNetObjectReference.Create(value); + return _jsRuntime.InvokeAsync("$.bb_notify_display", _objRef, callbackMethodName, model); + } + /// /// Dispose 方法 /// diff --git a/src/BootstrapBlazor/wwwroot/js/bootstrap.blazor.bundle.min.js b/src/BootstrapBlazor/wwwroot/js/bootstrap.blazor.bundle.min.js index a9992d48ca0d458fc7fa49daf02593f70456c9b9..e73d0cc6096420a66a968e7357a195396b67b020 100644 --- a/src/BootstrapBlazor/wwwroot/js/bootstrap.blazor.bundle.min.js +++ b/src/BootstrapBlazor/wwwroot/js/bootstrap.blazor.bundle.min.js @@ -13,4 +13,4 @@ var QRCode;!function(){function a(a){this.mode=c.MODE_8BIT_BYTE,this.data=a,this /*! Summernote v0.8.18 | (c) 2013- Alan Hong and other contributors | MIT license */ !function(t,e){if("object"==typeof exports&&"object"==typeof module)module.exports=e(require("jquery"));else if("function"==typeof define&&define.amd)define(["jquery"],e);else{var n="object"==typeof exports?e(require("jquery")):e(t.jQuery);for(var o in n)("object"==typeof exports?exports:t)[o]=n[o]}}(self,(function(t){return(()=>{"use strict";var e={9458:e=>{e.exports=t}},n={};function o(t){var i=n[t];if(void 0!==i)return i.exports;var r=n[t]={exports:{}};return e[t](r,r.exports,o),r.exports}o.amdO={},o.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return o.d(e,{a:e}),e},o.d=(t,e)=>{for(var n in e)o.o(e,n)&&!o.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:e[n]})},o.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),o.r=t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})};var i={};return(()=>{o.r(i);var t=o(9458),e=o.n(t);function n(t){return(n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function r(t,e){for(var n=0;n'),u=s('