diff --git a/packages/dashboard-v2/package.json b/packages/dashboard-v2/package.json index 80070815..2e17c56b 100644 --- a/packages/dashboard-v2/package.json +++ b/packages/dashboard-v2/package.json @@ -33,7 +33,6 @@ "nanoid": "^3.3.1", "path-browserify": "^1.0.1", "postcss": "^8.4.6", - "pretty-bytes": "^6.0.0", "react": "^17.0.1", "react-dom": "^17.0.1", "react-dropzone": "^12.0.4", diff --git a/packages/dashboard-v2/src/components/CurrentPlan/CurrentPlan.js b/packages/dashboard-v2/src/components/CurrentPlan/CurrentPlan.js index d6df8506..f9bc101a 100644 --- a/packages/dashboard-v2/src/components/CurrentPlan/CurrentPlan.js +++ b/packages/dashboard-v2/src/components/CurrentPlan/CurrentPlan.js @@ -1,9 +1,9 @@ import dayjs from "dayjs"; import relativeTime from "dayjs/plugin/relativeTime"; -import prettyBytes from "pretty-bytes"; import { useUser } from "../../contexts/user"; import useActivePlan from "../../hooks/useActivePlan"; +import humanBytes from "../../lib/humanBytes"; import { ContainerLoadingIndicator } from "../LoadingIndicator"; import LatestPayment from "./LatestPayment"; @@ -33,7 +33,7 @@ const CurrentPlan = () => {

{activePlan.name}

{activePlan.price === 0 && activePlan.limits && ( -

{prettyBytes(activePlan.limits.storageLimit, { binary: true })} without paying a dime! 🎉

+

{humanBytes(activePlan.limits.storageLimit)} without paying a dime! 🎉

)} {activePlan.price !== 0 && (user.subscriptionCancelAtPeriodEnd ? ( diff --git a/packages/dashboard-v2/src/components/CurrentUsage/CurrentUsage.js b/packages/dashboard-v2/src/components/CurrentUsage/CurrentUsage.js index f9dbbc36..c678585b 100644 --- a/packages/dashboard-v2/src/components/CurrentUsage/CurrentUsage.js +++ b/packages/dashboard-v2/src/components/CurrentUsage/CurrentUsage.js @@ -1,5 +1,4 @@ import { useEffect, useMemo, useState } from "react"; -import fileSize from "pretty-bytes"; import { Link } from "gatsby"; import cn from "classnames"; import useSWR from "swr"; @@ -10,6 +9,7 @@ import { ContainerLoadingIndicator } from "../LoadingIndicator"; import { GraphBar } from "./GraphBar"; import { UsageGraph } from "./UsageGraph"; +import humanBytes from "../../lib/humanBytes"; const useUsageData = () => { const { user } = useUser(); @@ -45,7 +45,7 @@ const useUsageData = () => { }; const size = (bytes) => { - const text = fileSize(bytes ?? 0, { maximumFractionDigits: 0, binary: true }); + const text = humanBytes(bytes ?? 0, { precision: 0 }); const [value, unit] = text.split(" "); return { diff --git a/packages/dashboard-v2/src/components/FileList/useFormattedFilesData.js b/packages/dashboard-v2/src/components/FileList/useFormattedFilesData.js index 87bf1af6..10639458 100644 --- a/packages/dashboard-v2/src/components/FileList/useFormattedFilesData.js +++ b/packages/dashboard-v2/src/components/FileList/useFormattedFilesData.js @@ -1,7 +1,7 @@ import { useMemo } from "react"; -import prettyBytes from "pretty-bytes"; import dayjs from "dayjs"; import { DATE_FORMAT } from "../../lib/config"; +import humanBytes from "../../lib/humanBytes"; const parseFileName = (fileName) => { const lastDotIndex = Math.max(0, fileName.lastIndexOf(".")) || Infinity; @@ -16,7 +16,7 @@ const formatItem = ({ size, name: rawFileName, uploadedOn, downloadedOn, ...rest return { ...rest, date, - size: prettyBytes(size), + size: humanBytes(size, { precision: 2 }), type, name, }; diff --git a/packages/dashboard-v2/src/components/Uploader/UploaderItem.js b/packages/dashboard-v2/src/components/Uploader/UploaderItem.js index 46653877..7e19051b 100644 --- a/packages/dashboard-v2/src/components/Uploader/UploaderItem.js +++ b/packages/dashboard-v2/src/components/Uploader/UploaderItem.js @@ -1,6 +1,5 @@ import * as React from "react"; import cn from "classnames"; -import bytes from "pretty-bytes"; import { StatusCodes } from "http-status-codes"; import copy from "copy-text-to-clipboard"; import path from "path-browserify"; @@ -9,6 +8,7 @@ import { ProgressBar } from "./ProgressBar"; import UploaderItemIcon from "./UploaderItemIcon"; import buildUploadErrorMessage from "./buildUploadErrorMessage"; import skynetClient from "../../services/skynetClient"; +import humanBytes from "../../lib/humanBytes"; const getFilePath = (file) => file.webkitRelativePath || file.path || file.name; @@ -88,7 +88,7 @@ export default function UploaderItem({ onUploadStateChange, upload }) {
{upload.status === "uploading" && ( - Uploading {bytes(upload.file.size * upload.progress)} of {bytes(upload.file.size)} + Uploading {humanBytes(upload.file.size * upload.progress)} of {humanBytes(upload.file.size)} )} {upload.status === "enqueued" && Upload in queue, please wait} diff --git a/packages/dashboard-v2/src/components/Uploader/buildUploadErrorMessage.js b/packages/dashboard-v2/src/components/Uploader/buildUploadErrorMessage.js index c41cd717..11cfed4b 100644 --- a/packages/dashboard-v2/src/components/Uploader/buildUploadErrorMessage.js +++ b/packages/dashboard-v2/src/components/Uploader/buildUploadErrorMessage.js @@ -1,5 +1,5 @@ import { getReasonPhrase } from "http-status-codes"; -import bytes from "pretty-bytes"; +import humanBytes from "../../lib/humanBytes"; export default function buildUploadErrorMessage(error) { // The request was made and the server responded with a status code that falls out of the range of 2xx @@ -29,7 +29,7 @@ export default function buildUploadErrorMessage(error) { const matchTusMaxFileSizeError = error.message.match(/upload exceeds maximum size: \d+ > (?\d+)/); if (matchTusMaxFileSizeError) { - return `File exceeds size limit of ${bytes(parseInt(matchTusMaxFileSizeError.groups.limit, 10))}`; + return `File exceeds size limit of ${humanBytes(matchTusMaxFileSizeError.groups.limit, { precision: 0 })}`; } // TODO: We should add a note "our team has been notified" and have some kind of notification with this error. diff --git a/packages/dashboard-v2/src/lib/humanBytes.js b/packages/dashboard-v2/src/lib/humanBytes.js new file mode 100644 index 00000000..ac1fbfa2 --- /dev/null +++ b/packages/dashboard-v2/src/lib/humanBytes.js @@ -0,0 +1,21 @@ +const UNITS = ["B", "kB", "MB", "GB", "TB", "PB", "EB"]; +const BASE = 1024; +const DEFAULT_OPTIONS = { precision: 1 }; + +export default function humanBytes(bytes, { precision } = DEFAULT_OPTIONS) { + if (!Number.isFinite(bytes) || bytes < 0) { + throw new TypeError(`Expected a finite, positive number. Received: ${typeof bytes}: ${bytes}`); + } + + let value = bytes; + let unitIndex = 0; + + while (value >= BASE) { + value /= BASE; + unitIndex += 1; + } + + const localizedValue = value.toLocaleString(undefined, { maximumFractionDigits: precision }); + + return `${localizedValue} ${UNITS[unitIndex]}`; +} diff --git a/packages/dashboard-v2/src/pages/auth/registration.js b/packages/dashboard-v2/src/pages/auth/registration.js index 5764ad6a..899abe50 100644 --- a/packages/dashboard-v2/src/pages/auth/registration.js +++ b/packages/dashboard-v2/src/pages/auth/registration.js @@ -1,5 +1,4 @@ import { useCallback, useState } from "react"; -import bytes from "pretty-bytes"; import AuthLayout from "../../layouts/AuthLayout"; @@ -10,12 +9,13 @@ import { usePortalSettings } from "../../contexts/portal-settings"; import { PlansProvider, usePlans } from "../../contexts/plans"; import { Metadata } from "../../components/Metadata"; import { useUser } from "../../contexts/user"; +import humanBytes from "../../lib/humanBytes"; const FreePortalHeader = () => { const { plans } = usePlans(); const freePlan = plans.find(({ price }) => price === 0); - const freeStorage = freePlan?.limits ? bytes(freePlan.limits?.storageLimit, { binary: true }) : null; + const freeStorage = freePlan?.limits ? humanBytes(freePlan.limits?.storageLimit, { binary: true }) : null; return (
diff --git a/packages/dashboard-v2/src/pages/upgrade.js b/packages/dashboard-v2/src/pages/upgrade.js index 9f69487e..f3a531da 100644 --- a/packages/dashboard-v2/src/pages/upgrade.js +++ b/packages/dashboard-v2/src/pages/upgrade.js @@ -1,5 +1,4 @@ import * as React from "react"; -import bytes from "pretty-bytes"; import styled from "styled-components"; import { useUser } from "../contexts/user"; @@ -14,6 +13,7 @@ import { usePortalSettings } from "../contexts/portal-settings"; import { Alert } from "../components/Alert"; import HighlightedLink from "../components/HighlightedLink"; import { Metadata } from "../components/Metadata"; +import humanBytes from "../lib/humanBytes"; const PAID_PORTAL_BREAKPOINTS = [ { @@ -67,9 +67,9 @@ const Price = ({ price }) => (
); -const bandwidth = (value) => `${bytes(value, { bits: true })}/s`; +const bandwidth = (value) => `${humanBytes(value, { bits: true })}/s`; -const storage = (value) => bytes(value, { binary: true }); +const storage = (value) => humanBytes(value, { binary: true }); const localizedNumber = (value) => value.toLocaleString(); diff --git a/packages/dashboard-v2/yarn.lock b/packages/dashboard-v2/yarn.lock index 0ac39653..bf3123ca 100644 --- a/packages/dashboard-v2/yarn.lock +++ b/packages/dashboard-v2/yarn.lock @@ -12696,11 +12696,6 @@ pretty-bytes@^5.4.1: resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== -pretty-bytes@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-6.0.0.tgz#928be2ad1f51a2e336add8ba764739f9776a8140" - integrity sha512-6UqkYefdogmzqAZWzJ7laYeJnaXDy2/J+ZqiiMtS7t7OfpXWTlaeGMwX8U6EFvPV/YWWEKRkS8hKS4k60WHTOg== - pretty-error@^2.1.1, pretty-error@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.2.tgz#be89f82d81b1c86ec8fdfbc385045882727f93b6"