1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00
lens/src/renderer/components/notifications/notifications.store.tsx
Sebastian Malton cd154a8343 Add auto-update and pre-release update user settings
- Add settings in user preferences for auto-updating (default false) and
  for allowing pre-release versions (default false)

- Use in-Lens notifications instead of OS notifications as those were
  found to be flaky

- Add rudimentary main->renderer notification system.

Signed-off-by: Sebastian Malton <sebastian@malton.name>
2020-12-21 17:14:48 -05:00

128 lines
3.6 KiB
TypeScript

import React from "react";
import { action, observable } from "mobx"
import { autobind } from "../../utils";
import isObject from "lodash/isObject"
import uniqueId from "lodash/uniqueId";
import { JsonApiErrorParsed } from "../../api/json-api";
import logger from "../../../main/logger";
import { ipcRenderer } from "electron";
import { IpcChannel, NotificationChannelAdd } from "../../../common/ipc";
import { Button, ButtonProps } from "../button";
export type IMessageId = string | number;
export type IMessage = React.ReactNode | React.ReactNode[] | JsonApiErrorParsed;
export enum NotificationStatus {
OK = "ok",
ERROR = "error",
INFO = "info",
}
export interface INotification {
id?: IMessageId;
message: IMessage;
status?: NotificationStatus;
timeout?: number; // auto-hiding timeout in milliseconds, 0 = no hide
onClose?(): void; // additonal logic on when the notification times out or is closed by the "x"
}
export interface MainNotification {
title: string;
body: string;
buttons?: ({
backchannel: IpcChannel;
} & ButtonProps)[];
status: NotificationStatus;
timeout?: number;
closeChannel?: IpcChannel;
}
function renderButtons(id: IpcChannel, buttons?: MainNotification["buttons"]): React.ReactNode {
if (!buttons) {
return null;
}
return (
<>
<br />
<div className="flex row align-right box grow">
{buttons.map(({ backchannel, ...props}) => (
<Button {...props} onClick={() => {
console.log(`sending to ${backchannel}`)
ipcRenderer.send(backchannel);
notificationsStore.remove(id);
}} />
))}
</div>
</>
)
}
@autobind()
export class NotificationsStore {
public notifications = observable<INotification>([], { deep: false });
protected autoHideTimers = new Map<IMessageId, number>();
registerIpcListener(): void {
logger.info(`[NOTIFICATION-STORE] start to listen for notifications requests from main`);
ipcRenderer.on(NotificationChannelAdd, (event, model: MainNotification) => {
console.log(model);
const id = uniqueId("notification_");
this.add({
message: (
<>
<b>{model.title}</b>
<p>{model.body}</p>
{renderButtons(id, model.buttons)}
</>
),
id,
status: model.status,
timeout: model.timeout,
onClose: () => {
model.closeChannel && ipcRenderer.send(model.closeChannel);
}
});
})
}
addAutoHideTimer(notification: INotification) {
this.removeAutoHideTimer(notification);
const { id, timeout } = notification;
if (timeout) {
const timer = window.setTimeout(() => this.remove(id), timeout);
this.autoHideTimers.set(id, timer);
}
}
removeAutoHideTimer(notification: INotification) {
const { id } = notification;
if (this.autoHideTimers.has(id)) {
clearTimeout(this.autoHideTimers.get(id));
this.autoHideTimers.delete(id);
}
}
@action
add(notification: INotification) {
if (!notification.id) {
notification.id = uniqueId("notification_");
}
const index = this.notifications.findIndex(item => item.id === notification.id);
if (index > -1) this.notifications.splice(index, 1, notification)
else this.notifications.push(notification);
this.addAutoHideTimer(notification);
}
@action
remove(itemOrId: IMessageId | INotification) {
if (!isObject(itemOrId)) {
itemOrId = this.notifications.find(item => item.id === itemOrId);
}
return this.notifications.remove(itemOrId as INotification);
}
}
export const notificationsStore = new NotificationsStore();