support for changing the app name, info modal

This commit is contained in:
ajvpot
2025-08-02 04:27:36 +02:00
parent b990123256
commit 6c297b4daf
4 changed files with 48 additions and 37 deletions
+6 -15
View File
@@ -1,6 +1,7 @@
"use client";
import React, { createContext, useContext, useState, useEffect, useRef, useLayoutEffect, ReactNode } from "react";
import { getChannelIdFromKey } from "../lib/meshcore";
import Modal from "./Modal";
// Config shape
type NodeType = "meshcore" | "meshtastic";
@@ -269,19 +270,10 @@ function validateMeshcoreKey(key: string): string | null {
function MeshcoreKeyModal({ config, setConfig, onClose }: { config: Config, setConfig: (c: Config) => void, onClose: () => void }) {
return (
<div className="fixed inset-0 z-[3000] flex items-center justify-center bg-black bg-opacity-40">
<div className="bg-white dark:bg-neutral-900 rounded-lg shadow-lg p-6 min-w-[350px] max-w-[90vw] max-h-[60vh] overflow-auto border border-gray-200 dark:border-neutral-700 relative">
<button
className="absolute top-2 right-2 text-gray-400 hover:text-gray-700 dark:hover:text-gray-200"
onClick={onClose}
aria-label="Close key modal"
>
<svg width="24" height="24" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" /></svg>
</button>
<h2 className="text-lg font-semibold mb-4">Meshcore Private Keys</h2>
<p className="mb-4 text-sm text-gray-600 dark:text-gray-300">
These keys will be used to decrypt messages. <b>Your keys are never shared with the server</b>, so your messages remain secure.
</p>
<Modal isOpen={true} onClose={onClose} title="Meshcore Private Keys" maxWidth="90vw">
<p className="mb-4 text-sm text-gray-600 dark:text-gray-300">
These keys will be used to decrypt messages. <b>Your keys are never shared with the server</b>, so your messages remain secure.
</p>
<div className="flex flex-col gap-1 mb-2 p-2 border rounded bg-gray-100 dark:bg-neutral-700 opacity-80">
<div className="flex items-center gap-2">
<input
@@ -389,7 +381,6 @@ function MeshcoreKeyModal({ config, setConfig, onClose }: { config: Config, setC
>
Add Meshcore Key
</button>
</div>
</div>
</Modal>
);
}
+4 -3
View File
@@ -4,6 +4,7 @@ import { Cog6ToothIcon, InformationCircleIcon } from "@heroicons/react/24/outlin
import { useConfig } from "./ConfigContext";
import React, { useState } from "react";
import InfoModal from "./InfoModal";
import { getAppName } from "../lib/api";
interface HeaderProps {
configButtonRef?: React.Ref<HTMLButtonElement>;
@@ -17,7 +18,7 @@ export default function Header({ configButtonRef }: HeaderProps) {
<>
<header className="w-full flex items-center justify-between px-6 py-3 bg-white dark:bg-neutral-900 shadow z-20">
<nav className="flex gap-6 items-center">
<Link href="/" className="font-bold text-lg">MeshExplorer</Link>
<Link href="/" className="font-bold text-lg">{getAppName()}</Link>
<Link href="/messages">Messages</Link>
<Link href="/stats">Stats</Link>
</nav>
@@ -25,7 +26,7 @@ export default function Header({ configButtonRef }: HeaderProps) {
<button
onClick={() => setInfoModalOpen(true)}
className="flex items-center gap-2 px-3 py-2 rounded hover:bg-neutral-100 dark:hover:bg-neutral-800"
aria-label="Open information about MeshExplorer"
aria-label={`Open information about ${getAppName()}`}
>
<InformationCircleIcon className="h-6 w-6" />
<span className="hidden sm:inline">Info</span>
@@ -41,7 +42,7 @@ export default function Header({ configButtonRef }: HeaderProps) {
</button>
</div>
</header>
{infoModalOpen && <InfoModal onClose={() => setInfoModalOpen(false)} />}
<InfoModal isOpen={infoModalOpen} onClose={() => setInfoModalOpen(false)} />
</>
);
}
+29 -19
View File
@@ -1,28 +1,20 @@
"use client";
import React from "react";
import { getAppName } from "../lib/api";
import Modal from "./Modal";
interface InfoModalProps {
isOpen: boolean;
onClose: () => void;
}
export default function InfoModal({ onClose }: InfoModalProps) {
export default function InfoModal({ isOpen, onClose }: InfoModalProps) {
return (
<div className="fixed inset-0 z-[3000] flex items-center justify-center bg-black bg-opacity-40">
<div className="bg-white dark:bg-neutral-900 rounded-lg shadow-lg p-6 min-w-[350px] max-w-[500px] max-h-[60vh] overflow-auto border border-gray-200 dark:border-neutral-700 relative">
<button
className="absolute top-2 right-2 text-gray-400 hover:text-gray-700 dark:hover:text-gray-200"
onClick={onClose}
aria-label="Close info modal"
>
<svg width="24" height="24" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" /></svg>
</button>
<h2 className="text-lg font-semibold mb-4">About MeshExplorer</h2>
<div className="space-y-4">
<Modal isOpen={isOpen} onClose={onClose} title={`About ${getAppName()}`}>
<div className="space-y-4">
<div>
<p className="text-sm text-gray-600 dark:text-gray-300 mb-3">
MeshExplorer is a web-based visualization tool for exploring and monitoring mesh networks.
{getAppName()} is a web-based visualization tool for exploring and monitoring mesh networks.
It provides real-time mapping of network nodes, message tracking, and statistical analysis for MeshCore and Meshtastic networks.
</p>
</div>
@@ -40,10 +32,29 @@ export default function InfoModal({ onClose }: InfoModalProps) {
</div>
</div>
<div className="border-t border-gray-200 dark:border-neutral-700 pt-4">
<h3 className="text-md font-medium mb-2">Community</h3>
<p className="text-sm text-gray-600 dark:text-gray-300 mb-3">
Connect with the Puget Mesh community, a volunteer group supporting AREDN, Meshtastic, and other off-grid mesh networks in the Puget Sound Region.
</p>
<a
href="https://pugetmesh.org/"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-2 px-3 py-2 bg-green-600 text-white rounded hover:bg-green-700 text-sm"
>
<svg className="h-4 w-4" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
<path strokeLinecap="round" strokeLinejoin="round" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
Visit Puget Mesh
</a>
</div>
<div className="border-t border-gray-200 dark:border-neutral-700 pt-4">
<h3 className="text-md font-medium mb-2">Contributing</h3>
<p className="text-sm text-gray-600 dark:text-gray-300 mb-3">
MeshExplorer is open source and welcomes contributions from the community.
{getAppName()} is open source and welcomes contributions from the community.
</p>
<a
href="https://github.com/ajvpot/meshexplorer"
@@ -58,7 +69,6 @@ export default function InfoModal({ onClose }: InfoModalProps) {
</a>
</div>
</div>
</div>
</div>
);
</Modal>
);
}
+9
View File
@@ -7,6 +7,15 @@ export function getApiBaseUrl(): string {
return process.env.NEXT_PUBLIC_API_URL || '';
}
/**
* Get the application name.
* If NEXT_PUBLIC_APP_NAME is set, it will be used as the application name.
* Otherwise, "MeshExplorer" will be used as the default.
*/
export function getAppName(): string {
return process.env.NEXT_PUBLIC_APP_NAME || 'MeshExplorer';
}
/**
* Build a full API URL by combining the base URL with the endpoint path.
* @param endpoint - The API endpoint path (e.g., '/api/stats/total-nodes')