This commit is contained in:
rosetta
2026-01-30 05:01:05 +02:00
commit 83f38dc63f
327 changed files with 18725 additions and 0 deletions

14
app/hooks/useAuth.ts Normal file
View File

@@ -0,0 +1,14 @@
import { useState, useEffect } from 'react';
function useAuth() {
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
useEffect(() => {
const authStatus = localStorage.getItem('auth_key');
setIsAuthenticated(authStatus !== '');
}, []);
return isAuthenticated;
}
export default useAuth;

View File

@@ -0,0 +1,70 @@
import { usePrivateKeyHash } from "../providers/AccountProvider/usePrivateKeyHash";
import { usePrivatePlain } from "../providers/AccountProvider/usePrivatePlain";
import { usePublicKey } from "../providers/AccountProvider/usePublicKey";
export enum ConsoleLogLevel {
INFO = "INFO",
ERROR = "ERROR",
WARN = "WARN"
}
export function useConsoleLogger(component: string) {
const privatePlain = usePrivatePlain();
const publicKey = usePublicKey();
const privateKey = usePrivateKeyHash();
const constructTechnicalDetails = () => {
let time = new Date().toISOString();
//console.groupCollapsed('Details');
console.log('%cPublic Key: %c%s', 'color: orange; font-weight: bold;', 'color: white;', publicKey.trim() != "" ? publicKey : '[EMPTY]');
console.log('%cPrivate Key Hash: %c%s', 'color: orange; font-weight: bold;', 'color: white;', privateKey.trim() != "" ? privateKey : '[EMPTY]');
console.log('%cPrivate Plain: %c%s', 'color: orange; font-weight: bold;', 'color: white;', privatePlain.trim() != "" ? privatePlain : '[EMPTY]');
//console.groupEnd();
//console.groupCollapsed('Trace');
console.info('%cTime: %c%s', 'color: green; font-weight: bold;', 'color: white;', time);
console.info('%cComponent:%c %s', 'color: red; font-weight: bold;', 'color: #7CFC00; font-weight: bold;', component);
console.trace('%cStack trace:', 'color: purple; font-weight: bold;');
//console.groupEnd();
}
const constructLogLevelColor = (logLevel : ConsoleLogLevel) => {
switch(logLevel){
case ConsoleLogLevel.INFO:
return 'color: white; background-color: #00B5FF;';
case ConsoleLogLevel.ERROR:
return 'color: white; background-color: red;';
case ConsoleLogLevel.WARN:
return 'color: black; background-color: yellow;';
default:
return 'color: black; background-color: white;';
}
}
const constructMessageDetails = (message: string, logLevel : ConsoleLogLevel) => {
return [
`%c ${logLevel} %c ${message}`,
constructLogLevelColor(logLevel),
'color: #fefefe; font-weight: normal;'
];
}
const error = (message : string) => {
console.groupCollapsed(...constructMessageDetails(message, ConsoleLogLevel.ERROR));
constructTechnicalDetails();
console.groupEnd();
}
const info = (message : string) => {
console.groupCollapsed(...constructMessageDetails(message, ConsoleLogLevel.INFO));
constructTechnicalDetails();
console.groupEnd();
}
const warn = (message : string) => {
console.groupCollapsed(...constructMessageDetails(message, ConsoleLogLevel.WARN));
constructTechnicalDetails();
console.groupEnd();
}
return {error, info, warn};
}

View File

