Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Notification API #613

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions src/renderer/apis/notification.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { ButtonItemProps } from "@components/ButtonItem";
export interface NotificationProps {
id?: string;
timeout: number;
origin?: string;
name?: string;
color?: string;
iconColor?: string;
imageClassName?: string | undefined;
header: React.ReactNode;
content: React.ReactNode;
image: string;
icon?: React.FunctionComponent<React.SVGProps<SVGSVGElement>> | false;
buttons?: ButtonItemProps[];
className?: string;
style?: React.CSSProperties;
hideProgressBar?: boolean;
type?: string;
}
export interface NotificationPropsWithId extends NotificationProps {
id: string;
origin: string;
name: string;
color: string;
}
class RPNotificationHandler extends EventTarget {
private notifications = new Map<string, NotificationPropsWithId>();

public sendNotification(notification: NotificationPropsWithId): () => void {
this.notifications.set(notification.id, notification);
this.dispatchEvent(new CustomEvent("rpNotificationUpdate"));
return () => {
this.notifications.delete(notification.id);
this.dispatchEvent(new CustomEvent("rpNotificationUpdate"));
};
}

public getNotifications(): NotificationPropsWithId[] {
return Array.from(this.notifications.values());
}

public closeNotification(id: string): void {
this.notifications.delete(id);
this.dispatchEvent(new CustomEvent("rpNotificationUpdate"));
}
}
export const NotificationHandler = new RPNotificationHandler();

export class NotificationAPI {
public origin: string;
public name: string;
public color: string;
private notifications: Array<() => void> = [];

public constructor(origin: string, name: string, color?: string) {
this.origin = origin;
this.name = name;
this.color = color ?? "#5864f2";
}

public notify(notification: NotificationProps): () => void {
notification.name = this.name;
notification.origin = this.origin;
notification.type ??= "info";
notification.color ??= this.color;
notification.id = `${this.origin}-${this.name}-${notification.header}-${
notification.type
} -${Date.now()}`;
const dismiss = NotificationHandler.sendNotification(notification as NotificationPropsWithId);
this.notifications.push(dismiss);
return () => {
dismiss();
this.notifications = this.notifications.filter((f) => f !== dismiss);
};
}

public dismissAll(): void {
for (const dismiss of this.notifications) {
dismiss();
}
this.notifications = [];
}

public static api(name: string, color?: string): NotificationAPI {
return new NotificationAPI("API", name, color);
}

public static coremod(name: string, color?: string): NotificationAPI {
return new NotificationAPI("Coremod", name, color);
}

public static plugin(name: string, color?: string): NotificationAPI {
return new NotificationAPI("Plugin", name, color);
}
}
9 changes: 9 additions & 0 deletions src/renderer/coremods/notification/icons/Close.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { React } from "@common";
export default React.memo((props: React.SVGProps<SVGSVGElement>) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" {...props}>
<path
fill="currentColor"
d="M18.4 4L12 10.4L5.6 4L4 5.6L10.4 12L4 18.4L5.6 20L12 13.6L18.4 20L20 18.4L13.6 12L20 5.6L18.4 4Z"
/>
</svg>
));
9 changes: 9 additions & 0 deletions src/renderer/coremods/notification/icons/Danger.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { React } from "@common";
export default React.memo((props: React.SVGProps<SVGSVGElement>) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 15" {...props}>
<path
d="M7.02799 0.333252C3.346 0.333252 0.361328 3.31792 0.361328 6.99992C0.361328 10.6819 3.346 13.6666 7.02799 13.6666C10.71 13.6666 13.6947 10.6819 13.6947 6.99992C13.6947 3.31792 10.7093 0.333252 7.02799 0.333252ZM10.166 9.19525L9.22333 10.1379L7.02799 7.94325L4.83266 10.1379L3.89 9.19525L6.08466 6.99992L3.88933 4.80459L4.832 3.86259L7.02733 6.05792L9.22266 3.86259L10.1653 4.80459L7.97066 6.99992L10.166 9.19525Z"
fill="currentColor"
/>
</svg>
));
9 changes: 9 additions & 0 deletions src/renderer/coremods/notification/icons/Info.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { React } from "@common";
export default React.memo((props: React.SVGProps<SVGSVGElement>) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 11 11" {...props}>
<path
fill="currentColor"
d="M6 1C3.243 1 1 3.244 1 6c0 2.758 2.243 5 5 5s5-2.242 5-5c0-2.756-2.243-5-5-5zm0 2.376a.625.625 0 110 1.25.625.625 0 010-1.25zM7.5 8.5h-3v-1h1V6H5V5h1a.5.5 0 01.5.5v2h1v1z"
/>
</svg>
));
9 changes: 9 additions & 0 deletions src/renderer/coremods/notification/icons/Success.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { React } from "@common";
export default React.memo((props: React.SVGProps<SVGSVGElement>) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" {...props}>
<path
fill="currentColor"
d="M8.99991 16.17L4.82991 12L3.40991 13.41L8.99991 19L20.9999 7.00003L19.5899 5.59003L8.99991 16.17Z"
/>
</svg>
));
9 changes: 9 additions & 0 deletions src/renderer/coremods/notification/icons/Warning.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { React } from "@common";
export default React.memo((props: React.SVGProps<SVGSVGElement>) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 21 21" {...props}>
<path
d="M10 0C4.486 0 0 4.486 0 10C0 15.515 4.486 20 10 20C15.514 20 20 15.515 20 10C20 4.486 15.514 0 10 0ZM9 4H11V11H9V4ZM10 15.25C9.31 15.25 8.75 14.691 8.75 14C8.75 13.31 9.31 12.75 10 12.75C10.69 12.75 11.25 13.31 11.25 14C11.25 14.691 10.69 15.25 10 15.25Z"
fill="currentColor"
/>
</svg>
));
13 changes: 13 additions & 0 deletions src/renderer/coremods/notification/icons/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Close from "./Close";
import Danger from "./Danger";
import Info from "./Info";
import Success from "./Success";
import Warning from "./Warning";

