feat: add app config from server

This commit is contained in:
Aidar Shaikhutdin @makeweb.space 2023-06-20 14:02:35 +03:00
parent d27091ae67
commit a24343a18e
12 changed files with 95 additions and 24 deletions

View File

@ -5,6 +5,7 @@ import {
Element, Element,
Elements, Elements,
AuthTokens, AuthTokens,
Apps,
Assets, Assets,
AssetCategories, AssetCategories,
DailyForecasts, DailyForecasts,
@ -17,6 +18,7 @@ import {
const api = { const api = {
auth: createMethod<AuthTokens.Payload, AuthTokens.Response>(AuthTokens.createRequest), auth: createMethod<AuthTokens.Payload, AuthTokens.Response>(AuthTokens.createRequest),
getAppConfig: createMethod<Apps.Payload, Apps.Response>(Apps.createRequest),
getElement: createMethod<Element.Payload, Element.Response>(Element.createRequest), getElement: createMethod<Element.Payload, Element.Response>(Element.createRequest),
getElements: createMethod<Elements.Payload, Elements.Response>(Elements.createRequest), getElements: createMethod<Elements.Payload, Elements.Response>(Elements.createRequest),
getUser: createMethod<User.GetPayload, User.Response>(User.createGetRequest), getUser: createMethod<User.GetPayload, User.Response>(User.createGetRequest),

63
src/api/resources/Apps.ts Normal file
View File

@ -0,0 +1,63 @@
import routes from '../../routes'
import { getBaseHeaders } from '../utils'
export interface Payload {
bundleId: 'com.life.aura' | 'com.life.gusebamps' | 'auraweb'
}
export interface Response {
data: {
subscription_popup: boolean
locale: string
alerts: Alert
active_iaps: ActiveIAps[]
version: string
apple_music_api: AppleMusicApi
chargebee: ChargebeeConfig
first_open_subscription_popup: boolean
runs_before_subscription_popup: number
appirater_alerts: AppiraterAlertAppiraterAlert[]
}
}
export interface ActiveIAps {
bundle_id: string
active: boolean
subscription_type: 'trial' | 'monthly' | 'yearly'
}
export interface AppiraterAlertAppiraterAlert {
id: string
type: string
body: string
label: string
cancel: string
title: string
uses_until_prompt: string
days_until_prompt: string
time_before_reminding: string
significant_events_until_prompt: string
show: boolean
apple_id: string
appirater_link: string
appirater_pro_link: string
}
export interface Alert {
description: string
}
export interface AppleMusicApi {
jwt: string
}
export interface ChargebeeConfig {
site: string
publishableKey: string
gatewayAccountId: string
}
export const createRequest = ({ bundleId }: Payload): Request => {
const url = new URL(routes.server.apps(bundleId))
return new Request(url, { method: 'GET', headers: getBaseHeaders() })
}

View File

@ -1,5 +1,6 @@
export * as Assets from './Assets' export * as Assets from './Assets'
export * as AssetCategories from './AssetCategories' export * as AssetCategories from './AssetCategories'
export * as Apps from './Apps'
export * as User from './User' export * as User from './User'
export * as DailyForecasts from './UserDailyForecasts' export * as DailyForecasts from './UserDailyForecasts'
export * as Auras from './Auras' export * as Auras from './Auras'

View File

@ -1,4 +1,4 @@
import { useCallback, useEffect, useId } from 'react' import { useCallback, useEffect } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { PaymentIntent } from '@chargebee/chargebee-js-types' import { PaymentIntent } from '@chargebee/chargebee-js-types'
import { useApi, useApiCall, extractErrorMessage, SubscriptionReceipts } from '../../../../api' import { useApi, useApiCall, extractErrorMessage, SubscriptionReceipts } from '../../../../api'
@ -17,7 +17,7 @@ interface ApplePayButtonProps {
export function ApplePayButton({ onSuccess, onError }: ApplePayButtonProps): JSX.Element { export function ApplePayButton({ onSuccess, onError }: ApplePayButtonProps): JSX.Element {
const api = useApi() const api = useApi()
const buttonId = useId() const buttonId = 'apple-pay-btn'
const { i18n } = useTranslation() const { i18n } = useTranslation()
const { token } = useAuth() const { token } = useAuth()
const { applePay } = usePayment() const { applePay } = usePayment()
@ -49,7 +49,7 @@ export function ApplePayButton({ onSuccess, onError }: ApplePayButtonProps): JSX
}, [data, applePay, buttonId, i18n.language, api, token, onSuccess, onError]) }, [data, applePay, buttonId, i18n.language, api, token, onSuccess, onError])
return isPending ? <Loader /> : ( return isPending ? <Loader /> : (
<div id={buttonId} style={{ height: 60 }}>{ <div id={buttonId} className='pay-btn'>{
error ? <ErrorText message={extractErrorMessage(error)} isShown={true} size='large'/> : null error ? <ErrorText message={extractErrorMessage(error)} isShown={true} size='large'/> : null
}</div> }</div>
) )

View File

@ -77,7 +77,7 @@ export function CardModal({ open, onClose, onSuccess, onError }: CardModalProps)
setStatus('subscribing') setStatus('subscribing')
cardRef.current?.tokenize({}) cardRef.current?.tokenize({})
.then((result: ChargebeeTokenizeResult) => { .then((result: ChargebeeTokenizeResult) => {
return api.createSubscriptionReceipt({ token, itemPriceId, gwToken: result.token }) return api.createSubscriptionReceipt({ token, itemPriceId, gwToken: result.vaultToken })
}) })
.then(({ subscription_receipt }: SubscriptionReceipts.Response) => { .then(({ subscription_receipt }: SubscriptionReceipts.Response) => {
setStatus('success') setStatus('success')

View File

@ -1,4 +1,4 @@
import { useCallback, useEffect, useId } from 'react' import { useCallback, useEffect } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { PaymentIntent } from '@chargebee/chargebee-js-types' import { PaymentIntent } from '@chargebee/chargebee-js-types'
import { SubscriptionReceipts, extractErrorMessage, useApi, useApiCall } from '../../../../api' import { SubscriptionReceipts, extractErrorMessage, useApi, useApiCall } from '../../../../api'
@ -17,7 +17,7 @@ interface GooglePayButtonProps {
export function GooglePayButton({ onSuccess, onError }: GooglePayButtonProps): JSX.Element { export function GooglePayButton({ onSuccess, onError }: GooglePayButtonProps): JSX.Element {
const api = useApi() const api = useApi()
const buttonId = useId() const buttonId = 'google-pay-btn'
const { i18n } = useTranslation() const { i18n } = useTranslation()
const { token } = useAuth() const { token } = useAuth()
const { googlePay } = usePayment() const { googlePay } = usePayment()
@ -49,7 +49,7 @@ export function GooglePayButton({ onSuccess, onError }: GooglePayButtonProps): J
}, [data, googlePay, buttonId, i18n.language, onSuccess, onError]) }, [data, googlePay, buttonId, i18n.language, onSuccess, onError])
return isPending ? <Loader /> : ( return isPending ? <Loader /> : (
<div id={buttonId} style={{ height: 60 }}>{ <div id={buttonId} className='pay-btn'>{
error ? <ErrorText message={extractErrorMessage(error)} isShown={true} size='large'/> : null error ? <ErrorText message={extractErrorMessage(error)} isShown={true} size='large'/> : null
}</div> }</div>
) )

View File

@ -98,4 +98,16 @@
padding-left: 12px; padding-left: 12px;
line-height: 1.5; line-height: 1.5;
font-weight: 500; font-weight: 500;
}
.pay-btn {
min-height: 60px;
max-width: 400px;
min-width: 250px;
width: 100%;
}
.pay-btn .gpay-button {
min-height: 60px;
border-radius: 20px;
} }

View File

@ -1,8 +0,0 @@
const config = {
chargebee: {
site: 'kefirapp-test',
publishableKey: 'test_VtWSamZEfP175nqGZhkD0uvoouHieElv',
},
}
export default config

View File

@ -10,15 +10,18 @@ import { ApiContext, createApi } from './api'
import { LegalContext, buildLegal } from './legal' import { LegalContext, buildLegal } from './legal'
import { PaymentContext } from './payment' import { PaymentContext } from './payment'
import { getClientLocale, buildResources, fallbackLng } from './locales' import { getClientLocale, buildResources, fallbackLng } from './locales'
import type { AppConfig } from './types'
import App from './components/App' import App from './components/App'
const init = async (config: AppConfig) => { const init = async () => {
const api = createApi() const api = createApi()
const lng = getClientLocale() const lng = getClientLocale()
const response = await api.getElements({ locale: lng }) const [elementsResponse, configResponse] = await Promise.all([
const resources = buildResources(response) api.getElements({ locale: lng }),
const legal = buildLegal(response) api.getAppConfig({ bundleId: 'auraweb' }),
])
const resources = buildResources(elementsResponse)
const legal = buildLegal(elementsResponse)
const config = configResponse.data
const i18nextInstance = i18next.createInstance() const i18nextInstance = i18next.createInstance()
const options = { lng, resources, fallbackLng, postProcess: [ `reactPostprocessor` ] } const options = { lng, resources, fallbackLng, postProcess: [ `reactPostprocessor` ] }
await i18nextInstance.use(initReactI18next).use(new ReactPostprocessor()).init(options) await i18nextInstance.use(initReactI18next).use(new ReactPostprocessor()).init(options)

View File

@ -1,5 +1,4 @@
import ReactDOM from 'react-dom/client' import ReactDOM from 'react-dom/client'
import config from './config'
import init from './init' import init from './init'
import 'react-circular-progressbar/dist/styles.css' import 'react-circular-progressbar/dist/styles.css'
import './fonts.css' import './fonts.css'
@ -18,7 +17,7 @@ const getRootElement = (id: string): HTMLElement => {
} }
const startApp = async () => { const startApp = async () => {
const vdom = await init(config) const vdom = await init()
const rootElement = getRootElement('root') const rootElement = getRootElement('root')
ReactDOM.createRoot(rootElement).render(vdom) ReactDOM.createRoot(rootElement).render(vdom)

View File

@ -22,6 +22,7 @@ const routes = {
token: () => [apiHost, prefix, 'auth', 'token.json'].join('/'), token: () => [apiHost, prefix, 'auth', 'token.json'].join('/'),
elements: () => [apiHost, prefix, 'elements.json'].join('/'), elements: () => [apiHost, prefix, 'elements.json'].join('/'),
element: (type: string) => [apiHost, prefix, 'elements', `${type}.json`].join('/'), element: (type: string) => [apiHost, prefix, 'elements', `${type}.json`].join('/'),
apps: (bundleId: string) => [apiHost, prefix, 'apps', `${bundleId}.json`].join('/'),
assets: (category: string) => [apiHost, prefix, 'assets', 'categories', `${category}.json`].join('/'), assets: (category: string) => [apiHost, prefix, 'assets', 'categories', `${category}.json`].join('/'),
assetCategories: () => [apiHost, prefix, 'assets', 'categories.json'].join('/'), assetCategories: () => [apiHost, prefix, 'assets', 'categories.json'].join('/'),
dailyForecasts: () => [apiHost, prefix, 'user', 'daily_forecast.json'].join('/'), dailyForecasts: () => [apiHost, prefix, 'user', 'daily_forecast.json'].join('/'),

View File

@ -1,5 +1,4 @@
import { Chargebee } from '@chargebee/chargebee-js-types' import { Chargebee } from '@chargebee/chargebee-js-types'
import config from './config'
declare global { declare global {
interface Window { interface Window {
@ -22,5 +21,4 @@ export interface SignupForm {
birthtime: string birthtime: string
} }
export type AppConfig = typeof config
export type UserStatus = 'lead' | 'registred' | 'subscribed' | 'unsubscribed' export type UserStatus = 'lead' | 'registred' | 'subscribed' | 'unsubscribed'