lodash#reverse TypeScript Examples

The following examples show how to use lodash#reverse. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: BarChart.tsx    From aqualink-app with MIT License 5 votes vote down vote up
BarChart = ({ collection, classes }: BarChartProps) => {
  const theme = useTheme();
  const nLevels = 5;
  const groupedByAlert = groupBy(
    collection.sites,
    (site) => site.collectionData?.tempWeeklyAlert
  );

  const mostFrequentAlert = maxBy(Object.values(groupedByAlert), "length");

  return (
    <>
      <Box color={theme.palette.grey[500]} margin="0 0 14px 97px">
        <Typography variant="subtitle2">Sites by Alert Level</Typography>
      </Box>
      <Box flexGrow={1} width="100%">
        <Grid
          className={classes.alertsWrapper}
          container
          justify="space-between"
          direction="column"
        >
          {reverse(times(nLevels, Number)).map((level) => {
            const interval = findIntervalByLevel(level);
            return (
              <Grid item key={interval.label}>
                <Grid container alignItems="center">
                  <Grid item>
                    <Box
                      className={classes.alertLabelWrapper}
                      bgcolor={interval.color}
                    >
                      <Typography
                        className={classes.alertLabel}
                        color={
                          level === 3 || level === 4
                            ? "textPrimary"
                            : "textSecondary"
                        }
                        variant="subtitle2"
                      >
                        {interval.label}
                      </Typography>
                    </Box>
                  </Grid>
                  <Grid className={classes.barWrapper} item>
                    <Box width="100%" display="flex" alignItems="center">
                      <Box
                        width={percentageCalculator(
                          groupedByAlert?.[level]?.length || 0,
                          mostFrequentAlert?.length
                        )}
                        height="28px"
                        bgcolor={interval.color}
                      />
                      <Typography className={classes.siteCount}>
                        {groupedByAlert?.[level]?.length || 0}
                      </Typography>
                    </Box>
                  </Grid>
                </Grid>
              </Grid>
            );
          })}
        </Grid>
      </Box>
    </>
  );
}
Example #2
Source File: text.ts    From S2 with MIT License 4 votes vote down vote up
drawObjectText = (
  cell: S2CellType,
  multiData?: MultiData,
  // 绘制指标列头需要禁用
  disabledConditions?: boolean,
) => {
  const { x } = cell.getTextAndIconPosition(0).text;
  const {
    y,
    height: totalTextHeight,
    width: totalTextWidth,
  } = cell.getContentArea();
  const text = multiData || (cell.getMeta().fieldValue as MultiData);
  const { values: textValues } = text;
  const { valuesCfg } = cell?.getMeta().spreadsheet.options.style.cellCfg;
  const textCondition = disabledConditions ? null : valuesCfg?.conditions?.text;
  if (!isArray(textValues)) {
    drawBullet(textValues, cell);
    return;
  }

  const widthPercent = valuesCfg?.widthPercent;
  const dataCellStyle = cell.getStyle(CellTypes.DATA_CELL);
  const { textAlign } = dataCellStyle.text;
  const padding = dataCellStyle.cell.padding;

  const realHeight = totalTextHeight / (textValues.length + 1);
  let labelHeight = 0;
  // 绘制单元格主标题
  if (text?.label) {
    labelHeight = realHeight / 2;
    const labelStyle = dataCellStyle.bolderText;

    renderText(
      cell,
      [],
      calX(x, padding.right),
      y + labelHeight,
      getEllipsisText({
        text: text.label,
        maxWidth: totalTextWidth,
        fontParam: labelStyle,
      }),
      labelStyle,
    );
  }

  // 绘制指标
  let curText: string | number;
  let curX: number;
  let curY: number = y + realHeight / 2;
  let curWidth: number;
  let totalWidth = 0;
  for (let i = 0; i < textValues.length; i++) {
    curY = y + realHeight * (i + 1) + labelHeight; // 加上label的高度
    totalWidth = 0;
    const measures = clone(textValues[i]);
    if (textAlign === 'right') {
      reverse(measures); // 右对齐拿到的x坐标为最右坐标,指标顺序需要反过来
    }

    for (let j = 0; j < measures.length; j++) {
      curText = measures[j];
      const curStyle = getTextStyle(
        i,
        j,
        cell?.getMeta() as ViewMeta,
        curText,
        dataCellStyle,
        textCondition,
      );
      curWidth = !isEmpty(widthPercent)
        ? totalTextWidth * (widthPercent[j] / 100)
        : totalTextWidth / text.values[0].length; // 指标个数相同,任取其一即可

      curX = calX(x, padding.right, totalWidth, textAlign);
      totalWidth += curWidth;
      const { placeholder } = cell?.getMeta().spreadsheet.options;
      const emptyPlaceholder = getEmptyPlaceholder(
        cell?.getMeta(),
        placeholder,
      );
      renderText(
        cell,
        [],
        curX,
        curY,
        getEllipsisText({
          text: curText,
          maxWidth: curWidth,
          fontParam: curStyle,
          placeholder: emptyPlaceholder,
        }),
        curStyle,
      );
    }
  }
}
Example #3
Source File: GameLandingPage.tsx    From client with GNU General Public License v3.0 4 votes vote down vote up
export function GameLandingPage({ match, location }: RouteComponentProps<{ contract: string }>) {
  const history = useHistory();
  const terminalHandle = useRef<TerminalHandle>();
  const gameUIManagerRef = useRef<GameUIManager | undefined>();
  const topLevelContainer = useRef<HTMLDivElement | null>(null);

  const [gameManager, setGameManager] = useState<GameManager | undefined>();
  const [terminalVisible, setTerminalVisible] = useState(true);
  const [initRenderState, setInitRenderState] = useState(InitRenderState.NONE);
  const [ethConnection, setEthConnection] = useState<EthConnection | undefined>();
  const [step, setStep] = useState(TerminalPromptStep.NONE);

  const params = new URLSearchParams(location.search);
  const useZkWhitelist = params.has('zkWhitelist');
  const selectedAddress = params.get('account');
  const contractAddress = address(match.params.contract);
  const isLobby = contractAddress !== address(CONTRACT_ADDRESS);

  useEffect(() => {
    getEthConnection()
      .then((ethConnection) => setEthConnection(ethConnection))
      .catch((e) => {
        alert('error connecting to blockchain');
        console.log(e);
      });
  }, []);

  const isProd = process.env.NODE_ENV === 'production';

  const advanceStateFromNone = useCallback(
    async (terminal: React.MutableRefObject<TerminalHandle | undefined>) => {
      const issues = await unsupportedFeatures();

      if (issues.includes(Incompatibility.MobileOrTablet)) {
        terminal.current?.println(
          'ERROR: Mobile or tablet device detected. Please use desktop.',
          TerminalTextStyle.Red
        );
      }

      if (issues.includes(Incompatibility.NoIDB)) {
        terminal.current?.println(
          'ERROR: IndexedDB not found. Try using a different browser.',
          TerminalTextStyle.Red
        );
      }

      if (issues.includes(Incompatibility.UnsupportedBrowser)) {
        terminal.current?.println(
          'ERROR: Browser unsupported. Try Brave, Firefox, or Chrome.',
          TerminalTextStyle.Red
        );
      }

      if (issues.length > 0) {
        terminal.current?.print(
          `${issues.length.toString()} errors found. `,
          TerminalTextStyle.Red
        );
        terminal.current?.println('Please resolve them and refresh the page.');
        setStep(TerminalPromptStep.ASKING_WAITLIST_EMAIL);
      } else {
        setStep(TerminalPromptStep.COMPATIBILITY_CHECKS_PASSED);
      }
    },
    []
  );

  const advanceStateFromCompatibilityPassed = useCallback(
    async (terminal: React.MutableRefObject<TerminalHandle | undefined>) => {
      if (isLobby) {
        terminal.current?.newline();
        terminal.current?.printElement(
          <MythicLabelText text={`You are joining a Dark Forest lobby`} />
        );
        terminal.current?.newline();
        terminal.current?.newline();
      } else {
        terminal.current?.newline();
        terminal.current?.newline();
        terminal.current?.printElement(<MythicLabelText text={`                 Dark Forest`} />);
        terminal.current?.newline();
        terminal.current?.newline();

        terminal.current?.print('    ');
        terminal.current?.print('Version', TerminalTextStyle.Sub);
        terminal.current?.print('    ');
        terminal.current?.print('Date', TerminalTextStyle.Sub);
        terminal.current?.print('              ');
        terminal.current?.print('Champion', TerminalTextStyle.Sub);
        terminal.current?.newline();

        terminal.current?.print('    v0.1       ', TerminalTextStyle.Text);
        terminal.current?.print('02/05/2020        ', TerminalTextStyle.Text);
        terminal.current?.printLink(
          'Dylan Field',
          () => {
            window.open('https://twitter.com/zoink');
          },
          TerminalTextStyle.Text
        );
        terminal.current?.newline();
        terminal.current?.print('    v0.2       ', TerminalTextStyle.Text);
        terminal.current?.println('06/06/2020        Nate Foss', TerminalTextStyle.Text);
        terminal.current?.print('    v0.3       ', TerminalTextStyle.Text);
        terminal.current?.print('08/07/2020        ', TerminalTextStyle.Text);
        terminal.current?.printLink(
          '@hideandcleanse',
          () => {
            window.open('https://twitter.com/hideandcleanse');
          },
          TerminalTextStyle.Text
        );
        terminal.current?.newline();
        terminal.current?.print('    v0.4       ', TerminalTextStyle.Text);
        terminal.current?.print('10/02/2020        ', TerminalTextStyle.Text);
        terminal.current?.printLink(
          'Jacob Rosenthal',
          () => {
            window.open('https://twitter.com/jacobrosenthal');
          },
          TerminalTextStyle.Text
        );
        terminal.current?.newline();
        terminal.current?.print('    v0.5       ', TerminalTextStyle.Text);
        terminal.current?.print('12/25/2020        ', TerminalTextStyle.Text);
        terminal.current?.printElement(
          <TextPreview
            text={'0xb05d95422bf8d5024f9c340e8f7bd696d67ee3a9'}
            focusedWidth={'100px'}
            unFocusedWidth={'100px'}
          />
        );
        terminal.current?.println('');

        terminal.current?.print('    v0.6 r1    ', TerminalTextStyle.Text);
        terminal.current?.print('05/22/2021        ', TerminalTextStyle.Text);
        terminal.current?.printLink(
          'Ansgar Dietrichs',
          () => {
            window.open('https://twitter.com/adietrichs');
          },
          TerminalTextStyle.Text
        );
        terminal.current?.newline();

        terminal.current?.print('    v0.6 r2    ', TerminalTextStyle.Text);
        terminal.current?.print('06/28/2021        ', TerminalTextStyle.Text);
        terminal.current?.printLink(
          '@orden_gg',
          () => {
            window.open('https://twitter.com/orden_gg');
          },
          TerminalTextStyle.Text
        );
        terminal.current?.newline();

        terminal.current?.print('    v0.6 r3    ', TerminalTextStyle.Text);
        terminal.current?.print('08/22/2021        ', TerminalTextStyle.Text);
        terminal.current?.printLink(
          '@dropswap_gg',
          () => {
            window.open('https://twitter.com/dropswap_gg');
          },
          TerminalTextStyle.Text
        );
        terminal.current?.newline();

        terminal.current?.print('    v0.6 r4    ', TerminalTextStyle.Text);
        terminal.current?.print('10/01/2021        ', TerminalTextStyle.Text);
        terminal.current?.printLink(
          '@orden_gg',
          () => {
            window.open('https://twitter.com/orden_gg');
          },
          TerminalTextStyle.Text
        );
        terminal.current?.newline();

        terminal.current?.print('    v0.6 r5    ', TerminalTextStyle.Text);
        terminal.current?.print('02/18/2022        ', TerminalTextStyle.Text);
        terminal.current?.printLink(
          '@d_fdao',
          () => {
            window.open('https://twitter.com/d_fdao');
          },
          TerminalTextStyle.Text
        );
        terminal.current?.print(' + ');
        terminal.current?.printLink(
          '@orden_gg',
          () => {
            window.open('https://twitter.com/orden_gg');
          },
          TerminalTextStyle.Text
        );
        terminal.current?.newline();
        terminal.current?.newline();
      }

      const accounts = getAccounts();
      terminal.current?.println(`Found ${accounts.length} accounts on this device.`);
      terminal.current?.println(``);

      if (accounts.length > 0) {
        terminal.current?.print('(a) ', TerminalTextStyle.Sub);
        terminal.current?.println('Login with existing account.');
      }

      terminal.current?.print('(n) ', TerminalTextStyle.Sub);
      terminal.current?.println(`Generate new burner wallet account.`);
      terminal.current?.print('(i) ', TerminalTextStyle.Sub);
      terminal.current?.println(`Import private key.`);
      terminal.current?.println(``);
      terminal.current?.println(`Select an option:`, TerminalTextStyle.Text);

      if (selectedAddress !== null) {
        terminal.current?.println(
          `Selecting account ${selectedAddress} from url...`,
          TerminalTextStyle.Green
        );

        // Search accounts backwards in case a player has used a private key more than once.
        // In that case, we want to take the most recently created account.
        const account = reverse(getAccounts()).find((a) => a.address === selectedAddress);
        if (!account) {
          terminal.current?.println('Unrecognized account found in url.', TerminalTextStyle.Red);
          return;
        }

        try {
          await ethConnection?.setAccount(account.privateKey);
          setStep(TerminalPromptStep.ACCOUNT_SET);
        } catch (e) {
          terminal.current?.println(
            'An unknown error occurred. please try again.',
            TerminalTextStyle.Red
          );
        }
      } else {
        const userInput = await terminal.current?.getInput();
        if (userInput === 'a' && accounts.length > 0) {
          setStep(TerminalPromptStep.DISPLAY_ACCOUNTS);
        } else if (userInput === 'n') {
          setStep(TerminalPromptStep.GENERATE_ACCOUNT);
        } else if (userInput === 'i') {
          setStep(TerminalPromptStep.IMPORT_ACCOUNT);
        } else {
          terminal.current?.println('Unrecognized input. Please try again.');
          await advanceStateFromCompatibilityPassed(terminal);
        }
      }
    },
    [isLobby, ethConnection, selectedAddress]
  );

  const advanceStateFromDisplayAccounts = useCallback(
    async (terminal: React.MutableRefObject<TerminalHandle | undefined>) => {
      terminal.current?.println(``);
      const accounts = getAccounts();
      for (let i = 0; i < accounts.length; i += 1) {
        terminal.current?.print(`(${i + 1}): `, TerminalTextStyle.Sub);
        terminal.current?.println(`${accounts[i].address}`);
      }
      terminal.current?.println(``);
      terminal.current?.println(`Select an account:`, TerminalTextStyle.Text);

      const selection = +((await terminal.current?.getInput()) || '');
      if (isNaN(selection) || selection > accounts.length) {
        terminal.current?.println('Unrecognized input. Please try again.');
        await advanceStateFromDisplayAccounts(terminal);
      } else {
        const account = accounts[selection - 1];
        try {
          await ethConnection?.setAccount(account.privateKey);
          setStep(TerminalPromptStep.ACCOUNT_SET);
        } catch (e) {
          terminal.current?.println(
            'An unknown error occurred. please try again.',
            TerminalTextStyle.Red
          );
        }
      }
    },
    [ethConnection]
  );

  const advanceStateFromGenerateAccount = useCallback(
    async (terminal: React.MutableRefObject<TerminalHandle | undefined>) => {
      const newWallet = Wallet.createRandom();
      const newSKey = newWallet.privateKey;
      const newAddr = address(newWallet.address);
      try {
        addAccount(newSKey);
        ethConnection?.setAccount(newSKey);

        terminal.current?.println(``);
        terminal.current?.print(`Created new burner wallet with address `);
        terminal.current?.printElement(<TextPreview text={newAddr} unFocusedWidth={'100px'} />);
        terminal.current?.println(``);
        terminal.current?.println('');
        terminal.current?.println(
          'Note: Burner wallets are stored in local storage.',
          TerminalTextStyle.Text
        );
        terminal.current?.println('They are relatively insecure and you should avoid ');
        terminal.current?.println('storing substantial funds in them.');
        terminal.current?.println('');
        terminal.current?.println('Also, clearing browser local storage/cache will render your');
        terminal.current?.println(
          'burner wallets inaccessible, unless you export your private keys.'
        );
        terminal.current?.println('');
        terminal.current?.println('Press any key to continue:', TerminalTextStyle.Text);

        await terminal.current?.getInput();
        setStep(TerminalPromptStep.ACCOUNT_SET);
      } catch (e) {
        terminal.current?.println(
          'An unknown error occurred. please try again.',
          TerminalTextStyle.Red
        );
      }
    },
    [ethConnection]
  );

  const advanceStateFromImportAccount = useCallback(
    async (terminal: React.MutableRefObject<TerminalHandle | undefined>) => {
      terminal.current?.println(
        'Enter the 0x-prefixed private key of the account you wish to import',
        TerminalTextStyle.Text
      );
      terminal.current?.println(
        "NOTE: THIS WILL STORE THE PRIVATE KEY IN YOUR BROWSER'S LOCAL STORAGE",
        TerminalTextStyle.Text
      );
      terminal.current?.println(
        'Local storage is relatively insecure. We recommend only importing accounts with zero-to-no funds.'
      );
      const newSKey = (await terminal.current?.getInput()) || '';
      try {
        const newAddr = address(utils.computeAddress(newSKey));

        addAccount(newSKey);

        ethConnection?.setAccount(newSKey);
        terminal.current?.println(`Imported account with address ${newAddr}.`);
        setStep(TerminalPromptStep.ACCOUNT_SET);
      } catch (e) {
        terminal.current?.println(
          'An unknown error occurred. please try again.',
          TerminalTextStyle.Red
        );
      }
    },
    [ethConnection]
  );

  const advanceStateFromAccountSet = useCallback(
    async (terminal: React.MutableRefObject<TerminalHandle | undefined>) => {
      try {
        const playerAddress = ethConnection?.getAddress();
        if (!playerAddress || !ethConnection) throw new Error('not logged in');

        const whitelist = await ethConnection.loadContract<DarkForest>(
          contractAddress,
          loadDiamondContract
        );
        const isWhitelisted = await whitelist.isWhitelisted(playerAddress);
        // TODO(#2329): isWhitelisted should just check the contractOwner
        const adminAddress = address(await whitelist.adminAddress());

        terminal.current?.println('');
        terminal.current?.print('Checking if whitelisted... ');

        // TODO(#2329): isWhitelisted should just check the contractOwner
        if (isWhitelisted || playerAddress === adminAddress) {
          terminal.current?.println('Player whitelisted.');
          terminal.current?.println('');
          terminal.current?.println(`Welcome, player ${playerAddress}.`);
          // TODO: Provide own env variable for this feature
          if (!isProd) {
            // in development, automatically get some ether from faucet
            const balance = weiToEth(await ethConnection?.loadBalance(playerAddress));
            if (balance === 0) {
              await requestDevFaucet(playerAddress);
            }
          }
          setStep(TerminalPromptStep.FETCHING_ETH_DATA);
        } else {
          setStep(TerminalPromptStep.ASKING_HAS_WHITELIST_KEY);
        }
      } catch (e) {
        console.error(`error connecting to whitelist: ${e}`);
        terminal.current?.println(
          'ERROR: Could not connect to whitelist contract. Please refresh and try again in a few minutes.',
          TerminalTextStyle.Red
        );
        setStep(TerminalPromptStep.TERMINATED);
      }
    },
    [ethConnection, isProd, contractAddress]
  );

  const advanceStateFromAskHasWhitelistKey = useCallback(
    async (terminal: React.MutableRefObject<TerminalHandle | undefined>) => {
      terminal.current?.print('Do you have a whitelist key?', TerminalTextStyle.Text);
      terminal.current?.println(' (y/n)');
      const userInput = await terminal.current?.getInput();
      if (userInput === 'y') {
        setStep(TerminalPromptStep.ASKING_WHITELIST_KEY);
      } else if (userInput === 'n') {
        setStep(TerminalPromptStep.ASKING_WAITLIST_EMAIL);
      } else {
        terminal.current?.println('Unrecognized input. Please try again.');
        await advanceStateFromAskHasWhitelistKey(terminal);
      }
    },
    []
  );

  const advanceStateFromAskWhitelistKey = useCallback(
    async (terminal: React.MutableRefObject<TerminalHandle | undefined>) => {
      const address = ethConnection?.getAddress();
      if (!address) throw new Error('not logged in');

      terminal.current?.println(
        'Please enter your invite key (XXXXXX-XXXXXX-XXXXXX-XXXXXX):',
        TerminalTextStyle.Sub
      );

      const key = (await terminal.current?.getInput()) || '';

      terminal.current?.print('Processing key... (this may take up to 30s)');
      terminal.current?.newline();

      if (!useZkWhitelist) {
        let registerConfirmationResponse = {} as RegisterConfirmationResponse;
        try {
          registerConfirmationResponse = await callRegisterAndWaitForConfirmation(
            key,
            address,
            terminal
          );
        } catch (e) {
          registerConfirmationResponse = {
            canRetry: true,
            errorMessage:
              'There was an error connecting to the whitelist server. Please try again later.',
          };
        }

        if (!registerConfirmationResponse.txHash) {
          terminal.current?.println(
            'ERROR: ' + registerConfirmationResponse.errorMessage,
            TerminalTextStyle.Red
          );
          if (registerConfirmationResponse.canRetry) {
            terminal.current?.println('Press any key to try again.');
            await terminal.current?.getInput();
            advanceStateFromAskWhitelistKey(terminal);
          } else {
            setStep(TerminalPromptStep.ASKING_WAITLIST_EMAIL);
          }
        } else {
          terminal.current?.print('Successfully joined game. ', TerminalTextStyle.Green);
          terminal.current?.print(`Welcome, player `);
          terminal.current?.println(address, TerminalTextStyle.Text);
          terminal.current?.print('Sent player $0.15 :) ', TerminalTextStyle.Blue);
          terminal.current?.printLink(
            '(View Transaction)',
            () => {
              window.open(`${BLOCK_EXPLORER_URL}/${registerConfirmationResponse.txHash}`);
            },
            TerminalTextStyle.Blue
          );
          terminal.current?.newline();
          setStep(TerminalPromptStep.ASKING_PLAYER_EMAIL);
        }
      } else {
        if (!ethConnection) throw new Error('no eth connection');
        const contractsAPI = await makeContractsAPI({ connection: ethConnection, contractAddress });

        const keyBigInt = bigIntFromKey(key);
        const snarkArgs = await getWhitelistArgs(keyBigInt, address, terminal);
        try {
          const ukReceipt = await contractsAPI.contract.useKey(
            snarkArgs[ZKArgIdx.PROOF_A],
            snarkArgs[ZKArgIdx.PROOF_B],
            snarkArgs[ZKArgIdx.PROOF_C],
            [...snarkArgs[ZKArgIdx.DATA]]
          );
          await ukReceipt.wait();
          terminal.current?.print('Successfully joined game. ', TerminalTextStyle.Green);
          terminal.current?.print(`Welcome, player `);
          terminal.current?.println(address, TerminalTextStyle.Text);
          terminal.current?.print('Sent player $0.15 :) ', TerminalTextStyle.Blue);
          terminal.current?.printLink(
            '(View Transaction)',
            () => {
              window.open(`${BLOCK_EXPLORER_URL}/${ukReceipt.hash}`);
            },
            TerminalTextStyle.Blue
          );
          terminal.current?.newline();
          setStep(TerminalPromptStep.ASKING_PLAYER_EMAIL);
        } catch (e) {
          const error = e.error;
          if (error instanceof Error) {
            const invalidKey = error.message.includes('invalid key');
            if (invalidKey) {
              terminal.current?.println(`ERROR: Key ${key} is not valid.`, TerminalTextStyle.Red);
              setStep(TerminalPromptStep.ASKING_WAITLIST_EMAIL);
            } else {
              terminal.current?.println(`ERROR: Something went wrong.`, TerminalTextStyle.Red);
              terminal.current?.println('Press any key to try again.');
              await terminal.current?.getInput();
              advanceStateFromAskWhitelistKey(terminal);
            }
          }
          console.error('Error whitelisting.');
        }
      }
    },
    [ethConnection, contractAddress, useZkWhitelist]
  );

  const advanceStateFromAskWaitlistEmail = useCallback(
    async (terminal: React.MutableRefObject<TerminalHandle | undefined>) => {
      terminal.current?.println(
        'Enter your email address to sign up for the whitelist.',
        TerminalTextStyle.Text
      );
      const email = (await terminal.current?.getInput()) || '';
      terminal.current?.print('Response pending... ');
      const response = await submitInterestedEmail(email);
      if (response === EmailResponse.Success) {
        terminal.current?.println('Email successfully recorded. ', TerminalTextStyle.Green);
        terminal.current?.println(
          'Keep an eye out for updates and invite keys in the next few weeks. Press ENTER to return to the homepage.',
          TerminalTextStyle.Sub
        );
        setStep(TerminalPromptStep.TERMINATED);
        (await await terminal.current?.getInput()) || '';
        history.push('/');
      } else if (response === EmailResponse.Invalid) {
        terminal.current?.println('Email invalid. Please try again.', TerminalTextStyle.Red);
      } else {
        terminal.current?.print('ERROR: Server error. ', TerminalTextStyle.Red);
        terminal.current?.print('Press ENTER to return to homepage.', TerminalTextStyle.Sub);
        (await await terminal.current?.getInput()) || '';
        setStep(TerminalPromptStep.TERMINATED);
        history.push('/');
      }
    },
    [history]
  );

  const advanceStateFromAskPlayerEmail = useCallback(
    async (terminal: React.MutableRefObject<TerminalHandle | undefined>) => {
      const address = ethConnection?.getAddress();
      if (!address) throw new Error('not logged in');

      terminal.current?.print('Enter your email address. ', TerminalTextStyle.Text);
      terminal.current?.println("We'll use this email address to notify you if you win a prize.");

      const email = (await terminal.current?.getInput()) || '';
      const response = await submitPlayerEmail(await ethConnection?.signMessageObject({ email }));

      if (response === EmailResponse.Success) {
        terminal.current?.println('Email successfully recorded.');
        setStep(TerminalPromptStep.FETCHING_ETH_DATA);
      } else if (response === EmailResponse.Invalid) {
        terminal.current?.println('Email invalid.', TerminalTextStyle.Red);
        advanceStateFromAskPlayerEmail(terminal);
      } else {
        terminal.current?.println('Error recording email.', TerminalTextStyle.Red);
        setStep(TerminalPromptStep.FETCHING_ETH_DATA);
      }
    },
    [ethConnection]
  );

  const advanceStateFromFetchingEthData = useCallback(
    async (terminal: React.MutableRefObject<TerminalHandle | undefined>) => {
      let newGameManager: GameManager;

      try {
        if (!ethConnection) throw new Error('no eth connection');

        newGameManager = await GameManager.create({
          connection: ethConnection,
          terminal,
          contractAddress,
        });
      } catch (e) {
        console.error(e);

        setStep(TerminalPromptStep.ERROR);

        terminal.current?.print(
          'Network under heavy load. Please refresh the page, and check ',
          TerminalTextStyle.Red
        );

        terminal.current?.printLink(
          'https://blockscout.com/poa/xdai/',
          () => {
            window.open('https://blockscout.com/poa/xdai/');
          },
          TerminalTextStyle.Red
        );

        terminal.current?.println('');

        return;
      }

      setGameManager(newGameManager);

      window.df = newGameManager;

      const newGameUIManager = await GameUIManager.create(newGameManager, terminal);

      window.ui = newGameUIManager;

      terminal.current?.newline();
      terminal.current?.println('Connected to Dark Forest Contract');
      gameUIManagerRef.current = newGameUIManager;

      if (!newGameManager.hasJoinedGame()) {
        setStep(TerminalPromptStep.NO_HOME_PLANET);
      } else {
        const browserHasData = !!newGameManager.getHomeCoords();
        if (!browserHasData) {
          terminal.current?.println(
            'ERROR: Home coords not found on this browser.',
            TerminalTextStyle.Red
          );
          setStep(TerminalPromptStep.ASK_ADD_ACCOUNT);
          return;
        }
        terminal.current?.println('Validated Local Data...');
        setStep(TerminalPromptStep.ALL_CHECKS_PASS);
      }
    },
    [ethConnection, contractAddress]
  );

  const advanceStateFromAskAddAccount = useCallback(
    async (terminal: React.MutableRefObject<TerminalHandle | undefined>) => {
      terminal.current?.println('Import account home coordinates? (y/n)', TerminalTextStyle.Text);
      terminal.current?.println(
        "If you're importing an account, make sure you know what you're doing."
      );
      const userInput = await terminal.current?.getInput();
      if (userInput === 'y') {
        setStep(TerminalPromptStep.ADD_ACCOUNT);
      } else if (userInput === 'n') {
        terminal.current?.println('Try using a different account and reload.');
        setStep(TerminalPromptStep.TERMINATED);
      } else {
        terminal.current?.println('Unrecognized input. Please try again.');
        await advanceStateFromAskAddAccount(terminal);
      }
    },
    []
  );

  const advanceStateFromAddAccount = useCallback(
    async (terminal: React.MutableRefObject<TerminalHandle | undefined>) => {
      const gameUIManager = gameUIManagerRef.current;

      if (gameUIManager) {
        try {
          terminal.current?.println('x: ', TerminalTextStyle.Blue);
          const x = parseInt((await terminal.current?.getInput()) || '');
          terminal.current?.println('y: ', TerminalTextStyle.Blue);
          const y = parseInt((await terminal.current?.getInput()) || '');
          if (
            Number.isNaN(x) ||
            Number.isNaN(y) ||
            Math.abs(x) > 2 ** 32 ||
            Math.abs(y) > 2 ** 32
          ) {
            throw 'Invalid home coordinates.';
          }
          if (await gameUIManager.addAccount({ x, y })) {
            terminal.current?.println('Successfully added account.');
            terminal.current?.println('Initializing game...');
            setStep(TerminalPromptStep.ALL_CHECKS_PASS);
          } else {
            throw 'Invalid home coordinates.';
          }
        } catch (e) {
          terminal.current?.println(`ERROR: ${e}`, TerminalTextStyle.Red);
          terminal.current?.println('Please try again.');
        }
      } else {
        terminal.current?.println('ERROR: Game UI Manager not found. Terminating session.');
        setStep(TerminalPromptStep.TERMINATED);
      }
    },
    []
  );

  const advanceStateFromNoHomePlanet = useCallback(
    async (terminal: React.MutableRefObject<TerminalHandle | undefined>) => {
      terminal.current?.println('Welcome to DARK FOREST.');

      const gameUIManager = gameUIManagerRef.current;
      if (!gameUIManager) {
        terminal.current?.println('ERROR: Game UI Manager not found. Terminating session.');
        setStep(TerminalPromptStep.TERMINATED);
        return;
      }

      if (Date.now() / 1000 > gameUIManager.getEndTimeSeconds()) {
        terminal.current?.println('ERROR: This game has ended. Terminating session.');
        setStep(TerminalPromptStep.TERMINATED);
        return;
      }

      terminal.current?.newline();

      terminal.current?.println('We collect a minimal set of statistics such as SNARK proving');
      terminal.current?.println('times and average transaction times across browsers, to help ');
      terminal.current?.println('us optimize performance and fix bugs. You can opt out of this');
      terminal.current?.println('in the Settings pane.');
      terminal.current?.println('');

      terminal.current?.newline();

      terminal.current?.println('Press ENTER to find a home planet. This may take up to 120s.');
      terminal.current?.println('This will consume a lot of CPU.');

      await terminal.current?.getInput();

      gameUIManager.getGameManager().on(GameManagerEvent.InitializedPlayer, () => {
        setTimeout(() => {
          terminal.current?.println('Initializing game...');
          setStep(TerminalPromptStep.ALL_CHECKS_PASS);
        });
      });

      gameUIManager
        .joinGame(async (e) => {
          console.error(e);

          terminal.current?.println('Error Joining Game:');
          terminal.current?.println('');
          terminal.current?.println(e.message, TerminalTextStyle.Red);
          terminal.current?.println('');
          terminal.current?.println('Press Enter to Try Again:');

          await terminal.current?.getInput();
          return true;
        })
        .catch((error: Error) => {
          terminal.current?.println(
            `[ERROR] An error occurred: ${error.toString().slice(0, 10000)}`,
            TerminalTextStyle.Red
          );
        });
    },
    []
  );

  const advanceStateFromAllChecksPass = useCallback(
    async (terminal: React.MutableRefObject<TerminalHandle | undefined>) => {
      terminal.current?.println('');
      terminal.current?.println('Press ENTER to begin');
      terminal.current?.println("Press 's' then ENTER to begin in SAFE MODE - plugins disabled");

      const input = await terminal.current?.getInput();

      if (input === 's') {
        const gameUIManager = gameUIManagerRef.current;
        gameUIManager?.getGameManager()?.setSafeMode(true);
      }

      setStep(TerminalPromptStep.COMPLETE);
      setInitRenderState(InitRenderState.COMPLETE);
      terminal.current?.clear();

      terminal.current?.println('Welcome to the Dark Forest.', TerminalTextStyle.Green);
      terminal.current?.println('');
      terminal.current?.println(
        "This is the Dark Forest interactive JavaScript terminal. Only use this if you know exactly what you're doing."
      );
      terminal.current?.println('');
      terminal.current?.println('Try running: df.getAccount()');
      terminal.current?.println('');
    },
    []
  );

  const advanceStateFromComplete = useCallback(
    async (terminal: React.MutableRefObject<TerminalHandle | undefined>) => {
      const input = (await terminal.current?.getInput()) || '';
      let res = '';
      try {
        // indrect eval call: http://perfectionkills.com/global-eval-what-are-the-options/
        res = (1, eval)(input);
        if (res !== undefined) {
          terminal.current?.println(res.toString(), TerminalTextStyle.Text);
        }
      } catch (e) {
        res = e.message;
        terminal.current?.println(`ERROR: ${res}`, TerminalTextStyle.Red);
      }
      advanceStateFromComplete(terminal);
    },
    []
  );

  const advanceStateFromError = useCallback(async () => {
    await neverResolves();
  }, []);

  const advanceState = useCallback(
    async (terminal: React.MutableRefObject<TerminalHandle | undefined>) => {
      if (step === TerminalPromptStep.NONE && ethConnection) {
        await advanceStateFromNone(terminal);
      } else if (step === TerminalPromptStep.COMPATIBILITY_CHECKS_PASSED) {
        await advanceStateFromCompatibilityPassed(terminal);
      } else if (step === TerminalPromptStep.DISPLAY_ACCOUNTS) {
        await advanceStateFromDisplayAccounts(terminal);
      } else if (step === TerminalPromptStep.GENERATE_ACCOUNT) {
        await advanceStateFromGenerateAccount(terminal);
      } else if (step === TerminalPromptStep.IMPORT_ACCOUNT) {
        await advanceStateFromImportAccount(terminal);
      } else if (step === TerminalPromptStep.ACCOUNT_SET) {
        await advanceStateFromAccountSet(terminal);
      } else if (step === TerminalPromptStep.ASKING_HAS_WHITELIST_KEY) {
        await advanceStateFromAskHasWhitelistKey(terminal);
      } else if (step === TerminalPromptStep.ASKING_WHITELIST_KEY) {
        await advanceStateFromAskWhitelistKey(terminal);
      } else if (step === TerminalPromptStep.ASKING_WAITLIST_EMAIL) {
        await advanceStateFromAskWaitlistEmail(terminal);
      } else if (step === TerminalPromptStep.ASKING_PLAYER_EMAIL) {
        await advanceStateFromAskPlayerEmail(terminal);
      } else if (step === TerminalPromptStep.FETCHING_ETH_DATA) {
        await advanceStateFromFetchingEthData(terminal);
      } else if (step === TerminalPromptStep.ASK_ADD_ACCOUNT) {
        await advanceStateFromAskAddAccount(terminal);
      } else if (step === TerminalPromptStep.ADD_ACCOUNT) {
        await advanceStateFromAddAccount(terminal);
      } else if (step === TerminalPromptStep.NO_HOME_PLANET) {
        await advanceStateFromNoHomePlanet(terminal);
      } else if (step === TerminalPromptStep.ALL_CHECKS_PASS) {
        await advanceStateFromAllChecksPass(terminal);
      } else if (step === TerminalPromptStep.COMPLETE) {
        await advanceStateFromComplete(terminal);
      } else if (step === TerminalPromptStep.ERROR) {
        await advanceStateFromError();
      }
    },
    [
      step,
      advanceStateFromAccountSet,
      advanceStateFromAddAccount,
      advanceStateFromAllChecksPass,
      advanceStateFromAskAddAccount,
      advanceStateFromAskHasWhitelistKey,
      advanceStateFromAskPlayerEmail,
      advanceStateFromAskWaitlistEmail,
      advanceStateFromAskWhitelistKey,
      advanceStateFromCompatibilityPassed,
      advanceStateFromComplete,
      advanceStateFromDisplayAccounts,
      advanceStateFromError,
      advanceStateFromFetchingEthData,
      advanceStateFromGenerateAccount,
      advanceStateFromImportAccount,
      advanceStateFromNoHomePlanet,
      advanceStateFromNone,
      ethConnection,
    ]
  );

  useEffect(() => {
    const uiEmitter = UIEmitter.getInstance();
    uiEmitter.emit(UIEmitterEvent.UIChange);
  }, [initRenderState]);

  useEffect(() => {
    const gameUiManager = gameUIManagerRef.current;
    if (!terminalVisible && gameUiManager) {
      const tutorialManager = TutorialManager.getInstance(gameUiManager);
      tutorialManager.acceptInput(TutorialState.Terminal);
    }
  }, [terminalVisible]);

  useEffect(() => {
    if (terminalHandle.current && topLevelContainer.current) {
      advanceState(terminalHandle);
    }
  }, [terminalHandle, topLevelContainer, advanceState]);

  return (
    <Wrapper initRender={initRenderState} terminalEnabled={terminalVisible}>
      <GameWindowWrapper initRender={initRenderState} terminalEnabled={terminalVisible}>
        {gameUIManagerRef.current && topLevelContainer.current && gameManager && (
          <TopLevelDivProvider value={topLevelContainer.current}>
            <UIManagerProvider value={gameUIManagerRef.current}>
              <GameWindowLayout
                terminalVisible={terminalVisible}
                setTerminalVisible={setTerminalVisible}
              />
            </UIManagerProvider>
          </TopLevelDivProvider>
        )}
        <TerminalToggler
          terminalEnabled={terminalVisible}
          setTerminalEnabled={setTerminalVisible}
        />
      </GameWindowWrapper>
      <TerminalWrapper initRender={initRenderState} terminalEnabled={terminalVisible}>
        <Terminal ref={terminalHandle} promptCharacter={'$'} />
      </TerminalWrapper>
      <div ref={topLevelContainer}></div>
    </Wrapper>
  );
}
Example #4
Source File: TransactionLogPane.tsx    From client with GNU General Public License v3.0 4 votes vote down vote up
function QueuedTransactionsTable({ transactions }: { transactions: Wrapper<TransactionRecord> }) {
  const uiManager = useUIManager();
  const visibleTransactions = reverse(values(transactions.value));

  const headers = ['Type', 'Hash', 'State', 'Planet', 'Updated', 'Actions'];
  const alignments: Array<'r' | 'c' | 'l'> = ['c', 'c', 'c', 'c', 'c', 'c'];

  const cancelTransaction = useCallback(
    (tx: Transaction) => {
      uiManager.getGameManager().getContractAPI().cancelTransaction(tx);
    },
    [uiManager]
  );

  const retryTransaction = useCallback(
    (tx: Transaction) => {
      uiManager.getGameManager().getContractAPI().submitTransaction(tx.intent);
    },
    [uiManager]
  );

  const prioritizeTransaction = useCallback(
    (tx: Transaction) => {
      uiManager.getGameManager().getContractAPI().prioritizeTransaction(tx);
    },
    [uiManager]
  );

  const queuedTransctions = useCallback(() => {
    return values(transactions.value).filter((tx) => ['Init', 'Prioritized'].includes(tx.state));
  }, [transactions]);

  const cancelAllQueuedTransactions = useCallback(() => {
    queuedTransctions().forEach((queuedTx) => {
      try {
        cancelTransaction(queuedTx);
      } catch {}
    });
  }, [cancelTransaction, queuedTransctions]);

  const columns = [
    (tx: Transaction) => (
      <Sub
        style={{
          display: 'block',
          whiteSpace: 'nowrap',
          textOverflow: 'ellipsis',
          width: '54px',
          overflow: 'hidden',
        }}
      >
        <TooltipTrigger name={TooltipName.Empty} extraContent={humanizeTransactionType(tx)}>
          {humanizeTransactionType(tx)}
        </TooltipTrigger>
      </Sub>
    ),
    (tx: Transaction) => (
      <div style={{ minWidth: '80px' }}>
        <TxLink tx={tx} />
      </div>
    ),
    (tx: Transaction) => <TransactionState tx={tx} />,
    (tx: Transaction) => {
      const planet = getPlanetFromTransaction(uiManager, tx);
      if (!planet) return <></>;

      return (
        <div
          style={{
            display: 'flex',
            flexDirection: 'row',
            alignItems: 'center',
            minWidth: '144px',
          }}
        >
          <PlanetThumb planet={planet} />
          <PlanetLink planet={planet}>
            <PlanetName>{getPlanetName(planet)}</PlanetName>
          </PlanetLink>
        </div>
      );
    },
    (tx: Transaction) => (
      <Sub style={{ display: 'block', minWidth: '80px' }}>
        <TimeAgo
          date={tx.lastUpdatedAt}
          formatter={(value: number, unit: TimeAgo.Unit, suffix: TimeAgo.Suffix) => {
            let newUnit = unit as string;

            if (unit === 'second' && value === 0) return 'just now';
            if (unit === 'second') newUnit = 's';
            if (unit === 'minute') newUnit = 'm';
            if (unit === 'hour') newUnit = 'h';
            if (unit === 'day') newUnit = 'd';

            return `${value}${newUnit} ${suffix}`;
          }}
        />
      </Sub>
    ),
    (tx: Transaction) => (
      <TransactionActions
        tx={tx}
        cancelTransaction={cancelTransaction}
        retryTransaction={retryTransaction}
        prioritizeTransaction={prioritizeTransaction}
      />
    ),
  ];

  const sortingFunctions = [
    (a: Transaction, b: Transaction): number =>
      a.intent.methodName.localeCompare(b.intent.methodName),
    (_a: Transaction, _b: Transaction): number => 0,
    (a: Transaction, b: Transaction): number => a.state.localeCompare(b.state),
    (a: Transaction, b: Transaction): number => {
      const planetA = getPlanetFromTransaction(uiManager, a);
      if (!planetA) return -1;

      const planetB = getPlanetFromTransaction(uiManager, b);
      if (!planetB) return 1;

      return getPlanetName(planetB).localeCompare(getPlanetName(planetA));
    },
    (a: Transaction, b: Transaction): number => b.lastUpdatedAt - a.lastUpdatedAt,
  ];

  return (
    <TableContainer>
      <div
        style={{
          width: '100%',
          display: 'flex',
          flexDirection: 'row',
          justifyContent: 'space-around',
        }}
      >
        <div
          style={{
            display: 'flex',
            flexDirection: 'row',
            alignItems: 'center',
          }}
        >
          {queuedTransctions().length !== 0 && (
            <Loader type='Circles' color={dfstyles.colors.subtext} height={23} width={23} />
          )}
          <Spacer width={8} />
          {queuedTransctions().length} queued transactions
          <Spacer width={8} />
          {queuedTransctions().length !== 0 && (
            <Btn onClick={cancelAllQueuedTransactions}>cancel all</Btn>
          )}
        </div>
      </div>
      <Spacer height={8} />
      <SortableTable
        paginated={true}
        rows={visibleTransactions}
        headers={headers}
        columns={columns}
        sortFunctions={sortingFunctions}
        alignments={alignments}
      />
    </TableContainer>
  );
}
Example #5
Source File: GetStatistics.ts    From next-basics with GNU General Public License v3.0 4 votes vote down vote up
export async function GetStatistics(
  categoryGroups: CategoryGroup[] = [],
  stories: Story[] = []
): Promise<any> {
  const storyList = getAllStoryListV2(categoryGroups, stories);
  const allCommonBricks = filter(storyList, (v) => v.type === "brick");
  const allCommonTemplates = reject(storyList, (v) => v.type === "brick");

  const commonBricksName = map(allCommonBricks, "id");
  const commonTemplatesName = map(allCommonTemplates, "id");
  const bootstrap = await AuthSdk.bootstrap();
  // 不统计iframe嵌套老站点的微应用
  const microAppsWithoutLegacy = reject(bootstrap.storyboards, [
    "app.legacy",
    "iframe",
  ]);
  const microAppsBricks = microAppsWithoutLegacy.map((microApp) => {
    const allBricksName = scanBricksInStoryboard(microApp, false);
    const allTemplatesName = scanTemplatesInStoryboard(microApp, false);
    const countAndSortAllBricks = reverse(
      sortBy(
        map(
          countBy(allBricksName, (item) => {
            return item;
          }),
          (v, k) => {
            return {
              id: k,
              count: v,
              type: "brick",
              isCommon: ifCommon(k, commonBricksName),
            };
          }
        ),
        "count"
      )
    );
    const countAndSortAllTemplates = reverse(
      sortBy(
        map(
          countBy(allTemplatesName, (item) => {
            return item;
          }),
          (v, k) => {
            return {
              id: k,
              count: v,
              type: "template",
              isCommon: ifCommon(k, commonTemplatesName),
            };
          }
        ),
        "count"
      )
    );
    microApp.countBricksAndTemplates = reverse(
      sortBy([...countAndSortAllBricks, ...countAndSortAllTemplates], "count")
    );
    microApp.countCommonBricksAndTemplates = filter(
      [...countAndSortAllBricks, ...countAndSortAllTemplates],
      "isCommon"
    ).length;

    const statisticsAll = [
      ...map(allBricksName, (v) => {
        return {
          type: "brick",
          id: v,
          isCommon: ifCommon(v, commonBricksName),
          app: microApp.app,
        };
      }),
      ...map(allTemplatesName, (v) => ({
        type: "template",
        id: v,
        isCommon: ifCommon(v, commonTemplatesName),
        app: microApp.app,
      })),
    ];
    microApp.statisticsAll = statisticsAll;
    microApp.commonUsedRate = getPercentage(
      filter(statisticsAll, (item) => item.isCommon).length /
        statisticsAll.length
    );
    return microApp;
  });
  const microAppsStatisticsMap = keyBy(microAppsBricks, "app.id");
  const microAppsSubMenu = {
    title: "微应用列表",
    menuItems: map(microAppsBricks, (item) => {
      return {
        text: item.app.name,
        to: `/developers/statistics/micro-app-statistics/${item.app.id}`,
      };
    }),
  };
  const microAppStatisticsRedirectTo = `${microAppsSubMenu.menuItems[0].to}`;
  const allMicroAppsBricksAndTemplate = flatten(
    map(microAppsBricks, "statisticsAll")
  );
  const allMicroAppsBricks = map(
    filter(allMicroAppsBricksAndTemplate, (item) => item.type === "brick"),
    "id"
  );
  const allMicroAppsTemplates = map(
    filter(allMicroAppsBricksAndTemplate, (item) => item.type === "template"),
    "id"
  );

  // 统计所有构件
  const allBricksAndTemplateGroupBy = groupBy(
    allMicroAppsBricksAndTemplate,
    (item) => item.type + "," + item.id
  );
  const countAllBricksAndTemplate = map(
    uniqBy(allMicroAppsBricksAndTemplate, (item) => item.type + "," + item.id),
    (v) => {
      return {
        id: v.id,
        type: v.type,
        isCommon: v.isCommon,
        count: allBricksAndTemplateGroupBy[v.type + "," + v.id].length,
        appList: map(
          uniqBy(
            allBricksAndTemplateGroupBy[v.type + "," + v.id],
            (v) => v.app.id
          ),
          "app"
        ),
      };
    }
  );

  // 排名前二十的构件
  let countCommonBricksUsed: any[] = reverse(
    sortBy(
      filter(
        map(
          countBy(allMicroAppsBricks, (item) => {
            return includes(commonBricksName, item) && item;
          }),
          (v, k) => {
            return {
              id: k,
              count: v,
            };
          }
        ),
        (item) => item.id !== "false" && item.id !== "div"
      ),
      "count"
    )
  ).slice(0, 20);
  countCommonBricksUsed = map(countCommonBricksUsed, (item) => {
    const found = find(allCommonBricks, ["id", item.id]);
    if (found) {
      item.author = found.subTitle;
      item.type = found.type;
      item.url = "/developers/brick-book/" + item.type + "/" + item.id;
    }
    return item;
  });
  // 排名前二十的模板
  let countCommonTemplatesUsed: any[] = reverse(
    sortBy(
      filter(
        map(
          countBy(allMicroAppsTemplates, (item) => {
            return includes(commonTemplatesName, item) && item;
          }),
          (v, k) => {
            return {
              id: k,
              count: v,
            };
          }
        ),
        (item) => item.id !== "false"
      ),
      "count"
    )
  ).slice(0, 20);
  countCommonTemplatesUsed = map(countCommonTemplatesUsed, (item) => {
    const found = find(allCommonTemplates, ["id", item.id]);
    item.author = found.subTitle;
    item.type = found.type;
    item.url = "/developers/brick-book/" + item.type + "/" + item.id;
    return item;
  });
  const topBricksAndTemplates = reverse(
    sortBy([...countCommonBricksUsed, ...countCommonTemplatesUsed], "count")
  ).slice(0, 20);
  const result = {
    microAppsCount: bootstrap.storyboards.length,
    microAppsWithoutLegacyCount: microAppsWithoutLegacy.length,
    iframeMicroApps:
      bootstrap.storyboards.length - microAppsWithoutLegacy.length,
    allBricksAndTemplatesCount:
      uniq(allMicroAppsBricks).length + uniq(allMicroAppsTemplates).length,
    commonBricksAndTemplatesCount:
      commonBricksName.length + allCommonTemplates.length,
    commonBricksAndTemplatesUsedRate: getPercentage(
      (filter(allMicroAppsBricks, (item) => {
        return includes(commonBricksName, item);
      }).length +
        filter(allMicroAppsTemplates, (item) => {
          return includes(commonTemplatesName, item);
        }).length) /
        (allMicroAppsBricks.length + allMicroAppsTemplates.length)
    ),
    microAppsPieChart: {
      title: "微应用总数:" + bootstrap.storyboards.length,
      data: {
        legendList: ["全新微应用", "iframe嵌套微应用"],
        seriesData: [
          {
            name: "全新微应用",
            value: microAppsWithoutLegacy.length,
          },
          {
            name: "iframe嵌套微应用",
            value: bootstrap.storyboards.length - microAppsWithoutLegacy.length,
          },
        ],
      },
    },
    microAppsSubMenu,
    microAppStatisticsRedirectTo,
    microAppsStatisticsMap,
    topBricksAndTemplates,
    countAllBricksAndTemplate,
    packageNames: uniq(
      compact(
        [...allMicroAppsBricks, ...allMicroAppsTemplates].map(
          (v) => v.split(".")?.[0]
        )
      )
    ),
  };
  return result;
}
Example #6
Source File: status.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
Status = () => {
  const query = routeInfoStore.useStore((s) => s.query);
  const dashboard = monitorStatusStore.useStore((s) => s.dashboard);
  const [isFetchingList] = useLoading(monitorStatusStore, ['getProjectDashboard']);
  const { getProjectDashboard, deleteMetric } = monitorStatusStore.effects;
  const { clearDashboardInfo } = monitorStatusStore.reducers;

  const { type = 'All' } = query || {};

  const [{ modalVisible, formData, filterType }, updater, update] = useUpdate({
    modalVisible: false,
    formData: null as MONITOR_STATUS.IMetricsBody | null,
    filterType: type,
  });

  useEffectOnce(() => {
    getProjectDashboard();
    return () => {
      clearDashboardInfo();
    };
  });

  const toggleModal = (_data?: MONITOR_STATUS.IMetricsBody) => {
    update({
      modalVisible: !modalVisible,
      formData: _data,
    });
  };

  const handleEdit = (_data: MONITOR_STATUS.IMetricsBody) => {
    toggleModal(_data);
  };

  const handleDelete = (id: string) => {
    Modal.confirm({
      title: i18n.t('msp:are you sure to delete this monitor?'),
      onOk: () => {
        deleteMetric(id).then(() => {
          getProjectDashboard();
        });
      },
    });
  };

  const handleDetailClick = (e: any, path: string) => {
    e.stopPropagation();
    goTo(path);
  };

  let data = [] as any[];
  if (dashboard && dashboard.metrics) {
    const curMetrics = dashboard.metrics;
    data = reverse(Object.keys(curMetrics)).map((id) => {
      return {
        id,
        ...(curMetrics[id] || {}),
      };
    });
  }
  let filterData = data;
  if (filterType !== 'All') {
    filterData = filter(filterData, (item) => item.status === filterType);
  }

  const typeMap = {
    All: {
      text: i18n.t('msp:All'),
      status: 'success',
      color: 'green',
    },
    Operational: {
      text: i18n.t('msp:Normal'),
      status: 'success',
      color: 'green',
    },
    'Major Outage': {
      text: i18n.t('msp:Downtime'),
      status: 'error',
      color: 'red',
    },
    Miss: {
      text: i18n.t('msp:No data'),
      status: 'default',
      color: 'grey',
    },
  };

  const defaultMap = {
    text: i18n.t('msp:No data'),
    color: 'grey',
  };

  const columns = [
    {
      title: i18n.t('msp:Index'),
      dataIndex: 'name',
    },
    {
      title: i18n.t('Status'),
      dataIndex: 'status',
      render: (status: string) => (
        <>
          <Badge status={(typeMap[status] || defaultMap).status} text={(typeMap[status] || defaultMap).text} />
        </>
      ),
    },
    {
      title: i18n.t('msp:Online rate'),
      dataIndex: 'uptime',
    },
    {
      title: `${i18n.t('msp:Downtime')}(${i18n.t('msp:last one hour')})`,
      dataIndex: 'downDuration',
    },
    {
      title: 'Apdex',
      dataIndex: 'apdex',
      render: (text: string) => text && floor(+text, 2),
    },
    {
      title: i18n.t('msp:Average response time'),
      dataIndex: 'latency',
      render: (text: string) => <span>{text} ms</span>,
    },
    {
      title: `${i18n.t('msp:response graph')}(${i18n.t('msp:last one hour')})`,
      dataIndex: 'chart',
      width: 160,
      render: (_text: string, record: MONITOR_STATUS.IMetricsBody) => {
        const { chart } = record;
        if (!chart) {
          return <div style={{ height: '58px' }} />;
        }
        return (
          <div>
            <StatusChart
              xAxisData={chart.time}
              data={chart.latency}
              style={{ width: '120px', height: '40px', minHeight: 0 }}
            />
            <ul className="status-list">
              {chart.status.map((item: string, i: number) => (
                <li key={String(i)} className={typeMap[item].color} />
              ))}
            </ul>
          </div>
        );
      },
    },
    {
      title: i18n.t('msp:Root cause analysis'),
      dataIndex: 'requestId',
      width: 90,
      hidden: true,
      render: (requestId: string) =>
        !!requestId && (
          <span
            className="reason-analysis-span"
            onClick={(e) => {
              handleDetailClick(e, resolvePath(`../error/request-detail/${requestId}`));
            }}
          >
            {i18n.t('msp:details')}
          </span>
        ),
    },
  ];

  const actions: IActions<MONITOR_STATUS.IMetricsBody> = {
    render: (record: MONITOR_STATUS.IMetricsBody) => renderMenu(record),
  };

  const renderMenu = (record: MONITOR_STATUS.IMetricsBody) => {
    const { editMonitor, deleteMonitor } = {
      editMonitor: {
        title: i18n.t('Edit'),
        onClick: () => handleEdit(record),
      },
      deleteMonitor: {
        title: i18n.t('Delete'),
        onClick: () => handleDelete(record.id),
      },
    };

    return [editMonitor, deleteMonitor];
  };
  let hasDown: { text: string; type: 'info' | 'error' | 'success' } = {
    text: 'No data',
    type: 'info',
  };
  if (dashboard.downCount !== undefined) {
    hasDown =
      dashboard.downCount > 0
        ? { text: `${dashboard.downCount} Down`, type: 'error' }
        : { text: 'All up', type: 'success' };
  }

  return (
    <div className="project-status-page">
      <TopButtonGroup>
        <Button className="add-button" type="primary" onClick={() => toggleModal()}>
          {i18n.t('msp:Add Monitoring')}
        </Button>
      </TopButtonGroup>
      <ErdaAlert className="erda-alert mb-2" message={hasDown.text} type={hasDown.type} closeable={false} />
      <RadioTabs
        className="mb-2"
        onChange={updater.filterType}
        defaultValue={filterType}
        options={map(typeMap, (item, key) => ({ value: key, label: item.text }))}
      />
      <AddModal
        modalVisible={modalVisible}
        toggleModal={toggleModal}
        formData={formData}
        afterSubmit={getProjectDashboard}
      />
      <Table
        rowKey="id"
        onRow={(record) => {
          return {
            onClick: () => goTo(`./${record.id}`),
          };
        }}
        actions={actions}
        loading={isFetchingList}
        columns={columns}
        dataSource={filterData}
        onChange={() => getProjectDashboard()}
      />
    </div>
  );
}
Example #7
Source File: perm-table.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
PermOperation = (props: IOperationProps) => {
  const { index, curDataKey, wholeData, curColumnKey, dataItem, deleteData = noop, editData = noop } = props;
  const keyArr = curDataKey.split('.');
  const curKey = keyArr.pop();
  const [editFormVis, setEditFormVis] = React.useState(false);
  const editFormRef = React.useRef(null as any);
  const [addFormVis, setAddFormVis] = React.useState(false);
  const addFormRef = React.useRef(null as any);

  const handleAddSubmit = (form: FormInstance) => {
    form.validateFields().then((values: { addStr: string }) => {
      const { addStr = '' } = values;
      const permArr = addStr.split('>');
      const newData = reduce(
        reverse(permArr),
        (sumObj, item, idx) => {
          let reObj = { ...sumObj };
          if (idx === 0) {
            const actionStrArr = item.split('');
            const actions = actionStrArr
              .slice(1, actionStrArr.length - 1)
              .join('')
              .split(',');
            const actionObj = {};
            map(actions, (aItem) => {
              const [aKey, aName] = aItem.split(':');
              actionObj[aKey] = { ...actionData, name: aName };
            });
            reObj = { ...reObj, ...actionObj };
            return reObj;
          } else {
            const [key, name] = item.split(':');
            reObj = { [key]: { name, ...reObj } };
          }
          return reObj;
        },
        {},
      );
      editData({ keyPath: keyArr.join('.'), preKey: curKey, subData: newData });
      setAddFormVis(false);
    });
  };

  const addFieldList = [
    {
      label: '添加项',
      name: 'addStr',
      className: 'mb-2',
      rules: [
        {
          validator: (rule: any, v: string, callback: Function) => {
            if (v) {
              const strArr = v.split('>');
              let errorTip = '';
              for (let i = 0, len = strArr.length; i < len; i++) {
                if (i !== strArr.length - 1) {
                  errorTip = regPermKey(strArr[i]);
                } else if (!(strArr[i].startsWith('[') && strArr[i].endsWith(']'))) {
                  errorTip = addTip;
                } else {
                  const actionStrArr = strArr[i].split('');
                  const actions = actionStrArr
                    .slice(1, actionStrArr.length - 1)
                    .join('')
                    .split(',');
                  for (let j = 0, len2 = actions.length; j < len2; j++) {
                    !errorTip && (errorTip = regPermKey(actions[j]));
                  }
                }
                if (errorTip) break;
              }
              if (errorTip) {
                return callback(errorTip);
              }
            }
            callback();
          },
        },
      ],
      extraProps: { ...fieldLayout },
      itemProps: {
        autoComplete: 'off',
        placeholder: `${addTip}`,
      },
    },
    {
      label: '',
      className: 'mb-1',
      extraProps: { ...fieldLayout },
      getComp: ({ form }: { form: FormInstance }) => (
        <div className="flex flex-wrap justify-center items-center">
          <Button type="primary" onClick={() => handleAddSubmit(form)}>
            添加
          </Button>
        </div>
      ),
    },
  ];

  const handleEditSubmit = (form: FormInstance) => {
    form.validateFields().then((values: any) => {
      editData({ ...values, keyPath: keyArr.join('.'), preKey: curKey });
      setEditFormVis(false);
    });
  };

  const editFieldList = [
    {
      label: 'key',
      name: 'key',
      className: 'mb-2',
      initialValue: dataItem.key,
      rules: [
        { ...regRules.commonStr },
        {
          validator: (rule: any, v: string, callback: Function) => {
            const dKey = keyArr.concat(v).join('.');
            if (v && v !== curKey && get(wholeData, dKey)) {
              return callback('存在相同的key,请重新填写');
            }
            callback();
          },
        },
      ],
      itemProps: {
        disabled: index === 0,
        autoComplete: 'off',
      },
      extraProps: { ...fieldLayout },
    },
    {
      label: '名称',
      name: 'name',
      className: 'mb-2',
      initialValue: dataItem.name,
      itemProps: {
        autoComplete: 'off',
      },
      extraProps: { ...fieldLayout },
    },
    {
      label: '',
      className: 'mb-1',
      extraProps: { ...fieldLayout },
      getComp: ({ form }: { form: FormInstance }) => (
        <div className="flex flex-wrap justify-center items-center">
          <Button type="primary" onClick={() => handleEditSubmit(form)}>
            保存
          </Button>
        </div>
      ),
    },
  ];

  const deleteOp =
    index !== 0 ? (
      <DeleteConfirm
        key="delete"
        title="确认删除?"
        secondTitle={`${curColumnKey !== 'action' ? `删除 (${dataItem.name}), 将会删除其所有关联子权限` : ''}`}
        onConfirm={() => {
          deleteData(curDataKey);
        }}
      >
        <CustomIcon className="perm-op-item" type="shanchu" />
      </DeleteConfirm>
    ) : null;

  const addOp =
    curColumnKey !== 'action' ? (
      <Popover
        key="add"
        placement="right"
        overlayClassName="dice-perm-edit-form add"
        title="添加"
        content={<RenderForm ref={addFormRef} list={addFieldList} />}
        visible={addFormVis}
        onVisibleChange={setAddFormVis}
      >
        <CustomIcon type="cir-add" className="perm-op-item" />
      </Popover>
    ) : null;

  const editOp = (
    <Popover
      overlayClassName="dice-perm-edit-form edit"
      content={<RenderForm ref={editFormRef} list={editFieldList} layout="vertical" />}
      placement="right"
      title="编辑"
      key="edit"
      visible={editFormVis}
      onVisibleChange={setEditFormVis}
    >
      <CustomIcon className="perm-op-item" type="setting" />
    </Popover>
  );
  const optArr = [deleteOp, editOp, addOp];
  return <div className="perm-operation-box">{optArr}</div>;
}
Example #8
Source File: index.ts    From TidGi-Desktop with Mozilla Public License 2.0 4 votes vote down vote up
public async buildContextMenuAndPopup(
    template: MenuItemConstructorOptions[] | IpcSafeMenuItem[],
    info: IOnContextMenuInfo,
    webContentsOrWindowName: WindowNames | WebContents = WindowNames.main,
  ): Promise<void> {
    let webContents: WebContents;
    if (typeof webContentsOrWindowName === 'string') {
      const windowToPopMenu = this.windowService.get(webContentsOrWindowName);
      const webContentsOfWindowToPopMenu = windowToPopMenu?.webContents;
      if (windowToPopMenu === undefined || webContentsOfWindowToPopMenu === undefined) {
        return;
      }
      webContents = webContentsOfWindowToPopMenu;
    } else {
      webContents = webContentsOrWindowName;
    }
    const sidebar = await this.preferenceService.get('sidebar');
    const contextMenuBuilder = new ContextMenuBuilder(webContents);
    const menu = contextMenuBuilder.buildMenuForElement(info);
    // show workspace menu to manipulate workspaces if sidebar is not open
    if (!sidebar) {
      menu.append(new MenuItem({ type: 'separator' }));
      const workspaces = await this.workspaceService.getWorkspacesAsList();
      const services = {
        auth: this.authService,
        context: this.contextService,
        git: this.gitService,
        native: this.nativeService,
        view: this.viewService,
        wiki: this.wikiService,
        wikiGitWorkspace: this.wikiGitWorkspaceService,
        window: this.windowService,
        workspace: this.workspaceService,
        workspaceView: this.workspaceViewService,
      };
      menu.append(
        new MenuItem({
          label: i18next.t('ContextMenu.OpenCommandPalette'),
          enabled: workspaces.length > 0,
          click: () => {
            void this.wikiService.requestWikiSendActionMessage('open-command-palette');
          },
        }),
      );
      menu.append(
        new MenuItem({
          label: i18next.t('Menu.Workspaces'),
          submenu: [
            ...(await Promise.all(
              workspaces.map(async (workspace) => {
                const workspaceContextMenuTemplate = await getWorkspaceMenuTemplate(workspace, i18next.t.bind(i18next), services);
                return {
                  label: workspace.name,
                  submenu: workspaceContextMenuTemplate,
                };
              }),
            )),
            {
              label: i18next.t('WorkspaceSelector.Add'),
              click: async () => await this.windowService.open(WindowNames.addWorkspace),
            },
          ],
        }),
      );
      menu.append(
        new MenuItem({
          label: i18next.t('WorkspaceSelector.OpenWorkspaceMenuName'),
          submenu: workspaces.map((workspace) => ({
            label: i18next.t('WorkspaceSelector.OpenWorkspaceTagTiddler', { tagName: workspace.tagName ?? workspace.name }),
            click: async () => {
              await openWorkspaceTagTiddler(workspace, services);
            },
          })),
        }),
      );
    }
    menu.append(
      new MenuItem({
        label: i18next.t('ContextMenu.Back'),
        enabled: webContents.canGoBack(),
        click: () => {
          webContents.goBack();
        },
      }),
    );
    menu.append(
      new MenuItem({
        label: i18next.t('ContextMenu.Forward'),
        enabled: webContents.canGoForward(),
        click: () => {
          webContents.goForward();
        },
      }),
    );
    menu.append(
      new MenuItem({
        label: i18next.t('ContextMenu.Reload'),
        click: () => {
          webContents.reload();
        },
      }),
    );
    menu.append(
      new MenuItem({
        label: i18next.t('ContextMenu.RestartService'),
        click: async () => {
          const workspace = await this.workspaceService.getActiveWorkspace();
          if (workspace !== undefined) {
            await this.workspaceViewService.restartWorkspaceViewService(workspace.id);
            await this.workspaceViewService.realignActiveWorkspace(workspace.id);
          }
        },
      }),
    );
    menu.append(
      new MenuItem({
        label: sidebar ? i18next.t('Preference.HideSideBar') : i18next.t('Preference.ShowSideBar'),
        click: async () => {
          await this.preferenceService.set('sidebar', !sidebar);
          await this.workspaceViewService.realignActiveWorkspace();
        },
      }),
    );
    menu.append(new MenuItem({ type: 'separator' }));
    menu.append(
      new MenuItem({
        label: i18next.t('ContextMenu.More'),
        submenu: [
          {
            label: i18next.t('ContextMenu.Preferences'),
            click: async () => await this.windowService.open(WindowNames.preferences),
          },
          { type: 'separator' },
          {
            label: i18next.t('ContextMenu.About'),
            click: async () => await this.windowService.open(WindowNames.about),
          },
          {
            label: i18next.t('ContextMenu.TidGiSupport'),
            click: async () => await shell.openExternal('https://github.com/tiddly-gittly/TidGi-Desktop/issues/new/choose'),
          },
          {
            label: i18next.t('ContextMenu.TidGiWebsite'),
            click: async () => await shell.openExternal('https://github.com/tiddly-gittly/TidGi-Desktop'),
          },
          { type: 'separator' },
          {
            label: i18next.t('ContextMenu.Quit'),
            click: () => app.quit(),
          },
        ],
      }),
    );

    // add custom menu items
    if (template !== undefined && Array.isArray(template) && template.length > 0) {
      // if our menu item config is pass from the renderer process, we reconstruct callback from the ipc.on channel id.
      const menuItems = (typeof template?.[0]?.click === 'string'
        ? mainMenuItemProxy(template as IpcSafeMenuItem[], webContents)
        : template) as unknown as MenuItemConstructorOptions[];
      menu.insert(0, new MenuItem({ type: 'separator' }));
      // we are going to prepend items, so inverse first, so order will remain
      reverse(menuItems)
        .map((menuItem) => new MenuItem(menuItem))
        .forEach((menuItem) => menu.insert(0, menuItem));
    }

    menu.popup();
  }