mirror of
https://github.com/aljazceru/rabbit.git
synced 2025-12-18 06:24:25 +01:00
feat: show number with SI prefix
This commit is contained in:
@@ -35,6 +35,7 @@ import usePubkey from '@/nostr/usePubkey';
|
||||
import useReactions from '@/nostr/useReactions';
|
||||
import useReposts from '@/nostr/useReposts';
|
||||
import ensureNonNull from '@/utils/ensureNonNull';
|
||||
import { formatSiPrefix } from '@/utils/siPrefix';
|
||||
import timeout from '@/utils/timeout';
|
||||
|
||||
export type ActionProps = {
|
||||
@@ -129,7 +130,9 @@ const ReactionAction = (props: { event: NostrEvent }) => {
|
||||
</Show>
|
||||
</button>
|
||||
<Show when={!config().hideCount && !config().showEmojiReaction && reactions().length > 0}>
|
||||
<div class="text-sm text-zinc-400">{reactions().length}</div>
|
||||
<div class="text-sm text-zinc-400">
|
||||
{formatSiPrefix(reactions().length, { minDigits: 4 })}
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
<Show when={config().useEmojiReaction}>
|
||||
@@ -205,7 +208,9 @@ const RepostAction = (props: { event: NostrEvent }) => {
|
||||
<ArrowPathRoundedSquare />
|
||||
</button>
|
||||
<Show when={!config().hideCount && reposts().length > 0}>
|
||||
<div class="text-sm text-zinc-400">{reposts().length}</div>
|
||||
<div class="text-sm text-zinc-400">
|
||||
{formatSiPrefix(reposts().length, { minDigits: 4 })}
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -13,6 +13,7 @@ import { zapReceipt } from '@/nostr/event';
|
||||
import useProfile from '@/nostr/useProfile';
|
||||
import { fetchLnurlPayRequestMetadata, getLnurlPayRequestUrl, verifyZapReceipt } from '@/nostr/zap';
|
||||
import ensureNonNull from '@/utils/ensureNonNull';
|
||||
import { formatSiPrefix } from '@/utils/siPrefix';
|
||||
|
||||
export type ZapReceiptProps = {
|
||||
event: NostrEvent;
|
||||
@@ -75,7 +76,7 @@ const ZapReceipt: Component<ZapReceiptProps> = (props) => {
|
||||
<div class="h-4 w-4 shrink-0 text-amber-500" aria-hidden="true">
|
||||
<Bolt />
|
||||
</div>
|
||||
<div class="mt-[-2px] shrink-0 text-xs">{event().amountSats()}</div>
|
||||
<div class="mt-[-2px] shrink-0 text-xs">{formatSiPrefix(event().amountSats())}</div>
|
||||
</div>
|
||||
<div class="notification-user flex gap-1 overflow-hidden">
|
||||
<div class="author-icon h-5 w-5 shrink-0 overflow-hidden rounded">
|
||||
|
||||
43
src/utils/siPrefix.test.ts
Normal file
43
src/utils/siPrefix.test.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import assert from 'assert';
|
||||
|
||||
import { describe, it } from 'vitest';
|
||||
|
||||
import siPrefix, { formatSiPrefix, type SIPrefix } from '@/utils/siPrefix';
|
||||
|
||||
describe('siPrefix', () => {
|
||||
it.each<{ given: number; expected: SIPrefix }>([
|
||||
{ given: 0, expected: { value: 0, prefix: null } },
|
||||
{ given: 1, expected: { value: 1, prefix: null } },
|
||||
{ given: 999, expected: { value: 999, prefix: null } },
|
||||
{ given: 1000, expected: { value: 1000, prefix: null } },
|
||||
{ given: 9999, expected: { value: 9999, prefix: null } },
|
||||
{ given: 10000, expected: { value: 10, prefix: 'k' } },
|
||||
{ given: 12345, expected: { value: 12.35, prefix: 'k' } },
|
||||
{ given: 98765, expected: { value: 98.77, prefix: 'k' } },
|
||||
{ given: 986999, expected: { value: 987, prefix: 'k' } },
|
||||
{ given: 999999, expected: { value: 1, prefix: 'M' } },
|
||||
{ given: 1e6, expected: { value: 1, prefix: 'M' } },
|
||||
])('should return $expected for $given', ({ given, expected }) => {
|
||||
const actual = siPrefix(given, { precision: 2, minDigits: 5 });
|
||||
assert.deepStrictEqual(actual, expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatSiPrefix', () => {
|
||||
it.each<{ given: number; expected: string }>([
|
||||
{ given: 0, expected: '0' },
|
||||
{ given: 1, expected: '1' },
|
||||
{ given: 999, expected: '999' },
|
||||
{ given: 1000, expected: '1000' },
|
||||
{ given: 9999, expected: '9999' },
|
||||
{ given: 10000, expected: '10k' },
|
||||
{ given: 12345, expected: '12.4k' },
|
||||
{ given: 98765, expected: '98.8k' },
|
||||
{ given: 986999, expected: '987k' },
|
||||
{ given: 998999, expected: '999k' },
|
||||
{ given: 999999, expected: '1M' },
|
||||
])('should return $expected for $given', ({ given, expected }) => {
|
||||
const actual = formatSiPrefix(given);
|
||||
assert.deepStrictEqual(actual, expected);
|
||||
});
|
||||
});
|
||||
50
src/utils/siPrefix.ts
Normal file
50
src/utils/siPrefix.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
const Prefixes = ['k', 'M', 'G', 'T', 'P'] as const;
|
||||
|
||||
export type SIPrefix = {
|
||||
value: number;
|
||||
prefix: (typeof Prefixes)[number] | null;
|
||||
};
|
||||
|
||||
const siPrefix = (
|
||||
value: number,
|
||||
options?: { precision?: number; minDigits?: number },
|
||||
): SIPrefix => {
|
||||
const precision = options?.precision ?? 3;
|
||||
if (precision <= 0) throw new Error(`precision is too small: ${precision}`);
|
||||
|
||||
const minDigits = options?.minDigits ?? 4;
|
||||
const minValue = 10 ** (minDigits - 1);
|
||||
if (value < minValue) return { value, prefix: null };
|
||||
|
||||
const divider = 10 ** (Math.floor(Math.log10(value) / 3) * 3);
|
||||
const divided = value / divider;
|
||||
let rounded = Math.round(divided * 10 ** precision) / 10 ** precision;
|
||||
let index = Math.max(Math.floor(Math.log10(value) / 3) - 1, 0);
|
||||
if (rounded >= 1000) {
|
||||
rounded /= 1000;
|
||||
index += 1;
|
||||
}
|
||||
if (index >= Prefixes.length) return { value, prefix: null };
|
||||
|
||||
return { value: rounded, prefix: Prefixes[index] };
|
||||
};
|
||||
|
||||
export const formatSiPrefix = (
|
||||
inputValue: number,
|
||||
options?: { digits?: number; minDigits?: number },
|
||||
): string => {
|
||||
const { value, prefix } = siPrefix(inputValue, {
|
||||
precision: 2,
|
||||
minDigits: options?.minDigits ?? 5,
|
||||
});
|
||||
if (prefix == null) return `${inputValue}`;
|
||||
|
||||
const digits = options?.digits ?? 3;
|
||||
const wholeNumberDigits = Math.floor(Math.log10(value)) + 1;
|
||||
const precision = Math.max(digits - wholeNumberDigits, 0);
|
||||
const rounded = Math.round(value * 10 ** precision) / 10 ** precision;
|
||||
|
||||
return `${rounded}${prefix}`;
|
||||
};
|
||||
|
||||
export default siPrefix;
|
||||
Reference in New Issue
Block a user