diff --git a/admin/src/Api.js b/admin/src/Api.js index d3ef607..4e23b01 100644 --- a/admin/src/Api.js +++ b/admin/src/Api.js @@ -1,7 +1,7 @@ -import firebase from 'firebase/app'; -import 'firebase/auth'; +import firebase from "firebase/app"; +import "firebase/auth"; -const API = 'http://localhost:5000/watch-party-crx/us-central1/admin'; +const API = "http://localhost:5000/watch-party-crx/us-central1/admin"; const fetcher = async ({ endpoint, method, bodyData }) => { const idToken = await firebase.auth().currentUser.getIdToken(); @@ -10,15 +10,21 @@ const fetcher = async ({ endpoint, method, bodyData }) => { method, headers: { Authorization: `Token ${idToken}:${firebase.auth().currentUser.uid}`, - 'Access-Control-Allow-Origin': '*', + "Access-Control-Allow-Origin": "*", }, body, }); }; -export const getPartyDashboardData = () => fetcher({ endpoint: 'partyDashboard', method: 'GET' }); -export const getServerDashboardData = () => fetcher({ endpoint: 'serverDashboard', method: 'GET' }); +export const getPartyDashboardData = () => + fetcher({ endpoint: "partyDashboard", method: "GET" }); +export const getServerDashboardData = () => + fetcher({ endpoint: "serverDashboard", method: "GET" }); export const postUpdateVersionActive = (bodyData) => - fetcher({ endpoint: 'updateVersionActive', method: 'POST', bodyData }); + fetcher({ endpoint: "updateVersionActive", method: "POST", bodyData }); export const postUpdateVideoSelectors = (bodyData) => - fetcher({ endpoint: 'updateVideoSelectors', method: 'POST', bodyData }); + fetcher({ endpoint: "updateVideoSelectors", method: "POST", bodyData }); +export const postUpdateVideoNameSelectors = (bodyData) => + fetcher({ endpoint: "updateVideoNameSelectors", method: "POST", bodyData }); +export const postSendMessage = (bodyData) => + fetcher({ endpoint: "sendMessage", method: "POST", bodyData }); diff --git a/admin/src/Dashboard.jsx b/admin/src/Dashboard.jsx index 2bf420f..76fa77d 100644 --- a/admin/src/Dashboard.jsx +++ b/admin/src/Dashboard.jsx @@ -1,7 +1,7 @@ -import React, { Component } from 'react'; -import firebase from 'firebase/app'; -import 'firebase/auth'; -import 'firebase/firestore'; +import React, { Component } from "react"; +import firebase from "firebase/app"; +import "firebase/auth"; +import "firebase/firestore"; import { Button, Card, @@ -15,13 +15,21 @@ import { Row, Col, PageHeader, -} from 'antd'; + InputNumber, + Descriptions, + Badge, + Collapse, + Modal, + Select, +} from "antd"; import { getPartyDashboardData, getServerDashboardData, postUpdateVersionActive, postUpdateVideoSelectors, -} from './Api'; + postUpdateVideoNameSelectors, + postSendMessage, +} from "./Api"; import { CheckCircleTwoTone, CloseCircleTwoTone, @@ -35,88 +43,60 @@ import { LogoutOutlined, SendOutlined, PlusCircleOutlined, -} from '@ant-design/icons'; +} from "@ant-design/icons"; const PLAYER_SOURCES = [ - { text: 'Netflix', value: 'netflix.' }, - { text: 'Amazon', value: 'amazon.' }, - { text: 'Hulu', value: 'hulu.' }, - { text: 'Disney+', value: 'disneyplus.' }, - { text: 'YouTube', value: 'youtube.' }, - { text: 'HBO Now', value: 'hbonow.' }, - { text: 'HBO Max', value: 'hbomax.' }, - { text: 'Peacock', value: 'peacocktv.' }, - { text: 'Google Play', value: 'play.google.' }, - { text: 'Crunchyroll', value: 'crunchyroll.' }, + { text: "Netflix", value: "netflix." }, + { text: "Amazon", value: "amazon." }, + { text: "Hulu", value: "hulu." }, + { text: "Disney+", value: "disneyplus." }, + { text: "YouTube", value: "youtube." }, + { text: "HBO Now", value: "hbonow." }, + { text: "HBO Max", value: "hbomax." }, + { text: "Peacock", value: "peacocktv." }, + { text: "Google Play", value: "play.google." }, + { text: "Crunchyroll", value: "crunchyroll." }, ]; -const partiesColumns = [ +const MESSAGE_TYPES = [ { - title: 'Party Name', - dataIndex: 'id', - sorter: (a, b) => (a.id.toLowerCase() < b.id.toLowerCase() ? -1 : 1), + text: "REPLY", + value: "interactive/text", }, { - title: 'Source', - dataIndex: 'source', - sorter: (a, b) => (a.source.toLowerCase() < b.source.toLowerCase() ? -1 : 1), - filters: PLAYER_SOURCES, - onFilter: (v, r) => r.source === v, - render: (a) => PLAYER_SOURCES.filter((p) => p.value === a)[0].text, + text: "LINK", + value: "interactive/link", }, { - title: 'Created At', - dataIndex: 'createTime', - defaultSortOrder: 'descend', - sorter: (a, b) => Date.parse(a.createTime) - Date.parse(b.createTime), - render: (a) => new Date(Date.parse(a)).toLocaleString(), - }, - { - title: 'Updated At', - dataIndex: 'updateTime', - sorter: (a, b) => Date.parse(a.updateTime) - Date.parse(b.updateTime), - render: (a) => { - const minutes = (Date.now() - Date.parse(a)) / 60000; - const dS = new Date(a).toLocaleString(); - return ( - {dS} - ); - }, - }, - { - title: 'On Call', - dataIndex: 'onCall', - filters: [ - { - text: 'On Call', - value: true, - }, - { - text: 'Not On Call', - value: false, - }, - ], - render: (a) => - a ? ( - - ) : ( - - ), - onFilter: (v, r) => r.onCall === v, + text: "MESSAGE", + value: "", }, ]; +const emptyMessage = { + type: "", + text: "", + uid: "", + name: "", + endpoint: "", + // time set on send, +}; + export default class Dashboard extends Component { constructor(props) { super(props); this.state = { partiesOverview: null, - activeTab: '1', + activeTab: "1", refreshing: true, selectedParties: [], nameSelectors: {}, selectors: {}, versions: [], + activeLimit: 5, + afkLimit: 10, + messageModel: false, + newMessage: emptyMessage, }; this.serverStatusListener = null; } @@ -128,7 +108,6 @@ export default class Dashboard extends Component { getPartyDashboardData() .then((res) => res.json()) .then((res) => { - console.log(res); this.setState({ partiesOverview: res.partyData, refreshing: false }); }); }; @@ -141,14 +120,16 @@ export default class Dashboard extends Component { this.setState({ refreshing: true }); const { activeTab } = this.state; switch (activeTab) { - case '1': + case "1": this.getPartiesOverview(); break; - case '2': - case '3': - case '4': + case "2": + case "3": + case "4": this.getVersionsOverview(); break; + default: + break; } }; tabChange = (activeTab) => { @@ -157,14 +138,18 @@ export default class Dashboard extends Component { updateVersionMessage = (message, version) => { this.setState({ refreshing: true }); const { versions } = this.state; - const versionData = versions.filter((v) => v.version.split('.').join('') === version)[0]; + const versionData = versions.filter( + (v) => v.version.split(".").join("") === version + )[0]; versionData.message = message; this.updateVersion(version, versionData); }; updateVersionActive = (active, version) => { this.setState({ refreshing: true }); const { versions } = this.state; - const versionData = versions.filter((v) => v.version.split('.').join('') === version)[0]; + const versionData = versions.filter( + (v) => v.version.split(".").join("") === version + )[0]; versionData.active = active; this.updateVersion(version, versionData); }; @@ -174,13 +159,10 @@ export default class Dashboard extends Component { .catch((err) => notification.error({ description: err.message, - message: 'Unable To Update', + message: "Unable To Update", }) ); }; - viewPartyDetails(party) { - console.log(party); - } getSourceName(source) { return PLAYER_SOURCES.filter((p) => p.value === source)[0].text; } @@ -197,7 +179,7 @@ export default class Dashboard extends Component { .catch((err) => { notification.error({ description: err.message, - message: 'Unable To Update', + message: "Unable To Update", }); this.setState({ refreshing: false }); @@ -210,32 +192,43 @@ export default class Dashboard extends Component { }; handleAlertTitleChange = (value, version, index) => { const { versions } = this.state; - versions.filter((vs) => vs.version === version)[0].alerts[index].title = value; + versions.filter((vs) => vs.version === version)[0].alerts[ + index + ].title = value; this.setState({ versions }); }; handleAlertDescriptionChange = (value, version, index) => { const { versions } = this.state; - versions.filter((vs) => vs.version === version)[0].alerts[index].description = value; + versions.filter((vs) => vs.version === version)[0].alerts[ + index + ].description = value; this.setState({ versions }); }; handleAlertLinkChange = (value, version, index) => { const { versions } = this.state; - versions.filter((vs) => vs.version === version)[0].alerts[index].link = value; + versions.filter((vs) => vs.version === version)[0].alerts[ + index + ].link = value; this.setState({ versions }); }; handleAlertLinkTextChange = (value, version, index) => { const { versions } = this.state; - versions.filter((vs) => vs.version === version)[0].alerts[index].linkText = value; + versions.filter((vs) => vs.version === version)[0].alerts[ + index + ].linkText = value; this.setState({ versions }); }; deleteAlert = (version, index) => { const { versions } = this.state; versions.filter((vs) => vs.version === version)[0].alerts.splice(index, 1); if ( - versions.filter((vs) => vs.version === version)[0].importantAlert === index || + versions.filter((vs) => vs.version === version)[0].importantAlert === + index || versions.filter((vs) => vs.version === version)[0].alerts.length < index ) { - versions.filter((vs) => vs.version === version)[0].importantAlert = undefined; + versions.filter( + (vs) => vs.version === version + )[0].importantAlert = undefined; } this.setState({ versions }); }; @@ -244,10 +237,10 @@ export default class Dashboard extends Component { versions .filter((vs) => vs.version === version)[0] .alerts.push({ - title: '', - description: '', - link: '', - linkText: '', + title: "", + description: "", + link: "", + linkText: "", }); this.setState({ versions }); }; @@ -255,11 +248,12 @@ export default class Dashboard extends Component { this.setState({ refreshing: true }); const { versions } = this.state; const versionData = versions.filter((v) => v.version === version)[0]; - if (versionData.alerts.length > 0) this.updateVersion(version.split('.').join(''), versionData); + if (versionData.alerts.length > 0) + this.updateVersion(version.split(".").join(""), versionData); else { notification.error({ - message: 'Missing Alerts', - description: 'Must have atleast one alert', + message: "Missing Alerts", + description: "Must have atleast one alert", }); this.setState({ refreshing: false }); @@ -268,7 +262,9 @@ export default class Dashboard extends Component { changeImportantAlert = (active, version, index) => { const { versions } = this.state; if (active === false) { - versions.filter((vs) => vs.version === version)[0].importantAlert = undefined; + versions.filter( + (vs) => vs.version === version + )[0].importantAlert = undefined; } else { versions.filter((vs) => vs.version === version)[0].importantAlert = index; } @@ -277,16 +273,73 @@ export default class Dashboard extends Component { getAddMessageText = () => { const { activeTab } = this.state; switch (activeTab) { - case '1': - return 'Send Message'; - case '2': - return 'Add Version'; - case '3': - return 'Add Video Selector'; - case '4': - return 'Add Name Selector'; + case "1": + return "Send Message"; + case "2": + return "Add Version"; + case "3": + return "Add Video Selector"; + case "4": + return "Add Name Selector"; + default: + return ""; } }; + getPrettyTime = (t) => { + return t > 3600 + ? new Date(t * 1000).toISOString().substr(11, 8) + : new Date(t * 1000).toISOString().substr(14, 5); + }; + handleVideoNameSelectorChange = (event, source, type) => { + const { nameSelectors } = this.state; + nameSelectors[source][type] = event.target.value; + this.setState({ nameSelectors }); + }; + updateVideoNameSelector = () => { + this.setState({ refreshing: true }); + const { nameSelectors } = this.state; + postUpdateVideoNameSelectors({ nameSelectors }) + .then(() => this.refreshData()) + .catch((err) => { + notification.error({ + description: err.message, + message: "Unable To Update", + }); + + this.setState({ refreshing: false }); + }); + }; + sendOrAdd = () => { + const { activeTab } = this.state; + switch (activeTab) { + case "1": + this.setState({ messageModel: true }); + break; + default: + break; + } + }; + editMessage = (value, field) => { + const { newMessage } = this.state; + newMessage[field] = value; + this.setState({ newMessage }); + }; + sendMessage = async () => { + this.setState({ refreshing: true }); + const { selectedParties, newMessage } = this.state; + newMessage.time = new Date().toLocaleString(); + await postSendMessage({ selectedParties, newMessage }).catch((err) => { + notification.error({ + description: err.message, + message: "Unable To Send Message", + }); + }); + this.setState({ + refreshing: false, + newMessage: emptyMessage, + messageModel: false, + }); + }; render() { const { partiesOverview, @@ -296,26 +349,133 @@ export default class Dashboard extends Component { versions, nameSelectors, selectors, + activeLimit, + afkLimit, + messageModel, + newMessage, } = this.state; - const versionsColumns = [ + const partiesColumns = [ { - title: 'Version', - dataIndex: 'version', - defaultSortOrder: 'descend', - sorter: (a, b) => - parseInt(a.version.split('.').join('')) - parseInt(b.version.split('.').join('')), + title: "Party Name", + dataIndex: "id", + sorter: (a, b) => (a.id.toLowerCase() < b.id.toLowerCase() ? -1 : 1), }, { - title: 'Active', - dataIndex: 'active', + title: "Source", + dataIndex: "source", + sorter: (a, b) => + a.source.toLowerCase() < b.source.toLowerCase() ? -1 : 1, + filters: [ + ...PLAYER_SOURCES, + { + text: "Is Playing", + value: "PLAYING", + }, + { + text: "Is Paused", + value: "PAUSED", + }, + ], + onFilter: (v, r) => r.source === v || r.status === v, + render: (a, r) => ( + + ), + }, + { + title: "Created At", + dataIndex: "createTime", + defaultSortOrder: "descend", + sorter: (a, b) => Date.parse(a.createTime) - Date.parse(b.createTime), + render: (a) => new Date(Date.parse(a)).toLocaleString(), + }, + { + title: "Updated At", + dataIndex: "updateTime", filters: [ { - text: 'Active', + text: "Active", + value: "a", + }, + { + text: "Inactive", + value: "i", + }, + ], + onFilter: (v, r) => + v === "a" + ? (Date.now() - Date.parse(r.updateTime)) / 60000 < afkLimit + : (Date.now() - Date.parse(r.updateTime)) / 60000 > afkLimit, + sorter: (a, b) => Date.parse(a.updateTime) - Date.parse(b.updateTime), + render: (a) => { + const minutes = (Date.now() - Date.parse(a)) / 60000; + const dS = new Date(a).toLocaleString(); + return ( + + {dS} + + ); + }, + }, + { + title: "On Call", + dataIndex: "onCall", + filters: [ + { + text: "On Call", value: true, }, { - text: 'Inactive', + text: "Not On Call", + value: false, + }, + ], + render: (a) => + a ? ( + + ) : ( + + ), + onFilter: (v, r) => r.onCall === v, + }, + ]; + + const versionsColumns = [ + { + title: "Version", + dataIndex: "version", + defaultSortOrder: "descend", + sorter: (a, b) => + parseInt(a.version.split(".").join("")) - + parseInt(b.version.split(".").join("")), + }, + { + title: "Active", + dataIndex: "active", + filters: [ + { + text: "Active", + value: true, + }, + { + text: "Inactive", value: false, }, ], @@ -323,18 +483,22 @@ export default class Dashboard extends Component { render: (a, r) => ( this.updateVersionActive(c, r.version.split('.').join(''))} + onChange={(c) => + this.updateVersionActive(c, r.version.split(".").join("")) + } checked={a} /> ), }, { - title: 'Inactive Message', - dataIndex: 'message', + title: "Inactive Message", + dataIndex: "message", render: (a, r) => ( this.updateVersionMessage(v, r.version.split('.').join(''))} + onSearch={(v) => + this.updateVersionMessage(v, r.version.split(".").join("")) + } value={a} onChange={(e) => this.handleVersionMessageChange(e, r.version)} enterButton={} @@ -344,9 +508,98 @@ export default class Dashboard extends Component { ]; return (
+ firebase.auth().signOut()} + key="1" + type="primary" + danger + icon={} + > + Logout + , + ]} + /> + this.setState({ messageModel: false }), + }} + okButtonProps={{ + disabled: refreshing, + icon: , + onClick: this.sendMessage, + }} + okText="Send Message" + > + + this.editMessage(e.target.value, "text")} + placeholder="Message" + /> + {(newMessage.type === "interactive/link" || + newMessage.type === "interactive/text") && ( + this.editMessage(e.target.value, "endpoint")} + placeholder={ + newMessage.type === "interactive/link" ? "Link" : "Endpoint" + } + /> + )} + {newMessage.type === "interactive/link" && ( + this.editMessage(e.target.value, "name")} + placeholder="Link Text" + /> + )} + + this.setState({ activeLimit })} + /> + + ), + activeTab === "1" && ( + + this.setState({ afkLimit })} + /> + + ), , - , + activeTab === "3" && ( + + ), + activeTab === "4" && ( + + ), ]} /> @@ -378,11 +647,12 @@ export default class Dashboard extends Component { this.setState({ selectedParties }), + onChange: (selectedParties) => + this.setState({ selectedParties }), }} loading={partiesOverview === null || refreshing} pagination={{ - position: ['bottomCenter'], + position: ["bottomCenter"], total: partiesOverview ? partiesOverview.length : 0, showTotal: (t, r) => `${r[0]} to ${r[1]} of ${t}`, defaultPageSize: 10, @@ -390,23 +660,52 @@ export default class Dashboard extends Component { pageSizeOptions: [10, 25, 50], }} rowKey={(p) => p.id} - onRow={(r) => ({ - onClick: () => this.viewPartyDetails(r), - })} dataSource={partiesOverview} columns={partiesColumns} + expandable={{ + rowExpandable: (r) => true, + expandedRowClassName: () => "party-details-wrapper", + expandedRowRender: (r) => ( + + + {r.userCount} + + + {r.messageCount} + + + {r.showName} + + + {this.getPrettyTime(r.time)} + + + + + + ), + }} />
p.version} dataSource={versions} columns={versionsColumns} expandable={{ rowExpandable: (r) => r.alerts, - expandedRowClassName: () => 'alerts-wrapper', + expandedRowClassName: () => "alerts-wrapper", expandedRowRender: (r) => ( {r.alerts.map((a, i) => ( @@ -422,9 +721,15 @@ export default class Dashboard extends Component { />, - this.changeImportantAlert(active, r.version, i) + this.changeImportantAlert( + active, + r.version, + i + ) + } + checked={ + r.importantAlert && r.importantAlert === i } - checked={r.importantAlert && r.importantAlert === i} />, ]} > @@ -432,7 +737,11 @@ export default class Dashboard extends Component { placeholder="title" value={a.title} onChange={(e) => - this.handleAlertTitleChange(e.target.value, r.version, i) + this.handleAlertTitleChange( + e.target.value, + r.version, + i + ) } /> - this.handleAlertDescriptionChange(e.target.value, r.version, i) + this.handleAlertDescriptionChange( + e.target.value, + r.version, + i + ) } /> Link @@ -449,14 +762,22 @@ export default class Dashboard extends Component { value={a.linkText} placeholder="Link Text" onChange={(e) => - this.handleAlertLinkTextChange(e.target.value, r.version, i) + this.handleAlertLinkTextChange( + e.target.value, + r.version, + i + ) } /> - this.handleAlertLinkChange(e.target.value, r.version, i) + this.handleAlertLinkChange( + e.target.value, + r.version, + i + ) } /> @@ -472,7 +793,10 @@ export default class Dashboard extends Component { - this.saveAlerts(r.version)} className="add-alert-card"> + this.saveAlerts(r.version)} + className="add-alert-card" + > @@ -493,19 +817,41 @@ export default class Dashboard extends Component { onChange={(e) => this.handleVideoSelectorChange(e, source)} /> ))} - - name selectors + + {Object.keys(nameSelectors) + .sort() + .map((source) => ( + + + this.handleVideoNameSelectorChange(e, source, "show") + } + /> + + this.handleVideoNameSelectorChange( + e, + source, + "episode" + ) + } + /> + + ))} + diff --git a/admin/src/index.css b/admin/src/index.css index 541ad3c..056bde9 100644 --- a/admin/src/index.css +++ b/admin/src/index.css @@ -1,5 +1,5 @@ -@import '~antd/dist/antd.css'; -@import '~antd/dist/antd.dark.css' screen and (prefers-color-scheme: dark); +@import "~antd/dist/antd.css"; +@import "~antd/dist/antd.dark.css" screen and (prefers-color-scheme: dark); #root, #root .app-wrapper { @@ -29,8 +29,12 @@ border-top-left-radius: 0px; } +.dashboard-wrapper { + padding: 35px 70px; + padding-top: 0; +} + .dashboard-wrapper .ant-card { - margin: 35px 70px; border-radius: 26px; filter: drop-shadow(0px 0px 5px rgba(221, 221, 221, 0.438)); } @@ -52,6 +56,7 @@ margin: 5px 2px !important; filter: none !important; border-radius: initial !important; + max-width: 400px; } .alerts-wrapper .ant-card-body input, @@ -81,3 +86,47 @@ span#call-icon svg { width: 25px; height: 25px; } + +.title-header { + margin-bottom: 17.5px; +} + +.title-header .ant-page-header-heading-title, +.title-header .ant-page-header-heading-sub-title { + letter-spacing: 1.5px; +} + +.party-details-wrapper > td { + padding: 0 !important; +} + +.party-details-wrapper .ant-descriptions-view table { + width: 100% !important; +} + +.party-details-wrapper .ant-descriptions-view table td, +.party-details-wrapper .ant-descriptions-view table th { + border-bottom: 1px solid #f0f0f0; +} + +.ant-page-header .ant-badge { + margin-right: 10px; +} + +.ant-page-header .ant-badge-count { + right: 100%; + z-index: 9; + top: -10%; +} + +.ant-page-header .active .ant-badge-count { + background: green; +} +.ant-page-header .afk .ant-badge-count { + background: rgb(79, 120, 209); +} + +.ant-modal-body > * { + margin: 5px 0px; + width: 100%; +} diff --git a/app/src/Inactive.js b/app/src/Inactive.js index a82fffc..6d0d8fb 100644 --- a/app/src/Inactive.js +++ b/app/src/Inactive.js @@ -9,14 +9,16 @@ ALL CONTENT ON WATCH PARTY'S WEBSITE OR IN WATCH PARTY'S CHROME EXTENSION APP, U CITED ARE THE SOLE INTELECTUAL PROPERTY OF AYUSHYA AMITABH, AND MAY NOT BE COPIED, EDITED, OR REPRODUCED WITHOUT PERMISSION. ************************************************************************/ -import React from 'react'; -import { Typography } from '@material-ui/core'; -import { Warning } from '@material-ui/icons'; +import React from "react"; +import { Typography } from "@material-ui/core"; +import { Warning } from "@material-ui/icons"; const Inactive = (props) => (
- SORRY, WATCH PARTY IS UNAVAILABLE RIGHT NOW + + SORRY, WATCH PARTY IS UNAVAILABLE RIGHT NOW + {props.status}
); diff --git a/functions/admin/index.js b/functions/admin/index.js index 586d188..d200e3e 100644 --- a/functions/admin/index.js +++ b/functions/admin/index.js @@ -1,10 +1,14 @@ -const superAuth = require('./superAuth'); -const partyDashboard = require('./partyDashboard'); -const serverDashboard = require('./serverDashboard'); -const updateVersionActive = require('./updateVersionActive'); -const updateVideoSelectors = require('./updateVideoSelectors'); +const superAuth = require("./superAuth"); +const partyDashboard = require("./partyDashboard"); +const serverDashboard = require("./serverDashboard"); +const updateVersionActive = require("./updateVersionActive"); +const updateVideoSelectors = require("./updateVideoSelectors"); +const updateVideoNameSelectors = require("./updateVideoNameSelectors"); +const sendMessage = require("./sendMessage"); module.exports = { + sendMessage, + updateVideoNameSelectors, updateVideoSelectors, updateVersionActive, serverDashboard, diff --git a/functions/admin/partyDashboard.js b/functions/admin/partyDashboard.js index 2b0b335..d8dc9f8 100644 --- a/functions/admin/partyDashboard.js +++ b/functions/admin/partyDashboard.js @@ -1,14 +1,14 @@ /* eslint-disable promise/no-nesting */ -const admin = require('firebase-admin'); -const Logger = require('../Logger'); -const mLogger = new Logger('admin', 'partyDashboard'); +const admin = require("firebase-admin"); +const Logger = require("../Logger"); +const mLogger = new Logger("admin", "partyDashboard"); module.exports = async (req, res) => { mLogger.start(); var partyData = await admin .firestore() - .collection('rooms') + .collection("rooms") .get() .then((roomSnapshot) => { const docData = []; @@ -18,38 +18,43 @@ module.exports = async (req, res) => { createTime: doc.createTime.toDate().toUTCString(), updateTime: doc.updateTime.toDate().toUTCString(), source: doc.data().player.source, - onCall: doc.data().hasOwnProperty('conference'), - showName: doc.data().player.showName || '', + onCall: doc.data().hasOwnProperty("conference"), + showName: doc.data().player.showName || "", time: doc.data().player.time || 0, - status: doc.data().player.status || '', + status: doc.data().player.status || "", }); }); return docData; }) .catch((err) => { - mLogger.error('FAILED TO RESOLVE PARTIES', err); - res.status(500).send({ message: 'Unable to get all parties', error: err }); + mLogger.error("FAILED TO RESOLVE PARTIES", err); + res + .status(500) + .send({ message: "Unable to get all parties", error: err }); }); - await partyData.forEach((party) => { - const messageCount = admin + const p = await partyData.map(async (party) => { + const messageCount = await admin .firestore() - .collection('rooms') + .collection("rooms") .doc(party.id) - .collection('messages') + .collection("messages") .get() .then((snap) => snap.size); - const userCount = admin + const userCount = await admin .firestore() - .collection('rooms') + .collection("rooms") .doc(party.id) - .collection('users') + .collection("users") .get() .then((snap) => snap.size); party.messageCount = messageCount || 0; party.userCount = userCount || 0; + return party; }); - mLogger.success('GOT ALL PARTIES', { parties: partyData.length }); - return res.status(200).send({ partyData }); + return Promise.all(p).then((v) => { + mLogger.success("GOT ALL PARTIES", { parties: partyData.length }); + return res.status(200).send({ partyData }); + }); }; diff --git a/functions/admin/sendMessage.js b/functions/admin/sendMessage.js new file mode 100644 index 0000000..38d3b3c --- /dev/null +++ b/functions/admin/sendMessage.js @@ -0,0 +1,32 @@ +const admin = require("firebase-admin"); +const Logger = require("../Logger"); +const mLogger = new Logger("admin", "sendMessage"); + +module.exports = async (req, res) => { + mLogger.start(); + const { selectedParties, newMessage } = JSON.parse(req.body); + if (selectedParties && newMessage && selectedParties.length > 0) { + if (newMessage.text && newMessage.time && newMessage.uid) { + newMessage.time = new Date(); + await selectedParties.map(async (partyId) => { + await admin + .firestore() + .collection("rooms") + .doc(partyId) + .collection("messages") + .add(newMessage) + .catch((err) => { + mLogger.error("FAILED TO SEND MESSAGE", { partyId, err }); + }); + }); + mLogger.success("SENT MESSAGES", { newMessage, selectedParties }); + res.status(200).send({ message: "SENT MESSAGES" }); + } else { + mLogger.error("MESSAGE NOT VALID"); + res.status(500).send({ message: "Message is missing fields" }); + } + } else { + mLogger.error("VALUES NOT RECEIVED"); + res.status(500).send({ message: "Values not received" }); + } +}; diff --git a/functions/admin/updateVideoNameSelectors.js b/functions/admin/updateVideoNameSelectors.js new file mode 100644 index 0000000..9ceefbe --- /dev/null +++ b/functions/admin/updateVideoNameSelectors.js @@ -0,0 +1,26 @@ +const admin = require("firebase-admin"); +const Logger = require("../Logger"); +const mLogger = new Logger("admin", "updateVideoNameSelectors"); + +module.exports = async (req, res) => { + mLogger.start(); + const { nameSelectors } = JSON.parse(req.body); + if (nameSelectors) { + await admin + .firestore() + .collection("server") + .doc("status") + .update({ + nameSelectors, + }) + .catch((err) => { + mLogger.error("FAILED TO UPDATE", err); + res.status(500).send({ message: "FAILED TO UPDATE", error: err }); + }); + mLogger.success("UPDATED NAME SELECTORS"); + res.status(200).send({ message: "Updated video name selectors" }); + } else { + mLogger.error("VALUES NOT RECEIVED"); + res.status(500).send({ message: "Values not received" }); + } +}; diff --git a/functions/app.js b/functions/app.js index 7c97cbc..2b5c682 100644 --- a/functions/app.js +++ b/functions/app.js @@ -1,40 +1,46 @@ -const FunctionModules = require('./modules/index'); -const Webhooks = require('./webhooks/index'); -const AdminModules = require('./admin/index'); -const express = require('express'); -const cors = require('cors'); +const FunctionModules = require("./modules/index"); +const Webhooks = require("./webhooks/index"); +const AdminModules = require("./admin/index"); +const express = require("express"); +const cors = require("cors"); // WEBHOOKS const webhooks = express(); webhooks.use(cors({ origin: true })); -webhooks.all('/conferenceEnded', Webhooks.conferenceEnd); -webhooks.all('/scheduledCleanup', Webhooks.scheduledCleanUp); -webhooks.all('/ask', Webhooks.ask); -webhooks.all('/userCleanUp', Webhooks.userCleanUp); +webhooks.all("/conferenceEnded", Webhooks.conferenceEnd); +webhooks.all("/scheduledCleanup", Webhooks.scheduledCleanUp); +webhooks.all("/ask", Webhooks.ask); +webhooks.all("/userCleanUp", Webhooks.userCleanUp); // VERSION 1 const app = express(); app.use(cors({ origin: true })); app.use(FunctionModules.authenticator); -app.get('/validator/:partyName', FunctionModules.tokenValidator); -app.get('/refresher/:token', FunctionModules.tokenRefresher); -app.post('/create', FunctionModules.createRoom); -app.post('/join', FunctionModules.joinRoom); -app.post('/message', FunctionModules.sendMessage); -app.post('/updatePlayer', FunctionModules.updatePlayer); -app.post('/updateVoxeet', FunctionModules.updateVoxeet); -app.delete('/leave/:rid/users/:uid', FunctionModules.leaveRoom); -app.delete('/delete/:rid', FunctionModules.deleteRoom); -app.put('/updateAdminOnly/:rid/:adminOnly', FunctionModules.updateAdminOnly); +app.get("/validator/:partyName", FunctionModules.tokenValidator); +app.get("/refresher/:token", FunctionModules.tokenRefresher); +app.post("/create", FunctionModules.createRoom); +app.post("/join", FunctionModules.joinRoom); +app.post("/message", FunctionModules.sendMessage); +app.post("/updatePlayer", FunctionModules.updatePlayer); +app.post("/updateVoxeet", FunctionModules.updateVoxeet); +app.delete("/leave/:rid/users/:uid", FunctionModules.leaveRoom); +app.delete("/delete/:rid", FunctionModules.deleteRoom); +app.put("/updateAdminOnly/:rid/:adminOnly", FunctionModules.updateAdminOnly); // ADMIN const admin = express(); -admin.use(cors({ origin: ['http://localhost:3000', 'https://admin.chromewatchparty.com/*'] })); +admin.use( + cors({ + origin: ["http://localhost:3000", "https://admin.chromewatchparty.com/*"], + }) +); admin.use(AdminModules.superAuth); -admin.get('/partyDashboard', AdminModules.partyDashboard); -admin.get('/serverDashboard', AdminModules.serverDashboard); -admin.post('/updateVersionActive', AdminModules.updateVersionActive); -admin.post('/updateVideoSelectors', AdminModules.updateVideoSelectors); +admin.get("/partyDashboard", AdminModules.partyDashboard); +admin.get("/serverDashboard", AdminModules.serverDashboard); +admin.post("/updateVersionActive", AdminModules.updateVersionActive); +admin.post("/updateVideoSelectors", AdminModules.updateVideoSelectors); +admin.post("/updateVideoNameSelectors", AdminModules.updateVideoNameSelectors); +admin.post("/sendMessage", AdminModules.sendMessage); module.exports = { v1: app,