Can change theme
This commit is contained in:
		
							
								
								
									
										7
									
								
								remote_frontend/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										7
									
								
								remote_frontend/package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -9,6 +9,7 @@
 | 
			
		||||
      "version": "0.0.0",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@fluentui/react-components": "^9.49.0",
 | 
			
		||||
        "@fluentui/react-icons": "^2.0.238",
 | 
			
		||||
        "react": "^18.2.0",
 | 
			
		||||
        "react-dom": "^18.2.0"
 | 
			
		||||
      },
 | 
			
		||||
@@ -1415,9 +1416,9 @@
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@fluentui/react-icons": {
 | 
			
		||||
      "version": "2.0.237",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@fluentui/react-icons/-/react-icons-2.0.237.tgz",
 | 
			
		||||
      "integrity": "sha512-J+6XDB/Y3G3nfNWvKKI6xhUXDN6/xvhksnV+zfXiGb6tqtK7QiihAzvRRgPnKuS3BH/H1TM7k3KC19V0AJrTDg==",
 | 
			
		||||
      "version": "2.0.238",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@fluentui/react-icons/-/react-icons-2.0.238.tgz",
 | 
			
		||||
      "integrity": "sha512-2T87k8lP4k2h98aDY2WlSACa2u0WBll1BQ/0+hsS0ir4uvRLRofLi5rtRUz3i1aOymeKvCoQzW6q1m03Z7JEOw==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@griffel/react": "^1.0.0",
 | 
			
		||||
        "tslib": "^2.1.0"
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "@fluentui/react-components": "^9.49.0",
 | 
			
		||||
    "@fluentui/react-icons": "^2.0.238",
 | 
			
		||||
    "react": "^18.2.0",
 | 
			
		||||
    "react-dom": "^18.2.0"
 | 
			
		||||
  },
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,8 @@
 | 
			
		||||
import {
 | 
			
		||||
  Menu,
 | 
			
		||||
  MenuButton,
 | 
			
		||||
  MenuItem,
 | 
			
		||||
  MenuList,
 | 
			
		||||
  MenuPopover,
 | 
			
		||||
  MenuTrigger,
 | 
			
		||||
  makeStyles,
 | 
			
		||||
  typographyStyles,
 | 
			
		||||
} from "@fluentui/react-components";
 | 
			
		||||
import { makeStyles, typographyStyles } from "@fluentui/react-components";
 | 
			
		||||
import { ServerApi } from "./api/ServerApi";
 | 
			
		||||
import { AuthRouteWidget } from "./routes/AuthRouteWidget";
 | 
			
		||||
import { AsyncWidget } from "./widgets/AsyncWidget";
 | 
			
		||||
import { AuthApi } from "./api/AuthApi";
 | 
			
		||||
import { useAlert } from "./hooks/providers/AlertDialogProvider";
 | 
			
		||||
import { useConfirm } from "./hooks/providers/ConfirmDialogProvider";
 | 
			
		||||
import { MainMenu } from "./widgets/MainMenu";
 | 
			
		||||
 | 
			
		||||
