mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-29 18:13:47 +00:00
fix: support 100% discount display + fix Telegram link widget race condition
- formatPrice shows "Бесплатно/Free" when price is 0 (SubscriptionPurchase, Subscription, RenewSubscription) - Add subscription.free locale key (ru/en/zh/fa) - Fix TelegramLinkWidget NS_BINDING_ABORTED: move legacy widget callback to ref pattern, remove unstable deps (showToast, t, navigate) from useEffect — prevents iframe destruction on re-render - Clean up OIDC effect deps
This commit is contained in:
@@ -324,6 +324,7 @@
|
|||||||
"used": "Used",
|
"used": "Used",
|
||||||
"of": "of",
|
"of": "of",
|
||||||
"renew": "Renew",
|
"renew": "Renew",
|
||||||
|
"free": "Free",
|
||||||
"extend": "Extend Subscription",
|
"extend": "Extend Subscription",
|
||||||
"buyTraffic": "Buy Traffic",
|
"buyTraffic": "Buy Traffic",
|
||||||
"buyDevices": "Buy Devices",
|
"buyDevices": "Buy Devices",
|
||||||
|
|||||||
@@ -285,6 +285,7 @@
|
|||||||
"used": "استفاده شده",
|
"used": "استفاده شده",
|
||||||
"of": "از",
|
"of": "از",
|
||||||
"renew": "تمدید",
|
"renew": "تمدید",
|
||||||
|
"free": "رایگان",
|
||||||
"extend": "تمدید اشتراک",
|
"extend": "تمدید اشتراک",
|
||||||
"buyTraffic": "خرید ترافیک",
|
"buyTraffic": "خرید ترافیک",
|
||||||
"buyDevices": "خرید دستگاه",
|
"buyDevices": "خرید دستگاه",
|
||||||
|
|||||||
@@ -339,6 +339,7 @@
|
|||||||
"used": "Использовано",
|
"used": "Использовано",
|
||||||
"of": "из",
|
"of": "из",
|
||||||
"renew": "Продлить",
|
"renew": "Продлить",
|
||||||
|
"free": "Бесплатно",
|
||||||
"extend": "Продлить подписку",
|
"extend": "Продлить подписку",
|
||||||
"buyTraffic": "Докупить трафик",
|
"buyTraffic": "Докупить трафик",
|
||||||
"buyDevices": "Докупить устройства",
|
"buyDevices": "Докупить устройства",
|
||||||
|
|||||||
@@ -285,6 +285,7 @@
|
|||||||
"used": "已使用",
|
"used": "已使用",
|
||||||
"of": "/",
|
"of": "/",
|
||||||
"renew": "续费",
|
"renew": "续费",
|
||||||
|
"free": "免费",
|
||||||
"extend": "延长订阅",
|
"extend": "延长订阅",
|
||||||
"buyTraffic": "购买流量",
|
"buyTraffic": "购买流量",
|
||||||
"buyDevices": "增加设备",
|
"buyDevices": "增加设备",
|
||||||
|
|||||||
@@ -157,25 +157,13 @@ function TelegramLinkWidget() {
|
|||||||
isOIDC,
|
isOIDC,
|
||||||
widgetConfig?.oidc_client_id,
|
widgetConfig?.oidc_client_id,
|
||||||
widgetConfig?.request_access,
|
widgetConfig?.request_access,
|
||||||
showToast,
|
|
||||||
t,
|
|
||||||
scriptLoaded,
|
scriptLoaded,
|
||||||
handleScriptFailed,
|
handleScriptFailed,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Legacy widget effect (only when NOT OIDC) with timeout
|
// Ref-based callback for legacy widget (avoids re-creating iframe on every render)
|
||||||
useEffect(() => {
|
const handleWidgetAuthRef = useRef<(user: Record<string, unknown>) => void>(undefined);
|
||||||
if (isOIDC || !containerRef.current || !botUsername) return;
|
handleWidgetAuthRef.current = async (user: Record<string, unknown>) => {
|
||||||
|
|
||||||
const container = containerRef.current;
|
|
||||||
while (container.firstChild) {
|
|
||||||
container.removeChild(container.firstChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
const callbackName = '__onTelegramLinkAuth';
|
|
||||||
(window as unknown as Record<string, unknown>)[callbackName] = async (
|
|
||||||
user: Record<string, unknown>,
|
|
||||||
) => {
|
|
||||||
if (!mountedRef.current) return;
|
if (!mountedRef.current) return;
|
||||||
try {
|
try {
|
||||||
const response = await authApi.linkTelegram({
|
const response = await authApi.linkTelegram({
|
||||||
@@ -198,6 +186,22 @@ function TelegramLinkWidget() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Legacy widget effect (only when NOT OIDC) with timeout
|
||||||
|
useEffect(() => {
|
||||||
|
if (isOIDC || !containerRef.current || !botUsername) return;
|
||||||
|
|
||||||
|
const container = containerRef.current;
|
||||||
|
while (container.firstChild) {
|
||||||
|
container.removeChild(container.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
const callbackName = '__onTelegramLinkAuth';
|
||||||
|
(window as unknown as Record<string, unknown>)[callbackName] = (
|
||||||
|
user: Record<string, unknown>,
|
||||||
|
) => {
|
||||||
|
handleWidgetAuthRef.current?.(user);
|
||||||
|
};
|
||||||
|
|
||||||
const script = document.createElement('script');
|
const script = document.createElement('script');
|
||||||
script.src = 'https://telegram.org/js/telegram-widget.js?23';
|
script.src = 'https://telegram.org/js/telegram-widget.js?23';
|
||||||
script.setAttribute('data-telegram-login', botUsername);
|
script.setAttribute('data-telegram-login', botUsername);
|
||||||
@@ -227,16 +231,7 @@ function TelegramLinkWidget() {
|
|||||||
container.removeChild(container.firstChild);
|
container.removeChild(container.firstChild);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [
|
}, [isOIDC, botUsername, handleScriptFailed]);
|
||||||
isOIDC,
|
|
||||||
botUsername,
|
|
||||||
navigate,
|
|
||||||
showToast,
|
|
||||||
t,
|
|
||||||
queryClient,
|
|
||||||
handleLinkResult,
|
|
||||||
handleScriptFailed,
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (!botUsername && !isOIDC) {
|
if (!botUsername && !isOIDC) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -177,7 +177,9 @@ export default function RenewSubscription() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="text-right">
|
<div className="text-right">
|
||||||
<div className="text-base font-semibold" style={{ color: g.text }}>
|
<div className="text-base font-semibold" style={{ color: g.text }}>
|
||||||
{formatAmount(option.price_kopeks / 100)} {currencySymbol}
|
{option.price_kopeks === 0
|
||||||
|
? t('subscription.free', 'Бесплатно')
|
||||||
|
: `${formatAmount(option.price_kopeks / 100)} ${currencySymbol}`}
|
||||||
</div>
|
</div>
|
||||||
{months > 1 && (
|
{months > 1 && (
|
||||||
<div className="text-[11px]" style={{ color: g.textSecondary }}>
|
<div className="text-[11px]" style={{ color: g.textSecondary }}>
|
||||||
|
|||||||
@@ -183,7 +183,10 @@ export default function Subscription() {
|
|||||||
const destructiveConfirm = useDestructiveConfirm();
|
const destructiveConfirm = useDestructiveConfirm();
|
||||||
|
|
||||||
// Helper to format price from kopeks
|
// Helper to format price from kopeks
|
||||||
const formatPrice = (kopeks: number) => `${formatAmount(kopeks / 100)} ${currencySymbol}`;
|
const formatPrice = (kopeks: number) =>
|
||||||
|
kopeks === 0
|
||||||
|
? t('subscription.free', 'Бесплатно')
|
||||||
|
: `${formatAmount(kopeks / 100)} ${currencySymbol}`;
|
||||||
|
|
||||||
// Device/traffic topup state
|
// Device/traffic topup state
|
||||||
const [showDeviceTopup, setShowDeviceTopup] = useState(false);
|
const [showDeviceTopup, setShowDeviceTopup] = useState(false);
|
||||||
|
|||||||
@@ -37,7 +37,10 @@ export default function SubscriptionPurchase() {
|
|||||||
const { isDark } = useTheme();
|
const { isDark } = useTheme();
|
||||||
const g = getGlassColors(isDark);
|
const g = getGlassColors(isDark);
|
||||||
|
|
||||||
const formatPrice = (kopeks: number) => `${formatAmount(kopeks / 100)} ${currencySymbol}`;
|
const formatPrice = (kopeks: number) =>
|
||||||
|
kopeks === 0
|
||||||
|
? t('subscription.free', 'Бесплатно')
|
||||||
|
: `${formatAmount(kopeks / 100)} ${currencySymbol}`;
|
||||||
|
|
||||||
// Subscription query (shares cache with /subscription page)
|
// Subscription query (shares cache with /subscription page)
|
||||||
const { data: subscriptionResponse, isLoading } = useQuery({
|
const { data: subscriptionResponse, isLoading } = useQuery({
|
||||||
|
|||||||
Reference in New Issue
Block a user