@@ -0,0 +1,60 @@
import { IconBell, IconBellOff, IconPin, IconPinnedOff } from "@tabler/icons-react";
import { useContextMenu } from "../providers/ContextMenuProvider/useContextMenu"
import { useRosettaColors } from "./useRosettaColors";
import { useDialogState } from "../providers/DialogStateProvider.tsx/useDialogState";
export function useDialogContextMenu() : {
openContextMenu: (dialog_id: string) => void;
} {
const openContext = useContextMenu();
const colors = useRosettaColors();
const {muted, pinned, muteToggle, pinToggle} = useDialogState();
const openContextMenu = (dialog_id: string) => {
openContext([
{
label: 'Unmute',
action: () => {
muteToggle(dialog_id);
},
icon: <IconBell color={colors.success} size={14}></IconBell>,
cond: async () => {
return muted.includes(dialog_id) === true;
}
},
{
label: 'Mute',
action: () => {
muteToggle(dialog_id);
},
icon: <IconBellOff color={colors.error} size={14}></IconBellOff>,
cond: async () => {
return muted.includes(dialog_id) === false;
}
},
{
label: 'Pin',
action: () => {
pinToggle(dialog_id);
},
icon: <IconPin color={colors.success} size={14}></IconPin>,
cond: async () => {
return pinned.includes(dialog_id) === false;
}
},
{
label: 'Unpin',
action: () => {
pinToggle(dialog_id);
},
icon: <IconPinnedOff color={colors.error} size={14}></IconPinnedOff>,
cond: async () => {
return pinned.includes(dialog_id) === true;
}
}
], true, true);
}
return {
openContextMenu
}
}

View File

@@ -0,0 +1,13 @@
export function useFileStorage() {
const writeFile = async (file: string, data : string | Buffer, inWorkingDir : boolean = true) => {
const result = await window.electron.ipcRenderer.invoke('fileStorage:writeFile', file, data, inWorkingDir);
return result;
}
const readFile = async (file : string, inWorkingDir : boolean = true) => {
const result = await window.electron.ipcRenderer.invoke('fileStorage:readFile', file, inWorkingDir);
return result;
}
return {writeFile, readFile};
}

15
app/hooks/useLogger.ts Normal file
View File

@@ -0,0 +1,15 @@
import { usePublicKey } from "../providers/AccountProvider/usePublicKey";
import { useUserCache } from "../providers/InformationProvider/useUserCache";
export function useLogger(view : string = 'general') {
const publicKey = usePublicKey();
const userInfo = useUserCache(publicKey);
const logFunction = (message : string) => {
const publicTrimmed = publicKey.substring(0, 10) + '...' + publicKey.substring(publicKey.length - 10);
const logString = `${userInfo ? `[${userInfo.title}]` : `[unknown]`} [${publicTrimmed}] [${view}] [${new Date().toISOString()}] ${message}`;
window.electron.ipcRenderer.invoke('logger:log', logString);
}
return logFunction;
}

View File

@@ -0,0 +1,8 @@
import { useMantineTheme } from "@mantine/core";
import { useColorScheme } from "@mantine/hooks";
export function useMainColor () {
const colorScheme = useColorScheme();
const theme = useMantineTheme();
return colorScheme == "light" ? theme.colors.gray[1] : theme.colors.dark[8];
}

View File

@@ -0,0 +1,7 @@
export function useNotification() {
const show = (title : string, body : string) => {
window.electron.ipcRenderer.invoke('notification:show', title, body);
}
return show;
}

27
app/hooks/useQueue.ts Normal file
View File

@@ -0,0 +1,27 @@
import { useRef } from "react";
/**
* Нужно, чтобы избежать состояния гонки
*/
export function useQueue<T>() {
const queue = useRef<T[]>([]);
const inProcess = (task: T): boolean => {
return queue.current.includes(task);
}
const addToQueue = (task: T) => {
queue.current.push(task);
}
const removeFromQueue = (task: T) => {
queue.current = queue.current.filter(t => t !== task);
}
return {
inProcess,
addToQueue,
removeFromQueue,
queue
}
}

View File

@@ -0,0 +1,8 @@
import { useMediaQuery } from "@mantine/hooks";
export function useRosettaBreakpoints() {
return {
lg: useMediaQuery('(min-width: 775px)'),
md: useMediaQuery('(min-width: 490px)')
}
}

View File

@@ -0,0 +1,20 @@
import { useComputedColorScheme, useMantineTheme } from "@mantine/core";
export function useRosettaColors () {
const colorScheme = useComputedColorScheme();
const theme = useMantineTheme();
return {
boxColor: colorScheme == 'light' ? theme.white : theme.colors.dark[7],
mainColor: colorScheme == 'light' ? theme.colors.gray[1] : theme.colors.dark[8],
borderColor: colorScheme == 'light' ? theme.colors.gray[2] : theme.colors.dark[6],
chevrons: {
active: colorScheme == 'light' ? theme.colors.gray[6] : theme.colors.dark[2],
disabled: colorScheme == 'light' ? theme.colors.gray[3] : theme.colors.dark[6]
},
success: theme.colors.green[5],
error: theme.colors.red[5],
brandColor: theme.colors.blue[6],
warning: theme.colors.orange[5]
};
}

