diff --git a/CHANGELOG.md b/CHANGELOG.md index 73fe3340..ccd84609 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# v1.2.7 + +- [FP-2787](https://movai.atlassian.net/browse/FP-2787): IDE - Topics - Can't open multiple Topics tabs + # v1.2.6 - [FP-2739](https://movai.atlassian.net/browse/FP-2739): Port properties of the node templates are turning to strings after editing them @@ -34,7 +38,7 @@ - [FP-2603](https://movai.atlassian.net/browse/FP-2603): Properties dropdown closing after edit - [FP-2633](https://movai.atlassian.net/browse/FP-2633): Configuration Key not found error -- [FP-2655](https://movai.atlassian.net/browse/FP-2655): SCENE\_EDITOR-Old\_Values\_after\_selecting\_another\_entity +- [FP-2655](https://movai.atlassian.net/browse/FP-2655): SCENE_EDITOR-Old_Values_after_selecting_another_entity - [FP-2657](https://movai.atlassian.net/browse/FP-2657): Users with only read permissions can change the scene - [FP-2664](https://movai.atlassian.net/browse/FP-2664): Update react in all libs/apps - [FP-2557](https://movai.atlassian.net/browse/FP-2557): Removing frontend callbacks from backend diff --git a/package.json b/package.json index d7a4e793..d4ac42e1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mov-ai/mov-fe-lib-ide", - "version": "1.2.6-1", + "version": "1.2.7-0", "main": "./dist/index.js", "publishConfig": { "registry": "https://npm.pkg.github.com/mov-ai" diff --git a/src/engine/ReactPlugin/ToolReactPlugin.js b/src/engine/ReactPlugin/ToolReactPlugin.js index 9f0aca7c..cdf7f894 100644 --- a/src/engine/ReactPlugin/ToolReactPlugin.js +++ b/src/engine/ReactPlugin/ToolReactPlugin.js @@ -32,13 +32,9 @@ export function withToolPlugin(ReactComponent, methods = []) { */ useEffect(() => { PluginManagerIDE.resetBookmarks(); - on( - PLUGINS.TABS.NAME, - PLUGINS.TABS.ON.ACTIVE_TAB_CHANGE, - async ({ id }) => { - if (profile.name === id) PluginManagerIDE.resetBookmarks(); - } - ); + on(PLUGINS.TABS.NAME, PLUGINS.TABS.ON.ACTIVE_TAB_CHANGE, async () => { + PluginManagerIDE.resetBookmarks(); + }); return () => { off(PLUGINS.TABS.NAME, PLUGINS.TABS.ON.ACTIVE_TAB_CHANGE); diff --git a/src/plugins/views/Tabs/hooks/useTabLayout.js b/src/plugins/views/Tabs/hooks/useTabLayout.js index c950044c..448f25e4 100644 --- a/src/plugins/views/Tabs/hooks/useTabLayout.js +++ b/src/plugins/views/Tabs/hooks/useTabLayout.js @@ -26,7 +26,7 @@ const useTabLayout = (props, dockRef) => { const activeTabId = useRef(null); const firstLoad = useRef(true); const preventReloadNewDoc = useRef(false); - const tabsById = useRef(new Map()); + const tabsByIdRef = useRef(new Map()); const [layout, setLayout] = useState({ ...DEFAULT_LAYOUT }); const { addTabToStack, removeTabFromStack, getNextTabFromStack } = useTabStack(workspaceManager); @@ -47,8 +47,9 @@ const useTabLayout = (props, dockRef) => { if (!tab.extension) { const toolName = tab.id; const tabData = getToolTabData(tab, tab.tabProps); - tabsById.current.set(tabData.id, tabData); - workspaceManager.setTabs(tabsById.current); + + tabsByIdRef.current.set(tabData.id, tabData); + workspaceManager.setTabs(tabsByIdRef.current); // This fixes an issue where on first load a non editor tab // was being added to the tabStack (HomeTab) and that meant @@ -210,10 +211,10 @@ const useTabLayout = (props, dockRef) => { const tabIndex = box.tabs.findIndex(_el => _el.id === prevTabId); box.tabs[tabIndex] = tabData; box.activeId = tabData.id; - tabsById.current.delete(prevTabId); - tabsById.current.set(tabData.id, tabData); + tabsByIdRef.current.delete(prevTabId); + tabsByIdRef.current.set(tabData.id, tabData); addTabToStack(tabData, location); - workspaceManager.setTabs(tabsById.current); + workspaceManager.setTabs(tabsByIdRef.current); workspaceManager.setLayout(newLayout); } return { newLayout, box }; @@ -339,7 +340,7 @@ const useTabLayout = (props, dockRef) => { */ const _onLayoutRemoveTab = useCallback( (newLayout, tabId, forceClose) => { - const { name, scope, isNew, isDirty } = tabsById.current.get(tabId); + const { name, scope, isNew, isDirty } = tabsByIdRef.current.get(tabId); if (isDirty && !forceClose) { const document = { id: tabId, name, scope, isNew }; @@ -355,8 +356,8 @@ const useTabLayout = (props, dockRef) => { } // Remove tab and apply new layout - tabsById.current.delete(tabId); - workspaceManager.setTabs(tabsById.current); + tabsByIdRef.current.delete(tabId); + workspaceManager.setTabs(tabsByIdRef.current); const dock = getDockFromTabId(tabId); removeTabFromStack(tabId, dock); applyLayout(newLayout); @@ -423,13 +424,13 @@ const useTabLayout = (props, dockRef) => { data => { const { instance: model, value: isDirty } = data; const tabId = model.getUrl(); - const currentTabData = tabsById.current.get(tabId); + const currentTabData = tabsByIdRef.current.get(tabId); const currentDirtyState = Boolean(currentTabData?.isDirty); // Doesn't trigger update if dirty state didn't change if (!currentTabData || currentDirtyState === isDirty) return; // Set new dirty state const newTabData = { ...currentTabData, isDirty: isDirty }; - tabsById.current.set(tabId, newTabData); + tabsByIdRef.current.set(tabId, newTabData); // Trigger tab update if (!dockRef.current) return; const currentTab = findTab(tabId); @@ -538,10 +539,11 @@ const useTabLayout = (props, dockRef) => { emit(PLUGINS.TABS.ON.ACTIVE_TAB_CHANGE, { id: tabData.id }); addTabToStack(tabData, tabPosition); - tabsById.current.set(tabData.id, tabData); - workspaceManager.setTabs(tabsById.current); + tabsByIdRef.current.set(tabData.id, tabData); + workspaceManager.setTabs(tabsByIdRef.current); const existingTab = findTab(tabData.id); + if (existingTab) { focusExistingTab(tabData.id); return; @@ -631,8 +633,9 @@ const useTabLayout = (props, dockRef) => { */ const loadTab = useCallback( data => { - const tabFromMemory = tabsById.current.get(data.id); + const tabFromMemory = tabsByIdRef.current.get(data.id); if (!tabFromMemory && !data.content) return; + const { id, content, @@ -642,9 +645,11 @@ const useTabLayout = (props, dockRef) => { extension, isDirty, isNew, + tabIncrement, tabProps } = tabFromMemory ?? data; - tabsById.current.set(id, { + + tabsByIdRef.current.set(id, { id, scope, name, @@ -653,11 +658,19 @@ const useTabLayout = (props, dockRef) => { extension, isNew, isDirty, + tabIncrement, tabProps }); - const tabData = { id, scope, name, tabTitle, extension }; + const tabData = { + id, + scope, + name, + tabTitle, + extension + }; return { id: id, + tabIncrement, title: _getCustomTab(tabData, _closeTab, isDirty), content: content, closable: true @@ -676,7 +689,7 @@ const useTabLayout = (props, dockRef) => { (newLayout, tabId, direction) => { const isActuallyTabChange = activeTabId.current !== tabId; const dock = getDockFromTabId(tabId); - const tabData = tabsById.current.get(tabId); + const tabData = tabsByIdRef.current.get(tabId); let newActiveTabId = tabId; // Attempt to close tab @@ -697,7 +710,7 @@ const useTabLayout = (props, dockRef) => { // Emit new active tab id if (!tabId) return; - if(isActuallyTabChange){ + if (isActuallyTabChange) { activeTabId.current = newActiveTabId; emit(PLUGINS.TABS.ON.ACTIVE_TAB_CHANGE, { id: newActiveTabId }); } @@ -743,7 +756,7 @@ const useTabLayout = (props, dockRef) => { * @returns {string} active tab id */ const getActiveTab = useCallback(() => { - return tabsById.current.get(activeTabId.current); + return tabsByIdRef.current.get(activeTabId.current); }, []); /** @@ -819,7 +832,7 @@ const useTabLayout = (props, dockRef) => { layoutActiveIdIsValid(lastLayout); - tabsById.current = lastTabs; + tabsByIdRef.current = lastTabs; // Install current tabs plugins lastTabs.forEach(tab => { const { content, ...others } = tab; @@ -829,7 +842,7 @@ const useTabLayout = (props, dockRef) => { Promise.allSettled(tabs).then(_tabs => { _tabs.forEach(tab => { tab.status === "fulfilled" && - tabsById.current.set(tab.value.id, tab.value); + tabsByIdRef.current.set(tab.value.id, tab.value); }); setLayout(lastLayout); diff --git a/src/tools/HomeTab/HomeTab.jsx b/src/tools/HomeTab/HomeTab.jsx index c892bbbb..bce20844 100644 --- a/src/tools/HomeTab/HomeTab.jsx +++ b/src/tools/HomeTab/HomeTab.jsx @@ -95,7 +95,6 @@ export const getHomeTab = () => { name: HOMETAB_PROFILE.title, tabTitle: HOMETAB_PROFILE.title, scope: HOMETAB_PROFILE.name, - extension: "", - isTool: true + extension: "" }; }; diff --git a/src/tools/NotInstalled/NotInstalled.jsx b/src/tools/NotInstalled/NotInstalled.jsx index 60137828..aba949bf 100644 --- a/src/tools/NotInstalled/NotInstalled.jsx +++ b/src/tools/NotInstalled/NotInstalled.jsx @@ -13,7 +13,7 @@ const NotInstalled = props => { export default NotInstalled; -export const getTabData = (id, name) => { +export const getNotInstalledTabData = (id, name) => { const tabTitle = name || id; return { id: id, diff --git a/src/tools/index.js b/src/tools/index.js index 2dff2ce8..4af5d261 100644 --- a/src/tools/index.js +++ b/src/tools/index.js @@ -1,6 +1,6 @@ import React from "react"; import PluginManagerIDE from "../engine/PluginManagerIDE/PluginManagerIDE"; -import { getTabData } from "./NotInstalled/NotInstalled"; +import { getNotInstalledTabData } from "./NotInstalled/NotInstalled"; const APP_TOOLS = {}; @@ -9,12 +9,14 @@ export const addTool = (name, tool) => { }; export const getToolTabData = (tab, props = {}) => { - const { id, name } = tab; - const notInstalledTab = getTabData(id, name); - const data = id in APP_TOOLS ? APP_TOOLS[id].tabData : notInstalledTab; + const { id, name, scope } = tab; + const notInstalledTab = getNotInstalledTabData(id, name); + const data = + APP_TOOLS[scope]?.tabData ?? APP_TOOLS[id]?.tabData ?? notInstalledTab; + // Sanitize tab data to avoid TypeError: Converting circular structure to JSON if (!data.content) { - const plugin = PluginManagerIDE.getPlugin(id); + const plugin = PluginManagerIDE.getPlugin(data.scope); data.content = plugin.render(props) || notInstalledTab.content; } if (props.tabTitle) data.name = props.tabTitle; @@ -24,7 +26,7 @@ export const getToolTabData = (tab, props = {}) => { data.content = React.cloneElement(data.content, mergedProps); } - return data; + return { ...data, ...tab }; }; export const hasTool = name => { diff --git a/src/utils/Utils.js b/src/utils/Utils.js index 20279a21..f84cfbe0 100644 --- a/src/utils/Utils.js +++ b/src/utils/Utils.js @@ -20,6 +20,26 @@ export const defaultFunction = (name, logToConsole = true) => { if (logToConsole) console.warn(`${name} not implemented`); }; +/** + * Tries to find the next increment on an array of numbers + * @param {Number[]} numArray + */ +export const findNextIncrement = numArray => { + if (!numArray || numArray.length < 2) + return numArray[0] ? numArray[0] + 1 : 1; + + const max = Math.max(...numArray); + const min = 1; + + for (let i = min; i <= max; i++) { + if (!numArray.includes(i)) { + return i; + } + } + + return max + 1; +}; + /** * Returns a given icon with props in a method * @param {SvgIcon} Icon diff --git a/src/utils/generalFunctions.js b/src/utils/generalFunctions.js index 7ec837a5..1cf6051f 100644 --- a/src/utils/generalFunctions.js +++ b/src/utils/generalFunctions.js @@ -1,11 +1,15 @@ import { i18n } from "@mov-ai/mov-fe-lib-react"; import AppSettings from "../App/AppSettings"; import { PLUGINS } from "../utils/Constants"; +import { findNextIncrement } from "./Utils"; +import Workspace from "./Workspace"; import movaiIconWhite from "../Branding/movai-logo-white.png"; import { getHomeTab } from "../tools/HomeTab/HomeTab"; import { getShortcutsTab } from "../tools/AppShortcuts/AppShortcuts"; import { getToolTabData } from "../tools"; +const workspaceManager = new Workspace(); + //======================================================================================== /* * * Private Methods * @@ -70,10 +74,26 @@ export function aboutPopup(call, classes = {}) { /** * Open Tool tab * @param {function} call : Plugin call method - * @param {string} toolName : Tool Unique Name + * @param {string} scope : Tool Unique Name */ -export function openTool(call, toolName = getHomeTab().id, props = {}) { - const tabData = getToolTabData({ id: toolName }, props); +export function openTool(call, scope = getHomeTab().scope, props = {}) { + const tabData = getToolTabData({ scope }, props); + + if (Object.hasOwn(tabData, "tabIncrement")) { + const thisTabIds = [...workspaceManager.getTabs().values()] + .filter(tab => tab.scope === tabData.scope) + .map(tab => tab.tabIncrement); + + const currentIncrement = findNextIncrement(thisTabIds); + tabData.tabIncrement = currentIncrement; + + if (currentIncrement > 1) { + tabData.id = `${tabData.scope}_${currentIncrement}`; + tabData.name = `${tabData.name} ${currentIncrement}`; + tabData.tabTitle = `${tabData.tabTitle} ${currentIncrement}`; + } + } + call(PLUGINS.TABS.NAME, PLUGINS.TABS.CALL.OPEN, tabData); }