Загрузка данных
import { createSelector } from '@reduxjs/toolkit';
import isEqual from 'lodash/isEqual';
import { CREATE_NEW_WORKSPACE_ID } from '@configs/appConfig';
import { RootState } from '@store/store';
import { createFieldSelector } from '@store/utils/createFieldSelector';
import { getTheNewestWidget } from '@utils/getTheNewestWidget';
import { WidgetContentType } from 'types/Widgets';
import { Workspace, WorkspaceLoadState } from 'types/Workspace';
import { widgetsSelector } from './widgets';
export const workspacesSelector = (state: RootState) => state.workspaces.workspaces;
export const currentWorkspaceIdSelector = (state: RootState) =>
state.workspaces.workspaces.find(({ isCurrent }) => isCurrent)?.id;
export const currentWorkspaceLoadStatusSelector = (state: RootState): WorkspaceLoadState['status'] => {
const workspaceId = currentWorkspaceIdSelector(state);
if (workspaceId === undefined || workspaceId === CREATE_NEW_WORKSPACE_ID) {
return 'loaded';
}
return state.workspaces.loadStates?.[workspaceId]?.status ?? 'loading';
};
export const workspaceWidgetsSelector = createSelector(
[widgetsSelector, currentWorkspaceIdSelector],
(wgts, currentWorkspaceId) => wgts.filter(({ workspaceId }) => workspaceId === currentWorkspaceId),
{
memoizeOptions: {
maxSize: 10,
resultEqualityCheck: isEqual,
},
},
);
export const savedWorkspacesSelector = createSelector(workspacesSelector, (workspaces) =>
workspaces.filter((item) => item.saved),
);
export const closedWorkspacesSelector = createSelector(workspacesSelector, (workspaces) =>
workspaces.filter((item) => !item.saved),
);
export function workspaceByIdSelector(id: number): (state: RootState) => Workspace | undefined;
export function workspaceByIdSelector<K extends keyof Workspace>(
id: number,
field: K,
): (state: RootState) => Workspace[K] | undefined;
export function workspaceByIdSelector(id: number, field?: keyof Workspace) {
return (state: RootState) => {
const workspace = state.workspaces.workspaces.find((w) => w.id === id);
if (field !== undefined) {
return workspace?.[field];
}
return workspace;
};
}
export const selectIsCurrentWorkspaceNew = (state: RootState) =>
state.workspaces.workspaces.find(({ isCurrent }) => isCurrent)?.isNew ?? false;
export const currentWorkspaceSelector = (state: RootState) =>
state.workspaces.workspaces.find(({ isCurrent }) => isCurrent);
export const currentWorkspaceLayoutSelector = createSelector(
currentWorkspaceSelector,
(workspace) => workspace?.layout,
);
export const workspaceByNameSelector = (name: string) =>
createFieldSelector((state: RootState) => workspacesSelector(state).find((w) => w.name === name));
export const workspacesNamesSelector = createSelector(workspacesSelector, (workspaces) =>
workspaces.map((w) => w.name),
);
export const newestWidgetIdSelector = createSelector(
[widgetsSelector, (_: RootState, type: WidgetContentType) => type],
(widgets, type) => getTheNewestWidget(widgets, type)?.id,
);
import React from 'react';
import { useDispatch } from 'react-redux';
import { Loader } from '@components/Loader';
import { Z_INDEX } from '@configs/appConfig';
import features from '@features';
import { useAppSelect } from '@hooks/useAppSelector';
import { useLocalisation } from '@hooks/useLocalisation';
import { HelpCenter } from '@modules/HelpCenter';
import { ModalRoot } from '@modules/ModalRoot';
import { ModalsContainer } from '@modules/ModalsContainer';
import { useAddressDepositWithdrawalFormHotkey } from '@modules/MXT/AddressDepositWithdrawalForm/hooks';
import { useDepositFormHotkey } from '@modules/MXT/DepositForm/hooks/useDepositFormHotkey';
import { NewsWorkspace } from '@modules/NewsWorkspace/NewsWorkspace';
import { isSidebarOpenSelector } from '@store/selectors/modals';
import {
currentWorkspaceLoadStatusSelector,
savedWorkspacesSelector,
workspaceWidgetsSelector,
} from '@store/selectors/workspaces';
import { closeSidebarModal, openSidebarModal } from '@store/slices/modals';
import { WorkspaceErrorHandler } from '@terminal/desktop/components/WorkspaceErrorHandler';
import { Sidebar } from '@uikit/Sidebar';
import { EmptyWorkspace } from './components/EmptyWorkspace/EmptyWorkspace';
import { Header } from './components/Header';
import { Profile } from './components/Profile';
import { SidebarContent } from './components/SidebarContent';
import StartPage from './components/StartPage';
import styles from './desktop.module.scss';
import { WorkspaceContainer } from './workspaces/components/WorkspaceContainer';
import { Workspace } from './workspaces/default';
import { Workspace as NewWorkspace } from './workspaces/mesh';
export const DesktopContent: React.FC = () => {
const hasSavedWorkspaces = useAppSelect((state) => savedWorkspacesSelector(state).length > 0);
const noWidgets = useAppSelect((state) => workspaceWidgetsSelector(state).length === 0);
const workspaceLoadStatus = useAppSelect(currentWorkspaceLoadStatusSelector);
const isSidebarOpen = useAppSelect(isSidebarOpenSelector);
const dispatch = useDispatch();
useDepositFormHotkey();
useAddressDepositWithdrawalFormHotkey();
const onCloseSidebar = () => {
dispatch(closeSidebarModal());
};
const localisation = useLocalisation();
const isCurrentWorkspaceHelpCenterWidgets = useAppSelect(
(state) =>
state.workspaces.workspaces.find(
({ isCurrent, name, isNew }) => isCurrent && !isNew && name === localisation.helpCenter,
)?.isAddWidgets,
);
const isCurrentWorkspaceNewsWidgets = useAppSelect(
(state) =>
state.workspaces.workspaces.find(
({ isCurrent, name, isNew }) => isCurrent && !isNew && name === localisation.news,
)?.isAddWidgets,
);
const isCurrentWorkspaceSupportWidgets = useAppSelect(
(state) =>
state.workspaces.workspaces.find(
({ isCurrent, name, isNew }) => isCurrent && !isNew && name === localisation.profile,
)?.isAddWidgets,
);
const isShowCurrentWidgets: boolean = useAppSelect(
(state) => state.workspaces.workspaces.find(({ isCurrent, code }) => isCurrent && !code)?.isAddWidgets ?? false,
);
const isCurrentWorkspaceNew = useAppSelect(
({ workspaces }) => workspaces.workspaces.find(({ isCurrent }) => isCurrent)?.isNew,
);
const isWorkspaceLoading = isShowCurrentWidgets && workspaceLoadStatus === 'loading';
const isWorkspaceLoaded = workspaceLoadStatus === 'loaded';
const handleToggle = () => {
if (isSidebarOpen) {
dispatch(closeSidebarModal());
} else {
dispatch(openSidebarModal());
}
};
return (
<>
<Header />
<div className={styles['main-workspace-wrapper']}>
{isSidebarOpen && (
<Sidebar
className={styles['sidebar-container-main']}
sidebarBodyClassName={styles['sidebar-body-main']}
width={550}
open={isSidebarOpen}
onClose={onCloseSidebar}
zIndex={Z_INDEX.APP_SIDEBAR}
>
<SidebarContent />
</Sidebar>
)}
<WorkspaceContainer isMobileView={false}>
{isWorkspaceLoading ? (
<div className={styles['workspace-loader']}>
<Loader />
</div>
) : (
<>
{isWorkspaceLoaded &&
isShowCurrentWidgets &&
noWidgets &&
hasSavedWorkspaces &&
!isCurrentWorkspaceNew && <EmptyWorkspace onAddWidgetClick={handleToggle} />}
{isWorkspaceLoaded && isShowCurrentWidgets && noWidgets && isCurrentWorkspaceNew && (
<div className={styles['start-page']}>
<StartPage />
</div>
)}
<WorkspaceErrorHandler isMobileView={false}>
{features.meshLayout ? <NewWorkspace /> : <Workspace />}
</WorkspaceErrorHandler>
</>
)}
</WorkspaceContainer>
{isCurrentWorkspaceHelpCenterWidgets === false && <HelpCenter />}
{isCurrentWorkspaceNewsWidgets === false && <NewsWorkspace />}
{isCurrentWorkspaceSupportWidgets === false && <Profile />}
<ModalsContainer />
<ModalRoot />
</div>
</>
);
};
@import 'colors.scss';
.main-workspace-wrapper {
flex: 1;
width: 100%;
position: relative;
background-color: red;
min-height: 0;
}
.main-workspace {
background-color: $background-bottom;
overflow: hidden;
position: absolute;
top: 0;
right: 0;
left: 0;
bottom: 0;
.workspace-loader {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
.start-view {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
height: 100%;
margin-top: 12%;
.grid-container {
display: grid;
gap: 4px;
grid-template-columns: repeat(2, auto);
cursor: pointer;
.block {
background-color: $surface-button-basis-secondary;
border-radius: 4px;
display: flex;
justify-content: center;
align-items: center;
height: 54px;
width: 127px;
overflow: hidden;
.candle-image {
position: relative;
top: 6px;
width: 50px;
opacity: 0.6;
transition: all 0.4s ease;
mix-blend-mode: screen;
filter: brightness(1.4);
}
.smallGrid {
display: grid;
grid-template-columns: repeat(4, auto);
gap: 10px;
transition: all 0.4s ease;
}
.dot {
width: 2px;
height: 2px;
border-radius: 50%;
background-color: $surface-icon-basis-disabled;
transition: all 0.4s ease;
}
.lineGroup {
display: flex;
flex-direction: column;
justify-content: center;
margin: 12px;
gap: 5px;
border-radius: 1px;
transition: all 0.4s ease;
}
.line {
height: 1px;
width: 39px;
background-color: $surface-icon-basis-disabled;
transition: background-color 0.2s ease;
}
}
&:hover .candle-image {
transform: scale(1.2) translateY(-3px);
}
&:hover .line {
background-color: $surface-icon-basis-muted-secondary;
width: 45px;
}
&:hover .smallGrid,
&:hover .lineGroup {
transform: scale(1.15);
}
&:hover .lineGroup {
gap: 6px;
}
&:hover .smallGrid {
transform: scale(1.4);
}
&:hover .block {
background-color: $action-surface-hover;
}
}
.main-title {
font-size: 32px;
line-height: 36px;
color: var(--thm-text-button-primary);
margin-top: 32px;
}
.main-text {
font-size: 14px;
line-height: 16px;
color: var(--thm-text-button-primary);
margin: 24px 0 32px 0;
}
.bottom-text {
font-size: 12px;
line-height: 16px;
color: var(--thm-text-button-primary);
margin-top: 20px;
}
}
.connect-side-block {
height: 100%;
width: 50%;
position: absolute;
top: 0;
background-color: $terminal-bauhaus;
opacity: 0.5;
}
}
.main-workspace-mobile {
display: none;
}
.sidebar-container-main {
:global {
.ant-drawer-body {
padding: 0 24px 24px 24px;
}
}
}
.sidebar-body-main {
&::-webkit-scrollbar {
width: 8px;
}
}
.start-page {
height: 100%;
box-sizing: border-box;
padding-block: 100px 32px;
overflow-y: auto;
}