export default {
Close,
Danger,
Info,
Success,
Warning,
};
5 changes: 5 additions & 0 deletions src/renderer/coremods/notification/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import NotificationContainer from "./notification";

export function _renderNotification(): React.ReactElement {
return <NotificationContainer />;
}
187 changes: 187 additions & 0 deletions src/renderer/coremods/notification/notification.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
/*======== CSS Variables ========*/
.theme-dark {
--replugged-notification-box-shadow: rgba(0, 0, 0, 0.2);
--replugged-notification-border: rgba(28, 36, 43, 0.6);
}
.theme-light {
--replugged-notification-box-shadow: rgba(255, 255, 255, 0.1);
--replugged-notification-border: rgba(199, 199, 199, 0.06);
}
/*======== Toast Styling ========*/

.replugged-notification-container {
display: flex;
flex-direction: column;
align-items: flex-end;
justify-content: flex-end;
position: fixed;
bottom: 25px;
right: 25px;
z-index: 999;
}
.replugged-notification {
display: flex;
flex-direction: column;
margin-bottom: 10px;
background-color: var(--background-secondary);
border: 1px solid var(--replugged-notification-border);
box-shadow: 0 8px 16px 0 var(--replugged-notification-box-shadow);
border-radius: 8px;
max-width: 600px;
min-width: 223px;
width: 320px;
position: relative;
animation:
slide-in 0.5s ease,
shake 1.4s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
}

.replugged-notification .header {
display: flex;
color: var(--header-primary);
font-weight: 600;
font-size: 24px;
line-height: 1.2;
background-color: var(--background-primary);
border-radius: 8px 8px 0 0;
padding: 12px 20px;
justify-content: center;
align-items: center; /* Center items vertically */
}

.replugged-notification .header span:has(.icon) {
display: flex;
align-items: center;
justify-content: center;
margin-right: 8px;
margin-top: 3px;
}

.replugged-notification .header .icon {
display: flex;
align-items: center;
justify-content: center;
}

.replugged-notification .header .icon img {
width: 18px;
height: 18px;
}

.replugged-notification .header .dismiss {
width: 16px;
height: 16px;
opacity: 0.5;
transition: opacity 0.2s;
margin-left: auto;
cursor: pointer;
font-size: 12px;
margin-bottom: 5px;
}

.replugged-notification .contents {
display: flex;
border-radius: 0 0 8px 8px;
text-align: center;
justify-content: flex-end;
flex-direction: column;
padding: 10px;
}

.replugged-notification .contents .inner {
color: var(--text-normal);
font-size: 14px;
line-height: 1.4;
background-color: var(--background-tertiary);
border-bottom: 1px solid solid var(--replugged-notification-border);
border-radius: 8px;
padding: 10px;
margin-bottom: 6px;
}

.replugged-notification .buttons {
display: flex;
flex-wrap: wrap;
width: 95%;
padding: 10px;
}

.replugged-notification .buttons button {
box-sizing: border-box;
min-width: calc(50% - 10px);
padding: 8px;
margin: 8px 4px;
flex: 1;
border: none;
border-radius: 4px;
cursor: pointer;
}

.replugged-notification .buttons button[class*="lookGhost"] {
opacity: 0.8;
transition:
background-color 0.17s ease,
color 0.17s ease,
opacity 0.17s ease,
transform 0.17s ease;
}

.replugged-notification .buttons button[class*="lookGhost"]:hover {
opacity: 1;
}

.replugged-notification.leaving {
animation: slide-out 0.7s ease-out;
}

/*========= Header Types =========*/
.replugged-notification.info .icon {
color: var(--blurple);
}

.replugged-notification.warning .icon {
color: var(--info-warning-foreground);
}

.replugged-notification.danger .icon {
color: var(--info-danger-foreground);
}

.replugged-notification.success .icon {
color: var(--info-positive-foreground);
}

/*========== Animations ==========*/
@keyframes shake {
10%,
90% {
transform: translate3d(1px, 0, 0);
}
20%,
80% {
transform: translate3d(2px, 0, 0);
}
30%,
50%,
70% {
transform: translate3d(-4px, 0, 0);
}
40%,
60% {
transform: translate3d(4px, 0, 0);
}
}

@keyframes slide-in {
from {
margin-right: -500px;
opacity: 0;
}
}

@keyframes slide-out {
to {
margin-right: -500px;
opacity: 0;
}
}
Loading
Loading