diff --git a/src/components/Sidebar/SidebarTab.tsx b/src/components/Sidebar/SidebarTab.tsx
index 9c3dc476..2c674fdb 100644
--- a/src/components/Sidebar/SidebarTab.tsx
+++ b/src/components/Sidebar/SidebarTab.tsx
@@ -17,7 +17,7 @@ const SidebarTab = ({
return (
{title}
diff --git a/src/components/Table/TableBody.tsx b/src/components/Table/TableBody.tsx
index 80e62708..7f5a5a0b 100644
--- a/src/components/Table/TableBody.tsx
+++ b/src/components/Table/TableBody.tsx
@@ -24,7 +24,7 @@ const TableBody = ({ title, data, columns }: ITableBodyProps) => {
-
+
{title}
{/* ({ title, data, columns }: ITableBodyProps) => {
{/* Header element */}
-
+
{table.getHeaderGroups().map((headerGroup) => (
// Row to contain header data
{headerGroup.headers.map((header) => (
// Mapping each header into the row
@@ -64,7 +65,7 @@ const TableBody = ({ title, data, columns }: ITableBodyProps) => {
{row.getVisibleCells().map((cell) => (
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
diff --git a/src/components/Table/TableLayout.tsx b/src/components/Table/TableLayout.tsx
index 45fd7f8a..6c69b4d2 100644
--- a/src/components/Table/TableLayout.tsx
+++ b/src/components/Table/TableLayout.tsx
@@ -19,8 +19,8 @@ const TableLayout = ({
backtrace,
}: ITableLayoutProps) => {
return (
-
-
+
+
diff --git a/src/components/Waypoints/WaypointMenu.tsx b/src/components/Waypoints/WaypointMenu.tsx
index cdc89bf6..d4d11595 100644
--- a/src/components/Waypoints/WaypointMenu.tsx
+++ b/src/components/Waypoints/WaypointMenu.tsx
@@ -65,24 +65,27 @@ const WaypointMenu = ({ editWaypoint }: IWaypointMenuProps) => {
activeWaypoint;
return (
-
+
{!!icon &&
{String.fromCodePoint(icon)}
}
-
+
{getWaypointTitle(activeWaypoint)}
-
+
{t("map.panes.waypointInfo.description")}
@@ -92,12 +95,12 @@ const WaypointMenu = ({ editWaypoint }: IWaypointMenuProps) => {
-
+
{t("map.panes.waypointInfo.details")}
-
+
{lockedTo ? (
@@ -111,7 +114,7 @@ const WaypointMenu = ({ editWaypoint }: IWaypointMenuProps) => {
? t("map.panes.waypointInfo.onlyNodeEdit", {
nodeName: usersMap[lockedTo]?.shortName || lockedTo,
})
- : t("map.panes.waypointInfo.anyoneEdit")}
+ : t("map.panes.waypointInfo.locked.anyoneEdit")}
@@ -133,7 +136,7 @@ const WaypointMenu = ({ editWaypoint }: IWaypointMenuProps) => {
-
+
@@ -159,7 +162,7 @@ const WaypointMenu = ({ editWaypoint }: IWaypointMenuProps) => {
-
+
{!expire ? (
@@ -198,7 +201,7 @@ const WaypointMenu = ({ editWaypoint }: IWaypointMenuProps) => {
type="button"
onClick={handleEditWaypoint}
disabled={!!lockedTo && lockedTo !== device?.myNodeInfo.myNodeNum}
- className=" text-gray-500 hover:text-gray-600 disabled:text-gray-300 disabled:cursor-not-allowed transition-colors"
+ className=" text-gray-500 dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 disabled:text-gray-300 dark:disabled:text-gray-600 disabled:cursor-not-allowed transition-colors"
>
{t("map.panes.waypointInfo.editWaypoint")}
@@ -206,7 +209,7 @@ const WaypointMenu = ({ editWaypoint }: IWaypointMenuProps) => {
diff --git a/src/components/config/ConfigInput.tsx b/src/components/config/ConfigInput.tsx
index c1f24ed4..4fa0fa21 100644
--- a/src/components/config/ConfigInput.tsx
+++ b/src/components/config/ConfigInput.tsx
@@ -24,7 +24,7 @@ const ConfigInput = forwardRef<
{error &&
{error}
}
diff --git a/src/components/config/ConfigLayout.tsx b/src/components/config/ConfigLayout.tsx
index c7b634cf..f437a225 100644
--- a/src/components/config/ConfigLayout.tsx
+++ b/src/components/config/ConfigLayout.tsx
@@ -24,15 +24,15 @@ const ConfigLayout = ({
children,
}: IConfigLayoutProps) => {
return (
-
+
-
+
-
+
{title}
@@ -42,7 +42,9 @@ const ConfigLayout = ({
className="cursor-pointer"
onClick={() => onTitleIconClick()}
>
- {renderTitleIcon("w-6 h-6 text-gray-400 my-auto")}
+ {renderTitleIcon(
+ "w-6 h-6 text-gray-400 dark:text-gray-400 my-auto"
+ )}
diff --git a/src/components/config/ConfigOption.tsx b/src/components/config/ConfigOption.tsx
index 10fc910b..ac518ff4 100644
--- a/src/components/config/ConfigOption.tsx
+++ b/src/components/config/ConfigOption.tsx
@@ -18,10 +18,12 @@ const ConfigOption = ({
@@ -31,10 +33,14 @@ const ConfigOption = ({
);
};
diff --git a/src/components/config/ConfigSelect.tsx b/src/components/config/ConfigSelect.tsx
new file mode 100644
index 00000000..af0c4caa
--- /dev/null
+++ b/src/components/config/ConfigSelect.tsx
@@ -0,0 +1,35 @@
+import React, { forwardRef } from "react";
+import type { DetailedHTMLProps, SelectHTMLAttributes } from "react";
+import type { UseFormRegister } from "react-hook-form";
+
+import ConfigLabel from "@components/config/ConfigLabel";
+
+export interface IConfigSelectProps
+ extends DetailedHTMLProps<
+ SelectHTMLAttributes
,
+ HTMLSelectElement
+ > {
+ text: string;
+ error?: string;
+}
+
+// eslint-disable-next-line react/display-name
+const ConfigSelect = forwardRef<
+ HTMLSelectElement,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ IConfigSelectProps & ReturnType>
+>(({ text, error, ...rest }, ref) => {
+ return (
+
+
+
+ );
+});
+
+export default ConfigSelect;
diff --git a/src/components/config/ConfigTitlebar.tsx b/src/components/config/ConfigTitlebar.tsx
index a50f0fd7..468cb63c 100644
--- a/src/components/config/ConfigTitlebar.tsx
+++ b/src/components/config/ConfigTitlebar.tsx
@@ -26,16 +26,20 @@ const ConfigTitle = ({
children,
}: IConfigTitleProps) => {
return (
-
-
+
+
-
{title}
-
{subtitle}
+
+ {title}
+
+
+ {subtitle}
+
diff --git a/src/components/config/channel/ChannelConfigDetail.tsx b/src/components/config/channel/ChannelConfigDetail.tsx
index 2f0f34f8..c6ea971e 100644
--- a/src/components/config/channel/ChannelConfigDetail.tsx
+++ b/src/components/config/channel/ChannelConfigDetail.tsx
@@ -7,8 +7,8 @@ import { RotateCcw } from "lucide-react";
import debounce from "lodash.debounce";
import ConfigTitlebar from "@components/config/ConfigTitlebar";
-import ConfigLabel from "@components/config/ConfigLabel";
import ConfigInput from "@components/config/ConfigInput";
+import ConfigSelect from "@components/config/ConfigSelect";
import {
ChannelConfigInput,
@@ -135,22 +135,20 @@ const ChannelConfigDetail = ({
onIconClick={handleFormReset}
>
-
-
-
+
+
+
+
{
{...register("enabled")}
/>
-
-
-
+
+
+
+
{
onIconClick={handleFormReset}
>
-
-
-
+
+
+
+
+
+
+
+
{
{/* TODO BUTTON GPIO */}
{/* TODO BUZZER GPIO */}
-
-
-
+
+
+
+
{
{...register("compassNorthTop")}
/>
-
-
-
+
+
+
+
+
{
{...register("flipScreen")}
/>
-
-
-
+
+
+
+
+
+
+
{
{...register("headingBold")}
/>
-
-
-
+
+
+
+
+
{
{...register("screenOnSecs")}
/>
-
-
-
+
+
+
{
onIconClick={handleFormReset}
>
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{
{...register("usePreset")}
/>
-
-
-
+
+
+
+
+
+
+
+
{
{...register("ethEnabled")}
/>
-
-
-
+
+
+
{
{...register("pttPin")}
/>
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
{t("connectPage.tabs.tcp.connecting")}
>
) : (
<>
-
+
{t("connectPage.tabs.tcp.connect")}
>
diff --git a/src/components/pages/ApplicationStatePage.tsx b/src/components/pages/ApplicationStatePage.tsx
index 710ee3de..10c7af7b 100644
--- a/src/components/pages/ApplicationStatePage.tsx
+++ b/src/components/pages/ApplicationStatePage.tsx
@@ -5,21 +5,29 @@ import ReactJson from "react-json-view";
import NavigationBacktrace from "@components/NavigationBacktrace";
import { selectRootState } from "@features/device/deviceSelectors";
+import { useIsDarkMode } from "@utils/hooks";
const ApplicationStatePage = () => {
const { t } = useTranslation();
+ const { isDarkMode } = useIsDarkMode();
+
const rootState = useSelector(selectRootState());
const backtrace = [t("applicationState.title")];
return (
-
-
+
);
diff --git a/src/components/pages/ConnectPage.tsx b/src/components/pages/ConnectPage.tsx
index 77bde5fa..e0d9d19c 100644
--- a/src/components/pages/ConnectPage.tsx
+++ b/src/components/pages/ConnectPage.tsx
@@ -4,12 +4,10 @@ import { useDispatch, useSelector } from "react-redux";
import { open } from "@tauri-apps/api/shell";
import * as Tabs from "@radix-ui/react-tabs";
+import MeshLogoLight from "@app/assets/Mesh_Logo_Light.svg";
+import MeshLogoDark from "@app/assets/Mesh_Logo_Dark.svg";
import Hero_Image from "@app/assets/onboard_hero_image.jpg";
-// TODO export these as svg
-import Meshtastic_Logo_Dark from "@app/assets/Mesh_Logo_Black.png";
-import Meshtastic_Logo_Light from "@app/assets/Mesh_Logo_White.svg";
-
import ConnectTab from "@components/connection/ConnectTab";
import TcpConnectPane from "@components/connection/TcpConnectPane";
import SerialConnectPane from "@components/connection/SerialConnectPane";
@@ -34,6 +32,7 @@ import {
import { requestSliceActions } from "@features/requests/requestReducer";
import { ConnectionType } from "@utils/connections";
+import { useIsDarkMode } from "@utils/hooks";
import "@components/SplashScreen/SplashScreen.css";
@@ -47,6 +46,8 @@ export interface IOnboardPageProps {
const ConnectPage = ({ unmountSelf }: IOnboardPageProps) => {
const { t } = useTranslation();
+ const { isDarkMode } = useIsDarkMode();
+
const dispatch = useDispatch();
const availableSerialPorts = useSelector(selectAvailablePorts());
const autoConnectPort = useSelector(selectAutoConnectPort());
@@ -186,9 +187,8 @@ const ConnectPage = ({ unmountSelf }: IOnboardPageProps) => {
diff --git a/src/components/pages/MessagingPage.tsx b/src/components/pages/MessagingPage.tsx
index 382f8935..cc62bc53 100644
--- a/src/components/pages/MessagingPage.tsx
+++ b/src/components/pages/MessagingPage.tsx
@@ -45,8 +45,8 @@ const MessagingPage = () => {
{activeChannelIdx != null && !!channels[activeChannelIdx] ? (
) : (
-
-
+
+
{t("messaging.noChannelsSelected")}
diff --git a/src/index.css b/src/index.css
index 8b0525fb..ccbcbda8 100644
--- a/src/index.css
+++ b/src/index.css
@@ -34,6 +34,17 @@ body {
@apply border;
@apply border-gray-100;
@apply shadow-lg;
+
+ /* Note: nesting of attributes not supported */
+ @media (prefers-color-scheme: dark) {
+ /* @apply bg-gray-800; */
+ --tw-bg-opacity: 1;
+ background-color: rgb(31 41 55 / var(--tw-bg-opacity));
+
+ /* @apply border-gray-700; */
+ --tw-border-opacity: 1;
+ border-color: rgb(55 65 81 / var(--tw-border-opacity));
+ }
}
.default-overlay.no-shadow {
@@ -50,3 +61,8 @@ body {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
+
+/* Overrides background color on react-json-view */
+.react-json-view {
+ background-color: transparent !important;
+}
diff --git a/src/utils/hooks.ts b/src/utils/hooks.ts
index 409ad839..24e17a33 100644
--- a/src/utils/hooks.ts
+++ b/src/utils/hooks.ts
@@ -1,4 +1,5 @@
import { useEffect, useState } from "react";
+import { error } from "@utils/errors";
/**
* This hook is intended for use in components that need to rerender
@@ -16,3 +17,34 @@ export const useComponentReload = (interval: number) => {
return time;
};
+
+// https://usehooks-ts.com/react-hook/use-dark-mode
+const COLOR_SCHEME_QUERY = "(prefers-color-scheme: dark)";
+
+export interface IUseDarkMode {
+ isDarkMode: boolean;
+}
+
+/**
+ * This hook is intended for use in components that need to know
+ * whether the user has dark mode enabled for conditional rendering.
+ * Developers should only use this when it is not possible to use
+ * CSS media queries.
+ * @returns Whether the user has dark mode enabled at the OS level
+ */
+export const useIsDarkMode = (): IUseDarkMode => {
+ // Browser needs to support matchMedia but can't make useState conditional
+ if (!window.matchMedia) {
+ error("Browser doesn't support window.matchMedia method");
+ }
+
+ const runQuery = () => window.matchMedia(COLOR_SCHEME_QUERY);
+
+ const [query, setQuery] = useState
(runQuery);
+
+ query.onchange = () => {
+ setQuery(runQuery);
+ };
+
+ return { isDarkMode: query.matches };
+};
diff --git a/src/utils/nodes.ts b/src/utils/nodes.ts
index 37b9ccb5..dc58e48e 100644
--- a/src/utils/nodes.ts
+++ b/src/utils/nodes.ts
@@ -61,35 +61,35 @@ export const getColorClassFromNodeState = (
switch (nodeState) {
case "selected":
return {
- text: "text-blue-500",
- fill: "fill-blue-500",
- background: "bg-blue-500",
- border: "border-blue-500",
+ text: "text-blue-500 dark:text-blue-300",
+ fill: "fill-blue-500 dark:text-blue-300",
+ background: "bg-blue-500 dark:bg-blue-300",
+ border: "border-blue-500 dark:border-blue-300",
};
case "warning":
return {
- text: "text-orange-500",
- fill: "fill-orange-500",
- background: "bg-orange-500",
- border: "border-orange-500",
+ text: "text-orange-500 dark:text-orange-300",
+ fill: "fill-orange-500 dark:text-orange-300",
+ background: "bg-orange-500 dark:bg-orange-300",
+ border: "border-orange-500 dark:border-orange-300",
};
case "error":
return {
- text: "text-red-500",
- fill: "fill-red-500",
- background: "bg-red-500",
- border: "border-red-500",
+ text: "text-red-500 dark:text-red-300",
+ fill: "fill-red-500 dark:text-red-300",
+ background: "bg-red-500 dark:bg-red-300",
+ border: "border-red-500 dark:border-red-300",
};
// Nominal
default:
return {
- text: "text-gray-500",
- fill: "fill-gray-500",
- background: "bg-gray-500",
- border: "border-gray-500",
+ text: "text-gray-500 dark:text-gray-400",
+ fill: "fill-gray-500 dark:text-gray-400",
+ background: "bg-gray-500 dark:bg-gray-400",
+ border: "border-gray-500 dark:border-gray-400",
};
}
};
From 5ef9488ec9d14adaed952882183cdbaae470e1ec Mon Sep 17 00:00:00 2001
From: Adam McQuilkin <46639306+ajmcquilkin@users.noreply.github.com>
Date: Wed, 26 Jul 2023 16:05:39 -0700
Subject: [PATCH 3/6] Refactored map style config into appConfig
---
src/components/Map/MapView.tsx | 7 +++--
.../Messaging/TextMessageBubble.tsx | 10 +++----
.../config/application/MapConfigPage.tsx | 11 ++++----
src/features/appConfig/appConfigSagas.ts | 6 +++--
src/features/appConfig/appConfigSelectors.ts | 10 ++++++-
src/features/appConfig/appConfigSlice.ts | 26 ++++++++++++++++++-
src/features/map/mapSlice.ts | 11 --------
7 files changed, 54 insertions(+), 27 deletions(-)
diff --git a/src/components/Map/MapView.tsx b/src/components/Map/MapView.tsx
index efdd3a7c..55a1718f 100644
--- a/src/components/Map/MapView.tsx
+++ b/src/components/Map/MapView.tsx
@@ -40,6 +40,7 @@ import MapNodeTooltip from "@components/Map/MapNodeTooltip";
import MeshWaypoint from "@components/Waypoints/MeshWaypoint";
import WaypointMenu from "@components/Waypoints/WaypointMenu";
+import { selectMapConfigState } from "@features/appConfig/appConfigSelectors";
import {
selectActiveNodeId,
selectActiveWaypoint,
@@ -75,9 +76,11 @@ export const MapView = () => {
const activeNodeId = useSelector(selectActiveNodeId());
const showInfoPane = useSelector(selectInfoPane());
- const { nodesFeatureCollection, edgesFeatureCollection, viewState, config } =
+ const { nodesFeatureCollection, edgesFeatureCollection, viewState } =
useSelector(selectMapState());
+ const { style } = useSelector(selectMapConfigState());
+
const waypoints = useSelector(selectAllWaypoints());
const activeWaypoint = useSelector(selectActiveWaypoint());
@@ -243,7 +246,7 @@ export const MapView = () => {