mirror of
				https://github.com/zulip/zulip-desktop.git
				synced 2025-11-04 05:53:21 +00:00 
			
		
		
		
	Remove macOS notification inline replies feature.
node-mac-notifier no longer builds on macOS with Electron 11 (error: no template named 'remove_cv_t' in namespace 'std'). It was previously implicated in crashes on macOS (#1016). And we no longer have any macOS developers that seem to be maintaining this feature (e.g. #1022 is stalled). Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
		@@ -9,10 +9,6 @@ interface CompatElectronBridge extends ElectronBridge {
 | 
			
		||||
(() => {
 | 
			
		||||
	const zulipWindow = window as typeof window & {
 | 
			
		||||
		electron_bridge: CompatElectronBridge;
 | 
			
		||||
		narrow?: {
 | 
			
		||||
			by_subject?: (target_id: number, options: {trigger?: string}) => void;
 | 
			
		||||
			by_topic?: (target_id: number, options: {trigger?: string}) => void;
 | 
			
		||||
		};
 | 
			
		||||
		page_params?: unknown;
 | 
			
		||||
		raw_electron_bridge: ElectronBridge;
 | 
			
		||||
	};
 | 
			
		||||
@@ -48,16 +44,9 @@ interface CompatElectronBridge extends ElectronBridge {
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		const {narrow, page_params} = zulipWindow;
 | 
			
		||||
		const {page_params} = zulipWindow;
 | 
			
		||||
		if (page_params !== undefined) {
 | 
			
		||||
			electron_bridge.send_event('zulip-loaded', {
 | 
			
		||||
				narrow_by_topic: (id: number) => {
 | 
			
		||||
					const narrowByTopic = narrow?.by_topic ?? narrow?.by_subject;
 | 
			
		||||
					if (narrowByTopic !== undefined) {
 | 
			
		||||
						narrowByTopic(id, {trigger: 'notification'});
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
			electron_bridge.send_event('zulip-loaded');
 | 
			
		||||
		}
 | 
			
		||||
	})();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,113 +0,0 @@
 | 
			
		||||
import {ipcRenderer} from 'electron';
 | 
			
		||||
 | 
			
		||||
import MacNotifier from 'node-mac-notifier';
 | 
			
		||||
 | 
			
		||||
import electron_bridge from '../electron-bridge';
 | 
			
		||||
import * as ConfigUtil from '../utils/config-util';
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
	appId, customReply, focusCurrentServer, parseReply
 | 
			
		||||
} from './helpers';
 | 
			
		||||
 | 
			
		||||
type ReplyHandler = (response: string) => void;
 | 
			
		||||
type ClickHandler = () => void;
 | 
			
		||||
let replyHandler: ReplyHandler;
 | 
			
		||||
let clickHandler: ClickHandler;
 | 
			
		||||
 | 
			
		||||
interface NotificationHandlerArgs {
 | 
			
		||||
	response: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class DarwinNotification {
 | 
			
		||||
	tag: number;
 | 
			
		||||
 | 
			
		||||
	constructor(title: string, options: NotificationOptions) {
 | 
			
		||||
		const silent: boolean = ConfigUtil.getConfigItem('silent') || false;
 | 
			
		||||
		const {icon} = options;
 | 
			
		||||
		const profilePic = new URL(icon, location.href).href;
 | 
			
		||||
 | 
			
		||||
		this.tag = Number.parseInt(options.tag, 10);
 | 
			
		||||
		const notification = new MacNotifier(title, Object.assign(options, {
 | 
			
		||||
			bundleId: appId,
 | 
			
		||||
			canReply: true,
 | 
			
		||||
			silent,
 | 
			
		||||
			icon: profilePic
 | 
			
		||||
		}));
 | 
			
		||||
 | 
			
		||||
		notification.addEventListener('click', () => {
 | 
			
		||||
			// Focus to the server who sent the
 | 
			
		||||
			// notification if not focused already
 | 
			
		||||
			if (clickHandler) {
 | 
			
		||||
				clickHandler();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			focusCurrentServer();
 | 
			
		||||
			ipcRenderer.send('focus-app');
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		notification.addEventListener('reply', this.notificationHandler);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	static requestPermission(): void {
 | 
			
		||||
		// Do nothing
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Override default Notification permission
 | 
			
		||||
	static get permission(): NotificationPermission {
 | 
			
		||||
		return ConfigUtil.getConfigItem('showNotification') ? 'granted' : 'denied';
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	get onreply(): ReplyHandler {
 | 
			
		||||
		return replyHandler;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	set onreply(handler: ReplyHandler) {
 | 
			
		||||
		replyHandler = handler;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	get onclick(): ClickHandler {
 | 
			
		||||
		return clickHandler;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	set onclick(handler: ClickHandler) {
 | 
			
		||||
		clickHandler = handler;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Not something that is common or
 | 
			
		||||
	// used by zulip server but added to be
 | 
			
		||||
	// future proff.
 | 
			
		||||
	addEventListener(event: string, handler: ClickHandler | ReplyHandler): void {
 | 
			
		||||
		if (event === 'click') {
 | 
			
		||||
			clickHandler = handler as ClickHandler;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (event === 'reply') {
 | 
			
		||||
			replyHandler = handler as ReplyHandler;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	async notificationHandler({response}: NotificationHandlerArgs): Promise<void> {
 | 
			
		||||
		response = await parseReply(response);
 | 
			
		||||
		focusCurrentServer();
 | 
			
		||||
		if (electron_bridge.send_notification_reply_message_supported) {
 | 
			
		||||
			electron_bridge.send_event('send_notification_reply_message', this.tag, response);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		electron_bridge.emit('narrow-by-topic', this.tag);
 | 
			
		||||
		if (replyHandler) {
 | 
			
		||||
			replyHandler(response);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		customReply(response);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Method specific to notification api
 | 
			
		||||
	// used by zulip
 | 
			
		||||
	close(): void {
 | 
			
		||||
		// Do nothing
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = DarwinNotification;
 | 
			
		||||
@@ -1,69 +1,8 @@
 | 
			
		||||
import {remote} from 'electron';
 | 
			
		||||
 | 
			
		||||
import Logger from '../utils/logger-util';
 | 
			
		||||
 | 
			
		||||
const logger = new Logger({
 | 
			
		||||
	file: 'errors.log',
 | 
			
		||||
	timestamp: true
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Do not change this
 | 
			
		||||
export const appId = 'org.zulip.zulip-electron';
 | 
			
		||||
 | 
			
		||||
export type BotListItem = [string, string];
 | 
			
		||||
const botsList: BotListItem[] = [];
 | 
			
		||||
let botsListLoaded = false;
 | 
			
		||||
 | 
			
		||||
// This function load list of bots from the server
 | 
			
		||||
// in case botsList isn't already completely loaded when required in parseRely
 | 
			
		||||
export async function loadBots(): Promise<void> {
 | 
			
		||||
	botsList.length = 0;
 | 
			
		||||
	const response = await fetch('/json/users');
 | 
			
		||||
	if (response.ok) {
 | 
			
		||||
		const {members} = await response.json();
 | 
			
		||||
		members.forEach(({is_bot, full_name}: Record<string, unknown>) => {
 | 
			
		||||
			if (is_bot && typeof full_name === 'string') {
 | 
			
		||||
				const bot = `@${full_name}`;
 | 
			
		||||
				const mention = `@**${bot.replace(/^@/, '')}**`;
 | 
			
		||||
				botsList.push([bot, mention]);
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
		botsListLoaded = true;
 | 
			
		||||
	} else {
 | 
			
		||||
		logger.log('Load bots request failed: ', await response.text());
 | 
			
		||||
		logger.log('Load bots request status: ', response.status);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function checkElements(...elements: unknown[]): boolean {
 | 
			
		||||
	let status = true;
 | 
			
		||||
	elements.forEach(element => {
 | 
			
		||||
		if (element === null || element === undefined) {
 | 
			
		||||
			status = false;
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
	return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function customReply(reply: string): void {
 | 
			
		||||
	// Server does not support notification reply yet.
 | 
			
		||||
	const buttonSelector = '.messagebox #send_controls button[type=submit]';
 | 
			
		||||
	const messageboxSelector = '.selected_message .messagebox .messagebox-border .messagebox-content';
 | 
			
		||||
	const textarea: HTMLInputElement = document.querySelector('#compose-textarea');
 | 
			
		||||
	const messagebox: HTMLButtonElement = document.querySelector(messageboxSelector);
 | 
			
		||||
	const sendButton: HTMLButtonElement = document.querySelector(buttonSelector);
 | 
			
		||||
 | 
			
		||||
	// Sanity check for old server versions
 | 
			
		||||
	const elementsExists = checkElements(textarea, messagebox, sendButton);
 | 
			
		||||
	if (!elementsExists) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	textarea.value = reply;
 | 
			
		||||
	messagebox.click();
 | 
			
		||||
	sendButton.click();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const currentWindow = remote.getCurrentWindow();
 | 
			
		||||
const webContents = remote.getCurrentWebContents();
 | 
			
		||||
const webContentsId = webContents.id;
 | 
			
		||||
@@ -73,65 +12,3 @@ const webContentsId = webContents.id;
 | 
			
		||||
export function focusCurrentServer(): void {
 | 
			
		||||
	currentWindow.webContents.send('focus-webview-with-id', webContentsId);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// This function parses the reply from to notification
 | 
			
		||||
// making it easier to reply from notification eg
 | 
			
		||||
// @username in reply will be converted to @**username**
 | 
			
		||||
// #stream in reply will be converted to #**stream**
 | 
			
		||||
// bot mentions are not yet supported
 | 
			
		||||
export async function parseReply(reply: string): Promise<string> {
 | 
			
		||||
	const usersDiv = document.querySelectorAll('#user_presences li');
 | 
			
		||||
	const streamHolder = document.querySelectorAll('#stream_filters li');
 | 
			
		||||
 | 
			
		||||
	type UsersItem = BotListItem;
 | 
			
		||||
	type StreamsItem = BotListItem;
 | 
			
		||||
	const users: UsersItem[] = [];
 | 
			
		||||
	const streams: StreamsItem[] = [];
 | 
			
		||||
 | 
			
		||||
	usersDiv.forEach(userRow => {
 | 
			
		||||
		const anchor = userRow.querySelector('span a');
 | 
			
		||||
		if (anchor !== null) {
 | 
			
		||||
			const user = `@${anchor.textContent.trim()}`;
 | 
			
		||||
			const mention = `@**${user.replace(/^@/, '')}**`;
 | 
			
		||||
			users.push([user, mention]);
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	streamHolder.forEach(stream => {
 | 
			
		||||
		const streamAnchor = stream.querySelector('div a');
 | 
			
		||||
		if (streamAnchor !== null) {
 | 
			
		||||
			const streamName = `#${streamAnchor.textContent.trim()}`;
 | 
			
		||||
			const streamMention = `#**${streamName.replace(/^#/, '')}**`;
 | 
			
		||||
			streams.push([streamName, streamMention]);
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	users.forEach(([user, mention]) => {
 | 
			
		||||
		if (reply.includes(user)) {
 | 
			
		||||
			const regex = new RegExp(user, 'g');
 | 
			
		||||
			reply = reply.replace(regex, mention);
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	streams.forEach(([stream, streamMention]) => {
 | 
			
		||||
		const regex = new RegExp(stream, 'g');
 | 
			
		||||
		reply = reply.replace(regex, streamMention);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	// If botsList isn't completely loaded yet, make a request for list
 | 
			
		||||
	if (!botsListLoaded) {
 | 
			
		||||
		await loadBots();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Iterate for every bot name and replace in reply
 | 
			
		||||
	// @botname with @**botname**
 | 
			
		||||
	botsList.forEach(([bot, mention]) => {
 | 
			
		||||
		if (reply.includes(bot)) {
 | 
			
		||||
			const regex = new RegExp(bot, 'g');
 | 
			
		||||
			reply = reply.replace(regex, mention);
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	reply = reply.replace(/\\n/, '\n');
 | 
			
		||||
	return reply;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -9,12 +9,6 @@ const {app} = remote;
 | 
			
		||||
// On windows 8 we have to explicitly set the appUserModelId otherwise notification won't work.
 | 
			
		||||
app.setAppUserModelId(appId);
 | 
			
		||||
 | 
			
		||||
let Notification = DefaultNotification;
 | 
			
		||||
 | 
			
		||||
if (process.platform === 'darwin') {
 | 
			
		||||
	Notification = require('./darwin-notifications');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface NotificationData {
 | 
			
		||||
	close: () => void;
 | 
			
		||||
	title: string;
 | 
			
		||||
@@ -39,7 +33,7 @@ export function newNotification(
 | 
			
		||||
	options: NotificationOptions | undefined,
 | 
			
		||||
	dispatch: (type: string, eventInit: EventInit) => boolean
 | 
			
		||||
): NotificationData {
 | 
			
		||||
	const notification = new Notification(title, options);
 | 
			
		||||
	const notification = new DefaultNotification(title, options);
 | 
			
		||||
	for (const type of ['click', 'close', 'error', 'show']) {
 | 
			
		||||
		notification.addEventListener(type, (ev: Event) => {
 | 
			
		||||
			if (!dispatch(type, ev)) {
 | 
			
		||||
 
 | 
			
		||||
@@ -4,15 +4,10 @@ import fs from 'fs';
 | 
			
		||||
import isDev from 'electron-is-dev';
 | 
			
		||||
 | 
			
		||||
import electron_bridge from './electron-bridge';
 | 
			
		||||
import {loadBots} from './notification/helpers';
 | 
			
		||||
import * as NetworkError from './pages/network';
 | 
			
		||||
 | 
			
		||||
contextBridge.exposeInMainWorld('raw_electron_bridge', electron_bridge);
 | 
			
		||||
 | 
			
		||||
electron_bridge.once('zulip-loaded', async () => {
 | 
			
		||||
	await loadBots();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
ipcRenderer.on('logout', () => {
 | 
			
		||||
	// Create the menu for the below
 | 
			
		||||
	const dropdown: HTMLElement = document.querySelector('.dropdown-toggle');
 | 
			
		||||
@@ -51,7 +46,7 @@ ipcRenderer.on('show-notification-settings', () => {
 | 
			
		||||
	}, 100);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
electron_bridge.once('zulip-loaded', ({narrow_by_topic}) => {
 | 
			
		||||
electron_bridge.once('zulip-loaded', () => {
 | 
			
		||||
	// Redirect users to network troubleshooting page
 | 
			
		||||
	const getRestartButton = document.querySelector('.restart_get_events_button');
 | 
			
		||||
	if (getRestartButton) {
 | 
			
		||||
@@ -59,8 +54,6 @@ electron_bridge.once('zulip-loaded', ({narrow_by_topic}) => {
 | 
			
		||||
			ipcRenderer.send('forward-message', 'reload-viewer');
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	electron_bridge.on('narrow-by-topic', narrow_by_topic);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
window.addEventListener('load', (event: any): void => {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										26
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										26
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -1848,6 +1848,7 @@
 | 
			
		||||
      "version": "1.5.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
 | 
			
		||||
      "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "optional": true,
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "file-uri-to-path": "1.0.0"
 | 
			
		||||
@@ -4766,12 +4767,6 @@
 | 
			
		||||
      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
 | 
			
		||||
      "dev": true
 | 
			
		||||
    },
 | 
			
		||||
    "event-target-shim": {
 | 
			
		||||
      "version": "1.1.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-1.1.1.tgz",
 | 
			
		||||
      "integrity": "sha1-qG5e5r2qFgVEddp5fM3fDFVphJE=",
 | 
			
		||||
      "optional": true
 | 
			
		||||
    },
 | 
			
		||||
    "events": {
 | 
			
		||||
      "version": "3.2.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz",
 | 
			
		||||
@@ -5153,6 +5148,7 @@
 | 
			
		||||
      "version": "1.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
 | 
			
		||||
      "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "optional": true
 | 
			
		||||
    },
 | 
			
		||||
    "filelist": {
 | 
			
		||||
@@ -8029,7 +8025,9 @@
 | 
			
		||||
    "nan": {
 | 
			
		||||
      "version": "2.14.2",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
 | 
			
		||||
      "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ=="
 | 
			
		||||
      "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "optional": true
 | 
			
		||||
    },
 | 
			
		||||
    "nanomatch": {
 | 
			
		||||
      "version": "1.2.13",
 | 
			
		||||
@@ -8133,17 +8131,6 @@
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node-mac-notifier": {
 | 
			
		||||
      "version": "1.2.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/node-mac-notifier/-/node-mac-notifier-1.2.0.tgz",
 | 
			
		||||
      "integrity": "sha512-+9FZ01BbPMv3pQVRWgPlaIKbhQl35Pn3WmRg96zIrCJHb4XvClnAqc0+aPfHrWs8o1PYMAQFeYK5tF69ljkKQw==",
 | 
			
		||||
      "optional": true,
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "bindings": "^1.2.1",
 | 
			
		||||
        "event-target-shim": "^1.1.1",
 | 
			
		||||
        "uuid": "^3.3.2"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node-preload": {
 | 
			
		||||
      "version": "0.2.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz",
 | 
			
		||||
@@ -11872,7 +11859,8 @@
 | 
			
		||||
    "uuid": {
 | 
			
		||||
      "version": "3.4.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
 | 
			
		||||
      "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
 | 
			
		||||
      "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
 | 
			
		||||
      "dev": true
 | 
			
		||||
    },
 | 
			
		||||
    "v8-compile-cache": {
 | 
			
		||||
      "version": "2.2.0",
 | 
			
		||||
 
 | 
			
		||||
@@ -160,13 +160,9 @@
 | 
			
		||||
    "get-stream": "^6.0.0",
 | 
			
		||||
    "i18n": "^0.13.2",
 | 
			
		||||
    "iso-639-1": "^2.1.4",
 | 
			
		||||
    "nan": "^2.14.2",
 | 
			
		||||
    "node-json-db": "^1.1.0",
 | 
			
		||||
    "semver": "^7.3.4"
 | 
			
		||||
  },
 | 
			
		||||
  "optionalDependencies": {
 | 
			
		||||
    "node-mac-notifier": "^1.1.0"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@types/adm-zip": "^0.4.33",
 | 
			
		||||
    "@types/auto-launch": "^5.0.1",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								typings.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								typings.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -21,8 +21,6 @@ declare module '@electron-elements/send-feedback' {
 | 
			
		||||
 | 
			
		||||
declare module 'electron-connect';
 | 
			
		||||
 | 
			
		||||
declare module 'node-mac-notifier';
 | 
			
		||||
 | 
			
		||||
declare module '@yaireo/tagify';
 | 
			
		||||
 | 
			
		||||
interface ClipboardDecrypter {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user