UNPKG

lazycrypto-cli

Version:

A simple CLI app to view cryptocurrency indicators

239 lines 8.26 kB
import dotenv from "dotenv"; dotenv.config(); import React, { useEffect, useState } from "react"; import { Box, Text, useApp } from "ink"; import MultiCryptoDashboard from "./MultiCryptoDashboard.js"; import ConfigPanel from "./ConfigPanel.js"; import TimeframeSelector from "./TimeframeSelector.js"; import OrderPanel from "./CryptoData/orderPanel/OrderPanel.js"; import { readJsonFromFile } from "../utils/readJsonFile.js"; import { writeJsonToFile } from "../utils/writeJsonFile.js"; import os from "os"; import path from "path"; import fs from "fs/promises"; import { getArgs } from "../utils/getArgs.js"; import { setupZellijLayout } from "./CryptoData/terminals/zellij.js"; import { setupTmuxLayout } from "./CryptoData/terminals/tmux.js"; import { useKeyBinds } from "../hooks/useKeybinds.js"; import { useStdoutDimensions } from "../hooks/useStdoutDimensions.js"; import { useDebounced } from "../hooks/useDebounced.js"; const clearTerminal = () => { console.clear(); }; const App = () => { const [isConfigPanelVisible, setIsConfigPanelVisible] = useState(false); const [isTimeframeSelectorVisible, setIsTimeframeSelectorVisible] = useState(false); const [isOrderPanelVisible, setIsOrderPanelVisible] = useState(false); const [isTradesVisible, setIsTradesVisible] = useState(false); const [isLoading, setIsLoading] = useState(true); const [apiKey, setApiKey] = useState(""); const [apiSecret, setApiSecret] = useState(""); const [apiPassphrase, setApiPassphrase] = useState(""); const [configData, setConfigData] = useState({}); const [selectedTimeframe, setSelectedTimeframe] = useState("15min"); const [currentSymbol, setCurrentSymbol] = useState("BTC-USDT"); const [showKeybinds, setShowKeyBinds] = useState(false); const [showCryptoMenu, setShowCryptoMenu] = useState(false); const [refreshKey, setRefreshKey] = useState(0); const [rawColumns, rawRows] = useStdoutDimensions(); const columns = useDebounced(rawColumns, 150); const rows = useDebounced(rawRows, 150); const { exit } = useApp(); const { isMin, isString } = getArgs(); useKeyBinds({ isLoading, isConfigPanelVisible, isTimeframeSelectorVisible, isOrderPanelVisible, apiSecret, apiPassphrase, setIsConfigPanelVisible, setIsTradesVisible, setIsTimeframeSelectorVisible, setIsOrderPanelVisible, setShowKeyBinds, setShowCryptoMenu, setRefreshKey }); useEffect(() => { clearTerminal(); if (isMin) { setupZellijLayout(); setupTmuxLayout(); } checkConfig(); }, [isMin]); const checkConfig = async () => { const configDir = path.join(os.homedir(), ".config/lazycrypto"); const filePath = path.join(configDir, "config.json"); try { const configData = await readJsonFromFile(filePath); if (configData) { setApiKey(configData.kucoinApiKey || ""); setApiSecret(configData.kucoinApiSecret || ""); setApiPassphrase(configData.kucoinApiPassphrase || ""); setConfigData(configData); if (configData.timeframe) { setSelectedTimeframe(configData.timeframe); } if (configData.currentSymbol) { setCurrentSymbol(configData.currentSymbol); } setIsLoading(false); } else { setIsLoading(false); } } catch (err) { setIsLoading(false); } }; const handleApiKeySave = async credentials => { const configDir = path.join(os.homedir(), ".config/lazycrypto"); const filePath = path.join(configDir, "config.json"); try { await fs.mkdir(configDir, { recursive: true }); const newConfigData = { apiKey: typeof credentials === "string" ? credentials : credentials.apiKey, ...(credentials.kucoinApiKey && { kucoinApiKey: credentials.kucoinApiKey, kucoinApiSecret: credentials.kucoinApiSecret, kucoinApiPassphrase: credentials.kucoinApiPassphrase }), timeframe: selectedTimeframe, currentSymbol: currentSymbol, createdAt: configData.createdAt || new Date().toISOString(), updatedAt: new Date().toISOString() }; await writeJsonToFile(newConfigData, filePath); setApiKey(newConfigData.kucoinApiKey); setApiSecret(newConfigData.kucoinApiSecret || ""); setApiPassphrase(newConfigData.kucoinApiPassphrase || ""); setConfigData(newConfigData); setIsConfigPanelVisible(false); } catch (err) { console.error("Error saving config:", err); } }; const handleTimeframeSelect = async timeframe => { const configDir = path.join(os.homedir(), ".config/lazycrypto"); const filePath = path.join(configDir, "config.json"); try { const updatedConfig = { ...configData, timeframe: timeframe, updatedAt: new Date().toISOString() }; await writeJsonToFile(updatedConfig, filePath); setSelectedTimeframe(timeframe); setConfigData(updatedConfig); setIsTimeframeSelectorVisible(false); } catch (err) { console.error("Error saving timeframe config:", err); setSelectedTimeframe(timeframe); setIsTimeframeSelectorVisible(false); } }; const handleSymbolChange = async symbol => { const configDir = path.join(os.homedir(), ".config/lazycrypto"); const filePath = path.join(configDir, "config.json"); setCurrentSymbol(symbol); try { const updatedConfig = { ...configData, currentSymbol: symbol, updatedAt: new Date().toISOString() }; await writeJsonToFile(updatedConfig, filePath); setConfigData(updatedConfig); } catch (err) { console.error("Error saving symbol config:", err); } }; const handleConfigCancel = () => { if (apiKey) { setIsConfigPanelVisible(false); } else { exit(); } }; const handleTimeframeSelectorCancel = () => { setIsTimeframeSelectorVisible(false); }; const handleOrderPanelClose = () => { setIsOrderPanelVisible(false); }; const handleBack = () => { exit(); }; if (columns !== rawColumns || rows !== rawRows) { return /*#__PURE__*/React.createElement(Box, { justifyContent: "center", alignItems: "center", minHeight: 10 }, /*#__PURE__*/React.createElement(Text, null, "\u26A1")); } if (isLoading) { return /*#__PURE__*/React.createElement(Box, { flexDirection: "column", padding: isMin ? 0 : 1 }, /*#__PURE__*/React.createElement(Text, { color: "cyan" }, "Loading configuration...")); } if (isConfigPanelVisible) { return /*#__PURE__*/React.createElement(ConfigPanel, { onSave: handleApiKeySave, onCancel: handleConfigCancel, configData: configData, includeTrading: true }); } if (isTimeframeSelectorVisible) { return /*#__PURE__*/React.createElement(TimeframeSelector, { currentTimeframe: selectedTimeframe, onSelect: handleTimeframeSelect, onCancel: handleTimeframeSelectorCancel }); } if (isOrderPanelVisible) { return /*#__PURE__*/React.createElement(OrderPanel, { apiKey: apiKey, apiSecret: apiSecret, apiPassphrase: apiPassphrase, onClose: handleOrderPanelClose, currentSymbol: currentSymbol, selectedTimeframe: selectedTimeframe }); } return /*#__PURE__*/React.createElement(Box, { flexDirection: "column", width: columns, height: rows }, /*#__PURE__*/React.createElement(MultiCryptoDashboard, { apiKey: apiKey, selectedTimeframe: selectedTimeframe, onBack: handleBack, onSymbolChange: handleSymbolChange, isMinified: isMin, isTradesVisible: isTradesVisible, showCryptoMenu: showCryptoMenu, setShowCryptoMenu: setShowCryptoMenu, refreshKey: refreshKey, setRefreshKey: setRefreshKey }), /*#__PURE__*/React.createElement(Box, { flexDirection: "row", justifyContent: "flex-end" }, showKeybinds ? /*#__PURE__*/React.createElement(Text, { dimColor: true }, "'S' cryptos | 'O' order | 'R' refresh | 'T' timeframe | 'shift' + 't' toggle trades | 'C' config") : /*#__PURE__*/React.createElement(Text, { dimColor: true }, "'h' for help"))); }; export default App;