diff --git a/frontend/.eslintrc.json b/frontend/.eslintrc.json index d0024c93..4993d733 100644 --- a/frontend/.eslintrc.json +++ b/frontend/.eslintrc.json @@ -26,6 +26,7 @@ "react-hooks/exhaustive-deps": "off", "react/prop-types": "off", "react/react-in-jsx-scope": "off", + "@typescript-eslint/strict-boolean-expressions": "off", "@typescript-eslint/naming-convention": [ "error", { diff --git a/frontend/src/basic/NavBar/NavBar.tsx b/frontend/src/basic/NavBar/NavBar.tsx index 34cbbfaf..a6794ef2 100644 --- a/frontend/src/basic/NavBar/NavBar.tsx +++ b/frontend/src/basic/NavBar/NavBar.tsx @@ -166,7 +166,7 @@ const NavBar = (): JSX.Element => { value='order' disabled={ !Boolean(slot?.hashId) || - !(slot?.getRobot(slot?.activeShortAlias ?? '')?.activeOrderId != null) + !Boolean(slot?.getRobot(slot?.activeShortAlias ?? '')?.activeOrderId) } icon={} iconPosition='start' diff --git a/frontend/src/basic/OrderPage/index.tsx b/frontend/src/basic/OrderPage/index.tsx index 64f425b0..cf290be5 100644 --- a/frontend/src/basic/OrderPage/index.tsx +++ b/frontend/src/basic/OrderPage/index.tsx @@ -56,9 +56,9 @@ const OrderPage = (): JSX.Element => { .then((order) => { if (order?.bad_request !== undefined) { setBadOrder(order.bad_request); - } else if (order?.id != null) { + } else if (Boolean(order?.id)) { setCurrentOrder(order); - if (order.is_participant) { + if (order?.is_participant) { garage.updateOrder(order); } } @@ -82,23 +82,25 @@ const OrderPage = (): JSX.Element => { navigate('/robot'); }; - const orderDetailsSpace = - currentOrder != null ? ( - { - navigate('/robot'); - }} - /> - ) : ( - <> - ); + const orderDetailsSpace = currentOrder ? ( + { + navigate('/robot'); + }} + /> + ) : ( + <> + ); - const tradeBoxSpace = - currentOrder != null ? : <>; + const tradeBoxSpace = currentOrder ? ( + + ) : ( + <> + ); return ( diff --git a/frontend/src/basic/RobotPage/Onboarding.tsx b/frontend/src/basic/RobotPage/Onboarding.tsx index 323dc4c5..6e11be3c 100644 --- a/frontend/src/basic/RobotPage/Onboarding.tsx +++ b/frontend/src/basic/RobotPage/Onboarding.tsx @@ -164,7 +164,7 @@ const Onboarding = ({ - {robot?.found === true && slot?.lastShortAlias != null ? ( + {robot?.found && Boolean(slot?.lastShortAlias) ? ( {t('Welcome back!')} diff --git a/frontend/src/basic/RobotPage/TokenInput.tsx b/frontend/src/basic/RobotPage/TokenInput.tsx index 83f226c0..192d8fad 100644 --- a/frontend/src/basic/RobotPage/TokenInput.tsx +++ b/frontend/src/basic/RobotPage/TokenInput.tsx @@ -71,7 +71,7 @@ const TokenInput = ({ { systemClient.copyToClipboard(inputToken); setShowCopied(true); diff --git a/frontend/src/components/Dialogs/Coordinator.tsx b/frontend/src/components/Dialogs/Coordinator.tsx index 288094b4..8a3b977d 100644 --- a/frontend/src/components/Dialogs/Coordinator.tsx +++ b/frontend/src/components/Dialogs/Coordinator.tsx @@ -414,12 +414,14 @@ const CoordinatorDialog = ({ open = false, onClose, network, shortAlias }: Props )} - {coordinator?.info?.notice_severity != null && + {Boolean(coordinator?.info?.notice_severity) && coordinator?.info?.notice_severity !== 'none' && ( - + {t('Coordinator Notice')} -
+
)} @@ -478,13 +480,13 @@ const CoordinatorDialog = ({ open = false, onClose, network, shortAlias }: Props )} - {coordinator?.loadingInfo ? ( + {Boolean(coordinator?.loadingInfo) ? ( - ) : coordinator?.info !== undefined ? ( + ) : Boolean(coordinator?.info) ? ( - {coordinator?.policies !== undefined && ( + {Boolean(coordinator?.policies) && ( { diff --git a/frontend/src/components/Dialogs/Profile.tsx b/frontend/src/components/Dialogs/Profile.tsx index 3b109b39..0c185245 100644 --- a/frontend/src/components/Dialogs/Profile.tsx +++ b/frontend/src/components/Dialogs/Profile.tsx @@ -83,7 +83,7 @@ const ProfileDialog = ({ open = false, baseUrl, onClose }: Props): JSX.Element = diff --git a/frontend/src/components/MakerForm/MakerForm.tsx b/frontend/src/components/MakerForm/MakerForm.tsx index 0a8a719e..8844508d 100644 --- a/frontend/src/components/MakerForm/MakerForm.tsx +++ b/frontend/src/components/MakerForm/MakerForm.tsx @@ -93,7 +93,7 @@ const MakerForm = ({ useEffect(() => { const slot = garage.getSlot(); - if (slot?.token != null) void federation.fetchRobot(garage, slot?.token); + if (Boolean(slot?.token)) void federation.fetchRobot(garage, slot?.token); }, [garage.currentSlot]); useEffect(() => { @@ -287,7 +287,7 @@ const MakerForm = ({ const handleCreateOrder = function (): void { const slot = garage.getSlot(); - if (slot?.activeShortAlias != null) { + if (Boolean(slot?.activeShortAlias)) { setBadRequest(t('You are already maker of an active order')); return; } diff --git a/frontend/src/components/OrderDetails/index.tsx b/frontend/src/components/OrderDetails/index.tsx index d913801d..ae994b0c 100644 --- a/frontend/src/components/OrderDetails/index.tsx +++ b/frontend/src/components/OrderDetails/index.tsx @@ -40,7 +40,6 @@ import type Coordinator from '../../models'; import { statusBadgeColor, pn, amountToString, computeSats } from '../../utils'; import TakeButton from './TakeButton'; import { F2fMapDialog } from '../Dialogs'; -import { AppContext, type UseAppStoreType } from '../../contexts/AppContext'; import { GarageContext, type UseGarageStoreType } from '../../contexts/GarageContext'; import { type UseFederationStoreType, FederationContext } from '../../contexts/FederationContext'; import { type Order } from '../../models'; @@ -62,7 +61,6 @@ const OrderDetails = ({ }: OrderDetailsProps): JSX.Element => { const { t } = useTranslation(); const theme = useTheme(); - const { hostUrl } = useContext(AppContext); const { federation } = useContext(FederationContext); const { orderUpdatedAt } = useContext(GarageContext); const [coordinator] = useState(federation.getCoordinator(shortAlias)); diff --git a/frontend/src/components/RobotInfo/index.tsx b/frontend/src/components/RobotInfo/index.tsx index bb93dc8d..e01ad85c 100644 --- a/frontend/src/components/RobotInfo/index.tsx +++ b/frontend/src/components/RobotInfo/index.tsx @@ -203,7 +203,7 @@ const RobotInfo: React.FC = ({ coordinator, onClose }: Props) => { - {robot?.tgEnabled === true ? ( + {Boolean(robot?.tgEnabled) ? ( {t('Telegram enabled')} @@ -241,7 +241,7 @@ const RobotInfo: React.FC = ({ coordinator, onClose }: Props) => { { - setStealthInvoice(!(robot?.stealthInvoices === true)); + setStealthInvoice(!Boolean(robot?.stealthInvoices)); }} /> } diff --git a/frontend/src/components/TradeBox/EncryptedChat/EncryptedTurtleChat/index.tsx b/frontend/src/components/TradeBox/EncryptedChat/EncryptedTurtleChat/index.tsx index 47a3b737..0acdb224 100644 --- a/frontend/src/components/TradeBox/EncryptedChat/EncryptedTurtleChat/index.tsx +++ b/frontend/src/components/TradeBox/EncryptedChat/EncryptedTurtleChat/index.tsx @@ -88,7 +88,7 @@ const EncryptedTurtleChat: React.FC = ({ const loadMessages: () => void = () => { const shortAlias = garage.getSlot()?.activeShortAlias; - if (!(shortAlias == null)) return; + if (!shortAlias) return; const { url, basePath } = federation .getCoordinator(shortAlias) @@ -123,7 +123,7 @@ const EncryptedTurtleChat: React.FC = ({ const onMessage = (dataFromServer: ServerMessage): void => { const robot = garage.getSlot(); - if (robot != null && dataFromServer != null) { + if (robot && dataFromServer != null) { // If we receive an encrypted message if (dataFromServer.message.substring(0, 27) === `-----BEGIN PGP MESSAGE-----`) { void decryptMessage( @@ -178,8 +178,11 @@ const EncryptedTurtleChat: React.FC = ({ }; const onButtonClicked = (e: React.FormEvent): void => { - const robot = garage.getSlot(); - if (robot?.token !== undefined && value.includes(robot.token)) { + const robot = garage.getSlot()?.getRobot(); + + if (!robot) return; + + if (robot?.token && value.includes(robot.token)) { alert( `Aye! You just sent your own robot robot.token to your peer in chat, that's a catastrophic idea! So bad your message was blocked.`, ); @@ -199,7 +202,7 @@ const EncryptedTurtleChat: React.FC = ({ order_id: orderId, offset: lastIndex, }, - { tokenSHA256: robot?.tokenSHA256 }, + { tokenSHA256: robot?.tokenSHA256 ?? '' }, ) .then((response) => { if (response != null) { @@ -215,7 +218,7 @@ const EncryptedTurtleChat: React.FC = ({ }); } // Else if message is not empty send message - else if (value !== '' && robot?.pubKey != null) { + else if (value !== '' && Boolean(robot?.pubKey)) { setWaitingEcho(true); setLastSent(value); encryptMessage(value, robot?.pubKey, peerPubKey ?? '', robot?.encPrivKey, robot?.token) diff --git a/frontend/src/components/TradeBox/index.tsx b/frontend/src/components/TradeBox/index.tsx index 0d60fb33..f16b6d9d 100644 --- a/frontend/src/components/TradeBox/index.tsx +++ b/frontend/src/components/TradeBox/index.tsx @@ -156,7 +156,7 @@ const TradeBox = ({ baseUrl, onStartAgain }: TradeBoxProps): JSX.Element => { const renewOrder = function (): void { const currentOrder = garage.getSlot()?.order; - if (currentOrder != null) { + if (Boolean(currentOrder)) { const body = { type: currentOrder.type, currency: currentOrder.currency, @@ -362,7 +362,7 @@ const TradeBox = ({ baseUrl, onStartAgain }: TradeBoxProps): JSX.Element => { // Effect on Order Status change (used for WebLN) useEffect(() => { const currentOrder = garage.getSlot()?.order; - if (currentOrder != null && currentOrder?.status !== lastOrderStatus) { + if (currentOrder && currentOrder?.status !== lastOrderStatus) { setLastOrderStatus(currentOrder.status); void handleWebln(currentOrder); } @@ -380,7 +380,7 @@ const TradeBox = ({ baseUrl, onStartAgain }: TradeBoxProps): JSX.Element => { titleIcon: () => <>, }; - if (order == null) return baseContract; + if (!order) return baseContract; const status = order.status; const isBuyer = order.is_buyer; diff --git a/frontend/src/contexts/FederationContext.ts b/frontend/src/contexts/FederationContext.ts index 19ace65d..4a5ff4f0 100644 --- a/frontend/src/contexts/FederationContext.ts +++ b/frontend/src/contexts/FederationContext.ts @@ -89,26 +89,24 @@ export const useFederationStore = (): UseFederationStoreType => { useEffect(() => { // On bitcoin network change we reset book, limits and federation info and fetch everything again - setFederation(() => { - const newFed = initialFederationContext.federation; - newFed.registerHook('onFederationReady', () => { - setCoordinatorUpdatedAt(new Date().toISOString()); - }); - newFed.registerHook('onCoordinatorUpdate', () => { - setFederationUpdatedAt(new Date().toISOString()); - }); - void newFed.start(origin, settings, hostUrl); - return newFed; + const newFed = initialFederationContext.federation; + newFed.registerHook('onFederationReady', () => { + setCoordinatorUpdatedAt(new Date().toISOString()); }); + newFed.registerHook('onCoordinatorUpdate', () => { + setFederationUpdatedAt(new Date().toISOString()); + }); + void newFed.start(origin, settings, hostUrl); + setFederation(newFed); }, [settings.network, torStatus]); - const onOrderReceived = (order: any): void => { - if (order?.bad_request !== undefined) { + const onOrderReceived = (order: Order): void => { + if (order?.bad_request) { setBadOrder(order.bad_request); setDelay(99999999); - garage.updateOrder(undefined); + garage.updateOrder(null); } - if (order?.id != null) { + if (order?.id) { setDelay( order.status >= 0 && order.status <= 18 ? page === 'order' @@ -116,7 +114,7 @@ export const useFederationStore = (): UseFederationStoreType => { : statusToDelay[order.status] * 5 // If user is not looking at "order" tab, refresh less often. : 99999999, ); - garage.updateOrder(order as Order); + garage.updateOrder(order); setBadOrder(undefined); } }; @@ -124,12 +122,12 @@ export const useFederationStore = (): UseFederationStoreType => { const fetchCurrentOrder = (): void => { const activeSlot = garage.getSlot(); const robot = activeSlot?.getRobot(activeSlot?.activeShortAlias ?? ''); - if (robot != null && activeSlot?.activeShortAlias != null) { - const coordinator = federation.getCoordinator(activeSlot?.activeShortAlias); + if (robot?.activeOrderId && activeSlot?.activeShortAlias) { + const coordinator = federation.getCoordinator(activeSlot?.activeShortAlias ?? ''); coordinator - .fetchOrder(robot.activeOrderId, robot) + ?.fetchOrder(robot.activeOrderId, robot) .then((order) => { - onOrderReceived(order); + onOrderReceived(order as Order); }) .finally(() => { setTimer(setTimeout(fetchCurrentOrder, delay)); @@ -147,18 +145,18 @@ export const useFederationStore = (): UseFederationStoreType => { }; }, []); + useEffect(() => { + if (page === 'offers') void federation.updateBook(); + }, [page]); + useEffect(() => { const slot = garage.getSlot(); const robot = slot?.getRobot(); - if (robot != null && garage.currentSlot != null) { + if (robot && garage.currentSlot) { if (open.profile && Boolean(slot?.hashId) && slot?.token) { void federation.fetchRobot(garage, slot?.token); // refresh/update existing robot - } else if ( - robot.token !== undefined && - robot.encPrivKey !== undefined && - robot.pubKey !== undefined - ) { + } else if (robot.token && robot.encPrivKey && robot.pubKey) { void federation.fetchRobot(garage, robot.token); // create new robot with existing token and keys (on network and coordinator change) } } diff --git a/frontend/src/models/Coordinator.model.ts b/frontend/src/models/Coordinator.model.ts index 8b3548fd..ae745756 100644 --- a/frontend/src/models/Coordinator.model.ts +++ b/frontend/src/models/Coordinator.model.ts @@ -141,7 +141,6 @@ export class Coordinator { this.url = String(this[settings.network][origin]); this.basePath = ''; } - void this.update(() => { onStarted(this.shortAlias); }); @@ -157,14 +156,20 @@ export class Coordinator { this.loadInfo(onDataLoad); }; - generateAllMakerAvatars = (data: [PublicOrder]) => { + updateBook = async (onUpdate: (shortAlias: string) => void = () => {}): Promise => { + this.loadBook(() => { + onUpdate(this.shortAlias); + }); + }; + + generateAllMakerAvatars = async (data: [PublicOrder]): Promise => { for (const order of data) { - robohash.generate(order.maker_hash_id, 'small'); + void robohash.generate(order.maker_hash_id, 'small'); } }; loadBook = (onDataLoad: () => void = () => {}): void => { - if (this.enabled === false) return; + if (!this.enabled) return; if (this.loadingBook) return; this.loadingBook = true; @@ -172,12 +177,12 @@ export class Coordinator { apiClient .get(this.url, `${this.basePath}/api/book/`) .then((data) => { - if (data.not_found === undefined) { + if (!data?.not_found) { this.book = (data as PublicOrder[]).map((order) => { order.coordinatorShortAlias = this.shortAlias; return order; }); - this.generateAllMakerAvatars(data); + void this.generateAllMakerAvatars(data); onDataLoad(); } }) @@ -190,7 +195,7 @@ export class Coordinator { }; loadLimits = (onDataLoad: () => void = () => {}): void => { - if (this.enabled === false) return; + if (!this.enabled) return; if (this.loadingLimits) return; this.loadingLimits = true; @@ -218,7 +223,7 @@ export class Coordinator { }; loadInfo = (onDataLoad: () => void = () => {}): void => { - if (this.enabled === false) return; + if (!this.enabled) return; if (this.loadingInfo) return; this.loadingInfo = true; @@ -275,7 +280,7 @@ export class Coordinator { }; fecthRobot = async (garage: Garage, token: string): Promise => { - if (this.enabled === false) return null; + if (!this.enabled) return null; const robot = garage?.getSlot(token)?.getRobot() ?? null; @@ -324,7 +329,7 @@ export class Coordinator { }; fetchOrder = async (orderId: number, robot: Robot): Promise => { - if (this.enabled === false) return null; + if (!this.enabled) return null; if (!(robot.token != null)) return null; const authHeaders = robot.getAuthHeaders(); @@ -355,7 +360,7 @@ export class Coordinator { bad_invoice?: string; successful_withdrawal?: boolean; }> => { - if (this.enabled === false) return null; + if (!this.enabled) return null; const robot = garage.getSlot(index)?.getRobot(); @@ -377,7 +382,7 @@ export class Coordinator { }; fetchStealth = async (wantsStealth: boolean, garage: Garage, index: string): Promise => { - if (this.enabled === false) return null; + if (!this.enabled) return null; const robot = garage?.getSlot(index)?.getRobot(); diff --git a/frontend/src/models/Federation.model.ts b/frontend/src/models/Federation.model.ts index c5e1e2a6..99fcbd09 100644 --- a/frontend/src/models/Federation.model.ts +++ b/frontend/src/models/Federation.model.ts @@ -68,7 +68,6 @@ export class Federation { this.onCoordinatorSaved(shortAlias); }; this.loading = true; - // Object.values(this.coordinators).forEach(async (coor) => { for (const coor of Object.values(this.coordinators)) { await coor.start(origin, settings, hostUrl, onCoordinatorStarted); } @@ -76,7 +75,6 @@ export class Federation { update = async (): Promise => { this.loading = false; - // Object.values(this.coordinators).forEach(async (coor) => { for (const coor of Object.values(this.coordinators)) { await coor.update(() => { this.onCoordinatorSaved(coor.shortAlias); @@ -84,6 +82,15 @@ export class Federation { } }; + updateBook = async (): Promise => { + this.loading = false; + for (const coor of Object.values(this.coordinators)) { + await coor.updateBook(() => { + this.onCoordinatorSaved(coor.shortAlias); + }); + } + }; + updateExchange = (): void => { this.exchange.info = updateExchangeInfo(this); }; diff --git a/frontend/src/models/Garage.model.ts b/frontend/src/models/Garage.model.ts index f48c0bb2..af5c9299 100644 --- a/frontend/src/models/Garage.model.ts +++ b/frontend/src/models/Garage.model.ts @@ -58,7 +58,7 @@ class Garage { if (slotsDump !== '') { const rawSlots = JSON.parse(slotsDump); Object.values(rawSlots).forEach((rawSlot: Record) => { - if (rawSlot?.token != null) { + if (rawSlot?.token) { this.createSlot(rawSlot?.token); Object.keys(rawSlot.robots).forEach((shortAlias) => { const rawRobot = rawSlot.robots[shortAlias]; @@ -76,7 +76,7 @@ class Garage { // Slots getSlot: (token?: string) => Slot | null = (token) => { const currentToken = token ?? this.currentSlot; - return currentToken != null ? this.slots[currentToken] ?? null : null; + return currentToken ? this.slots[currentToken] ?? null : null; }; createSlot: (token: string) => Slot | null = (token) => { @@ -89,7 +89,7 @@ class Garage { deleteSlot: (token?: string) => void = (token) => { const targetIndex = token ?? this.currentSlot; - if (targetIndex != null) { + if (targetIndex) { Reflect.deleteProperty(this.slots, targetIndex); this.currentSlot = null; this.triggerHook('onRobotUpdate'); @@ -103,7 +103,7 @@ class Garage { token, ) => { const slot = this.getSlot(token); - if (attributes != null) { + if (attributes) { if (attributes.copiedToken !== undefined) slot?.setCopiedToken(attributes.copiedToken); this.triggerHook('onRobotUpdate'); } @@ -116,7 +116,7 @@ class Garage { shortAlias, attributes, ) => { - if (token === null || shortAlias === null) return; + if (!token || !shortAlias) return; let slot = this.getSlot(token); @@ -132,18 +132,22 @@ class Garage { }; // Orders - updateOrder: (order: Order) => void = (order) => { + updateOrder: (order: Order | null) => void = (order) => { const slot = this.getSlot(); if (slot != null) { - const updatedOrder = slot.order ?? null; - if (updatedOrder !== null && updatedOrder.id === order.id) { - Object.assign(updatedOrder, order); - slot.order = updatedOrder; + if (order !== null) { + const updatedOrder = slot.order ?? null; + if (updatedOrder !== null && updatedOrder.id === order.id) { + Object.assign(updatedOrder, order); + slot.order = updatedOrder; + } else { + slot.order = order; + } + if (slot.order?.is_participant) { + slot.activeShortAlias = order.shortAlias; + } } else { - slot.order = order; - } - if (slot.order?.is_participant) { - slot.activeShortAlias = order.shortAlias; + slot.order = null; } this.triggerHook('onOrderUpdate'); this.save(); diff --git a/frontend/src/models/Slot.model.ts b/frontend/src/models/Slot.model.ts index 6b1754ad..ea38dcce 100644 --- a/frontend/src/models/Slot.model.ts +++ b/frontend/src/models/Slot.model.ts @@ -10,8 +10,8 @@ class Slot { this.hashId = sha256(sha256(this.token)); this.nickname = generate_roboname(this.hashId); // trigger RoboHash avatar generation in webworker and store in RoboHash class cache. - robohash.generate(this.hashId, 'small'); - robohash.generate(this.hashId, 'large'); + void robohash.generate(this.hashId, 'small'); + void robohash.generate(this.hashId, 'large'); this.robots = {}; this.order = null; @@ -35,11 +35,11 @@ class Slot { }; getRobot = (shortAlias?: string): Robot | null => { - if (shortAlias != null) { + if (shortAlias) { return this.robots[shortAlias]; - } else if (this.activeShortAlias !== null && this.robots[this.activeShortAlias] != null) { + } else if (this.activeShortAlias !== null && this.robots[this.activeShortAlias]) { return this.robots[this.activeShortAlias]; - } else if (this.lastShortAlias !== null && this.robots[this.lastShortAlias] != null) { + } else if (this.lastShortAlias !== null && this.robots[this.lastShortAlias]) { return this.robots[this.lastShortAlias]; } else if (Object.values(this.robots).length > 0) { return Object.values(this.robots)[0];