Skip to content

Commit

Permalink
started working with redux
Browse files Browse the repository at this point in the history
  • Loading branch information
foxriver76 committed Nov 20, 2023
1 parent a646643 commit 8dea2f6
Show file tree
Hide file tree
Showing 15 changed files with 247 additions and 97 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"@alcalzone/release-script-plugin-iobroker": "^3.6.0",
"@alcalzone/release-script-plugin-license": "^3.5.9",
"@iobroker/vis-2-widgets-testing": "^0.3.0",
"@tsconfig/node16": "^16.1.1",
"chai": "^4.3.10",
"gulp": "^4.0.2",
"iobroker.web": "*",
Expand Down
1 change: 1 addition & 0 deletions src/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module.exports = {
'airbnb',
// 'react-app',
'plugin:eqeqeq-fix/recommended',
'plugin:@typescript-eslint/recommended',
],
parserOptions: {
ecmaFeatures: {
Expand Down
4 changes: 3 additions & 1 deletion src/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"@mui/material": "5.14.14",
"@mui/styles": "5.14.14",
"@mui/x-date-pickers": "^6.18.1",
"@reduxjs/toolkit": "^1.9.7",
"@sentry/browser": "^7.80.1",
"@sentry/integrations": "^7.80.1",
"ace-builds": "^1.31.2",
Expand Down Expand Up @@ -47,6 +48,7 @@
"react-dropzone": "^14.2.3",
"react-icons": "^4.12.0",
"react-scripts": "^5.0.1",
"redux": "^4.2.1",
"sass": "^1.69.5",
"uuid": "^9.0.1"
},
Expand All @@ -68,4 +70,4 @@
"not ie <= 11",
"not op_mini all"
]
}
}
108 changes: 54 additions & 54 deletions src/src/App.jsx

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/src/Attributes/Widget/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,7 @@ class Widget extends Component {

static buildDataImage(project, selectedView, selectedWidgets) {
const result = {};
(selectedWidgets || []).sort().forEach(wid => result[wid] = project[selectedView].widgets[wid]);
([...selectedWidgets] || []).sort().forEach(wid => result[wid] = project[selectedView].widgets[wid]);
return JSON.stringify(result);
}

Expand Down
12 changes: 7 additions & 5 deletions src/src/Marketplace/MarketplaceDialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
import { Close } from '@mui/icons-material';

import { I18n } from '@iobroker/adapter-react-v5';
import { store } from '../Store';

const MarketplaceDialog = props => {
const VisMarketplace = window.VisMarketplace?.default;
Expand All @@ -16,15 +17,17 @@ const MarketplaceDialog = props => {
if (props.installWidget) {
installWidget = async marketplace => {
const widgets = [];
Object.keys(props.project).forEach(view => {
const project = store.getState().visProject;

Object.keys(project).forEach(view => {
if (view !== '___settings') {
const viewWidgets = {
name: view,
widgets: [],
};
Object.keys(props.project[view].widgets).forEach(widget => {
if (props.project[view].widgets[widget].marketplace?.widget_id === marketplace.widget_id &&
props.project[view].widgets[widget].marketplace?.version !== marketplace.version) {
Object.keys(project[view].widgets).forEach(widget => {
if (project[view].widgets[widget].marketplace?.widget_id === marketplace.widget_id &&
project[view].widgets[widget].marketplace?.version !== marketplace.version) {
viewWidgets.widgets.push(widget);
}
});
Expand Down Expand Up @@ -81,7 +84,6 @@ MarketplaceDialog.propTypes = {
installedWidgets: PropTypes.array,
updateWidgets: PropTypes.func,
installWidget: PropTypes.func,
project: PropTypes.object,
themeName: PropTypes.string,
};

Expand Down
27 changes: 15 additions & 12 deletions src/src/Runtime.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ import {
} from './Vis/visUtils';
import VisWidgetsCatalog from './Vis/visWidgetsCatalog';

import { store, updateProject } from './Store';

const generateClassName = createGenerateClassName({
productionPrefix: 'vis-r',
});
Expand Down Expand Up @@ -185,11 +187,11 @@ class Runtime extends GenericApp {
.then(file => {
try {
const ts = JSON.parse(file.file || file).___settings.ts;
if (ts === this.state.visProject.___settings.ts) {
if (ts === store.getState().visProject.___settings.ts) {
return;
}
const tsInt = parseInt(ts.split('.'), 10);
if (tsInt < parseInt(this.state.visProject.___settings.ts.split('.'), 10)) {
if (tsInt < parseInt(store.getState().visProject.___settings.ts.split('.'), 10)) {
// ignore older files
return;
}
Expand Down Expand Up @@ -327,7 +329,7 @@ class Runtime extends GenericApp {
}

syncMultipleWidgets(project) {
project = project || this.state.visProject;
project = project || store.getState().visProject;
Object.keys(project).forEach(view => {
if (view === '___settings') {
return;
Expand Down Expand Up @@ -548,10 +550,11 @@ class Runtime extends GenericApp {
visUserCss: null,
history: [project],
historyCursor: 0,
visProject: project,
projectName,
});

store.dispatch(updateProject(project));

await this.changeView(selectedView);

// only in edit mode and only after VisMarketplace was loaded
Expand All @@ -565,7 +568,7 @@ class Runtime extends GenericApp {
this.resolutionTimer && clearTimeout(this.resolutionTimer);
this.resolutionTimer = setTimeout(async () => {
this.resolutionTimer = null;
const view = Runtime.findViewWithNearestResolution(this.state.visProject);
const view = Runtime.findViewWithNearestResolution(store.getState().visProject);
if (view && view !== this.state.selectedView) {
await this.changeView(view);
}
Expand Down Expand Up @@ -625,7 +628,7 @@ class Runtime extends GenericApp {
async onConnectionReady() {
// preload all widgets first
if (this.state.widgetsLoaded === Runtime.WIDGETS_LOADING_STEP_HTML_LOADED) {
await VisWidgetsCatalog.collectRxInformation(this.socket, this.state.visProject, this.changeProject);
await VisWidgetsCatalog.collectRxInformation(this.socket, store.getState().visProject, this.changeProject);
await this.setStateAsync({ widgetsLoaded: Runtime.WIDGETS_LOADING_STEP_ALL_LOADED });
}

Expand Down Expand Up @@ -708,7 +711,7 @@ class Runtime extends GenericApp {

// Check that all selectedWidgets exist
for (let i = selectedWidgets.length - 1; i >= 0; i--) {
if (!this.state.visProject[selectedView] || !this.state.visProject[selectedView].widgets || !this.state.visProject[selectedView].widgets[selectedWidgets[i]]) {
if (!store.getState().visProject[selectedView] || !store.getState().visProject[selectedView].widgets || !store.getState().visProject[selectedView].widgets[selectedWidgets[i]]) {
selectedWidgets = selectedWidgets.splice(i, 1);
}
}
Expand All @@ -725,8 +728,8 @@ class Runtime extends GenericApp {
newState.alignValues = [];
}

if (!this.state.runtime && !this.state.visProject.___settings.openedViews.includes(selectedView)) {
const project = JSON.parse(JSON.stringify(this.state.visProject));
if (!this.state.runtime && !store.getState().visProject.___settings.openedViews.includes(selectedView)) {
const project = JSON.parse(JSON.stringify(store.getState().visProject));
project.___settings.openedViews.push(selectedView);
await this.changeProject(project, true);
}
Expand Down Expand Up @@ -795,7 +798,7 @@ class Runtime extends GenericApp {
async onWidgetsLoaded() {
let widgetsLoaded = Runtime.WIDGETS_LOADING_STEP_HTML_LOADED;
if (this.socket.isConnected()) {
await VisWidgetsCatalog.collectRxInformation(this.socket, this.state.visProject, this.changeProject);
await VisWidgetsCatalog.collectRxInformation(this.socket, store.getState().visProject, this.changeProject);
widgetsLoaded = Runtime.WIDGETS_LOADING_STEP_ALL_LOADED;
}
this.setState({ widgetsLoaded });
Expand Down Expand Up @@ -975,7 +978,7 @@ class Runtime extends GenericApp {
visCommonCss={this.state.visCommonCss}
visUserCss={this.state.visUserCss}
lang={this.socket.systemLang}
views={this.state.visProject}
views={store.getState().visProject}
adapterName={this.adapterName}
instance={this.instance}
selectedWidgets={this.state.selectedWidgets}
Expand Down Expand Up @@ -1019,7 +1022,7 @@ class Runtime extends GenericApp {
<StyledEngineProvider injectFirst>
<ThemeProvider theme={this.state.theme}>
{
!this.state.loaded || !this.state.visProject ?
!this.state.loaded || !store.getState().visProject.___settings ?
<Loader theme={this.state.themeType} /> :
this.getVisEngine()
}
Expand Down
62 changes: 62 additions & 0 deletions src/src/Store.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { createReducer, configureStore, createAction } from '@reduxjs/toolkit';

interface ProjectSettings {
darkReloadScreen: boolean;
destroyViewsAfter: number;
folders: {id: string, name: string, parentId: string}[];
openedViews: string[];
reconnectInterval: number;
reloadOnEdit: boolean;
reloadOnSleep: number;
statesDebounceTime: number;
}

interface Widget {
data: Record<string, unknown>;
style: Record<string, unknown>;
tpl: string;
widgetSet: string;
}

interface View {
activeWidgets: string[];
filterList: string[];
rerender: boolean;
settings: Record<string, unknown>;
widgets: Record<string, Widget>;
}

interface Project {
// @ts-expect-error this type has bad code-style, we should refactor the views in a views: Record<string, View> attribute
___settings: ProjectSettings;
[view: string]: View;
}
export const updateProject = createAction<Project>('project/update');
export const updateView = createAction<{viewId: string, data: View}>('view/update');
export const updateWidget = createAction<{viewId: string, widgetId: string, data: Widget}>('widget/update');

const reducer = createReducer(
{
visProject: {} as Project,
},
builder => {
builder
.addCase(updateProject, (state, action) => {
state.visProject = action.payload as Project;
})
.addCase(updateView, (state, action) => {
const { viewId, data } = action.payload;
state.visProject[viewId] = data;
})
.addCase(updateWidget, (state, action) => {
const { viewId, widgetId, data } = action.payload;
state.visProject[viewId].widgets[widgetId] = data;
});
},
);

export const store = configureStore({
reducer,
});

store.dispatch(updateProject);
7 changes: 5 additions & 2 deletions src/src/Toolbar/ToolbarItems.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import { I18n } from '@iobroker/adapter-react-v5';

import MultiSelect from './MultiSelect';

import { store } from '../Store';

const styles = theme => ({
toolbarBlock: {
display: 'flex',
Expand Down Expand Up @@ -52,7 +54,8 @@ const styles = theme => ({
});

const getItem = (item, key, props, full) => {
const view = props.project[props.selectedView];
const { visProject } = store.getState();
const view = visProject[props.selectedView];

if (!item || item.hide) {
!item && console.warn(`Strange item: ${key}`);
Expand All @@ -68,7 +71,7 @@ const getItem = (item, key, props, full) => {
if (!item.field) {
return;
}
const project = JSON.parse(JSON.stringify(props.project));
const project = JSON.parse(JSON.stringify(visProject));
project[props.selectedView].settings[item.field] = changeValue;

props.changeProject(project);
Expand Down
3 changes: 2 additions & 1 deletion src/src/Toolbar/Views.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import ViewsManager from './ViewsManager';
import ToolbarItems from './ToolbarItems';

import ViewDialog from './ViewsManager/ViewDialog';
import { store } from '../Store';

const styles = () => ({
label: {
Expand Down Expand Up @@ -101,7 +102,7 @@ const Views = props => {
setDialogName={setDialogName}
setDialogParentId={setDialogParentId}
classes={{}}
{...props}
project={store.getState().visProject}
/>
</>;
};
Expand Down
20 changes: 11 additions & 9 deletions src/src/Toolbar/ViewsManager/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import ExportDialog from './ExportDialog';
import ImportDialog from './ImportDialog';
import FolderDialog from './FolderDialog';
import { DndPreview, isTouchDevice } from '../../Utils';
import { store } from '../../Store';

const styles = theme => ({
viewManageButtonActions: theme.classes.viewManageButtonActions,
Expand Down Expand Up @@ -74,20 +75,22 @@ const ViewsManager = props => {
}
}, []);

const { visProject } = store.getState();

const moveFolder = (id, parentId) => {
const project = JSON.parse(JSON.stringify(props.project));
const project = JSON.parse(JSON.stringify(visProject));
project.___settings.folders.find(folder => folder.id === id).parentId = parentId;
props.changeProject(project);
};

const moveView = (name, parentId) => {
const project = JSON.parse(JSON.stringify(props.project));
const project = JSON.parse(JSON.stringify(visProject));
project[name].parentId = parentId;
props.changeProject(project);
};

const importViewAction = (view, data) => {
const project = JSON.parse(JSON.stringify(props.project));
const project = JSON.parse(JSON.stringify(visProject));
const viewObject = JSON.parse(data);
if (!viewObject || !viewObject.settings || !viewObject.widgets || !viewObject.activeWidgets) {
return;
Expand All @@ -97,9 +100,9 @@ const ViewsManager = props => {
props.changeProject(project);
};

const renderViews = parentId => Object.keys(props.project)
const renderViews = parentId => Object.keys(visProject)
.filter(name => !name.startsWith('___'))
.filter(name => (parentId ? props.project[name].parentId === parentId : !props.project[name].parentId))
.filter(name => (parentId ? visProject[name].parentId === parentId : !visProject[name].parentId))
.sort((name1, name2) => (name1.toLowerCase() < name2.toLowerCase() ? 0 : 1))
.map((name, key) => <div key={key} className={props.classes.viewContainer}>
<View
Expand All @@ -115,7 +118,7 @@ const ViewsManager = props => {
</div>);

const renderFolders = parentId => {
const folders = props.project.___settings.folders
const folders = visProject.___settings.folders
.filter(folder => (parentId ? folder.parentId === parentId : !folder.parentId));

return folders.map((folder, key) => <div key={key}>
Expand Down Expand Up @@ -212,14 +215,14 @@ const ViewsManager = props => {
onClose={() => setImportDialog(false)}
view={importDialog || ''}
importViewAction={importViewAction}
project={props.project}
project={visProject}
themeName={props.themeName}
/> : null}
{exportDialog !== false ? <ExportDialog
open
onClose={() => setExportDialog(false)}
view={exportDialog || ''}
project={props.project}
project={visProject}
themeName={props.themeName}
/> : null}
</IODialog>;
Expand All @@ -231,7 +234,6 @@ ViewsManager.propTypes = {
name: PropTypes.string,
onClose: PropTypes.func,
open: PropTypes.bool,
project: PropTypes.object,
showDialog: PropTypes.func,
themeName: PropTypes.string,
toggleView: PropTypes.func,
Expand Down
Loading

0 comments on commit 8dea2f6

Please sign in to comment.