diff --git a/backend/VERSION.json b/backend/VERSION.json index 3f63e6eb628..adba0b93c83 100644 --- a/backend/VERSION.json +++ b/backend/VERSION.json @@ -1,4 +1,4 @@ -{ "VERSION" : "5.7.0", +{ "VERSION" : "5.7.1", "unity" : { "current" : "2.20.0", "supported": [] diff --git a/backend/package.json b/backend/package.json index 3825ceed2cd..5bdda9284c2 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "3drepo.io", - "version": "5.7.0", + "version": "5.7.1", "engines": { "node": "18.x.x" }, diff --git a/backend/src/v4/routes/apidoc.json b/backend/src/v4/routes/apidoc.json index a97b6c8c346..4b0c45c6b05 100644 --- a/backend/src/v4/routes/apidoc.json +++ b/backend/src/v4/routes/apidoc.json @@ -1,3 +1,3 @@ { - "version": "5.7.0" + "version": "5.7.1" } diff --git a/frontend/.storybook/preview.js b/frontend/.storybook/preview.js index 4c6f2791e3c..e4ced611ea3 100644 --- a/frontend/.storybook/preview.js +++ b/frontend/.storybook/preview.js @@ -1,7 +1,7 @@ import { theme as V5Theme } from '@/v5/ui/themes/theme'; import { theme as V5ViewerTheme } from '@/v5/ui/routes/viewer/theme'; import { IntlProvider } from 'react-intl'; -import { getIntlProviderProps } from '@/v5/services/intl'; +import { getIntl } from '@/v5/services/intl'; import { GlobalStyle } from '@/v5/ui/themes/global'; import { ThemeProvider as MuiThemeProvider } from '@mui/material'; import { ThemeProvider, createGlobalStyle } from 'styled-components'; @@ -73,7 +73,7 @@ const withThemeProvider = (Story, context)=>{ export const decorators = [ withThemeProvider, (Story) => ( - + ), diff --git a/frontend/package.json b/frontend/package.json index ff53ff46863..65cf75f2040 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "3drepo.io-frontend", - "version": "5.7.0", + "version": "5.7.1", "description": "The frontend for 3drepo.io", "engines": { "node": "18.x.x" diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index aa3dfa68d1c..44aa8e1f406 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -30,7 +30,7 @@ import { Root as V5Root } from '@/v5/ui/routes'; import { UnityUtil } from '@/globals/unity-util'; import { clientConfigService } from '@/v4/services/clientConfig'; -import { formatMessage, getIntlProviderProps, initializeIntl } from '@/v5/services/intl'; +import { formatMessage, getIntl, initializeIntl } from '@/v5/services/intl'; import { initializeActionsDispatchers } from '@/v5/helpers/actionsDistpatchers.helper'; import { IntlProvider } from 'react-intl'; import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; @@ -68,7 +68,7 @@ const render = () => { ReactDOM.render( - + diff --git a/frontend/src/v4/routes/viewerGui/viewerGui.component.tsx b/frontend/src/v4/routes/viewerGui/viewerGui.component.tsx index b7d8df23ed4..67782058910 100644 --- a/frontend/src/v4/routes/viewerGui/viewerGui.component.tsx +++ b/frontend/src/v4/routes/viewerGui/viewerGui.component.tsx @@ -16,7 +16,7 @@ */ import { Tickets } from '@/v5/ui/routes/viewer/tickets/tickets.component'; -import { isEmpty } from 'lodash'; +import { isEmpty, isEqual } from 'lodash'; import { PureComponent } from 'react'; import { Toolbar } from '@/v5/ui/routes/viewer/toolbar/toolbar.component'; import { VIEWER_EVENTS } from '../../constants/viewer'; @@ -181,6 +181,7 @@ export class ViewerGui extends PureComponent { this.props.setPanelVisibility(VIEWER_PANELS.COMPARE, false); this.props.resetCompareComponent(); } + } public componentWillUnmount() { diff --git a/frontend/src/v4/routes/viewerGui/viewerGui.container.ts b/frontend/src/v4/routes/viewerGui/viewerGui.container.ts index bf11550dbda..7e82afc55e7 100644 --- a/frontend/src/v4/routes/viewerGui/viewerGui.container.ts +++ b/frontend/src/v4/routes/viewerGui/viewerGui.container.ts @@ -19,6 +19,7 @@ import { TeamspacesActions } from '@/v4/modules/teamspaces'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { createStructuredSelector } from 'reselect'; +import { selectSelectedTicket } from '@/v5/store/tickets/card/ticketsCard.selectors'; import { CompareActions } from '../../modules/compare'; import { selectCurrentTeamspace, selectCurrentUser } from '../../modules/currentUser'; diff --git a/frontend/src/v5/helpers/viewpoint.helpers.ts b/frontend/src/v5/helpers/viewpoint.helpers.ts index d20410e1487..56c12a55fce 100644 --- a/frontend/src/v5/helpers/viewpoint.helpers.ts +++ b/frontend/src/v5/helpers/viewpoint.helpers.ts @@ -20,11 +20,8 @@ import { generateViewpoint as generateViewpointV4, getNodesIdsFromSharedIds, toS import { formatMessage } from '@/v5/services/intl'; import { dispatch, getState } from '@/v4/modules/store'; import { isEmpty, isString } from 'lodash'; -import { Viewer as ViewerService } from '@/v4/services/viewer/viewer'; -import { TreeActions } from '@/v4/modules/tree'; -import { ViewerGuiActions } from '@/v4/modules/viewerGui'; import { selectCurrentTeamspace } from '../store/teamspaces/teamspaces.selectors'; -import { TicketsCardActionsDispatchers } from '../services/actionsDispatchers'; +import { ViewpointsActions } from '@/v4/modules/viewpoints'; export const convertToV5GroupNodes = (objects) => objects.map((object) => ({ container: object.model as string, @@ -73,6 +70,12 @@ const convertToV5GroupOverride = (group: any, type: ViewpointGroupOverrideType): override.opacity = opacity; } + if (group.transformation) { + const { transformation } = group; + override.transformation = transformation; + } + + return override; }; @@ -100,10 +103,8 @@ export const getViewerState = async () => { return state; }; -const mergeGroups = (groups: any[]) => ({ objects: groups.flatMap((group) => group.objects) }); - const convertToV4Group = (groupOverride: GroupOverride) => { - const { color, opacity, group: v5Group } = groupOverride; + const { color, opacity, transformation, group: v5Group } = groupOverride; if (isString(v5Group)) { return { color: [0, 0, 0, 0], objects: [] }; // theres no info yet so I say us an empty group @@ -121,9 +122,15 @@ const convertToV4Group = (groupOverride: GroupOverride) => { group.opacity = opacity; } + if (transformation) { + group.transformation = transformation; + } + return group; }; +const mergeGroups = (groups: any[]) => ({ objects: groups.flatMap((group) => group.objects) }); + export const viewpointV5ToV4 = (viewpoint: Viewpoint) => { let v4Viewpoint:any = {}; if (viewpoint.camera) { @@ -140,14 +147,15 @@ export const viewpointV5ToV4 = (viewpoint: Viewpoint) => { v4Viewpoint.clippingPlanes = viewpoint.clippingPlanes; } - if (!isEmpty(viewpoint.state?.colored)) { - v4Viewpoint.override_groups = viewpoint.state.colored.map(convertToV4Group); - } - if (!isEmpty(viewpoint.state?.transformed)) { v4Viewpoint.transformation_groups = viewpoint.state.transformed.map(convertToV4Group); } + + if (!isEmpty(viewpoint.state?.colored)) { + v4Viewpoint.override_groups = viewpoint.state.colored.map(convertToV4Group); + } + if (!isEmpty(viewpoint.state?.hidden)) { v4Viewpoint.hidden_group = mergeGroups(viewpoint.state.hidden.map(convertToV4Group)); } @@ -160,19 +168,21 @@ export const meshObjectsToV5GroupNode = (objects) => objects.map((obj) => ({ _ids: obj.mesh_ids, })); -export const toColorAndTransparencyDicts = (overrides: GroupOverride[]): OverridesDicts => { - const toMeshDictionary = (objects: V4GroupObjects, color: string, opacity: number): OverridesDicts => objects.shared_ids.reduce((dict, id) => { - if (color !== undefined) { +export const toGroupPropertiesDicts = (overrides: GroupOverride[]): OverridesDicts => { + const toMeshDictionary = (objects: V4GroupObjects, color: string, opacity: number): OverridesDicts => + objects.shared_ids.reduce((dict, id) => { + if (color !== undefined) { // eslint-disable-next-line no-param-reassign - dict.overrides[id] = color; - } + dict.overrides[id] = color; + } - if (opacity !== undefined) { + if (opacity !== undefined) { // eslint-disable-next-line no-param-reassign - dict.transparencies[id] = opacity; - } - return dict; - }, { overrides: {}, transparencies: {} } as OverridesDicts); + dict.transparencies[id] = opacity; + } + + return dict; + }, { overrides: {}, transparencies: {} } as OverridesDicts); return overrides.reduce((acum, current) => { const color = current.color ? getGroupHexColor(current.color) : undefined; @@ -186,32 +196,21 @@ export const toColorAndTransparencyDicts = (overrides: GroupOverride[]): Overrid dict.overrides = { ...dict.overrides, ...overrideDict.overrides }; // eslint-disable-next-line no-param-reassign dict.transparencies = { ...dict.transparencies, ...overrideDict.transparencies }; + return dict; }, acum); }, { overrides: {}, transparencies: {} } as OverridesDicts); }; export const goToView = async (view: Viewpoint) => { - if (isEmpty(view?.state?.colored) && isEmpty(view?.state?.hidden) && isEmpty(view?.camera) && isEmpty(view?.clippingPlanes)) { + if ( + isEmpty(view?.state?.colored) && + isEmpty(view?.state?.hidden) && + isEmpty(view?.state?.transformed) && + isEmpty(view?.camera) && + isEmpty(view?.clippingPlanes)) { return; } - - dispatch(ViewerGuiActions.clearColorOverrides()); - await ViewerService.setViewpoint(view); - const overrides = toColorAndTransparencyDicts(view?.state?.colored || []); - TicketsCardActionsDispatchers.setOverrides(overrides); - - await ViewerService.clearHighlights(); - - if (view?.state) { - dispatch(TreeActions.setHiddenGeometryVisible(!!view.state.showHidden)); - } - - const v4HiddenObjects = convertToV4GroupNodes(view.state?.hidden?.flatMap((hiddenOverride) => (hiddenOverride.group as Group)?.objects || [])); - if (v4HiddenObjects.length) { - dispatch(TreeActions.hideNodesBySharedIds(v4HiddenObjects, true)); - } else { - dispatch(TreeActions.showAllNodes()); - } + dispatch(ViewpointsActions.showViewpoint(null, null, viewpointV5ToV4(view))); }; diff --git a/frontend/src/v5/services/intl.ts b/frontend/src/v5/services/intl.ts index 6df663fa6f1..681d178dbb3 100644 --- a/frontend/src/v5/services/intl.ts +++ b/frontend/src/v5/services/intl.ts @@ -43,12 +43,19 @@ export const initializeIntl = (locale: string) => { // Locale of the fallback defaultMessage defaultLocale: DEFAULT_LOCALE, messages, + onError: (error) => { + if (error.code === 'MISSING_TRANSLATION') { + return; + } + + console.error(error); + }, }, cache, ); }; -const getIntl = () => { +export const getIntl = () => { if (!intlInternal) { initializeIntl(DEFAULT_LOCALE); } @@ -70,16 +77,3 @@ export const formatRelativeTime: typeof intlInternal.formatRelativeTime = (value // eslint-disable-next-line max-len export const formatPlural: typeof intlInternal.formatPlural = (value, opts?): Intl.LDMLPluralRule => getIntl().formatPlural(value, opts); - -export const getIntlProviderProps = () => ({ - messages: getIntl().messages, - defaultLocal: getIntl().defaultLocale, - locale: getIntl().locale, - onError: (error) => { - if (error.code === 'MISSING_TRANSLATION' && getIntl().locale === DEFAULT_LOCALE) { - return; - } - - console.error(error); - }, -}); diff --git a/frontend/src/v5/store/tickets/card/ticketsCard.redux.ts b/frontend/src/v5/store/tickets/card/ticketsCard.redux.ts index 1836c258ca7..f28b5607562 100644 --- a/frontend/src/v5/store/tickets/card/ticketsCard.redux.ts +++ b/frontend/src/v5/store/tickets/card/ticketsCard.redux.ts @@ -36,6 +36,7 @@ export const { Types: TicketsCardTypes, Creators: TicketsCardActions } = createA resetState: [], setOverrides: ['overrides'], setUnsavedTicket: ['ticket'], + setTransformations: ['transformations'], }, { prefix: 'TICKETS_CARD/' }) as { Types: Constants; Creators: ITicketsCardActionCreators }; export interface ITicketsCardState { @@ -47,6 +48,7 @@ export interface ITicketsCardState { readOnly: boolean, overrides: OverridesDicts | null, unsavedTicket: EditableTicket | null, + transformations: any, } export const INITIAL_STATE: ITicketsCardState = { @@ -60,6 +62,7 @@ export const INITIAL_STATE: ITicketsCardState = { }, view: TicketsCardViews.List, overrides: null, + transformations: null, readOnly: false, unsavedTicket: null, }; @@ -156,5 +159,5 @@ export interface ITicketsCardActionCreators { setReadOnly: (readOnly: boolean) => SetReadOnlyAction, resetState: () => ResetStateAction, setOverrides: (overrides: OverridesDicts) => SetOverridesAction, - setUnsavedTicket: (ticket: EditableTicket) => SetUnsavedTicketAction, + setUnsavedTicket: (ticket: EditableTicket) => SetUnsavedTicketAction } diff --git a/frontend/src/v5/store/tickets/card/ticketsCard.selectors.ts b/frontend/src/v5/store/tickets/card/ticketsCard.selectors.ts index cc446984cc9..9959e1d0705 100644 --- a/frontend/src/v5/store/tickets/card/ticketsCard.selectors.ts +++ b/frontend/src/v5/store/tickets/card/ticketsCard.selectors.ts @@ -67,7 +67,7 @@ export const selectSelectedTicketPinId = createSelector( export const selectTicketOverridesDict = createSelector( selectTicketsCardDomain, - (ticketCardState) => ticketCardState.overrides || { overrides: {}, transparencies: {} }, + (ticketCardState) => ticketCardState.overrides || { overrides: {}, transparencies: {}, transformations: {} }, ); export const selectTicketOverrides = createSelector( diff --git a/frontend/src/v5/store/tickets/tickets.helpers.ts b/frontend/src/v5/store/tickets/tickets.helpers.ts index 05852c0f604..1d33b3b4477 100644 --- a/frontend/src/v5/store/tickets/tickets.helpers.ts +++ b/frontend/src/v5/store/tickets/tickets.helpers.ts @@ -273,6 +273,7 @@ const fillEmptyOverrides = (values: Partial) => { viewValue.state ||= {} as any; viewValue.state.colored ||= []; viewValue.state.hidden ||= []; + viewValue.state.transformed ||= []; } }); }; diff --git a/frontend/src/v5/store/tickets/tickets.selectors.ts b/frontend/src/v5/store/tickets/tickets.selectors.ts index aaa30c4d9b8..9996695d7a8 100644 --- a/frontend/src/v5/store/tickets/tickets.selectors.ts +++ b/frontend/src/v5/store/tickets/tickets.selectors.ts @@ -19,8 +19,8 @@ import { createSelector } from 'reselect'; import { orderBy } from 'lodash'; import { BaseProperties } from '@/v5/ui/routes/viewer/tickets/tickets.constants'; import { ITicketsState } from './tickets.redux'; -import { createPropertiesWithGroups } from './ticketsGroups.helpers'; -import { ITicket, Properties, TicketWithModelIdAndName } from './tickets.types'; +import { createPropertiesWithGroups, ticketWithGroups } from './ticketsGroups.helpers'; +import { ITicket, TicketWithModelIdAndName } from './tickets.types'; import { selectContainers } from '../containers/containers.selectors'; import { selectFederations } from '../federations/federations.selectors'; @@ -70,19 +70,19 @@ export const selectTicketsGroups = createSelector( (state) => state.groupsByGroupId, ); -export const selectTickets = createSelector( +const selectTicketsRaw = createSelector( selectTicketsDomain, (state, modelId) => modelId, + (state, modelId) => state.ticketsByModelId[modelId] || [], +); + +export const selectTickets = createSelector( + selectTicketsRaw, selectTicketsGroups, - (state, modelId, groups): ITicket[] => { + (ticketsList, groups): ITicket[] => { const tickets = []; - (state.ticketsByModelId[modelId] || []).forEach((ticket) => { - let { properties } = ticket; - properties = createPropertiesWithGroups(properties, groups); - tickets.push({ - ...ticket, - properties, - }); + ticketsList.forEach((ticket) => { + tickets.push({ ...ticket, properties: createPropertiesWithGroups(ticket.properties, groups) }); }); return orderBy(tickets, `properties.${BaseProperties.CREATED_AT}`, 'desc'); @@ -90,7 +90,7 @@ export const selectTickets = createSelector( ); export const selectTicketByIdRaw = createSelector( - selectTickets, + selectTicketsRaw, (_, modelId, ticketId) => ticketId, (tickets, ticketId) => tickets.find(({ _id }) => _id === ticketId) || null, ); @@ -102,25 +102,7 @@ export const selectTicketById = createSelector( if (!ticket) { return ticket; } - - let { properties } = ticket; - properties = createPropertiesWithGroups(properties, groups); - const finalTicket = { - ...ticket, - properties, - }; - - if (ticket.modules) { - let { modules } = ticket; - modules = Object.keys(modules).reduce((partialModules, key) => { - // eslint-disable-next-line no-param-reassign - partialModules[key] = createPropertiesWithGroups(modules[key], groups); - return partialModules; - }, {} as Record); - - finalTicket.modules = modules; - } - return finalTicket; + return ticketWithGroups(ticket, groups); }, ); diff --git a/frontend/src/v5/store/tickets/tickets.types.ts b/frontend/src/v5/store/tickets/tickets.types.ts index 49effdb2b85..4ed64a5a85d 100644 --- a/frontend/src/v5/store/tickets/tickets.types.ts +++ b/frontend/src/v5/store/tickets/tickets.types.ts @@ -153,12 +153,18 @@ export enum ViewpointGroupOverrideType { TRANSFORMED, } -type ColorAndOpacity = { +export type TransformMatrix = [number, number, number, number, + number, number, number, number, + number, number, number, number, + number, number, number, number]; + +type GroupProperties = { color?: [number, number, number], opacity?: number, + transformation?: TransformMatrix }; -export type GroupOverride = ColorAndOpacity & { +export type GroupOverride = GroupProperties & { prefix?: string[], group: string | Group, key?: number; @@ -182,10 +188,11 @@ export type IGroupSettingsForm = GroupOverride & { group: Group }; type MeshIdColorDict = Record; type MeshIdTransparencyDict = Record; +export type MeshIdTransformDict = Record; export type OverridesDicts = { overrides: MeshIdColorDict, - transparencies: MeshIdTransparencyDict + transparencies: MeshIdTransparencyDict, }; export type TicketWithModelIdAndName = ITicket & { modelId: string; modelName: string }; diff --git a/frontend/src/v5/store/tickets/ticketsGroups.helpers.ts b/frontend/src/v5/store/tickets/ticketsGroups.helpers.ts index 758f98533cf..1821e8f5c18 100644 --- a/frontend/src/v5/store/tickets/ticketsGroups.helpers.ts +++ b/frontend/src/v5/store/tickets/ticketsGroups.helpers.ts @@ -16,7 +16,7 @@ */ import { isString } from 'lodash'; -import { Group, GroupOverride, ViewpointState, Properties } from './tickets.types'; +import { Group, GroupOverride, ViewpointState, Properties, ITicket } from './tickets.types'; const overrideWithGroups = (groups: Record) => (override: GroupOverride) => { const overrideToReturn = { ...override }; @@ -57,6 +57,18 @@ export const createPropertiesWithGroups = (properties, groups) => Object.keys(pr return partialProps; }, {} as Properties); + +export const ticketWithGroups = (ticket: ITicket, groups: Record) => { + const properties = createPropertiesWithGroups(ticket.properties, groups); + + + const modules:Record = {}; + Object.keys(ticket.modules).forEach((moduleName) => + modules[moduleName] = createPropertiesWithGroups(ticket.modules[moduleName], groups), + ); + return { ...ticket, properties, modules }; +}; + /* eslint-enable no-param-reassign */ export const getSanitizedSmartGroup = (group) => { diff --git a/frontend/src/v5/ui/routes/viewer/tickets/ticketDetails/ticketsDetailsCard.component.tsx b/frontend/src/v5/ui/routes/viewer/tickets/ticketDetails/ticketsDetailsCard.component.tsx index 9f565bc09b9..a36b76f73e7 100644 --- a/frontend/src/v5/ui/routes/viewer/tickets/ticketDetails/ticketsDetailsCard.component.tsx +++ b/frontend/src/v5/ui/routes/viewer/tickets/ticketDetails/ticketsDetailsCard.component.tsx @@ -18,24 +18,25 @@ import { ArrowBack, CardContainer, CardHeader, HeaderButtons } from '@components/viewer/cards/card.styles'; import { useContext, useEffect, useRef } from 'react'; import { useParams } from 'react-router-dom'; -import { TicketsCardHooksSelectors, TicketsHooksSelectors, TreeHooksSelectors } from '@/v5/services/selectorsHooks'; +import { TicketsCardHooksSelectors, TicketsHooksSelectors } from '@/v5/services/selectorsHooks'; import { TicketsCardActionsDispatchers, TicketsActionsDispatchers } from '@/v5/services/actionsDispatchers'; import { findEditedGroup, modelIsFederation, sanitizeViewVals, templateAlreadyFetched } from '@/v5/store/tickets/tickets.helpers'; import { getValidators } from '@/v5/store/tickets/tickets.validators'; import { FormProvider, useForm } from 'react-hook-form'; import { CircleButton } from '@controls/circleButton'; import { yupResolver } from '@hookform/resolvers/yup'; -import { isEmpty, set } from 'lodash'; +import { get, isEmpty, set } from 'lodash'; import { dirtyValues, filterErrors, nullifyEmptyObjects, removeEmptyObjects } from '@/v5/helpers/form.helper'; import { FormattedMessage } from 'react-intl'; import { InputController } from '@controls/inputs/inputController.component'; import { goToView } from '@/v5/helpers/viewpoint.helpers'; -import { AdditionalProperties, TicketsCardViews } from '../tickets.constants'; +import { TicketsCardViews } from '../tickets.constants'; import { TicketForm } from '../ticketsForm/ticketForm.component'; import { ChevronLeft, ChevronRight } from './ticketDetails.styles'; import { TicketGroups } from '../ticketsForm/ticketGroups/ticketGroups.component'; import { TicketContext, TicketDetailsView } from '../ticket.context'; import { useSearchParam } from '../../../useSearchParam'; +import { Viewpoint } from '@/v5/store/tickets/tickets.types'; enum IndexChange { PREV = -1, @@ -46,14 +47,11 @@ export const TicketDetailsCard = () => { const { teamspace, project, containerOrFederation, revision } = useParams(); const [, setTicketId] = useSearchParam('ticketId'); const { view, setDetailViewAndProps, viewProps } = useContext(TicketContext); - const treeNodesList = TreeHooksSelectors.selectTreeNodesList(); const isFederation = modelIsFederation(containerOrFederation); - const tickets = TicketsHooksSelectors.selectTickets(containerOrFederation); const filteredTickets = TicketsCardHooksSelectors.selectTicketsWithAllFiltersApplied() as any; const ticketId = TicketsCardHooksSelectors.selectSelectedTicketId(); - const ticket = tickets.find((t) => t._id === ticketId); + const ticket = TicketsHooksSelectors.selectTicketById(containerOrFederation, ticketId); const template = TicketsHooksSelectors.selectTemplateById(containerOrFederation, ticket?.type); - const defaultView = ticket?.properties?.[AdditionalProperties.DEFAULT_VIEW]; const currentIndex = filteredTickets.findIndex((tckt) => tckt._id === ticket._id); const initialIndex = useRef(currentIndex); const disableCycleButtons = currentIndex > -1 ? filteredTickets.length < 2 : filteredTickets.length < 1; @@ -139,22 +137,19 @@ export const TicketDetailsCard = () => { formData.reset(ticket); }, [JSON.stringify(ticket)]); - useEffect(() => { - if (view === TicketDetailsView.Groups) return; - goToView(defaultView); - }, [ticket._id, treeNodesList, JSON.stringify(defaultView?.camera)]); - - useEffect(() => { - if (view === TicketDetailsView.Groups) return; - const { state } = defaultView || {}; - goToView({ state }); - }, [JSON.stringify(defaultView?.state)]); - useEffect(() => () => { onBlurHandler(); setTicketId(); }, []); + const onClickBackFromGroups = () => { + setDetailViewAndProps(TicketDetailsView.Form); + const viewpoint = get(ticket, viewProps.name) as Viewpoint; + const { state } = viewpoint || {}; + goToView({ state }); + TicketsCardActionsDispatchers.setOverrides(null); + }; + if (!ticket) return null; return ( @@ -163,7 +158,7 @@ export const TicketDetailsCard = () => { && ( <> - setDetailViewAndProps(TicketDetailsView.Form)} /> + {ticket.title}: . */ -import { useEffect } from 'react'; +import { useContext, useEffect } from 'react'; import { useParams } from 'react-router-dom'; import { combineSubscriptions } from '@/v5/services/realtime/realtime.service'; import { modelIsFederation } from '@/v5/store/tickets/tickets.helpers'; @@ -27,14 +27,30 @@ import { enableRealtimeFederationUpdateTicket, enableRealtimeFederationUpdateTicketGroup, } from '@/v5/services/realtime/ticket.events'; -import { ContainersHooksSelectors, FederationsHooksSelectors, TicketsCardHooksSelectors } from '@/v5/services/selectorsHooks'; +import { ContainersHooksSelectors, FederationsHooksSelectors, TicketsCardHooksSelectors, TreeHooksSelectors } from '@/v5/services/selectorsHooks'; import { TicketsActionsDispatchers, TicketsCardActionsDispatchers, UsersActionsDispatchers } from '@/v5/services/actionsDispatchers'; -import { TicketsCardViews } from './tickets.constants'; +import { AdditionalProperties, TicketsCardViews } from './tickets.constants'; import { TicketsListCard } from './ticketsList/ticketsListCard.component'; import { TicketDetailsCard } from './ticketDetails/ticketsDetailsCard.component'; import { NewTicketCard } from './newTicket/newTicket.component'; import { ViewerParams } from '../../routes.constants'; -import { TicketContextComponent } from './ticket.context'; +import { TicketContext, TicketContextComponent, TicketDetailsView } from './ticket.context'; +import { goToView } from '@/v5/helpers/viewpoint.helpers'; + + +const ShowViewpoint = () => { + const selectedTicket = TicketsCardHooksSelectors.selectSelectedTicket(); + const { view: detailsView } = useContext(TicketContext); + const currView = selectedTicket?.properties?.[AdditionalProperties.DEFAULT_VIEW]; + const treeNodesList = TreeHooksSelectors.selectTreeNodesList(); + + useEffect(() => { + if (detailsView === TicketDetailsView.Groups) return; + goToView(currView); + }, [JSON.stringify(currView), treeNodesList]); + + return null; +}; export const Tickets = () => { const { teamspace, project, containerOrFederation, revision } = useParams(); @@ -81,10 +97,11 @@ export const Tickets = () => { }, [containerOrFederation]); return ( - <> + {view === TicketsCardViews.List && } - {view === TicketsCardViews.Details && } - {view === TicketsCardViews.New && } - + {view === TicketsCardViews.Details && } + {view === TicketsCardViews.New && } + + ); }; diff --git a/frontend/src/v5/ui/routes/viewer/tickets/ticketsForm/properties/ticketView/ticketView.component.tsx b/frontend/src/v5/ui/routes/viewer/tickets/ticketsForm/properties/ticketView/ticketView.component.tsx index b723a4c6500..e12e3d39092 100644 --- a/frontend/src/v5/ui/routes/viewer/tickets/ticketsForm/properties/ticketView/ticketView.component.tsx +++ b/frontend/src/v5/ui/routes/viewer/tickets/ticketsForm/properties/ticketView/ticketView.component.tsx @@ -99,6 +99,7 @@ export const TicketView = ({ const { state, ...view } = cloneDeep(value || {}); state.colored = []; state.hidden = []; + state.transformed = []; onChange?.({ state, ...view }); }; diff --git a/frontend/src/v5/ui/routes/viewer/tickets/ticketsForm/ticketGroups/ticketGroups.component.tsx b/frontend/src/v5/ui/routes/viewer/tickets/ticketsForm/ticketGroups/ticketGroups.component.tsx index d6421d2defa..ac17cf99544 100644 --- a/frontend/src/v5/ui/routes/viewer/tickets/ticketsForm/ticketGroups/ticketGroups.component.tsx +++ b/frontend/src/v5/ui/routes/viewer/tickets/ticketsForm/ticketGroups/ticketGroups.component.tsx @@ -20,7 +20,7 @@ import { Group, GroupOverride, Viewpoint, ViewpointState } from '@/v5/store/tick import { useEffect, useState } from 'react'; import { useDispatch, useSelector, useStore } from 'react-redux'; import { TreeActions } from '@/v4/modules/tree'; -import { convertToV4GroupNodes, toColorAndTransparencyDicts } from '@/v5/helpers/viewpoint.helpers'; +import { convertToV4GroupNodes, toGroupPropertiesDicts } from '@/v5/helpers/viewpoint.helpers'; import { cloneDeep, isString, uniqBy, xor } from 'lodash'; import { VIEWER_PANELS } from '@/v4/constants/viewerGui'; import { selectLeftPanels } from '@/v4/modules/viewerGui'; @@ -184,7 +184,7 @@ export const TicketGroups = ({ value, onChange, onBlur }: TicketGroupsProps) => useEffect(() => { const colored = selectedColorIndexes.map((i) => state.colored[i]).filter(Boolean); if (colored.some(({ group }) => isString(group))) return; - TicketsCardActionsDispatchers.setOverrides(toColorAndTransparencyDicts(colored)); + TicketsCardActionsDispatchers.setOverrides(toGroupPropertiesDicts(colored)); }, [selectedColorIndexes, value]); useEffect(() => { @@ -194,7 +194,7 @@ export const TicketGroups = ({ value, onChange, onBlur }: TicketGroupsProps) => }, [hasClearedOverrides]); useEffect(() => { - dispatch(ViewpointsActions.setSelectedViewpoint(null)); + dispatch(ViewpointsActions.clearColorOverrides()); ViewerService.on(VIEWER_EVENTS.BACKGROUND_SELECTED, clearHighlightedIndex); return () => ViewerService.off(VIEWER_EVENTS.BACKGROUND_SELECTED, clearHighlightedIndex); diff --git a/frontend/src/v5/ui/routes/viewer/tickets/ticketsList/ticketsList.component.tsx b/frontend/src/v5/ui/routes/viewer/tickets/ticketsList/ticketsList.component.tsx index f5e49ca22c2..6675621b756 100644 --- a/frontend/src/v5/ui/routes/viewer/tickets/ticketsList/ticketsList.component.tsx +++ b/frontend/src/v5/ui/routes/viewer/tickets/ticketsList/ticketsList.component.tsx @@ -21,7 +21,6 @@ import { uniq, xor } from 'lodash'; import { TicketsHooksSelectors, TicketsCardHooksSelectors } from '@/v5/services/selectorsHooks'; import { TicketsActionsDispatchers, TicketsCardActionsDispatchers } from '@/v5/services/actionsDispatchers'; import { FilterChip } from '@controls/chip/filterChip/filterChip.styles'; -import { goToView } from '@/v5/helpers/viewpoint.helpers'; import { VIEWER_EVENTS } from '@/v4/constants/viewer'; import { formatMessage } from '@/v5/services/intl'; import { EmptyListMessage } from '@controls/dashedContainer/emptyListMessage/emptyListMessage.styles'; @@ -31,7 +30,6 @@ import { Viewer as ViewerService } from '@/v4/services/viewer/viewer'; import { TicketItem } from './ticketItem/ticketItem.component'; import { List, Filters, CompletedFilterChip } from './ticketsList.styles'; import { ViewerParams } from '../../../routes.constants'; -import { AdditionalProperties } from '../tickets.constants'; import { hasDefaultPin } from '../ticketsForm/properties/coordsProperty/coordsProperty.helpers'; import { TicketSearchInput } from './ticketSearchInput/ticketSearchInput.component'; @@ -69,11 +67,6 @@ export const TicketsList = ({ tickets }: TicketsListProps) => { TicketsActionsDispatchers.fetchTicketGroups(teamspace, project, containerOrFederation, ticket._id, revision); }; - useEffect(() => { - const view = selectedTicket?.properties?.[AdditionalProperties.DEFAULT_VIEW]; - goToView(view); - }, [selectedTicket?.properties?.[AdditionalProperties.DEFAULT_VIEW]]); - useEffect(() => { TicketsCardActionsDispatchers.setSelectedTicketPin(selectedTicket?._id);