This commit is contained in:
Sacha Weatherstone
2021-06-15 23:15:33 +10:00
parent 7d220e2683
commit 339143ef7f
22 changed files with 161 additions and 217 deletions
-7
View File
@@ -15,7 +15,6 @@ import {
import Header from './components/Header';
import Main from './Main';
import { channelSubject$, nodeSubject$, preferencesSubject$ } from './streams';
import { LanguageEnum } from './translations/TranslationContext';
const App = (): JSX.Element => {
const [deviceStatus, setDeviceStatus] =
@@ -31,10 +30,6 @@ const App = (): JSX.Element => {
const [isReady, setIsReady] = React.useState<boolean>(false);
const [lastMeshInterraction, setLastMeshInterraction] =
React.useState<number>(0);
const [language, setLanguage] = React.useState<LanguageEnum>(
LanguageEnum.ENGLISH,
);
const [darkmode, setDarkmode] = React.useState<boolean>(false);
React.useEffect(() => {
@@ -115,8 +110,6 @@ const App = (): JSX.Element => {
isReady={isReady}
myNodeInfo={myNodeInfo}
connection={connection}
language={language}
setLanguage={setLanguage}
darkmode={darkmode}
setDarkmode={setDarkmode}
/>
+34 -36
View File
@@ -11,21 +11,21 @@ import type {
import ChatMessage from './components/ChatMessage';
import MessageBox from './components/MessageBox';
import Sidebar from './components/Sidebar';
import type { LanguageEnum } from './translations/TranslationContext';
import { TranslationContext } from './translations/TranslationContext';
import { useTranslationsContextValue } from './hooks/useTranslationsContextValue';
import { TranslationsContext } from './translations/TranslationsContext';
interface MainProps {
connection: ISerialConnection | IHTTPConnection | IBLEConnection;
myNodeInfo: Protobuf.MyNodeInfo;
isReady: boolean;
language: LanguageEnum;
setLanguage: React.Dispatch<React.SetStateAction<LanguageEnum>>;
darkmode: boolean;
setDarkmode: React.Dispatch<React.SetStateAction<boolean>>;
}
const Main = (props: MainProps): JSX.Element => {
const { translations } = React.useContext(TranslationContext);
const translationsContextValue = useTranslationsContextValue();
const { translations } = React.useContext(TranslationsContext);
const [messages, setMessages] = React.useState<
{ message: Types.TextPacket; ack: boolean }[]
>([]);
@@ -65,41 +65,39 @@ const Main = (props: MainProps): JSX.Element => {
}, [props.connection, messages]);
return (
<div className="flex flex-col md:flex-row flex-grow m-3 space-y-2 md:space-y-0 space-x-0 md:space-x-2">
<div className="flex flex-col flex-grow container mx-auto">
<div className="flex flex-col flex-grow py-6 space-y-2">
{messages.length ? (
messages.map((message, Main) => (
<ChatMessage
key={Main}
message={message}
myId={props.myNodeInfo.myNodeNum}
/>
))
) : (
<div className="m-auto text-2xl text-gray-500">
{translations.no_messages_message}
</div>
)}
<TranslationsContext.Provider value={translationsContextValue}>
<div className="flex flex-col md:flex-row flex-grow m-3 space-y-2 md:space-y-0 space-x-0 md:space-x-2">
<div className="flex flex-col flex-grow container mx-auto">
<div className="flex flex-col flex-grow py-6 space-y-2">
{messages.length ? (
messages.map((message, Main) => (
<ChatMessage
key={Main}
message={message}
myId={props.myNodeInfo.myNodeNum}
/>
))
) : (
<div className="m-auto text-2xl text-gray-500">
{translations.no_messages_message}
</div>
)}
</div>
<MessageBox
connection={props.connection}
isReady={props.isReady}
sidebarOpen={sidebarOpen}
setSidebarOpen={setSidebarOpen}
/>
</div>
<MessageBox
connection={props.connection}
isReady={props.isReady}
<Sidebar
myId={props.myNodeInfo.myNodeNum}
sidebarOpen={sidebarOpen}
setSidebarOpen={setSidebarOpen}
darkmode={props.darkmode}
setDarkmode={props.setDarkmode}
/>
</div>
<Sidebar
isReady={props.isReady}
connection={props.connection}
language={props.language}
setLanguage={props.setLanguage}
myId={props.myNodeInfo.myNodeNum}
sidebarOpen={sidebarOpen}
darkmode={props.darkmode}
setDarkmode={props.setDarkmode}
/>
</div>
</TranslationsContext.Provider>
);
};
+2 -2
View File
@@ -7,7 +7,7 @@ import type {
ISerialConnection,
} from '@meshtastic/meshtasticjs';
import { TranslationContext } from '../translations/TranslationContext';
import { TranslationsContext } from '../translations/TranslationsContext';
export interface MessageBoxProps {
sidebarOpen: boolean;
@@ -17,7 +17,7 @@ export interface MessageBoxProps {
}
const MessageBox = (props: MessageBoxProps): JSX.Element => {
const { translations } = React.useContext(TranslationContext);
const { translations } = React.useContext(TranslationsContext);
const [currentMessage, setCurrentMessage] = React.useState('');
const sendMessage = () => {
if (props.isReady) {
+2 -18
View File
@@ -1,22 +1,11 @@
import React from 'react';
import type {
IBLEConnection,
IHTTPConnection,
ISerialConnection,
} from '@meshtastic/meshtasticjs';
import type { LanguageEnum } from '../translations/TranslationContext';
import Channels from './Sidebar/Channels/Index';
import Device from './Sidebar/Device/Index';
import Nodes from './Sidebar/Nodes/Index';
import UI from './Sidebar/UI/Index';
interface SidebarProps {
isReady: boolean;
connection: ISerialConnection | IHTTPConnection | IBLEConnection;
language: LanguageEnum;
setLanguage: React.Dispatch<React.SetStateAction<LanguageEnum>>;
myId: number;
sidebarOpen: boolean;
darkmode: boolean;
@@ -31,15 +20,10 @@ const Sidebar = (props: SidebarProps): JSX.Element => {
} flex-col rounded-md md:ml-0 shadow-md border w-full max-w-sm`}
>
<Nodes myId={props.myId} />
<Device isReady={props.isReady} connection={props.connection} />
<Device />
<Channels />
<div className="flex-grow border-b"></div>
<UI
language={props.language}
setLanguage={props.setLanguage}
darkmode={props.darkmode}
setDarkmode={props.setDarkmode}
/>
<UI darkmode={props.darkmode} setDarkmode={props.setDarkmode} />
</div>
);
};
+2 -2
View File
@@ -7,11 +7,11 @@ import {
HashtagIcon,
} from '@heroicons/react/outline';
import { TranslationContext } from '../../../translations/TranslationContext';
import { TranslationsContext } from '../../../translations/TranslationsContext';
import ChannelList from './ChannelList';
const Channels = (): JSX.Element => {
const { translations } = React.useContext(TranslationContext);
const { translations } = React.useContext(TranslationsContext);
return (
<Disclosure>
{({ open }) => (
+4 -17
View File
@@ -6,22 +6,12 @@ import {
ChevronDownIcon,
ChevronRightIcon,
} from '@heroicons/react/outline';
import type {
IBLEConnection,
IHTTPConnection,
ISerialConnection,
} from '@meshtastic/meshtasticjs';
import { TranslationContext } from '../../../translations/TranslationContext';
import { TranslationsContext } from '../../../translations/TranslationsContext';
import Settings from './Settings';
interface DeviceProps {
isReady: boolean;
connection: ISerialConnection | IHTTPConnection | IBLEConnection;
}
const Device = (props: DeviceProps): JSX.Element => {
const { translations } = React.useContext(TranslationContext);
const Device = (): JSX.Element => {
const { translations } = React.useContext(TranslationsContext);
return (
<Disclosure>
{({ open }) => (
@@ -46,10 +36,7 @@ const Device = (props: DeviceProps): JSX.Element => {
</div>
}
>
<Settings
connection={props.connection}
isReady={props.isReady}
/>
<Settings />
</React.Suspense>
</>
</Disclosure.Panel>
+3 -13
View File
@@ -5,23 +5,13 @@ import { useForm } from 'react-hook-form';
import JSONPretty from 'react-json-pretty';
import { SaveIcon } from '@heroicons/react/outline';
import type {
IBLEConnection,
IHTTPConnection,
ISerialConnection,
} from '@meshtastic/meshtasticjs';
import { Protobuf } from '@meshtastic/meshtasticjs';
import { preferencesResource } from '../../../streams';
import { TranslationContext } from '../../../translations/TranslationContext';
import { TranslationsContext } from '../../../translations/TranslationsContext';
export interface SettingsProps {
isReady: boolean;
connection: ISerialConnection | IHTTPConnection | IBLEConnection;
}
const Settings = (props: SettingsProps): JSX.Element => {
const { translations } = React.useContext(TranslationContext);
const Settings = (): JSX.Element => {
const { translations } = React.useContext(TranslationsContext);
const preferences = useObservableSuspense(preferencesResource);
const { register, handleSubmit } =
+2 -2
View File
@@ -7,7 +7,7 @@ import {
UsersIcon,
} from '@heroicons/react/outline';
import { TranslationContext } from '../../../translations/TranslationContext';
import { TranslationsContext } from '../../../translations/TranslationsContext';
import NodeList from './NodeList';
interface NodesProps {
@@ -15,7 +15,7 @@ interface NodesProps {
}
const Nodes = (props: NodesProps): JSX.Element => {
const { translations } = React.useContext(TranslationContext);
const { translations } = React.useContext(TranslationsContext);
return (
<Disclosure>
{({ open }) => (
+1 -1
View File
@@ -36,7 +36,7 @@ const Node = (props: NodeProps): JSX.Element => {
) : null}
<Avatar
size={30}
name={props.node.data.user?.longName}
name={props.node.data.user?.longName ?? 'Unknown'}
variant="beam"
colors={[
'#213435',
+3 -9
View File
@@ -7,19 +7,16 @@ import {
CogIcon,
} from '@heroicons/react/outline';
import type { LanguageEnum } from '../../../translations/TranslationContext';
import { TranslationContext } from '../../../translations/TranslationContext';
import { TranslationsContext } from '../../../translations/TranslationsContext';
import Translations from './Translations';
interface UIProps {
language: LanguageEnum;
setLanguage: React.Dispatch<React.SetStateAction<LanguageEnum>>;
darkmode: boolean;
setDarkmode: React.Dispatch<React.SetStateAction<boolean>>;
}
const UI = (props: UIProps): JSX.Element => {
const { translations } = React.useContext(TranslationContext);
const { translations } = React.useContext(TranslationsContext);
return (
<Disclosure>
{({ open }) => (
@@ -36,10 +33,7 @@ const UI = (props: UIProps): JSX.Element => {
</div>
</Disclosure.Button>
<Disclosure.Panel>
<Translations
language={props.language}
setLanguage={props.setLanguage}
/>
<Translations />
</Disclosure.Panel>
</>
)}
+11 -15
View File
@@ -7,16 +7,12 @@ import { ChevronDownIcon, ChevronRightIcon } from '@heroicons/react/outline';
import {
LanguageEnum,
TranslationContext,
} from '../../../translations/TranslationContext';
TranslationsContext,
} from '../../../translations/TranslationsContext';
export interface TranslationsProps {
language: LanguageEnum;
setLanguage: React.Dispatch<React.SetStateAction<LanguageEnum>>;
}
const Translations = (props: TranslationsProps): JSX.Element => {
const { translations } = React.useContext(TranslationContext);
const Translations = (): JSX.Element => {
const { translations, language, setLanguage } =
React.useContext(TranslationsContext);
return (
<Disclosure>
{({ open }) => (
@@ -30,11 +26,11 @@ const Translations = (props: TranslationsProps): JSX.Element => {
)}
{translations.language_title}
<div className="my-auto">
{props.language === LanguageEnum.ENGLISH ? (
{language === LanguageEnum.ENGLISH ? (
<Us className="ml-2 w-8" />
) : props.language === LanguageEnum.JAPANESE ? (
) : language === LanguageEnum.JAPANESE ? (
<Jp className="ml-2 w-8" />
) : props.language === LanguageEnum.PORTUGUESE ? (
) : language === LanguageEnum.PORTUGUESE ? (
<Br className="ml-2 w-8" />
) : null}
</div>
@@ -44,7 +40,7 @@ const Translations = (props: TranslationsProps): JSX.Element => {
<div
className="flex bg-gray-100 hover:bg-gray-200 cursor-pointer justify-between p-2"
onClick={() => {
props.setLanguage(LanguageEnum.ENGLISH);
setLanguage(LanguageEnum.ENGLISH);
}}
>
English <Us className="w-8 my-auto" />
@@ -52,7 +48,7 @@ const Translations = (props: TranslationsProps): JSX.Element => {
<div
className="flex bg-gray-100 hover:bg-gray-200 cursor-pointer justify-between p-2"
onClick={() => {
props.setLanguage(LanguageEnum.PORTUGUESE);
setLanguage(LanguageEnum.PORTUGUESE);
}}
>
Português <Br className="w-8 my-auto" />
@@ -60,7 +56,7 @@ const Translations = (props: TranslationsProps): JSX.Element => {
<div
className="flex bg-gray-100 hover:bg-gray-200 cursor-pointer justify-between p-2"
onClick={() => {
props.setLanguage(LanguageEnum.JAPANESE);
setLanguage(LanguageEnum.JAPANESE);
}}
>
日本語 <Jp className="w-8 my-auto" />
+42
View File
@@ -0,0 +1,42 @@
import React from 'react';
import Translations_EN from '../translations/en';
import Translations_JP from '../translations/jp';
import Translations_PT from '../translations/pt';
import type {
languageTemplate,
TranslationsContextData,
} from '../translations/TranslationsContext';
import { LanguageEnum } from '../translations/TranslationsContext';
export const useTranslationsContextValue = (): TranslationsContextData => {
const [currentLanguage, setcurrentLanguage] = React.useState<LanguageEnum>(
LanguageEnum.ENGLISH,
);
const [translation, setTranslation] =
React.useState<languageTemplate>(Translations_EN);
const setLanguage = React.useCallback(
(language: LanguageEnum) => {
setcurrentLanguage(language);
switch (language) {
case LanguageEnum.ENGLISH:
setTranslation(Translations_EN);
break;
case LanguageEnum.JAPANESE:
setTranslation(Translations_JP);
break;
case LanguageEnum.PORTUGUESE:
setTranslation(Translations_PT);
break;
}
},
[setcurrentLanguage, setTranslation],
);
return {
language: currentLanguage,
setLanguage: setLanguage,
translations: translation,
};
};
+1 -4
View File
@@ -4,7 +4,6 @@ import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { TranslationContext } from './translations/TranslationContext';
const rootElement = document.getElementById('root');
if (!rootElement) throw new Error('Failed to find the root element');
@@ -12,9 +11,7 @@ const root = ReactDOM.createRoot(rootElement);
root.render(
<React.StrictMode>
<TranslationContext>
<App />
</TranslationContext>
<App />
</React.StrictMode>,
);
-73
View File
@@ -1,73 +0,0 @@
import React from 'react';
import Translations_EN from './en';
import Translations_JP from './jp';
import Translations_PT from './pt';
export interface languageTemplate {
no_messages_message: string;
ui_settings_title: string;
nodes_title: string;
color_scheme_title: string;
language_title: string;
device_settings_title: string;
device_channels_title: string;
device_region_title: string;
device_wifi_ssid: string;
device_wifi_psk: string;
save_changes_button: string;
no_nodes_message: string;
no_message_placeholder: string;
}
export enum LanguageEnum {
ENGLISH,
JAPANESE,
PORTUGUESE,
}
const Context = React.createContext<{
language: LanguageEnum;
setLanguage: React.Dispatch<React.SetStateAction<LanguageEnum>>;
translations: languageTemplate;
}>({
language: LanguageEnum.ENGLISH,
setLanguage: () => {},
translations: Translations_EN,
});
export const TranslationContext = ({
children,
}: {
children: React.ReactNode;
}): JSX.Element => {
const [language, setLanguage] = React.useState<LanguageEnum>(
LanguageEnum.ENGLISH,
);
const [translation, setTranslation] =
React.useState<languageTemplate>(Translations_EN);
React.useEffect(() => {
switch (language) {
case LanguageEnum.ENGLISH:
setTranslation(Translations_EN);
break;
case LanguageEnum.JAPANESE:
setTranslation(Translations_JP);
break;
case LanguageEnum.PORTUGUESE:
setTranslation(Translations_PT);
break;
}
}, [language]);
return (
<Context.Provider
value={{
language: language,
setLanguage: setLanguage,
translations: translation,
}}
>
{children}
</Context.Provider>
);
};
+41
View File
@@ -0,0 +1,41 @@
import React from 'react';
import Translations_EN from './en';
export interface languageTemplate {
no_messages_message: string;
ui_settings_title: string;
nodes_title: string;
color_scheme_title: string;
language_title: string;
device_settings_title: string;
device_channels_title: string;
device_region_title: string;
device_wifi_ssid: string;
device_wifi_psk: string;
save_changes_button: string;
no_nodes_message: string;
no_message_placeholder: string;
}
export enum LanguageEnum {
ENGLISH,
JAPANESE,
PORTUGUESE,
}
export interface TranslationsContextData {
language: LanguageEnum;
setLanguage: (postId: number) => void;
translations: languageTemplate;
}
export const translationsContextDefaultValue: TranslationsContextData = {
language: LanguageEnum.ENGLISH,
setLanguage: () => null,
translations: Translations_EN,
};
export const TranslationsContext = React.createContext<TranslationsContextData>(
translationsContextDefaultValue,
);
+1 -1
View File
@@ -1,4 +1,4 @@
import type { languageTemplate } from './TranslationContext';
import type { languageTemplate } from './TranslationsContext';
export default {
no_messages_message: 'No messages yet',
+1 -1
View File
@@ -1,4 +1,4 @@
import type { languageTemplate } from './TranslationContext';
import type { languageTemplate } from './TranslationsContext';
export default {
no_messages_message: 'まだメッセージはありません',
+1 -1
View File
@@ -1,4 +1,4 @@
import type { languageTemplate } from './TranslationContext';
import type { languageTemplate } from './TranslationsContext';
export default {
no_messages_message: 'Não a mensagens ainda',