From b5d16a8068ed55b784f8e10f19a99cc7f11b8bc7 Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Thu, 23 Dec 2021 15:48:58 +0100 Subject: feat: Add react and mobx integration --- packages/renderer/package.json | 1 + packages/renderer/src/components/StoreProvider.tsx | 24 +++++++++ packages/renderer/src/components/ThemeProvider.tsx | 26 +++++++++ packages/renderer/src/index.tsx | 53 ++++++++----------- packages/renderer/src/stores/RootStore.ts | 61 ++++++++++++++++++++++ 5 files changed, 135 insertions(+), 30 deletions(-) create mode 100644 packages/renderer/src/components/StoreProvider.tsx create mode 100644 packages/renderer/src/components/ThemeProvider.tsx create mode 100644 packages/renderer/src/stores/RootStore.ts (limited to 'packages/renderer') diff --git a/packages/renderer/package.json b/packages/renderer/package.json index ee627b4..ceec3a5 100644 --- a/packages/renderer/package.json +++ b/packages/renderer/package.json @@ -15,6 +15,7 @@ "@mui/material": "^5.2.5", "@sophie/shared": "workspace:*", "mobx": "^6.3.10", + "mobx-react-lite": "^3.2.2", "mobx-state-tree": "^5.1.0", "react": "^17.0.2", "react-dom": "^17.0.2" diff --git a/packages/renderer/src/components/StoreProvider.tsx b/packages/renderer/src/components/StoreProvider.tsx new file mode 100644 index 0000000..51c39f7 --- /dev/null +++ b/packages/renderer/src/components/StoreProvider.tsx @@ -0,0 +1,24 @@ +import React, { createContext, useContext } from 'react'; + +import type { RootStore } from '../stores/RootStore'; + +const StoreContext = createContext(null); + +export function useStore(): RootStore { + const store = useContext(StoreContext); + if (store === null) { + throw new Error('useStore can only be called inside of StoreProvider'); + } + return store; +} + +export function StoreProvider({ children, store }: { + children: JSX.Element | JSX.Element[], + store: RootStore, +}): JSX.Element { + return ( + + {children} + + ); +} diff --git a/packages/renderer/src/components/ThemeProvider.tsx b/packages/renderer/src/components/ThemeProvider.tsx new file mode 100644 index 0000000..8be4f6a --- /dev/null +++ b/packages/renderer/src/components/ThemeProvider.tsx @@ -0,0 +1,26 @@ +import { observer } from 'mobx-react-lite'; +import { + unstable_createMuiStrictModeTheme as createTheme, + ThemeProvider as MuiThemeProvider, +} from '@mui/material/styles'; +import React from 'react'; + +import { useStore } from './StoreProvider'; + +export const ThemeProvider = observer(({ children }: { + children: JSX.Element | JSX.Element[], +}): JSX.Element => { + const { shared: { shouldUseDarkColors } } = useStore(); + + const theme = createTheme({ + palette: { + mode: shouldUseDarkColors ? 'dark' : 'light', + }, + }); + + return ( + + {children} + + ); +}); diff --git a/packages/renderer/src/index.tsx b/packages/renderer/src/index.tsx index 37daaa6..34b21de 100644 --- a/packages/renderer/src/index.tsx +++ b/packages/renderer/src/index.tsx @@ -2,56 +2,49 @@ import '@fontsource/roboto/300.css'; import '@fontsource/roboto/400.css'; import '@fontsource/roboto/500.css'; import '@fontsource/roboto/700.css'; -import { applyPatch, applySnapshot } from 'mobx-state-tree'; +import { observer } from 'mobx-react-lite'; import Button from "@mui/material/Button"; import CssBaseline from "@mui/material/CssBaseline"; -import { - unstable_createMuiStrictModeTheme as createTheme, - ThemeProvider, -} from '@mui/material/styles'; import React from 'react'; import { render } from 'react-dom'; -import { sharedStore } from '@sophie/shared'; +import { StoreProvider, useStore } from './components/StoreProvider'; +import { ThemeProvider } from './components/ThemeProvider'; import { exposeToReduxDevtools } from './devTools'; +import { createAndConnectRootStore } from './stores/RootStore'; const isDevelopment = import.meta.env.MODE === 'development'; -const store = sharedStore.create(); +const store = createAndConnectRootStore(window.sophieRenderer); if (isDevelopment) { exposeToReduxDevtools(store); } -window.sophieRenderer.setSharedStoreListener({ - onSnapshot(snapshot) { - applySnapshot(store, snapshot); - }, - onPatch(patch) { - applyPatch(store, patch); - }, -}); +const Example = observer(() => { + const { shared: { clickCount } } = useStore(); -const theme = createTheme({ - palette: { - mode: 'dark', - }, + return ( + + ); }); -function App() { +function Root(): JSX.Element { return ( - - - - + + + + + + ); } -render(, document.querySelector('#app')); +render(, document.querySelector('#app')); diff --git a/packages/renderer/src/stores/RootStore.ts b/packages/renderer/src/stores/RootStore.ts new file mode 100644 index 0000000..86efac6 --- /dev/null +++ b/packages/renderer/src/stores/RootStore.ts @@ -0,0 +1,61 @@ +import { + applySnapshot, + applyPatch, + getEnv as getAnyEnv, + IAnyStateTreeNode, + Instance, + types +} from 'mobx-state-tree'; +import { sharedStore, SophieRenderer } from '@sophie/shared'; + +export interface RootEnv { + ipc: SophieRenderer; +} + +/** + * Gets a well-typed environment from `model`. + * + * Only useable inside state trees created by `createAndConnectRootStore`. + * + * @param model The state tree node. + */ +export function getEnv(model: IAnyStateTreeNode): RootEnv { + return getAnyEnv(model); +} + +export const rootStore = types.model('RootStore', { + shared: sharedStore, +}).actions((self) => ({ + buttonClick() { + getEnv(self).ipc.buttonClick(); + }, +})); + +export interface RootStore extends Instance {} + +/** + * Creates a new `RootStore` with a new environment and connects it to `ipc`. + * + * Changes to the `shared` store in the main process will be propagated to + * the newly created store via `ipc`. + * + * @param ipc The `sophieRenderer` context bridge. + */ +export function createAndConnectRootStore(ipc: SophieRenderer): RootStore { + const store = rootStore.create({ + shared: {}, + }, { + ipc, + }); + + ipc.setSharedStoreListener({ + onSnapshot(snapshot) { + applySnapshot(store.shared, snapshot); + }, + onPatch(patch) { + applyPatch(store.shared, patch); + }, + }); + + return store; +} -- cgit v1.2.3-70-g09d2