@windrun-huaiin/third-ui 7.5.3 → 7.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -17,17 +17,30 @@ function MoneyPriceInteractive({ data, config, upgradeApiEndpoint, signInPath })
17
17
  const fingerprintContext = fingerprintProvider.useFingerprintContextSafe();
18
18
  const { redirectToSignIn, user } = nextjs.useClerk();
19
19
  const router = navigation.useRouter();
20
- // 根据用户订阅状态确定初始 billing type
21
- const getInitialBillingType = React.useCallback(() => {
20
+ const [billingType, setBillingType] = React.useState(() => {
22
21
  var _a;
23
- // 如果用户有活跃订阅,使用订阅的计费周期
22
+ // 懒初始化:只在组件首次渲染时计算一次
23
+ // 如果用户有活跃订阅,通过 priceId 精确匹配计费周期
24
24
  if (((_a = fingerprintContext === null || fingerprintContext === void 0 ? void 0 : fingerprintContext.xSubscription) === null || _a === void 0 ? void 0 : _a.status) === 'active' && fingerprintContext.xSubscription.priceId) {
25
- return fingerprintContext.xSubscription.priceId.includes('yearly') ? 'yearly' : 'monthly';
25
+ const userPriceId = fingerprintContext.xSubscription.priceId;
26
+ const providerConfig = moneyPriceConfigUtil.getActiveProviderConfig(config);
27
+ // 检查所有年付计划的 priceId
28
+ const yearlyPriceIds = [
29
+ providerConfig.products.free.plans.yearly.priceId,
30
+ providerConfig.products.pro.plans.yearly.priceId,
31
+ providerConfig.products.ultra.plans.yearly.priceId
32
+ ];
33
+ // 如果匹配到年付计划,返回 yearly,否则返回 monthly
34
+ if (yearlyPriceIds.includes(userPriceId)) {
35
+ return 'yearly';
36
+ }
37
+ else {
38
+ return 'monthly';
39
+ }
26
40
  }
27
41
  // 否则使用默认值
28
42
  return data.billingSwitch.defaultKey;
29
- }, [fingerprintContext, data.billingSwitch.defaultKey]);
30
- const [billingType, setBillingType] = React.useState(getInitialBillingType());
43
+ });
31
44
  const [isProcessing, setIsProcessing] = React.useState(false);
32
45
  const [tooltip, setTooltip] = React.useState({ show: false, content: '', x: 0, y: 0 });
33
46
  // 确定用户状态
@@ -122,6 +135,26 @@ function MoneyPriceInteractive({ data, config, upgradeApiEndpoint, signInPath })
122
135
  return;
123
136
  }
124
137
  const result = yield response.json();
138
+ // 处理HTTP错误状态码
139
+ if (!response.ok) {
140
+ const errorMessage = result.error || `Request failed with status ${response.status}`;
141
+ console.error('Upgrade request failed:', errorMessage);
142
+ // 根据状态码决定处理方式
143
+ if (response.status === 401 || response.status === 403) {
144
+ // 鉴权失败,重定向到登录页
145
+ if (signInPath) {
146
+ window.location.href = signInPath;
147
+ }
148
+ else {
149
+ redirectToSignIn();
150
+ }
151
+ }
152
+ else {
153
+ // 其他错误(如500等),显示错误提示
154
+ alert(`Operation failed: ${errorMessage}`);
155
+ }
156
+ return;
157
+ }
125
158
  if (result.success && ((_a = result.data) === null || _a === void 0 ? void 0 : _a.sessionUrl)) {
126
159
  window.location.href = result.data.sessionUrl;
127
160
  }
@@ -220,19 +253,6 @@ function MoneyPriceInteractive({ data, config, upgradeApiEndpoint, signInPath })
220
253
  }, []);
221
254
  // State for button portals
222
255
  const [buttonPortals, setButtonPortals] = React.useState([]);
223
- // 当 fingerprint context 变化时,更新 billing type 以匹配用户的订阅状态
224
- React.useEffect(() => {
225
- const newBillingType = getInitialBillingType();
226
- if (newBillingType !== billingType) {
227
- setBillingType(newBillingType);
228
- // 延迟执行以确保 DOM 已渲染
229
- setTimeout(() => {
230
- updatePriceDisplay(newBillingType);
231
- updateButtonStyles(newBillingType);
232
- updateDiscountInfo(newBillingType);
233
- }, 0);
234
- }
235
- }, [fingerprintContext, getInitialBillingType, billingType, updatePriceDisplay, updateButtonStyles, updateDiscountInfo]);
236
256
  // 处理月付/年付切换和 tooltip 功能
