mirror of
https://github.com/meshtastic/web.git
synced 2025-12-24 00:00:01 -05:00
feat: add dependency injection using tanstack context (#680)
* feat: add dep injection using tanstack context * fixed small typo
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -4,4 +4,5 @@ stats.html
|
||||
.vercel
|
||||
.vite
|
||||
dev-dist
|
||||
__screenshots__*
|
||||
__screenshots__*
|
||||
*.diff
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "src/core/connection"]
|
||||
path = src/core/connection
|
||||
url = https://github.com/meshtastic/js.git
|
||||
@@ -82,6 +82,7 @@ export const NodeDetailsDialog = ({
|
||||
function handleDirectMessage() {
|
||||
if (!node) return;
|
||||
navigate({ to: `/messages/direct/${node.num}` });
|
||||
setDialogOpen("nodeDetails", false);
|
||||
}
|
||||
|
||||
function handleRequestPosition() {
|
||||
|
||||
@@ -51,7 +51,7 @@ interface MessageItemProps {
|
||||
export const MessageItem = ({ message }: MessageItemProps) => {
|
||||
const { getNode } = useDevice();
|
||||
const { getMyNodeNum } = useMessageStore();
|
||||
const { t, i18n } = useTranslation();
|
||||
const { t, i18n } = useTranslation("messages");
|
||||
|
||||
const MESSAGE_STATUS_MAP = useMemo(
|
||||
(): Record<MessageState, MessageStatusInfo> => ({
|
||||
@@ -78,7 +78,7 @@ export const MessageItem = ({ message }: MessageItemProps) => {
|
||||
);
|
||||
|
||||
const UNKNOWN_STATUS = useMemo((): MessageStatusInfo => ({
|
||||
displayText: t("delveryStatus.unknown.displayText"),
|
||||
displayText: t("deliveryStatus.unknown.displayText"),
|
||||
icon: AlertCircle,
|
||||
ariaLabel: t("deliveryStatus.unknown.label"),
|
||||
iconClassName: "text-red-500 dark:text-red-400",
|
||||
@@ -99,7 +99,7 @@ export const MessageItem = ({ message }: MessageItemProps) => {
|
||||
const { displayName, shortName, isFavorite } = useMemo(() => {
|
||||
const userIdHex = message.from.toString(16).toUpperCase().padStart(2, "0");
|
||||
const last4 = userIdHex.slice(-4);
|
||||
const fallbackName = t("message_item_fallbackName_withLastFour", { last4 });
|
||||
const fallbackName = t("fallbackName", { last4 });
|
||||
const longName = messageUser?.user?.longName;
|
||||
const derivedShortName = messageUser?.user?.shortName || fallbackName;
|
||||
const derivedDisplayName = longName || derivedShortName;
|
||||
|
||||
@@ -70,7 +70,7 @@ const Generator = (
|
||||
key: "bit8",
|
||||
},
|
||||
{
|
||||
text: t("security.empty"),
|
||||
text: t("security.0bit"),
|
||||
value: "0",
|
||||
key: "bit0",
|
||||
},
|
||||
|
||||
@@ -114,6 +114,8 @@ export interface Device {
|
||||
hasNodeError: (nodeNum: number) => boolean;
|
||||
incrementUnread: (nodeNum: number) => void;
|
||||
resetUnread: (nodeNum: number) => void;
|
||||
getUnreadCount: (nodeNum: number) => number;
|
||||
getAllUnreadCount: () => number;
|
||||
getNodes: (
|
||||
filter?: (node: Protobuf.Mesh.NodeInfo) => boolean,
|
||||
) => Protobuf.Mesh.NodeInfo[];
|
||||
@@ -664,6 +666,24 @@ export const useDeviceStore = createStore<PrivateDeviceState>((set, get) => ({
|
||||
}),
|
||||
);
|
||||
},
|
||||
getUnreadCount: (nodeNum: number): number => {
|
||||
const device = get().devices.get(id);
|
||||
if (!device) {
|
||||
return 0;
|
||||
}
|
||||
return device.unreadCounts.get(nodeNum) ?? 0;
|
||||
},
|
||||
getAllUnreadCount: (): number => {
|
||||
const device = get().devices.get(id);
|
||||
if (!device) {
|
||||
return 0;
|
||||
}
|
||||
let totalUnread = 0;
|
||||
device.unreadCounts.forEach((count) => {
|
||||
totalUnread += count;
|
||||
});
|
||||
return totalUnread;
|
||||
},
|
||||
resetUnread: (nodeNum: number) => {
|
||||
set(
|
||||
produce<PrivateDeviceState>((draft) => {
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
import React from "react";
|
||||
import "@app/index.css";
|
||||
import { enableMapSet } from "immer";
|
||||
import "maplibre-gl/dist/maplibre-gl.css";
|
||||
import { StrictMode, Suspense } from "react";
|
||||
import { Suspense } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import "./i18n/config.ts";
|
||||
import { createRouter, RouterProvider } from "@tanstack/react-router";
|
||||
import { routeTree } from "@app/routes.tsx";
|
||||
import { router } from "@app/routes.tsx";
|
||||
import { useAppStore } from "@core/stores/appStore.ts";
|
||||
import { useMessageStore } from "@core/stores/messageStore/index.ts";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
declare module "@tanstack/react-router" {
|
||||
interface Register {
|
||||
@@ -16,16 +20,27 @@ declare module "@tanstack/react-router" {
|
||||
const container = document.getElementById("root") as HTMLElement;
|
||||
const root = createRoot(container);
|
||||
|
||||
enableMapSet();
|
||||
function IndexPage() {
|
||||
enableMapSet();
|
||||
const appStore = useAppStore();
|
||||
const messageStore = useMessageStore();
|
||||
const translation = useTranslation();
|
||||
|
||||
const router = createRouter({
|
||||
routeTree,
|
||||
});
|
||||
const context = React.useMemo(() => ({
|
||||
stores: {
|
||||
app: appStore,
|
||||
message: messageStore,
|
||||
},
|
||||
i18n: translation,
|
||||
}), [appStore, messageStore]);
|
||||
|
||||
root.render(
|
||||
<StrictMode>
|
||||
<Suspense fallback={null}>
|
||||
<RouterProvider router={router} />
|
||||
</Suspense>
|
||||
</StrictMode>,
|
||||
);
|
||||
return (
|
||||
<React.StrictMode>
|
||||
<Suspense fallback={null}>
|
||||
<RouterProvider router={router} context={context} />
|
||||
</Suspense>
|
||||
</React.StrictMode>
|
||||
);
|
||||
}
|
||||
|
||||
root.render(<IndexPage />);
|
||||
|
||||
@@ -47,7 +47,7 @@ export const MessagesPage = () => {
|
||||
getNodes,
|
||||
getNode,
|
||||
hasNodeError,
|
||||
unreadCounts,
|
||||
getUnreadCount,
|
||||
resetUnread,
|
||||
connection,
|
||||
} = useDevice();
|
||||
@@ -105,7 +105,7 @@ export const MessagesPage = () => {
|
||||
})
|
||||
.map((node) => ({
|
||||
...node,
|
||||
unreadCount: unreadCounts.get(node.num) ?? 0,
|
||||
unreadCount: getUnreadCount(node.num) ?? 0,
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
const diff = b.unreadCount - a.unreadCount;
|
||||
@@ -211,7 +211,7 @@ export const MessagesPage = () => {
|
||||
{filteredChannels?.map((channel) => (
|
||||
<SidebarButton
|
||||
key={channel.index}
|
||||
count={unreadCounts.get(channel.index)}
|
||||
count={getUnreadCount(channel.index)}
|
||||
label={channel.settings?.name ||
|
||||
(channel.index === 0
|
||||
? t("page.broadcastLabel", { ns: "channels" })
|
||||
@@ -236,10 +236,10 @@ export const MessagesPage = () => {
|
||||
</Sidebar>
|
||||
), [
|
||||
filteredChannels,
|
||||
unreadCounts,
|
||||
numericChatId,
|
||||
chatType,
|
||||
isCollapsed,
|
||||
getUnreadCount,
|
||||
navigateToChat,
|
||||
resetUnread,
|
||||
t,
|
||||
@@ -249,7 +249,7 @@ export const MessagesPage = () => {
|
||||
() => (
|
||||
<SidebarSection
|
||||
label=""
|
||||
className="px-0 flex flex-col h-full overflow-y-auto"
|
||||
className="px-0 flex-col h-full overflow-y-auto"
|
||||
>
|
||||
<label className="p-2 block">
|
||||
<Input
|
||||
|
||||
@@ -1,17 +1,32 @@
|
||||
import { createRoute, redirect } from "@tanstack/react-router";
|
||||
import {
|
||||
createRootRouteWithContext,
|
||||
createRoute,
|
||||
createRouter,
|
||||
redirect,
|
||||
} from "@tanstack/react-router";
|
||||
import { Dashboard } from "@pages/Dashboard/index.tsx";
|
||||
import MessagesPage from "@pages/Messages.tsx";
|
||||
import MapPage from "@pages/Map/index.tsx";
|
||||
import ConfigPage from "@pages/Config/index.tsx";
|
||||
import ChannelsPage from "@pages/Channels.tsx";
|
||||
import NodesPage from "@pages/Nodes/index.tsx";
|
||||
import { createRootRoute } from "@tanstack/react-router";
|
||||
import { App } from "./App.tsx";
|
||||
import { DialogManager } from "@components/Dialog/DialogManager.tsx";
|
||||
import { z } from "zod";
|
||||
import { useAppStore } from "@core/stores/appStore.ts";
|
||||
import { useMessageStore } from "@core/stores/messageStore/index.ts";
|
||||
import { App } from "./App.tsx";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const rootRoute = createRootRoute({
|
||||
component: App,
|
||||
interface AppContext {
|
||||
stores: {
|
||||
app: ReturnType<typeof useAppStore>;
|
||||
message: ReturnType<typeof useMessageStore>;
|
||||
};
|
||||
i18n: ReturnType<typeof useTranslation>;
|
||||
}
|
||||
|
||||
export const rootRoute = createRootRouteWithContext<AppContext>()({
|
||||
component: () => <App />,
|
||||
});
|
||||
|
||||
const indexRoute = createRoute({
|
||||
@@ -96,7 +111,7 @@ const dialogWithParamsRoute = createRoute({
|
||||
component: DialogManager,
|
||||
});
|
||||
|
||||
export const routeTree = rootRoute.addChildren([
|
||||
const routeTree = rootRoute.addChildren([
|
||||
indexRoute,
|
||||
messagesRoute,
|
||||
messagesWithParamsRoute,
|
||||
@@ -107,4 +122,14 @@ export const routeTree = rootRoute.addChildren([
|
||||
dialogWithParamsRoute,
|
||||
]);
|
||||
|
||||
export { rootRoute };
|
||||
const router = createRouter({
|
||||
routeTree,
|
||||
context: {
|
||||
stores: {
|
||||
app: {} as ReturnType<typeof useAppStore>,
|
||||
message: {} as ReturnType<typeof useMessageStore>,
|
||||
},
|
||||
i18n: {} as ReturnType<typeof import("react-i18next").useTranslation>,
|
||||
},
|
||||
});
|
||||
export { router };
|
||||
|
||||
Reference in New Issue
Block a user