diff --git a/rsbuild.config.ts b/rsbuild.config.ts index 3191db5c..b9076c33 100644 --- a/rsbuild.config.ts +++ b/rsbuild.config.ts @@ -1,6 +1,6 @@ +import { execSync } from "node:child_process"; import { defineConfig } from "@rsbuild/core"; import { pluginReact } from "@rsbuild/plugin-react"; -import { execSync } from "node:child_process"; let hash = ""; diff --git a/src/App.tsx b/src/App.tsx index 8c15dc97..b512b628 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,6 +4,7 @@ import { CommandPalette } from "@components/CommandPalette.tsx"; import { DeviceSelector } from "@components/DeviceSelector.tsx"; import { DialogManager } from "@components/Dialog/DialogManager"; import { NewDeviceDialog } from "@components/Dialog/NewDeviceDialog.tsx"; +import { KeyBackupReminder } from "@components/KeyBackupReminder"; import { Toaster } from "@components/Toaster.tsx"; import Footer from "@components/UI/Footer.tsx"; import { ThemeController } from "@components/generic/ThemeController.tsx"; @@ -11,7 +12,6 @@ import { useAppStore } from "@core/stores/appStore.ts"; import { useDeviceStore } from "@core/stores/deviceStore.ts"; import { Dashboard } from "@pages/Dashboard/index.tsx"; import { MapProvider } from "react-map-gl"; -import { KeyBackupReminder } from "@components/KeyBackupReminder"; export const App = (): JSX.Element => { const { getDevice } = useDeviceStore(); diff --git a/src/components/Dialog/DialogManager.tsx b/src/components/Dialog/DialogManager.tsx index afebdf5d..e1769751 100644 --- a/src/components/Dialog/DialogManager.tsx +++ b/src/components/Dialog/DialogManager.tsx @@ -1,11 +1,11 @@ import { RemoveNodeDialog } from "@app/components/Dialog/RemoveNodeDialog.tsx"; import { DeviceNameDialog } from "@components/Dialog/DeviceNameDialog.tsx"; import { ImportDialog } from "@components/Dialog/ImportDialog.tsx"; +import { PkiBackupDialog } from "@components/Dialog/PKIBackupDialog"; import { QRDialog } from "@components/Dialog/QRDialog.tsx"; import { RebootDialog } from "@components/Dialog/RebootDialog.tsx"; import { ShutdownDialog } from "@components/Dialog/ShutdownDialog.tsx"; import { useDevice } from "@core/stores/deviceStore.ts"; -import { PkiBackupDialog } from "@components/Dialog/PKIBackupDialog"; export const DialogManager = (): JSX.Element => { const { channels, config, dialog, setDialogOpen } = useDevice(); diff --git a/src/components/Dialog/NewDeviceDialog.tsx b/src/components/Dialog/NewDeviceDialog.tsx index e3330cbf..c84bdfbb 100644 --- a/src/components/Dialog/NewDeviceDialog.tsx +++ b/src/components/Dialog/NewDeviceDialog.tsx @@ -11,7 +11,6 @@ import { DialogHeader, DialogTitle, } from "@components/UI/Dialog.tsx"; -import { AlertCircle, InfoIcon, } from "lucide-react"; import { Tabs, TabsContent, @@ -19,8 +18,9 @@ import { TabsTrigger, } from "@components/UI/Tabs.tsx"; import { Subtle } from "@components/UI/Typography/Subtle.tsx"; -import { Link } from "../UI/Typography/Link"; +import { AlertCircle, InfoIcon } from "lucide-react"; import { Fragment } from "react/jsx-runtime"; +import { Link } from "../UI/Typography/Link"; export interface TabElementProps { closeDialog: () => void; @@ -50,26 +50,25 @@ const links: { [key: string]: string } = { "https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts", }; -const listFormatter = new Intl.ListFormat('en', { - style: 'long', - type: 'conjunction' +const listFormatter = new Intl.ListFormat("en", { + style: "long", + type: "conjunction", }); const ErrorMessage = ({ missingFeatures }: FeatureErrorProps) => { if (missingFeatures.length === 0) return null; - const browserFeatures = missingFeatures.filter(feature => feature !== "Secure Context"); + const browserFeatures = missingFeatures.filter( + (feature) => feature !== "Secure Context", + ); const needsSecureContext = missingFeatures.includes("Secure Context"); const formatFeatureList = (features: string[]) => { const parts = listFormatter.formatToParts(features); return parts.map((part) => { - if (part.type === 'element') { + if (part.type === "element") { return ( - + {part.value} ); @@ -94,12 +93,8 @@ const ErrorMessage = ({ missingFeatures }: FeatureErrorProps) => { <> {browserFeatures.length > 0 && " Additionally, it"} {browserFeatures.length === 0 && "This application"} requires a{" "} - - secure context - - . Please connect using HTTPS or localhost. + secure context. + Please connect using HTTPS or localhost. )}

@@ -146,10 +141,7 @@ export const NewDeviceDialog = ({ {tabs.map((tab) => ( - + {tab.label} ))} @@ -157,7 +149,9 @@ export const NewDeviceDialog = ({ {tabs.map((tab) => (
- {tab.isDisabled ? : null} + {tab.isDisabled ? ( + + ) : null} onOpenChange(false)} />
diff --git a/src/components/Form/FormPasswordGenerator.tsx b/src/components/Form/FormPasswordGenerator.tsx index 784086fb..622e502e 100644 --- a/src/components/Form/FormPasswordGenerator.tsx +++ b/src/components/Form/FormPasswordGenerator.tsx @@ -2,12 +2,12 @@ import type { BaseFormBuilderProps, GenericFormElementProps, } from "@components/Form/DynamicForm.tsx"; +import type { ButtonVariant } from "@components/UI/Button"; import { Generator } from "@components/UI/Generator.tsx"; import { Eye, EyeOff } from "lucide-react"; import type { ChangeEventHandler, MouseEventHandler } from "react"; import { useState } from "react"; import { Controller, type FieldValues } from "react-hook-form"; -import type { ButtonVariant } from "@components/UI/Button"; export interface PasswordGeneratorProps extends BaseFormBuilderProps { type: "passwordGenerator"; @@ -44,9 +44,9 @@ export function PasswordGenerator({ action={ field.hide ? { - icon: passwordShown ? EyeOff : Eye, - onClick: togglePasswordVisiblity, - } + icon: passwordShown ? EyeOff : Eye, + onClick: togglePasswordVisiblity, + } : undefined } devicePSKBitCount={field.devicePSKBitCount} diff --git a/src/components/PageComponents/Channel.tsx b/src/components/PageComponents/Channel.tsx index ef316c91..dcd69c17 100644 --- a/src/components/PageComponents/Channel.tsx +++ b/src/components/PageComponents/Channel.tsx @@ -23,7 +23,8 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => { channel?.settings?.psk.length ?? 16, ); const [validationText, setValidationText] = useState(); - const [preSharedDialogOpen, setPreSharedDialogOpen] = useState(false); + const [preSharedDialogOpen, setPreSharedDialogOpen] = + useState(false); const onSubmit = (data: ChannelValidation) => { const channel = new Protobuf.Channel.Channel({ @@ -99,12 +100,13 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => { psk: pass, positionEnabled: channel?.settings?.moduleSettings?.positionPrecision !== - undefined && + undefined && channel?.settings?.moduleSettings?.positionPrecision > 0, preciseLocation: channel?.settings?.moduleSettings?.positionPrecision === 32, positionPrecision: - channel?.settings?.moduleSettings?.positionPrecision === undefined + channel?.settings?.moduleSettings?.positionPrecision === + undefined ? 10 : channel?.settings?.moduleSettings?.positionPrecision, }, @@ -133,12 +135,19 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => { type: "passwordGenerator", name: "settings.psk", label: "Pre-Shared Key", - description: "Supported PSK lengths: 256-bit, 128-bit, 8-bit, Empty (0-bit)", + description: + "Supported PSK lengths: 256-bit, 128-bit, 8-bit, Empty (0-bit)", validationText: validationText, devicePSKBitCount: bitCount ?? 0, inputChange: inputChangeEvent, selectChange: selectChangeEvent, - actionButtons: [{ text: 'Generate', variant: 'success', onClick: preSharedClickEvent }], + actionButtons: [ + { + text: "Generate", + variant: "success", + onClick: preSharedClickEvent, + }, + ], hide: true, properties: { value: pass, @@ -185,29 +194,29 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => { enumValue: config.display?.units === 0 ? { - "Within 23 km": 10, - "Within 12 km": 11, - "Within 5.8 km": 12, - "Within 2.9 km": 13, - "Within 1.5 km": 14, - "Within 700 m": 15, - "Within 350 m": 16, - "Within 200 m": 17, - "Within 90 m": 18, - "Within 50 m": 19, - } + "Within 23 km": 10, + "Within 12 km": 11, + "Within 5.8 km": 12, + "Within 2.9 km": 13, + "Within 1.5 km": 14, + "Within 700 m": 15, + "Within 350 m": 16, + "Within 200 m": 17, + "Within 90 m": 18, + "Within 50 m": 19, + } : { - "Within 15 miles": 10, - "Within 7.3 miles": 11, - "Within 3.6 miles": 12, - "Within 1.8 miles": 13, - "Within 0.9 miles": 14, - "Within 0.5 miles": 15, - "Within 0.2 miles": 16, - "Within 600 feet": 17, - "Within 300 feet": 18, - "Within 150 feet": 19, - }, + "Within 15 miles": 10, + "Within 7.3 miles": 11, + "Within 3.6 miles": 12, + "Within 1.8 miles": 13, + "Within 0.9 miles": 14, + "Within 0.5 miles": 15, + "Within 0.2 miles": 16, + "Within 600 feet": 17, + "Within 300 feet": 18, + "Within 150 feet": 19, + }, }, }, ], diff --git a/src/components/PageComponents/Config/Bluetooth.tsx b/src/components/PageComponents/Config/Bluetooth.tsx index b01d3c69..47eeeca4 100644 --- a/src/components/PageComponents/Config/Bluetooth.tsx +++ b/src/components/PageComponents/Config/Bluetooth.tsx @@ -6,20 +6,16 @@ import { useState } from "react"; export const Bluetooth = (): JSX.Element => { const { config, setWorkingConfig } = useDevice(); - const [bluetoothValidationText, setBluetoothValidationText] = useState(); + const [bluetoothValidationText, setBluetoothValidationText] = + useState(); - const bluetoothPinChangeEvent = ( - e: React.ChangeEvent, - ) => { - if (e.target.value[0] == "0") - { + const bluetoothPinChangeEvent = (e: React.ChangeEvent) => { + if (e.target.value[0] == "0") { setBluetoothValidationText("Bluetooth Pin cannot start with 0."); - } - else - { + } else { setBluetoothValidationText(""); } - } + }; const onSubmit = (data: BluetoothValidation) => { setWorkingConfig( diff --git a/src/components/PageComponents/Config/Security.tsx b/src/components/PageComponents/Config/Security.tsx index d6e76cc9..7152b387 100644 --- a/src/components/PageComponents/Config/Security.tsx +++ b/src/components/PageComponents/Config/Security.tsx @@ -12,7 +12,8 @@ import { Eye, EyeOff } from "lucide-react"; import { useState } from "react"; export const Security = (): JSX.Element => { - const { config, nodes, hardware, setWorkingConfig, setDialogOpen } = useDevice(); + const { config, nodes, hardware, setWorkingConfig, setDialogOpen } = + useDevice(); const [privateKey, setPrivateKey] = useState( fromByteArray(config.security?.privateKey ?? new Uint8Array(0)), @@ -31,7 +32,8 @@ export const Security = (): JSX.Element => { ); const [adminKeyValidationText, setAdminKeyValidationText] = useState(); - const [privateKeyDialogOpen, setPrivateKeyDialogOpen] = useState(false); + const [privateKeyDialogOpen, setPrivateKeyDialogOpen] = + useState(false); const onSubmit = (data: SecurityValidation) => { if (privateKeyValidationText || adminKeyValidationText) return; @@ -76,7 +78,7 @@ export const Security = (): JSX.Element => { const pkiBackupClickEvent = () => { setDialogOpen("pkiBackup", true); - } + }; const pkiRegenerate = () => { const privateKey = getX25519PrivateKey(); @@ -202,7 +204,7 @@ export const Security = (): JSX.Element => { name: "isManaged", label: "Managed", description: - 'If true, device configuration options are only able to be changed remotely by a Remote Admin node via admin messages. Do not enable this option unless a suitable Remote Admin node has been setup, and the public key stored in the field below.', + "If true, device configuration options are only able to be changed remotely by a Remote Admin node via admin messages. Do not enable this option unless a suitable Remote Admin node has been setup, and the public key stored in the field below.", }, { type: "text", diff --git a/src/components/PageComponents/Connect/Serial.tsx b/src/components/PageComponents/Connect/Serial.tsx index 53c623cb..4333e668 100644 --- a/src/components/PageComponents/Connect/Serial.tsx +++ b/src/components/PageComponents/Connect/Serial.tsx @@ -58,8 +58,9 @@ export const Serial = ({ closeDialog }: TabElementProps): JSX.Element => { await onConnect(port); }} > - {`# ${index} - ${usbVendorId ?? "UNK"} - ${usbProductId ?? "UNK" - }`} + {`# ${index} - ${usbVendorId ?? "UNK"} - ${ + usbProductId ?? "UNK" + }`} ); })} diff --git a/src/components/PageComponents/Map/NodeDetail.tsx b/src/components/PageComponents/Map/NodeDetail.tsx index d1cbd8a9..00847150 100644 --- a/src/components/PageComponents/Map/NodeDetail.tsx +++ b/src/components/PageComponents/Map/NodeDetail.tsx @@ -1,11 +1,12 @@ -import { Mono } from "@components/generic/Mono.tsx"; +import { Separator } from "@app/components/UI/Seperator"; import { H5 } from "@app/components/UI/Typography/H5.tsx"; import { Subtle } from "@app/components/UI/Typography/Subtle.tsx"; -import { Separator } from "@app/components/UI/Seperator"; +import { Mono } from "@components/generic/Mono.tsx"; import { TimeAgo } from "@components/generic/Table/tmp/TimeAgo.tsx"; import { Hashicon } from "@emeraldpay/hashicon-react"; import { Protobuf } from "@meshtastic/js"; import type { Protobuf as ProtobufType } from "@meshtastic/js"; +import { numberToHexUnpadded } from "@noble/curves/abstract/utils"; import { BatteryChargingIcon, BatteryFullIcon, @@ -17,7 +18,6 @@ import { MountainSnow, Star, } from "lucide-react"; -import { numberToHexUnpadded } from "@noble/curves/abstract/utils"; export interface NodeDetailProps { node: ProtobufType.Mesh.NodeInfo; diff --git a/src/components/PageComponents/Messages/Message.tsx b/src/components/PageComponents/Messages/Message.tsx index 22ed52b2..fc9a7b95 100644 --- a/src/components/PageComponents/Messages/Message.tsx +++ b/src/components/PageComponents/Messages/Message.tsx @@ -45,7 +45,7 @@ export const Message = ({ {sender?.user?.longName ?? "UNK"} - {message.rxTime.toLocaleDateString()} + {message.rxTime.toLocaleDateString()} {message.rxTime.toLocaleTimeString(undefined, { diff --git a/src/components/Toaster.tsx b/src/components/Toaster.tsx index f5531bba..d50ba3b8 100644 --- a/src/components/Toaster.tsx +++ b/src/components/Toaster.tsx @@ -17,7 +17,6 @@ export function Toaster() { @@ -32,4 +31,4 @@ export function Toaster() { ); -} \ No newline at end of file +} diff --git a/src/components/UI/Button.tsx b/src/components/UI/Button.tsx index e1b2d704..18861065 100644 --- a/src/components/UI/Button.tsx +++ b/src/components/UI/Button.tsx @@ -39,7 +39,7 @@ export type ButtonVariant = VariantProps["variant"]; export interface ButtonProps extends React.ButtonHTMLAttributes, - VariantProps { } + VariantProps {} const Button = React.forwardRef( ({ className, variant, size, ...props }, ref) => { diff --git a/src/components/UI/Toast.tsx b/src/components/UI/Toast.tsx index d40b294a..b2fd7131 100644 --- a/src/components/UI/Toast.tsx +++ b/src/components/UI/Toast.tsx @@ -1,11 +1,11 @@ -import * as React from "react" -import * as ToastPrimitives from "@radix-ui/react-toast" -import { cva, type VariantProps } from "class-variance-authority" -import { X } from 'lucide-react' +import * as ToastPrimitives from "@radix-ui/react-toast"; +import { type VariantProps, cva } from "class-variance-authority"; +import { X } from "lucide-react"; +import * as React from "react"; -import { cn } from "@core/utils/cn" +import { cn } from "@core/utils/cn"; -const ToastProvider = ToastPrimitives.Provider +const ToastProvider = ToastPrimitives.Provider; const ToastViewport = React.forwardRef< React.ElementRef, @@ -15,33 +15,34 @@ const ToastViewport = React.forwardRef< ref={ref} className={cn( "fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-24 sm:right-6 sm:top-auto sm:flex-col md:max-w-[420px]", - className + className, )} {...props} /> -)) -ToastViewport.displayName = ToastPrimitives.Viewport.displayName +)); +ToastViewport.displayName = ToastPrimitives.Viewport.displayName; const toastVariants = cva( "group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full", { variants: { variant: { - default: "border bg-background text-foreground dark:bg-slate-700 dark:border-slate-600 dark:text-slate-50", + default: + "border bg-background text-foreground dark:bg-slate-700 dark:border-slate-600 dark:text-slate-50", destructive: - "group destructive bg-red-600 text-white dark:border-red-900 dark:bg-red-900 dark:text-red-50" + "group destructive bg-red-600 text-white dark:border-red-900 dark:bg-red-900 dark:text-red-50", }, }, defaultVariants: { variant: "default", }, - } -) + }, +); const Toast = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef & - VariantProps + VariantProps >(({ className, variant, ...props }, ref) => { return ( - ) -}) -Toast.displayName = ToastPrimitives.Root.displayName + ); +}); +Toast.displayName = ToastPrimitives.Root.displayName; const ToastAction = React.forwardRef< React.ElementRef, @@ -61,12 +62,12 @@ const ToastAction = React.forwardRef< ref={ref} className={cn( "inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive", - className + className, )} {...props} /> -)) -ToastAction.displayName = ToastPrimitives.Action.displayName +)); +ToastAction.displayName = ToastPrimitives.Action.displayName; const ToastClose = React.forwardRef< React.ElementRef, @@ -76,15 +77,15 @@ const ToastClose = React.forwardRef< ref={ref} className={cn( "absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600 dark:text-slate-400 dark:hover:text-slate-50", - className + className, )} toast-close="" {...props} > -)) -ToastClose.displayName = ToastPrimitives.Close.displayName +)); +ToastClose.displayName = ToastPrimitives.Close.displayName; const ToastTitle = React.forwardRef< React.ElementRef, @@ -95,8 +96,8 @@ const ToastTitle = React.forwardRef< className={cn("text-sm font-semibold", className)} {...props} /> -)) -ToastTitle.displayName = ToastPrimitives.Title.displayName +)); +ToastTitle.displayName = ToastPrimitives.Title.displayName; const ToastDescription = React.forwardRef< React.ElementRef, @@ -107,12 +108,12 @@ const ToastDescription = React.forwardRef< className={cn("text-sm opacity-90", className)} {...props} /> -)) -ToastDescription.displayName = ToastPrimitives.Description.displayName +)); +ToastDescription.displayName = ToastPrimitives.Description.displayName; -type ToastProps = React.ComponentPropsWithoutRef +type ToastProps = React.ComponentPropsWithoutRef; -type ToastActionElement = React.ReactElement +type ToastActionElement = React.ReactElement; export { type ToastProps, @@ -124,5 +125,4 @@ export { ToastDescription, ToastClose, ToastAction, -} - +}; diff --git a/src/components/UI/Typography/Link.tsx b/src/components/UI/Typography/Link.tsx index efcfbdf6..4857da1f 100644 --- a/src/components/UI/Typography/Link.tsx +++ b/src/components/UI/Typography/Link.tsx @@ -11,7 +11,10 @@ export const Link = ({ href, children, className }: LinkProps): JSX.Element => ( href={href} target={"_blank"} rel="noopener noreferrer" - className={cn("font-medium text-slate-900 underline underline-offset-4 dark:text-slate-50", className)} + className={cn( + "font-medium text-slate-900 underline underline-offset-4 dark:text-slate-50", + className, + )} > {children} diff --git a/src/core/hooks/useBrowserFeatureDetection.ts b/src/core/hooks/useBrowserFeatureDetection.ts index a31b7b5a..17d35ac2 100644 --- a/src/core/hooks/useBrowserFeatureDetection.ts +++ b/src/core/hooks/useBrowserFeatureDetection.ts @@ -1,6 +1,6 @@ -import { useMemo } from 'react'; +import { useMemo } from "react"; -export type BrowserFeature = 'Web Bluetooth' | 'Web Serial' | 'Secure Context'; +export type BrowserFeature = "Web Bluetooth" | "Web Serial" | "Secure Context"; interface BrowserSupport { supported: BrowserFeature[]; @@ -10,9 +10,13 @@ interface BrowserSupport { export function useBrowserFeatureDetection(): BrowserSupport { const support = useMemo(() => { const features: [BrowserFeature, boolean][] = [ - ['Web Bluetooth', !!navigator?.bluetooth], - ['Web Serial', !!navigator?.serial], - ['Secure Context', window.location.protocol === 'https:' || window.location.hostname === 'localhost'] + ["Web Bluetooth", !!navigator?.bluetooth], + ["Web Serial", !!navigator?.serial], + [ + "Secure Context", + window.location.protocol === "https:" || + window.location.hostname === "localhost", + ], ]; return features.reduce( @@ -21,9 +25,9 @@ export function useBrowserFeatureDetection(): BrowserSupport { list.push(feature); return acc; }, - { supported: [], unsupported: [] } + { supported: [], unsupported: [] }, ); }, []); return support; -} \ No newline at end of file +} diff --git a/src/core/hooks/useKeyBackupReminder.tsx b/src/core/hooks/useKeyBackupReminder.tsx index ee65d161..0da9b0af 100644 --- a/src/core/hooks/useKeyBackupReminder.tsx +++ b/src/core/hooks/useKeyBackupReminder.tsx @@ -17,11 +17,11 @@ interface ReminderState { lastShown: string; } -const TOAST_APPEAR_DELAY = 10_000 // 10 seconds; -const TOAST_DURATION = 30_000 // 30 seconds;: +const TOAST_APPEAR_DELAY = 10_000; // 10 seconds; +const TOAST_DURATION = 30_000; // 30 seconds;: // remind user in 1 year to backup keys again, if they accept the reminder; -const ON_ACCEPT_REMINDER_DAYS = 365 +const ON_ACCEPT_REMINDER_DAYS = 365; function isReminderExpired(lastShown: string): boolean { const lastShownDate = new Date(lastShown); @@ -35,13 +35,14 @@ export function useBackupReminder({ reminderInDays = 7, enabled, message, - onAccept = () => { }, + onAccept = () => {}, cookieOptions, }: UseBackupReminderOptions) { const { toast } = useToast(); const toastShownRef = useRef(false); - const { value: reminderCookie, setCookie } = - useCookie("key_backup_reminder"); + const { value: reminderCookie, setCookie } = useCookie( + "key_backup_reminder", + ); const suppressReminder = useCallback( (days: number) => { @@ -69,39 +70,37 @@ export function useBackupReminder({ toastShownRef.current = true; - const { dismiss } = toast( - { - title: "Backup Reminder", - duration: TOAST_DURATION, - delay: TOAST_APPEAR_DELAY, - description: message, - action: ( -
- - -
- ), - }, - ); + const { dismiss } = toast({ + title: "Backup Reminder", + duration: TOAST_DURATION, + delay: TOAST_APPEAR_DELAY, + description: message, + action: ( +
+ + +
+ ), + }); return () => { if (!toastShownRef.current) { diff --git a/src/core/hooks/useToast.ts b/src/core/hooks/useToast.ts index c913d223..8c4a91b8 100644 --- a/src/core/hooks/useToast.ts +++ b/src/core/hooks/useToast.ts @@ -31,21 +31,21 @@ type ActionType = typeof actionTypes; type Action = | { - type: ActionType["ADD_TOAST"]; - toast: ToasterToast; - } + type: ActionType["ADD_TOAST"]; + toast: ToasterToast; + } | { - type: ActionType["UPDATE_TOAST"]; - toast: Partial; - } + type: ActionType["UPDATE_TOAST"]; + toast: Partial; + } | { - type: ActionType["DISMISS_TOAST"]; - toastId?: ToasterToast["id"]; - } + type: ActionType["DISMISS_TOAST"]; + toastId?: ToasterToast["id"]; + } | { - type: ActionType["REMOVE_TOAST"]; - toastId?: ToasterToast["id"]; - }; + type: ActionType["REMOVE_TOAST"]; + toastId?: ToasterToast["id"]; + }; interface State { toasts: ToasterToast[]; @@ -81,7 +81,7 @@ export const reducer = (state: State, action: Action): State => { return { ...state, toasts: state.toasts.map((t) => - t.id === action.toast.id ? { ...t, ...action.toast } : t + t.id === action.toast.id ? { ...t, ...action.toast } : t, ), }; @@ -103,10 +103,10 @@ export const reducer = (state: State, action: Action): State => { toasts: state.toasts.map((t) => t.id === toastId || toastId === undefined ? { - ...t, - open: false, - } - : t + ...t, + open: false, + } + : t, ), }; } @@ -193,4 +193,4 @@ function useToast() { }; } -export { toast, useToast }; \ No newline at end of file +export { toast, useToast }; diff --git a/src/pages/Map.tsx b/src/pages/Map.tsx index e326b336..1be9455d 100644 --- a/src/pages/Map.tsx +++ b/src/pages/Map.tsx @@ -1,5 +1,5 @@ -import { Subtle } from "@app/components/UI/Typography/Subtle.tsx"; import { NodeDetail } from "@app/components/PageComponents/Map/NodeDetail"; +import { Subtle } from "@app/components/UI/Typography/Subtle.tsx"; import { cn } from "@app/core/utils/cn.ts"; import { PageLayout } from "@components/PageLayout.tsx"; import { Sidebar } from "@components/Sidebar.tsx"; @@ -8,6 +8,7 @@ import { SidebarButton } from "@components/UI/Sidebar/sidebarButton.tsx"; import { useAppStore } from "@core/stores/appStore.ts"; import { useDevice } from "@core/stores/deviceStore.ts"; import { Hashicon } from "@emeraldpay/hashicon-react"; +import type { Protobuf } from "@meshtastic/js"; import { numberToHexUnpadded } from "@noble/curves/abstract/utils"; import { bbox, lineString } from "@turf/turf"; import { @@ -19,7 +20,6 @@ import { import { useCallback, useEffect, useState } from "react"; import { AttributionControl, Marker, Popup, useMap } from "react-map-gl"; import MapGl from "react-map-gl/maplibre"; -import { Protobuf } from "@meshtastic/js"; export const MapPage = (): JSX.Element => { const { nodes, waypoints } = useDevice(); @@ -148,7 +148,10 @@ export const MapPage = (): JSX.Element => { }} > {waypoints.map((wp) => (