237
257
  React.useEffect(() => {
238
258
  const monthlyButton = document.querySelector('[data-billing-button="monthly"]');
@@ -286,10 +306,6 @@ function MoneyPriceInteractive({ data, config, upgradeApiEndpoint, signInPath })
286
306
  }
287
307
  });
288
308
  });
289
- // Initial updates
290
- updatePriceDisplay(billingType);
291
- updateDiscountInfo(billingType);
292
- updateButtonStyles(billingType);
293
309
  return () => {
294
310
  if (monthlyButton) {
295
311
  monthlyButton.removeEventListener('click', handleMonthlyClick);
@@ -303,7 +319,28 @@ function MoneyPriceInteractive({ data, config, upgradeApiEndpoint, signInPath })
303
319
  element.removeEventListener('mouseleave', handlers.mouseleave);
304
320
  });
305
321
  };
306
- }, [data, billingType, updatePriceDisplay, updateButtonStyles, updateDiscountInfo, userContext, handleLogin, handleUpgrade, isProcessing]);
322
+ }, [data, updatePriceDisplay, updateButtonStyles, updateDiscountInfo, userContext, handleLogin, handleUpgrade, isProcessing]);
323
+ // 单独的 effect 用于初始更新,只在组件挂载时执行一次
324
+ React.useEffect(() => {
325
+ // 直接在这里重新计算,避免闭包问题
326
+ const initialBillingType = (() => {
327
+ var _a;
328
+ if (((_a = fingerprintContext === null || fingerprintContext === void 0 ? void 0 : fingerprintContext.xSubscription) === null || _a === void 0 ? void 0 : _a.status) === 'active' && fingerprintContext.xSubscription.priceId) {
329
+ const userPriceId = fingerprintContext.xSubscription.priceId;
330
+ const providerConfig = moneyPriceConfigUtil.getActiveProviderConfig(config);
331
+ const yearlyPriceIds = [
332
+ providerConfig.products.free.plans.yearly.priceId,
333
+ providerConfig.products.pro.plans.yearly.priceId,
334
+ providerConfig.products.ultra.plans.yearly.priceId
335
+ ];
336
+ return yearlyPriceIds.includes(userPriceId) ? 'yearly' : 'monthly';
337
+ }
338
+ return data.billingSwitch.defaultKey;
339
+ })();
340
+ updatePriceDisplay(initialBillingType);
341
+ updateDiscountInfo(initialBillingType);
342
+ updateButtonStyles(initialBillingType);
343
+ }, []); // 空依赖数组,只执行一次
307
344
  // Create button portals after component mounts
