aboutsummaryrefslogtreecommitdiff
path: root/users/ryan/modules/simple-bar/simple-bar-source/lib/components/data/crypto.jsx
blob: 3faf4eadf7d265280dde15cf7f2e7591389f493a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
import * as Uebersicht from "uebersicht";
import * as DataWidget from "./data-widget.jsx";
import * as DataWidgetLoader from "./data-widget-loader.jsx";
import * as Icons from "../icons/icons.jsx";
import useWidgetRefresh from "../../hooks/use-widget-refresh";
import useServerSocket from "../../hooks/use-server-socket";
import { useSimpleBarContext } from "../simple-bar-context.jsx";
import * as Utils from "../../utils";

export { cryptoStyles as styles } from "../../styles/components/data/crypto";

const { React } = Uebersicht;

const DEFAULT_REFRESH_FREQUENCY = 5 * 60 * 1000; // Default refresh frequency set to 5 minutes

/**
 * Crypto Widget Component
 * @returns {JSX.Element|null} The Crypto Widget component
 */
export const Widget = React.memo(() => {
  const { displayIndex, settings, pushMissive } = useSimpleBarContext();
  const { widgets, cryptoWidgetOptions } = settings;
  const { cryptoWidget } = widgets;
  const {
    refreshFrequency,
    denomination,
    identifiers,
    precision,
    showOnDisplay,
    showIcon,
  } = cryptoWidgetOptions;

  // Calculate the refresh frequency using the provided or default value
  const refresh = React.useMemo(
    () =>
      Utils.getRefreshFrequency(refreshFrequency, DEFAULT_REFRESH_FREQUENCY),
    [refreshFrequency],
  );

  // Determine if the widget should be visible on the current display
  const visible =
    Utils.isVisibleOnDisplay(displayIndex, showOnDisplay) && cryptoWidget;

  const ref = React.useRef();
  const denominatorToken = getDenominatorToken(denomination);

  // Memoize cleanedUpIdentifiers to prevent recreation on every render
  const cleanedUpIdentifiers = React.useMemo(
    () => identifiers.replace(/ /g, ""),
    [identifiers],
  );

  // Memoize enumeratedIdentifiers to prevent recreation on every render
  const enumeratedIdentifiers = React.useMemo(
    () => cleanedUpIdentifiers.replace(/ /g, "").split(","),
    [cleanedUpIdentifiers],
  );

  const [state, setState] = React.useState();
  const [loading, setLoading] = React.useState(visible);

  // Reset the widget state
  const resetWidget = () => {
    setState(undefined);
    setLoading(false);
  };

  /**
   * Fetches cryptocurrency prices from the CoinGecko API
   */
  const getCrypto = React.useCallback(async () => {
    if (!visible) return;
    const response = await fetch(
      `https://api.coingecko.com/api/v3/simple/price?ids=${cleanedUpIdentifiers}&vs_currencies=${denomination}`,
    );
    const result = await response.json();

    const prices = enumeratedIdentifiers.map((id) => {
      const value = parseFloat(result[id][denomination]).toFixed(precision);
      return `${denominatorToken}${value}`;
    });
    setState(prices);
    setLoading(false);
  }, [
    visible,
    cleanedUpIdentifiers,
    denomination,
    enumeratedIdentifiers,
    precision,
    denominatorToken,
  ]);

  useServerSocket("crypto", visible, getCrypto, resetWidget, setLoading);
  useWidgetRefresh(visible, getCrypto, refresh);

  /**
   * Refreshes the cryptocurrency prices
   * @param {Event} e - The event object
   */
  const refreshCrypto = (e) => {
    Utils.clickEffect(e);
    setLoading(true);
    getCrypto();
    Utils.notification("Refreshing price from coingecko.com...", pushMissive);
  };

  if (loading) return <DataWidgetLoader.Widget className="crypto" />;
  if (!state) return null;

  const classes = Utils.classNames("crypto");

  return enumeratedIdentifiers.map((id, i) => (
    <DataWidget.Widget
      key={id}
      classes={classes}
      ref={ref}
      Icon={showIcon ? getIcon(id) : null}
      href={`https://coingecko.com/en/coins/${id}`}
      onClick={(e) => openCrypto(e, pushMissive)}
      onRightClick={refreshCrypto}
    >
      {state[i]}
    </DataWidget.Widget>
  ));
});

Widget.displayName = "Crypto";

/**
 * Returns the icon component for a given cryptocurrency identifier
 * @param {string} identifier - The cryptocurrency identifier
 * @returns {JSX.Element} The icon component
 */
function getIcon(identifier) {
  if (identifier === "celo") return Icons.Celo;
  if (identifier === "ethereum") return Icons.Ethereum;
  if (identifier === "bitcoin") return Icons.Bitcoin;
  return Icons.Moon;
}

/**
 * Returns the symbol for a given denomination
 * @param {string} denomination - The denomination (e.g., "usd", "eur")
 * @returns {string} The symbol for the denomination
 */
function getDenominatorToken(denomination) {
  if (denomination === "usd") return "$";
  if (denomination === "eur") return "€";
  return "";
}

/**
 * Opens the cryptocurrency price chart
 * @param {Event} e - The event object
 * @param {Function} pushMissive - The function to push a notification
 */
function openCrypto(e, pushMissive) {
  Utils.clickEffect(e);
  Utils.notification("Opening price chart from coingecko.com...", pushMissive);
}