Browse Source

chore: update packages to latest version. feat: replace hashicon lib with vanilla component

pull/385/head
Dan Ditomaso 1 year ago
parent
commit
9b843f6483
  1. 87
      package.json
  2. 4987
      pnpm-lock.yaml
  3. 5
      src/DeviceWrapper.tsx
  4. 12
      src/PageRouter.tsx
  5. 14
      src/components/CommandPalette.tsx
  6. 12
      src/components/DeviceSelector.tsx
  7. 6
      src/components/DeviceSelectorButton.tsx
  8. 2
      src/components/Form/FormSelect.tsx
  9. 6
      src/components/Form/FormWrapper.tsx
  10. 6
      src/components/PageComponents/Map/NodeDetail.tsx
  11. 10
      src/components/PageComponents/Messages/Message.tsx
  12. 94
      src/components/UI/Avatar.tsx
  13. 4
      src/components/UI/Sidebar/SidebarSection.tsx
  14. 15
      src/components/generic/Table/index.tsx
  15. 1
      src/index.css
  16. 3
      src/pages/Channels.tsx
  17. 4
      src/pages/Config/index.tsx
  18. 25
      src/pages/Map.tsx
  19. 43
      src/pages/Messages.tsx
  20. 13
      src/pages/Nodes.tsx

87
package.json

@ -27,65 +27,64 @@
"homepage": "https://meshtastic.org",
"dependencies": {
"@bufbuild/protobuf": "^1.10.0",
"@emeraldpay/hashicon-react": "^0.5.2",
"@meshtastic/js": "2.3.7-5",
"@noble/curves": "^1.5.0",
"@radix-ui/react-accordion": "^1.2.0",
"@radix-ui/react-checkbox": "^1.1.0",
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-menubar": "^1.1.1",
"@radix-ui/react-popover": "^1.1.1",
"@radix-ui/react-scroll-area": "^1.1.0",
"@radix-ui/react-select": "^2.1.1",
"@radix-ui/react-separator": "^1.1.0",
"@radix-ui/react-switch": "^1.1.0",
"@radix-ui/react-tabs": "^1.1.0",
"@radix-ui/react-toast": "^1.2.1",
"@radix-ui/react-tooltip": "^1.1.1",
"@turf/turf": "^6.5.0",
"@noble/curves": "^1.8.1",
"@radix-ui/react-accordion": "^1.2.2",
"@radix-ui/react-checkbox": "^1.1.3",
"@radix-ui/react-dialog": "^1.1.5",
"@radix-ui/react-dropdown-menu": "^2.1.5",
"@radix-ui/react-label": "^2.1.1",
"@radix-ui/react-menubar": "^1.1.5",
"@radix-ui/react-popover": "^1.1.5",
"@radix-ui/react-scroll-area": "^1.2.2",
"@radix-ui/react-select": "^2.1.5",
"@radix-ui/react-separator": "^1.1.1",
"@radix-ui/react-switch": "^1.1.2",
"@radix-ui/react-tabs": "^1.1.2",
"@radix-ui/react-toast": "^1.2.5",
"@radix-ui/react-tooltip": "^1.1.7",
"@turf/turf": "^7.2.0",
"base64-js": "^1.5.1",
"class-validator": "^0.14.1",
"class-variance-authority": "^0.7.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.0.0",
"cmdk": "^1.0.4",
"crypto-random-string": "^5.0.0",
"immer": "^10.1.1",
"js-cookie": "^3.0.5",
"lucide-react": "^0.363.0",
"mapbox-gl": "^3.6.0",
"lucide-react": "^0.474.0",
"mapbox-gl": "^3.9.4",
"maplibre-gl": "4.1.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.52.0",
"react-map-gl": "7.1.7",
"react-qrcode-logo": "^2.10.0",
"rfc4648": "^1.5.3",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-hook-form": "^7.54.2",
"react-map-gl": "7.1.9",
"react-qrcode-logo": "^3.0.0",
"rfc4648": "^1.5.4",
"timeago-react": "^3.0.6",
"vite-plugin-node-polyfills": "^0.22.0",
"zustand": "4.5.2"
"vite-plugin-node-polyfills": "^0.23.0",
"zustand": "5.0.3"
},
"devDependencies": {
"@biomejs/biome": "^1.8.2",
"@rsbuild/core": "^1.0.10",
"@rsbuild/plugin-react": "^1.0.3",
"@types/chrome": "^0.0.263",
"@biomejs/biome": "^1.9.4",
"@rsbuild/core": "^1.2.3",
"@rsbuild/plugin-react": "^1.1.0",
"@types/chrome": "^0.0.299",
"@types/js-cookie": "^3.0.6",
"@types/node": "^20.14.9",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@types/w3c-web-serial": "^1.0.6",
"@types/node": "^22.12.0",
"@types/react": "^19.0.8",
"@types/react-dom": "^19.0.3",
"@types/w3c-web-serial": "^1.0.7",
"@types/web-bluetooth": "^0.0.20",
"autoprefixer": "^10.4.19",
"gzipper": "^7.2.0",
"postcss": "^8.4.38",
"autoprefixer": "^10.4.20",
"gzipper": "^8.2.0",
"postcss": "^8.5.1",
"simple-git-hooks": "^2.11.1",
"tailwind-merge": "^2.3.0",
"tailwindcss": "^3.4.4",
"tailwind-merge": "^2.6.0",
"tailwindcss": "^3.4.17",
"tailwindcss-animate": "^1.0.7",
"tar": "^6.2.1",
"typescript": "^5.5.2"
"tar": "^7.4.3",
"typescript": "^5.7.3"
},
"packageManager": "[email protected]"
}