308
345
  React.useEffect(() => {
309
346
  const portals = [];
@@ -2,7 +2,7 @@
2
2
  import { __awaiter } from '../../node_modules/.pnpm/@rollup_plugin-typescript@12.1.4_rollup@4.46.2_tslib@2.8.1_typescript@5.9.2/node_modules/tslib/tslib.es6.mjs';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
4
  import { useClerk } from '@clerk/nextjs';
5
- import { useCallback, useState, useMemo, useEffect } from 'react';
5
+ import { useState, useCallback, useMemo, useEffect } from 'react';
6
6
  import { useFingerprintContextSafe } from '../../clerk/fingerprint/fingerprint-provider.mjs';
7
7
  import { cn } from '@windrun-huaiin/lib/utils';
8
8
  import { useRouter } from 'next/navigation';
@@ -15,17 +15,30 @@ function MoneyPriceInteractive({ data, config, upgradeApiEndpoint, signInPath })
15
15
  const fingerprintContext = useFingerprintContextSafe();
16
16
  const { redirectToSignIn, user } = useClerk();
17
17
  const router = useRouter();
18
- // 根据用户订阅状态确定初始 billing type
19
- const getInitialBillingType = useCallback(() => {
18
+ const [billingType, setBillingType] = useState(() => {
20
19
  var _a;
21
- // 如果用户有活跃订阅,使用订阅的计费周期
20
+ // 懒初始化:只在组件首次渲染时计算一次
21
+ // 如果用户有活跃订阅,通过 priceId 精确匹配计费周期
22
22
  if (((_a = fingerprintContext === null || fingerprintContext === void 0 ? void 0 : fingerprintContext.xSubscription) === null || _a === void 0 ? void 0 : _a.status) === 'active' && fingerprintContext.xSubscription.priceId) {
23
- return fingerprintContext.xSubscription.priceId.includes('yearly') ? 'yearly' : 'monthly';
23
+ const userPriceId = fingerprintContext.xSubscription.priceId;
24
+ const providerConfig = getActiveProviderConfig(config);
25
+ // 检查所有年付计划的 priceId
26
+ const yearlyPriceIds = [
27
+ providerConfig.products.free.plans.yearly.priceId,
28
+ providerConfig.products.pro.plans.yearly.priceId,
29
+ providerConfig.products.ultra.plans.yearly.priceId
30
+ ];
31
+ // 如果匹配到年付计划,返回 yearly,否则返回 monthly
32
+ if (yearlyPriceIds.includes(userPriceId)) {
33
+ return 'yearly';
34
+ }
35
+ else {
36
+ return 'monthly';
37
+ }
24
38
  }
25
39
  // 否则使用默认值
26
40
  return data.billingSwitch.defaultKey;
27
- }, [fingerprintContext, data.billingSwitch.defaultKey]);
28
- const [billingType, setBillingType] = useState(getInitialBillingType());
41
+ });
29
42
  const [isProcessing, setIsProcessing] = useState(false);
30
43
  const [tooltip, setTooltip] = useState({ show: false, content: '', x: 0, y: 0 });
31
44
  // 确定用户状态
@@ -120,6 +133,26 @@ function MoneyPriceInteractive({ data, config, upgradeApiEndpoint, signInPath })
120
133
  return;
121
134
  }
122
135
  const result = yield response.json();
136
+ // 处理HTTP错误状态码
137
+ if (!response.ok) {
138
+ const errorMessage = result.error || `Request failed with status ${response.status}`;
139
+ console.error('Upgrade request failed:', errorMessage);
140
+ // 根据状态码决定处理方式
141
+ if (response.status === 401 || response.status === 403) {
142
+ // 鉴权失败,重定向到登录页
143
+ if (signInPath) {
144
+ window.location.href = signInPath;
145
+ }
146
+ else {
147
+ redirectToSignIn();
148
+ }
149
+ }
150
+ else {
151
+ // 其他错误(如500等),显示错误提示
152
+ alert(`Operation failed: ${errorMessage}`);
153
+ }
154
+ return;
155
+ }
123
156
  if (result.success && ((_a = result.data) === null || _a === void 0 ? void 0 : _a.sessionUrl)) {
124
157
  window.location.href = result.data.sessionUrl;
125
158
  }
@@ -218,19 +251,6 @@ function MoneyPriceInteractive({ data, config, upgradeApiEndpoint, signInPath })
218
251
  }, []);
219
252
  // State for button portals
220
253
  const [buttonPortals, setButtonPortals] = useState([]);
221
- // 当 fingerprint context 变化时,更新 billing type 以匹配用户的订阅状态
222
- useEffect(() => {
223
- const newBillingType = getInitialBillingType();
224
- if (newBillingType !== billingType) {
225
- setBillingType(newBillingType);
226
- // 延迟执行以确保 DOM 已渲染
227
- setTimeout(() => {
228
- updatePriceDisplay(newBillingType);
229
- updateButtonStyles(newBillingType);
230
- updateDiscountInfo(newBillingType);
231
- }, 0);
232
- }
233
- }, [fingerprintContext, getInitialBillingType, billingType, updatePriceDisplay, updateButtonStyles, updateDiscountInfo]);
234
254
  // 处理月付/年付切换和 tooltip 功能
235
255
  useEffect(() => {
236
256
  const monthlyButton = document.querySelector('[data-billing-button="monthly"]');
@@ -284,10 +304,6 @@ function MoneyPriceInteractive({ data, config, upgradeApiEndpoint, signInPath })
284
304
  }
285
305
  });
286
306
  });