0
app/hooks/useSetup.ts Normal file
View File

124
app/hooks/useUpdater.ts Normal file
View File

@@ -0,0 +1,124 @@
import { useEffect, useState } from "react";
import { PacketRequestUpdate } from "../providers/ProtocolProvider/protocol/packets/packet.requestupdate";
import { APPLICATION_ARCH, APPLICATION_PLATFROM, CORE_VERSION } from "../constants";
import { PacketKernelUpdate } from "../providers/ProtocolProvider/protocol/packets/packet.kernelupdate";
import { usePacket } from "../providers/ProtocolProvider/usePacket";
import { PacketAppUpdate } from "../providers/ProtocolProvider/protocol/packets/packet.appupdate";
import { compareVersions } from "../utils/update";
import { useSender } from "../providers/ProtocolProvider/useSender";
import { useConsoleLogger } from "./useConsoleLogger";
import { useFileStorage } from "./useFileStorage";
import { APP_VERSION } from "../version";
import { useMemory } from "../providers/MemoryProvider/useMemory";
export enum UpdateStatus {
IDLE,
DOWNLOADING,
COMPILE,
READY_FOR_RESTART
}
export function useUpdater() {
const send = useSender();
const [kernelOutdatedForNextAppUpdates,
setKernelOutdatedForNextAppUpdates] = useState(false);
const [kernelUpdateUrl, setKernelUpdateUrl] = useState("");
const [appUpdateUrl, setAppUpdateUrl] = useState("");
const [appActualVersion, setAppActualVersion] = useState("");
const [kernelActualVersion, setKernelActualVersion] = useState("");
const [downloadProgress, setDownloadProgress] = useMemory<number>("dp", 0, true);
const [updateStatus, setUpdateStatus] = useMemory<UpdateStatus>("us", UpdateStatus.IDLE, true);
const {error, info} = useConsoleLogger('useUpdater');
const {writeFile} = useFileStorage();
useEffect(() => {
let packet = new PacketRequestUpdate();
packet.setAppVersion(APP_VERSION);
packet.setKernelVersion(CORE_VERSION);
packet.setArch(APPLICATION_ARCH);
packet.setPlatform(APPLICATION_PLATFROM);
send(packet);
}, []);
usePacket(0x0D, (packet : PacketKernelUpdate) => {
let url = packet.getUrl();
let version = packet.getVersion();
setKernelActualVersion(version);
setKernelUpdateUrl(url);
console.info("Kernel update available: ", version, url);
}, []);
usePacket(0x0E, (packet : PacketAppUpdate) => {
let url = packet.getUrl();
let version = packet.getVersion();
let kernelVersionRequired = packet.getKernelVersionRequired();
if(compareVersions(CORE_VERSION, kernelVersionRequired) < 0){
error("Kernel version is outdated. Cannot update app.");
setKernelOutdatedForNextAppUpdates(true);
return;
}
setAppActualVersion(version);
setAppUpdateUrl(url);
}, []);
const downloadLastApplicationUpdate = () => {
if(appUpdateUrl == ""){
return;
}
if(updateStatus != UpdateStatus.IDLE){
return;
}
setUpdateStatus(UpdateStatus.DOWNLOADING);
const xhr = new XMLHttpRequest();
xhr.open("GET", appUpdateUrl, true);
xhr.responseType = "blob";
xhr.onprogress = (event) => {
if (event.lengthComputable) {
const percentComplete = (event.loaded / event.total) * 100;
setDownloadProgress(Math.round(percentComplete));
}
};
xhr.onload = async () => {
if (xhr.status === 200) {
setUpdateStatus(UpdateStatus.COMPILE);
const blob : Blob = xhr.response;
let bundleName = `bundle ${appActualVersion}.zip`;
await writeFile(bundleName, Buffer.from(await blob.arrayBuffer()));
info("Update downloaded, starting compiler...");
await window.electron.ipcRenderer.invoke('update:installServiceUpdate', bundleName);
info("Update compiled successfully.");
setTimeout(() => {
setUpdateStatus(UpdateStatus.READY_FOR_RESTART);
}, 10000);
}
}
xhr.onerror = () => {
error("Error downloading update");
setUpdateStatus(UpdateStatus.IDLE);
}
xhr.send();
}
const restartAppForUpdateApply = () => {
if(updateStatus != UpdateStatus.READY_FOR_RESTART){
return;
}
window.electron.ipcRenderer.invoke('update:restartApp');
}
return {
appUpdateUrl,
kernelUpdateUrl,
appActualVersion,
kernelActualVersion,
kernelOutdatedForNextAppUpdates,
downloadProgress,
updateStatus,
downloadLastApplicationUpdate,
restartAppForUpdateApply
}
}

