lodash-es#find TypeScript Examples

The following examples show how to use lodash-es#find. 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: App.tsx    From yasd with MIT License 4 votes vote down vote up
App: React.FC = () => {
  const { t, i18n } = useTranslation()
  const [isNetworkModalOpen, setIsNetworkModalOpen] = useState(false)
  const location = useLocation()
  const history = useHistory()
  const profileDispatch = useProfileDispatch()
  const profile = useProfile()
  const [hasInit, setHasInit] = useState(false)
  const isCurrentVersionFetched = useRef(true)
  const platformVersion = usePlatformVersion()

  const onCloseApplication = useCallback(() => {
    if (isRunInSurge()) {
      store.remove(LastUsedProfile)
      store.remove(ExistingProfiles)
    }

    window.location.replace('/')
  }, [])

  useEffect(
    () => {
      const existingProfiles = store.get(ExistingProfiles)
      const lastId = store.get(LastUsedProfile)
      const result = find<Profile>(existingProfiles, { id: lastId })

      if (result) {
        profileDispatch({
          type: 'update',
          payload: result,
        })
      }

      setHasInit(true)
    },
    // eslint-disable-next-line
    [],
  )

  useEffect(() => {
    if (hasInit && !profile && location.pathname !== '/') {
      history.replace('/')
    }
  }, [hasInit, history, location.pathname, profile])

  useEffect(() => {
    ReactGA.pageview(location.pathname)
  }, [location.pathname])

  useEffect(() => {
    const language: string | null = store.get(LastUsedLanguage)

    if (language && language !== i18n.language) {
      i18n.changeLanguage(language)
    }
  }, [i18n])

  useEffect(() => {
    if (
      !profile?.platform ||
      !isCurrentVersionFetched.current ||
      location.pathname === '/'
    ) {
      return
    }

    httpClient
      .request({
        url: '/environment',
        method: 'GET',
      })
      .then((res) => {
        const currentPlatformVersion = res.headers['x-surge-version']

        if (currentPlatformVersion !== platformVersion) {
          profileDispatch({
            type: 'updatePlatformVersion',
            payload: {
              platformVersion: currentPlatformVersion,
            },
          })
        }

        isCurrentVersionFetched.current = false
      })
      .catch((err) => {
        console.error(err)
        toast.error(t('common.surge_too_old'))
      })
  }, [location, platformVersion, profile?.platform, profileDispatch, t])

  return (
    <SWRConfig
      value={{
        onError: (error) => {
          if (location.pathname !== '/') {
            if (!error.response && error.request) {
              // 无法连接服务器
              setIsNetworkModalOpen(true)
            }
          }
        },
        refreshWhenOffline: true,
      }}
    >
      <ScrollToTop />
      <ToastContainer />
      <NetworkErrorModal
        reloadButton={isRunInSurge()}
        isOpen={isNetworkModalOpen}
        onClose={onCloseApplication}
      />
      <NewVersionAlert />

      <PageLayout>
        <Switch>
          <Route exact path="/">
            {isRunInSurge() ? <SurgeLandingPage /> : <LandingPage />}
          </Route>
          <Route exact path="/home">
            <IndexPage />
          </Route>
          <Route exact path="/policies">
            <PoliciesPage />
          </Route>
          <Route exact path="/requests">
            <RequestsPage />
          </Route>
          <Route exact path="/traffic">
            <TrafficPage />
          </Route>
          <Route exact path="/modules">
            <ModulesPage />
          </Route>
          <Route exact path="/scripting">
            <ScriptingPage />
          </Route>
          <Route exact path="/scripting/evaluate">
            <EvaluatePage />
          </Route>
          <Route exact path="/dns">
            <DnsPage />
          </Route>
          <Route exact path="/devices">
            <DevicesPage />
          </Route>
          <Route exact path="/profiles/current">
            <ProfilePage />
          </Route>
          <Route path="*">
            <Redirect to="/" />
          </Route>
        </Switch>
      </PageLayout>
    </SWRConfig>
  )
}
Example #2
Source File: SetHostModal.tsx    From yasd with MIT License 4 votes vote down vote up
SetHostModal: React.FC = () => {
  const { t } = useTranslation()
  const [existingProfiles, setExistingProfiles] = useState<Array<Profile>>([])
  const { setModal } = useModal()
  const currentProfile = useProfile()
  const history = useHistory()

  const selectProfile = useCallback(
    (id: string) => {
      const profile = find(existingProfiles, { id })

      if (profile) {
        store.set(LastUsedProfile, profile.id)
        window.location.reload()
      }
    },
    [existingProfiles],
  )

  useEffect(() => {
    const storedExistingProfiles = store.get(ExistingProfiles)

    if (storedExistingProfiles) {
      setExistingProfiles(storedExistingProfiles)
    }
  }, [])

  const showModal = () => {
    setModal({
      // eslint-disable-next-line react/display-name
      children: ({ onClose }) => {
        return (
          <ModalWrapper>
            <ModalHeader title={t('landing.history')} onClose={onClose} />

            <div tw="bg-gray-100 divide-y divide-gray-200 rounded overflow-hidden">
              {existingProfiles.map((profile) => {
                return (
                  <div
                    key={profile.id}
                    tw="flex flex-row items-center hover:bg-gray-200"
                  >
                    {profile.id === currentProfile?.id && (
                      <Badge variant="success" tw="ml-3 text-xs md:text-sm">
                        {t('landing.current')}
                      </Badge>
                    )}
                    <div tw="flex-1">
                      <ProfileCell
                        profile={profile}
                        checkConnectivity
                        onClick={() => selectProfile(profile.id)}
                      />
                    </div>
                  </div>
                )
              })}
            </div>

            <div tw="mt-4">
              <Button
                size="kilo"
                variant="primary"
                onClick={() => history.replace('/')}
              >
                {t('landing.add_new_host')}
              </Button>
            </div>
          </ModalWrapper>
        )
      },
      onClose() {
        // noop
      },
    })
  }

  return (
    <IconButton label={'change host'} tw="w-10 h-10 p-1" onClick={showModal}>
      <Laptop />
    </IconButton>
  )
}
Example #3
Source File: Regular.tsx    From yasd with MIT License 4 votes vote down vote up
Page: React.FC = () => {
  const history = useHistory()
  const protocol = window.location.protocol
  const { isLoading, setIsLoading } = useAuthData()
  const [existingProfiles, setExistingProfiles, getExistingProfiles] =
    useSetState<Array<Profile>>([])
  const profileDispatch = useProfileDispatch()
  const { t } = useTranslation()
  const {
    getValues,
    register,
    handleSubmit,
    control,
    clearErrors,
    setError,
    reset,
    formState: { errors },
  } = useForm<RegularFormFields>({
    defaultValues: {
      keepCredential: false,
      useTls: window.location.protocol === 'https:',
    },
  })

  const addProfile = (config: Omit<Profile, 'id'>): Profile => {
    const profile: Profile = {
      ...config,
      id: uuid(),
    }
    const newProfiles = [profile, ...existingProfiles]
    setExistingProfiles(newProfiles)

    if (getValues('keepCredential')) {
      store.set(ExistingProfiles, newProfiles)
      store.set(LastUsedProfile, profile.id)
    }

    return profile
  }

  const selectProfile = (id: string) => {
    getExistingProfiles().then((profiles) => {
      const profile = find(profiles, { id })

      if (profile) {
        if (getValues('keepCredential')) {
          store.set(LastUsedProfile, profile.id)
        }

        profileDispatch({
          type: 'update',
          payload: profile,
        })
        history.replace('/home')
      }
    })
  }

  const deleteProfile = (id: string) => {
    const profiles = existingProfiles.filter((item) => item.id !== id)

    setExistingProfiles(profiles)
    store.set(ExistingProfiles, profiles)
  }

  const onSubmit = (data: RegularFormFields) => {
    if (!data.name || !data.host || !data.port || !data.key) {
      return
    }

    setIsLoading(true)

    tryHost(data.useTls ? 'https:' : 'http:', data.host, data.port, data.key)
      .then((res) => {
        clearErrors()

        const newProfile = addProfile({
          name: data.name,
          host: data.host,
          port: Number(data.port),
          key: data.key,
          platform: res.platform,
          platformVersion: res.platformVersion,
          platformBuild: res.platformBuild,
          tls: data.useTls,
        })

        reset()
        setIsLoading(false)
        selectProfile(newProfile.id)
      })
      .catch((err) => {
        setError('key', {
          type: 'invalid',
          message: err.message,
        })
        setError('host', {
          type: 'invalid',
        })
        setError('port', {
          type: 'invalid',
        })
        console.error(err)
        setIsLoading(false)
      })
  }

  useEffect(() => {
    const storedExistingProfiles = store.get(ExistingProfiles)

    if (storedExistingProfiles) {
      setExistingProfiles(storedExistingProfiles)
    }
  }, [setExistingProfiles])

  return (
    <div
      css={css`
        padding-bottom: calc(env(safe-area-inset-bottom) + 1.25rem);
      `}
    >
      <Header />

      <div tw="max-w-xs sm:max-w-sm md:max-w-md mx-auto">
        <Heading size={'tera'}>{t('landing.add_new_host')}</Heading>

        <div tw="bg-teal-100 border-t-4 border-teal-500 rounded-b text-teal-900 text-sm px-4 py-3 mb-4 shadow-md">
          <p tw="leading-normal mb-2">
            该功能仅 Surge iOS 4.4.0 和 Surge Mac 4.0.0 以上版本支持。
          </p>
          <p tw="leading-normal mb-4">
            <a
              href="https://manual.nssurge.com/others/http-api.html#configuration"
              target="_blank"
              rel="noreferrer"
              tw="border-b border-solid border-teal-500"
            >
              ? 开启方式
            </a>
          </p>
          <p tw="leading-normal mb-2">
            Surge Mac v4.0.6 (1280) 开始已支持开启 HTTPS API,故不再支持使用
            yasd-helper。
          </p>
        </div>

        <form onSubmit={handleSubmit(onSubmit)}>
          <Input
            type="text"
            invalid={!!errors?.name}
            validationHint={getValidationHint(
              {
                required: t('devices.err_required'),
              },
              errors?.name,
            )}
            label={t('landing.name')}
            placeholder="Mac"
            {...register('name', { required: true })}
          />
          <Input
            type="text"
            invalid={!!errors?.host}
            label={t('landing.host')}
            placeholder="127.0.0.1"
            validationHint={t('landing.host_tips')}
            {...register('host', { required: true })}
          />
          <Input
            type="number"
            invalid={!!errors?.port}
            label={t('landing.port')}
            placeholder="6171"
            validationHint={getValidationHint(
              {
                required: t('devices.err_required'),
              },
              errors?.port,
            )}
            {...register('port', { required: true })}
          />
          <Input
            type="password"
            invalid={!!errors?.key}
            validationHint={getValidationHint(
              {
                required: t('devices.err_required'),
              },
              errors?.key,
            )}
            label={t('landing.key')}
            placeholder="examplekey"
            {...register('key', { required: true })}
          />

          <div>
            <Controller
              name="useTls"
              control={control}
              render={({ field }) => (
                <Checkbox
                  disabled={protocol === 'https:'}
                  checked={field.value}
                  onChange={field.onChange}
                >
                  {t('landing.https')}
                </Checkbox>
              )}
            />
            <Controller
              name="keepCredential"
              control={control}
              render={({ field }) => (
                <Checkbox checked={field.value} onChange={field.onChange}>
                  {t('landing.remember_me')}
                </Checkbox>
              )}
            />
          </div>

          <div tw="mt-6">
            <LoadingButton
              type="submit"
              variant="primary"
              stretch
              isLoading={isLoading}
              loadingLabel={t('landing.is_loading')}
            >
              {t('landing.confirm')}
            </LoadingButton>
          </div>
        </form>
      </div>

      {existingProfiles.length > 0 && (
        <div tw="max-w-xs sm:max-w-sm md:max-w-md mx-auto mt-10">
          <Heading size={'mega'}>{t('landing.history')}</Heading>

          <div tw="bg-gray-100 divide-y divide-gray-200 rounded overflow-hidden">
            {existingProfiles.map((profile) => {
              return (
                <div key={profile.id} tw="hover:bg-gray-200">
                  <ProfileCell
                    profile={profile}
                    variant="left"
                    checkConnectivity
                    showDelete
                    onClick={() => selectProfile(profile.id)}
                    onDelete={() => deleteProfile(profile.id)}
                  />
                </div>
              )
            })}
          </div>
        </div>
      )}

      <div tw="max-w-xs sm:max-w-sm md:max-w-md mx-auto mt-10">
        <Ad />
      </div>

      <div tw="mt-10">
        <ChangeLanguage />
      </div>
    </div>
  )
}
Example #4
Source File: Surge.tsx    From yasd with MIT License 4 votes vote down vote up
Page: React.FC = () => {
  const { t } = useTranslation()
  const { isLoading, setIsLoading } = useAuthData()
  const [existingProfiles, setExistingProfiles, getExistingProfiles] =
    useSetState<Array<Profile>>([])
  const profileDispatch = useProfileDispatch()
  const profile = useProfile()
  const history = useHistory()
  const {
    getValues,
    register,
    handleSubmit,
    control,
    clearErrors,
    setError,
    formState: { errors },
  } = useForm<SurgeFormFields>({
    defaultValues: {
      keepCredential: false,
    },
  })

  const addProfile = (config: Omit<Profile, 'id'>): Profile => {
    const profile: Profile = {
      ...config,
      id: uuid(),
    }
    const newProfiles = [profile, ...existingProfiles]
    setExistingProfiles(newProfiles)

    if (getValues('keepCredential')) {
      store.set(ExistingProfiles, newProfiles)
      store.set(LastUsedProfile, profile.id)
    }

    return profile
  }

  const selectProfile = (id: string) => {
    getExistingProfiles().then((profiles) => {
      const profile = find(profiles, { id })

      if (profile) {
        if (getValues('keepCredential')) {
          store.set(LastUsedProfile, profile.id)
        }

        profileDispatch({
          type: 'update',
          payload: profile,
        })
      }
    })
  }

  const getHost: () => {
    protocol: string
    hostname: string
    port: string
  } = () => {
    const protocol = window.location.protocol

    if (process.env.NODE_ENV === 'production') {
      return {
        protocol,
        hostname: window.location.hostname,
        port: window.location.port,
      }
    }
    return {
      protocol: process.env.REACT_APP_PROTOCOL as string,
      hostname: process.env.REACT_APP_HOST as string,
      port: process.env.REACT_APP_PORT as string,
    }
  }

  const onSubmit = (data: SurgeFormFields) => {
    if (!data.key) {
      return
    }

    const { hostname, port, protocol } = getHost()
    setIsLoading(true)

    tryHost(protocol, hostname, port, data.key)
      .then((res) => {
        clearErrors()

        const newProfile = addProfile({
          name: res.name || 'Surge for Mac',
          host: hostname,
          port: Number(port),
          key: data.key,
          platform: res.platform,
          platformVersion: res.platformVersion,
          platformBuild: res.platformBuild,
          tls: protocol === 'https:',
        })

        setIsLoading(false)
        selectProfile(newProfile.id)
      })
      .catch((err) => {
        setError('key', {
          type: 'invalid',
          message: err.message,
        })
        console.error(err)
        setIsLoading(false)
      })
  }

  useEffect(() => {
    const storedExistingProfiles = store.get(ExistingProfiles)
    const lastId = store.get(LastUsedProfile)

    if (storedExistingProfiles) {
      const result = find<Profile>(storedExistingProfiles, { id: lastId })

      setExistingProfiles(storedExistingProfiles)

      if (result) {
        profileDispatch({
          type: 'update',
          payload: result,
        })
      }
    }
  }, [profileDispatch, setExistingProfiles])

  useEffect(() => {
    if (profile) {
      history.replace('/home')
    }
  }, [profile, history])

  return (
    <div
      css={css`
        padding-bottom: calc(env(safe-area-inset-bottom) + 1.25rem);
      `}
    >
      <Header />

      <div tw="max-w-xs sm:max-w-sm md:max-w-md mx-auto">
        <Heading size={'tera'}>{t('landing.login')}</Heading>

        <form onSubmit={handleSubmit(onSubmit)}>
          <Input
            type="password"
            invalid={!!errors?.key}
            validationHint={getValidationHint(
              {
                required: t('devices.err_required'),
              },
              errors?.key,
            )}
            label={t('landing.key')}
            placeholder="examplekey"
            {...register('key', { required: true })}
          />

          <div>
            <Controller
              name="keepCredential"
              control={control}
              render={({ field }) => (
                <Checkbox checked={field.value} onChange={field.onChange}>
                  {t('landing.remember_me')}
                </Checkbox>
              )}
            />
          </div>

          <div tw="mt-6">
            <LoadingButton
              type="submit"
              variant="primary"
              stretch
              isLoading={isLoading}
              loadingLabel={t('landing.is_loading')}
            >
              {t('landing.confirm')}
            </LoadingButton>
          </div>
        </form>
      </div>

      <div tw="mt-10">
        <ChangeLanguage />
      </div>
    </div>
  )
}