get feature list on web

This commit is contained in:
Hongbo Wu
2024-03-28 15:27:37 +08:00
parent cc2fe04568
commit f677f1167f
10 changed files with 62 additions and 34 deletions

View File

@ -3696,7 +3696,7 @@ export enum UploadImportFileType {
export type User = {
__typename?: 'User';
email?: Maybe<Scalars['String']>;
featureList?: Maybe<Array<Maybe<Feature>>>;
featureList?: Maybe<Array<Feature>>;
features?: Maybe<Array<Maybe<Scalars['String']>>>;
followersCount?: 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']> = {
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>;
followersCount?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
friendsCount?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;

View File

@ -2984,7 +2984,7 @@ enum UploadImportFileType {
type User {
email: String
featureList: [Feature]
featureList: [Feature!]
features: [String]
followersCount: Int
friendsCount: Int

View File

@ -368,6 +368,17 @@ export const functionResolvers = {
}
return undefined
},
async featureList(
_: User,
__: Record<string, unknown>,
ctx: WithDataSourcesContext
) {
if (!ctx.claims?.uid) {
return undefined
}
return findUserFeatures(ctx.claims.uid)
},
async features(
user: User,
__: Record<string, unknown>,
@ -376,18 +387,8 @@ export const functionResolvers = {
if (!ctx.claims?.uid) {
return undefined
}
const userFeatures = await findUserFeatures(ctx.claims.uid)
return userFeatures.map((feature) => feature.name)
},
async featureList(
_: User,
__: Record<string, unknown>,
ctx: WithDataSourcesContext
) {
if (!ctx.uid) {
return undefined
}
return findUserFeatures(ctx.uid)
return (await findUserFeatures(ctx.claims.uid)).map((f) => f.name)
},
},
Article: {

View File

@ -89,7 +89,7 @@ const schema = gql`
source: String
intercomHash: String
features: [String]
featureList: [Feature]
featureList: [Feature!]
}
type Profile {

View File

@ -123,10 +123,8 @@ export const signFeatureToken = (
}
export const findUserFeatures = async (userId: string) => {
return getRepository(Feature).find({
where: {
user: { id: userId },
},
return getRepository(Feature).findBy({
user: { id: userId },
})
}

View File

@ -7,5 +7,10 @@ export const userHasFeature = (
if (!user) {
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())
)
}

View 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
}

View File

@ -1,16 +1,17 @@
import { gql } from 'graphql-request'
import { Feature, featureFragment } from '../fragments/featureFragment'
import { gqlFetcher } from '../networkHelpers'
export interface OptInFeatureInput {
name: string
}
export interface OptInFeatureSuccess {
feature: { id: string }
export interface OptInFeatureResponse {
feature?: Feature
}
interface Response {
optInFeature: OptInFeatureSuccess
optInFeature: OptInFeatureResponse
}
export async function optInFeature(
@ -21,7 +22,7 @@ export async function optInFeature(
optInFeature(input: $input) {
... on OptInFeatureSuccess {
feature {
id
...FeatureFields
}
}
... on OptInFeatureError {
@ -29,17 +30,14 @@ export async function optInFeature(
}
}
}
${featureFragment}
`
try {
const data = await gqlFetcher(mutation, {
input,
})
const output = data as Response | undefined
if (
!output ||
!output.optInFeature ||
'errorCodes' in output?.optInFeature
) {
if (!output || !output.optInFeature.feature) {
return false
}
return true

View File

@ -1,5 +1,6 @@
import { gql } from 'graphql-request'
import useSWR from 'swr'
import { Feature, featureFragment } from '../fragments/featureFragment'
import { publicGqlFetcher } from '../networkHelpers'
type ViewerQueryResponse = {
@ -22,6 +23,7 @@ export type UserBasicData = {
source: string
intercomHash: string
features: string[]
featureList: Feature[]
}
export type UserProfile = {
@ -48,8 +50,12 @@ export function useGetViewerQuery(): ViewerQueryResponse {
source
intercomHash
features
featureList {
...FeatureFields
}
}
}
${featureFragment}
`
const { data, error, mutate } = useSWR(query, publicGqlFetcher)

View File

@ -6,6 +6,7 @@ import { HStack, VStack } from '../../../components/elements/LayoutPrimitives'
import { StyledText } from '../../../components/elements/StyledText'
import { SettingsLayout } from '../../../components/templates/SettingsLayout'
import { styled } from '../../../components/tokens/stitches.config'
import { userHasFeature } from '../../../lib/featureFlag'
import { optInFeature } from '../../../lib/networking/mutations/optIntoFeatureMutation'
import { useGetViewerQuery } from '../../../lib/networking/queries/useGetViewerQuery'
import { applyStoredTheme } from '../../../lib/themeUpdater'
@ -43,13 +44,11 @@ export default function Account(): JSX.Element {
)
const hasYouTube = useMemo(() => {
return (
(viewerData?.me?.features.indexOf('youtube-transcripts') ?? -1) !== -1
)
return userHasFeature(viewerData?.me, 'youtube-transcripts')
}, [viewerData])
const hasNotion = useMemo(() => {
return (viewerData?.me?.features.indexOf('notion') ?? -1) !== -1
return userHasFeature(viewerData?.me, 'notion')
}, [viewerData])
applyStoredTheme()