Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Update redux UI, add action update UI, add state,diff and actio… #37

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/example/src/app/redux/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,4 @@ const styles = StyleSheet.create({
alignItems: 'center',
justifyContent: 'center',
},
});
});
4 changes: 3 additions & 1 deletion apps/example/src/app/redux/store.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { configureStore } from '@reduxjs/toolkit';
import { configureStore, Middleware } from '@reduxjs/toolkit';
import counterReducer from './counterSlice';
import { reduxDevToolsMiddleware } from '@dev-plugins/redux/src/useReduxDevTools';

export const store = configureStore({
reducer: {
counter: counterReducer,
},
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(reduxDevToolsMiddleware),
});

export type RootState = ReturnType<typeof store.getState>;
Expand Down
17 changes: 15 additions & 2 deletions packages/redux/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,23 @@ npx expo install @dev-plugins/redux
import { useReduxDevTools } from '@dev-plugins/redux';
import { store } from './store';


export default function App() {
useReduxDevTools(store);

/* ... */
}
```
```

### Add middleware to store

```jsx
import { reduxDevToolsMiddleware } from '@dev-plugins/redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';

const store = createStore(
rootReducer,
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(reduxDevToolsMiddleware),
);

```
21 changes: 16 additions & 5 deletions packages/redux/src/useReduxDevTools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,29 @@ import { Store } from '@reduxjs/toolkit';
*
* @param store - The Redux store.
*/

let lastAction = null;
export function useReduxDevTools(store: Store) {
const client = useDevToolsPluginClient('redux');

useEffect(() => {
client?.sendMessage("storeUpdated", store.getState());
client?.sendMessage('storeUpdated', { store: store.getState(), lastAction });

const unsubscribeFn = store.subscribe(() => {
const state = store.getState();
client?.sendMessage("storeUpdated", state);
client?.sendMessage('storeUpdated', { store: store.getState(), lastAction });
});

return () => {
unsubscribeFn();
}
}, [client])
}
};
}, [client, lastAction]);
}

export const reduxDevToolsMiddleware = (store) => (next) => (action) => {
if (action !== lastAction) {
lastAction = { ...action, time: new Date().toISOString() };
}
const result = next(action);
return result;
};
117 changes: 106 additions & 11 deletions packages/redux/webui/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,130 @@
import { App } from 'antd';
import { App, List, Typography, Divider, Tabs, ThemeConfig, ConfigProvider } from 'antd'; // Importing components from Ant Design
import { useDevToolsPluginClient, type EventSubscription } from 'expo/devtools';
import { Fragment, useEffect, useState } from 'react';
import ReactJson from 'react-json-view';

const { Item } = List;
const { Text } = Typography;
const { TabPane } = Tabs;

export default function Main() {
const client = useDevToolsPluginClient('redux');
const [storeHistory, setStoreHistory] = useState<any[]>([])
const [storeHistory, setStoreHistory] = useState<any[]>([]);
const [selectedState, setSelectedState] = useState<any>();

useEffect(() => {
const subscriptions: EventSubscription[] = [];

subscriptions.push(
client?.addMessageListener('storeUpdated', (data) => {
setStoreHistory(prevHistory => [...prevHistory, data]);
if (!data.store) return;
setStoreHistory((prevHistory) => [
...prevHistory,
{ state: data.store, action: data.lastAction },
]);
})
);

return () => {
for (const subscription of subscriptions) {
subscription?.remove();
}
};
}, [client]);


const handleItemClick = (index: number) => {
setSelectedState(storeHistory[index]);
};

const calculateDiff = (stateBefore: typeof selectedState, stateAfter: typeof selectedState) => {
const diff: { [key: string]: any } = {};

if (!stateBefore) return stateAfter;

Object.keys(stateAfter).forEach((key) => {
if (stateBefore[key] !== stateAfter[key]) {
diff[key] = stateAfter[key];
}
});

return diff;
};

return (
<App style={{ width: '100%', height: '100%', padding: '0.75em', overflowY: 'scroll' }}>
{storeHistory.map((history, index) => {
return <Fragment key={index}><ReactJson src={history} /></Fragment>
})}

<App
style={{
display: 'flex',
width: '100%',
height: '100%',
padding: '0.75em',
overflowY: 'scroll',
}}>
<div style={{ flex: 1 }}>
<Divider orientation="left">Action History</Divider>
<List
bordered
dataSource={storeHistory}
renderItem={(item, index) => (
<Item
onClick={() => handleItemClick(index)}
style={{
cursor: 'pointer',
backgroundColor: selectedState === item ? '#f0f0f0' : 'white',
}}>
<Text strong>{index + 1}</Text> {/* Displaying index for clarity */}
&nbsp; &nbsp;
<Text strong>{!item?.action && '@@INIT'}</Text> {/* Displaying initial state */}
<Text strong>{item.action?.type?.toUpperCase()}</Text> {/* Displaying action type */}
<br />
<Text type="secondary">
{item.action?.time && new Date(item.action.time).toLocaleTimeString()}
</Text>
{/* Displaying timestamp */}
</Item>
)}
/>
</div>
<div style={{ flex: 2, marginLeft: '1em' }}>
<Tabs defaultActiveKey="1" tabPosition="left">
<TabPane tab="State" key="1">
<ReactJson src={selectedState?.state} />
</TabPane>
<TabPane tab="Action Details" key="2">
{selectedState?.action ? (
<>
<ReactJson
src={Object.keys(selectedState.state).reduce(
(acc: { [key: string]: any }, key) => {
if (key !== 'time') {
acc[key] = selectedState.state[key];
}
return acc;
},
{}
)}
/>
<Text type="secondary">
Action dispatched at{' '}
{selectedState?.action?.time &&
new Date(selectedState?.action.time).toLocaleTimeString()}
</Text>
</>
) : (
<Text>@@INIT</Text>
)}
</TabPane>
<TabPane tab="Diff" key="3">
{selectedState && (
<ReactJson
src={calculateDiff(
storeHistory[storeHistory.indexOf(selectedState) - 1]?.state,
selectedState.state
)}
/>
)}
</TabPane>
</Tabs>
</div>
</App>
);
}