import * as React from "react" import styled from "styled-components" import * as Toast from "../Toast" import gql from "graphql-tag" import * as cookie from "js-cookie" import { withApollo } from "react-apollo" import { getConfig } from "jamplay-common/remote-config" import ApolloClient from "apollo-client" const Container = styled.div` position: fixed; z-index: 100; top: 100px; right: 30px; & > * { margin-bottom: 10px; animation: fadeIn 1s; @keyframes fadeIn { from {opacity: 0;} to {opacity: 1;} } } ` let connection const POLLING_DURATION = 20000 function createConnection() { const t = cookie.get("polling_timestamp") || "0" try { const latestTimestamp = t ? parseInt(t, 10) : 0 console.log(Date.now() - latestTimestamp) if (Date.now() - latestTimestamp > POLLING_DURATION) { console.log(`[notification] polling channel avaliable, start polling ${Date.now()}`) } else { console.log(`skip polling... (polling already exists try again in ${POLLING_DURATION})`) setTimeout(() => createConnection(), POLLING_DURATION) return } } catch (e) { console.error(e) cookie.set("polling_timestamp", 0) } const xhr = new XMLHttpRequest() const token = cookie.get("token") xhr.open( "get", (getConfig().publicRuntimeConfig.legacyPollingUrl) + `?duration=${POLLING_DURATION}` ) xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8") xhr.setRequestHeader("Authorization", `Bearer ${token}`) xhr.onprogress = () => { // console.log(`[notification] polling.... `) const res = (xhr.responseText.toString().split("?;?")).filter((chunk) => chunk.length > 0) try { console.log(res[res.length - 1]) if (!res[res.length - 1]) { return } const result = JSON.parse(res[res.length - 1]) if (result.isUpdated > 0) { cookie.set("isUpdateNoti", Number(cookie.get("isUpdateNoti") || 0) + 1) } } catch (e) { console.error(res[res.length - 1], e) } } xhr.onload = () => { console.log("[notification] end polling") const res = (xhr.responseText.toString().split("?;?")).filter((chunk) => chunk.length > 0) try { const end = JSON.parse(res[res.length - 1]) // last chunk should confirm success // to restart polling console.log(end) if (end.done) { setTimeout( // delay new polling to maintain server performance () => { (window as any).notificationPolling = createConnection() }, 5000 ) } else { console.warn("[notification] long polling has been interupt...") } } catch (e) { console.warn("[notification] notification service not avaliable") } } xhr.onerror = () => { console.warn("[notification] cannot connect to notification polling endpoint reload to retry....") } xhr.timeout = 50000 console.log("[notification] start polling") cookie.set("polling_timestamp", Date.now()) xhr.send() return xhr } export type TProps = { client: ApolloClient } let version = 0 const markAsRead: boolean[] = [] class ToastNotificationLegacy extends React.Component { public _cancel = false public _timer public state: any = { notifications: [] } public componentDidMount() { if (!connection) { createConnection() connection = 1 } const client = this.props.client if (typeof window !== "undefined") { this._timer = setInterval(async () => { const isUpdateNoti = cookie.get("isUpdateNoti") if (isUpdateNoti && version < Number(isUpdateNoti)) { version = Number(isUpdateNoti) const result = await client.query({ query: gql` query { user { _id notifications { text _id textAttr isRead } } } `, fetchPolicy: "network-only", context: { service: "nap" }, }) const notifications = result && result.data && result.data && result.data.user && result.data.user.notifications ? result.data.user.notifications : [] if (notifications.length > 0) { // yield put({type: 'app/close-dialog', from: 'notification.sagas'}) --> remove it because it's remove payment-error toast const addNoti: any[] = [] for (const noti of notifications) { const isAlreadyShow = markAsRead[noti._id] if (!noti.isRead && !isAlreadyShow) { markAsRead[noti._id] = true addNoti.push(noti) } } if (addNoti.length) { this.setState((state) => ({ notifications: state.notifications.concat(addNoti) })) } } } }, 1000) } } public componentWillUnmount() { clearInterval(this._timer) } public handleOnClose = (_id) => { this.setState( (state) => ({ notifications: state.notifications.filter((n) => n._id !== _id)}) ) } public render() { if (typeof window === "undefined") { return } return ( { this.state.notifications.map((notification) => ( )) } ) } } export default withApollo(ToastNotificationLegacy)