import * as React from 'react' import { openAppLofter } from 'nw-app-lofter' import { isLofter } from 'nw-detect'; import { parse } from 'query-string-for-all'; import { callHandler } from 'nejsbridge/dist/bridge.lofter.es'; import { Props, State } from './type' import { Popup } from '../common/popup'; import { Tabs, TabPane } from '../common/tabs'; import AddressModal from './address-modal'; import { MissionTitle, MissionList, MissionDesc, } from './missions'; import { AwardList, } from './awards' import { showMessage } from '../common/toast/toast' import { InfoIcon, ClockIcon } from '../common/icons' import { goToLogin } from '../utils/login'; import { formatMonthDate, formatFullDate } from '../utils/format'; import localParam from '../utils/localParam'; import log from '../utils/lofter-log' import { appNavigate } from '../utils/navigate' import { isCardChannel } from '../utils/card-utils'; import { fetchTotalMission, convertSimplePrize } from './actions'; import { BaseComponent } from '../base-component' import * as Styled from './index.style' const urlData = parse(location.search); const { rem } = Styled; interface InfoBlockProps { points: number, text: string, icon: string, color?: string, } export class InfoBlock extends React.Component { render() { const { points, text, icon, color } = this.props; return ( {text} 图标 x{points} ) } } export class CardMission extends React.Component { constructor(props: Props) { super(props); const urlData = localParam(); if (urlData.mission === 'true' || props.alwaysShow) { if (isLofter() || isCardChannel) { // 站内或渠道内,允许做任务 this.state.showMissionPopup = true; } else if (!this.props.isEdit){ this.openApp(); } } } static defaultProps = new Props() state = new State() missionScrollRef: HTMLElement; awardScrollRef: HTMLElement; // missionCode: string; // awardCode: string; awardAddressData: { activityCode: string, prizeActivityCode: string, prizeCode: string, } dataRequestTime: number = 0; getData = async ({ time, callback, } : { time?: number, callback?: () => void } = {}) => { try { const res = await fetchTotalMission(this.props.activityCode, time); if (res.code === 200) { this.setState({ isLogin: true, points: res.data.points, pointsIcon: res.data.pointsIcon, missionData: res.data.missionActivity, awardData: res.data.awardActivity, hintMissionStatus: res.data.hintMissionStatus, }, () => { callback && callback(); }) } else { if (res.code !== 401) { showMessage({ text: res.msg }) } } console.log('activity data', time, res); } catch (error) { console.log('Get data failed', error) showMessage('活动太火爆了,请稍后再来哦~') } } openApp = () => { openAppLofter({ path: 'webview', query: { url: location.href } }) } checkAppAndLogin = (type: 'mission' | 'award', callback: () => void) => { if (window.location.host.indexOf('cms-yaolu.hz.netease.com') >= 0){ callback(); return; } // 如果站外,并且不是渠道,直接唤起app才能做任务 if (!isLofter() && !isCardChannel) { this.openApp(); return; } if (type === 'mission') { if (this.state.missionData && this.state.missionData.activityStatus === -1) { showMessage('活动还没开始哦~') return; } if (this.state.missionData && this.state.missionData.activityStatus === 1) { showMessage('活动已经结束啦~') return; } } if (type === 'award') { if (this.state.awardData && this.state.awardData.status === -1) { showMessage('活动还没开始哦~') return; } if (this.state.awardData && this.state.awardData.status === 1) { showMessage('活动已经结束啦~') return; } } if (!this.state.isLogin) { goToLogin(() => { this.getData({ time: new Date().getTime(), callback }); }, 'card') return; } // 为了兼容5.1设备不支持visibility api,每次拉起弹窗都更新一次数据 this.getData({ time: new Date().getTime(), }); callback(); } showMissionPopup = () => { log.capture(`card-mission-click`, { category: 'cmsact', action: 0, scene: 'cmsact', v: '1.0.0' }) this.checkAppAndLogin('mission', () => { if (!this.state.missionData || !this.state.missionData.tabList) { showMessage('活动太火爆了,请稍后再来哦~') return; } this.setState({ showMissionPopup: true }) }) } hideMissionPopup = () => { this.setState({ showMissionPopup: false }) } missionMaskClick = () => { const { alwaysShow, maskClickTarget, isEdit } = this.props; if (isEdit) return; if (alwaysShow) { if (maskClickTarget) { if (maskClickTarget.startsWith('https://www.lofter.com/market/fe/html/cardHome.html') && urlData.from === 'cardHome'){ callHandler('njb_closeCurrentWebview'); } else { appNavigate(maskClickTarget); } } return; }; this.hideMissionPopup(); } showAwardPopup = () => { log.capture(`card-award-click`, { category: 'cmsact', action: 0, scene: 'cmsact', v: '1.0.0' }) this.checkAppAndLogin('award', () => { if (!this.state.awardData || !this.state.awardData.items) { showMessage('活动太火爆了,请稍后再来哦~') return; } this.setState({ showAwardPopup: true }) }) } hideAwardPopup = () => { this.setState({ showAwardPopup: false }) } awardMaskClick = () => { const { alwaysShow, maskClickTarget } = this.props; if (alwaysShow) { if (maskClickTarget) { if (maskClickTarget.startsWith('https://www.lofter.com/market/fe/html/cardHome.html') && urlData.from === 'cardHome'){ callHandler('njb_closeCurrentWebview'); } else { appNavigate(maskClickTarget); } } return; }; this.hideAwardPopup(); } showAddressModal = ({ activityCode, prizeActivityCode, prizeCode, } :{ activityCode: string, prizeActivityCode: string, prizeCode: string, }) => { this.awardAddressData = { activityCode, prizeActivityCode, prizeCode }; this.setState({ showAddressModal: true, }) } hideAddressModal = () => { this.awardAddressData = { activityCode: '', prizeActivityCode: '', prizeCode: '' }; this.setState({ showAddressModal: false, }) } submitAddressPrize = (data: any, callback?: () => void) => { console.log('submitAddressPrize', data); if (!this.awardAddressData || !this.awardAddressData.activityCode || !this.awardAddressData.prizeCode) { console.warn('No award adress data preset'); return; } convertSimplePrize({ addressId: data.addressId, activityCode: this.awardAddressData.activityCode, prizeActivityCode: this.awardAddressData.prizeActivityCode, prizeCode: this.awardAddressData.prizeCode, }).then(() => { this.hideAddressModal(); this.getData({ time: new Date().getTime(), }); callback && callback(); }) } monitorVisibility = () => { if (this.props.isEdit) return; const handleVisibilitychange = () => { if (!document.hidden) { console.log('monitorVisibility update', new Date().getTime()); this.getData({ time: new Date().getTime(), }); } }; document.addEventListener('visibilitychange', handleVisibilitychange); } loadFont = () => { const { point : { font } } = this.props if (font) { const style = document.createElement('style'); style.innerText = `@font-face { font-family: "Point Font"; src: url("${font}") format("truetype"); }`; document.head.appendChild(style); } } static getDerivedStateFromProps(props: Props, state: State) { if (props.alwaysShow && !state.showMissionPopup) { return { showMissionPopup: true, } } return null; } componentDidMount() { // if (this.props.isEdit) { // this.watch() // } this.loadFont(); this.monitorVisibility(); this.getData({ time: new Date().getTime(), }); } watch = () => { let el = document.getElementById('viewport-left') let vwEl = document.getElementById('viewport') let left = vwEl.getBoundingClientRect().left this.setState({ viewportLeft: left }) const config = { attributes: true, childList: true} const callback = (mutationList: any) => { mutationList.forEach((mutation: any) => { switch(mutation.type) { case 'childList': this.setState({ viewportLeft: vwEl.getBoundingClientRect().left }) break; } }) } let observer = new MutationObserver(callback) observer.observe(el, config) } triggerMission = () => { log.capture(`card-award-mission-click`, { category: 'cmsact', action: 0, scene: 'cmsact', v: '1.0.0' }) this.hideAwardPopup(); this.showMissionPopup(); } triggerAward = () => { log.capture(`card-mission-award-click`, { category: 'cmsact', action: 0, scene: 'cmsact', v: '1.0.0' }) this.hideMissionPopup(); this.showAwardPopup(); } goRule = () => { const { ruleUrl, isEdit } = this.props; if (ruleUrl && !isEdit) { appNavigate(ruleUrl); } } render() { const { activityCode, style, isEdit, mission, dot, award, point, missionPanel, awardPanel, previewMission, previewAward, previewAddress, mainColor = '#000', combinedBackground, addressModal, ruleMarginLeft, missionRuleTop, awardRuleTop, alwaysShow, } = this.props const { viewportLeft, missionData, awardData, points, pointsIcon, hintMissionStatus, } = this.state; let { showMissionPopup, showAwardPopup, showAddressModal, } = this.state; if (isEdit && previewMission) { showMissionPopup = true } if (isEdit && previewAward) { showAwardPopup = true } if (isEdit && previewAddress) { showAddressModal = true } return (
{ // if (node) { // node.style.setProperty("z-index", "auto", "important"); // } // }} > {!alwaysShow &&
任务热区
兑换热区
{points}
} 规则 {(missionPanel.topGap && typeof missionPanel.topGap === 'number') ? : null} {missionPanel.titleImage && missionPanel.titleImage.url && } { this.missionScrollRef = ref }} color={mainColor}> { missionData && missionData.tabList && missionData.tabList.map((tab, index) => { return ( {tab.tabDesc} ) }) } { missionPanel && (missionPanel.triggerImage && missionPanel.triggerImage.url || missionPanel.triggerText) && {missionPanel.triggerText} } 规则 {(awardPanel.topGap && typeof awardPanel.topGap === 'number') ? : null} {awardPanel.titleImage && awardPanel.titleImage.url && } { awardData && } { awardData && { this.awardScrollRef = ref }} color={mainColor} list={awardData.items} points={points} pointsIcon={pointsIcon} awardData={awardData} activityCode={activityCode} refreshData={this.getData} showAddressModal={this.showAddressModal} /> } { awardPanel && {awardPanel.triggerText} }
) } }