CheckAccess.tsx 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. import showAlert from "@/components/basic/Alert"
  2. import { View, Text } from "@tarojs/components";
  3. import './CheckAccess.scss'
  4. import { memo, useContext, useEffect, useState } from "react";
  5. import { TimeFormatter } from "@/utils/time_format";
  6. import { useDispatch, useSelector } from "react-redux";
  7. import { getPlans, setPlan } from "@/services/trackTimeDuration";
  8. import { chooseMode, initTarget, updateStep } from "@/store/set_target";
  9. import { jumpPage } from "../hooks/Common";
  10. import { useDidHide, useDidShow } from "@tarojs/taro";
  11. import Taro from "@tarojs/taro";
  12. import { useTranslation } from "react-i18next";
  13. import { kIsIOS } from "@/utils/tools";
  14. import { ColorType } from "@/context/themes/color";
  15. // import { useIsFocused } from "@react-navigation/native";
  16. let confirmAction: any = null;
  17. let cancelAction: any = null;
  18. let timer;
  19. let access;
  20. let useNavigation, AppState, Modal, useIsFocused;
  21. if (process.env.TARO_ENV == 'rn') {
  22. AppState = require("react-native").AppState
  23. Modal = require("react-native").Modal
  24. useNavigation = require("@react-navigation/native").useNavigation
  25. useIsFocused = require("@react-navigation/native").useIsFocused
  26. }
  27. const CheckAccess = memo((props: { record: any, count: number, access: any }) => {
  28. // export default function CheckAccess(props: { record: any, count: number }) {
  29. const [showFastAlert, setShowFastAlert] = useState(false)
  30. const [alertTitle, setAlertTitle] = useState('')
  31. const [alertContent, setAlertContent] = useState('')
  32. const [showCancel, setShowCancel] = useState(false)
  33. const [keepContent, setKeepContent] = useState(false)
  34. const [cancelText, setCancelText] = useState('')
  35. const [confirmText, setConfirmText] = useState('')
  36. const [subConfirmText, setSubConfirmText] = useState('')
  37. const user = useSelector((state: any) => state.user);
  38. const { t } = useTranslation()
  39. //判断选择混合场景,如果成功了,就弹窗
  40. const [checkChooseMixed, setCheckChooseMixed] = useState(false)
  41. const [chooseMixedSuccess, setChooseMixedSuccess] = useState(false)
  42. const ring = useSelector((state: any) => state.ring);
  43. const dispatch = useDispatch();
  44. let navigation;
  45. let isFocused = false;
  46. if (useNavigation) {
  47. navigation = useNavigation()
  48. isFocused = useIsFocused()
  49. }
  50. useEffect(() => {
  51. var obj = props.record
  52. if (props.access) {
  53. const member_expire = props.access.member.expire
  54. const member_status = props.access.member.status
  55. if (member_expire && new Date().getTime() > member_expire && member_status && member_status=='MEMBER') {
  56. if (global.indexPageRefresh) {
  57. global.indexPageRefresh()
  58. }
  59. }
  60. if (!user.test_user) {
  61. return
  62. }
  63. if (!global.memberAlert) {
  64. return;
  65. }
  66. global.ring = ring;
  67. const currentStatus = props.access.fast_sleep.current.qualification.status
  68. const preStatus = props.access.fast_sleep.previous.qualification.status
  69. if ((preStatus == 'PROVISIONAL_QUALIFIED' || preStatus == 'QUALIFIED_UNTIL_EXPIRE') &&
  70. currentStatus == 'PENDING' &&
  71. // global.ring.current_record.scenario != 'FAST' &&
  72. obj.current_record.status == 'WAIT_FOR_START') {
  73. loseGain(props.access.fast_sleep)
  74. }
  75. // else if ((preStatus == 'PROVISIONAL_QUALIFIED' || preStatus == 'QUALIFIED_UNTIL_EXPIRE') &&
  76. // currentStatus == 'NOT_QUALIFIED' &&
  77. // global.ring.current_record.scenario == 'FAST_SLEEP' &&
  78. // obj.current_record.status == 'WAIT_FOR_START') {
  79. // loseGain(props.access.fast_sleep)
  80. // }
  81. else if (currentStatus == 'PROVISIONAL_QUALIFIED') {
  82. Taro.setStorage({ key: 'showedDisqualifiedAlert', data: false })
  83. const { fast_expire, sleep_expire } = props.access.fast_sleep.current.qualification
  84. if (fast_expire && new Date().getTime() > fast_expire && obj.current_record.status == 'WAIT_FOR_START') {
  85. if (global.indexPageRefresh) {
  86. global.indexPageRefresh()
  87. }
  88. }
  89. if (sleep_expire && new Date().getTime() > sleep_expire && obj.current_record.status == 'WAIT_FOR_START') {
  90. if (global.indexPageRefresh) {
  91. global.indexPageRefresh()
  92. }
  93. }
  94. }
  95. else if (currentStatus == 'QUALIFIED_UNTIL_EXPIRE') {
  96. Taro.setStorage({ key: 'showedDisqualifiedAlert', data: false })
  97. const member_expire = props.access.member.expire
  98. if (member_expire && new Date().getTime() > member_expire) {
  99. if (global.indexPageRefresh) {
  100. global.indexPageRefresh()
  101. }
  102. }
  103. // if (member_expire && new Date().getTime() > member_expire && obj.current_record.status == 'WAIT_FOR_START' &&
  104. // global.ring.current_record.scenario != 'FAST') {
  105. // if (global.indexPageRefresh) {
  106. // global.indexPageRefresh()
  107. // }
  108. // }
  109. }
  110. if (obj.current_record.status != 'WAIT_FOR_START') {
  111. Taro.setStorage({ key: 'showedDisqualifiedAlert', data: false })
  112. }
  113. }
  114. }, [props.count])
  115. // const handleAppStateChange = (nextAppState) => {
  116. // console.log(nextAppState)
  117. // if (nextAppState != 'active') {
  118. // return
  119. // }
  120. // };
  121. function viewWillAppear() {
  122. if (checkChooseMixed && chooseMixedSuccess) {
  123. upgradeSuccess()
  124. confirmAction = null
  125. }
  126. setCheckChooseMixed(false)
  127. setChooseMixedSuccess(false)
  128. }
  129. function viewWillDisappear() {
  130. }
  131. // useDidShow(() => {
  132. // console.log('user did show');
  133. // viewWillAppear()
  134. // })
  135. useEffect(() => {
  136. if (process.env.TARO_ENV == 'rn') {
  137. // AppState.addEventListener('change', handleAppStateChange);
  138. navigation.addListener('focus', () => {
  139. viewWillAppear()
  140. });
  141. // 当页面即将消失时执行
  142. navigation.addListener('blur', () => {
  143. viewWillDisappear()
  144. });
  145. }
  146. global.chooseMixed = () => {
  147. setChooseMixedSuccess(true)
  148. setShowFastAlert(false)
  149. setKeepContent(false)
  150. }
  151. global.paySuccess = (res) => {
  152. cancelAction = null
  153. confirmAction = null
  154. setShowFastAlert(false)
  155. setKeepContent(false)
  156. setTimeout(() => {
  157. global.refreshAccess(res)
  158. }, 2000)
  159. }
  160. global.checkAccess = (acccessData: any) => {
  161. debugger
  162. if (!user.test_user) {
  163. return
  164. }
  165. if (!global.memberAlert) {
  166. return;
  167. }
  168. const currentStatus = acccessData.current.qualification.status
  169. const preStatus = acccessData.previous.qualification.status
  170. access = acccessData
  171. setSubConfirmText('')
  172. confirmAction = null
  173. const { trigger_event } = access.current.qualification;
  174. //删除记录后仍有资格,不做特殊处理。只有删除时为NOT_SATISFIED_AFTER_DELETE时,弹失去资格弹窗
  175. if (trigger_event == 'SATISFIED_AFTER_DELETE') {
  176. return;
  177. }
  178. if (preStatus == 'NOT_QUALIFIED' && currentStatus == 'NOT_QUALIFIED') {
  179. const { streak_fast_current, streak_fast_min_required } = acccessData.current.qualification.condition
  180. if (streak_fast_current == 0) {
  181. return;
  182. }
  183. setShowFastAlert(true)
  184. setKeepContent(false)
  185. setAlertTitle(t('feature.check_access.non_access.title'))
  186. setAlertContent(t('feature.check_access.non_access.desc',
  187. {
  188. day: streak_fast_current,
  189. // day_left: streak_fast_min_required - streak_fast_current,
  190. // day_unit: streak_fast_min_required - streak_fast_current == 1 ? 'day' : 'days'
  191. }))
  192. setShowCancel(false)
  193. setConfirmText(t('feature.check_access.non_access.btn'))
  194. }
  195. else if (preStatus == 'NOT_QUALIFIED' && (currentStatus == 'PROVISIONAL_QUALIFIED' || currentStatus == 'QUALIFIED_UNTIL_EXPIRE') && global.ring.current_record.scenario != 'FAST_SLEEP') {
  196. const { streak_fast_current } = acccessData.current.qualification.condition
  197. const reason = t('feature.check_access.gain_access.reason', { day: streak_fast_current })
  198. Taro.setStorage({ key: 'showedDisqualifiedAlert', data: false })
  199. var expire = acccessData.current.qualification.fast_expire
  200. setShowFastAlert(true)
  201. setKeepContent(true)
  202. setAlertTitle(t('feature.check_access.gain_access.title'))
  203. setAlertContent(t('feature.check_access.gain_access.desc', { reason: reason }))
  204. setShowCancel(true)
  205. setCancelText(t('feature.check_access.gain_access.stay_btn'))
  206. setConfirmText(t('feature.check_access.gain_access.upgrade'))
  207. setSubConfirmText(t('feature.check_access.gain_access.upgrade_desc', { time: TimeFormatter.countdown(expire) }))
  208. confirmAction = upgrade
  209. // if (process.env.TARO_ENV=='rn' && !kIsIOS){
  210. // return
  211. // }
  212. timer = setInterval(() => {
  213. if (new Date().getTime() > expire) {
  214. cancel()
  215. return
  216. }
  217. setSubConfirmText(t('feature.check_access.gain_access.upgrade_desc', { time: TimeFormatter.countdown(expire) }))
  218. // setConfirmText(`Upgrade to Fasting with Sleep\n(Offer Expiring in ${TimeFormatter.countdown(expire)})`)
  219. }, 1000)
  220. }
  221. else if ((preStatus == 'PROVISIONAL_QUALIFIED' || preStatus == 'QUALIFIED_UNTIL_EXPIRE') && (currentStatus == 'PROVISIONAL_QUALIFIED' || currentStatus == 'QUALIFIED_UNTIL_EXPIRE')) {
  222. const { streak_fast_current, streak_sleep_current } = acccessData.current.qualification.condition
  223. if (global.ring.current_record.scenario == 'FAST') {
  224. var expire = acccessData.current.qualification.fast_expire
  225. const reason = t('feature.check_access.gain_access.reason', { day: streak_fast_current })
  226. setShowFastAlert(true)
  227. // setKeepContent(false)
  228. setKeepContent(true)
  229. setAlertTitle(t('feature.check_access.gain_access.title'))
  230. setAlertContent(t('feature.check_access.gain_access.desc', { reason: reason }))
  231. setShowCancel(true)
  232. setCancelText(t('feature.check_access.gain_access.stay_btn'))
  233. setConfirmText(t('feature.check_access.gain_access.upgrade'))
  234. setSubConfirmText(t('feature.check_access.gain_access.upgrade_desc', { time: TimeFormatter.countdown(expire) }))
  235. confirmAction = upgrade
  236. timer = setInterval(() => {
  237. if (new Date().getTime() > expire) {
  238. cancel()
  239. return
  240. }
  241. setSubConfirmText(t('feature.check_access.gain_access.upgrade_desc', { time: TimeFormatter.countdown(expire) }))
  242. // setConfirmText(`Upgrade to Fasting with Sleep\n(Offer Expiring in ${TimeFormatter.countdown(expire)})`)
  243. }, 1000)
  244. return;
  245. }
  246. setKeepContent(false)
  247. setShowFastAlert(true)
  248. setAlertTitle(t('feature.check_access.stay_qualified.title'))
  249. setAlertContent(t('feature.check_access.stay_qualified.desc', { fast_streak_day: streak_fast_current, sleep_streak_day: streak_sleep_current }))
  250. setShowCancel(false)
  251. setConfirmText(t('feature.check_access.stay_qualified.btn'))
  252. }
  253. else if ((preStatus == 'QUALIFIED_UNTIL_EXPIRE' || preStatus == 'PROVISIONAL_QUALIFIED')
  254. && currentStatus == 'PENDING'
  255. // && global.ring.current_record.scenario == 'FAST_SLEEP'
  256. ) {
  257. loseGain(acccessData);
  258. }
  259. // else if ((preStatus == 'QUALIFIED_UNTIL_EXPIRE' || preStatus == 'PROVISIONAL_QUALIFIED')
  260. // && currentStatus == 'NOT_QUALIFIED'
  261. // && global.ring.current_record.scenario == 'FAST_SLEEP'
  262. // ) {
  263. // loseGain(acccessData);
  264. // }
  265. }
  266. }, [])
  267. function upgradeSuccess() {
  268. setShowFastAlert(false)
  269. }
  270. async function loseGain(access) {
  271. var showedDisqualifiedAlert = await getStorage('showedDisqualifiedAlert') || false;
  272. if (showedDisqualifiedAlert) {
  273. return;
  274. }
  275. if (timer) {
  276. clearInterval(timer)
  277. timer = null
  278. }
  279. var desc = ''
  280. const { trigger_event, fast_expire, sleep_expire, condition } = access.current.qualification;
  281. var dt = fast_expire;
  282. if (sleep_expire && sleep_expire < fast_expire) {
  283. dt = sleep_expire
  284. }
  285. var date = TimeFormatter.getMonthAndDayByTimestamp(dt, true)
  286. var day_unit = Math.max(condition.streak_fast_min_required - 1, 1) == 1 ? 'day' : 'days';
  287. switch (trigger_event) {
  288. case 'FAST_STREAK_LOST':
  289. desc = t('feature.check_access.lost_reason.fast_streak_lost');
  290. break;
  291. case 'SLEEP_STREAK_LOST':
  292. desc = t('feature.check_access.lost_reason.sleep_streak_lost');
  293. break;
  294. case 'FAST_SLEEP_STREAKS_LOST':
  295. desc = t('feature.check_access.lost_reason.fast_sleep_streaks_lost');
  296. break;
  297. case 'SLEEP_STREAK_NOT_GROWING':
  298. desc = t('feature.check_access.lost_reason.sleep_streak_not_growing');
  299. break;
  300. case 'NOT_SATISFIED_AFTER_DELETE':
  301. desc = t('feature.check_access.lost_reason.not_satisfied_after_delete', { day: Math.max(condition.streak_fast_min_required - 1, 1), day_unit: day_unit });
  302. break;
  303. case 'NA':
  304. default:
  305. break;
  306. }
  307. // console.error('失去资格弹窗', trigger_event, desc);
  308. setShowFastAlert(true)
  309. setKeepContent(true)
  310. setAlertTitle(t('feature.check_access.lose_access.title'))
  311. setAlertContent(t('feature.check_access.lose_access.desc', { reason: desc, require_days: condition.streak_fast_min_required }))
  312. setShowCancel(true)
  313. setCancelText(t('feature.check_access.lose_access.btn'))
  314. setConfirmText('Become a PRO member')
  315. setSubConfirmText('')
  316. setTimeout(() => {
  317. cancelAction = changeFastScenaria
  318. confirmAction = pay
  319. }, 100)
  320. // confirmAction = pay
  321. // cancelAction = changeFastScenaria
  322. Taro.setStorage({ key: 'showedDisqualifiedAlert', data: true })
  323. // changeFastScenaria()
  324. }
  325. function changeFastScenaria() {
  326. cancelAction = null
  327. confirmAction = null
  328. const { start_time, end_time } = global.ring.schedule.fast
  329. var params: any = {
  330. scenario: 'FAST',
  331. trigger_event: 'POPUP_PENDING_SUBSCRIBE_FAIL',
  332. method: 'USER_SET',
  333. schedule: {
  334. fast: {
  335. start_time: start_time,
  336. end_time: end_time,
  337. },
  338. }
  339. }
  340. setPlan(params).then(res => {
  341. if (global.indexPageRefresh) {
  342. global.indexPageRefresh()
  343. }
  344. })
  345. }
  346. async function getStorage(key: string) {
  347. try {
  348. const res = await Taro.getStorage({ key });
  349. return res.data;
  350. } catch {
  351. return '';
  352. }
  353. }
  354. function upgrade() {
  355. const { prev_input, valid, start_time, end_time } = global.ring.schedule.sleep
  356. if (prev_input && valid) {
  357. setShowFastAlert(true)
  358. setKeepContent(true)
  359. setAlertTitle(t('feature.check_access.sleep_schedule.title'))
  360. setAlertContent(t('feature.check_access.sleep_schedule.desc',
  361. { start_time: start_time, end_time: end_time }))
  362. setShowCancel(true)
  363. setCancelText(t('feature.check_access.sleep_schedule.cancel'))
  364. setConfirmText(t('feature.check_access.sleep_schedule.confirm'))
  365. setSubConfirmText('')
  366. setTimeout(() => {
  367. cancelAction = cancelSetting
  368. confirmAction = goSetting
  369. }, 100)
  370. }
  371. else {
  372. goSetting()
  373. }
  374. // const { expire } = access.current.qualification
  375. // setShowFastAlert(true)
  376. // setAlertTitle('Congrats on Your PRO Status!')
  377. // setAlertContent(`To preserve this status, continue your fasting streak in ${TimeFormatter.countdown(expire)} and begin your sleep streak. Remember to keep both of them going.`)
  378. // setShowCancel(false)
  379. // setConfirmText(`I got this!`)
  380. // timer = setInterval(() => {
  381. // if (new Date().getTime() > expire) {
  382. // cancel()
  383. // return
  384. // }
  385. // setAlertContent(`To preserve this status, continue your fasting streak in ${TimeFormatter.countdown(expire)} and begin your sleep streak. Remember to keep both of them going.`)
  386. // }, 1000)
  387. }
  388. function goSetting() {
  389. getPlans().then(res => {
  390. const data = res as { scenarios: any[] };
  391. console.log(data)
  392. var targets: any = {}
  393. data.scenarios.forEach((item) => {
  394. if (item.name == 'FAST') {
  395. if (item.schedule.fast.duration.prev_input) {
  396. item.schedule.fast.duration.last_value = item.schedule.fast.duration.init_value
  397. }
  398. targets.fast = item
  399. }
  400. else if (item.name == 'SLEEP') {
  401. targets.sleep = item
  402. }
  403. })
  404. dispatch(initTarget(targets))
  405. dispatch(updateStep({ step: 2 }))
  406. dispatch(chooseMode({ isMixed: true }))
  407. setCheckChooseMixed(true)
  408. jumpPage(`/pages/clock/SetGoal?upgrade=true&isSelf=1&trigger_event=POPUP_QUALIFIED_UPGRADED_SET`, 'SetGoal', navigation, {
  409. upgrade: true,
  410. isSelf: 1,
  411. trigger_event: 'POPUP_QUALIFIED_UPGRADED_SET'
  412. })
  413. confirmAction = goSetting
  414. })
  415. }
  416. function pay() {
  417. jumpPage('', 'ProductList', navigation)
  418. setTimeout(() => {
  419. confirmAction = pay
  420. cancelAction = changeFastScenaria
  421. }, 200)
  422. }
  423. function cancelSetting() {
  424. const { start_time, end_time } = global.ring.schedule.fast
  425. var params: any = {
  426. scenario: 'FAST_SLEEP',
  427. method: 'USER_SET',
  428. trigger_event: 'POPUP_QUALIFIED_UPGRADED_DISMISS',
  429. schedule: {
  430. fast: {
  431. start_time: start_time,
  432. end_time: end_time,
  433. },
  434. sleep: {
  435. start_time: global.ring.schedule.sleep.start_time,
  436. end_time: global.ring.schedule.sleep.end_time,
  437. }
  438. }
  439. }
  440. setPlan(params).then(res => {
  441. if (global.indexPageRefresh) {
  442. global.indexPageRefresh()
  443. }
  444. upgradeSuccess()
  445. })
  446. }
  447. function confirm() {
  448. if (!keepContent) {
  449. setShowFastAlert(false)
  450. }
  451. if (timer) {
  452. clearInterval(timer)
  453. timer = null
  454. }
  455. if (confirmAction) confirmAction()
  456. confirmAction = null;
  457. }
  458. function cancel() {
  459. setShowFastAlert(false)
  460. confirmAction = null;
  461. if (timer) {
  462. clearInterval(timer)
  463. timer = null
  464. }
  465. if (cancelAction) cancelAction()
  466. cancelAction = null;
  467. }
  468. function alertPop() {
  469. return <View className="fast_alert_container">
  470. <View className="fast_alert_content" catchMove>
  471. <View className="fast_alert_title">{alertTitle}</View>
  472. <View className="fast_alert_detail">{alertContent}</View>
  473. <View className={subConfirmText.length > 0 ? 'fast_alert_confirm confirm_multline' : 'fast_alert_confirm'} onClick={confirm}>
  474. <Text style={{ fontWeight: 'bold', color: ColorType.black }}>{confirmText}</Text>
  475. {
  476. subConfirmText.length > 0 && <Text className="confirm_sub_text">{subConfirmText}</Text>
  477. }
  478. </View>
  479. {
  480. showCancel && <View className="fast_alert_cancel" onClick={cancel}>{cancelText}</View>
  481. }
  482. {
  483. user.test_user && global.memberAlert && <View style={{ display: 'flex', flexDirection: 'column' }}>
  484. <Text style={{ color: '#fff', fontSize: 12 }}>member status:{props.access.member.status}</Text>
  485. <Text style={{ color: '#fff', fontSize: 12 }}>current status:{props.access.fast_sleep.current.qualification.status}</Text>
  486. <Text style={{ color: '#fff', fontSize: 12 }}>previous status:{props.access.fast_sleep.previous.qualification.status}</Text>
  487. <Text style={{ color: '#fff', fontSize: 12 }}>trigger event:{props.access.fast_sleep.current.qualification.trigger_event}</Text>
  488. <Text style={{ color: '#fff', fontSize: 12 }}>lost_access_total:{props.access.fast_sleep.current.qualification.condition.lost_access_total}</Text>
  489. <Text style={{ color: '#fff', fontSize: 12 }}>streak_fast_min_required:{props.access.fast_sleep.current.qualification.condition.streak_fast_min_required}</Text>
  490. </View>
  491. }
  492. </View>
  493. </View>
  494. }
  495. return <View>
  496. {
  497. user.test_user && global.memberAlert && showFastAlert && process.env.TARO_ENV == 'weapp' && alertPop()
  498. }
  499. {
  500. user.test_user && global.memberAlert && showFastAlert && isFocused && process.env.TARO_ENV == 'rn' && <Modal transparent={true}>
  501. <View style={{ backgroundColor: 'rgba(0,0,0,0.95)', width: '100%', height: '100%', alignItems: 'center', justifyContent: 'center' }}>
  502. {alertPop()}
  503. </View>
  504. </Modal>
  505. }
  506. </View>
  507. })
  508. export default CheckAccess;