Merge pull request #1406 from omnivore-app/set-voice-in-user-personalization

set voice in user personalization
This commit is contained in:
Hongbo Wu 2022-11-11 16:57:49 +08:00 committed by GitHub
commit a78074b0da
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 81 additions and 30 deletions

View file

@ -22,7 +22,7 @@ export class NewsletterEmail {
user!: User
@Column('varchar', { nullable: true })
confirmationCode?: string
confirmationCode?: string | null
@CreateDateColumn()
createdAt!: Date

View file

@ -19,34 +19,34 @@ export class UserPersonalization {
user!: User
@Column('text', { nullable: true })
fontFamily?: string
fontFamily?: string | null
@Column('integer', { nullable: true })
fontSize?: number
fontSize?: number | null
@Column('text', { nullable: true })
margin?: number
margin?: number | null
@Column('text', { nullable: true })
theme?: string
theme?: string | null
@Column('text', { nullable: true })
libraryLayoutType?: string
libraryLayoutType?: string | null
@Column('text', { nullable: true })
librarySortOrder?: string
librarySortOrder?: string | null
@Column('text', { nullable: true })
speechVoice?: string
speechVoice?: string | null
@Column('text', { nullable: true })
speechSecondaryVoice?: string
speechSecondaryVoice?: string | null
@Column('text', { nullable: true })
speechRate?: string
speechRate?: string | null
@Column('text', { nullable: true })
speechVolume?: string
speechVolume?: string | null
@CreateDateColumn({ default: () => 'CURRENT_TIMESTAMP' })
createdAt!: Date

View file

@ -1936,6 +1936,7 @@ export type SetUserPersonalizationError = {
};
export enum SetUserPersonalizationErrorCode {
NotFound = 'NOT_FOUND',
Unauthorized = 'UNAUTHORIZED'
}
@ -1945,6 +1946,10 @@ export type SetUserPersonalizationInput = {
libraryLayoutType?: InputMaybe<Scalars['String']>;
librarySortOrder?: InputMaybe<SortOrder>;
margin?: InputMaybe<Scalars['Int']>;
speechRate?: InputMaybe<Scalars['String']>;
speechSecondaryVoice?: InputMaybe<Scalars['String']>;
speechVoice?: InputMaybe<Scalars['String']>;
speechVolume?: InputMaybe<Scalars['String']>;
theme?: InputMaybe<Scalars['String']>;
};
@ -2458,6 +2463,10 @@ export type UserPersonalization = {
libraryLayoutType?: Maybe<Scalars['String']>;
librarySortOrder?: Maybe<SortOrder>;
margin?: Maybe<Scalars['Int']>;
speechRate?: Maybe<Scalars['String']>;
speechSecondaryVoice?: Maybe<Scalars['String']>;
speechVoice?: Maybe<Scalars['String']>;
speechVolume?: Maybe<Scalars['String']>;
theme?: Maybe<Scalars['String']>;
};
@ -4767,6 +4776,10 @@ export type UserPersonalizationResolvers<ContextType = ResolverContext, ParentTy
libraryLayoutType?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
librarySortOrder?: Resolver<Maybe<ResolversTypes['SortOrder']>, ParentType, ContextType>;
margin?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
speechRate?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
speechSecondaryVoice?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
speechVoice?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
speechVolume?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
theme?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};

View file

@ -1432,6 +1432,7 @@ type SetUserPersonalizationError {
}
enum SetUserPersonalizationErrorCode {
NOT_FOUND
UNAUTHORIZED
}
@ -1441,6 +1442,10 @@ input SetUserPersonalizationInput {
libraryLayoutType: String
librarySortOrder: SortOrder
margin: Int
speechRate: String
speechSecondaryVoice: String
speechVoice: String
speechVolume: String
theme: String
}
@ -1911,6 +1916,10 @@ type UserPersonalization {
libraryLayoutType: String
librarySortOrder: SortOrder
margin: Int
speechRate: String
speechSecondaryVoice: String
speechVoice: String
speechVolume: String
theme: String
}

View file

@ -1,27 +1,45 @@
import {
GetUserPersonalizationResult,
GetUserPersonalizationError,
SetUserPersonalizationSuccess,
SetUserPersonalizationError,
GetUserPersonalizationResult,
MutationSetUserPersonalizationArgs,
SetUserPersonalizationError,
SetUserPersonalizationErrorCode,
SetUserPersonalizationSuccess,
SortOrder,
} from '../../generated/graphql'
import { authorized } from '../../utils/helpers'
import { UserPersonalization } from '../../entity/user_personalization'
import { AppDataSource } from '../../server'
import { getRepository, setClaims } from '../../entity/utils'
export const setUserPersonalizationResolver = authorized<
SetUserPersonalizationSuccess,
SetUserPersonalizationError,
MutationSetUserPersonalizationArgs
>(async (_, { input }, { models, authTrx, claims: { uid } }) => {
const updatedUserPersonalization = await authTrx((tx) =>
models.userPersonalization.upsert(
>(async (_, { input }, { claims: { uid }, log }) => {
log.info('setUserPersonalizationResolver', { uid, input })
const result = await AppDataSource.transaction(async (entityManager) => {
await setClaims(entityManager, uid)
return entityManager.getRepository(UserPersonalization).upsert(
{
userId: uid,
user: { id: uid },
...input,
},
tx
['user']
)
)
})
if (result.identifiers.length === 0) {
return {
errorCodes: [SetUserPersonalizationErrorCode.NotFound],
}
}
const updatedUserPersonalization = await getRepository(
UserPersonalization
).findOneBy({ id: result.identifiers[0].id as string })
// Cast SortOrder from string to enum
const librarySortOrder = updatedUserPersonalization?.librarySortOrder as

View file

@ -963,6 +963,10 @@ const schema = gql`
margin: Int
libraryLayoutType: String
librarySortOrder: SortOrder
speechVoice: String
speechSecondaryVoice: String
speechRate: String
speechVolume: String
}
# Query: UserPersonalization
@ -985,6 +989,7 @@ const schema = gql`
| SetUserPersonalizationError
enum SetUserPersonalizationErrorCode {
UNAUTHORIZED
NOT_FOUND
}
type SetUserPersonalizationError {
errorCodes: [SetUserPersonalizationErrorCode!]!
@ -999,6 +1004,10 @@ const schema = gql`
margin: Int
libraryLayoutType: String @sanitize
librarySortOrder: SortOrder
speechVoice: String
speechSecondaryVoice: String
speechRate: String
speechVolume: String
}
# Type: ArticleSavingRequest

View file

@ -106,9 +106,7 @@ const validateInvite = async (
return false
}
const membershipRepo = entityManager.getRepository(GroupMembership)
const numMembers = await membershipRepo.count({
where: { invite: invite },
})
const numMembers = await membershipRepo.countBy({ invite: { id: invite.id } })
if (numMembers >= invite.maxMembers) {
console.log('rejecting invite, too many users', invite, numMembers)
return false

View file

@ -1,5 +1,8 @@
import { env } from '../env'
export const corsConfig = {
credentials: true,
// allow https://studio.apollographql.com for local env
origin: [
'https://omnivore.app',
'https://dev.omnivore.app',
@ -9,5 +12,6 @@ export const corsConfig = {
'https://web-demo.omnivore.app',
'http://localhost:3000',
'lsp://logseq.io',
env.dev.isLocal && 'https://studio.apollographql.com',
],
}

View file

@ -10,7 +10,7 @@ import sinon, { SinonFakeTimers } from 'sinon'
import { env } from '../../src/env'
import { Like } from 'typeorm'
describe('features resolvers', () => {
xdescribe('features resolvers', () => {
let loginUser: User
let authToken: string

View file

@ -90,7 +90,7 @@ describe('Integrations resolvers', () => {
before(async () => {
existingIntegration = await getRepository(Integration).save({
user: loginUser,
user: { id: loginUser.id },
type: DataIntegrationType.Readwise,
token: 'fakeToken',
})
@ -138,7 +138,7 @@ describe('Integrations resolvers', () => {
afterEach(async () => {
await getRepository(Integration).delete({
user: loginUser,
user: { id: loginUser.id },
type: integrationType,
})
})
@ -191,7 +191,7 @@ describe('Integrations resolvers', () => {
before(async () => {
otherUser = await createTestUser('otherUser')
existingIntegration = await getRepository(Integration).save({
user: otherUser,
user: { id: otherUser.id },
type: DataIntegrationType.Readwise,
token: 'fakeToken',
})
@ -219,7 +219,7 @@ describe('Integrations resolvers', () => {
context('when integration belongs to the user', () => {
before(async () => {
existingIntegration = await getRepository(Integration).save({
user: loginUser,
user: { id: loginUser.id },
type: DataIntegrationType.Readwise,
token: 'fakeToken',
})
@ -321,7 +321,7 @@ describe('Integrations resolvers', () => {
before(async () => {
existingIntegration = await getRepository(Integration).save({
user: loginUser,
user: { id: loginUser.id },
type: DataIntegrationType.Readwise,
token: 'fakeToken',
})
@ -367,7 +367,7 @@ describe('Integrations resolvers', () => {
beforeEach(async () => {
existingIntegration = await getRepository(Integration).save({
user: loginUser,
user: { id: loginUser.id },
type: DataIntegrationType.Readwise,
token: 'fakeToken',
taskName: 'some task name',