Add action buttons to flashNotification
Used by upgrade notification
This commit is contained in:
+5
-1
@@ -822,8 +822,12 @@ export class Client {
|
||||
) {
|
||||
this.versionMismatchNotified = true;
|
||||
this.ui.flashNotification(
|
||||
"A new version of SilverBullet is available. Reload to update.",
|
||||
"A new version of SilverBullet client is available. A reload or two is required to update.",
|
||||
"warning",
|
||||
{
|
||||
timeout: 0,
|
||||
actions: [{ name: "Reload", run: () => location.reload() }],
|
||||
},
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -18,6 +18,7 @@ export function TopBar({
|
||||
isLoading,
|
||||
notifications,
|
||||
onRename,
|
||||
onDismissNotification,
|
||||
actionButtons,
|
||||
darkMode,
|
||||
vimMode,
|
||||
@@ -40,6 +41,7 @@ export function TopBar({
|
||||
progressPercentage?: number;
|
||||
progressType?: string;
|
||||
onRename: (newName?: string) => Promise<void>;
|
||||
onDismissNotification: (id: number) => void;
|
||||
onClick: () => void;
|
||||
actionButtons: ActionButton[];
|
||||
lhs?: ComponentChildren;
|
||||
@@ -97,7 +99,36 @@ export function TopBar({
|
||||
key={notification.id}
|
||||
className={`sb-notification-${notification.type}`}
|
||||
>
|
||||
{notification.message}
|
||||
<span className="sb-notification-message">
|
||||
{notification.message}
|
||||
</span>
|
||||
{notification.actions && notification.actions.length > 0 && (
|
||||
<span className="sb-notification-actions">
|
||||
{notification.actions.map((action, i) => (
|
||||
<button
|
||||
key={i}
|
||||
className="sb-button"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
action.run();
|
||||
}}
|
||||
>
|
||||
{action.name}
|
||||
</button>
|
||||
))}
|
||||
</span>
|
||||
)}
|
||||
{notification.persistent && (
|
||||
<button
|
||||
className="sb-notification-dismiss"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onDismissNotification(notification.id);
|
||||
}}
|
||||
>
|
||||
×
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
+29
-7
@@ -20,6 +20,7 @@ import { Panel } from "./components/panel.tsx";
|
||||
import { safeRun } from "@silverbulletmd/silverbullet/lib/async";
|
||||
import type {
|
||||
FilterOption,
|
||||
NotificationAction,
|
||||
NotificationType,
|
||||
} from "@silverbulletmd/silverbullet/type/client";
|
||||
import { notificationDismissTimeouts } from "@silverbulletmd/silverbullet/type/client";
|
||||
@@ -86,8 +87,26 @@ export class MainUI {
|
||||
|
||||
viewDispatch: (action: Action) => void = () => {};
|
||||
|
||||
flashNotification(message: string, type: NotificationType = "info") {
|
||||
flashNotification(
|
||||
message: string,
|
||||
type: NotificationType = "info",
|
||||
options?: {
|
||||
timeout?: number;
|
||||
actions?: NotificationAction[];
|
||||
},
|
||||
) {
|
||||
const id = Math.floor(Math.random() * 1000000);
|
||||
const dismiss = () => {
|
||||
this.viewDispatch({ type: "dismiss-notification", id });
|
||||
};
|
||||
const persistent = options?.timeout === 0;
|
||||
const actions = options?.actions?.map((action) => ({
|
||||
name: action.name,
|
||||
run: () => {
|
||||
action.run();
|
||||
dismiss();
|
||||
},
|
||||
}));
|
||||
this.viewDispatch({
|
||||
type: "show-notification",
|
||||
notification: {
|
||||
@@ -95,14 +114,14 @@ export class MainUI {
|
||||
type,
|
||||
message,
|
||||
date: new Date(),
|
||||
actions,
|
||||
persistent,
|
||||
},
|
||||
});
|
||||
setTimeout(() => {
|
||||
this.viewDispatch({
|
||||
type: "dismiss-notification",
|
||||
id: id,
|
||||
});
|
||||
}, notificationDismissTimeouts[type]);
|
||||
if (!persistent) {
|
||||
const timeout = options?.timeout ?? notificationDismissTimeouts[type];
|
||||
setTimeout(dismiss, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
showProgress(progressPercentage?: number, progressType?: "sync" | "index") {
|
||||
@@ -386,6 +405,9 @@ export class MainUI {
|
||||
!viewState.current ? "" : getNameFromPath(viewState.current.path)
|
||||
}
|
||||
notifications={viewState.notifications}
|
||||
onDismissNotification={(id) => {
|
||||
dispatch({ type: "dismiss-notification", id });
|
||||
}}
|
||||
isOnline={viewState.isOnline}
|
||||
unsavedChanges={viewState.unsavedChanges}
|
||||
isLoading={viewState.isLoading}
|
||||
|
||||
@@ -22,6 +22,7 @@ import { getCM as vimGetCm, Vim } from "@replit/codemirror-vim";
|
||||
import type { SysCallMapping } from "../system.ts";
|
||||
import type {
|
||||
FilterOption,
|
||||
NotificationAction,
|
||||
NotificationType,
|
||||
UploadFile,
|
||||
} from "@silverbulletmd/silverbullet/type/client";
|
||||
@@ -289,8 +290,9 @@ export function editorSyscalls(client: Client): SysCallMapping {
|
||||
_ctx,
|
||||
message: string,
|
||||
type: NotificationType = "info",
|
||||
options?: { timeout?: number; actions?: NotificationAction[] },
|
||||
) => {
|
||||
client.ui.flashNotification(message, type);
|
||||
client.ui.flashNotification(message, type, options);
|
||||
},
|
||||
"editor.filterBox": (
|
||||
_ctx,
|
||||
|
||||
+29
-1
@@ -34,9 +34,37 @@
|
||||
z-index: 100;
|
||||
|
||||
> div {
|
||||
padding: 3px;
|
||||
padding: 3px 8px;
|
||||
margin-bottom: 3px;
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.sb-notification-actions {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
flex-shrink: 0;
|
||||
|
||||
.sb-button {
|
||||
font-size: 0.85em;
|
||||
padding: 1px 6px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.sb-notification-dismiss {
|
||||
background: none;
|
||||
border: none;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
font-size: 1.1em;
|
||||
padding: 0 2px;
|
||||
opacity: 0.7;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,11 +19,18 @@ export const notificationDismissTimeouts: Record<NotificationType, number> = {
|
||||
warning: 8000,
|
||||
};
|
||||
|
||||
export type NotificationAction = {
|
||||
name: string;
|
||||
run: () => void;
|
||||
};
|
||||
|
||||
export type Notification = {
|
||||
id: number;
|
||||
message: string;
|
||||
type: NotificationType;
|
||||
date: Date;
|
||||
actions?: NotificationAction[];
|
||||
persistent?: boolean;
|
||||
};
|
||||
|
||||
export type PanelMode = number;
|
||||
|
||||
+24
-1
@@ -238,12 +238,35 @@ Example:
|
||||
editor.hidePanel("rhs")
|
||||
```
|
||||
|
||||
### editor.flashNotification(message, type)
|
||||
### editor.flashNotification(message, type, options?)
|
||||
Shows a flash notification.
|
||||
|
||||
Parameters:
|
||||
- `message`: The message to display
|
||||
- `type`: Notification type — `"info"` (default), `"error"`, or `"warning"`
|
||||
- `options` (optional): Table with additional options:
|
||||
- `timeout`: Dismiss timeout in milliseconds. Use `0` for persistent notifications (default: 4000/5000/8000 depending on type)
|
||||
- `actions`: List of action buttons, each with:
|
||||
- `name`: Button label
|
||||
- `run`: Function to call when clicked (auto-dismisses the notification)
|
||||
|
||||
Example:
|
||||
```lua
|
||||
-- Simple notification
|
||||
editor.flashNotification("Operation completed", "info")
|
||||
|
||||
-- Persistent notification with action button
|
||||
editor.flashNotification("Update available", "warning", {
|
||||
timeout = 0,
|
||||
actions = {
|
||||
{
|
||||
name = "Reload",
|
||||
run = function()
|
||||
editor.reloadUI()
|
||||
end
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### editor.downloadFile(filename, dataUrl)
|
||||
|
||||
@@ -4,6 +4,7 @@ An attempt at documenting the changes/new features introduced in each release.
|
||||
Whenever a commit is pushed to the `main` branch, within ~5 minutes, it will be released as a docker image with the `:v2` tag, and a binary in the [edge release](https://github.com/silverbulletmd/silverbullet/releases/tag/edge). If you want to live on the bleeding edge of SilverBullet goodness (or regression) this is where to do it.
|
||||
|
||||
* **Technical: Deno → Node.js migration**: The TypeScript/client codebase has been migrated from Deno to Node.js, now using vitest for tests. This _should_ purely be a tooling change, but may inadvertently have lead to regressions. Please report if you find anything.
|
||||
* `editor.flashNotification` now supports an optional third argument with `timeout` (use `0` for persistent notifications) and `actions` (buttons with callbacks).
|
||||
* [[Outlines]] commands have been thoroughly reworked. Should now be more robust and better tested outline move/indent operations. New features:
|
||||
* Now also works with numbered items (and renumbers them)
|
||||
* Now works with headers (moves around entire sections)
|
||||
|
||||
Reference in New Issue
Block a user