View File

@@ -0,0 +1,13 @@
import { useMemory } from "../providers/MemoryProvider/useMemory";
export enum ViewPanelsState {
DIALOGS_PANEL_HIDE,
DIALOGS_PANEL_SHOW,
DIALOGS_PANEL_ONLY,
DIALOGS_PANEL_HIDE_VIEW_RIGHT_PANEL
}
export function useViewPanelsState() : [ViewPanelsState, (state: ViewPanelsState) => void] {
const [viewState, setViewState] = useMemory<ViewPanelsState>('view_panels_state', ViewPanelsState.DIALOGS_PANEL_SHOW, true);
return [viewState, setViewState];
}

30
app/hooks/useWindow.ts Normal file
View File

@@ -0,0 +1,30 @@
// Desc: Custom hook to handle window resizing
export enum ElectronTheme {
SYSTEM = 'system',
DARK = 'dark',
LIGHT = 'light'
}
const useWindow = () => {
const setSize = (width : number, height : number) => {
window.api.send('window-resize', { width: width, height: height });
}
const setResizeble = (isResizeble : boolean) => {
window.api.send('window-resizeble', isResizeble);
}
const setTheme = (theme : ElectronTheme) => {
window.api.send('window-theme', theme);
}
return {
setSize,
setResizeble,
setTheme
}
}
export default useWindow;

View File

@@ -0,0 +1,22 @@
export function useWindowActions() {
const toggle = () => {
console.info("TOGGLE");
window.electron.ipcRenderer.invoke('window-toggle');
}
const close = () => {
console.info("CLOSE");
window.electron.ipcRenderer.invoke('window-close');
}
const minimize = () => {
console.info("MINIMIZE");
window.electron.ipcRenderer.invoke('window-minimize');
}
return {
toggle,
minimize,
close
}
}

View File

@@ -0,0 +1,25 @@
import { useEffect, useState } from "react";
export function useWindowFocus() {
const [isFocused, setIsFocused] = useState<boolean>(document.hasFocus());
function handleFocus() {
setIsFocused(true);
}
function handleBlur() {
setIsFocused(false);
}
useEffect(() => {
window.addEventListener('focus', handleFocus);
window.addEventListener('blur', handleBlur);
return () => {
window.removeEventListener('focus', handleFocus);
window.removeEventListener('blur', handleBlur);
};
}, []);
return isFocused;
}

View File

@@ -0,0 +1,39 @@
import { useEffect, useState } from "react";
interface WindowState {
isMinimized: boolean;
isMaximized: boolean;
isFullScreen: boolean;
isVisible: boolean;
isFocused: boolean;
isResizable: boolean;
isClosable: boolean;
isDestroyed: boolean;
bounds: any;
}
export function useWindowState() : WindowState {
const [windowState, setWindowState] = useState<WindowState>({
isMinimized: false,
isMaximized: false,
isFullScreen: false,
isVisible: false,
isFocused: false,
isResizable: false,
isClosable: false,
isDestroyed: false,
bounds: {}
});
useEffect(() => {
window.electron.ipcRenderer.on('window-state-changed', () => {
window.electron.ipcRenderer.invoke('window-state').then((state) => {
setWindowState(state);
});
});
window.electron.ipcRenderer.invoke('window-state').then((state) => {
setWindowState(state);
});
}, []);
return windowState;
}