From d77fcf39fd4be98fdf1c7081d7b32ee8ec73813d Mon Sep 17 00:00:00 2001 From: kyranjamie Date: Tue, 15 Dec 2020 17:41:58 +0100 Subject: [PATCH] fix: context menu crashing, closes #413 --- .github/workflows/debug-build.yml | 2 +- app/api/api.ts | 7 +- .../transaction-list-context-menu.ts | 66 +++++++++++-------- .../transaction-list-item.tsx | 14 ++-- app/main.dev.ts | 40 ++++++++--- 5 files changed, 82 insertions(+), 47 deletions(-) diff --git a/.github/workflows/debug-build.yml b/.github/workflows/debug-build.yml index 40716f8..8d62c75 100644 --- a/.github/workflows/debug-build.yml +++ b/.github/workflows/debug-build.yml @@ -98,7 +98,7 @@ jobs: - uses: lucasmotta/pull-request-sticky-header@1.0.0 if: (!contains(needs.*.result, 'failure')) with: - header: '> [Download the latest builds](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}).' + header: '> [Download the latest build](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})' GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Remove header if build failed diff --git a/app/api/api.ts b/app/api/api.ts index 6138715..511494f 100644 --- a/app/api/api.ts +++ b/app/api/api.ts @@ -21,7 +21,12 @@ export class Api { async getAddressTransactions(address: string) { return axios.get( - urljoin(this.baseUrl, `/extended/v1/address/${address}/transactions`) + urljoin(this.baseUrl, `/extended/v1/address/${address}/transactions`), + { + params: { + limit: 50, + }, + } ); } diff --git a/app/components/home/transaction-list/transaction-list-context-menu.ts b/app/components/home/transaction-list/transaction-list-context-menu.ts index 31f7b90..8263acd 100644 --- a/app/components/home/transaction-list/transaction-list-context-menu.ts +++ b/app/components/home/transaction-list/transaction-list-context-menu.ts @@ -1,4 +1,3 @@ -import { useClipboard } from '@blockstack/ui'; import { Transaction } from '@blockstack/stacks-blockchain-api-types'; import { hasMemo, getRecipientAddress } from '@utils/tx-utils'; @@ -12,54 +11,65 @@ export function deregisterHandler(el: HTMLButtonElement | null, handler: (e: Eve if (el === null) return; el.removeEventListener('contextmenu', handler); } - -type UiClipboard = ReturnType; - interface TxListContextMenu { tx: Transaction; copy: { - txid: UiClipboard; - recipientAddress: UiClipboard; - memo: UiClipboard; - date: UiClipboard; - txDetails: UiClipboard; - explorerLink: UiClipboard; + txid: string; + recipientAddress: string; + memo: string; + date: string; + txDetails: string; + explorerLink: string; }; } export function createTxListContextMenu(event: Event, { tx, copy }: TxListContextMenu) { event.preventDefault(); - const menuItems: Electron.MenuItemConstructorOptions[] = [ + const menuItems: { menu: Electron.MenuItemConstructorOptions; textToCopy?: string }[] = [ { - label: 'Copy to clipboard', - enabled: false, + menu: { + label: 'Copy to clipboard', + enabled: false, + }, }, - { type: 'separator' }, + { menu: { type: 'separator' } }, { - label: 'Transaction ID', - click: () => copy.txid.onCopy(), + textToCopy: copy.txid, + menu: { + label: 'Transaction ID', + }, }, { - label: 'Recipient address', - visible: !!getRecipientAddress(tx), - click: () => copy.recipientAddress.onCopy(), + textToCopy: copy.recipientAddress, + menu: { + label: 'Recipient address', + visible: !!getRecipientAddress(tx), + }, }, { - label: 'Memo', - visible: hasMemo(tx), - click: () => copy.memo.onCopy(), + textToCopy: copy.memo, + menu: { + label: 'Memo', + visible: hasMemo(tx), + }, }, { - label: 'Timestamp', - click: () => copy.date.onCopy(), + textToCopy: copy.date, + menu: { + label: 'Timestamp', + }, }, { - label: 'Transaction (as JSON)', - click: () => copy.txDetails.onCopy(), + textToCopy: copy.txDetails, + menu: { + label: 'Transaction (as JSON)', + }, }, { - label: 'Explorer link', - click: () => copy.explorerLink.onCopy(), + textToCopy: copy.explorerLink, + menu: { + label: 'Explorer link', + }, }, ]; api.contextMenu(menuItems); diff --git a/app/components/home/transaction-list/transaction-list-item.tsx b/app/components/home/transaction-list/transaction-list-item.tsx index 04aeb8f..9336fc9 100644 --- a/app/components/home/transaction-list/transaction-list-item.tsx +++ b/app/components/home/transaction-list/transaction-list-item.tsx @@ -9,7 +9,7 @@ import React, { } from 'react'; import { useSelector } from 'react-redux'; import { useHover, useFocus } from 'use-events'; -import { Box, Text, useClipboard } from '@blockstack/ui'; +import { Box, Text } from '@blockstack/ui'; import { Transaction } from '@blockstack/stacks-blockchain-api-types'; import { RootState } from '@store/index'; @@ -104,12 +104,12 @@ export const TransactionListItem: FC = props => { } const { current: copy } = useRef({ - txid: useClipboard(tx.tx_id), - recipientAddress: useClipboard(getRecipientAddress(tx) || ''), - memo: useClipboard(memo || ''), - date: useClipboard(txDate.toISOString()), - txDetails: useClipboard(JSON.stringify(tx, null, 2)), - explorerLink: useClipboard(makeExplorerLink(tx.tx_id)), + txid: tx.tx_id, + recipientAddress: getRecipientAddress(tx) || '', + memo: memo || '', + date: txDate.toISOString(), + txDetails: JSON.stringify(tx, null, 2), + explorerLink: makeExplorerLink(tx.tx_id), }); useLayoutEffect(() => { diff --git a/app/main.dev.ts b/app/main.dev.ts index 7d98e7c..815f955 100644 --- a/app/main.dev.ts +++ b/app/main.dev.ts @@ -16,7 +16,7 @@ import 'core-js/stable'; import 'regenerator-runtime/runtime'; import path from 'path'; -import { app, BrowserWindow, ipcMain, Menu, MenuItem, session } from 'electron'; +import { app, BrowserWindow, clipboard, ipcMain, Menu, session } from 'electron'; import { autoUpdater } from 'electron-updater'; import log from 'electron-log'; import windowState from 'electron-window-state'; @@ -192,12 +192,32 @@ ipcMain.handle('reload-app', () => { mainWindow?.reload(); }); -ipcMain.on('context-menu-open', (_e, { menuItems }) => { - const menu = new Menu(); - menuItems.forEach((item: Electron.MenuItemConstructorOptions) => menu.append(new MenuItem(item))); - menu.popup({ window: mainWindow?.getParentWindow() }); - menu.once('menu-will-close', () => { - // `destroy` call untyped - (menu as any).destroy(); - }); -}); +// +// TODO: refactor to be more generic +// There's a bug where click handler doesn't fire for the top-level menu +ipcMain.on( + 'context-menu-open', + ( + _e, + args: { menuItems: { menu: Electron.MenuItemConstructorOptions; textToCopy?: string }[] } + ) => { + const copyMenu = args.menuItems.map(item => { + const newItem = { ...item }; + if (newItem.textToCopy) { + newItem.menu.click = () => clipboard.writeText(newItem.textToCopy as any); + } + return newItem.menu; + }); + const contextMenu = Menu.buildFromTemplate([ + { + label: 'Copy', + submenu: copyMenu, + }, + ]); + contextMenu.popup({ window: mainWindow?.getParentWindow() }); + contextMenu.once('menu-will-close', () => { + // `destroy` call untyped + (contextMenu as any).destroy(); + }); + } +);