287
- // Initial updates
288
- updatePriceDisplay(billingType);
289
- updateDiscountInfo(billingType);
290
- updateButtonStyles(billingType);
291
307
  return () => {
292
308
  if (monthlyButton) {
293
309
  monthlyButton.removeEventListener('click', handleMonthlyClick);
@@ -301,7 +317,28 @@ function MoneyPriceInteractive({ data, config, upgradeApiEndpoint, signInPath })
301
317
  element.removeEventListener('mouseleave', handlers.mouseleave);
302
318
  });
303
319
  };
304
- }, [data, billingType, updatePriceDisplay, updateButtonStyles, updateDiscountInfo, userContext, handleLogin, handleUpgrade, isProcessing]);
320
+ }, [data, updatePriceDisplay, updateButtonStyles, updateDiscountInfo, userContext, handleLogin, handleUpgrade, isProcessing]);
321
+ // 单独的 effect 用于初始更新,只在组件挂载时执行一次
322
+ useEffect(() => {
323
+ // 直接在这里重新计算,避免闭包问题
324
+ const initialBillingType = (() => {
325
+ var _a;
326
+ if (((_a = fingerprintContext === null || fingerprintContext === void 0 ? void 0 : fingerprintContext.xSubscription) === null || _a === void 0 ? void 0 : _a.status) === 'active' && fingerprintContext.xSubscription.priceId) {
327
+ const userPriceId = fingerprintContext.xSubscription.priceId;
328
+ const providerConfig = getActiveProviderConfig(config);
329
+ const yearlyPriceIds = [
330
+ providerConfig.products.free.plans.yearly.priceId,
331
+ providerConfig.products.pro.plans.yearly.priceId,
332
+ providerConfig.products.ultra.plans.yearly.priceId
333
+ ];
334
+ return yearlyPriceIds.includes(userPriceId) ? 'yearly' : 'monthly';
335
+ }
336
+ return data.billingSwitch.defaultKey;
337
+ })();
338
+ updatePriceDisplay(initialBillingType);
339
+ updateDiscountInfo(initialBillingType);
340
+ updateButtonStyles(initialBillingType);
341
+ }, []); // 空依赖数组,只执行一次
305
342
  // Create button portals after component mounts
