actions.ts
2 years ago
constants.ts
2 years ago
index.ts
2 years ago
reducer.ts
2 years ago
types.ts
2 years ago
reducer.ts
220 lines
| 1 | /** |
| 2 | * Types & Constants |
| 3 | */ |
| 4 | import { |
| 5 | ACTION_DECREASE_NEW_ASYNC_REQUEST_COUNTDOWN, |
| 6 | ACTION_ENQUEUE_ASYNC_REQUEST, |
| 7 | ACTION_INCREASE_AI_ASSISTANT_REQUESTS_COUNT, |
| 8 | ACTION_REQUEST_AI_ASSISTANT_FEATURE, |
| 9 | ACTION_SET_PLANS, |
| 10 | ACTION_SET_AI_ASSISTANT_FEATURE_REQUIRE_UPGRADE, |
| 11 | ACTION_STORE_AI_ASSISTANT_FEATURE, |
| 12 | ASYNC_REQUEST_COUNTDOWN_INIT_VALUE, |
| 13 | FREE_PLAN_REQUESTS_LIMIT, |
| 14 | UNLIMITED_PLAN_REQUESTS_LIMIT, |
| 15 | ACTION_SET_TIER_PLANS_ENABLED, |
| 16 | } from './constants'; |
| 17 | import type { PlanStateProps } from './types'; |
| 18 | |
| 19 | const INITIAL_STATE: PlanStateProps = { |
| 20 | plans: [], |
| 21 | features: { |
| 22 | aiAssistant: { |
| 23 | hasFeature: true, |
| 24 | isOverLimit: false, |
| 25 | requestsCount: 0, |
| 26 | requestsLimit: FREE_PLAN_REQUESTS_LIMIT, |
| 27 | requireUpgrade: false, |
| 28 | errorMessage: '', |
| 29 | errorCode: '', |
| 30 | upgradeType: 'default', |
| 31 | currentTier: { |
| 32 | slug: 'ai-assistant-tier-free', |
| 33 | value: 0, |
| 34 | limit: 20, |
| 35 | }, |
| 36 | usagePeriod: { |
| 37 | currentStart: '', |
| 38 | nextStart: '', |
| 39 | requestsCount: 0, |
| 40 | }, |
| 41 | nextTier: null, |
| 42 | tierPlansEnabled: false, |
| 43 | _meta: { |
| 44 | isRequesting: false, |
| 45 | asyncRequestCountdown: ASYNC_REQUEST_COUNTDOWN_INIT_VALUE, |
| 46 | asyncRequestTimerId: 0, |
| 47 | }, |
| 48 | }, |
| 49 | }, |
| 50 | }; |
| 51 | |
| 52 | export default function reducer( state = INITIAL_STATE, action ) { |
| 53 | switch ( action.type ) { |
| 54 | case ACTION_SET_PLANS: |
| 55 | return { |
| 56 | ...state, |
| 57 | plans: action.plans, |
| 58 | }; |
| 59 | |
| 60 | case ACTION_REQUEST_AI_ASSISTANT_FEATURE: |
| 61 | return { |
| 62 | ...state, |
| 63 | features: { |
| 64 | ...state.features, |
| 65 | aiAssistant: { |
| 66 | ...state.features.aiAssistant, |
| 67 | _meta: { |
| 68 | ...state.features.aiAssistant._meta, |
| 69 | isRequesting: true, |
| 70 | asyncRequestCountdown: ASYNC_REQUEST_COUNTDOWN_INIT_VALUE, // restore the countdown |
| 71 | asyncRequestTimerId: 0, // reset the timer id |
| 72 | }, |
| 73 | }, |
| 74 | }, |
| 75 | }; |
| 76 | |
| 77 | case ACTION_STORE_AI_ASSISTANT_FEATURE: { |
| 78 | return { |
| 79 | ...state, |
| 80 | features: { |
| 81 | ...state.features, |
| 82 | aiAssistant: { |
| 83 | ...action.feature, |
| 84 | _meta: { |
| 85 | ...state.features.aiAssistant._meta, |
| 86 | isRequesting: false, |
| 87 | }, |
| 88 | }, |
| 89 | }, |
| 90 | }; |
| 91 | } |
| 92 | |
| 93 | case ACTION_INCREASE_AI_ASSISTANT_REQUESTS_COUNT: { |
| 94 | // Usage Period data |
| 95 | const usagePeriod = state.features.aiAssistant.usagePeriod || { requestsCount: 0 }; |
| 96 | |
| 97 | // Increase requests counters |
| 98 | const requestsCount = state.features.aiAssistant.requestsCount + action.count; |
| 99 | usagePeriod.requestsCount += action.count; |
| 100 | |
| 101 | // Current tier value |
| 102 | const currentTierValue = state.features.aiAssistant.currentTier?.value; |
| 103 | |
| 104 | const isFreeTierPlan = |
| 105 | ( typeof currentTierValue === 'undefined' && ! state.features.aiAssistant.hasFeature ) || |
| 106 | currentTierValue === 0; |
| 107 | |
| 108 | const isUnlimitedTierPlan = |
| 109 | ( typeof currentTierValue === 'undefined' && state.features.aiAssistant.hasFeature ) || |
| 110 | currentTierValue === 1; |
| 111 | |
| 112 | // Request limit defined with the current tier limit by default. |
| 113 | let requestsLimit = state.features.aiAssistant.currentTier?.limit; |
| 114 | |
| 115 | if ( isUnlimitedTierPlan ) { |
| 116 | requestsLimit = UNLIMITED_PLAN_REQUESTS_LIMIT; |
| 117 | } else if ( isFreeTierPlan ) { |
| 118 | requestsLimit = state.features.aiAssistant.requestsLimit; |
| 119 | } |
| 120 | |
| 121 | const currentCount = |
| 122 | isUnlimitedTierPlan || isFreeTierPlan // @todo: update once tier data is available |
| 123 | ? requestsCount |
| 124 | : state.features.aiAssistant.usagePeriod?.requestsCount; |
| 125 | |
| 126 | /** |
| 127 | * Compute the AI Assistant Feature data optimistically, |
| 128 | * based on the Jetpack_AI_Helper::get_ai_assistance_feature() helper. |
| 129 | * |
| 130 | * @see _inc/lib/class-jetpack-ai-helper.php |
| 131 | */ |
| 132 | const isOverLimit = currentCount >= requestsLimit; |
| 133 | |
| 134 | // highest tier holds a soft limit so requireUpgrade is false on that case (nextTier null means highest tier) |
| 135 | const requireUpgrade = isOverLimit && state.features.aiAssistant.nextTier !== null; |
| 136 | |
| 137 | return { |
| 138 | ...state, |
| 139 | features: { |
| 140 | ...state.features, |
| 141 | aiAssistant: { |
| 142 | ...state.features.aiAssistant, |
| 143 | isOverLimit, |
| 144 | requestsCount, |
| 145 | requireUpgrade, |
| 146 | usagePeriod: { ...usagePeriod }, |
| 147 | }, |
| 148 | }, |
| 149 | }; |
| 150 | } |
| 151 | |
| 152 | case ACTION_DECREASE_NEW_ASYNC_REQUEST_COUNTDOWN: { |
| 153 | return { |
| 154 | ...state, |
| 155 | features: { |
| 156 | ...state.features, |
| 157 | aiAssistant: { |
| 158 | ...state.features.aiAssistant, |
| 159 | _meta: { |
| 160 | ...state.features.aiAssistant._meta, |
| 161 | asyncRequestCountdown: state.features.aiAssistant._meta.asyncRequestCountdown - 1, |
| 162 | }, |
| 163 | }, |
| 164 | }, |
| 165 | }; |
| 166 | } |
| 167 | |
| 168 | case ACTION_ENQUEUE_ASYNC_REQUEST: { |
| 169 | return { |
| 170 | ...state, |
| 171 | features: { |
| 172 | ...state.features, |
| 173 | aiAssistant: { |
| 174 | ...state.features.aiAssistant, |
| 175 | _meta: { |
| 176 | ...state.features.aiAssistant._meta, |
| 177 | asyncRequestTimerId: action.timerId, |
| 178 | }, |
| 179 | }, |
| 180 | }, |
| 181 | }; |
| 182 | } |
| 183 | |
| 184 | case ACTION_SET_AI_ASSISTANT_FEATURE_REQUIRE_UPGRADE: { |
| 185 | /* |
| 186 | * If we require an upgrade, we are also over the limit; |
| 187 | * The opposite is not true, we can be over the limit without |
| 188 | * requiring an upgrade, for example when we are on the highest tier. |
| 189 | * In this case, we don't want to set isOverLimit to false. |
| 190 | */ |
| 191 | return { |
| 192 | ...state, |
| 193 | features: { |
| 194 | ...state.features, |
| 195 | aiAssistant: { |
| 196 | ...state.features.aiAssistant, |
| 197 | requireUpgrade: action.requireUpgrade, |
| 198 | ...( action.requireUpgrade ? { isOverLimit: true } : {} ), |
| 199 | }, |
| 200 | }, |
| 201 | }; |
| 202 | } |
| 203 | |
| 204 | case ACTION_SET_TIER_PLANS_ENABLED: { |
| 205 | return { |
| 206 | ...state, |
| 207 | features: { |
| 208 | ...state.features, |
| 209 | aiAssistant: { |
| 210 | ...state.features.aiAssistant, |
| 211 | tierPlansEnabled: action.tierPlansEnabled, |
| 212 | }, |
| 213 | }, |
| 214 | }; |
| 215 | } |
| 216 | } |
| 217 | |
| 218 | return state; |
| 219 | } |
| 220 |