const useStyles = makeStyles({
 | 
			
		||||
  title: typographyStyles.title2,
 | 
			
		||||
@@ -32,20 +21,8 @@ export function App() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function AppInner(): React.ReactElement {
 | 
			
		||||
  const alert = useAlert();
 | 
			
		||||
  const confirm = useConfirm();
 | 
			
		||||
  const styles = useStyles();
 | 
			
		||||
 | 
			
		||||
  const signOut = async () => {
 | 
			
		||||
    try {
 | 
			
		||||
      if (!(await confirm("Do you really want to sign out?"))) return;
 | 
			
		||||
      await AuthApi.SignOut();
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      console.error(e);
 | 
			
		||||
      alert("Failed to perform sign out!");
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  if (!ServerApi.Config.authenticated && !ServerApi.Config.disable_auth)
 | 
			
		||||
    return <AuthRouteWidget />;
 | 
			
		||||
 | 
			
		||||
@@ -59,17 +36,7 @@ function AppInner(): React.ReactElement {
 | 
			
		||||
    >
 | 
			
		||||
      <div style={{ display: "flex", justifyContent: "space-between" }}>
 | 
			
		||||
        <span className={styles.title}>VirtWebRemote</span>
 | 
			
		||||
        <Menu>
 | 
			
		||||
          <MenuTrigger disableButtonEnhancement>
 | 
			
		||||
            <MenuButton>Account</MenuButton>
 | 
			
		||||
          </MenuTrigger>
 | 
			
		||||
 | 
			
		||||
          <MenuPopover>
 | 
			
		||||
            <MenuList>
 | 
			
		||||
              <MenuItem onClick={signOut}>Sign out</MenuItem>
 | 
			
		||||
            </MenuList>
 | 
			
		||||
          </MenuPopover>
 | 
			
		||||
        </Menu>
 | 
			
		||||
        <MainMenu />
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										55
									
								
								remote_frontend/src/hooks/providers/ThemeProvider.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								remote_frontend/src/hooks/providers/ThemeProvider.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
import {
 | 
			
		||||
  FluentProvider,
 | 
			
		||||
  teamsDarkTheme,
 | 
			
		||||
  teamsHighContrastTheme,
 | 
			
		||||
  teamsLightTheme,
 | 
			
		||||
  webDarkTheme,
 | 
			
		||||
  webLightTheme,
 | 
			
		||||
} from "@fluentui/react-components";
 | 
			
		||||
import React from "react";
 | 
			
		||||
 | 
			
		||||
export type Theme =
 | 
			
		||||
  | "highcontrast"
 | 
			
		||||
  | "teamsdark"
 | 
			
		||||
  | "teamslight"
 | 
			
		||||
  | "webdark"
 | 
			
		||||
  | "weblight";
 | 
			
		||||
 | 
			
		||||
type ThemeContext = { theme: Theme; set: (theme: Theme) => void };
 | 
			
		||||
 | 
			
		||||
const ThemeContextK = React.createContext<ThemeContext | null>(null);
 | 
			
		||||
 | 
			
		||||
export function ThemeProvider(p: React.PropsWithChildren): React.ReactElement {
 | 
			
		||||
  const [theme, setTheme] = React.useState<Theme>("highcontrast");
 | 
			
		||||
 | 
			
		||||
  let fluentTheme = teamsHighContrastTheme;
 | 
			
		||||
  switch (theme) {
 | 
			
		||||
    case "teamsdark":
 | 
			
		||||
      fluentTheme = teamsDarkTheme;
 | 
			
		||||
      break;
 | 
			
		||||
    case "teamslight":
 | 
			
		||||
      fluentTheme = teamsLightTheme;
 | 
			
		||||
      break;
 | 
			
		||||
    case "highcontrast":
 | 
			
		||||
      fluentTheme = teamsHighContrastTheme;
 | 
			
		||||
      break;
 | 
			
		||||
    case "webdark":
 | 
			
		||||
      fluentTheme = webDarkTheme;
 | 
			
		||||
      break;
 | 
			
		||||
    case "weblight":
 | 
			
		||||
      fluentTheme = webLightTheme;
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <ThemeContextK.Provider value={{ theme: theme, set: setTheme }}>
 | 
			
		||||
      <FluentProvider theme={fluentTheme} style={{ display: "flex", flex: 1 }}>
 | 
			
		||||
        {p.children}
 | 
			
		||||
      </FluentProvider>
 | 
			
		||||
    </ThemeContextK.Provider>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function useTheme(): ThemeContext {
 | 
			
		||||
  return React.useContext(ThemeContextK)!;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,25 +1,19 @@
 | 
			
		||||
import React from "react";
 | 
			
		||||
import ReactDOM from "react-dom/client";
 | 
			
		||||
import "./index.css";
 | 
			
		||||
import { App } from "./App";
 | 
			
		||||
import {
 | 
			
		||||
  FluentProvider,
 | 
			
		||||
  teamsHighContrastTheme,
 | 
			
		||||
} from "@fluentui/react-components";
 | 
			
		||||
import { AlertDialogProvider } from "./hooks/providers/AlertDialogProvider";
 | 
			
		||||
import { ConfirmDialogProvider } from "./hooks/providers/ConfirmDialogProvider";
 | 
			
		||||
import { ThemeProvider } from "./hooks/providers/ThemeProvider";
 | 
			
		||||
import "./index.css";
 | 
			
		||||
 | 
			
		||||
ReactDOM.createRoot(document.getElementById("root")!).render(
 | 
			
		||||
  <React.StrictMode>
 | 
			
		||||
    <FluentProvider
 | 
			
		||||
      theme={teamsHighContrastTheme}
 | 
			
		||||
      style={{ display: "flex", flex: 1 }}
 | 
			
		||||
    >
 | 
			
		||||
    <ThemeProvider>
 | 
			
		||||
      <AlertDialogProvider>
 | 
			
		||||
        <ConfirmDialogProvider>
 | 
			
		||||
          <App />
 | 
			
		||||
        </ConfirmDialogProvider>
 | 
			
		||||
      </AlertDialogProvider>
 | 
			
		||||
    </FluentProvider>
 | 
			
		||||
    </ThemeProvider>
 | 
			
		||||
  </React.StrictMode>
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										69
									
								
								remote_frontend/src/widgets/MainMenu.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								remote_frontend/src/widgets/MainMenu.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
			
		||||
import {
 | 
			
		||||
  Menu,
 | 
			
		||||
  MenuButton,
 | 
			
		||||
  MenuItem,
 | 
			
		||||
  MenuList,
 | 
			
		||||
  MenuPopover,
 | 
			
		||||
  MenuTrigger,
 | 
			
		||||
} from "@fluentui/react-components";
 | 
			
		||||
import { Checkmark12Regular } from "@fluentui/react-icons";
 | 
			
		||||
import { AuthApi } from "./../api/AuthApi";
 | 
			
		||||
import { useAlert } from "./../hooks/providers/AlertDialogProvider";
 | 
			
		||||
import { useConfirm } from "./../hooks/providers/ConfirmDialogProvider";
 | 
			
		||||
import { Theme, useTheme } from "./../hooks/providers/ThemeProvider";
 | 
			
		||||
 | 
			
		||||
export function MainMenu(): React.ReactElement {
 | 
			
		||||
  const alert = useAlert();
 | 
			
		||||
  const confirm = useConfirm();
 | 
			
		||||
 | 
			
		||||
  const signOut = async () => {
 | 
			
		||||
    try {
 | 
			
		||||
      if (!(await confirm("Do you really want to sign out?"))) return;
 | 
			
		||||
      await AuthApi.SignOut();
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      console.error(e);
 | 
			
		||||
      alert("Failed to perform sign out!");
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
  return (
 | 
			
		||||
    <Menu>
 | 
			
		||||
      <MenuTrigger disableButtonEnhancement>
 | 
			
		||||
        <MenuButton>Settings</MenuButton>
 | 
			
		||||
      </MenuTrigger>
 | 
			
		||||
 | 
			
		||||
      <MenuPopover>
 | 
			
		||||
        <MenuList>
 | 
			
		||||
          <Menu>
 | 
			
		||||
            <MenuTrigger disableButtonEnhancement>
 | 
			
		||||
              <MenuItem>Theme</MenuItem>
 | 
			
		||||
            </MenuTrigger>
 | 
			
		||||
 | 
			
		||||
            <MenuPopover>
 | 
			
		||||
              <MenuList>
 | 
			
		||||
                <ThemeMenuItem label="Teams Light" value="teamslight" />
 | 
			
		||||
                <ThemeMenuItem label="Teams Dark" value="teamsdark" />
 | 
			
		||||
                <ThemeMenuItem label="Web Light" value="weblight" />
 | 
			
		||||
                <ThemeMenuItem label="Web Dark" value="webdark" />
 | 
			
		||||
                <ThemeMenuItem label="High contrast" value="highcontrast" />
 | 
			
		||||
              </MenuList>
 | 
			
		||||
            </MenuPopover>
 | 
			
		||||
          </Menu>
 | 
			
		||||
          <MenuItem onClick={signOut}>Sign out</MenuItem>
 | 
			
		||||
        </MenuList>
 | 
			
		||||
      </MenuPopover>
 | 
			
		||||
    </Menu>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function ThemeMenuItem(p: { label: string; value: Theme }): React.ReactElement {
 | 
			
		||||
  const theme = useTheme();
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <MenuItem
 | 
			
		||||
      icon={theme.theme === p.value ? <Checkmark12Regular /> : undefined}
 | 
			
		||||
      onClick={() => theme.set(p.value)}
 | 
			
		||||
    >
 | 
			
		||||
      {p.label}
 | 
			
		||||
    </MenuItem>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user