diff --git a/.eslintignore b/.eslintignore index 0a3500e28..91cc4695e 100644 --- a/.eslintignore +++ b/.eslintignore @@ -578,6 +578,9 @@ packages/app-desktop/gui/ResizableLayout/utils/persist.test.js.map packages/app-desktop/gui/ResizableLayout/utils/removeItem.d.ts packages/app-desktop/gui/ResizableLayout/utils/removeItem.js packages/app-desktop/gui/ResizableLayout/utils/removeItem.js.map +packages/app-desktop/gui/ResizableLayout/utils/removeKeylessItems.d.ts +packages/app-desktop/gui/ResizableLayout/utils/removeKeylessItems.js +packages/app-desktop/gui/ResizableLayout/utils/removeKeylessItems.js.map packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.d.ts packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.js packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.js.map diff --git a/.gitignore b/.gitignore index bc446bd25..b5a6d14c2 100644 --- a/.gitignore +++ b/.gitignore @@ -564,6 +564,9 @@ packages/app-desktop/gui/ResizableLayout/utils/persist.test.js.map packages/app-desktop/gui/ResizableLayout/utils/removeItem.d.ts packages/app-desktop/gui/ResizableLayout/utils/removeItem.js packages/app-desktop/gui/ResizableLayout/utils/removeItem.js.map +packages/app-desktop/gui/ResizableLayout/utils/removeKeylessItems.d.ts +packages/app-desktop/gui/ResizableLayout/utils/removeKeylessItems.js +packages/app-desktop/gui/ResizableLayout/utils/removeKeylessItems.js.map packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.d.ts packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.js packages/app-desktop/gui/ResizableLayout/utils/setLayoutItemProps.js.map diff --git a/packages/app-desktop/gui/MainScreen/MainScreen.tsx b/packages/app-desktop/gui/MainScreen/MainScreen.tsx index d0050851e..c4c9154d8 100644 --- a/packages/app-desktop/gui/MainScreen/MainScreen.tsx +++ b/packages/app-desktop/gui/MainScreen/MainScreen.tsx @@ -34,6 +34,7 @@ import ShareFolderDialog from '../ShareFolderDialog/ShareFolderDialog'; import { ShareInvitation } from '@joplin/lib/services/share/reducer'; import ShareService from '@joplin/lib/services/share/ShareService'; import { reg } from '@joplin/lib/registry'; +import removeKeylessItems from '../ResizableLayout/utils/removeKeylessItems'; const { connect } = require('react-redux'); const { PromptDialog } = require('../PromptDialog.min.js'); @@ -234,6 +235,14 @@ class MainScreenComponent extends React.Component { try { output = loadLayout(Object.keys(userLayout).length ? userLayout : null, defaultLayout, rootLayoutSize); + // For unclear reasons, layout items sometimes end up witout a key. + // In that case, we can't do anything with them, so remove them + // here. It could be due to the deprecated plugin API, which allowed + // creating panel without a key, although in this case it should + // have been set automatically. + // https://github.com/laurent22/joplin/issues/4926 + output = removeKeylessItems(output); + if (!findItemByKey(output, 'sideBar') || !findItemByKey(output, 'noteList') || !findItemByKey(output, 'editor')) { throw new Error('"sideBar", "noteList" and "editor" must be present in the layout'); } diff --git a/packages/app-desktop/gui/ResizableLayout/utils/removeKeylessItems.ts b/packages/app-desktop/gui/ResizableLayout/utils/removeKeylessItems.ts new file mode 100644 index 000000000..0faf5971b --- /dev/null +++ b/packages/app-desktop/gui/ResizableLayout/utils/removeKeylessItems.ts @@ -0,0 +1,30 @@ +import produce from 'immer'; +import iterateItems from './iterateItems'; +import { LayoutItem } from './types'; +import validateLayout from './validateLayout'; + +interface ItemToRemove { + parent: LayoutItem; + index: number; +} + +export default function(layout: LayoutItem): LayoutItem { + const itemsToRemove: ItemToRemove[] = []; + + const output = produce(layout, (layoutDraft: LayoutItem) => { + iterateItems(layoutDraft, (itemIndex: number, item: LayoutItem, parent: LayoutItem) => { + if (!item.key) itemsToRemove.push({ parent, index: itemIndex }); + return true; + }); + + itemsToRemove.sort((a: ItemToRemove, b: ItemToRemove) => { + return a.index > b.index ? -1 : +1; + }); + + for (const item of itemsToRemove) { + item.parent.children.splice(item.index, 1); + } + }); + + return output !== layout ? validateLayout(output) : layout; +}