react-i18next#Trans TypeScript Examples

The following examples show how to use react-i18next#Trans. 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: index.tsx    From genshin-optimizer with MIT License 8 votes vote down vote up
export default function ArtifactInfoDisplay() {
  const { t } = useTranslation("artifact")
  return <Grid container spacing={1} >
    <Grid item xs={12} lg={5} xl={4}>
      <ImgFullwidth src={artifactcard} />
    </Grid>
    <Grid item xs={12} lg={7} xl={8}>
      <Trans t={t} i18nKey="info.section1">
        <Typography variant="h5">Substat rolls</Typography>
        <Typography gutterBottom>The <b>number of rolls</b> a substat has is shown to the left of the substat. As the number gets higher, the substat is more colorful:<Colors />.</Typography>

        <Typography variant="h5">Substat Roll Value</Typography>
        <Typography gutterBottom>The Roll Value(RV) of an subtat is a percentage of the current value over the highest potential 5<Stars stars={1} /> value. From the Image, the maximum roll value of CRIT DMG is 7.8%. In RV: <b>5.8/7.8 = 69.2%.</b></Typography>

        <Typography variant="h5">Current Roll Value vs. Maximum Roll Value</Typography>
        <Typography gutterBottom>When a 5<Stars stars={1} /> have 9(4+5) total rolls, with each of the rolls having the highest value, that is defined as a 900% RV artifact. However, most of the artifacts are not this lucky. The <b>Current RV</b> of an artifact is a percentage over that 100% artifact. The <b>Maximum RV</b> is the maximum possible RV an artifact can achieve, if the remaining artifact rolls from upgrades are the hightest possible value.</Typography>

        <Typography variant="h5">Locking an artifact</Typography>
        <Typography>By locking an artifact <FontAwesomeIcon icon={faBan} />, This artifact will not be picked up by the build generator for optimization. An equipped artifact is locked by default.</Typography>
      </Trans>
    </Grid>
    <Grid item xs={12} lg={6} xl={7} >
      <Trans t={t} i18nKey="info.section2">
        <Typography variant="h5">Artifact Editor</Typography>
        <Typography gutterBottom>A fully featured artifact editor, that can accept any 3<Stars stars={1} /> to 5<Stars stars={1} /> Artifact. When a substat is inputted, it can calculate the exact roll values. It will also make sure that you have the correct number of rolls in the artifact according to the level, along with other metrics of validation.</Typography>

        <Typography variant="h5">Scan screenshots</Typography>
        <Typography gutterBottom>Manual input is not your cup of tea? You can scan in your artifacts with screenshots! On the Artifact Editor, click the <SqBadge color="info">Show Me How!</SqBadge> button to learn more.</Typography>

        <Typography variant="h6">Automatic Artifact Scanner</Typography>
        <Typography gutterBottom>If you are playing Genshin on PC, you can download a tool that automatically scans all your artifacts for you, and you can then import that data in <FontAwesomeIcon icon={faCog} /> Database. <Link component={RouterLink} to="/scanner">Click here</Link> for a list of scanners that are compatible with GO.</Typography>

        <Typography variant="h5">Duplicate/Upgrade artifact detection</Typography>
        <Typography>Did you know GO can detect if you are adding a <b>duplicate</b> artifact that exists in the system? It can also detect if the current artifact in editor is an <b>upgrade</b> of an existing artifact as well. Once a duplicate/upgrade is detected, a preview will allow you to compare the two artifacts in question(See Image).</Typography>
      </Trans>
    </Grid>
    <Grid item xs={12} lg={6} xl={5}>
      <ImgFullwidth src={artifacteditor} />
    </Grid>
    <Grid item xs={12} lg={7} xl={6}>
      <ImgFullwidth src={artifactfilter} />
    </Grid>
    <Grid item xs={12} lg={5} xl={6}>
      <Trans t={t} i18nKey="info.section3">
        <Typography variant="h5">Artifact Inventory</Typography>
        <Typography gutterBottom>All your artifacts that you've added to GO is displayed here. The filters here allow you to further refine your view of your artifacts. </Typography>
        <Typography variant="h5">Example: Finding Fodder Artifacts</Typography>
        <Typography>By utilizing the artifact filter, and the artifact RV, you can quickly find artifacts to feed as food.</Typography>
        <Typography>In this example, the filters are set thusly: </Typography>
        <Typography component="div" >
          <ul>
            <li>Limit level to 0-8.</li>
            <li>Unlocked artifacts in Inventory.</li>
            <li>Removing the contribution of flat HP, flat DEF and Energy Recharge to RV calculations.</li>
            <li>Sorted by Ascending Max Roll Value.</li>
          </ul>
        </Typography>
        <Typography>This will filter the artifact Inventory by the lowest RV artifacts, for desired substats.</Typography>
      </Trans>
    </Grid>
  </Grid>
}
Example #2
Source File: Welcome.tsx    From NewWorldMinimap with MIT License 6 votes vote down vote up
export default function Welcome() {
    const { classes } = useStyles();
    const { t } = useTranslation();

    function tryForceMap() {
        backgroundController.debug_setGameRunning(true);
    }

    return <div className={classes.root}>
        <img className={classes.background} src='/img/map-crop.svg' />
        <h2>{t('welcome.title')}</h2>
        <p>{t('welcome.primary')}</p>
        <p>{t('welcome.settings')}</p>
        <p>
            <Trans i18nKey='welcome.discord' tOptions={{ discord: discordUrl }}>
                Join <a className={classes.discordLink} href={discordUrl} target='_blank'>discord</a>!
            </Trans>
        </p>
        {!NWMM_APP_BUILD_PRODUCTION && <>
            <hr />
            <p>{t('welcome.devBuild')}</p>
            <Button onClick={tryForceMap}>{t('welcome.forceMap')}</Button>
        </>}
        <div className={classes.gap} />
        <LanguagePicker />
    </div>;
}
Example #3
Source File: PeriodSwitch.tsx    From nosgestesclimat-site with MIT License 6 votes vote down vote up
export default function PeriodSwitch() {
	const dispatch = useDispatch()
	const currentUnit = useSelector(targetUnitSelector)

	const units = ['€/mois', '€/an']
	return (
		<span id="PeriodSwitch">
			<span className="base ui__ small radio toggle">
				{units.map(unit => (
					<label key={unit}>
						<input
							name="defaultUnit"
							type="radio"
							value={unit}
							onChange={() => dispatch(updateUnit(unit))}
							checked={currentUnit === unit}
						/>
						<span>
							<Trans>{unit}</Trans>
						</span>
					</label>
				))}
			</span>
		</span>
	)
}
Example #4
Source File: About.tsx    From tobira with Apache License 2.0 6 votes vote down vote up
About: React.FC = () => {
    const { t } = useTranslation();

    return (
        <div css={{ margin: "0 auto", maxWidth: 600 }}>
            <PageTitle title={t("about-tobira.title")} />
            <p css={{ margin: "16px 0" }}>
                <Trans i18nKey="about-tobira.body">
                    Description.
                    <a href="https://github.com/elan-ev/tobira">GitHub repo</a>
                </Trans>
            </p>
            <h2>{t("version-information")}</h2>
            <code>tobira {CONFIG.version}</code>
        </div>
    );
}
Example #5
Source File: SortByButton.tsx    From genshin-optimizer with MIT License 6 votes vote down vote up
// Assumes that all the sortKeys has corresponding translations in ui.json sortMap
export default function SortByButton({ sortKeys, value, onChange, ascending, onChangeAsc, ...props }: SortByButtonProps) {
  const { t } = useTranslation("ui")
  return <Box display="flex" alignItems="center" gap={1}>
    <Trans t={t} i18nKey={t("sortBy") as any}>Sort by: </Trans>
    <ButtonGroup {...props} >
      <DropdownButton title={<Trans t={t} i18nKey={t(`sortMap.${value}`) as any}>{{ value: t(`sortMap.${value}`) }}</Trans>}>
        {sortKeys.map(key =>
          <MenuItem key={key} selected={value === key} disabled={value === key} onClick={() => onChange(key)}>{t(`sortMap.${key}`) as any}</MenuItem>)}
      </DropdownButton>
      <Button onClick={() => onChangeAsc(!ascending)} startIcon={<FontAwesomeIcon icon={ascending ? faSortAmountDownAlt : faSortAmountUp} className="fa-fw" />}>
        {ascending ? <Trans t={t} i18nKey="ascending" >Ascending</Trans> : <Trans t={t} i18nKey="descending" >Descending</Trans>}
      </Button>
    </ButtonGroup>
  </Box>
}
Example #6
Source File: index.test.tsx    From oasis-wallet-web with Apache License 2.0 6 votes vote down vote up
jest.mock('react-i18next', () => ({
  Trans: (({ i18nKey }) => <>{i18nKey}</>) as TransType,
  useTranslation: () => {
    return {
      t: str => str,
      i18n: {
        changeLanguage: () => new Promise(() => {}),
      },
    } as UseTranslationResponse<'translation'>
  },
}))
Example #7
Source File: WalletForm.tsx    From subscan-multisig-react with Apache License 2.0 6 votes vote down vote up
function confirmToAdd(accountExist: KeyringAddress, confirm: () => void) {
  return Modal.confirm({
    cancelText: <Trans>cancel</Trans>,
    okText: <Trans>confirm</Trans>,
    onOk: (close) => {
      if (confirm) {
        confirm();
      }

      close();
    },
    maskClosable: false,
    closeIcon: false,
    content: (
      <div>
        <p className="mb-4">
          <Trans>
            There is an account configured by the same member and threshold. Confirm to replace it with a new account?
          </Trans>
        </p>
        <Descriptions column={1} size="small" title={<Trans>Origin Account</Trans>}>
          <Descriptions.Item label={<Trans>name</Trans>}>{accountExist.meta.name}</Descriptions.Item>
          <Descriptions.Item label={<Trans>threshold</Trans>}>
            {/*  eslint-disable-next-line @typescript-eslint/no-explicit-any */}
            {(accountExist.meta as any).threshold}
          </Descriptions.Item>
          <Descriptions.Item label={<Trans>Create Time</Trans>}>
            {format(accountExist.meta.whenCreated || 0, 'yyyy-MM-dd hh:mm:ss')}
          </Descriptions.Item>
        </Descriptions>
      </div>
    ),
  });
}
Example #8
Source File: NewUserMessage.tsx    From TidGi-Desktop with Mozilla Public License 2.0 6 votes vote down vote up
export function NewUserMessage(props: IProps): JSX.Element {
  const { t } = useTranslation();
  return (
    <AddWorkspaceGuideInfoContainer onClick={async () => await window.service.window.open(WindowNames.addWorkspace)}>
      {props.sidebar ? (
        <>
          <Arrow image={props.themeSource === 'dark' ? arrowWhite : arrowBlack} />
          <TipWithSidebar id="new-user-tip">
            <Trans t={t} i18nKey="AddWorkspace.MainPageTipWithSidebar">
              <Tip2Text>Click</Tip2Text>
              <Avatar>+</Avatar>
              <Tip2Text>to get started!</Tip2Text>
            </Trans>
          </TipWithSidebar>
        </>
      ) : (
        <TipWithoutSidebar id="new-user-tip">
          <Tip2Text>
            <Trans t={t} i18nKey="AddWorkspace.MainPageTipWithoutSidebar">
              <span>Click </span>
              <strong>Workspaces &gt; Add Workspace</strong>
              <span>Or </span>
              <strong>Click Here</strong>
              <span> to get started!</span>
            </Trans>
          </Tip2Text>
        </TipWithoutSidebar>
      )}
    </AddWorkspaceGuideInfoContainer>
  );
}
Example #9
Source File: LanguagePicker.tsx    From fishbowl with MIT License 5 votes vote down vote up
LanguagePicker = () => {
  const classes = useStyles()
  const { t, i18n } = useTranslation()

  return (
    <Box textAlign="center">
      <ul className={classes.root}>
        {SupportedLanguages.map((code) => (
          <li key={code}>
            <Chip
              label={languageNameFromCode(code)}
              variant="outlined"
              color={code === i18n.language ? "primary" : "default"}
              onClick={() => {
                i18n.changeLanguage(code)
              }}
            />
          </li>
        ))}
        <li>
          <IconButton
            component={Link}
            size="small"
            href="https://github.com/avimoondra/fishbowl#Localization"
            target="_blank"
          >
            <AddCircleOutlineIcon />
          </IconButton>
        </li>
      </ul>
      <Typography color="textSecondary" variant="caption">
        <Trans t={t} i18nKey="languagesPoweredBy">
          {"Powered by our friends at "}
          <Link href="https://locize.com" target="_blank">
            {{ serviceName: "locize" }}
          </Link>
          .
        </Trans>
      </Typography>
    </Box>
  )
}
Example #10
Source File: disclamer-btn.component.tsx    From cwa-quick-test-frontend with Apache License 2.0 5 votes vote down vote up
DisclamerButton = (props: any) => {

    const { t } = useTranslation();
    const [show, setShow] = React.useState(false);

    React.useEffect(() => {
        setShow(props.firstTimeShow);
        props.onInit();
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [])

    return (
        <>
            <Image
                src={SpeechBubbleImage}
                className='speech-bubble'
                onClick={() => { setShow(true) }}
            />

            <Modal
                contentClassName='data-modal'
                show={show}
                backdrop={true}
                onHide={() => { setShow(false) }}
                keyboard={false}
                centered
            >
                <Modal.Header id='data-header' className='pb-0' >
                    <Row>
                        <Col >
                            <Card.Title className='m-0 jcc-xs-jcfs-md' as={'h2'} >
                                {t('translation:disclaimer-title')}</Card.Title>
                        </Col>
                    </Row>
                </Modal.Header>

                <Modal.Body className='bg-light py-0'>
                    <hr />
                    <h5 className='disclaimer-text'>
                        <Trans>{props.disclaimerText}</Trans>
                    </h5>

                    <hr />

                    <FormGroupConsentCkb controlId='formDoNotShowCheckbox' title={t('translation:disclaimer-do-not-show')}
                        onChange={(evt: any) => props.onCheckChange(evt.currentTarget.checked)}
                        type='checkbox'
                        checked={props.checked}
                    />
                </Modal.Body>

                <Modal.Footer id='data-footer'>
                    <Container className='p-0'>
                        <Row className='justify-content-end'>
                            <Col xs='6' className='p-0'>
                                <Button
                                    className='py-0'
                                    block
                                    onClick={() => { setShow(false) }}
                                >
                                    {t('translation:ok')}
                                </Button>
                            </Col>
                        </Row>
                    </Container>
                </Modal.Footer>
            </Modal>
        </>
    )
}
Example #11
Source File: BannerExtension.tsx    From crust-apps with Apache License 2.0 5 votes vote down vote up
function BannerExtension (): React.ReactElement | null {
  const { t } = useTranslation();
  const { hasInjectedAccounts } = useApi();
  const upgradableCount = useExtensionCounter();
  const phishing = useRef<string>(t<string>('Since some extensions, such as the polkadot-js extension, protects you against all community reported phishing sites, there are valid reasons to use them for additional protection, even if you are not storing accounts in it.'));

  if (!isSupported || !browserName) {
    return null;
  }

  if (isWeb3Injected) {
    if (hasInjectedAccounts) {
      if (!upgradableCount) {
        return null;
      }

      return (
        <Banner type='warning'>
          <p>{t<string>('You have {{upgradableCount}} extensions that need to be updated with the latest chain properties in order to display the correct information for the chain you are connected to. This update includes chain metadata and chain properties.', { replace: { upgradableCount } })}</p>
          <p><Trans key='extensionUpgrade'>Visit your <a href='#/settings/metadata'>settings page</a> to apply the updates to the injected extensions.</Trans></p>
        </Banner>
      );
    }

    return (
      <Banner type='warning'>
        <p>{t<string>('One or more extensions are detected in your browser, however no accounts has been injected.')}</p>
        <p>{t<string>('Ensure that the extension has accounts, some accounts are visible globally and available for this chain and that you gave the application permission to access accounts from the extension to use them.')}</p>
        <p>{phishing.current}</p>
      </Banner>
    );
  }

  return (
    <Banner type='warning'>
      <p>{t<string>('It is recommended that you create/store your accounts securely and externally from the app. On {{yourBrowser}} the following browser extensions are available for use -', {
        replace: {
          yourBrowser: stringUpperFirst(browserName)
        }
      })}</p>
      <ul>{availableExtensions[browserName].map(({ desc, link, name }): React.ReactNode => (
        <li key={name}>
          <a
            href={link}
            rel='noopener noreferrer'
            target='_blank'
          >
            {name}
          </a> ({t(desc)})
        </li>
      ))
      }</ul>
      <p>{t<string>('Accounts injected from any of these extensions will appear in this application and be available for use. The above list is updated as more extensions with external signing capability become available.')}&nbsp;<a
        href='https://github.com/polkadot-js/extension'
        rel='noopener noreferrer'
        target='_blank'
      >{t<string>('Learn more...')}</a></p>
      <p>{phishing.current}</p>
    </Banner>
  );
}
Example #12
Source File: ViewScore.tsx    From longwave with MIT License 5 votes vote down vote up
export function ViewScore() {
  const { t } = useTranslation();
  const { gameState, clueGiver, spectrumCard } = useContext(GameModelContext);

  if (!clueGiver) {
    return null;
  }

  let score = GetScore(gameState.spectrumTarget, gameState.guess);
  let bonusCoopTurn = false;
  if (gameState.gameType === GameType.Cooperative && score === 4) {
    score = 3;
    bonusCoopTurn = true;
  }

  const wasCounterGuessCorrect =
    (gameState.counterGuess === "left" &&
      gameState.spectrumTarget < gameState.guess) ||
    (gameState.counterGuess === "right" &&
      gameState.spectrumTarget > gameState.guess);

  return (
    <div>
      <Spectrum
        spectrumCard={spectrumCard}
        handleValue={gameState.guess}
        targetValue={gameState.spectrumTarget}
      />
      <CenteredColumn>
        <div>
          {t("viewscore.player_clue", { givername: clueGiver.name })}:{" "}
          <strong>{gameState.clue}</strong>
        </div>
        <div>
          {t("viewscore.score")}: {score} {t("viewscore.points")}!
        </div>
        {gameState.gameType === GameType.Teams && (
          <div>
            {TeamName(TeamReverse(clueGiver.team), t)} {t("viewscore.got")}{" "}
            {wasCounterGuessCorrect
              ? t("viewscore.1_point_correct_guess")
              : t("viewscore.0_point_wrong_guess")}
          </div>
        )}
        {bonusCoopTurn && <Trans
          i18nKey={t('viewscore.bonus_turn')}
          components={{
            strong: <strong />,
          }}
        />}
        <NextTurnOrEndGame />
      </CenteredColumn>
    </div>
  );
}
Example #13
Source File: FeedbackForm.tsx    From nosgestesclimat-site with MIT License 5 votes vote down vote up
export default function FeedbackForm({ onEnd, onCancel }: Props) {
	// const tracker = useContext(TrackerContext)
	const pathname = useLocation().pathname
	const page = pathname.split('/').slice(-1)[0]
	const isSimulateur = pathname.includes('simulateurs')
	const lang = useTranslation().i18n.language
	useEffect(() => {
		const script = document.createElement('script')
		script.src = 'https://code.jquery.com/jquery-2.1.4.min.js'

		document.body.appendChild(script)
		setTimeout(() => {
			const script = document.createElement('script')
			script.id = 'zammad_form_script'
			script.async = true
			script.onload = () => {
				$('#feedback-form').ZammadForm({
					messageTitle: `Remarque sur ${
						isSimulateur ? 'le simulateur' : 'la page'
					} ${page}`,
					messageSubmit: 'Envoyer',
					messageThankYou:
						'Merci pour votre retour ! Vous pouvez aussi nous contacter directement à [email protected]',
					lang,
					attributes: [
						{
							display: 'Message',
							name: 'body',
							tag: 'textarea',
							placeholder: 'Your Message...',
							defaultValue: '',
							rows: 7
						},
						{
							display: 'Nom',
							name: 'name',
							tag: 'input',
							type: 'text',
							defaultValue: '-'
						},
						{
							display: 'Email (pour recevoir notre réponse)',
							name: 'email',
							tag: 'input',
							type: 'email',
							placeholder: 'Your Email'
						}
					]
				})
			}
			script.src = 'https://mon-entreprise.zammad.com/assets/form/form.js'
			document.body.appendChild(script)
		}, 100)
		// tracker.push(['trackEvent', 'Feedback', 'written feedback submitted'])
	}, [])

	return (
		<ScrollToElement onlyIfNotVisible>
			<div style={{ textAlign: 'end' }}>
				<button
					onClick={() => onCancel()}
					className="ui__ link-button"
					style={{ textDecoration: 'none', marginLeft: '0.3rem' }}
					aria-label="close"
				>
					X
				</button>
			</div>

			<p>
				<Trans i18nKey="feedback.bad.form.headline">
					Votre retour nous est précieux afin d'améliorer ce site en continu.
					Sur quoi devrions nous travailler afin de mieux répondre à vos
					attentes ?
				</Trans>
			</p>
			<div id="feedback-form"></div>
		</ScrollToElement>
	)
}
Example #14
Source File: EventsView.tsx    From next-basics with GNU General Public License v3.0 5 votes vote down vote up
export function EventsView(): React.ReactElement {
  const { t } = useTranslation(NS_NEXT_BUILDER);
  const { nodes } = useBuilderData();
  const bricksWithEvents = getBricksWithEvents(nodes);
  const [q, setQ] = React.useState<string>(null);
  const hoverNodeUid = useHoverNodeUid();
  const manager = useBuilderDataManager();

  const handleSearch = React.useCallback((value: string): void => {
    setQ(value);
  }, []);

  const filteredBricks = React.useMemo(
    () => filterBricksWithEvents(bricksWithEvents, q),
    [bricksWithEvents, q]
  );

  const handleMouseEnter = (uid: number): void => {
    const prevUid = manager.getHoverNodeUid();
    if (prevUid !== uid) {
      manager.setHoverNodeUid(uid);
    }
  };
  const handleMouseLeave = (uid: number): void => {
    const prevUid = manager.getHoverNodeUid();
    if (prevUid === uid) {
      manager.setHoverNodeUid(undefined);
    }
  };

  return (
    <ToolboxPane
      title={t(K.EVENTS)}
      tooltips={
        <>
          <p>
            <Trans t={t} i18nKey={K.EVENTS_VIEW_TIPS_1} />
          </p>
          <p>
            <Trans t={t} i18nKey={K.EVENTS_VIEW_TIPS_2} />
          </p>
        </>
      }
    >
      <SearchComponent
        placeholder={t(K.SEARCH_BRICKS_WITH_EVENTS)}
        onSearch={handleSearch}
      />
      <div
        className={`${styles.eventsWrapper} ${sharedStyles.customScrollbarContainer}`}
      >
        <ul className={styles.brickList}>
          {filteredBricks.map((brick) => (
            <li
              key={brick.node.$$uid}
              data-testid={`brick-with-events-item-${brick.node.$$uid}`}
              onMouseEnter={() => handleMouseEnter(brick.node.$$uid)}
              onMouseLeave={() => handleMouseLeave(brick.node.$$uid)}
            >
              <BrickWithEventsItem
                {...brick}
                hover={brick.node.$$uid === hoverNodeUid}
              />
            </li>
          ))}
        </ul>
      </div>
    </ToolboxPane>
  );
}
Example #15
Source File: RemoveButton.tsx    From tobira with Apache License 2.0 5 votes vote down vote up
RemoveButton: React.FC<Props> = ({ block: blockRef, onConfirm }) => {
    const { t } = useTranslation();


    const block = useFragment(graphql`
        fragment RemoveButtonData on Block {
            id
        }
    `, blockRef);


    const [commit] = useMutation<RemoveButtonMutation>(graphql`
        mutation RemoveButtonMutation($id: ID!) {
            removeBlock(id: $id) {
                id @deleteRecord
                realm {
                    ... ContentManageRealmData
                }
            }
        }
    `);

    const remove = () => {
        commit({
            variables: block,
            onCompleted: () => {
                currentRef(modalRef).done();
            },
            onError: error => {
                currentRef(modalRef).reportError(
                    displayCommitError(error, t("manage.realm.content.removing-failed")),
                );
            },
        });
    };


    const modalRef = useRef<ConfirmationModalHandle>(null);

    return <>
        <Button
            title={t("manage.realm.content.remove")}
            css={{
                color: "var(--danger-color)",
                "&&:hover": {
                    backgroundColor: "var(--danger-color)",
                    color: "var(--danger-color-bw-contrast)",
                },
            }}
            onClick={() => {
                currentRef(modalRef).open();
                onConfirm?.();
            }}
        >
            <FiTrash />
        </Button>
        <ConfirmationModal
            buttonContent={t("manage.realm.content.remove")}
            onSubmit={remove}
            ref={modalRef}
        >
            <p>
                <Trans i18nKey="manage.realm.danger-zone.delete.cannot-be-undone" />
            </p>
        </ConfirmationModal>
    </>;
}
Example #16
Source File: ArtifactFilterDisplay.tsx    From genshin-optimizer with MIT License 5 votes vote down vote up
export default function ArtifactFilterDisplay({ filterOption, filterOptionDispatch, }: { filterOption: FilterOption, filterOptionDispatch: (any) => void }) {
  const { t } = useTranslation(["artifact", "ui"]);

  const { artSetKeys = [], mainStatKeys = [], rarity = [], slotKeys = [], levelLow, levelHigh, substats = [],
    location = "", exclusion = ["excluded", "included"], locked = ["locked", "unlocked"] } = filterOption

  return <Grid container spacing={1}>
    {/* left */}
    <Grid item xs={12} md={6} display="flex" flexDirection="column" gap={1}>
      {/* Artifact stars filter */}
      <SolidToggleButtonGroup fullWidth onChange={(e, newVal) => filterOptionDispatch({ rarity: newVal })} value={rarity} size="small">
        {allArtifactRarities.map(star => <ToggleButton key={star} value={star}><Stars stars={star} /></ToggleButton>)}
      </SolidToggleButtonGroup>
      {/* Artifact Slot */}
      <SolidToggleButtonGroup fullWidth onChange={(e, newVal) => filterOptionDispatch({ slotKeys: newVal })} value={slotKeys} size="small">
        {allSlotKeys.map(slotKey => <ToggleButton key={slotKey} value={slotKey}>{artifactSlotIcon(slotKey)}</ToggleButton>)}
      </SolidToggleButtonGroup>
      {/* exclusion + locked */}
      <Box display="flex" gap={1}>
        <SolidToggleButtonGroup fullWidth value={exclusion} onChange={(e, newVal) => filterOptionDispatch({ exclusion: newVal })} size="small">
          <ToggleButton value="excluded" sx={{ display: "flex", gap: 1 }}>
            <FontAwesomeIcon icon={faBan} /><Trans i18nKey={"exclusion.excluded"} t={t} />
          </ToggleButton>
          <ToggleButton value="included" sx={{ display: "flex", gap: 1 }}>
            <FontAwesomeIcon icon={faChartLine} /><Trans i18nKey={"exclusion.included"} t={t} />
          </ToggleButton>
        </SolidToggleButtonGroup>
        <SolidToggleButtonGroup fullWidth value={locked} onChange={(e, newVal) => filterOptionDispatch({ locked: newVal })} size="small">
          <ToggleButton value="locked" sx={{ display: "flex", gap: 1 }}>
            <Lock /><Trans i18nKey={"ui:locked"} t={t} />
          </ToggleButton>
          <ToggleButton value="unlocked" sx={{ display: "flex", gap: 1 }}>
            <LockOpen /><Trans i18nKey={"ui:unlocked"} t={t} />
          </ToggleButton>
        </SolidToggleButtonGroup>
      </Box>
      {/* Artiface level filter */}
      <ArtifactLevelSlider showLevelText levelLow={levelLow} levelHigh={levelHigh}
        setLow={levelLow => filterOptionDispatch({ levelLow })}
        setHigh={levelHigh => filterOptionDispatch({ levelHigh })}
        setBoth={(levelLow, levelHigh) => filterOptionDispatch({ levelLow, levelHigh })} />
      <Grid container display="flex" gap={1}>
        <Grid item flexGrow={1}>
          {/* location */}
          <CharacterAutocomplete
            value={location}
            onChange={location => filterOptionDispatch({ location })}
            placeholderText={t("artifact:filterLocation.any")}
            defaultText={t("artifact:filterLocation.any")}
            labelText={t("artifact:filterLocation.location")}
            showDefault
            showInventory
            showEquipped
          />
        </Grid>
      </Grid>
    </Grid>
    {/* right */}
    <Grid item xs={12} md={6} display="flex" flexDirection="column" gap={1}>
      {/* Artifact Set */}
      <ArtifactSetMultiAutocomplete artSetKeys={artSetKeys} setArtSetKeys={artSetKeys => filterOptionDispatch({ artSetKeys })} />
      <ArtifactMainStatMultiAutocomplete mainStatKeys={mainStatKeys} setMainStatKeys={mainStatKeys => filterOptionDispatch({ mainStatKeys })} />
      <ArtifactSubstatMultiAutocomplete substatKeys={substats} setSubstatKeys={substats => filterOptionDispatch({ substats })} />
    </Grid>
  </Grid>
}
Example #17
Source File: index.tsx    From oasis-wallet-web with Apache License 2.0 5 votes vote down vote up
export function ErrorFormatter(props: Props) {
  const { t } = useTranslation()
  const message = props.message

  const errorMap: { [code in WalletErrors]: string | React.ReactElement } = {
    [WalletErrors.UnknownError]: t('errors.unknown', 'Unknown error: {{message}}', { message }),
    [WalletErrors.UnknownGrpcError]: t('errors.unknownGrpc', 'Unknown gRPC error: {{message}}', { message }),
    [WalletErrors.InvalidAddress]: t('errors.invalidAddress', 'Invalid address'),
    [WalletErrors.InvalidPrivateKey]: t('errors.invalidPrivateKey', 'Invalid private key'),
    [WalletErrors.InsufficientBalance]: t('errors.insufficientBalance', 'Insufficient balance'),
    [WalletErrors.CannotSendToSelf]: t('errors.cannotSendToSelf', 'Cannot send to yourself'),
    [WalletErrors.InvalidNonce]: t('errors.invalidNonce', 'Invalid nonce (transaction number)'),
    [WalletErrors.DuplicateTransaction]: t('errors.duplicateTransaction', 'Duplicate transaction'),
    [WalletErrors.NoOpenWallet]: t('errors.noOpenWallet', 'No wallet opened'),
    [WalletErrors.USBTransportNotSupported]: t(
      'errors.usbTransportNotSupported',
      'Your browser does not support WebUSB (e.g. Firefox). Try using Chrome.',
    ),
    [WalletErrors.USBTransportError]: t('errors.usbTransportError', 'USB Transport error: {{message}}.', {
      message,
    }),
    [WalletErrors.LedgerAppVersionNotSupported]: t(
      'errors.ledgerAppVersionNotSupported',
      'Oasis App on Ledger is closed or outdated. Make sure Ledger is unlocked, the Oasis App is opened and up to date.',
    ),
    [WalletErrors.LedgerTransactionRejected]: t(
      'errors.ledgerTransactionRejected',
      'Transaction rejected on Ledger.',
    ),
    [WalletErrors.LedgerNoDeviceSelected]: (
      <Trans
        i18nKey="errors.ledgerNoDeviceSelected"
        t={t}
        defaults="No Ledger device selected. Make sure it is connected, <0>check common USB connection issues with Ledger</0>, and <1>check site permissions don't block USB devices</1>."
        components={[
          <Anchor
            href="https://support.ledger.com/hc/en-us/articles/115005165269-Fix-USB-connection-issues-with-Ledger-Live?support=true"
            target="_blank"
            rel="noopener"
          />,
          <Anchor href="https://support.google.com/chrome/answer/114662" target="_blank" rel="noopener" />,
        ]}
      />
    ),
    [WalletErrors.LedgerCannotOpenOasisApp]: t(
      'errors.ledgerCannotOpenOasisApp',
      'Could not open Oasis App on Ledger. Make sure Ledger is unlocked and the Oasis App is opened.',
    ),
    [WalletErrors.LedgerOasisAppIsNotOpen]: t(
      'errors.LedgerOasisAppIsNotOpen',
      'Oasis App on Ledger is closed.',
    ),
    [WalletErrors.LedgerUnknownError]: t('errors.unknownLedgerError', 'Unknown ledger error: {{message}}', {
      message,
    }),
  }

  const error = errorMap[props.code]
  return <>{error}</>
}
Example #18
Source File: index.tsx    From nanolooker with MIT License 5 votes vote down vote up
LanguagePreferences: React.FC<Props> = ({ isDetailed }) => {
  const { t } = useTranslation();

  return (
    <Row style={{ alignItems: !isDetailed ? "center" : "flex-start" }}>
      <Col xs={isDetailed ? 24 : undefined}>
        <Text className={isDetailed ? "preference-detailed-title" : ""}>
          {t("preferences.language")}
        </Text>
      </Col>
      {isDetailed ? (
        <Col xs={16}>
          <Trans
            i18nKey="preferences.languageDetailed"
            style={{ marginLeft: "12px" }}
          >
            <a
              href="https://github.com/running-coder/nanolooker/tree/master/src/i18n/locales"
              rel="noopener noreferrer"
              target="_blank"
            >
              {t("preferences.contribute")}
            </a>
          </Trans>
        </Col>
      ) : null}

      <Col
        xs={isDetailed ? 8 : undefined}
        style={{ textAlign: "right" }}
        flex="auto"
      >
        <Select
          value={i18next.language}
          onChange={value => {
            i18next.changeLanguage(value);
            localStorage.setItem(LOCALSTORAGE_KEYS.LANGUAGE, value);
          }}
          style={{ width: 120 }}
        >
          <Option value="en">English</Option>
          <Option value="fr">Français</Option>
          <Option value="es">Español</Option>
          <Option value="ar">العربية</Option>
          <Option value="de">Deutsch</Option>
          <Option value="fa">فارسی</Option>
          <Option value="hi">हिन्दी</Option>
          <Option value="it">Italiano</Option>
          <Option value="ja">日本語</Option>
          <Option value="ko">한국어</Option>
          <Option value="nl">Nederlands</Option>
          <Option value="pl">Polski</Option>
          <Option value="pt">Português</Option>
          <Option value="ru">Pусский</Option>
          <Option value="tr">Türkçe</Option>
          <Option value="vi">Tiếng Việt</Option>
          <Option value="zh">中文</Option>
        </Select>
      </Col>
    </Row>
  );
}
Example #19
Source File: PaymentInfo.tsx    From subscan-multisig-react with Apache License 2.0 5 votes vote down vote up
function PaymentInfo({ accountId, className = '', extrinsic }: Props): React.ReactElement<Props> | null {
  const { t } = useTranslation();
  const { api } = useApi();
  const [dispatchInfo, setDispatchInfo] = useState<RuntimeDispatchInfo | null>(null);
  const balances = useCall<DeriveBalancesAll>(api.derive.balances?.all, [accountId]);
  const mountedRef = useIsMountedRef();

  useEffect((): void => {
    accountId &&
      extrinsic &&
      isFunction(extrinsic.paymentInfo) &&
      isFunction(api.rpc.payment?.queryInfo) &&
      setTimeout((): void => {
        try {
          extrinsic
            .paymentInfo(accountId)
            .then((info) => mountedRef.current && setDispatchInfo(info))
            .catch(console.error);
        } catch (error) {
          console.error(error);
        }
      }, 0);
  }, [api, accountId, extrinsic, mountedRef]);

  if (!dispatchInfo || !extrinsic) {
    return null;
  }

  const isFeeError =
    api.consts.balances &&
    !api.tx.balances?.transfer.is(extrinsic) &&
    balances?.accountId.eq(accountId) &&
    (balances.availableBalance.lte(dispatchInfo.partialFee) ||
      balances.freeBalance.sub(dispatchInfo.partialFee).lte(api.consts.balances.existentialDeposit as unknown as BN));

  return (
    <>
      <Expander
        className={className}
        summary={
          <Trans i18nKey="feesForSubmission">
            Fees of <span className="highlight">{formatBalance(dispatchInfo.partialFee, { withSiFull: true })}</span>{' '}
            will be applied to the submission
          </Trans>
        }
      />
      {isFeeError && (
        <MarkWarning
          content={t<string>(
            'The account does not have enough free funds (excluding locked/bonded/reserved) available to cover the transaction fees without dropping the balance below the account existential amount.'
          )}
        />
      )}
    </>
  );
}
Example #20
Source File: About.tsx    From TidGi-Desktop with Mozilla Public License 2.0 5 votes vote down vote up
export default function About(): JSX.Element {
  const { t } = useTranslation();
  const versions = usePromiseValue(async () => {
    const processVersions = await window.service.context.get('environmentVersions');
    return [
      { name: 'Electron Version', version: processVersions.electron },
      { name: 'Node Version', version: processVersions.node },
      { name: 'Chromium Version', version: processVersions.chrome },
    ];
  }, [] as Array<{ name: string; version: string }>);

  const appVersion = usePromiseValue<string>(async () => await window.service.context.get('appVersion'));
  const platform = usePromiseValue<string>(async () => await window.service.context.get('platform'));

  return (
    <DialogContent>
      <div id="test" data-usage="For spectron automating testing" />
      <Helmet>
        <title>{t('ContextMenu.About')}</title>
      </Helmet>
      <Icon src={iconPath} alt="TidGi" />
      <Title>TidGi ({platform ?? 'Unknown Platform'})</Title>
      <TidGiVersion>{`Version v${appVersion ?? ' - '}.`}</TidGiVersion>
      <DependenciesVersionsContainer>
        {versions?.map(({ name, version }) => (
          <DependenciesVersions key={name}>
            {name}: {version}
          </DependenciesVersions>
        ))}
      </DependenciesVersionsContainer>

      <ButtonContainer>
        <GoToTheWebsiteButton onClick={async () => await window.service.native.open('https://github.com/tiddly-gittly/TidGi-Desktop')}>
          Website
        </GoToTheWebsiteButton>
        <GoToTheWebsiteButton onClick={async () => await window.service.native.open('https://github.com/tiddly-gittly/TidGi-Desktop/issues/new/choose')}>
          Support
        </GoToTheWebsiteButton>
      </ButtonContainer>

      <MadeBy>
        <Trans t={t} i18nKey="Dialog.MadeWithLove">
          <span>Made with </span>
          <span role="img" aria-label="love">
            ❤
          </span>
          <span> by </span>
        </Trans>
        <Link
          onClick={async () => await window.service.native.open('https://onetwo.ren/wiki/')}
          onKeyDown={async (event) => {
            if (event.key !== 'Enter') {
              return;
            }
            await window.service.native.open('https://onetwo.ren/wiki/');
          }}
          role="link"
          tabIndex={0}>
          {t('LinOnetwo')}
        </Link>
        <span> && </span>
        <Link
          onClick={async () => await window.service.native.open('https://webcatalog.app/?utm_source=tidgi_app')}
          onKeyDown={async (event) => {
            if (event.key !== 'Enter') {
              return;
            }
            await window.service.native.open('https://webcatalog.app/?utm_source=tidgi_app');
          }}
          role="link"
          tabIndex={0}>
          {t('Preference.WebCatalog')}
        </Link>
      </MadeBy>
    </DialogContent>
  );
}
Example #21
Source File: ConvertQUICKPage.tsx    From interface-v2 with GNU General Public License v3.0 4 votes vote down vote up
ConvertQUICKPage: React.FC = () => {
  const classes = useStyles();
  const { t } = useTranslation();
  const { account, library } = useActiveWeb3React();
  const [quickAmount, setQUICKAmount] = useState('');
  const [quickV2Amount, setQUICKV2Amount] = useState('');
  const [approving, setApproving] = useState(false);
  const [attemptConverting, setAttemptConverting] = useState(false);
  const [showConfirm, setShowConfirm] = useState(false);
  const [txPending, setTxPending] = useState(false);
  const [txHash, setTxHash] = useState('');
  const [txError, setTxError] = useState('');

  const quickToken = returnTokenFromKey('QUICK');
  const quickBalance = useTokenBalance(account ?? undefined, quickToken);
  const quickConvertContract = useQUICKConversionContract();
  const parsedAmount = tryParseAmount(quickAmount, quickToken);
  const [approval, approveCallback] = useApproveCallback(
    parsedAmount,
    quickConvertContract?.address,
  );

  const quickConvertingText = t('convertingQUICKtoQUICKV2', {
    quickAmount,
    quickV2Amount,
  });
  const quickConvertedText = t('convertedQUICKtoQUICKV2', {
    quickAmount,
    quickV2Amount,
  });
  const txSubmittedQuickConvertText = t('submittedTxQUICKConvert', {
    quickAmount,
    quickV2Amount,
  });
  const successQuickConvertedText = t('successConvertedQUICKtoQUICKV2', {
    quickAmount,
    quickV2Amount,
  });

  const isInsufficientQUICK =
    Number(quickAmount) > Number(quickBalance?.toExact() ?? 0);
  const buttonText = useMemo(() => {
    if (!quickAmount || !Number(quickAmount)) {
      return t('enterAmount');
    } else if (approval !== ApprovalState.APPROVED) {
      return t('approve');
    } else if (isInsufficientQUICK) {
      return t('insufficientBalance');
    } else {
      return t('convert');
    }
  }, [isInsufficientQUICK, quickAmount, t, approval]);
  const addTransaction = useTransactionAdder();
  const finalizedTransaction = useTransactionFinalizer();

  const handleDismissConfirmation = () => {
    setShowConfirm(false);
  };

  const attemptToApprove = async () => {
    setApproving(true);
    try {
      await approveCallback();
      setApproving(false);
    } catch (e) {
      setApproving(false);
    }
  };

  const convertQUICK = async () => {
    if (quickConvertContract && library && parsedAmount) {
      setAttemptConverting(true);
      setShowConfirm(true);
      await quickConvertContract
        .quickToQuickX(parsedAmount.raw.toString(), {
          gasLimit: 300000,
        })
        .then(async (response: TransactionResponse) => {
          setAttemptConverting(false);
          setTxPending(true);
          setTxError('');
          setTxHash('');
          addTransaction(response, {
            summary: quickConvertingText,
          });
          try {
            const tx = await response.wait();
            finalizedTransaction(tx, {
              summary: quickConvertedText,
            });
            setTxPending(false);
            setTxHash(tx.transactionHash);
          } catch (err) {
            setTxPending(false);
            setTxError(t('errorInTx'));
          }
        })
        .catch(() => {
          setAttemptConverting(false);
          setTxPending(false);
          setTxHash('');
          setTxError(t('txRejected'));
        });
    }
  };

  return (
    <Box width='100%' maxWidth={488} id='convertQUICKPage'>
      <Typography variant='h4'>{t('convert')} QUICK</Typography>
      <Box className={classes.wrapper}>
        <Box display='flex' alignItems='center' mb={3}>
          <Box className={classes.iconWrapper}>
            <img src={QUICKIcon} alt='QUICK' />
          </Box>
          <Typography variant='h6'>QUICK(OLD)</Typography>
          <Box mx={1.5} className={classes.convertArrow}>
            <ArrowForward />
          </Box>
          <Box className={classes.iconWrapper}>
            <QUICKV2Icon />
          </Box>
          <Typography variant='h6'>QUICK(NEW)</Typography>
        </Box>
        <Typography variant='body2' color='textSecondary'>
          <Trans i18nKey='convertQuick'>
            Convert your QUICK(OLD) to QUICK(NEW). Read more about QUICK token
            split{' '}
            <a
              href='https://quickswap-layer2.medium.com/you-voted-for-a-1-1000-token-split-to-make-quick-more-appealing-9c25c2a2dd7e'
              rel='noreferrer'
              target='_blank'
            >
              here
            </a>
          </Trans>
        </Typography>
        <Box className={classes.conversionRate}>
          <Typography variant='caption'>
            {t('conversionRate')}: 1 QUICK(OLD) ={' '}
            {GlobalConst.utils.QUICK_CONVERSION_RATE} QUICK(NEW)
          </Typography>
        </Box>
        <Box mt={4} mb={2}>
          <Typography variant='body2' color='textSecondary'>
            {t('yourbalance')}: {formatTokenAmount(quickBalance)}
          </Typography>
          <Box
            className={cx(
              classes.currencyInput,
              isInsufficientQUICK && classes.errorInput,
            )}
          >
            <NumericalInput
              placeholder='0.00'
              value={quickAmount}
              fontSize={18}
              onUserInput={(value) => {
                const digits =
                  value.indexOf('.') > -1 ? value.split('.')[1].length : 0;
                let fixedVal = value;
                if (digits > quickToken.decimals) {
                  fixedVal = Number(value).toFixed(quickToken.decimals);
                }
                setQUICKAmount(fixedVal);
                setQUICKV2Amount(
                  (
                    Number(fixedVal) * GlobalConst.utils.QUICK_CONVERSION_RATE
                  ).toLocaleString('fullwide', {
                    useGrouping: false,
                    maximumFractionDigits: quickToken.decimals,
                  }),
                );
              }}
            />
            <Box
              mr={1}
              className={classes.maxButton}
              onClick={() => {
                if (quickBalance) {
                  setQUICKAmount(quickBalance.toExact());
                  setQUICKV2Amount(
                    (
                      Number(quickBalance.toExact()) *
                      GlobalConst.utils.QUICK_CONVERSION_RATE
                    ).toString(),
                  );
                }
              }}
            >
              {t('max')}
            </Box>
            <Typography variant='h6'>QUICK(OLD)</Typography>
          </Box>
          {isInsufficientQUICK && (
            <Typography variant='body2' className={classes.errorText}>
              {t('insufficientBalance', { symbol: 'QUICK' })}
            </Typography>
          )}
        </Box>
        <Box ml={2} className={classes.convertArrow}>
          <ArrowDownward />
        </Box>
        <Box mt={2} mb={4}>
          <Typography variant='body2' color='textSecondary'>
            {t('youwillreceive')}:
          </Typography>
          <Box className={classes.currencyInput}>
            <NumericalInput
              placeholder='0.00'
              value={quickV2Amount}
              fontSize={18}
              onUserInput={(value) => {
                setQUICKV2Amount(value);
                const quickAmount = (
                  Number(value) / GlobalConst.utils.QUICK_CONVERSION_RATE
                ).toLocaleString('fullwide', {
                  useGrouping: false,
                  maximumFractionDigits: quickToken.decimals,
                });
                setQUICKAmount(quickAmount);
              }}
            />
            <Typography variant='h6'>QUICK(NEW)</Typography>
          </Box>
        </Box>
        <Box display='flex' justifyContent='center'>
          <Button
            disabled={
              approving ||
              attemptConverting ||
              isInsufficientQUICK ||
              !quickAmount ||
              !Number(quickAmount)
            }
            className={classes.convertButton}
            onClick={() => {
              if (approval === ApprovalState.APPROVED) {
                convertQUICK();
              } else {
                attemptToApprove();
              }
            }}
          >
            {buttonText}
          </Button>
        </Box>
      </Box>
      {showConfirm && (
        <TransactionConfirmationModal
          isOpen={showConfirm}
          onDismiss={handleDismissConfirmation}
          attemptingTxn={attemptConverting}
          txPending={txPending}
          hash={txHash}
          content={() =>
            txError ? (
              <TransactionErrorContent
                onDismiss={handleDismissConfirmation}
                message={txError}
              />
            ) : (
              <ConfirmationModalContent
                title={t('convertingQUICK')}
                onDismiss={handleDismissConfirmation}
                content={() => (
                  <Box textAlign='center'>
                    <Box mt={6} mb={5}>
                      <CircularProgress size={80} />
                    </Box>
                    <Typography variant='body1'>
                      {quickConvertingText}
                    </Typography>
                  </Box>
                )}
              />
            )
          }
          pendingText={quickConvertingText}
          modalContent={
            txPending ? txSubmittedQuickConvertText : successQuickConvertedText
          }
        />
      )}
    </Box>
  );
}
Example #22
Source File: SubmissionForm.tsx    From fishbowl with MIT License 4 votes vote down vote up
function SubmissionForm(props: { onSubmit: () => void }) {
  const { t } = useTranslation()
  const currentPlayer = React.useContext(CurrentPlayerContext)
  const currentGame = React.useContext(CurrentGameContext)
  const [submitCards, { called }] = useSubmitCardsMutation()

  const numSubmitted = filter(
    currentGame.cards,
    (card) => card.player_id === currentPlayer.id
  ).length

  const numToSubmit =
    (currentGame.num_entries_per_player &&
      currentGame.num_entries_per_player - numSubmitted) ||
    0

  const [words, setWords] = React.useState<Array<string>>(
    Array.from(
      {
        length: numToSubmit,
      },
      () => ""
    )
  )

  React.useEffect(() => {
    setWords(
      Array.from(
        {
          length: numToSubmit,
        },
        () => ""
      )
    )
  }, [numToSubmit])

  const emptyWords = words.some((word) => word.length < 1)

  return (
    <>
      <Grid item>
        <Title
          text={t("cardSubmission.title", "Submit {{ count }} card", {
            count: numToSubmit,
            defaultValue_plural: "Submit {{ count }} cards",
          })}
        />
      </Grid>

      <Grid item>
        {t(
          "cardSubmission.description",
          'These cards will be put into the "fishbowl," and drawn randomly in rounds of {{ rounds }}. They can be words, familiar phrases, or inside jokes!',
          {
            rounds: currentGame.rounds
              .map((round) => startCase(round.value))
              .join(", "),
          }
        )}
      </Grid>

      {currentGame.starting_letter && (
        <Grid item>
          <Trans t={t} i18nKey="cardSubmission.descriptionLetter">
            {"They must start with the letter "}
            <b>{{ letter: currentGame.starting_letter.toLocaleUpperCase() }}</b>
            .
          </Trans>
        </Grid>
      )}

      <Grid item container direction="column" spacing={2} alignItems="center">
        {words.map((_, index) => {
          return (
            <Grid item key={index}>
              <SubmissionCard
                onChange={(value: string) => {
                  const newWords = cloneDeep(words)
                  newWords[index] = value
                  setWords(newWords)
                }}
                word={words[index]}
              />
            </Grid>
          )
        })}
      </Grid>

      <Grid item>
        <Button
          variant="contained"
          color="primary"
          size="large"
          disabled={called || emptyWords}
          onClick={async () => {
            await submitCards({
              variables: {
                cards: words.map((word) => {
                  return {
                    player_id: currentPlayer.id,
                    game_id: currentGame.id,
                    word: word,
                  }
                }),
              },
            })
            props.onSubmit()
          }}
        >
          {t("cardSubmission.submitButton", "Submit")}
        </Button>
      </Grid>
    </>
  )
}
Example #23
Source File: header.component.tsx    From cwa-quick-test-frontend with Apache License 2.0 4 votes vote down vote up
Header = (props: any) => {

    const navigation = useNavigation();
    const history = useHistory();
    const { t } = useTranslation();
    const { keycloak } = useKeycloak();

    const [userName, setUserName] = React.useState('');
    const [isInit, setIsInit] = React.useState(false)

    const [environmentName] = useLocalStorage('environmentName', '');

    React.useEffect(() => {
        if (navigation)
            setIsInit(true);
    }, [navigation])

    // set user name from keycloak
    React.useEffect(() => {

        if (keycloak.idTokenParsed) {
            setUserName((keycloak.idTokenParsed as any).name);
        }

    }, [keycloak])

    const handleLogout = () => {
        keycloak.logout({ redirectUri: window.location.origin + navigation!.calculatedRoutes.landing });
    }

    const changePasswordUrl = keycloak.authServerUrl + 'realms/' + keycloak.realm + '/account/password';

    return (!isInit ? <></> :
        <Container className='position-relative'>
            {/* simple header with logo */}

            {/* user icon and user name */}
            <Row id='qt-header'>
                <Image id='c19-logo' src={C19Logo} />
                <span className='header-font my-auto mx-1 pt-1'>
                    {t('translation:title')}
                    {!environmentName
                        ? <></>
                        : <span className='environment-font my-auto mx-1'>
                            {'\n' + environmentName}
                        </span>
                    }
                </span>
                {!(environmentName && history.location.pathname === navigation?.routes.root)
                    ? <></>
                    : < span className='environment-info-text py-3'>
                        <Trans>
                            {t('translation:environment-info1')}
                            {<a
                                href={t('translation:environment-info-link')}
                                target='blank'
                            >
                                {t('translation:environment-info-link')}
                            </a>}
                            {'.'}
                        </Trans>
                    </span>
                }

            </Row>
            {/* {!environmentName
                ? <></>
                : <Row id='qt-environment'>
                    <span className='header-font my-auto mx-1'>
                        {environmentName}
                    </span>
                </Row>
            } */}
            <Navbar id='user-container' >
                <NavDropdown
                    className="nav-dropdown-title"
                    title={userName}
                    id="responsive-navbar-nav"
                >
                    <Nav.Link
                        className='mx-0 dropdown-item'
                        onClick={handleLogout}
                    >
                        {t('translation:logout')}
                    </Nav.Link>
                    <NavDropdown.Divider className='m-0' />
                    <Nav.Link className='mx-0 dropdown-item' href={changePasswordUrl} target='passwordchange'>
                        {t('translation:change-password')}
                    </Nav.Link>
                </NavDropdown>
            </Navbar>
        </Container >
    )
}
Example #24
Source File: index.tsx    From crust-apps with Apache License 2.0 4 votes vote down vote up
function MaxwellClaims (): React.ReactElement<Props> {
  const [didCopy, setDidCopy] = useState(false);
  const [ethereumAddress, setEthereumAddress] = useState<string | undefined | null>(null);
  const [signature, setSignature] = useState<EcdsaSignature | null>(null);
  const [step, setStep] = useState<Step>(Step.Account);
  const [accountId, setAccountId] = useState<string | null>(null);
  const { api, systemChain } = useApi();
  const { t } = useTranslation();
  const [statusOpen, setStatusOpen] = useState<boolean>(false);
  const [result, setResult] = useState<string>('');
  const [status, setStatus] = useState<string>('');
  const [ethereumTxHashValid, setEthereumTxHashValid] = useState<boolean>(false);
  const [isBusy, setIsBusy] = useState<boolean>(false);
  const [isValid, setIsValid] = useState(false);
  const [ethereumTxHash, setEthereumTxHash] = useState<string | undefined | null>(null);

  // This preclaimEthereumAddress holds the result of `api.query.claims.preclaims`:
  // - an `EthereumAddress` when there's a preclaim
  // - null if no preclaim
  // - `PRECLAIMS_LOADING` if we're fetching the results
  const [preclaimEthereumAddress, setPreclaimEthereumAddress] = useState<string | null | undefined | typeof PRECLAIMS_LOADING>(PRECLAIMS_LOADING);
  const isPreclaimed = !!preclaimEthereumAddress && preclaimEthereumAddress !== PRECLAIMS_LOADING;
  const claimLimit = useCall<BalanceOf>(api.query.claims.claimLimit);

  // Everytime we change account, reset everything, and check if the accountId
  // has a preclaim.
  useEffect(() => {
    if (!accountId) {
      return;
    }

    setStep(Step.Account);
    setEthereumAddress(null);
    setEthereumTxHash(null);
    setPreclaimEthereumAddress(PRECLAIMS_LOADING);

    if (!api.query.claims || !api.query.claims.preclaims) {
      return setPreclaimEthereumAddress(null);
    }

    api.query.claims
      .preclaims<Option<EthereumAddress>>(accountId)
      .then((preclaim): void => {
        const address = preclaim.unwrapOr(null)?.toString();

        setEthereumAddress(address);
        setPreclaimEthereumAddress(address);
      })
      .catch((): void => setPreclaimEthereumAddress(null));
  }, [accountId, api.query.claims, api.query.claims.preclaims]);

  // Old claim process used `api.tx.claims.claim`, and didn't have attest
  const isOldClaimProcess = !api.tx.claims.claimAttest;

  useEffect(() => {
    if (didCopy) {
      setTimeout((): void => {
        setDidCopy(false);
      }, 1000);
    }
  }, [didCopy]);

  const goToStepAccount = useCallback(() => {
    setStep(Step.Account);
    setEthereumTxHash("");
    setEthereumTxHashValid(false);
  }, []);

  const goToStepSign = useCallback(() => {
    setStep(Step.Sign);
  }, []);

  const goToStepClaim = useCallback(() => {
    setStep(Step.Claim);
  }, []);

  const handleAccountStep = useCallback(async () => {
    setIsBusy(true);
    const result = await httpPost("https://bridge-api.crust.network/claim/" + ethereumTxHash);

    setIsBusy(false);
    setResult(result.statusText);
    setStatus(result.status);

    if (result.code == 200) {
      setStatusOpen(true);
      setEthereumTxHashValid(true);
      goToStepSign();
    } else {
      api.query.claims
        .claims<Option<BalanceOf>>(ethereumTxHash?.toString())
        .then((claim): void => {
          const claimOpt = JSON.parse(JSON.stringify(claim));

          if (claimOpt) {
            api.query.claims
              .claimed<Option<BalanceOf>>(ethereumTxHash?.toString())
              .then((claimed): void => {
                const isClaimed = JSON.parse(JSON.stringify(claimed));

                if (isClaimed) {
                  setStatusOpen(true);
                } else {
                  setStatusOpen(true);
                  setResult('MintClaimSuccess');
                  setStatus('success');
                  setEthereumTxHashValid(true);
                  goToStepSign();
                }
              });
          } else {
            setStatusOpen(true);
          }
        })
        .catch((): void => setIsBusy(false));
    }
  }, [ethereumAddress, goToStepClaim, goToStepSign, isPreclaimed, isOldClaimProcess, ethereumTxHash]);

  const onChangeEthereumTxHash = useCallback((hex: string) => {
    let [isValid, value] = convertInput(hex);

    isValid = isValid && (
      length !== -1
        ? value.length === 32
        : value.length !== 0
    );
    setIsValid(isValid);
    setEthereumTxHash(hex.trim());
  }, [ethereumTxHash]);

  function convertInput (value: string): [boolean, Uint8Array] {
    if (value === '0x') {
      return [true, new Uint8Array([])];
    } else if (value.startsWith('0x')) {
      try {
        return [true, hexToU8a(value)];
      } catch (error) {
        return [false, new Uint8Array([])];
      }
    }

    // maybe it is an ss58?
    try {
      return [true, decodeAddress(value)];
    } catch (error) {
      // we continue
    }

    return isAscii(value)
      ? [true, stringToU8a(value)]
      : [value === '0x', new Uint8Array([])];
  }

  // Depending on the account, decide which step to show.
  // const handleAccountStep = useCallback(() => {
  //   if (isPreclaimed) {
  //     goToStepClaim();
  //   } else if (ethereumAddress || isOldClaimProcess) {
  //     goToStepSign();
  //   } else {
  //     setStep(Step.ETHAddress);
  //   }
  // }, [ethereumAddress, goToStepClaim, goToStepSign, isPreclaimed, isOldClaimProcess]);

  const onChangeSignature = useCallback((event: React.SyntheticEvent<Element>) => {
    const { value: signatureJson } = event.target as HTMLInputElement;

    const { ethereumAddress, signature } = recoverFromJSON(signatureJson);

    setEthereumAddress(ethereumAddress?.toString());
    setSignature(signature);
  }, []);

  const onChangeEthereumAddress = useCallback((value: string) => {
    // FIXME We surely need a better check than just a trim

    setEthereumAddress(value.trim());
  }, []);

  const onCopy = useCallback(() => {
    setDidCopy(true);
  }, []);

  // If it's 1/ not preclaimed and 2/ not the old claiming process, fetch the
  // statement kind to sign.
  const statementKind = useCall<StatementKind | null>(!isPreclaimed && !isOldClaimProcess && !!ethereumAddress && api.query.claims.signing, [ethereumAddress], transformStatement);

  const statementSentence = getStatement(systemChain, statementKind)?.sentence || '';

  const prefix = u8aToString(api.consts.claims.prefix.toU8a(true));
  const payload = accountId
    ? `${prefix}${u8aToHex(decodeAddress(accountId), -1, false)}${statementSentence}${ethereumTxHash?.substring(2)}`
    : '';

  return (
    <main>
      {!isOldClaimProcess && <Warning />}
      <h1>
        <Trans>Claim your <em>{TokenUnit.abbr}</em> tokens</Trans>
      </h1>
      <Columar>
        <Columar.Column>
          <Card withBottomMargin>
            <h3><span style={{"wordWrap": "break-word", "wordBreak": "break-all"}}>{t<string>(`0. Please make sure you have the authority to make signature with the private key of the wallet account `)}<span style={{ 'fontWeight': 'bold' }}>({t<string>('address: ')}<a href='https://etherscan.io/address/0x17a9037cdfb24ffcc13697d03c3bcd4dff34732b' target="_blank">0x17A9037cdFB24FfcC13697d03C3bcd4DFF34732b</a>)</span><span>{t<string>(', using an exchange account to sent a transfer (withdrawal) transaction will be invalidated and cause asset loss.')}</span> <span style={{ 'fontWeight': 'bold', 'color': 'red' }}>{t<string>(` This is Maxwell's claim. If you want to claim your tokens to the mainnet, please click on the upper left corner to switch to the mainnet, You are responsible for the consequences!`)}</span></span></h3>
            <img style={{'marginLeft': 'auto', 'marginRight': 'auto', 'display': 'block', "width": "150px" }} src={claimPng as string} />
          </Card>
          {(<Card withBottomMargin>
            <h3>{t<string>(`1. Select your {{chain}} account and enter`, {
                replace: {
                  chain: systemChain
                }
              })} <a href='https://etherscan.io/token/0x32a7C02e79c4ea1008dD6564b35F131428673c41'>{t('ERC20 CRU')}</a> {t<string>('transfer tx hash')}, <span>{t<string>(`The remaining claim limit is `)}<span style={{'color': '#ff8812', 'textDecoration': 'underline', 'fontStyle': 'italic'}}>{formatBalance(claimLimit, { withUnit: 'CRU' })}</span><span>{t<string>(`, If your claim amount is greater than the claim limit, please wait for the limit update`)}</span></span> </h3>
            <InputAddress
              defaultValue={accountId}
              help={t<string>('The account you want to claim to.')}
              isDisabled={ethereumTxHashValid}
              label={t<string>('claim to account')}
              onChange={setAccountId}
              type='account'
            />
            <Input
              autoFocus
              className='full'
              help={t<string>('The Ethereum CRU transfer tx hash (starting by "0x")')}
              isDisabled={ethereumTxHashValid}
              isError={!isValid}
              label={t<string>('Ethereum tx hash')}
              onChange={onChangeEthereumTxHash}
              placeholder={t<string>('0x prefixed hex, e.g. 0x1234 or ascii data')}
              value={ethereumTxHash || ''}
            />    
            {(step === Step.Account) && (<Button.Group>
              <Button
                icon='sign-in-alt'
                isBusy={isBusy}
                isDisabled={preclaimEthereumAddress === PRECLAIMS_LOADING || ethereumTxHash === null || ethereumTxHash === '' || !isValid}
                label={preclaimEthereumAddress === PRECLAIMS_LOADING
                  ? t<string>('Loading')
                  : t<string>('Continue')
                }
                onClick={handleAccountStep}
              />
            </Button.Group>)}   
            <HttpStatus
              isStatusOpen={statusOpen}
              message={result}
              setStatusOpen={setStatusOpen}
              status={status}
            />
          </Card>)}
          {
            // We need to know the ethereuem address only for the new process
            // to be able to know the statement kind so that the users can sign it
            (step >= Step.ETHAddress && !isPreclaimed && !isOldClaimProcess) && (
              <Card withBottomMargin>
                <h3>{t<string>('2. Enter the ETH address from the sale.')}</h3>
                <Input
                  autoFocus
                  className='full'
                  help={t<string>('The the Ethereum address you used during the pre-sale (starting by "0x")')}
                  label={t<string>('Pre-sale ethereum address')}
                  onChange={onChangeEthereumAddress}
                  value={ethereumAddress || ''}
                />
                {(step === Step.ETHAddress) && (
                  <Button.Group>
                    <Button
                      icon='sign-in-alt'
                      isDisabled={!ethereumAddress}
                      label={t<string>('Continue')}
                      onClick={goToStepSign}
                    />
                  </Button.Group>
                )}
              </Card>
            )}
          {(step >= Step.Sign && !isPreclaimed) && (
            <Card>
              <h3>{t<string>('{{step}}. Sign with your ETH address', { replace: { step: isOldClaimProcess ? '2' : '3' } })}</h3>
              {!isOldClaimProcess && (
                <Statement
                  kind={statementKind}
                  systemChain={systemChain}
                />
              )}
              <div>{t<string>('Copy the following string and sign it with the Ethereum account you used during the pre-sale in the wallet of your choice, using the string as the payload, and then paste the transaction signature object below:')}</div>
              <CopyToClipboard
                onCopy={onCopy}
                text={payload}
              >
                <Payload
                  data-for='tx-payload'
                  data-tip
                >
                  {payload}
                </Payload>
              </CopyToClipboard>
              <Tooltip
                place='right'
                text={didCopy ? t<string>('copied') : t<string>('click to copy')}
                trigger='tx-payload'
              />
              <div>{t<string>('Paste the signed message into the field below. The placeholder text is there as a hint to what the message should look like:')}</div>
              <Signature
                onChange={onChangeSignature}
                placeholder={`{\n  "address": "0x ...",\n  "msg": "${prefix}...",\n  "sig": "0x ...",\n  "version": "3",\n  "signer": "..."\n}`}
                rows={10}
              />
              {(step === Step.Sign) && (
                <Button.Group>
                  <Button
                    icon='sign-in-alt'
                    isDisabled={!accountId || !signature}
                    label={t<string>('Confirm claim')}
                    onClick={goToStepClaim}
                  />
                </Button.Group>
              )}
            </Card>
          )}
        </Columar.Column>
        <Columar.Column>
          
          {(step >= Step.Claim) && (
            isPreclaimed
              ? <AttestDisplay
                accountId={accountId}
                ethereumAddress={ethereumAddress}
                onSuccess={goToStepAccount}
                statementKind={statementKind}
                systemChain={systemChain}
              />
              : <ClaimDisplay
                accountId={accountId}
                ethereumAddress={ethereumAddress}
                ethereumSignature={signature}
                isOldClaimProcess={isOldClaimProcess}
                onSuccess={goToStepAccount}
                statementKind={statementKind}
                ethereumTxHash={ethereumTxHash}
              />
          )}
        </Columar.Column>
      </Columar>
    </main>
  );
}
Example #25
Source File: PageFeedback.tsx    From nosgestesclimat-site with MIT License 4 votes vote down vote up
export default function PageFeedback({
	customMessage,
	customEventName
}: PageFeedbackProps) {
	const location = useLocation()
	const tracker = useContext(TrackerContext)
	const [state, setState] = useState({
		showForm: false,
		showThanks: false,
		feedbackAlreadyGiven: feedbackAlreadyGiven([
			customEventName || 'rate page usefulness',
			location.pathname
		])
	})

	const handleFeedback = useCallback(({ useful }: { useful: boolean }) => {
		tracker.push([
			'trackEvent',
			'Feedback',
			useful ? 'positive rating' : 'negative rating',
			location.pathname
		])
		const feedback = [
			customEventName || 'rate page usefulness',
			location.pathname,
			useful ? 10 : 0.1
		] as [string, string, number]
		tracker.push(['trackEvent', 'Feedback', ...feedback])
		saveFeedbackOccurrenceInLocalStorage(feedback)
		setState({
			showThanks: useful,
			feedbackAlreadyGiven: true,
			showForm: !useful
		})
	}, [])

	const handleErrorReporting = useCallback(() => {
		tracker.push(['trackEvent', 'Feedback', 'report error', location.pathname])
		setState({ ...state, showForm: true })
	}, [])

	if (state.feedbackAlreadyGiven && !state.showForm && !state.showThanks) {
		return null
	}

	return (
		<div
			className="ui__ container"
			style={{ display: 'flex', justifyContent: 'center' }}
		>
			<div className="feedback-page ui__ notice ">
				{!state.showForm && !state.showThanks && (
					<>
						<div style={{ flexShrink: 0 }}>
							{customMessage || (
								<Trans i18nKey="feedback.question">
									Cette page vous est utile ?
								</Trans>
							)}{' '}
						</div>
						<div className="feedbackButtons">
							<button
								className="ui__ link-button"
								onClick={() => handleFeedback({ useful: true })}
							>
								<Trans>Oui</Trans>
							</button>{' '}
							<button
								className="ui__ link-button"
								onClick={() => handleFeedback({ useful: false })}
							>
								<Trans>Non</Trans>
							</button>
							<button
								className="ui__ link-button"
								onClick={handleErrorReporting}
							>
								<Trans i18nKey="feedback.reportError">
									Faire une suggestion
								</Trans>
							</button>
						</div>
					</>
				)}
				{state.showThanks && (
					<div>
						<Trans i18nKey="feedback.thanks">
							Merci pour votre retour ! Vous pouvez nous contacter directement à{' '}
							<a href="mailto:[email protected]">
								[email protected]
							</a>
						</Trans>
					</div>
				)}
				{state.showForm && (
					<Form
						onEnd={() =>
							setState({ ...state, showThanks: true, showForm: false })
						}
						onCancel={() =>
							setState({ ...state, showThanks: false, showForm: false })
						}
					/>
				)}
			</div>
		</div>
	)
}
Example #26
Source File: index.tsx    From rocketredis with MIT License 4 votes vote down vote up
DeleteConnectionModal: React.FC<ConnectionModalProps> = ({
  visible,
  onRequestClose,
  connectionToDelete
}) => {
  const formRef = useRef<FormHandles>(null)
  const { t } = useTranslation('deleteConnection')
  const { addToast } = useToast()
  const setConnections = useSetRecoilState(connectionsState)

  const [deleteConnectionLoading, toggleDeleteConnectionLoading] = useToggle(
    false
  )

  const handleCloseModal = useCallback(() => {
    if (onRequestClose) {
      onRequestClose()
    }
  }, [onRequestClose])

  const handleDeleteConnection = useCallback(
    async ({ confirmation: deleteConfirmation }: DeleteConnectionFormData) => {
      try {
        toggleDeleteConnectionLoading()

        formRef.current?.setErrors({})

        if (deleteConfirmation !== 'DELETE') {
          formRef.current?.setErrors({
            confirmation: t('form.unconfirmedDeletionError')
          })

          return
        }

        const connections = deleteAndGetConnections(connectionToDelete)

        setConnections(connections)

        addToast({
          type: 'success',
          title: 'Connection deleted',
          description: 'This connection will not be available anymore.'
        })

        handleCloseModal()
      } catch (error) {
        addToast({
          type: 'error',
          title: 'Error deleting connection',
          description: error.message || 'Unexpected error occurred, try again.'
        })
      } finally {
        toggleDeleteConnectionLoading()
      }
    },
    [
      toggleDeleteConnectionLoading,
      t,
      addToast,
      connectionToDelete,
      setConnections,
      handleCloseModal
    ]
  )

  return (
    <Modal visible={visible} onRequestClose={onRequestClose}>
      <h1>{t('title')}</h1>

      <TextContent>
        <p>
          <Trans
            t={t}
            i18nKey="deletingConnectionMessage"
            values={{ name: connectionToDelete.name }}
          >
            The connection <strong>{name}</strong> will be permanently deleted.
          </Trans>
        </p>

        <p>
          <Trans t={t} i18nKey="confirmDeletionMessage" />
        </p>
      </TextContent>

      <Form ref={formRef} onSubmit={handleDeleteConnection}>
        <Input name="confirmation" />

        <ActionsContainer>
          <ButtonGroup>
            <Button onClick={handleCloseModal} type="button" color="opaque">
              {t('form.cancel')}
            </Button>

            <Button loading={deleteConnectionLoading} type="submit" color="red">
              <FiTrash />
              {t('form.confirmDeletion')}
            </Button>
          </ButtonGroup>
        </ActionsContainer>
      </Form>
    </Modal>
  )
}
Example #27
Source File: DataView.tsx    From next-basics with GNU General Public License v3.0 4 votes vote down vote up
export function DataView({
  onContextUpdate,
}: DataViewProps): React.ReactElement {
  const { nodes } = useBuilderData();
  const { t } = useTranslation(NS_NEXT_BUILDER);
  const rootNode = useBuilderNode({ isRoot: true });
  const contextWithUniqueSymbolId: ContextConfWithSymbolId[] = useMemo(() => {
    const originContext = ((rootNode?.context as ContextConf[]) ?? []).map(
      (v) => ({
        ...v,
        [symbolId]: uniqueId(),
      })
    );
    return originContext;
  }, [rootNode?.context]);

  const [settingItemForm] = Form.useForm();
  const [q, setQ] = useState<string>("");
  const [visible, setVisible] = useState<boolean>(false);
  const [settingItem, setSettingItem] = useState<ContextConf>();
  const settingUid = useRef<string | undefined>();
  const [hoverContextName, setHoverContextName] = useState<string>();
  const manager = useBuilderDataManager();
  const [highlightedContexts, setHighlightedContexts] = useState(
    new Set<string>()
  );

  const handleSearch = (value: string): void => {
    setQ(value);
  };

  const filteredContextList: ContextConfWithSymbolId[] = useMemo(
    () => deepFilter(contextWithUniqueSymbolId, q),
    [contextWithUniqueSymbolId, q]
  );

  useEffect(() => {
    const nodesToHighlight = new Set<number>();
    if (hoverContextName) {
      nodes.forEach((node) => {
        if (scanContextsInAny(node.$$normalized).includes(hoverContextName)) {
          nodesToHighlight.add(node.$$uid);
        }
      });
    }
    manager.setHighlightNodes(nodesToHighlight);
  }, [hoverContextName, manager, nodes]);

  useEffect(() => {
    const highlights = new Set<string>();
    if (hoverContextName) {
      contextWithUniqueSymbolId.forEach((ctx) => {
        if (
          ctx.name !== hoverContextName &&
          scanContextsInAny(ctx).includes(hoverContextName)
        ) {
          highlights.add(ctx[symbolId]);
        }
      });
    }
    setHighlightedContexts(highlights);
  }, [hoverContextName, contextWithUniqueSymbolId]);

  const setData = (contextValue?: ContextConf, uid?: string): void => {
    const isValue = !contextValue?.resolve;
    settingItemForm.resetFields();
    if (isValue) {
      const formValue = {
        name: contextValue?.name,
        type: ContextType.VALUE,
        ...safeDumpFields({
          value: contextValue?.value,
          if: contextValue?.if,
          onChange: contextValue?.onChange,
        }),
      };
      settingItemForm.setFieldsValue(formValue);
    } else {
      const formValue = {
        name: contextValue?.name,
        ...((contextValue.resolve as SelectorProviderResolveConf).provider
          ? {
              type: ContextType.SELECTOR_RESOLVE,
              provider: (contextValue.resolve as SelectorProviderResolveConf)
                .provider,
            }
          : (
              contextValue?.resolve as UseProviderResolveConf
            ).useProvider?.includes("@")
          ? {
              type: ContextType.FLOW_API,
              flowApi: (contextValue.resolve as UseProviderResolveConf)
                .useProvider,
            }
          : {
              type: ContextType.RESOLVE,
              useProvider: (contextValue.resolve as UseProviderResolveConf)
                .useProvider,
            }),
        ...safeDumpFields({
          args: (contextValue.resolve as EntityResolveConf).args,
          value: contextValue.value,
          if: contextValue.if,
          resolveIf: contextValue.resolve.if,
          transform: contextValue.resolve.transform,
          onReject: contextValue.resolve.onReject,
          onChange: contextValue?.onChange,
        }),
      };
      settingItemForm.setFieldsValue(formValue);
    }

    setSettingItem(contextValue);
    setVisible(true);
    settingUid.current = uid;
  };

  const handleOk = (): void => {
    settingItemForm.submit();
  };

  const handleContextItemUpdate = (
    contextItem: ContextConfWithSymbolId
  ): void => {
    const targetIndex = settingUid.current
      ? findIndex(contextWithUniqueSymbolId, [symbolId, settingUid.current])
      : contextWithUniqueSymbolId.length;
    const newContext = update(contextWithUniqueSymbolId, {
      [targetIndex]: {
        $set: contextItem,
      },
    });
    onContextUpdate?.(newContext);
    setVisible(false);
  };

  const handleCancel = (): void => {
    setVisible(false);
  };

  const handleContextItemDelete = (
    e: React.MouseEvent<HTMLElement, MouseEvent>,
    dataItem: ContextConfWithSymbolId
  ): void => {
    e.stopPropagation();
    // istanbul ignore next
    Modal.confirm({
      title: "Delete Confirm",
      icon: <ExclamationCircleOutlined />,
      content: (
        <span>
          Are you sure delete data{" "}
          <strong className={styles.dangerText}>{dataItem.name}</strong>?
        </span>
      ),
      okText: "Yes",
      okType: "danger",
      cancelText: "No",
      onOk() {
        const contextToSubmit = contextWithUniqueSymbolId.filter(
          (v) => v[symbolId] !== dataItem[symbolId]
        );
        onContextUpdate?.(contextToSubmit);
      },
    });
  };

  const handleDropItem = (dragIndex: number, hoverIndex: number): void => {
    if (dragIndex === hoverIndex) {
      return;
    }
    const dragItem = contextWithUniqueSymbolId[dragIndex];
    const newContext = update(contextWithUniqueSymbolId, {
      $splice: [
        [dragIndex, 1],
        [hoverIndex, 0, dragItem],
      ],
    });
    onContextUpdate?.(newContext);
  };

  const handleItemHover = (contextName?: string): void => {
    setHoverContextName(contextName);
  };

  return (
    <ToolboxPane
      title={t(K.DATA)}
      tooltips={
        <>
          <p>
            <Trans t={t} i18nKey={K.DATA_VIEW_TIPS_1} />
          </p>
          <p>
            <Trans t={t} i18nKey={K.DATA_VIEW_TIPS_2} />
          </p>
          <p>
            <Trans t={t} i18nKey={K.DATA_VIEW_TIPS_3} />
          </p>
        </>
      }
    >
      <SearchComponent placeholder={t(K.SEARCH_DATA)} onSearch={handleSearch} />
      <div className={styles.wrapper}>
        <Button
          icon={<PlusOutlined />}
          size="small"
          onClick={() => setData()}
          data-testid="add-data-btn"
        >
          {t(K.ADD_DATA)}
        </Button>
        <div
          className={`${styles.varList} ${sharedStyles.customScrollbarContainer}`}
        >
          {filteredContextList?.length > 0 &&
            filteredContextList.map((data, index) => (
              <ContextItem
                handleItemHover={handleItemHover}
                data={data}
                handleItemClick={() => setData(data, data[symbolId])}
                handleItemDelete={(e) => handleContextItemDelete(e, data)}
                handleDropItem={handleDropItem}
                index={index}
                canDrag={!q}
                key={data[symbolId]}
                highlighted={highlightedContexts.has(data[symbolId])}
              />
            ))}
        </div>
      </div>
      <ContextItemFormModal
        data={settingItem}
        onContextItemUpdate={handleContextItemUpdate}
        settingItemForm={settingItemForm}
        visible={visible}
        onOk={handleOk}
        onCancel={handleCancel}
      />
    </ToolboxPane>
  );
}
Example #28
Source File: AddChild.tsx    From tobira with Apache License 2.0 4 votes vote down vote up
AddChild: React.FC<Props> = ({ parent }) => {
    const { t } = useTranslation();

    type FormData = {
        name: string;
        pathSegment: string;
    };

    const { register, handleSubmit, formState: { errors } } = useForm<FormData>();
    const [commitError, setCommitError] = useState<JSX.Element | null>(null);

    const router = useRouter();

    const [commit, isInFlight] = useMutation(addChildMutation);
    const onSubmit = handleSubmit(data => {
        commit({
            variables: {
                realm: {
                    parent: parent.id,
                    name: data.name,
                    pathSegment: data.pathSegment,
                },
            },
            onCompleted: response => {
                const typedResponse = response as AddChildMutation$data;
                const path = pathToQuery(typedResponse.addRealm.path);
                router.goto(`/~manage/realm/content?path=${path}`);
            },
            onError: error => {
                setCommitError(displayCommitError(error, t("manage.add-child.failed-to-add")));
            },
        });
    });

    const validations = realmValidations(t);
    const breadcrumbs = (parent.isRoot ? parent.ancestors : parent.ancestors.concat(parent))
        .map(({ name, path }) => ({ label: name, link: path }));

    return (
        <RealmSettingsContainer>
            <Breadcrumbs path={breadcrumbs} tail={<i>{t("realm.add-sub-page")}</i>} />
            <PageTitle title={t("manage.add-child.heading")} />
            <p>
                {
                    parent.isRoot
                        ? t("manage.add-child.below-root")
                        : <Trans i18nKey="manage.add-child.below-this-parent">
                            {{ parent: parent.name }}
                        </Trans>
                }
            </p>
            <Form
                onSubmit={onSubmit}
                css={{
                    margin: "32px 0",
                    "& > div": { marginBottom: 32 },
                }}
            >
                <InputWithInfo info={t("manage.add-child.page-name-info")}>
                    <label htmlFor="name-field">{t("manage.realm.general.rename-label")}</label>
                    <Input
                        id="name-field"
                        css={{ width: 350, maxWidth: "100%" }}
                        placeholder={t("manage.realm.general.rename-label")}
                        error={!!errors.name}
                        autoFocus
                        {...register("name", validations.name)}
                    />
                    {boxError(errors.name?.message)}
                </InputWithInfo>

                <InputWithInfo
                    info={<Trans i18nKey="manage.add-child.path-segment-info">
                        {{ illegalChars: ILLEGAL_CHARS, reservedChars: RESERVED_CHARS }}
                    </Trans>}
                >
                    <label htmlFor="path-field">{t("manage.add-child.path-segment")}</label>
                    <PathSegmentInput
                        id="path-field"
                        base={parent.path}
                        error={!!errors.pathSegment}
                        {...register("pathSegment", validations.path)}
                    />
                    {boxError(errors.pathSegment?.message)}
                </InputWithInfo>

                <div>
                    <div css={{ display: "flex", alignItems: "center", gap: 16 }}>
                        <Button type="submit" kind="happy" disabled={isInFlight}>
                            {t("manage.add-child.button-create-page")}
                        </Button>
                        {isInFlight && <Spinner size={20} />}
                    </div>
                    {boxError(commitError)}
                </div>
            </Form>
        </RealmSettingsContainer>
    );
}
Example #29
Source File: SubstatInput.tsx    From genshin-optimizer with MIT License 4 votes vote down vote up
export default function SubstatInput({ index, artifact, setSubstat }: { index: number, artifact: ICachedArtifact | undefined, setSubstat: (index: number, substat: ISubstat) => void, }) {
  const { t } = useTranslation("artifact")
  const { mainStatKey = "", rarity = 5 } = artifact ?? {}
  const { key = "", value = 0, rolls = [], efficiency = 0 } = artifact?.substats[index] ?? {}

  const accurateValue = rolls.reduce((a, b) => a + b, 0)
  const unit = KeyMap.unit(key), rollNum = rolls.length

  let error: string = "", rollData: readonly number[] = [], allowedRolls = 0

  if (artifact) {
    // Account for the rolls it will need to fill all 4 substates, +1 for its base roll
    const rarity = artifact.rarity
    const { numUpgrades, high } = Artifact.rollInfo(rarity)
    const maxRollNum = numUpgrades + high - 3;
    allowedRolls = maxRollNum - rollNum
    rollData = key ? Artifact.getSubstatRollData(key, rarity) : []
  }
  const rollOffset = 7 - rollData.length

  if (!rollNum && key && value) error = error || t`editor.substat.error.noCalc`
  if (allowedRolls < 0) error = error || t("editor.substat.error.noOverRoll", { value: allowedRolls + rollNum })

  return <CardLight>
    <Box sx={{ display: "flex" }}>
      <ButtonGroup size="small" sx={{ width: "100%", display: "flex" }}>
        <DropdownButton
          startIcon={key ? StatIcon[key] : undefined}
          title={key ? KeyMap.getArtStr(key) : t('editor.substat.substatFormat', { value: index + 1 })}
          disabled={!artifact}
          color={key ? "success" : "primary"}
          sx={{ whiteSpace: "nowrap" }}>
          {key && <MenuItem onClick={() => setSubstat(index, { key: "", value: 0 })}>{t`editor.substat.noSubstat`}</MenuItem>}
          {allSubstatKeys.filter(key => mainStatKey !== key)
            .map(k => <MenuItem key={k} selected={key === k} disabled={key === k} onClick={() => setSubstat(index, { key: k, value: 0 })} >
              <ListItemIcon>{StatIcon[k]}</ListItemIcon>
              <ListItemText>{KeyMap.getArtStr(k)}</ListItemText>
            </MenuItem>)}
        </DropdownButton>
        <CustomNumberInputButtonGroupWrapper sx={{ flexBasis: 30, flexGrow: 1 }} >
          <CustomNumberInput
            float={unit === "%"}
            placeholder={t`editor.substat.selectSub`}
            value={key ? value : undefined}
            onChange={value => setSubstat(index, { key, value: value ?? 0 })}
            disabled={!key}
            error={!!error}
            sx={{
              px: 1,
            }}
            inputProps={{
              sx: { textAlign: "right" }
            }}
          />
        </CustomNumberInputButtonGroupWrapper>
        {!!rollData.length && <TextButton>{t`editor.substat.nextRolls`}</TextButton>}
        {rollData.map((v, i) => {
          let newValue = cacheValueString(accurateValue + v, unit)
          newValue = artifactSubstatRollCorrection[rarity]?.[key]?.[newValue] ?? newValue
          return <Button key={i} color={`roll${clamp(rollOffset + i, 1, 6)}` as any} disabled={(value && !rollNum) || allowedRolls <= 0} onClick={() => setSubstat(index, { key, value: parseFloat(newValue) })}>{newValue}</Button>
        })}
      </ButtonGroup>
    </Box>
    <Box sx={{ p: 1, }}>
      {error ? <SqBadge color="error">{t`ui:error`}</SqBadge> : <Grid container>
        <Grid item>
          <SqBadge color={rollNum === 0 ? "secondary" : `roll${clamp(rollNum, 1, 6)}`}>
            {rollNum ? t("editor.substat.RollCount", { count: rollNum }) : t`editor.substat.noRoll`}
          </SqBadge>
        </Grid>
        <Grid item flexGrow={1}>
          {!!rolls.length && [...rolls].sort().map((val, i) =>
            <Typography component="span" key={`${i}.${val}`} color={`roll${clamp(rollOffset + rollData.indexOf(val), 1, 6)}.main`} sx={{ ml: 1 }} >{cacheValueString(val, unit)}</Typography>)}
        </Grid>
        <Grid item xs="auto" flexShrink={1}>
          <Typography>
            <Trans t={t} i18nKey="editor.substat.eff" color="text.secondary">
              Efficiency: <PercentBadge valid={true} max={rollNum * 100} value={efficiency ? efficiency : t`editor.substat.noStat` as string} />
            </Trans>
          </Typography>
        </Grid>
      </Grid>}

    </Box>
  </CardLight >
}