v1.6.2/mass message, filters

This commit is contained in:
2020-08-15 18:02:04 -04:00
parent 43dc657582
commit 917d30ce05
9 changed files with 688 additions and 212 deletions
+14 -8
View File
@@ -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 });
+501 -155
View File
@@ -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 (
<Tag color={minutes <= 5 ? 'success' : minutes <= 15 ? 'processing' : 'default'}>{dS}</Tag>
);
},
},
{
title: 'On Call',
dataIndex: 'onCall',
filters: [
{
text: 'On Call',
value: true,
},
{
text: 'Not On Call',
value: false,
},
],
render: (a) =>
a ? (
<CheckCircleTwoTone color="#c0f300" id="call-icon" />
) : (
<CloseCircleTwoTone id="call-icon" />
),
onFilter: (v, r) => r.onCall === v,
text: "MESSAGE",
value: "",
},
];
const emptyMessage = {
type: "",
text: "",
uid: "<system>",
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) => (
<Badge
status={
r.status === "PLAYING"
? "success"
: r.status === "PAUSED"
? "warning"
: "error"
}
text={this.getSourceName(a)}
/>
),
},
{
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 (
<Tag
color={
minutes <= activeLimit
? "success"
: minutes <= afkLimit
? "processing"
: "default"
}
>
{dS}
</Tag>
);
},
},
{
title: "On Call",
dataIndex: "onCall",
filters: [
{
text: "On Call",
value: true,
},
{
text: 'Inactive',
text: "Not On Call",
value: false,
},
],
render: (a) =>
a ? (
<CheckCircleTwoTone twoToneColor="#c0f300" id="call-icon" />
) : (
<CloseCircleTwoTone twoToneColor="#ff5f5f" id="call-icon" />
),
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) => (
<Switch
disabled={refreshing}
onChange={(c) => 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) => (
<Input.Search
disabled={refreshing}
onSearch={(v) => 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={<EnterOutlined />}
@@ -344,24 +508,11 @@ export default class Dashboard extends Component {
];
return (
<div className="dashboard-wrapper">
<Card>
<PageHeader
className="title-header"
title="WATCH PARTY"
subTitle="ADMIN DASHBOARD"
extra={[
<Button
key="3"
loading={refreshing}
disabled={refreshing}
onClick={this.refreshData}
shape="circle"
icon={refreshing ? <LoadingOutlined /> : <ReloadOutlined />}
/>,
<Button
key="2"
icon={activeTab === '1' ? <SendOutlined /> : <PlusCircleOutlined />}
disabled={activeTab === '1' && selectedParties.length === 0}
>
{this.getAddMessageText()}
</Button>,
<Button
onClick={() => firebase.auth().signOut()}
key="1"
@@ -373,16 +524,135 @@ export default class Dashboard extends Component {
</Button>,
]}
/>
<Modal
visible={messageModel}
closable={false}
destroyOnClose
maskClosable={false}
cancelButtonProps={{
disabled: refreshing,
onClick: () => this.setState({ messageModel: false }),
}}
okButtonProps={{
disabled: refreshing,
icon: <SendOutlined />,
onClick: this.sendMessage,
}}
okText="Send Message"
>
<Select
onChange={(e) => this.editMessage(e, "type")}
value={newMessage.type}
placeholder="Message Type"
disabled={refreshing}
>
{MESSAGE_TYPES.map((v) => (
<Select.Option key={`msg-type-option-${v.value}`} value={v.value}>
{v.text}
</Select.Option>
))}
</Select>
<Input.TextArea
disabled={refreshing}
value={newMessage.text}
onChange={(e) => this.editMessage(e.target.value, "text")}
placeholder="Message"
/>
{(newMessage.type === "interactive/link" ||
newMessage.type === "interactive/text") && (
<Input
disabled={refreshing}
value={newMessage.endpoint}
onChange={(e) => this.editMessage(e.target.value, "endpoint")}
placeholder={
newMessage.type === "interactive/link" ? "Link" : "Endpoint"
}
/>
)}
{newMessage.type === "interactive/link" && (
<Input
disabled={refreshing}
value={newMessage.name}
onChange={(e) => this.editMessage(e.target.value, "name")}
placeholder="Link Text"
/>
)}
</Modal>
<Card>
<PageHeader
extra={[
activeTab === "1" && (
<Badge key="5" count="ACTIVE" className="active">
<InputNumber
prefix="Active"
value={activeLimit}
onChange={(activeLimit) => this.setState({ activeLimit })}
/>
</Badge>
),
activeTab === "1" && (
<Badge key="4" count="AFK" className="afk">
<InputNumber
prefix="AFK"
key="4"
value={afkLimit}
onChange={(afkLimit) => this.setState({ afkLimit })}
/>
</Badge>
),
<Button
key="3"
loading={refreshing}
disabled={refreshing}
onClick={this.refreshData}
shape="circle"
icon={refreshing ? <LoadingOutlined /> : <ReloadOutlined />}
/>,
<Button
key="2"
onClick={this.sendOrAdd}
icon={
activeTab === "1" ? <SendOutlined /> : <PlusCircleOutlined />
}
disabled={activeTab === "1" && selectedParties.length === 0}
>
{this.getAddMessageText()}
</Button>,
activeTab === "3" && (
<Button
type="primary"
onClick={this.updateVideoSelector}
icon={<SaveOutlined />}
key="6"
loading={refreshing}
>
Save Video Selectors
</Button>
),
activeTab === "4" && (
<Button
type="primary"
onClick={this.updateVideoNameSelector}
icon={<SaveOutlined />}
key="7"
loading={refreshing}
>
Save Video Name Selectors
</Button>
),
]}
/>
<Tabs activeKey={activeTab} onChange={this.tabChange}>
<Tabs.TabPane tab="Parties" key="1">
<Table
rowSelection={{
selectedRowKeys: selectedParties,
onChange: (selectedParties) => 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) => (
<Descriptions bordered column={5}>
<Descriptions.Item span={2} label="# Of Users">
{r.userCount}
</Descriptions.Item>
<Descriptions.Item span={3} label="# Of Messages">
{r.messageCount}
</Descriptions.Item>
<Descriptions.Item span={5} label="Show Name">
{r.showName}
</Descriptions.Item>
<Descriptions.Item span={2} label="Show Time">
{this.getPrettyTime(r.time)}
</Descriptions.Item>
<Descriptions.Item span={3} label="Show Status">
<Badge
status={
r.status === "PLAYING"
? "success"
: r.status === "PAUSED"
? "warning"
: "error"
}
text={r.status}
/>
</Descriptions.Item>
</Descriptions>
),
}}
/>
</Tabs.TabPane>
<Tabs.TabPane tab="Versions" key="2">
<Table
loading={refreshing}
pagination={{ position: ['bottomCenter'] }}
pagination={{ position: ["bottomCenter"] }}
rowKey={(p) => p.version}
dataSource={versions}
columns={versionsColumns}
expandable={{
rowExpandable: (r) => r.alerts,
expandedRowClassName: () => 'alerts-wrapper',
expandedRowClassName: () => "alerts-wrapper",
expandedRowRender: (r) => (
<Row>
{r.alerts.map((a, i) => (
@@ -422,9 +721,15 @@ export default class Dashboard extends Component {
/>,
<Switch
onClick={(active) =>
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
)
}
/>
<Input.TextArea
@@ -440,7 +749,11 @@ export default class Dashboard extends Component {
autoSize={{ minRows: 2, maxRows: 2 }}
value={a.description}
onChange={(e) =>
this.handleAlertDescriptionChange(e.target.value, r.version, i)
this.handleAlertDescriptionChange(
e.target.value,
r.version,
i
)
}
/>
<Divider orientation="left">Link</Divider>
@@ -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
)
}
/>
<Input
value={a.link}
placeholder="Link"
onChange={(e) =>
this.handleAlertLinkChange(e.target.value, r.version, i)
this.handleAlertLinkChange(
e.target.value,
r.version,
i
)
}
/>
</Input.Group>
@@ -472,7 +793,10 @@ export default class Dashboard extends Component {
</Card>
</Col>
<Col>
<Card onClick={() => this.saveAlerts(r.version)} className="add-alert-card">
<Card
onClick={() => this.saveAlerts(r.version)}
className="add-alert-card"
>
<SaveFilled />
</Card>
</Col>
@@ -493,19 +817,41 @@ export default class Dashboard extends Component {
onChange={(e) => this.handleVideoSelectorChange(e, source)}
/>
))}
<Button
block
type="primary"
icon={<SaveOutlined />}
className="save-video-selector"
onClick={this.updateVideoSelector}
loading={refreshing}
>
Save Video Selectors
</Button>
</Tabs.TabPane>
<Tabs.TabPane tab="Name Selectors" key="4">
name selectors
<Collapse ghost>
{Object.keys(nameSelectors)
.sort()
.map((source) => (
<Collapse.Panel
key={`name-selector-${source}`}
header={this.getSourceName(source)}
>
<Input
key={`video-selector-${source}-show`}
className="video-selector-input"
addonBefore={"Show"}
value={nameSelectors[source]["show"]}
onChange={(e) =>
this.handleVideoNameSelectorChange(e, source, "show")
}
/>
<Input
key={`video-selector-${source}-episode`}
className="video-selector-input"
addonBefore={"Episode"}
value={nameSelectors[source]["episode"]}
onChange={(e) =>
this.handleVideoNameSelectorChange(
e,
source,
"episode"
)
}
/>
</Collapse.Panel>
))}
</Collapse>
</Tabs.TabPane>
</Tabs>
</Card>
+52 -3
View File
@@ -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%;
}
+6 -4
View File
@@ -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) => (
<div id="party-loading" className="inactive">
<Warning />
<Typography variant="overline">SORRY, WATCH PARTY IS UNAVAILABLE RIGHT NOW</Typography>
<Typography variant="overline">
SORRY, WATCH PARTY IS UNAVAILABLE RIGHT NOW
</Typography>
<Typography variant="caption">{props.status}</Typography>
</div>
);
+9 -5
View File
@@ -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,
+22 -17
View File
@@ -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 Promise.all(p).then((v) => {
mLogger.success("GOT ALL PARTIES", { parties: partyData.length });
return res.status(200).send({ partyData });
});
};
+32
View File
@@ -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" });
}
};
@@ -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" });
}
};
+30 -24
View File
@@ -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,