Feat(config): Align settings menu to match android/ios (#906)
* feat: aligned settings menu to match android/ios * updated sidebar text size. * Update packages/web/public/i18n/locales/en/config.json Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update packages/web/public/i18n/locales/en/config.json Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update packages/web/src/components/PageComponents/Settings/User.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update packages/web/src/components/PageComponents/ModuleConfig/Telemetry.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * linting/formatting fixes * fixed formatting issue --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
import { Channel } from "@app/components/PageComponents/Channels/Channel";
|
||||
import { Button } from "@components/UI/Button.tsx";
|
||||
import { Spinner } from "@components/UI/Spinner.tsx";
|
||||
import {
|
||||
Tabs,
|
||||
TabsContent,
|
||||
TabsList,
|
||||
TabsTrigger,
|
||||
} from "@components/UI/Tabs.tsx";
|
||||
import { useDevice } from "@core/stores";
|
||||
import type { Protobuf } from "@meshtastic/core";
|
||||
import i18next from "i18next";
|
||||
import { QrCodeIcon, UploadIcon } from "lucide-react";
|
||||
import { Suspense, useMemo } from "react";
|
||||
import type { UseFormReturn } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface ConfigProps {
|
||||
onFormInit: <T extends object>(methods: UseFormReturn<T>) => void;
|
||||
}
|
||||
|
||||
export const getChannelName = (channel: Protobuf.Channel.Channel) => {
|
||||
return channel.settings?.name.length
|
||||
? channel.settings?.name
|
||||
: channel.index === 0
|
||||
? i18next.t("page.broadcastLabel")
|
||||
: i18next.t("page.channelIndex", {
|
||||
ns: "channels",
|
||||
index: channel.index,
|
||||
});
|
||||
};
|
||||
|
||||
export const Channels = ({ onFormInit }: ConfigProps) => {
|
||||
const { channels, hasChannelChange, setDialogOpen } = useDevice();
|
||||
const { t } = useTranslation("channels");
|
||||
|
||||
const allChannels = Array.from(channels.values());
|
||||
const flags = useMemo(
|
||||
() =>
|
||||
new Map(
|
||||
allChannels.map((channel) => [
|
||||
channel.index,
|
||||
hasChannelChange(channel.index),
|
||||
]),
|
||||
),
|
||||
[allChannels, hasChannelChange],
|
||||
);
|
||||
|
||||
return (
|
||||
<Tabs defaultValue="channel_0">
|
||||
<TabsList className="w-full dark:bg-slate-700">
|
||||
{allChannels.map((channel) => (
|
||||
<TabsTrigger
|
||||
key={`channel_${channel.index}`}
|
||||
value={`channel_${channel.index}`}
|
||||
className="dark:text-white relative"
|
||||
>
|
||||
{getChannelName(channel)}
|
||||
{flags.get(channel.index) && (
|
||||
<span className="absolute -top-0.5 -right-0.5 z-50 flex size-3">
|
||||
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-sky-500 opacity-25" />
|
||||
<span className="relative inline-flex size-3 rounded-full bg-sky-500" />
|
||||
</span>
|
||||
)}
|
||||
</TabsTrigger>
|
||||
))}
|
||||
<Button
|
||||
className="ml-auto mr-1 h-8"
|
||||
onClick={() => setDialogOpen("import", true)}
|
||||
>
|
||||
<UploadIcon className="mr-2" size={14} />
|
||||
{t("page.import")}
|
||||
</Button>
|
||||
<Button className=" h-8" onClick={() => setDialogOpen("QR", true)}>
|
||||
<QrCodeIcon className="mr-2" size={14} />
|
||||
{t("page.export")}
|
||||
</Button>
|
||||
</TabsList>
|
||||
{allChannels.map((channel) => (
|
||||
<TabsContent
|
||||
key={`channel_${channel.index}`}
|
||||
value={`channel_${channel.index}`}
|
||||
>
|
||||
<Suspense fallback={<Spinner size="lg" className="my-5" />}>
|
||||
<Channel
|
||||
key={channel.index}
|
||||
onFormInit={onFormInit}
|
||||
channel={channel}
|
||||
/>
|
||||
</Suspense>
|
||||
</TabsContent>
|
||||
))}
|
||||
</Tabs>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user