recoil#useResetRecoilState TypeScript Examples

The following examples show how to use recoil#useResetRecoilState. 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: gridLibraryContext.ts    From frames with Mozilla Public License 2.0 6 votes vote down vote up
useGridReset = () => {
    const type = useResetRecoilState(GridTypeAtom);
    const value = useResetRecoilState(GridValueAtom);

    return () => {
        type();
        value();
    }
}
Example #2
Source File: survey-list.tsx    From clean-react with GNU General Public License v3.0 6 votes vote down vote up
SurveyList: React.FC<Props> = ({ loadSurveyList }: Props) => {
  const resetSurveyListState = useResetRecoilState(surveyListState)
  const handleError = useErrorHandler((error: Error) => {
    setState(old => ({ ...old, error: error.message }))
  })
  const [state, setState] = useRecoilState(surveyListState)
  const reload = (): void => setState(old => ({ surveys: [], error: '', reload: !old.reload }))

  useEffect(() => resetSurveyListState(), [])
  useEffect(() => {
    loadSurveyList.loadAll()
      .then(surveys => setState(old => ({ ...old, surveys })))
      .catch(handleError)
  }, [state.reload])

  return (
    <div className={Styles.surveyListWrap}>
      <Header />
      <div className={Styles.contentWrap}>
        <h2>Enquetes</h2>
        {state.error
          ? <Error error={state.error} reload={reload} />
          : <SurveyListItem surveys={state.surveys} />
        }
      </div>
      <Footer />
    </div>
  )
}
Example #3
Source File: ErrorPopup.tsx    From nextclade with MIT License 6 votes vote down vote up
export function ErrorPopup() {
  const { t } = useTranslationSafe()
  const error = useRecoilValue(globalErrorAtom)
  const dismissError = useResetRecoilState(globalErrorAtom)

  const reload = useReloadPage('/')

  if (!error) {
    return null
  }

  return (
    <Modal centered isOpen backdrop="static" toggle={dismissError} fade={false} size="lg">
      <ModalHeader toggle={dismissError} tag="div">
        <h3 className="text-center text-danger">{t('Error')}</h3>
      </ModalHeader>

      <ModalBody>
        <ErrorContent error={error} />
        <ErrorContentExplanation />
      </ModalBody>

      <ModalFooter>
        <div className="ml-auto">
          <Button type="button" color="danger" title={t('Reload the page and start Nextclade fresh')} onClick={reload}>
            {t('Restart Nextclade')}
          </Button>
          <ButtonOk type="button" color="secondary" title={t('Close this dialog window')} onClick={dismissError}>
            {t('Dismiss')}
          </ButtonOk>
        </div>
      </ModalFooter>
    </Modal>
  )
}
Example #4
Source File: opacityScroll.ts    From frames with Mozilla Public License 2.0 6 votes vote down vote up
export default function useOnScroll() {
    const [reference, setReference] = useRecoilState(OnScrollReference);
    const setStart = useSetRecoilState(StartRefHeight);
    const setCurrent = useSetRecoilState(CurrentRefHeight);
    const values = useRecoilValue(OpacitySelector);
    const setNavOpacity = useSetRecoilState(NavOpacityAtom);

    const refReset = useResetRecoilState(OnScrollReference);
    const currentReset = useResetRecoilState(CurrentRefHeight);
    const startRefReset = useResetRecoilState(StartRefHeight);

    const onScroll = () => {
        const currentHeight = reference?.getBoundingClientRect().top;
        setCurrent(currentHeight);
    }

    useEffect(() => {
        setNavOpacity((values.height < 0.5 ? 1.3: 0.9) - values.height);
    }, [values])

    const reset = () => {
        refReset();
        currentReset();
        startRefReset();
    }

    useEffect(() => {
        const height = reference?.getBoundingClientRect().top;
        setStart(height);
    }, [reference])

    return {reset, values, setReference, onScroll};
}
Example #5
Source File: infoContext.ts    From frames with Mozilla Public License 2.0 6 votes vote down vote up
resetInfo = () => {
    const trailer = useResetRecoilState(infoTrailerContext);
    const season = useResetRecoilState(InfoSeasonsContext);
    const media = useResetRecoilState(InfoMediaIdContext);
    const section = useResetRecoilState(InfoSectionContext);
    const sections = useResetRecoilState(InfoSectionsContext);

    return () => {
        trailer();
        section();
        season();
        media();
        sections();
    }
}
Example #6
Source File: homeContext.tsx    From frames with Mozilla Public License 2.0 6 votes vote down vote up
export function useReset() {
    const pGraph = useResetRecoilState(pGraphAtom);
    const image = useResetRecoilState(imageAtom);
    const section = useResetRecoilState(sectionAtom);
    const sHeight = useResetRecoilState(startHeight);
    const trailer = useResetRecoilState(TrailerAtom);
    const cb = useResetRecoilState(CurrentBanner);

    return () => {
        cb();
        trailer();
        sHeight();
        section();
        image();
        pGraph();
    }
}
Example #7
Source File: gridLibraryContext.ts    From frames with Mozilla Public License 2.0 6 votes vote down vote up
useGridReset = () => {
    const type = useResetRecoilState(GridTypeAtom);
    const value = useResetRecoilState(GridValueAtom);
    const onScreen = useResetRecoilState(GridOnScreenAtom);

    return () => {
        type();
        value();
        onScreen();
    }
}
Example #8
Source File: authContext.ts    From frames with Mozilla Public License 2.0 6 votes vote down vote up
useReset = () => {
    const error = useResetRecoilState(AuthContextErrorAtom);
    const process = useResetRecoilState(AuthContextProcessAtom);
    const email = useResetRecoilState(AuthContextEmailAtom);
    const password = useResetRecoilState(AuthContextPasswordAtom);
    const fade = useResetRecoilState(AuthFade);
    const authKey = useResetRecoilState(AuthKeyAtom);
    const picker = useResetRecoilState(AuthPicker);
    const auth = useResetRecoilState(Authenticated);

    return () => {
        auth();
        error();
        process();
        email();
        password();
        fade();
        authKey();
        picker();
    }
}
Example #9
Source File: editPicks.tsx    From frames with Mozilla Public License 2.0 6 votes vote down vote up
function useReset() {
    const media = useResetRecoilState(PickMediaAtom);
    const search = useResetRecoilState(SearchPickAtom);
    const active = useResetRecoilState(PickActiveAtom);
    const display = useResetRecoilState(PickDisplayAtom);
    const category = useResetRecoilState(PickCategoryAtom);
    const type = useResetRecoilState(PickTypeAtom);

    return () => {
        media();
        search();
        active();
        display();
        category();
        type();
    }
}
Example #10
Source File: editMedia.tsx    From frames with Mozilla Public License 2.0 6 votes vote down vote up
useReset = () => {
    const tmdb = useResetRecoilState(TmdbAtom);
    const logo = useResetRecoilState(LogoAtom);
    const backdrop = useResetRecoilState(BackdropAtom);
    const name = useResetRecoilState(NameAtom);
    const poster = useResetRecoilState(PosterAtom);
    const type = useResetRecoilState(TypeAtom);
    const location = useResetRecoilState(LocationAtom);
    const found = useResetRecoilState(FoundAtom);

    return () => {
        type();
        tmdb();
        location();
        logo();
        backdrop();
        name();
        poster();
        found();
    }
}
Example #11
Source File: opacityScroll.ts    From frames with Mozilla Public License 2.0 6 votes vote down vote up
export default function useOnScroll() {
    const [reference, setReference] = useRecoilState(OnScrollReference);
    const setStart = useSetRecoilState(StartRefHeight);
    const setCurrent = useSetRecoilState(CurrentRefHeight);
    const values = useRecoilValue(OpacitySelector);
    const setNavOpacity = useSetRecoilState(NavOpacityAtom);

    const refReset = useResetRecoilState(OnScrollReference);
    const currentReset = useResetRecoilState(CurrentRefHeight);
    const startRefReset = useResetRecoilState(StartRefHeight);

    const onScroll = () => {
        const currentHeight = reference?.getBoundingClientRect().top;
        setCurrent(currentHeight);
    }

    useEffect(() => {
        setNavOpacity((values.height < 0.5 ? 1.3: 0.9) - values.height);
    }, [values])

    const reset = () => {
        refReset();
        currentReset();
        startRefReset();
    }

    useEffect(() => {
        const height = reference?.getBoundingClientRect().top;
        setStart(height);
    }, [reference])

    return {reset, values, setReference, onScroll, navDark: values.lowOpacity > 0.7};
}
Example #12
Source File: authContext.ts    From frames with Mozilla Public License 2.0 6 votes vote down vote up
useReset = () => {
    const error = useResetRecoilState(AuthContextErrorAtom);
    const process = useResetRecoilState(AuthContextProcessAtom);
    const email = useResetRecoilState(AuthContextEmailAtom);
    const password = useResetRecoilState(AuthContextPasswordAtom);
    const fade = useResetRecoilState(AuthFade);
    const authKey = useResetRecoilState(AuthKeyAtom);
    const auth = useResetRecoilState(Authenticated);

    return () => {
        auth();
        error();
        process();
        email();
        password();
        fade();
        authKey();
    }
}
Example #13
Source File: navbar.tsx    From frames with Mozilla Public License 2.0 6 votes vote down vote up
function Sections() {
    const navContext = useRecoilValue(NavConTextAtom);
    const sections = ["home", "movies", "tv shows", "playlists", "collections"];
    const paths = ["/", "/movies", "/shows", "/playlist", "/collections"];
    const resetSearch = useResetRecoilState(SearchContextAtom);

    const reset = () => {
        resetSearch();
    };

    return (
        <div className={styles.navSections} onClick={reset}>
            {sections.map((item, v) => {
                return (
                    <Link key={v} href={paths[v]}>
                        <span
                            className={item === navContext ? styles.activeSection : styles.passiveSections}>{item}</span>
                    </Link>
                );
            })}
        </div>
    );
}
Example #14
Source File: infoContext.ts    From frames with Mozilla Public License 2.0 6 votes vote down vote up
resetInfo = () => {
    const season = useResetRecoilState(InfoSeasonsContext);
    const media = useResetRecoilState(InfoMediaIdContext);
    const section = useResetRecoilState(InfoSectionContext);
    const sections = useResetRecoilState(InfoSectionsContext);
    const info = useResetRecoilState(infoUserContext);

    return () => {
        section();
        season();
        media();
        sections();
        info();
    }
}
Example #15
Source File: homeContext.tsx    From frames with Mozilla Public License 2.0 6 votes vote down vote up
export function useReset() {
    const pGraph = useResetRecoilState(pGraphAtom);
    const image = useResetRecoilState(imageAtom);
    const section = useResetRecoilState(sectionAtom);
    const sHeight = useResetRecoilState(startHeight);

    return () => {
        sHeight();
        section();
        image();
        pGraph();
    }
}
Example #16
Source File: playback.ts    From frames with Mozilla Public License 2.0 5 votes vote down vote up
cleanUp = () => {
    const {user, signOut} = useUser();
    const response = useRecoilValue(framesVideoStateAtom);
    const fullscreenReset = useResetRecoilState(fullscreenAddressAtom);
    const framesVideoStateReset = useResetRecoilState(framesVideoStateAtom);
    const currentReset = useResetRecoilState(currentDuration);
    const framesReset = useResetRecoilState(framesPlayer);
    const framesPlayerStateReset = useResetRecoilState(framesPlayerStateAtom);
    const volumeFrameReset = useResetRecoilState(volumeFrameAtom);
    const displayReset = useResetRecoilState(displaySidesAtom);
    const shareAndDownloadReset = useResetRecoilState(shareAndDownloadAtom);
    const PipAndFullscreenReset = useResetRecoilState(PipAndFullscreenAtom);
    const SubtitlesAndUpNextReset = useResetRecoilState(SubtitlesAndUpNextAtom);
    const UpNextReset = useResetRecoilState(UpNextAtom);
    const HideImageReset = useResetRecoilState(HideImageAtom);
    const framesSubtitlesReset = useResetRecoilState(framesSubtitlesAtom);
    const SubtitlesSyncReset = useResetRecoilState(SubtitlesSyncAtom);
    const FramesPlayerErrorReset = useResetRecoilState(FramesPlayerErrorAtom);
    const CastEventReset = useResetRecoilState(CastEventAtom);
    const CastStateReset = useResetRecoilState(VideoStateAtom);
    const AirplayReset = useResetRecoilState(AirplayAtom);
    const alreadyStreamingReset = useResetRecoilState(AlreadyStreamingAtom);
    const {channel} = useGroupWatch();
    const {globalChannel} = useNotification();

    return  () => {
        channel.modifyPresenceState('online');
        globalChannel.modifyPresenceState('online');
        fetch('/api/stream/verifyStream?action=done');
        if (user?.role === Role.GUEST && response?.frame)
            signOut();

        fullscreenReset();
        framesVideoStateReset();
        currentReset();
        framesReset();
        framesPlayerStateReset();
        volumeFrameReset();
        displayReset();
        shareAndDownloadReset();
        PipAndFullscreenReset();
        SubtitlesAndUpNextReset();
        UpNextReset();
        HideImageReset();
        framesSubtitlesReset();
        SubtitlesSyncReset();
        FramesPlayerErrorReset();
        CastEventReset();
        CastStateReset();
        AirplayReset();
        alreadyStreamingReset();
    }
}
Example #17
Source File: survey-result.tsx    From clean-react with GNU General Public License v3.0 5 votes vote down vote up
SurveyResult: React.FC<Props> = ({ loadSurveyResult, saveSurveyResult }: Props) => {
  const resetSurveyResultState = useResetRecoilState(surveyResultState)
  const handleError = useErrorHandler((error: Error) => {
    setState(old => ({ ...old, surveyResult: null, isLoading: false, error: error.message }))
  })
  const [state, setState] = useRecoilState(surveyResultState)
  const setOnAnswer = useSetRecoilState(onSurveyAnswerState)
  const onAnswer = (answer: string): void => {
    if (!state.isLoading) {
      setState(old => ({ ...old, isLoading: true }))
      saveSurveyResult.save({ answer })
        .then(surveyResult => setState(old => ({ ...old, isLoading: false, surveyResult })))
        .catch(handleError)
    }
  }
  const reload = (): void => setState(old => ({ ...old, error: '', reload: !old.reload }))

  useEffect(() => {
    resetSurveyResultState()
    setOnAnswer({ onAnswer })
  }, [])

  useEffect(() => {
    loadSurveyResult.load()
      .then(surveyResult => setState(old => ({ ...old, surveyResult })))
      .catch(handleError)
  }, [state.reload])

  return (
    <div className={Styles.surveyResultWrap}>
      <Header />
      <div data-testid="survey-result" className={Styles.contentWrap}>
        {state.surveyResult && <SurveyResultData surveyResult={state.surveyResult} /> }
        {state.isLoading && <Loading />}
        {state.error && <Error error={state.error} reload={reload} />}
      </div>
      <Footer />
    </div>
  )
}
Example #18
Source File: signup.tsx    From clean-react with GNU General Public License v3.0 5 votes vote down vote up
SignUp: React.FC<Props> = ({ validation, addAccount }: Props) => {
  const resetSignUpState = useResetRecoilState(signUpState)
  const { setCurrentAccount } = useRecoilValue(currentAccountState)
  const history = useHistory()
  const [state, setState] = useRecoilState(signUpState)

  useEffect(() => resetSignUpState(), [])
  useEffect(() => validate('name'), [state.name])
  useEffect(() => validate('email'), [state.email])
  useEffect(() => validate('password'), [state.password])
  useEffect(() => validate('passwordConfirmation'), [state.passwordConfirmation])

  const validate = (field: string): void => {
    const { name, email, password, passwordConfirmation } = state
    const formData = { name, email, password, passwordConfirmation }
    setState(old => ({ ...old, [`${field}Error`]: validation.validate(field, formData) }))
    setState(old => ({ ...old, isFormInvalid: !!old.nameError || !!old.emailError || !!old.passwordError || !!old.passwordConfirmationError }))
  }

  const handleSubmit = async (event: React.FormEvent<HTMLFormElement>): Promise<void> => {
    event.preventDefault()
    try {
      if (state.isLoading || state.isFormInvalid) {
        return
      }
      setState(old => ({ ...old, isLoading: true }))
      const account = await addAccount.add({
        name: state.name,
        email: state.email,
        password: state.password,
        passwordConfirmation: state.passwordConfirmation
      })
      setCurrentAccount(account)
      history.replace('/')
    } catch (error) {
      setState(old => ({
        ...old,
        isLoading: false,
        mainError: error.message
      }))
    }
  }

  return (
    <div className={Styles.signupWrap}>
      <LoginHeader />
      <form data-testid="form" className={Styles.form} onSubmit={handleSubmit}>
        <h2>Criar Conta</h2>
        <Input type="text" name="name" placeholder="Digite seu nome" />
        <Input type="email" name="email" placeholder="Digite seu e-mail" />
        <Input type="password" name="password" placeholder="Digite sua senha" />
        <Input type="password" name="passwordConfirmation" placeholder="Repita sua senha" />
        <SubmitButton text="Cadastrar" />
        <Link data-testid="login-link" replace to="/login" className={Styles.link}>Voltar Para Login</Link>
        <FormStatus />
      </form>
      <Footer />
    </div>
  )
}
Example #19
Source File: login.tsx    From clean-react with GNU General Public License v3.0 5 votes vote down vote up
Login: React.FC<Props> = ({ validation, authentication }: Props) => {
  const resetLoginState = useResetRecoilState(loginState)
  const { setCurrentAccount } = useRecoilValue(currentAccountState)
  const history = useHistory()
  const [state, setState] = useRecoilState(loginState)

  useEffect(() => resetLoginState(), [])
  useEffect(() => validate('email'), [state.email])
  useEffect(() => validate('password'), [state.password])

  const validate = (field: string): void => {
    const { email, password } = state
    const formData = { email, password }
    setState(old => ({ ...old, [`${field}Error`]: validation.validate(field, formData) }))
    setState(old => ({ ...old, isFormInvalid: !!old.emailError || !!old.passwordError }))
  }

  const handleSubmit = async (event: React.FormEvent<HTMLFormElement>): Promise<void> => {
    event.preventDefault()
    try {
      if (state.isLoading || state.isFormInvalid) {
        return
      }
      setState(old => ({ ...old, isLoading: true }))
      const account = await authentication.auth({
        email: state.email,
        password: state.password
      })
      setCurrentAccount(account)
      history.replace('/')
    } catch (error) {
      setState(old => ({
        ...old,
        isLoading: false,
        mainError: error.message
      }))
    }
  }

  return (
    <div className={Styles.loginWrap}>
      <LoginHeader />
      <form data-testid="form" className={Styles.form} onSubmit={handleSubmit}>
        <h2>Login</h2>
        <Input type="email" name="email" placeholder="Digite seu e-mail" />
        <Input type="password" name="password" placeholder="Digite sua senha" />
        <SubmitButton text="Entrar" />
        <Link data-testid="signup-link" to="/signup" className={Styles.link}>Criar conta</Link>
        <FormStatus />
      </form>
      <Footer />
    </div>
  )
}
Example #20
Source File: SettingsActions.tsx    From phosphor-home with MIT License 5 votes vote down vote up
SettingsActions: React.FC = () => {
  const weight = useRecoilValue(iconWeightAtom);
  const size = useRecoilValue(iconSizeAtom);
  const color = useRecoilValue(iconColorAtom);
  const reset = useResetRecoilState(resetSettingsSelector);

  const [copied, setCopied] = useTransientState<boolean>(false, 2000);

  const copyDeepLinkToClipboard = () => {
    const paramString = new URLSearchParams([
      ["weight", weight.toString()],
      ["size", size.toString()],
      ["color", color.replace("#", "")],
    ]).toString();
    void navigator.clipboard
      ?.writeText(`${window.location.host}?${paramString}`)
      .then(() => {
        setCopied(true);
      })
      .catch(() => {
        alert("Clipboard permissions must be enabled to copy links!");
      });
  };

  return (
    <>
      <button
        className="action-button"
        title="Restore default settings"
        onClick={reset}
      >
        <ArrowCounterClockwise size={24} />
      </button>
      <button
        className="action-button"
        title="Copy URL for current settings"
        onClick={copyDeepLinkToClipboard}
      >
        {copied ? (
          <CheckCircle size={24} color="#1FA647" weight="fill" />
        ) : (
          <Link size={24} />
        )}
      </button>
    </>
  );
}
Example #21
Source File: DatasetCurrent.tsx    From nextclade with MIT License 5 votes vote down vote up
export function DatasetCurrent() {
  const { t } = useTranslationSafe()
  const [advancedOpen, setAdvancedOpen] = useState(false)
  const datasetCurrent = useRecoilValue(datasetCurrentAtom)
  const resetDatasetCurrent = useResetRecoilState(datasetCurrentNameAtom)

  const onChangeClicked = useCallback(() => {
    resetDatasetCurrent()
  }, [resetDatasetCurrent])

  const onCustomizeClicked = useCallback(() => setAdvancedOpen((advancedOpen) => !advancedOpen), [])

  if (!datasetCurrent) {
    return null
  }

  return (
    <CurrentDatasetInfoContainer>
      <CurrentDatasetInfoHeader>
        <DatasetInfoH4>{t('Selected pathogen')}</DatasetInfoH4>
      </CurrentDatasetInfoHeader>

      <CurrentDatasetInfoBody>
        <Row noGutters>
          <Col className="d-flex flex-row">
            <Left>
              <DatasetInfo dataset={datasetCurrent} />
            </Left>

            <Right>
              <ChangeButton type="button" color="secondary" onClick={onChangeClicked}>
                {t('Change')}
              </ChangeButton>
              <LinkExternal
                className="ml-auto mt-auto"
                href="https://github.com/nextstrain/nextclade_data/blob/master/CHANGELOG.md"
              >
                <small>{t('Recent dataset updates')}</small>
              </LinkExternal>
            </Right>
          </Col>
        </Row>

        <Row noGutters>
          <Col>
            <ButtonCustomize isOpen={advancedOpen} onClick={onCustomizeClicked} />

            <Collapse isOpen={advancedOpen}>
              <AdvancedModeExplanationWrapper>
                <AdvancedModeExplanationContent />
              </AdvancedModeExplanationWrapper>

              <FilePickerAdvanced />
            </Collapse>
          </Col>
        </Row>
      </CurrentDatasetInfoBody>
    </CurrentDatasetInfoContainer>
  )
}
Example #22
Source File: FramesStates.tsx    From frames with Mozilla Public License 2.0 5 votes vote down vote up
resetFrames = () => {
    const aSubs = useResetRecoilState(moveSubsAtom);
    const bSubs = useResetRecoilState(framesSubtitles);
    const cSubs = useResetRecoilState(subOverAtom);
    const dSubs = useResetRecoilState(displaySidesAtom);
    const eSubs = useResetRecoilState(displayInfoAtom);
    const fSubs = useResetRecoilState(playVideoAtom);
    const gSubs = useResetRecoilState(framesPlayer);
    const hSubs = useResetRecoilState(bufferingAtom);
    const iSubs = useResetRecoilState(volumeAtom);
    const jSubs = useResetRecoilState(mutedAtom);
    const kSubs = useResetRecoilState(activeSubs);
    const lSubs = useResetRecoilState(UpNextURL);
    const mSubs = useResetRecoilState(nextOver);
    const nSubs = useResetRecoilState(nextHolder);
    const oSubs = useResetRecoilState(currentDuration);
    const pOver = useResetRecoilState(shareOver);
    const qOver = useResetRecoilState(CastEventAtom);
    const rOver = useResetRecoilState(VideoStateAtom);
    const sOver = useResetRecoilState(differance);
    const uOver = useResetRecoilState(GWMOver);
    const vOver = useResetRecoilState(DownOver);

    return () => {
        aSubs();
        bSubs();
        cSubs();
        dSubs();
        eSubs();
        fSubs();
        gSubs();
        hSubs();
        iSubs();
        jSubs();
        kSubs();
        lSubs();
        pOver();
        mSubs();
        nSubs();
        oSubs();
        qOver();
        rOver();
        sOver();
        uOver();
        vOver();
    }
}
Example #23
Source File: MainInputFormSequenceFilePicker.tsx    From nextclade with MIT License 4 votes vote down vote up
export function MainInputFormSequenceFilePicker() {
  const { t } = useTranslationSafe()

  const datasetCurrent = useRecoilValue(datasetCurrentAtom)
  const [qrySeq, setQrySeq] = useRecoilState(qrySeqInputAtom)
  const removeQrySeq = useResetRecoilState(qrySeqInputAtom)
  const qrySeqError = useRecoilValue(qrySeqErrorAtom)

  const canRun = useRecoilValue(canRunAtom)
  const [shouldRunAutomatically, setShouldRunAutomatically] = useRecoilState(shouldRunAutomaticallyAtom)
  const hasRequiredInputs = useRecoilValue(hasRequiredInputsAtom)
  const hasInputErrors = useRecoilValue(hasInputErrorsAtom)

  const icon = useMemo(() => <FileIconFasta />, [])

  const run = useRunAnalysis()

  const setSequences = useCallback(
    (input: AlgorithmInput) => {
      setQrySeq(input)

      if (shouldRunAutomatically) {
        run()
      }
    },
    [run, setQrySeq, shouldRunAutomatically],
  )

  const setExampleSequences = useCallback(() => {
    if (datasetCurrent) {
      setQrySeq(new AlgorithmInputDefault(datasetCurrent))

      if (shouldRunAutomatically) {
        run()
      }
    }
  }, [datasetCurrent, run, setQrySeq, shouldRunAutomatically])

  const { isRunButtonDisabled, runButtonColor, runButtonTooltip } = useMemo(() => {
    const isRunButtonDisabled = !(canRun && hasRequiredInputs) || hasInputErrors
    return {
      isRunButtonDisabled,
      runButtonColor: isRunButtonDisabled ? 'secondary' : 'success',
      runButtonTooltip: isRunButtonDisabled
        ? t('Please provide input files for the algorithm')
        : t('Launch the algorithm!'),
    }
  }, [canRun, hasInputErrors, hasRequiredInputs, t])

  const LoadExampleLink = useMemo(() => {
    const cannotLoadExample = hasRequiredInputs || hasInputErrors || !datasetCurrent
    return (
      <Button color="link" onClick={setExampleSequences} disabled={cannotLoadExample}>
        {t('Load example')}
      </Button>
    )
  }, [datasetCurrent, hasInputErrors, hasRequiredInputs, setExampleSequences, t])

  const onToggleRunAutomatically = useCallback(() => {
    setShouldRunAutomatically((shouldRunAutomatically) => !shouldRunAutomatically)
  }, [setShouldRunAutomatically])

  return (
    <SequenceFilePickerContainer>
      <FilePicker
        title={t('Provide sequence data')}
        icon={icon}
        exampleUrl="https://example.com/sequences.fasta"
        pasteInstructions={t('Enter sequence data in FASTA or plain text format')}
        input={qrySeq}
        error={qrySeqError}
        isInProgress={false}
        onRemove={removeQrySeq}
        onInput={setSequences}
      />

      <Row noGutters className="mt-2">
        <Col className="w-100 d-flex">
          <FlexLeft>
            <Form className="d-flex h-100 mt-1">
              <FormGroup className="my-auto">
                <Toggle
                  identifier="toggle-run-automatically"
                  checked={shouldRunAutomatically}
                  onCheckedChanged={onToggleRunAutomatically}
                >
                  <span title="Run Nextclade automatically after sequence data is provided">
                    {t('Run automatically')}
                  </span>
                </Toggle>
              </FormGroup>
            </Form>
          </FlexLeft>

          <FlexRight>
            {LoadExampleLink}

            <ButtonRunStyled
              disabled={isRunButtonDisabled}
              color={runButtonColor}
              onClick={run}
              title={runButtonTooltip}
            >
              {t('Run')}
            </ButtonRunStyled>
          </FlexRight>
        </Col>
      </Row>
    </SequenceFilePickerContainer>
  )
}
Example #24
Source File: SystemSettings.tsx    From nextclade with MIT License 4 votes vote down vote up
export function SystemSettings() {
  const { t } = useTranslationSafe()

  const [numThreads, setNumThreads] = useRecoilState(numThreadsAtom)
  const resetNumThreads = useResetRecoilState(numThreadsAtom)
  const guess = useGuessNumThreads(numThreads)
  const handleValidate = useCallback((values: SettingsFormValues): FormikErrors<SettingsFormValues> => {
    const errors: FormikErrors<SettingsFormValues> = {}
    const { numThreads } = values
    if (!Number.isInteger(numThreads) || numThreads < 0 || numThreads > 1000) {
      errors.numThreads = 'Should be a positive integer from 1 to 1000'
    }
    return errors
  }, [])

  const setNumThreadsDebounced = useMemo(
    () => debounce(setNumThreads, 500, { leading: false, trailing: true }), // prettier-ignore
    [setNumThreads],
  )

  const handleSubmit = useCallback(
    (values: SettingsFormValues, { setSubmitting }: FormikHelpers<SettingsFormValues>) => {
      setNumThreadsDebounced(values.numThreads)
      setSubmitting(false)
    },
    [setNumThreadsDebounced],
  )

  const initialValues = useMemo(() => ({ numThreads }), [numThreads])
  const onReset = useCallback(() => ({ numThreads }), [numThreads])

  const memoryAvailable = useMemo(() => {
    return guess.memoryAvailable ? prettyBytes.format(guess.memoryAvailable) : t('unsupported')
  }, [guess.memoryAvailable, t])

  const memoryAvailablePerThread = useMemo(() => {
    return guess.memoryAvailable ? prettyBytes.format(guess.memoryAvailable / numThreads) : t('unsupported')
  }, [guess.memoryAvailable, numThreads, t])

  return (
    <Formik initialValues={initialValues} validate={handleValidate} onSubmit={handleSubmit} onReset={onReset}>
      {({ values, errors, touched, handleChange, handleBlur, resetForm }) => (
        <Form>
          <FormikAutoSubmit />

          <FormGroup>
            <Label className="d-block w-100">
              <NumericInput
                id="numThreads"
                min={1}
                max={1000}
                className={classNames('d-inline', errors?.numThreads && 'border-danger')}
                type="number"
                identifier="settings-num-threads-input"
                value={values.numThreads}
                onChange={handleChange}
                onBlur={handleBlur}
              />
              <span className="d-inline">
                <span className="mx-3">{t('Number of CPU threads')}</span>
                <span className="mx-auto">
                  <ButtonTransparent
                    className="my-0"
                    type="button"
                    title={t('Reset to default')}
                    // eslint-disable-next-line react-perf/jsx-no-new-function-as-prop
                    onClick={() => {
                      resetNumThreads()
                      resetForm()
                    }}
                  >
                    <MdRefresh /> {t('Reset')}
                  </ButtonTransparent>
                </span>
              </span>
              {touched.numThreads && errors?.numThreads && <p className="text-danger">{errors.numThreads}</p>}
              {guess.numThreads && guess.memoryAvailable && (
                <Alert className="mt-2 p-1" color="primary" isOpen fade={false}>
                  <TableSlim borderless className="small mb-1">
                    <tbody>
                      <tr>
                        <td>{t('Memory available*')}</td>
                        <td>{memoryAvailable}</td>
                      </tr>

                      <tr>
                        <td>{t('Memory per CPU thread')}</td>
                        <td>{memoryAvailablePerThread}</td>
                      </tr>

                      <tr>
                        <td>{t('Recommended number of CPU threads**')}</td>
                        <td>{guess.numThreads ?? t('unsupported')}</td>
                      </tr>

                      <tr>
                        <td colSpan={2} className="small">
                          {t('* Current value. This amount can change depending on load')}
                        </td>
                      </tr>

                      <tr>
                        <td colSpan={2} className="small">
                          {t('** {{appName}} requires at least {{memoryRequired}} of memory per thread', {
                            appName: PROJECT_NAME,
                            memoryRequired: prettyBytes.format(MEMORY_BYTES_PER_THREAD_MINIMUM),
                          })}
                        </td>
                      </tr>
                    </tbody>
                  </TableSlim>
                </Alert>
              )}
            </Label>
          </FormGroup>
        </Form>
      )}
    </Formik>
  )
}
Example #25
Source File: FilePickerAdvanced.tsx    From nextclade with MIT License 4 votes vote down vote up
export function FilePickerAdvanced() {
  const { t } = useTranslation()

  const [refSeq, setRefSeq] = useRecoilState(refSeqInputAtom)
  const refSeqError = useRecoilValue(refSeqErrorAtom)
  const resetRefSeq = useResetRecoilState(refSeqInputAtom)

  const [geneMap, setGeneMap] = useRecoilState(geneMapInputAtom)
  const geneMapError = useRecoilValue(geneMapErrorAtom)
  const resetGeneMap = useResetRecoilState(geneMapInputAtom)

  const [refTree, setRefTree] = useRecoilState(refTreeInputAtom)
  const refTreeError = useRecoilValue(refTreeErrorAtom)
  const resetRefTree = useResetRecoilState(refTreeInputAtom)

  const [qcConfig, setQcConfig] = useRecoilState(qcConfigInputAtom)
  const qcConfigError = useRecoilValue(qcConfigErrorAtom)
  const resetQcConfig = useResetRecoilState(qcConfigInputAtom)

  const [virusProperties, setVirusProperties] = useRecoilState(virusPropertiesInputAtom)
  const virusPropertiesError = useRecoilValue(virusPropertiesErrorAtom)
  const resetVirusProperties = useResetRecoilState(virusPropertiesInputAtom)

  const [primersCsv, setPrimersCsv] = useRecoilState(primersCsvInputAtom)
  const primersCsvError = useRecoilValue(primersCsvErrorAtom)
  const resetPrimersCsv = useResetRecoilState(primersCsvInputAtom)

  const iconCsv = useMemo(() => <FileIconCsv size={30} />, [])
  const iconFasta = useMemo(() => <FileIconFasta size={30} />, [])
  const iconGff = useMemo(() => <FileIconGff size={30} />, [])
  const iconJson = useMemo(() => <FileIconJson size={30} />, [])

  return (
    <Row noGutters>
      <Col>
        <FilePicker
          className="my-3"
          compact
          icon={iconJson}
          title={t('Reference tree')}
          exampleUrl="https://example.com/tree.json"
          pasteInstructions={t('Enter tree data in Auspice JSON v2 format')}
          input={refTree}
          error={refTreeError}
          onRemove={resetRefTree}
          onInput={setRefTree}
        />

        <FilePicker
          className="my-3"
          compact
          icon={iconFasta}
          title={t('Root sequence')}
          exampleUrl="https://example.com/root_seq.fasta"
          pasteInstructions={t('Enter root sequence data in FASTA or plain text format')}
          input={refSeq}
          error={refSeqError}
          onRemove={resetRefSeq}
          onInput={setRefSeq}
        />

        <FilePicker
          className="my-3"
          compact
          icon={iconJson}
          title={t('Quality control')}
          exampleUrl="https://example.com/qc.json"
          pasteInstructions={t('Enter QC config in JSON format')}
          input={qcConfig}
          error={qcConfigError}
          onRemove={resetQcConfig}
          onInput={setQcConfig}
        />

        <FilePicker
          className="my-3"
          compact
          icon={iconJson}
          title={t('Virus properties')}
          exampleUrl="https://example.com/virus_properties.json"
          pasteInstructions={t('Enter Virus attributes in JSON format')}
          input={virusProperties}
          error={virusPropertiesError}
          onRemove={resetVirusProperties}
          onInput={setVirusProperties}
        />

        <FilePicker
          className="my-3"
          compact
          icon={iconGff}
          title={t('Gene map')}
          exampleUrl="https://example.com/gene_map.json"
          pasteInstructions={t('Enter gene map data in JSON format')}
          input={geneMap}
          error={geneMapError}
          onRemove={resetGeneMap}
          onInput={setGeneMap}
        />

        <FilePicker
          className="my-3"
          compact
          icon={iconCsv}
          title={t('PCR primers')}
          exampleUrl="https://example.com/pcr_primers.csv"
          pasteInstructions={t('Enter PCR primers data in CSV format')}
          input={primersCsv}
          error={primersCsvError}
          onRemove={resetPrimersCsv}
          onInput={setPrimersCsv}
        />
      </Col>
    </Row>
  )
}
Example #26
Source File: index.spec.tsx    From recoil-persist with MIT License 4 votes vote down vote up
function testPersistWith(storage: TestableStorage) {
  describe(`Storage: ${storage.name}`, () => {
    const testKey = 'test-key'
    const { persistAtom } = recoilPersist({ key: testKey, storage })

    const getStateValue = () => {
      const value = storage.getState()[testKey]
      if (value == undefined) {
        return {}
      }
      return JSON.parse(value)
    }

    const getAtomKey = (key: string) => {
      return `${storage.name}_${key}`
    }

    const counterState = atom({
      key: getAtomKey('count'),
      default: 0,
      effects_UNSTABLE: [persistAtom],
    })

    const counterFamily = atomFamily({
      key: getAtomKey('countFamily'),
      default: 0,
      effects_UNSTABLE: [persistAtom],
    })

    const counterState4 = atom({
      key: getAtomKey('count4'),
      default: 0,
    })

    function Demo() {
      const [count, setCount] = useRecoilState(counterState)
      const [count2, setCount2] = useRecoilState(counterFamily('2'))
      const [count3, setCount3] = useRecoilState(counterFamily('3'))
      const [count4, setCount4] = useRecoilState(counterState4)
      const resetCounter3 = useResetRecoilState(counterFamily('3'))
      const updateMultiple = useRecoilCallback(({ set }) => () => {
        set(counterState, 10)
        set(counterFamily('2'), 10)
      })
      return (
        <div>
          <p data-testid="count-value">{count}</p>
          <p data-testid="count2-value">{count2}</p>
          <p data-testid="count3-value">{count3}</p>
          <p data-testid="count4-value">{count4}</p>
          <button
            data-testid="count-increase"
            onClick={() => setCount(count + 1)}
          >
            Increase
          </button>
          <button
            data-testid="count2-increase"
            onClick={() => setCount2(count2 + 1)}
          >
            Increase 2
          </button>
          <button
            data-testid="count3-increase"
            onClick={() => setCount3(count3 + 1)}
          >
            Increase 3
          </button>
          <button
            data-testid="count4-increase"
            onClick={() => setCount4(count4 + 1)}
          >
            Increase 4
          </button>
          <button
            data-testid="count3-null-value"
            onClick={() => setCount3(null)}
          >
            Set value to null
          </button>
          <button
            data-testid="count3-undefined-value"
            onClick={() => setCount3(undefined)}
          >
            Set value to undefined
          </button>
          <button data-testid="count3-reset" onClick={() => resetCounter3()}>
            Reset count 3
          </button>
          <button
            data-testid="update-multiple"
            onClick={() => updateMultiple()}
          >
            Update multiple
          </button>
        </div>
      )
    }

    beforeEach(() => {
      console.error = jest.fn()
    })

    afterEach(() => {
      storage.clear()
      jest.restoreAllMocks()
    })

    it('should be removed from storage on reset', async () => {
      const { getByTestId } = render(
        <RecoilRoot>
          <Demo />
        </RecoilRoot>,
      )

      fireEvent.click(getByTestId('count3-increase'))
      await waitFor(() =>
        expect(getByTestId('count3-value').innerHTML).toBe('1'),
      )

      expect(getStateValue()).toStrictEqual({
        [getAtomKey('countFamily__"3"')]: 1,
      })

      fireEvent.click(getByTestId('count3-reset'))
      await waitFor(() =>
        expect(getByTestId('count3-value').innerHTML).toBe('0'),
      )

      expect(getStateValue()).toStrictEqual({})
    })

    it('should handle reset atom with default value', async () => {
      const { getByTestId } = render(
        <RecoilRoot>
          <Demo />
        </RecoilRoot>,
      )

      fireEvent.click(getByTestId('count3-reset'))
      await waitFor(() =>
        expect(getByTestId('count3-value').innerHTML).toBe('0'),
      )

      expect(getStateValue()).toStrictEqual({})
    })

    it('should update storage with null', async () => {
      const { getByTestId } = render(
        <RecoilRoot>
          <Demo />
        </RecoilRoot>,
      )

      fireEvent.click(getByTestId('count3-null-value'))
      await waitFor(() =>
        expect(getByTestId('count3-value').innerHTML).toBe(''),
      )

      expect(getStateValue()).toStrictEqual({
        [getAtomKey('countFamily__"3"')]: null,
      })
    })

    it('should update storage with undefined', async () => {
      const { getByTestId } = render(
        <RecoilRoot>
          <Demo />
        </RecoilRoot>,
      )

      fireEvent.click(getByTestId('count3-undefined-value'))
      await waitFor(() =>
        expect(getByTestId('count3-value').innerHTML).toBe(''),
      )

      expect(getStateValue()).toStrictEqual({})
    })

    it('should update storage', async () => {
      const { getByTestId } = render(
        <RecoilRoot>
          <Demo />
        </RecoilRoot>,
      )

      fireEvent.click(getByTestId('count-increase'))
      await waitFor(() =>
        expect(getByTestId('count-value').innerHTML).toBe('1'),
      )

      expect(getStateValue()).toStrictEqual({
        [getAtomKey('count')]: 1,
      })
    })

    it('should update storage if using atomFamily', async () => {
      const { getByTestId } = render(
        <RecoilRoot>
          <Demo />
        </RecoilRoot>,
      )
      fireEvent.click(getByTestId('count2-increase'))
      await waitFor(() =>
        expect(getByTestId('count2-value').innerHTML).toBe('1'),
      )
      fireEvent.click(getByTestId('count3-increase'))
      await waitFor(() =>
        expect(getByTestId('count3-value').innerHTML).toBe('1'),
      )

      expect(getStateValue()).toStrictEqual({
        [getAtomKey('countFamily__"2"')]: 1,
        [getAtomKey('countFamily__"3"')]: 1,
      })
    })

    it('should not persist atom with no effect', async () => {
      const { getByTestId } = render(
        <RecoilRoot>
          <Demo />
        </RecoilRoot>,
      )

      fireEvent.click(getByTestId('count4-increase'))
      await waitFor(() =>
        expect(getByTestId('count4-value').innerHTML).toBe('1'),
      )

      expect(storage.getState()[testKey]).toBeUndefined()
    })

    it('should read state from storage', async () => {
      await storage.setItem(
        testKey,
        JSON.stringify({
          [getAtomKey('count')]: 1,
          [getAtomKey('countFamily__"2"')]: 1,
        }),
      )

      const { getByTestId } = render(
        <RecoilRoot>
          <Demo />
        </RecoilRoot>,
      )

      await waitFor(() =>
        expect(getByTestId('count-value').innerHTML).toBe('1'),
      )
      await waitFor(() =>
        expect(getByTestId('count2-value').innerHTML).toBe('1'),
      )
    })

    it('should use default value if not in storage', async () => {
      const { getByTestId } = render(
        <RecoilRoot>
          <Demo />
        </RecoilRoot>,
      )

      expect(getByTestId('count3-value').innerHTML).toBe('0')
    })

    it('should handle non jsonable object in storage', async () => {
      storage.setItem(testKey, 'test string')

      const { getByTestId } = render(
        <RecoilRoot>
          <Demo />
        </RecoilRoot>,
      )

      fireEvent.click(getByTestId('count-increase'))
      await waitFor(() =>
        expect(getByTestId('count-value').innerHTML).toBe('1'),
      )

      expect(getStateValue()).toStrictEqual({
        [getAtomKey('count')]: 1,
      })
    })

    it('should handle non jsonable object in state', async () => {
      let mock = jest.spyOn(JSON, 'stringify').mockImplementation(() => {
        throw Error('mock error')
      })

      const { getByTestId } = render(
        <RecoilRoot>
          <Demo />
        </RecoilRoot>,
      )

      fireEvent.click(getByTestId('count-increase'))
      await waitFor(() =>
        expect(getByTestId('count-value').innerHTML).toBe('1'),
      )
      expect(mock).toHaveBeenCalledTimes(1)
      expect(console.error).toHaveBeenCalledTimes(1)
    })

    it('should handle non-existent atom name stored in storage', async () => {
      storage.setItem(
        testKey,
        JSON.stringify({
          notExist: 'test value',
        }),
      )

      const { getByTestId } = render(
        <RecoilRoot>
          <Demo />
        </RecoilRoot>,
      )

      await waitFor(() =>
        expect(getByTestId('count-value').innerHTML).toBe('0'),
      )
    })

    it.skip('should handle updating multiple atomes', async () => {
      const { getByTestId } = render(
        <RecoilRoot>
          <Demo />
        </RecoilRoot>,
      )

      fireEvent.click(getByTestId('update-multiple'))
      await waitFor(() =>
        expect(getByTestId('count-value').innerHTML).toBe('10'),
      )

      await waitFor(() =>
        expect(getByTestId('count2-value').innerHTML).toBe('10'),
      )

      expect(getStateValue()).toStrictEqual({
        [getAtomKey('count')]: 10,
        [getAtomKey('countFamily__"2"')]: 10,
      })
    })
  })
}
Example #27
Source File: TransactionActions.tsx    From abrechnung with GNU Affero General Public License v3.0 4 votes vote down vote up
export default function TransactionActions({ groupID, transaction }) {
    const [confirmDeleteDialogOpen, setConfirmDeleteDialogOpen] = useState(false);

    const history = useHistory();
    const userPermissions = useRecoilValue(currUserPermissions(groupID));
    const setTransactions = useSetRecoilState(groupTransactions(transaction.group_id));
    const localTransactionChanges = useRecoilValue(pendingTransactionDetailChanges(transaction.id));
    const localPositionChanges = useRecoilValue(pendingTransactionPositionChanges(transaction.id));
    const resetLocalTransactionChanges = useResetRecoilState(pendingTransactionDetailChanges(transaction.id));
    const resetLocalPositionChanges = useResetRecoilState(pendingTransactionPositionChanges(transaction.id));

    const updateTransactionAndClearLocal = useRecoilTransaction_UNSTABLE(
        ({ get, set, reset }) =>
            (transaction: TransactionBackend) => {
                set(groupTransactions(transaction.group_id), (currTransactions) => {
                    return currTransactions.map((t) => (t.id === transaction.id ? transaction : t));
                });
                reset(pendingTransactionDetailChanges(transaction.id));
                reset(pendingTransactionPositionChanges(transaction.id));
            }
    );

    const edit = () => {
        if (!transaction.is_wip) {
            createTransactionChange({
                transactionID: transaction.id,
            })
                .then((t) => {
                    updateTransactionAndClearLocal(t);
                })
                .catch((err) => {
                    toast.error(err);
                });
        }
    };

    const abortEdit = () => {
        if (transaction.is_wip) {
            if (transaction.has_committed_changes) {
                discardTransactionChange({
                    transactionID: transaction.id,
                })
                    .then((t) => {
                        updateTransactionAndClearLocal(t);
                    })
                    .catch((err) => {
                        toast.error(err);
                    });
            } else {
                history.push(`/groups/${groupID}/`);
            }
        }
    };

    const commitEdit = () => {
        if (transaction.is_wip) {
            // update the transaction given the currently pending changes
            // find out which local changes we have and send them to da server
            const positions = Object.values(localPositionChanges.modified)
                .concat(
                    Object.values(localPositionChanges.added).map((position) => ({
                        ...position,
                        id: -1,
                    }))
                )
                .map((p) => ({
                    id: p.id,
                    name: p.name,
                    communist_shares: p.communist_shares,
                    price: p.price,
                    usages: p.usages,
                    deleted: p.deleted,
                }));

            if (Object.keys(localTransactionChanges).length > 0) {
                updateTransaction({
                    transactionID: transaction.id,
                    description: transaction.description,
                    value: transaction.value,
                    billedAt: transaction.billed_at,
                    currencySymbol: transaction.currency_symbol,
                    currencyConversionRate: transaction.currency_conversion_rate,
                    creditorShares: transaction.creditor_shares,
                    debitorShares: transaction.debitor_shares,
                    ...localTransactionChanges,
                    positions: positions.length > 0 ? positions : null,
                })
                    .then((t) => {
                        updateTransactionAndClearLocal(t);
                    })
                    .catch((err) => {
                        toast.error(err);
                    });
            } else if (positions.length > 0) {
                updateTransactionPositions({
                    transactionID: transaction.id,
                    positions: positions,
                })
                    .then((t) => {
                        updateTransactionAndClearLocal(t);
                    })
                    .catch((err) => {
                        toast.error(err);
                    });
            } else {
                commitTransaction({ transactionID: transaction.id })
                    .then((t) => {
                        updateTransactionAndClearLocal(t);
                    })
                    .catch((err) => {
                        toast.error(err);
                    });
            }
        }
    };

    const confirmDeleteTransaction = () => {
        deleteTransaction({ transactionID: transaction.id })
            .then((t) => {
                // TODO: use recoil transaction
                updateTransactionInState(t, setTransactions);
                resetLocalPositionChanges();
                resetLocalTransactionChanges();
                history.push(`/groups/${groupID}/`);
            })
            .catch((err) => {
                toast.error(err);
            });
    };

    return (
        <>
            <Grid container justifyContent="space-between">
                <Grid item sx={{ display: "flex", alignItems: "center" }}>
                    <IconButton
                        sx={{ display: { xs: "none", md: "inline-flex" } }}
                        component={RouterLink}
                        to={`/groups/${groupID}/`}
                    >
                        <ChevronLeft />
                    </IconButton>
                    <Chip color="primary" label={transaction.type} />
                </Grid>
                <Grid item>
                    {userPermissions.can_write && (
                        <>
                            {transaction.is_wip ? (
                                <>
                                    <Button color="primary" onClick={commitEdit}>
                                        Save
                                    </Button>
                                    <Button color="error" onClick={abortEdit}>
                                        Cancel
                                    </Button>
                                </>
                            ) : (
                                <IconButton color="primary" onClick={edit}>
                                    <Edit />
                                </IconButton>
                            )}
                            <IconButton color="error" onClick={() => setConfirmDeleteDialogOpen(true)}>
                                <Delete />
                            </IconButton>
                        </>
                    )}
                </Grid>
            </Grid>
            <Dialog maxWidth="xs" aria-labelledby="confirmation-dialog-title" open={confirmDeleteDialogOpen}>
                <DialogTitle id="confirmation-dialog-title">Confirm delete transaction</DialogTitle>
                <DialogContent dividers>
                    Are you sure you want to delete the transaction "{transaction.description}"
                </DialogContent>
                <DialogActions>
                    <Button autoFocus onClick={() => setConfirmDeleteDialogOpen(false)} color="primary">
                        Cancel
                    </Button>
                    <Button onClick={confirmDeleteTransaction} color="error">
                        Ok
                    </Button>
                </DialogActions>
            </Dialog>
        </>
    );
}
Example #28
Source File: groupWatch.ts    From frames with Mozilla Public License 2.0 4 votes vote down vote up
export function useGroupWatch() {
    const {user} = useUser();
    const setSide = useSetRecoilState(GroupWatchSide);
    const setState = useSetRecoilState(NavSectionAndOpacity);
    const [med, setId] = useRecoilState(GroupWatchMediaIdAtom);
    const [leader, setLeader] = useRecoilState(GroupLeader);
    const [room, setRoom] = useRecoilState(GroupRoom);
    const player = useRecoilValue(framesPlayer);
    const addMessage = useSetRecoilState(GroupWatchMessages);
    const resetMessages = useResetRecoilState(GroupWatchMessages);
    const connected = useRecoilValue(ConnectedAtom);
    const dispatch = useInfoDispatch();
    const router = useRouter();
    const base = useBase();
    const socket = useChannel(room, {username: user?.username || ''});

    const addNewMessage = useCallback((message: string) => {
       if (user){
           const nMsg = {
               id: Date.now(),
               username: user.username,
               message: message,
               received: new Date()
           }

           addMessage((prev) => [...prev, nMsg])
       }
    }, [addMessage, user])

    const connect = useCallback((room: string) => {
        if (user?.role === Role.GUEST) dispatch({
            type: "error",
            heading: "Unauthorised action attempt",
            message: 'Guest accounts cannot create or join GroupWatch'
        })

        else if (!connected) {
            setRoom(room);
            socket.forceConnect(room, {username: user?.username || ''});
        }
    }, [user, connected, setRoom, socket, dispatch])

    const sendMessage = useCallback((message: Message) => {
        if (!leader) {
            if (message.action === 'inform')
                return;

            else if (message.action === 'playing')
                message.playData = undefined;
        }

        socket.send<GroupWatchMessage>('speak', {...message, username: user?.username || ''});

        let messageString = '';
        switch (message.action) {
            case 'says':
                messageString = message.data as string;
                break;

            case 'playing':
                const playBool = message.data as boolean;
                messageString = `You have ${playBool ? 'played' : 'paused'} the video`;
                break;

            case 'buffering':
                const buffBool = message.data as boolean;
                messageString = `You have ${buffBool ? 'lost connection' : 'reconnected'} to the video session`;
                break;

            case 'skipped':
                messageString = `You skipped the video to the frame ${Math.ceil(message.data as number)}`;
                break;
        }

        if (messageString !== '')
            addNewMessage(messageString);
    }, [socket.send, leader, user, room, addNewMessage]);

    const joinRoom = useCallback(async (room: string) => {
        if (user?.role === Role.GUEST) dispatch({
            type: "error",
            heading: "Unauthorised action attempt",
            message: 'Guest accounts cannot create or join GroupWatch Sessions'
        })

        else {
            connect(room);
        }
    }, [user, dispatch, setRoom, connect]);

    const updateRoom = useCallback(async (auth: string) => {
        if (leader && connected && base) await base.makeRequest('/api/stream/groupWatch', {
            auth,
            roomKey: room
        }, 'POST');
    }, [leader, connected, room, base]);

    const pushNext = useCallback((data: string) => {
        if (leader) sendMessage({action: "next", data});
    }, [leader, sendMessage]);

    const disconnect = useCallback(() => {
        setRoom('');
        resetMessages();
        connected && dispatch({
            type: "warn", heading: "GroupWatch Session", message: 'You have left the GroupWatch Session'
        });
        setLeader(false);
        socket.disconnect();
    }, [sendMessage, socket.disconnect, dispatch]);

    const genRoom = useCallback(async (auth: string) => {
        if (user?.role === Role.GUEST) dispatch({
            type: "error",
            heading: "Unauthorised action attempt",
            message: 'Guest accounts cannot create or join GroupWatch'
        })

        else if (base) {
            if (!socket.connected) {
                const roomKey = base.generateKey(13, 5);
                await base.makeRequest('/api/stream/groupWatch', {auth, roomKey}, 'POST');
                connect(roomKey);
            }
        }
    }, [user, base, dispatch, connect, socket.connected, disconnect]);

    const openSession = useCallback(async (data: { id: number, auth?: string, location?: string }) => {
        if (socket.connected) {
            socket.disconnect();
            data.location && await router.replace(`/watch=${data.location}`, undefined, {shallow: true});
        } else {
            if (user?.role === Role.GUEST) {
                dispatch({
                    type: "error",
                    heading: "Unauthorised action attempt",
                    message: 'Guest accounts cannot create or join GroupWatch'
                })

                return;
            }

            player && player.pause();
            setId(data);
        }
    }, [setId, socket.connected, socket.disconnect, user, player]);

    const startSession = useCallback(async (playPause: (a : boolean) => void) => {
        NProgress.start();
        const url = `/room=${room}`;
        const asPath = `/watch=${med?.location}`;
        if (router.asPath !== url && router.asPath !== asPath)
            await router.push(url);

        else {
            if (router.asPath.includes('watch=')) {
                await router.replace(url, undefined, {shallow: true});
                await playPause(true);

            } else {
                if (leader)
                    await playPause(true);
                else
                    sendMessage({action: 'request-sync'});
            }

            setId(null);
            setSide(false);
            setState({section: 'watch', opacity: 1});
        }
        NProgress.done();
    }, [router, room, med, setId, setSide, setState, leader, sendMessage]);

    const endSession = useCallback(async (playPause: (a : boolean) => void, response?: FrameMediaLite) => {
        if (response && router.asPath.includes('room')) {
            const url = '/info?mediaId=' + response.id;
            const asPath = `/${response.type.toLowerCase()}=${response.name.replace(/\s/g, '+')}`;
            await router.push(url, asPath);
        } else {
            if (router.asPath.includes('watch=')) {
                setState({section: 'watch', opacity: -1});
                await playPause(true);
            } else
                setState({section: response?.type === MediaType.MOVIE ? 'movies' : 'tv shows', opacity: 1});

            disconnect();
            setId(null);
            setSide(false);
        }
    }, [router, setId, setSide, setState, disconnect]);

    return {
        room,
        channel: socket,
        disconnect,
        sendMessage,
        pushNext,
        genRoom,
        updateRoom,
        setLeader,
        leader, startSession, endSession,
        joinRoom, openSession,
        connected: socket.connected,
        lobbyOpen: !!med
    };
}
Example #29
Source File: notificationConext.ts    From frames with Mozilla Public License 2.0 4 votes vote down vote up
NotificationHandler = () => {
    const router = useRouter();
    const infoDispatch = useInfoDispatch();
    const confirmDispatch = useConfirmDispatch();
    const videoState = useRecoilValue(framesVideoStateAtom);
    const resetPlayer = useResetRecoilState(framesPlayer);
    const setIsStreaming = useSetRecoilState(AlreadyStreamingAtom);
    const startTime = useRecoilValue(fullscreenAddressAtom).startTime;
    const {user, globalTopic, notificationChannel, globalChannel, connect, disconnect, broadcastToSelf, signOut} = useNotification();

    useEffect(() => {
        if (user && globalTopic)
            connect();
        else
            disconnect();
    }, [user, globalTopic]);

    notificationChannel.subscribe<NotificationInterface>('shout', async data => {
        switch (data.type) {
            case 'streaming':
                const stream = /\/(frame|watch|room)=\w/.test(router.asPath) ;
                if (videoState && stream) {
                    const nData = {
                        name: videoState.name,
                        backdrop: videoState.backdrop,
                        episodeName: videoState.episodeName,
                        logo: videoState.logo,
                        startTime: startTime,
                    };

                    broadcastToSelf({
                        type: 'isStreaming',
                        title: 'Streaming',
                        message: `${startTime}`,
                        recipient: data.sender,
                        data: nData,
                    });
                }
                break;
            case 'isStreaming':
                if (data.data && data.data.startTime < startTime && data.recipient === user?.session)
                    setIsStreaming(data.data);
                break;
            case 'doneStreaming':
                resetPlayer();
                setIsStreaming(null);
                break;
            case 'signOut':
                infoDispatch({
                    type: 'error',
                    heading: data.title,
                    message: data.message,
                })
                await signOut();
                break;
            default:
                break;
        }
    })

    globalChannel.subscribe<NotificationInterface & {type: string}>('shout', data => {

    })

    globalChannel.subscribe<{body: NotificationInterface & {type: string}}>('whisper', async ({body: data}) => {
        switch (data.type) {
            case 'groupWatchInvite':
                confirmDispatch({
                    type: 'user',
                    heading: data.title,
                    message: data.message,
                    confirm: true,
                    confirmText: 'Join',
                    cancelText: 'Decline',
                    onOk: async () => {
                        await router.push(data.data.url);
                    },
                    onCancel: () => {},
                })
               break;
            default:
                break;
        }
    })

    return null;
}