Skip to content

Commit

Permalink
feat: init notification
Browse files Browse the repository at this point in the history
- Add `Notification.ts`
- Add file saving notification
- Adjust `#hot-container` z-index
- Fix file saving status

Resolves: #83, #18
  • Loading branch information
yinanazhou committed Jul 1, 2024
1 parent ca70bf0 commit a808173
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 2 deletions.
4 changes: 4 additions & 0 deletions assets/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,10 @@ a:hover {
/* ----------------------- */

/* Keep camelCase names as they are from the library */
#hot-container {
z-index: 7;
}

.handsontable.table-menu-btn .changeType {
background: #e2e2e2;
border-radius: 100%;
Expand Down
21 changes: 19 additions & 2 deletions src/Editor/CressTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { ImageHandler } from './ImageHandler';
import { ExportHandler } from './ExportHandler';
import { ColumnTools } from './ColumnTools';
import { updateAttachment } from '../Dashboard/Storage';
import { setSavedStatus } from '../utils/Unsaved';
import * as Notification from '../utils/Notification';

export class CressTable {
private table: Handsontable;
Expand Down Expand Up @@ -64,6 +66,7 @@ export class CressTable {
className: 'table-menu-btn',
licenseKey: 'non-commercial-and-evaluation',
afterChange() {
setSavedStatus(false);
this.validateCells();
},
beforeValidate: (value) => this.setProcessStatus(value),
Expand Down Expand Up @@ -99,9 +102,23 @@ export class CressTable {
const result = await updateAttachment(id, [inputHeader, ...body]);

if (result) {
// TODO: notification
setSavedStatus(true);
Notification.queueNotification('Saved', 'success');
} else {
// TODO: notification
Notification.queueNotification('Save failed', 'error');
}
});

document.body.addEventListener('keydown', async (evt) => {
if (evt.key === 's') {
const result = await updateAttachment(id, [inputHeader, ...body]);

if (result) {
setSavedStatus(true);
Notification.queueNotification('Saved', 'success');
} else {
Notification.queueNotification('Save failed', 'error');
}
}
});
}
Expand Down
2 changes: 2 additions & 0 deletions src/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,5 @@ export type validationStatus = 'unknown' | 'processing' | 'done';

/** An <svg> element from any DOM queries */
export type HTMLSVGElement = HTMLElement & SVGSVGElement;

export type NotificationType = 'default' | 'error' | 'warning' | 'success';
131 changes: 131 additions & 0 deletions src/utils/Notification.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { NotificationType } from '../Types';
import { v4 as uuidv4 } from 'uuid';

const notifications: Notification[] = new Array(0);
let currentModeMessage: Notification = null;
const NUMBER_TO_DISPLAY = 3; // Number of notifications to display at a time.
const TIMEOUT = 5000; // Number of notifications to display at a time.

const notificationIcon: Record<NotificationType, string> = {
default: '',
warning: '⚠️ ',
error: '🔴 ',
success: '✅ ',
};

/**
* A class to manage cress notifications.
*/
export class Notification {
message: string;
displayed: boolean;
id: string;
isModeMessage: boolean;
logInfo: string;
timeoutID: number;
type: NotificationType;
/**
* Create a new notification.
* @param message - Notification content.
*/
constructor(message: string, type: NotificationType, logInfo: string = null) {
this.message = notificationIcon[type] + message;
this.displayed = false;
this.id = uuidv4();
this.isModeMessage = message.search('Mode') !== -1;
this.logInfo = logInfo;
this.timeoutID = -1;
this.type = type;
}

/** Set the ID from setTimeout. */
setTimeoutId(id: number): void {
this.timeoutID = Math.max(id, -1);
}

/** Display the Notification. */
display(): void {
this.displayed = true;
}

/**
* @returns The UUID for this notification.
*/
getId(): string {
return this.id;
}
}

/**
* Clear the notification
* @param currentId - The ID of the notification to be cleared.
*/
function clearNotification(currentId: string): void {
if (document.getElementById(currentId)) {
document.getElementById(currentId).remove();
}
}

/**
* Display a notification.
* @param notification - Notification to display.
*/
function displayNotification(notification: Notification): void {
// Not sure what it does, maybe related to rodan/cress
if (notification.isModeMessage) {
if (currentModeMessage === null) {
currentModeMessage = notification;
} else {
window.clearTimeout(currentModeMessage.timeoutID);
return;
}
}

// Remove the top notification if exceeds maxmimum
notifications.push(notification);
if (notifications.length > NUMBER_TO_DISPLAY) {
const toRemove = notifications.shift();
clearNotification(toRemove.getId());
}
const notificationContent = document.getElementById('notification-content');
const newNotification = document.createElement('div');
newNotification.classList.add('cress-notification');
newNotification.classList.add(`cress-notification-${notification.type}`);
newNotification.id = notification.getId();
newNotification.innerHTML = notification.message;
notificationContent.append(newNotification);
notificationContent.style.display = '';
notification.display();
}

/**
* Start displaying notifications. Called automatically.
*/
function startNotification(notification: Notification): void {
displayNotification(notification);
notification.setTimeoutId(
window.setTimeout(clearNotification, TIMEOUT, notification.getId()),
);
document
.getElementById(notification.getId())
.addEventListener('click', () => {
window.clearTimeout(notification.timeoutID);
clearNotification(notification.getId());
});
}

/**
* Add a notification to the queue.
* @param notification - Notification content.
*/
export function queueNotification(
notificationContent: string,
type: NotificationType = 'default',
logInfo: string = null,
): void {
const notification = new Notification(notificationContent, type, logInfo);

startNotification(notification);
}

export default { queueNotification };

0 comments on commit a808173

Please sign in to comment.