306
343
  useEffect(() => {
307
344
  const portals = [];
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var coseBase$1 = require('../../../../../_virtual/cose-base.js');
3
+ var coseBase$1 = require('../../../../../_virtual/cose-base2.js');
4
4
  var layoutBase = require('../../../layout-base@1.0.2/node_modules/layout-base/layout-base.js');
5
5
 
6
6
  var coseBase = coseBase$1.__module.exports;
@@ -1,4 +1,4 @@
1
- import { __module as coseBase$1 } from '../../../../../_virtual/cose-base.mjs';
1
+ import { __module as coseBase$1 } from '../../../../../_virtual/cose-base2.mjs';
2
2
  import { __require as requireLayoutBase } from '../../../layout-base@1.0.2/node_modules/layout-base/layout-base.mjs';
3
3
 
4
4
  var coseBase = coseBase$1.exports;
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var coseBase$1 = require('../../../../../_virtual/cose-base2.js');
3
+ var coseBase$1 = require('../../../../../_virtual/cose-base.js');
4
4
  var layoutBase = require('../../../layout-base@2.0.1/node_modules/layout-base/layout-base.js');
5
5
 
6
6
  var coseBase = coseBase$1.__module.exports;
@@ -1,4 +1,4 @@
1
- import { __module as coseBase$1 } from '../../../../../_virtual/cose-base2.mjs';
1
+ import { __module as coseBase$1 } from '../../../../../_virtual/cose-base.mjs';
2
2
  import { __require as requireLayoutBase } from '../../../layout-base@2.0.1/node_modules/layout-base/layout-base.mjs';
3
3
 
4
4
  var coseBase = coseBase$1.exports;
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var layoutBase$1 = require('../../../../../_virtual/layout-base.js');
3
+ var layoutBase$1 = require('../../../../../_virtual/layout-base2.js');
4
4
 
5
5
  var layoutBase = layoutBase$1.__module.exports;
6
6
 
@@ -1,4 +1,4 @@
1
- import { __module as layoutBase$1 } from '../../../../../_virtual/layout-base.mjs';
1
+ import { __module as layoutBase$1 } from '../../../../../_virtual/layout-base2.mjs';
2
2
 
3
3
  var layoutBase = layoutBase$1.exports;
4
4
 
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var layoutBase$1 = require('../../../../../_virtual/layout-base2.js');
3
+ var layoutBase$1 = require('../../../../../_virtual/layout-base.js');
4
4
 
5
5
  var layoutBase = layoutBase$1.__module.exports;
6
6
 
@@ -1,4 +1,4 @@
1
- import { __module as layoutBase$1 } from '../../../../../_virtual/layout-base2.mjs';
1
+ import { __module as layoutBase$1 } from '../../../../../_virtual/layout-base.mjs';
2
2
 
3
3
  var layoutBase = layoutBase$1.exports;
4
4
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windrun-huaiin/third-ui",
3
- "version": "7.5.3",
3
+ "version": "7.6.1",
4
4
  "description": "Third-party integrated UI components for windrun-huaiin projects",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -161,6 +161,26 @@ export function MoneyPriceInteractive({
161
161
 
162
162
  const result = await response.json();
163
163
 
164
+ // 处理HTTP错误状态码
165
+ if (!response.ok) {
166
+ const errorMessage = result.error || `Request failed with status ${response.status}`;
167
+ console.error('Upgrade request failed:', errorMessage);
168
+
169
+ // 根据状态码决定处理方式
170
+ if (response.status === 401 || response.status === 403) {
171
+ // 鉴权失败,重定向到登录页
172
+ if (signInPath) {
173
+ window.location.href = signInPath;
174
+ } else {
175
+ redirectToSignIn();
176
+ }
177
+ } else {
178
+ // 其他错误(如500等),显示错误提示
179
+ alert(`Operation failed: ${errorMessage}`);
180
+ }
181
+ return;
182
+ }
183
+
164
184
  if (result.success && result.data?.sessionUrl) {
165
185
  window.location.href = result.data.sessionUrl;
166
186
  } else {
@@ -328,11 +348,6 @@ export function MoneyPriceInteractive({
328
348
  });
329
349
  });
330
350
 
331
- // Initial updates
332
- updatePriceDisplay(billingType);
333
- updateDiscountInfo(billingType);
334
- updateButtonStyles(billingType);
335
-
336
351
  return () => {
337
352
  if (monthlyButton) {
338
353
  monthlyButton.removeEventListener('click', handleMonthlyClick);
@@ -347,7 +362,31 @@ export function MoneyPriceInteractive({
347
362
  element.removeEventListener('mouseleave', handlers.mouseleave);
348
363
  });
349
364
  };
350
- }, [data, billingType, updatePriceDisplay, updateButtonStyles, updateDiscountInfo, userContext, handleLogin, handleUpgrade, isProcessing]);
365
+ }, [data, updatePriceDisplay, updateButtonStyles, updateDiscountInfo, userContext, handleLogin, handleUpgrade, isProcessing]);
366
+
367
+ // 单独的 effect 用于初始更新,只在组件挂载时执行一次
368
+ useEffect(() => {
369
+ // 直接在这里重新计算,避免闭包问题
370
+ const initialBillingType = (() => {
371
+ if (fingerprintContext?.xSubscription?.status === 'active' && fingerprintContext.xSubscription.priceId) {
372
+ const userPriceId = fingerprintContext.xSubscription.priceId;
373
+ const providerConfig = getActiveProviderConfig(config);
374
+
375
+ const yearlyPriceIds = [
376
+ providerConfig.products.free.plans.yearly.priceId,
377
+ providerConfig.products.pro.plans.yearly.priceId,
378
+ providerConfig.products.ultra.plans.yearly.priceId
379
+ ];
380
+
381
+ return yearlyPriceIds.includes(userPriceId) ? 'yearly' : 'monthly';
382
+ }
383
+ return data.billingSwitch.defaultKey as 'monthly' | 'yearly';
384
+ })();
385
+
386
+ updatePriceDisplay(initialBillingType);
387
+ updateDiscountInfo(initialBillingType);
388
+ updateButtonStyles(initialBillingType);
389
+ }, []); // 空依赖数组,只执行一次
351
390
 
352
391
  // Create button portals after component mounts
353
392
  useEffect(() => {