+ ${closeButton}
+ ${titleHtml}
+
${message}
+
`;
+
+ // Add event listeners
+ const closeButtonElement = toast.querySelector('.abp-toast-close-button');
+ if (closeButtonElement) {
+ closeButtonElement.addEventListener('click', () => this.remove(toast));
+ }
+
+ if (options.tapToDismiss) {
+ toast.addEventListener('click', () => this.remove(toast));
+ }
+
+ return toast;
+};
+
+// Show a toast with given options
+AbpToastService.prototype.show = function(message, title, severity = 'neutral', options = {}) {
+ const mergedOptions = this.extend({}, this.globalOptions, options);
+ const id = ++this.lastId;
+ const toast = this.createToastElement(message, title, severity, mergedOptions);
+
+ // Set data attributes for non-object options
+ Object.entries(mergedOptions)
+ .filter(([_, value]) => typeof value !== 'object')
+ .forEach(([key, value]) => toast.dataset[key] = value);
+
+ toast.dataset.id = id;
+ this.container.appendChild(toast);
+ this.toasts.push(toast);
+
+ // Auto remove if not sticky
+ if (!mergedOptions.sticky) {
+ setTimeout(() => this.remove(toast), mergedOptions.life);
+ }
+
+ return id;
+};
+
+// Remove a toast with animation
+AbpToastService.prototype.remove = function(toastElement) {
+ toastElement.classList.add('toast-removing');
+ setTimeout(() => {
+ if (toastElement.parentNode === this.container) {
+ this.container.removeChild(toastElement);
+ }
+ this.toasts = this.toasts.filter(t => t !== toastElement);
+ }, 300); // Match animation duration
+};
+
+// Convenience methods for different severities
+AbpToastService.prototype.success = function(message, title, options) {
+ return this.show(message, title, 'success', options);
+};
+
+AbpToastService.prototype.error = function(message, title, options) {
+ return this.show(message, title, 'error', options);
+};
+
+AbpToastService.prototype.info = function(message, title, options) {
+ return this.show(message, title, 'info', options);
+};
+
+AbpToastService.prototype.warning = function(message, title, options) {
+ return this.show(message, title, 'warning', options);
+};
+
+// Clear all toasts
+AbpToastService.prototype.clear = function(containerKey) {
+ if (containerKey) {
+ this.toasts = this.toasts.filter(toast => {
+ const shouldRemove = toast.dataset.containerKey === containerKey;
+ if (shouldRemove) {
+ this.remove(toast);
+ }
+ return !shouldRemove;
+ });
+ } else {
+ this.toasts.forEach(toast => this.remove(toast));
+ }
+};
+
+// Static method to set default options for all instances
+AbpToastService.setDefaultOptions = function(options) {
+ AbpToastService.defaultOptions = this.prototype.extend({}, AbpToastService.defaultOptions, options);
+};
+
+var abp = abp || {};
+(function () {
+ var toast;
+
+ function getToast() {
+ if (!toast) {
+ toast = new AbpToastService();
+ }
+ return toast;
+ }
+
+ abp.notify.success = function (message, title, options) {
+ getToast().success(message, title, options);
+ };
+
+ abp.notify.info = function (message, title, options) {
+ getToast().info(message, title, options);
+ };
+
+ abp.notify.warn = function (message, title, options) {
+ getToast().warning(message, title, options);
+ };
+
+ abp.notify.error = function (message, title, options) {
+ getToast().error(message, title, options);
+ };
+})();
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/toastr/abp-toastr.js b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/toastr/abp-toastr.js
deleted file mode 100644
index 37a7c40166..0000000000
--- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/wwwroot/libs/abp/aspnetcore-mvc-ui-theme-shared/toastr/abp-toastr.js
+++ /dev/null
@@ -1,34 +0,0 @@
-var abp = abp || {};
-(function () {
-
- if (!toastr) {
- return;
- }
-
- /* DEFAULTS *************************************************/
-
- toastr.options.positionClass = 'toast-bottom-right';
-
- /* NOTIFICATION *********************************************/
-
- var showNotification = function (type, message, title, options) {
- toastr[type](message, title, options);
- };
-
- abp.notify.success = function (message, title, options) {
- showNotification('success', message, title, options);
- };
-
- abp.notify.info = function (message, title, options) {
- showNotification('info', message, title, options);
- };
-
- abp.notify.warn = function (message, title, options) {
- showNotification('warning', message, title, options);
- };
-
- abp.notify.error = function (message, title, options) {
- showNotification('error', message, title, options);
- };
-
-})();
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/LocalDistributedEventBus.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/LocalDistributedEventBus.cs
index 04d07259a3..90c118eca1 100644
--- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/LocalDistributedEventBus.cs
+++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/LocalDistributedEventBus.cs
@@ -28,30 +28,6 @@ public class LocalDistributedEventBus : IDistributedEventBus, ISingletonDependen
ServiceScopeFactory = serviceScopeFactory;
AbpDistributedEventBusOptions = distributedEventBusOptions.Value;
Subscribe(distributedEventBusOptions.Value.Handlers);
-
- // For unit testing
- if (localEventBus is LocalEventBus eventBus)
- {
- eventBus.OnEventHandleInvoking = async (eventType, eventData) =>
- {
- await localEventBus.PublishAsync(new DistributedEventReceived()
- {
- Source = DistributedEventSource.Direct,
- EventName = EventNameAttribute.GetNameOrDefault(eventType),
- EventData = eventData
- }, onUnitOfWorkComplete: false);
- };
-
- eventBus.OnPublishing = async (eventType, eventData) =>
- {
- await localEventBus.PublishAsync(new DistributedEventSent()
- {
- Source = DistributedEventSource.Direct,
- EventName = EventNameAttribute.GetNameOrDefault(eventType),
- EventData = eventData
- }, onUnitOfWorkComplete: false);
- };
- }
}
public virtual void Subscribe(ITypeList