4987
pnpm-lock.yaml

File diff suppressed because it is too large

5
src/DeviceWrapper.tsx

@ -7,10 +7,7 @@ export interface DeviceWrapperProps {
device?: Device;
}
export const DeviceWrapper = ({
children,
device,
}: DeviceWrapperProps): JSX.Element => {
export const DeviceWrapper = ({ children, device }: DeviceWrapperProps) => {
return (
<DeviceContext.Provider value={device}>{children}</DeviceContext.Provider>
);

12
src/PageRouter.tsx

@ -1,11 +1,11 @@
import { useDevice } from "@core/stores/deviceStore.ts";
import { ChannelsPage } from "@pages/Channels.tsx";
import { ConfigPage } from "@pages/Config/index.tsx";
import { MapPage } from "@pages/Map.tsx";
import { MessagesPage } from "@pages/Messages.tsx";
import { NodesPage } from "@pages/Nodes.tsx";
import ChannelsPage from "@pages/Channels.tsx";
import ConfigPage from "@pages/Config/index.tsx";
import MapPage from "@pages/Map.tsx";
import MessagesPage from "@pages/Messages.tsx";
import NodesPage from "@pages/Nodes.tsx";
export const PageRouter = (): JSX.Element => {
export const PageRouter = () => {
const { activePage } = useDevice();
return (
<>

14
src/components/CommandPalette.tsx

@ -1,3 +1,4 @@
import { Avatar } from "@components/UI/Avatar";
import {
CommandDialog,
CommandEmpty,
@ -8,7 +9,6 @@ import {
} from "@components/UI/Command.tsx";
import { useAppStore } from "@core/stores/appStore.ts";
import { useDevice, useDeviceStore } from "@core/stores/deviceStore.ts";
import { Hashicon } from "@emeraldpay/hashicon-react";
import { useCommandState } from "cmdk";
import {
ArrowLeftRightIcon,
@ -51,11 +51,11 @@ export interface Command {
export interface SubItem {
label: string;
icon: JSX.Element;
icon: React.ReactNode;
action: () => void;
}
export const CommandPalette = (): JSX.Element => {
export const CommandPalette = () => {
const {
commandPaletteOpen,
setCommandPaletteOpen,
@ -125,9 +125,11 @@ export const CommandPalette = (): JSX.Element => {
device.nodes.get(device.hardware.myNodeNum)?.user?.longName ??
device.hardware.myNodeNum.toString(),
icon: (
<Hashicon
size={16}
value={device.hardware.myNodeNum.toString()}
<Avatar
text={
device.nodes.get(device.hardware.myNodeNum)?.user
?.shortName ?? device.hardware.myNodeNum.toString()
}
/>
),
action() {

12
src/components/DeviceSelector.tsx

@ -3,7 +3,6 @@ import { Separator } from "@components/UI/Seperator.tsx";
import { Code } from "@components/UI/Typography/Code.tsx";
import { useAppStore } from "@core/stores/appStore.ts";
import { useDeviceStore } from "@core/stores/deviceStore.ts";
import { Hashicon } from "@emeraldpay/hashicon-react";
import {
HomeIcon,
LanguagesIcon,
@ -12,6 +11,8 @@ import {
SearchIcon,
SunIcon,
} from "lucide-react";
import type { JSX } from "react";
import { Avatar } from "./UI/Avatar";
export const DeviceSelector = (): JSX.Element => {
const { getDevices } = useDeviceStore();
@ -44,9 +45,12 @@ export const DeviceSelector = (): JSX.Element => {
}}
active={selectedDevice === device.id}
>
<Hashicon
size={24}
value={device.hardware.myNodeNum.toString()}
<Avatar
text={
device.nodes
.get(device.hardware.myNodeNum)
?.user?.shortName.toString() ?? "UNK"
}
/>
</DeviceSelectorButton>
))}

6
src/components/DeviceSelectorButton.tsx

@ -8,15 +8,15 @@ export const DeviceSelectorButton = ({
active,
onClick,
children,
}: DeviceSelectorButtonProps): JSX.Element => (
}: DeviceSelectorButtonProps) => (
<li
className="aspect-w-1 aspect-h-1 relative w-full"
onClick={onClick}
onKeyDown={onClick}
>
{active && (
{/* {active && (
<div className="absolute -left-2 h-10 w-1.5 rounded-full bg-accent" />
)}
)} */}
<div className="flex aspect-square cursor-pointer flex-col items-center justify-center">
{children}
</div>

2
src/components/Form/FormSelect.tsx

@ -51,7 +51,7 @@ export function SelectInput<T extends FieldValues>({
</SelectTrigger>
<SelectContent>
{optionsEnumValues.map(([name, value]) => (
<SelectItem key={name} value={value.toString()}>
<SelectItem key={name + value} value={value.toString()}>
{formatEnumName
? name
.replace(/_/g, " ")

6
src/components/Form/FormWrapper.tsx

@ -15,9 +15,9 @@ export const FieldWrapper = ({
children,
valid,
validationText,
}: FieldWrapperProps): JSX.Element => (
}: FieldWrapperProps) => (
<div className="pt-6 sm:pt-5">
<div role="group" aria-labelledby="label-notifications">
<fieldset aria-labelledby="label-notifications">
<div className="sm:grid sm:grid-cols-3 sm:items-baseline sm:gap-4">
<Label>{label}</Label>
<div className="sm:col-span-2">
@ -32,6 +32,6 @@ export const FieldWrapper = ({
</div>
</div>
</div>
</div>
</fieldset>
</div>
);

6
src/components/PageComponents/Map/NodeDetail.tsx

@ -1,9 +1,9 @@
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 { Avatar } from "@components/UI/Avatar";
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";
@ -23,7 +23,7 @@ export interface NodeDetailProps {
node: ProtobufType.Mesh.NodeInfo;
}
export const NodeDetail = ({ node }: NodeDetailProps): JSX.Element => {
export const NodeDetail = ({ node }: NodeDetailProps) => {
const name = node.user?.longName || `!${numberToHexUnpadded(node.num)}`;
const hardwareType = Protobuf.Mesh.HardwareModel[
node.user?.hwModel ?? 0
@ -33,7 +33,7 @@ export const NodeDetail = ({ node }: NodeDetailProps): JSX.Element => {
<div className="dark:text-black">
<div className="flex gap-2">
<div className="flex flex-col items-center gap-2 min-w-6 pt-1">
<Hashicon value={node.num.toString()} size={22} />
<Avatar text={node.user?.shortName} />
<div>
{node.user?.publicKey && node.user?.publicKey.length > 0 ? (

10
src/components/PageComponents/Messages/Message.tsx

@ -1,5 +1,5 @@
import type { MessageWithState } from "@app/core/stores/deviceStore.ts";
import { Hashicon } from "@emeraldpay/hashicon-react";
import { Avatar } from "@components/UI/Avatar";
import type { Protobuf } from "@meshtastic/js";
import {
AlertCircleIcon,
@ -13,11 +13,7 @@ export interface MessageProps {
sender?: Protobuf.Mesh.NodeInfo;
}
export const Message = ({
lastMsgSameUser,
message,
sender,
}: MessageProps): JSX.Element => {
export const Message = ({ lastMsgSameUser, message, sender }: MessageProps) => {
return lastMsgSameUser ? (
<div className="ml-5 flex">
{message.state === "ack" ? (
@ -39,7 +35,7 @@ export const Message = ({
<div className="mx-4 mt-2 gap-2">
<div className="flex gap-2">
<div className="w-6 cursor-pointer">
<Hashicon value={(sender?.num ?? 0).toString()} size={32} />
<Avatar text={sender?.user?.shortName ?? "UNK"} />
</div>
<span className="cursor-pointer font-medium text-textPrimary">
{sender?.user?.longName ?? "UNK"}

94
src/components/UI/Avatar.tsx

@ -0,0 +1,94 @@
import { cn } from "@app/core/utils/cn";
import type React from "react";
type RGBColor = {
r: number;
g: number;
b: number;
a: number;
};
interface AvatarProps {
text: string;
size?: "sm" | "lg";
className?: string;
}
// biome-ignore lint/complexity/noStaticOnlyClass: stop being annoying Biome
class ColorUtils {
static hexToRgb(hex: number): RGBColor {
return {
r: (hex & 0xff0000) >> 16,
g: (hex & 0x00ff00) >> 8,
b: hex & 0x0000ff,
a: 255,
};
}
static rgbToHex(color: RGBColor): number {
return (
(Math.round(color.a) << 24) |
(Math.round(color.r) << 16) |
(Math.round(color.g) << 8) |
Math.round(color.b)
);
}
static isLight(color: RGBColor): boolean {
const brightness = (color.r * 299 + color.g * 587 + color.b * 114) / 1000;
return brightness > 127.5;
}
}
export const Avatar: React.FC<AvatarProps> = ({
text,
size = "sm",
className,
}) => {
const sizes = {
sm: "size-11 text-xs",
lg: "size-16 text-lg",
};
// Pick a color based on the text provided to function
const getColorFromText = (text: string): RGBColor => {
let hash = 0;
for (let i = 0; i < text.length; i++) {
hash = text.charCodeAt(i) + ((hash << 5) - hash);
}
return {
r: (hash & 0xff0000) >> 16,
g: (hash & 0x00ff00) >> 8,
b: hash & 0x0000ff,
a: 255,
};
};
const bgColor = getColorFromText(text ?? "UNK");
const isLight = ColorUtils.isLight(bgColor);
const textColor = isLight ? "#000000" : "#FFFFFF";
const initials = text?.toUpperCase().slice(0, 4) ?? "UNK";
return (
<div
className={cn(
`
rounded-full
flex
items-center
justify-center
size-11
font-semibold`,
sizes[size],
className,
)}
style={{
backgroundColor: `rgb(${bgColor.r}, ${bgColor.g}, ${bgColor.b})`,
color: textColor,
}}
>
<p className="p-1">{initials}</p>
</div>
);
};

4
src/components/UI/Sidebar/SidebarSection.tsx

@ -9,9 +9,9 @@ export interface SidebarSectionProps {
export const SidebarSection = ({
label: title,
children,
}: SidebarSectionProps): JSX.Element => (
}: SidebarSectionProps) => (
<div className="px-4 py-2">
<H4 className="mb-2 ml-2">{title}</H4>
<H4 className="mb-3 ml-2">{title}</H4>
<div className="space-y-1">{children}</div>
</div>
);

15
src/components/generic/Table/index.tsx

@ -74,15 +74,12 @@ export const Table = ({ headings, rows }: TableProps): JSX.Element => {
>
<div className="flex gap-2">
{heading.title}
{sortColumn === heading.title && (
<>
{sortOrder === "asc" ? (
<ChevronUpIcon size={16} />
) : (
<ChevronDownIcon size={16} />
)}
</>
)}
{sortColumn === heading.title &&
(sortOrder === "asc" ? (
<ChevronUpIcon size={16} />
) : (
<ChevronDownIcon size={16} />
))}
</div>
</th>
))}

1
src/index.css

@ -97,6 +97,5 @@
}
img {
-drag: none;
-webkit-user-drag: none;
}

3
src/pages/Channels.tsx

@ -20,7 +20,7 @@ export const getChannelName = (channel: Protobuf.Channel.Channel) =>
? "Primary"
: `Ch ${channel.index}`;
export const ChannelsPage = (): JSX.Element => {
const ChannelsPage = () => {
const { channels, setDialogOpen } = useDevice();
const [activeChannel, setActiveChannel] = useState<Types.ChannelNumber>(
Types.ChannelNumber.Primary,
@ -69,3 +69,4 @@ export const ChannelsPage = (): JSX.Element => {
</>
);
};
export default ChannelsPage;

4
src/pages/Config/index.tsx

@ -9,7 +9,7 @@ import { ModuleConfig } from "@pages/Config/ModuleConfig.tsx";
import { BoxesIcon, SaveIcon, SettingsIcon } from "lucide-react";
import { useState } from "react";
export const ConfigPage = (): JSX.Element => {
const ConfigPage = (): JSX.Element => {
const { workingConfig, workingModuleConfig, connection } = useDevice();
const [activeConfigSection, setActiveConfigSection] = useState<
"device" | "module"
@ -72,3 +72,5 @@ export const ConfigPage = (): JSX.Element => {
</>
);
};
export default ConfigPage;

25
src/pages/Map.tsx

@ -1,4 +1,5 @@
import { NodeDetail } from "@app/components/PageComponents/Map/NodeDetail";
import { Avatar } from "@app/components/UI/Avatar";
import { Subtle } from "@app/components/UI/Typography/Subtle.tsx";
import { cn } from "@app/core/utils/cn.ts";
import { PageLayout } from "@components/PageLayout.tsx";
@ -7,7 +8,6 @@ import { SidebarSection } from "@components/UI/Sidebar/SidebarSection.tsx";
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";
@ -17,11 +17,11 @@ import {
ZoomInIcon,
ZoomOutIcon,
} from "lucide-react";
import { useCallback, useEffect, useState } from "react";
import { type JSX, useCallback, useEffect, useMemo, useState } from "react";
import { AttributionControl, Marker, Popup, useMap } from "react-map-gl";
import MapGl from "react-map-gl/maplibre";
export const MapPage = (): JSX.Element => {
const MapPage = (): JSX.Element => {
const { nodes, waypoints } = useDevice();
const { rasterSources, darkMode } = useAppStore();
const { default: map } = useMap();
@ -30,7 +30,7 @@ export const MapPage = (): JSX.Element => {
const [selectedNode, setSelectedNode] =
useState<Protobuf.Mesh.NodeInfo | null>(null);
const allNodes = Array.from(nodes.values());
const allNodes = useMemo(() => Array.from(nodes.values()), [nodes]);
const getBBox = useCallback(() => {
if (!map) {
@ -135,9 +135,7 @@ export const MapPage = (): JSX.Element => {
renderWorldCopies={false}
maxPitch={0}
style={{
filter: darkMode
? "brightness(0.6) invert(1) contrast(3) hue-rotate(200deg) saturate(0.3) brightness(0.7)"
: "",
filter: darkMode ? "brightness(0.8)" : "",
}}
dragRotate={false}
touchZoomRotate={false}
@ -177,7 +175,7 @@ export const MapPage = (): JSX.Element => {
key={node.num}
longitude={(node.position.longitudeI ?? 0) / 1e7}
latitude={(node.position.latitudeI ?? 0) / 1e7}
style={{ filter: darkMode ? "invert(1)" : "" }}
// style={{ filter: darkMode ? "invert(1)" : "" }}
anchor="bottom"
onClick={() => {
setSelectedNode(node);
@ -190,8 +188,13 @@ export const MapPage = (): JSX.Element => {
});
}}
>
<div className="flex cursor-pointer gap-2 rounded-md border bg-backgroundPrimary p-1.5">
<Hashicon value={node.num.toString()} size={22} />
<div className="flex cursor-pointer gap-2 rounded-md bg-transparent p-1.5">
<Avatar
text={
node.user?.shortName.toString() ?? node.num.toString()
}
size="sm"
/>
<Subtle className={cn(zoom < 12 && "hidden")}>
{node.user?.longName ||
`!${numberToHexUnpadded(node.num)}`}
@ -217,3 +220,5 @@ export const MapPage = (): JSX.Element => {
</>
);
};
export default MapPage;

43
src/pages/Messages.tsx

@ -1,18 +1,18 @@
import { ChannelChat } from "@components/PageComponents/Messages/ChannelChat.tsx";
import { PageLayout } from "@components/PageLayout.tsx";
import { Sidebar } from "@components/Sidebar.tsx";
import { Avatar } from "@components/UI/Avatar.tsx";
import { SidebarSection } from "@components/UI/Sidebar/SidebarSection.tsx";
import { SidebarButton } from "@components/UI/Sidebar/sidebarButton.tsx";
import { useToast } from "@core/hooks/useToast.ts";
import { useDevice } from "@core/stores/deviceStore.ts";
import { Hashicon } from "@emeraldpay/hashicon-react";
import { Device, useDevice, useDeviceStore } from "@core/stores/deviceStore.ts";
import { Protobuf, Types } from "@meshtastic/js";
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
import { getChannelName } from "@pages/Channels.tsx";
import { HashIcon, LockIcon, LockOpenIcon, WaypointsIcon } from "lucide-react";
import { useState } from "react";
export const MessagesPage = (): JSX.Element => {
const MessagesPage = () => {
const { channels, nodes, hardware, messages, traceroutes, connection } =
useDevice();
const [chatType, setChatType] =
@ -56,18 +56,27 @@ export const MessagesPage = (): JSX.Element => {
))}
</SidebarSection>
<SidebarSection label="Nodes">
{filteredNodes.map((node) => (
<SidebarButton
key={node.num}
label={node.user?.longName ?? `!${numberToHexUnpadded(node.num)}`}
active={activeChat === node.num}
onClick={() => {
setChatType("direct");
setActiveChat(node.num);
}}
element={<Hashicon size={20} value={node.num.toString()} />}
/>
))}
<div className="flex flex-col gap-4">
{filteredNodes.map((node) => (
<SidebarButton
key={node.num}
label={
node.user?.longName ?? `!${numberToHexUnpadded(node.num)}`
}
active={activeChat === node.num}
onClick={() => {
setChatType("direct");
setActiveChat(node.num);
}}
element={
<Avatar
text={node.user?.shortName ?? node.num.toString()}
size="sm"
/>
}
/>
))}
</div>
</SidebarSection>
</Sidebar>
<div className="flex flex-col flex-grow">
@ -76,7 +85,7 @@ export const MessagesPage = (): JSX.Element => {
chatType === "broadcast" && currentChannel
? getChannelName(currentChannel)
: chatType === "direct" && nodes.get(activeChat)
? nodes.get(activeChat)?.user?.longName ?? nodeHex
? (nodes.get(activeChat)?.user?.longName ?? nodeHex)
: "Loading..."
}`}
actions={
@ -146,3 +155,5 @@ export const MessagesPage = (): JSX.Element => {
</>
);
};
export default MessagesPage;

13
src/pages/Nodes.tsx

@ -6,11 +6,10 @@ import { Mono } from "@components/generic/Mono.tsx";
import { Table } from "@components/generic/Table/index.tsx";
import { TimeAgo } from "@components/generic/Table/tmp/TimeAgo.tsx";
import { useDevice } from "@core/stores/deviceStore.ts";
import { Hashicon } from "@emeraldpay/hashicon-react";
import { Protobuf } from "@meshtastic/js";
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
import { LockIcon, LockOpenIcon, TrashIcon } from "lucide-react";
import { Fragment } from "react";
import { Fragment, type JSX } from "react";
import { base16 } from "rfc4648";
export interface DeleteNoteDialogProps {
@ -18,7 +17,7 @@ export interface DeleteNoteDialogProps {
onOpenChange: (open: boolean) => void;
}
export const NodesPage = (): JSX.Element => {
const NodesPage = (): JSX.Element => {
const { nodes, hardware, setDialogOpen } = useDevice();
const { setNodeNumToBeRemoved } = useAppStore();
@ -44,7 +43,11 @@ export const NodesPage = (): JSX.Element => {
{ title: "Remove", type: "normal", sortable: false },
]}
rows={filteredNodes.map((node) => [
<Hashicon key="icon" size={24} value={node.num.toString()} />,
<span
key={node.num}
className="h-3 w-3 rounded-full bg-accent"
/>,
<h1 key="header">
{node.user?.longName ??
(node.user?.macaddr
@ -111,3 +114,5 @@ export const NodesPage = (): JSX.Element => {
</>
);
};
export default NodesPage;

Loading…
Cancel
Save