Files
AFFiNE-Mirror/blocksuite/affine/shared/src/services/notification-service.ts
Saul-Mirone 6fb7f51ea2 refactor(editor): history as a store extension (#12214)
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **Refactor**
  - Improved history and undo/redo management across the app by introducing a dedicated history extension. Undo/redo operations now use a more focused undo manager, resulting in clearer and more consistent behavior.
- **Documentation**
  - Updated API documentation to reflect changes in history management, including revised method signatures and removal of outdated event references.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-05-12 01:50:57 +00:00

124 lines
3.4 KiB
TypeScript

import { createIdentifier, type ServiceProvider } from '@blocksuite/global/di';
import { EditorLifeCycleExtension } from '@blocksuite/std';
import { type ExtensionType, StoreIdentifier } from '@blocksuite/store';
import type { TemplateResult } from 'lit';
export interface NotificationService {
toast(
message: string,
options?: {
duration?: number;
portal?: HTMLElement;
}
): void;
confirm(options: {
title: string | TemplateResult;
message: string | TemplateResult;
confirmText?: string;
cancelText?: string;
abort?: AbortSignal;
}): Promise<boolean>;
prompt(options: {
title: string | TemplateResult;
message: string | TemplateResult;
autofill?: string;
placeholder?: string;
confirmText?: string;
cancelText?: string;
abort?: AbortSignal;
}): Promise<string | null>; // when cancel, return null
notify(options: {
title: string | TemplateResult;
message?: string | TemplateResult;
accent?: 'info' | 'success' | 'warning' | 'error';
duration?: number; // unit ms, give 0 to disable auto dismiss
abort?: AbortSignal;
actions?: {
key: string;
label: string | TemplateResult;
onClick: () => void;
}[];
onClose?: () => void;
}): void;
/**
* Notify with undo action, it is a helper function to notify with undo action.
* And the notification card will be closed when undo action is triggered by shortcut key or other ways.
*/
notifyWithUndoAction: (
options: Parameters<NotificationService['notify']>[0]
) => void;
}
export const NotificationProvider = createIdentifier<NotificationService>(
'AffineNotificationService'
);
export function NotificationExtension(
notificationService: Omit<NotificationService, 'notifyWithUndoAction'>
): ExtensionType {
return {
setup: di => {
di.addImpl(NotificationProvider, provider => {
return {
...notificationService,
notifyWithUndoAction: options => {
notifyWithUndoActionImpl(
provider,
notificationService.notify,
options
);
},
};
});
},
};
}
function notifyWithUndoActionImpl(
provider: ServiceProvider,
notify: NotificationService['notify'],
options: Parameters<NotificationService['notifyWithUndoAction']>[0]
) {
const store = provider.get(StoreIdentifier);
const abortController = new AbortController();
const abort = () => {
abortController.abort();
};
options.abort?.addEventListener('abort', abort);
const clearOnClose = () => {
store.history.undoManager.off('stack-item-added', addHandler);
store.history.undoManager.off('stack-item-popped', popHandler);
disposable.unsubscribe();
options.abort?.removeEventListener('abort', abort);
};
const addHandler = store.history.undoManager.on('stack-item-added', abort);
const popHandler = store.history.undoManager.on('stack-item-popped', abort);
const disposable = provider
.get(EditorLifeCycleExtension)
.slots.unmounted.subscribe(() => abort());
notify({
...options,
actions: [
{
key: 'notification-card-undo',
label: 'Undo',
onClick: () => {
store.undo();
abortController.abort();
},
},
...(options.actions ?? []),
],
abort: abortController.signal,
onClose: () => {
options.onClose?.();
clearOnClose();
},
});
}