diff --git a/app/components/DeviceVerify/DeviceVerify.tsx b/app/components/DeviceVerify/DeviceVerify.tsx new file mode 100644 index 0000000..6b965f6 --- /dev/null +++ b/app/components/DeviceVerify/DeviceVerify.tsx @@ -0,0 +1,16 @@ +import { DeviceEntry } from "@/app/providers/ProtocolProvider/protocol/packets/packet.device.list"; +import { Flex, Text } from "@mantine/core"; + +export interface DeviceVerifyProps { + device: DeviceEntry; +} + +export function DeviceVerify(props: DeviceVerifyProps) { + return ( + + + New login from {props.device.deviceName} + + + ); +} \ No newline at end of file diff --git a/app/components/DialogsPanel/DialogsPanel.tsx b/app/components/DialogsPanel/DialogsPanel.tsx index 86e782e..f4bde5e 100644 --- a/app/components/DialogsPanel/DialogsPanel.tsx +++ b/app/components/DialogsPanel/DialogsPanel.tsx @@ -8,6 +8,8 @@ import { useNavigate } from 'react-router-dom'; import { DialogsList } from '../DialogsList/DialogsList'; import { DialogsPanelHeader } from '../DialogsPanelHeader/DialogsPanelHeader'; import { useDialogsList } from '@/app/providers/DialogListProvider/useDialogsList'; +import { useVerifyRequest } from '@/app/providers/DeviceProvider/useVerifyRequest'; +import { DeviceVerify } from '../DeviceVerify/DeviceVerify'; export function DialogsPanel() { const [dialogsMode, setDialogsMode] = useState<'all' | 'requests'>('all'); @@ -15,6 +17,7 @@ export function DialogsPanel() { const {dialogs} = useDialogsList(); const colors = useRosettaColors(); const navigate = useNavigate(); + const device = useVerifyRequest(); useEffect(() => { ((async () => { @@ -51,6 +54,9 @@ export function DialogsPanel() { > + {device && ( + + )} {requestsCount > 0 && } diff --git a/app/providers/DeviceProvider/DeviceProvider.tsx b/app/providers/DeviceProvider/DeviceProvider.tsx index dca79c5..60b6866 100644 --- a/app/providers/DeviceProvider/DeviceProvider.tsx +++ b/app/providers/DeviceProvider/DeviceProvider.tsx @@ -1,76 +1,31 @@ -import { decodeWithPassword, encodeWithPassword } from "@/app/crypto/crypto"; -import { useFileStorage } from "@/app/hooks/useFileStorage"; -import { generateRandomKey } from "@/app/utils/utils"; -import { createContext, useEffect, useState } from "react"; +import { createContext, useState } from "react"; +import { DeviceEntry, PacketDeviceList } from "../ProtocolProvider/protocol/packets/packet.device.list"; +import { usePacket } from "../ProtocolProvider/usePacket"; interface DeviceProviderContextValue { - deviceId: string; + devices: DeviceEntry[]; } -export const DeviceProviderContext = createContext(null); +export const DeviceProviderContext = createContext(null); interface DeviceProviderProps { children?: React.ReactNode; } export function DeviceProvider(props: DeviceProviderProps) { - const [deviceId, setDeviceId] = useState(""); - const {writeFile, readFile} = useFileStorage(); + /** + * Подключенные устройства + */ + const [devices, setDevices] = useState([]); - useEffect(() => { - fetchDeviceId(); + usePacket(0x17, (packet : PacketDeviceList) => { + console.info("Device list update", packet.getDevices()); + setDevices(packet.getDevices()); }, []); - const fetchDeviceId = async () => { - const device = await readFile("device"); - if(device){ - const decoded = await decodeDevice(Buffer.from(device).toString('utf-8')); - if(decoded){ - setDeviceId(decoded); - return; - } - } - await createDeviceId(); - } - - const createDeviceId = async () => { - const newDevice = generateRandomKey(128); - const encoded = await encodeDevice(newDevice); - await writeFile("device", encoded); - setDeviceId(newDevice); - } - - const decodeDevice = async (data: string) => { - const hwid = window.deviceId; - const platform = window.deviceName; - const salt = "rosetta-device-salt"; - - try { - const decoded = await decodeWithPassword(hwid + platform + salt, data); - return decoded; - } catch (e) { - console.error("Failed to decode device data:", e); - return null; - } - } - - const encodeDevice = async (data: string) => { - const hwid = window.deviceId; - const platform = window.deviceName; - const salt = "rosetta-device-salt"; - - try { - const encoded = await encodeWithPassword(hwid + platform + salt, data); - return encoded; - } catch (e) { - console.error("Failed to encode device data:", e); - return null; - } - } - return ( {props.children} diff --git a/app/providers/DeviceProvider/useDevices.ts b/app/providers/DeviceProvider/useDevices.ts new file mode 100644 index 0000000..8f1f6e0 --- /dev/null +++ b/app/providers/DeviceProvider/useDevices.ts @@ -0,0 +1,13 @@ +import { useContext } from "react"; +import { DeviceProviderContext } from "./DeviceProvider"; +import { DeviceEntry } from "../ProtocolProvider/protocol/packets/packet.device.list"; + +export function useDevices() : DeviceEntry[] { + const context = useContext(DeviceProviderContext); + + if(!context) { + throw new Error("useDevices must be used within a DeviceProvider"); + } + + return context.devices; +} \ No newline at end of file diff --git a/app/providers/DeviceProvider/useVerifyRequest.ts b/app/providers/DeviceProvider/useVerifyRequest.ts new file mode 100644 index 0000000..c76f228 --- /dev/null +++ b/app/providers/DeviceProvider/useVerifyRequest.ts @@ -0,0 +1,23 @@ +import { useContext } from "react"; +import { DeviceEntry, DeviceVerifyState } from "../ProtocolProvider/protocol/packets/packet.device.list"; +import { DeviceProviderContext } from "./DeviceProvider"; + +/** + * Получает устройство ожидающее подтверждения, если оно есть + * @returns возвращает устройство которое сейчас ждет одобрения + * от других устройств + */ +export function useVerifyRequest() : DeviceEntry | null { + const context = useContext(DeviceProviderContext); + if(!context) { + throw new Error("useVerifyRequest must be used within a DeviceProvider"); + } + + const pending = context.devices.find(device => device.deviceVerify === DeviceVerifyState.NOT_VERIFIED); + + if(!pending) { + return null; + } + + return pending; +} \ No newline at end of file diff --git a/app/providers/ProtocolProvider/protocol/protocol.ts b/app/providers/ProtocolProvider/protocol/protocol.ts index 0b983c5..7fcc486 100644 --- a/app/providers/ProtocolProvider/protocol/protocol.ts +++ b/app/providers/ProtocolProvider/protocol/protocol.ts @@ -24,6 +24,8 @@ import { PacketGroupInviteInfo } from "./packets/packet.group.invite.info"; import { PacketGroupJoin } from "./packets/packet.group.join"; import { PacketGroupLeave } from "./packets/packet.group.leave"; import { PacketGroupBan } from "./packets/packet.group.ban"; +import { PacketDeviceNew } from "./packets/packet.device.new"; +import { PacketDeviceList } from "./packets/packet.device.list"; export default class Protocol extends EventEmitter { private serverAddress: string; @@ -105,7 +107,7 @@ export default class Protocol extends EventEmitter { this._supportedPackets.set(0x06, new PacketMessage()); this._supportedPackets.set(0x07, new PacketRead()); this._supportedPackets.set(0x08, new PacketDelivery()); - //TODO: 0x09 + this._supportedPackets.set(0x09, new PacketDeviceNew()); this._supportedPackets.set(0x0A, new PacketRequestUpdate()); this._supportedPackets.set(0x0B, new PacketTyping()); this._supportedPackets.set(0x0C, new PacketAvatar()); @@ -119,6 +121,7 @@ export default class Protocol extends EventEmitter { this._supportedPackets.set(0x14, new PacketGroupJoin()); this._supportedPackets.set(0x15, new PacketGroupLeave()); this._supportedPackets.set(0x16, new PacketGroupBan()); + this._supportedPackets.set(0x17, new PacketDeviceList()); } private _findWaiters(packetId: number): ((packet: Packet) => void)[] {