diff --git a/.storybook/storybook.requires.js b/.storybook/storybook.requires.js index eca88496f5..a956ef5e64 100644 --- a/.storybook/storybook.requires.js +++ b/.storybook/storybook.requires.js @@ -67,6 +67,7 @@ const getStories = () => { "./app/screens/authentication-screen/authentication-check-screen.stories.tsx": require("../app/screens/authentication-screen/authentication-check-screen.stories.tsx"), "./app/screens/authentication-screen/authentication-screen.stories.tsx": require("../app/screens/authentication-screen/authentication-screen.stories.tsx"), "./app/screens/authentication-screen/pin-screen.stories.tsx": require("../app/screens/authentication-screen/pin-screen.stories.tsx"), + "./app/screens/conversation/conversation.stories.tsx": require("../app/screens/conversation/conversation.stories.tsx"), "./app/screens/conversion-flow/conversion-success-screen.stories.tsx": require("../app/screens/conversion-flow/conversion-success-screen.stories.tsx"), "./app/screens/earns-map-screen/earns-map-screen.stories.tsx": require("../app/screens/earns-map-screen/earns-map-screen.stories.tsx"), "./app/screens/earns-screen/earns-quiz.stories.tsx": require("../app/screens/earns-screen/earns-quiz.stories.tsx"), diff --git a/.storybook/storybook.tsx b/.storybook/storybook.tsx index f04db77a4c..de63055e6b 100644 --- a/.storybook/storybook.tsx +++ b/.storybook/storybook.tsx @@ -20,9 +20,9 @@ import { NotificationsProvider } from "../app/components/notifications" RNBootSplash.hide({ fade: true }) const StorybookUI = getStorybookUI({ - enableWebsockets: true, // for @storybook/react-native-server + enableWebsockets: true, onDeviceUI: true, - initialSelection: { kind: "Notification Card UI", name: "Default" }, + initialSelection: { kind: "Conversation Screen", name: "Default" }, shouldPersistSelection: false, }) diff --git a/app/components/contact-modal/contact-modal.tsx b/app/components/contact-modal/contact-modal.tsx index fe99b37791..14826c7396 100644 --- a/app/components/contact-modal/contact-modal.tsx +++ b/app/components/contact-modal/contact-modal.tsx @@ -4,7 +4,10 @@ import ReactNativeModal from "react-native-modal" import { CONTACT_EMAIL_ADDRESS, WHATSAPP_CONTACT_NUMBER } from "@app/config" import { useI18nContext } from "@app/i18n/i18n-react" +import { RootStackParamList } from "@app/navigation/stack-param-lists" import { openWhatsApp } from "@app/utils/external" +import { useNavigation } from "@react-navigation/native" +import { StackNavigationProp } from "@react-navigation/stack" import { Icon, ListItem, makeStyles, useTheme } from "@rneui/themed" import TelegramOutline from "./telegram.svg" @@ -16,6 +19,7 @@ export const SupportChannels = { StatusPage: "statusPage", Mattermost: "mattermost", Faq: "faq", + Chatbot: "chatbot", } as const export type SupportChannels = (typeof SupportChannels)[keyof typeof SupportChannels] @@ -44,7 +48,18 @@ const ContactModal: React.FC = ({ theme: { colors }, } = useTheme() + const { navigate } = useNavigation>() + const contactOptionList = [ + { + id: SupportChannels.Chatbot, + name: LL.support.chatbot(), + icon: , + action: () => { + navigate("chatbot") + toggleModal() + }, + }, { id: SupportChannels.StatusPage, name: LL.support.statusPage(), diff --git a/app/components/contact-support-button/contact-support-button.tsx b/app/components/contact-support-button/contact-support-button.tsx index 07d15b58f3..c42affa532 100644 --- a/app/components/contact-support-button/contact-support-button.tsx +++ b/app/components/contact-support-button/contact-support-button.tsx @@ -37,6 +37,7 @@ export const ContactSupportButton = ({ isVisible={showContactSupport} toggleModal={() => setShowContactSupport(!showContactSupport)} supportChannels={[ + SupportChannels.Chatbot, SupportChannels.Faq, SupportChannels.StatusPage, SupportChannels.Email, diff --git a/app/graphql/generated.gql b/app/graphql/generated.gql index ac65f17080..3e56172354 100644 --- a/app/graphql/generated.gql +++ b/app/graphql/generated.gql @@ -496,6 +496,23 @@ mutation quizClaim($input: QuizClaimInput!) { } } +mutation supportChatMessageAdd($input: SupportChatMessageAddInput!) { + supportChatMessageAdd(input: $input) { + errors { + message + __typename + } + supportMessage { + id + message + role + timestamp + __typename + } + __typename + } +} + mutation userContactUpdateAlias($input: UserContactUpdateAliasInput!) { userContactUpdateAlias(input: $input) { errors { @@ -1458,6 +1475,20 @@ query settingsScreen { } } +query supportChat { + me { + id + supportChat { + id + message + role + timestamp + __typename + } + __typename + } +} + query supportedCountries { globals { supportedCountries { diff --git a/app/graphql/generated.ts b/app/graphql/generated.ts index bacf4787cc..c6b6997dc6 100644 --- a/app/graphql/generated.ts +++ b/app/graphql/generated.ts @@ -82,6 +82,8 @@ export type Scalars = { Username: { input: string; output: string; } /** Unique identifier of a wallet */ WalletId: { input: string; output: string; } + join__FieldSet: { input: string; output: string; } + link__Import: { input: string; output: string; } _FieldSet: { input: string; output: string; } }; @@ -1034,11 +1036,16 @@ export type Mutation = { readonly onChainUsdPaymentSendAsBtcDenominated: PaymentSendPayload; readonly onboardingFlowStart: OnboardingFlowStartResult; readonly quizClaim: QuizClaimPayload; + readonly supportChatMessageAdd: SupportChatMessageAddPayload; /** @deprecated will be moved to AccountContact */ readonly userContactUpdateAlias: UserContactUpdateAliasPayload; + readonly userDisableNotificationCategory: UserUpdateNotificationSettingsPayload; + readonly userDisableNotificationChannel: UserUpdateNotificationSettingsPayload; readonly userEmailDelete: UserEmailDeletePayload; readonly userEmailRegistrationInitiate: UserEmailRegistrationInitiatePayload; readonly userEmailRegistrationValidate: UserEmailRegistrationValidatePayload; + readonly userEnableNotificationCategory: UserUpdateNotificationSettingsPayload; + readonly userEnableNotificationChannel: UserUpdateNotificationSettingsPayload; readonly userLogin: AuthTokenPayload; readonly userLoginUpgrade: UpgradePayload; readonly userLogout: SuccessPayload; @@ -1259,11 +1266,26 @@ export type MutationQuizClaimArgs = { }; +export type MutationSupportChatMessageAddArgs = { + input: SupportChatMessageAddInput; +}; + + export type MutationUserContactUpdateAliasArgs = { input: UserContactUpdateAliasInput; }; +export type MutationUserDisableNotificationCategoryArgs = { + input: UserDisableNotificationCategoryInput; +}; + + +export type MutationUserDisableNotificationChannelArgs = { + input: UserDisableNotificationChannelInput; +}; + + export type MutationUserEmailRegistrationInitiateArgs = { input: UserEmailRegistrationInitiateInput; }; @@ -1274,6 +1296,16 @@ export type MutationUserEmailRegistrationValidateArgs = { }; +export type MutationUserEnableNotificationCategoryArgs = { + input: UserEnableNotificationCategoryInput; +}; + + +export type MutationUserEnableNotificationChannelArgs = { + input: UserEnableNotificationChannelInput; +}; + + export type MutationUserLoginArgs = { input: UserLoginInput; }; @@ -1808,6 +1840,24 @@ export type SuccessPayload = { readonly success?: Maybe; }; +export type SupportChatMessageAddInput = { + readonly message: Scalars['String']['input']; +}; + +export type SupportChatMessageAddPayload = { + readonly __typename: 'SupportChatMessageAddPayload'; + readonly errors: ReadonlyArray; + readonly supportMessage?: Maybe>>; +}; + +export type SupportMessage = { + readonly __typename: 'SupportMessage'; + readonly id: Scalars['ID']['output']; + readonly message: Scalars['String']['output']; + readonly role: Scalars['String']['output']; + readonly timestamp: Scalars['Timestamp']['output']; +}; + /** * Give details about an individual transaction. * Galoy have a smart routing system which is automatically @@ -1990,8 +2040,10 @@ export type User = { * When value is 'default' the intent is to use preferred language from OS settings. */ readonly language: Scalars['Language']['output']; + readonly notificationSettings: UserNotificationSettings; /** Phone number with international calling code. */ readonly phone?: Maybe; + readonly supportChat: ReadonlyArray; /** Whether TOTP is enabled for this user. */ readonly totpEnabled: Scalars['Boolean']['output']; /** @@ -2040,6 +2092,15 @@ export type UserContactUpdateAliasPayload = { readonly errors: ReadonlyArray; }; +export type UserDisableNotificationCategoryInput = { + readonly category: UserNotificationCategory; + readonly channel: UserNotificationChannel; +}; + +export type UserDisableNotificationChannelInput = { + readonly channel: UserNotificationChannel; +}; + export type UserEmailDeletePayload = { readonly __typename: 'UserEmailDeletePayload'; readonly errors: ReadonlyArray; @@ -2068,6 +2129,15 @@ export type UserEmailRegistrationValidatePayload = { readonly me?: Maybe; }; +export type UserEnableNotificationCategoryInput = { + readonly category: UserNotificationCategory; + readonly channel: UserNotificationChannel; +}; + +export type UserEnableNotificationChannelInput = { + readonly channel: UserNotificationChannel; +}; + export type UserLoginInput = { readonly code: Scalars['OneTimeAuthCode']['input']; readonly phone: Scalars['Phone']['input']; @@ -2082,6 +2152,33 @@ export type UserLogoutInput = { readonly deviceToken: Scalars['String']['input']; }; +export const UserNotificationCategory = { + AdminNotification: 'ADMIN_NOTIFICATION', + Balance: 'BALANCE', + Circles: 'CIRCLES', + Marketing: 'MARKETING', + Payments: 'PAYMENTS', + Price: 'PRICE', + Security: 'SECURITY' +} as const; + +export type UserNotificationCategory = typeof UserNotificationCategory[keyof typeof UserNotificationCategory]; +export const UserNotificationChannel = { + Push: 'PUSH' +} as const; + +export type UserNotificationChannel = typeof UserNotificationChannel[keyof typeof UserNotificationChannel]; +export type UserNotificationChannelSettings = { + readonly __typename: 'UserNotificationChannelSettings'; + readonly disabledCategories: ReadonlyArray; + readonly enabled: Scalars['Boolean']['output']; +}; + +export type UserNotificationSettings = { + readonly __typename: 'UserNotificationSettings'; + readonly push: UserNotificationChannelSettings; +}; + export type UserPhoneDeletePayload = { readonly __typename: 'UserPhoneDeletePayload'; readonly errors: ReadonlyArray; @@ -2141,6 +2238,11 @@ export type UserUpdateLanguagePayload = { readonly user?: Maybe; }; +export type UserUpdateNotificationSettingsPayload = { + readonly __typename: 'UserUpdateNotificationSettingsPayload'; + readonly notificationSettings: UserNotificationSettings; +}; + export type UserUpdateUsernameInput = { readonly username: Scalars['Username']['input']; }; @@ -2278,6 +2380,21 @@ export const WelcomeRange = { } as const; export type WelcomeRange = typeof WelcomeRange[keyof typeof WelcomeRange]; +export const Join__Graph = { + Circles: 'CIRCLES', + Galoy: 'GALOY', + Kyc: 'KYC' +} as const; + +export type Join__Graph = typeof Join__Graph[keyof typeof Join__Graph]; +export const Link__Purpose = { + /** `EXECUTION` features provide metadata necessary for operation execution. */ + Execution: 'EXECUTION', + /** `SECURITY` features provide metadata necessary to securely resolve fields. */ + Security: 'SECURITY' +} as const; + +export type Link__Purpose = typeof Link__Purpose[keyof typeof Link__Purpose]; export type MobileUpdateQueryVariables = Exact<{ [key: string]: never; }>; @@ -2418,6 +2535,18 @@ export type UserLogoutMutationVariables = Exact<{ export type UserLogoutMutation = { readonly __typename: 'Mutation', readonly userLogout: { readonly __typename: 'SuccessPayload', readonly success?: boolean | null } }; +export type SupportChatQueryVariables = Exact<{ [key: string]: never; }>; + + +export type SupportChatQuery = { readonly __typename: 'Query', readonly me?: { readonly __typename: 'User', readonly id: string, readonly supportChat: ReadonlyArray<{ readonly __typename: 'SupportMessage', readonly id: string, readonly message: string, readonly role: string, readonly timestamp: number }> } | null }; + +export type SupportChatMessageAddMutationVariables = Exact<{ + input: SupportChatMessageAddInput; +}>; + + +export type SupportChatMessageAddMutation = { readonly __typename: 'Mutation', readonly supportChatMessageAdd: { readonly __typename: 'SupportChatMessageAddPayload', readonly errors: ReadonlyArray<{ readonly __typename: 'GraphQLApplicationError', readonly message: string }>, readonly supportMessage?: ReadonlyArray<{ readonly __typename: 'SupportMessage', readonly id: string, readonly message: string, readonly role: string, readonly timestamp: number } | null> | null } }; + export type ConversionScreenQueryVariables = Exact<{ [key: string]: never; }>; @@ -3926,6 +4055,87 @@ export function useUserLogoutMutation(baseOptions?: Apollo.MutationHookOptions; export type UserLogoutMutationResult = Apollo.MutationResult; export type UserLogoutMutationOptions = Apollo.BaseMutationOptions; +export const SupportChatDocument = gql` + query supportChat { + me { + id + supportChat { + id + message + role + timestamp + } + } +} + `; + +/** + * __useSupportChatQuery__ + * + * To run a query within a React component, call `useSupportChatQuery` and pass it any options that fit your needs. + * When your component renders, `useSupportChatQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useSupportChatQuery({ + * variables: { + * }, + * }); + */ +export function useSupportChatQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(SupportChatDocument, options); + } +export function useSupportChatLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(SupportChatDocument, options); + } +export type SupportChatQueryHookResult = ReturnType; +export type SupportChatLazyQueryHookResult = ReturnType; +export type SupportChatQueryResult = Apollo.QueryResult; +export const SupportChatMessageAddDocument = gql` + mutation supportChatMessageAdd($input: SupportChatMessageAddInput!) { + supportChatMessageAdd(input: $input) { + errors { + message + } + supportMessage { + id + message + role + timestamp + } + } +} + `; +export type SupportChatMessageAddMutationFn = Apollo.MutationFunction; + +/** + * __useSupportChatMessageAddMutation__ + * + * To run a mutation, you first call `useSupportChatMessageAddMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useSupportChatMessageAddMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [supportChatMessageAddMutation, { data, loading, error }] = useSupportChatMessageAddMutation({ + * variables: { + * input: // value for 'input' + * }, + * }); + */ +export function useSupportChatMessageAddMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(SupportChatMessageAddDocument, options); + } +export type SupportChatMessageAddMutationHookResult = ReturnType; +export type SupportChatMessageAddMutationResult = Apollo.MutationResult; +export type SupportChatMessageAddMutationOptions = Apollo.BaseMutationOptions; export const ConversionScreenDocument = gql` query conversionScreen { me { @@ -7403,6 +7613,9 @@ export type ResolversTypes = { SignedDisplayMajorAmount: ResolverTypeWrapper; Subscription: ResolverTypeWrapper<{}>; SuccessPayload: ResolverTypeWrapper; + SupportChatMessageAddInput: SupportChatMessageAddInput; + SupportChatMessageAddPayload: ResolverTypeWrapper; + SupportMessage: ResolverTypeWrapper; Timestamp: ResolverTypeWrapper; TotpCode: ResolverTypeWrapper; TotpRegistrationId: ResolverTypeWrapper; @@ -7419,14 +7632,22 @@ export type ResolversTypes = { UserContact: ResolverTypeWrapper; UserContactUpdateAliasInput: UserContactUpdateAliasInput; UserContactUpdateAliasPayload: ResolverTypeWrapper; + UserDisableNotificationCategoryInput: UserDisableNotificationCategoryInput; + UserDisableNotificationChannelInput: UserDisableNotificationChannelInput; UserEmailDeletePayload: ResolverTypeWrapper; UserEmailRegistrationInitiateInput: UserEmailRegistrationInitiateInput; UserEmailRegistrationInitiatePayload: ResolverTypeWrapper; UserEmailRegistrationValidateInput: UserEmailRegistrationValidateInput; UserEmailRegistrationValidatePayload: ResolverTypeWrapper; + UserEnableNotificationCategoryInput: UserEnableNotificationCategoryInput; + UserEnableNotificationChannelInput: UserEnableNotificationChannelInput; UserLoginInput: UserLoginInput; UserLoginUpgradeInput: UserLoginUpgradeInput; UserLogoutInput: UserLogoutInput; + UserNotificationCategory: UserNotificationCategory; + UserNotificationChannel: UserNotificationChannel; + UserNotificationChannelSettings: ResolverTypeWrapper; + UserNotificationSettings: ResolverTypeWrapper; UserPhoneDeletePayload: ResolverTypeWrapper; UserPhoneRegistrationInitiateInput: UserPhoneRegistrationInitiateInput; UserPhoneRegistrationValidateInput: UserPhoneRegistrationValidateInput; @@ -7438,6 +7659,7 @@ export type ResolversTypes = { UserUpdate: ResolverTypeWrapper['UserUpdate']>; UserUpdateLanguageInput: UserUpdateLanguageInput; UserUpdateLanguagePayload: ResolverTypeWrapper; + UserUpdateNotificationSettingsPayload: ResolverTypeWrapper; UserUpdateUsernameInput: UserUpdateUsernameInput; UserUpdateUsernamePayload: ResolverTypeWrapper; Username: ResolverTypeWrapper; @@ -7447,6 +7669,10 @@ export type ResolversTypes = { WelcomeLeaderboardInput: WelcomeLeaderboardInput; WelcomeProfile: ResolverTypeWrapper; WelcomeRange: WelcomeRange; + join__FieldSet: ResolverTypeWrapper; + join__Graph: Join__Graph; + link__Import: ResolverTypeWrapper; + link__Purpose: Link__Purpose; }; /** Mapping between all available schema types and the resolvers parents */ @@ -7616,6 +7842,9 @@ export type ResolversParentTypes = { SignedDisplayMajorAmount: Scalars['SignedDisplayMajorAmount']['output']; Subscription: {}; SuccessPayload: SuccessPayload; + SupportChatMessageAddInput: SupportChatMessageAddInput; + SupportChatMessageAddPayload: SupportChatMessageAddPayload; + SupportMessage: SupportMessage; Timestamp: Scalars['Timestamp']['output']; TotpCode: Scalars['TotpCode']['output']; TotpRegistrationId: Scalars['TotpRegistrationId']['output']; @@ -7629,14 +7858,20 @@ export type ResolversParentTypes = { UserContact: UserContact; UserContactUpdateAliasInput: UserContactUpdateAliasInput; UserContactUpdateAliasPayload: UserContactUpdateAliasPayload; + UserDisableNotificationCategoryInput: UserDisableNotificationCategoryInput; + UserDisableNotificationChannelInput: UserDisableNotificationChannelInput; UserEmailDeletePayload: UserEmailDeletePayload; UserEmailRegistrationInitiateInput: UserEmailRegistrationInitiateInput; UserEmailRegistrationInitiatePayload: UserEmailRegistrationInitiatePayload; UserEmailRegistrationValidateInput: UserEmailRegistrationValidateInput; UserEmailRegistrationValidatePayload: UserEmailRegistrationValidatePayload; + UserEnableNotificationCategoryInput: UserEnableNotificationCategoryInput; + UserEnableNotificationChannelInput: UserEnableNotificationChannelInput; UserLoginInput: UserLoginInput; UserLoginUpgradeInput: UserLoginUpgradeInput; UserLogoutInput: UserLogoutInput; + UserNotificationChannelSettings: UserNotificationChannelSettings; + UserNotificationSettings: UserNotificationSettings; UserPhoneDeletePayload: UserPhoneDeletePayload; UserPhoneRegistrationInitiateInput: UserPhoneRegistrationInitiateInput; UserPhoneRegistrationValidateInput: UserPhoneRegistrationValidateInput; @@ -7648,6 +7883,7 @@ export type ResolversParentTypes = { UserUpdate: ResolversUnionTypes['UserUpdate']; UserUpdateLanguageInput: UserUpdateLanguageInput; UserUpdateLanguagePayload: UserUpdateLanguagePayload; + UserUpdateNotificationSettingsPayload: UserUpdateNotificationSettingsPayload; UserUpdateUsernameInput: UserUpdateUsernameInput; UserUpdateUsernamePayload: UserUpdateUsernamePayload; Username: Scalars['Username']['output']; @@ -7655,14 +7891,67 @@ export type ResolversParentTypes = { WalletId: Scalars['WalletId']['output']; WelcomeLeaderboardInput: WelcomeLeaderboardInput; WelcomeProfile: WelcomeProfile; + join__FieldSet: Scalars['join__FieldSet']['output']; + link__Import: Scalars['link__Import']['output']; +}; + +export type Join__EnumValueDirectiveArgs = { + graph: Join__Graph; }; -export type DeferDirectiveArgs = { - if?: Scalars['Boolean']['input']; - label?: Maybe; +export type Join__EnumValueDirectiveResolver = DirectiveResolverFn; + +export type Join__FieldDirectiveArgs = { + external?: Maybe; + graph?: Maybe; + override?: Maybe; + provides?: Maybe; + requires?: Maybe; + type?: Maybe; + usedOverridden?: Maybe; +}; + +export type Join__FieldDirectiveResolver = DirectiveResolverFn; + +export type Join__GraphDirectiveArgs = { + name: Scalars['String']['input']; + url: Scalars['String']['input']; }; -export type DeferDirectiveResolver = DirectiveResolverFn; +export type Join__GraphDirectiveResolver = DirectiveResolverFn; + +export type Join__ImplementsDirectiveArgs = { + graph: Join__Graph; + interface: Scalars['String']['input']; +}; + +export type Join__ImplementsDirectiveResolver = DirectiveResolverFn; + +export type Join__TypeDirectiveArgs = { + extension?: Scalars['Boolean']['input']; + graph: Join__Graph; + isInterfaceObject?: Scalars['Boolean']['input']; + key?: Maybe; + resolvable?: Scalars['Boolean']['input']; +}; + +export type Join__TypeDirectiveResolver = DirectiveResolverFn; + +export type Join__UnionMemberDirectiveArgs = { + graph: Join__Graph; + member: Scalars['String']['input']; +}; + +export type Join__UnionMemberDirectiveResolver = DirectiveResolverFn; + +export type LinkDirectiveArgs = { + as?: Maybe; + for?: Maybe; + import?: Maybe>>; + url?: Maybe; +}; + +export type LinkDirectiveResolver = DirectiveResolverFn; export type AccountResolvers = { __resolveType: TypeResolveFn<'ConsumerAccount', ParentType, ContextType>; @@ -8180,10 +8469,15 @@ export type MutationResolvers>; onboardingFlowStart?: Resolver>; quizClaim?: Resolver>; + supportChatMessageAdd?: Resolver>; userContactUpdateAlias?: Resolver>; + userDisableNotificationCategory?: Resolver>; + userDisableNotificationChannel?: Resolver>; userEmailDelete?: Resolver; userEmailRegistrationInitiate?: Resolver>; userEmailRegistrationValidate?: Resolver>; + userEnableNotificationCategory?: Resolver>; + userEnableNotificationChannel?: Resolver>; userLogin?: Resolver>; userLoginUpgrade?: Resolver>; userLogout?: Resolver>; @@ -8487,6 +8781,20 @@ export type SuccessPayloadResolvers; }; +export type SupportChatMessageAddPayloadResolvers = { + errors?: Resolver, ParentType, ContextType>; + supportMessage?: Resolver>>, ParentType, ContextType>; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type SupportMessageResolvers = { + id?: Resolver; + message?: Resolver; + role?: Resolver; + timestamp?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + export interface TimestampScalarConfig extends GraphQLScalarTypeConfig { name: 'Timestamp'; } @@ -8567,7 +8875,9 @@ export type UserResolvers, ParentType, ContextType>; id?: Resolver; language?: Resolver; + notificationSettings?: Resolver; phone?: Resolver, ParentType, ContextType>; + supportChat?: Resolver, ParentType, ContextType>; totpEnabled?: Resolver; username?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; @@ -8607,6 +8917,17 @@ export type UserEmailRegistrationValidatePayloadResolvers; }; +export type UserNotificationChannelSettingsResolvers = { + disabledCategories?: Resolver, ParentType, ContextType>; + enabled?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type UserNotificationSettingsResolvers = { + push?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + export type UserPhoneDeletePayloadResolvers = { errors?: Resolver, ParentType, ContextType>; me?: Resolver, ParentType, ContextType>; @@ -8648,6 +8969,11 @@ export type UserUpdateLanguagePayloadResolvers; }; +export type UserUpdateNotificationSettingsPayloadResolvers = { + notificationSettings?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + export type UserUpdateUsernamePayloadResolvers = { errors?: Resolver, ParentType, ContextType>; user?: Resolver, ParentType, ContextType>; @@ -8693,6 +9019,14 @@ export type WelcomeProfileResolvers; }; +export interface Join__FieldSetScalarConfig extends GraphQLScalarTypeConfig { + name: 'join__FieldSet'; +} + +export interface Link__ImportScalarConfig extends GraphQLScalarTypeConfig { + name: 'link__Import'; +} + export type Resolvers = { Account?: AccountResolvers; AccountDeletePayload?: AccountDeletePayloadResolvers; @@ -8808,6 +9142,8 @@ export type Resolvers = { SignedDisplayMajorAmount?: GraphQLScalarType; Subscription?: SubscriptionResolvers; SuccessPayload?: SuccessPayloadResolvers; + SupportChatMessageAddPayload?: SupportChatMessageAddPayloadResolvers; + SupportMessage?: SupportMessageResolvers; Timestamp?: GraphQLScalarType; TotpCode?: GraphQLScalarType; TotpRegistrationId?: GraphQLScalarType; @@ -8823,6 +9159,8 @@ export type Resolvers = { UserEmailDeletePayload?: UserEmailDeletePayloadResolvers; UserEmailRegistrationInitiatePayload?: UserEmailRegistrationInitiatePayloadResolvers; UserEmailRegistrationValidatePayload?: UserEmailRegistrationValidatePayloadResolvers; + UserNotificationChannelSettings?: UserNotificationChannelSettingsResolvers; + UserNotificationSettings?: UserNotificationSettingsResolvers; UserPhoneDeletePayload?: UserPhoneDeletePayloadResolvers; UserPhoneRegistrationValidatePayload?: UserPhoneRegistrationValidatePayloadResolvers; UserTotpDeletePayload?: UserTotpDeletePayloadResolvers; @@ -8830,13 +9168,22 @@ export type Resolvers = { UserTotpRegistrationValidatePayload?: UserTotpRegistrationValidatePayloadResolvers; UserUpdate?: UserUpdateResolvers; UserUpdateLanguagePayload?: UserUpdateLanguagePayloadResolvers; + UserUpdateNotificationSettingsPayload?: UserUpdateNotificationSettingsPayloadResolvers; UserUpdateUsernamePayload?: UserUpdateUsernamePayloadResolvers; Username?: GraphQLScalarType; Wallet?: WalletResolvers; WalletId?: GraphQLScalarType; WelcomeProfile?: WelcomeProfileResolvers; + join__FieldSet?: GraphQLScalarType; + link__Import?: GraphQLScalarType; }; export type DirectiveResolvers = { - defer?: DeferDirectiveResolver; + join__enumValue?: Join__EnumValueDirectiveResolver; + join__field?: Join__FieldDirectiveResolver; + join__graph?: Join__GraphDirectiveResolver; + join__implements?: Join__ImplementsDirectiveResolver; + join__type?: Join__TypeDirectiveResolver; + join__unionMember?: Join__UnionMemberDirectiveResolver; + link?: LinkDirectiveResolver; }; diff --git a/app/i18n/en/index.ts b/app/i18n/en/index.ts index c30f331d2b..a2eccfea5a 100644 --- a/app/i18n/en/index.ts +++ b/app/i18n/en/index.ts @@ -2705,6 +2705,7 @@ const en: BaseTranslation = { faq: "FAQ", enjoyingApp: "Enjoying the app?", statusPage: "Status Page", + chatbot: "Chatbot", telegram: "Telegram", mattermost: "Mattermost", thankYouText: "Thank you for the feedback, would you like to suggest an improvement?", diff --git a/app/i18n/i18n-types.ts b/app/i18n/i18n-types.ts index de788faceb..9d479882ce 100644 --- a/app/i18n/i18n-types.ts +++ b/app/i18n/i18n-types.ts @@ -8459,6 +8459,10 @@ type RootTranslation = { * S​t​a​t​u​s​ ​P​a​g​e */ statusPage: string + /** + * C​h​a​t​b​o​t + */ + chatbot: string /** * T​e​l​e​g​r​a​m */ @@ -17274,6 +17278,10 @@ export type TranslationFunctions = { * Status Page */ statusPage: () => LocalizedString + /** + * Chatbot + */ + chatbot: () => LocalizedString /** * Telegram */ diff --git a/app/i18n/raw-i18n/source/en.json b/app/i18n/raw-i18n/source/en.json index 72cbf3e107..add00aa2ff 100644 --- a/app/i18n/raw-i18n/source/en.json +++ b/app/i18n/raw-i18n/source/en.json @@ -2599,6 +2599,7 @@ "faq": "FAQ", "enjoyingApp": "Enjoying the app?", "statusPage": "Status Page", + "chatbot": "Chatbot", "telegram": "Telegram", "mattermost": "Mattermost", "thankYouText": "Thank you for the feedback, would you like to suggest an improvement?", diff --git a/app/navigation/root-navigator.tsx b/app/navigation/root-navigator.tsx index 25ef6617b5..3cca209143 100644 --- a/app/navigation/root-navigator.tsx +++ b/app/navigation/root-navigator.tsx @@ -5,6 +5,7 @@ import LearnIcon from "@app/assets/icons/learn.svg" import MapIcon from "@app/assets/icons/map.svg" import { useIsAuthed } from "@app/graphql/is-authed-context" import { useI18nContext } from "@app/i18n/i18n-react" +import { ConversationScreen } from "@app/screens/conversation/conversation" import { ConversionConfirmationScreen, ConversionDetailsScreen, @@ -409,6 +410,13 @@ export const RootStack = () => { title: LL.FullOnboarding.title(), }} /> + ) } diff --git a/app/navigation/stack-param-lists.ts b/app/navigation/stack-param-lists.ts index cfc73f367a..c7e6bacb96 100644 --- a/app/navigation/stack-param-lists.ts +++ b/app/navigation/stack-param-lists.ts @@ -96,6 +96,7 @@ export type RootStackParamList = { totpLoginValidate: { authToken: string } webView: { url: string; initialTitle?: string } fullOnboardingFlow: undefined + chatbot: undefined } export type PeopleStackParamList = { diff --git a/app/screens/conversation/conversation.stories.tsx b/app/screens/conversation/conversation.stories.tsx new file mode 100644 index 0000000000..6c269721c6 --- /dev/null +++ b/app/screens/conversation/conversation.stories.tsx @@ -0,0 +1,75 @@ +import * as React from "react" + +import { MockedProvider } from "@apollo/client/testing" +import { Meta } from "@storybook/react" + +import { StoryScreen } from "../../../.storybook/views" +import { createCache } from "../../graphql/cache" +import { SupportChatDocument } from "../../graphql/generated" +import { IsAuthedContextProvider } from "../../graphql/is-authed-context" +import { ConversationScreen } from "./conversation" + +const mockEmpty = [ + { + request: { + query: SupportChatDocument, + }, + result: { + data: { + me: { + __typename: "User", + id: "70df9822-efe0-419c-b864-c9efa99872ea", + supportChat: [], + }, + __typename: "Query", + }, + }, + }, +] + +const mockShort = [ + { + request: { + query: SupportChatDocument, + }, + result: { + data: { + me: { + __typename: "User", + id: "70df9822-efe0-419c-b864-c9efa99872ea", + supportChat: [ + { + __typename: "SupportChat", + id: "1", + message: "Hello", + role: "user", + timestamp: 1677184189, + }, + { + __typename: "SupportChat", + id: "2", + message: "Hi", + role: "assistant", + timestamp: 1677184190, + }, + ], + }, + __typename: "Query", + }, + }, + }, +] + +export default { + title: "Conversation Screen", + component: ConversationScreen, + decorators: [(Story) => {Story()}], +} as Meta + +export const Default = () => ( + + + + + +) diff --git a/app/screens/conversation/conversation.tsx b/app/screens/conversation/conversation.tsx new file mode 100644 index 0000000000..7138e4a11b --- /dev/null +++ b/app/screens/conversation/conversation.tsx @@ -0,0 +1,336 @@ +import { useState } from "react" +import { FlatList, Keyboard, TouchableHighlight, View } from "react-native" +import { TextInput } from "react-native-gesture-handler" +import Icon from "react-native-vector-icons/Ionicons" + +import { gql } from "@apollo/client" +import { Screen } from "@app/components/screen" +import { + SupportChatQuery, + useSupportChatMessageAddMutation, + useSupportChatQuery, +} from "@app/graphql/generated" +import { useActionSheet } from "@expo/react-native-action-sheet" +import Clipboard from "@react-native-clipboard/clipboard" +import { Text, makeStyles, useTheme } from "@rneui/themed" +import Markdown from "@ronradtke/react-native-markdown-display" + +type SupportChatMe = SupportChatQuery["me"] +type SupportChatArray = NonNullable["supportChat"] +type SupportChatMessage = NonNullable[number] + +gql` + query supportChat { + me { + id + supportChat { + id + message + role + timestamp + } + } + } + + mutation supportChatMessageAdd($input: SupportChatMessageAddInput!) { + supportChatMessageAdd(input: $input) { + errors { + message + } + supportMessage { + id + message + role + timestamp + } + } + } +` + +export const ConversationScreen = () => { + const styles = useStyles() + const { theme } = useTheme() + + const supportChatQuery = useSupportChatQuery({ fetchPolicy: "cache-and-network" }) + const supportChat = supportChatQuery.data?.me?.supportChat ?? [] + + const [supportChatMessageAdd, { loading }] = useSupportChatMessageAddMutation() + + const [input, setInput] = useState("") + const [pendingInput, setPendingInput] = useState("") + + const supportChatMaybeInput = pendingInput + ? [ + ...supportChat, + { + role: "user", + message: pendingInput, + timestamp: new Date().getTime(), + id: "pending", + __typename: "SupportMessage" as const, + }, + ] + : supportChat + + const { showActionSheetWithOptions } = useActionSheet() + + const copyToClipboard = (text: string) => { + Clipboard.setString(text) + } + + async function addMessageToThread() { + try { + if (!input) return + Keyboard.dismiss() + setPendingInput(input) + setInput("") + await supportChatMessageAdd({ + variables: { input: { message: input } }, + update: (cache, { data }) => { + // TODO + }, + }) + } catch (err) { + console.log("error: ", err) + } finally { + setPendingInput("") + } + } + + function onChangeInputText(v: string) { + setInput(v) + } + + async function clearChat() { + if (loading) return + // setOpenaiResponse([]) + setInput("") + } + + async function showClipboardActionsheet(text: string) { + console.log("showClipboardActionsheet", text) + + const cancelButtonIndex = 2 + showActionSheetWithOptions( + { + options: ["Copy to clipboard", "Clear chat", "cancel"], + cancelButtonIndex, + }, + (selectedIndex) => { + if (selectedIndex === Number(0)) { + copyToClipboard(text) + } + if (selectedIndex === 1) { + clearChat() + } + }, + ) + } + + function renderItem({ item, index }: { item: SupportChatMessage; index: number }) { + return ( + + {item.role === "user" && ( + + + {item.message} + + + )} + {item.role === "assistant" && ( + + {item.message} + showClipboardActionsheet(item.message)} + underlayColor={"transparent"} + > + + + + + + )} + + ) + } + + return ( + + + + + + + + + + + + ) +} + +const useStyles = makeStyles(({ colors }) => ({ + optionsIconWrapper: { + padding: 10, + paddingTop: 9, + alignItems: "flex-end", + }, + promptResponse: { + marginTop: 10, + }, + textStyleContainer: { + borderWidth: 1, + marginRight: 25, + borderColor: colors.grey3, + padding: 15, + paddingBottom: 6, + paddingTop: 5, + margin: 10, + marginTop: 0, + borderRadius: 13, + }, + textStyle: { + body: { + color: colors.grey0, + }, + paragraph: { + color: colors.grey0, + fontSize: 16, + }, + heading1: { + color: colors.grey0, + marginVertical: 5, + }, + heading2: { + marginTop: 20, + color: colors.grey0, + marginBottom: 5, + }, + heading3: { + marginTop: 20, + color: colors.grey0, + marginBottom: 5, + }, + heading4: { + marginTop: 10, + color: colors.grey0, + marginBottom: 5, + }, + heading5: { + marginTop: 10, + color: colors.grey0, + marginBottom: 5, + }, + heading6: { + color: colors.grey0, + marginVertical: 5, + }, + list_item: { + marginTop: 7, + fontSize: 16, + }, + ordered_list_icon: { + color: colors.grey0, + fontSize: 16, + }, + bullet_list: { + marginTop: 10, + }, + ordered_list: { + marginTop: 7, + }, + bullet_list_icon: { + color: colors.grey0, + fontSize: 16, + }, + code_inline: { + color: colors.grey1, + backgroundColor: colors.grey4, + borderWidth: 1, + borderColor: "rgba(255, 255, 255, .1)", + }, + hr: { + backgroundColor: "rgba(255, 255, 255, .1)", + height: 1, + }, + fence: { + marginVertical: 5, + padding: 10, + color: colors.grey1, + backgroundColor: colors.grey4, + borderColor: "rgba(255, 255, 255, .1)", + }, + tr: { + borderBottomWidth: 1, + borderColor: "rgba(255, 255, 255, .2)", + flexDirection: "row", + }, + table: { + marginTop: 7, + borderWidth: 1, + borderColor: "rgba(255, 255, 255, .2)", + borderRadius: 3, + }, + blockquote: { + backgroundColor: "#312e2e", + borderColor: "#CCC", + borderLeftWidth: 4, + marginLeft: 5, + paddingHorizontal: 5, + marginVertical: 5, + }, + } as any, + promptTextContainer: { + flex: 1, + alignItems: "flex-end", + marginRight: 15, + marginLeft: 24, + }, + promptTextWrapper: { + borderRadius: 8, + borderTopRightRadius: 0, + backgroundColor: colors._lightBlue, + }, + promptText: { + color: colors._white, + paddingVertical: 5, + paddingHorizontal: 9, + fontSize: 16, + }, + chatInputContainer: { + paddingTop: 5, + borderColor: colors.grey3, + width: "100%", + flexDirection: "row", + alignItems: "center", + paddingBottom: 5, + }, + input: { + flex: 1, + borderWidth: 1, + borderRadius: 99, + color: colors.grey0, + marginHorizontal: 10, + paddingVertical: 10, + paddingHorizontal: 21, + paddingRight: 39, + borderColor: colors.grey4, + }, + chatButton: { + marginRight: 14, + padding: 5, + borderRadius: 99, + backgroundColor: colors._lightBlue, + }, +})) diff --git a/app/screens/settings-screen/settings-screen.tsx b/app/screens/settings-screen/settings-screen.tsx index fae26d1b35..dbc32e0cd0 100644 --- a/app/screens/settings-screen/settings-screen.tsx +++ b/app/screens/settings-screen/settings-screen.tsx @@ -296,6 +296,7 @@ export const SettingsScreen: React.FC = () => { id: "contact-us", action: () => { setContactMethods([ + SupportChannels.Chatbot, SupportChannels.Faq, SupportChannels.StatusPage, SupportChannels.Email, diff --git a/codegen.yml b/codegen.yml index b419a42a85..289f88fcfe 100644 --- a/codegen.yml +++ b/codegen.yml @@ -1,6 +1,6 @@ overwrite: true -schema: "https://api.staging.galoy.io/graphql" -# schema: "./supergraph.graphql" +# schema: "https://api.staging.galoy.io/graphql" +schema: "./supergraph.graphql" documents: - "app/**/*.ts" - "app/**/*.tsx" diff --git a/ios/GaloyApp.xcodeproj/project.pbxproj b/ios/GaloyApp.xcodeproj/project.pbxproj index b39f3830ed..1b7b1ce5b3 100644 --- a/ios/GaloyApp.xcodeproj/project.pbxproj +++ b/ios/GaloyApp.xcodeproj/project.pbxproj @@ -586,7 +586,11 @@ ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = "$(inherited)"; OTHER_CPLUSPLUSFLAGS = "$(inherited)"; - OTHER_LDFLAGS = "$(inherited)"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-Wl", + "-ld_classic", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; }; @@ -649,7 +653,11 @@ MTL_ENABLE_DEBUG_INFO = NO; OTHER_CFLAGS = "$(inherited)"; OTHER_CPLUSPLUSFLAGS = "$(inherited)"; - OTHER_LDFLAGS = "$(inherited)"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-Wl", + "-ld_classic", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 3798b85aa6..c0c2895360 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -967,7 +967,7 @@ SPEC CHECKSUMS: AppCheckCore: d0d4bcb6f90fd9f69958da5350467b79026b38c7 boost: 57d2868c099736d80fcd648bf211b4431e51a558 BVLinearGradient: 880f91a7854faff2df62518f0281afb1c60d49a3 - DoubleConversion: fea03f2699887d960129cc54bba7e52542b6f953 + DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 FBLazyVector: 5fbbff1d7734827299274638deb8ba3024f6c597 FBReactNativeSpec: 638095fe8a01506634d77b260ef8a322019ac671 Firebase: 333ec7c6b12fa09c77b5162cda6b862102211d50 @@ -986,11 +986,11 @@ SPEC CHECKSUMS: FirebaseSessions: f06853e30f99fe42aa511014d7ee6c8c319f08a3 FirebaseSharedSwift: c92645b392db3c41a83a0aa967de16f8bad25568 fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 - glog: 035f1e36e53b355cf70f6434d161b36e7d21fecd - GoogleAppMeasurement: 70ce9aa438cff1cfb31ea3e660bcc67734cb716e - GoogleDataTransport: 57c22343ab29bc686febbf7cbb13bad167c2d8fe - GoogleUtilities: 0759d1a57ebb953965c2dfe0ba4c82e95ccc2e34 - GT3Captcha-iOS: 5e3b1077834d8a9d6f4d64a447a30af3e14affe6 + glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b + GoogleAppMeasurement: 453eb0de99fcf2bdec9403e9ac5d7871fdba3e3f + GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a + GoogleUtilities: d053d902a8edaa9904e1bd00c37535385b8ed152 + GT3Captcha-iOS: 3e7737ece3b2210ba19802be381b9aa88007f045 hermes-engine: 9180d43df05c1ed658a87cc733dc3044cf90c00a libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 nanopb: 438bc412db1928dac798aa6fd75726007be04262 diff --git a/package.json b/package.json index 5f75a10ba2..87658a1303 100644 --- a/package.json +++ b/package.json @@ -57,8 +57,9 @@ "e2e:test": "detox test --configuration" }, "dependencies": { - "@apollo/client": "^3.9.6", + "@apollo/client": "^3.9.9", "@bitcoinerlab/secp256k1": "^1.0.5", + "@expo/react-native-action-sheet": "^4.0.1", "@formatjs/intl-getcanonicallocales": "^2.3.0", "@formatjs/intl-locale": "^3.4.3", "@formatjs/intl-relativetimeformat": "^11.2.10", @@ -78,6 +79,7 @@ "@react-navigation/stack": "^6.3.20", "@rneui/base": "^4.0.0-rc.8", "@rneui/themed": "^4.0.0-rc.8", + "@ronradtke/react-native-markdown-display": "^8.0.0", "apollo3-cache-persist": "^0.14.1", "axios": "^1.6.2", "bech32": "^2.0.0", diff --git a/supergraph.graphql b/supergraph.graphql index 5ff862d20e..b0c5007bc4 100644 --- a/supergraph.graphql +++ b/supergraph.graphql @@ -1275,6 +1275,7 @@ type Mutation onChainUsdPaymentSend(input: OnChainUsdPaymentSendInput!): PaymentSendPayload! @join__field(graph: GALOY) onChainUsdPaymentSendAsBtcDenominated(input: OnChainUsdPaymentSendAsBtcDenominatedInput!): PaymentSendPayload! @join__field(graph: GALOY) quizClaim(input: QuizClaimInput!): QuizClaimPayload! @join__field(graph: GALOY) + supportChatMessageAdd(input: SupportChatMessageAddInput!): SupportChatMessageAddPayload! @join__field(graph: GALOY) userContactUpdateAlias(input: UserContactUpdateAliasInput!): UserContactUpdateAliasPayload! @join__field(graph: GALOY) @deprecated(reason: "will be moved to AccountContact") userEmailDelete: UserEmailDeletePayload! @join__field(graph: GALOY) userEmailRegistrationInitiate(input: UserEmailRegistrationInitiateInput!): UserEmailRegistrationInitiatePayload! @join__field(graph: GALOY) @@ -1803,6 +1804,28 @@ type SuccessPayload success: Boolean } +input SupportChatMessageAddInput + @join__type(graph: GALOY) +{ + message: String! +} + +type SupportChatMessageAddPayload + @join__type(graph: GALOY) +{ + errors: [Error!]! + supportMessage: [SupportMessage] +} + +type SupportMessage + @join__type(graph: GALOY) +{ + id: ID! + message: String! + role: String! + timestamp: Timestamp! +} + """ Timestamp field, serialized as Unix time (the number of seconds since the Unix epoch) """ @@ -2014,6 +2037,7 @@ type User """Phone number with international calling code.""" phone: Phone + supportChat: [SupportMessage!]! """Whether TOTP is enabled for this user.""" totpEnabled: Boolean! diff --git a/yarn.lock b/yarn.lock index 9450286f51..04ff0e7b92 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15,10 +15,10 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@apollo/client@^3.9.6": - version "3.9.6" - resolved "https://registry.npmjs.org/@apollo/client/-/client-3.9.6.tgz#4292448d9b0a48244a60307b74d2fea7e83dfe70" - integrity sha512-+zpddcnZ4G2VZ0xIEnvIHFsLqeopNOnWuE2ZVbRuetLLpj/biLPNN719B/iofdd1/iHRclKfv0XaAmX6PBhYKA== +"@apollo/client@^3.9.9": + version "3.9.9" + resolved "https://registry.npmjs.org/@apollo/client/-/client-3.9.9.tgz#38f983a1ad24e2687abfced0a9c1c3bef8d32616" + integrity sha512-/sMecU/M0WK9knrguts1lSLV8xFKzIgOMVb4mi6MOxgJXjliDB8PvOtmXhTqh2cVMMR4TzXgOnb+af/690zlQw== dependencies: "@graphql-typed-document-node/core" "^3.1.1" "@wry/caches" "^1.0.0" @@ -1926,6 +1926,14 @@ base64-js "^1.2.3" xmlbuilder "^14.0.0" +"@expo/react-native-action-sheet@^4.0.1": + version "4.0.1" + resolved "https://registry.npmjs.org/@expo/react-native-action-sheet/-/react-native-action-sheet-4.0.1.tgz#fa78e55a87a741f235be2c4ce0b0ea2b6afd06cf" + integrity sha512-FwCFpjpB6yzrK8CIWssLlh/i6zQFytFBiJfNdz0mJ2ckU4hWk8SrjB37P0Q4kF7w0bnIdYzPgRbdPR9hnfFqPw== + dependencies: + "@types/hoist-non-react-statics" "^3.3.1" + hoist-non-react-statics "^3.3.0" + "@expo/sdk-runtime-versions@^1.0.0": version "1.0.0" resolved "https://registry.npmjs.org/@expo/sdk-runtime-versions/-/sdk-runtime-versions-1.0.0.tgz#d7ebd21b19f1c6b0395e50d78da4416941c57f7c" @@ -4354,6 +4362,16 @@ resolved "https://registry.npmjs.org/@rneui/themed/-/themed-4.0.0-rc.8.tgz#5c0e1aaa3d190ead88936693c5cef50ec404cd05" integrity sha512-8L/XOrL9OK/r+/iBLvx63TbIdZOXF8SIjN9eArMYm6kRbMr8m4BitXllDN8nBhBsSPNYvL6EAgjk+i2MfY4sBA== +"@ronradtke/react-native-markdown-display@^8.0.0": + version "8.0.0" + resolved "https://registry.npmjs.org/@ronradtke/react-native-markdown-display/-/react-native-markdown-display-8.0.0.tgz#ac2763290e19efed5d054fdb59c595af7b5edeea" + integrity sha512-i56CYXGXWDGN+dxF72dGiEn4Kld0L6c/JvcOrO4azX9YzVVl02F5EDgdb6fWUaiOl8gPqyUI7YIEU2OVGnIg6Q== + dependencies: + css-to-react-native "^3.2.0" + markdown-it "^13.0.1" + prop-types "^15.7.2" + react-native-fit-image "^1.5.5" + "@samverschueren/stream-to-observable@^0.3.0": version "0.3.1" resolved "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz#a21117b19ee9be70c379ec1877537ef2e1c63301" @@ -9820,6 +9838,15 @@ css-to-react-native@^2.2.1: css-color-keywords "^1.0.0" postcss-value-parser "^3.3.0" +css-to-react-native@^3.2.0: + version "3.2.0" + resolved "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz#cdd8099f71024e149e4f6fe17a7d46ecd55f1e32" + integrity sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ== + dependencies: + camelize "^1.0.0" + css-color-keywords "^1.0.0" + postcss-value-parser "^4.0.2" + css-tree@^1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" @@ -10821,7 +10848,7 @@ entities@^2.0.0: resolved "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== -entities@^3.0.1: +entities@^3.0.1, entities@~3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== @@ -15949,6 +15976,13 @@ lines-and-columns@^2.0.3: resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.4.tgz#d00318855905d2660d8c0822e3f5a4715855fc42" integrity sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A== +linkify-it@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz#01f1d5e508190d06669982ba31a7d9f56a5751ec" + integrity sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw== + dependencies: + uc.micro "^1.0.1" + listenercount@~1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz#84c8a72ab59c4725321480c975e6508342e70937" @@ -16525,6 +16559,17 @@ markdown-extensions@^1.1.0: resolved "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-1.1.1.tgz#fea03b539faeaee9b4ef02a3769b455b189f7fc3" integrity sha512-WWC0ZuMzCyDHYCasEGs4IPvLyTGftYwh6wIEOULOF0HXcqZlhwRzrK0w2VUlxWA98xnvb/jszw4ZSkJ6ADpM6Q== +markdown-it@^13.0.1: + version "13.0.2" + resolved "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.2.tgz#1bc22e23379a6952e5d56217fbed881e0c94d536" + integrity sha512-FtwnEuuK+2yVU7goGn/MJ0WBZMM9ZPgU9spqlFs7/A/pDIUNSOQZhUgOqYCficIuR2QaFnrt8LHqBWsbTAoI5w== + dependencies: + argparse "^2.0.1" + entities "~3.0.1" + linkify-it "^4.0.1" + mdurl "^1.0.1" + uc.micro "^1.0.5" + marky@^1.2.2: version "1.2.5" resolved "https://registry.npmjs.org/marky/-/marky-1.2.5.tgz#55796b688cbd72390d2d399eaaf1832c9413e3c0" @@ -16634,7 +16679,7 @@ mdn-data@2.0.30: resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc" integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA== -mdurl@^1.0.0: +mdurl@^1.0.0, mdurl@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g== @@ -19071,7 +19116,7 @@ postcss-value-parser@^3.3.0: resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== -postcss-value-parser@^4.1.0: +postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0: version "4.2.0" resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== @@ -19797,6 +19842,13 @@ react-native-error-boundary@^1.2.3: version "6.0.0" resolved "git+https://github.com/hieuvp/react-native-fingerprint-scanner.git#9cecc0db326471c571553ea85f7c016fee2f803d" +react-native-fit-image@^1.5.5: + version "1.5.5" + resolved "https://registry.npmjs.org/react-native-fit-image/-/react-native-fit-image-1.5.5.tgz#c660d1ad74b9dcaa1cba27a0d9c23837e000226c" + integrity sha512-Wl3Vq2DQzxgsWKuW4USfck9zS7YzhvLNPpkwUUCF90bL32e1a0zOVQ3WsJILJOwzmPdHfzZmWasiiAUNBkhNkg== + dependencies: + prop-types "^15.5.10" + react-native-gesture-handler@^2.14.0: version "2.14.0" resolved "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.14.0.tgz#d6aec0d8b2e55c67557fd6107e828c0a1a248be8" @@ -23124,6 +23176,11 @@ ua-parser-js@^1.0.35: resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz#b5dc7b163a5c1f0c510b08446aed4da92c46373f" integrity sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ== +uc.micro@^1.0.1, uc.micro@^1.0.5: + version "1.0.6" + resolved "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" + integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== + uglify-es@^3.1.9: version "3.3.9" resolved "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677"