Загрузка данных


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;
}