import { useQuery } from '@tanstack/react-query' import { merge } from 'lodash' import React, { createContext, useMemo } from 'react' import { ColorSchemeName, useColorScheme } from 'react-native' import { AgeCheckContactInfo } from '../screens/age_check/screen_props' import { ApiResource, DeepPartial } from '../types' import { FailedResponse } from '../types' import { ProductAnalyticsConfig } from '../types/product_analytics' import { Client, ENV, OauthType, PartialToken, Session, Uri } from '../utils' import { setLiquidGlassCapability } from '../utils/liquid_glass' import { ChatTheme, defaultTheme, DefaultTheme } from '../utils/theme' export interface ChatProviderProps { env?: ENV giphyApiKey?: string onAgeDisqualification?: (params: AgeCheckContactInfo) => void onUnauthorizedResponse: (_response: FailedResponse) => void token?: PartialToken tokenType?: OauthType theme: CreateChatThemeProps edgeToEdge?: boolean liquidGlassEnabled?: boolean } export interface ChatContextValue extends Omit { session: Session theme: ChatTheme onAgeDisqualification: (params: AgeCheckContactInfo) => void productAnalyticsConfig: ProductAnalyticsConfig | undefined } export const ChatContext = createContext({ env: undefined, giphyApiKey: undefined, onAgeDisqualification: (_params: AgeCheckContactInfo) => {}, onUnauthorizedResponse: () => {}, session: new Session(), theme: defaultTheme('light'), token: undefined, edgeToEdge: true, liquidGlassEnabled: false, productAnalyticsConfig: undefined, }) export function ChatProvider({ children, value }: { children: any; value: ChatProviderProps }) { const { env, token, onAgeDisqualification = (_params: AgeCheckContactInfo) => {}, onUnauthorizedResponse, giphyApiKey, tokenType, edgeToEdge = true, liquidGlassEnabled = false, } = value // Set during render, not in an effect — navigator options factories read it // before effects would run. setLiquidGlassCapability(liquidGlassEnabled) const theme = useCreateChatTheme(value.theme || {}) const session = useMemo( () => new Session({ token, env, type: tokenType }), [env, token, tokenType] ) const productAnalyticsConfig = useProductAnalyticsConfig(session) const contextValue: ChatContextValue = { ...value, edgeToEdge, liquidGlassEnabled, env, giphyApiKey, onAgeDisqualification, onUnauthorizedResponse, session, theme, token, productAnalyticsConfig, } return {children} } export interface CreateChatThemeProps { theme?: DeepPartial colorScheme?: ColorSchemeName } export const useChatContext = () => { return React.useContext(ChatContext) } export const useCreateChatTheme = ({ theme: customTheme = {}, colorScheme: appColorScheme, }: CreateChatThemeProps): ChatTheme => { const internalColorScheme = useColorScheme() || 'light' const colorScheme = appColorScheme || internalColorScheme const theme = { ...merge({}, defaultTheme(colorScheme), customTheme), colors: { ...merge({}, defaultTheme(colorScheme).colors, customTheme?.colors), }, } return theme } interface ProductAnalyticsTokenResponse { type: 'ProductAnalyticsToken' id: string url: string metadata: Record } // expiry time comes from here: // https://github.com/planningcenter/pco-product-analytics/blob/e5bbc4c31b715b3c329c06a162829ee6967d3094/lib/pco/product/analytics/client_token_provider.rb#L24 const TOKEN_EXPIRY_TIME = 604800 * 1000 // 7 days in milliseconds const useProductAnalyticsConfig = (session: Session): ProductAnalyticsConfig | undefined => { const uri = useMemo(() => new Uri({ session }), [session]) const apiClient = useMemo( () => new Client({ root: uri.api(`/chat/v2`), defaultHeaders: uri.headers, version: '2018-11-01', }), [uri] ) const { data: config } = useQuery({ queryKey: ['product-analytics-token'], queryFn: () => apiClient .post>({ url: '/me/product_analytics_authorize', data: { data: { type: 'ProductAnalyticsToken', attributes: {}, }, }, }) .then(res => ({ token: res.data.id, endpoint: res.data.url, metadata: res.data.metadata, })), staleTime: TOKEN_EXPIRY_TIME, gcTime: TOKEN_EXPIRY_TIME, refetchInterval: TOKEN_EXPIRY_TIME, enabled: process.env.NODE_ENV !== 'test', }) return config }