// @ts-check import { APIWrapper, API_EVENT_TYPE } from "./api.js"; import { addMessage, animateGift, isPossiblyAnimatingGift, isAnimatingGiftUI } from "./dom_updates.js"; const api = new APIWrapper(); // const api = new APIWrapper(null, true, false); // const api = new APIWrapper(null, true, true); // show logs window.dbg = true; // exceptions const BreakException = {}; // classes class EventItem { constructor(event, priority){ this.event = event; this.priority = priority; } } // priority queue data structure class EventQueue { constructor(){ this.events = []; } enqueue(event, priority){ const eventItem = new EventItem(event, priority); let contain = false; try{ this.events.forEach((event, i) => { if(event.priority > eventItem.priority){ this.events.splice(i, 0, eventItem); contain = !contain; throw BreakException; } }); }catch(e){ if (e !== BreakException) throw e; } !contain && this.events.push(eventItem); } dequeue(){ if (this.isEmpty()) throw "Underflow"; return this.events.shift(); } front(){ if (this.isEmpty()) throw "No events in Queue"; return this.events[0]; } isEmpty(){ return this.events.length == 0; } get count(){ return this.events.length; } } // enum flags const EVENT_PRIORITY = { APIMessageEvent: 1, APIGiftEvent: 2, APIAnimatedGiftEvent: 3, } const PRIORITY_MAP = {}; PRIORITY_MAP[`${API_EVENT_TYPE.ANIMATED_GIFT}`] = EVENT_PRIORITY.APIAnimatedGiftEvent; PRIORITY_MAP[`${API_EVENT_TYPE.GIFT}`] = EVENT_PRIORITY.APIGiftEvent; PRIORITY_MAP[`${API_EVENT_TYPE.MESSAGE}`] = EVENT_PRIORITY.APIMessageEvent; const LOG_TYPE = Object.keys(window.console).reduce((eax, current) => { const item = {}; item[`${current}`] = current; return {...eax, ...item}; }, {}); // constants const EVENT_DELAY = 500; const COUNT = { animation: 0, message: 0 }; const queue = new EventQueue(); // helper const animateIt = event => !isAnimatingGiftUI() && animateGift(event); const log = (type, ...values) => window.dbg && window.console[`${type}`](...values); // logic const app = () => { if(!queue.isEmpty()){ const first = queue.front(); if([API_EVENT_TYPE.MESSAGE, API_EVENT_TYPE.GIFT].includes(first.event.type)){ addMessage(first.event); log(LOG_TYPE.log, '🚀 Fired->addMessage:\t', first); queue.dequeue(); }else if([API_EVENT_TYPE.ANIMATED_GIFT].includes(first.event.type) && !isPossiblyAnimatingGift()){ animateIt(first.event); log(LOG_TYPE.log, '🚀 Fired->animateGift:\t', first); queue.dequeue(); } } } // event stream api.setEventHandler((events) => { if(events.length > 0){ // computed instant events data count from stream for logs let fromCount = { animation: 0, message: 0, } events.map(event => { // queuing queue.enqueue(event, PRIORITY_MAP[event.type]); // for only log, +(bool) >> int fromCount.animation += +[API_EVENT_TYPE.ANIMATED_GIFT].includes(event.type); fromCount.message += +[API_EVENT_TYPE.MESSAGE, API_EVENT_TYPE.GIFT].includes(event.type); // general sum COUNT.animation += fromCount.animation; COUNT.message += fromCount.message; }); // when coming new data from event stream log(LOG_TYPE.table, { "all_new": events.length, "new_animations": fromCount.animation, "new_messages": fromCount.message, "queue_messages": COUNT.message, "queue_animations": COUNT.animation, }); } }) window.addEventListener('DOMContentLoaded', e => { app(); // when dom loaded, once invoke setInterval(app, EVENT_DELAY); // queuing like behavior observing }); // NOTE: UI helper methods from `dom_updates` are already imported above.