@umituz/react-native-subscription 2.39.8 → 2.39.10
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.
- package/package.json +1 -1
- package/src/domains/credits/application/CreditLimitCalculator.ts +6 -17
- package/src/domains/credits/core/UserCreditsDocument.ts +1 -1
- package/src/domains/credits/infrastructure/CreditsRepository.ts +3 -3
- package/src/domains/credits/infrastructure/operations/CreditsInitializer.ts +1 -1
- package/src/domains/credits/infrastructure/operations/CreditsWriter.ts +1 -1
- package/src/domains/paywall/components/PaywallFeatures.tsx +1 -1
- package/src/domains/paywall/components/PaywallFooter.tsx +1 -1
- package/src/domains/paywall/components/PaywallScreen.styles.ts +116 -44
- package/src/domains/paywall/components/PaywallScreen.tsx +184 -138
- package/src/domains/paywall/entities/types.ts +2 -0
- package/src/domains/paywall/hooks/usePaywallActions.ts +32 -40
- package/src/domains/paywall/utils/paywallLayoutUtils.ts +55 -0
- package/src/domains/revenuecat/core/types/RevenueCatData.ts +1 -1
- package/src/domains/revenuecat/core/types/RevenueCatTypes.ts +2 -2
- package/src/domains/revenuecat/infrastructure/services/RevenueCatInitializer.types.ts +1 -1
- package/src/domains/revenuecat/infrastructure/services/userSwitchHandler.ts +1 -1
- package/src/domains/subscription/application/SubscriptionInitializerTypes.ts +1 -1
- package/src/domains/subscription/application/SubscriptionSyncProcessor.ts +5 -22
- package/src/domains/subscription/application/SubscriptionSyncUtils.ts +1 -1
- package/src/domains/subscription/application/featureGate/featureGateBusinessRules.ts +27 -10
- package/src/domains/subscription/application/initializer/BackgroundInitializer.ts +42 -41
- package/src/domains/subscription/core/SubscriptionEvents.ts +1 -1
- package/src/domains/subscription/core/SubscriptionStatusHandlers.ts +1 -5
- package/src/domains/subscription/infrastructure/handlers/PackageHandler.ts +4 -6
- package/src/domains/subscription/infrastructure/handlers/PurchaseStatusResolver.ts +2 -2
- package/src/domains/subscription/infrastructure/handlers/package-operations/PackageRestorer.ts +1 -1
- package/src/domains/subscription/infrastructure/hooks/usePurchasePackage.ts +1 -1
- package/src/domains/subscription/infrastructure/hooks/useRestorePurchase.ts +1 -1
- package/src/domains/subscription/infrastructure/managers/SubscriptionManager.types.ts +2 -2
- package/src/domains/subscription/infrastructure/managers/initializationHandler.ts +1 -1
- package/src/domains/subscription/infrastructure/services/CustomerInfoListenerManager.ts +1 -1
- package/src/domains/subscription/infrastructure/services/PurchaseHandler.ts +2 -2
- package/src/domains/subscription/infrastructure/services/RestoreHandler.ts +4 -4
- package/src/domains/subscription/infrastructure/services/RevenueCatService.types.ts +1 -1
- package/src/domains/subscription/infrastructure/services/ServiceStateManager.ts +1 -1
- package/src/domains/subscription/infrastructure/services/listeners/CustomerInfoHandler.ts +4 -2
- package/src/domains/subscription/infrastructure/services/listeners/ListenerState.ts +1 -1
- package/src/domains/subscription/infrastructure/services/purchase/PurchaseErrorHandler.ts +3 -3
- package/src/domains/subscription/infrastructure/services/purchase/PurchaseExecutor.ts +2 -1
- package/src/domains/subscription/infrastructure/services/purchase/PurchaseValidator.ts +1 -1
- package/src/domains/subscription/infrastructure/services/revenueCatServiceInstance.ts +1 -1
- package/src/domains/subscription/infrastructure/utils/InitializationCache.ts +35 -42
- package/src/domains/subscription/infrastructure/utils/PremiumStatusSyncer.ts +3 -3
- package/src/domains/subscription/presentation/components/details/PremiumDetailsCardTypes.ts +1 -1
- package/src/domains/subscription/presentation/components/sections/SubscriptionSection.types.ts +1 -1
- package/src/domains/subscription/presentation/screens/SubscriptionDetailScreen.tsx +1 -1
- package/src/domains/subscription/presentation/screens/SubscriptionDetailScreen.types.ts +1 -1
- package/src/domains/subscription/presentation/stores/purchaseLoadingStore.ts +11 -8
- package/src/domains/subscription/presentation/useSubscriptionStatus.types.ts +1 -1
- package/src/domains/subscription/utils/featureGateUtils.ts +37 -0
- package/src/domains/subscription/utils/packageTypeFormatter.ts +1 -1
- package/src/domains/wallet/infrastructure/repositories/transaction/CollectionBuilder.ts +1 -1
- package/src/domains/wallet/infrastructure/repositories/transaction/TransactionFetcher.ts +2 -1
- package/src/domains/wallet/infrastructure/repositories/transaction/TransactionWriter.ts +2 -1
- package/src/domains/wallet/presentation/screens/WalletScreen.tsx +1 -1
- package/src/index.ts +5 -2
- package/src/init/createSubscriptionInitModule.ts +2 -1
- package/src/shared/infrastructure/SubscriptionEventBus.ts +24 -18
- package/src/domains/revenuecat/core/errors/index.ts +0 -3
- package/src/domains/revenuecat/core/types/index.ts +0 -3
- package/src/domains/subscription/application/initializer/index.ts +0 -2
- package/src/domains/subscription/core/types/index.ts +0 -3
- package/src/domains/subscription/infrastructure/handlers/package-operations/index.ts +0 -4
- package/src/domains/subscription/infrastructure/utils/renewal/index.ts +0 -3
- package/src/shared/infrastructure/firestore/index.ts +0 -2
- package/src/shared/presentation/index.ts +0 -1
|
@@ -1,71 +1,64 @@
|
|
|
1
|
+
interface CacheEntry {
|
|
2
|
+
promise: Promise<boolean>;
|
|
3
|
+
resolvedUserId: string | null;
|
|
4
|
+
completed: boolean;
|
|
5
|
+
}
|
|
6
|
+
|
|
1
7
|
export class InitializationCache {
|
|
2
|
-
private
|
|
3
|
-
private cacheKey: string | null = null;
|
|
4
|
-
private promiseCacheKey: string | null = null;
|
|
5
|
-
private resolvedUserId: string | null = null;
|
|
6
|
-
private promiseCompleted = true;
|
|
7
|
-
private pendingQueue: Map<string, Promise<boolean>> = new Map();
|
|
8
|
+
private entries: Map<string, CacheEntry> = new Map();
|
|
8
9
|
|
|
9
10
|
tryAcquireInitialization(cacheKey: string): { shouldInit: boolean; existingPromise: Promise<boolean> | null } {
|
|
10
|
-
const
|
|
11
|
-
if (
|
|
12
|
-
return { shouldInit: false, existingPromise:
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
if (
|
|
16
|
-
this.initPromise &&
|
|
17
|
-
this.cacheKey === cacheKey &&
|
|
18
|
-
this.promiseCompleted &&
|
|
19
|
-
this.promiseCacheKey === cacheKey
|
|
20
|
-
) {
|
|
21
|
-
return { shouldInit: false, existingPromise: this.initPromise };
|
|
11
|
+
const entry = this.entries.get(cacheKey);
|
|
12
|
+
if (entry) {
|
|
13
|
+
return { shouldInit: false, existingPromise: entry.promise };
|
|
22
14
|
}
|
|
23
15
|
|
|
24
16
|
return { shouldInit: true, existingPromise: null };
|
|
25
17
|
}
|
|
26
18
|
|
|
27
19
|
setPromise(promise: Promise<boolean>, cacheKey: string, realUserId: string | null): void {
|
|
28
|
-
|
|
29
|
-
|
|
20
|
+
const entry: CacheEntry = {
|
|
21
|
+
promise: null as any, // Placeholder to be assigned immediately
|
|
22
|
+
resolvedUserId: realUserId,
|
|
23
|
+
completed: false,
|
|
24
|
+
};
|
|
30
25
|
|
|
31
26
|
const chain: Promise<boolean> = promise
|
|
32
27
|
.then((result) => {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
28
|
+
const currentEntry = this.entries.get(cacheKey);
|
|
29
|
+
if (currentEntry === entry) {
|
|
30
|
+
currentEntry.completed = true;
|
|
31
|
+
if (!result) {
|
|
32
|
+
this.entries.delete(cacheKey);
|
|
33
|
+
}
|
|
36
34
|
}
|
|
37
|
-
this.promiseCompleted = true;
|
|
38
35
|
return result;
|
|
39
36
|
})
|
|
40
37
|
.catch((error) => {
|
|
41
|
-
if (this.
|
|
42
|
-
this.
|
|
43
|
-
this.promiseCacheKey = null;
|
|
44
|
-
this.cacheKey = null;
|
|
45
|
-
this.resolvedUserId = null;
|
|
38
|
+
if (this.entries.get(cacheKey) === entry) {
|
|
39
|
+
this.entries.delete(cacheKey);
|
|
46
40
|
}
|
|
47
|
-
this.promiseCompleted = true;
|
|
48
41
|
console.error('[InitializationCache] Initialization failed', { cacheKey, error });
|
|
49
42
|
return false;
|
|
50
|
-
})
|
|
51
|
-
.finally(() => {
|
|
52
|
-
this.pendingQueue.delete(cacheKey);
|
|
53
43
|
});
|
|
54
44
|
|
|
55
|
-
|
|
56
|
-
this.
|
|
45
|
+
entry.promise = chain;
|
|
46
|
+
this.entries.set(cacheKey, entry);
|
|
57
47
|
}
|
|
58
48
|
|
|
59
49
|
getCurrentUserId(): string | null {
|
|
60
|
-
|
|
50
|
+
// Find the first completed entry. This assumes we usually only have one active at a time,
|
|
51
|
+
// but it's much safer than shared state variables.
|
|
52
|
+
// In reality, SubscriptionManager.reset() clears this map on user switch.
|
|
53
|
+
for (const entry of this.entries.values()) {
|
|
54
|
+
if (entry.completed) {
|
|
55
|
+
return entry.resolvedUserId;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
61
59
|
}
|
|
62
60
|
|
|
63
61
|
reset(): void {
|
|
64
|
-
this.
|
|
65
|
-
this.cacheKey = null;
|
|
66
|
-
this.promiseCacheKey = null;
|
|
67
|
-
this.resolvedUserId = null;
|
|
68
|
-
this.promiseCompleted = true;
|
|
69
|
-
this.pendingQueue.clear();
|
|
62
|
+
this.entries.clear();
|
|
70
63
|
}
|
|
71
64
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { CustomerInfo } from "react-native-purchases";
|
|
2
|
-
import type { RevenueCatConfig } from "../../../revenuecat/core/types";
|
|
2
|
+
import type { RevenueCatConfig } from "../../../revenuecat/core/types/RevenueCatConfig";
|
|
3
3
|
import type { PurchaseSource } from "../../core/SubscriptionConstants";
|
|
4
|
-
import type { PackageType } from "../../../revenuecat/core/types";
|
|
5
|
-
import { getPremiumEntitlement } from "../../../revenuecat/core/types";
|
|
4
|
+
import type { PackageType } from "../../../revenuecat/core/types/RevenueCatTypes";
|
|
5
|
+
import { getPremiumEntitlement } from "../../../revenuecat/core/types/RevenueCatTypes";
|
|
6
6
|
|
|
7
7
|
export async function syncPremiumStatus(
|
|
8
8
|
config: RevenueCatConfig,
|
package/src/domains/subscription/presentation/components/sections/SubscriptionSection.types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { StyleProp, ViewStyle } from "react-native";
|
|
2
2
|
import type { SubscriptionStatusType } from "../../../core/SubscriptionConstants";
|
|
3
|
-
import type { CreditInfo } from "../../../core/types";
|
|
3
|
+
import type { CreditInfo } from "../../../core/types/CreditInfo";
|
|
4
4
|
import type { PremiumDetailsTranslations } from "../details/PremiumDetailsCardTypes";
|
|
5
5
|
|
|
6
6
|
export interface SubscriptionSectionConfig {
|
|
@@ -5,7 +5,7 @@ import { AtomicIcon, AtomicText } from "@umituz/react-native-design-system/atoms
|
|
|
5
5
|
import { NavigationHeader } from "@umituz/react-native-design-system/molecules";
|
|
6
6
|
import { useAppDesignTokens } from "@umituz/react-native-design-system/theme";
|
|
7
7
|
import { useAuthStore, selectUserId } from "@umituz/react-native-auth";
|
|
8
|
-
import { ScreenLayout } from "../../../../shared/presentation";
|
|
8
|
+
import { ScreenLayout } from "../../../../shared/presentation/layouts/ScreenLayout";
|
|
9
9
|
import { getCreditsRepository } from "../../../credits/infrastructure/CreditsRepositoryManager";
|
|
10
10
|
import { subscriptionEventBus, SUBSCRIPTION_EVENTS } from "../../../../shared/infrastructure/SubscriptionEventBus";
|
|
11
11
|
import { SubscriptionHeader } from "./components/SubscriptionHeader";
|
|
@@ -7,7 +7,6 @@ interface PurchaseLoadingState {
|
|
|
7
7
|
interface PurchaseLoadingActions {
|
|
8
8
|
startPurchase: (productId: string, source: "manual" | "auto-execution") => void;
|
|
9
9
|
endPurchase: (productId: string) => void;
|
|
10
|
-
isPurchasing: (productId?: string) => boolean;
|
|
11
10
|
reset: () => void;
|
|
12
11
|
}
|
|
13
12
|
|
|
@@ -17,7 +16,7 @@ const createInitialState = (): PurchaseLoadingState => ({
|
|
|
17
16
|
activePurchases: new Map(),
|
|
18
17
|
});
|
|
19
18
|
|
|
20
|
-
export const usePurchaseLoadingStore = create<PurchaseLoadingStore>((set
|
|
19
|
+
export const usePurchaseLoadingStore = create<PurchaseLoadingStore>((set) => ({
|
|
21
20
|
...createInitialState(),
|
|
22
21
|
|
|
23
22
|
startPurchase: (productId, source) => {
|
|
@@ -36,15 +35,19 @@ export const usePurchaseLoadingStore = create<PurchaseLoadingStore>((set, get) =
|
|
|
36
35
|
});
|
|
37
36
|
},
|
|
38
37
|
|
|
39
|
-
isPurchasing: (productId) => {
|
|
40
|
-
const state = get();
|
|
41
|
-
if (productId) return state.activePurchases.has(productId);
|
|
42
|
-
return state.activePurchases.size > 0;
|
|
43
|
-
},
|
|
44
|
-
|
|
45
38
|
reset: () => {
|
|
46
39
|
set(createInitialState());
|
|
47
40
|
},
|
|
48
41
|
}));
|
|
49
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Optimized selector for purchasing state.
|
|
45
|
+
* Use this to avoid re-renders when other parts of the state change.
|
|
46
|
+
*/
|
|
50
47
|
export const selectIsPurchasing = (state: PurchaseLoadingStore) => state.activePurchases.size > 0;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Optimized selector for a specific product's purchasing state.
|
|
51
|
+
*/
|
|
52
|
+
export const selectIsProductPurchasing = (productId: string) => (state: PurchaseLoadingStore) =>
|
|
53
|
+
state.activePurchases.has(productId);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export const DEFAULT_REQUIRED_CREDITS = 1;
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Checks if an action that requires auth-based credits can be executed.
|
|
5
|
+
*/
|
|
6
|
+
export function canExecuteAuthAction(
|
|
7
|
+
isWaitingForAuthCredits: boolean,
|
|
8
|
+
isCreditsLoaded: boolean,
|
|
9
|
+
hasPendingAction: boolean,
|
|
10
|
+
hasSubscription: boolean,
|
|
11
|
+
creditBalance: number,
|
|
12
|
+
requiredCredits: number
|
|
13
|
+
): boolean {
|
|
14
|
+
if (!isWaitingForAuthCredits || !isCreditsLoaded || !hasPendingAction) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
return hasSubscription || creditBalance >= requiredCredits;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Checks if a purchase action can be executed, typically after a purchase flow.
|
|
22
|
+
*/
|
|
23
|
+
export function canExecutePurchaseAction(
|
|
24
|
+
isWaitingForPurchase: boolean,
|
|
25
|
+
creditBalance: number,
|
|
26
|
+
prevBalance: number,
|
|
27
|
+
hasSubscription: boolean,
|
|
28
|
+
prevHasSubscription: boolean,
|
|
29
|
+
hasPendingAction: boolean
|
|
30
|
+
): boolean {
|
|
31
|
+
if (!isWaitingForPurchase || !hasPendingAction) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
const creditsIncreased = creditBalance > prevBalance;
|
|
35
|
+
const subscriptionAcquired = hasSubscription && !prevHasSubscription;
|
|
36
|
+
return creditsIncreased || subscriptionAcquired;
|
|
37
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PackageType } from "../../revenuecat/core/types";
|
|
1
|
+
import type { PackageType } from "../../revenuecat/core/types/RevenueCatTypes";
|
|
2
2
|
|
|
3
3
|
export function formatPackageTypeForDisplay(packageType: PackageType | string | null | undefined): string {
|
|
4
4
|
if (!packageType) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buildCollectionRef, type CollectionConfig } from "../../../../../shared/infrastructure/firestore";
|
|
1
|
+
import { buildCollectionRef, type CollectionConfig } from "../../../../../shared/infrastructure/firestore/collectionUtils";
|
|
2
2
|
import type { Firestore } from "@umituz/react-native-firebase";
|
|
3
3
|
import type { TransactionRepositoryConfig } from "../../../domain/types/transaction.types";
|
|
4
4
|
|
|
@@ -13,7 +13,8 @@ import type {
|
|
|
13
13
|
TransactionResult,
|
|
14
14
|
} from "../../../domain/types/transaction.types";
|
|
15
15
|
import { TransactionMapper } from "../../../domain/mappers/TransactionMapper";
|
|
16
|
-
import { requireFirestore
|
|
16
|
+
import { requireFirestore } from "../../../../../shared/infrastructure/firestore/collectionUtils";
|
|
17
|
+
import { mapErrorToResult } from "../../../../../shared/infrastructure/firestore/resultUtils";
|
|
17
18
|
import { getCollectionRef } from "./CollectionBuilder";
|
|
18
19
|
|
|
19
20
|
export async function fetchTransactions(
|
|
@@ -7,7 +7,8 @@ import type {
|
|
|
7
7
|
TransactionReason,
|
|
8
8
|
} from "../../../domain/types/transaction.types";
|
|
9
9
|
import { TransactionMapper } from "../../../domain/mappers/TransactionMapper";
|
|
10
|
-
import { requireFirestore
|
|
10
|
+
import { requireFirestore } from "../../../../../shared/infrastructure/firestore/collectionUtils";
|
|
11
|
+
import { mapErrorToResult } from "../../../../../shared/infrastructure/firestore/resultUtils";
|
|
11
12
|
import { getCollectionRef } from "./CollectionBuilder";
|
|
12
13
|
|
|
13
14
|
export async function addTransaction(
|
|
@@ -2,7 +2,7 @@ import React from "react";
|
|
|
2
2
|
import { View, StyleSheet, TouchableOpacity } from "react-native";
|
|
3
3
|
import { AtomicText, AtomicIcon, AtomicSpinner } from "@umituz/react-native-design-system/atoms";
|
|
4
4
|
import { useAppDesignTokens } from "@umituz/react-native-design-system/theme";
|
|
5
|
-
import { ScreenLayout } from "../../../../shared/presentation";
|
|
5
|
+
import { ScreenLayout } from "../../../../shared/presentation/layouts/ScreenLayout";
|
|
6
6
|
import { useNavigation } from "@react-navigation/native";
|
|
7
7
|
import { useWallet } from "../hooks/useWallet";
|
|
8
8
|
import { getWalletConfig } from "../../infrastructure/config/walletConfig";
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// Domain Layer - Constants & Types
|
|
2
2
|
export * from "./domains/subscription/core/SubscriptionConstants";
|
|
3
|
-
export type { SubscriptionMetadata
|
|
3
|
+
export type { SubscriptionMetadata } from "./domains/subscription/core/types/SubscriptionMetadata";
|
|
4
|
+
export type { PremiumStatus } from "./domains/subscription/core/types/PremiumStatus";
|
|
5
|
+
export type { CreditInfo } from "./domains/subscription/core/types/CreditInfo";
|
|
4
6
|
export {
|
|
5
7
|
createDefaultSubscriptionStatus,
|
|
6
8
|
isSubscriptionValid,
|
|
@@ -25,7 +27,8 @@ export {
|
|
|
25
27
|
export type { Result, Success, Failure } from "./shared/utils/Result";
|
|
26
28
|
|
|
27
29
|
// Infrastructure Layer (Services & Repositories)
|
|
28
|
-
export { initializeSubscription
|
|
30
|
+
export { initializeSubscription } from "./domains/subscription/application/initializer/SubscriptionInitializer";
|
|
31
|
+
export type { SubscriptionInitConfig, CreditPackageConfig } from "./domains/subscription/application/SubscriptionInitializerTypes";
|
|
29
32
|
|
|
30
33
|
export { CreditsRepository } from "./domains/credits/infrastructure/CreditsRepository";
|
|
31
34
|
export {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { InitModule } from '@umituz/react-native-design-system/init';
|
|
2
|
-
import { initializeSubscription
|
|
2
|
+
import { initializeSubscription } from "../domains/subscription/application/initializer/SubscriptionInitializer";
|
|
3
|
+
import type { SubscriptionInitConfig } from "../domains/subscription/application/SubscriptionInitializerTypes";
|
|
3
4
|
|
|
4
5
|
export interface SubscriptionInitModuleConfig extends Omit<SubscriptionInitConfig, 'apiKey'> {
|
|
5
6
|
getApiKey: () => string | undefined;
|
|
@@ -2,7 +2,7 @@ type EventCallback<T = unknown> = (data: T) => void;
|
|
|
2
2
|
|
|
3
3
|
class SubscriptionEventBus {
|
|
4
4
|
private static instance: SubscriptionEventBus;
|
|
5
|
-
private listeners:
|
|
5
|
+
private listeners: Map<string, Set<EventCallback>> = new Map();
|
|
6
6
|
|
|
7
7
|
private constructor() {}
|
|
8
8
|
|
|
@@ -14,52 +14,58 @@ class SubscriptionEventBus {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
on<T>(event: string, callback: EventCallback<T>): () => void {
|
|
17
|
-
if (!this.listeners
|
|
18
|
-
this.listeners
|
|
17
|
+
if (!this.listeners.has(event)) {
|
|
18
|
+
this.listeners.set(event, new Set());
|
|
19
19
|
}
|
|
20
|
-
|
|
20
|
+
|
|
21
|
+
const eventSet = this.listeners.get(event)!;
|
|
22
|
+
eventSet.add(callback as EventCallback);
|
|
21
23
|
|
|
22
24
|
return () => {
|
|
23
|
-
const listeners = this.listeners
|
|
25
|
+
const listeners = this.listeners.get(event);
|
|
24
26
|
if (listeners) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
delete this.listeners[event];
|
|
27
|
+
listeners.delete(callback as EventCallback);
|
|
28
|
+
if (listeners.size === 0) {
|
|
29
|
+
this.listeners.delete(event);
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
};
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
emit<T>(event: string, data: T): void {
|
|
35
|
-
|
|
36
|
+
const listeners = this.listeners.get(event);
|
|
37
|
+
if (!listeners || listeners.size === 0) return;
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
// Use microtask for async execution to not block main thread
|
|
40
|
+
// but keep it fast.
|
|
41
|
+
listeners.forEach(callback => {
|
|
42
|
+
queueMicrotask(() => {
|
|
39
43
|
try {
|
|
40
44
|
callback(data);
|
|
41
45
|
} catch (error) {
|
|
42
46
|
console.error('[SubscriptionEventBus] Listener error for event:', event, { error });
|
|
43
47
|
}
|
|
44
|
-
}).catch(error => {
|
|
45
|
-
console.error('[SubscriptionEventBus] Async listener error for event:', event, { error });
|
|
46
48
|
});
|
|
47
49
|
});
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
clear(event?: string): void {
|
|
51
53
|
if (event) {
|
|
52
|
-
|
|
54
|
+
this.listeners.delete(event);
|
|
53
55
|
} else {
|
|
54
|
-
this.listeners
|
|
56
|
+
this.listeners.clear();
|
|
55
57
|
}
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
getListenerCount(event?: string): number {
|
|
59
61
|
if (event) {
|
|
60
|
-
return this.listeners
|
|
62
|
+
return this.listeners.get(event)?.size ?? 0;
|
|
61
63
|
}
|
|
62
|
-
|
|
64
|
+
let total = 0;
|
|
65
|
+
this.listeners.forEach(set => {
|
|
66
|
+
total += set.size;
|
|
67
|
+
});
|
|
68
|
+
return total;
|
|
63
69
|
}
|
|
64
70
|
}
|
|
65
71
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./layouts/ScreenLayout";
|