From 36c312ca754b00ffdcf5afe9a603217f8ffc70f6 Mon Sep 17 00:00:00 2001 From: Jackson Harper Date: Tue, 19 Mar 2024 10:12:49 +0800 Subject: [PATCH] Add missing YT files --- .../mutations/optIntoFeatureMutation.ts | 49 ++++ packages/web/pages/settings/features/beta.tsx | 231 ++++++++++++++++++ 2 files changed, 280 insertions(+) create mode 100644 packages/web/lib/networking/mutations/optIntoFeatureMutation.ts create mode 100644 packages/web/pages/settings/features/beta.tsx diff --git a/packages/web/lib/networking/mutations/optIntoFeatureMutation.ts b/packages/web/lib/networking/mutations/optIntoFeatureMutation.ts new file mode 100644 index 000000000..26ac49d47 --- /dev/null +++ b/packages/web/lib/networking/mutations/optIntoFeatureMutation.ts @@ -0,0 +1,49 @@ +import { gql } from 'graphql-request' +import { gqlFetcher } from '../networkHelpers' + +export interface OptInFeatureInput { + name: string +} + +export interface OptInFeatureSuccess { + feature: { id: string } +} + +interface Response { + optInFeature: OptInFeatureSuccess +} + +export async function optInFeature( + input: OptInFeatureInput +): Promise { + const mutation = gql` + mutation OptInFeature($input: OptInFeatureInput!) { + optInFeature(input: $input) { + ... on OptInFeatureSuccess { + feature { + id + } + } + ... on OptInFeatureError { + errorCodes + } + } + } + ` + try { + const data = await gqlFetcher(mutation, { + input, + }) + const output = data as Response | undefined + if ( + !output || + !output.optInFeature || + 'errorCodes' in output?.optInFeature + ) { + return false + } + return true + } catch (err) { + return undefined + } +} diff --git a/packages/web/pages/settings/features/beta.tsx b/packages/web/pages/settings/features/beta.tsx new file mode 100644 index 000000000..c48d274de --- /dev/null +++ b/packages/web/pages/settings/features/beta.tsx @@ -0,0 +1,231 @@ +import { useCallback, useEffect, useMemo, useState } from 'react' +import { Toaster } from 'react-hot-toast' +import { Button } from '../../../components/elements/Button' +import { + Box, + HStack, + SpanBox, + VStack, +} from '../../../components/elements/LayoutPrimitives' +import { StyledText } from '../../../components/elements/StyledText' +import { SettingsLayout } from '../../../components/templates/SettingsLayout' +import { styled, theme } from '../../../components/tokens/stitches.config' +import { updateEmailMutation } from '../../../lib/networking/mutations/updateEmailMutation' +import { updateUserMutation } from '../../../lib/networking/mutations/updateUserMutation' +import { updateUserProfileMutation } from '../../../lib/networking/mutations/updateUserProfileMutation' +import { useGetLibraryItemsQuery } from '../../../lib/networking/queries/useGetLibraryItemsQuery' +import { useGetViewerQuery } from '../../../lib/networking/queries/useGetViewerQuery' +import { useValidateUsernameQuery } from '../../../lib/networking/queries/useValidateUsernameQuery' +import { applyStoredTheme } from '../../../lib/themeUpdater' +import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers' +import { ConfirmationModal } from '../../../components/patterns/ConfirmationModal' +import { ProgressBar } from '../../../components/elements/ProgressBar' +import { emptyTrashMutation } from '../../../lib/networking/mutations/emptyTrashMutation' +import { ProgressIndicator } from '@radix-ui/react-progress' +import { Spinner } from 'phosphor-react' +import { optInFeature } from '../../../lib/networking/mutations/optIntoFeatureMutation' + +const ACCOUNT_LIMIT = 50_000 + +const StyledLabel = styled('label', { + fontWeight: 600, + fontSize: '16px', + marginBottom: '5px', +}) + +export default function Account(): JSX.Element { + const { viewerData, isLoading, mutate } = useGetViewerQuery() + const [pageLoading, setPageLoading] = useState(false) + + const showSpinner = useMemo(() => { + return isLoading || pageLoading + }, [isLoading, pageLoading]) + + const requestFeatureAccess = useCallback( + async (featureName: string) => { + setPageLoading(true) + const result = await optInFeature({ name: featureName }) + if (!result) { + showErrorToast('Error opting into feature.') + } else { + showSuccessToast('Feature added') + } + mutate() + setPageLoading(false) + }, + [setPageLoading, mutate] + ) + + const hasYouTube = useMemo(() => { + return ( + (viewerData?.me?.features.indexOf('youtube-transcripts') ?? -1) !== -1 + ) + }, [viewerData]) + + applyStoredTheme() + + return ( + + + + + + + Enabled beta features + {!showSpinner ? ( + <> + {viewerData?.me?.features.map((feature) => { + return ( + + + {feature} + + ) + })} + + {!hasYouTube /* || !hasAISummaries || !hasDigest */ && ( + + Available beta features + + )} + + + {!hasYouTube && ( + + + - YouTube transcripts: nicely formatted documents + generated from YouTube transcript data. Currently + limited to videos under 30 minutes. + + + + )} + + {/* + + - AI Summaries: Short summaries of your newly saved + articles + + + + + + + - Daily digest: Every day we pick some of the items we + think You will enjoy reading the most and create a daily + digest of them. + + + */} + + + ) : ( + + + + )} + + + + + ) +}