[web] Add a hook for detecting mobile sized screens (#2490)

This commit is contained in:
Manav Rathi
2024-07-18 21:21:16 +05:30
committed by GitHub
5 changed files with 79 additions and 91 deletions

View File

@@ -5,7 +5,6 @@ import log from "@/base/log";
import { ensure } from "@/utils/ensure";
import { CenteredFlex } from "@ente/shared/components/Container";
import DialogBoxV2 from "@ente/shared/components/DialogBoxV2";
import EnteButton from "@ente/shared/components/EnteButton";
import FormPaper from "@ente/shared/components/Form/FormPaper";
import InfoItem from "@ente/shared/components/Info/InfoItem";
import { EnteMenuItem } from "@ente/shared/components/Menu/EnteMenuItem";
@@ -16,14 +15,7 @@ import ChevronRightIcon from "@mui/icons-material/ChevronRight";
import DeleteIcon from "@mui/icons-material/Delete";
import EditIcon from "@mui/icons-material/Edit";
import KeyIcon from "@mui/icons-material/Key";
import {
Box,
Button,
Stack,
Typography,
styled,
useMediaQuery,
} from "@mui/material";
import { Box, Stack, Typography, styled, useMediaQuery } from "@mui/material";
import { t } from "i18next";
import React, { useCallback, useEffect, useState } from "react";
import {
@@ -265,8 +257,34 @@ const ManagePasskeyDrawer: React.FC<ManagePasskeyDrawerProps> = ({
passkey,
onUpdateOrDeletePasskey,
}) => {
const { setDialogBoxAttributesV2 } = useAppContext();
const [showRenameDialog, setShowRenameDialog] = useState(false);
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
const showDeleteConfirmationDialog = useCallback(() => {
const handleDelete = async (setLoading: (value: boolean) => void) => {
setLoading(true);
try {
await deletePasskey(ensure(token), ensure(passkey).id);
onUpdateOrDeletePasskey();
} catch (e) {
log.error("Failed to delete passkey", e);
} finally {
setLoading(false);
}
};
setDialogBoxAttributesV2({
title: t("delete_passkey"),
content: t("delete_passkey_confirmation"),
proceed: {
text: t("DELETE"),
action: handleDelete,
variant: "critical",
},
close: { text: t("CANCEL") },
});
}, [token, passkey, onUpdateOrDeletePasskey, setDialogBoxAttributesV2]);
return (
<>
@@ -297,9 +315,7 @@ const ManagePasskeyDrawer: React.FC<ManagePasskeyDrawerProps> = ({
/>
<MenuItemDivider />
<EnteMenuItem
onClick={() => {
setShowDeleteDialog(true);
}}
onClick={showDeleteConfirmationDialog}
startIcon={<DeleteIcon />}
label={t("delete_passkey")}
color="critical"
@@ -321,19 +337,6 @@ const ManagePasskeyDrawer: React.FC<ManagePasskeyDrawerProps> = ({
}}
/>
)}
{token && passkey && (
<DeletePasskeyDialog
open={showDeleteDialog}
onClose={() => setShowDeleteDialog(false)}
token={token}
passkey={passkey}
onDeletePasskey={() => {
setShowDeleteDialog(false);
onUpdateOrDeletePasskey();
}}
/>
)}
</>
);
};
@@ -387,63 +390,3 @@ const RenamePasskeyDialog: React.FC<RenamePasskeyDialogProps> = ({
</DialogBoxV2>
);
};
interface DeletePasskeyDialogProps {
/** If `true`, then the dialog is shown. */
open: boolean;
/** Callback to invoke when the dialog wants to be closed. */
onClose: () => void;
/** Auth token for API requests. */
token: string;
/** The {@link Passkey} to delete. */
passkey: Passkey;
/** Callback to invoke when the passkey is deleted. */
onDeletePasskey: () => void;
}
const DeletePasskeyDialog: React.FC<DeletePasskeyDialogProps> = ({
open,
onClose,
token,
passkey,
onDeletePasskey,
}) => {
const [isDeleting, setIsDeleting] = useState(false);
const fullScreen = useMediaQuery("(max-width: 428px)");
const handleConfirm = async () => {
setIsDeleting(true);
try {
await deletePasskey(token, passkey.id);
onDeletePasskey();
} catch (e) {
log.error("Failed to delete passkey", e);
} finally {
setIsDeleting(false);
}
};
return (
<DialogBoxV2
fullWidth
{...{ open, onClose, fullScreen }}
attributes={{ title: t("delete_passkey") }}
>
<Stack spacing={"8px"}>
<Typography>{t("delete_passkey_confirmation")}</Typography>
<EnteButton
type="submit"
size="large"
color="critical"
loading={isDeleting}
onClick={handleConfirm}
>
{t("DELETE")}
</EnteButton>
<Button size="large" color={"secondary"} onClick={onClose}>
{t("CANCEL")}
</Button>
</Stack>
</DialogBoxV2>
);
};

View File

@@ -0,0 +1,10 @@
import { useMediaQuery, useTheme } from "@mui/material";
/**
* Return true if the screen width is classified as a "mobile" size.
*
* We use the MUI "sm" (small, 600px) breakpoint as the cutoff. This hook will
* return true if the size of the window's width is less than 600px.
*/
export const useIsMobileWidth = () =>
useMediaQuery(useTheme().breakpoints.down("sm"));

View File

@@ -1,3 +1,4 @@
import { useIsMobileWidth } from "@/base/hooks";
import { ensureOk } from "@/base/http";
import { getKV, removeKV, setKV } from "@/base/kv";
import log from "@/base/log";
@@ -11,7 +12,6 @@ import {
InputAdornment,
Link,
TextField,
useMediaQuery,
type ModalProps,
} from "@mui/material";
import { useFormik } from "formik";
@@ -33,7 +33,7 @@ interface DevSettingsProps {
* See: [Note: Configuring custom server].
*/
export const DevSettings: React.FC<DevSettingsProps> = ({ open, onClose }) => {
const fullScreen = useMediaQuery("(max-width: 428px)");
const fullScreen = useIsMobileWidth();
const handleDialogClose: ModalProps["onClose"] = (_, reason: string) => {
// Don't close on backdrop clicks.

View File

@@ -1,4 +1,5 @@
import { ensureElectron } from "@/base/electron";
import { useIsMobileWidth } from "@/base/hooks";
import { ut } from "@/base/i18n";
import ArrowForward from "@mui/icons-material/ArrowForward";
import {
@@ -10,7 +11,6 @@ import {
DialogTitle,
Typography,
styled,
useMediaQuery,
} from "@mui/material";
import React, { useEffect } from "react";
import { didShowWhatsNew } from "../services/changelog";
@@ -29,7 +29,7 @@ interface WhatsNewProps {
* last time this dialog was shown.
*/
export const WhatsNew: React.FC<WhatsNewProps> = ({ open, onClose }) => {
const fullScreen = useMediaQuery("(max-width: 428px)");
const fullScreen = useIsMobileWidth();
useEffect(() => {
if (open) void didShowWhatsNew(ensureElectron());

View File

@@ -1,5 +1,12 @@
import type { ButtonProps } from "@mui/material";
/**
* Customize the properties of the dialog box.
*
* Our custom dialog box helpers are meant for small message boxes, usually
* meant to confirm some user action. If more customization is needed, it might
* be a better idea to reach out for a bespoke MUI {@link DialogBox} instead.
*/
export interface DialogBoxAttributesV2 {
icon?: React.ReactNode;
/**
@@ -13,15 +20,43 @@ export interface DialogBoxAttributesV2 {
title?: React.ReactNode;
staticBackdrop?: boolean;
nonClosable?: boolean;
/**
* The dialog's content.
*/
content?: React.ReactNode;
/**
* Customize the cancel (dismiss) action button offered by the dialog box.
*
* Usually dialog boxes should have a cancel action, but this can be skipped
* to only show one of the other types of buttons.
*/
close?: {
/** The string to use as the label for the cancel button. */
text?: string;
/** The color of the button. */
variant?: ButtonProps["color"];
/**
* The function to call when the user cancels.
*
* If provided, this callback is invoked before closing the dialog.
*/
action?: () => void;
};
/**
* Customize the primary action button offered by the dialog box.
*/
proceed?: {
/** The string to use as the label for the primary action. */
text: string;
action: (setLoading?: (value: boolean) => void) => void | Promise<void>;
/**
* The function to call when the user presses the primary action button.
*
* It is passed a {@link setLoading} function that can be used to show
* or hide loading indicator or the primary action button.
*/
action:
| (() => void | Promise<void>)
| ((setLoading: (value: boolean) => void) => void | Promise<void>);
variant?: ButtonProps["color"];
disabled?: boolean;
};