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

Add redux toolkit #652

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Binary file modified .yarn/install-state.gz
Binary file not shown.
60 changes: 0 additions & 60 deletions packages/chord-chart-studio/build/assets/index-B0ChhUNb.js

This file was deleted.

This file was deleted.

60 changes: 60 additions & 0 deletions packages/chord-chart-studio/build/assets/index-BwbBfjx-.js

Large diffs are not rendered by default.

Large diffs are not rendered by default.

117 changes: 0 additions & 117 deletions packages/chord-chart-studio/build/assets/vendor-B9p68kIM.js

This file was deleted.

142 changes: 142 additions & 0 deletions packages/chord-chart-studio/build/assets/vendor-BWYuIjsn.js

Large diffs are not rendered by default.

14 changes: 6 additions & 8 deletions packages/chord-chart-studio/build/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,10 @@
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css?family=Open+Sans:400,700|Roboto+Mono:400,700"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/icon?family=Material+Icons"
href="https://fonts.googleapis.com/css2?family=Cabin:ital,wght@0,400..700;1,400..700&display=swap"
rel="stylesheet"
/>
<link rel="icon" href="/app/favicon.ico" sizes="32x32" />
Expand All @@ -39,10 +37,10 @@

gtag('config', 'G-EGKBT2J600');
</script>
<script type="module" crossorigin src="/app/assets/index-B0ChhUNb.js"></script>
<link rel="modulepreload" crossorigin href="/app/assets/vendor-B9p68kIM.js">
<script type="module" crossorigin src="/app/assets/index-BwbBfjx-.js"></script>
<link rel="modulepreload" crossorigin href="/app/assets/vendor-BWYuIjsn.js">
<link rel="stylesheet" crossorigin href="/app/assets/vendor-BHpWJQ6D.css">
<link rel="stylesheet" crossorigin href="/app/assets/index-BmEYkRDe.css">
<link rel="stylesheet" crossorigin href="/app/assets/index-s8b48HYU.css">
<script id="vite-plugin-pwa:register-sw" src="/app/registerSW.js"></script></head>
<body>
<div id="app" class="theme-dark"></div>
Expand Down
2 changes: 1 addition & 1 deletion packages/chord-chart-studio/build/sw.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions packages/chord-chart-studio/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"storybook": "8.0.0-rc.0"
},
"dependencies": {
"@reduxjs/toolkit": "^2.2.1",
"chord-mark": "^0.16.2",
"chord-mark-converters": "^0.16.2",
"chord-mark-themes": "^0.16.2",
Expand Down Expand Up @@ -61,9 +62,6 @@
"react-aria-components": "^1.1.1",
"react-dom": "^18.2.0",
"react-redux": "^8.1.3",
"redux": "^4.2.1",
"redux-devtools-extension": "^2.13.9",
"redux-thunk": "^2.4.2",
"reselect": "^4.1.8",
"scroll-sync-react": "^1.2.0",
"universal-router": "^9.2.0",
Expand Down
7 changes: 7 additions & 0 deletions packages/chord-chart-studio/src/core/createAppSlice.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { buildCreateSlice, asyncThunkCreator } from '@reduxjs/toolkit';

const createAppSlice = buildCreateSlice({
creators: { asyncThunk: asyncThunkCreator },
});

export default createAppSlice;
151 changes: 151 additions & 0 deletions packages/chord-chart-studio/src/db/files/filesSlice.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { v4 as uuidv4 } from 'uuid';
import _pick from 'lodash/pick';

import { createSlice } from '@reduxjs/toolkit';
import clock from '../../core/clock';

import editorModeOptions from '../options/editorModeOptions';
import { getCategoryOptions, getLatestModeOptions } from './selectors';

import { optionValueChanged } from '../options/optionsSlice';
import { editorModeChanged } from '../../ui/layout/app/uiSlice';

const initialState = {
allFiles: {},
};

function fileCreatedAction(title, content = '') {
if (!title) {
throw new TypeError('Cannot create a file without title');
}
return {
payload: {
id: uuidv4(),
title,
content,
},
};
}

function fileCreatedReducer(state, action) {
const { id, title, content } = action.payload;
state.allFiles[id] = { id, title, content };
}

function fileUpdatedAction(id, { title, content } = {}) {
if (!id) {
throw new TypeError('Cannot update a file without an id');
}
return {
payload: {
id,
title,
content,
},
};
}

function fileUpdatedReducer(state, action) {
const { id, title, content } = action.payload;

if (state.allFiles[id] && !(!title && typeof content === 'undefined')) {
if (title) {
state.allFiles[id].title = title;
}
if (typeof content !== 'undefined') {
state.allFiles[id].content = content;
}
}
}

function fileDeletedReducer(state, action) {
const id = action.payload;

if (id && state.allFiles[id]) {
delete state.allFiles[id];
}
}

/**
* Whenever the user set an option, we save it in the song entity, either:
* - for the current editing mode if it is a formatting option
* - in the preferences otherwise
*/
function optionValueChangedReducer(state, action) {
const { context, key, value, fileId, editorMode } = action.payload;

if (
['songFormatting', 'songPreferences'].includes(context) &&
state.allFiles[fileId]
) {
const optionCategory =
context === 'songPreferences' ? 'preferences' : editorMode;

if (!state.allFiles[fileId].options) {
state.allFiles[fileId].options = {};
}
if (!state.allFiles[fileId].options[optionCategory]) {
state.allFiles[fileId].options[optionCategory] = {};
}
state.allFiles[fileId].options[optionCategory].updatedAt = clock();
state.allFiles[fileId].options[optionCategory][key] = value;
}
return state;
}

/**
* When a user switch mode and the target mode does not have any saved settings yet,
* we apply the latest saved settings (all modes merged) for a better user flow
*/
function editorModeChangedReducer(state, action) {
const { fileId, mode: nextMode } = action.payload;

const hasOptionsForNextMode = !!getCategoryOptions(state, fileId, nextMode);

if (!hasOptionsForNextMode) {
const previousModeOptions = _pick(
getLatestModeOptions(state, fileId) || {},
editorModeOptions[nextMode]
);

if (Object.keys(previousModeOptions).length) {
previousModeOptions.updatedAt = clock();
if (!state.allFiles[fileId].options) {
state.allFiles[fileId].options = {};
}

state.allFiles[fileId].options[nextMode] = previousModeOptions;
}
}
return state;
}

const uiSlice = createSlice({
name: 'files',
initialState,
reducers: (create) => ({
fileCreated: create.preparedReducer(
fileCreatedAction,
fileCreatedReducer
),
fileImported: create.preparedReducer(
fileCreatedAction,
fileCreatedReducer
),
fileUpdated: create.preparedReducer(
fileUpdatedAction,
fileUpdatedReducer
),
fileDeleted: create.reducer(fileDeletedReducer),
}),
extraReducers: (builder) => {
builder
.addCase(optionValueChanged.fulfilled, optionValueChangedReducer)
.addCase(editorModeChanged.fulfilled, editorModeChangedReducer);
},
});

export const { fileCreated, fileImported, fileUpdated, fileDeleted } =
uiSlice.actions;

export default uiSlice.reducer;
7 changes: 3 additions & 4 deletions packages/chord-chart-studio/src/db/files/reducers.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import clock from '../../core/clock';
import * as actionTypes from './actionsTypes';

import { DB_OPTION_SET_OPTION_VALUE } from '../options/actionsTypes';
import { UI_LAYOUT_APP_SET_EDITOR_MODE } from '../../ui/layout/app/_state/actionsTypes';
import { getEditorMode } from '../../ui/layout/app/_state/selectors';
import { getEditorMode } from '../../ui/layout/app/uiSlice';
import { getSelectedId } from '../../fileManager/_state/selectors';
import { getLatestModeOptions, getCategoryOptions } from './selectors';
import editorModeOptions from '../options/editorModeOptions';
Expand Down Expand Up @@ -116,7 +115,7 @@ function addOption(fileState, category, key, value) {
*/
function setEditorMode(state, action, fullState) {
const fileId = getSelectedId(fullState);
const nextMode = action.payload.mode;
const nextMode = action.payload;

const hasOptionsForNextMode = !!getCategoryOptions(
fullState,
Expand Down Expand Up @@ -162,7 +161,7 @@ export default (state = initialState, action = {}, fullState = {}) => {
return deleteFile(state, action);
case DB_OPTION_SET_OPTION_VALUE:
return updateFileOption(state, action, fullState);
case UI_LAYOUT_APP_SET_EDITOR_MODE:
case 'ui/editorModeChanged':
return setEditorMode(state, action, fullState);
}
return state;
Expand Down
22 changes: 20 additions & 2 deletions packages/chord-chart-studio/src/db/files/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import _map from 'lodash/map';
import _cloneDeep from 'lodash/cloneDeep';
import _isEqual from 'lodash/isEqual';
import _sortBy from 'lodash/sortBy';
import _pick from 'lodash/pick';

import allEditorModeOptions from '../options/editorModeOptions'; //fixme: duh!
import { createSelectorCreator, defaultMemoize } from 'reselect';

// create a "selector creator" that uses lodash.isEqual instead of ===
Expand All @@ -19,8 +21,23 @@ export const getOne = (state, id) => {
return state.db.files.allFiles[id];
};

// Get defined options for a mode, either because they exist for the given mode,
// or building them from options stored in the other modes
export function getModeOptions(state, fileId, mode) {
let editorModeOptions = getCategoryOptions(state.db.files, fileId, mode);

if (!editorModeOptions) {
editorModeOptions = getLatestModeOptions(state.db.files, fileId) || {};
}
delete editorModeOptions.updatedAt;

// take only relevant options for the mode
return _pick(editorModeOptions, allEditorModeOptions[mode]);
}

// todo: not a selector anymore!
export const getCategoryOptions = (state, id, category) => {
const file = state.db.files.allFiles[id];
const file = state.allFiles[id];

if (!file) return;

Expand All @@ -33,8 +50,9 @@ export const getCategoryOptions = (state, id, category) => {
}
};

// todo: not a selector anymore!
export const getLatestModeOptions = (state, id) => {
const file = state.db.files.allFiles[id];
const file = state.allFiles[id];

if (!file) return;

Expand Down
Loading
Loading