Skip to content

Instantly share code, notes, and snippets.

@johnatandias
Last active April 8, 2023 19:32
Show Gist options
  • Save johnatandias/6ae5ecd052f714c0787763703e8f2557 to your computer and use it in GitHub Desktop.
Save johnatandias/6ae5ecd052f714c0787763703e8f2557 to your computer and use it in GitHub Desktop.
/**
* A simple broadcast class created in a "Reactive" model
* @constructor
* @template T t
*/
export class Subject<T> {
_changeObservers: { (param: T): void; }[] = [];
/**
* Subscribe to listener new messages about this subject
* @public
* @memberof Subject
* @param {function(t: T)} fn function passed as parameter
*/
subscribe = (fn: (T) => void) => {
this._changeObservers.push(fn);
};
/**
* Remove subscription about this subject
* It's a best practice unsubscribe in the app dispose life cycle
* @public
* @memberof Subject
* @param {function(t: T)} fn function passed as parameter
*/
unsubscribe = (fn: (T) => void) => {
this._changeObservers = this
._changeObservers
.filter((item) => {
if (item !== fn) return item;
});
};
/**
* Emits a new value on this stream
* @param {T} newObject
* @memberof Subject
*/
emit = (newObject: T) => {
this._changeObservers.forEach((observer) => observer(newObject));
}
}
import { Subject } from "@kaios/apis";
export interface ToastRequest {
/** Text to display on Toast */
text: string;
/** Delay to keep on the screen */
delay?: number
};
export interface ToastItem extends ToastRequest { removed: boolean };
class ToastSystemClass {
/** A change subject listener */
public onchange = new Subject<ToastItem>();
// A FIFO queue
private queue: ToastRequest[] = [];
// a simple semaphore to skip Toast process
private processing = false;
// Delay between Toasts
private delayBetween = 1000;
// Internal Toast process
private display = () => {
// indicate that system is in process
this.processing = true;
// since there is no items on queue, release the process
if (this.queue.length === 0) {
this.processing = false;
// clean up listeners
//this.onchange.emit(null);
return;
}
// FIFO queue, pop tha last Toast on queue to be the next
const nextToast: ToastItem = { ...this.queue.pop(), removed: false };
// emit a new Toast from the system
this.onchange.emit(nextToast);
setTimeout(() => {
// Immutable approach
// Flag this Toast to be removed from the screen
const changed: ToastItem = { ...nextToast, removed: true };
this.onchange.emit(changed);
}, nextToast.delay);
// Schedule the next check
setTimeout(() => this.display(), nextToast.delay + this.delayBetween);
}
/**
* Show/Schedule a new Toast on screen
* Keep in mind the multiple Toasts wiprocessingl be schedule in a queue
*
* The default delay is 3s seconds
*/
public show = (request: ToastRequest) => {
if (request === null) throw new Error("ToastRequest can not be null");
request.delay = request.delay || 3000;
this.queue.push(request);
// skip since we are already processing Toasts
if (this.processing)
return;
else
// call internally to display a new Toast
this.display();
}
}
/**
* ### Toast System
*
* #### Sample
*
* ```typescript
* import {ToastSystem} from '../systems/Toast.system';
*
* // create a Toast request
* const Toast: Toastrequest = {
* text: "Hey dude!",
* delay: 3500
* };
*
* // Queue a new Toast
* ToastSystem.show(Toast);
*
* ```
*/
export const ToastSystem = new ToastSystemClass()
import { Text } from '@kaios/kcomponents';
import React, { useEffect, useState } from 'react';
import css from './toast.css';
import { ToastItem, ToastSystem } from './toast.system';
export const Toast = (): JSX.Element | null => {
useEffect(() => {
// Component mounted
ToastSystem.onchange.subscribe(onChange);
// Destroying component
return function onDestroy() {
ToastSystem.onchange.unsubscribe(onChange);
}
}, []);
const [Toast, setToast] = useState<ToastItem>(null);
const onChange = (Toast) => setToast(Toast);
// skip non Toasts
if (!Toast) return null;
// Conditional apply removed class
const stateClass = Toast.removed ? css.leave : css.enter;
return <Text text={Toast.text} className={` ${css.container} ${stateClass}`} />;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment