pl-fe: don't assume everything that looks like a crypto address is a crypto address

Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
nicole mikołajczyk
2025-11-06 16:49:37 +01:00
parent d897cf1353
commit 3890792bb1
13 changed files with 15 additions and 18 deletions

View File

@ -70,7 +70,6 @@ const getAsyncRefreshHeader = (response: Pick<Response, 'headers'>): AsyncRefres
return null; return null;
}; };
interface RequestBody<Params = Record<string, any>> { interface RequestBody<Params = Record<string, any>> {
method?: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE'; method?: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE';
body?: any; body?: any;

View File

@ -80,7 +80,6 @@ const AccountHoverCard: React.FC<IAccountHoverCard> = ({ visible = true }) => {
}; };
}, []); }, []);
const { x, y, strategy, refs, context, placement } = useFloating({ const { x, y, strategy, refs, context, placement } = useFloating({
open: !!account, open: !!account,
elements: { elements: {

View File

@ -5,11 +5,12 @@ import HStack from 'pl-fe/components/ui/hstack';
import Icon from 'pl-fe/components/ui/icon'; import Icon from 'pl-fe/components/ui/icon';
import Stack from 'pl-fe/components/ui/stack'; import Stack from 'pl-fe/components/ui/stack';
import Text from 'pl-fe/components/ui/text'; import Text from 'pl-fe/components/ui/text';
import { CryptoIcon } from 'pl-fe/features/ui/util/async-components';
import { useModalsActions } from 'pl-fe/stores/modals'; import { useModalsActions } from 'pl-fe/stores/modals';
import { getTitle } from '../utils/coin-db'; import { getTitle } from '../utils/coin-db';
import CryptoIcon from './crypto-icon';
interface ICryptoAddress { interface ICryptoAddress {
address: string; address: string;
ticker: string; ticker: string;

View File

@ -2,10 +2,11 @@ import { QRCodeCanvas as QRCode } from 'qrcode.react';
import React from 'react'; import React from 'react';
import CopyableInput from 'pl-fe/components/copyable-input'; import CopyableInput from 'pl-fe/components/copyable-input';
import { CryptoIcon } from 'pl-fe/features/ui/util/async-components';
import { getTitle } from '../utils/coin-db'; import { getTitle } from '../utils/coin-db';
import CryptoIcon from './crypto-icon';
interface IDetailedCryptoAddress { interface IDetailedCryptoAddress {
address: string; address: string;
ticker: string; ticker: string;

View File

@ -3,7 +3,7 @@ import React from 'react';
import Stack from 'pl-fe/components/ui/stack'; import Stack from 'pl-fe/components/ui/stack';
import Widget from 'pl-fe/components/ui/widget'; import Widget from 'pl-fe/components/ui/widget';
import ProfileField from '../profile-field'; import { ProfileField } from '../../util/async-components';
import type { Account } from 'pl-api'; import type { Account } from 'pl-api';

View File

@ -17,8 +17,8 @@ import { usePlFeConfig } from 'pl-fe/hooks/use-pl-fe-config';
import { accountScrobbleQueryOptions } from 'pl-fe/queries/accounts/account-scrobble'; import { accountScrobbleQueryOptions } from 'pl-fe/queries/accounts/account-scrobble';
import { capitalize } from 'pl-fe/utils/strings'; import { capitalize } from 'pl-fe/utils/strings';
import { ProfileField } from '../../util/async-components';
import ProfileFamiliarFollowers from '../profile-familiar-followers'; import ProfileFamiliarFollowers from '../profile-familiar-followers';
import ProfileField from '../profile-field';
import ProfileStats from '../profile-stats'; import ProfileStats from '../profile-stats';
import type { Account } from 'pl-api'; import type { Account } from 'pl-api';

View File

@ -7,14 +7,19 @@ import Markup from 'pl-fe/components/markup';
import { ParsedContent } from 'pl-fe/components/parsed-content'; import { ParsedContent } from 'pl-fe/components/parsed-content';
import HStack from 'pl-fe/components/ui/hstack'; import HStack from 'pl-fe/components/ui/hstack';
import Icon from 'pl-fe/components/ui/icon'; import Icon from 'pl-fe/components/ui/icon';
import CryptoAddress from 'pl-fe/features/crypto-donate/components/crypto-address';
import LightningAddress from 'pl-fe/features/crypto-donate/components/lightning-address';
import coinDB from 'pl-fe/features/crypto-donate/utils/manifest-map';
import Emojify from 'pl-fe/features/emoji/emojify'; import Emojify from 'pl-fe/features/emoji/emojify';
import { CryptoAddress, LightningAddress } from 'pl-fe/features/ui/util/async-components';
import { unescapeHTML } from 'pl-fe/utils/html'; import { unescapeHTML } from 'pl-fe/utils/html';
import type { Account } from 'pl-api'; import type { Account } from 'pl-api';
const getTicker = (value: string): string => (value.match(/\$([a-zA-Z]*)/i) || [])[1]; const getTicker = (value: string): string => (value.match(/\$([a-zA-Z]*)/i) || [])[1];
const isTicker = (value: string): boolean => Boolean(getTicker(value)); const isTicker = (value: string): boolean => {
const ticker = getTicker(value);
return Boolean(ticker) && Boolean(coinDB[ticker.toLowerCase()]);
};
const isZapEmoji = (value: string) => /^\u26A1[\uFE00-\uFE0F]?$/.test(value); const isZapEmoji = (value: string) => /^\u26A1[\uFE00-\uFE0F]?$/.test(value);
const isTimezoneLabel = (value: string) => /^time( |)zone$/i.test(value); const isTimezoneLabel = (value: string) => /^time( |)zone$/i.test(value);
@ -49,7 +54,7 @@ const ProfileField: React.FC<IProfileField> = ({ accountId, field, emojis }) =>
address={unescapeHTML(field.value)} address={unescapeHTML(field.value)}
/> />
); );
} else if (isZapEmoji(field.name)) { } else if (isZapEmoji(field.name) && field.value.includes('@')) {
return <LightningAddress address={unescapeHTML(field.value)} />; return <LightningAddress address={unescapeHTML(field.value)} />;
} }

View File

@ -125,15 +125,13 @@ export const Audio = lazy(() => import('pl-fe/features/audio'));
export const ChatWidget = lazy(() => import('pl-fe/features/chats/components/chat-widget/chat-widget')); export const ChatWidget = lazy(() => import('pl-fe/features/chats/components/chat-widget/chat-widget'));
export const ComposeEditor = lazy(() => import('pl-fe/features/compose/editor')); export const ComposeEditor = lazy(() => import('pl-fe/features/compose/editor'));
export const ComposeForm = lazy(() => import('pl-fe/features/compose/components/compose-form')); export const ComposeForm = lazy(() => import('pl-fe/features/compose/components/compose-form'));
export const CryptoAddress = lazy(() => import('pl-fe/features/crypto-donate/components/crypto-address'));
export const CryptoIcon = lazy(() => import('pl-fe/features/crypto-donate/components/crypto-icon'));
export const DatePicker = lazy(() => import('pl-fe/features/birthdays/date-picker')); export const DatePicker = lazy(() => import('pl-fe/features/birthdays/date-picker'));
export const DropdownNavigation = lazy(() => import('pl-fe/components/dropdown-navigation')); export const DropdownNavigation = lazy(() => import('pl-fe/components/dropdown-navigation'));
export const EmojiPicker = lazy(() => import('pl-fe/features/emoji/components/emoji-picker')); export const EmojiPicker = lazy(() => import('pl-fe/features/emoji/components/emoji-picker'));
export const EventHeader = lazy(() => import('pl-fe/features/event/components/event-header')); export const EventHeader = lazy(() => import('pl-fe/features/event/components/event-header'));
export const LightningAddress = lazy(() => import('pl-fe/features/crypto-donate/components/lightning-address'));
export const MfaForm = lazy(() => import('pl-fe/features/security/mfa-form')); export const MfaForm = lazy(() => import('pl-fe/features/security/mfa-form'));
export const ModalRoot = lazy(() => import('pl-fe/features/ui/components/modal-root')); export const ModalRoot = lazy(() => import('pl-fe/features/ui/components/modal-root'));
export const ProfileField = lazy(() => import('pl-fe/features/ui/components/profile-field'));
export const AccountHoverCard = lazy(() => import('pl-fe/components/account-hover-card')); export const AccountHoverCard = lazy(() => import('pl-fe/components/account-hover-card'));
export const StatusHoverCard = lazy(() => import('pl-fe/components/status-hover-card')); export const StatusHoverCard = lazy(() => import('pl-fe/components/status-hover-card'));
export const Video = lazy(() => import('pl-fe/features/video')); export const Video = lazy(() => import('pl-fe/features/video'));

View File

@ -240,7 +240,6 @@ const Video: React.FC<IVideo> = ({
e.stopPropagation(); e.stopPropagation();
}; };
const handleMouseMove = useCallback(throttle(e => { const handleMouseMove = useCallback(throttle(e => {
if (seek.current && video.current) { if (seek.current && video.current) {
const { x } = getPointerPosition(seek.current, e); const { x } = getPointerPosition(seek.current, e);

View File

@ -3,7 +3,6 @@ import { useState } from 'react';
import { useSettings } from 'pl-fe/stores/settings'; import { useSettings } from 'pl-fe/stores/settings';
import resizeImage from 'pl-fe/utils/resize-image'; import resizeImage from 'pl-fe/utils/resize-image';
import { usePreview } from './use-preview'; import { usePreview } from './use-preview';
interface UseImageFieldOpts { interface UseImageFieldOpts {

View File

@ -19,7 +19,6 @@ const ListEditorModal: React.FC<BaseModalProps & ListEditorModalProps> = ({ list
const { isFetched } = useList(listId); const { isFetched } = useList(listId);
const onClickClose = () => { const onClickClose = () => {
onClose('LIST_EDITOR'); onClose('LIST_EDITOR');
}; };

View File

@ -13,7 +13,6 @@ const messages = defineMessages({
heading: { id: 'column.admin.users', defaultMessage: 'Users' }, heading: { id: 'column.admin.users', defaultMessage: 'Users' },
}); });
const UserIndexPage: React.FC = () => { const UserIndexPage: React.FC = () => {
const [params] = useSearchParams(); const [params] = useSearchParams();
const query = params.get('q') || ''; const query = params.get('q') || '';

View File

@ -1,6 +1,5 @@
import { InfiniteData, useInfiniteQuery, useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { InfiniteData, useInfiniteQuery, useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { importEntities } from 'pl-fe/actions/importer'; import { importEntities } from 'pl-fe/actions/importer';
import { useAccount } from 'pl-fe/api/hooks/accounts/use-account'; import { useAccount } from 'pl-fe/api/hooks/accounts/use-account';
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch'; import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
@ -14,7 +13,6 @@ import { minifyAdminAccount, minifyAdminAccountList } from '../utils/minify-list
import type { AdminPerformAccountActionParams, PaginatedResponse, AdminAccount, AdminGetAccountsParams, PaginationParams, AdminAccountAction } from 'pl-api'; import type { AdminPerformAccountActionParams, PaginatedResponse, AdminAccount, AdminGetAccountsParams, PaginationParams, AdminAccountAction } from 'pl-api';
const useAdminAccounts = makePaginatedResponseQuery( const useAdminAccounts = makePaginatedResponseQuery(
(params: Omit<AdminGetAccountsParams, keyof PaginationParams>) => ['admin', 'accountLists', params], (params: Omit<AdminGetAccountsParams, keyof PaginationParams>) => ['admin', 'accountLists', params],
(client, [params]) => client.admin.accounts.getAccounts(params).then(minifyAdminAccountList), (client, [params]) => client.admin.accounts.getAccounts(params).then(minifyAdminAccountList),