@anker-in/campaign-ui 0.4.6 → 0.5.0-beta.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.
- package/dist/cjs/components/LiveChatWidget/LiveChatWidget.js +1 -1
- package/dist/cjs/components/LiveChatWidget/LiveChatWidget.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/api/chat.d.ts +1 -1
- package/dist/cjs/components/LiveChatWidget/api/chat.js +2 -2
- package/dist/cjs/components/LiveChatWidget/api/chat.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/components/MessageList.js +2 -2
- package/dist/cjs/components/LiveChatWidget/components/MessageList.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/hooks/useChatAPI.js +1 -1
- package/dist/cjs/components/LiveChatWidget/hooks/useChatAPI.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/hooks/useChatState.d.ts +5 -0
- package/dist/cjs/components/LiveChatWidget/hooks/useChatState.js +1 -1
- package/dist/cjs/components/LiveChatWidget/hooks/useChatState.js.map +3 -3
- package/dist/cjs/components/LiveChatWidget/utils/fetcher.d.ts +3 -1
- package/dist/cjs/components/LiveChatWidget/utils/fetcher.js +1 -1
- package/dist/cjs/components/LiveChatWidget/utils/fetcher.js.map +3 -3
- package/dist/cjs/components/index.d.ts +1 -0
- package/dist/cjs/components/index.js +1 -1
- package/dist/cjs/components/index.js.map +2 -2
- package/dist/cjs/components/memberPopup/MemberPopup.d.ts +9 -0
- package/dist/cjs/components/memberPopup/MemberPopup.js +2 -0
- package/dist/cjs/components/memberPopup/MemberPopup.js.map +7 -0
- package/dist/cjs/components/memberPopup/context/provider.d.ts +11 -0
- package/dist/cjs/components/memberPopup/context/provider.js +2 -0
- package/dist/cjs/components/memberPopup/context/provider.js.map +7 -0
- package/dist/cjs/components/memberPopup/context/viewStack.d.ts +12 -0
- package/dist/cjs/components/memberPopup/context/viewStack.js +2 -0
- package/dist/cjs/components/memberPopup/context/viewStack.js.map +7 -0
- package/dist/cjs/components/memberPopup/icons.d.ts +27 -0
- package/dist/cjs/components/memberPopup/icons.js +2 -0
- package/dist/cjs/components/memberPopup/icons.js.map +7 -0
- package/dist/cjs/components/memberPopup/index.d.ts +4 -0
- package/dist/cjs/components/memberPopup/index.js +2 -0
- package/dist/cjs/components/memberPopup/index.js.map +7 -0
- package/dist/cjs/components/memberPopup/panels/Activity/SubscribeModal.d.ts +10 -0
- package/dist/cjs/components/memberPopup/panels/Activity/SubscribeModal.js +2 -0
- package/dist/cjs/components/memberPopup/panels/Activity/SubscribeModal.js.map +7 -0
- package/dist/cjs/components/memberPopup/panels/Activity/hooks/useActivitySubscribe.d.ts +7 -0
- package/dist/cjs/components/memberPopup/panels/Activity/hooks/useActivitySubscribe.js +2 -0
- package/dist/cjs/components/memberPopup/panels/Activity/hooks/useActivitySubscribe.js.map +7 -0
- package/dist/cjs/components/memberPopup/panels/Activity/index.d.ts +7 -0
- package/dist/cjs/components/memberPopup/panels/Activity/index.js +2 -0
- package/dist/cjs/components/memberPopup/panels/Activity/index.js.map +7 -0
- package/dist/cjs/components/memberPopup/panels/ConsumeCredits/RedeemCouponModal.d.ts +19 -0
- package/dist/cjs/components/memberPopup/panels/ConsumeCredits/RedeemCouponModal.js +2 -0
- package/dist/cjs/components/memberPopup/panels/ConsumeCredits/RedeemCouponModal.js.map +7 -0
- package/dist/cjs/components/memberPopup/panels/ConsumeCredits/RedeemCouponPanel.d.ts +20 -0
- package/dist/cjs/components/memberPopup/panels/ConsumeCredits/RedeemCouponPanel.js +2 -0
- package/dist/cjs/components/memberPopup/panels/ConsumeCredits/RedeemCouponPanel.js.map +7 -0
- package/dist/cjs/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemAndBuy.d.ts +12 -0
- package/dist/cjs/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemAndBuy.js +2 -0
- package/dist/cjs/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemAndBuy.js.map +7 -0
- package/dist/cjs/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemCoupon.d.ts +17 -0
- package/dist/cjs/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemCoupon.js +2 -0
- package/dist/cjs/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemCoupon.js.map +7 -0
- package/dist/cjs/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemableList.d.ts +8 -0
- package/dist/cjs/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemableList.js +2 -0
- package/dist/cjs/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemableList.js.map +7 -0
- package/dist/cjs/components/memberPopup/panels/ConsumeCredits/index.d.ts +7 -0
- package/dist/cjs/components/memberPopup/panels/ConsumeCredits/index.js +2 -0
- package/dist/cjs/components/memberPopup/panels/ConsumeCredits/index.js.map +7 -0
- package/dist/cjs/components/memberPopup/panels/EarnCredits/ActivatePanel.d.ts +8 -0
- package/dist/cjs/components/memberPopup/panels/EarnCredits/ActivatePanel.js +2 -0
- package/dist/cjs/components/memberPopup/panels/EarnCredits/ActivatePanel.js.map +7 -0
- package/dist/cjs/components/memberPopup/panels/EarnCredits/CompleteProfileModal.d.ts +10 -0
- package/dist/cjs/components/memberPopup/panels/EarnCredits/CompleteProfileModal.js +2 -0
- package/dist/cjs/components/memberPopup/panels/EarnCredits/CompleteProfileModal.js.map +7 -0
- package/dist/cjs/components/memberPopup/panels/EarnCredits/CompleteProfilePanel.d.ts +9 -0
- package/dist/cjs/components/memberPopup/panels/EarnCredits/CompleteProfilePanel.js +2 -0
- package/dist/cjs/components/memberPopup/panels/EarnCredits/CompleteProfilePanel.js.map +7 -0
- package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribeEmailPanel.d.ts +9 -0
- package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribeEmailPanel.js +2 -0
- package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribeEmailPanel.js.map +7 -0
- package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribeModal.d.ts +10 -0
- package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribeModal.js +2 -0
- package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribeModal.js.map +7 -0
- package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribePanel.d.ts +9 -0
- package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribePanel.js +2 -0
- package/dist/cjs/components/memberPopup/panels/EarnCredits/SubscribePanel.js.map +7 -0
- package/dist/cjs/components/memberPopup/panels/EarnCredits/hooks/useActivities.d.ts +18 -0
- package/dist/cjs/components/memberPopup/panels/EarnCredits/hooks/useActivities.js +2 -0
- package/dist/cjs/components/memberPopup/panels/EarnCredits/hooks/useActivities.js.map +7 -0
- package/dist/cjs/components/memberPopup/panels/EarnCredits/hooks/useSubscribed.d.ts +9 -0
- package/dist/cjs/components/memberPopup/panels/EarnCredits/hooks/useSubscribed.js +2 -0
- package/dist/cjs/components/memberPopup/panels/EarnCredits/hooks/useSubscribed.js.map +7 -0
- package/dist/cjs/components/memberPopup/panels/EarnCredits/hooks/useUpdateProfile.d.ts +17 -0
- package/dist/cjs/components/memberPopup/panels/EarnCredits/hooks/useUpdateProfile.js +2 -0
- package/dist/cjs/components/memberPopup/panels/EarnCredits/hooks/useUpdateProfile.js.map +7 -0
- package/dist/cjs/components/memberPopup/panels/EarnCredits/index.d.ts +13 -0
- package/dist/cjs/components/memberPopup/panels/EarnCredits/index.js +2 -0
- package/dist/cjs/components/memberPopup/panels/EarnCredits/index.js.map +7 -0
- package/dist/cjs/components/memberPopup/panels/Referral/hooks/useReferralDashboard.d.ts +13 -0
- package/dist/cjs/components/memberPopup/panels/Referral/hooks/useReferralDashboard.js +2 -0
- package/dist/cjs/components/memberPopup/panels/Referral/hooks/useReferralDashboard.js.map +7 -0
- package/dist/cjs/components/memberPopup/panels/Referral/hooks/useReferralOverview.d.ts +12 -0
- package/dist/cjs/components/memberPopup/panels/Referral/hooks/useReferralOverview.js +2 -0
- package/dist/cjs/components/memberPopup/panels/Referral/hooks/useReferralOverview.js.map +7 -0
- package/dist/cjs/components/memberPopup/panels/Referral/index.d.ts +7 -0
- package/dist/cjs/components/memberPopup/panels/Referral/index.js +2 -0
- package/dist/cjs/components/memberPopup/panels/Referral/index.js.map +7 -0
- package/dist/cjs/components/memberPopup/type.d.ts +201 -0
- package/dist/cjs/components/memberPopup/type.js +2 -0
- package/dist/cjs/components/memberPopup/type.js.map +7 -0
- package/dist/esm/components/LiveChatWidget/LiveChatWidget.js +1 -1
- package/dist/esm/components/LiveChatWidget/LiveChatWidget.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/api/chat.d.ts +1 -1
- package/dist/esm/components/LiveChatWidget/api/chat.js +2 -2
- package/dist/esm/components/LiveChatWidget/api/chat.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/components/MessageList.js +2 -2
- package/dist/esm/components/LiveChatWidget/components/MessageList.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/hooks/useChatAPI.js +1 -1
- package/dist/esm/components/LiveChatWidget/hooks/useChatAPI.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/hooks/useChatState.d.ts +5 -0
- package/dist/esm/components/LiveChatWidget/hooks/useChatState.js +1 -1
- package/dist/esm/components/LiveChatWidget/hooks/useChatState.js.map +3 -3
- package/dist/esm/components/LiveChatWidget/utils/fetcher.d.ts +3 -1
- package/dist/esm/components/LiveChatWidget/utils/fetcher.js +1 -1
- package/dist/esm/components/LiveChatWidget/utils/fetcher.js.map +3 -3
- package/dist/esm/components/index.d.ts +1 -0
- package/dist/esm/components/index.js +1 -1
- package/dist/esm/components/index.js.map +2 -2
- package/dist/esm/components/memberPopup/MemberPopup.d.ts +9 -0
- package/dist/esm/components/memberPopup/MemberPopup.js +2 -0
- package/dist/esm/components/memberPopup/MemberPopup.js.map +7 -0
- package/dist/esm/components/memberPopup/context/provider.d.ts +11 -0
- package/dist/esm/components/memberPopup/context/provider.js +2 -0
- package/dist/esm/components/memberPopup/context/provider.js.map +7 -0
- package/dist/esm/components/memberPopup/context/viewStack.d.ts +12 -0
- package/dist/esm/components/memberPopup/context/viewStack.js +2 -0
- package/dist/esm/components/memberPopup/context/viewStack.js.map +7 -0
- package/dist/esm/components/memberPopup/icons.d.ts +27 -0
- package/dist/esm/components/memberPopup/icons.js +2 -0
- package/dist/esm/components/memberPopup/icons.js.map +7 -0
- package/dist/esm/components/memberPopup/index.d.ts +4 -0
- package/dist/esm/components/memberPopup/index.js +2 -0
- package/dist/esm/components/memberPopup/index.js.map +7 -0
- package/dist/esm/components/memberPopup/panels/Activity/SubscribeModal.d.ts +10 -0
- package/dist/esm/components/memberPopup/panels/Activity/SubscribeModal.js +2 -0
- package/dist/esm/components/memberPopup/panels/Activity/SubscribeModal.js.map +7 -0
- package/dist/esm/components/memberPopup/panels/Activity/hooks/useActivitySubscribe.d.ts +7 -0
- package/dist/esm/components/memberPopup/panels/Activity/hooks/useActivitySubscribe.js +2 -0
- package/dist/esm/components/memberPopup/panels/Activity/hooks/useActivitySubscribe.js.map +7 -0
- package/dist/esm/components/memberPopup/panels/Activity/index.d.ts +7 -0
- package/dist/esm/components/memberPopup/panels/Activity/index.js +2 -0
- package/dist/esm/components/memberPopup/panels/Activity/index.js.map +7 -0
- package/dist/esm/components/memberPopup/panels/ConsumeCredits/RedeemCouponModal.d.ts +19 -0
- package/dist/esm/components/memberPopup/panels/ConsumeCredits/RedeemCouponModal.js +2 -0
- package/dist/esm/components/memberPopup/panels/ConsumeCredits/RedeemCouponModal.js.map +7 -0
- package/dist/esm/components/memberPopup/panels/ConsumeCredits/RedeemCouponPanel.d.ts +20 -0
- package/dist/esm/components/memberPopup/panels/ConsumeCredits/RedeemCouponPanel.js +2 -0
- package/dist/esm/components/memberPopup/panels/ConsumeCredits/RedeemCouponPanel.js.map +7 -0
- package/dist/esm/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemAndBuy.d.ts +12 -0
- package/dist/esm/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemAndBuy.js +2 -0
- package/dist/esm/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemAndBuy.js.map +7 -0
- package/dist/esm/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemCoupon.d.ts +17 -0
- package/dist/esm/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemCoupon.js +2 -0
- package/dist/esm/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemCoupon.js.map +7 -0
- package/dist/esm/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemableList.d.ts +8 -0
- package/dist/esm/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemableList.js +2 -0
- package/dist/esm/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemableList.js.map +7 -0
- package/dist/esm/components/memberPopup/panels/ConsumeCredits/index.d.ts +7 -0
- package/dist/esm/components/memberPopup/panels/ConsumeCredits/index.js +2 -0
- package/dist/esm/components/memberPopup/panels/ConsumeCredits/index.js.map +7 -0
- package/dist/esm/components/memberPopup/panels/EarnCredits/ActivatePanel.d.ts +8 -0
- package/dist/esm/components/memberPopup/panels/EarnCredits/ActivatePanel.js +2 -0
- package/dist/esm/components/memberPopup/panels/EarnCredits/ActivatePanel.js.map +7 -0
- package/dist/esm/components/memberPopup/panels/EarnCredits/CompleteProfileModal.d.ts +10 -0
- package/dist/esm/components/memberPopup/panels/EarnCredits/CompleteProfileModal.js +2 -0
- package/dist/esm/components/memberPopup/panels/EarnCredits/CompleteProfileModal.js.map +7 -0
- package/dist/esm/components/memberPopup/panels/EarnCredits/CompleteProfilePanel.d.ts +9 -0
- package/dist/esm/components/memberPopup/panels/EarnCredits/CompleteProfilePanel.js +2 -0
- package/dist/esm/components/memberPopup/panels/EarnCredits/CompleteProfilePanel.js.map +7 -0
- package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribeEmailPanel.d.ts +9 -0
- package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribeEmailPanel.js +2 -0
- package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribeEmailPanel.js.map +7 -0
- package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribeModal.d.ts +10 -0
- package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribeModal.js +2 -0
- package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribeModal.js.map +7 -0
- package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribePanel.d.ts +9 -0
- package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribePanel.js +2 -0
- package/dist/esm/components/memberPopup/panels/EarnCredits/SubscribePanel.js.map +7 -0
- package/dist/esm/components/memberPopup/panels/EarnCredits/hooks/useActivities.d.ts +18 -0
- package/dist/esm/components/memberPopup/panels/EarnCredits/hooks/useActivities.js +2 -0
- package/dist/esm/components/memberPopup/panels/EarnCredits/hooks/useActivities.js.map +7 -0
- package/dist/esm/components/memberPopup/panels/EarnCredits/hooks/useSubscribed.d.ts +9 -0
- package/dist/esm/components/memberPopup/panels/EarnCredits/hooks/useSubscribed.js +2 -0
- package/dist/esm/components/memberPopup/panels/EarnCredits/hooks/useSubscribed.js.map +7 -0
- package/dist/esm/components/memberPopup/panels/EarnCredits/hooks/useUpdateProfile.d.ts +17 -0
- package/dist/esm/components/memberPopup/panels/EarnCredits/hooks/useUpdateProfile.js +2 -0
- package/dist/esm/components/memberPopup/panels/EarnCredits/hooks/useUpdateProfile.js.map +7 -0
- package/dist/esm/components/memberPopup/panels/EarnCredits/index.d.ts +13 -0
- package/dist/esm/components/memberPopup/panels/EarnCredits/index.js +2 -0
- package/dist/esm/components/memberPopup/panels/EarnCredits/index.js.map +7 -0
- package/dist/esm/components/memberPopup/panels/Referral/hooks/useReferralDashboard.d.ts +13 -0
- package/dist/esm/components/memberPopup/panels/Referral/hooks/useReferralDashboard.js +2 -0
- package/dist/esm/components/memberPopup/panels/Referral/hooks/useReferralDashboard.js.map +7 -0
- package/dist/esm/components/memberPopup/panels/Referral/hooks/useReferralOverview.d.ts +12 -0
- package/dist/esm/components/memberPopup/panels/Referral/hooks/useReferralOverview.js +2 -0
- package/dist/esm/components/memberPopup/panels/Referral/hooks/useReferralOverview.js.map +7 -0
- package/dist/esm/components/memberPopup/panels/Referral/index.d.ts +7 -0
- package/dist/esm/components/memberPopup/panels/Referral/index.js +2 -0
- package/dist/esm/components/memberPopup/panels/Referral/index.js.map +7 -0
- package/dist/esm/components/memberPopup/type.d.ts +201 -0
- package/dist/esm/components/memberPopup/type.js +2 -0
- package/dist/esm/components/memberPopup/type.js.map +7 -0
- package/dist/index.d.mts +1775 -0
- package/dist/index.d.ts +1775 -0
- package/dist/index.js +18542 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +18514 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +1 -1
- package/src/components/LiveChatWidget/LiveChatWidget.tsx +6 -4
- package/src/components/LiveChatWidget/api/chat.ts +3 -2
- package/src/components/LiveChatWidget/components/MessageList.tsx +20 -4
- package/src/components/LiveChatWidget/hooks/useChatAPI.ts +2 -3
- package/src/components/LiveChatWidget/hooks/useChatState.ts +27 -7
- package/src/components/LiveChatWidget/utils/fetcher.ts +15 -2
- package/src/components/index.ts +2 -0
- package/src/components/memberPopup/MemberPopup.tsx +283 -0
- package/src/components/memberPopup/context/provider.tsx +52 -0
- package/src/components/memberPopup/context/viewStack.tsx +23 -0
- package/src/components/memberPopup/icons.tsx +147 -0
- package/src/components/memberPopup/index.ts +24 -0
- package/src/components/memberPopup/panels/ConsumeCredits/RedeemCouponModal.tsx +150 -0
- package/src/components/memberPopup/panels/ConsumeCredits/RedeemCouponPanel.tsx +142 -0
- package/src/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemAndBuy.ts +33 -0
- package/src/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemCoupon.ts +38 -0
- package/src/components/memberPopup/panels/ConsumeCredits/hooks/useRedeemableList.ts +55 -0
- package/src/components/memberPopup/panels/ConsumeCredits/index.tsx +220 -0
- package/src/components/memberPopup/panels/EarnCredits/ActivatePanel.tsx +323 -0
- package/src/components/memberPopup/panels/EarnCredits/CompleteProfileModal.tsx +186 -0
- package/src/components/memberPopup/panels/EarnCredits/CompleteProfilePanel.tsx +179 -0
- package/src/components/memberPopup/panels/EarnCredits/SubscribeEmailPanel.tsx +130 -0
- package/src/components/memberPopup/panels/EarnCredits/SubscribeModal.tsx +144 -0
- package/src/components/memberPopup/panels/EarnCredits/SubscribePanel.tsx +147 -0
- package/src/components/memberPopup/panels/EarnCredits/hooks/useActivities.ts +64 -0
- package/src/components/memberPopup/panels/EarnCredits/hooks/useSubscribed.ts +32 -0
- package/src/components/memberPopup/panels/EarnCredits/hooks/useUpdateProfile.ts +50 -0
- package/src/components/memberPopup/panels/EarnCredits/index.tsx +215 -0
- package/src/components/memberPopup/panels/Referral/hooks/useReferralOverview.ts +41 -0
- package/src/components/memberPopup/panels/Referral/index.tsx +108 -0
- package/src/components/memberPopup/type.ts +213 -0
- package/tailwind.config.ts +4 -0
- package/dist/cjs/stories/CartCard.stories.d.ts +0 -33
- package/dist/cjs/stories/CartCard.stories.js +0 -21
- package/dist/cjs/stories/CartCard.stories.js.map +0 -7
- package/dist/esm/stories/CartCard.stories.d.ts +0 -33
- package/dist/esm/stories/CartCard.stories.js +0 -21
- package/dist/esm/stories/CartCard.stories.js.map +0 -7
package/package.json
CHANGED
|
@@ -182,7 +182,7 @@ export const LiveChatWidget: React.FC<LiveChatWidgetProps> = ({
|
|
|
182
182
|
addMessage,
|
|
183
183
|
setMessages,
|
|
184
184
|
clearMessages,
|
|
185
|
-
|
|
185
|
+
startStream,
|
|
186
186
|
saveSession,
|
|
187
187
|
clearSession,
|
|
188
188
|
} = chatState
|
|
@@ -743,10 +743,12 @@ export const LiveChatWidget: React.FC<LiveChatWidgetProps> = ({
|
|
|
743
743
|
},
|
|
744
744
|
}
|
|
745
745
|
|
|
746
|
+
// 创建绑定了当前 streamId 的事件处理器,防止并发 stream 污染共享 buffer
|
|
747
|
+
const streamEventHandler = startStream()
|
|
748
|
+
|
|
746
749
|
// 发送消息到后端
|
|
747
750
|
await sendMessageStream(requestPayload, event => {
|
|
748
|
-
|
|
749
|
-
handleSSEEvent(event)
|
|
751
|
+
streamEventHandler(event)
|
|
750
752
|
|
|
751
753
|
// 特殊处理:会话过期(error 事件且 type 为 validation_error)
|
|
752
754
|
if (event.event === 'error' && event.data.type === 'validation_error') {
|
|
@@ -838,7 +840,7 @@ export const LiveChatWidget: React.FC<LiveChatWidgetProps> = ({
|
|
|
838
840
|
setMessages,
|
|
839
841
|
createSession,
|
|
840
842
|
sendMessageStream,
|
|
841
|
-
|
|
843
|
+
startStream,
|
|
842
844
|
saveSession,
|
|
843
845
|
clearSession,
|
|
844
846
|
onMessageSend,
|
|
@@ -42,9 +42,9 @@ export async function sendMessage(
|
|
|
42
42
|
request: ChatStreamRequest,
|
|
43
43
|
onEvent: (event: SSEEvent) => void,
|
|
44
44
|
customHeaders?: Record<string, string>,
|
|
45
|
-
recaptchaConfig?: RecaptchaConfig
|
|
45
|
+
recaptchaConfig?: RecaptchaConfig,
|
|
46
|
+
signal?: AbortSignal
|
|
46
47
|
): Promise<void> {
|
|
47
|
-
// 使用 fetcher 处理 reCAPTCHA 和自定义 headers
|
|
48
48
|
const response = await fetcher({
|
|
49
49
|
url: `${apiBaseUrl}/api/chat/stream`,
|
|
50
50
|
method: 'POST',
|
|
@@ -56,6 +56,7 @@ export async function sendMessage(
|
|
|
56
56
|
needRecaptcha: recaptchaConfig?.needRecaptcha,
|
|
57
57
|
recaptchaSitekey: recaptchaConfig?.recaptchaSitekey,
|
|
58
58
|
recaptchaAction: recaptchaConfig?.recaptchaAction || 'send_message',
|
|
59
|
+
signal,
|
|
59
60
|
})
|
|
60
61
|
|
|
61
62
|
if (!response.ok) {
|
|
@@ -124,6 +124,8 @@ export const MessageList: React.FC<MessageListProps> = ({
|
|
|
124
124
|
}, [])
|
|
125
125
|
const [showScrollButton, setShowScrollButton] = useState(false)
|
|
126
126
|
|
|
127
|
+
const [showEmpty, setShowEmpty] = useState(false)
|
|
128
|
+
|
|
127
129
|
// 检查是否接近底部
|
|
128
130
|
const isNearBottom = useCallback(
|
|
129
131
|
(threshold = 100) => {
|
|
@@ -187,17 +189,31 @@ export const MessageList: React.FC<MessageListProps> = ({
|
|
|
187
189
|
return () => clearTimeout(timeoutId)
|
|
188
190
|
}, [messages, autoScroll, listElement])
|
|
189
191
|
|
|
190
|
-
|
|
192
|
+
useEffect(() => {
|
|
193
|
+
if (!isLoadingHistory && messages.length === 0) {
|
|
194
|
+
const timer = setTimeout(() => setShowEmpty(true), 150)
|
|
195
|
+
return () => clearTimeout(timer)
|
|
196
|
+
} else {
|
|
197
|
+
setShowEmpty(false)
|
|
198
|
+
}
|
|
199
|
+
}, [isLoadingHistory, messages.length])
|
|
200
|
+
|
|
201
|
+
if (isLoadingHistory) {
|
|
202
|
+
return (
|
|
203
|
+
<div className={`flex flex-1 items-center justify-center overflow-hidden ${className}`}>
|
|
204
|
+
<LoadingIndicator />
|
|
205
|
+
</div>
|
|
206
|
+
)
|
|
207
|
+
}
|
|
208
|
+
|
|
191
209
|
if (messages.length === 0) {
|
|
192
|
-
if (
|
|
193
|
-
// 加载中,显示 loading
|
|
210
|
+
if (!showEmpty) {
|
|
194
211
|
return (
|
|
195
212
|
<div className={`flex flex-1 items-center justify-center overflow-hidden ${className}`}>
|
|
196
213
|
<LoadingIndicator />
|
|
197
214
|
</div>
|
|
198
215
|
)
|
|
199
216
|
}
|
|
200
|
-
// 空状态
|
|
201
217
|
return (
|
|
202
218
|
<div className={`flex-1 overflow-hidden ${className}`}>{emptyPlaceholder || <DefaultEmptyPlaceholder />}</div>
|
|
203
219
|
)
|
|
@@ -82,17 +82,16 @@ export function useChatAPI(options: UseChatAPIOptions): UseChatAPIReturn {
|
|
|
82
82
|
*/
|
|
83
83
|
const sendMessageStream = useCallback(
|
|
84
84
|
async (request: ChatStreamRequest, onEvent: (event: SSEEvent) => void) => {
|
|
85
|
-
// 如果正在发送,先中断之前的请求
|
|
86
85
|
if (abortControllerRef.current) {
|
|
87
86
|
abortControllerRef.current.abort()
|
|
88
87
|
}
|
|
89
88
|
|
|
90
|
-
// 创建新的 AbortController
|
|
91
89
|
abortControllerRef.current = new AbortController()
|
|
92
90
|
isSendingRef.current = true
|
|
91
|
+
const { signal } = abortControllerRef.current
|
|
93
92
|
|
|
94
93
|
try {
|
|
95
|
-
await sendMessage(apiBaseUrl, request, onEvent, headers, recaptchaConfig)
|
|
94
|
+
await sendMessage(apiBaseUrl, request, onEvent, headers, recaptchaConfig, signal)
|
|
96
95
|
} catch (error) {
|
|
97
96
|
// 忽略手动中断的错误
|
|
98
97
|
if (error instanceof Error && error.name === 'AbortError') {
|
|
@@ -461,6 +461,12 @@ export interface UseChatStateReturn {
|
|
|
461
461
|
*/
|
|
462
462
|
handleSSEEvent: (event: SSEEvent) => void
|
|
463
463
|
|
|
464
|
+
/**
|
|
465
|
+
* 开始新的 stream 会话,返回绑定了当前 streamId 的事件处理器。
|
|
466
|
+
* 调用此方法会使之前所有 stream 的事件处理器失效,防止并发 stream 污染共享 buffer。
|
|
467
|
+
*/
|
|
468
|
+
startStream: () => (event: SSEEvent) => void
|
|
469
|
+
|
|
464
470
|
/**
|
|
465
471
|
* 保存会话 ID
|
|
466
472
|
*/
|
|
@@ -560,6 +566,9 @@ export function useChatState(options: UseChatStateOptions = {}): UseChatStateRet
|
|
|
560
566
|
// 卡片缓存队列,用于存储需要延迟显示的卡片(在文本完成后显示)
|
|
561
567
|
const pendingCardsRef = useRef<MessageContent[]>([])
|
|
562
568
|
|
|
569
|
+
// 当前活跃 stream 的唯一 ID,用于丢弃过期 stream 的事件,防止并发 stream 共享 buffer
|
|
570
|
+
const activeStreamIdRef = useRef<string | null>(null)
|
|
571
|
+
|
|
563
572
|
/**
|
|
564
573
|
* 打开聊天窗口
|
|
565
574
|
*/
|
|
@@ -641,21 +650,20 @@ export function useChatState(options: UseChatStateOptions = {}): UseChatStateRet
|
|
|
641
650
|
* 根据事件类型进行不同的处理
|
|
642
651
|
*/
|
|
643
652
|
const handleSSEEvent = useCallback(
|
|
644
|
-
(event: SSEEvent) => {
|
|
653
|
+
(event: SSEEvent, streamId?: string) => {
|
|
645
654
|
const { event: eventType, data } = event
|
|
646
655
|
|
|
656
|
+
// 并发 stream 防护:除 message_start 外的所有事件必须匹配当前活跃 streamId
|
|
657
|
+
if (eventType !== 'message_start' && streamId !== activeStreamIdRef.current) {
|
|
658
|
+
return
|
|
659
|
+
}
|
|
660
|
+
|
|
647
661
|
switch (eventType) {
|
|
648
662
|
case 'message_start': {
|
|
649
|
-
// 开始接收新消息
|
|
650
663
|
setIsStreaming(true)
|
|
651
664
|
|
|
652
|
-
// 重置文本消息回调标记
|
|
653
665
|
textMessageCallbackTriggeredRef.current = false
|
|
654
|
-
|
|
655
|
-
// 重置文本缓冲区
|
|
656
666
|
textBufferRef.current = ''
|
|
657
|
-
|
|
658
|
-
// 重置卡片缓存队列
|
|
659
667
|
pendingCardsRef.current = []
|
|
660
668
|
|
|
661
669
|
// T039: 保存 sessionId 和 userId(如果后端返回)
|
|
@@ -1088,6 +1096,17 @@ export function useChatState(options: UseChatStateOptions = {}): UseChatStateRet
|
|
|
1088
1096
|
)
|
|
1089
1097
|
|
|
1090
1098
|
|
|
1099
|
+
const startStream = useCallback((): ((event: SSEEvent) => void) => {
|
|
1100
|
+
const streamId = `stream-${Date.now()}-${Math.random().toString(36).slice(2)}`
|
|
1101
|
+
activeStreamIdRef.current = streamId
|
|
1102
|
+
textBufferRef.current = ''
|
|
1103
|
+
pendingCardsRef.current = []
|
|
1104
|
+
productMapRef.current.clear()
|
|
1105
|
+
rawProductMapRef.current.clear()
|
|
1106
|
+
currentMessageRef.current = null
|
|
1107
|
+
return (event: SSEEvent) => handleSSEEvent(event, streamId)
|
|
1108
|
+
}, [handleSSEEvent])
|
|
1109
|
+
|
|
1091
1110
|
return {
|
|
1092
1111
|
messages,
|
|
1093
1112
|
isOpen,
|
|
@@ -1104,6 +1123,7 @@ export function useChatState(options: UseChatStateOptions = {}): UseChatStateRet
|
|
|
1104
1123
|
setMessages,
|
|
1105
1124
|
clearMessages,
|
|
1106
1125
|
handleSSEEvent,
|
|
1126
|
+
startStream,
|
|
1107
1127
|
saveSession,
|
|
1108
1128
|
clearSession,
|
|
1109
1129
|
}
|
|
@@ -60,6 +60,8 @@ export interface FetcherOptions {
|
|
|
60
60
|
recaptchaSitekey?: string
|
|
61
61
|
recaptchaAction?: string
|
|
62
62
|
recaptchaHeaderKey?: string
|
|
63
|
+
/** 外部传入的 AbortSignal,用于提前中止请求 */
|
|
64
|
+
signal?: AbortSignal
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
/**
|
|
@@ -76,8 +78,8 @@ export const fetcher = async ({
|
|
|
76
78
|
recaptchaSitekey,
|
|
77
79
|
recaptchaAction = '',
|
|
78
80
|
recaptchaHeaderKey = 'X-Recaptcha-Token',
|
|
81
|
+
signal: externalSignal,
|
|
79
82
|
}: FetcherOptions): Promise<Response> => {
|
|
80
|
-
// 获取 reCAPTCHA headers(如果需要)
|
|
81
83
|
let recaptchaHeaders: Record<string, string> = {}
|
|
82
84
|
if (needRecaptcha) {
|
|
83
85
|
if (!recaptchaSitekey) {
|
|
@@ -87,14 +89,23 @@ export const fetcher = async ({
|
|
|
87
89
|
}
|
|
88
90
|
}
|
|
89
91
|
|
|
90
|
-
// 准备请求体
|
|
91
92
|
const bodyData = body ? JSON.stringify(body) : undefined
|
|
92
93
|
|
|
94
|
+
// 合并外部 signal(用于手动中止)和超时 signal
|
|
93
95
|
const controller = new AbortController()
|
|
94
96
|
let timeoutTimer: NodeJS.Timeout | undefined
|
|
95
97
|
if (timeout) {
|
|
96
98
|
timeoutTimer = setTimeout(() => controller.abort(), timeout)
|
|
97
99
|
}
|
|
100
|
+
// 如果外部 signal 已中止或在请求期间中止,同步到内部 controller
|
|
101
|
+
const onExternalAbort = () => controller.abort()
|
|
102
|
+
if (externalSignal) {
|
|
103
|
+
if (externalSignal.aborted) {
|
|
104
|
+
controller.abort()
|
|
105
|
+
} else {
|
|
106
|
+
externalSignal.addEventListener('abort', onExternalAbort, { once: true })
|
|
107
|
+
}
|
|
108
|
+
}
|
|
98
109
|
|
|
99
110
|
try {
|
|
100
111
|
const response = await fetch(url, {
|
|
@@ -119,6 +130,8 @@ export const fetcher = async ({
|
|
|
119
130
|
clearTimeout(timeoutTimer)
|
|
120
131
|
}
|
|
121
132
|
throw error
|
|
133
|
+
} finally {
|
|
134
|
+
externalSignal?.removeEventListener('abort', onExternalAbort)
|
|
122
135
|
}
|
|
123
136
|
}
|
|
124
137
|
|
package/src/components/index.ts
CHANGED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { lazy, Suspense, useState, useCallback } from 'react'
|
|
4
|
+
import { Picture } from '@anker-in/headless-ui'
|
|
5
|
+
import { type HeadlessConfig, useHeadlessContext, classNames as cn } from '@anker-in/lib'
|
|
6
|
+
import {
|
|
7
|
+
MemberPopupActionType,
|
|
8
|
+
MemberPopupPanelType,
|
|
9
|
+
type MemberPopupCopy,
|
|
10
|
+
type MemberPopupContextValue,
|
|
11
|
+
type MemberPopupItem,
|
|
12
|
+
} from './type'
|
|
13
|
+
import { MemberPopupProvider } from './context/provider'
|
|
14
|
+
import { useMemberPopupContext } from './context/provider'
|
|
15
|
+
import { ViewStackContext, type ViewPayload } from './context/viewStack'
|
|
16
|
+
import { IconClose, IconArrow, IconCredit, IconBag, IconReferral, IconSwitch, IconSubscription } from './icons'
|
|
17
|
+
import { ROUNDED_BRANDS } from '../../constants'
|
|
18
|
+
|
|
19
|
+
const EarnCredits = lazy(() => import('./panels/EarnCredits'))
|
|
20
|
+
const ConsumeCredits = lazy(() => import('./panels/ConsumeCredits'))
|
|
21
|
+
const Referral = lazy(() => import('./panels/Referral'))
|
|
22
|
+
const CompleteProfile = lazy(() => import('./panels/EarnCredits/CompleteProfilePanel'))
|
|
23
|
+
const Subscribe = lazy(() => import('./panels/EarnCredits/SubscribePanel'))
|
|
24
|
+
const SubscribeEmail = lazy(() => import('./panels/EarnCredits/SubscribeEmailPanel'))
|
|
25
|
+
const Activate = lazy(() => import('./panels/EarnCredits/ActivatePanel'))
|
|
26
|
+
const RedeemCoupon = lazy(() => import('./panels/ConsumeCredits/RedeemCouponPanel'))
|
|
27
|
+
|
|
28
|
+
type MemberPopupProps = MemberPopupContextValue & {
|
|
29
|
+
headlessConfig: HeadlessConfig
|
|
30
|
+
copy: MemberPopupCopy
|
|
31
|
+
bottomOffset?: string
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function MemberPopup({ copy, bottomOffset = '24px', ...providerProps }: MemberPopupProps) {
|
|
35
|
+
return (
|
|
36
|
+
<MemberPopupProvider {...providerProps} earnCreditsCopy={copy.earnCredits}>
|
|
37
|
+
<MemberPopupContent copy={copy} bottomOffset={bottomOffset} />
|
|
38
|
+
</MemberPopupProvider>
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
type MemberPopupContentProps = {
|
|
43
|
+
copy: MemberPopupCopy
|
|
44
|
+
bottomOffset: string
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function MemberPopupContent({ copy, bottomOffset }: MemberPopupContentProps) {
|
|
48
|
+
const { profile, openSignInPopup, openSignUpPopup } = useMemberPopupContext()
|
|
49
|
+
const { brand, storeDomain } = useHeadlessContext()
|
|
50
|
+
const rounded = ROUNDED_BRANDS.includes(brand)
|
|
51
|
+
const isLogin = !!profile
|
|
52
|
+
const [isOpen, setIsOpen] = useState(false)
|
|
53
|
+
const [viewStack, setViewStack] = useState<ViewPayload[]>([])
|
|
54
|
+
const [isSubscribeSuccess, setIsSubscribeSuccess] = useState(false)
|
|
55
|
+
const [isActivateSuccess, setIsActivateSuccess] = useState(false)
|
|
56
|
+
const [isProfileCompleteSuccess, setIsProfileCompleteSuccess] = useState(false)
|
|
57
|
+
|
|
58
|
+
const currentView = viewStack[viewStack.length - 1] ?? null
|
|
59
|
+
|
|
60
|
+
const pushView = useCallback((view: ViewPayload) => {
|
|
61
|
+
setViewStack(prev => [...prev, view])
|
|
62
|
+
}, [])
|
|
63
|
+
|
|
64
|
+
const popView = useCallback(() => {
|
|
65
|
+
setViewStack(prev => prev.slice(0, -1))
|
|
66
|
+
}, [])
|
|
67
|
+
|
|
68
|
+
function handleAction(type: MemberPopupActionType, url?: string) {
|
|
69
|
+
switch (type) {
|
|
70
|
+
case MemberPopupActionType.ExternalLink:
|
|
71
|
+
if (url) window.open(url, '_blank', 'noopener,noreferrer')
|
|
72
|
+
break
|
|
73
|
+
case MemberPopupActionType.OpenEarnCredits:
|
|
74
|
+
pushView({ type: MemberPopupPanelType.EarnCredits })
|
|
75
|
+
break
|
|
76
|
+
case MemberPopupActionType.OpenConsumeCredits:
|
|
77
|
+
pushView({ type: MemberPopupPanelType.ConsumeCredits })
|
|
78
|
+
break
|
|
79
|
+
case MemberPopupActionType.OpenReferral:
|
|
80
|
+
pushView({ type: MemberPopupPanelType.Referral })
|
|
81
|
+
break
|
|
82
|
+
case MemberPopupActionType.OpenSubscribe:
|
|
83
|
+
pushView({ type: MemberPopupPanelType.Subscribe })
|
|
84
|
+
break
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function handleClose() {
|
|
89
|
+
setIsOpen(false)
|
|
90
|
+
setViewStack([])
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<ViewStackContext.Provider value={{ pushView, popView }}>
|
|
95
|
+
{isOpen && (
|
|
96
|
+
<div
|
|
97
|
+
className="fixed inset-0 z-[998]"
|
|
98
|
+
onClick={handleClose}
|
|
99
|
+
/>
|
|
100
|
+
)}
|
|
101
|
+
<div className="fixed right-0 z-[999] flex flex-col items-end pr-[16px]" style={{ bottom: bottomOffset }}>
|
|
102
|
+
{isOpen && (
|
|
103
|
+
<div
|
|
104
|
+
className={cn('relative mb-[8px] flex w-[420px] max-w-[calc(100vw-32px)] min-h-[600px] flex-col overflow-hidden bg-white shadow-[0_8px_32px_rgba(0,0,0,0.16)] cmp-md:w-[358px] cmp-sm:w-[258px]', rounded ? 'rounded-[12px]' : 'rounded-none')}
|
|
105
|
+
onClick={e => e.stopPropagation()}
|
|
106
|
+
>
|
|
107
|
+
<div className="absolute right-[-8px] top-[24px] size-0 border-y-8 border-l-8 border-y-transparent border-l-white" />
|
|
108
|
+
<div className="flex items-center justify-between px-[16px] pb-[8px] pt-[16px]">
|
|
109
|
+
<button
|
|
110
|
+
className="flex size-[24px] cursor-pointer items-center justify-center"
|
|
111
|
+
onClick={popView}
|
|
112
|
+
aria-label="Back"
|
|
113
|
+
>
|
|
114
|
+
<IconSwitch />
|
|
115
|
+
</button>
|
|
116
|
+
<button
|
|
117
|
+
className="flex size-[24px] cursor-pointer items-center justify-center"
|
|
118
|
+
onClick={handleClose}
|
|
119
|
+
aria-label="Close"
|
|
120
|
+
>
|
|
121
|
+
<IconClose />
|
|
122
|
+
</button>
|
|
123
|
+
</div>
|
|
124
|
+
|
|
125
|
+
<div
|
|
126
|
+
className="flex flex-1 flex-col gap-[12px] overflow-y-auto p-[16px]"
|
|
127
|
+
style={{ maxHeight: 'min(calc(80vh - 118px), 606px)' }}
|
|
128
|
+
>
|
|
129
|
+
{currentView === null && (
|
|
130
|
+
<>
|
|
131
|
+
{(copy.title || copy.subtitle) && (
|
|
132
|
+
<div className="flex flex-col gap-[4px]">
|
|
133
|
+
{copy.title && (
|
|
134
|
+
<h2 className="text-[24px] font-[700] leading-[1.2] text-[#080a0f]">{copy.title}</h2>
|
|
135
|
+
)}
|
|
136
|
+
{copy.subtitle && (
|
|
137
|
+
<p className="text-[16px] font-[700] leading-snug text-[#4a4c56]">{copy.subtitle}</p>
|
|
138
|
+
)}
|
|
139
|
+
</div>
|
|
140
|
+
)}
|
|
141
|
+
{copy.list.map((item, i) => (
|
|
142
|
+
<HomeCard key={i} item={item} onAction={handleAction} rounded={rounded} />
|
|
143
|
+
))}
|
|
144
|
+
</>
|
|
145
|
+
)}
|
|
146
|
+
|
|
147
|
+
{currentView !== null && (
|
|
148
|
+
<Suspense
|
|
149
|
+
fallback={
|
|
150
|
+
<div className="flex items-center justify-center py-[32px]">
|
|
151
|
+
<div className="size-[32px] animate-spin rounded-full border-[3px] border-[#080a0f] border-t-transparent" />
|
|
152
|
+
</div>
|
|
153
|
+
}
|
|
154
|
+
>
|
|
155
|
+
{currentView.type === MemberPopupPanelType.EarnCredits && copy.earnCredits && (
|
|
156
|
+
<EarnCredits
|
|
157
|
+
copy={copy.earnCredits}
|
|
158
|
+
onClose={popView}
|
|
159
|
+
isSubscribeSuccess={isSubscribeSuccess}
|
|
160
|
+
isActivateSuccess={isActivateSuccess}
|
|
161
|
+
isProfileCompleteSuccess={isProfileCompleteSuccess}
|
|
162
|
+
onSubscribeSuccess={() => setIsSubscribeSuccess(true)}
|
|
163
|
+
onActivateSuccess={() => setIsActivateSuccess(true)}
|
|
164
|
+
onProfileCompleteSuccess={() => setIsProfileCompleteSuccess(true)}
|
|
165
|
+
/>
|
|
166
|
+
)}
|
|
167
|
+
{currentView.type === MemberPopupPanelType.ConsumeCredits && copy.consumeCredits && (
|
|
168
|
+
<ConsumeCredits copy={copy.consumeCredits} onClose={popView} />
|
|
169
|
+
)}
|
|
170
|
+
{currentView.type === MemberPopupPanelType.Referral && copy.referral && (
|
|
171
|
+
<Referral copy={copy.referral} onClose={popView} />
|
|
172
|
+
)}
|
|
173
|
+
{currentView.type === MemberPopupPanelType.CompleteProfile && copy.earnCredits && (
|
|
174
|
+
<CompleteProfile copy={copy.earnCredits.completeProfile} shopifyStoreDomain={storeDomain} onClose={popView} onSuccess={currentView.props?.onSuccess} />
|
|
175
|
+
)}
|
|
176
|
+
{currentView.type === MemberPopupPanelType.Subscribe && copy.subscribe && (
|
|
177
|
+
<Subscribe copy={copy.subscribe} email={profile?.email} onClose={popView} onSuccess={currentView.props?.onSuccess} />
|
|
178
|
+
)}
|
|
179
|
+
{currentView.type === MemberPopupPanelType.SubscribeEmail && copy.earnCredits && (
|
|
180
|
+
<SubscribeEmail copy={copy.earnCredits.subscribe} email={profile?.email} onClose={popView} onSuccess={currentView.props?.onSuccess} />
|
|
181
|
+
)}
|
|
182
|
+
{currentView.type === MemberPopupPanelType.Activate && copy.earnCredits && (
|
|
183
|
+
<Activate copy={copy.earnCredits.authCodeActivate} onClose={popView} onSuccess={currentView.props?.onSuccess} />
|
|
184
|
+
)}
|
|
185
|
+
{currentView.type === MemberPopupPanelType.RedeemCoupon && currentView.props && copy.consumeCredits && (
|
|
186
|
+
<RedeemCoupon
|
|
187
|
+
itemTitle={currentView.props.itemTitle}
|
|
188
|
+
itemCredits={currentView.props.itemCredits}
|
|
189
|
+
itemImage={currentView.props.itemImage}
|
|
190
|
+
itemValue={currentView.props.itemValue}
|
|
191
|
+
itemOff={currentView.props.itemOff}
|
|
192
|
+
redeemId={currentView.props.redeemId}
|
|
193
|
+
userId={currentView.props.userId}
|
|
194
|
+
alpcConfig={currentView.props.alpcConfig}
|
|
195
|
+
copy={copy.consumeCredits.redeemModal}
|
|
196
|
+
pointUnit={copy.consumeCredits.pointUnit}
|
|
197
|
+
onClose={popView}
|
|
198
|
+
/>
|
|
199
|
+
)}
|
|
200
|
+
</Suspense>
|
|
201
|
+
)}
|
|
202
|
+
</div>
|
|
203
|
+
|
|
204
|
+
{copy.footer && !isLogin && (
|
|
205
|
+
<div className="border-t border-[#dadce0] p-[16px]">
|
|
206
|
+
<div className="flex items-center justify-between gap-[16px]">
|
|
207
|
+
<div className="flex items-center gap-[4px]">
|
|
208
|
+
<span className="text-[14px] font-[700] text-[#080a0f]">{copy.footer.loginHint}</span>
|
|
209
|
+
<button
|
|
210
|
+
className="cursor-pointer text-[14px] font-[700] text-[#080a0f] underline"
|
|
211
|
+
onClick={openSignInPopup}
|
|
212
|
+
>
|
|
213
|
+
{copy.footer.loginLabel}
|
|
214
|
+
</button>
|
|
215
|
+
</div>
|
|
216
|
+
<div className="flex shrink-0 items-center gap-[8px]">
|
|
217
|
+
<button
|
|
218
|
+
className={cn('cursor-pointer border-[1.6px] border-[#080a0f] px-[20px] pb-[10px] pt-[11px] text-[14px] font-[700] leading-[1.2] text-[#080a0f]', rounded ? 'rounded-[4px]' : 'rounded-none')}
|
|
219
|
+
onClick={openSignUpPopup}
|
|
220
|
+
>
|
|
221
|
+
{copy.footer.joinLabel}
|
|
222
|
+
</button>
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
225
|
+
</div>
|
|
226
|
+
)}
|
|
227
|
+
</div>
|
|
228
|
+
)}
|
|
229
|
+
|
|
230
|
+
<button
|
|
231
|
+
className="flex cursor-pointer items-center justify-center rounded-full bg-white shadow-lg"
|
|
232
|
+
onClick={() => setIsOpen(prev => !prev)}
|
|
233
|
+
aria-label={copy.triggerImg.alt ?? 'Open member popup'}
|
|
234
|
+
>
|
|
235
|
+
<Picture source={copy.triggerImg.url} alt={copy.triggerImg.alt ?? ''} className="size-[58px]" />
|
|
236
|
+
</button>
|
|
237
|
+
</div>
|
|
238
|
+
</ViewStackContext.Provider>
|
|
239
|
+
)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
type HomeCardProps = {
|
|
243
|
+
item: MemberPopupItem
|
|
244
|
+
onAction: (type: MemberPopupActionType, url?: string) => void
|
|
245
|
+
rounded: boolean
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function HomeCard({ item, onAction, rounded }: HomeCardProps) {
|
|
249
|
+
const ACTION_ICON: Partial<Record<MemberPopupActionType, typeof IconCredit>> = {
|
|
250
|
+
[MemberPopupActionType.OpenEarnCredits]: IconCredit,
|
|
251
|
+
[MemberPopupActionType.OpenConsumeCredits]: IconBag,
|
|
252
|
+
[MemberPopupActionType.OpenReferral]: IconReferral,
|
|
253
|
+
[MemberPopupActionType.OpenSubscribe]: IconSubscription,
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return (
|
|
257
|
+
<div className={cn('flex flex-col gap-[16px] bg-[#f5f6f7] p-[16px]', rounded ? 'rounded-[8px]' : 'rounded-none')}>
|
|
258
|
+
<div className="flex flex-col gap-[4px]">
|
|
259
|
+
<h3 className="text-[20px] font-[700] leading-[1.2] text-[#1e2024]">{item.title}</h3>
|
|
260
|
+
{item.description && <p className="text-[14px] font-[700] leading-[1.43] text-[#1e2024]">{item.description}</p>}
|
|
261
|
+
</div>
|
|
262
|
+
|
|
263
|
+
<div className="flex flex-col gap-[16px]">
|
|
264
|
+
{item.links.map((link, i) => {
|
|
265
|
+
const IconComponent = ACTION_ICON[link.type]
|
|
266
|
+
return (
|
|
267
|
+
<div key={i}>
|
|
268
|
+
{i > 0 && <div className="mb-[16px] h-px bg-[#dadce0]" />}
|
|
269
|
+
<button
|
|
270
|
+
className="flex w-full cursor-pointer items-center gap-[12px]"
|
|
271
|
+
onClick={() => onAction(link.type, link.url)}
|
|
272
|
+
>
|
|
273
|
+
{IconComponent && <IconComponent className="size-[24px] shrink-0" />}
|
|
274
|
+
<span className="flex-1 text-left text-[14px] font-[700] text-[#1e2024]">{link.label}</span>
|
|
275
|
+
<IconArrow className="size-[16px] shrink-0" />
|
|
276
|
+
</button>
|
|
277
|
+
</div>
|
|
278
|
+
)
|
|
279
|
+
})}
|
|
280
|
+
</div>
|
|
281
|
+
</div>
|
|
282
|
+
)
|
|
283
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { createContext, PropsWithChildren, useContext } from 'react'
|
|
2
|
+
import { HeadlessProvider, type HeadlessConfig } from '@anker-in/lib'
|
|
3
|
+
import { RegistrationProvider } from '../../registration'
|
|
4
|
+
import type { EarnCreditsCopy, MemberPopupContextValue } from '../type'
|
|
5
|
+
|
|
6
|
+
export const MemberPopupContext = createContext<MemberPopupContextValue>({
|
|
7
|
+
profile: null,
|
|
8
|
+
isLoadingProfile: false,
|
|
9
|
+
creditInfo: undefined,
|
|
10
|
+
fetchCreditInfo: () => {},
|
|
11
|
+
openSignUpPopup: () => {},
|
|
12
|
+
openSignInPopup: () => {},
|
|
13
|
+
taskIdToTypeMapping: {},
|
|
14
|
+
alpcConfig: {
|
|
15
|
+
brand: '',
|
|
16
|
+
locale: '',
|
|
17
|
+
appName: '',
|
|
18
|
+
},
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
type MemberPopupProviderProps = MemberPopupContextValue & {
|
|
22
|
+
headlessConfig: HeadlessConfig
|
|
23
|
+
earnCreditsCopy?: EarnCreditsCopy
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function MemberPopupProvider({
|
|
27
|
+
children,
|
|
28
|
+
headlessConfig,
|
|
29
|
+
earnCreditsCopy,
|
|
30
|
+
...contextValue
|
|
31
|
+
}: PropsWithChildren<MemberPopupProviderProps>) {
|
|
32
|
+
return (
|
|
33
|
+
<HeadlessProvider headlessConfig={headlessConfig}>
|
|
34
|
+
<RegistrationProvider
|
|
35
|
+
email={contextValue.profile?.email}
|
|
36
|
+
copy={{ authCodeActivate: earnCreditsCopy?.authCodeActivate }}
|
|
37
|
+
>
|
|
38
|
+
<MemberPopupContext.Provider value={contextValue}>
|
|
39
|
+
{children}
|
|
40
|
+
</MemberPopupContext.Provider>
|
|
41
|
+
</RegistrationProvider>
|
|
42
|
+
</HeadlessProvider>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function useMemberPopupContext() {
|
|
47
|
+
const context = useContext(MemberPopupContext)
|
|
48
|
+
if (!context) {
|
|
49
|
+
throw new Error('useMemberPopupContext must be used within a MemberPopupProvider')
|
|
50
|
+
}
|
|
51
|
+
return context
|
|
52
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { createContext, useContext, ReactNode } from 'react'
|
|
4
|
+
import type { MemberPopupPanelType } from '../type'
|
|
5
|
+
|
|
6
|
+
export type ViewPayload = {
|
|
7
|
+
type: MemberPopupPanelType
|
|
8
|
+
props?: Record<string, any>
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type ViewStackContextValue = {
|
|
12
|
+
pushView: (view: ViewPayload) => void
|
|
13
|
+
popView: () => void
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const ViewStackContext = createContext<ViewStackContextValue>({
|
|
17
|
+
pushView: () => {},
|
|
18
|
+
popView: () => {},
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
export function useViewStack() {
|
|
22
|
+
return useContext(ViewStackContext)
|
|
23
|
+
}
|