diff --git a/.gitea/workflows/windows.yaml b/.gitea/workflows/windows.yaml index cc000e5..e4095df 100644 --- a/.gitea/workflows/windows.yaml +++ b/.gitea/workflows/windows.yaml @@ -10,8 +10,6 @@ on: paths: - 'lib/**' - - jobs: build: runs-on: windows-latest diff --git a/app/components/DialogsList/DialogsList.tsx b/app/components/DialogsList/DialogsList.tsx index 275a1bc..97c8ab2 100644 --- a/app/components/DialogsList/DialogsList.tsx +++ b/app/components/DialogsList/DialogsList.tsx @@ -4,7 +4,7 @@ import animationData from './lottie.json'; import { Box, Flex, Skeleton, Text } from "@mantine/core"; import { useDialogsList } from "@/app/providers/DialogListProvider/useDialogsList"; import { GroupDialog } from "../GroupDialog/GroupDialog"; -import React from "react"; +import { AnimatePresence, motion } from "framer-motion"; interface DialogsListProps { mode: 'all' | 'requests'; @@ -13,6 +13,7 @@ interface DialogsListProps { export function DialogsList(props : DialogsListProps) { const {dialogs, loadingDialogs} = useDialogsList(); + const filteredDialogs = dialogs.filter(v => (v.is_request == (props.mode == 'requests'))); return ( <> @@ -36,21 +37,30 @@ export function DialogsList(props : DialogsListProps) { ))} )} - {loadingDialogs === 0 && dialogs.filter(v => (v.is_request == (props.mode == 'requests'))).map((dialog) => ( - - {dialog.dialog_id.startsWith('#group:') ? ( - - ) : ( - - )} - - ))} + + + {loadingDialogs === 0 && filteredDialogs.map((dialog) => ( + + {dialog.dialog_id.startsWith('#group:') ? ( + + ) : ( + + )} + + ))} + + ); } \ No newline at end of file diff --git a/app/components/DialogsPanelHeader/DialogsPanelHeader.tsx b/app/components/DialogsPanelHeader/DialogsPanelHeader.tsx index 04553c9..963aa26 100644 --- a/app/components/DialogsPanelHeader/DialogsPanelHeader.tsx +++ b/app/components/DialogsPanelHeader/DialogsPanelHeader.tsx @@ -7,14 +7,16 @@ import { useHotkeys } from "@mantine/hooks"; import { useNavigate } from "react-router-dom"; import { usePublicKey } from "@/app/providers/AccountProvider/usePublicKey"; import { DialogHeaderText } from "../DialogHeaderText/DialogHeaderText"; +import { useCoreDevice } from "@/app/providers/DeviceProvider/useCoreDevice"; export function DialogsPanelHeader() { const colors = useRosettaColors(); const logout = useLogout(); const navigate = useNavigate(); const publicKey = usePublicKey(); - const viewKeys = window.platform == 'darwin' ? '⌘' : 'Ctrl+'; - const triggerKeys = window.platform == 'darwin' ? 'mod' : 'Ctrl'; + const {platform} = useCoreDevice(); + const viewKeys = platform == 'darwin' ? '⌘' : 'Ctrl+'; + const triggerKeys = platform == 'darwin' ? 'mod' : 'Ctrl'; useHotkeys([ [`${triggerKeys}+L`, () => logout()], diff --git a/app/components/MessageAttachments/MessageAttachments.tsx b/app/components/MessageAttachments/MessageAttachments.tsx index 44c7714..6446d1e 100644 --- a/app/components/MessageAttachments/MessageAttachments.tsx +++ b/app/components/MessageAttachments/MessageAttachments.tsx @@ -42,6 +42,7 @@ export function MessageAttachments(props: MessageAttachmentsProps) { text: props.text, parent: props.parent, } + console.info("Rendering attachment", attachProps); switch (att.type) { case AttachmentType.MESSAGES: return diff --git a/app/components/MessageAttachments/MessageAvatar.tsx b/app/components/MessageAttachments/MessageAvatar.tsx index 99b1e01..561cb21 100644 --- a/app/components/MessageAttachments/MessageAvatar.tsx +++ b/app/components/MessageAttachments/MessageAvatar.tsx @@ -19,7 +19,7 @@ export function MessageAvatar(props: AttachmentProps) { download, downloadStatus, getBlob, - getPreview} = useAttachment(props.attachment, props.chacha_key_plain); + getPreview} = useAttachment(props.attachment, props.parent); const mainRef = useRef(null); const { open } = useImageViewer(); const preview = getPreview(); diff --git a/app/components/MessageAttachments/MessageFile.tsx b/app/components/MessageAttachments/MessageFile.tsx index fb4e26f..e17a5d6 100644 --- a/app/components/MessageAttachments/MessageFile.tsx +++ b/app/components/MessageAttachments/MessageFile.tsx @@ -6,6 +6,7 @@ import { IconArrowDown, IconFile, IconX } from "@tabler/icons-react"; import { dotCenterIfNeeded, humanFilesize } from "@/app/utils/utils"; import { AnimatedRoundedProgress } from "../AnimatedRoundedProgress/AnimatedRoundedProgress"; import { DeliveredMessageState } from "@/app/providers/DialogProvider/DialogProvider"; +import { useCore } from "@/app/hooks/useCore"; export function MessageFile(props : AttachmentProps) { const colors = useRosettaColors(); @@ -18,7 +19,7 @@ export function MessageFile(props : AttachmentProps) { } = useAttachment( props.attachment, - props.chacha_key_plain, + props.parent, ); const preview = getPreview(); const error = downloadStatus == DownloadStatus.ERROR; @@ -27,15 +28,15 @@ export function MessageFile(props : AttachmentProps) { const filetype = filename.split(".")[filename.split(".").length - 1]; const isEncrypting = props.delivered == DeliveredMessageState.WAITING && uploadedPercentage <= 0; const isUploading = props.delivered == DeliveredMessageState.WAITING && uploadedPercentage > 0 && uploadedPercentage < 100; + const {getDownloadsPath} = useCore(); const onClick = async () => { if(downloadStatus == DownloadStatus.ERROR){ return; } if(downloadStatus == DownloadStatus.DOWNLOADED){ - //let content = await getBlob(); - //let buffer = Buffer.from(content.split(",")[1], 'base64'); - let pathInDownloads = window.downloadsPath + "/Rosetta Downloads/" + filename; + const downloadsPath = await getDownloadsPath(); + let pathInDownloads = downloadsPath + "/Rosetta Downloads/" + filename; //await writeFile(pathInDownloads, buffer, false); window.shell.showItemInFolder(pathInDownloads); return; diff --git a/app/components/MessageAttachments/MessageImage.tsx b/app/components/MessageAttachments/MessageImage.tsx index 77f8ef2..695fa7d 100644 --- a/app/components/MessageAttachments/MessageImage.tsx +++ b/app/components/MessageAttachments/MessageImage.tsx @@ -19,7 +19,7 @@ export function MessageImage(props: AttachmentProps) { download, downloadStatus, getBlob, - getPreview } = useAttachment(props.attachment, props.chacha_key_plain); + getPreview } = useAttachment(props.attachment, props.parent); const mainRef = useRef(null); const error = downloadStatus == DownloadStatus.ERROR; const { open } = useImageViewer(); @@ -29,6 +29,7 @@ export function MessageImage(props: AttachmentProps) { const [blurhashPreview, setBlurhashPreview] = useState(""); useEffect(() => { + console.info("Consturcting image, download status: " + downloadStatus); constructBlob(); constructFromBlurhash(); }, [downloadStatus]); diff --git a/app/components/Messages/Messages.tsx b/app/components/Messages/Messages.tsx index e08df20..970a9b3 100644 --- a/app/components/Messages/Messages.tsx +++ b/app/components/Messages/Messages.tsx @@ -21,6 +21,7 @@ export function Messages() { const isFirstRenderRef = useRef(true); const previousScrollHeightRef = useRef(0); const distanceFromButtomRef = useRef(0); + const distanceFromTopRef = useRef(0); const [affix, setAffix] = useState(false); const [wallpaper] = useSetting @@ -120,7 +121,8 @@ export function Messages() { const lastMessage = messages[messages.length - 1]; // Скроллим если пользователь внизу или это его собственное сообщение - if ((shouldAutoScrollRef.current || lastMessage.from_me)) { + if ((shouldAutoScrollRef.current || lastMessage.from_me) && distanceFromTopRef.current > 10) { + console.info(distanceFromTopRef.current); scrollToBottom(true); } }, [messages.length, loading, scrollToBottom]); @@ -175,6 +177,8 @@ export function Messages() { onScrollPositionChange={(scroll) => { if (!viewportRef.current) return; + distanceFromTopRef.current = scroll.y; + // Загружаем старые сообщения при достижении верха if (scroll.y === 0 && !loading && messages.length >= 20) { loadMessagesToScrollAreaTop(); diff --git a/app/components/Topbar/Topbar.tsx b/app/components/Topbar/Topbar.tsx index 63590f9..c4006bf 100644 --- a/app/components/Topbar/Topbar.tsx +++ b/app/components/Topbar/Topbar.tsx @@ -5,17 +5,19 @@ import { useProtocolState } from "@/app/providers/ProtocolProvider/useProtocolSt import { ProtocolState } from "@/app/providers/ProtocolProvider/ProtocolProvider"; import { WindowsFrameButtons } from "../WindowsFrameButtons/WindowsFrameButtons"; import { MacFrameButtons } from "../MacFrameButtons/MacFrameButtons"; +import { useCoreDevice } from "@/app/providers/DeviceProvider/useCoreDevice"; export function Topbar() { const colors = useRosettaColors(); const [protocolState] = useProtocolState(); + const {platform} = useCoreDevice(); return ( - {window.platform == 'win32' && } - {window.platform == 'darwin' && } - {window.platform == 'linux' && } + {platform == 'win32' && } + {platform == 'darwin' && } + {platform == 'linux' && } {(protocolState == ProtocolState.CONNECTED || protocolState == ProtocolState.SYNCHRONIZATION || !window.location.hash.includes("main")) && diff --git a/app/constants.ts b/app/constants.ts index 6e96d3c..bba5292 100644 --- a/app/constants.ts +++ b/app/constants.ts @@ -1,13 +1,7 @@ import { AttachmentType } from "./providers/ProtocolProvider/protocol/packets/packet.message"; - -export const CORE_VERSION = window.version || "1.0.0"; - /** * Application directives */ -export const APPLICATION_PLATFROM = window.platform || "unknown"; -export const APPLICATION_ARCH = window.arch || "unknown"; -export const APP_PATH = window.appPath || "."; export const SIZE_LOGIN_WIDTH_PX = 300; export const DEVTOOLS_CHEATCODE = "rosettadev1"; export const AVATAR_PASSWORD_TO_ENCODE = "rosetta-a"; @@ -62,5 +56,6 @@ export const ALLOWED_DOMAINS_ZONES = [ 'gg', 'fm', 'tv', - 'im' + 'im', + 'sc' ]; \ No newline at end of file diff --git a/app/hooks/useCore.ts b/app/hooks/useCore.ts new file mode 100644 index 0000000..6f6f470 --- /dev/null +++ b/app/hooks/useCore.ts @@ -0,0 +1,62 @@ +export function useCore() { + const openExternal = (url: string) => { + window.shell.openExternal(url); + }; + + const showItemInFolder = (fullPath: string) => { + window.shell.showItemInFolder(fullPath); + }; + + const getCoreVersion = async () => { + const version = await window.electron.ipcRenderer.invoke('ipcCore:getCoreVersion'); + return version; + } + + const getArch = async () => { + const arch = await window.electron.ipcRenderer.invoke('ipcCore:getArch'); + return arch; + } + + const getUserDir = async () => { + const userDir = await window.electron.ipcRenderer.invoke('ipcCore:getUserDir'); + return userDir; + } + + const getAppPath = async () => { + const appPath = await window.electron.ipcRenderer.invoke('ipcCore:getAppPath'); + return appPath; + } + + const getDownloadsPath = async () => { + const downloadsPath = await window.electron.ipcRenderer.invoke('ipcCore:getDownloadsPath'); + return downloadsPath; + } + + const getPlatform = async () => { + const platform = await window.electron.ipcRenderer.invoke('ipcCore:getPlatform'); + return platform; + } + + const getDeviceName = async () => { + const deviceName = await window.electron.ipcRenderer.invoke('device:name'); + return deviceName; + } + + const getDeviceId = async () => { + const deviceId = await window.electron.ipcRenderer.invoke('device:id'); + return deviceId; + } + + return { + openExternal, + showItemInFolder, + getCoreVersion, + getArch, + getUserDir, + getAppPath, + getDownloadsPath, + getPlatform, + getDeviceName, + getDeviceId + } +} \ No newline at end of file diff --git a/app/hooks/useSetup.ts b/app/hooks/useSetup.ts deleted file mode 100644 index e69de29..0000000 diff --git a/app/providers/AttachmentProvider/useAttachment.ts b/app/providers/AttachmentProvider/useAttachment.ts index d153d60..d5d88d2 100644 --- a/app/providers/AttachmentProvider/useAttachment.ts +++ b/app/providers/AttachmentProvider/useAttachment.ts @@ -1,4 +1,4 @@ -import { useContext, useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import { useDownloadStatus } from "../TransportProvider/useDownloadStatus"; import { useUploadStatus } from "../TransportProvider/useUploadStatus"; import { useFileStorage } from "../../hooks/useFileStorage"; @@ -10,10 +10,11 @@ import { useDialogsCache } from "../DialogProvider/useDialogsCache"; import { useConsoleLogger } from "../../hooks/useConsoleLogger"; import { Attachment, AttachmentType } from "../ProtocolProvider/protocol/packets/packet.message"; import { useMemory } from "../MemoryProvider/useMemory"; -import { DialogContext } from "../DialogProvider/DialogProvider"; import { useSaveAvatar } from "../AvatarProvider/useSaveAvatar"; import { AVATAR_PASSWORD_TO_ENCODE } from "@/app/constants"; import { useDialog } from "../DialogProvider/useDialog"; +import { useCore } from "@/app/hooks/useCore"; +import { MessageProps } from "@/app/components/Messages/Message"; export enum DownloadStatus { DOWNLOADED, @@ -24,7 +25,7 @@ export enum DownloadStatus { ERROR } -export function useAttachment(attachment: Attachment, keyPlain: string) { +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); @@ -37,13 +38,8 @@ export function useAttachment(attachment: Attachment, keyPlain: string) { const {updateAttachmentInDialogCache} = useDialogsCache(); const {info} = useConsoleLogger('useAttachment'); const {updateAttachmentsInMessagesByAttachmentId} = useDialog(); - + const {getDownloadsPath} = useCore(); - const context = useContext(DialogContext); - if(!context) { - throw new Error("useAttachment must be used within a DialogProvider"); - } - const {dialog} = context; const saveAvatar = useSaveAvatar(); useEffect(() => { @@ -85,7 +81,8 @@ export function useAttachment(attachment: Attachment, keyPlain: string) { const preview = getPreview(); const filesize = parseInt(preview.split("::")[0]); const filename = preview.split("::")[1]; - let pathInDownloads = window.downloadsPath + "/Rosetta Downloads/" + filename; + const downloadsPath = await getDownloadsPath(); + let pathInDownloads = downloadsPath + "/Rosetta Downloads/" + filename; const exists = await fileExists(pathInDownloads, false); const existsLength = await size(pathInDownloads, false); if(exists && existsLength == filesize){ @@ -152,7 +149,7 @@ export function useAttachment(attachment: Attachment, keyPlain: string) { } setDownloadStatus(DownloadStatus.DECRYPTING); //console.info("Decrypted attachment ", Buffer.from(keyPlain, 'binary').toString('hex')); - const decrypted = await decodeWithPassword(keyPlain, downloadedBlob); + const decrypted = await decodeWithPassword(parentMessage.chacha_key_plain, downloadedBlob); setDownloadTag(""); if(attachment.type == AttachmentType.FILE) { /** @@ -161,8 +158,9 @@ export function useAttachment(attachment: Attachment, keyPlain: string) { */ const preview = getPreview(); const filename = preview.split("::")[1]; + const downloadsPath = await getDownloadsPath(); let buffer = Buffer.from(decrypted.split(",")[1], 'base64'); - let pathInDownloads = window.downloadsPath + "/Rosetta Downloads/" + filename; + let pathInDownloads = downloadsPath + "/Rosetta Downloads/" + filename; /** * Пишем файл в загрузки, но перед этим выбираем ему название, если файл в загрузках * уже есть с таким названием то добавляем к названию (1), (2) и так далее, чтобы не перезаписать существующий файл @@ -170,7 +168,7 @@ export function useAttachment(attachment: Attachment, keyPlain: string) { let finalPath = pathInDownloads; let fileIndex = 1; while (await fileExists(finalPath, false)) { - finalPath = window.downloadsPath + "/Rosetta Downloads/" + filename.split(".").slice(0, -1).join(".") + ` (${fileIndex})` + "." + filename.split(".").slice(-1); + finalPath = downloadsPath + "/Rosetta Downloads/" + filename.split(".").slice(0, -1).join(".") + ` (${fileIndex})` + "." + filename.split(".").slice(-1); fileIndex++; } await writeFile(finalPath, buffer, false); @@ -185,7 +183,10 @@ export function useAttachment(attachment: Attachment, keyPlain: string) { await writeFile(avatarPath, Buffer.from(await encodeWithPassword(AVATAR_PASSWORD_TO_ENCODE, decrypted))); setDownloadStatus(DownloadStatus.DOWNLOADED); - saveAvatar(dialog, avatarPath, decrypted); + /** + * Устанавливаем аватарку тому, кто ее прислал. + */ + saveAvatar(parentMessage.from, avatarPath, decrypted); return; } /** diff --git a/app/providers/AvatarProvider/AvatarProvider.tsx b/app/providers/AvatarProvider/AvatarProvider.tsx index 0ef4274..ba8b5e4 100644 --- a/app/providers/AvatarProvider/AvatarProvider.tsx +++ b/app/providers/AvatarProvider/AvatarProvider.tsx @@ -7,7 +7,15 @@ import { useConsoleLogger } from "@/app/hooks/useConsoleLogger"; import { useSystemAccounts } from "../SystemAccountsProvider/useSystemAccounts"; import { AVATAR_PASSWORD_TO_ENCODE } from "@/app/constants"; -export const AvatarContext = createContext({}); +export interface AvatarProviderContextValue { + deliveredAvatars: string[]; + saveAvatar: (fromPublicKey: string, path : string, decryptedContent : string) => Promise; + loadAvatarsFromCacheByPublicKey: (publicKey : string, allDecode? : boolean) => Promise; + changeAvatar: (base64Image : string, entity : string) => Promise; + decodedAvatarsCache: AvatarCacheEntry[]; +} + +export const AvatarContext = createContext(null); interface AvatarProviderProps { children: React.ReactNode; diff --git a/app/providers/AvatarProvider/useSaveAvatar.ts b/app/providers/AvatarProvider/useSaveAvatar.ts index b9ba0db..1177f69 100644 --- a/app/providers/AvatarProvider/useSaveAvatar.ts +++ b/app/providers/AvatarProvider/useSaveAvatar.ts @@ -1,9 +1,9 @@ import { useContext } from "react"; import { AvatarContext } from "./AvatarProvider"; -export function useSaveAvatar() { +export function useSaveAvatar() : (fromPublicKey: string, path : string, decryptedContent : string) => Promise { const context : any = useContext(AvatarContext); - if(!context){ + if(!context){ throw new Error("useSaveAvatar must be used within an AvatarProvider"); } return context.saveAvatar; diff --git a/app/providers/DeviceProvider/useCoreDevice.ts b/app/providers/DeviceProvider/useCoreDevice.ts new file mode 100644 index 0000000..c57b8c2 --- /dev/null +++ b/app/providers/DeviceProvider/useCoreDevice.ts @@ -0,0 +1,33 @@ +import { useCore } from "@/app/hooks/useCore"; +import { useEffect, useState } from "react"; + +export function useCoreDevice() : { + deviceId: string; + deviceName: string; + platform: string; +} { + const { getDeviceId, getDeviceName, getPlatform } = useCore(); + const [deviceId, setDeviceId] = useState(""); + const [deviceName, setDeviceName] = useState(""); + const [platform, setPlatform] = useState(""); + + useEffect(() => { + fetchDeviceInfo(); + }, []); + + const fetchDeviceInfo = async () => { + const deviceId = await getDeviceId(); + const deviceName = await getDeviceName(); + const platform = await getPlatform(); + setDeviceId(deviceId); + setDeviceName(deviceName); + setPlatform(platform); + console.info("Device info - ID:", deviceId, "Name:", deviceName); + } + + return { + deviceId, + deviceName, + platform + } +} \ No newline at end of file diff --git a/app/providers/DialogProvider/DialogProvider.tsx b/app/providers/DialogProvider/DialogProvider.tsx index d5fe8b7..38affed 100644 --- a/app/providers/DialogProvider/DialogProvider.tsx +++ b/app/providers/DialogProvider/DialogProvider.tsx @@ -840,10 +840,13 @@ export function DialogProvider(props: DialogProviderProps) { }); continue; } - const decrypted = await decodeWithPassword(privatePlain, Buffer.from(fileData, 'binary').toString()); + let blob = ""; + if(meta.type != AttachmentType.IMAGE){ + blob = await decodeWithPassword(privatePlain, Buffer.from(fileData, 'binary').toString()); + } attachments.push({ id: meta.id, - blob: decrypted, + blob: blob, type: meta.type, preview: meta.preview }); diff --git a/app/providers/DialogProvider/useDialogFiber.ts b/app/providers/DialogProvider/useDialogFiber.ts index 4ee0a24..b3710b3 100644 --- a/app/providers/DialogProvider/useDialogFiber.ts +++ b/app/providers/DialogProvider/useDialogFiber.ts @@ -445,7 +445,7 @@ export function useDialogFiber() { * чтобы когда приходит пачка сообщений с сервера в момент того как * пользователь был неактивен, не слать уведомления по всем этим сообщениям */ - if (!muted.includes(fromPublicKey) || protocolState == ProtocolState.SYNCHRONIZATION) { + if (!muted.includes(fromPublicKey) || protocolState != ProtocolState.SYNCHRONIZATION) { /** * Если пользователь в муте или сейчас идет синхронизация - не отправляем уведомление */ diff --git a/app/providers/SystemProvider/SystemProvider.tsx b/app/providers/SystemProvider/SystemProvider.tsx index 3ced45c..fcdc3cf 100644 --- a/app/providers/SystemProvider/SystemProvider.tsx +++ b/app/providers/SystemProvider/SystemProvider.tsx @@ -2,6 +2,7 @@ import { decodeWithPassword, encodeWithPassword } from "@/app/workers/crypto/cry import { useFileStorage } from "@/app/hooks/useFileStorage"; import { generateRandomKey } from "@/app/utils/utils"; import { createContext, useEffect, useState } from "react"; +import { useCore } from "@/app/hooks/useCore"; interface SystemProviderContextValue { id: string; @@ -21,7 +22,10 @@ export interface SystemProviderProps { */ export function SystemProvider(props: SystemProviderProps) { const [deviceId, setDeviceId] = useState(""); + const [deviceName, setDeviceName] = useState(""); + const [deviceOs, setDeviceOs] = useState(""); const {writeFile, readFile} = useFileStorage(); + const { getDeviceId, getDeviceName, getPlatform } = useCore(); useEffect(() => { fetchDeviceId(); @@ -29,6 +33,10 @@ export function SystemProvider(props: SystemProviderProps) { const fetchDeviceId = async () => { const device = await readFile("device"); + const name = await getDeviceName(); + const platform = await getPlatform(); + setDeviceName(name); + setDeviceOs(platform); if(device){ const decoded = await decodeDevice(Buffer.from(device).toString('utf-8')); if(decoded){ @@ -47,12 +55,11 @@ export function SystemProvider(props: SystemProviderProps) { } const decodeDevice = async (data: string) => { - const hwid = window.deviceId; - const platform = window.deviceName; + const hwid = await getDeviceId(); + const deviceName = await getDeviceName(); const salt = "rosetta-device-salt"; - try { - const decoded = await decodeWithPassword(hwid + platform + salt, data); + const decoded = await decodeWithPassword(hwid + deviceName + salt, data); return decoded; } catch (e) { console.error("Failed to decode device data:", e); @@ -61,29 +68,24 @@ export function SystemProvider(props: SystemProviderProps) { } const encodeDevice = async (data: string) => { - const hwid = window.deviceId; - const platform = window.deviceName; + const hwid = await getDeviceId(); + const deviceName = await getDeviceName(); const salt = "rosetta-device-salt"; try { - const encoded = await encodeWithPassword(hwid + platform + salt, data); + const encoded = await encodeWithPassword(hwid + deviceName + salt, data); return encoded; } catch (e) { console.error("Failed to encode device data:", e); return null; } } - - const systemName = window.deviceName || "Unknown Device"; - const systemOs = window.platform || "Unknown OS"; - - return ( {props.children} diff --git a/app/providers/UpdateProvider/UpdateProvider.tsx b/app/providers/UpdateProvider/UpdateProvider.tsx index c9b97d6..129fce0 100644 --- a/app/providers/UpdateProvider/UpdateProvider.tsx +++ b/app/providers/UpdateProvider/UpdateProvider.tsx @@ -4,8 +4,8 @@ import { useSender } from "../ProtocolProvider/useSender"; import { usePacket } from "../ProtocolProvider/usePacket"; import { useConsoleLogger } from "@/app/hooks/useConsoleLogger"; import { useFileStorage } from "@/app/hooks/useFileStorage"; -import { APPLICATION_ARCH, APPLICATION_PLATFROM, CORE_VERSION } from "@/app/constants"; import { APP_VERSION } from "@/app/version"; +import { useCore } from "@/app/hooks/useCore"; export interface UpdateProviderProps { children: React.ReactNode; @@ -58,6 +58,7 @@ export function UpdateProvider(props: UpdateProviderProps) { const [appUpdateUrl, setAppUpdateUrl] = useState(""); const [appActualVersion, setAppActualVersion] = useState(""); const {writeFile} = useFileStorage(); + const {getCoreVersion, getArch, getPlatform} = useCore(); useEffect(() => { let packet = new PacketRequestUpdate(); @@ -75,6 +76,9 @@ export function UpdateProvider(props: UpdateProviderProps) { }, []); const checkForUpdates = async () => { + const coreVersion = await getCoreVersion(); + const arch = await getArch(); + const platform = await getPlatform(); if(updateServerRef.current == null){ /** * SDU еще не определен @@ -85,7 +89,7 @@ export function UpdateProvider(props: UpdateProviderProps) { * Запрашиваем обновления с SDU сервера */ let response = await fetch - (`${updateServerRef.current}/updates/get?app=${APP_VERSION}&kernel=${CORE_VERSION}&arch=${APPLICATION_ARCH}&platform=${APPLICATION_PLATFROM}`).catch((e) => { + (`${updateServerRef.current}/updates/get?app=${APP_VERSION}&kernel=${coreVersion}&arch=${arch}&platform=${platform}`).catch((e) => { error("Failed to check for updates: " + e.message); }); if(!response || response.status != 200){ diff --git a/app/version.ts b/app/version.ts index a1b3d59..36c3ecf 100644 --- a/app/version.ts +++ b/app/version.ts @@ -1,15 +1,13 @@ -export const APP_VERSION = "1.0.4"; -export const CORE_MIN_REQUIRED_VERSION = "1.4.9"; +export const APP_VERSION = "1.0.5"; +export const CORE_MIN_REQUIRED_VERSION = "1.5.0"; export const RELEASE_NOTICE = ` -**Обновление v1.0.4** :emoji_1f631: -- Улучшеный UI для взаимодействия с отправкой изображений -- Исправлена блокировка потока при отправке изображений большого размера -- Исправлены проблемы с утечками памяти -- Исправлен вылет из приложения при попытке переслать сообщение -- Исправлены проблемы со скроллам в групповых чатах -- Исправлены проблемы с дерганием скролла в личных сообщениях -- Улучшен наблюдатель за изменениями размера в контенте -- Исправлена проблема с отображением аватара в упоминаниях -- Множественные исправления мелких багов и улучшения производительности +**Обновление v1.0.5** :emoji_1f631: +- Оптимизирован код ядра +- Исправление ошибки с системой обновления в результате гонки потоков в ядре +- Исправление уведомлений при синхронизации +- Анимация перемещения диалогов +- Оптимизирован код вложений +- Исправлен скролл при подгрузке сообщений сверху +- Ускорена загрузка диалогов при большом количестве тяжелых изображений `; \ No newline at end of file diff --git a/app/views/DeviceConfirm/DeviceConfirm.tsx b/app/views/DeviceConfirm/DeviceConfirm.tsx index 3fae8f2..7c60086 100644 --- a/app/views/DeviceConfirm/DeviceConfirm.tsx +++ b/app/views/DeviceConfirm/DeviceConfirm.tsx @@ -10,11 +10,14 @@ import { AnimatedButton } from "@/app/components/AnimatedButton/AnimatedButton"; import { useLogout } from "@/app/providers/AccountProvider/useLogout"; import { usePacket } from "@/app/providers/ProtocolProvider/usePacket"; import { PacketDeviceResolve, Solution } from "@/app/providers/ProtocolProvider/protocol/packets/packet.device.resolve"; +import { useCoreDevice } from "@/app/providers/DeviceProvider/useCoreDevice"; export function DeviceConfirm() { const [protocolState] = useProtocolState(); const navigate = useNavigate(); const logout = useLogout(); + const {deviceName} = useCoreDevice(); + useEffect(() => { if(protocolState == ProtocolState.CONNECTED) { @@ -60,7 +63,7 @@ export function DeviceConfirm() { - Confirm device {window.deviceName} on your first device to loading your chats. + Confirm device {deviceName} on your first device to loading your chats. diff --git a/app/views/Update/Update.tsx b/app/views/Update/Update.tsx index 5edc24e..32a63a4 100644 --- a/app/views/Update/Update.tsx +++ b/app/views/Update/Update.tsx @@ -4,15 +4,26 @@ import { RosettaPower } from "@/app/components/RosettaPower/RosettaPower"; import { SettingsAlert } from "@/app/components/SettingsAlert/SettingsAlert"; import { SettingsInput } from "@/app/components/SettingsInput/SettingsInput"; import { UpdateAlert } from "@/app/components/UpdateAlert/UpdateAlert"; -import { CORE_VERSION } from "@/app/constants"; +import { useCore } from "@/app/hooks/useCore"; import { UpdateStatus } from "@/app/providers/UpdateProvider/UpdateProvider"; import { useUpdater } from "@/app/providers/UpdateProvider/useUpdater"; import { APP_VERSION } from "@/app/version"; import { Box, Text } from "@mantine/core"; +import { useEffect, useState } from "react"; export function Update() { const {updateStatus} = useUpdater(); - + const {getCoreVersion} = useCore(); + const [coreVersion, setCoreVersion] = useState(""); + + useEffect(() => { + const fetchCoreVersion = async () => { + const version = await getCoreVersion(); + setCoreVersion(version); + } + fetchCoreVersion(); + }, [getCoreVersion]); + return ( <> @@ -23,7 +34,7 @@ export function Update() { - + If the kernel version is outdated, you need to reinstall the application so that this kernel continues to receive current updates. diff --git a/lib/main/app.ts b/lib/main/app.ts index a5a5d65..04ea6eb 100644 --- a/lib/main/app.ts +++ b/lib/main/app.ts @@ -1,4 +1,4 @@ -import { BrowserWindow, shell, app, ipcMain, nativeTheme, screen, powerMonitor } from 'electron' +import { BrowserWindow, shell, ipcMain, nativeTheme, screen, powerMonitor } from 'electron' import { join } from 'path' import fs from 'fs' import { WORKING_DIR } from './constants'; @@ -79,11 +79,6 @@ export function foundationIpcRegistration(mainWindow: BrowserWindow) { ipcMain.removeAllListeners("write-file"); ipcMain.removeAllListeners("read-file"); ipcMain.removeAllListeners("mkdir"); - ipcMain.removeHandler("get-core-version"); - ipcMain.removeHandler("get-arch"); - ipcMain.removeAllListeners("get-user-dir"); - ipcMain.removeHandler("get-downloads-path") - ipcMain.removeHandler("get-app-path"); ipcMain.removeHandler('open-dev-tools'); ipcMain.removeHandler('window-state'); ipcMain.removeHandler('window-toggle'); @@ -92,14 +87,6 @@ export function foundationIpcRegistration(mainWindow: BrowserWindow) { ipcMain.removeHandler('showItemInFolder'); ipcMain.removeHandler('openExternal'); - ipcMain.handle('showItemInFolder', (_, fullPath: string) => { - shell.showItemInFolder(fullPath); - }); - - ipcMain.handle('openExternal', (_, url: string) => { - shell.openExternal(url); - }); - ipcMain.handle('open-dev-tools', () => { if (mainWindow.webContents.isDevToolsOpened()) { return; @@ -208,27 +195,4 @@ export function foundationIpcRegistration(mainWindow: BrowserWindow) { mainWindow.webContents.send("mkdir-reply"); }); }); - /** - * Change to get-core-version - */ - ipcMain.handle("get-core-version", () => { - return app.getVersion(); - }); - - ipcMain.handle("get-arch", () => { - return process.arch; - }) - - ipcMain.on("get-user-dir", () => { - const userDir = app.getPath("userData"); - mainWindow.webContents.send("get-user-dir-reply", userDir); - }); - - ipcMain.handle("get-app-path", () => { - return app.getAppPath(); - }); - - ipcMain.handle("get-downloads-path", () => { - return app.getPath("downloads"); - }); } diff --git a/lib/main/ipcs/ipcCore.ts b/lib/main/ipcs/ipcCore.ts new file mode 100644 index 0000000..a5bf78d --- /dev/null +++ b/lib/main/ipcs/ipcCore.ts @@ -0,0 +1,34 @@ +import { app, ipcMain, shell } from "electron"; + +ipcMain.handle("ipcCore:getCoreVersion", () => { + return app.getVersion(); +}); + +ipcMain.handle("ipcCore:getArch", () => { + return process.arch; +}) + +ipcMain.handle("ipcCore:getUserDir", () => { + const userDir = app.getPath("userData"); + return userDir; +}); + +ipcMain.handle("ipcCore:getAppPath", () => { + return app.getAppPath(); +}); + +ipcMain.handle("ipcCore:getDownloadsPath", () => { + return app.getPath("downloads"); +}); + +ipcMain.handle('ipcCore:showItemInFolder', (_, fullPath: string) => { + shell.showItemInFolder(fullPath); +}); + +ipcMain.handle('ipcCore:openExternal', (_, url: string) => { + shell.openExternal(url); +}); + +ipcMain.handle('ipcCore:getPlatform', () => { + return process.platform; +}); \ No newline at end of file diff --git a/lib/main/main.ts b/lib/main/main.ts index a169a38..8a550ef 100644 --- a/lib/main/main.ts +++ b/lib/main/main.ts @@ -7,6 +7,7 @@ import './ipcs/ipcFilestorage' import './ipcs/ipcUpdate' import './ipcs/ipcNotification' import './ipcs/ipcDevice' +import './ipcs/ipcCore' import { Tray } from 'electron/main' import { join } from 'path' import { Logger } from './logger' diff --git a/lib/preload/preload.ts b/lib/preload/preload.ts index 6135ec5..ce67cae 100644 --- a/lib/preload/preload.ts +++ b/lib/preload/preload.ts @@ -4,46 +4,25 @@ import api from './api' const exposeContext = async () => { - let version = await ipcRenderer.invoke("get-core-version"); - let appPath = await ipcRenderer.invoke("get-app-path"); - let arch = await ipcRenderer.invoke("get-arch"); - let deviceName = await ipcRenderer.invoke("device:name"); - let deviceId = await ipcRenderer.invoke("device:id"); - - let downloadsPath = await ipcRenderer.invoke("get-downloads-path"); if (process.contextIsolated) { try { contextBridge.exposeInMainWorld('electron', electronAPI) contextBridge.exposeInMainWorld('api', api) - contextBridge.exposeInMainWorld('version', version); - contextBridge.exposeInMainWorld('platform', process.platform); - contextBridge.exposeInMainWorld('appPath', appPath); - contextBridge.exposeInMainWorld('arch', arch); - contextBridge.exposeInMainWorld('deviceName', deviceName); - contextBridge.exposeInMainWorld('deviceId', deviceId); contextBridge.exposeInMainWorld('shell', { openExternal: (url: string) => { - ipcRenderer.invoke('openExternal', url); + ipcRenderer.invoke('ipcCore:openExternal', url); }, showItemInFolder: (fullPath: string) => { - ipcRenderer.invoke('showItemInFolder', fullPath); + ipcRenderer.invoke('ipcCore:showItemInFolder', fullPath); } }); - contextBridge.exposeInMainWorld('downloadsPath', downloadsPath) } catch (error) { console.error(error) } } else { window.electron = electronAPI - window.api = api - window.version = version; - window.platform = process.platform; - window.appPath = appPath; - window.arch = arch; + window.api = api; window.shell = shell; - window.downloadsPath = downloadsPath; - window.deviceName = deviceName; - window.deviceId = deviceId; } } diff --git a/package.json b/package.json index cb48eb0..f080cee 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "Rosetta", - "version": "1.4.9", + "version": "1.5.0", "description": "Rosetta Messenger", "main": "./out/main/main.js", "license": "MIT",