[web] dynamic free storage - prepare for changes (#1637)

This commit is contained in:
Manav Rathi 2024-05-07 11:15:29 +05:30 committed by GitHub
commit ee8a6cfb55
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 149 additions and 87 deletions

View file

@ -0,0 +1,39 @@
name: "Update Crowdin translations (web)"
# This is a variant of web-crowdin.yml that also uploads the translated strings
# (in addition to the source strings). This allows us to change the strings in
# our source code for an automated refactoring (e.g. renaming a key), and then
# run this workflow to update the data in Crowdin taking our source code as the
# source of truth.
on:
# Only allow running manually.
workflow_dispatch:
jobs:
synchronize-with-crowdin:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Crowdin's action
uses: crowdin/github-action@v1
with:
base_path: "web/"
config: "web/crowdin.yml"
upload_sources: true
# This is what differs from web-crowdin.yml
upload_translations: true
download_translations: true
localization_branch_name: translations/web
create_pull_request: true
skip_untranslated_strings: true
pull_request_title: "[web] Updated translations"
pull_request_body: "Updated translations from [Crowdin](https://crowdin.com/project/ente-photos-web)"
pull_request_base_branch_name: "main"
project_id: 569613
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}

View file

@ -83,7 +83,8 @@ export default function Index() {
fontWeight: "normal",
}}
>
Enter this code on <b>Ente Photos</b> to pair this screen
Enter this code on <b>Ente Photos</b> to pair this
screen
</h1>
<div
style={{

View file

@ -19,7 +19,7 @@ import {
} from "react-window";
import { Duplicate } from "services/deduplicationService";
import { EnteFile } from "types/file";
import { convertBytesToHumanReadable } from "utils/file";
import { formattedByteSize } from "utils/units";
export enum ITEM_TYPE {
TIME = "TIME",
@ -310,8 +310,7 @@ export function DedupePhotoList({
*/
<SizeAndCountContainer span={columns}>
{listItem.fileCount} {t("FILES")},{" "}
{convertBytesToHumanReadable(listItem.fileSize || 0)}{" "}
{t("EACH")}
{formattedByteSize(listItem.fileSize || 0)} {t("EACH")}
</SizeAndCountContainer>
);
case ITEM_TYPE.FILE: {

View file

@ -22,9 +22,9 @@ import {
areEqual,
} from "react-window";
import { EnteFile } from "types/file";
import { convertBytesToHumanReadable } from "utils/file";
import { handleSelectCreator } from "utils/photoFrame";
import { PublicCollectionGalleryContext } from "utils/publicCollectionGallery";
import { formattedByteSize } from "utils/units";
const A_DAY = 24 * 60 * 60 * 1000;
const FOOTER_HEIGHT = 90;
@ -829,8 +829,7 @@ export function PhotoList({
return (
<SizeAndCountContainer span={columns}>
{listItem.fileCount} {t("FILES")},{" "}
{convertBytesToHumanReadable(listItem.fileSize || 0)}{" "}
{t("EACH")}
{formattedByteSize(listItem.fileSize || 0)} {t("EACH")}
</SizeAndCountContainer>
);
case ITEM_TYPE.FILE: {

View file

@ -7,8 +7,8 @@ import VideocamOutlined from "@mui/icons-material/VideocamOutlined";
import Box from "@mui/material/Box";
import { useEffect, useState } from "react";
import { EnteFile } from "types/file";
import { makeHumanReadableStorage } from "utils/billing";
import { changeFileName, updateExistingFilePubMetadata } from "utils/file";
import { formattedByteSize } from "utils/units";
import { FileNameEditDialog } from "./FileNameEditDialog";
import InfoItem from "./InfoItem";
@ -33,7 +33,7 @@ const getCaption = (file: EnteFile, parsedExifData) => {
captionParts.push(resolution);
}
if (fileSize) {
captionParts.push(makeHumanReadableStorage(fileSize));
captionParts.push(formattedByteSize(fileSize));
}
return (
<FlexWrapper gap={1}>

View file

@ -1,7 +1,7 @@
import { SpaceBetweenFlex } from "@ente/shared/components/Container";
import { Box, Typography } from "@mui/material";
import { t } from "i18next";
import { makeHumanReadableStorage } from "utils/billing";
import { formattedStorageByteSize } from "utils/units";
import { Progressbar } from "../../styledComponents";
@ -19,7 +19,7 @@ export function IndividualUsageSection({ usage, storage, fileCount }: Iprops) {
marginTop: 1.5,
}}
>
<Typography variant="mini">{`${makeHumanReadableStorage(
<Typography variant="mini">{`${formattedStorageByteSize(
storage - usage,
)} ${t("FREE")}`}</Typography>
<Typography variant="mini" fontWeight={"bold"}>

View file

@ -1,6 +1,6 @@
import { Box, styled, Typography } from "@mui/material";
import { t } from "i18next";
import { convertBytesToGBs, makeHumanReadableStorage } from "utils/billing";
import { bytesInGB, formattedStorageByteSize } from "utils/units";
const MobileSmallBox = styled(Box)`
display: none;
@ -30,9 +30,9 @@ export default function StorageSection({ usage, storage }: Iprops) {
fontWeight={"bold"}
sx={{ fontSize: "24px", lineHeight: "30px" }}
>
{`${makeHumanReadableStorage(usage, { roundUp: true })} ${t(
{`${formattedStorageByteSize(usage, { round: true })} ${t(
"OF",
)} ${makeHumanReadableStorage(storage)} ${t("USED")}`}
)} ${formattedStorageByteSize(storage)} ${t("USED")}`}
</Typography>
</DefaultBox>
<MobileSmallBox>
@ -40,9 +40,7 @@ export default function StorageSection({ usage, storage }: Iprops) {
fontWeight={"bold"}
sx={{ fontSize: "24px", lineHeight: "30px" }}
>
{`${convertBytesToGBs(usage)} / ${convertBytesToGBs(
storage,
)} ${t("GB")} ${t("USED")}`}
{`${bytesInGB(usage)} / ${bytesInGB(storage)} ${t("GB")} ${t("USED")}`}
</Typography>
</MobileSmallBox>
</Box>

View file

@ -5,11 +5,8 @@ import Box from "@mui/material/Box";
import Typography from "@mui/material/Typography";
import { t } from "i18next";
import { Trans } from "react-i18next";
import {
convertBytesToGBs,
hasAddOnBonus,
isSubscriptionCancelled,
} from "utils/billing";
import { hasAddOnBonus, isSubscriptionCancelled } from "utils/billing";
import { bytesInGB } from "utils/units";
import { ManageSubscription } from "../manageSubscription";
import { PeriodToggler } from "../periodToggler";
import Plans from "../plans";
@ -35,8 +32,7 @@ export default function PaidSubscriptionPlanSelectorCard({
{t("SUBSCRIPTION")}
</Typography>
<Typography variant="small" color={"text.muted"}>
{convertBytesToGBs(subscription.storage, 2)}{" "}
{t("GB")}
{bytesInGB(subscription.storage, 2)} {t("GB")}
</Typography>
</Box>
<IconButton onClick={closeModal} color="secondary">
@ -50,7 +46,7 @@ export default function PaidSubscriptionPlanSelectorCard({
<Trans
i18nKey="CURRENT_USAGE"
values={{
usage: `${convertBytesToGBs(usage, 2)} ${t("GB")}`,
usage: `${bytesInGB(usage, 2)} ${t("GB")}`,
}}
/>
</Typography>

View file

@ -2,7 +2,7 @@ import { SpaceBetweenFlex } from "@ente/shared/components/Container";
import { Box, styled, Typography } from "@mui/material";
import { Trans } from "react-i18next";
import { makeHumanReadableStorage } from "utils/billing";
import { formattedStorageByteSize } from "utils/units";
const RowContainer = styled(SpaceBetweenFlex)(({ theme }) => ({
// gap: theme.spacing(1.5),
@ -24,7 +24,7 @@ export function BFAddOnRow({ bonusData, closeModal }) {
<Trans
i18nKey={"ADD_ON_AVAILABLE_TILL"}
values={{
storage: makeHumanReadableStorage(
storage: formattedStorageByteSize(
bonus.storage,
),
date: bonus.validTill,

View file

@ -6,11 +6,8 @@ import { Badge } from "components/Badge";
import { PLAN_PERIOD } from "constants/gallery";
import { t } from "i18next";
import { Plan, Subscription } from "types/billing";
import {
convertBytesToGBs,
hasPaidSubscription,
isUserSubscribedPlan,
} from "utils/billing";
import { hasPaidSubscription, isUserSubscribedPlan } from "utils/billing";
import { bytesInGB } from "utils/units";
interface Iprops {
plan: Plan;
@ -66,7 +63,7 @@ export function PlanRow({
<PlanRowContainer>
<TopAlignedFluidContainer>
<Typography variant="h1" fontWeight={"bold"}>
{convertBytesToGBs(plan.storage)}
{bytesInGB(plan.storage)}
</Typography>
<FlexWrapper flexWrap={"wrap"} gap={1}>
<Typography variant="h3" color="text.muted">

View file

@ -31,44 +31,6 @@ enum RESPONSE_STATUS {
fail = "fail",
}
const StorageUnits = ["B", "KB", "MB", "GB", "TB"];
const ONE_GB = 1024 * 1024 * 1024;
export function convertBytesToGBs(bytes: number, precision = 0): string {
return (bytes / (1024 * 1024 * 1024)).toFixed(precision);
}
export function makeHumanReadableStorage(
bytes: number,
{ roundUp } = { roundUp: false },
): string {
if (bytes <= 0) {
return `0 ${t("STORAGE_UNITS.MB")}`;
}
const i = Math.floor(Math.log(bytes) / Math.log(1024));
let quantity = bytes / Math.pow(1024, i);
let unit = StorageUnits[i];
if (quantity > 100 && unit !== "GB") {
quantity /= 1024;
unit = StorageUnits[i + 1];
}
quantity = Number(quantity.toFixed(1));
if (bytes >= 10 * ONE_GB) {
if (roundUp) {
quantity = Math.ceil(quantity);
} else {
quantity = Math.round(quantity);
}
}
return `${quantity} ${t(`STORAGE_UNITS.${unit}`)}`;
}
export function hasPaidSubscription(subscription: Subscription) {
return (
subscription &&
@ -160,9 +122,8 @@ export function isSubscriptionPastDue(subscription: Subscription) {
);
}
export function isPopularPlan(plan: Plan) {
return plan.storage === 100 * ONE_GB;
}
export const isPopularPlan = (plan: Plan) =>
plan.storage === 100 * 1024 * 1024 * 1024; /* 100 GB */
export async function updateSubscription(
plan: Plan,

View file

@ -103,19 +103,6 @@ export async function getUpdatedEXIFFileForDownload(
}
}
export function convertBytesToHumanReadable(
bytes: number,
precision = 2,
): string {
if (bytes === 0 || isNaN(bytes)) {
return "0 MB";
}
const i = Math.floor(Math.log(bytes) / Math.log(1024));
const sizes = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
return (bytes / Math.pow(1024, i)).toFixed(precision) + " " + sizes[i];
}
export async function downloadFile(file: EnteFile) {
try {
const fileReader = new FileReader();

View file

@ -0,0 +1,85 @@
import { t } from "i18next";
const StorageUnits = ["B", "KB", "MB", "GB", "TB"];
/**
* Convert the given number of {@link bytes} to their equivalent GB string with
* {@link precision}.
*
* The returned string does not have the GB suffix.
*/
export const bytesInGB = (bytes: number, precision = 0): string =>
(bytes / (1024 * 1024 * 1024)).toFixed(precision);
/**
* Convert the given number of {@link bytes} to a user visible string in an
* appropriately sized unit.
*
* The returned string includes the (localized) unit suffix, e.g. "TB".
*
* @param precision Modify the number of digits after the decimal point.
* Defaults to 2.
*/
export function formattedByteSize(bytes: number, precision = 2): string {
if (bytes === 0 || isNaN(bytes)) {
return "0 MB";
}
const i = Math.floor(Math.log(bytes) / Math.log(1024));
const sizes = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
return (bytes / Math.pow(1024, i)).toFixed(precision) + " " + sizes[i];
}
interface FormattedStorageByteSizeOptions {
/**
* If `true` then round up the fractional quantity we obtain when dividing
* the number of bytes by the number of bytes in the unit that got chosen.
*
* The default behaviour is to take the ceiling.
*/
round?: boolean;
}
/**
* Convert the given number of storage {@link bytes} to a user visible string in
* an appropriately sized unit.
*
* This differs from {@link formattedByteSize} in that while
* {@link formattedByteSize} is meant for arbitrary byte sizes, this function
* has a few additional beautification heuristics that we want to apply when
* displaying the "storage size" (in different contexts) as opposed to, say, a
* generic "file size".
*
* @param options
*
* @return A user visible string, including the localized unit suffix.
*/
export const formattedStorageByteSize = (
bytes: number,
options?: FormattedStorageByteSizeOptions,
): string => {
if (bytes <= 0) {
return `0 ${t("STORAGE_UNITS.MB")}`;
}
const i = Math.floor(Math.log(bytes) / Math.log(1024));
let quantity = bytes / Math.pow(1024, i);
let unit = StorageUnits[i];
if (quantity > 100 && unit !== "GB") {
quantity /= 1024;
unit = StorageUnits[i + 1];
}
quantity = Number(quantity.toFixed(1));
if (bytes >= 10 * 1024 * 1024 * 1024 /* 10 GB */) {
if (options?.round) {
quantity = Math.ceil(quantity);
} else {
quantity = Math.round(quantity);
}
}
return `${quantity} ${t(`STORAGE_UNITS.${unit}`)}`;
};