Новая система вложений
This commit is contained in:
@@ -89,7 +89,10 @@ export function DialogInput() {
|
||||
blob: fileContent,
|
||||
id: generateRandomKey(8),
|
||||
type: AttachmentType.FILE,
|
||||
preview: files[0].size + "::" + files[0].name
|
||||
preview: files[0].size + "::" + files[0].name,
|
||||
transport_server: "",
|
||||
transport_tag: "",
|
||||
encoded_for: dialog
|
||||
}]);
|
||||
}
|
||||
});
|
||||
@@ -116,7 +119,10 @@ export function DialogInput() {
|
||||
type: AttachmentType.MESSAGES,
|
||||
id: generateRandomKey(8),
|
||||
blob: JSON.stringify([...replyMessages.messages]),
|
||||
preview: ""
|
||||
preview: "",
|
||||
transport_server: "",
|
||||
transport_tag: "",
|
||||
encoded_for: dialog
|
||||
}]);
|
||||
if(editableDivRef.current){
|
||||
editableDivRef.current.focus();
|
||||
@@ -230,7 +236,10 @@ export function DialogInput() {
|
||||
blob: avatars[0].avatar,
|
||||
id: generateRandomKey(8),
|
||||
type: AttachmentType.AVATAR,
|
||||
preview: ""
|
||||
preview: "",
|
||||
transport_server: "",
|
||||
transport_tag: "",
|
||||
encoded_for: dialog
|
||||
}]);
|
||||
if(editableDivRef.current){
|
||||
editableDivRef.current.focus();
|
||||
@@ -270,7 +279,10 @@ export function DialogInput() {
|
||||
blob: base64Image,
|
||||
id: attachmentId,
|
||||
type: AttachmentType.IMAGE,
|
||||
preview: ""
|
||||
preview: "",
|
||||
transport_server: "",
|
||||
transport_tag: "",
|
||||
encoded_for: dialog
|
||||
}]);
|
||||
}
|
||||
if(editableDivRef.current){
|
||||
@@ -304,7 +316,10 @@ export function DialogInput() {
|
||||
blob: fileContent,
|
||||
id: attachmentId,
|
||||
type: AttachmentType.FILE,
|
||||
preview: files[0].size + "::" + files[0].name
|
||||
preview: files[0].size + "::" + files[0].name,
|
||||
transport_server: "",
|
||||
transport_tag: "",
|
||||
encoded_for: dialog
|
||||
}]);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ export function MessageImage(props: AttachmentProps) {
|
||||
const [blurhashPreview, setBlurhashPreview] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
console.info(props.attachment);
|
||||
console.info("Consturcting image, download status: " + downloadStatus);
|
||||
constructBlob();
|
||||
constructFromBlurhash();
|
||||
|
||||
@@ -27,11 +27,10 @@ export enum DownloadStatus {
|
||||
}
|
||||
|
||||
export function useAttachment(attachment: Attachment, parentMessage: MessageProps) {
|
||||
const uuidRegex = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
||||
const uploadedPercentage = useUploadStatus(attachment.id);
|
||||
const downloadPercentage = useDownloadStatus(attachment.id);
|
||||
const [downloadStatus, setDownloadStatus] = useMemory("attachment-downloaded-status-" + attachment.id, DownloadStatus.PENDING, true);
|
||||
const [downloadTag, setDownloadTag] = useState("");
|
||||
const [downloadTag, setDownloadTag] = useState(attachment.transport_tag || "");
|
||||
const {readFile, writeFile, fileExists, size} = useFileStorage();
|
||||
const { downloadFile } = useTransport();
|
||||
const publicKey = usePublicKey();
|
||||
@@ -50,30 +49,18 @@ export function useAttachment(attachment: Attachment, parentMessage: MessageProp
|
||||
}, []);
|
||||
|
||||
const getPreview = () => {
|
||||
if(attachment.preview.split("::")[0].match(uuidRegex)){
|
||||
/**
|
||||
* Это тег загрузки
|
||||
*/
|
||||
return attachment.preview.split("::").splice(1).join("::");
|
||||
}
|
||||
return attachment.preview;
|
||||
}
|
||||
|
||||
const calcDownloadStatus = async () => {
|
||||
if(attachment.preview.split("::")[0].match(uuidRegex)){
|
||||
/**
|
||||
* Это тег загрузки
|
||||
*/
|
||||
setDownloadTag(attachment.preview.split("::")[0]);
|
||||
}
|
||||
if(!attachment.preview.split("::")[0].match(uuidRegex)){
|
||||
/**
|
||||
* Там не тег загрузки, значит это наш файл
|
||||
*/
|
||||
setDownloadStatus(DownloadStatus.DOWNLOADED);
|
||||
if (downloadStatus == DownloadStatus.DOWNLOADED) {
|
||||
return;
|
||||
}
|
||||
if (downloadStatus == DownloadStatus.DOWNLOADED) {
|
||||
if(attachment.transport_tag == ""){
|
||||
/**
|
||||
* Транспортного тега нет только у сообщений отправленных нами, значит он точно наш
|
||||
*/
|
||||
setDownloadStatus(DownloadStatus.DOWNLOADED);
|
||||
return;
|
||||
}
|
||||
if(attachment.type == AttachmentType.FILE){
|
||||
@@ -143,7 +130,7 @@ export function useAttachment(attachment: Attachment, parentMessage: MessageProp
|
||||
let downloadedBlob = '';
|
||||
try {
|
||||
downloadedBlob = await downloadFile(attachment.id,
|
||||
downloadTag);
|
||||
downloadTag, attachment.transport_server);
|
||||
} catch (e) {
|
||||
console.info(e);
|
||||
info("Error downloading attachment: " + attachment.id);
|
||||
|
||||
@@ -10,6 +10,7 @@ import { useDatabase } from "../DatabaseProvider/useDatabase";
|
||||
import { useConsoleLogger } from "@/app/hooks/useConsoleLogger";
|
||||
import { useDialogsCache } from "../DialogProvider/useDialogsCache";
|
||||
import { DialogContext } from "../DialogProvider/DialogProvider";
|
||||
import { useTransportServer } from "../TransportProvider/useTransportServer";
|
||||
|
||||
export function usePrepareAttachment() {
|
||||
const intervalsRef = useRef<NodeJS.Timeout>(null);
|
||||
@@ -19,6 +20,7 @@ export function usePrepareAttachment() {
|
||||
const {info} = useConsoleLogger('usePrepareAttachment');
|
||||
const {getDialogCache} = useDialogsCache();
|
||||
const context = useContext(DialogContext);
|
||||
const transportServer = useTransportServer();
|
||||
|
||||
const updateTimestampInDialogCache = (dialog : string, message_id: string) => {
|
||||
const dialogCache = getDialogCache(dialog);
|
||||
@@ -74,18 +76,6 @@ export function usePrepareAttachment() {
|
||||
}, (MESSAGE_MAX_TIME_TO_DELEVERED_S / 2) * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Удаляет старый тег если вложения были подготовлены заново
|
||||
* например при пересылке сообщений
|
||||
*/
|
||||
const removeOldTagIfAttachemtnsRePreapred = (preview : string) => {
|
||||
if(preview.indexOf("::") == -1){
|
||||
return preview;
|
||||
}
|
||||
let parts = preview.split("::");
|
||||
return parts.slice(1).join("::");
|
||||
}
|
||||
|
||||
/**
|
||||
* Подготавливает вложения для отправки. Подготовка
|
||||
* состоит в загрузке файлов на транспортный сервер, мы не делаем
|
||||
@@ -127,6 +117,16 @@ export function usePrepareAttachment() {
|
||||
const blurhash = await base64ImageToBlurhash(attachment.blob);
|
||||
attachment.preview = blurhash;
|
||||
}
|
||||
if(rePrepared && attachment.encoded_for == dialog){
|
||||
/**
|
||||
* Это пересланное сообщение и оно уже закодировано для этого диалога, значит не нужно его кодировать и загружать заново
|
||||
*/
|
||||
prepared.push({
|
||||
...attachment,
|
||||
blob: ""
|
||||
});
|
||||
continue;
|
||||
}
|
||||
doTimestampUpdateImMessageWhileAttachmentsSend(message_id, dialog);
|
||||
const content = await encodeWithPassword(password, attachment.blob);
|
||||
const upid = attachment.id;
|
||||
@@ -139,7 +139,10 @@ export function usePrepareAttachment() {
|
||||
}
|
||||
prepared.push({
|
||||
...attachment,
|
||||
preview: tag + "::" + (rePrepared ? removeOldTagIfAttachemtnsRePreapred(attachment.preview) : attachment.preview),
|
||||
transport_server: transportServer || "",
|
||||
transport_tag: tag,
|
||||
encoded_for: dialog,
|
||||
preview: attachment.preview,
|
||||
blob: ""
|
||||
});
|
||||
}
|
||||
|
||||
@@ -472,6 +472,9 @@ export function CallProvider(props : CallProviderProps) {
|
||||
id: generateRandomKey(16),
|
||||
preview: duration.toString(),
|
||||
type: AttachmentType.CALL,
|
||||
transport_server: "",
|
||||
transport_tag: "",
|
||||
encoded_for: "",
|
||||
blob: ""
|
||||
}], true);
|
||||
}
|
||||
|
||||
@@ -46,6 +46,9 @@ export interface AttachmentMeta {
|
||||
id: string;
|
||||
type: AttachmentType;
|
||||
preview: string;
|
||||
transport_tag: string;
|
||||
encoded_for: string;
|
||||
transport_server: string;
|
||||
}
|
||||
|
||||
export interface Message {
|
||||
@@ -469,9 +472,7 @@ export function DialogProvider(props: DialogProviderProps) {
|
||||
for(let i = 0; i < packet.getAttachments().length; i++) {
|
||||
const attachment = packet.getAttachments()[i];
|
||||
attachments.push({
|
||||
id: attachment.id,
|
||||
preview: attachment.preview,
|
||||
type: attachment.type,
|
||||
...attachment,
|
||||
blob: attachment.type == AttachmentType.MESSAGES ? await decodeWithPassword(chachaDecryptedKey.toString('utf-8'), attachment.blob) : ""
|
||||
});
|
||||
}
|
||||
@@ -549,9 +550,7 @@ export function DialogProvider(props: DialogProviderProps) {
|
||||
for(let i = 0; i < packet.getAttachments().length; i++) {
|
||||
const attachment = packet.getAttachments()[i];
|
||||
attachments.push({
|
||||
id: attachment.id,
|
||||
preview: attachment.preview,
|
||||
type: attachment.type,
|
||||
...attachment,
|
||||
blob: attachment.type == AttachmentType.MESSAGES ? await decodeWithPassword(groupKey, attachment.blob) : ""
|
||||
});
|
||||
}
|
||||
@@ -627,9 +626,7 @@ export function DialogProvider(props: DialogProviderProps) {
|
||||
for(let i = 0; i < packet.getAttachments().length; i++) {
|
||||
const attachment = packet.getAttachments()[i];
|
||||
attachments.push({
|
||||
id: attachment.id,
|
||||
preview: attachment.preview,
|
||||
type: attachment.type,
|
||||
...attachment,
|
||||
blob: attachment.type == AttachmentType.MESSAGES ? await decodeWithPassword(chachaDecryptedKey.toString('utf-8'), attachment.blob) : ""
|
||||
});
|
||||
}
|
||||
@@ -707,9 +704,7 @@ export function DialogProvider(props: DialogProviderProps) {
|
||||
for(let i = 0; i < packet.getAttachments().length; i++) {
|
||||
const attachment = packet.getAttachments()[i];
|
||||
attachments.push({
|
||||
id: attachment.id,
|
||||
preview: attachment.preview,
|
||||
type: attachment.type,
|
||||
...attachment,
|
||||
blob: attachment.type == AttachmentType.MESSAGES ? await decodeWithPassword(groupKey, attachment.blob) : ""
|
||||
});
|
||||
}
|
||||
@@ -964,7 +959,10 @@ export function DialogProvider(props: DialogProviderProps) {
|
||||
id: meta.id,
|
||||
blob: blob,
|
||||
type: meta.type,
|
||||
preview: meta.preview
|
||||
preview: meta.preview,
|
||||
transport_server: meta.transport_server,
|
||||
transport_tag: meta.transport_tag,
|
||||
encoded_for: meta.encoded_for
|
||||
});
|
||||
}
|
||||
return attachments;
|
||||
|
||||
@@ -118,7 +118,10 @@ export function useDialog() : {
|
||||
attachmentsMeta.push({
|
||||
id: attachment.id,
|
||||
type: attachment.type,
|
||||
preview: attachment.preview
|
||||
preview: attachment.preview,
|
||||
transport_server: attachment.transport_server,
|
||||
transport_tag: attachment.transport_tag,
|
||||
encoded_for: dialog
|
||||
});
|
||||
if(attachment.type == AttachmentType.FILE){
|
||||
/**
|
||||
|
||||
@@ -20,7 +20,7 @@ import { useGroupInviteStatus } from "./useGroupInviteStatus";
|
||||
import { Attachment, AttachmentType, PacketMessage } from "../ProtocolProvider/protocol/packets/packet.message";
|
||||
import { useUpdateSyncTime } from "./useUpdateSyncTime";
|
||||
import { useFileStorage } from "@/app/hooks/useFileStorage";
|
||||
import { DeliveredMessageState, Message } from "./DialogProvider";
|
||||
import { AttachmentMeta, DeliveredMessageState, Message } from "./DialogProvider";
|
||||
import { MESSAGE_MAX_LOADED, TIME_TO_INACTIVE_FOR_MESSAGES_UNREAD } from "@/app/constants";
|
||||
import { useMemory } from "../MemoryProvider/useMemory";
|
||||
import { useDialogsCache } from "./useDialogsCache";
|
||||
@@ -165,7 +165,7 @@ export function useSynchronize() {
|
||||
const nonce = chachaDecryptedKey.slice(32);
|
||||
const decryptedContent = await chacha20Decrypt(content, nonce.toString('hex'), key.toString('hex'));
|
||||
await updateSyncTime(timestamp);
|
||||
let attachmentsMeta: any[] = [];
|
||||
let attachmentsMeta: AttachmentMeta[] = [];
|
||||
let messageAttachments: Attachment[] = [];
|
||||
for (let i = 0; i < packet.getAttachments().length; i++) {
|
||||
const attachment = packet.getAttachments()[i];
|
||||
@@ -190,7 +190,10 @@ export function useSynchronize() {
|
||||
attachmentsMeta.push({
|
||||
id: attachment.id,
|
||||
type: attachment.type,
|
||||
preview: attachment.preview
|
||||
preview: attachment.preview,
|
||||
encoded_for: attachment.encoded_for,
|
||||
transport_server: attachment.transport_server,
|
||||
transport_tag: attachment.transport_tag
|
||||
});
|
||||
}
|
||||
|
||||
@@ -347,7 +350,7 @@ export function useSynchronize() {
|
||||
decryptedContent = '';
|
||||
}
|
||||
|
||||
let attachmentsMeta: any[] = [];
|
||||
let attachmentsMeta: AttachmentMeta[] = [];
|
||||
let messageAttachments: Attachment[] = [];
|
||||
for (let i = 0; i < packet.getAttachments().length; i++) {
|
||||
const attachment = packet.getAttachments()[i];
|
||||
@@ -372,7 +375,10 @@ export function useSynchronize() {
|
||||
attachmentsMeta.push({
|
||||
id: attachment.id,
|
||||
type: attachment.type,
|
||||
preview: attachment.preview
|
||||
preview: attachment.preview,
|
||||
encoded_for: attachment.encoded_for,
|
||||
transport_server: attachment.transport_server,
|
||||
transport_tag: attachment.transport_tag
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useConsoleLogger } from "@/app/hooks/useConsoleLogger";
|
||||
interface TransportContextValue {
|
||||
transportServer: string | null;
|
||||
uploadFile: (id: string, content: string) => Promise<any>;
|
||||
downloadFile: (id: string, tag: string) => Promise<string>;
|
||||
downloadFile: (id: string, tag: string, transportServer: string) => Promise<string>;
|
||||
uploading: TransportState[];
|
||||
downloading: TransportState[];
|
||||
}
|
||||
@@ -86,14 +86,14 @@ export function TransportProvider(props: TransportProviderProps) {
|
||||
* @param tag тег файла
|
||||
* @param chachaDecryptedKey ключ для расшифровки файла
|
||||
*/
|
||||
const downloadFile = (id: string, tag : string) : Promise<string> => {
|
||||
const downloadFile = (id: string, tag : string, transportServer: string) : Promise<string> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!transportServerRef.current) {
|
||||
if (!transportServer) {
|
||||
throw new Error("Transport server is not set");
|
||||
}
|
||||
setDownloading(prev => [...prev, { id: id, progress: 0 }]);
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', `${transportServerRef.current}/d/${tag}`);
|
||||
xhr.open('GET', `${transportServer}/d/${tag}`);
|
||||
xhr.responseType = 'text';
|
||||
|
||||
xhr.onprogress = (event) => {
|
||||
|
||||
12
app/providers/TransportProvider/useTransportServer.ts
Normal file
12
app/providers/TransportProvider/useTransportServer.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { useContext } from "react";
|
||||
import { TransportContext } from "./TransportProvider";
|
||||
|
||||
export function useTransportServer() {
|
||||
const context = useContext(TransportContext);
|
||||
if(!context){
|
||||
throw new Error("useTransportServer must be used within a TransportProvider");
|
||||
}
|
||||
const { transportServer } = context;
|
||||
|
||||
return transportServer;
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
export const SERVERS = [
|
||||
//'wss://cdn.rosetta-im.com',
|
||||
//'ws://10.211.55.2:3000',
|
||||
'ws://10.211.55.2:3000',
|
||||
//'ws://192.168.6.82:3000',
|
||||
'wss://wss.rosetta.im'
|
||||
//'wss://wss.rosetta.im'
|
||||
];
|
||||
|
||||
export function selectServer(): string {
|
||||
|
||||
181
lib/main/main.ts
181
lib/main/main.ts
@@ -18,109 +18,128 @@ const size = process.platform === 'darwin' ? 18 : 22
|
||||
const logger = Logger('main')
|
||||
|
||||
const icon = nativeImage
|
||||
.createFromPath(join(__dirname, '../../resources/R.png'))
|
||||
.resize({ width: size, height: size })
|
||||
.createFromPath(join(__dirname, '../../resources/R.png'))
|
||||
.resize({ width: size, height: size })
|
||||
|
||||
if (!lockInstance) {
|
||||
app.quit()
|
||||
process.exit(0)
|
||||
app.quit()
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
process.on('unhandledRejection', (reason) => {
|
||||
logger.log(`main thread error, reason: ${reason}`)
|
||||
logger.log(`main thread error, reason: ${reason}`)
|
||||
})
|
||||
|
||||
app.disableHardwareAcceleration()
|
||||
|
||||
app.on('second-instance', () => {
|
||||
const allWindows = BrowserWindow.getAllWindows()
|
||||
if (allWindows.length) {
|
||||
const mainWindow = allWindows[0]
|
||||
if (mainWindow.isMinimized()) mainWindow.restore()
|
||||
if (!mainWindow.isVisible()) mainWindow.show()
|
||||
mainWindow.focus()
|
||||
}
|
||||
const allWindows = BrowserWindow.getAllWindows()
|
||||
if (allWindows.length) {
|
||||
const mainWindow = allWindows[0]
|
||||
if (mainWindow.isMinimized()) mainWindow.restore()
|
||||
if (!mainWindow.isVisible()) mainWindow.show()
|
||||
mainWindow.focus()
|
||||
}
|
||||
})
|
||||
|
||||
export const restoreApplicationAfterClickOnTrayOrDock = () => {
|
||||
const allWindows = BrowserWindow.getAllWindows()
|
||||
if (allWindows.length > 0) {
|
||||
const mainWindow = allWindows[0]
|
||||
if (mainWindow.isMinimized()) {
|
||||
mainWindow.restore()
|
||||
return
|
||||
}
|
||||
if (!mainWindow.isVisible()) {
|
||||
mainWindow.show()
|
||||
}
|
||||
mainWindow.focus()
|
||||
} else {
|
||||
createAppWindow()
|
||||
}
|
||||
const allWindows = BrowserWindow.getAllWindows()
|
||||
if (allWindows.length > 0) {
|
||||
const mainWindow = allWindows[0]
|
||||
if (mainWindow.isMinimized()) {
|
||||
mainWindow.restore()
|
||||
return
|
||||
}
|
||||
if (!mainWindow.isVisible()) {
|
||||
mainWindow.show()
|
||||
}
|
||||
mainWindow.focus()
|
||||
} else {
|
||||
createAppWindow()
|
||||
}
|
||||
}
|
||||
|
||||
app.whenReady().then(async () => {
|
||||
electronApp.setAppUserModelId('Rosetta')
|
||||
electronApp.setAppUserModelId('Rosetta')
|
||||
|
||||
// Убираем File/View и оставляем только app + минимальный Edit (roles)
|
||||
if (process.platform === 'darwin') {
|
||||
const minimalMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: app.name,
|
||||
submenu: [
|
||||
{ role: 'about' },
|
||||
{ type: 'separator' },
|
||||
{ role: 'hide' },
|
||||
{ role: 'hideOthers' },
|
||||
{ role: 'unhide' },
|
||||
{ type: 'separator' },
|
||||
{ role: 'quit' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Edit',
|
||||
submenu: [
|
||||
{ role: 'undo' },
|
||||
{ role: 'redo' },
|
||||
{ type: 'separator' },
|
||||
{ role: 'cut' },
|
||||
{ role: 'copy' },
|
||||
{ role: 'paste' },
|
||||
{ role: 'pasteAndMatchStyle' },
|
||||
{ role: 'delete' },
|
||||
{ role: 'selectAll' }
|
||||
]
|
||||
}
|
||||
])
|
||||
Menu.setApplicationMenu(minimalMenu)
|
||||
} else {
|
||||
Menu.setApplicationMenu(null)
|
||||
}
|
||||
// Убираем File/View и оставляем только app + минимальный Edit (roles)
|
||||
if (process.platform === 'darwin') {
|
||||
const minimalMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: app.name,
|
||||
submenu: [
|
||||
{ role: 'about' },
|
||||
{ type: 'separator' },
|
||||
{ role: 'hide' },
|
||||
{ role: 'hideOthers' },
|
||||
{ role: 'unhide' },
|
||||
{ type: 'separator' },
|
||||
{ role: 'quit' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Edit',
|
||||
submenu: [
|
||||
{ role: 'undo' },
|
||||
{ role: 'redo' },
|
||||
{ type: 'separator' },
|
||||
{ role: 'cut' },
|
||||
{ role: 'copy' },
|
||||
{ role: 'paste' },
|
||||
{ role: 'pasteAndMatchStyle' },
|
||||
{ role: 'delete' },
|
||||
{ role: 'selectAll' }
|
||||
]
|
||||
}
|
||||
])
|
||||
Menu.setApplicationMenu(minimalMenu)
|
||||
} else {
|
||||
Menu.setApplicationMenu(null)
|
||||
}
|
||||
|
||||
tray = new Tray(icon)
|
||||
const contextMenu = Menu.buildFromTemplate([
|
||||
{ label: 'Open App', click: () => restoreApplicationAfterClickOnTrayOrDock() },
|
||||
{ label: 'Quit', click: () => app.quit() }
|
||||
])
|
||||
tray.setContextMenu(contextMenu)
|
||||
tray.setToolTip('Rosetta')
|
||||
tray.on('click', () => {
|
||||
restoreApplicationAfterClickOnTrayOrDock()
|
||||
})
|
||||
tray = new Tray(icon)
|
||||
const contextMenu = Menu.buildFromTemplate([
|
||||
{ label: 'Open App', click: () => restoreApplicationAfterClickOnTrayOrDock() },
|
||||
{ label: 'Quit', click: () => app.quit() }
|
||||
])
|
||||
tray.setContextMenu(contextMenu)
|
||||
tray.setToolTip('Rosetta')
|
||||
tray.on('click', () => {
|
||||
restoreApplicationAfterClickOnTrayOrDock()
|
||||
})
|
||||
|
||||
startApplication()
|
||||
startApplication()
|
||||
|
||||
app.on('browser-window-created', (_, window) => {
|
||||
optimizer.watchWindowShortcuts(window)
|
||||
})
|
||||
const isDevBuild =
|
||||
!app.isPackaged ||
|
||||
process.env.NODE_ENV === 'development' ||
|
||||
Boolean(process.env.ELECTRON_RENDERER_URL)
|
||||
|
||||
app.on('activate', () => {
|
||||
restoreApplicationAfterClickOnTrayOrDock()
|
||||
})
|
||||
app.on('browser-window-created', (_, window) => {
|
||||
// В production оставляем стандартную защиту шорткатов
|
||||
if (!isDevBuild) {
|
||||
optimizer.watchWindowShortcuts(window)
|
||||
return
|
||||
}
|
||||
|
||||
// В dev явно разрешаем Ctrl+R и Cmd+R для перезагрузки, так как в режиме разработки это часто нужно
|
||||
window.webContents.on('before-input-event', (event, input) => {
|
||||
const key = input.key?.toLowerCase?.() ?? ''
|
||||
const isReload = input.type === 'keyDown' && (input.meta || input.control) && key === 'r'
|
||||
if (isReload) {
|
||||
event.preventDefault()
|
||||
window.webContents.reloadIgnoringCache()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
app.on('activate', () => {
|
||||
restoreApplicationAfterClickOnTrayOrDock()
|
||||
})
|
||||
})
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform === 'darwin') {
|
||||
app.hide()
|
||||
}
|
||||
if (process.platform === 'darwin') {
|
||||
app.hide()
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user