get feature list on web
This commit is contained in:
@ -3696,7 +3696,7 @@ export enum UploadImportFileType {
|
|||||||
export type User = {
|
export type User = {
|
||||||
__typename?: 'User';
|
__typename?: 'User';
|
||||||
email?: Maybe<Scalars['String']>;
|
email?: Maybe<Scalars['String']>;
|
||||||
featureList?: Maybe<Array<Maybe<Feature>>>;
|
featureList?: Maybe<Array<Feature>>;
|
||||||
features?: Maybe<Array<Maybe<Scalars['String']>>>;
|
features?: Maybe<Array<Maybe<Scalars['String']>>>;
|
||||||
followersCount?: Maybe<Scalars['Int']>;
|
followersCount?: Maybe<Scalars['Int']>;
|
||||||
friendsCount?: Maybe<Scalars['Int']>;
|
friendsCount?: Maybe<Scalars['Int']>;
|
||||||
@ -7140,7 +7140,7 @@ export type UploadImportFileSuccessResolvers<ContextType = ResolverContext, Pare
|
|||||||
|
|
||||||
export type UserResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['User'] = ResolversParentTypes['User']> = {
|
export type UserResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['User'] = ResolversParentTypes['User']> = {
|
||||||
email?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
email?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||||
featureList?: Resolver<Maybe<Array<Maybe<ResolversTypes['Feature']>>>, ParentType, ContextType>;
|
featureList?: Resolver<Maybe<Array<ResolversTypes['Feature']>>, ParentType, ContextType>;
|
||||||
features?: Resolver<Maybe<Array<Maybe<ResolversTypes['String']>>>, ParentType, ContextType>;
|
features?: Resolver<Maybe<Array<Maybe<ResolversTypes['String']>>>, ParentType, ContextType>;
|
||||||
followersCount?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
|
followersCount?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
|
||||||
friendsCount?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
|
friendsCount?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
|
||||||
|
|||||||
@ -2984,7 +2984,7 @@ enum UploadImportFileType {
|
|||||||
|
|
||||||
type User {
|
type User {
|
||||||
email: String
|
email: String
|
||||||
featureList: [Feature]
|
featureList: [Feature!]
|
||||||
features: [String]
|
features: [String]
|
||||||
followersCount: Int
|
followersCount: Int
|
||||||
friendsCount: Int
|
friendsCount: Int
|
||||||
|
|||||||
@ -368,6 +368,17 @@ export const functionResolvers = {
|
|||||||
}
|
}
|
||||||
return undefined
|
return undefined
|
||||||
},
|
},
|
||||||
|
async featureList(
|
||||||
|
_: User,
|
||||||
|
__: Record<string, unknown>,
|
||||||
|
ctx: WithDataSourcesContext
|
||||||
|
) {
|
||||||
|
if (!ctx.claims?.uid) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
return findUserFeatures(ctx.claims.uid)
|
||||||
|
},
|
||||||
async features(
|
async features(
|
||||||
user: User,
|
user: User,
|
||||||
__: Record<string, unknown>,
|
__: Record<string, unknown>,
|
||||||
@ -376,18 +387,8 @@ export const functionResolvers = {
|
|||||||
if (!ctx.claims?.uid) {
|
if (!ctx.claims?.uid) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
const userFeatures = await findUserFeatures(ctx.claims.uid)
|
|
||||||
return userFeatures.map((feature) => feature.name)
|
return (await findUserFeatures(ctx.claims.uid)).map((f) => f.name)
|
||||||
},
|
|
||||||
async featureList(
|
|
||||||
_: User,
|
|
||||||
__: Record<string, unknown>,
|
|
||||||
ctx: WithDataSourcesContext
|
|
||||||
) {
|
|
||||||
if (!ctx.uid) {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
return findUserFeatures(ctx.uid)
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Article: {
|
Article: {
|
||||||
|
|||||||
@ -89,7 +89,7 @@ const schema = gql`
|
|||||||
source: String
|
source: String
|
||||||
intercomHash: String
|
intercomHash: String
|
||||||
features: [String]
|
features: [String]
|
||||||
featureList: [Feature]
|
featureList: [Feature!]
|
||||||
}
|
}
|
||||||
|
|
||||||
type Profile {
|
type Profile {
|
||||||
|
|||||||
@ -123,10 +123,8 @@ export const signFeatureToken = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const findUserFeatures = async (userId: string) => {
|
export const findUserFeatures = async (userId: string) => {
|
||||||
return getRepository(Feature).find({
|
return getRepository(Feature).findBy({
|
||||||
where: {
|
user: { id: userId },
|
||||||
user: { id: userId },
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,5 +7,10 @@ export const userHasFeature = (
|
|||||||
if (!user) {
|
if (!user) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return user.features.includes(feature)
|
return user.featureList.some(
|
||||||
|
(f) =>
|
||||||
|
f.name === feature &&
|
||||||
|
f.grantedAt &&
|
||||||
|
(!f.expiresAt || new Date(f.expiresAt) > new Date())
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
21
packages/web/lib/networking/fragments/featureFragment.ts
Normal file
21
packages/web/lib/networking/fragments/featureFragment.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { gql } from 'graphql-request'
|
||||||
|
|
||||||
|
export const featureFragment = gql`
|
||||||
|
fragment FeatureFields on Feature {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
grantedAt
|
||||||
|
expiresAt
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export interface Feature {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
createdAt: Date
|
||||||
|
updatedAt?: Date
|
||||||
|
grantedAt?: Date
|
||||||
|
expiresAt?: Date
|
||||||
|
}
|
||||||
@ -1,16 +1,17 @@
|
|||||||
import { gql } from 'graphql-request'
|
import { gql } from 'graphql-request'
|
||||||
|
import { Feature, featureFragment } from '../fragments/featureFragment'
|
||||||
import { gqlFetcher } from '../networkHelpers'
|
import { gqlFetcher } from '../networkHelpers'
|
||||||
|
|
||||||
export interface OptInFeatureInput {
|
export interface OptInFeatureInput {
|
||||||
name: string
|
name: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OptInFeatureSuccess {
|
export interface OptInFeatureResponse {
|
||||||
feature: { id: string }
|
feature?: Feature
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Response {
|
interface Response {
|
||||||
optInFeature: OptInFeatureSuccess
|
optInFeature: OptInFeatureResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function optInFeature(
|
export async function optInFeature(
|
||||||
@ -21,7 +22,7 @@ export async function optInFeature(
|
|||||||
optInFeature(input: $input) {
|
optInFeature(input: $input) {
|
||||||
... on OptInFeatureSuccess {
|
... on OptInFeatureSuccess {
|
||||||
feature {
|
feature {
|
||||||
id
|
...FeatureFields
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
... on OptInFeatureError {
|
... on OptInFeatureError {
|
||||||
@ -29,17 +30,14 @@ export async function optInFeature(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
${featureFragment}
|
||||||
`
|
`
|
||||||
try {
|
try {
|
||||||
const data = await gqlFetcher(mutation, {
|
const data = await gqlFetcher(mutation, {
|
||||||
input,
|
input,
|
||||||
})
|
})
|
||||||
const output = data as Response | undefined
|
const output = data as Response | undefined
|
||||||
if (
|
if (!output || !output.optInFeature.feature) {
|
||||||
!output ||
|
|
||||||
!output.optInFeature ||
|
|
||||||
'errorCodes' in output?.optInFeature
|
|
||||||
) {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { gql } from 'graphql-request'
|
import { gql } from 'graphql-request'
|
||||||
import useSWR from 'swr'
|
import useSWR from 'swr'
|
||||||
|
import { Feature, featureFragment } from '../fragments/featureFragment'
|
||||||
import { publicGqlFetcher } from '../networkHelpers'
|
import { publicGqlFetcher } from '../networkHelpers'
|
||||||
|
|
||||||
type ViewerQueryResponse = {
|
type ViewerQueryResponse = {
|
||||||
@ -22,6 +23,7 @@ export type UserBasicData = {
|
|||||||
source: string
|
source: string
|
||||||
intercomHash: string
|
intercomHash: string
|
||||||
features: string[]
|
features: string[]
|
||||||
|
featureList: Feature[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserProfile = {
|
export type UserProfile = {
|
||||||
@ -48,8 +50,12 @@ export function useGetViewerQuery(): ViewerQueryResponse {
|
|||||||
source
|
source
|
||||||
intercomHash
|
intercomHash
|
||||||
features
|
features
|
||||||
|
featureList {
|
||||||
|
...FeatureFields
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
${featureFragment}
|
||||||
`
|
`
|
||||||
|
|
||||||
const { data, error, mutate } = useSWR(query, publicGqlFetcher)
|
const { data, error, mutate } = useSWR(query, publicGqlFetcher)
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { HStack, VStack } from '../../../components/elements/LayoutPrimitives'
|
|||||||
import { StyledText } from '../../../components/elements/StyledText'
|
import { StyledText } from '../../../components/elements/StyledText'
|
||||||
import { SettingsLayout } from '../../../components/templates/SettingsLayout'
|
import { SettingsLayout } from '../../../components/templates/SettingsLayout'
|
||||||
import { styled } from '../../../components/tokens/stitches.config'
|
import { styled } from '../../../components/tokens/stitches.config'
|
||||||
|
import { userHasFeature } from '../../../lib/featureFlag'
|
||||||
import { optInFeature } from '../../../lib/networking/mutations/optIntoFeatureMutation'
|
import { optInFeature } from '../../../lib/networking/mutations/optIntoFeatureMutation'
|
||||||
import { useGetViewerQuery } from '../../../lib/networking/queries/useGetViewerQuery'
|
import { useGetViewerQuery } from '../../../lib/networking/queries/useGetViewerQuery'
|
||||||
import { applyStoredTheme } from '../../../lib/themeUpdater'
|
import { applyStoredTheme } from '../../../lib/themeUpdater'
|
||||||
@ -43,13 +44,11 @@ export default function Account(): JSX.Element {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const hasYouTube = useMemo(() => {
|
const hasYouTube = useMemo(() => {
|
||||||
return (
|
return userHasFeature(viewerData?.me, 'youtube-transcripts')
|
||||||
(viewerData?.me?.features.indexOf('youtube-transcripts') ?? -1) !== -1
|
|
||||||
)
|
|
||||||
}, [viewerData])
|
}, [viewerData])
|
||||||
|
|
||||||
const hasNotion = useMemo(() => {
|
const hasNotion = useMemo(() => {
|
||||||
return (viewerData?.me?.features.indexOf('notion') ?? -1) !== -1
|
return userHasFeature(viewerData?.me, 'notion')
|
||||||
}, [viewerData])
|
}, [viewerData])
|
||||||
|
|
||||||
applyStoredTheme()
|
applyStoredTheme()
|
||||||
|
|||||||
Reference in New Issue
Block a user